Current release:http://coding.hanfling.de/launch/release/UnExt-20150504.zipDescription: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.
//
// 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
/*=============================================================================
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
/*=============================================================================
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.
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).
#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:
//
// 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:
// 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.