logo
Main

Forums

Downloads

Unreal-Netiquette

Donate for Oldunreal:
Donate

borderline

Links to our wiki:
Wiki

Walkthrough

Links

Tutorials

Unreal Reference

Usermaps

borderline

Contact us:
Submit News
Page Index Toggle Pages: 1 2 [3]  Send TopicPrint
Very Hot Topic (More than 25 Replies) HUD scaling (Read 7013 times)
Masterkent
Developer Team
Offline



Posts: 852
Location: Russia
Joined: Apr 5th, 2013
Gender: Male
Re: HUD scaling
Reply #30 - Jun 14th, 2016 at 3:39pm
Print Post  
han wrote on Jun 14th, 2016 at 3:23pm:
I don't quite get how a modified ClipX/ClipY has any influence on the scaling

Modification of ClipX and ClipY is a hack which is supposed to make scripts draw HUD at lower resolution (when scale is higher than 1).

Quote:
or is this a 227 thing?

Modification of ClipX and ClipY is not.
SetTile3DOffset (which is used for stretching the HUD drawn in low resolution to full screen) is a 227 function. Perhaps, we need other function for better stretching though.
  
Back to top
 
IP Logged
 
han
Global Moderator
Unreal Rendering Guru
Developer Team
*****
Offline


Oldunreal member

Posts: 517
Location: Germany
Joined: Dec 10th, 2014
Gender: Male
Re: HUD scaling
Reply #31 - Jun 28th, 2016 at 8:37am
Print Post  
Slightly related, recently I'm messing around a bit with fonts, and did an 2x Version of the Engine.SmallFont.

  

HX on Mod DB. Revision on Steam.
Back to top
 
IP Logged
 
Masterkent
Developer Team
Offline



Posts: 852
Location: Russia
Joined: Apr 5th, 2013
Gender: Male
Re: HUD scaling
Reply #32 - Aug 11th, 2017 at 6:44pm
Print Post  
Preliminary description of implementing HUD scaling support.

There are two substantially different approaches to scaling: drawing the image using physical screen coordinates and parametrized detail level with no stretching or drawing the image using virtual screen coordinates and static detail level with stretching. These approaches can be applied together: some parts of HUD can be drawn with higher detail, some parts can be drawn with stretching.

Here I outline what should or could be done in order to implement some basic support of HUD scaling as a combination of the given two approaches where the second one would initially prevail. Such implementation of scaling should be compatible with the majority of existing custom mods when using scale factors above 1, albeit 100% compatibility probably cannot be guaranteed, no matter what we do. Users who continue to use the standard scale factor 1 should not notice any differences, except possible fixes for various issues that can be applied in the process of working on the advanced functionality. That is, this new feature is not mandatory to use, hence if users encounter any compatibility problems when enabling it, they can disable it at any moment by setting the HUD scale factor to 1.

Using this description, we can estimate the volume of work for each side that would agree to participate in implementing the given feature set. Some details are intentionally omitted, because considering all things in depth doesn't make sense if the chosen direction won't be approved. After approval and creating an initial basic implementation, we can polish it later.

The initial implementation should serve as a proof of concept; in particular, we could check how well we can scale HUD elements drawn by third-party implementations, such as the ones used in JCoopZ, EDM6, XCoop, UTeamFix, etc. If we get satisfactory results, we can think about further tweaking (in order to achieve better compatibility with mods); otherwise, we can pick other direction.

Alternatively, we could pick the first approach exclusively. It needs much less changes in the C++ code base, but then any parts of HUD drawn by third-party scripts won't be scaled unless those scripts provide their own implementation of scaling.

--------------------------------------------------------------------------------

Note that this is only a preliminary list of changes to consider, it can be amended at any moment.

1. Add new data member HudScale in Engine.HUD:

Code
Select All
	var globalconfig int HudMode;
+	var globalconfig int HudScale;
	var globalconfig int Crosshair;
	var() class<menu> MainMenuType;
	var() string HUDConfigWindowType; 


2. Modify Engine.Canvas.

2.1. Add new data members ScreenWidth, ScreenHeight, ScreenOffsetX, ScreenOffsetY, ScreenScale:

Code
Select All
	// Modifiable properties.
	var font    Font;            // Font for DrawText.
	var float   SpaceX, SpaceY;  // Spacing for after Draw*.
	var float   OrgX, OrgY;      // Origin for drawing.
	var float   ClipX, ClipY;    // Bottom right clipping region.
	var float   CurX, CurY;      // Current position for drawing.
	var float   Z;               // Z location. 1=no screenflash, 2=yes screenflash.
	var byte    Style;           // Drawing style STY_None means don't draw.
	var float   CurYL;           // Largest Y size since DrawText.
	var color   DrawColor;       // Color for drawing.
	var bool    bCenter;         // Whether to center the text.
	var bool    bNoSmooth;       // Don't bilinear filter.
	var const bool bUseOffset,bUseFlatZ; // Used by 3D tile offset.
-	var const int SizeX, SizeY;  // Zero-based actual dimensions.
+	var const int SizeX, SizeY;  // Scaled zero-based dimensions of the screen.
+	var const int ScreenWidth;   // Screen width in physical pixels.
+	var const int ScreenHeight;  // Screen height in physical pixels.
+	var const int ScreenOffsetX; // Primary origin for drawing text and tiles.
+	var const int ScreenOffsetY; // Primary origin for drawing text and tiles.
+	var int     ScreenScale;     // Scale for drawing text and tiles. 


ScreenWidth and SizeX initially should be set to the same value; similarly for ScreenHeight and SizeY.
ScreenOffsetX and ScreenOffsetY should be initialized with zero value.
ScreenScale should be initialized with value 1.

2.2. Add new functions SetScreenScale and SetHUDScreenScale:

Code
Select All
// SetScreenScale can be used to draw text and tiles using reduced screen resolution and stretch the
// resulting image to the original screen size (or nearly so).
// The function works as if it had the following UScript definition while all modified data members were
// declared without the const qualifier
//
//	function bool SetScreenScale(int MainScale, optional int SpecialScale)
//	{
//		if (MainScale < 1 || SizeX / MainScale < 320 || SizeY / MainScale < 240)
//			return false;
//
//		if (SpecialScale == 0)
//		{
//			SizeX = ScreenWidth / MainScale;
//			SizeY = ScreenHeight / MainScale;
//			ScreenOffsetX = (ScreenWidth - SizeX * MainScale) / 2;
//			ScreenOffsetY = (ScreenHeight - SizeY * MainScale) / 2;
//			ClipX = SizeX;
//			ClipY = SizeY;
//			if (Viewport != none && Viewport.Console != none)
//			{
//				Viewport.Console.FrameX = SizeX;
//				Viewport.Console.FrameY = SizeY;
//			}
//			ScreenScale = MainScale;
//			return true;
//		}
//
//		if (SpecialScale < 0 || MainScale / SpecialScale * SpecialScale != MainScale)
//			return false;
//
//		SizeX = ScreenWidth / MainScale * MainScale / SpecialScale;  // may be less than ScreenWidth / SpecialScale
//		SizeY = ScreenHeight / MainScale * MainScale / SpecialScale; // may be less than ScreenHeight / SpecialScale
//		ScreenOffsetX = (ScreenWidth - ScreenWidth / MainScale * MainScale) / 2;
//		ScreenOffsetY = (ScreenHeight - ScreenHeight / MainScale * MainScale) / 2;
//		ClipX = SizeX;
//		ClipY = SizeY;
//		if (Viewport != none && Viewport.Console != none)
//		{
//			Viewport.Console.FrameX = SizeX;
//			Viewport.Console.FrameY = SizeY;
//		}
//		ScreenScale = SpecialScale;
//		return true;
//	}

native function bool SetScreenScale(int MainScale, optional int SpecialScale);

// SetHUDScreenScale specification:
// Effects: returns SetScreenScale(class'HUD'.default.HudScale, SpecialScale)
// Note: calling SetHUDScreenScale(1) usually should be preferred to calling SetScreenScale(1) when
//       drawing HD parts of HUD that are not supposed to be stretched, then stretched and
//       nonstretched elements have the same bounds at the right and bottom borders of the screen.
//       F.e., for physical resolution 2560x1440 and HudScale == 3, the left top point of the
//       bounding rectangle should be {0, 0} and its right bottom point should be {2559, 1440};
//       for physical resolution 2048x1536 and HudScale == 3, the left top point of the
//       bounding rectangle should be {1, 0} and its right bottom point should be {2047, 1536}.

function bool SetHUDScreenScale(optional int SpecialScale)
{
	return SetScreenScale(class'HUD'.default.HudScale, SpecialScale);
} 


Although it's unlikely that someone would want to override these functions in subclasses, I'd prefer to have them not declared with the final keyword. Calling new final functions is a real PITA when maintaining compatibility with old versions of the game, because such calls may cause failure to load the entire package containing them, while calls to non-final functions do not cause any troubles as long as they are not evaluated in run-time (for a given game version, it's possible to choose the best compatible implementation using Level.EngineVersion). When facing with incompatibilities caused by declaring functions as final, a script programmer has to use some hacks, e.g., manually remove the final keyword, recompile the standard *.u package(s) and compile custom scripts against the hacked reference code.

Note that in some situations we cannot properly stretch the HUD drawn in low resolution to the full screen. For example, when reducing 2048x1536 three times and rounding the resulting sizes to integer numbers (I think, allowing non-integer width or height would be a bad idea), we get 682x512. After stretching the small rectangle using the same factor 3, we get 2046x1536. This bounding rectangle should be placed in the middle of the screen (leaving 2 vertical one-pixel width lines at the left and the right borders of the screen unused). This is for what ScreenOffsetX and ScreenOffsetY are used.

When drawing HD parts of HUD with temporarily disabled stretching, it makes sense to use the same bounding rectangle which is used for the stretched parts of HUD, this is why SetScreenScale has parameter SpecialScale that overrides MainScale (then MainScale is used only to determine the bounding rectangle).

2.3. Modify functions that draw text and tiles as described below.

2.3.1. Add parameters XScale and YScale in functions DrawText and DrawTextClipped:

final function DrawText( coerce string Text, optional bool CR, optional float XScale, optional float YScale );
final function DrawTextClipped( coerce string Text, optional bool bCheckHotKey, optional float XScale, optional float YScale );

The meaning is similar to UE3/4 params. When XScale is zero, value 1 is used instead, similarly for YScale.

2.3.2. For functions

DrawText
DrawTile
DrawTileClipped
DrawTextClipped

the following replacements should be done for the purposes of drawing:

     OrgX -> ScreenOffsetX + OrgX * ScreenScale
     OrgY -> ScreenOffsetY + OrgY * ScreenScale
     CurX -> CurX * ScreenScale
     CurY -> CurY * ScreenScale

Additionally, for non-zero values XScale, YScale, XL, YL, arbitrary suitable values Text, CR, bCheckHotKey, Tex, U, V, UL, VL, and ScreenScale == N where N > 1,

- the behavior of DrawText(Text, CR, XScale, YScale) should be equivalent to

DrawText(Text, CR, XScale * N, YScale * N) while ScreenScale == 1,

- the behavior of DrawTile(Tex, XL, YL, U, V, UL, VL) should be equivalent to

DrawTile(Tex, XL * N, YL * N, U, V, UL, VL) while ScreenScale == 1,

- the behavior of DrawTileClipped(Tex, XL, YL, U, V, UL, VL) should be equivalent to

DrawTileClipped(Tex, XL * N, YL * N, U, V, UL, VL) while ScreenScale == 1,

- the behavior of DrawTextClipped(Text, bCheckHotKey, XScale, YScale) should be equivalent to

DrawTextClipped(Text, bCheckHotKey, XScale * N, YScale * N) while ScreenScale == 1.

That is, ScreenScale serves as additional scaling factor.

3. Add fonts Tahoma40, TahomaB40, Tahoma60, and TahomaB60 in UWindowFonts.utx.

4. Set appropriate screen scale before drawing HUD or its parts and when finishing drawing such things.

4.1. Insert an equivalent of

Code
Select All
if (class'HUD'.default.HudScale)
	Canvas.SetHUDScreenScale(); 


before invoking events

PlayerPawn.PreRender
PlayerPawn.PostRender
Console.PostRender

and an equivalent of

Code
Select All
if (class'HUD'.default.HudScale)
	Canvas.SetScreenScale(1); 


after invoking these events.

4.2. Modify function UWindow.WindowConsole.UWindow.PostRender as follows:

Code
Select All
	event PostRender( canvas Canvas )
	{
		local int j,l;

		if ( Viewport.Actor.myHUD != None )
			Viewport.Actor.myHUD.DisplayMessages(Canvas);
		if ( TimeDemo != None )
			TimeDemo.PostRender( Canvas );
+		Canvas.SetScreenScale(1);
		l = Array_Size(Interactions);
		for ( j=0; j<l; j++ )
		{
			if ( Interactions[j].bRequestRender )
				Interactions[j].PostRender(Canvas);
		}
		if (Root != None)
			Root.bUWindowActive = True;
		RenderUWindow( Canvas );
	} 


4.3. Modify function UWindow.WindowConsole.PostRender as follows:

Code
Select All
event PostRender( canvas Canvas )
{
	local int j,l;

	if ( !bCreatedRoot )
	{
		bShowConsole = False;
+ 	  Canvas.SetScreenScale(1);
		CreateRootWindow(Canvas);
	}
+	Canvas.SetHUDScreenScale();
	Super.PostRender(Canvas);
+	Canvas.SetScreenScale(1);
	l = Array_Size(Interactions);
	for ( j=0; j<l; j++ )
	{
		if ( Interactions[j].bRequestRender )
			Interactions[j].PostRender(Canvas);
	}
} 


4.4. Modify function UWindow.WindowConsole.CreateRootWindow as follows:

Code
Select All
function CreateRootWindow(Canvas Canvas)
{
	local int i;
	local string OutText;
	local class<UWindowRootWindow> RootClass;

+	Canvas.SetScreenScale(1);

	if (Canvas != None)
	{
		OldClipX = Canvas.ClipX;
		OldClipY = Canvas.ClipY;
	}
	else 


4.5. Modify function UWindow.WindowConsole.RenderUWindow as follows:

Code
Select All
function RenderUWindow( canvas Canvas )
{
	local UWindowWindow NewFocusWindow;

+	Canvas.SetScreenScale(1);

	Canvas.bNoSmooth = True;
	Canvas.Z = 1;
	Canvas.Style = 1;
	Canvas.DrawColor.r = 255;
	Canvas.DrawColor.g = 255;
	Canvas.DrawColor.b = 255;

	if (Viewport.bWindowsMouseAvailable && Root != None) 


4.6. Modify function UnrealShare.Translator as follows:

Code
Select All
	var globalconfig float TranslatorScale;
-	var string FontNames[4];
-	var font LoadedFonts[4];
+	var string FontNames[10];
+	var font LoadedFonts[10];
	var() texture HiResHUD,LowResHUD; 



Code
Select All
simulated function DrawTranslator( Canvas Canvas )
{
-	local float TempX,TempY;
+	local float OldClipX, OldClipY, OldSpaceX;
	local string CurrentMessage;
	local byte i;

	Canvas.bCenter = false;
-	TempX = Canvas.ClipX;
-	TempY = Canvas.ClipY;
+	OldClipX = Canvas.ClipX;
+	OldClipY = Canvas.ClipY;
+	OldSpaceX = Canvas.SpaceX;
	if ( bShowHint && Len(Hint)!=0 )
		CurrentMessage = HintString@Hint;
	else CurrentMessage = NewMessage;
	Canvas.Style = 2;

+	if (TranslatorScale > 0)
+		Canvas.SetHUDScreenScale(1);

-	if( TranslatorScale<=1.f )
+	if (TranslatorScale <= 1.0)
	{
		Canvas.SetPos(Canvas.ClipX/2-128, Canvas.ClipY/2-68);
		Canvas.DrawIcon(LowResHUD, 1.0);
		Canvas.SetOrigin(Canvas.ClipX/2-110,Canvas.ClipY/2-52);
		Canvas.SetClip(225,110);
		Canvas.SetPos(0,0);
		Canvas.Font = Canvas.MedFont;
	}
	else
	{
		Canvas.SetPos(Canvas.ClipX/2-128*TranslatorScale, Canvas.ClipY/2-68*TranslatorScale);
		Canvas.DrawTile( HiResHUD, TranslatorScale*256.f, TranslatorScale*256.f, 0, 0, HiResHUD.USize, HiResHUD.VSize );
-		if( TranslatorScale<1.3f )
-			Canvas.Font = Canvas.MedFont;
-		else
-		{
-			if( TranslatorScale<=1.7f )
-				i = 0;
-			else if( TranslatorScale<=2.35f )
-				i = 1;
-			else if( TranslatorScale<=4.25f )
-				i = 2;
-			else i = 3;
-			if( LoadedFonts[i]==None )
-			{
-				LoadedFonts[i] = Font(DynamicLoadObject(FontNames[i],Class'Font'));
-				if( LoadedFonts[i]==None )
-					LoadedFonts[i] = Canvas.MedFont;
-			}
-			Canvas.Font = LoadedFonts[i];
-			Canvas.DrawColor = MakeColor(0,255,0);
-		}
+
+		if (TranslatorScale < 1.2)
+			i = 0;
+		else if (TranslatorScale < 1.4)
+			i = 1;
+		else if (TranslatorScale < 1.6)
+			i = 2;
+		else if (TranslatorScale < 1.8)
+			i = 3;
+		else if (TranslatorScale < 2.0)
+			i = 4;
+		else if (TranslatorScale < 2.4)
+			i = 5;
+		else if (TranslatorScale < 3.0)
+			i = 6;
+		else if (TranslatorScale < 4.0)
+			i = 7;
+		else if (TranslatorScale < 6.0)
+			i = 8;
+		else
+			i = 9;
+
+		if (LoadedFonts[i] == none)
+		{
+			LoadedFonts[i] = Font(DynamicLoadObject(FontNames[i], Class'Font'));
+			if (LoadedFonts[i] == none)
+				LoadedFonts[i] = Canvas.MedFont;
+		}
+		Canvas.Font = LoadedFonts[i];
+		Canvas.DrawColor = MakeColor(0, 255, 0);
+		Canvas.SpaceX = 1;
-		Canvas.SetOrigin(Canvas.ClipX/2-100*TranslatorScale,Canvas.ClipY/2-52*TranslatorScale);
+		Canvas.SetOrigin(Canvas.ClipX/2 - 107 * TranslatorScale, Canvas.ClipY/2 - 52 * TranslatorScale);
-		Canvas.SetClip(205*TranslatorScale,110*TranslatorScale);
+		Canvas.SetClip(215 * TranslatorScale, 110 * TranslatorScale);
		Canvas.SetPos(0,0);
	}
	Canvas.Style = 1;
	Canvas.DrawText(CurrentMessage, False);
	Canvas.ClipX = OldClipX;
	Canvas.ClipY = OldClipY;
	Canvas.SpaceX = OldSpaceX;
	Canvas.Font = Canvas.MedFont;
	Canvas.DrawColor = MakeColor(255,255,255);
+	if (TranslatorScale > 0)
+		Canvas.SetHUDScreenScale();
} 


Code
Select All
defaultproperties
{
				TranslatorScale=1.000000
-				FontNames(0)="UWindowFonts.Tahoma14"
-				FontNames(1)="UWindowFonts.Tahoma16"
-				FontNames(2)="UWindowFonts.Tahoma20"
-				FontNames(3)="UWindowFonts.Tahoma30"
+				FontNames(0)="UWindowFonts.Tahoma10"
+				FontNames(1)="UWindowFonts.Tahoma12"
+				FontNames(2)="UWindowFonts.Tahoma14"
+				FontNames(3)="UWindowFonts.Tahoma16"
+				FontNames(4)="UWindowFonts.Tahoma18"
+				FontNames(5)="UWindowFonts.Tahoma20"
+				FontNames(6)="UWindowFonts.Tahoma14"
+				FontNames(7)="UWindowFonts.Tahoma30"
+				FontNames(8)="UWindowFonts.Tahoma40"
+				FontNames(9)="UWindowFonts.Tahoma60"
				HiResHUD=Texture'UnrealShare.Icons.TranslatorHUDHD'
				LowResHUD=Texture'UnrealShare.Icons.TranslatorHUD3' 


The last change introduces observable differences for all scale factors of HUD (including 1). It's strange to have small text on 3x and 4x-sized Translator. However, this change implies that long strings which could fit in scaled Translator's rectangle before may not fit in it after applying the change.
« Last Edit: Aug 11th, 2017 at 7:57pm by Masterkent »  
Back to top
 
IP Logged
 
han
Global Moderator
Unreal Rendering Guru
Developer Team
*****
Offline


Oldunreal member

Posts: 517
Location: Germany
Joined: Dec 10th, 2014
Gender: Male
Re: HUD scaling
Reply #33 - Aug 12th, 2017 at 8:10am
Print Post  
DrawTile() won't need additional parameters, you can already draw anisotropic scaled Tiles with it.

For Fonts, I think best way would rather be to manually create 2x/3x versions of the Fonts or whatever is needed and import than, rather than support drawing them scaled over DrawText. In any case any non integer scale will look odd for them anyway.

In general I think the best way would be to always work on the physical Canvas size and have the UI parts it's on code for drawing for a particular sceen size. Still, some common config variable which would determine the user preference for UI scale would be nice, so code can use it to determine intended size.
  

HX on Mod DB. Revision on Steam.
Back to top
 
IP Logged
 
Masterkent
Developer Team
Offline



Posts: 852
Location: Russia
Joined: Apr 5th, 2013
Gender: Male
Re: HUD scaling
Reply #34 - Aug 12th, 2017 at 11:48am
Print Post  
han wrote on Aug 12th, 2017 at 8:10am:
DrawTile() won't need additional parameters, you can already draw anisotropic scaled Tiles with it.

The suggested property Canvas.ScreenScale is not a function parameter, it's a variable that can be changed globally and affect several calls to DrawTile. We have a lot of legacy code written by third parties, and probably nobody will fix every separate call to DrawTile there by substitution of proper arguments that would imply scaling.

han wrote on Aug 12th, 2017 at 8:10am:
For Fonts, I think best way would rather be to manually create 2x/3x versions of the Fonts or whatever is needed and import than, rather than support drawing them scaled over DrawText.

We can use that approach only in a code base which is under our control. If we do not implement scaling for existing custom HUD implementations, then some users will notice that they cannot use the new setting to scale HUD when playing on some servers online (for example, on servers that use JCoopZ, XCoop, EDM6, or UTeamFix).

han wrote on Aug 12th, 2017 at 8:10am:
In any case any non integer scale will look odd for them anyway.

That's true for thin fonts, but I'm not sure about fonts like LargeFont.

han wrote on Aug 12th, 2017 at 8:10am:
In general I think the best way would be to always work on the physical Canvas size and have the UI parts it's on code for drawing for a particular sceen size.

The best way is the combination of the two approaches, because then we can simultaneously achieve the best quality where we can implement HD drawing and the best compatibility with third-party implementations for which the only thing we can do is to offer the following choice: if you can scale your custom HUD properly, disable our screen virtualization mechanism and draw scaled parts using your own means, otherwise we will upscale your low-res HUD using the virtualization.

han wrote on Aug 12th, 2017 at 8:10am:
Still, some common config variable which would determine the user preference for UI scale would be nice, so code can use it to determine intended size.

I think that using only a single common setting for UI windows and HUD would not be a good idea. If a user face with some troubles caused by HUD scaling, he should be able to disable that particular kind of scaling without altering scaling of UI windows. However, we can define that for the case of class'Hud'.default.HudScale == 0 the HUD scale should be assumed to be equal to int(class'UWindowRootWindow'.default.GUIScale). Implementing this may be a bit tricky though, because then Engine.u stuff will have a dependency on a property defined in UWindow.u.
« Last Edit: Aug 12th, 2017 at 4:31pm by Masterkent »  
Back to top
 
IP Logged
 
Page Index Toggle Pages: 1 2 [3] 
Send TopicPrint
Bookmarks: del.icio.us Digg Facebook Google Google+ Linked in reddit StumbleUpon Twitter Yahoo