For direct access use https://forums.oldunreal.com
It's been quite a while since oldunreal had an overhaul, but we are moving to another server which require some updates and changes. The biggest change is the migration of our old reliable YaBB forum to phpBB. This system expects you to login with your username and old password known from YaBB.
If you experience any problems there is also the usual "password forgotten" function. Don't forget to clear your browser cache!
If you have any further concerns feel free to contact me: Smirftsch@oldunreal.com

[UnExt] Extending interface in UnrealGames.

Here you can add and find information and help for other Unreal Engine titles. Currently for UE1 and UE2 based software.
Post Reply
User avatar
han
Global Moderator
Posts: 686
Joined: Wed Dec 10, 2014 12:38 am

[UnExt] Extending interface in UnrealGames.

Post by han »

Current release:
http://coding.hanfling.de/launch/release/UnExt-20150504.zip

Description:
Unlinke in Unreal 227, the interfaces such as URenderDevice, UAudioSubsystem, UTexture, etc. are rather fixed. So if one would want to extend that functionality one would need to pull in either other packages and thus making these a requirement and largely incompatible, or one adds simply adding console commands. However both solutions are not really satisfying.

I can up with a powerful solution using the FUnknown (COM) interface, which is completely unused, which does not require linking and thus not pulling in any other dependencies.

Code: Select all

//
// COM IUnknown interface.
//
class CORE_API FUnknown
{
public:
	virtual DWORD STDCALL QueryInterface( const FGuid& RefIID, void** InterfacePtr ) {return 0;}
	virtual DWORD STDCALL AddRef() {return 0;}
	virtual DWORD STDCALL Release() {return 0;}
};
So basic approach is to have some public available header set, where the interfaces and guid are defined. I started it and simply called the main header UnExt.h.

Currently just one interface/guid is defined named IKnowWonderRenderDevice, which matches the URenderDevice interface found in Disney's Brother Bear and probably their UE1 Harry Potter games.

My first use of it was to add a seperate gamma command to my gameengine class, which would check for that interface, and if not found fall back to using old Flush() style to update gamma.

So lets start with the header files:

UnExt.h

Code: Select all

/*=============================================================================
	UnExt.h: UnrealEngine Extension main header.
	
	Revision history:
		* Created by Sebastian Kaufel
=============================================================================*/

#ifndef UNEXT_GUID_ONLY
#define UNEXT_AUTOGENERATE_GUID(name,a,b,c,d) extern FGuid name;
#endif

#ifdef UNEXT_INCLUDE_RENDERDEVICES
#include "UnExtRenDev.h"
#endif

#ifndef UNEXT_GUID_ONLY
#undef UNEXT_AUTOGENERATE_GUID
#endif NAMES_ONLY

/*----------------------------------------------------------------------------
	The End.
----------------------------------------------------------------------------*/
UnExtRenDev.h

Code: Select all

/*=============================================================================
	UnExtRenDev.h: UnrealEngine Extension render device header.
	
	Revision history:
		* Created by Sebastian Kaufel
=============================================================================*/

UNEXT_AUTOGENERATE_GUID(IID_IKnowWonderRenderDevice,0xCB1E8908,0xEF4C4A4A,0xA5DD8D16,0x4F335D26)

#ifndef UNEXT_GUID_ONLY

/*-----------------------------------------------------------------------------
	KnowWonder RenderDevice interface.

	Motivation:
		Disney's Brother Bear offers an extended URenderDevice interface,
		which reflects a logical next step to clean up the rendering path
		with it's introduction of an interface to draw bunches of polygons,
		optional font rendering, distance fog and gamma handling.

	Reference implementation:
		* Disney's Brother Bear.

	Notes:
		* Might be (partially) implemented in KnowWonder's Harry Potter games.
-----------------------------------------------------------------------------*/

struct IKnowWonderRenderDevice
{
	virtual UBOOL Init( UViewport* InViewport, INT NewX, INT NewY, INT NewColorBytes, UBOOL Fullscreen )=0;
	virtual UBOOL SetRes( INT NewX, INT NewY, INT NewColorBytes, UBOOL Fullscreen )=0;
	virtual void Exit()=0;
	virtual void Flush( UBOOL AllowPrecache )=0;
	virtual UBOOL Exec( const TCHAR* Cmd, FOutputDevice& Ar )=0;
	virtual void Lock( FPlane FlashScale, FPlane FlashFog, FPlane ScreenClear, DWORD RenderLockFlags, BYTE* HitData, INT* HitSize )=0;
	virtual void Unlock( UBOOL Blit )=0;
	virtual void UpdateGamma()=0;
	virtual void RestoreGamma()=0;
	virtual void DrawComplexSurface( FSceneNode* Frame, FSurfaceInfo& Surface, FSurfaceFacet& Facet, DWORD, BYTE )=0;
	virtual INT MaxVertices()=0;
	virtual void DrawTriangles( FSceneNode* Frame, FTextureInfo& Info, FTransTexture** Pts, INT NumPts, _WORD* RemapTable, INT NumIndices, DWORD PolyFlags, class FSpanBuffer* Span )=0;
	virtual void DrawGouraudPolygon( FSceneNode* Frame, FTextureInfo& Info, FTransTexture** Pts, INT NumPts, DWORD PolyFlags, FSpanBuffer* Span )=0;
	virtual void DrawTile( FSceneNode* Frame, FTextureInfo& Info, FLOAT X, FLOAT Y, FLOAT XL, FLOAT YL, FLOAT U, FLOAT V, FLOAT UL, FLOAT VL, class FSpanBuffer* Span, FLOAT Z, FPlane Color, FPlane Fog, DWORD PolyFlags )=0;
	virtual void Draw3DLine( FSceneNode* Frame, FPlane Color, DWORD LineFlags, FVector OrigP, FVector OrigQ )=0;
	virtual void Draw2DClippedLine( FSceneNode* Frame, FPlane Color, DWORD LineFlags, FVector P1, FVector P2 )=0;
	virtual void Draw2DLine( FSceneNode* Frame, FPlane Color, DWORD LineFlags, FVector P1, FVector P2 )=0;
	virtual void Draw2DPoint( FSceneNode* Frame, FPlane Color, DWORD LineFlags, FLOAT X1, FLOAT Y1, FLOAT X2, FLOAT Y2, FLOAT Z )=0;
	virtual void ClearZ( FSceneNode* Frame )=0;
	virtual void PushHit( const BYTE* Data, INT Count )=0;
	virtual void PopHit( INT Count, UBOOL bForce )=0;
	virtual void GetStats( TCHAR* Result )=0;
	virtual void ReadPixels( FColor* Pixels )=0;
	virtual void EndFlash()=0;
	virtual void DrawStats( FSceneNode* Frame )=0;
	virtual void SetSceneNode( FSceneNode* Frame )=0;
	virtual void PrecacheTexture( FTextureInfo& Info, DWORD PolyFlags )=0;
	virtual void SetDistanceFog( UBOOL Enable, FLOAT FogStart, FLOAT FogEnd, FColor Color )=0;
	virtual void SetDistanceFogForceReset( UBOOL Enable )=0;
	virtual UBOOL GetDistanceFogForceReset()=0;
	virtual UBOOL HasNativeDrawString()=0;
	virtual void DrawString( DWORD, UFont* Font, INT& X, INT& Y, const TCHAR* Text, const FPlane& Color )=0;
};

#endif // UNEXT_GUID_ONLY

/*----------------------------------------------------------------------------
	The End.
----------------------------------------------------------------------------*/
After including that header (e.g. in your YourRenderDevicePrivate.h) next step would be to add that interface to your RenderDevice and implement the required virtual functions of it.

Code: Select all

class UOpenGLRenderDevice : public URenderDevice, public IKnowWonderRenderDevice { ... }
Because you don't want to rely on additional (static) *.lib files, you need to have the Guid's in your Package. You can do this rather similar to Names (without the need for the InitNames() function).

Code: Select all

#define UNEXT_GUID_ONLY
#define UNEXT_AUTOGENERATE_GUID(name,a,b,c,d) FGuid name = FGuid(a,b,c,d);
#include "UnExt.h"
#undef AUTOGENERATE_FUNCTION
#undef UNEXT_GUID_ONLY
So next step is to implement the QueryInterface() method in your RenderDevice:

Code: Select all

	//
	// FUnknown.
	//
	DWORD STDCALL QueryInterface( const FGuid& RefIID, void** InterfacePtr )
	{
		if ( RefIID==IID_IKnowWonderRenderDevice )
		{
			*(IKnowWonderRenderDevice**)InterfacePtr = this;
			return 1;
		}
		return 0;
	}
Thats all you need to implement in your RenderDevice to make use of it.

So for using it on the other side you include the UnExt.h header to, and add the code for UNEXT_AUTOGENERATE_GUID in the same way. Next step is to query this interface, e.g. as part of my URevisionGameEngine::Exec() function:

Code: Select all

	// Gamma update command using IKnowWonderRenderDevice
	else if( ParseCommand(&Str,TEXT("UPDATEGAMMA")) )
	{
		if ( Client && Client->Viewports.Num() && Client->Viewports(0) && Client->Viewports(0)->RenDev )
		{
			IKnowWonderRenderDevice* I = NULL;
			if ( Client->Viewports(0)->RenDev->QueryInterface(IID_IKnowWonderRenderDevice,(void**)&I) && I )
			{
				I->UpdateGamma();
				Out.Logf( TEXT("Updated gamma using IKnowWonderRenderDevice.") );
				return 1;
			}

			// Fallback.
			Client->Viewports(0)->RenDev->Flush( 0 );
			Out.Logf( TEXT("Updated gamma using fallback.") );
			return 1;
		}

		// Fallback for Fallback.
		Flush( 0 );
		Out.Logf( TEXT("Updated gamma using fallbacks fallback.") );
		return 1;
	}
So thats just a start, one can go ahead and make up an extended canvas class and hook in the HasNativeDrawString() / DrawString(), etc.

To sum up:
You get a clean way to extend individual components, have a clean way to check for these capabilities and don't need to depend on any specific class or implementation and get a great deal of compatiblity this way.
Last edited by han on Sat Oct 24, 2015 1:47 pm, edited 1 time in total.
HX on Mod DB. Revision on Steam. Löffels on Patreon.
User avatar
han
Global Moderator
Posts: 686
Joined: Wed Dec 10, 2014 12:38 am

Re: Extending interface in UnrealGames.

Post by han »

Proposal: Standarization of timing source usage
I have just another idea what should be standarized. Despite usage of RDTSC is still a problem for some old System running old Software, it would be nice to have a defined way to control which timing source is using.

The idea is to bend GTimestamp, a globally accessible variable. In stock UnrealEngine 1 it's value 0 indicates that appSecondsSlow() is used as a timing source, and the value 1 indicates that the RDTSC counter is used. It might be worth to mention that setting GTimestamp to 0 cause problems at least if the system uptime is a few weeks.

So my suggestion is to extend the meaning of GTimestamp in the following way:

Code: Select all

GTimestamp = 0: appSecondsSlow()
GTimestamp = 1; RDTSC based timer as implemented in headers.
GTimestamp = 2; (WIN32) QueryPerformanceCounter API.
GTimestamp = 3; (WIN32) timeGetTime.
GTimestamp = 4; (WIN32) GetSystemTimePreciseAsFileTime.
Notes:
QPC API is just included because Kenties Deus Ex Launcher uses it. I personally use a check for RDTSC validity (OS >= Windows Vista, TscInvariant flag set) and fallback to timeGetTime() which Unreal 227 uses as a timing source.

Probably the best approach would be to develop the reference implementation as part of UnExt as appSecondsUnExt(). That way a component can just use appSecondsUnExt() instead of appSeconds() to support the feature.

It is the responsibility of the game Launcher to set the it's appropreciated GTimestamp value, though I might add a sample implementation for the selection code to UnExt.

Additions to UnExt:
IHumanHeadAudioSubsystem offering StopSoundId() / StopAllSound() functionality and additionaly adds PostRender() for older engine versions (Nerf, etc.).

Adaption of UnExt:
Recent ALAudio builds support the IHumanHeadAudioSubsystem API.
Deus Ex: Revision optionally utilizes IKnowWonderRenderDevice::UpdateGamma() for it's gamma adjustment screen.
Last edited by han on Wed Oct 28, 2015 12:48 pm, edited 1 time in total.
HX on Mod DB. Revision on Steam. Löffels on Patreon.
User avatar
Smirftsch
Administrator
Posts: 8999
Joined: Wed Apr 29, 1998 10:00 pm
Location: NaPali
Contact:

Re: [UnExt] Extending interface in UnrealGames.

Post by Smirftsch »

to bad GetSystemTimePreciseAsFileTime won't work on pre Win8 systems. That's something I really miss.
Although, I stumbled across NtQuerySystemTime - maybe that's worth a look yet. On the other hand MS pretty much declared this deprecated already. For some reason I always come back to timeGetTime.
Last edited by Smirftsch on Sun Oct 25, 2015 11:58 am, edited 1 time in total.
Sometimes you have to lose a fight to win the war.
User avatar
han
Global Moderator
Posts: 686
Joined: Wed Dec 10, 2014 12:38 am

Re: [UnExt] Extending interface in UnrealGames.

Post by han »

Selecting a timing source is really not a simple task and has lots of caveeats (and we both discussed the topic quite a lot in the past).

However, the timing thing is sth. which solves itsself more an more. One of the reasons beeing that new CPU models over the last couple of years reverted back to having a constant frequency TSC as well as System > WinXp do handle synchronisation (if needed) across cores gracefully. So the amount of Systems running WinXP or lower and/or using a CPU without a constant frequency TSC dimishs in the future. Also one can just go ahead and use GetSystemTimePreciseAsFileTime() on Win8+ as the amount of Systems >= Win8 get more an more. So all in all the problems solves itsself, though choosing the right timer for the problematic systems remains a hard descission. But making this descission is not a part of UnExt. UnExt just deferres the descission to the Launcher and make it easy for others components to comply with this descission as having different timing sources across the same application can cause issues.
HX on Mod DB. Revision on Steam. Löffels on Patreon.
User avatar
han
Global Moderator
Posts: 686
Joined: Wed Dec 10, 2014 12:38 am

Re: [UnExt] Extending interface in UnrealGames.

Post by han »

Proposal: Standard interface for querying capabilities
A general useable interface query certain capabilities would be useful. A usage example would be an URenderDevice implementation which queries the URenderBase implementation used about a UNEXT_RENDEV_CAP_LOCKS_WITH_BUMPMAP capability to figure out if whether the URenderBase implementation passes an extended FSurfaceInfo to [noparse]URenderDevice::DrawComplexSurface()[/noparse] which adds FTextureInfo* BumpMap. This way the Render can lock the (unused) UTexture::BumpMap and passes it to the RenderDevice which is in turn is aware whether the shorter FSurfaceInfo or the extended one is used and can thus safely access the additional BumpMap. This way one can continue to use the UTexture class and use it's BumpMap property for rendering. The format of the BumpMap could be RGBA8, with the first 3 channels beeing normals, and the alpha channel beeing a displacement offset to be used for geometry generation in RenderDevice scope.

While my first idea is to simpy use some boolean style QueryCapability() function, it might be a better idea to add a more extensible interface, allowing to query other standard UnrealEngine datatypes (UBOOL, INT, DWORD, BYTE, SWORD, TCHAR*, FString, void*, etc.). An idea for such an interface would be a similar approach to glGet*() https://www.opengl.org/sdk/docs/man/html/glGet.xhtml
Last edited by han on Wed Oct 28, 2015 1:29 pm, edited 1 time in total.
HX on Mod DB. Revision on Steam. Löffels on Patreon.
Post Reply

Return to “Unreal Engine 1 and 2”