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