// TODO: Find a way to replug this

        /// <summary>
        /// Adds a default frustum culling for rendering only meshes that are only inside the frustum/
        /// </summary>
        /// <param name="modelRenderer">The model renderer.</param>
        /// <returns>ModelRenderer.</returns>
        //public static ModelComponentRenderer AddDefaultFrustumCulling(this ModelComponentRenderer modelRenderer)
        //{
        //    modelRenderer.UpdateMeshes = FrustumCulling;
        //    return modelRenderer;
        //}

        private static void FrustumCulling(RenderContext context, FastList<RenderMesh> meshes)
        {
            Matrix viewProjection, mat1, mat2;

            // Compute view * projection
            context.Parameters.Get(TransformationKeys.View, out mat1);
            context.Parameters.Get(TransformationKeys.Projection, out mat2);
            Matrix.Multiply(ref mat1, ref mat2, out viewProjection);

            var frustum = new BoundingFrustum(ref viewProjection);

            for (var i = 0; i < meshes.Count; ++i)
            {
                var renderMesh = meshes[i];

                // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/
                // Get world matrix
                renderMesh.Mesh.Parameters.Get(TransformationKeys.World, out mat1);

                // Compute transformed AABB (by world)
                var boundingBoxExt = new BoundingBoxExt(renderMesh.Mesh.BoundingBox);
                boundingBoxExt.Transform(mat1);

                // Perform frustum culling
                if (!frustum.Contains(ref boundingBoxExt))
                {
                    meshes.SwapRemoveAt(i--);
                }
            }
        }
Example #2
0
        // TODO GRAPHICS REFACTOR not thread-safe
        public void Collect(RenderView view)
        {
            ReevaluateActiveRenderStages();

            // Collect objects, and perform frustum culling
            // TODO GRAPHICS REFACTOR Create "VisibilityObject" (could contain multiple RenderNode) and separate frustum culling from RenderSystem
            // TODO GRAPHICS REFACTOR optimization: maybe we could process all views at once (swap loop between per object and per view)

            // View bounds calculation
            view.MinimumDistance = float.PositiveInfinity;
            view.MaximumDistance = float.NegativeInfinity;

            Matrix viewInverse = view.View;
            viewInverse.Invert();
            var plane = new Plane(viewInverse.Forward, Vector3.Dot(viewInverse.TranslationVector, viewInverse.Forward)); // TODO: Point-normal-constructor seems wrong. Check.

            // TODO: This should be configured by the creator of the view. E.g. near clipping can be enabled for spot light shadows.
            var ignoreDepthPlanes = view is ShadowMapRenderView;

            // Prepare culling mask
            foreach (var renderViewStage in view.RenderStages)
            {
                var renderStageIndex = renderViewStage.RenderStage.Index;
                viewRenderStageMask[renderStageIndex / RenderStageMaskSizePerEntry] |= 1U << (renderStageIndex % RenderStageMaskSizePerEntry);
            }

            // Create the bounding frustum locally on the stack, so that frustum.Contains is performed with boundingBox that is also on the stack
            // TODO GRAPHICS REFACTOR frustum culling is currently hardcoded (cf previous TODO, we should make this more modular and move it out of here)
            var frustum = new BoundingFrustum(ref view.ViewProjection);
            var cullingMode = view.CullingMode;

            // TODO GRAPHICS REFACTOR we currently forward SceneCameraRenderer.CullingMask
            // Note sure this is really a good mechanism long term (it forces to recreate multiple time the same view, instead of using RenderStage + selectors or a similar mechanism)
            // This is still supported so that existing gizmo code kept working with new graphics refactor. Might be reconsidered at some point.
            var cullingMask = view.CullingMask;

            // Process objects
            foreach (var renderObject in RenderObjects)
            {
                // Skip not enabled objects
                if (!renderObject.Enabled || ((EntityGroupMask)(1U << (int)renderObject.RenderGroup) & cullingMask) == 0)
                    continue;

                // Custom per-view filtering
                bool skip = false;
                lock (ViewObjectFilters)
                {
                    // TODO HACK First filter with global static filters
                    foreach (var filter in ViewObjectFilters)
                    {
                        if (!filter(view, renderObject))
                        {
                            skip = true;
                            break;
                        }
                    }

                    if (skip)
                        continue;
                }

                // TODO HACK Then filter with RenderSystem filters
                foreach (var filter in RenderSystem.ViewObjectFilters)
                {
                    if (!filter(view, renderObject))
                    {
                        skip = true;
                        break;
                    }
                }

                if (skip)
                    continue;

                var renderStageMask = RenderData.GetData(RenderStageMaskKey);
                var renderStageMaskNode = renderObject.VisibilityObjectNode * stageMaskMultiplier;

                // Determine if this render object belongs to this view
                bool renderStageMatch = false;
                unsafe
                {
                    fixed (uint* viewRenderStageMaskStart = viewRenderStageMask)
                    fixed (uint* objectRenderStageMaskStart = renderStageMask.Data)
                    {
                        var viewRenderStageMaskPtr = viewRenderStageMaskStart;
                        var objectRenderStageMaskPtr = objectRenderStageMaskStart + renderStageMaskNode.Index;
                        for (int i = 0; i < viewRenderStageMask.Length; ++i)
                        {
                            if ((*viewRenderStageMaskPtr++ & *objectRenderStageMaskPtr++) != 0)
                            {
                                renderStageMatch = true;
                                break;
                            }
                        }
                    }
                }

                // Object not part of this view because no render stages in this objects are visible in this view
                if (!renderStageMatch)
                    continue;

                // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/
                // Compute transformed AABB (by world)
                if (cullingMode == CameraCullingMode.Frustum
                    && renderObject.BoundingBox.Extent != Vector3.Zero
                    && !FrustumContainsBox(ref frustum, ref renderObject.BoundingBox, ignoreDepthPlanes))
                {
                    continue;
                }

                // Add object to list of visible objects
                // TODO GRAPHICS REFACTOR we should be able to push multiple elements with future VisibilityObject
                // TODO GRAPHICS REFACTOR not thread-safe
                view.RenderObjects.Add(renderObject);

                // Calculate bounding box of all render objects in the view
                if (renderObject.BoundingBox.Extent != Vector3.Zero)
                    CalculateMinMaxDistance(view, ref plane, ref renderObject.BoundingBox);
            }
        }
Example #3
0
        private static bool FrustumContainsBox(ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt, bool ignoreDepthPlanes)
        {
            unsafe
            {
                fixed (Plane* planeStart = &frustum.LeftPlane)
                {
                    var plane = planeStart;
                    for (int i = 0; i < 6; ++i)
                    {
                        if (ignoreDepthPlanes && i > 3)
                            continue;

                        // Previous code:
                        if (Vector3.Dot(boundingBoxExt.Center, plane->Normal)
                            + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X)
                            + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y)
                            + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z)
                            <= -plane->D)
                            return false;
                        plane++;
                    }
                }

                return true;
            }
        }
Example #4
0
        /// <summary>
        /// Determines whether a <see cref="BoundingFrustum" /> intersects or contains an AABB determined by its center and extent.
        /// Faster variant specific for frustum culling.
        /// </summary>
        /// <param name="frustum">The frustum.</param>
        /// <param name="boundingBoxExt">The bounding box ext.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public static bool FrustumContainsBox(ref BoundingFrustum frustum, ref BoundingBoxExt boundingBoxExt)
        {
            unsafe
            {
                fixed (Plane* planeStart = &frustum.LeftPlane)
                {
                    var plane = planeStart;
                    for (int i = 0; i < 6; ++i)
                    {
                        // Previous code:
                        if (Vector3.Dot(boundingBoxExt.Center, plane->Normal)
                            + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X)
                            + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y)
                            + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z)
                            <= -plane->D)
                            return false;
                        plane++;
                    }
                }

                return true;
            }
/*
            unsafe
            {
                fixed (Plane* planeStart = &frustum.LeftPlane)
                fixed (Vector3* pExtent = &boundingBoxExt.Extent)
                {
                    var plane = planeStart;
                    for (int i = 0; i < 6; ++i)
                    {
                        // Previous code:
                        //if (Vector3.Dot(boundingBoxExt.Center, plane->Normal)
                        //    + boundingBoxExt.Extent.X * Math.Abs(plane->Normal.X)
                        //    + boundingBoxExt.Extent.Y * Math.Abs(plane->Normal.Y)
                        //    + boundingBoxExt.Extent.Z * Math.Abs(plane->Normal.Z)
                        //    <= -plane->D)

                        // Optimized version (only 1 dot and cheaper Math.Abs)
                        // https://fgiesen.wordpress.com/2010/10/17/view-frustum-culling/
                        // return dot3(center, plane) + dot3(extent, absPlane) <= -plane.w;
                        // or
                        // vector4 signFlip = componentwise_and(plane, 0x80000000);
                        // vector3 centerOffset = xor(extent, signFlip)
                        // dot3(center + centerOffset, plane) <= -plane.w;

                        uint val = (((uint*)&plane->Normal)[0] & 0x80000000) ^ ((uint*)pExtent)[0];
                        var dist = plane->Normal.X * ((*(float*)(&val)) + boundingBoxExt.Center.X);

                        val = (((uint*)&plane->Normal)[1] & 0x80000000) ^ ((uint*)pExtent)[1];
                        dist += plane->Normal.Y * ((*(float*)(&val)) + boundingBoxExt.Center.Y);

                        val = (((uint*)&plane->Normal)[2] & 0x80000000) ^ ((uint*)pExtent)[2];
                        dist += plane->Normal.Z * ((*(float*)(&val)) + boundingBoxExt.Center.Z);

                        if (dist <= -plane->D)
                            return false;

                        plane++;
                    }
                }

                return true;
            }
 */
        }
Example #5
0
        /// <summary>
        /// Calculates the projection matrix and view matrix.
        /// </summary>
        /// <param name="screenAspectRatio">The current screen aspect ratio. If null, use the <see cref="AspectRatio"/> even if <see cref="UseCustomAspectRatio"/> is false.</param>
        public void Update(float? screenAspectRatio)
        {
            // Calculates the View
            if (!UseCustomViewMatrix)
            {
                var worldMatrix = EnsureEntity.Transform.WorldMatrix;
                Matrix.Invert(ref worldMatrix, out ViewMatrix);
            }
            
            // Calculates the projection
            // TODO: Should we throw an error if Projection is not set?
            if (!UseCustomProjectionMatrix)
            {
                // Calculates the aspect ratio
                var aspectRatio = AspectRatio;
                if (!UseCustomAspectRatio && screenAspectRatio.HasValue)
                {
                    aspectRatio = screenAspectRatio.Value;
                }

                ProjectionMatrix = Projection == CameraProjectionMode.Perspective ?
                    Matrix.PerspectiveFovRH(MathUtil.DegreesToRadians(VerticalFieldOfView), aspectRatio, NearClipPlane, FarClipPlane) :
                    Matrix.OrthoRH(aspectRatio * OrthographicSize, OrthographicSize, NearClipPlane, FarClipPlane);
            }

            // Update ViewProjectionMatrix
            Matrix.Multiply(ref ViewMatrix, ref ProjectionMatrix, out ViewProjectionMatrix);

            // Update the frustum.
            Frustum = new BoundingFrustum(ref ViewProjectionMatrix);
        }
        private void PrepareRenderMeshes(RenderModel renderModel, List<Mesh> meshes, ref FastListStruct<RenderMesh> renderMeshes, RenderItemCollection opaqueList, RenderItemCollection transparentList)
        {
            // Add new render meshes
            for (int i = renderMeshes.Count; i < meshes.Count; i++)
            {
                var renderMesh = new RenderMesh(renderModel, meshes[i]);
                renderMeshes.Add(renderMesh);
            }

            // Create the bounding frustum locally on the stack, so that frustum.Contains is performed with boundingBox that is also on the stack
            var frustum = new BoundingFrustum(ref ViewProjectionMatrix);

            for (int i = 0; i < renderMeshes.Count; i++)
            {
                var renderMesh = renderMeshes[i];
                // Update the model hierarchy
                var modelViewHierarchy = renderModel.ModelComponent.ModelViewHierarchy;
                modelViewHierarchy.UpdateRenderMesh(renderMesh);

                if (!renderMesh.Enabled)
                {
                    continue;
                }

                // Upload skinning blend matrices
                BoundingBoxExt boundingBox;
                skinningUpdater.Update(modelViewHierarchy, renderMesh, out boundingBox);

                // Fast AABB transform: http://zeuxcg.org/2010/10/17/aabb-from-obb-with-component-wise-abs/
                // Compute transformed AABB (by world)
                // TODO: CullingMode should be pluggable
                // TODO: This should not be necessary. Add proper bounding boxes to gizmos etc.
                if (CullingMode == CullingMode.Frustum && boundingBox.Extent != Vector3.Zero && !frustum.Contains(ref boundingBox))
                {
                    continue;
                }

                // Project the position
                // TODO: This could be done in a SIMD batch, but we need to figure-out how to plugin in with RenderMesh object
                var worldPosition = new Vector4(renderMesh.WorldMatrix.TranslationVector, 1.0f);
                Vector4 projectedPosition;
                Vector4.Transform(ref worldPosition, ref ViewProjectionMatrix, out projectedPosition);
                var projectedZ = projectedPosition.Z / projectedPosition.W;

                renderMesh.RasterizerState = renderMesh.IsGeometryInverted ? RasterizerStateForInvertedGeometry : RasterizerState;

                renderMesh.UpdateMaterial();

                var list = renderMesh.HasTransparency ? transparentList : opaqueList;
                list.Add(new RenderItem(this, renderMesh, projectedZ));
            }
        }
        /// <summary>
        /// Calculates the projection matrix and view matrix.
        /// </summary>
        /// <param name="screenAspectRatio">The current screen aspect ratio. If null, use the <see cref="AspectRatio"/> even if <see cref="UseCustomAspectRatio"/> is false.</param>
        public void Update(float? screenAspectRatio)
        {
            // Calculates the View
            if (!UseCustomViewMatrix)
            {
                var worldMatrix = EnsureEntity.Transform.WorldMatrix;

                Vector3 scale, translation;
                worldMatrix.Decompose(out scale, out ViewMatrix, out translation);

                // Transpose ViewMatrix (rotation only, so equivalent to inversing it)
                ViewMatrix.Transpose();

                // Rotate our translation so that we can inject it in the view matrix directly
                Vector3.TransformCoordinate(ref translation, ref ViewMatrix, out translation);

                // Apply inverse of translation (equivalent to opposite)
                ViewMatrix.TranslationVector = -translation;
            }
            
            // Calculates the projection
            // TODO: Should we throw an error if Projection is not set?
            if (!UseCustomProjectionMatrix)
            {
                // Calculates the aspect ratio
                var aspectRatio = AspectRatio;
                if (screenAspectRatio.HasValue && !UseCustomAspectRatio)
                {
                    aspectRatio = screenAspectRatio.Value;
                }

                ProjectionMatrix = Projection == CameraProjectionMode.Perspective ?
                    Matrix.PerspectiveFovRH(MathUtil.DegreesToRadians(VerticalFieldOfView), aspectRatio, NearClipPlane, FarClipPlane) :
                    Matrix.OrthoRH(aspectRatio * OrthographicSize, OrthographicSize, NearClipPlane, FarClipPlane);
            }

            // Update ViewProjectionMatrix
            Matrix.Multiply(ref ViewMatrix, ref ProjectionMatrix, out ViewProjectionMatrix);

            // Update the frustum.
            Frustum = new BoundingFrustum(ref ViewProjectionMatrix);
        }