Пример #1
0
        /// <summary>
        /// Draw this effect mesh.
        /// </summary>
        public void Draw(RenderContext context)
        {
            // Retrieve effect parameters
            var mesh = Mesh;
            var currentRenderData = mesh.Draw;
            var material          = Material;
            var vao           = vertexArrayObject;
            var drawCount     = currentRenderData.DrawCount;
            var primitiveType = currentRenderData.PrimitiveType;

            parameters.Set(TransformationKeys.World, WorldMatrix);

            // TODO: We should clarify exactly how to override rasterizer states. Currently setup here on Context.Parameters to allow Material/ModelComponent overrides, but this is ugly
            context.Parameters.Set(Effect.RasterizerStateKey, RasterizerState);

            if (context.IsPicking()) // TODO move this code corresponding to picking outside of the runtime code!
            {
                parameters.Set(ModelComponentPickingShaderKeys.ModelComponentId, new Color4(RenderModel.ModelComponent.Id));
                parameters.Set(ModelComponentPickingShaderKeys.MeshId, new Color4(Mesh.NodeIndex));
                parameters.Set(ModelComponentPickingShaderKeys.MaterialId, new Color4(Mesh.MaterialIndex));

                // Don't use the materials blend state on picking targets
                parameters.Set(Effect.BlendStateKey, null);
            }

            if (material != null && material.TessellationMethod != ParadoxTessellationMethod.None)
            {
                var tessellationMethod = material.TessellationMethod;

                // adapt the primitive type and index buffer to the tessellation used
                if (tessellationMethod.PerformsAdjacentEdgeAverage())
                {
                    vao       = GetOrCreateVertexArrayObjectAEN(context);
                    drawCount = 12 / 3 * drawCount;
                }
                primitiveType = tessellationMethod.GetPrimitiveType();
            }

            //using (Profiler.Begin(ProfilingKeys.PrepareMesh))
            {
                // Order of application of parameters:
                // - RenderPass.Parameters
                // - ModelComponent.Parameters
                // - RenderMesh.Parameters (originally copied from mesh parameters)
                // The order is based on the granularity level of each element and how shared it can be. Material is heavily shared, a model contains many meshes. An renderMesh is unique.
                // TODO: really copy mesh parameters into renderMesh instead of just referencing the meshDraw parameters.

                //var modelComponent = RenderModel.ModelComponent;
                //var hasModelComponentParams = modelComponent != null && modelComponent.Parameters != null;

                //var materialParameters = material != null && material.Parameters != null ? material.Parameters : null;

                parameterCollections.Clear();

                parameterCollections.Add(context.Parameters);
                FillParameterCollections(ref parameterCollections);

                // Check if we need to recreate the EffectParameterCollectionGroup
                // TODO: We can improve performance by redesigning FillParameterCollections to avoid ArrayExtensions.ArraysReferenceEqual (or directly check the appropriate parameter collections)
                // This also happens in another place: DynamicEffectCompiler (we probably want to factorize it when doing additional optimizations)
                if (parameterCollectionGroup == null || parameterCollectionGroup.Effect != Effect || !ArrayExtensions.ArraysReferenceEqual(ref previousParameterCollections, ref parameterCollections))
                {
                    previousParameterCollections.Clear();
                    previousParameterCollections.AddRange(parameterCollections);
                    parameterCollectionGroup = new EffectParameterCollectionGroup(context.GraphicsDevice, Effect, previousParameterCollections.Count, previousParameterCollections.Items);
                }

                Effect.Apply(context.GraphicsDevice, parameterCollectionGroup, true);
            }

            //using (Profiler.Begin(ProfilingKeys.RenderMesh))
            {
                if (currentRenderData != null)
                {
                    var graphicsDevice = context.GraphicsDevice;

                    graphicsDevice.SetVertexArrayObject(vao);

                    if (currentRenderData.IndexBuffer == null)
                    {
                        graphicsDevice.Draw(primitiveType, drawCount, currentRenderData.StartLocation);
                    }
                    else
                    {
                        graphicsDevice.DrawIndexed(primitiveType, drawCount, currentRenderData.StartLocation);
                    }
                }
            }
        }
Пример #2
0
        protected override void DrawCore(RenderContext context, RenderItemCollection renderItems, int fromIndex, int toIndex)
        {
            // build the list of the UI elements to render
            uiElementStates.Clear();
            for (var i = fromIndex; i <= toIndex; ++i)
            {
                var renderItem = renderItems[i];
                uiElementStates.Add((UIComponentProcessor.UIComponentState)renderItem.DrawContext);
            }

            // evaluate the current draw time (game instance is null for thumbnails)
            var drawTime = game != null ? game.DrawTime : new GameTime();

            // update the rendering context
            renderingContext.Time         = drawTime;
            renderingContext.RenderTarget = CurrentRenderFrame.RenderTargets[0]; // TODO: avoid hardcoded index 0

            // cache the ratio between viewport and target.
            var viewportSize = context.GraphicsDevice.Viewport.Size;

            viewportTargetRatio = new Vector2(viewportSize.X / renderingContext.RenderTarget.Width, viewportSize.Y / renderingContext.RenderTarget.Height);

            // compact all the pointer events that happened since last frame to avoid performing useless hit tests.
            CompactPointerEvents();

            // allocate temporary graphics resources if needed
            Texture scopedDepthBuffer = null;

            foreach (var uiElement in uiElementStates)
            {
                if (uiElement.UIComponent.IsFullScreen)
                {
                    var renderTarget = renderingContext.RenderTarget;
                    var description  = TextureDescription.New2D(renderTarget.Width, renderTarget.Height, PixelFormat.D24_UNorm_S8_UInt, TextureFlags.DepthStencil);
                    scopedDepthBuffer = PushScopedResource(context.Allocator.GetTemporaryTexture(description));
                    break;
                }
            }

            // render the UI elements of all the entities
            foreach (var uiElementState in uiElementStates)
            {
                var uiComponent = uiElementState.UIComponent;
                var rootElement = uiComponent.RootElement;
                if (rootElement == null)
                {
                    continue;
                }

                var updatableRootElement = (IUIElementUpdate)rootElement;

                // calculate the size of the virtual resolution depending on target size (UI canvas)
                var virtualResolution = uiComponent.VirtualResolution;
                var targetSize        = new Vector2(renderingContext.RenderTarget.Width, renderingContext.RenderTarget.Height);
                if (uiComponent.IsFullScreen)
                {
                    // update the virtual resolution of the renderer
                    if (uiComponent.VirtualResolutionMode == VirtualResolutionMode.FixedWidthAdaptableHeight)
                    {
                        virtualResolution.Y = virtualResolution.X * targetSize.Y / targetSize.X;
                    }
                    if (uiComponent.VirtualResolutionMode == VirtualResolutionMode.FixedHeightAdaptableWidth)
                    {
                        virtualResolution.X = virtualResolution.Y * targetSize.X / targetSize.Y;
                    }
                }

                // Update the view parameters
                if (uiComponent.IsFullScreen)
                {
                    viewParameters.Update(uiComponent.Entity, virtualResolution);
                }
                else
                {
                    var cameraComponent = context.Tags.Get(CameraComponentRenderer.Current);
                    viewParameters.Update(uiComponent.Entity, cameraComponent);
                }

                // Analyze the input and trigger the UI element touch and key events
                // Note: this is done before measuring/arranging/drawing the element in order to avoid one frame latency on clicks.
                //       But by doing so the world matrices taken for hit test are the ones calculated during last frame.
                using (Profiler.Begin(UIProfilerKeys.TouchEventsUpdate))
                {
                    foreach (var uiState in uiElementStates)
                    {
                        if (uiState.UIComponent.RootElement == null)
                        {
                            continue;
                        }

                        UpdateMouseOver(uiState);
                        UpdateTouchEvents(uiState, drawTime);
                    }
                }

                // update the rendering context values specific to this element
                renderingContext.Resolution           = virtualResolution;
                renderingContext.ViewMatrix           = viewParameters.ViewMatrix;
                renderingContext.ProjectionMatrix     = viewParameters.ProjectionMatrix;
                renderingContext.ViewProjectionMatrix = viewParameters.ViewProjectionMatrix;
                renderingContext.DepthStencilBuffer   = uiComponent.IsFullScreen ? scopedDepthBuffer : CurrentRenderFrame.DepthStencil;
                renderingContext.ShouldSnapText       = uiComponent.SnapText;

                // calculate an estimate of the UI real size by projecting the element virtual resolution on the screen
                var virtualOrigin            = viewParameters.ViewProjectionMatrix.Row4;
                var virtualWidth             = new Vector4(virtualResolution.X / 2, 0, 0, 1);
                var virtualHeight            = new Vector4(0, virtualResolution.Y / 2, 0, 1);
                var transformedVirtualWidth  = Vector4.Zero;
                var transformedVirtualHeight = Vector4.Zero;
                for (int i = 0; i < 4; i++)
                {
                    transformedVirtualWidth[i]  = virtualWidth[0] * viewParameters.ViewProjectionMatrix[0 + i] + viewParameters.ViewProjectionMatrix[12 + i];
                    transformedVirtualHeight[i] = virtualHeight[1] * viewParameters.ViewProjectionMatrix[4 + i] + viewParameters.ViewProjectionMatrix[12 + i];
                }
                var projectedOrigin        = virtualOrigin.XY() / virtualOrigin.W;
                var projectedVirtualWidth  = viewportSize * (transformedVirtualWidth.XY() / transformedVirtualWidth.W - projectedOrigin);
                var projectedVirtualHeight = viewportSize * (transformedVirtualHeight.XY() / transformedVirtualHeight.W - projectedOrigin);

                // update layouting context.
                layoutingContext.VirtualResolution          = virtualResolution;
                layoutingContext.RealResolution             = viewportSize;
                layoutingContext.RealVirtualResolutionRatio = new Vector2(projectedVirtualWidth.Length() / virtualResolution.X, projectedVirtualHeight.Length() / virtualResolution.Y);
                rootElement.LayoutingContext = layoutingContext;

                // perform the time-based updates of the UI element
                updatableRootElement.Update(drawTime);

                // update the UI element disposition
                rootElement.Measure(virtualResolution);
                rootElement.Arrange(virtualResolution, false);

                // update the UI element hierarchical properties
                var rootMatrix = Matrix.Translation(-virtualResolution / 2); // UI world is rotated of 180degrees along Ox
                updatableRootElement.UpdateWorldMatrix(ref rootMatrix, rootMatrix != uiElementState.LastRootMatrix);
                updatableRootElement.UpdateElementState(0);
                uiElementState.LastRootMatrix = rootMatrix;

                // clear and set the Depth buffer as required
                if (uiComponent.IsFullScreen)
                {
                    context.GraphicsDevice.Clear(renderingContext.DepthStencilBuffer, DepthStencilClearOptions.DepthBuffer | DepthStencilClearOptions.Stencil);
                }
                context.GraphicsDevice.SetDepthAndRenderTarget(renderingContext.DepthStencilBuffer, renderingContext.RenderTarget);

                // start the image draw session
                renderingContext.StencilTestReferenceValue = 0;
                batch.Begin(ref viewParameters.ViewProjectionMatrix, context.GraphicsDevice.BlendStates.AlphaBlend, uiSystem.KeepStencilValueState, renderingContext.StencilTestReferenceValue);

                // Render the UI elements in the final render target
                ReccursiveDrawWithClipping(context, rootElement);

                // end the image draw session
                batch.End();
            }

            // clear the list of compacted pointer events of time frame
            ClearPointerEvents();

            // revert the depth stencil buffer to the default value
            context.GraphicsDevice.SetDepthAndRenderTargets(CurrentRenderFrame.DepthStencil, CurrentRenderFrame.RenderTargets);
        }