and though bugs are the bane of my existence, rest assured the wretched thing will get the best of care here

...
 
Commits (9)
......@@ -3,46 +3,6 @@
//
#pragma once
#include <cstdint>
#include <system_error>
#include <type_traits>
template <typename func_type, uint32_t address> class InMemoryFunc
{
public:
auto operator()() noexcept
{
return reinterpret_cast<func_type>( address );
}
};
template <typename RetType, uint32_t vtable_id, typename C, typename... Args>
auto InMemoryVirtualFunc( C _this, Args... args )
{
using return_type = RetType( __thiscall * )( C, Args... );
auto address = ( *reinterpret_cast<void ***>( _this ) )[vtable_id];
if constexpr ( std::is_void_v<RetType> )
reinterpret_cast<return_type>( address )( _this, args... );
else
return reinterpret_cast<return_type>( address )( _this, args... );
}
template <typename RetType, typename... Args>
auto InMemoryFuncCall( uint32_t address, Args... args )
{
using return_type = RetType( __cdecl * )( Args... );
if constexpr ( std::is_void_v<RetType> )
reinterpret_cast<return_type>( address )( args... );
else
return reinterpret_cast<return_type>( address )( args... );
}
template <typename RetType, typename... Args>
auto InMemoryThisCall( uint32_t address, Args... args )
{
using return_type = RetType( __thiscall * )( Args... );
if constexpr ( std::is_void_v<RetType> )
reinterpret_cast<return_type>( address )( args... );
else
return reinterpret_cast<return_type>( address )( args... );
}
constexpr uint32_t Version_unknown = 0xF;
constexpr uint32_t Version_1_0_en = 0;
......
......@@ -4,6 +4,7 @@
#include "Clock.h"
#include "../call_redirection_util.h"
#include <injection_utils/InjectorHelpers.h>
bool Clock::GetIsTimeInRange( unsigned char from, unsigned char to )
{
return InMemoryFuncCall<bool>(
......
......@@ -4,7 +4,7 @@
#include "Clouds.h"
#include "../call_redirection_util.h"
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <injection_utils/InjectorHelpers.h>
int32_t Clouds::RenderBackground( int16_t, int16_t, int16_t, int16_t, int16_t,
int16_t, int16_t )
......
......@@ -3,7 +3,7 @@
//
#include "Entity.h"
#include "../call_redirection_util.h"
#include <injection_utils/InjectorHelpers.h>
void Entity::CreateRwObject() { InMemoryVirtualFunc<void, 5>( this ); }
......
......@@ -4,7 +4,7 @@
#include "PointLights.h"
#include "../call_redirection_util.h"
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
int32_t PointLights::AddLight( char a1, float x, float y, float z, float dx,
......
......@@ -8,9 +8,8 @@
#include "Clock.h"
#include "ModelInfo.h"
#include "Streaming.h"
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <algorithm>
#include <cmath>
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
#include <span>
......
......@@ -4,7 +4,7 @@
#include "Shadows.h"
#include "../call_redirection_util.h"
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <injection_utils/InjectorHelpers.h>
void Shadows::Patch()
{
......
......@@ -4,6 +4,7 @@
#include "Streaming.h"
#include "../call_redirection_util.h"
#include <injection_utils/InjectorHelpers.h>
uint32_t &Streaming::mNumModelsRequested = *reinterpret_cast<uint32_t *>(
GetAddressByGame( 0x8E2C10, 0x8E2CC4, 0x8F2E04 ) );
......
......@@ -4,6 +4,8 @@
#include "TxdStore.h"
#include "../call_redirection_util.h"
#include <injection_utils/InjectorHelpers.h>
int32_t TxdStore::FindTxdSlot( const char *name )
{
return InMemoryFuncCall<int32_t>(
......
......@@ -4,6 +4,7 @@
#include "World.h"
#include "../call_redirection_util.h"
#include <injection_utils/InjectorHelpers.h>
uint16_t &World::mCurrentScanCode = *reinterpret_cast<uint16_t *>(
GetAddressByGame( 0x95CC64, 0x95CE1C, 0x96CF5C ) );
......
......@@ -5,7 +5,7 @@
#include "base_model_pipeline.h"
#include "../call_redirection_util.h"
#include "../gta3_geometry_proxy.h"
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
#include <rw_engine/rw_frame/rw_frame.h>
#include <rw_engine/rw_macro_constexpr.h>
......
......@@ -5,8 +5,8 @@
#include "material_system_patches.h"
#include "../call_redirection_util.h"
#include "../game/TxdStore.h"
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <common_headers.h>
#include <injection_utils/InjectorHelpers.h>
#include <material_storage.h>
#include <rw_engine/rh_backend/material_backend.h>
......
......@@ -4,7 +4,7 @@
#include "rwd3d8_patches.h"
#include "../call_redirection_util.h"
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <injection_utils/InjectorHelpers.h>
#include <rw_engine/system_funcs/rw_device_system_globals.h>
#include <vector>
......
......@@ -5,14 +5,13 @@
#include "skin_model_pipeline.h"
#include "../call_redirection_util.h"
#include "../gta3_geometry_proxy.h"
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
#include <rw_engine/i_anim_hierarcy.h>
#include <rw_engine/rw_frame/rw_frame.h>
#include <rw_engine/rw_macro_constexpr.h>
#include <rw_engine/rw_rh_skin_pipeline.h>
#include <rw_engine/system_funcs/rw_device_system_globals.h>
#include <rw_game_hooks.h>
using namespace rh::rw::engine;
......
#include "gta_sa_internal_classes/CColorSet.h"
#include "idle_hook.h"
#include <DebugUtils/DebugLogger.h>
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <algorithm>
#include <injection_utils/InjectorHelpers.h>
#include <ipc/ipc_utils.h>
#include <render_client/render_client.h>
#include <rw_engine/anim_hierarcy_rw36.h>
......
......@@ -13,8 +13,8 @@
#include "renderloop.h"
#include <Engine/IRenderer.h>
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <common_headers.h>
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
// static RwRGBA gColourTop = *reinterpret_cast<RwRGBA *>( 0xB72CA0 );
......
......@@ -11,6 +11,18 @@ include_directories(
set(SOURCES
dllmain.cpp
game/TxdStore.cpp
game/World.cpp
game/Renderer.cpp
game/Entity.cpp
game/Streaming.cpp
game/ModelInfo.cpp
game/Clock.cpp
game/Vector.cpp
game_patches/material_system_patches.cpp
game_patches/rwd3d8_patches.cpp
game_patches/base_model_pipeline.cpp
game_patches/skin_model_pipeline.cpp
)
#set ( CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON )
......
#include "game/Renderer.h"
#include "game_patches/base_model_pipeline.h"
#include "game_patches/material_system_patches.h"
#include "game_patches/rwd3d8_patches.h"
#include "game_patches/skin_model_pipeline.h"
#include <ConfigUtils/ConfigurationManager.h>
#include <DebugUtils/DebugLogger.h>
#include <Engine/Common/IDeviceState.h>
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <Windows.h>
#include <filesystem>
#include <injection_utils/InjectorHelpers.h>
#include <ipc/ipc_utils.h>
#include <render_client/render_client.h>
#include <rw_engine/anim_hierarcy_rw36.h>
#include <rw_engine/rh_backend/raster_backend.h>
#include <rw_engine/rp_geometry_rw36.h>
#include <rw_engine/rw_api_injectors.h>
#include <rw_engine/rw_frame/rw_frame.h>
#include <rw_engine/rw_macro_constexpr.h>
#include <rw_engine/rw_rh_pipeline.h>
#include <rw_engine/rw_rh_skin_pipeline.h>
#include <rw_engine/system_funcs/rw_device_system_globals.h>
#include <rw_game_hooks.h>
......@@ -29,106 +29,6 @@ RwTexturePointerTable rh::rw::engine::g_pTexture_API = {
reinterpret_cast<RwTextureSetName_FN>( 0x64DF40 ),
reinterpret_cast<RwTextureSetName_FN>( 0x64DFB0 ) };
struct RwVideoModeVice
{
int32_t width;
int32_t height;
int32_t depth;
uint32_t flags;
};
RwVideoModeVice *MyRwEngineGetVideoModeInfo( RwVideoModeVice *modeinfo,
int32_t modeIndex )
{
RwVideoMode videoMode{};
if ( !SystemHandler( rwDEVICESYSTEMGETMODEINFO, &videoMode, nullptr,
modeIndex ) )
return nullptr;
modeinfo->width = videoMode.width;
modeinfo->height = videoMode.height;
modeinfo->depth = videoMode.depth;
modeinfo->flags = videoMode.flags;
return modeinfo;
}
static int32_t D3D8AtomicAllInOneNode( void * /*self*/,
const RxPipelineNodeParam *params )
{
static RpGeometryRw36 geometry_interface_35{};
RpAtomic * atomic;
atomic = static_cast<RpAtomic *>( params->dataParam );
RpGeometry *geom = atomic->geometry;
geometry_interface_35.Init( geom );
if ( InstanceAtomic( atomic, &geometry_interface_35 ) !=
RenderStatus::Instanced )
return 0;
auto ltm = RwFrameGetLTM(
static_cast<RwFrame *>( rwObject::GetParent( atomic ) ) );
DrawAtomic(
atomic, &geometry_interface_35, [&ltm, atomic]( ResEnty *res_entry ) {
auto & renderer = gRenderClient->RenderState.MeshDrawCalls;
DrawCallInfo info{};
info.DrawCallId = reinterpret_cast<uint64_t>( atomic );
info.MeshId = res_entry->meshData;
info.WorldTransform = DirectX::XMFLOAT4X3{
ltm->right.x, ltm->up.x, ltm->at.x, ltm->pos.x,
ltm->right.y, ltm->up.y, ltm->at.y, ltm->pos.y,
ltm->right.z, ltm->up.z, ltm->at.z, ltm->pos.z,
};
auto mesh_list = geometry_interface_35.GetMeshList();
auto materials =
renderer.AllocateDrawCallMaterials( mesh_list.size() );
for ( auto i = 0; i < mesh_list.size(); i++ )
materials[i] = ConvertMaterialData( mesh_list[i].material );
renderer.RecordDrawCall( info );
} );
return 1;
}
static int32_t D3D8SkinAtomicAllInOneNode( void * /*self*/,
const RxPipelineNodeParam *params )
{
static RpGeometryRw36 geometry_interface_35{};
RpAtomic * atomic;
atomic = static_cast<RpAtomic *>( params->dataParam );
RpGeometry *geom = atomic->geometry;
// RpMeshHeader *meshHeader = geom->mesh;
geometry_interface_35.Init( geom );
if ( InstanceSkinAtomic( atomic, &geometry_interface_35 ) !=
RenderStatus::Instanced )
return 0;
auto ltm = RwFrameGetLTM(
static_cast<RwFrame *>( rwObject::GetParent( atomic ) ) );
rh::rw::engine::DrawAtomic(
atomic, &geometry_interface_35,
[&ltm, atomic]( rh::rw::engine::ResEnty *res_entry ) {
auto &renderer = gRenderClient->RenderState.SkinMeshDrawCalls;
SkinDrawCallInfo info{};
info.DrawCallId = reinterpret_cast<uint64_t>( atomic );
info.MeshId = res_entry->meshData;
info.WorldTransform = DirectX::XMFLOAT4X3{
ltm->right.x, ltm->up.x, ltm->at.x, ltm->pos.x,
ltm->right.y, ltm->up.y, ltm->at.y, ltm->pos.y,
ltm->right.z, ltm->up.z, ltm->at.z, ltm->pos.z,
};
auto mesh_list = geometry_interface_35.GetMeshList();
auto materials =
renderer.AllocateDrawCallMaterials( mesh_list.size() );
for ( auto i = 0; i < mesh_list.size(); i++ )
materials[i] = ConvertMaterialData( mesh_list[i].material );
static AnimHierarcyRw36 g_anim{};
PrepareBoneMatrices( info.BoneTransform, atomic, g_anim );
renderer.RecordDrawCall( info );
} );
return 1;
}
int32_t true_hook() { return 1; }
struct CVector
{
......@@ -213,71 +113,6 @@ int32_t water_render()
return 1;
}
RpMaterial *RpMaterialStreamReadImpl( void *stream )
{
return ( reinterpret_cast<RpMaterial *(__cdecl *)( void * )>( 0x655920 ) )(
stream );
}
RwTexture *RwTextureRead( const char *name, const char *maskName )
{
return (
reinterpret_cast<RwTexture *(__cdecl *)( const char *, const char * )>(
0x64E110 ) )( name, maskName );
}
std::unordered_map<std::string, std::string> g_specular_storage{};
void InitSpecStorage()
{
static bool is_initialized = false;
if ( is_initialized )
return;
namespace fs = std::filesystem;
auto dir_path = fs::current_path() / "materials";
if ( !fs::exists( dir_path ) )
return;
for ( auto &p : fs::directory_iterator( dir_path ) )
{
const fs::path &file_path = p.path();
if ( file_path.extension() == ".mat" )
{
auto file = fopen( file_path.generic_string().c_str(), "rt" );
char specFileName[80];
char normalFileName[80];
if ( file )
{
auto res = fscanf( file, "%79s\n", specFileName );
if ( res == EOF )
fclose( file );
fclose( file );
g_specular_storage[file_path.stem().generic_string()] =
std::string( specFileName );
}
}
}
is_initialized = true;
}
RpMaterial *RpMaterialStreamRead( void *stream )
{
auto material = RpMaterialStreamReadImpl( stream );
if ( material == nullptr )
return nullptr;
auto &mat_ext = BackendMaterialPlugin::GetData( material );
if ( material->texture )
{
InitSpecStorage();
auto string_s_name = std::string( material->texture->name );
auto spec_name_iter = g_specular_storage.find( string_s_name );
if ( spec_name_iter != g_specular_storage.end() )
mat_ext.mSpecTex =
RwTextureRead( spec_name_iter->second.c_str(), nullptr );
}
return material;
}
BOOL emptystuff() { return false; }
BOOL WINAPI DllMain( HINSTANCE hModule, DWORD ul_reason_for_call,
......@@ -292,6 +127,12 @@ BOOL WINAPI DllMain( HINSTANCE hModule, DWORD ul_reason_for_call,
rh::rw::engine::IPCSettings::mMode =
rh::rw::engine::IPCRenderMode::CrossProcessClient;
rh::rw::engine::IPCSettings::mProcessName = "gta_vc_render_driver.exe";
/// Init config
auto cfg_mgr = rh::engine::ConfigurationManager::Instance();
const auto cfg_path = "rh_config.cfg";
if ( !cfg_mgr.LoadFromFile( cfg_path ) )
cfg_mgr.SaveToFile( cfg_path );
RwPointerTable gtavc_ptr_table{};
gtavc_ptr_table.mRwDevicePtr = 0x6DDE3C;
gtavc_ptr_table.mRwRwDeviceGlobalsPtr = 0x7DD708;
......@@ -317,32 +158,31 @@ BOOL WINAPI DllMain( HINSTANCE hModule, DWORD ul_reason_for_call,
RwGameHooks::Patch( gtavc_ptr_table );
//
SetPointer( 0x6DF9AC,
reinterpret_cast<void *>( D3D8AtomicAllInOneNode ) );
SetPointer(
0x6DF8EC,
reinterpret_cast<void *>( D3D8SkinAtomicAllInOneNode ) ); // skin
// Hide default sky
RedirectJump( 0x53F650, reinterpret_cast<void *>( true_hook ) );
RedirectJump( 0x53F380, reinterpret_cast<void *>( true_hook ) );
// Enable Z-Test for clouds
uint8_t ztest = 1;
Patch( 0x53FCD3, &ztest, sizeof( ztest ) );
// RedirectJump( 0x401000, reinterpret_cast<void *>( logstuff ) );
BaseModelPipeline::Patch();
SkinModelPipeline::Patch();
PatchMaterialSystem();
RwD3D8Patches::Patch();
Renderer::Patch();
/// PBR tests
RedirectCall( 0x66DD06,
reinterpret_cast<void *>( RpMaterialStreamRead ) );
RedirectJump( 0x642B70,
reinterpret_cast<void *>( MyRwEngineGetVideoModeInfo ) );
// Im3D
// SetPointer( 0x6DF754, reinterpret_cast<void *>( rxD3D8SubmitNode ) );
// check dxt support
RedirectJump( 0x61E310, reinterpret_cast<void *>( true_hook ) );
// matfx disable
// RedirectJump( 0x655EB0, reinterpret_cast<void *>( true_hook ) );
// Transparent water is buggy with RTX
RedirectCall( 0x4A65AE, reinterpret_cast<void *>( water_render ) );
// RedirectCall( 0x4A65AE, reinterpret_cast<void *>( water_render ) );
// RedirectCall( 0x4CA267, reinterpret_cast<void *>( emptystuff ) );
// Buggy cutscene shadows
RedirectJump( 0x625D80, reinterpret_cast<void *>( emptystuff ) );
// Lights
......
//
// Created by peter on 08.12.2020.
//
#include "Clock.h"
#include <injection_utils/InjectorHelpers.h>
bool Clock::GetIsTimeInRange( unsigned char from, unsigned char to )
{
return InMemoryFuncCall<bool>( 0x4870F0, from, to );
}
//
// Created by peter on 08.12.2020.
//
#pragma once
class Clock
{
public:
static bool GetIsTimeInRange( unsigned char from, unsigned char to );
};
\ No newline at end of file
//
// Created by peter on 23.05.2020.
//
#include "Entity.h"
#include <injection_utils/InjectorHelpers.h>
void Entity::CreateRwObject() { InMemoryVirtualFunc<void, 5>( this ); }
void Entity::PreRender() { InMemoryVirtualFunc<void, 12>( this ); }
void Entity::Render() { InMemoryVirtualFunc<void, 13>( this ); }
\ No newline at end of file
//
// Created by peter on 23.05.2020.
//
#pragma once
#include <common_headers.h>
#include <cstdint>
class Matrix
{
public:
RwMatrix m_matrix;
RwMatrix *m_attachment;
bool m_hasRwMatrix; // are we the owner?
};
static_assert( sizeof( Matrix ) == 0x48, "Matrix: error" );
class Placeable
{
public:
void * mVtable;
Matrix mMatrix;
};
static_assert( sizeof( Placeable ) == 0x4C, "CPlaceable: error" );
class Entity : public Placeable
{
public:
RwObject *mRwObject;
uint32_t mType : 3;
uint32_t mStatus : 5;
// flagsA
uint32_t bUsesCollision : 1; // does entity use collision
uint32_t bCollisionProcessed : 1; // has object been processed by a
// ProcessEntityCollision function
uint32_t bIsStatic : 1; // is entity static
uint32_t bHasContacted : 1; // has entity processed some contact forces
uint32_t bPedPhysics : 1;
uint32_t bIsStuck : 1; // is entity stuck
uint32_t
bIsInSafePosition : 1; // is entity in a collision free safe position
uint32_t bUseCollisionRecords : 1;
// flagsB
uint32_t bWasPostponed : 1; // was entity control processing postponed
uint32_t bExplosionProof : 1;
uint32_t bIsVisible : 1; // is the entity visible
uint32_t bHasCollided : 1;
uint32_t bRenderScorched : 1;
uint32_t bHasBlip : 1;
uint32_t bIsBIGBuilding : 1; // Set if this entity is a big building
uint32_t bStreamBIGBuilding : 1; // Set if this entity is a big building
// VC inserts one more flag here: if drawdist <= 2000
uint32_t bRenderDamaged : 1; // use damaged LOD models for objects with
// applicable damage
uint32_t bBulletProof : 1;
uint32_t bFireProof : 1;
uint32_t bCollisionProof : 1;
uint32_t bMeleeProof : 1;
uint32_t bOnlyDamagedByPlayer : 1;
uint32_t bStreamingDontDelete : 1; // Dont let the streaming remove this
uint32_t bRemoveFromWorld : 1; // remove this entity next time it should be
// processed
uint32_t bHasHitWall : 1; // has collided with a building (changes
// subsequent collisions)
uint32_t bImBeingRendered : 1; // don't delete me because I'm being rendered
uint32_t bTouchingWater : 1; // used by cBuoyancy::ProcessBuoyancy
uint32_t bIsSubway : 1; // set when subway, but maybe different meaning?
uint32_t bDrawLast : 1; // draw object last
uint32_t bNoBrightHeadLights : 1;
uint32_t bDoNotRender : 1;
uint32_t bDistanceFade : 1; // Fade entity because it is far away
uint32_t mFlagE1 : 1;
uint32_t mFlagE2 : 1;
uint32_t bOffscreen : 1; // offscreen flag. This can only be trusted when it
// is set to true
uint32_t
bIsStaticWaitingForCollision : 1; // this is used by script created
// entities - they are static until
// the collision is loaded below them
uint32_t bDontStream : 1; // tell the streaming not to stream me
uint32_t bUnderwater : 1; // this object is underwater change drawing order
uint32_t bHasPreRenderEffects : 1; // Object has a prerender effects
// attached to it
uint16_t mScanCode;
uint16_t mRandomSeed;
int16_t mModelIndex;
uint8_t mLevel; // int16
uint8_t mArea; // int16
void * mFirstReference;
void CreateRwObject();
void PreRender();
void Render();
};
static_assert( sizeof( Entity ) == 0x64, "CEntity: error" );
\ No newline at end of file
//
// Created by peter on 23.05.2020.
//
#include "ModelInfo.h"
//#include "../call_redirection_util.h"
BaseModelInfo **ModelInfo::mModelInfoPtrs =
reinterpret_cast<BaseModelInfo **>( 0x92D4C8 );
RpAtomic *SimpleModelInfo::GetAtomicFromDistance( float d )
{
d /= 4.0f;
for ( int i = mIsDamaged ? mFirstDamaged : 0; i < mNumAtomics; i++ )
if ( d < mLodDistances[i] )
return mAtomics[i];
return nullptr;
}
//
// Created by peter on 23.05.2020.
//
#pragma once
#include <common_headers.h>
#include <cstdint>
constexpr auto MAX_MODEL_NAME = 21;
enum ModeInfoType : uint8_t
{
MITYPE_NA = 0,
MITYPE_SIMPLE = 1,
MITYPE_MLO = 2,
MITYPE_TIME = 3,
MITYPE_CLUMP = 4,
MITYPE_VEHICLE = 5,
MITYPE_PED = 6,
MITYPE_XTRACOMPS = 7,
};
class BaseModelInfo
{
public:
void * mVtable;
char mName[MAX_MODEL_NAME]{};
ModeInfoType mType;
uint8_t mNum2dEffects;
bool mFreeCol;
void * mColModel;
int16_t m2dFxId;
int16_t mObjectId;
uint16_t mRefCount;
int16_t mTxdSlot;
};
static_assert( sizeof( BaseModelInfo ) == 0x28, "BaseModelInfo: error" );
class SimpleModelInfo : public BaseModelInfo
{
public:
// atomics[2] is often a pointer to the non-LOD modelinfo
RpAtomic *mAtomics[3];
// m_lodDistances[2] holds the near distance for LODs
float mLodDistances[3];
uint8_t mNumAtomics;
uint8_t mAlpha;
uint16_t mFirstDamaged : 2; // 0: no damage model
// 1: 1 and 2 are damage models
// 2: 2 is damage model
uint16_t mNormalCull : 1;
uint16_t mIsDamaged : 1;
uint16_t mIsBigBuilding : 1;
uint16_t mNoFade : 1;
uint16_t mDrawLast : 1;
uint16_t mAdditive : 1;
uint16_t mIsSubway : 1;
uint16_t mIgnoreLight : 1;
uint16_t mNoZWrite : 1;
RpAtomic * GetAtomicFromDistance( float d );
SimpleModelInfo *GetRelatedModel( void )
{
return (SimpleModelInfo *)mAtomics[2];
}
float GetNearDistance() { return mLodDistances[2] * 4.0f; }
void IncreaseAlpha()
{
if ( mAlpha >= 0xEF )
mAlpha = 0xFF;
else
mAlpha += 0x10;
}
};
static_assert( sizeof( SimpleModelInfo ) == 0x44, "SimpleModelInfo: error" );
class ModelInfo
{
public:
static BaseModelInfo *GetModelInfo( int id ) { return mModelInfoPtrs[id]; }
private:
static BaseModelInfo **mModelInfoPtrs;
};
class TimeModelInfo : public SimpleModelInfo
{
public:
int32_t mTimeOn;
int32_t mTimeOff;
SimpleModelInfo *mOtherTimeModel;
};
static_assert( sizeof( TimeModelInfo ) == 0x50, "TimeModelInfo: error" );
\ No newline at end of file
//
// Created by peter on 23.05.2020.
//
#pragma once
#include <iterator>
template <typename T> class PtrNode
{
public:
T * item;
PtrNode<T> *prev;
PtrNode<T> *next;
};
template <typename T> class PtrList
{
public:
PtrNode<T> *first;
class iterator
{
const PtrNode<T> *mIterPtr;
public:
// The std::iterator class template (used as a base class to provide
// typedefs) is deprecated in C++17. (The <iterator> header is NOT
// deprecated.) The C++ Standard has never required user-defined
// iterators to derive from std::iterator.
// To fix this warning, stop deriving from std::iterator and start
// providing publicly accessible typedefs named iterator_category,
// value_type, difference_type, pointer, and reference.
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T *;
using reference = T &;
explicit iterator( const PtrNode<T> *ptr = nullptr ) : mIterPtr( ptr )
{
}
const T * operator*() const { return mIterPtr->item; }
T * operator*() { return mIterPtr->item; }
iterator &operator++()
{
mIterPtr = mIterPtr->next;
return *this;
}
iterator operator++( int )
{
iterator retval = *this;
++( *this );
return retval;
}
iterator &operator--()
{
mIterPtr = mIterPtr->prev;
return *this;
}
iterator operator--( int )
{
iterator retval = *this;
--( *this );
return retval;
}
bool operator==( const iterator &other ) const
{
return mIterPtr == other.mIterPtr;
}
bool operator!=( const iterator &other ) const
{
return !( *this == other );
}
};
iterator begin() const { return iterator( first ); }
iterator end() const { return iterator(); }
iterator begin() { return iterator( first ); }
iterator end() { return iterator(); }
};
\ No newline at end of file
//
// Created by peter on 22.05.2020.
//
#include "Renderer.h"
#include "Clock.h"
#include "ModelInfo.h"
#include "Streaming.h"
#include <cmath>
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
#include <span>
enum Visbility
{
VIS_INVISIBLE,
VIS_VISIBLE,
VIS_OFFSCREEN,
VIS_STREAMME
};
int32_t &Renderer::mNoOfVisibleEntities =
*reinterpret_cast<int32_t *>( 0xA0D1E4 );
Entity **Renderer::mVisibleEntityPtrs = reinterpret_cast<Entity **>( 0x7D54F8 );
std::array<Entity *, 8000> Renderer::mVisibleEntities{};
RwV3d & Renderer::mCameraPosition = *reinterpret_cast<RwV3d *>( 0x975398 );
uint32_t Renderer::mLightCount = 0;
void RpAtomicSetGeometry( RpAtomic *atomic, RpGeometry *geometry, int flags )
{
InMemoryFuncCall<void>( 0x640ED0, atomic, geometry, flags );
}
void Renderer::ScanWorld()
{
mNoOfVisibleEntities = 0;
World::AdvanceScanCode();
auto sector_x = static_cast<int>( World::GetSectorX( mCameraPosition.x ) );
auto sector_y = static_cast<int>( World::GetSectorY( mCameraPosition.y ) );
float view_distance =
500.0f; // GameRendererConfigBlock::It.SectorScanDistance;
int sector_scan_x = ceil( view_distance / World::SECTOR_SIZE_X );
int sector_scan_y = ceil( view_distance / World::SECTOR_SIZE_Y );
auto min_sector_y = ( std::max )( 0, sector_y - sector_scan_y );
auto max_sector_y = ( std::min )( 80, sector_y + sector_scan_y );
auto min_sector_x = ( std::max )( 0, sector_x - sector_scan_x );
auto max_sector_x = ( std::min )( 80, sector_x + sector_scan_x );
for ( int y = min_sector_y; y < max_sector_y; y++ )
for ( int x = min_sector_x; x < max_sector_x; x++ )
ScanSectorList( World::mSectors[( y * 80 + x )] );
for ( int id = 0; id < 3; id++ )
{
auto &ptr_list = World::mBigBuildings[id];
for ( auto obj : ptr_list )
{
if ( obj->mScanCode == World::mCurrentScanCode )
continue; // already seen
obj->mScanCode = World::mCurrentScanCode;
obj->bOffscreen = false;
switch ( SetupBigBuildingVisibility( obj ) )
{
case VIS_VISIBLE:
if ( mNoOfVisibleEntities < 1000 )
mVisibleEntities[mNoOfVisibleEntities++] = obj;
break;
case VIS_STREAMME:
if ( Streaming::mNumModelsRequested <
/*GameRendererConfigBlock::It.ModelStreamLimit*/ 100 )
Streaming::RequestModel( obj->mModelIndex, 0 );
break;
case VIS_INVISIBLE: continue;
}
}
}
}
inline RwV3d operator-( RwV3d a, RwV3d b )
{
return { a.x - b.x, a.y - b.y, a.z - b.z };
}
inline float length( RwV3d a )
{
return std::sqrt( a.x * a.x + a.y * a.y + a.z * a.z );
}
void Renderer::ScanSectorList( const WorldSector &sector )
{
for ( const auto &obj_list : sector.mEntityList )
{
for ( auto obj : obj_list )
{
if ( obj->mScanCode == World::mCurrentScanCode )
continue; // already seen
obj->mScanCode = World::mCurrentScanCode;
obj->bOffscreen = false;
switch ( SetupEntityVisibility( obj ) )
{
case VIS_VISIBLE:
if ( mNoOfVisibleEntities < 1000 )
mVisibleEntities[mNoOfVisibleEntities++] = obj;
break;
case VIS_STREAMME:
if ( Streaming::mNumModelsRequested <
/*GameRendererConfigBlock::It.ModelStreamLimit*/ 100 )
Streaming::RequestModel( obj->mModelIndex, 0 );
break;
}
}
}
}
int32_t Renderer::SetupEntityVisibility( Entity *ent )
{
auto *base_mi = ModelInfo::GetModelInfo( ent->mModelIndex );
if ( ent->bDrawLast )
return VIS_INVISIBLE;
if ( base_mi->mType != MITYPE_SIMPLE && base_mi->mType != MITYPE_TIME )
{
if ( ent->mRwObject == nullptr )
return VIS_STREAMME;
return ent->bIsVisible ? VIS_VISIBLE : VIS_INVISIBLE;
}
auto * mi = static_cast<SimpleModelInfo *>( base_mi );
TimeModelInfo * ti;
SimpleModelInfo *other;
float dist;
// bool request = true;
if ( mi->mType == MITYPE_TIME )
{
ti = static_cast<TimeModelInfo *>( mi );
other = ti->mOtherTimeModel;
if ( Clock::GetIsTimeInRange( ti->mTimeOn, ti->mTimeOff ) )
{
// don't fade in, or between time objects
ti->mAlpha = 255;
}
else
{
// Hide if possible
return VIS_INVISIBLE;
}
}
else
{
/*if ( mi->m_type != MITYPE_SIMPLE )
{
if ( FindPlayerVehicle() == ent &&
TheCamera.Cams[TheCamera.ActiveCam].Mode ==
CCam::MODE_1STPERSON )
{
// Player's vehicle in first person mode
if ( TheCamera.Cams[TheCamera.ActiveCam].DirectionWasLooking ==
LOOKING_FORWARD ||
ent->GetModelIndex() == MI_RHINO ||
ent->GetModelIndex() == MI_COACH ||
TheCamera.m_bInATunnelAndABigVehicle )
{
ent->bNoBrightHeadLights = true;
}
else
{
m_pFirstPersonVehicle = (CVehicle *)ent;
ent->bNoBrightHeadLights = false;
}
return VIS_OFFSCREEN;
}
else
{
// All sorts of Clumps
if ( ent->m_rwObject == nil || !ent->bIsVisible )
return VIS_INVISIBLE;
if ( !ent->GetIsOnScreen() )
return VIS_OFFSCREEN;
if ( ent->bDrawLast )
{
dist = ( ent->GetPosition() - ms_vecCameraPosition )
.Magnitude();
CVisibilityPlugins::InsertEntityIntoSortedList( ent, dist );
ent->bDistanceFade = false;
return VIS_INVISIBLE;
}
else
return VIS_VISIBLE;
}
return VIS_INVISIBLE;
}*/
/*if ( ent->m_type == ENTITY_TYPE_OBJECT &&
( (CObject *)ent )->ObjectCreatedBy == TEMP_OBJECT )
{
if ( ent->mRwObject == nullptr || !ent->bIsVisible )
return VIS_INVISIBLE;
return ent->GetIsOnScreen() ? VIS_VISIBLE : VIS_OFFSCREEN;
}*/
if ( ent->bDontStream )
{
if ( ent->mRwObject == nullptr || !ent->bIsVisible )
return VIS_INVISIBLE;
return VIS_VISIBLE;
}
}
// Simple ModelInfo
dist = length( ent->mMatrix.m_matrix.pos - mCameraPosition );
// This can only happen with multi-atomic models (e.g. railtracks)
// but why do we bump up the distance? can only be fading...
/*if ( LOD_DISTANCE + STREAM_DISTANCE < dist &&
dist < mi->GetLargestLodDistance() )
dist = mi->GetLargestLodDistance();*/
// if ( ent->mType == ENTITY_TYPE_OBJECT && ent->bRenderDamaged )
// mi->mIsDamaged = true;
RpAtomic *a = mi->GetAtomicFromDistance( dist );
if ( a )
{
mi->mIsDamaged = false;
if ( ent->mRwObject == nullptr )
ent->CreateRwObject();
assert( ent->mRwObject );
auto *rwobj = (RpAtomic *)ent->mRwObject;
// Make sure our atomic uses the right geometry and not
// that of an atomic for another draw distance.
if ( a->geometry != rwobj->geometry )
RpAtomicSetGeometry( rwobj, a->geometry,
/*rpATOMICSAMEBOUNDINGSPHERE*/ 0x1 );
mi->IncreaseAlpha();
if ( ent->mRwObject == nullptr || !ent->bIsVisible )
return VIS_INVISIBLE;
/*if ( !ent->GetIsOnScreen() )
{
mi->m_alpha = 255;
return VIS_OFFSCREEN;
}*/
if ( mi->mAlpha != 255 )
{
// CVisibilityPlugins::InsertEntityIntoSortedList( ent, dist );
ent->bDistanceFade = true;
// return VIS_INVISIBLE;
}
/*if ( mi->m_drawLast || ent->bDrawLast )
{
CVisibilityPlugins::InsertEntityIntoSortedList( ent, dist );
ent->bDistanceFade = false;
return VIS_INVISIBLE;
}*/
return VIS_VISIBLE;
}
// Object is not loaded, figure out what to do
if ( mi->mNoFade )
{
mi->mIsDamaged = false;
// request model
// if ( dist - STREAM_DISTANCE < mi->GetLargestLodDistance() &&
// request
// )
return VIS_STREAMME;
// return VIS_INVISIBLE;
}
// We might be fading
a = mi->GetAtomicFromDistance( dist /*- FADE_DISTANCE*/ );
mi->mIsDamaged = false;
if ( a == nullptr )
{
// request model
// if ( dist - FADE_DISTANCE - STREAM_DISTANCE <
// mi->GetLargestLodDistance() &&
// request )
return VIS_STREAMME;
// return VIS_INVISIBLE;
}
if ( ent->mRwObject == nullptr )
ent->CreateRwObject();
assert( ent->mRwObject );
// RpAtomic *rwobj = (RpAtomic *)ent->mRwObject;
/*if ( RpAtomicGetGeometry( a ) != RpAtomicGetGeometry( rwobj ) )
RpAtomicSetGeometry(
rwobj, RpAtomicGetGeometry( a ),
rpATOMICSAMEBOUNDINGSPHERE ); */
mi->IncreaseAlpha();
if ( ent->mRwObject == nullptr || !ent->bIsVisible )
return VIS_INVISIBLE;
return VIS_OFFSCREEN;
/*
if ( !ent->GetIsOnScreen() )
{
mi->m_alpha = 255;
return VIS_OFFSCREEN;
}
else
{
CVisibilityPlugins::InsertEntityIntoSortedList( ent, dist );
ent->bDistanceFade = true;
return VIS_OFFSCREEN; // Why this?
}*/
}
void Renderer::PreRender()
{
Renderer::mLightCount = 0;
for ( uint32_t id = 0; id < mNoOfVisibleEntities; id++ )
{
// assert( mVisibleEntities[id] != nullptr );
mVisibleEntities[id]->PreRender();
}
}
void RenderWater()
{
InMemoryFuncCall<void>( 0x5C1710 );
InMemoryFuncCall<void>( 0x5BFF00 );
}
void Renderer::Render()
{
// Setup timecycle(move out of here)
auto &sky_state = rh::rw::engine::gRenderClient->RenderState.SkyState;
int & m_nCurrentSkyBottomBlue = *(int *)0x9B6DF4;
int & m_nCurrentSkyBottomGreen = *(int *)0x97F208;
int & m_nCurrentSkyBottomRed = *(int *)0xA0D958;
int & m_nCurrentSkyTopBlue = *(int *)0x978D1C;
int & m_nCurrentSkyTopGreen = *(int *)0xA0FD70;
int & m_nCurrentSkyTopRed = *(int *)0xA0CE98;
float &m_fCurrentAmbientRed = *(float *)0x94DBC8;
float &m_fCurrentAmbientGreen = *(float *)0x9B6AA4;
float &m_fCurrentAmbientBlue = *(float *)0x9B6AA0;
auto *vec_to_sun_arr = (RwV3d *)0x792C70;
int & current_tc_value = *(int *)0xA0CFF8;
sky_state.mSkyTopColor[0] = float( m_nCurrentSkyTopRed ) / 255.0f;
sky_state.mSkyTopColor[1] = float( m_nCurrentSkyTopGreen ) / 255.0f;
sky_state.mSkyTopColor[2] = float( m_nCurrentSkyTopBlue ) / 255.0f;
sky_state.mSkyTopColor[3] = 1.0f;
sky_state.mSkyBottomColor[0] = float( m_nCurrentSkyBottomRed ) / 255.0f;
sky_state.mSkyBottomColor[1] = float( m_nCurrentSkyBottomGreen ) / 255.0f;
sky_state.mSkyBottomColor[2] = float( m_nCurrentSkyBottomBlue ) / 255.0f;
sky_state.mSkyBottomColor[3] = 1.0f;
sky_state.mAmbientColor[0] =
m_fCurrentAmbientRed; // float( m_nCurrentSkyTopRed ) / 255.0f;
sky_state.mAmbientColor[1] =
m_fCurrentAmbientGreen; // float( m_nCurrentSkyTopGreen ) / 255.0f;
sky_state.mAmbientColor[2] =
m_fCurrentAmbientBlue; // float( m_nCurrentSkyTopBlue ) / 255.0f;
sky_state.mAmbientColor[3] = 1.0f;
sky_state.mSunDir[0] = vec_to_sun_arr[current_tc_value].x;
sky_state.mSunDir[1] = vec_to_sun_arr[current_tc_value].y;
sky_state.mSunDir[2] = vec_to_sun_arr[current_tc_value].z;
sky_state.mSunDir[3] = 1.0f;
// For no obvious reason rendering non-buildings before buildings "fixes"
// some bugs, e.g. lack of textures on palms
// TODO: Investigate why some textures reset rasters after loading
for ( uint32_t id = 0; id < mNoOfVisibleEntities; id++ )
{
assert( mVisibleEntities[id] != nullptr );
// InMemoryFuncCall<void>( 0x4C9DA0, mVisibleEntities[id] );
if ( mVisibleEntities[id]->mType != 1 )
mVisibleEntities[id]->Render();
}
for ( uint32_t id = 0; id < mNoOfVisibleEntities; id++ )
{
assert( mVisibleEntities[id] != nullptr );
// InMemoryFuncCall<void>( 0x4C9DA0, mVisibleEntities[id] );
if ( mVisibleEntities[id]->mType == 1 )
mVisibleEntities[id]->Render();
}
// Render clouds
InMemoryFuncCall<void>( 0x53FC50 );
RenderWater();
// Render rain
InMemoryFuncCall<void>( 0x57BF40 );
}
int32_t Renderer::SetupBigBuildingVisibility( Entity *ent )
{
auto *base_mi = ModelInfo::GetModelInfo( ent->mModelIndex );
if ( base_mi->mType != MITYPE_SIMPLE )
return ent->bIsVisible ? VIS_VISIBLE : VIS_INVISIBLE;
auto *mi = static_cast<SimpleModelInfo *>( base_mi );
auto dist = length( ent->mMatrix.m_matrix.pos - mCameraPosition );
SimpleModelInfo *nonLOD = mi->GetRelatedModel();
// Find out whether to draw below near distance.
// This is only the case if there is a non-LOD which is either not
// loaded or not completely faded in yet.
if ( dist < mi->GetNearDistance() &&
dist < 300.0f * /*GameRendererConfigBlock::It.LodMultiplier*/ 4.0f )
{
// No non-LOD or non-LOD is completely visible.
if ( nonLOD == nullptr || nonLOD->mAtomics[0] && nonLOD->mAlpha == 255 )
return VIS_INVISIBLE;
}
RpAtomic *a = mi->GetAtomicFromDistance( dist );
if ( a )
{
if ( ent->mRwObject == nullptr )
ent->CreateRwObject();
assert( ent->mRwObject );
// RpAtomic *rwobj = (RpAtomic *)ent->mRwObject;
if ( !ent->bIsVisible )
return VIS_INVISIBLE;
return VIS_VISIBLE;
}
return VIS_INVISIBLE;
}
void Renderer::Patch()
{
RedirectJump( 0x4C85E0, reinterpret_cast<void *>( Renderer::ScanWorld ) );
RedirectJump( 0x4CA1F0, reinterpret_cast<void *>( Renderer::PreRender ) );
RedirectJump( 0x4A6570, reinterpret_cast<void *>( Renderer::Render ) );
}
//
// Created by peter on 22.05.2020.
//
#pragma once
#include "PtrList.h"
#include "World.h"
#include <ConfigUtils/ConfigBlock.h>
#include <array>
class Renderer
{
public:
static void ScanWorld();
static void PreRender();
static void Render();
static void ScanSectorList( const WorldSector &sector );
static int32_t SetupEntityVisibility( Entity *ent );
static int32_t SetupBigBuildingVisibility( Entity *ent );
static uint32_t mLightCount;
static void Patch();
private:
static int32_t & mNoOfVisibleEntities;
static Entity ** mVisibleEntityPtrs;
static std::array<Entity *, 8000> mVisibleEntities;
static int32_t mNoOfInVisibleEntities;
static Entity ** mInVisibleEntityPtrs;
static RwV3d & mCameraPosition;
};
//
// Created by peter on 23.05.2020.
//
#include "Streaming.h"
#include <injection_utils/InjectorHelpers.h>
uint32_t &Streaming::mNumModelsRequested =
*reinterpret_cast<uint32_t *>( 0x975354 );
void Streaming::RequestModel( int32_t model, uint32_t flags )
{
InMemoryFuncCall<void>( 0x40E310, model, flags );
}
//
// Created by peter on 23.05.2020.
//
#pragma once
#include <cstdint>
class Streaming
{
public:
static void RequestModel( int32_t model, uint32_t flags );
static uint32_t &mNumModelsRequested;
};
#include "TxdStore.h"
//#include "../call_redirection_util.h"
#include <injection_utils/InjectorHelpers.h>
int32_t TxdStore::FindTxdSlot( const char *name )
{
return InMemoryFuncCall<int32_t>( 0x580D70, name );
}
void TxdStore::PushCurrentTxd() { return InMemoryFuncCall<void>( 0x580AC0 ); }
void TxdStore::PopCurrentTxd() { return InMemoryFuncCall<void>( 0x580AA0 ); }
void TxdStore::SetCurrentTxd( int32_t id )
{
return InMemoryFuncCall<void>( 0x580AD0, id );
}
//
// Created by peter on 23.02.2021.
//
#pragma once
#include <cstdint>
class TxdStore
{
public:
static int32_t FindTxdSlot( const char *name );
static void PushCurrentTxd();
static void PopCurrentTxd();
static void SetCurrentTxd( int32_t id );
// static void LoadTxd( int32_t id );
};
\ No newline at end of file
//
// Created by peter on 29.11.2020.
//
#include "Vector.h"
//
// Created by peter on 29.11.2020.
//
#pragma once
#include <cmath>
class Vector
{
public:
float x, y, z;
float Magnitude( void ) const { return sqrt( x * x + y * y + z * z ); }
};
inline Vector operator-( const Vector &left, const Vector &right )
{
return Vector{ left.x - right.x, left.y - right.y, left.z - right.z };
}
//
// Created by peter on 23.05.2020.
//
#include "World.h"
#include <injection_utils/InjectorHelpers.h>
uint16_t &World::mCurrentScanCode = *reinterpret_cast<uint16_t *>( 0xA1098E );
WorldSector * World::mSectors = reinterpret_cast<WorldSector *>( 0x792D30 );
PtrList<Entity> *World::mBigBuildings =
reinterpret_cast<PtrList<Entity> *>( 0x9785FC );
void World::AdvanceScanCode()
{
if ( ++mCurrentScanCode != 0 )
return;
ClearScanCodes();
mCurrentScanCode = 1;
}
void World::ClearScanCodes() { InMemoryFuncCall<void>( 0x4D7460 ); }
float World::GetSectorX( float f )
{
return ( ( f - WORLD_MIN_X ) / SECTOR_SIZE_X );
}
float World::GetSectorY( float f )
{
return ( ( f - WORLD_MIN_Y ) / SECTOR_SIZE_Y );
}
//
// Created by peter on 23.05.2020.
//
#pragma once
#include "Entity.h"
#include "PtrList.h"
#include <cstdint>
struct WorldSector
{
PtrList<Entity> mEntityList[10];
};
static_assert( sizeof( WorldSector ) == 0x28,
"WorldSector is binary incompatible with game struct" );
class World
{
public:
static constexpr auto SECTOR_SIZE_X = ( 50.0f );
static constexpr auto SECTOR_SIZE_Y = ( 50.0f );
static constexpr auto WORLD_MIN_X = ( -2400.0f );
static constexpr auto WORLD_MIN_Y = ( -2000.0f );
static void AdvanceScanCode();
static void ClearScanCodes();
static float GetSectorX( float f );
static float GetSectorY( float f );
private:
static uint16_t & mCurrentScanCode;
static WorldSector * mSectors;
static PtrList<Entity> *mBigBuildings;
friend class Renderer;
};
//
// Created by peter on 26.10.2020.
//
#include "base_model_pipeline.h"
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
#include <rw_engine/rp_geometry_rw36.h>
#include <rw_engine/rw_frame/rw_frame.h>
#include <rw_engine/rw_macro_constexpr.h>
#include <rw_engine/rw_rh_pipeline.h>
#include <rw_game_hooks.h>
using namespace rh::rw::engine;
static RpGeometryRw36 geometry_interface{};
static int32_t D3D8AtomicAllInOneNode( void * /*self*/,
const RxPipelineNodeParam *params )
{
RpAtomic *atomic;
atomic = static_cast<RpAtomic *>( params->dataParam );
RpGeometry *geom = atomic->geometry;
geometry_interface.Init( geom );
if ( InstanceAtomic( atomic, &geometry_interface ) !=
RenderStatus::Instanced )
return 0;
auto ltm = RwFrameGetLTM(
static_cast<RwFrame *>( rwObject::GetParent( atomic ) ) );
DrawAtomic(
atomic, &geometry_interface, [&ltm, atomic]( ResEnty *res_entry ) {
auto &renderer = gRenderClient->RenderState.MeshDrawCalls;
auto mesh_list = geometry_interface.GetMeshList();
auto materials =
renderer.AllocateDrawCallMaterials( mesh_list.size() );
for ( auto i = 0; i < mesh_list.size(); i++ )
materials[i] = ConvertMaterialData( mesh_list[i].material );
DrawCallInfo info{};
info.DrawCallId = reinterpret_cast<uint64_t>( atomic );
info.MeshId = res_entry->meshData;
info.WorldTransform = DirectX::XMFLOAT4X3{
ltm->right.x, ltm->up.x, ltm->at.x, ltm->pos.x,
ltm->right.y, ltm->up.y, ltm->at.y, ltm->pos.y,
ltm->right.z, ltm->up.z, ltm->at.z, ltm->pos.z,
};
renderer.RecordDrawCall( info );
} );
return 1;
}
void BaseModelPipeline::Patch()
{
SetPointer( 0x6DF9AC, reinterpret_cast<void *>( D3D8AtomicAllInOneNode ) );
}
//
// Created by peter on 26.10.2020.
//
#pragma once
class BaseModelPipeline
{
public:
static void Patch();
};
\ No newline at end of file
//
// Created by peter on 23.02.2021.
//
//
// Created by peter on 06.01.2021.
//
#include "material_system_patches.h"
#include "../game/TxdStore.h"
#include <common_headers.h>
#include <injection_utils/InjectorHelpers.h>
#include <material_storage.h>
#include <rw_engine/rh_backend/material_backend.h>
using namespace rh::rw::engine;
RwTexture *RwTextureRead( const char *name, const char *maskName )
{
return InMemoryFuncCall<RwTexture *>( 0x64E110, name, maskName );
}
RpMaterial *RpMaterialStreamReadImpl( void *stream )
{
return InMemoryFuncCall<RpMaterial *>( 0x655920, stream );
}
RpMaterial *RpMaterialStreamRead( void *stream )
{
auto material = RpMaterialStreamReadImpl( stream );
if ( material == nullptr )
return nullptr;
// Old RenderWare doesn't support "Always" callbacks, so we just hack it in
// for GTA VC
if ( BackendMaterialPlugin::CallAlwaysCb( material ) == FALSE )
return nullptr;
return material;
}
RwTexture *ReadTextureCallback( std::string_view dict, std::string_view name )
{
auto txd_slot = TxdStore::FindTxdSlot( dict.data() );
if ( txd_slot < 0 )
return nullptr;
TxdStore::PushCurrentTxd();
TxdStore::SetCurrentTxd( txd_slot );
auto texture = RwTextureRead( name.data(), nullptr );
TxdStore::PopCurrentTxd();
return texture;
}
void PatchMaterialSystem()
{
// Register material system texture reading CB
MaterialExtensionSystem::GetInstance().RegisterReadTextureCallback(
ReadTextureCallback );
RedirectCall( 0x66DD06, reinterpret_cast<void *>( RpMaterialStreamRead ) );
}
//
// Created by peter on 23.02.2021.
//
#pragma once
void PatchMaterialSystem();
\ No newline at end of file
//
// Created by peter on 23.02.2021.
//
#include "rwd3d8_patches.h"
#include <injection_utils/InjectorHelpers.h>
#include <rw_engine/system_funcs/rw_device_system_globals.h>
#include <vector>
/// Old renderware videomode struct, lacks format and refresh-rate
struct Rw35VideoMode
{
int32_t width;
int32_t height;
int32_t depth;
uint32_t flags;
};
Rw35VideoMode *RwEngineGetVideoModeInfoFix( Rw35VideoMode *modeinfo,
int32_t modeIndex )
{
RwVideoMode videoMode{};
if ( !rh::rw::engine::SystemHandler( rwDEVICESYSTEMGETMODEINFO, &videoMode,
nullptr, modeIndex ) )
return nullptr;
modeinfo->width = videoMode.width;
modeinfo->height = videoMode.height;
modeinfo->depth = videoMode.depth;
modeinfo->flags = videoMode.flags;
return modeinfo;
}
void RwD3D8Patches::Patch()
{
RedirectJump( 0x642B70,
reinterpret_cast<void *>( RwEngineGetVideoModeInfoFix ) );
}
//
// Created by peter on 18.01.2021.
//
#pragma once
class RwD3D8Patches
{
public:
static void Patch();
};
\ No newline at end of file
//
// Created by peter on 07.01.2021.
//
#include "skin_model_pipeline.h"
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
#include <rw_engine/anim_hierarcy_rw36.h>
#include <rw_engine/i_anim_hierarcy.h>
#include <rw_engine/rp_geometry_rw36.h>
#include <rw_engine/rw_frame/rw_frame.h>
#include <rw_engine/rw_macro_constexpr.h>
#include <rw_engine/rw_rh_skin_pipeline.h>
#include <rw_engine/system_funcs/rw_device_system_globals.h>
#include <rw_game_hooks.h>
using namespace rh::rw::engine;
static int32_t SkinD3D8AtomicAllInOneNode( void * /*self*/,
const RxPipelineNodeParam *params )
{
static RpGeometryRw36 geometry_interface_35{};
auto * atomic = static_cast<RpAtomic *>( params->dataParam );
RpGeometry * geom = atomic->geometry;
geometry_interface_35.Init( geom );
if ( InstanceSkinAtomic( atomic, &geometry_interface_35 ) !=
RenderStatus::Instanced )
return 0;
auto ltm = RwFrameGetLTM(
static_cast<RwFrame *>( rwObject::GetParent( atomic ) ) );
DrawAtomic( atomic, &geometry_interface_35,
[&ltm, atomic]( rh::rw::engine::ResEnty *res_entry ) {
auto &renderer =
gRenderClient->RenderState.SkinMeshDrawCalls;
static AnimHierarcyRw36 g_anim{};
auto mesh_list = geometry_interface_35.GetMeshList();
auto materials =
renderer.AllocateDrawCallMaterials( mesh_list.size() );
for ( auto i = 0; i < mesh_list.size(); i++ )
materials[i] =
ConvertMaterialData( mesh_list[i].material );
SkinDrawCallInfo info{};
info.DrawCallId = reinterpret_cast<uint64_t>( atomic );
info.MeshId = res_entry->meshData;
info.WorldTransform = DirectX::XMFLOAT4X3{
ltm->right.x, ltm->up.x, ltm->at.x, ltm->pos.x,
ltm->right.y, ltm->up.y, ltm->at.y, ltm->pos.y,
ltm->right.z, ltm->up.z, ltm->at.z, ltm->pos.z,
};
PrepareBoneMatrices( info.BoneTransform, atomic, g_anim );
renderer.RecordDrawCall( info );
} );
return 1;
}
void SkinModelPipeline::Patch()
{
SetPointer( 0x6DF8EC,
reinterpret_cast<void *>( SkinD3D8AtomicAllInOneNode ) );
}
//
// Created by peter on 07.01.2021.
//
#pragma once
class SkinModelPipeline
{
public:
static void Patch();
};
\ No newline at end of file
//
// Created by peter on 20.04.2020.
//
#include <ConfigUtils/ConfigurationManager.h>
#include <DebugUtils/DebugLogger.h>
#include <ipc/ipc_utils.h>
#include <ipc/shared_memory_queue_client.h>
......@@ -62,6 +63,12 @@ int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
IPCSettings::mMode = IPCRenderMode::CrossProcessRenderer;
/// Init config
auto cfg_mgr = rh::engine::ConfigurationManager::Instance();
auto cfg_path = "renderer_config.cfg";
if ( !cfg_mgr.LoadFromFile( cfg_path ) )
cfg_mgr.SaveToFile( cfg_path );
/// Init renderer
RwGameHooks::Patch(
{ .mRwDevicePtr = reinterpret_cast<INT_PTR>( &gInstance.rwDevice ) } );
......
......@@ -92,11 +92,11 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
UNITY_BUILD TRUE
)
if (MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /EHs-c-)
else ()
target_compile_options(${PROJECT_NAME} PRIVATE -fno-exceptions)
endif ()
#if (MSVC)
# target_compile_options(${PROJECT_NAME} PRIVATE /EHs-c-)
#else ()
# target_compile_options(${PROJECT_NAME} PRIVATE -fno-exceptions)
#endif ()
if (ANDROID)
target_compile_definitions(${PROJECT_NAME} PRIVATE -DANDROID_BUILD -DVK_USE_PLATFORM_ANDROID_KHR)
else ()
......@@ -105,7 +105,7 @@ else ()
endif ()
if (USE_VULKAN_API)
target_compile_definitions(${PROJECT_NAME} PRIVATE -DUSE_VULKAN_API -DVULKAN_HPP_NO_EXCEPTIONS -DJSON_NOEXCEPTION)
target_compile_definitions(${PROJECT_NAME} PRIVATE -DUSE_VULKAN_API -DVULKAN_HPP_NO_EXCEPTIONS)
endif ()
......
......@@ -137,20 +137,19 @@ bool VulkanImGUI::UploadFonts( ICommandBuffer *cmd_buff )
ImGui_ImplVulkan_CreateFontsTexture( command_buffer->GetBuffer() );
return mFontsUploaded;
}
void VulkanImGUI::DrawGui( ICommandBuffer * /*cmd_buff */ )
void VulkanImGUI::DrawGui( ICommandBuffer *cmd_buff )
{
// ImGui::Render();
ImGui::Render();
// auto command_buffer = reinterpret_cast<VulkanCommandBuffer *>( cmd_buff
// );
// ImGui_ImplVulkan_RenderDrawData( ImGui::GetDrawData(),
// command_buffer->GetBuffer() );
auto command_buffer = reinterpret_cast<VulkanCommandBuffer *>( cmd_buff );
ImGui_ImplVulkan_RenderDrawData( ImGui::GetDrawData(),
command_buffer->GetBuffer() );
}
void VulkanImGUI::BeginFrame()
{
ImGui_ImplVulkan_NewFrame();
// ImGui_ImplWin32_NewFrame();
// ImGui::NewFrame();
ImGui::NewFrame();
}
VulkanImGUI::~VulkanImGUI()
{
......
......@@ -107,6 +107,7 @@ set(SOURCES
render_driver/render_driver.cpp
render_driver/framebuffer_loop.cpp
render_driver/framebuffer_state.cpp
render_driver/imgui_win32_driver_handler.cpp
render_driver/gpu_resources/resource_mgr.cpp
render_driver/gpu_resources/raster_pool.cpp
......@@ -117,8 +118,10 @@ set(SOURCES
render_client/im3d_state_recorder.cpp
render_client/mesh_instance_state_recorder.cpp
render_client/skin_instance_state_recorder.cpp
render_client/imgui_state_recorder.cpp
material_storage.cpp)
material_storage.cpp
)
add_library(rw_rh_engine_lib STATIC ${SOURCES})
set_target_properties(${PROJECT_NAME} PROPERTIES
......
......@@ -10,6 +10,7 @@ namespace rh::rw::engine
FrameState FrameState::Deserialize( MemoryReader &reader )
{
FrameState state{};
state.ImGuiInput = reader.Read<ImGuiInputState>();
state.Viewport = reader.Read<MainViewportState>();
state.Sky = reader.Read<SkyState>();
state.Lights = AnalyticLightsState::Deserialize( reader );
......
......@@ -4,6 +4,7 @@
#pragma once
#include <Engine/Common/ArrayProxy.h>
#include <data_desc/imgui_input_state.h>
#include <data_desc/light_system/lighting_state.h>
#include <data_desc/sky_state.h>
#include <data_desc/viewport_state.h>
......@@ -19,6 +20,7 @@ struct FrameState
{
MainViewportState * Viewport;
SkyState * Sky;
ImGuiInputState * ImGuiInput;
AnalyticLightsState Lights;
Im2DRenderState Im2D;
Im3DRenderState Im3D;
......
//
// Created by peter on 24.02.2021.
//
#pragma once
namespace rh::rw::engine
{
struct ImGuiInputState
{
float MousePos[2]; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX,
// -FLT_MAX) if mouse is unavailable (on another screen,
// etc.)
bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras
// (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses
// left and right buttons. Others buttons allows us to
// track if the mouse is being used by your application +
// available to user as a convenience via IsMouse** API.
float
MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text.
float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse
// with an horizontal wheel, may not be filled by all
// back-ends.
bool KeyCtrl; // Keyboard modifier pressed: Control
bool KeyShift; // Keyboard modifier pressed: Shift
bool KeyAlt; // Keyboard modifier pressed: Alt
bool EnablePause; // Enable Pause
bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the
// "native" order your engine has access to keyboard
// keys, so you can use your own defines/enums for keys).
//
// helper
bool IsAnyMouseDown()
{
for ( auto mouse : MouseDown )
if ( mouse )
return true;
return false;
}
bool Reset()
{
for ( auto mouse : MouseDown )
if ( mouse )
return true;
return false;
}
};
} // namespace rh::rw::engine
\ No newline at end of file
#pragma once
#include <Windows.h>
#include <cstring>
#include <system_error>
#include <type_traits>
static void Patch( INT_PTR address, void *data, SIZE_T size )
{
......@@ -58,3 +60,24 @@ inline static void Nop( INT_PTR address, SIZE_T size )
memset( adr_ptr, 0x90, size );
VirtualProtect( adr_ptr, size, protect[0], &protect[1] );
}
template <typename RetType, uint32_t vtable_id, typename C, typename... Args>
auto InMemoryVirtualFunc( C _this, Args... args )
{
using return_type = RetType( __thiscall * )( C, Args... );
auto address = ( *reinterpret_cast<void ***>( _this ) )[vtable_id];
if constexpr ( std::is_void_v<RetType> )
reinterpret_cast<return_type>( address )( _this, args... );
else
return reinterpret_cast<return_type>( address )( _this, args... );
}
template <typename RetType, typename... Args>
auto InMemoryFuncCall( uint32_t address, Args... args )
{
using return_type = RetType( __cdecl * )( Args... );
if constexpr ( std::is_void_v<RetType> )
reinterpret_cast<return_type>( address )( args... );
else
return reinterpret_cast<return_type>( address )( args... );
}
\ No newline at end of file
......@@ -52,16 +52,24 @@ void MaterialExtensionSystem::ParseMaterialDesc(
return;
}
auto &desc = mMaterials[mat_desc.stem().generic_string()];
try
{
nlohmann::json desc_json{};
filestream >> desc_json;
nlohmann::json desc_json{};
filestream >> desc_json;
auto tex_name = desc_json["spec_tex"].get<std::string>();
desc.mSpecularTextureName.fill( 0 );
std::copy( tex_name.begin(), tex_name.end(),
desc.mSpecularTextureName.begin() );
auto tex_name = desc_json["spec_tex"].get<std::string>();
desc.mSpecularTextureName.fill( 0 );
std::copy( tex_name.begin(), tex_name.end(),
desc.mSpecularTextureName.begin() );
desc.mTextureDictName = desc_json["tex_dict_slot_name"].get<std::string>();
desc.mTextureDictName =
desc_json["tex_dict_slot_name"].get<std::string>();
}
catch ( const std::exception &ex )
{
rh::debug::DebugLogger::ErrorFmt(
"Failed to parse material description %s", ex.what() );
}
}
RwTexture *MaterialExtensionSystem::ReadTexture( std::string_view dictionary,
......
......@@ -2,6 +2,7 @@
// Created by peter on 10.02.2021.
//
#pragma once
#include "data_desc/imgui_input_state.h"
#include "data_desc/immediate_mode/im_state.h"
#include "data_desc/sky_state.h"
#include "data_desc/viewport_state.h"
......@@ -17,6 +18,7 @@ namespace rh::rw::engine
class ClientRenderState
{
public:
ImGuiInputState ImGuiInputState{};
MainViewportState ViewportState;
SkyState SkyState;
ImmediateState ImState;
......
//
// Created by peter on 23.02.2021.
//
#include "imgui_state_recorder.h"
#include "render_client.h"
#include <data_desc/imgui_input_state.h>
#include <imgui.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <DebugUtils/DebugLogger.h>
#include <Windows.h>
// Win32 message handler (process Win32 mouse/keyboard inputs, etc.)
LRESULT ImGui_ImplWin32_WndProcHandler( HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam )
{
if ( !rh::rw::engine::gRenderClient )
return 0;
auto &input_state =
rh::rw::engine::gRenderClient->RenderState.ImGuiInputState;
switch ( msg )
{
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONDBLCLK:
case WM_XBUTTONDOWN:
case WM_XBUTTONDBLCLK:
{
int button = 0;
if ( msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK )
{
button = 0;
}
if ( msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK )
{
button = 1;
}
if ( msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK )
{
button = 2;
}
if ( msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK )
{
button = ( GET_XBUTTON_WPARAM( wParam ) == XBUTTON1 ) ? 3 : 4;
}
if ( !input_state.IsAnyMouseDown() && ::GetCapture() == nullptr )
::SetCapture( hwnd );
input_state.MouseDown[button] = true;
return 0;
}
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_XBUTTONUP:
{
int button = 0;
if ( msg == WM_LBUTTONUP )
{
button = 0;
}
if ( msg == WM_RBUTTONUP )
{
button = 1;
}
if ( msg == WM_MBUTTONUP )
{
button = 2;
}
if ( msg == WM_XBUTTONUP )
{
button = ( GET_XBUTTON_WPARAM( wParam ) == XBUTTON1 ) ? 3 : 4;
}
input_state.MouseDown[button] = false;
if ( !input_state.IsAnyMouseDown() && ::GetCapture() == hwnd )
::ReleaseCapture();
return 0;
}
case WM_MOUSEWHEEL:
input_state.MouseWheel +=
(float)GET_WHEEL_DELTA_WPARAM( wParam ) / (float)WHEEL_DELTA;
return 0;
case WM_MOUSEHWHEEL:
input_state.MouseWheelH +=
(float)GET_WHEEL_DELTA_WPARAM( wParam ) / (float)WHEEL_DELTA;
return 0;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if ( wParam < 256 )
input_state.KeysDown[wParam] = true;
return 0;
case WM_KEYUP:
case WM_SYSKEYUP:
if ( wParam < 256 )
input_state.KeysDown[wParam] = false;
return 0;
case WM_CHAR:
// You can also use ToAscii()+GetKeyboardState() to retrieve characters.
// TODO: decide if needed
// if ( wParam > 0 && wParam < 0x10000 )
// io.AddInputCharacterUTF16( (unsigned short)wParam );
return 0;
case WM_SETCURSOR:
// if ( LOWORD( lParam ) == HTCLIENT &&
// ImGui_ImplWin32_UpdateMouseCursor() )
// return 1;
return 0;
case WM_DEVICECHANGE:
// if ( (UINT)wParam == DBT_DEVNODES_CHANGED )
// g_WantUpdateHasGamepad = true;
return 0;
}
return 0;
}
static HHOOK imgui_hook_wndproc;
LRESULT CALLBACK ImGuiMsgProc( int code, WPARAM wParam, LPARAM lParam )
{
if ( code >= 0 )
{
auto msg = reinterpret_cast<LPMSG>( lParam );
if ( ImGui_ImplWin32_WndProcHandler( msg->hwnd, msg->message,
msg->wParam, msg->lParam ) )
{
return CallNextHookEx( imgui_hook_wndproc, code, wParam, lParam );
}
}
return CallNextHookEx( imgui_hook_wndproc, code, wParam, lParam );
}
void rh::rw::engine::InstallWinProcHook()
{
imgui_hook_wndproc = SetWindowsHookEx( WH_GETMESSAGE, ImGuiMsgProc, nullptr,
GetCurrentThreadId() );
assert( imgui_hook_wndproc != nullptr );
}
void rh::rw::engine::RemoveWinProcHook()
{
assert( imgui_hook_wndproc != nullptr );
auto unhook_result = UnhookWindowsHookEx( imgui_hook_wndproc );
if ( !unhook_result )
debug::DebugLogger::ErrorFmt( "Failed to uninstall window procedure "
"hook required for imgui! ErrorCode %u",
GetLastError() );
}
void rh::rw::engine::UpdateStateClient( ImGuiInputState &state )
{
POINT pos;
if ( HWND active_window = ::GetForegroundWindow() )
if ( ::GetCursorPos( &pos ) && ::ScreenToClient( active_window, &pos ) )
{
state.MousePos[0] = (float)pos.x;
state.MousePos[1] = (float)pos.y;
}
state.KeyCtrl = ( ::GetKeyState( VK_CONTROL ) & 0x8000 ) != 0;
state.KeyShift = ( ::GetKeyState( VK_SHIFT ) & 0x8000 ) != 0;
state.KeyAlt = ( ::GetKeyState( VK_MENU ) & 0x8000 ) != 0;
}
//
// Created by peter on 23.02.2021.
//
#pragma once
namespace rh::rw::engine
{
struct ImGuiInputState;
void InstallWinProcHook();
void UpdateStateClient( ImGuiInputState &state );
void RemoveWinProcHook();
} // namespace rh::rw::engine
\ No newline at end of file
......@@ -32,6 +32,7 @@ SkinInstanceStateRecorder::AllocateDrawCallMaterials( uint64_t count )
void SkinInstanceStateRecorder::RecordDrawCall( const SkinDrawCallInfo &info )
{
assert( DrawCallCount < 100 );
auto mat_count = MeshData[DrawCallCount].MaterialListCount;
MeshData[DrawCallCount] = info;
MeshData[DrawCallCount].MaterialListStart = MaterialCount;
......
//
// Created by peter on 24.02.2021.
//
#include "imgui_win32_driver_handler.h"
#include <data_desc/imgui_input_state.h>
#include <imgui.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
namespace rh::rw::engine
{
HWND Window{};
LARGE_INTEGER Time{};
LARGE_INTEGER TicksPerSecond{};
ImGuiMouseCursor LastMouseCursor = ImGuiMouseCursor_COUNT;
bool ImGuiWin32DriverHandler::Init( void *hwnd )
{
if ( !::QueryPerformanceFrequency( &TicksPerSecond ) )
return false;
if ( !::QueryPerformanceCounter( &TicksPerSecond ) )
return false;
Window = static_cast<HWND>( hwnd );
ImGuiIO &io = ImGui::GetIO();
io.BackendFlags |=
ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor()
// values (optional)
io.BackendFlags |=
ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos
// requests (optional, rarely used)
io.BackendPlatformName = "imgui_impl_win32_rh";
io.ImeWindowHandle = hwnd;
// Keyboard mapping. ImGui will use those indices to peek into the
// io.KeysDown[] array that we will update during the application
// lifetime.
io.KeyMap[ImGuiKey_Tab] = VK_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR;
io.KeyMap[ImGuiKey_PageDown] = VK_NEXT;
io.KeyMap[ImGuiKey_Home] = VK_HOME;
io.KeyMap[ImGuiKey_End] = VK_END;
io.KeyMap[ImGuiKey_Insert] = VK_INSERT;
io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN;
io.KeyMap[ImGuiKey_A] = 'A';
io.KeyMap[ImGuiKey_C] = 'C';
io.KeyMap[ImGuiKey_V] = 'V';
io.KeyMap[ImGuiKey_X] = 'X';
io.KeyMap[ImGuiKey_Y] = 'Y';
io.KeyMap[ImGuiKey_Z] = 'Z';
return true;
}
void ImGuiWin32DriverHandler::Shutdown()
{
Window = nullptr;
Time.QuadPart = 0;
TicksPerSecond.QuadPart = 0;
LastMouseCursor = ImGuiMouseCursor_COUNT;
}
void ImGuiWin32DriverHandler::UpdateMousePos()
{
ImGuiIO &io = ImGui::GetIO();
// Set OS mouse position if requested (rarely used, only when
// ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
if ( io.WantSetMousePos )
{
POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y };
if ( ::ClientToScreen( Window, &pos ) )
::SetCursorPos( pos.x, pos.y );
}
// Set mouse position
io.MousePos = ImVec2( -FLT_MAX, -FLT_MAX );
POINT pos;
if ( HWND active_window = ::GetForegroundWindow() )
if ( active_window == Window || ::IsChild( active_window, Window ) )
if ( ::GetCursorPos( &pos ) && ::ScreenToClient( Window, &pos ) )
io.MousePos = ImVec2( (float)pos.x, (float)pos.y );
}
bool ImGuiWin32DriverHandler::UpdateMouseCursor()
{
ImGuiIO &io = ImGui::GetIO();
if ( io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange )
return false;
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if ( imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor )
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no
// cursor
::SetCursor( nullptr );
}
else
{
// Show OS mouse cursor
LPTSTR win32_cursor;
switch ( imgui_cursor )
{
default:
case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break;
case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break;
case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break;
case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break;
case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break;
case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break;
case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break;
case ImGuiMouseCursor_Hand: win32_cursor = IDC_HAND; break;
case ImGuiMouseCursor_NotAllowed: win32_cursor = IDC_NO; break;
}
::SetCursor( ::LoadCursor( nullptr, win32_cursor ) );
}
return true;
}
void ImGuiWin32DriverHandler::NewFrame( const ImGuiInputState &state )
{
ImGui::GetCurrentContext();
ImGuiIO &io = ImGui::GetIO();
/* IM_ASSERT( io.Fonts->IsBuilt() &&
"Font atlas not built! It is generally built by the renderer "
"backend. Missing call to renderer NewFrame() function? e.g. "
"ImGui_ImplVulkan_NewFrame()." );*/
// Setup display size (every frame to accommodate for window resizing)
RECT rect = { 0, 0, 0, 0 };
if ( !::GetClientRect( Window, &rect ) )
assert( false && "Failed to get client rect for renderer window!" );
io.DisplaySize = ImVec2( (float)( rect.right - rect.left ),
(float)( rect.bottom - rect.top ) );
// Setup time step
LARGE_INTEGER current_time = { 0ll };
::QueryPerformanceCounter( &current_time );
io.DeltaTime = (float)( current_time.QuadPart - Time.QuadPart ) /
TicksPerSecond.QuadPart;
Time = current_time;
// Read keyboard modifiers inputs
io.KeyCtrl = state.KeyCtrl; //( ::GetKeyState( VK_CONTROL ) & 0x8000 ) != 0;
io.KeyShift = state.KeyShift; //( ::GetKeyState( VK_SHIFT ) & 0x8000 ) != 0;
io.KeyAlt = state.KeyAlt; //( ::GetKeyState( VK_MENU ) & 0x8000 ) != 0;
io.KeySuper = false;
// Filled by window proc handler on client side
for ( auto i = 0; i < 5; i++ )
io.MouseDown[i] = state.MouseDown[i];
for ( auto i = 0; i < 512; i++ )
io.KeysDown[i] = state.KeysDown[i];
io.MouseWheel = state.MouseWheel;
io.MouseWheelH = state.MouseWheelH;
// Update OS mouse position
UpdateMousePos();
io.MousePos = ImVec2( state.MousePos[0], state.MousePos[1] );
// Update OS mouse cursor with the cursor requested by imgui
ImGuiMouseCursor mouse_cursor =
io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor();
if ( LastMouseCursor != mouse_cursor )
{
LastMouseCursor = mouse_cursor;
UpdateMouseCursor();
}
}
} // namespace rh::rw::engine
\ No newline at end of file
//
// Created by peter on 24.02.2021.
//
#pragma once
namespace rh::rw::engine
{
struct ImGuiInputState;
class ImGuiWin32DriverHandler
{
public:
bool Init( void *hwnd );
void Shutdown();
void UpdateMousePos();
bool UpdateMouseCursor();
void NewFrame( const ImGuiInputState &state );
};
} // namespace rh::rw::engine
\ No newline at end of file
......@@ -13,6 +13,7 @@
#include <Engine/Common/IDeviceState.h>
#include <Engine/VulkanImpl/VulkanCommandBuffer.h>
#include <Engine/VulkanImpl/VulkanDeviceState.h>
#include <imgui.h>
#include <render_driver/gpu_resources/raster_pool.h>
#include <rw_engine/rh_backend/raster_backend.h>
#include <rw_engine/rw_macro_constexpr.h>
......@@ -271,5 +272,10 @@ void RTAOPass::Execute( void *tlas, rh::engine::ICommandBuffer *cmd_buffer )
mBilFil0->Execute( vk_cmd_buff );
}
rh::engine::IImageView *RTAOPass::GetAOView() { return mBlurredAOBufferView; }
void RTAOPass::UpdateUI()
{
ImGui::DragFloat( "AO radius:", &mParams.max_distance, 0.5f, 0.5f,
10000.0f );
}
} // namespace rh::rw::engine
\ No newline at end of file
......@@ -52,6 +52,7 @@ class RTAOPass
rh::engine::IImageView *GetAOView();
void Execute( void *tlas, rh::engine::ICommandBuffer *cmd_buffer );
void UpdateUI();
private:
rh::engine::IDeviceState & Device;
......
......@@ -22,6 +22,7 @@
#include <Engine/EngineConfigBlock.h>
#include <Engine/VulkanImpl/VulkanCommandBuffer.h>
#include <Engine/VulkanImpl/VulkanDeviceState.h>
#include <Engine/VulkanImpl/VulkanWin32Window.h>
#include <data_desc/frame_info.h>
#include <imgui.h>
#include <ipc/MemoryReader.h>
......@@ -215,8 +216,9 @@ RayTracingRenderer::Render( const FrameState & state,
if ( imgui )
{
ImGuiDriver->NewFrame( *state.ImGuiInput );
imgui->BeginFrame();
DrawGUI();
DrawGUI( state );
imgui->DrawGui( dest );
}
dest->EndRenderPass();
......@@ -343,7 +345,7 @@ RayTracingRenderer::~RayTracingRenderer()
delete fb;
}
void RayTracingRenderer::DrawGUI()
void RayTracingRenderer::DrawGUI( const FrameState &scene )
{
static auto last_frame_time = std::chrono::high_resolution_clock::now();
auto ms_from_lf =
......@@ -351,7 +353,6 @@ void RayTracingRenderer::DrawGUI()
std::chrono::high_resolution_clock::now() - last_frame_time )
.count() /
1000.0f;
return;
ImGui::Begin( "Info" );
ImGui::BeginGroup();
......@@ -368,7 +369,80 @@ void RayTracingRenderer::DrawGUI()
mFrameTimeGraph.size();
ImGui::Text( "Avg CPU record time:%.3f ms.", avg_frame_rec_time );
ImGui::Text( "FPS:%.1f", 1000.0f / ( ms_from_lf + 0.0001f ) );
mRTAOPass->UpdateUI();
ImGui::EndGroup();
if ( ImGui::CollapsingHeader( "Im2DCalls" ) )
{
ImGui::Text( "Im2DCall count: %llu",
static_cast<uint64_t>( scene.Im2D.DrawCalls.Size() ) );
int id = 0;
for ( const auto &im2d_call : scene.Im2D.DrawCalls )
{
ImGui::PushID( id );
if ( ImGui::CollapsingHeader( "Im2DCall" ) )
{
ImGui::Text( "RasterId : %llu", im2d_call.mRasterId );
ImGui::Text( "Index count : %u", im2d_call.mVertexCount );
ImGui::Text( "Vertex count : %u", im2d_call.mIndexCount );
}
ImGui::PopID();
id++;
}
}
if ( ImGui::CollapsingHeader( "AnimatedDrawCalls" ) )
{
ImGui::Text(
"AnimatedInstances count: %llu",
static_cast<uint64_t>( scene.SkinInstances.DrawCalls.Size() ) );
int id = 0;
int mtx_id = 0;
for ( const auto &instance : scene.SkinInstances.DrawCalls )
{
ImGui::PushID( id );
if ( ImGui::CollapsingHeader( "AnimatedInstance" ) )
{
ImGui::Text( "Instance Id : %llu", instance.DrawCallId );
ImGui::Text( "Mesh id : %llu", instance.MeshId );
ImGui::Text(
"World Transform\n "
"row_0 - x:%f; y:%f; z:%f;\n"
"row_1 - x:%f; y:%f; z:%f;\n"
"row_2 - x:%f; y:%f; z:%f;\n"
"row_3 - x:%f; y:%f; z:%f;",
instance.WorldTransform._11, instance.WorldTransform._12,
instance.WorldTransform._13, instance.WorldTransform._21,
instance.WorldTransform._22, instance.WorldTransform._23,
instance.WorldTransform._31, instance.WorldTransform._32,
instance.WorldTransform._33, instance.WorldTransform._41,
instance.WorldTransform._42, instance.WorldTransform._43 );
if ( ImGui::CollapsingHeader( "Matrices" ) )
{
for ( const auto &bone_mtx : instance.BoneTransform )
{
ImGui::PushID( mtx_id );
ImGui::Text( "Matrix %u\n "
"row_0 - x:%f; y:%f; z:%f;\n"
"row_1 - x:%f; y:%f; z:%f;\n"
"row_2 - x:%f; y:%f; z:%f;\n"
"row_3 - x:%f; y:%f; z:%f;",
mtx_id, bone_mtx._11, bone_mtx._12,
bone_mtx._13, bone_mtx._21, bone_mtx._22,
bone_mtx._23, bone_mtx._31, bone_mtx._32,
bone_mtx._33, bone_mtx._41, bone_mtx._42,
bone_mtx._43 );
ImGui::PopID();
mtx_id++;
}
}
}
ImGui::PopID();
id++;
}
}
ImGui::End();
last_frame_time = std::chrono::high_resolution_clock::now();
}
......@@ -406,11 +480,12 @@ RayTracingRenderer::GetImGui( rh::engine::IRenderPass *pass )
using namespace rh::engine;
if ( mImGUI )
return mImGUI;
ImGuiDriver = new ImGuiWin32DriverHandler();
mImGUI = dynamic_cast<VulkanDeviceState &>( Device ).CreateImGUI( &Window );
mImGUI->Init( { pass } );
ImGuiDriver->Init(
dynamic_cast<VulkanWin32Window &>( Window ).GetHandle() );
ScopedPointer cmd_buffer = Device.CreateCommandBuffer();
cmd_buffer->BeginRecord();
......
......@@ -10,6 +10,7 @@
#include <array>
#include <render_client/mesh_instance_state_recorder.h>
#include <render_driver/frame_renderer.h>
#include <render_driver/imgui_win32_driver_handler.h>
#include <rw_engine/rh_backend/im2d_backend.h>
#include <rw_engine/rh_backend/im2d_renderer.h>
#include <rw_engine/rh_backend/im3d_renderer.h>
......@@ -75,7 +76,7 @@ class RayTracingRenderer : public IFrameRenderer
std::vector<rh::engine::CommandBufferSubmitInfo>
Render( const FrameState &scene, rh::engine::ICommandBuffer *dest,
const rh::engine::SwapchainFrame &frame ) override;
void DrawGUI();
void DrawGUI( const FrameState &scene );
rh::engine::IRenderPass *GetForwardPass();
rh::engine::VulkanImGUI *GetImGui( rh::engine::IRenderPass *pass );
......@@ -115,6 +116,7 @@ class RayTracingRenderer : public IFrameRenderer
ScopedPointer<DeferredCompositionPass> mDeferredComposePass;
ScopedPointer<TiledLightCulling> mTiledLightCulling;
ScopedPointer<rh::engine::VulkanImGUI> mImGUI;
ScopedPointer<ImGuiWin32DriverHandler> ImGuiDriver;
float mCPURecordTime = 0;
uint64_t mGameViewRasterId = 0;
uint64_t mBlasBuilt = 0;
......
......@@ -58,4 +58,8 @@ BackendRasterExt::~BackendRasterExt()
mImageId = BackendRasterPlugin::NullRasterId;
}
BackendRasterExt::BackendRasterExt()
{
mImageId = BackendRasterPlugin::NullRasterId;
}
} // namespace rh::rw::engine
......@@ -30,11 +30,14 @@ class BackendRasterPlugin
struct BackendRasterExt
{
uint64_t mImageId = BackendRasterPlugin::NullRasterId;
uint64_t mImageId;
~BackendRasterExt();
BackendRasterExt() = default;
BackendRasterExt( const BackendRasterExt & ) = delete;
BackendRasterExt();
BackendRasterExt( const BackendRasterExt & ) = delete;
BackendRasterExt( BackendRasterExt &&other ) noexcept = delete;
BackendRasterExt &operator=( const BackendRasterExt &other ) = delete;
BackendRasterExt &operator=( BackendRasterExt &&other ) = delete;
};
struct RasterHeader
......
......@@ -127,7 +127,7 @@ void GenerateNormals( VertexDescPosColorUVNormals *verticles,
}
RwResEntry *InstanceAtomicGeometry( RpGeometryInterface *geom_io, void *owner,
RwResEntry *& resEntryPointer,
RwResEntry ** resEntryPointer,
const RpMeshHeader *meshHeader )
{
using namespace rh::engine;
......@@ -135,14 +135,14 @@ RwResEntry *InstanceAtomicGeometry( RpGeometryInterface *geom_io, void *owner,
resEntry = reinterpret_cast<ResEnty *>(
gRwDeviceGlobals.ResourceFuncs.AllocateResourceEntry(
owner, &resEntryPointer, sizeof( ResEnty ) - sizeof( RwResEntry ),
owner, resEntryPointer, sizeof( ResEnty ) - sizeof( RwResEntry ),
[]( RwResEntry *resEntry ) noexcept {
auto *entry = reinterpret_cast<ResEnty *>( resEntry );
if ( entry != nullptr )
DestroyBackendMesh( entry->meshData );
} ) );
resEntryPointer = resEntry;
*resEntryPointer = resEntry;
if ( resEntry == nullptr )
return nullptr;
......@@ -179,12 +179,7 @@ RwResEntry *InstanceAtomicGeometry( RpGeometryInterface *geom_io, void *owner,
material.mDiffuseColor = mesh_material->color;
material.mSpecular = mesh_material->surfaceProps.specular;
material.mDiffuseRasterIdx = -1;
if ( mesh_material->texture && mesh_material->texture->raster )
{
auto &raster =
BackendRasterPlugin::GetData( mesh_material->texture->raster );
material.mDiffuseRasterIdx = raster.mImageId;
}
geometry_mats.push_back( material );
if ( convert_to_list )
......@@ -370,21 +365,21 @@ RenderStatus InstanceAtomic( RpAtomic *atomic, RpGeometryInterface *geom_io )
}
if ( resEntry != nullptr )
return RenderStatus::Instanced;
RwResEntry *&resEntryPointer = geom_io->GetResEntryRef();
RwResEntry **resEntryPointer = &geom_io->GetResEntryRef();
void * owner;
meshHeader = geom_io->GetMeshHeader();
if ( geom_io->GetMorphTargetCount() != 1 )
{
owner = atomic;
resEntryPointer = atomic->repEntry;
resEntryPointer = &atomic->repEntry;
}
else
{
owner = atomic->geometry;
resEntryPointer = geom_io->GetResEntryRef();
resEntryPointer = &geom_io->GetResEntryRef();
}
resEntry = InstanceAtomicGeometry(
geom_io, owner, geom_io->GetResEntryRef(), meshHeader );
resEntry = InstanceAtomicGeometry( geom_io, owner, resEntryPointer,
meshHeader );
if ( resEntry == nullptr )
return RenderStatus::Failure;
geom_io->Unlock();
......@@ -423,9 +418,13 @@ void DrawAtomic( RpAtomic *atomic, RpGeometryInterface *geom_io,
const std::function<void( ResEnty * )> &render_callback )
{
geom_io->Init( atomic->geometry );
auto *resEntry = reinterpret_cast<ResEnty *>( geom_io->GetResEntry() );
ResEnty *entry;
if ( geom_io->GetMorphTargetCount() != 1 )
entry = reinterpret_cast<ResEnty *>( atomic->repEntry );
else
entry = reinterpret_cast<ResEnty *>( geom_io->GetResEntry() );
if ( render_callback )
render_callback( resEntry );
render_callback( entry );
// if ( resEntry && resEntry->modelInstance )
// pipeline->DrawMesh( context, resEntry->modelInstance );
}
......
......@@ -7,7 +7,6 @@
#include <DirectXMathConvert.inl>
#include <DirectXMathMatrix.inl>
#include <Engine/Common/types/primitive_type.h>
#include <rw_engine/rh_backend/material_backend.h>
#include <rw_engine/rh_backend/mesh_rendering_backend.h>
#include <rw_engine/rh_backend/raster_backend.h>
#include <rw_engine/rh_backend/skinned_mesh_backend.h>
......@@ -75,7 +74,7 @@ void GenerateSkinNormals( VertexDescPosColorUVNormals *verticles,
RwResEntry *RHInstanceSkinAtomicGeometry( RpGeometryInterface *geom_io,
void * owner,
RwResEntry *& resEntryPointer,
RwResEntry ** resEntryPointer,
const RpMeshHeader * meshHeader,
RpHAnimHierarchy * pHierarchy,
RwFrame * pFrame )
......@@ -84,14 +83,14 @@ RwResEntry *RHInstanceSkinAtomicGeometry( RpGeometryInterface *geom_io,
resEntry = reinterpret_cast<ResEnty *>(
gRwDeviceGlobals.ResourceFuncs.AllocateResourceEntry(
owner, &resEntryPointer, sizeof( ResEnty ) - sizeof( RwResEntry ),
owner, resEntryPointer, sizeof( ResEnty ) - sizeof( RwResEntry ),
[]( RwResEntry *resEntry ) noexcept {
auto *entry = reinterpret_cast<ResEnty *>( resEntry );
if ( entry != nullptr )
DestroySkinMesh( entry->meshData );
} ) );
resEntryPointer = resEntry;
*resEntryPointer = resEntry;
if ( resEntry == nullptr )
return nullptr;
......@@ -433,27 +432,26 @@ RenderStatus InstanceSkinAtomic( RpAtomic * atomic,
}
if ( resEntry != nullptr )
return RenderStatus::Instanced;
RwResEntry *&resEntryPointer = geom_io->GetResEntryRef();
RwResEntry **resEntryPointer = &geom_io->GetResEntryRef();
void * owner;
meshHeader = geom_io->GetMeshHeader();
if ( geom_io->GetMorphTargetCount() != 1 )
{
owner = atomic;
resEntryPointer = atomic->repEntry;
resEntryPointer = &atomic->repEntry;
}
else
{
owner = atomic->geometry;
resEntryPointer = geom_io->GetResEntryRef();
resEntryPointer = &geom_io->GetResEntryRef();
}
auto anim_hier =
gRwDeviceGlobals.SkinFuncs.AtomicGetHAnimHierarchy( atomic );
auto frame = static_cast<RwFrame *>( rwObject::GetParent( atomic ) );
resEntry = RHInstanceSkinAtomicGeometry( geom_io, owner,
geom_io->GetResEntryRef(),
meshHeader, anim_hier, frame );
resEntry = RHInstanceSkinAtomicGeometry(
geom_io, owner, resEntryPointer, meshHeader, anim_hier, frame );
if ( resEntry == nullptr )
return RenderStatus::Failure;
geom_io->Unlock();
......
......@@ -8,6 +8,7 @@
#include <Engine/Common/ISwapchain.h>
#include <data_desc/frame_info.h>
#include <ipc/shared_memory_queue_client.h>
#include <render_client/imgui_state_recorder.h>
#include <render_client/render_client.h>
#include <render_driver/gpu_resources/resource_mgr.h>
#include <render_driver/render_driver.h>
......@@ -44,6 +45,8 @@ T ReadChainBlock( MemoryReader &reader )
void SerializeDrawCalls( MemoryWriter &&writer )
{
auto &state = gRenderClient->RenderState;
writer.Write( &state.ImGuiInputState );
writer.Write( &state.ViewportState );
writer.Write( &state.SkyState );
state.Lights.Serialize( writer );
......@@ -55,7 +58,62 @@ void SerializeDrawCalls( MemoryWriter &&writer )
bool RenderSceneCmd::Invoke()
{
TaskQueue.ExecuteTask( SharedMemoryTaskType::RENDER, SerializeDrawCalls );
auto & state = gRenderClient->RenderState;
static auto key_ctrl_state = 0;
bool was_paused = false;
/// Debug Pause Loop, allows to stop execution for a moment and change some
/// values
do
{
UpdateStateClient( state.ImGuiInputState );
// TODO: Replace with something more flexible
key_ctrl_state += ( state.ImGuiInputState.KeyAlt &&
state.ImGuiInputState.KeysDown['1'] )
? 1
: -1;
if ( key_ctrl_state > 3 )
state.ImGuiInputState.EnablePause =
!state.ImGuiInputState.EnablePause;
if ( key_ctrl_state < 0 )
key_ctrl_state = 0;
TaskQueue.ExecuteTask( SharedMemoryTaskType::RENDER,
SerializeDrawCalls );
if ( state.ImGuiInputState.EnablePause )
{
MSG msg;
// Read pending messages
while (
PeekMessageA( &msg, nullptr, 0, 0, PM_NOYIELD | PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessageA( &msg );
}
// Show cursor on pause
{
auto result = ::ShowCursor( true );
while ( result < 0 )
result = ::ShowCursor( true );
}
was_paused = true;
}
} while ( state.ImGuiInputState.EnablePause );
// Hide cursor if game was paused
if ( was_paused )
{
{
auto result = ::ShowCursor( false );
while ( result >= 0 )
result = ::ShowCursor( false );
}
}
gRenderClient->RenderState.Im2D.Flush();
gRenderClient->RenderState.Im3D.Flush();
gRenderClient->RenderState.MeshDrawCalls.Flush();
......
......@@ -2,10 +2,10 @@
// Created by peter on 19.01.2021.
//
#include "start_cmd.h"
#include "rw_device_system_globals.h"
#include <DebugUtils/DebugLogger.h>
#include <Engine/Common/IDeviceState.h>
#include <ipc/shared_memory_queue_client.h>
#include <render_client/imgui_state_recorder.h>
#include <render_driver/render_driver.h>
namespace rh::rw::engine
......@@ -23,6 +23,7 @@ bool StartSystemCmdImpl::Invoke( HWND window )
// serialize
memory_writer.Write( &window );
} );
InstallWinProcHook();
return true;
}
......
......@@ -5,6 +5,7 @@
#include "stop_cmd.h"
#include <Engine/Common/IDeviceState.h>
#include <ipc/shared_memory_queue_client.h>
#include <render_client/imgui_state_recorder.h>
#include <render_driver/render_driver.h>
namespace rh::rw::engine
......@@ -17,6 +18,7 @@ StopSystemCmdImpl::StopSystemCmdImpl( SharedMemoryTaskQueue &task_queue )
bool StopSystemCmdImpl::Invoke()
{
RemoveWinProcHook();
TaskQueue.ExecuteTask( SharedMemoryTaskType::DESTROY_WINDOW );
return true;
}
......
#include "rw_game_hooks.h"
#include <DebugUtils/DebugLogger.h>
#include <MemoryInjectionUtils/InjectorHelpers.h>
#include <injection_utils/InjectorHelpers.h>
#include <render_client/render_client.h>
#include <rw_engine/rh_backend/im2d_backend.h>
#include <rw_engine/rh_backend/im3d_backend.h>
......
......@@ -62,10 +62,12 @@ void main()
vec4 normals_d = imageLoad(normals, ivec2(gl_LaunchIDNV.xy));
vec4 mat_params = imageLoad(material_params, ivec2(gl_LaunchIDNV.xy));
normals_d.xyz = normalize(normals_d.xyz);
vec3 hitValue = vec3(0);
float roughness = (1 - mat_params.g);
float refl_int = MicrofacetFresnel(-direction.xyz, normals_d.xyz, 0);
vec4 world_pos = vec4(origin.xyz + direction.xyz * normals_d.w, 1);
if (normals_d.w > 1000.0f || mat_params.r * refl_int< 0.01f){
if (normals_d.w > 1000.0f || mat_params.r * refl_int< 0.01f || roughness > 0.65f){
imageStore(result, ivec2(gl_LaunchIDNV.xy), vec4(0.0, 0.0, 0.0, 0));
return;
}
......@@ -75,8 +77,6 @@ void main()
vec3 random_s_dir = texture(sampler2D(randomNoise, baseSampler), vec2((gl_LaunchIDNV.xy)%64+ jitter.xy * (refl_params.time_stamp%64))/64.0f).xyz * 2.0f - 1.0f.xxx;
vec3 hitValue = vec3(0);
float roughness = (1 - mat_params.g);
roughness*=roughness;
vec3 ref_dir = normalize(reflect(direction.xyz, normals_d.xyz) + random_s_dir * roughness * 0.5f);
traceNV(topLevelAS, rayFlags,
......