2022-02-27 12:53:44 +00:00
# include <STDInclude.hpp>
2022-06-22 17:36:06 +02:00
# include "IMaterial.hpp"
2017-01-19 22:23:59 +01:00
2023-01-03 12:16:44 +00:00
# include <Utils/Json.hpp>
2022-12-14 09:40:15 +01:00
# define IW4X_MAT_BIN_VERSION "1"
# define IW4X_MAT_JSON_VERSION 1
2017-01-19 22:23:59 +01:00
namespace Assets
{
2022-12-14 09:40:15 +01:00
const std : : unordered_map < std : : string , std : : string > techSetCorrespondance =
{
{ " effect " , " effect_blend " } ,
{ " effect " , " effect_blend " } ,
{ " effect_nofog " , " effect_blend_nofog " } ,
{ " effect_zfeather " , " effect_zfeather_blend " } ,
{ " effect_zfeather_falloff " , " effect_zfeather_falloff_add " } ,
{ " effect_zfeather_nofog " , " effect_zfeather_add_nofog " } ,
{ " wc_unlit_add " , " wc_unlit_add_lin " } ,
{ " wc_unlit_distfalloff " , " wc_unlit_distfalloff_replace " } ,
{ " wc_unlit_multiply " , " wc_unlit_multiply_lin " } ,
{ " wc_unlit_falloff_add " , " wc_unlit_falloff_add_lin " } ,
{ " wc_unlit " , " wc_unlit_replace_lin " } ,
{ " wc_unlit_alphatest " , " wc_unlit_blend_lin " } ,
{ " wc_unlit_blend " , " wc_unlit_blend_lin " } ,
{ " wc_unlit_replace " , " wc_unlit_replace_lin " } ,
{ " wc_unlit_nofog " , " wc_unlit_replace_lin_nofog_nocast " } ,
{ " mc_unlit_replace " , " mc_unlit_replace_lin " } ,
{ " mc_unlit_nofog " , " mc_unlit_blend_nofog_ua " } ,
{ " mc_unlit " , " mc_unlit_replace_lin_nocast " } ,
{ " mc_unlit_alphatest " , " mc_unlit_blend_lin " } ,
{ " mc_effect_nofog " , " mc_effect_blend_nofog " } ,
{ " mc_effect_falloff_add_nofog " , " mc_effect_falloff_add_nofog_eyeoffset " } ,
} ;
2018-12-17 14:29:18 +01:00
void IMaterial : : load ( Game : : XAssetHeader * header , const std : : string & name , Components : : ZoneBuilder : : Zone * builder )
2017-01-19 22:23:59 +01:00
{
2022-12-14 09:40:15 +01:00
if ( ! header - > data ) this - > loadJson ( header , name , builder ) ; // Check if we want to load a material from disk
if ( ! header - > data ) this - > loadBinary ( header , name , builder ) ; // Check if we want to load a material from disk (binary format)
2017-01-19 22:23:59 +01:00
if ( ! header - > data ) this - > loadNative ( header , name , builder ) ; // Check if there is a native one
2022-12-14 09:40:15 +01:00
}
void IMaterial : : loadJson ( Game : : XAssetHeader * header , const std : : string & name , [[maybe_unused]] Components : : ZoneBuilder : : Zone * builder )
{
Components : : FileSystem : : File materialInfo ( std : : format ( " materials/{}.iw4x.json " , name ) ) ;
if ( ! materialInfo . exists ( ) ) return ;
Game : : Material * asset = builder - > getAllocator ( ) - > allocate < Game : : Material > ( ) ;
nlohmann : : json materialJson ;
try
{
materialJson = nlohmann : : json : : parse ( materialInfo . getBuffer ( ) ) ;
}
catch ( const std : : exception & e )
{
Components : : Logger : : Print ( " Invalid material json for {} (broken json {}) \n " , name , e . what ( ) ) ;
}
if ( ! materialJson . is_object ( ) )
{
Components : : Logger : : Print ( " Invalid material json for {} (Is it zonebuilder format?) \n " , name ) ;
return ;
}
if ( materialJson [ " version " ] . get < int > ( ) ! = IW4X_MAT_JSON_VERSION )
{
Components : : Logger : : PrintError ( Game : : CON_CHANNEL_ERROR , " Invalid material json version for {}, expected {} and got {} \n " , name , IW4X_MAT_JSON_VERSION , materialJson [ " version " ] . get < std : : string > ( ) ) ;
return ;
}
try
{
asset - > info . name = builder - > getAllocator ( ) - > duplicateString ( materialJson [ " name " ] . get < std : : string > ( ) ) ;
asset - > info . gameFlags = static_cast < char > ( Utils : : Json : : ReadFlags ( materialJson [ " gameFlags " ] . get < std : : string > ( ) , sizeof ( char ) ) ) ;
asset - > info . sortKey = materialJson [ " sortKey " ] . get < char > ( ) ;
// * We do techset later * //
asset - > info . textureAtlasRowCount = materialJson [ " textureAtlasRowCount " ] . get < unsigned char > ( ) ;
asset - > info . textureAtlasColumnCount = materialJson [ " textureAtlasColumnCount " ] . get < unsigned char > ( ) ;
asset - > info . surfaceTypeBits = static_cast < unsigned int > ( Utils : : Json : : ReadFlags ( materialJson [ " surfaceTypeBits " ] . get < std : : string > ( ) , sizeof ( int ) ) ) ;
asset - > info . hashIndex = materialJson [ " hashIndex " ] . get < unsigned short > ( ) ;
asset - > cameraRegion = materialJson [ " cameraRegion " ] . get < char > ( ) ;
}
catch ( const nlohmann : : json : : exception & e )
{
Components : : Logger : : PrintError ( Game : : CON_CHANNEL_ERROR , " Invalid material json for {} (broken json {}) \n " , name , e . what ( ) ) ;
return ;
}
if ( materialJson [ " gfxDrawSurface " ] . is_object ( ) )
{
asset - > info . drawSurf . fields . customIndex = materialJson [ " gfxDrawSurface " ] [ " customIndex " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . hasGfxEntIndex = materialJson [ " gfxDrawSurface " ] [ " hasGfxEntIndex " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . materialSortedIndex = materialJson [ " gfxDrawSurface " ] [ " materialSortedIndex " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . objectId = materialJson [ " gfxDrawSurface " ] [ " objectId " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . prepass = materialJson [ " gfxDrawSurface " ] [ " prepass " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . primarySortKey = materialJson [ " gfxDrawSurface " ] [ " primarySortKey " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . reflectionProbeIndex = materialJson [ " gfxDrawSurface " ] [ " reflectionProbeIndex " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . sceneLightIndex = materialJson [ " gfxDrawSurface " ] [ " sceneLightIndex " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . surfType = materialJson [ " gfxDrawSurface " ] [ " surfType " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . unused = materialJson [ " gfxDrawSurface " ] [ " unused " ] . get < long long > ( ) ;
asset - > info . drawSurf . fields . useHeroLighting = materialJson [ " gfxDrawSurface " ] [ " useHeroLighting " ] . get < long long > ( ) ;
}
asset - > stateFlags = static_cast < char > ( Utils : : Json : : ReadFlags ( materialJson [ " stateFlags " ] . get < std : : string > ( ) , sizeof ( char ) ) ) ;
if ( materialJson [ " textureTable " ] . is_array ( ) )
{
nlohmann : : json : : array_t textureTable = materialJson [ " textureTable " ] ;
asset - > textureCount = static_cast < unsigned char > ( textureTable . size ( ) ) ;
asset - > textureTable = builder - > getAllocator ( ) - > allocateArray < Game : : MaterialTextureDef > ( asset - > textureCount ) ;
for ( size_t i = 0 ; i < textureTable . size ( ) ; i + + )
{
auto & textureJson = textureTable [ i ] ;
if ( textureJson . is_object ( ) )
{
Game : : MaterialTextureDef * textureDef = & asset - > textureTable [ i ] ;
textureDef - > semantic = textureJson [ " semantic " ] . get < Game : : TextureSemantic > ( ) ;
textureDef - > samplerState = textureJson [ " samplerState " ] . get < char > ( ) ;
textureDef - > nameStart = textureJson [ " nameStart " ] . get < char > ( ) ;
textureDef - > nameEnd = textureJson [ " nameEnd " ] . get < char > ( ) ;
textureDef - > nameHash = textureJson [ " nameHash " ] . get < unsigned int > ( ) ;
if ( textureDef - > semantic = = Game : : TextureSemantic : : TS_WATER_MAP )
{
Game : : water_t * water = builder - > getAllocator ( ) - > allocate < Game : : water_t > ( ) ;
if ( textureJson [ " water " ] . is_object ( ) )
{
auto & waterJson = textureJson [ " water " ] ;
if ( waterJson [ " image " ] . is_string ( ) )
{
auto imageName = waterJson [ " image " ] . get < std : : string > ( ) ;
2023-02-05 11:29:34 +00:00
water - > image = Components : : AssetHandler : : FindAssetForZone ( Game : : XAssetType : : ASSET_TYPE_IMAGE , imageName , builder ) . image ;
2022-12-14 09:40:15 +01:00
}
water - > amplitude = waterJson [ " amplitude " ] . get < float > ( ) ;
water - > M = waterJson [ " M " ] . get < int > ( ) ;
water - > N = waterJson [ " N " ] . get < int > ( ) ;
water - > Lx = waterJson [ " Lx " ] . get < float > ( ) ;
water - > Lz = waterJson [ " Lz " ] . get < float > ( ) ;
water - > gravity = waterJson [ " gravity " ] . get < float > ( ) ;
water - > windvel = waterJson [ " windvel " ] . get < float > ( ) ;
auto winddir = waterJson [ " winddir " ] . get < std : : vector < float > > ( ) ;
if ( winddir . size ( ) = = 2 )
{
std : : copy ( winddir . begin ( ) , winddir . end ( ) , water - > winddir ) ;
}
auto codeConstant = waterJson [ " codeConstant " ] . get < std : : vector < float > > ( ) ;
if ( codeConstant . size ( ) = = 4 )
{
std : : copy ( codeConstant . begin ( ) , codeConstant . end ( ) , water - > codeConstant ) ;
}
/// H0
[[maybe_unused]] auto idealSize = water - > M * water - > N * sizeof ( Game : : complex_s ) ;
auto h064 = waterJson [ " H0 " ] . get < std : : string > ( ) ;
auto predictedSize = static_cast < unsigned long > ( std : : ceilf ( ( h064 . size ( ) / 4.f ) * 3.f ) ) ;
assert ( predictedSize > = idealSize ) ;
auto h0 = reinterpret_cast < Game : : complex_s * > ( builder - > getAllocator ( ) - > allocate ( predictedSize ) ) ;
[[maybe_unused]] auto h0Result = base64_decode (
h064 . data ( ) ,
h064 . size ( ) ,
reinterpret_cast < unsigned char * > ( h0 ) ,
& predictedSize
) ;
assert ( h0Result = = CRYPT_OK ) ;
water - > H0 = h0 ;
/// WTerm
auto wTerm64 = waterJson [ " wTerm " ] . get < std : : string > ( ) ;
auto predictedWTermSize = static_cast < unsigned long > ( std : : ceilf ( ( wTerm64 . size ( ) / 4.f ) * 3.f ) ) ;
auto wTerm = reinterpret_cast < float * > ( builder - > getAllocator ( ) - > allocate ( predictedWTermSize ) ) ;
[[maybe_unused]] auto wTermResult = base64_decode (
wTerm64 . data ( ) ,
wTerm64 . size ( ) ,
reinterpret_cast < unsigned char * > ( wTerm ) ,
& predictedWTermSize
) ;
assert ( wTermResult = = CRYPT_OK ) ;
water - > wTerm = wTerm ;
}
textureDef - > u . water = water ;
}
else
{
textureDef - > u . image = nullptr ;
if ( textureJson [ " image " ] . is_string ( ) )
{
2023-02-04 18:29:04 +00:00
textureDef - > u . image = Components : : AssetHandler : : FindAssetForZone
(
Game : : XAssetType : : ASSET_TYPE_IMAGE ,
2022-12-14 09:40:15 +01:00
textureJson [ " image " ] . get < std : : string > ( ) ,
builder
) . image ;
}
}
}
}
}
// Statebits
if ( materialJson [ " stateBitsEntry " ] . is_array ( ) )
{
nlohmann : : json : : array_t stateBitsEntry = materialJson [ " stateBitsEntry " ] ;
for ( size_t i = 0 ; i < std : : min ( stateBitsEntry . size ( ) , 48u ) ; i + + )
{
asset - > stateBitsEntry [ i ] = stateBitsEntry [ i ] . get < char > ( ) ;
}
}
if ( materialJson [ " stateBitsTable " ] . is_array ( ) )
{
nlohmann : : json : : array_t array = materialJson [ " stateBitsTable " ] ;
asset - > stateBitsCount = static_cast < unsigned char > ( array . size ( ) ) ;
asset - > stateBitsTable = builder - > getAllocator ( ) - > allocateArray < Game : : GfxStateBits > ( array . size ( ) ) ;
size_t statebitTableIndex = 0 ;
for ( auto & jsonStateBitEntry : array )
{
auto stateBit = & asset - > stateBitsTable [ statebitTableIndex + + ] ;
unsigned int loadbits0 = 0 ;
unsigned int loadbits1 = 0 ;
# define READ_INT_LB_FROM_JSON(x) unsigned int x = jsonStateBitEntry[#x].get<unsigned int>()
# define READ_BOOL_LB_FROM_JSON(x) bool x = jsonStateBitEntry[#x].get<bool>()
READ_INT_LB_FROM_JSON ( srcBlendRgb ) ;
READ_INT_LB_FROM_JSON ( dstBlendRgb ) ;
READ_INT_LB_FROM_JSON ( blendOpRgb ) ;
READ_INT_LB_FROM_JSON ( srcBlendAlpha ) ;
READ_INT_LB_FROM_JSON ( dstBlendAlpha ) ;
READ_INT_LB_FROM_JSON ( blendOpAlpha ) ;
READ_INT_LB_FROM_JSON ( depthTest ) ;
READ_INT_LB_FROM_JSON ( polygonOffset ) ;
const auto alphaTest = jsonStateBitEntry [ " alphaTest " ] . get < std : : string > ( ) ;
const auto cullFace = jsonStateBitEntry [ " cullFace " ] . get < std : : string > ( ) ;
READ_BOOL_LB_FROM_JSON ( colorWriteRgb ) ;
READ_BOOL_LB_FROM_JSON ( colorWriteAlpha ) ;
READ_BOOL_LB_FROM_JSON ( polymodeLine ) ;
READ_BOOL_LB_FROM_JSON ( gammaWrite ) ;
READ_BOOL_LB_FROM_JSON ( depthWrite ) ;
READ_BOOL_LB_FROM_JSON ( stencilFrontEnabled ) ;
READ_BOOL_LB_FROM_JSON ( stencilBackEnabled ) ;
READ_INT_LB_FROM_JSON ( stencilFrontPass ) ;
READ_INT_LB_FROM_JSON ( stencilFrontFail ) ;
READ_INT_LB_FROM_JSON ( stencilFrontZFail ) ;
READ_INT_LB_FROM_JSON ( stencilFrontFunc ) ;
READ_INT_LB_FROM_JSON ( stencilBackPass ) ;
READ_INT_LB_FROM_JSON ( stencilBackFail ) ;
READ_INT_LB_FROM_JSON ( stencilBackZFail ) ;
READ_INT_LB_FROM_JSON ( stencilBackFunc ) ;
loadbits0 | = srcBlendRgb < < Game : : GFXS0_SRCBLEND_RGB_SHIFT ;
loadbits0 | = dstBlendRgb < < Game : : GFXS0_DSTBLEND_RGB_SHIFT ;
loadbits0 | = blendOpRgb < < Game : : GFXS0_BLENDOP_RGB_SHIFT ;
loadbits0 | = srcBlendAlpha < < Game : : GFXS0_SRCBLEND_ALPHA_SHIFT ;
loadbits0 | = dstBlendAlpha < < Game : : GFXS0_DSTBLEND_ALPHA_SHIFT ;
loadbits0 | = blendOpAlpha < < Game : : GFXS0_BLENDOP_ALPHA_SHIFT ;
if ( depthTest = = - 1 )
{
loadbits1 | = Game : : GFXS1_DEPTHTEST_DISABLE ;
}
else
{
loadbits1 | = depthTest < < Game : : GFXS1_DEPTHTEST_SHIFT ;
}
loadbits1 | = polygonOffset < < Game : : GFXS1_POLYGON_OFFSET_SHIFT ;
if ( alphaTest = = " disable " )
{
loadbits0 | = Game : : GFXS0_ATEST_DISABLE ;
}
else if ( alphaTest = = " >0 " )
{
loadbits0 | = Game : : GFXS0_ATEST_GT_0 ;
}
else if ( alphaTest = = " <128 " )
{
loadbits0 | = Game : : GFXS0_ATEST_LT_128 ;
}
else if ( alphaTest = = " >=128 " )
{
loadbits0 | = Game : : GFXS0_ATEST_GE_128 ;
}
else
{
Components : : Logger : : PrintError ( Game : : CON_CHANNEL_ERROR , " Invalid alphatest loadbit0 '{}' in material {} \n " , alphaTest , name ) ;
return ;
}
if ( cullFace = = " none " )
{
loadbits0 | = Game : : GFXS0_CULL_NONE ;
}
else if ( cullFace = = " back " )
{
loadbits0 | = Game : : GFXS0_CULL_BACK ;
}
else if ( cullFace = = " front " )
{
loadbits0 | = Game : : GFXS0_CULL_FRONT ;
}
else
{
Components : : Logger : : PrintError ( Game : : CON_CHANNEL_ERROR , " Invalid cullFace loadbit0 '{}' in material {} \n " , cullFace , name ) ;
return ;
}
if ( gammaWrite )
{
loadbits0 | = Game : : GFXS0_GAMMAWRITE ;
}
if ( colorWriteAlpha )
{
loadbits0 | = Game : : GFXS0_COLORWRITE_ALPHA ;
}
if ( colorWriteRgb )
{
loadbits0 | = Game : : GFXS0_COLORWRITE_RGB ;
}
if ( polymodeLine )
{
loadbits0 | = Game : : GFXS0_POLYMODE_LINE ;
}
if ( depthWrite )
{
loadbits1 | = Game : : GFXS1_DEPTHWRITE ;
}
if ( stencilFrontEnabled )
{
loadbits1 | = Game : : GFXS1_STENCIL_FRONT_ENABLE ;
}
if ( stencilBackEnabled )
{
loadbits1 | = Game : : GFXS1_STENCIL_BACK_ENABLE ;
}
loadbits1 | = stencilFrontPass < < Game : : GFXS1_STENCIL_FRONT_PASS_SHIFT ;
loadbits1 | = stencilFrontFail < < Game : : GFXS1_STENCIL_FRONT_FAIL_SHIFT ;
loadbits1 | = stencilFrontZFail < < Game : : GFXS1_STENCIL_FRONT_ZFAIL_SHIFT ;
loadbits1 | = stencilFrontFunc < < Game : : GFXS1_STENCIL_FRONT_FUNC_SHIFT ;
loadbits1 | = stencilBackPass < < Game : : GFXS1_STENCIL_BACK_PASS_SHIFT ;
loadbits1 | = stencilBackFail < < Game : : GFXS1_STENCIL_BACK_FAIL_SHIFT ;
loadbits1 | = stencilBackZFail < < Game : : GFXS1_STENCIL_BACK_ZFAIL_SHIFT ;
loadbits1 | = stencilBackFunc < < Game : : GFXS1_STENCIL_BACK_FUNC_SHIFT ;
stateBit - > loadBits [ 0 ] = loadbits0 ;
stateBit - > loadBits [ 1 ] = loadbits1 ;
}
// Constant table
if ( materialJson [ " constantTable " ] . is_array ( ) )
{
nlohmann : : json : : array_t constants = materialJson [ " constantTable " ] ;
asset - > constantCount = static_cast < char > ( constants . size ( ) ) ;
auto table = builder - > getAllocator ( ) - > allocateArray < Game : : MaterialConstantDef > ( asset - > constantCount ) ;
for ( size_t constantIndex = 0 ; constantIndex < asset - > constantCount ; constantIndex + + )
{
auto & constant = constants [ constantIndex ] ;
auto entry = & table [ constantIndex ] ;
auto litVec = constant [ " literal " ] . get < std : : vector < float > > ( ) ;
std : : copy ( litVec . begin ( ) , litVec . end ( ) , entry - > literal ) ;
auto constantName = constant [ " name " ] . get < std : : string > ( ) ;
std : : copy ( constantName . begin ( ) , constantName . end ( ) , entry - > name ) ;
entry - > nameHash = constant [ " nameHash " ] . get < unsigned int > ( ) ;
}
asset - > constantTable = table ;
}
if ( materialJson [ " techniqueSet " ] . is_string ( ) )
{
const std : : string techsetName = materialJson [ " techniqueSet " ] . get < std : : string > ( ) ;
asset - > techniqueSet = findWorkingTechset ( techsetName , asset , builder ) ;
if ( asset - > techniqueSet = = nullptr )
{
assert ( false ) ;
}
}
header - > material = asset ;
}
}
Game : : MaterialTechniqueSet * IMaterial : : findWorkingTechset ( std : : string techsetName , [[maybe_unused]] Game : : Material * material , Components : : ZoneBuilder : : Zone * builder ) const
{
Game : : MaterialTechniqueSet * techset ;
// Pass 1: Identical techset (1:1)
2023-02-05 11:29:34 +00:00
techset = Components : : AssetHandler : : FindAssetForZone ( Game : : XAssetType : : ASSET_TYPE_TECHNIQUE_SET , techsetName , builder ) . techniqueSet ;
2022-12-14 09:40:15 +01:00
if ( techset ! = nullptr )
{
return techset ;
}
// We do no more cause we use CoD4 techset and they should always be present
// If one day we want to go back to mw2 fallback we can add extra steps here!
return nullptr ;
2017-01-19 22:23:59 +01:00
}
2018-12-17 14:29:18 +01:00
void IMaterial : : loadBinary ( Game : : XAssetHeader * header , const std : : string & name , Components : : ZoneBuilder : : Zone * builder )
2017-01-19 22:23:59 +01:00
{
2017-05-30 10:03:12 +02:00
static const char * techsetSuffix [ ] =
{
" _lin " ,
" _add_lin " ,
" _replace " ,
" _eyeoffset " ,
" _blend " ,
" _blend_nofog " ,
" _add " ,
" _nofog " ,
" _nocast " ,
2017-06-02 22:18:14 +02:00
" _add_lin_nofog " ,
2017-05-30 10:03:12 +02:00
} ;
2022-12-11 18:54:24 +01:00
Components : : FileSystem : : File materialFile ( std : : format ( " materials/{}.iw4xMaterial " , name ) ) ;
2017-01-19 22:23:59 +01:00
if ( ! materialFile . exists ( ) ) return ;
Utils : : Stream : : Reader reader ( builder - > getAllocator ( ) , materialFile . getBuffer ( ) ) ;
2017-03-18 15:19:31 +01:00
char * magic = reader . readArray < char > ( 7 ) ;
2022-10-16 16:17:42 +01:00
if ( std : : memcmp ( magic , " IW4xMat " , 7 ) ! = 0 )
2017-01-19 22:23:59 +01:00
{
2022-06-12 23:07:53 +02:00
Components : : Logger : : Error ( Game : : ERR_FATAL , " Reading material '{}' failed, header is invalid! " , name ) ;
2017-01-19 22:23:59 +01:00
}
2017-03-18 15:19:31 +01:00
std : : string version ;
version . push_back ( reader . read < char > ( ) ) ;
2022-12-14 09:40:15 +01:00
if ( version ! = IW4X_MAT_BIN_VERSION )
2017-01-19 22:23:59 +01:00
{
2022-12-14 09:40:15 +01:00
Components : : Logger : : Error ( Game : : ERR_FATAL , " Reading material '{}' failed, expected version is {}, but it was {}! " , name , IW4X_MAT_BIN_VERSION , version ) ;
2017-01-19 22:23:59 +01:00
}
2022-10-16 16:17:42 +01:00
auto * asset = reader . readObject < Game : : Material > ( ) ;
2017-01-19 22:23:59 +01:00
2018-05-09 14:33:52 +02:00
if ( asset - > info . name )
2017-01-19 22:23:59 +01:00
{
2018-05-09 14:33:52 +02:00
asset - > info . name = reader . readCString ( ) ;
2017-01-19 22:23:59 +01:00
}
2017-03-18 15:19:31 +01:00
if ( asset - > techniqueSet )
2017-01-19 22:23:59 +01:00
{
2021-05-12 14:35:32 +02:00
std : : string techsetName = reader . readString ( ) ;
if ( ! techsetName . empty ( ) & & techsetName . front ( ) = = ' , ' ) techsetName . erase ( techsetName . begin ( ) ) ;
2023-02-05 11:29:34 +00:00
asset - > techniqueSet = Components : : AssetHandler : : FindAssetForZone ( Game : : XAssetType : : ASSET_TYPE_TECHNIQUE_SET , techsetName , builder ) . techniqueSet ;
2017-03-18 15:19:31 +01:00
2017-04-16 01:51:41 +02:00
if ( ! asset - > techniqueSet )
2017-03-18 15:19:31 +01:00
{
2017-04-20 21:52:50 +02:00
// Workaround for effect techsets having _nofog suffix
std : : string suffix ;
2021-05-12 14:35:32 +02:00
if ( Utils : : String : : StartsWith ( techsetName , " effect_ " ) & & Utils : : String : : EndsWith ( techsetName , " _nofog " ) )
2017-04-20 21:52:50 +02:00
{
suffix = " _nofog " ;
2021-05-12 14:35:32 +02:00
Utils : : String : : Replace ( techsetName , suffix , " " ) ;
2017-04-20 21:52:50 +02:00
}
2017-04-16 01:51:41 +02:00
for ( int i = 0 ; i < ARRAYSIZE ( techsetSuffix ) ; + + i )
2017-03-18 15:19:31 +01:00
{
2023-02-05 11:29:34 +00:00
Game : : MaterialTechniqueSet * techsetPtr = Components : : AssetHandler : : FindAssetForZone ( Game : : XAssetType : : ASSET_TYPE_TECHNIQUE_SET , ( techsetName + techsetSuffix [ i ] + suffix ) , builder ) . techniqueSet ;
2017-04-16 01:51:41 +02:00
if ( techsetPtr )
{
asset - > techniqueSet = techsetPtr ;
if ( asset - > techniqueSet - > name [ 0 ] = = ' , ' ) continue ; // Try to find a better one
2022-06-12 23:07:53 +02:00
Components : : Logger : : Print ( " Techset '{}' has been mapped to '{}' \n " , techsetName , asset - > techniqueSet - > name ) ;
2017-04-16 01:51:41 +02:00
break ;
}
2017-03-18 15:19:31 +01:00
}
2017-01-19 22:23:59 +01:00
}
2022-06-30 19:42:29 +02:00
else
{
Components : : Logger : : Print ( " Techset {} exists with the same name in iw4, and was mapped 1:1 with {} \n " , techsetName , asset - > techniqueSet - > name ) ;
2021-05-12 14:35:32 +02:00
}
2017-03-18 15:19:31 +01:00
if ( ! asset - > techniqueSet )
2017-01-19 22:23:59 +01:00
{
2022-06-12 23:07:53 +02:00
Components : : Logger : : Error ( Game : : ERR_FATAL , " Missing techset: '{}' not found " , techsetName ) ;
2021-05-12 14:35:32 +02:00
}
2017-01-19 22:23:59 +01:00
}
2017-03-18 15:19:31 +01:00
if ( asset - > textureTable )
2017-01-19 22:23:59 +01:00
{
2017-03-18 15:19:31 +01:00
asset - > textureTable = reader . readArray < Game : : MaterialTextureDef > ( asset - > textureCount ) ;
for ( char i = 0 ; i < asset - > textureCount ; + + i )
2017-01-19 22:23:59 +01:00
{
2017-03-18 15:19:31 +01:00
Game : : MaterialTextureDef * textureDef = & asset - > textureTable [ i ] ;
2022-12-14 09:40:15 +01:00
if ( textureDef - > semantic = = Game : : TextureSemantic : : TS_WATER_MAP )
2017-03-18 15:19:31 +01:00
{
2018-05-09 14:33:52 +02:00
if ( textureDef - > u . water )
2017-03-18 15:19:31 +01:00
{
Game : : water_t * water = reader . readObject < Game : : water_t > ( ) ;
2018-05-09 14:33:52 +02:00
textureDef - > u . water = water ;
2017-03-18 15:19:31 +01:00
// Save_water_t
if ( water - > H0 )
{
water - > H0 = reader . readArray < Game : : complex_s > ( water - > M * water - > N ) ;
}
if ( water - > wTerm )
{
water - > wTerm = reader . readArray < float > ( water - > M * water - > N ) ;
}
if ( water - > image )
{
2023-02-05 11:29:34 +00:00
water - > image = Components : : AssetHandler : : FindAssetForZone ( Game : : XAssetType : : ASSET_TYPE_IMAGE , reader . readString ( ) , builder ) . image ;
2017-03-18 15:19:31 +01:00
}
}
}
2018-05-09 14:33:52 +02:00
else if ( textureDef - > u . image )
2017-03-18 15:19:31 +01:00
{
2023-02-05 11:29:34 +00:00
textureDef - > u . image = Components : : AssetHandler : : FindAssetForZone ( Game : : XAssetType : : ASSET_TYPE_IMAGE , reader . readString ( ) , builder ) . image ;
2017-03-18 15:19:31 +01:00
}
2017-01-19 22:23:59 +01:00
}
2017-03-18 15:19:31 +01:00
}
2017-01-19 22:23:59 +01:00
2017-03-18 15:19:31 +01:00
if ( asset - > constantTable )
{
asset - > constantTable = reader . readArray < Game : : MaterialConstantDef > ( asset - > constantCount ) ;
}
2017-01-19 22:23:59 +01:00
2018-05-09 14:33:52 +02:00
if ( asset - > stateBitsTable )
2017-03-18 15:19:31 +01:00
{
2018-05-09 14:33:52 +02:00
asset - > stateBitsTable = reader . readArray < Game : : GfxStateBits > ( asset - > stateBitsCount ) ;
2017-01-19 22:23:59 +01:00
}
2017-03-18 15:19:31 +01:00
header - > material = asset ;
2017-01-19 22:23:59 +01:00
2017-04-15 22:50:20 +02:00
static thread_local bool replacementFound ;
replacementFound = false ;
2017-01-19 22:23:59 +01:00
// Find correct sortkey by comparing techsets
2017-05-01 13:08:34 +02:00
Game : : DB_EnumXAssetEntries ( Game : : XAssetType : : ASSET_TYPE_MATERIAL , [ asset ] ( Game : : XAssetEntry * entry )
2017-01-19 22:23:59 +01:00
{
2022-12-14 09:40:15 +01:00
if ( ! replacementFound )
{
Game : : XAssetHeader header = entry - > asset . header ;
2017-05-01 13:08:34 +02:00
2022-12-14 09:40:15 +01:00
const char * name = asset - > techniqueSet - > name ;
if ( name [ 0 ] = = ' , ' ) + + name ;
2017-01-19 22:23:59 +01:00
2022-12-14 09:40:15 +01:00
if ( std : : string ( name ) = = header . material - > techniqueSet - > name )
{
asset - > info . sortKey = header . material - > info . sortKey ;
2017-04-15 22:50:20 +02:00
2022-12-14 09:40:15 +01:00
// This is temp, as nobody has time to fix materials
asset - > stateBitsCount = header . material - > stateBitsCount ;
asset - > stateBitsTable = header . material - > stateBitsTable ;
std : : memcpy ( asset - > stateBitsEntry , header . material - > stateBitsEntry , ARRAYSIZE ( asset - > stateBitsEntry ) ) ;
asset - > constantCount = header . material - > constantCount ;
asset - > constantTable = header . material - > constantTable ;
Components : : Logger : : Print ( " For {}, copied constants & statebits from {} \n " , asset - > info . name , header . material - > info . name ) ;
replacementFound = true ;
}
2017-04-16 01:51:41 +02:00
}
2022-12-14 09:40:15 +01:00
} , false ) ;
2017-03-18 15:19:31 +01:00
2017-05-30 10:52:22 +02:00
if ( ! replacementFound )
2017-05-30 10:03:12 +02:00
{
2017-05-30 10:52:22 +02:00
auto techsetMatches = [ ] ( Game : : Material * m1 , Game : : Material * m2 )
{
Game : : MaterialTechniqueSet * t1 = m1 - > techniqueSet ;
Game : : MaterialTechniqueSet * t2 = m2 - > techniqueSet ;
if ( ! t1 | | ! t2 ) return false ;
2018-05-09 14:33:52 +02:00
if ( t1 - > remappedTechniqueSet & & t2 - > remappedTechniqueSet & & std : : string ( t1 - > remappedTechniqueSet - > name ) = = t2 - > remappedTechniqueSet - > name ) return true ;
2017-05-30 10:52:22 +02:00
for ( int i = 0 ; i < ARRAYSIZE ( t1 - > techniques ) ; + + i )
{
if ( ! t1 - > techniques [ i ] & & ! t2 - > techniques [ i ] ) continue ; ;
if ( ! t1 - > techniques [ i ] | | ! t2 - > techniques [ i ] ) return false ;
2021-04-06 11:33:14 +02:00
// Apparently, this is really not that important
//if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false;
2017-05-30 10:52:22 +02:00
}
2017-05-30 10:03:12 +02:00
2017-05-30 10:52:22 +02:00
return true ;
} ;
2017-05-30 10:03:12 +02:00
2021-04-04 12:06:24 +02:00
2017-05-30 10:52:22 +02:00
Game : : DB_EnumXAssetEntries ( Game : : XAssetType : : ASSET_TYPE_MATERIAL , [ asset , techsetMatches ] ( Game : : XAssetEntry * entry )
2017-05-30 10:03:12 +02:00
{
2022-12-14 09:40:15 +01:00
if ( ! replacementFound )
2017-05-30 10:03:12 +02:00
{
2022-12-14 09:40:15 +01:00
Game : : XAssetHeader header = entry - > asset . header ;
if ( techsetMatches ( header . material , asset ) )
{
Components : : Logger : : Print ( " Material {} with techset {} has been mapped to {} \n " , asset - > info . name , asset - > techniqueSet - > name , header . material - > techniqueSet - > name ) ;
asset - > info . sortKey = header . material - > info . sortKey ;
replacementFound = true ;
}
2017-05-30 10:03:12 +02:00
}
2022-12-14 09:40:15 +01:00
} , false ) ;
2017-05-30 10:03:12 +02:00
}
2017-04-24 21:14:32 +02:00
if ( ! replacementFound & & asset - > techniqueSet )
2017-04-15 22:50:20 +02:00
{
2022-06-12 23:07:53 +02:00
Components : : Logger : : Print ( " No replacement found for material {} with techset {} \n " , asset - > info . name , asset - > techniqueSet - > name ) ;
2021-04-04 12:06:24 +02:00
std : : string techName = asset - > techniqueSet - > name ;
2022-12-14 09:40:15 +01:00
if ( const auto itr = techSetCorrespondance . find ( techName ) ; itr ! = techSetCorrespondance . end ( ) )
2022-05-31 18:38:09 +02:00
{
2022-12-14 09:40:15 +01:00
auto & iw4TechSetName = itr - > second ;
auto * iw4TechSet = Game : : DB_FindXAssetEntry ( Game : : ASSET_TYPE_TECHNIQUE_SET , iw4TechSetName . data ( ) ) ;
2021-04-04 12:06:24 +02:00
2022-12-14 09:40:15 +01:00
if ( iw4TechSet )
2021-04-28 18:49:17 +02:00
{
2021-04-21 23:41:06 +02:00
Game : : DB_EnumXAssetEntries ( Game : : XAssetType : : ASSET_TYPE_MATERIAL , [ asset , iw4TechSet ] ( Game : : XAssetEntry * entry )
2021-04-04 12:06:24 +02:00
{
2022-12-14 09:40:15 +01:00
if ( ! replacementFound )
2021-04-04 12:06:24 +02:00
{
2022-12-14 09:40:15 +01:00
Game : : XAssetHeader header = entry - > asset . header ;
2022-06-12 23:07:53 +02:00
Components : : Logger : : Print ( " Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {} \n " ,
asset - > info . name , asset - > techniqueSet - > name , header . material - > techniqueSet - > name , header . material - > info . name ) ;
2022-01-18 11:10:53 +00:00
2022-12-14 09:40:15 +01:00
// Yeah this has a tendency to fuck up a LOT of transparent materials
if ( header . material - > techniqueSet = = iw4TechSet - > asset . header . techniqueSet & & std : : string ( header . material - > info . name ) . find ( " icon " ) ! = std : : string : : npos )
{
Components : : Logger : : Print ( " Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {} \n " ,
asset - > info . name , asset - > techniqueSet - > name , header . material - > techniqueSet - > name , header . material - > info . name ) ;
asset - > info . sortKey = header . material - > info . sortKey ;
asset - > techniqueSet = iw4TechSet - > asset . header . techniqueSet ;
2022-01-18 11:10:53 +00:00
2022-12-14 09:40:15 +01:00
// this is terrible!
asset - > stateBitsCount = header . material - > stateBitsCount ;
asset - > stateBitsTable = header . material - > stateBitsTable ;
std : : memcpy ( asset - > stateBitsEntry , header . material - > stateBitsEntry , 48 ) ;
asset - > constantCount = header . material - > constantCount ;
asset - > constantTable = header . material - > constantTable ;
2022-01-18 11:10:53 +00:00
2022-12-14 09:40:15 +01:00
replacementFound = true ;
}
2021-04-04 12:06:24 +02:00
}
2022-12-14 09:40:15 +01:00
} , false ) ;
2021-04-04 12:06:24 +02:00
2022-12-14 09:40:15 +01:00
if ( ! replacementFound )
2021-04-28 18:49:17 +02:00
{
2022-06-12 23:07:53 +02:00
Components : : Logger : : Print ( " Could not find any loaded material with techset {} (in replacement of {}), so I cannot set the sortkey for material {} \n " , iw4TechSetName , asset - > techniqueSet - > name , asset - > info . name ) ;
2021-04-04 12:06:24 +02:00
}
}
2022-12-14 09:40:15 +01:00
else
2021-04-28 18:49:17 +02:00
{
2022-06-12 23:07:53 +02:00
Components : : Logger : : Print ( " Could not find any loaded techset with iw4 name {} for iw3 techset {} \n " , iw4TechSetName , asset - > techniqueSet - > name ) ;
2021-04-04 12:06:24 +02:00
}
}
2022-12-14 09:40:15 +01:00
else
2021-04-28 18:49:17 +02:00
{
2022-06-12 23:07:53 +02:00
Components : : Logger : : Print ( " Could not match iw3 techset {} with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable. \n " , techName ) ;
2021-04-04 12:06:24 +02:00
}
2017-04-15 22:50:20 +02:00
}
2017-03-18 15:19:31 +01:00
if ( ! reader . end ( ) )
{
2022-06-12 23:07:53 +02:00
Components : : Logger : : Error ( Game : : ERR_FATAL , " Material data left! " ) ;
2017-03-18 15:19:31 +01:00
}
2018-05-09 11:18:31 +02:00
2018-05-10 18:04:12 +02:00
/*char baseIndex = 0;
2018-05-09 11:18:31 +02:00
for ( char i = 0 ; i < asset - > stateBitsCount ; + + i )
{
2018-05-09 14:33:52 +02:00
auto stateBits = asset - > stateBitsTable [ i ] ;
2018-05-09 11:18:31 +02:00
if ( stateBits . loadBits [ 0 ] = = 0x18128812 & &
stateBits . loadBits [ 1 ] = = 0xD ) // Seems to be like a default stateBit causing a 'generic' initialization
{
baseIndex = i ;
break ;
}
}
for ( int i = 0 ; i < 48 ; + + i )
{
if ( ! asset - > techniqueSet - > techniques [ i ] & & asset - > stateBitsEntry [ i ] ! = - 1 )
{
asset - > stateBitsEntry [ i ] = - 1 ;
}
if ( asset - > techniqueSet - > techniques [ i ] & & asset - > stateBitsEntry [ i ] = = - 1 )
{
asset - > stateBitsEntry [ i ] = baseIndex ;
}
2018-05-10 18:04:12 +02:00
} */
2017-01-19 22:23:59 +01:00
}
2018-12-17 14:29:18 +01:00
void IMaterial : : loadNative ( Game : : XAssetHeader * header , const std : : string & name , Components : : ZoneBuilder : : Zone * /*builder*/ )
2017-01-19 22:23:59 +01:00
{
header - > material = Components : : AssetHandler : : FindOriginalAsset ( this - > getType ( ) , name . data ( ) ) . material ;
}
void IMaterial : : mark ( Game : : XAssetHeader header , Components : : ZoneBuilder : : Zone * builder )
{
Game : : Material * asset = header . material ;
if ( asset - > techniqueSet )
{
builder - > loadAsset ( Game : : XAssetType : : ASSET_TYPE_TECHNIQUE_SET , asset - > techniqueSet ) ;
}
if ( asset - > textureTable )
{
for ( char i = 0 ; i < asset - > textureCount ; + + i )
{
2018-05-09 14:33:52 +02:00
if ( asset - > textureTable [ i ] . u . image )
2017-01-19 22:23:59 +01:00
{
2022-12-14 09:40:15 +01:00
if ( asset - > textureTable [ i ] . semantic = = Game : : TextureSemantic : : TS_WATER_MAP )
2017-01-19 22:23:59 +01:00
{
2018-05-09 14:33:52 +02:00
if ( asset - > textureTable [ i ] . u . water - > image )
2017-01-19 22:23:59 +01:00
{
2018-05-09 14:33:52 +02:00
builder - > loadAsset ( Game : : XAssetType : : ASSET_TYPE_IMAGE , asset - > textureTable [ i ] . u . water - > image ) ;
2017-01-19 22:23:59 +01:00
}
}
else
{
2018-05-09 14:33:52 +02:00
builder - > loadAsset ( Game : : XAssetType : : ASSET_TYPE_IMAGE , asset - > textureTable [ i ] . u . image ) ;
2017-01-19 22:23:59 +01:00
}
}
}
}
}
void IMaterial : : save ( Game : : XAssetHeader header , Components : : ZoneBuilder : : Zone * builder )
{
AssertSize ( Game : : Material , 96 ) ;
Utils : : Stream * buffer = builder - > getBuffer ( ) ;
Game : : Material * asset = header . material ;
Game : : Material * dest = buffer - > dest < Game : : Material > ( ) ;
buffer - > save ( asset ) ;
buffer - > pushBlock ( Game : : XFILE_BLOCK_VIRTUAL ) ;
2018-05-09 14:33:52 +02:00
if ( asset - > info . name )
2017-01-19 22:23:59 +01:00
{
2018-05-09 14:33:52 +02:00
buffer - > saveString ( builder - > getAssetName ( this - > getType ( ) , asset - > info . name ) ) ;
Utils : : Stream : : ClearPointer ( & dest - > info . name ) ;
2017-01-19 22:23:59 +01:00
}
if ( asset - > techniqueSet )
{
dest - > techniqueSet = builder - > saveSubAsset ( Game : : XAssetType : : ASSET_TYPE_TECHNIQUE_SET , asset - > techniqueSet ) . techniqueSet ;
}
if ( asset - > textureTable )
{
AssertSize ( Game : : MaterialTextureDef , 12 ) ;
// Pointer/Offset insertion is untested, but it worked in T6, so I think it's fine
if ( builder - > hasPointer ( asset - > textureTable ) )
{
dest - > textureTable = builder - > getPointer ( asset - > textureTable ) ;
}
else
{
buffer - > align ( Utils : : Stream : : ALIGN_4 ) ;
builder - > storePointer ( asset - > textureTable ) ;
2022-10-16 16:17:42 +01:00
auto * destTextureTable = buffer - > dest < Game : : MaterialTextureDef > ( ) ;
2017-01-19 22:23:59 +01:00
buffer - > saveArray ( asset - > textureTable , asset - > textureCount ) ;
2022-10-16 16:17:42 +01:00
for ( std : : uint8_t i = 0 ; i < asset - > textureCount ; + + i )
2017-01-19 22:23:59 +01:00
{
2022-10-16 16:17:42 +01:00
auto * destTextureDef = & destTextureTable [ i ] ;
auto * textureDef = & asset - > textureTable [ i ] ;
2017-01-19 22:23:59 +01:00
2022-12-14 09:40:15 +01:00
if ( textureDef - > semantic = = Game : : TextureSemantic : : TS_WATER_MAP )
2017-01-19 22:23:59 +01:00
{
AssertSize ( Game : : water_t , 68 ) ;
Game : : water_t * destWater = buffer - > dest < Game : : water_t > ( ) ;
2018-05-09 14:33:52 +02:00
Game : : water_t * water = textureDef - > u . water ;
2017-01-19 22:23:59 +01:00
if ( water )
{
buffer - > align ( Utils : : Stream : : ALIGN_4 ) ;
buffer - > save ( water ) ;
2018-05-09 14:33:52 +02:00
Utils : : Stream : : ClearPointer ( & destTextureDef - > u . water ) ;
2017-01-19 22:23:59 +01:00
// Save_water_t
if ( water - > H0 )
{
buffer - > align ( Utils : : Stream : : ALIGN_4 ) ;
buffer - > save ( water - > H0 , 8 , water - > M * water - > N ) ;
Utils : : Stream : : ClearPointer ( & destWater - > H0 ) ;
}
if ( water - > wTerm )
{
buffer - > align ( Utils : : Stream : : ALIGN_4 ) ;
buffer - > save ( water - > wTerm , 4 , water - > M * water - > N ) ;
Utils : : Stream : : ClearPointer ( & destWater - > wTerm ) ;
}
if ( water - > image )
{
destWater - > image = builder - > saveSubAsset ( Game : : XAssetType : : ASSET_TYPE_IMAGE , water - > image ) . image ;
}
}
}
2018-05-09 14:33:52 +02:00
else if ( textureDef - > u . image )
2017-01-19 22:23:59 +01:00
{
2018-05-09 14:33:52 +02:00
destTextureDef - > u . image = builder - > saveSubAsset ( Game : : XAssetType : : ASSET_TYPE_IMAGE , textureDef - > u . image ) . image ;
2017-01-19 22:23:59 +01:00
}
}
Utils : : Stream : : ClearPointer ( & dest - > textureTable ) ;
}
}
if ( asset - > constantTable )
{
AssertSize ( Game : : MaterialConstantDef , 32 ) ;
if ( builder - > hasPointer ( asset - > constantTable ) )
{
dest - > constantTable = builder - > getPointer ( asset - > constantTable ) ;
}
else
{
buffer - > align ( Utils : : Stream : : ALIGN_16 ) ;
builder - > storePointer ( asset - > constantTable ) ;
buffer - > saveArray ( asset - > constantTable , asset - > constantCount ) ;
Utils : : Stream : : ClearPointer ( & dest - > constantTable ) ;
}
}
2018-05-09 14:33:52 +02:00
if ( asset - > stateBitsTable )
2017-01-19 22:23:59 +01:00
{
2018-05-09 14:33:52 +02:00
if ( builder - > hasPointer ( asset - > stateBitsTable ) )
2017-01-19 22:23:59 +01:00
{
2018-05-09 14:33:52 +02:00
dest - > stateBitsTable = builder - > getPointer ( asset - > stateBitsTable ) ;
2017-01-19 22:23:59 +01:00
}
else
{
buffer - > align ( Utils : : Stream : : ALIGN_4 ) ;
2018-05-09 14:33:52 +02:00
builder - > storePointer ( asset - > stateBitsTable ) ;
2017-01-19 22:23:59 +01:00
2018-05-09 14:33:52 +02:00
buffer - > save ( asset - > stateBitsTable , 8 , asset - > stateBitsCount ) ;
Utils : : Stream : : ClearPointer ( & dest - > stateBitsTable ) ;
2017-01-19 22:23:59 +01:00
}
}
buffer - > popBlock ( ) ;
}
}