From 7fd32180e2fc39ae99eef0c2f1acf45f4d63d05f Mon Sep 17 00:00:00 2001 From: Garux Date: Wed, 25 Mar 2020 11:53:14 +0300 Subject: [PATCH] * support misc_model::_remap facility of q3map2 q3map2: fix inconsistency, introduced in d92c32d453d97c5f86cc918ed50e626588682dd4 (_remap result could depend on _remap keys order, e.g. remapping src: moo/rock, moo/sand-rock by: rock, sand-rock suffix matches; rock could be used for moo/sand-rock) --- plugins/entity/miscmodel.cpp | 102 +++++++++++++++++++++++++++++++++++ tools/quake3/q3map2/model.c | 33 ++++++------ 2 files changed, 117 insertions(+), 18 deletions(-) diff --git a/plugins/entity/miscmodel.cpp b/plugins/entity/miscmodel.cpp index 25a13a9e..0f82583f 100644 --- a/plugins/entity/miscmodel.cpp +++ b/plugins/entity/miscmodel.cpp @@ -50,6 +50,87 @@ #include "entity.h" +#include "modelskinkey.h" + +#include "modelskin.h" +class RemapKeysObserver : public Entity::Observer, public ModelSkin +{ + class RemapKey + { + const Callback& m_skinChangedCallback; + public: + CopiedString m_from; + CopiedString m_to; + RemapKey( const Callback& skinChangedCallback ) : m_skinChangedCallback( skinChangedCallback ){ + } + void remapKeyChanged( const char* value ){ + const char* split = strchr( value, ';' ); + if( split != nullptr ){ + m_from = { value, split }; + StringOutputStream stream( 64 ); + stream << PathCleaned( split + 1 ); + m_to = stream.c_str(); + } + else{ + m_from = ""; + m_to = ""; + } + m_skinChangedCallback(); + } + typedef MemberCaller1 remapKeyChangedCaller; + }; + typedef std::multimap RemapKeys; + RemapKeys m_remapKeys; + const Callback m_skinChangedCallback; + +public: + RemapKeysObserver() = delete; + RemapKeysObserver( const RemapKeysObserver& ) = delete; + RemapKeysObserver operator=( const RemapKeysObserver& ) = delete; + RemapKeysObserver( const Callback& skinChangedCallback ) : m_skinChangedCallback( skinChangedCallback ){ + } + +void insert( const char* key, EntityKeyValue& value ) override { + if( string_equal_prefix( key, "_remap" ) ){ + value.attach( RemapKey::remapKeyChangedCaller( m_remapKeys.emplace( key, m_skinChangedCallback )->second ) ); + } +} +void erase( const char* key, EntityKeyValue& value ) override { + if( string_equal_prefix( key, "_remap" ) ){ + for( RemapKeys::iterator i = m_remapKeys.find( key ); i != m_remapKeys.end() && string_equal( ( *i ).first.c_str(), key ); ){ + value.detach( RemapKey::remapKeyChangedCaller( i->second ) ); + i = m_remapKeys.erase( i ); + } + } +} + +void attach( ModuleObserver& observer ) override { +} +void detach( ModuleObserver& observer ) override { +} +bool realised() const override { + return true; +} +const char* getRemap( const char* name ) const override { // this logic is supposed to respect one in q3map2 + const char* to = ""; + std::size_t fromlen = 0; + for( const auto& pair : m_remapKeys ){ + const RemapKey& remapKey = pair.second; + if( remapKey.m_from == "*" && fromlen == 0 ){ // only globbing, if no respective match + to = remapKey.m_to.c_str(); + } + else if( string_equal_suffix_nocase( name, remapKey.m_from.c_str() ) && strlen( remapKey.m_from.c_str() ) > fromlen ){ // longer match has priority + to = remapKey.m_to.c_str(); + fromlen = strlen( remapKey.m_from.c_str() ); + } + } + return to; +} +void forEachRemap( const SkinRemapCallback& callback ) const override { +} +}; + + const char EXCLUDE_NAME[] = "misc_model"; class MiscModel : @@ -57,6 +138,7 @@ class MiscModel : { EntityKeyValues m_entity; KeyObserverMap m_keyObservers; +RemapKeysObserver m_remapKeysObserver; MatrixTransform m_transform; OriginKey m_originKey; @@ -114,10 +196,20 @@ void scaleChanged(){ updateTransform(); } typedef MemberCaller ScaleChangedCaller; + +void skinChanged(){ + scene::Node* node = m_model.getNode(); + if ( node != 0 ) { + Node_modelSkinChanged( *node ); + } +} +typedef MemberCaller SkinChangedCaller; + public: MiscModel( EntityClass* eclass, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform ) : m_entity( eclass ), + m_remapKeysObserver( SkinChangedCaller( *this ) ), m_originKey( OriginChangedCaller( *this ) ), m_origin( ORIGINKEY_IDENTITY ), m_anglesKey( AnglesChangedCaller( *this ) ), @@ -134,6 +226,7 @@ MiscModel( EntityClass* eclass, scene::Node& node, const Callback& transformChan } MiscModel( const MiscModel& other, scene::Node& node, const Callback& transformChanged, const Callback& evaluateTransform ) : m_entity( other.m_entity ), + m_remapKeysObserver( SkinChangedCaller( *this ) ), m_originKey( OriginChangedCaller( *this ) ), m_origin( ORIGINKEY_IDENTITY ), m_anglesKey( AnglesChangedCaller( *this ) ), @@ -155,10 +248,12 @@ void instanceAttach( const scene::Path& path ){ m_filter.instanceAttach(); m_entity.instanceAttach( path_find_mapfile( path.begin(), path.end() ) ); m_entity.attach( m_keyObservers ); + m_entity.attach( m_remapKeysObserver ); } } void instanceDetach( const scene::Path& path ){ if ( --m_instanceCounter.m_count == 0 ) { + m_entity.detach( m_remapKeysObserver ); m_entity.detach( m_keyObservers ); m_entity.instanceDetach( path_find_mapfile( path.begin(), path.end() ) ); m_filter.instanceDetach(); @@ -184,6 +279,9 @@ Nameable& getNameable(){ TransformNode& getTransformNode(){ return m_transform; } +ModelSkin& getModelSkin(){ + return m_remapKeysObserver; +} void attach( scene::Traversable::Observer* observer ){ m_model.attach( observer ); @@ -328,6 +426,7 @@ TypeCasts(){ NodeContainedCast::install( m_casts ); NodeContainedCast::install( m_casts ); NodeContainedCast::install( m_casts ); + NodeContainedCast::install( m_casts ); } NodeTypeCastTable& get(){ return m_casts; @@ -367,6 +466,9 @@ Nameable& get( NullType){ Namespaced& get( NullType){ return m_contained.getNamespaced(); } +ModelSkin& get( NullType){ + return m_contained.getModelSkin(); +} MiscModelNode( EntityClass* eclass ) : m_node( this, this, StaticTypeCasts::instance().get() ), diff --git a/tools/quake3/q3map2/model.c b/tools/quake3/q3map2/model.c index 7cc001e6..e0492ddb 100644 --- a/tools/quake3/q3map2/model.c +++ b/tools/quake3/q3map2/model.c @@ -218,12 +218,11 @@ void InsertModel( const char *name, int skin, int frame, m4x4_t transform, remap shaderInfo_t *si; mapDrawSurface_t *ds; bspDrawVert_t *dv; - char *picoShaderName; + const char *picoShaderName; char shaderName[ MAX_QPATH ]; picoVec_t *xyz, *normal, *st; byte *color; picoIndex_t *indexes; - remap_t *rm, *rmto, *glob; skinfile_t *sf, *sf2; char skinfilename[ MAX_QPATH ]; char *skinfilecontent; @@ -371,26 +370,24 @@ void InsertModel( const char *name, int skin, int frame, m4x4_t transform, remap } /* handle shader remapping */ - glob = rmto = NULL; - for ( rm = remap; rm != NULL; rm = rm->next ) { - if ( strEqual( rm->from, "*" ) ) { - glob = rm; + const char* to = NULL; + size_t fromlen = 0; + for ( remap_t *rm = remap; rm != NULL; rm = rm->next ) + { + if ( strEqual( rm->from, "*" ) && fromlen == 0 ) { // only globbing, if no respective match + to = rm->to; + } + else if( striEqualSuffix( picoShaderName, rm->from ) && strlen( rm->from ) > fromlen ){ // longer match has priority + to = rm->to; + fromlen = strlen( rm->from ); + } } - else if( striEqualSuffix( picoShaderName, rm->from ) ){ - rmto = rm; - if( striEqual( picoShaderName, rm->from ) ) // exact match priority - break; + if( to != NULL ){ + Sys_FPrintf( SYS_VRB, ( fromlen == 0? "Globbing '%s' to '%s'\n" : "Remapping '%s' to '%s'\n" ), picoShaderName, to ); + picoShaderName = to; } } - if( rmto != NULL ){ - Sys_FPrintf( SYS_VRB, "Remapping '%s' to '%s'\n", picoShaderName, rmto->to ); - picoShaderName = rmto->to; - } - else if ( glob != NULL ) { - Sys_FPrintf( SYS_VRB, "Globbing '%s' to '%s'\n", picoShaderName, glob->to ); - picoShaderName = glob->to; - } /* shader renaming for sof2 */ if ( renameModelShaders ) {