Example #1
0
        private void RenderHiDef(TextureCube texture, Matrix orientation, float exposure, RenderContext context)
        {
            var graphicsDevice = context.GraphicsService.GraphicsDevice;

            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            graphicsDevice.BlendState        = BlendState.Opaque;

            var    cameraNode = context.CameraNode;
            Matrix view       = cameraNode.View;
            Matrix projection = cameraNode.Camera.Projection;

            // Cube maps are left handed --> Sample with inverted z. (Otherwise, the
            // cube map and objects or texts in it are mirrored.)
            var mirrorZ = Matrix.CreateScale(1, 1, -1);

            _parameterWorldViewProjection.SetValue(
                (Matrix)(projection * view * new Matrix(orientation, Vector3.Zero) * mirrorZ));
            _parameterExposure.SetValue(new Vector4(exposure, exposure, exposure, 1));
            _textureParameter.SetValue(texture);

            if (context.IsHdrEnabled())
            {
                _passLinear.Apply();
            }
            else
            {
                _passGamma.Apply();
            }

            _submesh.Draw();
            savedRenderState.Restore();
        }
Example #2
0
        internal virtual void ProcessJobs(RenderContext context, RenderOrder order)
        {
            if (order == RenderOrder.BackToFront || order == RenderOrder.FrontToBack)
            {
                // The scene nodes are already sorted by distance.
                order = RenderOrder.UserDefined;
            }

            var savedRenderState = new RenderStateSnapshot(context.GraphicsService.GraphicsDevice);

            int index    = 0;
            var jobs     = Jobs.Array;
            int jobCount = Jobs.Count;

            while (index < jobCount)
            {
                var renderer = jobs[index].Renderer;

                // Find end of current batch.
                int endIndexExclusive = index + 1;
                while (endIndexExclusive < jobCount && jobs[endIndexExclusive].Renderer == renderer)
                {
                    endIndexExclusive++;
                }

                // Restore the render state. (The integrated scene node renderers properly
                // restore the render state, but third-party renderers might mess it up.)
                if (index > 0)
                {
                    savedRenderState.Restore();
                }

                // Submit batch to renderer.
                // (Use Accessor to expose current batch as IList<SceneNode>.)
                JobsAccessor.Set(Jobs, index, endIndexExclusive);
                renderer.Render(JobsAccessor, context, order);
                JobsAccessor.Reset();

                index = endIndexExclusive;
            }

            savedRenderState.Restore();
        }
Example #3
0
        /// <summary>
        /// Draws the textures.
        /// </summary>
        /// <param name="context">The render context.</param>
        /// <remarks>
        /// If <see cref="SpriteBatch"/> is <see langword="null"/>, then <see cref="Render"/> does
        /// nothing.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="context"/> is <see langword="null"/>.
        /// </exception>
        public void Render(RenderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (SpriteBatch == null)
            {
                return;
            }

            var count = _textures.Count;

            if (count == 0)
            {
                return;
            }

            context.Validate(SpriteBatch);

            var savedRenderState = new RenderStateSnapshot(SpriteBatch.GraphicsDevice);

            SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullNone);

            for (int i = 0; i < count; i++)
            {
                var textureInfo = _textures[i];

                if (textureInfo.Texture.IsDisposed)
                {
                    continue;
                }

                if (TextureHelper.IsFloatingPointFormat(textureInfo.Texture.Format))
                {
                    // Floating-point textures must not use linear hardware filtering!
                    SpriteBatch.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
                    SpriteBatch.Draw(textureInfo.Texture, textureInfo.Rectangle, Color.White);
                    SpriteBatch.GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
                }
                else
                {
                    SpriteBatch.Draw(textureInfo.Texture, textureInfo.Rectangle, Color.White);
                }
            }

            SpriteBatch.End();

            savedRenderState.Restore();
        }
Example #4
0
        private void ProcessJobs(RenderContext context)
        {
            var graphicsDevice   = GraphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            PrepareBillboards(context);

            // Batch billboards using the same texture.
            int index    = 0;
            var jobs     = _jobs.Array;
            int jobCount = _jobs.Count;

            while (index < jobCount)
            {
                uint textureId = jobs[index].TextureId;

                int endIndex = index + 1;
                while (endIndex < jobCount && jobs[endIndex].TextureId == textureId)
                {
                    endIndex++;
                }

                // Submit batch.
                if (textureId == FontTextureId)
                {
                    // Text
                    EndBillboards(context);
                    DrawText(index, endIndex, context);
                }
                else
                {
                    // Billboards, particles
                    BeginBillboards(context);
                    DrawBillboards(index, endIndex, context);
                }

                index = endIndex;
            }

            EndBillboards(context);
            savedRenderState.Restore();
        }
Example #5
0
        internal void ProcessInternal(RenderContext context)
        {
            Debug.Assert(Enabled, "PostProcessor.ProcessInternal should only be called when the post-processor is enabled.");

            var graphicsDevice = GraphicsService.GraphicsDevice;

            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            // Set render states. The blend state must be set by the user!
            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.DepthStencilState = DepthStencilState.None;

            // Preform post-processing.
            OnProcess(context);

            savedRenderState.Restore();

            // Reset the texture stages. If a floating point texture is set, we get exceptions
            // when a sampler with bilinear filtering is set.

            graphicsDevice.ResetTextures();
        }
        /// <summary>
        /// Clears the current render target (which must be the G-buffer).
        /// </summary>
        /// <param name="context">The render context.</param>
        public void Render(RenderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            context.Validate(_effect);

            var graphicsDevice   = _effect.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.DepthStencilState = DepthStencilState.None;
            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.BlendState        = BlendState.Opaque;

            // Clear to maximum depth.
            _parameterDepth.SetValue(1.0f);

            // The environment is facing the camera.
            // --> Set normal = cameraBackward.
            var cameraNode = context.CameraNode;

            _parameterNormal.SetValue((cameraNode != null) ? (Vector3)cameraNode.ViewInverse.GetColumn(2).XYZ : Vector3.Backward);

            // Clear specular to arbitrary value.
            _parameterSpecularPower.SetValue(1.0f);

            _effect.CurrentTechnique.Passes[0].Apply();

            // Draw full-screen quad using clip space coordinates.
            graphicsDevice.DrawQuad(
                new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0, 0)),
                new VertexPositionTexture(new Vector3(1, -1, 0), new Vector2(1, 1)));

            savedRenderState.Restore();
        }
        private void DrawText(int index, int endIndex, RenderContext context)
        {
            var graphicsDevice    = context.GraphicsService.GraphicsDevice;
            var savedRenderStates = new RenderStateSnapshot(graphicsDevice);

            // The sprite batch and text effect are only created when needed.
            if (_spriteBatch == null)
            {
                _spriteBatch = context.GraphicsService.GetSpriteBatch();
                _textEffect  = new BasicEffect(graphicsDevice)
                {
                    TextureEnabled     = true,
                    VertexColorEnabled = true,
                };
            }

            _textEffect.View       = (Matrix)context.CameraNode.View;
            _textEffect.Projection = context.CameraNode.Camera.Projection;

            var jobs = _jobs.Array;

            while (index < endIndex)
            {
                var node = jobs[index++].Node as BillboardNode;
                if (node == null)
                {
                    continue;
                }

                var billboard = node.Billboard as TextBillboard;
                if (billboard == null)
                {
                    continue;
                }

                var font = billboard.Font ?? _defaultFont;
                if (font == null)
                {
                    continue;
                }

                var text          = billboard.Text as string;
                var stringBuilder = billboard.Text as StringBuilder;
                if (string.IsNullOrEmpty(text) && (stringBuilder == null || stringBuilder.Length == 0))
                {
                    continue;
                }

                Vector3F position    = node.PoseWorld.Position;
                var      orientation = billboard.Orientation;

                #region ----- Billboarding -----

                // (Code copied from BillboardBatchReach.)

                // Normal
                Vector3F normal;
                if (orientation.Normal == BillboardNormal.ViewPlaneAligned)
                {
                    normal = _defaultNormal;
                }
                else if (orientation.Normal == BillboardNormal.ViewpointOriented)
                {
                    Vector3F n = _cameraPose.Position - position;
                    normal = n.TryNormalize() ? n : _defaultNormal;
                }
                else
                {
                    normal = node.Normal;
                }

                // Axis = up vector
                Vector3F axis = node.Axis;
                if (orientation.IsAxisInViewSpace)
                {
                    axis = _cameraPose.ToWorldDirection(axis);
                }

                if (1 - Vector3F.Dot(normal, axis) < Numeric.EpsilonF)
                {
                    // Normal and axis are parallel.
                    // --> Bend normal by adding a fraction of the camera down vector.
                    Vector3F cameraDown = -_cameraPose.Orientation.GetColumn(1);
                    normal += cameraDown * 0.001f;
                    normal.Normalize();
                }

                // Compute right.
                //Vector3F right = Vector3F.Cross(axis, normal);
                // Inlined:
                Vector3F right;
                right.X = axis.Y * normal.Z - axis.Z * normal.Y;
                right.Y = axis.Z * normal.X - axis.X * normal.Z;
                right.Z = axis.X * normal.Y - axis.Y * normal.X;
                if (!right.TryNormalize())
                {
                    right = normal.Orthonormal1; // Normal and axis are parallel --> Choose random perpendicular vector.
                }
                if (orientation.IsAxisFixed)
                {
                    // Make sure normal is perpendicular to right and up.
                    //normal = Vector3F.Cross(right, axis);
                    // Inlined:
                    normal.X = right.Y * axis.Z - right.Z * axis.Y;
                    normal.Y = right.Z * axis.X - right.X * axis.Z;
                    normal.Z = right.X * axis.Y - right.Y * axis.X;

                    // No need to normalize because right and up are normalized and perpendicular.
                }
                else
                {
                    // Make sure axis is perpendicular to normal and right.
                    //axis = Vector3F.Cross(normal, right);
                    // Inlined:
                    axis.X = normal.Y * right.Z - normal.Z * right.Y;
                    axis.Y = normal.Z * right.X - normal.X * right.Z;
                    axis.Z = normal.X * right.Y - normal.Y * right.X;

                    // No need to normalize because normal and right are normalized and perpendicular.
                }
                #endregion

                _textEffect.World = new Matrix(right.X, right.Y, right.Z, 0,
                                               -axis.X, -axis.Y, -axis.Z, 0,
                                               normal.X, normal.Y, normal.Z, 0,
                                               position.X, position.Y, position.Z, 1);

                Vector3F color3F = node.Color * billboard.Color;
                float    alpha   = node.Alpha * billboard.Alpha;
                Color    color   = new Color(color3F.X * alpha,
                                             color3F.Y * alpha,
                                             color3F.Z * alpha,
                                             alpha);

                Vector2 size   = (text != null) ? font.MeasureString(text) : font.MeasureString(stringBuilder);
                Vector2 origin = size / 2;
                float   scale  = node.ScaleWorld.Y; // Assume uniform scale.

                _spriteBatch.Begin(SpriteSortMode.Immediate, null, null, graphicsDevice.DepthStencilState, RasterizerState.CullNone, _textEffect);
                if (text != null)
                {
                    _spriteBatch.DrawString(font, text, Vector2.Zero, color, 0, origin, scale, SpriteEffects.None, 0);
                }
                else
                {
                    _spriteBatch.DrawString(font, stringBuilder, Vector2.Zero, color, 0, origin, scale, SpriteEffects.None, 0);
                }

                _spriteBatch.End();
            }

            savedRenderStates.Restore();
        }
Example #8
0
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            context.Validate(_effect);
            context.ThrowIfCameraMissing();

            var graphicsDevice   = context.GraphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            // Camera properties
            int viewportHeight = graphicsDevice.Viewport.Height;
            var cameraNode     = context.CameraNode;
            var projection     = cameraNode.Camera.Projection;

            _parameterProjection.SetValue(projection);

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            for (int i = 0; i < numberOfNodes; i++)
            {
                var node = nodes[i] as CloudLayerNode;
                if (node == null)
                {
                    continue;
                }

                // CloudLayerNode is visible in current frame.
                node.LastFrame = frame;

                if (node.CloudMap.Texture == null)
                {
                    continue;
                }

                var sunDirection = node.SunDirection;
                _parameterSunDirection.SetValue((Vector3)sunDirection);
                _parameterSkyCurvature.SetValue(node.SkyCurvature);
                _parameterTextureMatrix.SetValue((Matrix) new Matrix44F(node.TextureMatrix, Vector3F.Zero));

                // The sample at the pixel counts as one, the rest are for the blur.
                // Note: We must not set -1 because a for loop like
                //   for (int i = 0; i < -1, i++)
                // crashes the AMD DX9 WP8.1 graphics driver. LOL
                _parameterNumberOfSamples.SetValue(Math.Max(0, node.NumberOfSamples - 1));

                _parameterSampleDistance.SetValue(node.SampleDistance);
                _parameterScatterParameters.SetValue(new Vector3(node.ForwardScatterExponent, node.ForwardScatterScale, node.ForwardScatterOffset));
                _parameterHorizonFade.SetValue(new Vector2(node.HorizonFade, node.HorizonBias));
                _parameterSunLight.SetValue((Vector3)node.SunLight);
                _parameterAmbientLight.SetValue(new Vector4((Vector3)node.AmbientLight, node.Alpha));
                _parameterTexture.SetValue(node.CloudMap.Texture);

                // Occlusion query.
                if (graphicsDevice.GraphicsProfile != GraphicsProfile.Reach && node.SunQuerySize >= Numeric.EpsilonF)
                {
                    bool skipQuery = false;
                    if (node.OcclusionQuery != null)
                    {
                        if (node.OcclusionQuery.IsComplete)
                        {
                            node.TryUpdateSunOcclusion();
                        }
                        else
                        {
                            // The previous query is still not finished. Do not start a new query, this would
                            // create a SharpDX warning.
                            skipQuery = true;
                        }
                    }
                    else
                    {
                        node.OcclusionQuery = new OcclusionQuery(graphicsDevice);
                    }

                    if (!skipQuery)
                    {
                        node.IsQueryPending = true;

                        float totalPixels = viewportHeight * node.SunQuerySize;
                        totalPixels   *= totalPixels;
                        node.QuerySize = totalPixels;

                        // Use a camera which looks at the sun.
                        // Get an relative up vector which is not parallel to the forward direction.
                        var lookAtUp = Vector3F.UnitY;
                        if (Vector3F.AreNumericallyEqual(sunDirection, lookAtUp))
                        {
                            lookAtUp = Vector3F.UnitZ;
                        }

                        Vector3F zAxis = -sunDirection;
                        Vector3F xAxis = Vector3F.Cross(lookAtUp, zAxis).Normalized;
                        Vector3F yAxis = Vector3F.Cross(zAxis, xAxis);

                        var lookAtSunView = new Matrix(xAxis.X, yAxis.X, zAxis.X, 0,
                                                       xAxis.Y, yAxis.Y, zAxis.Y, 0,
                                                       xAxis.Z, yAxis.Z, zAxis.Z, 0,
                                                       0, 0, 0, 1);
                        _parameterView.SetValue(lookAtSunView);

                        graphicsDevice.BlendState        = GraphicsHelper.BlendStateNoColorWrite;
                        graphicsDevice.DepthStencilState = DepthStencilState.None;
                        graphicsDevice.RasterizerState   = RasterizerState.CullNone;

                        // Create small quad shortly behind the near plane.
                        // Note: We use an "untranslated" view matrix, so we can ignore the camera position.
                        float    width  = (projection.Top - projection.Bottom) * node.SunQuerySize;
                        Vector3F right  = sunDirection.Orthonormal1 * (width / 2);
                        Vector3F up     = sunDirection.Orthonormal2 * (width / 2);
                        Vector3F center = sunDirection * (projection.Near * 1.0001f);
                        _queryGeometry[0] = center - up - right;
                        _queryGeometry[1] = center + up - right;
                        _queryGeometry[2] = center - up + right;
                        _queryGeometry[3] = center + up + right;

                        if (node.CloudMap.Texture.Format == SurfaceFormat.Alpha8)
                        {
                            _passOcclusionAlpha.Apply();
                        }
                        else
                        {
                            _passOcclusionRgb.Apply();
                        }

                        node.OcclusionQuery.Begin();
                        graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _queryGeometry, 0, 2,
                                                          VertexPosition.VertexDeclaration);
                        node.OcclusionQuery.End();
                    }
                }
                else
                {
                    node.IsQueryPending = false;
                    node.SunOcclusion   = 0;
                }

                Matrix viewUntranslated = (Matrix) new Matrix44F(cameraNode.PoseWorld.Orientation.Transposed, new Vector3F(0));
                _parameterView.SetValue(viewUntranslated);

                // Render clouds.
                graphicsDevice.BlendState        = BlendState.AlphaBlend;
                graphicsDevice.RasterizerState   = RasterizerState.CullNone;
                graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;

                if (context.IsHdrEnabled())
                {
                    if (node.CloudMap.Texture.Format == SurfaceFormat.Alpha8)
                    {
                        _passCloudAlphaLinear.Apply();
                    }
                    else
                    {
                        _passCloudRgbLinear.Apply();
                    }
                }
                else
                {
                    if (node.CloudMap.Texture.Format == SurfaceFormat.Alpha8)
                    {
                        _passCloudAlphaGamma.Apply();
                    }
                    else
                    {
                        _passCloudRgbGamma.Apply();
                    }
                }

                _submesh.Draw();
            }

            savedRenderState.Restore();
        }
Example #9
0
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (nodes.Count == 0)
            return;

              context.Validate(_effect);

              var originalRenderTarget = context.RenderTarget;
              var originalViewport = context.Viewport;

              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.BlendState = BlendState.Opaque;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.DepthStencilState = DepthStencilState.None;

              int frame = context.Frame;
              float deltaTime = (float)context.DeltaTime.TotalSeconds;

              for (int nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++)
              {
            var cloudNode = nodes[nodeIndex] as CloudLayerNode;
            if (cloudNode == null)
              continue;

            var cloudMap = cloudNode.CloudMap as LayeredCloudMap;
            if (cloudMap == null)
              continue;

            // We update the cloud map only once per frame.
            if (cloudMap.LastFrame == frame)
              continue;

            cloudMap.LastFrame = frame;

            var layers = cloudMap.Layers;
            var animationTimes = cloudMap.AnimationTimes;
            var sources = cloudMap.SourceLayers;
            var targets = cloudMap.TargetLayers;
            var renderTargets = cloudMap.LayerTextures;

            // Animate the cloud map layers.
            for (int i = 0; i < LayeredCloudMap.NumberOfTextures; i++)
            {
              if (layers[i] == null || layers[i].Texture != null)
            continue;

              if (cloudMap.Random == null)
            cloudMap.Random = new Random(cloudMap.Seed);

              // Make sure there is a user-defined texture or data for procedural textures.
              if (sources[i] == null)
              {
            // Each octave is 128 x 128 (= 1 / 4 of the 512 * 512 noise texture).
            sources[i] = new PackedTexture(null, _noiseTexture, cloudMap.Random.NextVector2F(0, 1), new Vector2F(0.25f));
            targets[i] = new PackedTexture(null, _noiseTexture, cloudMap.Random.NextVector2F(0, 1), new Vector2F(0.25f));
            renderTargets[i] = new RenderTarget2D(graphicsDevice, 128, 128, false, SurfaceFormat.Alpha8, DepthFormat.None);
              }

              // Update animation time.
              animationTimes[i] += deltaTime * layers[i].AnimationSpeed;

              // Update source and target if animation time is beyond 1.
              if (animationTimes[i] > 1)
              {
            // Wrap animation time.
            animationTimes[i] = animationTimes[i] % 1;

            // Swap source and target.
            MathHelper.Swap(ref sources[i], ref targets[i]);

            // Set target to a new random part of the noise texture.
            targets[i].Offset = cloudMap.Random.NextVector2F(0, 1);
              }

              // Lerp source and target together to get the final noise texture.
              graphicsDevice.SetRenderTarget(renderTargets[i]);
              _parameterViewportSize.SetValue(new Vector2(graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height));
              _parameterTextures[0].SetValue(sources[i].TextureAtlas);
              _parameterTextures[1].SetValue(targets[i].TextureAtlas);
              _parameterTexture0Parameters.SetValue(new Vector4(sources[i].Scale.X, sources[i].Scale.Y, sources[i].Offset.X, sources[i].Offset.Y));
              _parameterTexture1Parameters.SetValue(new Vector4(targets[i].Scale.X, targets[i].Scale.Y, targets[i].Offset.X, targets[i].Offset.Y));
              _parameterLerp.SetValue(animationTimes[i]);
              _passLerp.Apply();
              graphicsDevice.DrawFullScreenQuad();
            }

            // Initialize the cloud map.
            if (cloudMap.Texture == null || cloudMap.Size != cloudMap.Texture.Width)
            {
              cloudMap.Texture.SafeDispose();

              var cloudTexture = new RenderTarget2D(
            graphicsDevice,
            cloudMap.Size,
            cloudMap.Size,
            false,
            SurfaceFormat.Alpha8,
            DepthFormat.None);

              cloudMap.SetTexture(cloudTexture);
            }

            // Combine the layers.
            graphicsDevice.SetRenderTarget((RenderTarget2D)cloudMap.Texture);
            _parameterViewportSize.SetValue(new Vector2(cloudMap.Texture.Width, cloudMap.Texture.Height));
            for (int i = 0; i < LayeredCloudMap.NumberOfTextures; i++)
            {
              var layer = layers[i] ?? EmptyLayer;
              _parameterTextures[i].SetValue(layer.Texture ?? renderTargets[i]);
              _parameterMatrices[i].SetValue((Matrix)new Matrix44F(layer.TextureMatrix, Vector3F.Zero));
              _parameterDensities[i].SetValue(new Vector2(layer.DensityScale, layer.DensityOffset));
            }
            _parameterCoverage.SetValue(cloudMap.Coverage);
            _parameterDensity.SetValue(cloudMap.Density);
            _passDensity.Apply();
            graphicsDevice.DrawFullScreenQuad();
              }

              savedRenderState.Restore();
              graphicsDevice.SetRenderTarget(null);
              context.RenderTarget = originalRenderTarget;
              context.Viewport = originalViewport;
        }
        private void DrawText(int index, int endIndex, RenderContext context)
        {
            var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderStates = new RenderStateSnapshot(graphicsDevice);

              // The sprite batch and text effect are only created when needed.
              if (_spriteBatch == null)
              {
            _spriteBatch = context.GraphicsService.GetSpriteBatch();
            _textEffect = new BasicEffect(graphicsDevice)
            {
              TextureEnabled = true,
              VertexColorEnabled = true,
            };
              }

              _textEffect.View = (Matrix)context.CameraNode.View;
              _textEffect.Projection = context.CameraNode.Camera.Projection;

              var jobs = _jobs.Array;
              while (index < endIndex)
              {
            var node = jobs[index++].Node as BillboardNode;
            if (node == null)
              continue;

            var billboard = node.Billboard as TextBillboard;
            if (billboard == null)
              continue;

            var font = billboard.Font ?? _defaultFont;
            if (font == null)
              continue;

            var text = billboard.Text as string;
            var stringBuilder = billboard.Text as StringBuilder;
            if (string.IsNullOrEmpty(text) && (stringBuilder == null || stringBuilder.Length == 0))
              continue;

            Vector3F position = node.PoseWorld.Position;
            var orientation = billboard.Orientation;

            #region ----- Billboarding -----

            // (Code copied from BillboardBatchReach.)

            // Normal
            Vector3F normal;
            if (orientation.Normal == BillboardNormal.ViewPlaneAligned)
            {
              normal = _defaultNormal;
            }
            else if (orientation.Normal == BillboardNormal.ViewpointOriented)
            {
              Vector3F n = _cameraPose.Position - position;
              normal = n.TryNormalize() ? n : _defaultNormal;
            }
            else
            {
              normal = node.Normal;
            }

            // Axis = up vector
            Vector3F axis = node.Axis;
            if (orientation.IsAxisInViewSpace)
              axis = _cameraPose.ToWorldDirection(axis);

            if (1 - Vector3F.Dot(normal, axis) < Numeric.EpsilonF)
            {
              // Normal and axis are parallel.
              // --> Bend normal by adding a fraction of the camera down vector.
              Vector3F cameraDown = -_cameraPose.Orientation.GetColumn(1);
              normal += cameraDown * 0.001f;
              normal.Normalize();
            }

            // Compute right.
            //Vector3F right = Vector3F.Cross(axis, normal);
            // Inlined:
            Vector3F right;
            right.X = axis.Y * normal.Z - axis.Z * normal.Y;
            right.Y = axis.Z * normal.X - axis.X * normal.Z;
            right.Z = axis.X * normal.Y - axis.Y * normal.X;
            if (!right.TryNormalize())
              right = normal.Orthonormal1;   // Normal and axis are parallel --> Choose random perpendicular vector.

            if (orientation.IsAxisFixed)
            {
              // Make sure normal is perpendicular to right and up.
              //normal = Vector3F.Cross(right, axis);
              // Inlined:
              normal.X = right.Y * axis.Z - right.Z * axis.Y;
              normal.Y = right.Z * axis.X - right.X * axis.Z;
              normal.Z = right.X * axis.Y - right.Y * axis.X;

              // No need to normalize because right and up are normalized and perpendicular.
            }
            else
            {
              // Make sure axis is perpendicular to normal and right.
              //axis = Vector3F.Cross(normal, right);
              // Inlined:
              axis.X = normal.Y * right.Z - normal.Z * right.Y;
              axis.Y = normal.Z * right.X - normal.X * right.Z;
              axis.Z = normal.X * right.Y - normal.Y * right.X;

              // No need to normalize because normal and right are normalized and perpendicular.
            }
            #endregion

            _textEffect.World = new Matrix(right.X, right.Y, right.Z, 0,
                                       -axis.X, -axis.Y, -axis.Z, 0,
                                       normal.X, normal.Y, normal.Z, 0,
                                       position.X, position.Y, position.Z, 1);

            Vector3F color3F = node.Color * billboard.Color;
            float alpha = node.Alpha * billboard.Alpha;
            Color color = new Color(color3F.X * alpha,
                                color3F.Y * alpha,
                                color3F.Z * alpha,
                                alpha);

            Vector2 size = (text != null) ? font.MeasureString(text) : font.MeasureString(stringBuilder);
            Vector2 origin = size / 2;
            float scale = node.ScaleWorld.Y; // Assume uniform scale.

            _spriteBatch.Begin(SpriteSortMode.Immediate, null, null, graphicsDevice.DepthStencilState, RasterizerState.CullNone, _textEffect);
            if (text != null)
              _spriteBatch.DrawString(font, text, Vector2.Zero, color, 0, origin, scale, SpriteEffects.None, 0);
            else
              _spriteBatch.DrawString(font, stringBuilder, Vector2.Zero, color, 0, origin, scale, SpriteEffects.None, 0);

            _spriteBatch.End();
              }

              savedRenderStates.Restore();
        }
Example #11
0
        /// <inheritdoc/>
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (nodes.Count == 0)
            {
                return;
            }

            context.ThrowIfCameraMissing();

            var originalRenderTarget = context.RenderTarget;
            var originalViewport     = context.Viewport;
            var originalSceneNode    = context.SceneNode;
            var originalTechnique    = context.Technique;

            var graphicsDevice   = context.GraphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            //int frame = context.Frame;
            //float deltaTime = (float)context.DeltaTime.TotalSeconds;

            for (int nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++)
            {
                var node = nodes[nodeIndex] as TerrainNode;
                if (node == null)
                {
                    continue;
                }

                context.SceneNode = node;

                context.RenderPass = RenderPassBase;
                ProcessClipmap(node, node.BaseClipmap, context);

                context.RenderPass = RenderPassDetail;
                ProcessClipmap(node, node.DetailClipmap, context);
            }

            context.RenderPass = null;

            // Clear invalid regions stored in terrain. (Note: Terrains can be shared.)
            for (int nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++)
            {
                var node = nodes[nodeIndex] as TerrainNode;
                if (node == null)
                {
                    continue;
                }

                node.Terrain.InvalidBaseRegions.Clear();
                node.Terrain.InvalidDetailRegions.Clear();
            }

            // The clipmap layers use a MipMapLodBias which must be reset.
            graphicsDevice.ResetSamplerStates();

            savedRenderState.Restore();
            graphicsDevice.SetRenderTarget(null);
            context.RenderTarget            = originalRenderTarget;
            context.Viewport                = originalViewport;
            context.SceneNode               = originalSceneNode;
            context.MaterialBinding         = null;
            context.MaterialInstanceBinding = null;
            context.Technique               = originalTechnique;
        }
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (nodes.Count == 0)
            {
                return;
            }

            context.Validate(_effect);
            context.ThrowIfCameraMissing();

            var graphicsDevice   = context.GraphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.BlendState        = BlendState.AlphaBlend;
            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;

            // Camera properties
            var    cameraNode = context.CameraNode;
            Matrix view       = (Matrix) new Matrix(cameraNode.PoseWorld.Orientation.Transposed, new Vector3());

            _parameterView.SetValue(view);
            Matrix projection = cameraNode.Camera.Projection;

            _parameterProjection.SetValue(projection);

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            for (int i = 0; i < numberOfNodes; i++)
            {
                var node = nodes[i] as GradientSkyNode;
                if (node == null)
                {
                    continue;
                }

                // GradientSkyNode is visible in current frame.
                node.LastFrame = frame;

                _parameterSunDirection.SetValue((Vector3)node.SunDirection);
                _parameterFrontColor.SetValue((Vector4)node.FrontColor);
                _parameterZenithColor.SetValue((Vector4)node.ZenithColor);
                _parameterBackColor.SetValue((Vector4)node.BackColor);
                _parameterGroundColor.SetValue((Vector4)node.GroundColor);
                _parameterShift.SetValue(new Vector4(node.FrontZenithShift, node.BackZenithShift, node.FrontGroundShift, node.BackGroundShift));

                if (node.CieSkyStrength < Numeric.EpsilonF)
                {
                    if (context.IsHdrEnabled())
                    {
                        _passLinear.Apply();
                    }
                    else
                    {
                        _passGamma.Apply();
                    }
                }
                else
                {
                    var p = node.CieSkyParameters;
                    _parameterAbcd.SetValue(new Vector4(p.A, p.B, p.C, p.D));
                    _parameterEAndStrength.SetValue(new Vector2(p.E, node.CieSkyStrength));

                    if (context.IsHdrEnabled())
                    {
                        _passCieLinear.Apply();
                    }
                    else
                    {
                        _passCieGamma.Apply();
                    }
                }

                _submesh.Draw();
            }

            savedRenderState.Restore();
        }
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            context.ThrowIfCameraMissing();
            context.ThrowIfSceneMissing();

            var originalRenderTarget  = context.RenderTarget;
            var originalViewport      = context.Viewport;
            var originalReferenceNode = context.ReferenceNode;

            var cameraNode = context.CameraNode;

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            // The scene node renderer should use the light camera instead of the player camera.
            context.CameraNode = _perspectiveCameraNode;
            context.Technique  = "Omnidirectional";

            var graphicsService  = context.GraphicsService;
            var graphicsDevice   = graphicsService.GraphicsDevice;
            var renderTargetPool = graphicsService.RenderTargetPool;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            for (int i = 0; i < numberOfNodes; i++)
            {
                var lightNode = nodes[i] as LightNode;
                if (lightNode == null)
                {
                    continue;
                }

                var shadow = lightNode.Shadow as CubeMapShadow;
                if (shadow == null)
                {
                    continue;
                }

                var light = lightNode.Light as PointLight;
                if (light == null)
                {
                    throw new GraphicsException("CubeMapShadow can only be used with a PointLight.");
                }

                // LightNode is visible in current frame.
                lightNode.LastFrame = frame;

                if (shadow.ShadowMap == null)
                {
                    shadow.ShadowMap = renderTargetPool.ObtainCube(
                        new RenderTargetFormat(
                            shadow.PreferredSize,
                            null,
                            false,
                            shadow.Prefer16Bit ? SurfaceFormat.HalfSingle : SurfaceFormat.Single,
                            DepthFormat.Depth24));
                }

                ((PerspectiveProjection)_perspectiveCameraNode.Camera.Projection).SetFieldOfView(
                    ConstantsF.PiOver2, 1, shadow.Near, light.Range);

                // World units per texel at a planar distance of 1 world unit.
                float unitsPerTexel = _perspectiveCameraNode.Camera.Projection.Width / (shadow.ShadowMap.Size * shadow.Near);

                // Convert depth bias from "texel" to  world space.
                // Minus to move receiver closer to light.
                shadow.EffectiveDepthBias = -shadow.DepthBias * unitsPerTexel;

                // Convert normal offset from "texel" to world space.
                shadow.EffectiveNormalOffset = shadow.NormalOffset * unitsPerTexel;

                var pose = lightNode.PoseWorld;

                context.ReferenceNode = lightNode;
                context.Object        = shadow;

                bool shadowMapContainsSomething = false;
                for (int side = 0; side < 6; side++)
                {
                    context.Data[RenderContextKeys.ShadowTileIndex] = BoxedIntegers[side];

                    graphicsDevice.SetRenderTarget(shadow.ShadowMap, CubeMapFaces[side]);
                    // context.RenderTarget = shadow.ShadowMap;   // TODO: Support cube maps targets in the render context.
                    context.Viewport = graphicsDevice.Viewport;

                    graphicsDevice.Clear(Color.White);

                    _perspectiveCameraNode.View = Matrix.CreateLookAt(
                        pose.Position,
                        pose.ToWorldPosition(CubeMapForwardVectors[side]),
                        pose.ToWorldDirection(CubeMapUpVectors[side]));

                    // Abort if this cube map frustum does not touch the camera frustum.
                    if (!context.Scene.HaveContact(cameraNode, _perspectiveCameraNode))
                    {
                        continue;
                    }

                    graphicsDevice.DepthStencilState = DepthStencilState.Default;
                    graphicsDevice.RasterizerState   = RasterizerState.CullCounterClockwise;
                    graphicsDevice.BlendState        = BlendState.Opaque;

                    shadowMapContainsSomething |= RenderCallback(context);
                }

                // Recycle shadow map if empty.
                if (!shadowMapContainsSomething)
                {
                    renderTargetPool.Recycle(shadow.ShadowMap);
                    shadow.ShadowMap = null;
                }
            }

            graphicsDevice.SetRenderTarget(null);
            savedRenderState.Restore();

            context.CameraNode    = cameraNode;
            context.Technique     = null;
            context.RenderTarget  = originalRenderTarget;
            context.Viewport      = originalViewport;
            context.ReferenceNode = originalReferenceNode;
            context.Object        = null;
            context.Data[RenderContextKeys.ShadowTileIndex] = null;
        }
Example #14
0
        private void RenderReach(SkyboxNode node, RenderContext context)
        {
            var graphicsDevice = context.GraphicsService.GraphicsDevice;

              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
              graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
              graphicsDevice.BlendState = node.EnableAlphaBlending ? BlendState.AlphaBlend : BlendState.Opaque;
              graphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

              // Change viewport to render all pixels at max z.
              var originalViewport = graphicsDevice.Viewport;
              var viewport = originalViewport;
              viewport.MinDepth = viewport.MaxDepth;
              graphicsDevice.Viewport = viewport;

              var cameraNode = context.CameraNode;
              var view = cameraNode.View;
              view.Translation = Vector3F.Zero;
              var projection = cameraNode.Camera.Projection;

              var basicEffect = (BasicEffect)_effect;
              basicEffect.View = (Matrix)view;
              basicEffect.Projection = projection;
              basicEffect.DiffuseColor = (Vector3)node.Color;
              basicEffect.Alpha = node.EnableAlphaBlending ? node.Alpha : 1;

              // Scale skybox such that it lies within view frustum:
              //   distance of a skybox corner = √3
              //   √3 * scale = far
              //   => scale = far / √3
              // (Note: If  near > far / √3  then the skybox will be clipped.)
              float scale = projection.Far * 0.577f;

              var orientation = node.PoseWorld.Orientation;

              // Positive X
              basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.PositiveX);
              basicEffect.World = (Matrix)new Matrix44F(orientation * scale, Vector3F.Zero);
              basicEffect.CurrentTechnique.Passes[0].Apply();
              graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

              // Negative X
              // transform = scale * rotY(180°)
              var transform = new Matrix33F(-scale, 0, 0, 0, scale, 0, 0, 0, -scale);
              basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.NegativeX);
              basicEffect.World = (Matrix)new Matrix44F(orientation * transform, Vector3F.Zero);
              basicEffect.CurrentTechnique.Passes[0].Apply();
              graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

              // Positive Y
              // transform = scale * rotX(90°) * rotY(90°)
              transform = new Matrix33F(0, 0, scale, scale, 0, 0, 0, scale, 0);
              basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.PositiveY);
              basicEffect.World = (Matrix)new Matrix44F(orientation * transform, Vector3F.Zero);
              basicEffect.CurrentTechnique.Passes[0].Apply();
              graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

              // Negative Y
              // transform = scale * rotX(-90°) * rotY(90°)
              transform = new Matrix33F(0, 0, scale, -scale, 0, 0, 0, -scale, 0);
              basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.NegativeY);
              basicEffect.World = (Matrix)new Matrix44F(orientation * transform, Vector3F.Zero);
              basicEffect.CurrentTechnique.Passes[0].Apply();
              graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

              // Cube maps are left-handed, where as the world is right-handed!

              // Positive Z (= negative Z in world space)
              // transform = scale * rotY(90°)
              transform = new Matrix33F(0, 0, scale, 0, scale, 0, -scale, 0, 0);
              basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.PositiveZ);
              basicEffect.World = (Matrix)new Matrix44F(orientation * transform, Vector3F.Zero);
              basicEffect.CurrentTechnique.Passes[0].Apply();
              graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

              // Negative Z (= positive Z in world space)
              // transform = scale * rotY(-90°)
              transform = new Matrix33F(0, 0, -scale, 0, scale, 0, scale, 0, 0);
              basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.NegativeZ);
              basicEffect.World = (Matrix)new Matrix44F(orientation * transform, Vector3F.Zero);
              basicEffect.CurrentTechnique.Passes[0].Apply();
              graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

              graphicsDevice.Viewport = originalViewport;
              savedRenderState.Restore();
        }
Example #15
0
        private void RenderHiDef(SkyboxNode node, RenderContext context)
        {
            var graphicsDevice = context.GraphicsService.GraphicsDevice;

              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
              graphicsDevice.BlendState = node.EnableAlphaBlending ? BlendState.AlphaBlend : BlendState.Opaque;

              bool sourceIsFloatingPoint = TextureHelper.IsFloatingPointFormat(node.Texture.Format);

              // Set sampler state. (Floating-point textures cannot use linear filtering. (XNA would throw an exception.))
              if (sourceIsFloatingPoint)
            graphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
              else
            graphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;

              var cameraNode = context.CameraNode;
              Matrix44F view = cameraNode.View;
              Matrix44F projection = cameraNode.Camera.Projection;

              // Cube maps are left handed --> Sample with inverted z. (Otherwise, the
              // cube map and objects or texts in it are mirrored.)
              var mirrorZ = Matrix44F.CreateScale(1, 1, -1);
              Matrix33F orientation = node.PoseWorld.Orientation;
              _parameterWorldViewProjection.SetValue((Matrix)(projection * view * new Matrix44F(orientation, Vector3F.Zero) * mirrorZ));

              Vector4 color = node.EnableAlphaBlending
                      ? new Vector4((Vector3)node.Color * node.Alpha, node.Alpha) // Premultiplied
                      : new Vector4((Vector3)node.Color, 1);                      // Opaque
              _parameterColor.SetValue(color);
              _textureParameter.SetValue(node.Texture);

              if (node.Encoding is RgbEncoding)
              {
            _parameterTextureSize.SetValue(node.Texture.Size);
            if (context.IsHdrEnabled())
              _passRgbToRgb.Apply();
            else
              _passRgbToSRgb.Apply();
              }
              else if (node.Encoding is SRgbEncoding)
              {
            if (!sourceIsFloatingPoint)
            {
              if (context.IsHdrEnabled())
            _passSRgbToRgb.Apply();
              else
            _passSRgbToSRgb.Apply();
            }
            else
            {
              throw new GraphicsException("sRGB encoded skybox cube maps must not use a floating point format.");
            }
              }
              else if (node.Encoding is RgbmEncoding)
              {
            float max = GraphicsHelper.ToGamma(((RgbmEncoding)node.Encoding).Max);
            _parameterRgbmMaxValue.SetValue(max);

            if (context.IsHdrEnabled())
              _passRgbmToRgb.Apply();
            else
              _passRgbmToSRgb.Apply();
              }
              else
              {
            throw new NotSupportedException("The SkyBoxRenderer supports only RgbEncoding, SRgbEncoding and RgbmEncoding.");
              }

              _submesh.Draw();
              savedRenderState.Restore();
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.ThrowIfCameraMissing();
              context.ThrowIfSceneMissing();

              var originalRenderTarget = context.RenderTarget;
              var originalViewport = context.Viewport;
              var originalReferenceNode = context.ReferenceNode;

              var cameraNode = context.CameraNode;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              // The scene node renderer should use the light camera instead of the player camera.
              context.CameraNode = _perspectiveCameraNode;
              context.Technique = "Omnidirectional";

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var renderTargetPool = graphicsService.RenderTargetPool;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              for (int i = 0; i < numberOfNodes; i++)
              {
            var lightNode = nodes[i] as LightNode;
            if (lightNode == null)
              continue;

            var shadow = lightNode.Shadow as CubeMapShadow;
            if (shadow == null)
              continue;

            var light = lightNode.Light as PointLight;
            if (light == null)
              throw new GraphicsException("CubeMapShadow can only be used with a PointLight.");

            // LightNode is visible in current frame.
            lightNode.LastFrame = frame;

            if (shadow.ShadowMap == null)
            {
              shadow.ShadowMap = renderTargetPool.ObtainCube(
            new RenderTargetFormat(
              shadow.PreferredSize,
              null,
              false,
              shadow.Prefer16Bit ? SurfaceFormat.HalfSingle : SurfaceFormat.Single,
              DepthFormat.Depth24));
            }

            ((PerspectiveProjection)_perspectiveCameraNode.Camera.Projection).SetFieldOfView(
              ConstantsF.PiOver2, 1, shadow.Near, light.Range);

            // World units per texel at a planar distance of 1 world unit.
            float unitsPerTexel = _perspectiveCameraNode.Camera.Projection.Width / (shadow.ShadowMap.Size * shadow.Near);

            // Convert depth bias from "texel" to  world space.
            // Minus to move receiver closer to light.
            shadow.EffectiveDepthBias = -shadow.DepthBias * unitsPerTexel;

            // Convert normal offset from "texel" to world space.
            shadow.EffectiveNormalOffset = shadow.NormalOffset * unitsPerTexel;

            var pose = lightNode.PoseWorld;

            context.ReferenceNode = lightNode;
            context.Object = shadow;

            bool shadowMapContainsSomething = false;
            for (int side = 0; side < 6; side++)
            {
              context.Data[RenderContextKeys.ShadowTileIndex] = BoxedIntegers[side];

              graphicsDevice.SetRenderTarget(shadow.ShadowMap, CubeMapFaces[side]);
              // context.RenderTarget = shadow.ShadowMap;   // TODO: Support cube maps targets in the render context.
              context.Viewport = graphicsDevice.Viewport;

              graphicsDevice.Clear(Color.White);

              _perspectiveCameraNode.View = Matrix44F.CreateLookAt(
            pose.Position,
            pose.ToWorldPosition(CubeMapForwardVectors[side]),
            pose.ToWorldDirection(CubeMapUpVectors[side]));

              // Abort if this cube map frustum does not touch the camera frustum.
              if (!context.Scene.HaveContact(cameraNode, _perspectiveCameraNode))
            continue;

              graphicsDevice.DepthStencilState = DepthStencilState.Default;
              graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
              graphicsDevice.BlendState = BlendState.Opaque;

              shadowMapContainsSomething |= RenderCallback(context);
            }

            // Recycle shadow map if empty.
            if (!shadowMapContainsSomething)
            {
              renderTargetPool.Recycle(shadow.ShadowMap);
              shadow.ShadowMap = null;
            }
              }

              graphicsDevice.SetRenderTarget(null);
              savedRenderState.Restore();

              context.CameraNode = cameraNode;
              context.Technique = null;
              context.RenderTarget = originalRenderTarget;
              context.Viewport = originalViewport;
              context.ReferenceNode = originalReferenceNode;
              context.Object = null;
              context.Data[RenderContextKeys.ShadowTileIndex] = null;
        }
Example #17
0
        /// <summary>
        /// Draws the texts.
        /// </summary>
        /// <param name="context">The render context.</param>
        /// <remarks>
        /// If <see cref="SpriteBatch"/> or <see cref="SpriteFont"/> are <see langword="null"/>, then 
        /// <see cref="Render"/> does nothing.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="context"/> is <see langword="null"/>.
        /// </exception>
        public void Render(RenderContext context)
        {
            if (context == null)
            throw new ArgumentNullException("context");

              if (SpriteBatch == null || SpriteFont == null)
            return;

              context.Validate(SpriteBatch);

              if (_texts2D.Count == 0 && _texts3D.Count == 0)
            return;

              if (_texts3D.Count > 0)
            context.ThrowIfCameraMissing();

              var savedRenderState = new RenderStateSnapshot(SpriteBatch.GraphicsDevice);

              if (EnableDepthTest)
              {
            SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullNone);
              }
              else
              {
            SpriteBatch.Begin();
              }

              // ----- Draw world space text.
              if (_texts3D.Count > 0)
              {
            CameraNode cameraNode = context.CameraNode;
            Matrix44F viewProjection = cameraNode.Camera.Projection * cameraNode.View;
            Viewport viewport = SpriteBatch.GraphicsDevice.Viewport;

            foreach (var textInfo in _texts3D)
            {
              // Transform position from world space to the viewport.
              Vector3F pos = viewport.ProjectToViewport(textInfo.Position, viewProjection);
              if (pos.Z < 0 || pos.Z > 1)
            continue;

              // Snap to pixels. Also add a small bias in one direction because when we draw text at
              // certain positions (e.g. view space origin) and the presentation target width is an
              // odd number, the pos will be exactly at pixel centers and due to numerical errors it
              // would jitter between pixels if the camera moves slightly.
              pos.X = (float)Math.Round(pos.X + 0.01f);
              pos.Y = (float)Math.Round(pos.Y + 0.01f);

              var textAsString = textInfo.Text as string;
              if (!string.IsNullOrEmpty(textAsString))
              {
            var textOrigin = GetOrigin(textAsString, textInfo.RelativeOrigin);
            SpriteBatch.DrawString(SpriteFont, textAsString, new Vector2(pos.X, pos.Y), textInfo.Color, 0, textOrigin, 1.0f, SpriteEffects.None, pos.Z);
              }
              else
              {
            var textAsStringBuilder = textInfo.Text as StringBuilder;
            if (textAsStringBuilder != null && textAsStringBuilder.Length > 0)
            {
              var textOrigin = GetOrigin(textAsStringBuilder, textInfo.RelativeOrigin);
              SpriteBatch.DrawString(SpriteFont, textAsStringBuilder, new Vector2(pos.X, pos.Y), textInfo.Color, 0, textOrigin, 1, SpriteEffects.None, pos.Z);
            }
              }
            }
              }

              // ----- Draw screen space text.
              foreach (var textInfo in _texts2D)
              {
            var textAsString = textInfo.Text as string;
            if (!string.IsNullOrEmpty(textAsString))
            {
              var textOrigin = GetOrigin(textAsString, textInfo.RelativeOrigin);
              SpriteBatch.DrawString(SpriteFont, textAsString, (Vector2)textInfo.Position, textInfo.Color, 0, textOrigin, 1, SpriteEffects.None, 0);
            }
            else
            {
              var textAsStringBuilder = textInfo.Text as StringBuilder;
              if (textAsStringBuilder != null && textAsStringBuilder.Length > 0)
              {
            var textOrigin = GetOrigin(textAsStringBuilder, textInfo.RelativeOrigin);
            SpriteBatch.DrawString(SpriteFont, textAsStringBuilder, (Vector2)textInfo.Position, textInfo.Color, 0, textOrigin, 1, SpriteEffects.None, 0);
              }
            }
              }

              SpriteBatch.End();

              savedRenderState.Restore();
        }
Example #18
0
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (nodes.Count == 0)
            return;

              context.Validate(_spriteBatch);
              context.ThrowIfCameraMissing();

              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              // Camera properties
              var cameraNode = context.CameraNode;
              Matrix44F viewProjection = cameraNode.Camera.Projection * cameraNode.View;
              var viewport = graphicsDevice.Viewport;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              SpriteSortMode sortMode;
              switch (order)
              {
            case RenderOrder.Default:
              sortMode = SpriteSortMode.Texture;
              break;
            case RenderOrder.FrontToBack:
              sortMode = SpriteSortMode.FrontToBack;
              break;
            case RenderOrder.BackToFront:
              sortMode = SpriteSortMode.BackToFront;
              break;
            case RenderOrder.UserDefined:
            default:
              sortMode = SpriteSortMode.Deferred;
              break;
              }

              _spriteBatch.Begin(sortMode, graphicsDevice.BlendState, null, graphicsDevice.DepthStencilState, null);

              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i] as SpriteNode;
            if (node == null)
              continue;

            // SpriteNode is visible in current frame.
            node.LastFrame = frame;

            // Position, size, and origin in pixels.
            Vector3F position = new Vector3F();
            Vector2 size = new Vector2();
            Vector2 origin = new Vector2();

            var bitmapSprite = node.Sprite as ImageSprite;
            if (bitmapSprite != null)
            {
              var packedTexture = bitmapSprite.Texture;
              if (packedTexture != null)
              {
            // Project into viewport and snap to pixels.
            position = viewport.ProjectToViewport(node.PoseWorld.Position, viewProjection);
            position.X = (int)(position.X + 0.5f);
            position.Y = (int)(position.Y + 0.5f);

            // Get source rectangle (pixel bounds).
            var sourceRectangle = packedTexture.GetBounds(node.AnimationTime);
            size = new Vector2(sourceRectangle.Width, sourceRectangle.Height);

            // Premultiply color.
            Vector3F color3F = node.Color;
            float alpha = node.Alpha;
            Color color = new Color(color3F.X * alpha, color3F.Y * alpha, color3F.Z * alpha, alpha);

            // Get absolute origin (relative to pixel bounds).
            origin = (Vector2)node.Origin * size;

            // Draw using SpriteBatch.
            _spriteBatch.Draw(
              packedTexture.TextureAtlas, new Vector2(position.X, position.Y), sourceRectangle,
              color, node.Rotation, origin, (Vector2)node.Scale, SpriteEffects.None, position.Z);
              }
            }
            else
            {
              var textSprite = node.Sprite as TextSprite;
              if (textSprite != null)
              {
            var font = textSprite.Font ?? _spriteFont;
            if (font != null)
            {
              // Text can be a string or StringBuilder.
              var text = textSprite.Text as string;
              if (text != null)
              {
                if (text.Length > 0)
                {
                  // Project into viewport and snap to pixels.
                  position = viewport.ProjectToViewport(node.PoseWorld.Position, viewProjection);
                  position.X = (int)(position.X + 0.5f);
                  position.Y = (int)(position.Y + 0.5f);

                  // Premultiply color.
                  Vector3F color3F = node.Color;
                  float alpha = node.Alpha;
                  Color color = new Color(color3F.X * alpha, color3F.Y * alpha, color3F.Z * alpha, alpha);

                  // Get absolute origin (relative to pixel bounds).
                  size = font.MeasureString(text);
                  origin = (Vector2)node.Origin * size;

                  // Draw using SpriteBatch.
                  _spriteBatch.DrawString(
                    font, text, new Vector2(position.X, position.Y),
                    color, node.Rotation, origin, (Vector2)node.Scale,
                    SpriteEffects.None, position.Z);
                }
              }
              else
              {
                var stringBuilder = textSprite.Text as StringBuilder;
                if (stringBuilder != null && stringBuilder.Length > 0)
                {
                  // Project into viewport and snap to pixels.
                  position = viewport.ProjectToViewport(node.PoseWorld.Position, viewProjection);
                  position.X = (int)(position.X + 0.5f);
                  position.Y = (int)(position.Y + 0.5f);

                  // Premultiply color.
                  Vector3F color3F = node.Color;
                  float alpha = node.Alpha;
                  Color color = new Color(color3F.X * alpha, color3F.Y * alpha, color3F.Z * alpha, alpha);

                  // Get absolute origin (relative to pixel bounds).
                  size = font.MeasureString(stringBuilder);
                  origin = (Vector2)node.Origin * size;

                  // Draw using SpriteBatch.
                  _spriteBatch.DrawString(
                    font, stringBuilder, new Vector2(position.X, position.Y),
                    color, node.Rotation, origin, (Vector2)node.Scale,
                    SpriteEffects.None, position.Z);
                }
              }
            }
              }
            }

            // Store bounds an depth for hit tests.
            node.LastBounds = new Rectangle(
              (int)(position.X - origin.X),
              (int)(position.Y - origin.Y),
              (int)(size.X * node.Scale.X),
              (int)(size.Y * node.Scale.Y));

            node.LastDepth = position.Z;
              }

              _spriteBatch.End();
              savedRenderState.Restore();
        }
Example #19
0
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              float deltaTime = (float)context.DeltaTime.TotalSeconds;

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var renderTargetPool = graphicsService.RenderTargetPool;

              var cameraNode = context.CameraNode;
              Projection projection = cameraNode.Camera.Projection;
              Pose view = cameraNode.PoseWorld.Inverse;

              // Around the camera we push the waves down to avoid that the camera cuts the near plane.
              // Get largest vector from camera to near plane corners.
              float nearPlaneRadius =
            new Vector3F(Math.Max(Math.Abs(projection.Right), Math.Abs(projection.Left)),
                     Math.Max(Math.Abs(projection.Top), Math.Abs(projection.Bottom)),
                     projection.Near
                    ).Length;

              var originalSourceTexture = context.SourceTexture;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              // Water surface is opaque.
              graphicsDevice.BlendState = BlendState.Opaque;

              #region ----- Common Effect Parameters -----

              _parameterView.SetValue(view);
              _parameterProjection.SetValue(projection);
              _parameterCameraParameters.SetValue(new Vector4(
            (Vector3)cameraNode.PoseWorld.Position,
            cameraNode.Camera.Projection.Far));

              var viewport = graphicsDevice.Viewport;
              _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height));

              _parameterTime.SetValue((float)context.Time.TotalSeconds);

              // Query ambient and directional lights.
              var lightQuery = context.Scene.Query<GlobalLightQuery>(cameraNode, context);
              Vector3F ambientLight = Vector3F.Zero;
              if (lightQuery.AmbientLights.Count > 0)
              {
            var light = (AmbientLight)lightQuery.AmbientLights[0].Light;
            ambientLight = light.Color * light.Intensity * light.HdrScale;
              }

              _parameterAmbientLight.SetValue((Vector3)ambientLight);

              Vector3F directionalLightDirection = new Vector3F(0, -1, 0);
              Vector3F directionalLightIntensity = Vector3F.Zero;
              if (lightQuery.DirectionalLights.Count > 0)
              {
            var lightNode = lightQuery.DirectionalLights[0];
            var light = (DirectionalLight)lightNode.Light;
            directionalLightDirection = -lightNode.PoseWorld.Orientation.GetColumn(2);
            directionalLightIntensity = light.Color * light.SpecularIntensity * light.HdrScale;
              }

              _parameterDirectionalLightDirection.SetValue((Vector3)directionalLightDirection);
              _parameterDirectionalLightIntensity.SetValue((Vector3)directionalLightIntensity);

              _parameterGBuffer0.SetValue(context.GBuffer0);

              if (_parameterNoiseMap != null)
            _parameterNoiseMap.SetValue(_noiseMap);
              #endregion

              #region ----- Fog Parameters -----

              var fogNodes = context.Scene.Query<FogQuery>(cameraNode, context).FogNodes;
              SetFogParameters(fogNodes, cameraNode, directionalLightDirection);
              #endregion

              _parameterProjectedGridParameters.SetValue(new Vector3(
              ProjectedGridParameters.EdgeAttenuation,
              ProjectedGridParameters.DistanceAttenuationStart,
              ProjectedGridParameters.DistanceAttenuationEnd));

              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i] as WaterNode;
            if (node == null)
              continue;

            // Node is visible in current frame.
            node.LastFrame = frame;

            var data = node.RenderData as WaterRenderData;
            if (data == null)
            {
              data = new WaterRenderData();
              node.RenderData = data;
            }

            var water = node.Water;
            bool isCameraUnderwater = node.EnableUnderwaterEffect && node.IsUnderwater(cameraNode.PoseWorld.Position);

            #region ----- Wave bending -----

            // Waves should not cut the near plane. --> Bend waves up or down if necessary.

            // Limits
            float upperLimit; // Waves must not move above this value.
            float lowerLimit; // Waves must not move below this value.

            // Bending fades in over interval [bendStart, bendEnd]:
            //   distance ≤ bendStart ............. Wave is bent up or down.
            //   bendStart < distance < bendEnd ... Lerp between normal wave and bent wave.
            //   distance ≥ bendEnd ............... Normal wave.
            float bendStart = 1 * nearPlaneRadius;
            float bendEnd = 10 * nearPlaneRadius;

            if (!isCameraUnderwater)
            {
              // Bend waves down below the camera.
              upperLimit = cameraNode.PoseWorld.Position.Y - nearPlaneRadius;
              lowerLimit = -1e20f;

              if (node.EnableUnderwaterEffect)
              {
            if (node.Waves == null || node.Waves.DisplacementMap == null)
            {
              // No displacement. The wave bending stuff does not work because the surface
              // is usually not tessellated. We have to render the underwater geometry when
              // camera near plane might cut the water surface.
              if (node.Volume == null)
              {
                // Test water plane.
                isCameraUnderwater = (cameraNode.PoseWorld.Position.Y - nearPlaneRadius) < node.PoseWorld.Position.Y;
              }
              else
              {
                // Test water AABB.
                var aabb = node.Aabb;
                aabb.Minimum -= new Vector3F(nearPlaneRadius);
                aabb.Maximum += new Vector3F(nearPlaneRadius);
                isCameraUnderwater = GeometryHelper.HaveContact(aabb, cameraNode.PoseWorld.Position);
              }
            }
              }
            }
            else
            {
              // Camera is underwater, bend triangles up above camera.
              upperLimit = 1e20f;
              lowerLimit = cameraNode.PoseWorld.Position.Y + nearPlaneRadius;
            }

            _parameterCameraMisc.SetValue(new Vector4(upperLimit, lowerLimit, bendStart, bendEnd));
            #endregion

            // Update the submesh for the given water volume.
            data.UpdateSubmesh(graphicsService, node);

            #region ----- Scroll Normal Maps -----

            // We update the normal map offsets once(!) per frame.
            // Note: We could skip the offsets and compute all in the shader using absolute
            // time instead of deltaTime, but then the user cannot change the NormalMapVelocity
            // smoothly.
            if (data.LastNormalUpdateFrame != frame)
            {
              data.LastNormalUpdateFrame = frame;

              var baseVelocity = (node.Flow != null) ? node.Flow.BaseVelocity : Vector3F.Zero;

              // Increase offset.
              // (Note: We have to subtract value and divide by scale because if the normal
              // should scroll to the right, we have to move the texcoords in the other direction.)
              data.NormalMapOffset0.X -= (water.NormalMap0Velocity.X + baseVelocity.X) * deltaTime / water.NormalMap0Scale;
              data.NormalMapOffset0.Y -= (water.NormalMap0Velocity.Z + baseVelocity.Y) * deltaTime / water.NormalMap0Scale;
              data.NormalMapOffset1.X -= (water.NormalMap1Velocity.X + baseVelocity.X) * deltaTime / water.NormalMap1Scale;
              data.NormalMapOffset1.Y -= (water.NormalMap1Velocity.Z + baseVelocity.Y) * deltaTime / water.NormalMap1Scale;

              // Keep only the fractional part to avoid overflow.
              data.NormalMapOffset0.X = MathHelper.Frac(data.NormalMapOffset0.X);
              data.NormalMapOffset0.Y = MathHelper.Frac(data.NormalMapOffset0.Y);
              data.NormalMapOffset1.X = MathHelper.Frac(data.NormalMapOffset1.X);
              data.NormalMapOffset1.Y = MathHelper.Frac(data.NormalMapOffset1.Y);
            }
            #endregion

            _parameterSurfaceLevel.SetValue(node.PoseWorld.Position.Y);

            #region ----- Reflection Parameters -----

            if (node.PlanarReflection != null
            && node.PlanarReflection.ActualIsEnabled
            && node.PlanarReflection.RenderToTexture.Texture is Texture2D)
            {
              // Planar reflection.
              var renderToTexture = node.PlanarReflection.RenderToTexture;
              var texture = (Texture2D)renderToTexture.Texture;

              _parameterReflectionTypeParameters.SetValue(new Vector2(0, 1));
              _parameterReflectionMatrix.SetValue((Matrix)renderToTexture.TextureMatrix);
              _parameterReflectionTextureSize.SetValue(new Vector2(texture.Width, texture.Height));
              if (_parameterPlanarReflectionMap != null)
            _parameterPlanarReflectionMap.SetValue(texture);

              _parameterReflectionParameters.SetValue(new Vector4(
            (Vector3)water.ReflectionColor,
            water.ReflectionDistortion));
            }
            else if (node.SkyboxReflection != null)
            {
              // Cube map reflection.
              var rgbmEncoding = node.SkyboxReflection.Encoding as RgbmEncoding;
              float rgbmMax = 1;
              if (rgbmEncoding != null)
            rgbmMax = GraphicsHelper.ToGamma(rgbmEncoding.Max);
              else if (!(node.SkyboxReflection.Encoding is SRgbEncoding))
            throw new NotImplementedException("The reflected skybox must be encoded using sRGB or RGBM.");

              _parameterReflectionTypeParameters.SetValue(new Vector2(1, rgbmMax));

              // Cube maps are left handed --> Sample with inverted z. (Otherwise, the
              // cube map and objects or texts in it are mirrored.)
              var mirrorZ = Matrix44F.CreateScale(1, 1, -1);
              Matrix33F orientation = node.SkyboxReflection.PoseWorld.Orientation;
              _parameterReflectionMatrix.SetValue((Matrix)(new Matrix44F(orientation, Vector3F.Zero) * mirrorZ));

              if (_parameterCubeReflectionMap != null)
            _parameterCubeReflectionMap.SetValue(node.SkyboxReflection.Texture);

              _parameterReflectionParameters.SetValue(new Vector4(
            (Vector3)(water.ReflectionColor * node.SkyboxReflection.Color),
            water.ReflectionDistortion));
            }
            else
            {
              // No reflection texture. The reflection shows only the ReflectionColor.
              _parameterReflectionTypeParameters.SetValue(new Vector2(-1, 1));
              _parameterReflectionParameters.SetValue(new Vector4(
            (Vector3)water.ReflectionColor,
            water.ReflectionDistortion));
            }
            #endregion

            #region ----- Refraction Parameters -----

            // If we do not have a source texture, resolve the current render target
            // and immediately rebuilt it.
            if (context.SourceTexture == null && context.RenderTarget != null)
            {
              // Get RebuildZBufferRenderer from RenderContext.
              RebuildZBufferRenderer rebuildZBufferRenderer = null;
              object obj;
              if (context.Data.TryGetValue(RenderContextKeys.RebuildZBufferRenderer, out obj))
            rebuildZBufferRenderer = obj as RebuildZBufferRenderer;

              // If we didn't find the renderer in the context, use a default instance.
              if (rebuildZBufferRenderer == null)
              {
            if (_defaultRebuildZBufferRenderer == null)
              _defaultRebuildZBufferRenderer = new RebuildZBufferRenderer(graphicsService);

            rebuildZBufferRenderer = _defaultRebuildZBufferRenderer;
              }

              context.SourceTexture = context.RenderTarget;
              context.RenderTarget = renderTargetPool.Obtain2D(new RenderTargetFormat(context.RenderTarget));
              graphicsDevice.SetRenderTarget(context.RenderTarget);
              graphicsDevice.Viewport = context.Viewport;
              rebuildZBufferRenderer.Render(context, context.SourceTexture);
            }

            _parameterRefractionTexture.SetValue(context.SourceTexture);
            _parameterRefractionParameters.SetValue(new Vector4(
              ((Vector3)water.RefractionColor),
              water.RefractionDistortion));
            #endregion

            #region ----- Other Water Effect Parameters -----

            if (water.NormalMap0 != null)
            {
              if (_parameterNormalMap0 != null)
            _parameterNormalMap0.SetValue(water.NormalMap0);

              _parameterNormalMap0Parameters.SetValue(new Vector4(
            1 / water.NormalMap0Scale,
            data.NormalMapOffset0.X,
            data.NormalMapOffset0.Y,
            water.NormalMap0Strength));
            }
            else
            {
              if (_parameterNormalMap0 != null)
            _parameterNormalMap0.SetValue(_graphicsService.GetDefaultNormalTexture());
              _parameterNormalMap0Parameters.SetValue(new Vector4(1, 0, 0, 0));
            }

            if (water.NormalMap1 != null)
            {
              if (_parameterNormalMap1 != null)
            _parameterNormalMap1.SetValue(water.NormalMap1);

              _parameterNormalMap1Parameters.SetValue(new Vector4(
            1 / water.NormalMap1Scale,
            data.NormalMapOffset1.X,
            data.NormalMapOffset1.Y,
            water.NormalMap1Strength));
            }
            else
            {
              if (_parameterNormalMap1 != null)
            _parameterNormalMap1.SetValue(_graphicsService.GetDefaultNormalTexture());
              _parameterNormalMap1Parameters.SetValue(new Vector4(1, 0, 0, 0));
            }

            _parameterSpecularParameters.SetValue(new Vector4((Vector3)water.SpecularColor, water.SpecularPower));
            _parameterUnderwaterFogParameters.SetValue((Vector3)water.UnderwaterFogDensity);
            _parameterFresnelParameters.SetValue(new Vector3(water.FresnelBias, water.FresnelScale, water.FresnelPower));
            _parameterIntersectionSoftness.SetValue(water.IntersectionSoftness);

            // We apply some arbitrary scale factors to the water and scatter colors to
            // move the values into a similar range from the user's perspective.
            _parameterWaterColor.SetValue((Vector3)water.WaterColor / 10);
            _parameterScatterColor.SetValue((Vector3)water.ScatterColor);

            if (_parameterFoamMap != null)
            {
              _parameterFoamMap.SetValue(water.FoamMap);
              _parameterFoamParameters0.SetValue(new Vector4(
            (Vector3)water.FoamColor,
            1 / water.FoamMapScale));

              _parameterFoamParameters1.SetValue(new Vector4(
            water.FoamDistortion,
            water.FoamShoreIntersection,
            // Enable crest foam only if we have waves.
            node.Waves != null ? water.FoamCrestMin : float.MaxValue,
            water.FoamCrestMax));
            }

            _parameterCausticsSampleCount.SetValue(water.CausticsSampleCount);
            _parameterCausticsParameters.SetValue(new Vector4(
              water.CausticsSampleOffset,
              water.CausticsDistortion,
              water.CausticsPower,
              water.CausticsIntensity));
            #endregion

            #region ----- Wave Map -----

            var waves = node.Waves;

            // The displacement map can be null but the normal map must not be null.
            if (waves != null && waves.NormalMap != null)
            {
              // Type: 0 = Tiling, 1 = Clamp.
              float waveType;
              if (waves.IsTiling)
            waveType = 0;
              else
            waveType = 1;

              _parameterWaveMapParameters.SetValue(new Vector4(
            1.0f / waves.TileSize,                          // Scale
            0.5f - waves.TileCenter.X / waves.TileSize,     // Offset X
            0.5f - waves.TileCenter.Z / waves.TileSize,     // Offset Y
            waveType));

              if (_parameterDisplacementTexture != null)
              {
            if (waves.DisplacementMap != null)
              _parameterDisplacementTexture.SetValue(waves.DisplacementMap);
            else
              _parameterDisplacementTexture.SetValue(graphicsService.GetDefaultTexture2DBlack4F());
              }

              _parameterWaveMapSize.SetValue(new Vector2(
            waves.NormalMap.Width,
            waves.NormalMap.Height));
              if (_parameterWaveNormalMap != null)
            _parameterWaveNormalMap.SetValue(waves.NormalMap);
            }
            else
            {
              _parameterWaveMapParameters.SetValue(new Vector4(0, 0, 0, 0));
            }
            #endregion

            #region ----- Flow -----

            if (node.Flow != null)
            {
              var flow = node.Flow;
              float flowMapSpeed = (flow.FlowMap != null) ? flow.FlowMapSpeed : 0;
              _parameterFlowParameters0.SetValue(new Vector4(flow.SurfaceSlopeSpeed, flowMapSpeed, flow.CycleDuration, flow.MaxSpeed));
              _parameterFlowParameters1.SetValue(new Vector3(flow.MinStrength, 1 / flow.NoiseMapScale, flow.NoiseMapStrength));

              if (_parameterFlowMap != null)
            _parameterFlowMap.SetValue(flow.FlowMap);

              // Get world space (x, z) to texture space matrix.
              Aabb aabb = node.Shape.GetAabb();
              Vector3F extent = aabb.Extent;
              Matrix44F m = Matrix44F.CreateScale(1 / extent.X, 1, 1 / extent.Z)
                        * Matrix44F.CreateTranslation(-aabb.Minimum.X, 0, -aabb.Minimum.Z)
                        * Matrix44F.CreateScale(1 / node.ScaleLocal.X, 1, 1 / node.ScaleLocal.Z)
                        * node.PoseWorld.Inverse;

              // We use a 3x3 2d scale/rotation/translation matrix, ignoring the y component.
              _parameterFlowMapTextureMatrix.SetValue(new Matrix(m.M00, m.M20, 0, 0,
                                                             m.M02, m.M22, 0, 0,
                                                             m.M03, m.M23, 1, 0,
                                                             0, 0, 0, 0));

              // Get local flow direction to world flow direction matrix.
              // We use a 2x2 2d rotation matrix, ignoring the y component.
              var r = node.PoseWorld.Orientation;
              _parameterFlowMapWorldMatrix.SetValue(new Matrix(r.M00, r.M20, 0, 0,
                                                           r.M02, r.M22, 0, 0,
                                                           0, 0, 0, 0,
                                                           0, 0, 0, 0));
            }
            else
            {
              _parameterFlowParameters0.SetValue(new Vector4(0, 0, 0, 0));
              _parameterFlowParameters1.SetValue(new Vector3(0, 0, 0));
            }
            #endregion

            if (isCameraUnderwater)
              RenderUnderwaterGeometry(node, cameraNode);

            RenderSurface(node, cameraNode, isCameraUnderwater);
              }

              // Reset texture effect parameters.
              _parameterGBuffer0.SetValue((Texture2D)null);
              _parameterRefractionTexture.SetValue((Texture2D)null);

              if (_parameterPlanarReflectionMap != null)
            _parameterPlanarReflectionMap.SetValue((Texture2D)null);

              if (_parameterCubeReflectionMap != null)
            _parameterCubeReflectionMap.SetValue((TextureCube)null);

              if (_parameterNormalMap0 != null)
            _parameterNormalMap0.SetValue((Texture2D)null);

              if (_parameterNormalMap1 != null)
            _parameterNormalMap1.SetValue((Texture2D)null);

              if (_parameterDisplacementTexture != null)
            _parameterDisplacementTexture.SetValue((Texture2D)null);

              if (_parameterNoiseMap != null)
            _parameterNoiseMap.SetValue((Texture2D)null);

              if (_parameterWaveNormalMap != null)
            _parameterWaveNormalMap.SetValue((Texture2D)null);

              if (_parameterFlowMap != null)
            _parameterFlowMap.SetValue((Texture2D)null);

              // This seems to be necessary because the Displacement Texture (vertex texture!)
              // is not automatically removed from the texture stage, and the WaterWavesRenderer
              // cannot write into it. XNA Bug!?
              _passProjectedGrid.Apply();

              savedRenderState.Restore();

              // Restore original render context.
              if (originalSourceTexture == null)
              {
            // Current render target has been resolved and used as source texture.
            // A new render target (from pool) has been set. (See region "Refraction Parameters".)
            // --> Previous render target needs to be recycled.
            renderTargetPool.Recycle(context.SourceTexture);
              }

              context.SourceTexture = originalSourceTexture;
        }
Example #20
0
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            context.ThrowIfCameraMissing();
            context.ThrowIfSceneMissing();

            var originalRenderTarget  = context.RenderTarget;
            var originalViewport      = context.Viewport;
            var originalReferenceNode = context.ReferenceNode;

            // Camera properties
            var cameraNode = context.CameraNode;
            var cameraPose = cameraNode.PoseWorld;
            var projection = cameraNode.Camera.Projection;

            if (!(projection is PerspectiveProjection))
            {
                throw new NotImplementedException(
                          "Cascaded shadow maps not yet implemented for scenes with orthographic camera.");
            }

            float fieldOfViewY = projection.FieldOfViewY;
            float aspectRatio  = projection.AspectRatio;

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            // The scene node renderer should use the light camera instead of the player camera.
            context.CameraNode = _orthographicCameraNode;
            context.Technique  = "Directional";

            var graphicsService  = context.GraphicsService;
            var graphicsDevice   = graphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            for (int i = 0; i < numberOfNodes; i++)
            {
                var lightNode = nodes[i] as LightNode;
                if (lightNode == null)
                {
                    continue;
                }

                var shadow = lightNode.Shadow as CascadedShadow;
                if (shadow == null)
                {
                    continue;
                }

                // LightNode is visible in current frame.
                lightNode.LastFrame = frame;

                var format = new RenderTargetFormat(
                    shadow.PreferredSize * shadow.NumberOfCascades,
                    shadow.PreferredSize,
                    false,
                    shadow.Prefer16Bit ? SurfaceFormat.HalfSingle : SurfaceFormat.Single,
                    DepthFormat.Depth24);

                bool allLocked = shadow.IsCascadeLocked[0] && shadow.IsCascadeLocked[1] && shadow.IsCascadeLocked[2] && shadow.IsCascadeLocked[3];

                if (shadow.ShadowMap == null)
                {
                    shadow.ShadowMap = graphicsService.RenderTargetPool.Obtain2D(format);
                    allLocked        = false; // Need to render shadow map.
                }

                // If we can reuse the whole shadow map texture, abort early.
                if (allLocked)
                {
                    continue;
                }

                _csmSplitDistances[0] = projection.Near;
                _csmSplitDistances[1] = shadow.Distances.X;
                _csmSplitDistances[2] = shadow.Distances.Y;
                _csmSplitDistances[3] = shadow.Distances.Z;
                _csmSplitDistances[4] = shadow.Distances.W;

                // (Re-)Initialize the array for cached matrices in the CascadedShadow.
                if (shadow.ViewProjections == null || shadow.ViewProjections.Length < shadow.NumberOfCascades)
                {
                    shadow.ViewProjections = new Matrix[shadow.NumberOfCascades];
                }

                // Initialize the projection matrices to an empty matrix.
                // The unused matrices should not contain valid projections because
                // CsmComputeSplitOptimized in CascadedShadowMask.fxh should not choose
                // the wrong cascade.
                for (int j = 0; j < shadow.ViewProjections.Length; j++)
                {
                    if (!shadow.IsCascadeLocked[j]) // Do not delete cached info for cached cascade.
                    {
                        shadow.ViewProjections[j] = new Matrix();
                    }
                }

                // If some cascades are cached, we have to create a new shadow map and copy
                // the old cascades into the new shadow map.
                if (shadow.IsCascadeLocked[0] || shadow.IsCascadeLocked[1] || shadow.IsCascadeLocked[2] || shadow.IsCascadeLocked[3])
                {
                    var oldShadowMap = shadow.ShadowMap;
                    shadow.ShadowMap = graphicsService.RenderTargetPool.Obtain2D(new RenderTargetFormat(oldShadowMap));

                    graphicsDevice.SetRenderTarget(shadow.ShadowMap);
                    graphicsDevice.Clear(Color.White);

                    var spriteBatch = graphicsService.GetSpriteBatch();
                    spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone);
                    for (int cascade = 0; cascade < shadow.NumberOfCascades; cascade++)
                    {
                        if (shadow.IsCascadeLocked[cascade])
                        {
                            var viewport  = GetViewport(shadow, cascade);
                            var rectangle = new Rectangle(viewport.X, viewport.Y, viewport.Width, viewport.Height);
                            spriteBatch.Draw(oldShadowMap, rectangle, rectangle, Color.White);
                        }
                    }
                    spriteBatch.End();

                    graphicsService.RenderTargetPool.Recycle(oldShadowMap);
                }
                else
                {
                    graphicsDevice.SetRenderTarget(shadow.ShadowMap);
                    graphicsDevice.Clear(Color.White);
                }

                context.RenderTarget             = shadow.ShadowMap;
                graphicsDevice.DepthStencilState = DepthStencilState.Default;
                graphicsDevice.RasterizerState   = RasterizerState.CullCounterClockwise;
                graphicsDevice.BlendState        = BlendState.Opaque;

                context.ReferenceNode = lightNode;
                context.Object        = shadow;
                context.ShadowNear    = 0; // Obsolete: Only kept for backward compatibility.

                bool shadowMapContainsSomething = false;
                for (int split = 0; split < shadow.NumberOfCascades; split++)
                {
                    if (shadow.IsCascadeLocked[split])
                    {
                        continue;
                    }

                    context.Data[RenderContextKeys.ShadowTileIndex] = CubeMapShadowMapRenderer.BoxedIntegers[split];

                    // near/far of this split.
                    float near = _csmSplitDistances[split];
                    float far  = Math.Max(_csmSplitDistances[split + 1], near + Numeric.EpsilonF);

                    // Create a view volume for this split.
                    _splitVolume.SetFieldOfView(fieldOfViewY, aspectRatio, near, far);

                    // Find the bounding sphere of the split camera frustum.
                    Vector3 center;
                    float   radius;
                    GetBoundingSphere(_splitVolume, out center, out radius);

                    // Extend radius to get enough border for filtering.
                    int shadowMapSize = shadow.ShadowMap.Height;

                    // We could extend by (ShadowMapSize + BorderTexels) / ShadowMapSize;
                    // Add at least 1 texel. (This way, shadow mask shader can clamp uv to
                    // texture rect in without considering half texel border to avoid sampling outside..)
                    radius *= (float)(shadowMapSize + 1) / shadowMapSize;

                    // Convert center to light space.
                    Pose lightPose = lightNode.PoseWorld;
                    center = cameraPose.ToWorldPosition(center);
                    center = lightPose.ToLocalPosition(center);

                    // Snap center to texel positions to avoid shadow swimming.
                    SnapPositionToTexels(ref center, 2 * radius, shadowMapSize);

                    // Convert center back to world space.
                    center = lightPose.ToWorldPosition(center);

                    Matrix  orientation            = lightPose.Orientation;
                    Vector3 backward               = orientation.GetColumn(2);
                    var     orthographicProjection = (OrthographicProjection)_orthographicCameraNode.Camera.Projection;

                    // Create a tight orthographic frustum around the cascade's bounding sphere.
                    orthographicProjection.SetOffCenter(-radius, radius, -radius, radius, 0, 2 * radius);
                    Vector3 cameraPosition = center + radius * backward;
                    Pose    frustumPose    = new Pose(cameraPosition, orientation);
                    Pose    view           = frustumPose.Inverse;
                    shadow.ViewProjections[split] = (Matrix)view * orthographicProjection;

                    // Convert depth bias from "texel" to light space [0, 1] depth.
                    // Minus sign to move receiver depth closer to light. Divide by depth to normalize.
                    float unitsPerTexel = orthographicProjection.Width / shadow.ShadowMap.Height;
                    shadow.EffectiveDepthBias[split] = -shadow.DepthBias[split] * unitsPerTexel / orthographicProjection.Depth;

                    // Convert normal offset from "texel" to world space.
                    shadow.EffectiveNormalOffset[split] = shadow.NormalOffset[split] * unitsPerTexel;

                    // For rendering the shadow map, move near plane back by MinLightDistance
                    // to catch occluders in front of the cascade.
                    orthographicProjection.Near       = -shadow.MinLightDistance;
                    _orthographicCameraNode.PoseWorld = frustumPose;

                    // Set a viewport to render a tile in the texture atlas.
                    graphicsDevice.Viewport = GetViewport(shadow, split);
                    context.Viewport        = graphicsDevice.Viewport;

                    shadowMapContainsSomething |= RenderCallback(context);
                }

                // Recycle shadow map if empty.
                if (!shadowMapContainsSomething)
                {
                    graphicsService.RenderTargetPool.Recycle(shadow.ShadowMap);
                    shadow.ShadowMap = null;
                }
            }

            graphicsDevice.SetRenderTarget(null);
            savedRenderState.Restore();

            context.CameraNode    = cameraNode;
            context.ShadowNear    = float.NaN;
            context.Technique     = null;
            context.RenderTarget  = originalRenderTarget;
            context.Viewport      = originalViewport;
            context.ReferenceNode = originalReferenceNode;
            context.Object        = null;
            context.Data[RenderContextKeys.ShadowTileIndex] = null;
        }
Example #21
0
        /// <inheritdoc/>
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (nodes.Count == 0)
            return;

              context.Validate(_effect);

              var originalRenderTarget = context.RenderTarget;
              var originalViewport = context.Viewport;

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              //var renderTargetPool = graphicsService.RenderTargetPool;

              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.BlendState = BlendState.Opaque;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.DepthStencilState = DepthStencilState.None;

              int frame = context.Frame;

              for (int nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++)
              {
            var node = nodes[nodeIndex] as WaterNode;
            if (node == null)
              continue;

            var waves = node.Waves as OceanWaves;
            if (waves == null)
              continue;

            // We update the waves only once per frame.
            if (waves.LastFrame == frame)
              continue;

            waves.LastFrame = frame;

            float time = (float)context.Time.TotalSeconds;

            // Initialize h0 spectrum. Perform CPU FFT.
            waves.Update(graphicsDevice, time);

            int n = waves.TextureSize;

            // Allocate textures in the first frame and when the TextureSize was changed.
            if (waves.DisplacementSpectrum == null || waves.DisplacementSpectrum.Width != n)
            {
              waves.DisplacementSpectrum.SafeDispose();
              waves.NormalSpectrum.SafeDispose();
              waves.DisplacementMap.SafeDispose();
              waves.NormalMap.SafeDispose();

              waves.DisplacementSpectrum = new RenderTarget2D(_graphicsService.GraphicsDevice, n, n, false, SurfaceFormat.Vector4, DepthFormat.None);
              waves.NormalSpectrum = new RenderTarget2D(_graphicsService.GraphicsDevice, n, n, false, SurfaceFormat.Vector4, DepthFormat.None);
              waves.DisplacementMap = new RenderTarget2D(_graphicsService.GraphicsDevice, n, n, false, SurfaceFormat.Vector4, DepthFormat.None);
              waves.NormalMap = new RenderTarget2D(
            _graphicsService.GraphicsDevice,
            n,
            n,
            true,
            SurfaceFormat.Color,
            DepthFormat.None);
            }

            // Create spectrum (h, D, N) for current time from h0.
            _renderTargetBindings[0] = new RenderTargetBinding(waves.DisplacementSpectrum);
            _renderTargetBindings[1] = new RenderTargetBinding(waves.NormalSpectrum);
            graphicsDevice.SetRenderTargets(_renderTargetBindings);
            _parameterSize.SetValue((float)n);
            _parameterSpectrumParameters.SetValue(new Vector4(
              waves.TileSize,
              waves.Gravity,
              time,
              waves.HeightScale));
            _parameterSourceTexture.SetValue(waves.H0Spectrum);
            _passSpectrum.Apply();
            graphicsDevice.DrawFullScreenQuad();

            // Do inverse FFT.
            _fft.Process(
              context,
              false,
              waves.DisplacementSpectrum,
              waves.NormalSpectrum,
              (RenderTarget2D)waves.DisplacementMap,
              (RenderTarget2D)waves.NormalMap,
              waves.Choppiness);

            #region ----- Old Debugging Code -----

            // Create textures from CPU FFT data for debug visualization.
            //n = waves.CpuSize;
            //var s0Data = new Vector4[n * n];
            //var s1Data = new Vector4[n * n];
            //var s0 = new RenderTarget2D(_graphicsService.GraphicsDevice, n, n, false, SurfaceFormat.Vector4, DepthFormat.None);
            //var s1 = new RenderTarget2D(_graphicsService.GraphicsDevice, n, n, false, SurfaceFormat.Vector4, DepthFormat.None);
            //for (int y = 0; y < n; y++)
            //{
            //  for (int x = 0; x < n; x++)
            //  {
            //s0Data[y * n + x] = new Vector4(
            //  -waves._D[x, y].X * waves.Choppiness,
            //  waves._h[x, y].X * 1,
            //  -waves._D[x, y].Y * waves.Choppiness,
            //  1);

            //s1Data[y * n + x] = new Vector4(
            //  waves._N[x, y].X,
            //  waves._N[x, y].Y,
            //  0,
            //  0);
            //  }
            //}
            //s0.SetData(s0Data);
            //s1.SetData(s1Data);
            //WaterSample._t0 = s0;
            //WaterSample._t1 = waves.DisplacementMap;
            #endregion
              }

              savedRenderState.Restore();
              graphicsDevice.SetRenderTarget(null);
              context.RenderTarget = originalRenderTarget;
              context.Viewport = originalViewport;

              _renderTargetBindings[0] = default(RenderTargetBinding);
              _renderTargetBindings[1] = default(RenderTargetBinding);

              // Reset the texture stages. If a floating point texture is set, we get exceptions
              // when a sampler with bilinear filtering is set.
            #if !MONOGAME
              graphicsDevice.ResetTextures();
            #endif
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              var graphicsDevice = _effect.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.DepthStencilState = DepthStencilState.None;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd;

              var viewport = graphicsDevice.Viewport;
              _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height));
              _parameterGBuffer0.SetValue(context.GBuffer0);
              _parameterGBuffer1.SetValue(context.GBuffer1);

              var cameraNode = context.CameraNode;
              Matrix viewProjection = (Matrix)cameraNode.View * cameraNode.Camera.Projection;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              var isHdrEnabled = context.IsHdrEnabled();

              for (int i = 0; i < numberOfNodes; i++)
              {
            var lightNode = nodes[i] as LightNode;
            if (lightNode == null)
              continue;

            var light = lightNode.Light as AmbientLight;
            if (light == null)
              continue;

            // LightNode is visible in current frame.
            lightNode.LastFrame = frame;

            float hdrScale = isHdrEnabled ? light.HdrScale : 1;
            _parameterLightColor.SetValue((Vector3)light.Color * light.Intensity * hdrScale);
            _parameterHemisphericAttenuation.SetValue(light.HemisphericAttenuation);

            Vector3F upWorld = lightNode.PoseWorld.ToWorldDirection(Vector3F.Up);
            _parameterUp.SetValue((Vector3)upWorld);

            if (lightNode.Clip != null)
            {
              var data = lightNode.RenderData as LightRenderData;
              if (data == null)
              {
            data = new LightRenderData();
            lightNode.RenderData = data;
              }

              data.UpdateClipSubmesh(context.GraphicsService, lightNode);

              graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateOnePassStencilFail;
              graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite;

              _parameterWorldViewProjection.SetValue((Matrix)data.ClipMatrix * viewProjection);
              _passClip.Apply();
              data.ClipSubmesh.Draw();

              graphicsDevice.DepthStencilState = lightNode.InvertClip
            ? GraphicsHelper.DepthStencilStateStencilEqual0
            : GraphicsHelper.DepthStencilStateStencilNotEqual0;
              graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd;
            }
            else
            {
              graphicsDevice.DepthStencilState = DepthStencilState.None;
            }

            _passLight.Apply();
            graphicsDevice.DrawFullScreenQuad();
              }

              savedRenderState.Restore();
        }
Example #23
0
        /// <summary>
        /// Draws the points.
        /// </summary>
        /// <param name="context">The render context.</param>
        /// <remarks>
        /// If <see cref="Effect"/> is <see langword="null"/>, then <see cref="Render"/> does nothing.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="context"/> is <see langword="null"/>.
        /// </exception>
        public void Render(RenderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (Effect == null)
            {
                return;
            }

            int numberOfPoints = _points.Count;

            if (numberOfPoints <= 0)
            {
                return;
            }

            context.Validate(Effect);
            context.ThrowIfCameraMissing();

            // Render state.
            var graphicsDevice   = context.GraphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.RasterizerState = RasterizerState.CullNone;

            // Reset the texture stages. If a floating point texture is set, we get exceptions
            // when a sampler with bilinear filtering is set.
            graphicsDevice.ResetTextures();

            // Effect parameters.
            Effect.Alpha              = 1;
            Effect.DiffuseColor       = new Vector3(1, 1, 1);
            Effect.LightingEnabled    = false;
            Effect.TextureEnabled     = false;
            Effect.VertexColorEnabled = true;
            Effect.World              = Matrix.Identity;
            Effect.View       = Matrix.Identity;
            Effect.Projection = Matrix.Identity;
            Effect.CurrentTechnique.Passes[0].Apply();

            // Get WorldViewProjection matrix.
            Matrix view       = (Matrix)context.CameraNode.View;
            Matrix projection = context.CameraNode.Camera.Projection;
            Matrix wvp        = Matrix.Multiply(view, projection);

            // The x and y point size relative to the viewport.
            Viewport viewport = graphicsDevice.Viewport;
            float    sizeX    = PointSize / viewport.Width;
            float    sizeY    = sizeX * viewport.Width / viewport.Height;

            // Resize buffer if necessary.
            ResizeBuffer(graphicsDevice, numberOfPoints);

            // Submit points. The loop is only needed if we have more points than can
            // be submitted with one draw call.
            var startPointIndex = 0;

            while (startPointIndex < numberOfPoints)
            {
                // Number of points in this batch.
                int pointsPerBatch = Math.Min(numberOfPoints - startPointIndex, _buffer.Length / 6);

                // Create vertices for points. All positions are directly in clip space!
                for (int i = 0; i < pointsPerBatch; i++)
                {
                    var point = _points[startPointIndex + i];

                    // Transform point position to clip space.
                    Vector3 positionWorld = (Vector3)point.Position;
                    Vector3 positionClip;
                    Vector3.Transform(ref positionWorld, ref wvp, out positionClip);
                    float w = (float)((double)positionWorld.X * wvp.M14 + (double)positionWorld.Y * wvp.M24 + (double)positionWorld.Z * wvp.M34 + wvp.M44);

                    // Homogeneous divide.
                    positionClip /= w;

                    // 2 triangles create a point quad. Clip space goes from -1 to 1, therefore
                    // we do not need to divide sizeX and sizeY by 2.
                    Vector3 bottomLeft  = positionClip + new Vector3(-sizeX, +sizeY, 0);
                    Vector3 bottomRight = positionClip + new Vector3(+sizeX, +sizeY, 0);
                    Vector3 topLeft     = positionClip + new Vector3(-sizeX, -sizeY, 0);
                    Vector3 topRight    = positionClip + new Vector3(+sizeX, -sizeY, 0);
                    _buffer[i * 6 + 0].Position = bottomLeft;
                    _buffer[i * 6 + 0].Color    = point.Color;
                    _buffer[i * 6 + 1].Position = bottomRight;
                    _buffer[i * 6 + 1].Color    = point.Color;
                    _buffer[i * 6 + 2].Position = topLeft;
                    _buffer[i * 6 + 2].Color    = point.Color;
                    _buffer[i * 6 + 3].Position = bottomRight;
                    _buffer[i * 6 + 3].Color    = point.Color;
                    _buffer[i * 6 + 4].Position = topRight;
                    _buffer[i * 6 + 4].Color    = point.Color;
                    _buffer[i * 6 + 5].Position = topLeft;
                    _buffer[i * 6 + 5].Color    = point.Color;
                }

                // Draw triangles.
                graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, _buffer, 0, pointsPerBatch * 2);

                startPointIndex += pointsPerBatch;
            }

            savedRenderState.Restore();
        }
Example #24
0
        private void VisualizeBuffer(int level, RenderContext context, int passIndex)
        {
            if (context == null)
            throw new ArgumentNullException("context");

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var originalRenderState = new RenderStateSnapshot(graphicsDevice);

              var viewport = graphicsDevice.Viewport;
              _parameterTargetSize.SetValue(new Vector2(viewport.Width, viewport.Height));
              _parameterDebugLevel.SetValue((float)level);
              _effect.CurrentTechnique = _techniqueVisualize;
              _techniqueVisualize.Passes[passIndex].Apply();

              graphicsDevice.DepthStencilState = DepthStencilState.None;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              // Do not override blend state: Use current blend state.

              graphicsDevice.DrawFullScreenQuad();

              originalRenderState.Restore();
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.ThrowIfCameraMissing();
              context.ThrowIfSceneMissing();

              var originalRenderTarget = context.RenderTarget;
              var originalViewport = context.Viewport;
              var originalReferenceNode = context.ReferenceNode;

              // Camera properties
              var cameraNode = context.CameraNode;
              var cameraPose = cameraNode.PoseWorld;
              var projection = cameraNode.Camera.Projection;
              if (!(projection is PerspectiveProjection))
            throw new NotImplementedException(
              "Cascaded shadow maps not yet implemented for scenes with orthographic camera.");

              float fieldOfViewY = projection.FieldOfViewY;
              float aspectRatio = projection.AspectRatio;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              // The scene node renderer should use the light camera instead of the player camera.
              context.CameraNode = _orthographicCameraNode;
              context.Technique = "Directional";

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              for (int i = 0; i < numberOfNodes; i++)
              {
            var lightNode = nodes[i] as LightNode;
            if (lightNode == null)
              continue;

            var shadow = lightNode.Shadow as CascadedShadow;
            if (shadow == null)
              continue;

            // LightNode is visible in current frame.
            lightNode.LastFrame = frame;

            var format = new RenderTargetFormat(
              shadow.PreferredSize * shadow.NumberOfCascades,
              shadow.PreferredSize,
              false,
              shadow.Prefer16Bit ? SurfaceFormat.HalfSingle : SurfaceFormat.Single,
              DepthFormat.Depth24);

            bool allLocked = shadow.IsCascadeLocked[0] && shadow.IsCascadeLocked[1] && shadow.IsCascadeLocked[2] && shadow.IsCascadeLocked[3];

            if (shadow.ShadowMap == null)
            {
              shadow.ShadowMap = graphicsService.RenderTargetPool.Obtain2D(format);
              allLocked = false;   // Need to render shadow map.
            }

            // If we can reuse the whole shadow map texture, abort early.
            if (allLocked)
              continue;

            _csmSplitDistances[0] = projection.Near;
            _csmSplitDistances[1] = shadow.Distances.X;
            _csmSplitDistances[2] = shadow.Distances.Y;
            _csmSplitDistances[3] = shadow.Distances.Z;
            _csmSplitDistances[4] = shadow.Distances.W;

            // (Re-)Initialize the array for cached matrices in the CascadedShadow.
            if (shadow.ViewProjections == null || shadow.ViewProjections.Length < shadow.NumberOfCascades)
              shadow.ViewProjections = new Matrix[shadow.NumberOfCascades];

            // Initialize the projection matrices to an empty matrix.
            // The unused matrices should not contain valid projections because
            // CsmComputeSplitOptimized in CascadedShadowMask.fxh should not choose
            // the wrong cascade.
            for (int j = 0; j < shadow.ViewProjections.Length; j++)
            {
              if (!shadow.IsCascadeLocked[j])    // Do not delete cached info for cached cascade.
            shadow.ViewProjections[j] = new Matrix();
            }

            // If some cascades are cached, we have to create a new shadow map and copy
            // the old cascades into the new shadow map.
            if (shadow.IsCascadeLocked[0] || shadow.IsCascadeLocked[1] || shadow.IsCascadeLocked[2] || shadow.IsCascadeLocked[3])
            {
              var oldShadowMap = shadow.ShadowMap;
              shadow.ShadowMap = graphicsService.RenderTargetPool.Obtain2D(new RenderTargetFormat(oldShadowMap));

              graphicsDevice.SetRenderTarget(shadow.ShadowMap);
              graphicsDevice.Clear(Color.White);

              var spriteBatch = graphicsService.GetSpriteBatch();
              spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone);
              for (int cascade = 0; cascade < shadow.NumberOfCascades; cascade++)
              {
            if (shadow.IsCascadeLocked[cascade])
            {
              var viewport = GetViewport(shadow, cascade);
              var rectangle = new Rectangle(viewport.X, viewport.Y, viewport.Width, viewport.Height);
              spriteBatch.Draw(oldShadowMap, rectangle, rectangle, Color.White);
            }
              }
              spriteBatch.End();

              graphicsService.RenderTargetPool.Recycle(oldShadowMap);
            }
            else
            {
              graphicsDevice.SetRenderTarget(shadow.ShadowMap);
              graphicsDevice.Clear(Color.White);
            }

            context.RenderTarget = shadow.ShadowMap;
            graphicsDevice.DepthStencilState = DepthStencilState.Default;
            graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
            graphicsDevice.BlendState = BlendState.Opaque;

            context.ReferenceNode = lightNode;
            context.Object = shadow;
            context.ShadowNear = 0;           // Obsolete: Only kept for backward compatibility.

            bool shadowMapContainsSomething = false;
            for (int split = 0; split < shadow.NumberOfCascades; split++)
            {
              if (shadow.IsCascadeLocked[split])
            continue;

              context.Data[RenderContextKeys.ShadowTileIndex] = CubeMapShadowMapRenderer.BoxedIntegers[split];

              // near/far of this split.
              float near = _csmSplitDistances[split];
              float far = Math.Max(_csmSplitDistances[split + 1], near + Numeric.EpsilonF);

              // Create a view volume for this split.
              _splitVolume.SetFieldOfView(fieldOfViewY, aspectRatio, near, far);

              // Find the bounding sphere of the split camera frustum.
              Vector3F center;
              float radius;
              GetBoundingSphere(_splitVolume, out center, out radius);

              // Extend radius to get enough border for filtering.
              int shadowMapSize = shadow.ShadowMap.Height;

              // We could extend by (ShadowMapSize + BorderTexels) / ShadowMapSize;
              // Add at least 1 texel. (This way, shadow mask shader can clamp uv to
              // texture rect in without considering half texel border to avoid sampling outside..)
              radius *= (float)(shadowMapSize + 1) / shadowMapSize;

              // Convert center to light space.
              Pose lightPose = lightNode.PoseWorld;
              center = cameraPose.ToWorldPosition(center);
              center = lightPose.ToLocalPosition(center);

              // Snap center to texel positions to avoid shadow swimming.
              SnapPositionToTexels(ref center, 2 * radius, shadowMapSize);

              // Convert center back to world space.
              center = lightPose.ToWorldPosition(center);

              Matrix33F orientation = lightPose.Orientation;
              Vector3F backward = orientation.GetColumn(2);
              var orthographicProjection = (OrthographicProjection)_orthographicCameraNode.Camera.Projection;

              // Create a tight orthographic frustum around the cascade's bounding sphere.
              orthographicProjection.SetOffCenter(-radius, radius, -radius, radius, 0, 2 * radius);
              Vector3F cameraPosition = center + radius * backward;
              Pose frustumPose = new Pose(cameraPosition, orientation);
              Pose view = frustumPose.Inverse;
              shadow.ViewProjections[split] = (Matrix)view * orthographicProjection;

              // Convert depth bias from "texel" to light space [0, 1] depth.
              // Minus sign to move receiver depth closer to light. Divide by depth to normalize.
              float unitsPerTexel = orthographicProjection.Width / shadow.ShadowMap.Height;
              shadow.EffectiveDepthBias[split] = -shadow.DepthBias[split] * unitsPerTexel / orthographicProjection.Depth;

              // Convert normal offset from "texel" to world space.
              shadow.EffectiveNormalOffset[split] = shadow.NormalOffset[split] * unitsPerTexel;

              // For rendering the shadow map, move near plane back by MinLightDistance
              // to catch occluders in front of the cascade.
              orthographicProjection.Near = -shadow.MinLightDistance;
              _orthographicCameraNode.PoseWorld = frustumPose;

              // Set a viewport to render a tile in the texture atlas.
              graphicsDevice.Viewport = GetViewport(shadow, split);
              context.Viewport = graphicsDevice.Viewport;

              shadowMapContainsSomething |= RenderCallback(context);
            }

            // Recycle shadow map if empty.
            if (!shadowMapContainsSomething)
            {
              graphicsService.RenderTargetPool.Recycle(shadow.ShadowMap);
              shadow.ShadowMap = null;
            }
              }

              graphicsDevice.SetRenderTarget(null);
              savedRenderState.Restore();

              context.CameraNode = cameraNode;
              context.ShadowNear = float.NaN;
              context.Technique = null;
              context.RenderTarget = originalRenderTarget;
              context.Viewport = originalViewport;
              context.ReferenceNode = originalReferenceNode;
              context.Object = null;
              context.Data[RenderContextKeys.ShadowTileIndex] = null;
        }
Example #26
0
        private void VisualizeQuery(SceneNode node, RenderContext context, int passIndex)
        {
            if (node == null)
            throw new ArgumentNullException("node");
              if (context == null)
            throw new ArgumentNullException("context");

              context.ThrowIfCameraMissing();

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var originalRenderState = new RenderStateSnapshot(graphicsDevice);

              var viewport = graphicsDevice.Viewport;
              _parameterTargetSize.SetValue(new Vector2(viewport.Width, viewport.Height));

              var aabb = node.Aabb;
              _parameterDebugMinimum.SetValue((Vector3)aabb.Minimum);
              _parameterDebugMaximum.SetValue((Vector3)aabb.Maximum);

              _effect.CurrentTechnique = _techniqueVisualize;
              _techniqueVisualize.Passes[passIndex].Apply();

              graphicsDevice.DepthStencilState = DepthStencilState.None;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              // Do not override blend state: Use current blend state.

              graphicsDevice.DrawFullScreenQuad();

              originalRenderState.Restore();
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");
              if (context.Scene == null)
            throw new ArgumentException("Scene needs to be set in render context.", "context");
              if (context.CameraNode == null)
            throw new ArgumentException("Camera needs to be set in render context.", "context");
              if (!(context.CameraNode.Camera.Projection is PerspectiveProjection))
            throw new ArgumentException("The camera in the render context must use a perspective projection.", "context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              int frame = context.Frame;

              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              var originalRenderTarget = context.RenderTarget;
              var originalViewport = context.Viewport;
              var originalCameraNode = context.CameraNode;
              var originalLodCameraNode = context.LodCameraNode;
              float originalLodBias = context.LodBias;
              var originalReferenceNode = context.ReferenceNode;

              Pose originalCameraPose = originalCameraNode.PoseWorld;
              Vector3F originalCameraPosition = originalCameraPose.Position;
              Matrix33F originalCameraOrientation = originalCameraPose.Orientation;

              Vector3F right = originalCameraOrientation.GetColumn(0);
              Vector3F up = originalCameraOrientation.GetColumn(1);
              Vector3F back = originalCameraOrientation.GetColumn(2);

              try
              {
            // Use foreach instead of for-loop to catch InvalidOperationExceptions in
            // case the collection is modified.
            for (int i = 0; i < numberOfNodes; i++)
            {
              var node = nodes[i] as PlanarReflectionNode;
              if (node == null)
            continue;

              // Update each node only once per frame.
              if (node.LastFrame == frame)
            continue;

              node.LastFrame = frame;

              var texture = node.RenderToTexture.Texture;
              if (texture == null)
            continue;

              var renderTarget = texture as RenderTarget2D;
              if (renderTarget == null)
            throw new GraphicsException(
              "PlanarReflectionNode.RenderToTexture.Texture is invalid. The texture must be a RenderTarget2D.");

              // RenderToTexture instances can be shared. --> Update them only once per frame.
              if (node.RenderToTexture.LastFrame == frame)
            continue;

              // Do not render if we look at the back of the reflection plane.
              Vector3F planeNormal = node.NormalWorld;
              Vector3F planePosition = node.PoseWorld.Position;
              Vector3F planeToCamera = originalCameraPosition - planePosition;
              if (Vector3F.Dot(planeNormal, planeToCamera) < 0)
            continue;

              var cameraNode = node.CameraNode;

              // Reflect camera pose.
              Pose cameraPose;
              cameraPose.Position = planePosition + Reflect(planeToCamera, planeNormal);
              cameraPose.Orientation = new Matrix33F();
              cameraPose.Orientation.SetColumn(0, Reflect(right, planeNormal));
              cameraPose.Orientation.SetColumn(1, -Reflect(up, planeNormal));
              cameraPose.Orientation.SetColumn(2, Reflect(back, planeNormal));
              cameraNode.PoseWorld = cameraPose;

              // The projection of the player camera.
              var originalProjection = originalCameraNode.Camera.Projection;
              // The projection of the reflected camera.
              var projection = (PerspectiveProjection)cameraNode.Camera.Projection;

              // Choose optimal projection. We get the screen-space bounds of the reflection node.
              // Then we make the FOV so small that it exactly contains the node.
              projection.Set(originalProjection);

              var bounds = GraphicsHelper.GetBounds(cameraNode, node);

              // Abort if the bounds are empty.
              if (Numeric.AreEqual(bounds.X, bounds.Z) || Numeric.AreEqual(bounds.Y, bounds.W))
            continue;

              // Apply FOV scale to bounds.
              float fovScale = node.FieldOfViewScale;
              float deltaX = (bounds.Z - bounds.X) * (fovScale - 1) / 2;
              bounds.X -= deltaX;
              bounds.Z += deltaX;
              float deltaY = (bounds.W - bounds.Y) * (fovScale - 1) / 2;
              bounds.Y -= deltaY;
              bounds.W += deltaY;

              // Update projection to contain only the node bounds.
              projection.Left = projection.Left + bounds.X * projection.Width;
              projection.Right = projection.Left + bounds.Z * projection.Width;
              projection.Top = projection.Top - bounds.Y * projection.Height;
              projection.Bottom = projection.Top - bounds.W * projection.Height;

              // Set far clip plane.
              if (node.Far.HasValue)
            projection.Far = node.Far.Value;

              // Set near clip plane.
              Vector3F planeNormalCamera = cameraPose.ToLocalDirection(-node.NormalWorld);
              Vector3F planePointCamera = cameraPose.ToLocalPosition(node.PoseWorld.Position);
              projection.NearClipPlane = new Plane(planeNormalCamera, planePointCamera);

              context.CameraNode = cameraNode;
              context.LodCameraNode = cameraNode;
              context.LodBias = node.LodBias ?? originalLodBias;
              context.ReferenceNode = node;

              context.RenderTarget = renderTarget;
              context.Viewport = new Viewport(0, 0, renderTarget.Width, renderTarget.Height);

              RenderCallback(context);

              // Update other properties of RenderToTexture.
              node.RenderToTexture.LastFrame = frame;
              node.RenderToTexture.TextureMatrix = GraphicsHelper.ProjectorBiasMatrix
                                               * cameraNode.Camera.Projection
                                               * cameraNode.PoseWorld.Inverse;
            }
              }
              catch (InvalidOperationException exception)
              {
            throw new GraphicsException(
              "InvalidOperationException was raised in PlanarReflectionRenderer.Render(). "
              + "This can happen if a SceneQuery instance that is currently in use is modified in the "
              + "RenderCallback. --> Use different SceneQuery types in the method which calls "
              + "SceneCaptureRenderer.Render() and in the RenderCallback method.",
              exception);
              }

              graphicsDevice.SetRenderTarget(null);
              savedRenderState.Restore();

              context.RenderTarget = originalRenderTarget;
              context.Viewport = originalViewport;
              context.CameraNode = originalCameraNode;
              context.LodCameraNode = originalLodCameraNode;
              context.LodBias = originalLodBias;
              context.ReferenceNode = originalReferenceNode;
        }
Example #28
0
        public void Query(IList<SceneNode> nodes, RenderContext context)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              context.ThrowIfCameraMissing();

              int numberOfNodes = nodes.Count;  // Note: nodes may contain null entries!
              if (numberOfNodes == 0)
              {
            Statistics.ObjectsTotal = 0;
            Statistics.ObjectsCulled = 0;
            Statistics.ShadowCastersTotal = 0;
            Statistics.ShadowCastersCulled = 0;
            return;
              }

              var originalRenderTarget = context.RenderTarget;
              var originalViewport = context.Viewport;

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var originalRenderState = new RenderStateSnapshot(graphicsDevice);

              graphicsDevice.DepthStencilState = DepthStencilState.None;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.BlendState = BlendState.Opaque;

              // Note: The camera parameters are set in Render().

              // ----- Query data.
              // The vertices store the AABB and the pixel address to which the results is
              // written. In MonoGame, we can simply submit the data as a point list. However,
              // XNA does not support point lists. As a workaround we can submit the data as a
              // line strip:
              // - The line strip needs to be continuous: Line strips go left-to-right at even
              //   lines and right-to-left at odd lines.
              // - According to DirectX 9 line rasterization rules, the last pixel of a line
              //   is excluded. An additional vertex needs to be appended to ensure that the
              //   last pixel is rendered.
              if (_queryData == null || _queryData.Length < numberOfNodes + 1)
              {
            // We need at least numberOfNodes + 1 vertices. We use NextPowerOf2() which
            // returns a value > numberOfNodes.
            _queryData = new OcclusionVertex[MathHelper.NextPowerOf2((uint)numberOfNodes)];
              }

              Debug.Assert(_shadowCasters.Count == 0, "List of shadow casters has not been cleared.");

              int index = 0;
              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i];
            if (node == null)
              continue;

            // Ignore empty shapes.
            if (node.Shape is EmptyShape)
            {
              nodes[i] = null;  // Cull scene node!
              continue;
            }

            if (node.CastsShadows)
              _shadowCasters.Add(node);

            // Pixel address
            _queryData[index].Pixel = ToPixelAddress(index);

            // AABB
            var aabb = node.Aabb;
            _queryData[index].Minimum = aabb.Minimum;
            _queryData[index].Maximum = aabb.Maximum;

            // Position, Scale and MaxDistance are used for distance culling.
            _queryData[index].Position = node.PoseWorld.Position;
            _queryData[index].Scale = node.ScaleWorld;
            _queryData[index].MaxDistance = node.MaxDistance;

            index++;
              }

              // Append additional vertex. (For XNA line strips.)
              {
            // Copy last vertex and increment pixel address.
            _queryData[index] = _queryData[index - 1];
            _queryData[index].Pixel = ToPixelAddress(index);
              }

              int actualNumberOfNodes = index;

              // Allocate render target storing the results.
              int numberOfQueries = _lightHzbAvailable ? actualNumberOfNodes + _shadowCasters.Count : actualNumberOfNodes;
              int desiredBufferHeight = (numberOfQueries - 1) / ResultsBufferWidth + 1;
              Debug.Assert(ResultsBufferWidth * desiredBufferHeight >= actualNumberOfNodes, "Sanity check.");
              if (_resultsBuffer == null || _resultsBuffer.Height < desiredBufferHeight)
              {
            if (_resultsBuffer != null)
              _resultsBuffer.Dispose();

            _resultsBuffer = new RenderTarget2D(graphicsDevice, ResultsBufferWidth, desiredBufferHeight, false, SurfaceFormat.Single, DepthFormat.None);
            _results = new float[ResultsBufferWidth * desiredBufferHeight];
              }

              // Set new render target before binding the _cameraHzb.
              graphicsDevice.SetRenderTarget(_resultsBuffer);

              float width = _hzbLevels[0].Width;
              float height = _hzbLevels[0].Height;
              Vector2 sourceSize = new Vector2(width, height);
              Vector2 targetSize = new Vector2(_resultsBuffer.Width, _resultsBuffer.Height);
              Vector2 texelSize = new Vector2(1.0f / _cameraHzb.Width, 1.0f / _cameraHzb.Height);
              Vector2 halfTexelSize = 0.5f * texelSize;

              _parameterClampAabbMinimum.SetValue((Vector3)_cameraAabb.Minimum);
              _parameterClampAabbMaximum.SetValue((Vector3)_cameraAabb.Maximum);
              _parameterHzbSize.SetValue(sourceSize);
              _parameterTargetSize.SetValue(targetSize);
              _parameterAtlasSize.SetValue(new Vector2(_cameraHzb.Width, _cameraHzb.Height));
              _parameterTexelOffset.SetValue(texelSize);
              _parameterHalfTexelOffset.SetValue(halfTexelSize);
              _parameterMaxLevel.SetValue((float)_hzbLevels.Length - 1);
              _parameterHzbTexture.SetValue(_cameraHzb);
              _parameterLightHzbTexture.SetValue(_lightHzb);

              _effect.CurrentTechnique = _techniqueQuery;
              _techniqueQuery.Passes[0].Apply();

            #if MONOGAME
              var primitiveType = PrimitiveType.PointList;
            #else
              var primitiveType = PrimitiveType.LineStrip;
            #endif

              graphicsDevice.DrawUserPrimitives(primitiveType, _queryData, 0, actualNumberOfNodes);

              // Query shadow casters.
              int numberOfShadowCasters = _shadowCasters.Count;
              if (_lightHzbAvailable)
              {
            int offset = actualNumberOfNodes;
            for (int i = 0; i < numberOfShadowCasters; i++)
            {
              var node = _shadowCasters[i];

              // Pixel address
              _queryData[i].Pixel = ToPixelAddress(offset + i);

              // AABB
              var aabb = node.Aabb;
              _queryData[i].Minimum = aabb.Minimum;
              _queryData[i].Maximum = aabb.Maximum;

              // Position, Scale and MaxDistance are used for distance culling.
              _queryData[i].Position = node.PoseWorld.Position;
              _queryData[i].Scale = node.ScaleWorld;
              _queryData[i].MaxDistance = node.MaxDistance;
            }

            // Append additional vertex. (For XNA line strips.)
            {
              // Copy last vertex and increment pixel address.
              _queryData[numberOfShadowCasters] = _queryData[numberOfShadowCasters - 1];
              _queryData[numberOfShadowCasters].Pixel = ToPixelAddress(offset + numberOfShadowCasters);
            }

            _parameterClampAabbMinimum.SetValue((Vector3)_lightAabb.Minimum);
            _parameterClampAabbMaximum.SetValue((Vector3)_lightAabb.Maximum);

            int passIndex = ProgressiveShadowCasterCulling ? 2 : 1;
            _techniqueQuery.Passes[passIndex].Apply();

            graphicsDevice.DrawUserPrimitives(primitiveType, _queryData, 0, numberOfShadowCasters);
              }

              // Read back results.
              graphicsDevice.SetRenderTarget(null);
              _resultsBuffer.GetData(_results);

              index = 0;
              int objectsCulled = 0;
              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i];
            if (node == null)
              continue;

            int resultIndex = ToResultsBufferIndex(index);

            float viewNormalizedDistance = _results[resultIndex];
            if (viewNormalizedDistance >= 0)
            {
              // Store view-normalized distance in SortTag.
              node.SortTag = viewNormalizedDistance;
            }
            else
            {
              // Scene node culled.
              nodes[i] = null;
              objectsCulled++;
            }

            index++;
              }

              int shadowCastersCulled = 0;
              if (_lightHzbAvailable)
              {
            int offset = actualNumberOfNodes;
            for (int i = 0; i < numberOfShadowCasters; i++)
            {
              var node = _shadowCasters[i];
              int resultIndex = ToResultsBufferIndex(offset + i);

              // ReSharper disable once CompareOfFloatsByEqualityOperator
              float viewNormalizedDistance = _results[resultIndex];
              if (_results[resultIndex] >= 0)
              {
            // Shadow caster is visible.
            node.ClearFlag(SceneNodeFlags.IsShadowCasterCulled);
            node.SortTag = viewNormalizedDistance;
              }
              else
              {
            // Shadow caster is culled.
            node.SetFlag(SceneNodeFlags.IsShadowCasterCulled);
            shadowCastersCulled++;
              }
            }
              }

              Statistics.ObjectsTotal = actualNumberOfNodes;
              Statistics.ObjectsCulled = objectsCulled;
              Statistics.ShadowCastersTotal = _shadowCasters.Count;
              Statistics.ShadowCastersCulled = shadowCastersCulled;

              _shadowCasters.Clear();

              originalRenderState.Restore();

              context.RenderTarget = originalRenderTarget;
              context.Viewport = originalViewport;
        }
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            // Note: The camera node is not used by the StandardShadowMapRenderer.
            // Still throw an exception if null for consistency. (All other shadow map
            // renderers need a camera node.)
            context.ThrowIfCameraMissing();
            context.ThrowIfSceneMissing();

            var originalRenderTarget  = context.RenderTarget;
            var originalViewport      = context.Viewport;
            var originalReferenceNode = context.ReferenceNode;

            var cameraNode = context.CameraNode;

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            context.Technique = "Default";

            var graphicsService  = context.GraphicsService;
            var graphicsDevice   = graphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            for (int i = 0; i < numberOfNodes; i++)
            {
                var lightNode = nodes[i] as LightNode;
                if (lightNode == null)
                {
                    continue;
                }

                var shadow = lightNode.Shadow as StandardShadow;
                if (shadow == null)
                {
                    continue;
                }

                // LightNode is visible in current frame.
                lightNode.LastFrame = frame;

                // Get a new shadow map if necessary.
                if (shadow.ShadowMap == null)
                {
                    shadow.ShadowMap = graphicsService.RenderTargetPool.Obtain2D(
                        new RenderTargetFormat(
                            shadow.PreferredSize,
                            shadow.PreferredSize,
                            false,
                            shadow.Prefer16Bit ? SurfaceFormat.HalfSingle : SurfaceFormat.Single,
                            DepthFormat.Depth24));
                }

                // Create a suitable shadow camera.
                CameraNode lightCameraNode;
                if (lightNode.Light is ProjectorLight)
                {
                    var light = (ProjectorLight)lightNode.Light;
                    if (light.Projection is PerspectiveProjection)
                    {
                        var lp = (PerspectiveProjection)light.Projection;
                        var cp = (PerspectiveProjection)_perspectiveCameraNode.Camera.Projection;
                        cp.SetOffCenter(lp.Left, lp.Right, lp.Bottom, lp.Top, lp.Near, lp.Far);

                        lightCameraNode = _perspectiveCameraNode;
                    }
                    else //if (light.Projection is OrthographicProjection)
                    {
                        var lp = (OrthographicProjection)light.Projection;
                        var cp = (OrthographicProjection)_orthographicCameraNode.Camera.Projection;
                        cp.SetOffCenter(lp.Left, lp.Right, lp.Bottom, lp.Top, lp.Near, lp.Far);

                        lightCameraNode = _orthographicCameraNode;
                    }
                }
                else if (lightNode.Light is Spotlight)
                {
                    var light = (Spotlight)lightNode.Light;
                    var cp    = (PerspectiveProjection)_perspectiveCameraNode.Camera.Projection;
                    cp.SetFieldOfView(2 * light.CutoffAngle, 1, shadow.DefaultNear, light.Range);

                    lightCameraNode = _perspectiveCameraNode;
                }
                else
                {
                    throw new GraphicsException("StandardShadow can only be used with a Spotlight or a ProjectorLight.");
                }

                lightCameraNode.PoseWorld = lightNode.PoseWorld;

                // Store data for use in StandardShadowMaskRenderer.
                shadow.Near       = lightCameraNode.Camera.Projection.Near;
                shadow.Far        = lightCameraNode.Camera.Projection.Far;
                shadow.View       = lightCameraNode.PoseWorld.Inverse;
                shadow.Projection = lightCameraNode.Camera.Projection;

                // World units per texel at a planar distance of 1 world unit.
                float unitsPerTexel = lightCameraNode.Camera.Projection.Width / (shadow.ShadowMap.Height * shadow.Near);

                // Convert depth bias from "texel" to world space.
                // Minus to move receiver depth closer to light.
                shadow.EffectiveDepthBias = -shadow.DepthBias * unitsPerTexel;

                // Convert normal offset from "texel" to world space.
                shadow.EffectiveNormalOffset = shadow.NormalOffset * unitsPerTexel;

                graphicsDevice.SetRenderTarget(shadow.ShadowMap);
                context.RenderTarget = shadow.ShadowMap;
                context.Viewport     = graphicsDevice.Viewport;

                graphicsDevice.Clear(Color.White);

                // The scene node renderer should use the light camera instead of the player camera.
                context.CameraNode    = lightCameraNode;
                context.ReferenceNode = lightNode;
                context.Object        = shadow;

                graphicsDevice.DepthStencilState = DepthStencilState.Default;
                graphicsDevice.RasterizerState   = RasterizerState.CullCounterClockwise;
                graphicsDevice.BlendState        = BlendState.Opaque;

                bool shadowMapContainsSomething = RenderCallback(context);
                if (!shadowMapContainsSomething)
                {
                    // Shadow map is empty. Recycle it.
                    graphicsService.RenderTargetPool.Recycle(shadow.ShadowMap);
                    shadow.ShadowMap = null;
                }
            }

            graphicsDevice.SetRenderTarget(null);
            savedRenderState.Restore();

            context.CameraNode    = cameraNode;
            context.Technique     = null;
            context.RenderTarget  = originalRenderTarget;
            context.Viewport      = originalViewport;
            context.ReferenceNode = originalReferenceNode;
            context.Object        = null;
        }
Example #30
0
        public void Render(IList<SceneNode> occluders, LightNode lightNode, SceneNodeRenderer renderer, RenderContext context)
        {
            if (context == null)
            throw new ArgumentNullException("context");

              context.ThrowIfCameraMissing();

              // ----- Sort occluders by type: IOcclusionProxy vs. SceneNode
              SortOccluders(occluders, renderer, context);
              Statistics.Occluders = _occlusionProxies.Count + _sceneNodes.Count;

              // ----- Update all IOcclusionProxy in background.
              if (_occlusionProxies.Count > 0)
              {
            if (EnableMultithreading)
              _updateTask = Parallel.Start(_updateOcclusionProxies);
            else
              UpdateOcclusionProxies();
              }

              // ----- Backup render state.
              var originalRenderTarget = context.RenderTarget;
              var originalViewport = context.Viewport;

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var originalRenderState = new RenderStateSnapshot(graphicsDevice);

              // ----- Camera properties
              var cameraNode = context.CameraNode;
              Matrix cameraView = (Matrix)cameraNode.View;
              var cameraProjection = cameraNode.Camera.Projection;
              Matrix cameraViewProjection = cameraView * cameraProjection;

              if (lightNode == null)
              {
            _lightHzbAvailable = false;
              }
              else
              {
            // ----- Render light HZB.
            _lightHzbAvailable = true;

            var shadow = lightNode.Shadow as CascadedShadow;
            if (shadow == null)
              throw new ArgumentException("LightNode expected to have a CascadedShadow.", "lightNode");

            // Set up orthographic camera similar to CascadedShadowMapRenderer.
            context.CameraNode = _orthographicCameraNode;

            // Part of camera frustum covered by shadow map.
            var maxShadowDistance = shadow.Distances[shadow.NumberOfCascades - 1];
            _splitVolume.SetFieldOfView(cameraProjection.FieldOfViewY, cameraProjection.AspectRatio, cameraProjection.Near, Math.Min(cameraProjection.Far, maxShadowDistance));

            // Find the bounding sphere of the camera frustum.
            Vector3F center;
            float radius;
            GetBoundingSphere(_splitVolume, out center, out radius);

            Matrix33F orientation = lightNode.PoseWorld.Orientation;
            Vector3F lightBackward = orientation.GetColumn(2);
            var orthographicProjection = (OrthographicProjection)_orthographicCameraNode.Camera.Projection;

            // Create a tight orthographic frustum around the cascade's bounding sphere.
            orthographicProjection.SetOffCenter(-radius, radius, -radius, radius, 0, 2 * radius);
            center = cameraNode.PoseWorld.ToWorldPosition(center);
            Vector3F cameraPosition = center + radius * lightBackward;
            Pose frustumPose = new Pose(cameraPosition, orientation);

            // For rendering the shadow map, move near plane back by MinLightDistance
            // to catch occluders in front of the cascade.
            orthographicProjection.Near = -shadow.MinLightDistance;
            _orthographicCameraNode.PoseWorld = frustumPose;

            Pose lightView = frustumPose.Inverse;
            Matrix lightViewProjection = (Matrix)lightView * orthographicProjection;

            _parameterCameraViewProj.SetValue(lightViewProjection);
            _parameterCameraNear.SetValue(orthographicProjection.Near);
            _parameterCameraFar.SetValue(orthographicProjection.Far);

            RenderOccluders(renderer, context);
            CreateDepthHierarchy(_lightHzb, context);

            // Set effect parameters for use in Query().
            _lightAabb = _orthographicCameraNode.Aabb;
            _parameterLightViewProj.SetValue(lightViewProjection);
            _parameterLightToCamera.SetValue(Matrix.Invert(lightViewProjection) * cameraViewProjection);

            context.CameraNode = cameraNode;
              }

              // ----- Render camera HZB.
              // Set camera parameters. (These effect parameters are also needed in Query()!)
              _cameraAabb = cameraNode.Aabb;
              _parameterCameraViewProj.SetValue(cameraViewProjection);
              _parameterCameraNear.SetValue(cameraProjection.Near);
              _parameterCameraFar.SetValue(cameraProjection.Far);

              var lodCameraNode = context.LodCameraNode;
              if (lodCameraNode != null)
              {
            // Enable distance culling.
            _parameterCameraPosition.SetValue((Vector3)lodCameraNode.PoseWorld.Position);
            float yScale = Math.Abs(lodCameraNode.Camera.Projection.ToMatrix44F().M11);
            _parameterNormalizationFactor.SetValue(1.0f / yScale * cameraNode.LodBias * context.LodBias);
              }
              else
              {
            // Disable distance culling.
            _parameterCameraPosition.SetValue(new Vector3());
            _parameterNormalizationFactor.SetValue(0);
              }

              RenderOccluders(renderer, context);
              CreateDepthHierarchy(_cameraHzb, context);

              _sceneNodes.Clear();
              _occlusionProxies.Clear();

              // Restore render state.
              graphicsDevice.SetRenderTarget(null);
              originalRenderState.Restore();

              context.RenderTarget = originalRenderTarget;
              context.Viewport = originalViewport;
        }
Example #31
0
    private void ProcessJobs(RenderContext context)
    {
      // Set render states.
      var graphicsDevice = context.GraphicsService.GraphicsDevice;
      var savedRenderState = new RenderStateSnapshot(graphicsDevice);
      graphicsDevice.RasterizerState = RasterizerState.CullNone;

      bool renderFill = (Options & FigureRenderOptions.RenderFill) != 0;
      if (renderFill)
        SetupFill(context);

      bool renderStroke = (Options & FigureRenderOptions.RenderStroke) != 0;
      if (renderStroke)
        SetupStroke(context);

      var jobs = _jobs.Array;
      int jobCount = _jobs.Count;
      _mode = RenderMode.Undefined;
      for (int i = 0; i < jobCount; i++)
      {
        var node = jobs[i].Node;
        var renderData = node.Figure.RenderData;
        if (renderData == null)
          continue;

        var vertices = renderData.Vertices;
        if (vertices == null || vertices.Count == 0)
          continue;

        // We can cache a static vertex buffer for static figures with camera-independent dash patterns.
        if (node.IsStatic && node.DashInWorldSpace)
        {
          var nodeRenderData = node.RenderData as FigureNodeRenderData;
          if (nodeRenderData == null)
          {
            nodeRenderData = new FigureNodeRenderData();
            node.RenderData = nodeRenderData;
          }

          if (!nodeRenderData.IsValid)
            CacheVertexBuffer(node, _graphicsService.GraphicsDevice);
        }

        // Render filled polygons.
        var fillIndices = renderData.FillIndices;
        if (renderFill
            && fillIndices != null
            && fillIndices.Count > 0
            && !Numeric.IsZero(node.FillAlpha))
        {
          Fill(node, vertices, fillIndices);
        }

        // Render stroked lines.
        var strokeIndices = renderData.StrokeIndices;
        if (renderStroke
            && strokeIndices != null
            && strokeIndices.Count > 0
            && !Numeric.IsZero(node.StrokeThickness)
            && !Numeric.IsZero(node.StrokeAlpha))
        {
          Stroke(node, vertices, strokeIndices);
        }
      }

      Flush();

      savedRenderState.Restore();
    }
Example #32
0
        /// <summary>
        /// Draws the points.
        /// </summary>
        /// <param name="context">The render context.</param>
        /// <remarks>
        /// If <see cref="Effect"/> is <see langword="null"/>, then <see cref="Render"/> does nothing.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="context"/> is <see langword="null"/>.
        /// </exception>
        public void Render(RenderContext context)
        {
            if (context == null)
            throw new ArgumentNullException("context");

              if (Effect == null)
            return;

              int numberOfPoints = _points.Count;
              if (numberOfPoints <= 0)
            return;

              context.Validate(Effect);
              context.ThrowIfCameraMissing();

              // Render state.
              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.RasterizerState = RasterizerState.CullNone;

              // Reset the texture stages. If a floating point texture is set, we get exceptions
              // when a sampler with bilinear filtering is set.
              graphicsDevice.ResetTextures();

              // Effect parameters.
              Effect.Alpha = 1;
              Effect.DiffuseColor = new Vector3(1, 1, 1);
              Effect.LightingEnabled = false;
              Effect.TextureEnabled = false;
              Effect.VertexColorEnabled = true;
              Effect.World = Matrix.Identity;
              Effect.View = Matrix.Identity;
              Effect.Projection = Matrix.Identity;
              Effect.CurrentTechnique.Passes[0].Apply();

              // Get WorldViewProjection matrix.
              Matrix view = (Matrix)context.CameraNode.View;
              Matrix projection = context.CameraNode.Camera.Projection;
              Matrix wvp = Matrix.Multiply(view, projection);

              // The x and y point size relative to the viewport.
              Viewport viewport = graphicsDevice.Viewport;
              float sizeX = PointSize / viewport.Width;
              float sizeY = sizeX * viewport.Width / viewport.Height;

              // Resize buffer if necessary.
              ResizeBuffer(graphicsDevice, numberOfPoints);

              // Submit points. The loop is only needed if we have more points than can
              // be submitted with one draw call.
              var startPointIndex = 0;
              while (startPointIndex < numberOfPoints)
              {
            // Number of points in this batch.
            int pointsPerBatch = Math.Min(numberOfPoints - startPointIndex, _buffer.Length / 6);

            // Create vertices for points. All positions are directly in clip space!
            for (int i = 0; i < pointsPerBatch; i++)
            {
              var point = _points[startPointIndex + i];

              // Transform point position to clip space.
              Vector3 positionWorld = (Vector3)point.Position;
              Vector3 positionClip;
              Vector3.Transform(ref positionWorld, ref wvp, out positionClip);
              float w = (float)((double)positionWorld.X * wvp.M14 + (double)positionWorld.Y * wvp.M24 + (double)positionWorld.Z * wvp.M34 + wvp.M44);

              // Homogeneous divide.
              positionClip /= w;

              // 2 triangles create a point quad. Clip space goes from -1 to 1, therefore
              // we do not need to divide sizeX and sizeY by 2.
              Vector3 bottomLeft = positionClip + new Vector3(-sizeX, +sizeY, 0);
              Vector3 bottomRight = positionClip + new Vector3(+sizeX, +sizeY, 0);
              Vector3 topLeft = positionClip + new Vector3(-sizeX, -sizeY, 0);
              Vector3 topRight = positionClip + new Vector3(+sizeX, -sizeY, 0);
              _buffer[i * 6 + 0].Position = bottomLeft;
              _buffer[i * 6 + 0].Color = point.Color;
              _buffer[i * 6 + 1].Position = bottomRight;
              _buffer[i * 6 + 1].Color = point.Color;
              _buffer[i * 6 + 2].Position = topLeft;
              _buffer[i * 6 + 2].Color = point.Color;
              _buffer[i * 6 + 3].Position = bottomRight;
              _buffer[i * 6 + 3].Color = point.Color;
              _buffer[i * 6 + 4].Position = topRight;
              _buffer[i * 6 + 4].Color = point.Color;
              _buffer[i * 6 + 5].Position = topLeft;
              _buffer[i * 6 + 5].Color = point.Color;
            }

            // Draw triangles.
            graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, _buffer, 0, pointsPerBatch * 2);

            startPointIndex += pointsPerBatch;
              }

              savedRenderState.Restore();
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              var graphicsDevice = _effect.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.DepthStencilState = DepthStencilState.None;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd;

              var viewport = graphicsDevice.Viewport;
              _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height));
              _parameterGBuffer0.SetValue(context.GBuffer0);
              _parameterGBuffer1.SetValue(context.GBuffer1);

              var cameraNode = context.CameraNode;
              var cameraPose = cameraNode.PoseWorld;
              Matrix viewProjection = (Matrix)cameraNode.View * cameraNode.Camera.Projection;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              var isHdrEnabled = context.IsHdrEnabled();
              for (int i = 0; i < numberOfNodes; i++)
              {
            var lightNode = nodes[i] as LightNode;
            if (lightNode == null)
              continue;

            var light = lightNode.Light as ProjectorLight;
            if (light == null)
              continue;

            // LightNode is visible in current frame.
            lightNode.LastFrame = frame;

            float hdrScale = isHdrEnabled ? light.HdrScale : 1;
            _parameterDiffuseColor.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale);
            _parameterSpecularColor.SetValue((Vector3)light.Color * light.SpecularIntensity * hdrScale);
            _parameterTexture.SetValue(light.Texture);

            var lightPose = lightNode.PoseWorld;
            _parameterPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position));

            _parameterRange.SetValue(light.Projection.Far);
            _parameterAttenuation.SetValue(light.Attenuation);
            _parameterTextureMatrix.SetValue((Matrix)(GraphicsHelper.ProjectorBiasMatrix * light.Projection * (lightPose.Inverse * new Pose(cameraPose.Position))));

            var rectangle = GraphicsHelper.GetViewportRectangle(cameraNode, viewport, lightNode);
            var texCoordTopLeft = new Vector2F(rectangle.Left / (float)viewport.Width, rectangle.Top / (float)viewport.Height);
            var texCoordBottomRight = new Vector2F(rectangle.Right / (float)viewport.Width, rectangle.Bottom / (float)viewport.Height);
            GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, texCoordTopLeft, texCoordBottomRight, _frustumFarCorners);

            // Convert frustum far corners from view space to world space.
            for (int j = 0; j < _frustumFarCorners.Length; j++)
              _frustumFarCorners[j] = (Vector3)cameraPose.ToWorldDirection((Vector3F)_frustumFarCorners[j]);

            _parameterFrustumCorners.SetValue(_frustumFarCorners);

            bool hasShadow = (lightNode.Shadow != null && lightNode.Shadow.ShadowMask != null);
            if (hasShadow)
            {
              switch (lightNode.Shadow.ShadowMaskChannel)
              {
            case 0: _parameterShadowMaskChannel.SetValue(new Vector4(1, 0, 0, 0)); break;
            case 1: _parameterShadowMaskChannel.SetValue(new Vector4(0, 1, 0, 0)); break;
            case 2: _parameterShadowMaskChannel.SetValue(new Vector4(0, 0, 1, 0)); break;
            default: _parameterShadowMaskChannel.SetValue(new Vector4(0, 0, 0, 1)); break;
              }

              _parameterShadowMask.SetValue(lightNode.Shadow.ShadowMask);
            }

            if (lightNode.Clip != null)
            {
              var data = lightNode.RenderData as LightRenderData;
              if (data == null)
              {
            data = new LightRenderData();
            lightNode.RenderData = data;
              }

              data.UpdateClipSubmesh(context.GraphicsService, lightNode);

              graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateOnePassStencilFail;
              graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite;

              _parameterWorldViewProjection.SetValue((Matrix)data.ClipMatrix * viewProjection);
              _passClip.Apply();
              data.ClipSubmesh.Draw();

              graphicsDevice.DepthStencilState = lightNode.InvertClip
            ? GraphicsHelper.DepthStencilStateStencilEqual0
            : GraphicsHelper.DepthStencilStateStencilNotEqual0;
              graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd;
            }
            else
            {
              graphicsDevice.DepthStencilState = DepthStencilState.None;
            }

            if (hasShadow)
            {
              if (light.Texture.Format == SurfaceFormat.Alpha8)
            _passShadowedAlpha.Apply();
              else
            _passShadowedRgb.Apply();
            }
            else
            {
              if (light.Texture.Format == SurfaceFormat.Alpha8)
            _passDefaultAlpha.Apply();
              else
            _passDefaultRgb.Apply();
            }

            graphicsDevice.DrawQuad(rectangle);
              }

              savedRenderState.Restore();
        }
        /// <inheritdoc/>
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (nodes.Count == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.BlendState = BlendState.AlphaBlend;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;

              // Camera properties
              var cameraNode = context.CameraNode;
              Matrix view = (Matrix)new Matrix44F(cameraNode.PoseWorld.Orientation.Transposed, new Vector3F());
              _parameterView.SetValue(view);
              Matrix projection = cameraNode.Camera.Projection;
              _parameterProjection.SetValue(projection);

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i] as ScatteringSkyNode;
            if (node == null)
              continue;

            // ScatteringSkyNode is visible in current frame.
            node.LastFrame = frame;

            _parameterSunDirection.SetValue((Vector3)node.SunDirection);
            _parameterSunIntensity.SetValue((Vector3)(node.SunIntensity * node.SunColor));
            _parameterRadii.SetValue(new Vector4(
              node.AtmosphereHeight + node.PlanetRadius,    // Atmosphere radius
              node.PlanetRadius,                            // Ground radius
              node.ObserverAltitude + node.PlanetRadius,    // Observer radius
              node.ScaleHeight));                           // Absolute Scale height
            _parameterNumberOfSamples.SetValue(node.NumberOfSamples);
            _parameterBetaRayleigh.SetValue((Vector3)node.BetaRayleigh);
            _parameterBetaMie.SetValue((Vector3)node.BetaMie);
            _parameterGMie.SetValue(node.GMie);
            _parameterTransmittance.SetValue(node.Transmittance);

            if (node.BaseHorizonColor.IsNumericallyZero && node.BaseZenithColor.IsNumericallyZero)
            {
              // No base color.
              if (context.IsHdrEnabled())
            _passLinear.Apply();
              else
            _passGamma.Apply();
            }
            else
            {
              // Add base color.
              _parameterBaseHorizonColor.SetValue((Vector4)new Vector4F(node.BaseHorizonColor, node.BaseColorShift));
              _parameterBaseZenithColor.SetValue((Vector3)node.BaseZenithColor);

              if (context.IsHdrEnabled())
            _passLinearWithBaseColor.Apply();
              else
            _passGammaWithBaseColor.Apply();
            }

            _submesh.Draw();
              }

              savedRenderState.Restore();
        }
Example #35
0
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (nodes.Count == 0)
            {
                return;
            }

            context.Validate(_effect);

            var originalRenderTarget = context.RenderTarget;
            var originalViewport     = context.Viewport;

            var graphicsDevice   = context.GraphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.BlendState        = BlendState.Opaque;
            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.DepthStencilState = DepthStencilState.None;

            int   frame     = context.Frame;
            float deltaTime = (float)context.DeltaTime.TotalSeconds;

            for (int nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++)
            {
                var cloudNode = nodes[nodeIndex] as CloudLayerNode;
                if (cloudNode == null)
                {
                    continue;
                }

                var cloudMap = cloudNode.CloudMap as LayeredCloudMap;
                if (cloudMap == null)
                {
                    continue;
                }

                // We update the cloud map only once per frame.
                if (cloudMap.LastFrame == frame)
                {
                    continue;
                }

                cloudMap.LastFrame = frame;

                var layers         = cloudMap.Layers;
                var animationTimes = cloudMap.AnimationTimes;
                var sources        = cloudMap.SourceLayers;
                var targets        = cloudMap.TargetLayers;
                var renderTargets  = cloudMap.LayerTextures;

                // Animate the cloud map layers.
                for (int i = 0; i < LayeredCloudMap.NumberOfTextures; i++)
                {
                    if (layers[i] == null || layers[i].Texture != null)
                    {
                        continue;
                    }

                    if (cloudMap.Random == null)
                    {
                        cloudMap.Random = new Random(cloudMap.Seed);
                    }

                    // Make sure there is a user-defined texture or data for procedural textures.
                    if (sources[i] == null)
                    {
                        // Each octave is 128 x 128 (= 1 / 4 of the 512 * 512 noise texture).
                        sources[i]       = new PackedTexture(null, _noiseTexture, cloudMap.Random.NextVector2F(0, 1), new Vector2F(0.25f));
                        targets[i]       = new PackedTexture(null, _noiseTexture, cloudMap.Random.NextVector2F(0, 1), new Vector2F(0.25f));
                        renderTargets[i] = new RenderTarget2D(graphicsDevice, 128, 128, false, SurfaceFormat.Alpha8, DepthFormat.None);
                    }

                    // Update animation time.
                    animationTimes[i] += deltaTime * layers[i].AnimationSpeed;

                    // Update source and target if animation time is beyond 1.
                    if (animationTimes[i] > 1)
                    {
                        // Wrap animation time.
                        animationTimes[i] = animationTimes[i] % 1;

                        // Swap source and target.
                        MathHelper.Swap(ref sources[i], ref targets[i]);

                        // Set target to a new random part of the noise texture.
                        targets[i].Offset = cloudMap.Random.NextVector2F(0, 1);
                    }

                    // Lerp source and target together to get the final noise texture.
                    graphicsDevice.SetRenderTarget(renderTargets[i]);
                    _parameterViewportSize.SetValue(new Vector2(graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height));
                    _parameterTextures[0].SetValue(sources[i].TextureAtlas);
                    _parameterTextures[1].SetValue(targets[i].TextureAtlas);
                    _parameterTexture0Parameters.SetValue(new Vector4(sources[i].Scale.X, sources[i].Scale.Y, sources[i].Offset.X, sources[i].Offset.Y));
                    _parameterTexture1Parameters.SetValue(new Vector4(targets[i].Scale.X, targets[i].Scale.Y, targets[i].Offset.X, targets[i].Offset.Y));
                    _parameterLerp.SetValue(animationTimes[i]);
                    _passLerp.Apply();
                    graphicsDevice.DrawFullScreenQuad();
                }

                // Initialize the cloud map.
                if (cloudMap.Texture == null || cloudMap.Size != cloudMap.Texture.Width)
                {
                    cloudMap.Texture.SafeDispose();

                    var cloudTexture = new RenderTarget2D(
                        graphicsDevice,
                        cloudMap.Size,
                        cloudMap.Size,
                        false,
                        SurfaceFormat.Alpha8,
                        DepthFormat.None);

                    cloudMap.SetTexture(cloudTexture);
                }

                // Combine the layers.
                graphicsDevice.SetRenderTarget((RenderTarget2D)cloudMap.Texture);
                _parameterViewportSize.SetValue(new Vector2(cloudMap.Texture.Width, cloudMap.Texture.Height));
                for (int i = 0; i < LayeredCloudMap.NumberOfTextures; i++)
                {
                    var layer = layers[i] ?? EmptyLayer;
                    _parameterTextures[i].SetValue(layer.Texture ?? renderTargets[i]);
                    _parameterMatrices[i].SetValue((Matrix) new Matrix44F(layer.TextureMatrix, Vector3F.Zero));
                    _parameterDensities[i].SetValue(new Vector2(layer.DensityScale, layer.DensityOffset));
                }
                _parameterCoverage.SetValue(cloudMap.Coverage);
                _parameterDensity.SetValue(cloudMap.Density);
                _passDensity.Apply();
                graphicsDevice.DrawFullScreenQuad();
            }

            savedRenderState.Restore();
            graphicsDevice.SetRenderTarget(null);
            context.RenderTarget = originalRenderTarget;
            context.Viewport     = originalViewport;
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              // Note: The camera node is not used by the StandardShadowMapRenderer.
              // Still throw an exception if null for consistency. (All other shadow map
              // renderers need a camera node.)
              context.ThrowIfCameraMissing();
              context.ThrowIfSceneMissing();

              var originalRenderTarget = context.RenderTarget;
              var originalViewport = context.Viewport;
              var originalReferenceNode = context.ReferenceNode;

              var cameraNode = context.CameraNode;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              context.Technique = "Default";

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              for (int i = 0; i < numberOfNodes; i++)
              {
            var lightNode = nodes[i] as LightNode;
            if (lightNode == null)
              continue;

            var shadow = lightNode.Shadow as StandardShadow;
            if (shadow == null)
              continue;

            // LightNode is visible in current frame.
            lightNode.LastFrame = frame;

            // Get a new shadow map if necessary.
            if (shadow.ShadowMap == null)
            {
              shadow.ShadowMap = graphicsService.RenderTargetPool.Obtain2D(
            new RenderTargetFormat(
              shadow.PreferredSize,
              shadow.PreferredSize,
              false,
              shadow.Prefer16Bit ? SurfaceFormat.HalfSingle : SurfaceFormat.Single,
              DepthFormat.Depth24));
            }

            // Create a suitable shadow camera.
            CameraNode lightCameraNode;
            if (lightNode.Light is ProjectorLight)
            {
              var light = (ProjectorLight)lightNode.Light;
              if (light.Projection is PerspectiveProjection)
              {
            var lp = (PerspectiveProjection)light.Projection;
            var cp = (PerspectiveProjection)_perspectiveCameraNode.Camera.Projection;
            cp.SetOffCenter(lp.Left, lp.Right, lp.Bottom, lp.Top, lp.Near, lp.Far);

            lightCameraNode = _perspectiveCameraNode;
              }
              else //if (light.Projection is OrthographicProjection)
              {
            var lp = (OrthographicProjection)light.Projection;
            var cp = (OrthographicProjection)_orthographicCameraNode.Camera.Projection;
            cp.SetOffCenter(lp.Left, lp.Right, lp.Bottom, lp.Top, lp.Near, lp.Far);

            lightCameraNode = _orthographicCameraNode;
              }
            }
            else if (lightNode.Light is Spotlight)
            {
              var light = (Spotlight)lightNode.Light;
              var cp = (PerspectiveProjection)_perspectiveCameraNode.Camera.Projection;
              cp.SetFieldOfView(2 * light.CutoffAngle, 1, shadow.DefaultNear, light.Range);

              lightCameraNode = _perspectiveCameraNode;
            }
            else
            {
              throw new GraphicsException("StandardShadow can only be used with a Spotlight or a ProjectorLight.");
            }

            lightCameraNode.PoseWorld = lightNode.PoseWorld;

            // Store data for use in StandardShadowMaskRenderer.
            shadow.Near = lightCameraNode.Camera.Projection.Near;
            shadow.Far = lightCameraNode.Camera.Projection.Far;
            shadow.View = lightCameraNode.PoseWorld.Inverse;
            shadow.Projection = lightCameraNode.Camera.Projection;

            // World units per texel at a planar distance of 1 world unit.
            float unitsPerTexel = lightCameraNode.Camera.Projection.Width / (shadow.ShadowMap.Height * shadow.Near);

            // Convert depth bias from "texel" to world space.
            // Minus to move receiver depth closer to light.
            shadow.EffectiveDepthBias = -shadow.DepthBias * unitsPerTexel;

            // Convert normal offset from "texel" to world space.
            shadow.EffectiveNormalOffset = shadow.NormalOffset * unitsPerTexel;

            graphicsDevice.SetRenderTarget(shadow.ShadowMap);
            context.RenderTarget = shadow.ShadowMap;
            context.Viewport = graphicsDevice.Viewport;

            graphicsDevice.Clear(Color.White);

            // The scene node renderer should use the light camera instead of the player camera.
            context.CameraNode = lightCameraNode;
            context.ReferenceNode = lightNode;
            context.Object = shadow;

            graphicsDevice.DepthStencilState = DepthStencilState.Default;
            graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
            graphicsDevice.BlendState = BlendState.Opaque;

            bool shadowMapContainsSomething = RenderCallback(context);
            if (!shadowMapContainsSomething)
            {
              // Shadow map is empty. Recycle it.
              graphicsService.RenderTargetPool.Recycle(shadow.ShadowMap);
              shadow.ShadowMap = null;
            }
              }

              graphicsDevice.SetRenderTarget(null);
              savedRenderState.Restore();

              context.CameraNode = cameraNode;
              context.Technique = null;
              context.RenderTarget = originalRenderTarget;
              context.Viewport = originalViewport;
              context.ReferenceNode = originalReferenceNode;
              context.Object = null;
        }
Example #37
0
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              // Camera properties
              int viewportHeight = graphicsDevice.Viewport.Height;
              var cameraNode = context.CameraNode;
              var projection = cameraNode.Camera.Projection;
              _parameterProjection.SetValue(projection);

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i] as CloudLayerNode;
            if (node == null)
              continue;

            // CloudLayerNode is visible in current frame.
            node.LastFrame = frame;

            if (node.CloudMap.Texture == null)
              continue;

            var sunDirection = node.SunDirection;
            _parameterSunDirection.SetValue((Vector3)sunDirection);
            _parameterSkyCurvature.SetValue(node.SkyCurvature);
            _parameterTextureMatrix.SetValue((Matrix)new Matrix44F(node.TextureMatrix, Vector3F.Zero));

            // The sample at the pixel counts as one, the rest are for the blur.
            // Note: We must not set -1 because a for loop like
            //   for (int i = 0; i < -1, i++)
            // crashes the AMD DX9 WP8.1 graphics driver. LOL
            _parameterNumberOfSamples.SetValue(Math.Max(0, node.NumberOfSamples - 1));

            _parameterSampleDistance.SetValue(node.SampleDistance);
            _parameterScatterParameters.SetValue(new Vector3(node.ForwardScatterExponent, node.ForwardScatterScale, node.ForwardScatterOffset));
            _parameterHorizonFade.SetValue(new Vector2(node.HorizonFade, node.HorizonBias));
            _parameterSunLight.SetValue((Vector3)node.SunLight);
            _parameterAmbientLight.SetValue(new Vector4((Vector3)node.AmbientLight, node.Alpha));
            _parameterTexture.SetValue(node.CloudMap.Texture);

            // Occlusion query.
            if (graphicsDevice.GraphicsProfile != GraphicsProfile.Reach && node.SunQuerySize >= Numeric.EpsilonF)
            {
              bool skipQuery = false;
              if (node.OcclusionQuery != null)
              {
            if (node.OcclusionQuery.IsComplete)
            {
              node.TryUpdateSunOcclusion();
            }
            else
            {
              // The previous query is still not finished. Do not start a new query, this would
              // create a SharpDX warning.
              skipQuery = true;
            }
              }
              else
              {
            node.OcclusionQuery = new OcclusionQuery(graphicsDevice);
              }

              if (!skipQuery)
              {
            node.IsQueryPending = true;

            float totalPixels = viewportHeight * node.SunQuerySize;
            totalPixels *= totalPixels;
            node.QuerySize = totalPixels;

            // Use a camera which looks at the sun.
            // Get an relative up vector which is not parallel to the forward direction.
            var lookAtUp = Vector3F.UnitY;
            if (Vector3F.AreNumericallyEqual(sunDirection, lookAtUp))
              lookAtUp = Vector3F.UnitZ;

            Vector3F zAxis = -sunDirection;
            Vector3F xAxis = Vector3F.Cross(lookAtUp, zAxis).Normalized;
            Vector3F yAxis = Vector3F.Cross(zAxis, xAxis);

            var lookAtSunView = new Matrix(xAxis.X, yAxis.X, zAxis.X, 0,
                                           xAxis.Y, yAxis.Y, zAxis.Y, 0,
                                           xAxis.Z, yAxis.Z, zAxis.Z, 0,
                                           0, 0, 0, 1);
            _parameterView.SetValue(lookAtSunView);

            graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite;
            graphicsDevice.DepthStencilState = DepthStencilState.None;
            graphicsDevice.RasterizerState = RasterizerState.CullNone;

            // Create small quad shortly behind the near plane.
            // Note: We use an "untranslated" view matrix, so we can ignore the camera position.
            float width = (projection.Top - projection.Bottom) * node.SunQuerySize;
            Vector3F right = sunDirection.Orthonormal1 * (width / 2);
            Vector3F up = sunDirection.Orthonormal2 * (width / 2);
            Vector3F center = sunDirection * (projection.Near * 1.0001f);
            _queryGeometry[0] = center - up - right;
            _queryGeometry[1] = center + up - right;
            _queryGeometry[2] = center - up + right;
            _queryGeometry[3] = center + up + right;

            if (node.CloudMap.Texture.Format == SurfaceFormat.Alpha8)
              _passOcclusionAlpha.Apply();
            else
              _passOcclusionRgb.Apply();

            node.OcclusionQuery.Begin();
            graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _queryGeometry, 0, 2,
              VertexPosition.VertexDeclaration);
            node.OcclusionQuery.End();
              }
            }
            else
            {
              node.IsQueryPending = false;
              node.SunOcclusion = 0;
            }

            Matrix viewUntranslated = (Matrix)new Matrix44F(cameraNode.PoseWorld.Orientation.Transposed, new Vector3F(0));
            _parameterView.SetValue(viewUntranslated);

            // Render clouds.
            graphicsDevice.BlendState = BlendState.AlphaBlend;
            graphicsDevice.RasterizerState = RasterizerState.CullNone;
            graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;

            if (context.IsHdrEnabled())
            {
              if (node.CloudMap.Texture.Format == SurfaceFormat.Alpha8)
            _passCloudAlphaLinear.Apply();
              else
            _passCloudRgbLinear.Apply();
            }
            else
            {
              if (node.CloudMap.Texture.Format == SurfaceFormat.Alpha8)
            _passCloudAlphaGamma.Apply();
              else
            _passCloudRgbGamma.Apply();
            }

            _submesh.Draw();
              }

              savedRenderState.Restore();
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (nodes.Count == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.BlendState = BlendState.AlphaBlend;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;

              // Camera properties
              var cameraNode = context.CameraNode;
              Matrix view = (Matrix)new Matrix44F(cameraNode.PoseWorld.Orientation.Transposed, new Vector3F());
              _parameterView.SetValue(view);
              Matrix projection = cameraNode.Camera.Projection;
              _parameterProjection.SetValue(projection);

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i] as GradientTextureSkyNode;
            if (node == null)
              continue;

            // GradientTextureSkyNode is visible in current frame.
            node.LastFrame = frame;

            _parameterSunDirection.SetValue((Vector3)node.SunDirection);
            _parameterTime.SetValue((float)node.TimeOfDay.TotalHours / 24);
            _parameterColor.SetValue((Vector4)node.Color);
            _parameterFrontTexture.SetValue(node.FrontTexture);
            _parameterBackTexture.SetValue(node.BackTexture);

            if (node.CieSkyStrength < Numeric.EpsilonF)
            {
              if (context.IsHdrEnabled())
            _passLinear.Apply();
              else
            _passGamma.Apply();
            }
            else
            {
              var p = node.CieSkyParameters;
              _parameterAbcd.SetValue(new Vector4(p.A, p.B, p.C, p.D));
              _parameterEAndStrength.SetValue(new Vector2(p.E, node.CieSkyStrength));

              if (context.IsHdrEnabled())
            _passCieLinear.Apply();
              else
            _passCieGamma.Apply();
            }
            _submesh.Draw();
              }

              savedRenderState.Restore();
        }
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            context.Validate(_effect);
            context.ThrowIfCameraMissing();

            var graphicsDevice   = _effect.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.DepthStencilState = DepthStencilState.None;
            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.BlendState        = GraphicsHelper.BlendStateAdd;

            var viewport = graphicsDevice.Viewport;

            _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height));
            _parameterGBuffer0.SetValue(context.GBuffer0);
            _parameterGBuffer1.SetValue(context.GBuffer1);

            var    cameraNode     = context.CameraNode;
            Pose   cameraPose     = cameraNode.PoseWorld;
            Matrix viewProjection = (Matrix)cameraNode.View * cameraNode.Camera.Projection;

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            context.CameraNode.LastFrame = frame;

            var isHdrEnabled = context.IsHdrEnabled();

            for (int i = 0; i < numberOfNodes; i++)
            {
                var lightNode = nodes[i] as LightNode;
                if (lightNode == null)
                {
                    continue;
                }

                var light = lightNode.Light as PointLight;
                if (light == null)
                {
                    continue;
                }

                // LightNode is visible in current frame.
                lightNode.LastFrame = frame;

                float hdrScale = isHdrEnabled ? light.HdrScale : 1;
                _parameterDiffuseColor.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale);
                _parameterSpecularColor.SetValue((Vector3)light.Color * light.SpecularIntensity * hdrScale);

                Pose lightPose = lightNode.PoseWorld;

                bool hasShadow = (lightNode.Shadow != null && lightNode.Shadow.ShadowMask != null);
                if (hasShadow)
                {
                    switch (lightNode.Shadow.ShadowMaskChannel)
                    {
                    case 0: _parameterShadowMaskChannel.SetValue(new Vector4(1, 0, 0, 0)); break;

                    case 1: _parameterShadowMaskChannel.SetValue(new Vector4(0, 1, 0, 0)); break;

                    case 2: _parameterShadowMaskChannel.SetValue(new Vector4(0, 0, 1, 0)); break;

                    default: _parameterShadowMaskChannel.SetValue(new Vector4(0, 0, 0, 1)); break;
                    }

                    _parameterShadowMask.SetValue(lightNode.Shadow.ShadowMask);
                }

                _parameterPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position));
                _parameterRange.SetValue(light.Range);
                _parameterAttenuation.SetValue(light.Attenuation);

                bool hasTexture = (light.Texture != null);
                if (hasTexture)
                {
                    _parameterTexture.SetValue(light.Texture);

                    // Cube maps are left handed --> Sample with inverted z. (Otherwise, the
                    // cube map and objects or texts in it are mirrored.)
                    var mirrorZ = Matrix44F.CreateScale(1, 1, -1);
                    _parameterTextureMatrix.SetValue((Matrix)(mirrorZ * lightPose.Inverse));
                }

                var rectangle           = GraphicsHelper.GetViewportRectangle(cameraNode, viewport, lightPose.Position, light.Range);
                var texCoordTopLeft     = new Vector2F(rectangle.Left / (float)viewport.Width, rectangle.Top / (float)viewport.Height);
                var texCoordBottomRight = new Vector2F(rectangle.Right / (float)viewport.Width, rectangle.Bottom / (float)viewport.Height);
                GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, texCoordTopLeft, texCoordBottomRight, _frustumFarCorners);

                // Convert frustum far corners from view space to world space.
                for (int j = 0; j < _frustumFarCorners.Length; j++)
                {
                    _frustumFarCorners[j] = (Vector3)cameraPose.ToWorldDirection((Vector3F)_frustumFarCorners[j]);
                }

                _parameterFrustumCorners.SetValue(_frustumFarCorners);

                if (lightNode.Clip != null)
                {
                    var data = lightNode.RenderData as LightRenderData;
                    if (data == null)
                    {
                        data = new LightRenderData();
                        lightNode.RenderData = data;
                    }

                    data.UpdateClipSubmesh(context.GraphicsService, lightNode);

                    graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateOnePassStencilFail;
                    graphicsDevice.BlendState        = GraphicsHelper.BlendStateNoColorWrite;

                    _parameterWorldViewProjection.SetValue((Matrix)data.ClipMatrix * viewProjection);
                    _passClip.Apply();
                    data.ClipSubmesh.Draw();

                    graphicsDevice.DepthStencilState = lightNode.InvertClip
            ? GraphicsHelper.DepthStencilStateStencilEqual0
            : GraphicsHelper.DepthStencilStateStencilNotEqual0;
                    graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd;
                }
                else
                {
                    graphicsDevice.DepthStencilState = DepthStencilState.None;
                }

                if (hasShadow)
                {
                    if (hasTexture)
                    {
                        if (light.Texture.Format == SurfaceFormat.Alpha8)
                        {
                            _passShadowedTexturedAlpha.Apply();
                        }
                        else
                        {
                            _passShadowedTexturedRgb.Apply();
                        }
                    }
                    else
                    {
                        _passShadowed.Apply();
                    }
                }
                else
                {
                    if (hasTexture)
                    {
                        if (light.Texture.Format == SurfaceFormat.Alpha8)
                        {
                            _passTexturedAlpha.Apply();
                        }
                        else
                        {
                            _passTexturedRgb.Apply();
                        }
                    }
                    else
                    {
                        _passDefault.Apply();
                    }
                }

                graphicsDevice.DrawQuad(rectangle);
            }

            savedRenderState.Restore();
        }
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            context.Validate(_effect);
            context.ThrowIfCameraMissing();

            var graphicsDevice   = _effect.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.DepthStencilState = DepthStencilState.None;
            graphicsDevice.RasterizerState   = RasterizerState.CullNone;

            var cameraNode = context.CameraNode;

            _parameterViewInverse.SetValue(cameraNode.PoseWorld);
            _parameterGBuffer0.SetValue(context.GBuffer0);

            Viewport viewport = context.Viewport;

            _parameterParameters0.SetValue(new Vector2(viewport.Width, viewport.Height));

            if (_jitterMap == null)
            {
                _jitterMap = NoiseHelper.GetGrainTexture(context.GraphicsService, NoiseHelper.DefaultJitterMapWidth);
            }

            _parameterJitterMap.SetValue(_jitterMap);

            for (int i = 0; i < numberOfNodes; i++)
            {
                var lightNode = nodes[i] as LightNode;
                if (lightNode == null)
                {
                    continue;
                }

                var shadow = lightNode.Shadow as StandardShadow;
                if (shadow == null)
                {
                    continue;
                }

                if (shadow.ShadowMap == null || shadow.ShadowMask == null)
                {
                    continue;
                }

                // The effect must only render in a specific channel.
                // Do not change blend state if the correct write channels is already set, e.g. if this
                // shadow is part of a CompositeShadow, the correct blend state is already set.
                if ((int)graphicsDevice.BlendState.ColorWriteChannels != (1 << shadow.ShadowMaskChannel))
                {
                    graphicsDevice.BlendState = GraphicsHelper.BlendStateWriteSingleChannel[shadow.ShadowMaskChannel];
                }

                _parameterParameters1.SetValue(new Vector4(
                                                   shadow.Near,
                                                   shadow.Far,
                                                   shadow.EffectiveDepthBias,
                                                   shadow.EffectiveNormalOffset));

                // If we use a subset of the Poisson kernel, we have to normalize the scale.
                int   numberOfSamples = Math.Min(shadow.NumberOfSamples, PoissonKernel.Length);
                float filterRadius    = shadow.FilterRadius;
                if (numberOfSamples > 0)
                {
                    filterRadius /= PoissonKernel[numberOfSamples - 1].Length();
                }

                _parameterParameters2.SetValue(new Vector3(
                                                   shadow.ShadowMap.Width,
                                                   filterRadius,
                                                   // The StandardShadow.JitterResolution is the number of texels per world unit.
                                                   // In the shader the parameter JitterResolution contains the division by the jitter map size.
                                                   shadow.JitterResolution / _jitterMap.Width));

                _parameterLightPosition.SetValue((Vector3)cameraNode.PoseWorld.ToLocalPosition(lightNode.PoseWorld.Position));

                Matrix cameraViewToShadowView = cameraNode.PoseWorld * shadow.View;
                _parameterShadowView.SetValue(cameraViewToShadowView);
                _parameterShadowMatrix.SetValue(cameraViewToShadowView * shadow.Projection);
                _parameterShadowMap.SetValue(shadow.ShadowMap);

                var      rectangle           = GraphicsHelper.GetViewportRectangle(cameraNode, viewport, lightNode);
                Vector2F texCoordTopLeft     = new Vector2F(rectangle.Left / (float)viewport.Width, rectangle.Top / (float)viewport.Height);
                Vector2F texCoordBottomRight = new Vector2F(rectangle.Right / (float)viewport.Width, rectangle.Bottom / (float)viewport.Height);
                GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, texCoordTopLeft, texCoordBottomRight, _frustumFarCorners);
                _parameterFrustumCorners.SetValue(_frustumFarCorners);

                var pass = GetPass(numberOfSamples);

                if (numberOfSamples > 0)
                {
                    if (_lastNumberOfSamples != numberOfSamples)
                    {
                        // Create an array with the first n samples and the rest set to 0.
                        _lastNumberOfSamples = numberOfSamples;
                        for (int j = 0; j < numberOfSamples; j++)
                        {
                            _samples[j].X = PoissonKernel[j].X;
                            _samples[j].Y = PoissonKernel[j].Y;
                            _samples[j].Z = 1.0f / numberOfSamples;

                            // Note [HelmutG]: I have tried weights decreasing with distance but that did not
                            // look better.
                        }

                        // Set the rest to zero.
                        for (int j = numberOfSamples; j < _samples.Length; j++)
                        {
                            _samples[j] = Vector3.Zero;
                        }

                        _parameterSamples.SetValue(_samples);
                    }
                    else if (i == 0)
                    {
                        // Apply offsets in the first loop.
                        _parameterSamples.SetValue(_samples);
                    }
                }

                pass.Apply();

                graphicsDevice.DrawQuad(rectangle);
            }

            _parameterGBuffer0.SetValue((Texture2D)null);
            _parameterJitterMap.SetValue((Texture2D)null);
            _parameterShadowMap.SetValue((Texture2D)null);
            savedRenderState.Restore();
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              for (int i = 0; i < numberOfNodes; i++)
              {
            var lightNode = nodes[i] as LightNode;
            if (lightNode == null)
              continue;

            var shadow = lightNode.Shadow as CompositeShadow;
            if (shadow == null)
              continue;

            if (shadow.ShadowMask == null)
              continue;

            // Write into a single channel and use min() blending.
            graphicsDevice.BlendState = BlendStates[shadow.ShadowMaskChannel];

            for (int j = 0; j < shadow.Shadows.Count; j++)
            {
              // Temporarily set shadow mask and shadow mask channel of child shadows.
              var childShadow = shadow.Shadows[j];
              childShadow.ShadowMask = shadow.ShadowMask;
              childShadow.ShadowMaskChannel = shadow.ShadowMaskChannel;

              // Temporarily exchange LightNode.Shadow and render the child shadow.
              lightNode.Shadow = childShadow;

              for (int k = 0; k < _shadowMaskRenderers.Count; k++)
              {
            var renderer = _shadowMaskRenderers[k];
            if (renderer.CanRender(lightNode, context))
            {
              renderer.Render(lightNode, context);
              break;
            }
              }

              // Remove shadow mask references. Strictly speaking, the mask is correct
              // for the composite shadow. It is not correct for the child shadow. The child
              // shadow only contributes to the mask. Therefore, childShadowMask should not be
              // set.
              childShadow.ShadowMask = null;
              childShadow.ShadowMaskChannel = 0;
            }

            lightNode.Shadow = shadow;
              }

              savedRenderState.Restore();
        }
Example #42
0
        private void RenderHiDef(SkyboxNode node, RenderContext context)
        {
            var graphicsDevice = context.GraphicsService.GraphicsDevice;

            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            graphicsDevice.BlendState        = node.EnableAlphaBlending ? BlendState.AlphaBlend : BlendState.Opaque;

            bool sourceIsFloatingPoint = TextureHelper.IsFloatingPointFormat(node.Texture.Format);

            // Set sampler state. (Floating-point textures cannot use linear filtering. (XNA would throw an exception.))
            if (sourceIsFloatingPoint)
            {
                graphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
            }
            else
            {
                graphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
            }

            var    cameraNode = context.CameraNode;
            Matrix view       = cameraNode.View;
            Matrix projection = cameraNode.Camera.Projection;

            // Cube maps are left handed --> Sample with inverted z. (Otherwise, the
            // cube map and objects or texts in it are mirrored.)
            var    mirrorZ     = Matrix.CreateScale(1, 1, -1);
            Matrix orientation = node.PoseWorld.Orientation;

            _parameterWorldViewProjection.SetValue((Matrix)(projection * view * new Matrix(orientation, Vector3.Zero) * mirrorZ));

            Vector4 color = node.EnableAlphaBlending
                      ? new Vector4((Vector3)node.Color * node.Alpha, node.Alpha) // Premultiplied
                      : new Vector4((Vector3)node.Color, 1);                      // Opaque

            _parameterColor.SetValue(color);
            _textureParameter.SetValue(node.Texture);

            if (node.Encoding is RgbEncoding)
            {
                _parameterTextureSize.SetValue(node.Texture.Size);
                if (context.IsHdrEnabled())
                {
                    _passRgbToRgb.Apply();
                }
                else
                {
                    _passRgbToSRgb.Apply();
                }
            }
            else if (node.Encoding is SRgbEncoding)
            {
                if (!sourceIsFloatingPoint)
                {
                    if (context.IsHdrEnabled())
                    {
                        _passSRgbToRgb.Apply();
                    }
                    else
                    {
                        _passSRgbToSRgb.Apply();
                    }
                }
                else
                {
                    throw new GraphicsException("sRGB encoded skybox cube maps must not use a floating point format.");
                }
            }
            else if (node.Encoding is RgbmEncoding)
            {
                float max = GraphicsHelper.ToGamma(((RgbmEncoding)node.Encoding).Max);
                _parameterRgbmMaxValue.SetValue(max);

                if (context.IsHdrEnabled())
                {
                    _passRgbmToRgb.Apply();
                }
                else
                {
                    _passRgbmToSRgb.Apply();
                }
            }
            else
            {
                throw new NotSupportedException("The SkyBoxRenderer supports only RgbEncoding, SRgbEncoding and RgbmEncoding.");
            }

            _submesh.Draw();
            savedRenderState.Restore();
        }
Example #43
0
        /// <summary>
        /// Draws the textures.
        /// </summary>
        /// <param name="context">The render context.</param>
        /// <remarks>
        /// If <see cref="SpriteBatch"/> is <see langword="null"/>, then <see cref="Render"/> does 
        /// nothing.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="context"/> is <see langword="null"/>.
        /// </exception>
        public void Render(RenderContext context)
        {
            if (context == null)
            throw new ArgumentNullException("context");

              if (SpriteBatch == null)
            return;

              var count = _textures.Count;
              if (count == 0)
            return;

              context.Validate(SpriteBatch);

              var savedRenderState = new RenderStateSnapshot(SpriteBatch.GraphicsDevice);

              SpriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.None, RasterizerState.CullNone);

              for (int i = 0; i < count; i++)
              {
            var textureInfo = _textures[i];

            if (textureInfo.Texture.IsDisposed)
              continue;

            if (TextureHelper.IsFloatingPointFormat(textureInfo.Texture.Format))
            {
              // Floating-point textures must not use linear hardware filtering!
              SpriteBatch.GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp;
              SpriteBatch.Draw(textureInfo.Texture, textureInfo.Rectangle, Color.White);
              SpriteBatch.GraphicsDevice.SamplerStates[0] = SamplerState.LinearClamp;
            }
            else
            {
              SpriteBatch.Draw(textureInfo.Texture, textureInfo.Rectangle, Color.White);
            }
              }

              SpriteBatch.End();

              savedRenderState.Restore();
        }
    public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
    {
      ThrowIfDisposed();

      if (nodes == null)
        throw new ArgumentNullException("nodes");
      if (context == null)
        throw new ArgumentNullException("context");

      // Lens flares are used sparsely in most games. --> Early out, if possible.
      int numberOfNodes = nodes.Count;
      if (nodes.Count == 0)
        return;

      context.Validate(_spriteBatch);
      context.ThrowIfCameraMissing();

      var graphicsDevice = context.GraphicsService.GraphicsDevice;
      var savedRenderState = new RenderStateSnapshot(graphicsDevice);
      bool hiDef = (graphicsDevice.GraphicsProfile == GraphicsProfile.HiDef);

      // Camera properties
      var cameraNode = context.CameraNode;
      var cameraPose = cameraNode.PoseWorld;
      Vector3 cameraForward = -cameraPose.Orientation.GetColumn(2); // 3rd column vector (negated)
      Matrix view = cameraNode.View;
      Matrix projection = cameraNode.Camera.Projection;

      // The flares are positioned on a line from the origin through the center of 
      // the screen.
      var viewport = graphicsDevice.Viewport;
      Vector2F screenCenter = new Vector2F(viewport.Width / 2.0f, viewport.Height / 2.0f);

      if (_transformParameter != null)
      {
        // ----- Original:
        // Matrix matrix = (Matrix)(Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1)
        //                 * Matrix.CreateTranslation(-0.5f, -0.5f, 0)); // Half-pixel offset (only for Direct3D 9).
        // ----- Inlined:
        Matrix matrix = new Matrix();
        float oneOverW = 1.0f / viewport.Width;
        float oneOverH = 1.0f / viewport.Height;
        matrix.M11 = oneOverW * 2f;
        matrix.M22 = -oneOverH * 2f;
        matrix.M33 = -1f;
        matrix.M44 = 1f;

        matrix.M41 = -1f;
        matrix.M42 = 1f;
#else
        // Direct3D 9: half-pixel offset
        matrix.M41 = -oneOverW - 1f;
        matrix.M42 = oneOverH + 1f;


        _transformParameter.SetValue(matrix);
      }

      // Update SceneNode.LastFrame for all visible nodes.
      int frame = context.Frame;
      cameraNode.LastFrame = frame;

      // Choose current effect technique: Linear vs. Gamma-corrected Writes.
      if (_effect != null)
        _effect.CurrentTechnique = context.IsHdrEnabled() ? _techniqueLinear : _techniqueGamma;

      _spriteBatch.Begin(SpriteSortMode.Texture, BlendState.Additive, null, null, null, _effect);
      for (int i = 0; i < numberOfNodes; i++)
      {
        var node = nodes[i] as LensFlareNode;
        if (node == null)
          continue;

        var lensFlare = node.LensFlare;
        float size, intensity;
        if (hiDef)
        {
          // HiDef profile
          object dummy;
          cameraNode.ViewDependentData.TryGetValue(node, out dummy);
          var renderData = dummy as OcclusionData;
          if (renderData == null || renderData.VisiblePixels == 0)
            continue;

          lensFlare.OnGetSizeAndIntensity(node, context, renderData.VisiblePixels, renderData.TotalPixels, out size, out intensity);
        }
        else
        {
          // Reach profile
          lensFlare.OnGetSizeAndIntensity(node, context, 0, 0, out size, out intensity);
        }

        if (size <= 0 || intensity < MinIntensity)
          continue;

        // LensFlareNode is visible in current frame.
        node.LastFrame = frame;

        // Project position to screen space.
        Vector2F screenPosition;
        if (lensFlare.IsDirectional)
        {
          // ----- Directional lights
          Vector3 lightDirectionWorld = -node.PoseWorld.Orientation.GetColumn(2);  // 3rd column vector (negated)
          Vector3 lightDirectionView = cameraPose.ToLocalDirection(lightDirectionWorld);

          // In Reach profile check light direction for visibility.
          // (In HiDef profile this check is done UpdateOcclusion().)
          if (!hiDef && lightDirectionView.Z < 0)
          {
            // Light comes from behind camera.
            continue;
          }

          Vector3 position = viewport.ProjectToViewport(-lightDirectionView, projection);
          screenPosition = new Vector2F(position.X, position.Y);
        }
        else
        {
          // ----- Local lights
          Vector3 position = node.PoseWorld.Position;

          // In Reach profile check light direction for visibility.
          // (In HiDef profile this check is done UpdateOcclusion().)
          if (!hiDef)
          {
            Vector3 cameraToNode = position - cameraPose.Position;
            float distance = Vector3.Dot(cameraToNode, cameraForward);
            if (distance < cameraNode.Camera.Projection.Near)
            {
              // Light is behind near plane.
              continue;
            }
          }

          position = viewport.ProjectToViewport(position, projection * view);
          screenPosition = new Vector2F(position.X, position.Y);
        }

        Vector2F flareVector = screenCenter - screenPosition;
        foreach (var flare in lensFlare.Elements)
        {
          if (flare == null)
            continue;

          var packedTexture = flare.Texture;
          if (packedTexture == null)
            continue;

          // Position the flare on a line from the lens flare origin through the 
          // screen center.
          Vector2F position = screenPosition + flareVector * flare.Distance;

          // The intensity controls the alpha value.
          Vector4 color = flare.Color.ToVector4();
          color.W *= intensity;

          // Get texture.
          Texture2D textureAtlas = packedTexture.TextureAtlas;
          Vector2F textureAtlasSize = new Vector2F(textureAtlas.Width, textureAtlas.Height);
          Vector2F textureOffset = packedTexture.Offset * textureAtlasSize;
          Vector2F textureSize = packedTexture.Scale * textureAtlasSize;
          Rectangle sourceRectangle = new Rectangle((int)textureOffset.X, (int)textureOffset.Y, (int)textureSize.X, (int)textureSize.Y);

          // The image rotates around its origin (= reference point) - usually the
          // center of the image.
          Vector2F origin = textureSize * flare.Origin;
          float rotation = flare.Rotation;
          Vector2F direction = flareVector;
          if (Numeric.IsNaN(rotation) && direction.TryNormalize())
          {
            // NaN = automatic rotation:
            // Determine angle between direction and reference vector (0, 1):
            // From http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/issues/index.htm:
            // rotation = atan2(v2.y,v2.x) - atan2(v1.y,v1.x)
            //          = atan2(v2.y,v2.x) - atan2(1,0)
            //          = atan2(v2.y,v2.x) - π/2
            rotation = (float)Math.Atan2(direction.Y, direction.X) - ConstantsF.PiOver2;
          }

          Vector2F scale = size * viewport.Height * flare.Scale / textureSize.Y;

          // Render flare using additive blending.
          _spriteBatch.Draw(textureAtlas, (Vector2)position, sourceRectangle, new Color(color),
                            rotation, (Vector2)origin, (Vector2)scale, SpriteEffects.None, 0);
        }
      }

      _spriteBatch.End();
      savedRenderState.Restore();
    }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              var graphicsDevice = _effect.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.DepthStencilState = DepthStencilState.None;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;

              // Set camera properties.
              var cameraNode = context.CameraNode;
              var cameraPose = cameraNode.PoseWorld;
              Matrix viewInverse = cameraPose;
              _parameterViewInverse.SetValue(viewInverse);
              _parameterGBuffer0.SetValue(context.GBuffer0);

              Viewport viewport = context.Viewport;
              _parameterParameters0.SetValue(new Vector2(viewport.Width, viewport.Height));

              // Set jitter map.
              if (_jitterMap == null)
            _jitterMap = NoiseHelper.GetGrainTexture(context.GraphicsService, NoiseHelper.DefaultJitterMapWidth);

              _parameterJitterMap.SetValue(_jitterMap);

              float cameraFar = context.CameraNode.Camera.Projection.Far;

              for (int i = 0; i < numberOfNodes; i++)
              {
            var lightNode = nodes[i] as LightNode;
            if (lightNode == null)
              continue;

            var shadow = lightNode.Shadow as CascadedShadow;
            if (shadow == null)
              continue;

            if (shadow.ShadowMap == null || shadow.ShadowMask == null)
              continue;

            // The effect must only render in a specific channel.
            // Do not change blend state if the correct write channels is already set, e.g. if this
            // shadow is part of a CompositeShadow, the correct blend state is already set.
            if ((int)graphicsDevice.BlendState.ColorWriteChannels != (1 << shadow.ShadowMaskChannel))
              graphicsDevice.BlendState = GraphicsHelper.BlendStateWriteSingleChannel[shadow.ShadowMaskChannel];

            _parameterParameters1.SetValue(new Vector4(
              shadow.FadeOutRange,
              shadow.Distances[shadow.NumberOfCascades - 1],
              shadow.VisualizeCascades ? 1 : 0,
              shadow.ShadowFog));

            float filterRadius = shadow.FilterRadius;

            // If we use a subset of the Poisson kernel, we have to normalize the scale.
            int numberOfSamples = Math.Min(shadow.NumberOfSamples, StandardShadowMaskRenderer.PoissonKernel.Length);

            // Not all shader passes support cascade visualization. Use a similar pass instead.
            if (shadow.VisualizeCascades)
            {
              if (numberOfSamples < 0)
              {
            numberOfSamples = 4;
              }
              else if (numberOfSamples == 0)
              {
            numberOfSamples = 1;
            filterRadius = 0;
              }
            }

            // The best dithered CSM supports max 22 samples.
            if (shadow.CascadeSelection == ShadowCascadeSelection.BestDithered && numberOfSamples > 22)
              numberOfSamples = 22;

            if (numberOfSamples > 0)
              filterRadius /= StandardShadowMaskRenderer.PoissonKernel[numberOfSamples - 1].Length();

            _parameterParameters2.SetValue(new Vector4(
              shadow.ShadowMap.Width,
              shadow.ShadowMap.Height,
              filterRadius,
              // The StandardShadow.JitterResolution is the number of texels per world unit.
              // In the shader the parameter JitterResolution contains the division by the jitter map size.
              shadow.JitterResolution / _jitterMap.Width));

            // Split distances.
            if (_parameterDistances != null)
            {
              // Set not used entries to large values.
              Vector4F distances = shadow.Distances;
              for (int j = shadow.NumberOfCascades; j < 4; j++)
            distances[j] = 10 * cameraFar;

              _parameterDistances.SetValue((Vector4)distances);
            }

            Debug.Assert(shadow.ViewProjections.Length == 4);
            for (int j = 0; j < _matrices.Length; j++)
              _matrices[j] = viewInverse * shadow.ViewProjections[j];

            _parameterShadowMatrices.SetValue(_matrices);

            _parameterDepthBias.SetValue((Vector4)shadow.EffectiveDepthBias);
            _parameterNormalOffset.SetValue((Vector4)shadow.EffectiveNormalOffset);

            Vector3F lightBackwardWorld = lightNode.PoseWorld.Orientation.GetColumn(2);
            _parameterLightDirection.SetValue((Vector3)cameraPose.ToLocalDirection(lightBackwardWorld));
            _parameterNumberOfCascades.SetValue(shadow.NumberOfCascades);
            _parameterShadowMap.SetValue(shadow.ShadowMap);

            var rectangle = GraphicsHelper.GetViewportRectangle(cameraNode, viewport, lightNode);
            Vector2F texCoordTopLeft = new Vector2F(rectangle.Left / (float)viewport.Width, rectangle.Top / (float)viewport.Height);
            Vector2F texCoordBottomRight = new Vector2F(rectangle.Right / (float)viewport.Width, rectangle.Bottom / (float)viewport.Height);
            GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, texCoordTopLeft, texCoordBottomRight, _frustumFarCorners);
            _parameterFrustumCorners.SetValue(_frustumFarCorners);

            var pass = GetPass(numberOfSamples, shadow.CascadeSelection, shadow.VisualizeCascades);

            if (numberOfSamples > 0)
            {
              if (_lastNumberOfSamples != numberOfSamples)
              {
            // Create an array with the first n samples and the rest set to 0.
            _lastNumberOfSamples = numberOfSamples;
            for (int j = 0; j < numberOfSamples; j++)
            {
              _samples[j].Y = StandardShadowMaskRenderer.PoissonKernel[j].Y;
              _samples[j].X = StandardShadowMaskRenderer.PoissonKernel[j].X;
              _samples[j].Z = 1.0f / numberOfSamples;
            }

            // Set the rest to zero.
            for (int j = numberOfSamples; j < _samples.Length; j++)
              _samples[j] = Vector3.Zero;

            _parameterSamples.SetValue(_samples);
              }
              else if (i == 0)
              {
            // Apply offsets in the first loop.
            _parameterSamples.SetValue(_samples);
              }
            }

            pass.Apply();

            graphicsDevice.DrawQuad(rectangle);
              }

              _parameterGBuffer0.SetValue((Texture2D)null);
              _parameterJitterMap.SetValue((Texture2D)null);
              _parameterShadowMap.SetValue((Texture2D)null);
              savedRenderState.Restore();
        }
Example #46
0
        // Perform FFTs.
        // 4 complex input images: source0.xy, source0.zw, source1.xy, source1.zw
        // 2 targets: target0 = displacement map, target1 = normal map using Color format.
        public void Process(RenderContext context, bool forward, Texture2D source0, Texture2D source1, RenderTarget2D target0, RenderTarget2D target1, float choppiness)
        {
            if (context == null)
            throw new ArgumentNullException("context");
              if (source0 == null)
            throw new ArgumentNullException("source0");
              if (source1 == null)
            throw new ArgumentNullException("source1");

              if (forward)
              {
            // For forward FFT, uncomment the LastPassScale stuff!
            throw new NotImplementedException("Forward FFT not implemented.");
              }

              var graphicsService = context.GraphicsService;
              var graphicsDevice = graphicsService.GraphicsDevice;
              var renderTargetPool = graphicsService.RenderTargetPool;

              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              graphicsDevice.BlendState = BlendState.Opaque;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.DepthStencilState = DepthStencilState.None;

              int size = source0.Width;
              _parameterSize.SetValue((float)size);

              _parameterChoppiness.SetValue(choppiness);

              int numberOfButterflyPasses = (int)MathHelper.Log2GreaterOrEqual((uint)source0.Width);
              // ReSharper disable once ConditionIsAlwaysTrueOrFalse
              _parameterButterflyTexture.SetValue(GetButterflyTexture(forward, numberOfButterflyPasses));

              var format = new RenderTargetFormat(size, size, false, source0.Format, DepthFormat.None);
              var tempPing0 = renderTargetPool.Obtain2D(format);
              var tempPing1 = renderTargetPool.Obtain2D(format);
              var tempPong0 = renderTargetPool.Obtain2D(format);
              var tempPong1 = renderTargetPool.Obtain2D(format);

              //_parameterIsLastPass.SetValue(false);

              // Perform horizontal and vertical FFT pass.
              for (int i = 0; i < 2; i++)
              {
            //_parameterLastPassScale.SetValue(1);

            // Perform butterfly passes. We ping-pong between two temp targets.
            for (int pass = 0; pass < numberOfButterflyPasses; pass++)
            {
              _parameterButterflyIndex.SetValue(0.5f / numberOfButterflyPasses + (float)pass / numberOfButterflyPasses);

              if (i == 0 && pass == 0)
              {
            // First pass.
            _renderTargetBindings[0] = new RenderTargetBinding(tempPing0);
            _renderTargetBindings[1] = new RenderTargetBinding(tempPing1);
            graphicsDevice.SetRenderTargets(_renderTargetBindings);
            _parameterSourceTexture0.SetValue(source0);
            _parameterSourceTexture1.SetValue(source1);
              }
              else if (i == 1 && pass == numberOfButterflyPasses - 1)
              {
            // Last pass.
            // We have explicit shader passes for the last FFT pass.
            break;

            //_parameterIsLastPass.SetValue(true);
            //if (forward)
            //  _parameterLastPassScale.SetValue(1.0f / size / size);

            //if (_renderTargetBindings[0].RenderTarget == tempPing0)
            //{
            //  _renderTargetBindings[0] = new RenderTargetBinding(target0);
            //  _renderTargetBindings[1] = new RenderTargetBinding(target1);
            //  graphicsDevice.SetRenderTargets(_renderTargetBindings);
            //  _parameterSourceTexture0.SetValue(tempPing0);
            //  _parameterSourceTexture1.SetValue(tempPing1);
            //}
            //else
            //{
            //  _renderTargetBindings[0] = new RenderTargetBinding(target0);
            //  _renderTargetBindings[1] = new RenderTargetBinding(target1);
            //  graphicsDevice.SetRenderTargets(_renderTargetBindings);
            //  _parameterSourceTexture0.SetValue(tempPong0);
            //  _parameterSourceTexture1.SetValue(tempPong1);
            //}
              }
              else
              {
            // Intermediate pass.
            if (_renderTargetBindings[0].RenderTarget == tempPing0)
            {
              _renderTargetBindings[0] = new RenderTargetBinding(tempPong0);
              _renderTargetBindings[1] = new RenderTargetBinding(tempPong1);
              graphicsDevice.SetRenderTargets(_renderTargetBindings);
              _parameterSourceTexture0.SetValue(tempPing0);
              _parameterSourceTexture1.SetValue(tempPing1);
            }
            else
            {
              _renderTargetBindings[0] = new RenderTargetBinding(tempPing0);
              _renderTargetBindings[1] = new RenderTargetBinding(tempPing1);
              graphicsDevice.SetRenderTargets(_renderTargetBindings);
              _parameterSourceTexture0.SetValue(tempPong0);
              _parameterSourceTexture1.SetValue(tempPong1);
            }
              }

              if (i == 0)
            _passFftHorizontal.Apply();
              else
            _passFftVertical.Apply();

              graphicsDevice.DrawFullScreenQuad();
            }
              }

              // Perform final vertical FFT passes. We have to perform them separately
              // because displacement map and normal map usually have different bit depth.
              // Final pass for displacement.
              graphicsDevice.SetRenderTarget(target0);
              if (_renderTargetBindings[1].RenderTarget == tempPing1)
            _parameterSourceTexture0.SetValue(tempPing0);
              else
            _parameterSourceTexture0.SetValue(tempPong0);

              _passFftDisplacement.Apply();
              graphicsDevice.DrawFullScreenQuad();

              // Final pass for normals.
              graphicsDevice.SetRenderTarget(target1);
              if (_renderTargetBindings[1].RenderTarget == tempPing1)
            _parameterSourceTexture0.SetValue(tempPing1);
              else
            _parameterSourceTexture0.SetValue(tempPong1);

              _passFftNormal.Apply();
              graphicsDevice.DrawFullScreenQuad();

              // Clean up.
              _renderTargetBindings[0] = default(RenderTargetBinding);
              _renderTargetBindings[1] = default(RenderTargetBinding);
              _parameterButterflyTexture.SetValue((Texture2D)null);
              _parameterSourceTexture0.SetValue((Texture2D)null);
              _parameterSourceTexture1.SetValue((Texture2D)null);

              renderTargetPool.Recycle(tempPing0);
              renderTargetPool.Recycle(tempPing1);
              renderTargetPool.Recycle(tempPong0);
              renderTargetPool.Recycle(tempPong1);

              savedRenderState.Restore();

              // Reset the texture stages. If a floating point texture is set, we get exceptions
              // when a sampler with bilinear filtering is set.
            #if !MONOGAME
              graphicsDevice.ResetTextures();
            #endif
        }
Example #47
0
        private void RenderReach(SkyboxNode node, RenderContext context)
        {
            var graphicsDevice = context.GraphicsService.GraphicsDevice;

            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.RasterizerState   = RasterizerState.CullCounterClockwise;
            graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            graphicsDevice.BlendState        = node.EnableAlphaBlending ? BlendState.AlphaBlend : BlendState.Opaque;
            graphicsDevice.SamplerStates[0]  = SamplerState.LinearClamp;

            // Change viewport to render all pixels at max z.
            var originalViewport = graphicsDevice.Viewport;
            var viewport         = originalViewport;

            viewport.MinDepth       = viewport.MaxDepth;
            graphicsDevice.Viewport = viewport;

            var cameraNode = context.CameraNode;
            var view       = cameraNode.View;

            view.Translation = Vector3.Zero;
            var projection = cameraNode.Camera.Projection;

            var basicEffect = (BasicEffect)_effect;

            basicEffect.View         = (Matrix)view;
            basicEffect.Projection   = projection;
            basicEffect.DiffuseColor = (Vector3)node.Color;
            basicEffect.Alpha        = node.EnableAlphaBlending ? node.Alpha : 1;

            // Scale skybox such that it lies within view frustum:
            //   distance of a skybox corner = √3
            //   √3 * scale = far
            //   => scale = far / √3
            // (Note: If  near > far / √3  then the skybox will be clipped.)
            float scale = projection.Far * 0.577f;

            var orientation = node.PoseWorld.Orientation;

            // Positive X
            basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.PositiveX);
            basicEffect.World   = (Matrix) new Matrix(orientation * scale, Vector3.Zero);
            basicEffect.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

            // Negative X
            // transform = scale * rotY(180°)
            var transform = new Matrix(-scale, 0, 0, 0, scale, 0, 0, 0, -scale);

            basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.NegativeX);
            basicEffect.World   = (Matrix) new Matrix(orientation * transform, Vector3.Zero);
            basicEffect.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

            // Positive Y
            // transform = scale * rotX(90°) * rotY(90°)
            transform           = new Matrix(0, 0, scale, scale, 0, 0, 0, scale, 0);
            basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.PositiveY);
            basicEffect.World   = (Matrix) new Matrix(orientation * transform, Vector3.Zero);
            basicEffect.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

            // Negative Y
            // transform = scale * rotX(-90°) * rotY(90°)
            transform           = new Matrix(0, 0, scale, -scale, 0, 0, 0, -scale, 0);
            basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.NegativeY);
            basicEffect.World   = (Matrix) new Matrix(orientation * transform, Vector3.Zero);
            basicEffect.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

            // Cube maps are left-handed, where as the world is right-handed!

            // Positive Z (= negative Z in world space)
            // transform = scale * rotY(90°)
            transform           = new Matrix(0, 0, scale, 0, scale, 0, -scale, 0, 0);
            basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.PositiveZ);
            basicEffect.World   = (Matrix) new Matrix(orientation * transform, Vector3.Zero);
            basicEffect.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

            // Negative Z (= positive Z in world space)
            // transform = scale * rotY(-90°)
            transform           = new Matrix(0, 0, -scale, 0, scale, 0, scale, 0, 0);
            basicEffect.Texture = GetTexture2D(graphicsDevice, node.Texture, CubeMapFace.NegativeZ);
            basicEffect.World   = (Matrix) new Matrix(orientation * transform, Vector3.Zero);
            basicEffect.CurrentTechnique.Passes[0].Apply();
            graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _faceVertices, 0, 2);

            graphicsDevice.Viewport = originalViewport;
            savedRenderState.Restore();
        }
Example #48
0
        internal void ProcessInternal(RenderContext context)
        {
            Debug.Assert(Enabled, "PostProcessor.ProcessInternal should only be called when the post-processor is enabled.");

              var graphicsDevice = GraphicsService.GraphicsDevice;

              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              // Set render states. The blend state must be set by the user!
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.DepthStencilState = DepthStencilState.None;

              // Preform post-processing.
              OnProcess(context);

              savedRenderState.Restore();

              // Reset the texture stages. If a floating point texture is set, we get exceptions
            // when a sampler with bilinear filtering is set.
            #if !MONOGAME
              graphicsDevice.ResetTextures();
            #endif
        }
    public void UpdateOcclusion(IList<SceneNode> nodes, RenderContext context)
    {
      // Measures the visibility of the light source by drawing a screen-aligned quad
      // using an occlusion query. The query result is used in the next frame.

      ThrowIfDisposed();

      if (nodes == null)
        throw new ArgumentNullException("nodes");
      if (context == null)
        throw new ArgumentNullException("context");

      context.Validate(_basicEffect);
      context.ThrowIfCameraMissing();

      // Occlusion queries require HiDef profile.
      var graphicsDevice = context.GraphicsService.GraphicsDevice;
      if (graphicsDevice.GraphicsProfile == GraphicsProfile.Reach)
        return;

      int numberOfNodes = nodes.Count;
      if (nodes.Count == 0)
        return;

      // Camera properties
      var cameraNode = context.CameraNode;
      var cameraPose = cameraNode.PoseWorld;
      Vector3 cameraRight = cameraPose.Orientation.GetColumn(0);    // 1st column vector
      Vector3 cameraUp = cameraPose.Orientation.GetColumn(1);       // 2nd column vector
      Vector3 cameraForward = -cameraPose.Orientation.GetColumn(2); // 3rd column vector (negated)
      Matrix view = cameraNode.View;
      Matrix projection = cameraNode.Camera.Projection;
      bool isOrthographic = (projection.M33 != 0);

      // The following factors are used to estimate the size of a quad in screen space.
      // (The equation is described in GraphicsHelper.GetScreenSize().)
      float xScale = Math.Abs(projection.M00 / 2);
      float yScale = Math.Abs(projection.M11 / 2);

      var viewport = graphicsDevice.Viewport;

      // Lens flares of directional lights are rendered directly on the screen.
      // --> Set up projection transformation for rendering in screen space.
      var orthographicProjection = Matrix.CreateOrthographicOffCenter(0, viewport.Width, viewport.Height, 0, 0, 1);

      // Set render states for rendering occlusion query geometry (quad).
      var savedRenderState = new RenderStateSnapshot(graphicsDevice);
      graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite;
      graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
      graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;

      for (int i = 0; i < numberOfNodes; i++)
      {
        var node = nodes[i] as LensFlareNode;
        if (node == null)
          continue;

        object dummy;
        cameraNode.ViewDependentData.TryGetValue(node, out dummy);
        var renderData = dummy as OcclusionData;
        if (renderData == null)
        {
          renderData = new OcclusionData();
          cameraNode.ViewDependentData[node] = renderData;
        }

        if (renderData.OcclusionQuery != null)
        {
          // Wait until previous occlusion query has completed.
          if (!renderData.OcclusionQuery.IsComplete)
            continue;

          // ----- Read result of previous occlusion query.
          int visiblePixels = renderData.OcclusionQuery.PixelCount;

          // OcclusionData.TotalPixels is only an approximation.
          // --> Clamp pixel count to [0, TotalPixels].
          if (visiblePixels > renderData.TotalPixels)
            visiblePixels = renderData.TotalPixels;

          renderData.VisiblePixels = visiblePixels;
        }

        // ----- Run new occlusion query.
        var lensFlare = node.LensFlare;

        // The user can disable the lens flare by setting LensFlare.Intensity to 0.
        float intensity = node.Intensity * lensFlare.Intensity;
        if (intensity < MinIntensity)
        {
          renderData.VisiblePixels = 0;
          continue;
        }

        float querySize;
        if (lensFlare.IsDirectional)
        {
          // ----- Directional lights

          // Ignore directional lights if camera has orthographic projection.
          // (The light source is infinitely far way and the camera frustum has only 
          // limited width and height. It is very unlikely that camera catches the 
          // directional light.
          if (isOrthographic)
          {
            renderData.VisiblePixels = 0;
            continue;
          }

          // Directional lights are positioned at infinite distance and are not affected
          // by the position of the camera.
          Vector3 lightDirectionWorld = -node.PoseWorld.Orientation.GetColumn(2);  // 3rd column vector (negated)
          Vector3 lightDirectionView = cameraPose.ToLocalDirection(lightDirectionWorld);
          if (lightDirectionView.Z < 0)
          {
            // Light comes from behind camera.
            renderData.VisiblePixels = 0;
            continue;
          }

          // Project position to viewport.
          Vector3 screenPosition = viewport.ProjectToViewport(-lightDirectionView, projection);

          // LensFlare.QuerySize is the size relative to viewport.
          querySize = lensFlare.QuerySize * viewport.Height;
          renderData.TotalPixels = (int)(querySize * querySize);
          if (renderData.TotalPixels < MinPixelSize)
          {
            // Cull small light sources.
            renderData.VisiblePixels = 0;
            continue;
          }

          // Draw quad in screen space.
          querySize /= 2;
          _queryGeometry[0].Position = new Vector3(screenPosition.X - querySize, screenPosition.Y - querySize, -1);
          _queryGeometry[1].Position = new Vector3(screenPosition.X + querySize, screenPosition.Y - querySize, -1);
          _queryGeometry[2].Position = new Vector3(screenPosition.X - querySize, screenPosition.Y + querySize, -1);
          _queryGeometry[3].Position = new Vector3(screenPosition.X + querySize, screenPosition.Y + querySize, -1);

          _basicEffect.World = Matrix.Identity;
          _basicEffect.View = Matrix.Identity;
          _basicEffect.Projection = orthographicProjection;
          _basicEffect.CurrentTechnique.Passes[0].Apply();

          if (renderData.OcclusionQuery == null)
            renderData.OcclusionQuery = new OcclusionQuery(graphicsDevice);

          renderData.OcclusionQuery.Begin();
          graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _queryGeometry, 0, 2);
          renderData.OcclusionQuery.End();
        }
        else
        {
          // ----- Local lights

          // Determine planar distance to camera.
          Vector3 position = node.PoseWorld.Position;
          Vector3 cameraToNode = position - cameraPose.Position;
          float distance = Vector3.Dot(cameraToNode, cameraForward);
          if (distance < cameraNode.Camera.Projection.Near)
          {
            // Light is behind near plane.
            renderData.VisiblePixels = 0;
            continue;
          }

          Debug.Assert(
            node.ScaleWorld.X > 0 && node.ScaleWorld.Y > 0 && node.ScaleWorld.Z > 0,
            "Assuming that all scale factors are positive.");

          // LensFlare.QuerySize is the size in world space.
          querySize = node.ScaleWorld.LargestComponent * node.LensFlare.QuerySize;

          // Estimate screen space size of query geometry.
          float screenSizeX = viewport.Width * querySize * xScale;
          float screenSizeY = viewport.Height * querySize * yScale;
          if (!isOrthographic)
          {
            float oneOverDistance = 1 / distance;
            screenSizeX *= oneOverDistance;
            screenSizeY *= oneOverDistance;
          }

          renderData.TotalPixels = (int)(screenSizeX * screenSizeY);
          if (renderData.TotalPixels < MinPixelSize)
          {
            // Cull small light sources.
            renderData.VisiblePixels = 0;
            continue;
          }

          // Draw screen-aligned quad in world space.
          querySize /= 2;
          Vector3 upVector = querySize * cameraUp;
          Vector3 rightVector = querySize * cameraRight;

          // Offset quad by half its size towards the camera. Otherwise, the geometry 
          // of the light source could obstruct the query geometry.
          position -= querySize * cameraToNode.Normalized;
          _queryGeometry[0].Position = (Vector3)(position - rightVector - upVector);
          _queryGeometry[1].Position = (Vector3)(position - rightVector + upVector);
          _queryGeometry[2].Position = (Vector3)(position + rightVector - upVector);
          _queryGeometry[3].Position = (Vector3)(position + rightVector + upVector);

          _basicEffect.World = Matrix.Identity;
          _basicEffect.View = (Matrix)view;
          _basicEffect.Projection = (Matrix)projection;
          _basicEffect.CurrentTechnique.Passes[0].Apply();

          if (renderData.OcclusionQuery == null)
            renderData.OcclusionQuery = new OcclusionQuery(graphicsDevice);

          renderData.OcclusionQuery.Begin();
          graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, _queryGeometry, 0, 2);
          renderData.OcclusionQuery.End();
        }
      }

      savedRenderState.Restore();
    }
Example #50
0
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

              if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (nodes.Count == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              var graphicsDevice = context.GraphicsService.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);

              // Camera properties
              var cameraNode = context.CameraNode;
              Matrix view = (Matrix)cameraNode.View;
              Matrix projection = cameraNode.Camera.Projection;
              Matrix viewProjection = view * projection;

              // Update SceneNode.LastFrame for all visible nodes.
              int frame = context.Frame;
              cameraNode.LastFrame = frame;

              // Blend additively over any cosmos textures.
              graphicsDevice.RasterizerState = RasterizerState.CullNone;
              graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
              graphicsDevice.BlendState = BlendState.Additive;

              _effectParameterViewportSize.SetValue(new Vector2(context.Viewport.Width, context.Viewport.Height));

              for (int i = 0; i < numberOfNodes; i++)
              {
            var node = nodes[i] as StarfieldNode;
            if (node == null)
              continue;

            // SkyboxNode is visible in current frame.
            node.LastFrame = frame;

            if (node.Stars != null && node.Stars.Count > 0)
            {
              Matrix world = (Matrix)new Matrix44F(node.PoseWorld.Orientation, Vector3F.Zero);
              _effectParameterWorldViewProjection.SetValue(world * viewProjection);

              // In [ZFX] the star luminance of the precomputed star data is scaled with
              // float const viewFactor = tan(fov);
              // float const resolutionFactor = resolution / 1920.0f;
              // float const luminanceScale = 1.0f / (viewFactor * viewFactor) * (resolutionFactor * resolutionFactor);
              // We ignore this here, but we could add this factor to the Intensity parameter.
              _effectParameterIntensity.SetValue((Vector3)node.Color);

              if (context.IsHdrEnabled())
            _effectPassLinear.Apply();
              else
            _effectPassGamma.Apply();

              var mesh = GetStarfieldMesh(node, context);
              mesh.Draw();
            }
              }

              savedRenderState.Restore();
        }
        private void Render(RenderContext context, Vector4F color, Texture2D colorTexture, bool preserveColor)
        {
            if (context == null)
            throw new ArgumentNullException("context");

              context.Validate(_effect);
              context.ThrowIfCameraMissing();
              context.ThrowIfGBuffer0Missing();

              var graphicsDevice = _effect.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateAlways;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;

              if (preserveColor)
            graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite;
              else
            graphicsDevice.BlendState = BlendState.Opaque;

              if (colorTexture != null)
              {
            if (TextureHelper.IsFloatingPointFormat(colorTexture.Format))
              graphicsDevice.SamplerStates[1] = SamplerState.PointClamp;
            else
              graphicsDevice.SamplerStates[1] = SamplerState.LinearClamp;
              }

              var projection = context.CameraNode.Camera.Projection;
              bool isPerspective = projection is PerspectiveProjection;
              float near = projection.Near * NearBias;
              float far = projection.Far * FarBias;
              var biasedProjection = isPerspective
                               ? Matrix44F.CreatePerspectiveOffCenter(
                                 projection.Left, projection.Right,
                                 projection.Bottom, projection.Top,
                                 near, far)
                               : Matrix44F.CreateOrthographicOffCenter(
                                 projection.Left, projection.Right,
                                 projection.Bottom, projection.Top,
                                 near, far);

              var viewport = graphicsDevice.Viewport;
              _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height));
              _parameterProjection.SetValue((Matrix)biasedProjection);
              _parameterCameraFar.SetValue(projection.Far);
              _parameterGBuffer0.SetValue(context.GBuffer0);
              _parameterColor.SetValue((Vector4)color);
              _parameterSourceTexture.SetValue(colorTexture);

              _effect.CurrentTechnique = isPerspective ? _techniquePerspective : _techniqueOrthographic;
              _effect.CurrentTechnique.Passes[(colorTexture == null) ? 0 : 1].Apply();

              graphicsDevice.DrawFullScreenQuad();

              graphicsDevice.ResetTextures();

              savedRenderState.Restore();
        }
Example #52
0
    /// <summary>
    /// Draws the batched primitives.
    /// </summary>
    /// <param name="context">The render context.</param>
    /// <exception cref="ArgumentNullException">
    /// <paramref name="context"/> is <see langword="null"/>.
    /// </exception>
    public void Render(RenderContext context)
    {
      if (context == null)
        throw new ArgumentNullException("context");

      if (Effect == null || _primitives.Count == 0)
        return;

      context.Validate(Effect);
      context.ThrowIfCameraMissing();

      var graphicsService = context.GraphicsService;
      var graphicsDevice = graphicsService.GraphicsDevice;
      var savedRenderState = new RenderStateSnapshot(graphicsDevice);
      if (AutoRasterizerState)
        graphicsDevice.RasterizerState = DrawWireFrame ? GraphicsHelper.RasterizerStateWireFrame : GraphicsHelper.RasterizerStateCullCounterClockwise;

      // Sort primitives if necessary.
      Matrix view = context.CameraNode.View;
      if (SortBackToFront && _usesTransparency)
      {
        // Update depth (distance from camera).
        foreach (var job in _primitives)
        {
          Vector3 position = job.Pose.Position;
          job.Depth = view.TransformPosition(position).Z;
        }

        _primitives.Sort(PrimitiveJobDepthComparer.Instance);
      }

      // Reset the texture stages. If a floating point texture is set, we get exceptions
      // when a sampler with bilinear filtering is set.
      graphicsDevice.ResetTextures();

      Effect.LightingEnabled = !DrawWireFrame;
      Effect.TextureEnabled = false;
      Effect.View = (Matrix)view;
      Effect.Projection = context.CameraNode.Camera.Projection;

      foreach (var job in _primitives)
      {
        Effect.Alpha = job.Color.A / 255.0f;
        Effect.DiffuseColor = job.Color.ToVector3();
        Effect.VertexColorEnabled = false;

        switch (job.Type)
        {
          case PrimitiveJobType.Box:
            RenderBox(graphicsService, job);
            break;
          case PrimitiveJobType.Capsule:
            RenderCapsule(graphicsService, job);
            break;
          case PrimitiveJobType.Cone:
            RenderCone(graphicsService, job);
            break;
          case PrimitiveJobType.Cylinder:
            RenderCylinder(graphicsService, job);
            break;
          case PrimitiveJobType.ViewVolume:
            RenderViewVolume(job, context);
            break;
          case PrimitiveJobType.Sphere:
            RenderSphere(graphicsService, job, context);
            break;
          case PrimitiveJobType.Shape:
            RenderShape(graphicsDevice, job);
            break;
          case PrimitiveJobType.Model:
            RenderModel(graphicsDevice, job);
            break;
          case PrimitiveJobType.Submesh:
            RenderSubmesh(job);
            break;
        }
      }

      if (DrawWireFrame)
      {
        _lineBatch.Render(context);
        _lineBatch.Clear();
      }

      savedRenderState.Restore();
    }
        /// <inheritdoc/>
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (nodes.Count == 0)
            {
                return;
            }

            context.Validate(_effect);
            context.ThrowIfCameraMissing();

            var graphicsDevice   = context.GraphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.BlendState        = BlendState.AlphaBlend;
            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;

            // Camera properties
            var    cameraNode = context.CameraNode;
            Matrix view       = (Matrix) new Matrix(cameraNode.PoseWorld.Orientation.Transposed, new Vector3());

            _parameterView.SetValue(view);
            Matrix projection = cameraNode.Camera.Projection;

            _parameterProjection.SetValue(projection);

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            for (int i = 0; i < numberOfNodes; i++)
            {
                var node = nodes[i] as ScatteringSkyNode;
                if (node == null)
                {
                    continue;
                }

                // ScatteringSkyNode is visible in current frame.
                node.LastFrame = frame;

                _parameterSunDirection.SetValue((Vector3)node.SunDirection);
                _parameterSunIntensity.SetValue((Vector3)(node.SunIntensity * node.SunColor));
                _parameterRadii.SetValue(new Vector4(
                                             node.AtmosphereHeight + node.PlanetRadius, // Atmosphere radius
                                             node.PlanetRadius,                         // Ground radius
                                             node.ObserverAltitude + node.PlanetRadius, // Observer radius
                                             node.ScaleHeight));                        // Absolute Scale height
                _parameterNumberOfSamples.SetValue(node.NumberOfSamples);
                _parameterBetaRayleigh.SetValue((Vector3)node.BetaRayleigh);
                _parameterBetaMie.SetValue((Vector3)node.BetaMie);
                _parameterGMie.SetValue(node.GMie);
                _parameterTransmittance.SetValue(node.Transmittance);

                if (node.BaseHorizonColor.IsNumericallyZero && node.BaseZenithColor.IsNumericallyZero)
                {
                    // No base color.
                    if (context.IsHdrEnabled())
                    {
                        _passLinear.Apply();
                    }
                    else
                    {
                        _passGamma.Apply();
                    }
                }
                else
                {
                    // Add base color.
                    _parameterBaseHorizonColor.SetValue((Vector4) new Vector4(node.BaseHorizonColor, node.BaseColorShift));
                    _parameterBaseZenithColor.SetValue((Vector3)node.BaseZenithColor);

                    if (context.IsHdrEnabled())
                    {
                        _passLinearWithBaseColor.Apply();
                    }
                    else
                    {
                        _passGammaWithBaseColor.Apply();
                    }
                }

                _submesh.Draw();
            }

            savedRenderState.Restore();
        }
Example #54
0
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (nodes.Count == 0)
            {
                return;
            }

            context.Validate(_spriteBatch);
            context.ThrowIfCameraMissing();

            var graphicsDevice   = context.GraphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            // Camera properties
            var       cameraNode     = context.CameraNode;
            Matrix44F viewProjection = cameraNode.Camera.Projection * cameraNode.View;
            var       viewport       = graphicsDevice.Viewport;

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            SpriteSortMode sortMode;

            switch (order)
            {
            case RenderOrder.Default:
                sortMode = SpriteSortMode.Texture;
                break;

            case RenderOrder.FrontToBack:
                sortMode = SpriteSortMode.FrontToBack;
                break;

            case RenderOrder.BackToFront:
                sortMode = SpriteSortMode.BackToFront;
                break;

            case RenderOrder.UserDefined:
            default:
                sortMode = SpriteSortMode.Deferred;
                break;
            }

            _spriteBatch.Begin(sortMode, graphicsDevice.BlendState, null, graphicsDevice.DepthStencilState, null);

            for (int i = 0; i < numberOfNodes; i++)
            {
                var node = nodes[i] as SpriteNode;
                if (node == null)
                {
                    continue;
                }

                // SpriteNode is visible in current frame.
                node.LastFrame = frame;

                // Position, size, and origin in pixels.
                Vector3F position = new Vector3F();
                Vector2  size     = new Vector2();
                Vector2  origin   = new Vector2();

                var bitmapSprite = node.Sprite as ImageSprite;
                if (bitmapSprite != null)
                {
                    var packedTexture = bitmapSprite.Texture;
                    if (packedTexture != null)
                    {
                        // Project into viewport and snap to pixels.
                        position   = viewport.ProjectToViewport(node.PoseWorld.Position, viewProjection);
                        position.X = (int)(position.X + 0.5f);
                        position.Y = (int)(position.Y + 0.5f);

                        // Get source rectangle (pixel bounds).
                        var sourceRectangle = packedTexture.GetBounds(node.AnimationTime);
                        size = new Vector2(sourceRectangle.Width, sourceRectangle.Height);

                        // Premultiply color.
                        Vector3F color3F = node.Color;
                        float    alpha   = node.Alpha;
                        Color    color   = new Color(color3F.X * alpha, color3F.Y * alpha, color3F.Z * alpha, alpha);

                        // Get absolute origin (relative to pixel bounds).
                        origin = (Vector2)node.Origin * size;

                        // Draw using SpriteBatch.
                        _spriteBatch.Draw(
                            packedTexture.TextureAtlas, new Vector2(position.X, position.Y), sourceRectangle,
                            color, node.Rotation, origin, (Vector2)node.Scale, SpriteEffects.None, position.Z);
                    }
                }
                else
                {
                    var textSprite = node.Sprite as TextSprite;
                    if (textSprite != null)
                    {
                        var font = textSprite.Font ?? _spriteFont;
                        if (font != null)
                        {
                            // Text can be a string or StringBuilder.
                            var text = textSprite.Text as string;
                            if (text != null)
                            {
                                if (text.Length > 0)
                                {
                                    // Project into viewport and snap to pixels.
                                    position   = viewport.ProjectToViewport(node.PoseWorld.Position, viewProjection);
                                    position.X = (int)(position.X + 0.5f);
                                    position.Y = (int)(position.Y + 0.5f);

                                    // Premultiply color.
                                    Vector3F color3F = node.Color;
                                    float    alpha   = node.Alpha;
                                    Color    color   = new Color(color3F.X * alpha, color3F.Y * alpha, color3F.Z * alpha, alpha);

                                    // Get absolute origin (relative to pixel bounds).
                                    size   = font.MeasureString(text);
                                    origin = (Vector2)node.Origin * size;

                                    // Draw using SpriteBatch.
                                    _spriteBatch.DrawString(
                                        font, text, new Vector2(position.X, position.Y),
                                        color, node.Rotation, origin, (Vector2)node.Scale,
                                        SpriteEffects.None, position.Z);
                                }
                            }
                            else
                            {
                                var stringBuilder = textSprite.Text as StringBuilder;
                                if (stringBuilder != null && stringBuilder.Length > 0)
                                {
                                    // Project into viewport and snap to pixels.
                                    position   = viewport.ProjectToViewport(node.PoseWorld.Position, viewProjection);
                                    position.X = (int)(position.X + 0.5f);
                                    position.Y = (int)(position.Y + 0.5f);

                                    // Premultiply color.
                                    Vector3F color3F = node.Color;
                                    float    alpha   = node.Alpha;
                                    Color    color   = new Color(color3F.X * alpha, color3F.Y * alpha, color3F.Z * alpha, alpha);

                                    // Get absolute origin (relative to pixel bounds).
                                    size   = font.MeasureString(stringBuilder);
                                    origin = (Vector2)node.Origin * size;

                                    // Draw using SpriteBatch.
                                    _spriteBatch.DrawString(
                                        font, stringBuilder, new Vector2(position.X, position.Y),
                                        color, node.Rotation, origin, (Vector2)node.Scale,
                                        SpriteEffects.None, position.Z);
                                }
                            }
                        }
                    }
                }

                // Store bounds an depth for hit tests.
                node.LastBounds = new Rectangle(
                    (int)(position.X - origin.X),
                    (int)(position.Y - origin.Y),
                    (int)(size.X * node.Scale.X),
                    (int)(size.Y * node.Scale.Y));

                node.LastDepth = position.Z;
            }

            _spriteBatch.End();
            savedRenderState.Restore();
        }
        internal override void ProcessJobs(RenderContext context, RenderOrder order)
        {
            var graphicsDevice   = _graphicsService.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);
            var target           = context.RenderTarget;
            var viewport         = context.Viewport;

            Debug.Assert(_shadowMasks.Length > 0);
            Debug.Assert(_shadowMasks[0] != null);

            RenderTarget2D lowResTarget = null;

            if (UseHalfResolution && Numeric.IsGreater(UpsampleDepthSensitivity, 0))
            {
                // Half-res rendering with upsampling.
                var format = new RenderTargetFormat(_shadowMasks[0]);
                format.Width  /= 2;
                format.Height /= 2;
                lowResTarget   = _graphicsService.RenderTargetPool.Obtain2D(format);
            }

            int index               = 0;
            var jobs                = Jobs.Array;
            int jobCount            = Jobs.Count;
            int lastShadowMaskIndex = -1;

            while (index < jobCount)
            {
                int shadowMaskIndex = (int)(jobs[index].SortKey >> 16);
                var renderer        = jobs[index].Renderer;

                // Find end of current batch.
                int endIndexExclusive = index + 1;
                while (endIndexExclusive < jobCount)
                {
                    if ((int)(jobs[endIndexExclusive].SortKey >> 16) != lastShadowMaskIndex ||
                        jobs[endIndexExclusive].Renderer != renderer)
                    {
                        break;
                    }

                    endIndexExclusive++;
                }

                // Restore the render state. (The integrated scene node renderers properly
                // restore the render state, but third-party renderers might mess it up.)
                if (index > 0)
                {
                    savedRenderState.Restore();
                }

                if (shadowMaskIndex != lastShadowMaskIndex)
                {
                    // Done with current shadow mask. Apply filter.
                    if (lastShadowMaskIndex >= 0)
                    {
                        PostProcess(context, context.RenderTarget, _shadowMasks[lastShadowMaskIndex]);
                    }

                    // Switch to next shadow mask.
                    lastShadowMaskIndex = shadowMaskIndex;

                    var shadowMask = lowResTarget ?? _shadowMasks[shadowMaskIndex];

                    // Set device render target and clear it to white (= no shadow).
                    graphicsDevice.SetRenderTarget(shadowMask);
                    context.RenderTarget = shadowMask;
                    context.Viewport     = graphicsDevice.Viewport;
                    graphicsDevice.Clear(Color.White);
                }

                // Submit batch to renderer.
                // (Use Accessor to expose current batch as IList<SceneNode>.)
                JobsAccessor.Set(Jobs, index, endIndexExclusive);
                renderer.Render(JobsAccessor, context, order);
                JobsAccessor.Reset();

                index = endIndexExclusive;
            }

            // Done with last shadow mask. Apply filter.
            PostProcess(context, context.RenderTarget, _shadowMasks[lastShadowMaskIndex]);

            savedRenderState.Restore();
            graphicsDevice.ResetTextures();
            graphicsDevice.SetRenderTarget(null);
            context.RenderTarget = target;
            context.Viewport     = viewport;

            _graphicsService.RenderTargetPool.Recycle(lowResTarget);
        }
Example #56
0
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
            if (context.Scene == null)
            {
                throw new ArgumentException("Scene needs to be set in render context.", "context");
            }
            if (context.CameraNode == null)
            {
                throw new ArgumentException("Camera needs to be set in render context.", "context");
            }
            if (!(context.CameraNode.Camera.Projection is PerspectiveProjection))
            {
                throw new ArgumentException("The camera in the render context must use a perspective projection.", "context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            var graphicsDevice = context.GraphicsService.GraphicsDevice;
            int frame          = context.Frame;

            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            var   originalRenderTarget  = context.RenderTarget;
            var   originalViewport      = context.Viewport;
            var   originalCameraNode    = context.CameraNode;
            var   originalLodCameraNode = context.LodCameraNode;
            float originalLodBias       = context.LodBias;
            var   originalReferenceNode = context.ReferenceNode;

            Pose      originalCameraPose        = originalCameraNode.PoseWorld;
            Vector3F  originalCameraPosition    = originalCameraPose.Position;
            Matrix33F originalCameraOrientation = originalCameraPose.Orientation;

            Vector3F right = originalCameraOrientation.GetColumn(0);
            Vector3F up    = originalCameraOrientation.GetColumn(1);
            Vector3F back  = originalCameraOrientation.GetColumn(2);

            try
            {
                // Use foreach instead of for-loop to catch InvalidOperationExceptions in
                // case the collection is modified.
                for (int i = 0; i < numberOfNodes; i++)
                {
                    var node = nodes[i] as PlanarReflectionNode;
                    if (node == null)
                    {
                        continue;
                    }

                    // Update each node only once per frame.
                    if (node.LastFrame == frame)
                    {
                        continue;
                    }

                    node.LastFrame = frame;

                    var texture = node.RenderToTexture.Texture;
                    if (texture == null)
                    {
                        continue;
                    }

                    var renderTarget = texture as RenderTarget2D;
                    if (renderTarget == null)
                    {
                        throw new GraphicsException(
                                  "PlanarReflectionNode.RenderToTexture.Texture is invalid. The texture must be a RenderTarget2D.");
                    }

                    // RenderToTexture instances can be shared. --> Update them only once per frame.
                    if (node.RenderToTexture.LastFrame == frame)
                    {
                        continue;
                    }

                    // Do not render if we look at the back of the reflection plane.
                    Vector3F planeNormal   = node.NormalWorld;
                    Vector3F planePosition = node.PoseWorld.Position;
                    Vector3F planeToCamera = originalCameraPosition - planePosition;
                    if (Vector3F.Dot(planeNormal, planeToCamera) < 0)
                    {
                        continue;
                    }

                    var cameraNode = node.CameraNode;

                    // Reflect camera pose.
                    Pose cameraPose;
                    cameraPose.Position    = planePosition + Reflect(planeToCamera, planeNormal);
                    cameraPose.Orientation = new Matrix33F();
                    cameraPose.Orientation.SetColumn(0, Reflect(right, planeNormal));
                    cameraPose.Orientation.SetColumn(1, -Reflect(up, planeNormal));
                    cameraPose.Orientation.SetColumn(2, Reflect(back, planeNormal));
                    cameraNode.PoseWorld = cameraPose;

                    // The projection of the player camera.
                    var originalProjection = originalCameraNode.Camera.Projection;
                    // The projection of the reflected camera.
                    var projection = (PerspectiveProjection)cameraNode.Camera.Projection;

                    // Choose optimal projection. We get the screen-space bounds of the reflection node.
                    // Then we make the FOV so small that it exactly contains the node.
                    projection.Set(originalProjection);

                    var bounds = GraphicsHelper.GetBounds(cameraNode, node);

                    // Abort if the bounds are empty.
                    if (Numeric.AreEqual(bounds.X, bounds.Z) || Numeric.AreEqual(bounds.Y, bounds.W))
                    {
                        continue;
                    }

                    // Apply FOV scale to bounds.
                    float fovScale = node.FieldOfViewScale;
                    float deltaX   = (bounds.Z - bounds.X) * (fovScale - 1) / 2;
                    bounds.X -= deltaX;
                    bounds.Z += deltaX;
                    float deltaY = (bounds.W - bounds.Y) * (fovScale - 1) / 2;
                    bounds.Y -= deltaY;
                    bounds.W += deltaY;

                    // Update projection to contain only the node bounds.
                    projection.Left   = projection.Left + bounds.X * projection.Width;
                    projection.Right  = projection.Left + bounds.Z * projection.Width;
                    projection.Top    = projection.Top - bounds.Y * projection.Height;
                    projection.Bottom = projection.Top - bounds.W * projection.Height;

                    // Set far clip plane.
                    if (node.Far.HasValue)
                    {
                        projection.Far = node.Far.Value;
                    }

                    // Set near clip plane.
                    Vector3F planeNormalCamera = cameraPose.ToLocalDirection(-node.NormalWorld);
                    Vector3F planePointCamera  = cameraPose.ToLocalPosition(node.PoseWorld.Position);
                    projection.NearClipPlane = new Plane(planeNormalCamera, planePointCamera);

                    context.CameraNode    = cameraNode;
                    context.LodCameraNode = cameraNode;
                    context.LodBias       = node.LodBias ?? originalLodBias;
                    context.ReferenceNode = node;

                    context.RenderTarget = renderTarget;
                    context.Viewport     = new Viewport(0, 0, renderTarget.Width, renderTarget.Height);

                    RenderCallback(context);

                    // Update other properties of RenderToTexture.
                    node.RenderToTexture.LastFrame     = frame;
                    node.RenderToTexture.TextureMatrix = GraphicsHelper.ProjectorBiasMatrix
                                                         * cameraNode.Camera.Projection
                                                         * cameraNode.PoseWorld.Inverse;
                }
            }
            catch (InvalidOperationException exception)
            {
                throw new GraphicsException(
                          "InvalidOperationException was raised in PlanarReflectionRenderer.Render(). "
                          + "This can happen if a SceneQuery instance that is currently in use is modified in the "
                          + "RenderCallback. --> Use different SceneQuery types in the method which calls "
                          + "SceneCaptureRenderer.Render() and in the RenderCallback method.",
                          exception);
            }

            graphicsDevice.SetRenderTarget(null);
            savedRenderState.Restore();

            context.RenderTarget  = originalRenderTarget;
            context.Viewport      = originalViewport;
            context.CameraNode    = originalCameraNode;
            context.LodCameraNode = originalLodCameraNode;
            context.LodBias       = originalLodBias;
            context.ReferenceNode = originalReferenceNode;
        }
        public override void Render(IList<SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            throw new ArgumentNullException("nodes");
              if (context == null)
            throw new ArgumentNullException("context");

              int numberOfNodes = nodes.Count;
              if (numberOfNodes == 0)
            return;

              context.Validate(_effect);
              context.ThrowIfCameraMissing();

              var graphicsDevice = _effect.GraphicsDevice;
              var savedRenderState = new RenderStateSnapshot(graphicsDevice);
              graphicsDevice.DepthStencilState = DepthStencilState.None;
              graphicsDevice.RasterizerState = RasterizerState.CullNone;

              var cameraNode = context.CameraNode;
              _parameterViewInverse.SetValue(cameraNode.PoseWorld);
              _parameterGBuffer0.SetValue(context.GBuffer0);

              Viewport viewport = context.Viewport;
              _parameterParameters0.SetValue(new Vector2(viewport.Width, viewport.Height));

              if (_jitterMap == null)
            _jitterMap = NoiseHelper.GetGrainTexture(context.GraphicsService, NoiseHelper.DefaultJitterMapWidth);

              _parameterJitterMap.SetValue(_jitterMap);

              for (int i = 0; i < numberOfNodes; i++)
              {
            var lightNode = nodes[i] as LightNode;
            if (lightNode == null)
              continue;

            var light = lightNode.Light as PointLight;
            if (light == null)
              return;

            var shadow = lightNode.Shadow as CubeMapShadow;
            if (shadow == null)
              continue;

            if (shadow.ShadowMap == null || shadow.ShadowMask == null)
              continue;

            // The effect must only render in a specific channel.
            // Do not change blend state if the correct write channels is already set, e.g. if this
            // shadow is part of a CompositeShadow, the correct blend state is already set.
            if ((int)graphicsDevice.BlendState.ColorWriteChannels != (1 << shadow.ShadowMaskChannel))
              graphicsDevice.BlendState = GraphicsHelper.BlendStateWriteSingleChannel[shadow.ShadowMaskChannel];

            _parameterParameters1.SetValue(new Vector4(
              shadow.Near,
              light.Range,
              shadow.EffectiveDepthBias,
              shadow.EffectiveNormalOffset));

            // If we use a subset of the Poisson kernel, we have to normalize the scale.
            int numberOfSamples = Math.Min(shadow.NumberOfSamples, StandardShadowMaskRenderer.PoissonKernel.Length);
            float filterRadius = shadow.FilterRadius;
            if (numberOfSamples > 0)
              filterRadius /= StandardShadowMaskRenderer.PoissonKernel[numberOfSamples - 1].Length();

            _parameterParameters2.SetValue(new Vector3(
              shadow.ShadowMap.Size,
              filterRadius,
              // The StandardShadow.JitterResolution is the number of texels per world unit.
              // In the shader the parameter JitterResolution contains the division by the jitter map size.
              shadow.JitterResolution / _jitterMap.Width));

            _parameterLightPosition.SetValue((Vector3)cameraNode.PoseWorld.ToLocalPosition(lightNode.PoseWorld.Position));

            _parameterShadowView.SetValue(lightNode.PoseWorld.Inverse * cameraNode.PoseWorld);
            _parameterShadowMap.SetValue(shadow.ShadowMap);

            var rectangle = GraphicsHelper.GetViewportRectangle(cameraNode, viewport, lightNode);
            Vector2F texCoordTopLeft = new Vector2F(rectangle.Left / (float)viewport.Width, rectangle.Top / (float)viewport.Height);
            Vector2F texCoordBottomRight = new Vector2F(rectangle.Right / (float)viewport.Width, rectangle.Bottom / (float)viewport.Height);
            GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, texCoordTopLeft, texCoordBottomRight, _frustumFarCorners);
            _parameterFrustumCorners.SetValue(_frustumFarCorners);

            var pass = GetPass(numberOfSamples);

            if (numberOfSamples > 0)
            {
              if (_lastNumberOfSamples != numberOfSamples)
              {
            // Create an array with the first n samples and the rest set to 0.
            _lastNumberOfSamples = numberOfSamples;
            for (int j = 0; j < numberOfSamples; j++)
            {
              _samples[j].Y = StandardShadowMaskRenderer.PoissonKernel[j].Y;
              _samples[j].X = StandardShadowMaskRenderer.PoissonKernel[j].X;
              _samples[j].Z = 1.0f / numberOfSamples;
            }

            // Set the rest to zero.
            for (int j = numberOfSamples; j < _samples.Length; j++)
              _samples[j] = Vector3.Zero;

            _parameterSamples.SetValue(_samples);
              }
              else if (i == 0)
              {
            // Apply offsets in the first loop.
            _parameterSamples.SetValue(_samples);
              }
            }

            pass.Apply();

            graphicsDevice.DrawQuad(rectangle);
              }

              _parameterGBuffer0.SetValue((Texture2D)null);
              _parameterJitterMap.SetValue((Texture2D)null);
              _parameterShadowMap.SetValue((Texture2D)null);
              savedRenderState.Restore();
        }
Example #58
0
        private void Render(RenderContext context, Vector4F color, Texture2D colorTexture, bool preserveColor)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            context.Validate(_effect);
            context.ThrowIfCameraMissing();
            context.ThrowIfGBuffer0Missing();

            var graphicsDevice   = _effect.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateAlways;
            graphicsDevice.RasterizerState   = RasterizerState.CullNone;

            if (preserveColor)
            {
                graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite;
            }
            else
            {
                graphicsDevice.BlendState = BlendState.Opaque;
            }

            if (colorTexture != null)
            {
                if (TextureHelper.IsFloatingPointFormat(colorTexture.Format))
                {
                    graphicsDevice.SamplerStates[1] = SamplerState.PointClamp;
                }
                else
                {
                    graphicsDevice.SamplerStates[1] = SamplerState.LinearClamp;
                }
            }

            var   projection       = context.CameraNode.Camera.Projection;
            bool  isPerspective    = projection is PerspectiveProjection;
            float near             = projection.Near * NearBias;
            float far              = projection.Far * FarBias;
            var   biasedProjection = isPerspective
                               ? Matrix44F.CreatePerspectiveOffCenter(
                projection.Left, projection.Right,
                projection.Bottom, projection.Top,
                near, far)
                               : Matrix44F.CreateOrthographicOffCenter(
                projection.Left, projection.Right,
                projection.Bottom, projection.Top,
                near, far);

            var viewport = graphicsDevice.Viewport;

            _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height));
            _parameterProjection.SetValue((Matrix)biasedProjection);
            _parameterCameraFar.SetValue(projection.Far);
            _parameterGBuffer0.SetValue(context.GBuffer0);
            _parameterColor.SetValue((Vector4)color);
            _parameterSourceTexture.SetValue(colorTexture);

            _effect.CurrentTechnique = isPerspective ? _techniquePerspective : _techniqueOrthographic;
            _effect.CurrentTechnique.Passes[(colorTexture == null) ? 0 : 1].Apply();

            graphicsDevice.DrawFullScreenQuad();

            graphicsDevice.ResetTextures();

            savedRenderState.Restore();
        }
Example #59
0
    private void ProcessJobs(RenderContext context, RenderOrder order)
    {
      Effect currentEffect = null;
      EffectEx currentEffectEx = null;
      EffectBinding currentMaterialBinding = null;

      // Set render states for drawing decals.
      var graphicsDevice = context.GraphicsService.GraphicsDevice;
      var savedRenderState = new RenderStateSnapshot(graphicsDevice);

      graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
      graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;

      if (!ClipAtNearPlane)
      {
        // Cache some info for near plane intersection tests.

        var cameraNode = context.CameraNode;
        var cameraPose = cameraNode.PoseWorld;
        var projection = cameraNode.Camera.Projection;

        // Get min and max of near plane AABB in view space.
        var min = new Vector3(projection.Left, projection.Bottom, -projection.Near);
        var max = new Vector3(projection.Right, projection.Top, -projection.Near);

        // Convert min and max to world space.
        min = cameraPose.ToWorldPosition(min);
        max = cameraPose.ToWorldPosition(max);

        // Get world space aabb
        _cameraNearPlaneAabbWorld = new Aabb(Vector3.Min(min, max), Vector3.Max(min, max));
      }

      // The BlendState is set below.
      bool isGBufferPass = string.Equals(context.RenderPass, "GBuffer", StringComparison.OrdinalIgnoreCase);  // InvariantCultureIgnoreCase would be better but is not available in WindowsStore.
      var blendState = isGBufferPass ? GBufferBlendState : BlendState.AlphaBlend;

      int index = 0;
      var jobs = _jobs.Array;
      int jobCount = _jobs.Count;
      while (index < jobCount)
      {
        // Update BlendState. (Needs to be done for each batch because decals can
        // change the blend mode in the material. For example, alpha-tested decals 
        // can disable alpha blending.)
        graphicsDevice.BlendState = blendState;

        uint materialKey = jobs[index].MaterialKey;
        var materialInstanceBinding = jobs[index].MaterialInstanceBinding;
        var materialBinding = materialInstanceBinding.MaterialBinding;
        var effectEx = materialBinding.EffectEx;

        Debug.Assert(effectEx != null, "EffectEx must not be null.");

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

        if (currentEffectEx != effectEx)
        {
          // ----- Next effect.
          currentEffectEx = effectEx;
          currentEffect = effectEx.Resource;

          // Reset ID. (Only used during state sorting.)
          ResetEffectId(effectEx);

          // Update and apply global bindings.
          foreach (var binding in currentEffectEx.ParameterBindings)
          {
            if (binding.Description.Hint == EffectParameterHint.Global)
            {
              binding.Update(context);
              binding.Apply(context);
            }
          }
        }

        if (currentMaterialBinding != materialBinding)
        {
          // ----- Next material.
          currentMaterialBinding = materialBinding;

          // Reset ID. (Only used during state sorting.)
          ResetMaterialId(materialBinding);

          // Update and apply material bindings.
          foreach (var binding in currentMaterialBinding.ParameterBindings)
          {
            binding.Update(context);
            binding.Apply(context);

            // In "GBuffer" pass the specular power is written to the alpha channel.
            // The specular power needs to be set as the BlendFactor. (See GBufferBlendState.)
            if (isGBufferPass && binding.Description.Semantic == DefaultEffectParameterSemantics.SpecularPower)
            {
              var specularPowerBinding = binding as EffectParameterBinding<float>;
              if (specularPowerBinding != null)
              {
                // Note: Specular power is currently encoded using log2 - see Deferred.fxh.
                // (Blending encoded values is mathematically not correct, but there are no
                // rules for blending specular powers anyway.)
                float specularPower = specularPowerBinding.Value;
                int encodedSpecularPower = (byte)((float)Math.Log(specularPower + 0.0001f, 2) / 17.6f * 255.0f);
                graphicsDevice.BlendFactor = new Color(255, 255, 255, encodedSpecularPower);
              }
            }
          }
        }

        // Note: EffectTechniqueBinding only returns the EffectTechnique, but does 
        // not set it as the current technique.
        var techniqueBinding = materialInstanceBinding.TechniqueBinding;
        var technique = techniqueBinding.GetTechnique(currentEffect, context);

        // See if there is an associated technique that supports hardware instancing.
        //var instancingTechnique = (EffectTechnique)null;
        //var techniqueDescription = currentEffectEx.TechniqueDescriptions[technique];
        //if (techniqueDescription != null)
        //  instancingTechnique = techniqueDescription.InstancingTechnique;

        //if (EnableInstancing && instancingTechnique != null)
        //{
        //  // ----- Instancing
        //  // Render all decals that share the same effect/material and batch instances 
        //  // into a single draw call.
        //  int count = 1;
        //  while (index + count < jobCount && jobs[index + count].MaterialKey == materialKey)
        //    count++;

        //  if (count >= InstancingThreshold)
        //  {
        //    // Draw decals using instancing.
        //    currentEffect.CurrentTechnique = instancingTechnique;
        //    var passBinding = techniqueBinding.GetPassBinding(instancingTechnique, context);
        //    DrawInstanced(ref passBinding, context, index, count);
        //    index += count;
        //  }
        //  else
        //  {
        //    // Draw decals without instancing.
        //    currentEffect.CurrentTechnique = technique;
        //    var passBinding = techniqueBinding.GetPassBinding(technique, context);
        //    Draw(ref passBinding, context, index, count, order);
        //    index += count;
        //  }
        //}
        //else
        {
          // ----- No instancing

          // Render all decals that share the same effect/material.
          int count = 1;
          while (index + count < jobCount && jobs[index + count].MaterialKey == materialKey)
            count++;

          currentEffect.CurrentTechnique = technique;
          var passBinding = techniqueBinding.GetPassBinding(technique, context);
          Draw(ref passBinding, context, index, count, order);
          index += count;
        }
      }

      context.MaterialBinding = null;
      context.MaterialInstanceBinding = null;

      savedRenderState.Restore();
    }
Example #60
0
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            ThrowIfDisposed();

            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            context.Validate(_effect);
            context.ThrowIfCameraMissing();
            context.ThrowIfGBuffer0Missing();

            // Fog is not used in all games. --> Early out, if possible.
            int numberOfNodes = nodes.Count;

            if (nodes.Count == 0)
            {
                return;
            }

            if (nodes.Count > 1)
            {
                // Get a sorted list of all fog nodes.
                if (_fogNodes == null)
                {
                    _fogNodes = new List <SceneNode>();
                }

                _fogNodes.Clear();
                for (int i = 0; i < numberOfNodes; i++)
                {
                    var node = nodes[i] as FogNode;
                    if (node != null)
                    {
                        _fogNodes.Add(node);
                        node.SortTag = node.Priority;
                    }
                }

                // Sort ascending. (Fog with lower priority is rendered first.)
                // Note: Since this list is a list of SceneNodes, we use the AscendingNodeComparer
                // instead of the AscendingFogNodeComparer. The Priority was written to the SortTag,
                // so this will work.
                _fogNodes.Sort(AscendingNodeComparer.Instance);
                nodes         = _fogNodes;
                numberOfNodes = _fogNodes.Count;
            }

            var graphicsDevice   = _effect.GraphicsDevice;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            graphicsDevice.RasterizerState   = RasterizerState.CullNone;
            graphicsDevice.DepthStencilState = DepthStencilState.None;
            graphicsDevice.BlendState        = BlendState.AlphaBlend;

            var viewport = graphicsDevice.Viewport;

            _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height));

            var cameraNode = context.CameraNode;
            var cameraPose = cameraNode.PoseWorld;

            GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, _cameraFrustumFarCorners);

            // Convert frustum far corners from view space to world space.
            for (int i = 0; i < _cameraFrustumFarCorners.Length; i++)
            {
                _cameraFrustumFarCorners[i] = (Vector3)cameraPose.ToWorldDirection((Vector3F)_cameraFrustumFarCorners[i]);
            }

            _parameterFrustumCorners.SetValue(_cameraFrustumFarCorners);
            _parameterGBuffer0.SetValue(context.GBuffer0);

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            bool  directionalLightIsSet      = false;
            float scatteringSymmetryStrength = 1;

            for (int i = 0; i < numberOfNodes; i++)
            {
                var node = nodes[i] as FogNode;
                if (node == null)
                {
                    continue;
                }

                // FogNode is visible in current frame.
                node.LastFrame = frame;

                var fog = node.Fog;

                if (fog.Density <= Numeric.EpsilonF)
                {
                    continue;
                }

                // Compute actual density and falloff.
                float fogDensity    = fog.Density;
                float heightFalloff = fog.HeightFalloff;
                // In previous versions, we gave FogDensity * 2^(-h*y) to the effect. Following code
                // avoids numerical problems where this value is numerically 0. This is now handled
                // in the shader.
                //if (!Numeric.IsZero(heightFalloff))
                //{
                //  float cameraDensity = (float)Math.Pow(2, -heightFalloff * cameraPose.Position.Y);
                //  // Trick: If the heightFalloff is very large, the e^x function can quickly reach
                //  // the float limit! If this happens, the shader will not compute any fog and this
                //  // looks like the fog disappears. To avoid this problem we reduce the heightFalloff
                //  // to keep the result of e^x always within floating point range.
                //  const float Limit = 1e-37f;
                //  if (cameraDensity < Limit)
                //  {
                //    heightFalloff = (float)Math.Log(Limit) / -cameraPose.Position.Y / ConstantsF.Ln2;
                //    cameraDensity = Limit;
                //  }

                //  // Compute actual fog density.
                //  // fogDensity is at world space height 0. If the fog node is on another height,
                //  // we change the fogDensity.
                //  fogDensity *= (float)Math.Pow(2, -heightFalloff * (-node.PoseWorld.Position.Y));
                //  // Combine camera and fog density.
                //  fogDensity *= cameraDensity;
                //}

                _parameterFogParameters.SetValue(new Vector4(fog.Start, fog.End, fogDensity, heightFalloff));
                _parameterColor0.SetValue((Vector4)fog.Color0);
                _parameterColor1.SetValue((Vector4)fog.Color1);

                // Compute world space reference heights.
                var fogBaseHeight = node.PoseWorld.Position.Y;
                var height0       = fogBaseHeight + fog.Height0;
                var height1       = fogBaseHeight + fog.Height1;
                // Avoid division by zero in the shader.
                if (Numeric.AreEqual(height0, height1))
                {
                    height1 = height0 + 0.0001f;
                }
                _parameterHeights.SetValue(new Vector4(
                                               cameraNode.PoseWorld.Position.Y,
                                               fogBaseHeight,
                                               height0,
                                               height1));

                var  scatteringSymmetry    = fog.ScatteringSymmetry;
                bool useScatteringSymmetry = !scatteringSymmetry.IsNumericallyZero;

                if (useScatteringSymmetry)
                {
                    if (!directionalLightIsSet)
                    {
                        scatteringSymmetryStrength = SetDirectionalLightParameter(context, cameraNode);
                        directionalLightIsSet      = true;
                    }
                }

                if (!useScatteringSymmetry || Numeric.IsZero(scatteringSymmetryStrength))
                {
                    // No phase function.
                    if (Numeric.IsZero(heightFalloff))
                    {
                        _passFog.Apply();
                    }
                    else
                    {
                        _passFogWithHeightFalloff.Apply();
                    }
                }
                else
                {
                    // Use phase function.
                    // Set parameters for phase function.
                    _parameterScatteringSymmetry.SetValue((Vector3)scatteringSymmetry * scatteringSymmetryStrength);

                    if (Numeric.IsZero(heightFalloff))
                    {
                        _passFogWithPhase.Apply();
                    }
                    else
                    {
                        _passFogWithHeightFalloffWithPhase.Apply();
                    }
                }

                graphicsDevice.DrawFullScreenQuad();
            }

            if (_fogNodes != null)
            {
                _fogNodes.Clear();
            }

            savedRenderState.Restore();
        }