From 7f81d93ecb5ed891f90c803354517763b277de7f Mon Sep 17 00:00:00 2001 From: SaiyansKing <38609240+SaiyansKing@users.noreply.github.com> Date: Sat, 11 Sep 2021 21:57:50 +0200 Subject: [PATCH] Fixes & Improvements -Fix hashing render states over uninitialized variables -Fix some awful glitches in indoor locations with enabled shadows -Remove unused calculations for some particle effects -Force water to be rendered always when texture is available -Change cache-in to not check for cache-out since dx7 code doesn't check for it too -Allow Steam Overlay to properly acquire/release inputs --- D3D11Engine/D2DSettingsDialog.cpp | 2 +- D3D11Engine/D3D11Engine.vcxproj | 2 + D3D11Engine/D3D11Engine.vcxproj.filters | 6 +++ D3D11Engine/D3D11GraphicsEngine.cpp | 56 +++++++++++++++---------- D3D11Engine/GothicAPI.cpp | 18 ++------ D3D11Engine/GothicGraphicsState.h | 10 ++++- D3D11Engine/SteamOverlay.cpp | 39 +++++++++++++++++ D3D11Engine/SteamOverlay.h | 7 ++++ D3D11Engine/WorldConverter.cpp | 18 ++++---- D3D11Engine/pch.h | 2 +- D3D11Engine/zCTexture.h | 3 +- 11 files changed, 114 insertions(+), 49 deletions(-) create mode 100644 D3D11Engine/SteamOverlay.cpp create mode 100644 D3D11Engine/SteamOverlay.h diff --git a/D3D11Engine/D2DSettingsDialog.cpp b/D3D11Engine/D2DSettingsDialog.cpp index af867efb..1a632888 100644 --- a/D3D11Engine/D2DSettingsDialog.cpp +++ b/D3D11Engine/D2DSettingsDialog.cpp @@ -137,7 +137,7 @@ XRESULT D2DSettingsDialog::InitControls() { SV_Checkbox* godraysCheckbox = new SV_Checkbox( MainView, MainPanel ); godraysCheckbox->SetSize( D2D1::SizeF( 160, 20 ) ); switch ( userLanguage ) { - case LANGUAGE_POLISH: godraysCheckbox->SetCaption( L"Promienie Boga" ); break; + case LANGUAGE_POLISH: godraysCheckbox->SetCaption( L"GodRays" ); break; default: godraysCheckbox->SetCaption( L"Enable GodRays" ); break; } godraysCheckbox->SetDataToUpdate( &Engine::GAPI->GetRendererState().RendererSettings.EnableGodRays ); diff --git a/D3D11Engine/D3D11Engine.vcxproj b/D3D11Engine/D3D11Engine.vcxproj index ad2add44..91844467 100644 --- a/D3D11Engine/D3D11Engine.vcxproj +++ b/D3D11Engine/D3D11Engine.vcxproj @@ -717,6 +717,7 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" + @@ -906,6 +907,7 @@ copy "$(OutDir)$(TargetName).pdb" "$(G1_SYSTEM_PATH)\ddraw.pdb" pch.h + diff --git a/D3D11Engine/D3D11Engine.vcxproj.filters b/D3D11Engine/D3D11Engine.vcxproj.filters index 3bb4a69c..64ec2528 100644 --- a/D3D11Engine/D3D11Engine.vcxproj.filters +++ b/D3D11Engine/D3D11Engine.vcxproj.filters @@ -723,6 +723,9 @@ ZenGin + + Tools + @@ -952,6 +955,9 @@ Engine\D3D11\Engine + + Tools + diff --git a/D3D11Engine/D3D11GraphicsEngine.cpp b/D3D11Engine/D3D11GraphicsEngine.cpp index 9955d39f..ee180d2c 100644 --- a/D3D11Engine/D3D11GraphicsEngine.cpp +++ b/D3D11Engine/D3D11GraphicsEngine.cpp @@ -40,6 +40,7 @@ #define DEBUG_D3D11 #endif +#include "SteamOverlay.h" #include namespace wrl = Microsoft::WRL; @@ -451,6 +452,7 @@ XRESULT D3D11GraphicsEngine::Init() { DXGI_FORMAT_R8G8B8A8_UNORM, nullptr, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_UNKNOWN, 1, 6 ); + SteamOverlay::Init(); return XR_SUCCESS; } @@ -773,6 +775,7 @@ XRESULT D3D11GraphicsEngine::OnBeginFrame() { s_firstFrame = false; + SteamOverlay::Update(); #ifdef BUILD_1_12F // Some shitty workaround for weird hidden window bug that happen on d3d11 renderer if ( !(GetWindowLongA( OutputWindow, GWL_STYLE ) & WS_VISIBLE) ) { @@ -2007,9 +2010,8 @@ XRESULT D3D11GraphicsEngine::DrawMeshInfoListAlphablended( // Draw the list for ( auto const& it : list ) { int indicesNumMod = 1; - if ( it.first.Material->GetAniTexture() != nullptr ) { - MyDirectDrawSurface7* surface = - it.first.Material->GetAniTexture()->GetSurface(); + if ( zCTexture* texture = it.first.Material->GetAniTexture() ) { + MyDirectDrawSurface7* surface = texture->GetSurface(); ID3D11ShaderResourceView* srv[3]; // Get diffuse and normalmap @@ -2026,16 +2028,15 @@ XRESULT D3D11GraphicsEngine::DrawMeshInfoListAlphablended( GetContext()->PSSetShaderResources( 0, 3, srv ); // Get the right shader for it - - BindShaderForTexture( it.first.Material->GetAniTexture(), false, - it.first.Material->GetAlphaFunc() ); + int alphaFunc = it.first.Material->GetAlphaFunc(); + BindShaderForTexture( texture, false, alphaFunc ); // Check for alphablending on world mesh - if ( lastAlphaFunc != it.first.Material->GetAlphaFunc() ) { - if ( it.first.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_BLEND ) + if ( lastAlphaFunc != alphaFunc ) { + if ( alphaFunc == zMAT_ALPHA_FUNC_BLEND ) Engine::GAPI->GetRendererState().BlendState.SetAlphaBlending(); - if ( it.first.Material->GetAlphaFunc() == zMAT_ALPHA_FUNC_ADD ) + if ( alphaFunc == zMAT_ALPHA_FUNC_ADD ) Engine::GAPI->GetRendererState().BlendState.SetAdditiveBlending(); Engine::GAPI->GetRendererState().BlendState.SetDirty(); @@ -2044,7 +2045,7 @@ XRESULT D3D11GraphicsEngine::DrawMeshInfoListAlphablended( Engine::GAPI->GetRendererState().DepthState.SetDirty(); UpdateRenderStates(); - lastAlphaFunc = it.first.Material->GetAlphaFunc(); + lastAlphaFunc = alphaFunc; } MaterialInfo* info = it.first.Info; @@ -2053,7 +2054,7 @@ XRESULT D3D11GraphicsEngine::DrawMeshInfoListAlphablended( info->Constantbuffer->BindToPixelShader( 2 ); // Don't let the game unload the texture after some time - it.first.Material->GetAniTexture()->CacheIn( 0.6f ); + texture->CacheIn( 0.6f ); // Draw the section-part DrawVertexBufferIndexedUINT( nullptr, nullptr, it.second->Indices.size(), @@ -2134,16 +2135,16 @@ XRESULT D3D11GraphicsEngine::DrawWorldMesh( bool noTextures ) { zCTexture* aniTex = worldMesh.first.Material->GetTexture(); if ( !aniTex ) continue; - if ( aniTex->CacheIn( 0.6f ) != zRES_CACHED_IN ) { - continue; - } - // Check surface type if ( worldMesh.first.Info->MaterialType == MaterialInfo::MT_Water ) { FrameWaterSurfaces[aniTex].push_back( worldMesh.second ); continue; } + if ( aniTex->CacheIn( 0.6f ) != zRES_CACHED_IN ) { + continue; + } + // Check if the animated texture and the registered textures are the // same MeshKey key = worldMesh.first; @@ -2233,7 +2234,7 @@ XRESULT D3D11GraphicsEngine::DrawWorldMesh( bool noTextures ) { GetContext()->PSSetShaderResources( 0, 3, srv ); // Get the right shader for it - BindShaderForTexture( mesh.first.Material->GetAniTexture(), false, + BindShaderForTexture( mesh.first.Texture, false, mesh.first.Material->GetAlphaFunc() ); if ( info ) { @@ -2242,10 +2243,10 @@ XRESULT D3D11GraphicsEngine::DrawWorldMesh( bool noTextures ) { info->Constantbuffer->BindToPixelShader( 2 ); // Don't let the game unload the texture after some time - mesh.first.Material->GetAniTexture()->CacheIn( 0.6f ); + //mesh.first.Texture->CacheIn( 0.6f ); boundInfo = info; } - bound = mesh.first.Material->GetAniTexture(); + bound = mesh.first.Texture; // Bind normalmap to HDS if ( !mesh.second->IndicesPNAEN.empty() ) { GetContext()->DSSetShaderResources( 0, 1, boundNormalmap.GetAddressOf() ); @@ -2628,10 +2629,9 @@ void D3D11GraphicsEngine::DrawWaterSurfaces() { for ( auto const& it : FrameWaterSurfaces ) { if ( it.first ) { // Bind diffuse - if ( it.first->CacheIn( -1 ) == - zRES_CACHED_IN ) // Force immediate cache in, because water is - // important! - it.first->Bind( 0 ); + it.first->CacheIn( -1 ); // Force immediate cache in, because water + // is important! + it.first->Bind( 0 ); } // Draw surfaces for ( auto const& mesh : it.second ) { @@ -4074,8 +4074,17 @@ XRESULT D3D11GraphicsEngine::DrawLighting( std::vector& lights ) Engine::GAPI->SetCameraReplacementPtr( &cr ); // Indoor worlds don't need shadowmaps for the world + static zTBspMode lastBspMode = zBSP_MODE_OUTDOOR; if ( Engine::GAPI->GetLoadedWorldInfo()->BspTree->GetBspTreeMode() == zBSP_MODE_OUTDOOR ) { RenderShadowmaps( WorldShadowCP, nullptr, true ); + lastBspMode = zBSP_MODE_OUTDOOR; + } else if ( Engine::GAPI->GetRendererState().RendererSettings.EnableShadows ) { + // We need to clear shadowmap to avoid some glitches in indoor locations + // only need to do it once :) + if ( lastBspMode == zBSP_MODE_OUTDOOR ) { + GetContext()->ClearDepthStencilView( WorldShadowmap1->GetDepthStencilView().Get(), D3D11_CLEAR_DEPTH, 0.0f, 0 ); + lastBspMode = zBSP_MODE_INDOOR; + } } SetDefaultStates(); @@ -4666,6 +4675,9 @@ LRESULT D3D11GraphicsEngine::OnWindowMessage( HWND hWnd, UINT msg, WPARAM wParam } UpdateClipCursor( hWnd ); + + // Sometimes Gothic doesn't aquire/release input so let's do it ourselves + Engine::GAPI->SetEnableGothicInput( m_isWindowActive ); } break; case WM_WINDOWPOSCHANGED: UpdateClipCursor( hWnd ); break; diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index b41b798a..82fc2954 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -386,7 +386,7 @@ void GothicAPI::SetEnableGothicInput( bool value ) { dInputKeyboard->Acquire(); } -#ifdef BUILD_GOTHIC_2_6_fix +/*#ifdef BUILD_GOTHIC_2_6_fix // Kill the check for doing freelook only in fullscreen, since we force the game to run windowed internally const int flSize = GothicMemoryLocations::GlobalObjects::NOP_FreelookWindowedCheckEnd - GothicMemoryLocations::GlobalObjects::NOP_FreelookWindowedCheckStart; @@ -401,7 +401,7 @@ void GothicAPI::SetEnableGothicInput( bool value ) { memcpy( &s_CheckInst[0], (void*)GothicMemoryLocations::GlobalObjects::NOP_FreelookWindowedCheckStart, flSize ); } -#endif +#endif*/ #endif } @@ -1918,16 +1918,12 @@ void GothicAPI::DrawParticleFX( zCVob* source, zCParticleFX* fx, ParticleFrameDa // Maybe create more emitters? fx->CheckDependentEmitter(); - DirectX::XMFLOAT4X4 sw; - source->GetWorldMatrix( &sw ); - zTParticle* pfx = fx->GetFirstParticle(); if ( pfx ) { // Get texture zCTexture* texture = nullptr; if ( zCParticleEmitter* emitter = fx->GetEmitter() ) { - texture = emitter->GetVisTexture(); - if ( texture ) { + if ( (texture = emitter->GetVisTexture()) != nullptr ) { // Check if it's loaded if ( texture->CacheIn( 0.6f ) != zRES_CACHED_IN ) { return; @@ -2006,12 +2002,6 @@ void GothicAPI::DrawParticleFX( zCVob* source, zCParticleFX* fx, ParticleFrameDa if ( alignment == zPARTICLE_ALIGNMENT_XY ) { ii.drawMode = 2; } else if ( alignment == zPARTICLE_ALIGNMENT_VELOCITY || alignment == zPARTICLE_ALIGNMENT_VELOCITY_3D ) { - // Rotate velocity so it fits with the vob - - XMFLOAT3 velocity; - XMStoreFloat3( &velocity, DirectX::XMVector3TransformNormal( XMVector3Normalize( XMLoadFloat3( &p->Vel ) ), XMMatrixTranspose( XMLoadFloat4x4( &sw ) ) ) ); - ii.velocity = velocity; - ii.drawMode = 3; } // TODO: Y-Locked! @@ -2027,7 +2017,7 @@ void GothicAPI::DrawParticleFX( zCVob* source, zCParticleFX* fx, ParticleFrameDa if ( fx->GetEmitter()->GetVisTexAniIsLooping() != 2 ) { // 2 seems to be some magic case with sinus smoothing color.w = std::min( p->Alpha, 255.0f ) / 255.0f; } else { - color.w = std::min( (zCParticleFX::SinSmooth( fabs( (p->Alpha - fx->GetEmitter()->GetVisAlphaStart()) * fx->GetEmitter()->GetAlphaDist() ) ) * p->Alpha) / 255.0f, 255.0f ); + color.w = std::min( (zCParticleFX::SinSmooth( fabs( (p->Alpha - fx->GetEmitter()->GetVisAlphaStart()) * fx->GetEmitter()->GetAlphaDist() ) ) * p->Alpha) / 255.0f, 1.0f ); } color.w = std::max( color.w, 0.0f ); diff --git a/D3D11Engine/GothicGraphicsState.h b/D3D11Engine/GothicGraphicsState.h index a7322c92..83f264a7 100644 --- a/D3D11Engine/GothicGraphicsState.h +++ b/D3D11Engine/GothicGraphicsState.h @@ -160,6 +160,7 @@ class BaseDepthBufferState; struct GothicDepthBufferStateInfo : public GothicPipelineState { GothicDepthBufferStateInfo() { StructSize = sizeof( GothicDepthBufferStateInfo ); + Padding[0] = Padding[1] = false; } /** Layed out for D3D11 */ @@ -186,6 +187,7 @@ struct GothicDepthBufferStateInfo : public GothicPipelineState { /** Depthbuffer settings */ bool DepthBufferEnabled; bool DepthWriteEnabled; + bool Padding[2]; ECompareFunc DepthBufferCompareFunc; /** Deletes all cached states */ @@ -224,6 +226,7 @@ class BaseBlendStateInfo; struct GothicBlendStateInfo : public GothicPipelineState { GothicBlendStateInfo() { StructSize = sizeof( GothicBlendStateInfo ); + Padding = false; } /** Layed out for D3D11 */ @@ -292,6 +295,7 @@ struct GothicBlendStateInfo : public GothicPipelineState { BlendOpAlpha = BO_BLEND_OP_ADD; BlendEnabled = true; AlphaToCoverage = false; + ColorWritesEnabled = true; } /** Sets up modualte blending */ @@ -304,6 +308,7 @@ struct GothicBlendStateInfo : public GothicPipelineState { BlendOpAlpha = BO_BLEND_OP_ADD; BlendEnabled = true; AlphaToCoverage = false; + ColorWritesEnabled = true; } EBlendFunc SrcBlend; @@ -315,6 +320,7 @@ struct GothicBlendStateInfo : public GothicPipelineState { bool BlendEnabled; bool AlphaToCoverage; bool ColorWritesEnabled; + bool Padding; /** Deletes all cached states */ static void DeleteCachedObjects() { @@ -365,6 +371,7 @@ class BaseRasterizerStateInfo; struct GothicRasterizerStateInfo : public GothicPipelineState { GothicRasterizerStateInfo() { StructSize = sizeof( GothicRasterizerStateInfo ); + Padding = false; } /** Layed out for D3D11 */ @@ -386,8 +393,9 @@ struct GothicRasterizerStateInfo : public GothicPipelineState { ECullMode CullMode; bool FrontCounterClockwise; bool DepthClipEnable; - int ZBias; bool Wireframe; + bool Padding; + int ZBias; /** Deletes all cached states */ static void DeleteCachedObjects() { diff --git a/D3D11Engine/SteamOverlay.cpp b/D3D11Engine/SteamOverlay.cpp new file mode 100644 index 00000000..1baca2a1 --- /dev/null +++ b/D3D11Engine/SteamOverlay.cpp @@ -0,0 +1,39 @@ +#include "Engine.h" +#include "GothicAPI.h" +#include "SteamOverlay.h" + +// We can detect whether steam overlay is visible by checking if it request using mouse or keyboard +// easiest way to do it would be by using steamapi callbacks however because the game can be launched +// without steam it doesn't feel right doing it that way +namespace SteamOverlay +{ + static bool IsSteamOverlayEnabled = false; + + typedef bool( WINAPI* PFN_IsOverlayEnabled )(); + typedef bool( WINAPI* PFN_SteamOverlayIsUsingMouse )(); + typedef bool( WINAPI* PFN_SteamOverlayIsUsingKeyboard )(); + static PFN_IsOverlayEnabled IsOverlayEnabled = nullptr; + static PFN_SteamOverlayIsUsingMouse SteamOverlayIsUsingMouse = nullptr; + static PFN_SteamOverlayIsUsingKeyboard SteamOverlayIsUsingKeyboard = nullptr; + + void Init() + { + HMODULE soModule = GetModuleHandleA( "GameOverlayRenderer.dll" ); + if ( soModule ) { + IsOverlayEnabled = reinterpret_cast(GetProcAddress( soModule, "IsOverlayEnabled" )); + SteamOverlayIsUsingMouse = reinterpret_cast(GetProcAddress( soModule, "SteamOverlayIsUsingMouse" )); + SteamOverlayIsUsingKeyboard = reinterpret_cast(GetProcAddress( soModule, "SteamOverlayIsUsingKeyboard" )); + } + } + + void Update() + { + if ( IsOverlayEnabled && IsOverlayEnabled() && (SteamOverlayIsUsingMouse || SteamOverlayIsUsingKeyboard) ) { + bool isSOEnabled = (SteamOverlayIsUsingMouse && SteamOverlayIsUsingMouse()) || (SteamOverlayIsUsingKeyboard && SteamOverlayIsUsingKeyboard()); + if ( IsSteamOverlayEnabled != isSOEnabled ) { + Engine::GAPI->SetEnableGothicInput( IsSteamOverlayEnabled ); + IsSteamOverlayEnabled = isSOEnabled; + } + } + } +} diff --git a/D3D11Engine/SteamOverlay.h b/D3D11Engine/SteamOverlay.h new file mode 100644 index 00000000..7e5af7f6 --- /dev/null +++ b/D3D11Engine/SteamOverlay.h @@ -0,0 +1,7 @@ +#pragma once + +namespace SteamOverlay +{ + void Init(); + void Update(); +}; diff --git a/D3D11Engine/WorldConverter.cpp b/D3D11Engine/WorldConverter.cpp index 8e44804a..fdbbd5f6 100644 --- a/D3D11Engine/WorldConverter.cpp +++ b/D3D11Engine/WorldConverter.cpp @@ -539,17 +539,19 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol // Calculate midpoint of this triange to get the section DirectX::XMFLOAT3 avgPos; - XMStoreFloat3( &avgPos, (XMLoadFloat3( &*poly->getVertices()[0]->Position.toXMFLOAT3() ) + XMLoadFloat3( &*poly->getVertices()[1]->Position.toXMFLOAT3() ) + XMLoadFloat3( &*poly->getVertices()[2]->Position.toXMFLOAT3() )) / 3.0f ); + XMStoreFloat3( &avgPos, (XMLoadFloat3( poly->getVertices()[0]->Position.toXMFLOAT3() ) + XMLoadFloat3( poly->getVertices()[1]->Position.toXMFLOAT3() ) + XMLoadFloat3( poly->getVertices()[2]->Position.toXMFLOAT3() )) / 3.0f ); + INT2 section = GetSectionOfPos( avgPos ); - (*outSections)[section.x][section.y].WorldCoordinates = section; + WorldMeshSectionInfo& sectionInfo = (*outSections)[section.x][section.y]; + sectionInfo.WorldCoordinates = section; //if ( poly->GetMaterial() && poly->GetMaterial()->GetMatGroup() == zMAT_GROUP_WATER ) { - //(*outSections)[section.x][section.y].OceanPoints.push_back(*poly->getVertices()[0]->Position.toXMFLOAT3()); + //sectionInfo.OceanPoints.push_back(*poly->getVertices()[0]->Position.toXMFLOAT3()); //continue; //} - XMFLOAT3& bbmin = (*outSections)[section.x][section.y].BoundingBox.Min; - XMFLOAT3& bbmax = (*outSections)[section.x][section.y].BoundingBox.Max; + XMFLOAT3& bbmin = sectionInfo.BoundingBox.Min; + XMFLOAT3& bbmax = sectionInfo.BoundingBox.Max; DWORD sectionColor = float4( (section.x % 2) + 0.5f, (section.x % 2) + 0.5f, 1, 1 ).ToDWORD(); @@ -605,11 +607,11 @@ HRESULT WorldConverter::ConvertWorldMesh( zCPolygon** polys, unsigned int numPol //key.Lightmap = poly->GetLightmap(); - if ( (*outSections)[section.x][section.y].WorldMeshes.count( key ) == 0 ) { + if ( sectionInfo.WorldMeshes.count( key ) == 0 ) { key.Info = Engine::GAPI->GetMaterialInfoFrom( key.Texture ); - (*outSections)[section.x][section.y].WorldMeshes[key] = new WorldMeshInfo; + sectionInfo.WorldMeshes[key] = new WorldMeshInfo; } - TriangleFanToList( &polyVertices[0], polyVertices.size(), &(*outSections)[section.x][section.y].WorldMeshes[key]->Vertices ); + TriangleFanToList( &polyVertices[0], polyVertices.size(), §ionInfo.WorldMeshes[key]->Vertices ); //if (mat && mat->GetTexture()) // LogInfo() << "Got texture name: " << mat->GetTexture()->GetName(); diff --git a/D3D11Engine/pch.h b/D3D11Engine/pch.h index 53be8f01..5d47e124 100644 --- a/D3D11Engine/pch.h +++ b/D3D11Engine/pch.h @@ -26,7 +26,7 @@ #define stdext std #endif -#define VERSION_NUMBER "17.7-dev21" +#define VERSION_NUMBER "17.7-dev22" __declspec(selectany) const char* VERSION_NUMBER_STR = VERSION_NUMBER; extern bool GMPModeActive; diff --git a/D3D11Engine/zCTexture.h b/D3D11Engine/zCTexture.h index e7d6d894..34e2ba4a 100644 --- a/D3D11Engine/zCTexture.h +++ b/D3D11Engine/zCTexture.h @@ -103,7 +103,7 @@ class zCTexture { zTResourceCacheState cacheState = GetCacheState(); if ( cacheState == zRES_CACHED_IN ) { TouchTimeStamp(); - } else if ( cacheState == zRES_CACHED_OUT || zCTextureCacheHack::ForceCacheIn ) { + } else/* if ( cacheState == zRES_CACHED_OUT || zCTextureCacheHack::ForceCacheIn )*/ { TouchTimeStampLocal(); /*zCTextureCacheHack::NumNotCachedTexturesInFrame++; @@ -121,7 +121,6 @@ class zCTexture { } #endif Engine::GAPI->SetBoundTexture( 7, this ); // Index 7 is reserved for cacheIn - //TouchTimeStampLocal(); // Cache the texture, overwrite priority if wanted. zCResourceManager::GetResourceManager()->CacheIn( this, zCTextureCacheHack::ForceCacheIn ? -1 : priority );