/// <inheritdoc /> protected override void OnRenderState() { // We're not calling the base implementation, because the default is to // render the scene from the view of an editing camera. The Game View, however, // is in the special position to render the actual game and completely ignore // any editing camera. // // base.OnRenderState(); Point2 clientSize = new Point2(this.RenderableControl.ClientSize.Width, this.RenderableControl.ClientSize.Height); Point2 targetSize = this.TargetRenderSize; Rect windowRect = this.LocalGameWindowRect; Vector2 imageSize; Rect viewportRect; DualityApp.CalculateGameViewport(targetSize, out viewportRect, out imageSize); // Render the game view background using a background color matching editor UI, // so users can discern between an area that isn't rendered to and a rendered // area of the game that happens to be black or outside the game viewport. DrawDevice.RenderVoid(new Rect(clientSize), new ColorRgba(64, 64, 64)); if (this.UseOffscreenBuffer) { // Render the scene to an offscreen buffer of matching size first this.SetupOutputRenderTarget(); DualityApp.Render(this.outputTarget, viewportRect, imageSize); // Blit the offscreen buffer to the window area this.SetupBlitDevice(); this.blitDevice.TargetSize = clientSize; this.blitDevice.ViewportRect = new Rect(clientSize); BatchInfo blitMaterial = this.blitDevice.RentMaterial(); blitMaterial.Technique = DrawTechnique.Solid; blitMaterial.MainTexture = this.outputTexture; TargetResize blitResize = this.TargetSizeFitsClientArea ? TargetResize.None : TargetResize.Fit; this.blitDevice.PrepareForDrawcalls(); this.blitDevice.AddFullscreenQuad(blitMaterial, blitResize); this.blitDevice.Render(); } else { Rect windowViewportRect = new Rect( windowRect.X + viewportRect.X, windowRect.Y + viewportRect.Y, viewportRect.W, viewportRect.H); // Render the scene centered into the designated viewport area this.CleanupRenderTarget(); DrawDevice.RenderVoid(windowRect); DualityApp.Render(null, windowViewportRect, imageSize); } }
/// <summary> /// Resizes rect boundaries to match the specified target size. /// </summary> /// <param name="mode"></param> /// <param name="baseSize"></param> /// <param name="targetSize"></param> /// <returns></returns> public static Vector2 Apply(this TargetResize mode, Vector2 baseSize, Vector2 targetSize) { Vector2 sizeRatio; float scale; switch (mode) { default: case TargetResize.None: return(baseSize); case TargetResize.Stretch: return(targetSize); case TargetResize.Fit: if (baseSize == Vector2.Zero) { baseSize = Vector2.One; } sizeRatio = targetSize / baseSize; if (sizeRatio.Y < sizeRatio.X) { scale = sizeRatio.Y; } else { scale = sizeRatio.X; } return(baseSize * scale); case TargetResize.Fill: if (baseSize == Vector2.Zero) { baseSize = Vector2.One; } sizeRatio = targetSize / baseSize; if (sizeRatio.Y > sizeRatio.X) { scale = sizeRatio.Y; } else { scale = sizeRatio.X; } return(baseSize * scale); } }
protected override void OnRenderScene(Scene scene, ContentRef <RenderTarget> target, Rect viewportRect, Vector2 imageSize) { // Render the entire scene into an internal offscreen target matching settings this.SetupSceneTarget(); base.OnRenderScene( scene, this.sceneTarget, new Rect(this.sceneTarget.Size), this.renderingSize); // Ensure we have a drawing device for screen space operations if (this.drawDevice == null) { this.drawDevice = new DrawDevice(); this.drawDevice.Projection = ProjectionMode.Screen; } // Configure the drawing device to match parameters and settings this.drawDevice.Target = target; this.drawDevice.TargetSize = imageSize; this.drawDevice.ViewportRect = viewportRect; // Blit the results to screen BatchInfo blitMaterial = this.drawDevice.RentMaterial(); blitMaterial.Technique = DrawTechnique.Solid; blitMaterial.MainTexture = this.sceneTargetTex; bool sceneTargetFitsOutput = this.sceneTarget.Size.X <= imageSize.X && this.sceneTarget.Size.Y <= imageSize.Y; TargetResize blitResize = sceneTargetFitsOutput ? TargetResize.None : TargetResize.Fit; this.drawDevice.ClearFlags = ClearFlag.All; this.drawDevice.PrepareForDrawcalls(); this.drawDevice.AddFullscreenQuad(blitMaterial, blitResize); this.drawDevice.Render(); // Draw a screen space diagnostic overlay this.drawDevice.ClearFlags = ClearFlag.Depth; this.drawDevice.PrepareForDrawcalls(); this.DrawDiagnosticOverlay(scene); this.drawDevice.Render(); }
/// <summary> /// Given the specified window size, this method calculates the window rectangle of the rendered /// viewport, as well as the game's rendered image size while taking into account application settings /// regarding forced rendering sizes. /// </summary> /// <param name="windowSize"></param> /// <param name="windowViewport"></param> /// <param name="renderTargetSize"></param> public static void CalculateGameViewport(Point2 windowSize, out Rect windowViewport, out Vector2 renderTargetSize) { Point2 forcedSize = DualityApp.AppData.Instance.ForcedRenderSize; TargetResize forcedResizeMode = DualityApp.AppData.Instance.ForcedRenderResizeMode; renderTargetSize = windowSize; windowViewport = new Rect(renderTargetSize); bool forcedResizeActive = forcedResizeMode != TargetResize.None && forcedSize.X > 0 && forcedSize.Y > 0 && forcedSize != renderTargetSize; if (forcedResizeActive) { Vector2 adjustedViewportSize = forcedResizeMode.Apply(forcedSize, windowViewport.Size); // Clip viewport and target size, so they don't exceed the window size. // This, strictly speaking, violates the forced rendering size, but for // resize modes like Fill, there is no other way to solve this. if (adjustedViewportSize.X > windowSize.X) { forcedSize.X = MathF.RoundToInt((float)forcedSize.X * (float)windowSize.X / (float)adjustedViewportSize.X); adjustedViewportSize.X = windowSize.X; } if (adjustedViewportSize.Y > windowSize.Y) { forcedSize.Y = MathF.RoundToInt((float)forcedSize.Y * (float)windowSize.Y / (float)adjustedViewportSize.Y); adjustedViewportSize.Y = windowSize.Y; } renderTargetSize = forcedSize; windowViewport = Rect.Align( Alignment.Center, windowViewport.Size.X * 0.5f, windowViewport.Size.Y * 0.5f, adjustedViewportSize.X, adjustedViewportSize.Y); } }
/// <summary> /// Generates a single drawcall that renders a fullscreen quad using the specified material. /// Assumes that the <see cref="DrawDevice"/> is set up to render in screen space. /// </summary> /// <param name="material"></param> /// <param name="resizeMode"></param> public void AddFullscreenQuad(BatchInfo material, TargetResize resizeMode) { Texture tex = material.MainTexture.Res; Vector2 uvRatio = tex != null ? tex.UVRatio : Vector2.One; Point2 inputSize = tex != null ? tex.ContentSize : Point2.Zero; // Fit the input material rect to the output size according to rendering step config Vector2 targetSize = resizeMode.Apply(inputSize, this.TargetSize); Rect targetRect = Rect.Align( Alignment.Center, this.TargetSize.X * 0.5f, this.TargetSize.Y * 0.5f, targetSize.X, targetSize.Y); // Fit the target rect to actual pixel coordinates to avoid unnecessary filtering offsets targetRect.X = (int)targetRect.X; targetRect.Y = (int)targetRect.Y; targetRect.W = MathF.Ceiling(targetRect.W); targetRect.H = MathF.Ceiling(targetRect.H); VertexC1P3T2[] vertices = new VertexC1P3T2[4]; vertices[0].Pos = new Vector3(targetRect.LeftX, targetRect.TopY, 0.0f); vertices[1].Pos = new Vector3(targetRect.RightX, targetRect.TopY, 0.0f); vertices[2].Pos = new Vector3(targetRect.RightX, targetRect.BottomY, 0.0f); vertices[3].Pos = new Vector3(targetRect.LeftX, targetRect.BottomY, 0.0f); vertices[0].TexCoord = new Vector2(0.0f, 0.0f); vertices[1].TexCoord = new Vector2(uvRatio.X, 0.0f); vertices[2].TexCoord = new Vector2(uvRatio.X, uvRatio.Y); vertices[3].TexCoord = new Vector2(0.0f, uvRatio.Y); vertices[0].Color = ColorRgba.White; vertices[1].Color = ColorRgba.White; vertices[2].Color = ColorRgba.White; vertices[3].Color = ColorRgba.White; this.AddVertices(material, VertexMode.Quads, vertices); }