Beispiel #1
0
        public override unsafe void GenerateSortKey(RenderView renderView, RenderViewStage renderViewStage, SortKey* sortKeys)
        {
            Matrix viewInverse = renderView.View;
            viewInverse.Invert();
            var plane = new Plane(viewInverse.Forward, Vector3.Dot(viewInverse.TranslationVector, viewInverse.Forward)); // TODO: Point-normal-constructor seems wrong. Check.

            var renderNodes = renderViewStage.RenderNodes;

            int distanceShift = 32 - distancePrecision;
            int stateShift = 32 - statePrecision;

            for (int i = 0; i < renderNodes.Count; ++i)
            {
                var renderNode = renderNodes[i];

                var renderObject = renderNode.RenderObject;
                var distance = CollisionHelper.DistancePlanePoint(ref plane, ref renderObject.BoundingBox.Center);
                var distanceI = ComputeDistance(distance);
                if (reverseDistance)
                    distanceI = ~distanceI;

                // Compute sort key
                sortKeys[i] = new SortKey { Value = ((ulong)renderNode.RootRenderFeature.SortKey << 56) | ((ulong)(distanceI >> distanceShift) << distancePosition) | ((ulong)(renderObject.StateSortKey >> stateShift) << statePosition), Index = i };
            }
        }
Beispiel #2
0
        public static void UpdateCameraToRenderView(RenderContext context, RenderView renderView)
        {
            var camera = context.Tags.Get(CameraComponentRendererExtensions.Current);
            var sceneCameraRenderer = context.Tags.Get(SceneCameraRenderer.Current);

            if (camera == null || sceneCameraRenderer == null)
                return;

            // Setup viewport size
            var currentViewport = sceneCameraRenderer.ComputedViewport;
            var aspectRatio = currentViewport.AspectRatio;

            // Update the aspect ratio
            if (camera.UseCustomAspectRatio)
            {
                aspectRatio = camera.AspectRatio;
            }

            // If the aspect ratio is calculated automatically from the current viewport, update matrices here
            camera.Update(aspectRatio);

            // Copy camera data
            renderView.View = camera.ViewMatrix;
            renderView.Projection = camera.ProjectionMatrix;
            renderView.NearClipPlane = camera.NearClipPlane;
            renderView.FarClipPlane = camera.FarClipPlane;
            renderView.Frustum = camera.Frustum;

            // Copy scene camera renderer data
            renderView.CullingMask = sceneCameraRenderer.CullingMask;
            renderView.CullingMode = sceneCameraRenderer.CullingMode;
            renderView.ViewSize = new Vector2(sceneCameraRenderer.ComputedViewport.Width, sceneCameraRenderer.ComputedViewport.Height);

            Matrix.Multiply(ref renderView.View, ref renderView.Projection, out renderView.ViewProjection);
        }
 public RenderNode(RenderObject renderObject, RenderView renderView, ViewObjectNodeReference viewObjectNode, RenderStage renderStage)
 {
     RenderObject = renderObject;
     RenderView = renderView;
     ViewObjectNode = viewObjectNode;
     EffectObjectNode = EffectObjectNodeReference.Invalid;
     RenderStage = renderStage;
     RenderEffect = null;
     Resources = null;
 }
        protected override void InitializeCore()
        {
            base.InitializeCore();

            RenderSystem = Context.Tags.Get(SceneInstance.CurrentRenderSystem);

            var sceneInstance = SceneInstance.GetCurrent(Context);

            // Describe views
            MainRenderView = new RenderView
            {
                SceneInstance = sceneInstance,
            };
            RenderSystem.Views.Add(MainRenderView);
        }
Beispiel #5
0
 public abstract unsafe void GenerateSortKey(RenderView renderView, RenderViewStage renderViewStage, SortKey* sortKeys);
        /// <summary>
        /// Creates a view object node during Extract phase.
        /// </summary>
        /// <param name="view"></param>
        /// <param name="renderObject"></param>
        /// <returns>The view object node reference.</returns>
        public ViewObjectNodeReference CreateViewObjectNode(RenderView view, RenderObject renderObject)
        {
            var renderViewNode = new ViewObjectNode(renderObject, view, renderObject.ObjectNode);

            // Create view node
            var index = viewObjectNodes.Add(renderViewNode);
            var result = new ViewObjectNodeReference(index);
            return result;
        }
        internal RenderNodeReference CreateRenderNode(RenderObject renderObject, RenderView renderView, ViewObjectNodeReference renderPerViewNode, RenderStage renderStage)
        {
            var renderNode = new RenderNode(renderObject, renderView, renderPerViewNode, renderStage);

            // Create view node
            var index = RenderNodes.Add(renderNode);
            var result = new RenderNodeReference(index);
            return result;
        }
        /// <inheritdoc/>
        public override unsafe void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex)
        {
            var commandList = context.CommandList;

            // Per view - this code was moved here from Prepare(...) so that we can apply the correct Viewport
            {
                var view = renderView;
                var viewFeature = view.Features[Index];

                Matrix.Multiply(ref view.View, ref view.Projection, out view.ViewProjection);

                // Copy ViewProjection to PerFrame cbuffer
                foreach (var viewLayout in viewFeature.Layouts)
                {
                    var resourceGroup = viewLayout.Entries[view.Index].Resources;
                    var mappedCB = resourceGroup.ConstantBuffer.Data;

                    // PerView constant buffer
                    var perViewOffset = viewLayout.GetConstantBufferOffset(this.perViewCBufferOffset);
                    if (perViewOffset != -1)
                    {
                        var perView = (ParticleUtilitiesPerView*)((byte*)mappedCB + perViewOffset);
                        perView->ViewMatrix = view.View;
                        perView->ProjectionMatrix = view.Projection;
                        perView->ViewProjectionMatrix = view.ViewProjection;
                        perView->ViewFrustum = new Vector4(view.ViewSize.X, view.ViewSize.Y, view.NearClipPlane, view.FarClipPlane);

                        perView->Viewport = new Vector4(0,
                                                        0,
                                                        ((float)context.CommandList.Viewport.Width) / ((float)context.CommandList.RenderTarget.Width),
                                                        ((float)context.CommandList.Viewport.Height) / ((float)context.CommandList.RenderTarget.Height));
                    }
                }
            }

            var renderParticleNodeData = RenderData.GetData(renderParticleNodeKey);

            // TODO: stackalloc?
            var descriptorSetsLocal = descriptorSets.Value;
            if (descriptorSetsLocal == null || descriptorSetsLocal.Length < EffectDescriptorSetSlotCount)
            {
                descriptorSetsLocal = descriptorSets.Value = new DescriptorSet[EffectDescriptorSetSlotCount];
            }

            for (var index = startIndex; index < endIndex; index++)
            {
                var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode;

                // Get effect
                var renderEffect = GetRenderNode(renderNodeReference).RenderEffect;
                if (renderEffect.Effect == null)
                    continue;

                // Get the extra node data
                var nodeData = renderParticleNodeData[renderNodeReference];
                if (nodeData.IndexCount <= 0)
                    continue;

                var resourceGroupOffset = ComputeResourceGroupOffset(renderNodeReference);

                // Update cbuffer
                renderEffect.Reflection.BufferUploader.Apply(commandList, ResourceGroupPool, resourceGroupOffset);

                // Bind descriptor sets
                for (int i = 0; i < descriptorSetsLocal.Length; ++i)
                {
                    var resourceGroup = ResourceGroupPool[resourceGroupOffset++];
                    if (resourceGroup != null)
                        descriptorSetsLocal[i] = resourceGroup.DescriptorSet;
                }

                commandList.SetPipelineState(renderEffect.PipelineState);
                commandList.SetDescriptorSets(0, descriptorSetsLocal);

                // Bind the buffers and draw
                commandList.SetVertexBuffer(0, nodeData.VertexBuffer, nodeData.VertexBufferOffset, nodeData.VertexBufferStride);
                commandList.SetIndexBuffer(nodeData.IndexBuffer, nodeData.IndexBufferOffset, ParticleBufferContext.IndexStride != sizeof(short));
                commandList.DrawIndexed(nodeData.IndexCount, 0);
            }
        }
Beispiel #9
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;
            var    viewDirection = new Vector3(view.View.M11, view.View.M21, view.View.M31);

            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 = DisableCulling ? CameraCullingMode.None : 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;

            lock (ViewObjectFilters)
            {
                // Process objects
                //foreach (var renderObject in RenderObjects)
                //Dispatcher.ForEach(RenderObjects, renderObject =>
                Dispatcher.For(0, RenderObjects.Count, () => collectorCache.Value, (index, cache) =>
                {
                    var renderObject = RenderObjects[index];

                    // Skip not enabled objects
                    if (!renderObject.Enabled || ((EntityGroupMask)(1U << (int)renderObject.RenderGroup) & cullingMask) == 0)
                    {
                        return;
                    }

                    // Custom per-view filtering
                    bool skip = false;

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

                    if (skip)
                    {
                        return;
                    }

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

                    if (skip)
                    {
                        return;
                    }

                    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)
                    {
                        return;
                    }

                    // 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))
                    {
                        return;
                    }

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

                    // Calculate bounding box of all render objects in the view
                    if (renderObject.BoundingBox.Extent != Vector3.Zero)
                    {
                        CalculateMinMaxDistance(ref plane, ref renderObject.BoundingBox, ref view.MinimumDistance, ref view.MaximumDistance);
                    }
                }, cache => cache.Flush());
            }

            view.RenderObjects.Close();
        }
Beispiel #10
0
        private static void CalculateMinMaxDistance(RenderView view, ref Plane plane, ref BoundingBoxExt boundingBox)
        {
            // TODO GRAPHICS REFACTOR: Optimize per-view: Only two corners need checking, depending on view direction. Also, currently unnecessary for shadow views.
            var minimum = boundingBox.Minimum;
            var maximum = boundingBox.Maximum;

            var point = minimum;
            var distance = CollisionHelper.DistancePlanePoint(ref plane, ref point);
            MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance);

            point.X = maximum.X;
            distance = CollisionHelper.DistancePlanePoint(ref plane, ref point);
            MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance);

            point.Y = maximum.Y;
            distance = CollisionHelper.DistancePlanePoint(ref plane, ref point);
            MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance);

            point.X = minimum.X;
            distance = CollisionHelper.DistancePlanePoint(ref plane, ref point);
            MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance);

            point.Z = maximum.Z;
            distance = CollisionHelper.DistancePlanePoint(ref plane, ref point);
            MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance);

            point.Y = minimum.Y;
            distance = CollisionHelper.DistancePlanePoint(ref plane, ref point);
            MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance);

            point.X = maximum.X;
            distance = CollisionHelper.DistancePlanePoint(ref plane, ref point);
            MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance);

            point.Y = maximum.Y;
            distance = CollisionHelper.DistancePlanePoint(ref plane, ref point);
            MinMax(distance, ref view.MinimumDistance, ref view.MaximumDistance);
        }
Beispiel #11
0
 /// <summary>
 /// Performs GPU updates and/or draw.
 /// </summary>
 /// <param name="context"></param>
 /// <param name="renderView"></param>
 /// <param name="renderViewStage"></param>
 /// <param name="startIndex"></param>
 /// <param name="endIndex"></param>
 public virtual void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex)
 {
 }
Beispiel #12
0
        /// <inheritdoc/>
        public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex)
        {
            var commandList = context.CommandList;

            foreach (var renderFeature in RenderFeatures)
            {
                renderFeature.Draw(context, renderView, renderViewStage, startIndex, endIndex);
            }

            var descriptorSets = new DescriptorSet[EffectDescriptorSetSlotCount];

            MeshDraw currentDrawData = null;

            for (int index = startIndex; index < endIndex; index++)
            {
                var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode;
                var renderNode          = GetRenderNode(renderNodeReference);

                var renderMesh = (RenderMesh)renderNode.RenderObject;
                var drawData   = renderMesh.ActiveMeshDraw;

                // Get effect
                // TODO: Use real effect slot
                var renderEffect = renderNode.RenderEffect;
                if (renderEffect.Effect == null)
                {
                    continue;
                }

                // Bind VB
                if (currentDrawData != drawData)
                {
                    for (int i = 0; i < drawData.VertexBuffers.Length; i++)
                    {
                        var vertexBuffer = drawData.VertexBuffers[i];
                        commandList.SetVertexBuffer(i, vertexBuffer.Buffer, vertexBuffer.Offset, vertexBuffer.Stride);
                    }
                    if (drawData.IndexBuffer != null)
                    {
                        commandList.SetIndexBuffer(drawData.IndexBuffer.Buffer, drawData.IndexBuffer.Offset, drawData.IndexBuffer.Is32Bit);
                    }
                    currentDrawData = drawData;
                }

                var resourceGroupOffset = ComputeResourceGroupOffset(renderNodeReference);

                // Update cbuffer
                renderEffect.Reflection.BufferUploader.Apply(context.CommandList, ResourceGroupPool, resourceGroupOffset);

                // Bind descriptor sets
                for (int i = 0; i < descriptorSets.Length; ++i)
                {
                    var resourceGroup = ResourceGroupPool[resourceGroupOffset++];
                    if (resourceGroup != null)
                    {
                        descriptorSets[i] = resourceGroup.DescriptorSet;
                    }
                }

                commandList.SetPipelineState(renderEffect.PipelineState);
                commandList.SetDescriptorSets(0, descriptorSets);

                // Draw
                if (drawData.IndexBuffer == null)
                {
                    commandList.Draw(drawData.DrawCount, drawData.StartLocation);
                }
                else
                {
                    commandList.DrawIndexed(drawData.DrawCount, drawData.StartLocation);
                }
            }
        }
Beispiel #13
0
 public abstract unsafe void GenerateSortKey(RenderView renderView, RenderViewStage renderViewStage, SortKey *sortKeys);
Beispiel #14
0
 /// <summary>
 /// Performs GPU updates and/or draw.
 /// </summary>
 /// <param name="context"></param>
 /// <param name="renderView"></param>
 /// <param name="renderViewStage"></param>
 /// <param name="startIndex"></param>
 /// <param name="endIndex"></param>
 public virtual void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex)
 {
 }
Beispiel #15
0
 /// <summary>
 /// Performs GPU updates and/or drawing, everytime a render view changes.
 /// </summary>
 /// <param name="context"></param>
 /// <param name="renderView"></param>
 /// <param name="renderViewStage"></param>
 public virtual void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage)
 {
 }
Beispiel #16
0
        public void Draw(RenderDrawContext renderDrawContext, RenderView renderView, RenderStage renderStage)
        {
            // Sync point: draw (from now, we should execute with a graphics device context to perform rendering)

            // Look for the RenderViewStage corresponding to this RenderView | RenderStage combination
            RenderViewStage renderViewStage = null;

            foreach (var currentRenderViewStage in renderView.RenderStages)
            {
                if (currentRenderViewStage.RenderStage == renderStage)
                {
                    renderViewStage = currentRenderViewStage;
                    break;
                }
            }

            if (renderViewStage == null)
            {
                throw new InvalidOperationException("Requested RenderView|RenderStage combination doesn't exist. Please add it to RenderView.RenderStages.");
            }

            // Generate and execute draw jobs
            var renderNodes     = renderViewStage.SortedRenderNodes;
            var renderNodeCount = renderViewStage.RenderNodes.Count;

            if (renderNodeCount == 0)
            {
                return;
            }

            if (!GraphicsDevice.IsDeferred)
            {
                int currentStart, currentEnd;
                for (currentStart = 0; currentStart < renderNodeCount; currentStart = currentEnd)
                {
                    var currentRenderFeature = renderNodes[currentStart].RootRenderFeature;
                    currentEnd = currentStart + 1;
                    while (currentEnd < renderNodeCount && renderNodes[currentEnd].RootRenderFeature == currentRenderFeature)
                    {
                        currentEnd++;
                    }

                    // Divide into task chunks for parallelism
                    currentRenderFeature.Draw(renderDrawContext, renderView, renderViewStage, currentStart, currentEnd);
                }
            }
            else
            {
                // Create at most one batch per processor
                int batchCount = Math.Min(Environment.ProcessorCount, renderNodeCount);
                int batchSize  = (renderNodeCount + (batchCount - 1)) / batchCount;
                batchCount = (renderNodeCount + (batchSize - 1)) / batchSize;

                // Remember state
                var depthStencilBuffer = renderDrawContext.CommandList.DepthStencilBuffer;
                var renderTargetView   = renderDrawContext.CommandList.RenderTargetCount > 0 ? renderDrawContext.CommandList.RenderTarget : null;
                var viewport           = renderDrawContext.CommandList.Viewport;

                // Collect one command list per batch and the main one up to this point
                if (commandLists == null || commandLists.Length < batchCount + 1)
                {
                    Array.Resize(ref commandLists, batchCount + 1);
                }
                commandLists[0] = renderDrawContext.CommandList.Close();

                Dispatcher.For(0, batchCount, () => renderDrawContext.RenderContext.GetThreadContext(), (batchIndex, threadContext) =>
                {
                    threadContext.CommandList.Reset();
                    threadContext.CommandList.ClearState();

                    // Transfer state to all command lists
                    threadContext.CommandList.SetRenderTarget(depthStencilBuffer, renderTargetView);
                    threadContext.CommandList.SetViewport(viewport);

                    var currentStart = batchSize * batchIndex;
                    int currentEnd;

                    var endExclusive = Math.Min(renderNodeCount, currentStart + batchSize);

                    if (endExclusive <= currentStart)
                    {
                        return;
                    }

                    for (; currentStart < endExclusive; currentStart = currentEnd)
                    {
                        var currentRenderFeature = renderNodes[currentStart].RootRenderFeature;
                        currentEnd = currentStart + 1;
                        while (currentEnd < endExclusive && renderNodes[currentEnd].RootRenderFeature == currentRenderFeature)
                        {
                            currentEnd++;
                        }

                        // Divide into task chunks for parallelism
                        currentRenderFeature.Draw(threadContext, renderView, renderViewStage, currentStart, currentEnd);
                    }

                    commandLists[batchIndex + 1] = threadContext.CommandList.Close();
                });

                GraphicsDevice.ExecuteCommandLists(batchCount + 1, commandLists);

                renderDrawContext.CommandList.Reset();
                renderDrawContext.CommandList.ClearState();

                // Reapply previous state
                renderDrawContext.CommandList.SetRenderTarget(depthStencilBuffer, renderTargetView);
                renderDrawContext.CommandList.SetViewport(viewport);
            }
        }
Beispiel #17
0
        /// <inheritdoc/>
        public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex)
        {
            var commandList = context.CommandList;

            foreach (var renderFeature in RenderFeatures)
            {
                renderFeature.Draw(context, renderView, renderViewStage, startIndex, endIndex);
            }

            var descriptorSets = new DescriptorSet[EffectDescriptorSetSlotCount];

            MeshDraw currentDrawData = null;
            for (int index = startIndex; index < endIndex; index++)
            {
                var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode;
                var renderNode = GetRenderNode(renderNodeReference);

                var renderMesh = (RenderMesh)renderNode.RenderObject;
                var drawData = renderMesh.ActiveMeshDraw;

                // Get effect
                // TODO: Use real effect slot
                var renderEffect = renderNode.RenderEffect;
                if (renderEffect.Effect == null)
                    continue;

                // Bind VB
                if (currentDrawData != drawData)
                {
                    for (int i = 0; i < drawData.VertexBuffers.Length; i++)
                    {
                        var vertexBuffer = drawData.VertexBuffers[i];
                        commandList.SetVertexBuffer(i, vertexBuffer.Buffer, vertexBuffer.Offset, vertexBuffer.Stride);
                    }
                    if (drawData.IndexBuffer != null)
                        commandList.SetIndexBuffer(drawData.IndexBuffer.Buffer, drawData.IndexBuffer.Offset, drawData.IndexBuffer.Is32Bit);
                    currentDrawData = drawData;
                }

                var resourceGroupOffset = ComputeResourceGroupOffset(renderNodeReference);
                
                // Update cbuffer
                renderEffect.Reflection.BufferUploader.Apply(context.CommandList, ResourceGroupPool, resourceGroupOffset);

                // Bind descriptor sets
                for (int i = 0; i < descriptorSets.Length; ++i)
                {
                    var resourceGroup = ResourceGroupPool[resourceGroupOffset++];
                    if (resourceGroup != null)
                        descriptorSets[i] = resourceGroup.DescriptorSet;
                }

                commandList.SetPipelineState(renderEffect.PipelineState);
                commandList.SetDescriptorSets(0, descriptorSets);

                // Draw
                if (drawData.IndexBuffer == null)
                {
                    commandList.Draw(drawData.DrawCount, drawData.StartLocation);
                }
                else
                {
                    commandList.DrawIndexed(drawData.DrawCount, drawData.StartLocation);
                }
            }
        }
        /// <inheritdoc/>
        public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex)
        {
            var commandList = context.CommandList;

            // TODO: PerView data
            Matrix viewInverse;
            Matrix.Invert(ref renderView.View, out viewInverse);

            var descriptorSets = new DescriptorSet[EffectDescriptorSetSlotCount];

            for (var index = startIndex; index < endIndex; index++)
            {
                var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode;
                var renderNode = GetRenderNode(renderNodeReference);

                var renderParticleEmitter = (RenderParticleEmitter)renderNode.RenderObject;
                if (renderParticleEmitter.ParticleEmitter.VertexBuilder.ResourceContext == null)
                    continue;

                // Generate vertices
                // TODO: Just just unmap/barrier here
                renderParticleEmitter.ParticleEmitter.BuildVertexBuffer(context.CommandList, ref viewInverse);

                // Get effect
                var renderEffect = renderNode.RenderEffect;
                if (renderEffect.Effect == null)
                    continue;

                // TODO GRAPHICS REFACTOR: Extract data
                var particleSystemComponent = renderParticleEmitter.RenderParticleSystem.ParticleSystemComponent;
                var particleSystem = particleSystemComponent.ParticleSystem;
                var vertexBuilder = renderParticleEmitter.ParticleEmitter.VertexBuilder;

                // Bind VB
                var vertexBuffer = vertexBuilder.ResourceContext.VertexBuffer;
                var indexBuffer = vertexBuilder.ResourceContext.IndexBuffer;
                commandList.SetVertexBuffer(0, vertexBuffer.Buffer, vertexBuffer.Offset, vertexBuffer.Stride);
                commandList.SetIndexBuffer(indexBuffer.Buffer, indexBuffer.Offset, indexBuffer.Is32Bit);

                var resourceGroupOffset = ComputeResourceGroupOffset(renderNodeReference);

                // Update cbuffer
                renderEffect.Reflection.BufferUploader.Apply(context.CommandList, ResourceGroupPool, resourceGroupOffset);

                // Bind descriptor sets
                for (int i = 0; i < descriptorSets.Length; ++i)
                {
                    var resourceGroup = ResourceGroupPool[resourceGroupOffset++];
                    if (resourceGroup != null)
                        descriptorSets[i] = resourceGroup.DescriptorSet;
                }

                commandList.SetPipelineState(renderEffect.PipelineState);
                commandList.SetDescriptorSets(0, descriptorSets);

                commandList.DrawIndexed(vertexBuilder.LivingQuads * vertexBuilder.IndicesPerQuad, vertexBuilder.ResourceContext.IndexBufferPosition);
            }
        }
Beispiel #19
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);
            }
        }
        public override void Draw(RenderDrawContext context, RenderView renderView, RenderViewStage renderViewStage, int startIndex, int endIndex)
        {
            base.Draw(context, renderView, renderViewStage, startIndex, endIndex);

            BlendStateDescription? previousBlendState = null;
            DepthStencilStateDescription? previousDepthStencilState = null;
            EffectInstance previousEffect = null;

            //TODO string comparison ...?
            var isPicking = renderViewStage.RenderStage.Name == "Picking";

            var device = RenderSystem.GraphicsDevice;

            var hasBegin = false;
            for (var index = startIndex; index < endIndex; index++)
            {
                var renderNodeReference = renderViewStage.SortedRenderNodes[index].RenderNode;
                var renderNode = GetRenderNode(renderNodeReference);

                var spriteState = (RenderSpriteStudio)renderNode.RenderObject;

                var transfoComp = spriteState.TransformComponent;
                var depthStencilState = DepthStencilStates.DepthRead;

                foreach (var node in spriteState.SpriteStudioComponent.SortedNodes)
                {
                    if (node.Sprite?.Texture == null || node.Sprite.Region.Width <= 0 || node.Sprite.Region.Height <= 0f || node.Hide != 0) continue;

                    // Update the sprite batch

                    BlendStateDescription spriteBlending;
                    switch (node.BaseNode.AlphaBlending)
                    {
                        case SpriteStudioBlending.Mix:
                            spriteBlending = BlendStates.AlphaBlend;
                            break;
                        case SpriteStudioBlending.Multiplication:
                            spriteBlending = MultBlendState;
                            break;
                        case SpriteStudioBlending.Addition:
                            spriteBlending = BlendStates.Additive;
                            break;
                        case SpriteStudioBlending.Subtraction:
                            spriteBlending = SubBlendState;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    // TODO: this should probably be moved to Prepare()
                    // 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(transfoComp.WorldMatrix.TranslationVector, 1.0f);

                    Vector4 projectedPosition;
                    Vector4.Transform(ref worldPosition, ref renderView.ViewProjection, out projectedPosition);
                    var projectedZ = projectedPosition.Z / projectedPosition.W;

                    var blendState = isPicking ? BlendStates.Default : spriteBlending;
                    var currentEffect = isPicking ? GetOrCreatePickingSpriteEffect() : ShadowObject.IsObjectSelected(spriteState.SpriteStudioComponent) ? GetOrCreateSelectedSpriteEffect() : null;
                    // TODO remove this code when material are available
                    if (previousEffect != currentEffect || blendState != previousBlendState || depthStencilState != previousDepthStencilState)
                    {
                        if (hasBegin)
                        {
                            sprite3DBatch.End();
                        }
                        sprite3DBatch.Begin(context.GraphicsContext, renderView.ViewProjection, SpriteSortMode.Deferred, blendState, null, depthStencilState, RasterizerStates.CullNone, currentEffect);
                        hasBegin = true;
                    }

                    previousEffect = currentEffect;
                    previousBlendState = blendState;
                    previousDepthStencilState = depthStencilState;

                    var sourceRegion = node.Sprite.Region;
                    var texture = node.Sprite.Texture;

                    // skip the sprite if no texture is set.
                    if (texture == null)
                        continue;

                    var color4 = Color4.White;
                    if (isPicking)
                    {
                        // TODO move this code corresponding to picking out of the runtime code.
                        color4 = new Color4(RuntimeIdHelper.ToRuntimeId(spriteState.SpriteStudioComponent));
                    }
                    else
                    {
                        if (node.BlendFactor > 0.0f)
                        {
                            switch (node.BlendType) //todo this should be done in a shader
                            {
                                case SpriteStudioBlending.Mix:
                                    color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency;
                                    break;
                                case SpriteStudioBlending.Multiplication:
                                    color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency;
                                    break;
                                case SpriteStudioBlending.Addition:
                                    color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency;
                                    break;
                                case SpriteStudioBlending.Subtraction:
                                    color4 = Color4.Lerp(color4, node.BlendColor, node.BlendFactor) * node.FinalTransparency;
                                    break;
                                default:
                                    throw new ArgumentOutOfRangeException();
                            }
                        }
                        else
                        {
                            color4 *= node.FinalTransparency;
                        }
                    }

                    var worldMatrix = node.ModelTransform*transfoComp.WorldMatrix;

                    // calculate normalized position of the center of the sprite (takes into account the possible rotation of the image)
                    var normalizedCenter = new Vector2(node.Sprite.Center.X/sourceRegion.Width - 0.5f, 0.5f - node.Sprite.Center.Y/sourceRegion.Height);
                    if (node.Sprite.Orientation == ImageOrientation.Rotated90)
                    {
                        var oldCenterX = normalizedCenter.X;
                        normalizedCenter.X = -normalizedCenter.Y;
                        normalizedCenter.Y = oldCenterX;
                    }
                    // apply the offset due to the center of the sprite
                    var size = node.Sprite.Size;
                    var centerOffset = Vector2.Modulate(normalizedCenter, size);
                    worldMatrix.M41 -= centerOffset.X*worldMatrix.M11 + centerOffset.Y*worldMatrix.M21;
                    worldMatrix.M42 -= centerOffset.X*worldMatrix.M12 + centerOffset.Y*worldMatrix.M22;

                    // draw the sprite
                    sprite3DBatch.Draw(texture, ref worldMatrix, ref sourceRegion, ref size, ref color4, node.Sprite.Orientation, SwizzleMode.None, projectedZ);
                }
            }

            if(hasBegin) sprite3DBatch.End();
        }