diff --git a/doomsday/client/include/resource/resourcesystem.h b/doomsday/client/include/resource/resourcesystem.h index ffb810db53..792a03ce1c 100644 --- a/doomsday/client/include/resource/resourcesystem.h +++ b/doomsday/client/include/resource/resourcesystem.h @@ -37,9 +37,13 @@ #include "Sprite" #include "Texture" #include "TextureScheme" +#include "uri.hh" #include #include #include +#include +#include +#include /** * Logical resources; materials, packages, textures, etc... @@ -95,8 +99,8 @@ class ResourceSystem : public de::System DENG2_ERROR(UnknownMaterialIdError); #ifdef __CLIENT__ - /// An unknown model def was referenced. @ingroup errors - DENG2_ERROR(UnknownModelDefError); + /// The referenced model def was not found. @ingroup errors + DENG2_ERROR(MissingModelDefError); /// The specified font id was invalid (out of range). @ingroup errors DENG2_ERROR(UnknownFontIdError); @@ -122,7 +126,7 @@ class ResourceSystem : public de::System public: /** * Construct a new resource system, configuring all resource classes and - * their collections. + * the associated resource collection schemes. */ ResourceSystem(); @@ -174,10 +178,9 @@ class ResourceSystem : public de::System */ int spriteCount(); - patchid_t declarePatch(de::String encodedName); - /** * Determines if a material exists for a @a path. + * * @return @c true if a material exists; otherwise @a false. * * @see hasMaterialManifest(), MaterialManifest::hasMaterial() @@ -210,6 +213,7 @@ class ResourceSystem : public de::System /** * Determines if a manifest exists for a material on @a path. + * * @return @c true if a manifest exists; otherwise @a false. */ bool hasMaterialManifest(de::Uri const &path) const; @@ -269,10 +273,8 @@ class ResourceSystem : public de::System * * @see allMaterialSchemes(), MaterialScheme::clear(). */ - inline void clearAllMaterialSchemes() - { - foreach(de::MaterialScheme *scheme, allMaterialSchemes()) - { + inline void clearAllMaterialSchemes() { + foreach(de::MaterialScheme *scheme, allMaterialSchemes()) { scheme->clear(); } } @@ -311,8 +313,7 @@ class ResourceSystem : public de::System * * @return Manifest for this URI. */ - inline de::MaterialManifest &declareMaterial(de::Uri const &uri) - { + inline de::MaterialManifest &declareMaterial(de::Uri const &uri) { return materialScheme(uri.scheme()).declare(uri.path()); } @@ -324,6 +325,7 @@ class ResourceSystem : public de::System /** * Determines if a texture exists for @a path. + * * @return @c true, if a texture exists; otherwise @a false. * * @see hasTextureManifest(), TextureManifest::hasTexture() @@ -367,6 +369,7 @@ class ResourceSystem : public de::System /** * Determines if a texture manifest exists for a declared texture on @a path. + * * @return @c true, if a manifest exists; otherwise @a false. */ bool hasTextureManifest(de::Uri const &path) const; @@ -455,9 +458,12 @@ class ResourceSystem : public de::System de::Texture *defineTexture(de::String schemeName, de::Uri const &resourceUri, de::Vector2i const &dimensions = de::Vector2i()); + patchid_t declarePatch(de::String encodedName); + #ifdef __CLIENT__ /** * Determines if a manifest exists for a resource on @a path. + * * @return @c true, if a manifest exists; otherwise @a false. */ bool hasFont(de::Uri const &path) const; @@ -568,7 +574,8 @@ class ResourceSystem : public de::System Model &model(modelid_t id); /** - * Determines if a model definition exists with the given @a id. + * Determines if a model definition exists with the given @a id. O(n) + * * @return @c true, if a definition exists; otherwise @a false. * * @see modelDef() @@ -597,21 +604,14 @@ class ResourceSystem : public de::System * Lookup a model definition for the specified mobj @a stateIndex. * * @param stateIndex Index of the mobj state. - * @param select Model selector argument. There may be multiple models for - * a given mobj state. The selector determines which is used - * according to some external selection criteria. + * @param select Model selector argument. There may be multiple models + * for a given mobj state. The selector determines which + * is used according to some external selection criteria. * * @return Found model definition; otherwise @c 0. */ ModelDef *modelDefForState(int stateIndex, int select = 0); - /** - * Is there a model for this mobj? The decision is made based on the state and tics - * of the mobj. Returns the modeldefs that are in effect at the moment (interlinks - * checked appropriately). - */ - float modelDefForMobj(struct mobj_s const *mo, ModelDef **mdef, ModelDef **nextmdef); - /** * Returns the total number of model definitions in the system. * @@ -787,9 +787,14 @@ class ResourceSystem : public de::System /** * Rewind all material animations back to their initial/starting state. * - * @see Materials::all(), MaterialVariant::restartAnimation() + * @see allMaterials(), MaterialAnimation::restart() */ - void restartAllMaterialAnimations(); + inline void restartAllMaterialAnimations() { + foreach(Material *material, allMaterials()) + foreach(MaterialAnimation *animation, material->animations()) { + animation->restart(); + } + } /** * Prepare resources for the current Map. diff --git a/doomsday/client/include/resource/sprite.h b/doomsday/client/include/resource/sprite.h index a1efbe52ef..ad8e1bbb9c 100644 --- a/doomsday/client/include/resource/sprite.h +++ b/doomsday/client/include/resource/sprite.h @@ -112,6 +112,13 @@ class Sprite ViewAngles const &viewAngles() const; #ifdef __CLIENT__ + /** + * Returns the radius of the sprite as it would visually appear to be. + * + * @note Presently considers rotation 0 only! + */ + double visualRadius() const; + /** * Produce a luminous object from the sprite configuration. The properties * of any resultant lumobj are configured in "sprite-local" space. This means diff --git a/doomsday/client/include/world/p_object.h b/doomsday/client/include/world/p_object.h index ea4084396a..0f720fe1c9 100644 --- a/doomsday/client/include/world/p_object.h +++ b/doomsday/client/include/world/p_object.h @@ -25,11 +25,13 @@ # error Attempted to include internal Doomsday p_object.h from a game #endif -#include - -#include - +#ifdef __CLIENT__ +# include "ModelDef" +# include "Sprite" +#endif #include "Sector" +#include +#include class BspLeaf; class Plane; @@ -161,6 +163,26 @@ void Mobj_GenerateLumobjs(mobj_t *mobj); */ float Mobj_ShadowStrength(mobj_t *mobj); +/** + * Determines which of the available sprites is in effect for the current mobj + * state and frame. May return @c 0 if the state and/or frame is not valid. + */ +Sprite *Mobj_Sprite(mobj_t const &mobj); + +/** + * Determines which of the available model definitions (if any), are in effect + * for the current mobj state and frame. (Interlinks are resolved). + * + * @param nextModef If non-zero the model definition for the @em next frame is + * written here. + * @param interp If non-zero and both model definitions are found the current + * interpolation point between the two is written here. + * + * @return Active model definition for the current frame (if any). + */ +ModelDef *Mobj_ModelDef(mobj_t const &mobj, ModelDef **nextModef = 0, + float *interp = 0); + #endif // __CLIENT__ coord_t Mobj_ApproxPointDistance(mobj_t *start, coord_t const *point); diff --git a/doomsday/client/src/def_main.cpp b/doomsday/client/src/def_main.cpp index 7087a0679f..037ade8667 100644 --- a/doomsday/client/src/def_main.cpp +++ b/doomsday/client/src/def_main.cpp @@ -1926,7 +1926,7 @@ void Def_PostInit() st->endFrame = -1; } } - catch(ResourceSystem::UnknownModelDefError const &) + catch(ResourceSystem::MissingModelDefError const &) {} // Ignore this error. } } diff --git a/doomsday/client/src/render/r_main.cpp b/doomsday/client/src/render/r_main.cpp index 6f2c15cea8..24ab1c1faa 100644 --- a/doomsday/client/src/render/r_main.cpp +++ b/doomsday/client/src/render/r_main.cpp @@ -1021,7 +1021,7 @@ void R_SetupPlayerSprites() dummy.state = psp->statePtr; dummy.tics = psp->tics; - inter = App_ResourceSystem().modelDefForMobj(&dummy, &mf, &nextmf); + mf = Mobj_ModelDef(dummy, &nextmf, &inter); if(mf) isModel = true; } diff --git a/doomsday/client/src/render/r_things.cpp b/doomsday/client/src/render/r_things.cpp index 73757b20f2..a109138ae7 100644 --- a/doomsday/client/src/render/r_things.cpp +++ b/doomsday/client/src/render/r_things.cpp @@ -175,7 +175,7 @@ void R_ProjectSprite(mobj_t *mo) // ...in an invalid state? if(!mo->state || mo->state == states) return; // ...no sprite frame is defined? - Sprite *sprite = App_ResourceSystem().spritePtr(mo->sprite, mo->frame); + Sprite *sprite = Mobj_Sprite(*mo); if(!sprite) return; // ...fully transparent? float const alpha = Mobj_Alpha(mo); @@ -193,7 +193,7 @@ void R_ProjectSprite(mobj_t *mo) float interp = 0; if(useModels) { - interp = App_ResourceSystem().modelDefForMobj(mo, &mf, &nextmf); + mf = Mobj_ModelDef(*mo, &nextmf, &interp); if(mf) { // Use a sprite if the object is beyond the maximum model distance. diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index 777041e7fc..083528a85e 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -2843,7 +2843,7 @@ static int projectSpriteWorker(mobj_t &mo, void * /*context*/) if(cluster.visCeiling().surface().hasSkyMaskedMaterial()) { - if(Sprite *sprite = App_ResourceSystem().spritePtr(mo.sprite, mo.frame)) + if(Sprite *sprite = Mobj_Sprite(mo)) { if(sprite->hasViewAngle(0)) { diff --git a/doomsday/client/src/render/sky.cpp b/doomsday/client/src/render/sky.cpp index 4f1425517c..ad789aa707 100644 --- a/doomsday/client/src/render/sky.cpp +++ b/doomsday/client/src/render/sky.cpp @@ -355,7 +355,7 @@ static void setupSkyModels(ded_sky_t *def) sm->yaw = modef->yaw; sm->frame = sm->model->subModelDef(0).frame; } - catch(ResourceSystem::UnknownModelDefError const &) + catch(ResourceSystem::MissingModelDefError const &) {} // Ignore this error. } } diff --git a/doomsday/client/src/resource/resourcesystem.cpp b/doomsday/client/src/resource/resourcesystem.cpp index 99388dec05..9acf2f642c 100644 --- a/doomsday/client/src/resource/resourcesystem.cpp +++ b/doomsday/client/src/resource/resourcesystem.cpp @@ -24,18 +24,16 @@ #ifdef __CLIENT__ # include "clientapp.h" # include "con_bar.h" +# include "sys_system.h" // novideo #endif #include "con_main.h" #include "def_main.h" -#include "resource/colorpalette.h" #include "resource/compositetexture.h" #include "resource/patch.h" #include "resource/patchname.h" #ifdef __CLIENT__ -# include "BitmapFont" -# include "CompositeBitmapFont" # include "MaterialSnapshot" #endif @@ -47,7 +45,6 @@ # include "gl/gl_texmanager.h" # include "render/rend_model.h" # include "render/rend_particle.h" // Rend_ParticleReleaseSystemTextures -# include "sys_system.h" // novideo // For smart caching logics: # include "network/net_demo.h" // playback @@ -71,11 +68,8 @@ # include # include #endif -#include "uri.hh" #include /// @todo remove me -#include // M_CycleIntoRange() -#include -#include +#include #include using namespace de; @@ -223,9 +217,9 @@ DENG2_PIMPL(ResourceSystem) uint fontManifestIdMapSize; FontManifest **fontManifestIdMap; ///< Index with fontid_t-1 - typedef std::vector ModelDefs; + typedef QVector ModelDefs; ModelDefs modefs; - std::vector stateModefs; // Index to the modefs array. + QVector stateModefs; // Index to the modefs array. typedef StringPool ModelRepository; ModelRepository *modelRepository; // Owns Model instances. @@ -1246,8 +1240,8 @@ DENG2_PIMPL(ResourceSystem) } // Get a new entry. - modefs.push_back(ModelDef(id.toUtf8().constData())); - return &modefs.back(); + modefs.append(ModelDef(id.toUtf8().constData())); + return &modefs.last(); } /** @@ -1257,27 +1251,30 @@ DENG2_PIMPL(ResourceSystem) ModelDef *getModelDef(int state, float interMark, int select) { // Is this a valid state? - if(state < 0 || state >= countStates.num) return 0; + if(state < 0 || state >= countStates.num) + { + return 0; + } // First try to find an existing modef. - for(uint i = 0; i < modefs.size(); ++i) + foreach(ModelDef const &modef, modefs) { - if(modefs[i].state == &states[state] && - modefs[i].interMark == interMark && modefs[i].select == select) + if(modef.state == &states[state] && + modef.interMark == interMark && modef.select == select) { - // Models are loaded in reverse order; this one already has - // a model. - return NULL; + // Models are loaded in reverse order; this one already has a model. + return 0; } } - modefs.push_back(ModelDef()); + modefs.append(ModelDef()); + ModelDef *md = &modefs.last(); // Set initial data. - ModelDef *md = &modefs.back(); md->state = &states[state]; md->interMark = interMark; md->select = select; + return md; } @@ -3012,9 +3009,9 @@ bool ResourceSystem::hasModelDef(de::String id) const if(!id.isEmpty()) { char const *idCStr = id.toUtf8().constData(); - DENG2_FOR_EACH(Instance::ModelDefs, i, d->modefs) + foreach(ModelDef const &modef, d->modefs) { - if(!strcmp(i->id, idCStr)) + if(!strcmp(modef.id, idCStr)) { return true; } @@ -3029,8 +3026,8 @@ ModelDef &ResourceSystem::modelDef(int index) { return d->modefs[index]; } - /// @throw UnknownModelDefIndexError An unknown model definition was referenced. - throw UnknownModelDefError("ResourceSystem::modelDef", "Invalid index " + String::number(index) + ", valid range " + Rangeui(0, modelDefCount()).asText()); + /// @throw MissingModelDefError An unknown model definition was referenced. + throw MissingModelDefError("ResourceSystem::modelDef", "Invalid index " + String::number(index) + ", valid range " + Rangeui(0, modelDefCount()).asText()); } ModelDef &ResourceSystem::modelDef(String id) @@ -3038,16 +3035,16 @@ ModelDef &ResourceSystem::modelDef(String id) if(!id.isEmpty()) { char const *idCStr = id.toUtf8().constData(); - DENG2_FOR_EACH(Instance::ModelDefs, i, d->modefs) + foreach(ModelDef const &modef, d->modefs) { - if(!strcmp(i->id, idCStr)) + if(!strcmp(modef.id, idCStr)) { - return *i; + return const_cast(modef); } } } - /// @throw UnknownModelDefIndexError An unknown model definition was referenced. - throw UnknownModelDefError("ResourceSystem::modelDef", QString("Invalid id %1").arg(id)); + /// @throw MissingModelDefError An unknown model definition was referenced. + throw MissingModelDefError("ResourceSystem::modelDef", QString("Invalid id %1").arg(id)); } ModelDef *ResourceSystem::modelDefForState(int stateIndex, int select) @@ -3056,7 +3053,7 @@ ModelDef *ResourceSystem::modelDefForState(int stateIndex, int select) { return 0; } - if(stateIndex < 0 || stateIndex >= int(d->stateModefs.size())) + if(stateIndex < 0 || stateIndex >= d->stateModefs.count()) { return 0; } @@ -3066,7 +3063,7 @@ ModelDef *ResourceSystem::modelDefForState(int stateIndex, int select) } DENG2_ASSERT(d->stateModefs[stateIndex] >= 0); - DENG2_ASSERT(d->stateModefs[stateIndex] < int(d->modefs.size())); + DENG2_ASSERT(d->stateModefs[stateIndex] < d->modefs.count()); ModelDef *def = &d->modefs[d->stateModefs[stateIndex]]; if(select) @@ -3085,155 +3082,9 @@ ModelDef *ResourceSystem::modelDefForState(int stateIndex, int select) return def; } -float ResourceSystem::modelDefForMobj(mobj_t const *mo, ModelDef **modef, ModelDef **nextmodef) -{ - // On the client it is possible that we don't know the mobj's state. - if(!mo->state) return -1; - - state_t &st = *mo->state; - - // By default there are no models. - *nextmodef = 0; - *modef = modelDefForState(&st - states, mo->selector); - if(!*modef) return -1; // No model available. - - float interp = -1; - - // World time animation? - bool worldTime = false; - if((*modef)->flags & MFF_WORLD_TIME_ANIM) - { - float duration = (*modef)->interRange[0]; - float offset = (*modef)->interRange[1]; - - // Validate/modify the values. - if(duration == 0) duration = 1; - - if(offset == -1) - { - offset = M_CycleIntoRange(MOBJ_TO_ID(mo), duration); - } - - interp = M_CycleIntoRange(App_World().time() / duration + offset, 1); - worldTime = true; - } - else - { - // Calculate the currently applicable intermark. - interp = 1.0f - (mo->tics - frameTimePos) / float( st.tics ); - } - -/*#if _DEBUG - if(mo->dPlayer) - { - qDebug() << "itp:" << interp << " mot:" << mo->tics << " stt:" << st.tics; - } -#endif*/ - - // First find the modef for the interpoint. Intermark is 'stronger' than interrange. - - // Scan interlinks. - while((*modef)->interNext && (*modef)->interNext->interMark <= interp) - { - *modef = (*modef)->interNext; - } - - if(!worldTime) - { - // Scale to the modeldef's interpolation range. - interp = (*modef)->interRange[0] + interp * ((*modef)->interRange[1] - - (*modef)->interRange[0]); - } - - // What would be the next model? Check interlinks first. - if((*modef)->interNext) - { - *nextmodef = (*modef)->interNext; - } - else if(worldTime) - { - *nextmodef = modelDefForState(&st - states, mo->selector); - } - else if(st.nextState > 0) // Check next state. - { - // Find the appropriate state based on interrange. - state_t *it = states + st.nextState; - bool foundNext = false; - if((*modef)->interRange[1] < 1) - { - // Current modef doesn't interpolate to the end, find the proper destination - // modef (it isn't just the next one). Scan the states that follow (and - // interlinks of each). - bool stopScan = false; - int max = 20; // Let's not be here forever... - while(!stopScan) - { - if(!((!modelDefForState(it - states) || - modelDefForState(it - states, mo->selector)->interRange[0] > 0) && - it->nextState > 0)) - { - stopScan = true; - } - else - { - // Scan interlinks, then go to the next state. - ModelDef *mdit; - if((mdit = modelDefForState(it - states, mo->selector)) && mdit->interNext) - { - forever - { - mdit = mdit->interNext; - if(mdit) - { - if(mdit->interRange[0] <= 0) // A new beginning? - { - *nextmodef = mdit; - foundNext = true; - } - } - - if(!mdit || foundNext) - { - break; - } - } - } - - if(foundNext) - { - stopScan = true; - } - else - { - it = states + it->nextState; - } - } - - if(max-- <= 0) - stopScan = true; - } - // @todo What about max == -1? What should 'it' be then? - } - - if(!foundNext) - { - *nextmodef = modelDefForState(it - states, mo->selector); - } - } - - // Is this group disabled? - if(useModels >= 2 && (*modef)->group & useModels) - { - *modef = *nextmodef = 0; - return -1; - } - - return interp; -} - int ResourceSystem::modelDefCount() const { - return d->modefs.size(); + return d->modefs.count(); } void ResourceSystem::initModels() @@ -3256,16 +3107,13 @@ void ResourceSystem::initModels() d->modelRepository = new StringPool(); // There can't be more modeldefs than there are DED Models. - for(uint i = 0; i < defs.models.size(); ++i) - { - d->modefs.push_back(ModelDef()); - } + d->modefs.resize(defs.models.size()); - // Clear the modef pointers of all States. - d->stateModefs.clear(); + // Clear the stateid => modeldef LUT. + d->stateModefs.resize(countStates.num); for(int i = 0; i < countStates.num; ++i) { - d->stateModefs.push_back(-1); + d->stateModefs[i] = -1; } // Read in the model files and their data. @@ -3285,14 +3133,14 @@ void ResourceSystem::initModels() // is important. We want to allow "patch" definitions, right? // For each modeldef we will find the "next" def. - for(int i = int(d->modefs.size()) - 1; i >= 0; --i) + for(int i = d->modefs.count() - 1; i >= 0; --i) { ModelDef *me = &d->modefs[i]; float minmark = 2; // max = 1, so this is "out of bounds". ModelDef *closest = 0; - for(int k = int(d->modefs.size()) - 1; k >= 0; --k) + for(int k = d->modefs.count() - 1; k >= 0; --k) { ModelDef *other = &d->modefs[k]; @@ -3310,7 +3158,7 @@ void ResourceSystem::initModels() } // Create selectlinks. - for(int i = int(d->modefs.size()) - 1; i >= 0; --i) + for(int i = d->modefs.count() - 1; i >= 0; --i) { ModelDef *me = &d->modefs[i]; @@ -3319,7 +3167,7 @@ void ResourceSystem::initModels() ModelDef *closest = 0; // Start scanning from the next definition. - for(int k = int(d->modefs.size()) - 1; k >= 0; --k) + for(int k = d->modefs.count() - 1; k >= 0; --k) { ModelDef *other = &d->modefs[k]; @@ -3342,7 +3190,7 @@ void ResourceSystem::initModels() int ResourceSystem::indexOf(ModelDef const *modelDef) { int index = int(modelDef - &d->modefs[0]); - if(index >= 0 && index < int(d->modefs.size())) + if(index >= 0 && index < d->modefs.count()) { return index; } @@ -3737,15 +3585,6 @@ MaterialVariantSpec const &ResourceSystem::materialSpec(MaterialContextId contex mipmapped, gammaCorrection, noStretch, toAlpha); } -void ResourceSystem::restartAllMaterialAnimations() -{ - foreach(Material *material, d->materials) - foreach(MaterialAnimation *animation, material->animations()) - { - animation->restart(); - } -} - static int findSpriteOwner(thinker_t *th, void *context) { mobj_t *mo = reinterpret_cast(th); diff --git a/doomsday/client/src/resource/sprite.cpp b/doomsday/client/src/resource/sprite.cpp index a23a9a4762..1b4f3c4f3d 100644 --- a/doomsday/client/src/resource/sprite.cpp +++ b/doomsday/client/src/resource/sprite.cpp @@ -126,7 +126,23 @@ Sprite::ViewAngle const &Sprite::closestViewAngle(angle_t mobjAngle, angle_t ang return viewAngle(rotation); } +Sprite::ViewAngles const &Sprite::viewAngles() const +{ + return d->viewAngles; +} + #ifdef __CLIENT__ +double Sprite::visualRadius() const +{ + if(hasViewAngle(0)) + { + Material *material = viewAngle(0).material; + MaterialSnapshot const &ms = material->prepare(Rend_SpriteMaterialSpec()); + return ms.width() / 2; + } + return 0; +} + Lumobj *Sprite::generateLumobj() const { LOG_AS("Sprite::generateLumobj"); @@ -154,8 +170,3 @@ Lumobj *Sprite::generateLumobj() const ->setZOffset(-tex.origin().y - pl->originY * ms.height()); } #endif // __CLIENT__ - -Sprite::ViewAngles const &Sprite::viewAngles() const -{ - return d->viewAngles; -} diff --git a/doomsday/client/src/world/p_mobj.cpp b/doomsday/client/src/world/p_mobj.cpp index 03db2731b6..82fd62b18c 100644 --- a/doomsday/client/src/world/p_mobj.cpp +++ b/doomsday/client/src/world/p_mobj.cpp @@ -291,17 +291,6 @@ DENG_EXTERN_C Sector *Mobj_Sector(mobj_t const *mobj) #ifdef __CLIENT__ -static ModelDef *currentModelDefForMobj(mobj_t const &mo) -{ - if(useModels) - { - ModelDef *mf = 0, *nextmf = 0; - App_ResourceSystem().modelDefForMobj(&mo, &mf, &nextmf); - return mf; - } - return 0; -} - boolean Mobj_OriginBehindVisPlane(mobj_t *mo) { if(!mo || !Mobj_HasCluster(*mo)) @@ -371,7 +360,7 @@ void Mobj_GenerateLumobjs(mobj_t *mo) return; } - Sprite *sprite = App_ResourceSystem().spritePtr(mo->sprite, mo->frame); + Sprite *sprite = Mobj_Sprite(*mo); if(!sprite) return; // Always use the front rotation when determining light properties. @@ -465,9 +454,9 @@ float Mobj_ShadowStrength(mobj_t *mo) } // Sprites have their own shadow strength factor. - if(!currentModelDefForMobj(*mo)) + if(!useModels || !Mobj_ModelDef(*mo)) { - if(Sprite *sprite = App_ResourceSystem().spritePtr(mo->sprite, mo->frame)) + if(Sprite *sprite = Mobj_Sprite(*mo)) { if(sprite->hasViewAngle(0)) { @@ -500,6 +489,158 @@ float Mobj_ShadowStrength(mobj_t *mo) return (0.6f - ambientLightLevel * 0.4f) * strength; } +Sprite *Mobj_Sprite(mobj_t const &mo) +{ + return App_ResourceSystem().spritePtr(mo.sprite, mo.frame); +} + +ModelDef *Mobj_ModelDef(mobj_t const &mo, ModelDef **retNextModef, float *retInter) +{ + ResourceSystem &resSys = App_ResourceSystem(); + + // By default there are no models. + if(retNextModef) *retNextModef = 0; + if(retInter) *retInter = -1; + + // On the client it is possible that we don't know the mobj's state. + if(!mo.state) return 0; + + state_t &st = *mo.state; + ModelDef *modef = resSys.modelDefForState(&st - states, mo.selector); + if(!modef) return 0; // No model available. + + float interp = -1; + + // World time animation? + bool worldTime = false; + if(modef->flags & MFF_WORLD_TIME_ANIM) + { + float duration = modef->interRange[0]; + float offset = modef->interRange[1]; + + // Validate/modify the values. + if(duration == 0) duration = 1; + + if(offset == -1) + { + offset = M_CycleIntoRange(MOBJ_TO_ID(&mo), duration); + } + + interp = M_CycleIntoRange(App_World().time() / duration + offset, 1); + worldTime = true; + } + else + { + // Calculate the currently applicable intermark. + interp = 1.0f - (mo.tics - frameTimePos) / float( st.tics ); + } + +/*#if _DEBUG + if(mo.dPlayer) + { + qDebug() << "itp:" << interp << " mot:" << mo.tics << " stt:" << st.tics; + } +#endif*/ + + // First find the modef for the interpoint. Intermark is 'stronger' than interrange. + + // Scan interlinks. + while(modef->interNext && modef->interNext->interMark <= interp) + { + modef = modef->interNext; + } + + if(!worldTime) + { + // Scale to the modeldef's interpolation range. + interp = modef->interRange[0] + interp + * (modef->interRange[1] - modef->interRange[0]); + } + + // What would be the next model? Check interlinks first. + if(retNextModef) + { + if(modef->interNext) + { + *retNextModef = modef->interNext; + } + else if(worldTime) + { + *retNextModef = resSys.modelDefForState(&st - states, mo.selector); + } + else if(st.nextState > 0) // Check next state. + { + // Find the appropriate state based on interrange. + state_t *it = states + st.nextState; + bool foundNext = false; + if(modef->interRange[1] < 1) + { + // Current modef doesn't interpolate to the end, find the proper destination + // modef (it isn't just the next one). Scan the states that follow (and + // interlinks of each). + bool stopScan = false; + int max = 20; // Let's not be here forever... + while(!stopScan) + { + if(!((!resSys.modelDefForState(it - states) || + resSys.modelDefForState(it - states, mo.selector)->interRange[0] > 0) && + it->nextState > 0)) + { + stopScan = true; + } + else + { + // Scan interlinks, then go to the next state. + ModelDef *mdit = resSys.modelDefForState(it - states, mo.selector); + if(mdit && mdit->interNext) + { + forever + { + mdit = mdit->interNext; + if(mdit) + { + if(mdit->interRange[0] <= 0) // A new beginning? + { + *retNextModef = mdit; + foundNext = true; + } + } + + if(!mdit || foundNext) + { + break; + } + } + } + + if(foundNext) + { + stopScan = true; + } + else + { + it = states + it->nextState; + } + } + + if(max-- <= 0) + stopScan = true; + } + // @todo What about max == -1? What should 'it' be then? + } + + if(!foundNext) + { + *retNextModef = resSys.modelDefForState(it - states, mo.selector); + } + } + } + + if(retInter) *retInter = interp; + + return modef; +} + #endif // __CLIENT__ #undef Mobj_AngleSmoothed @@ -581,22 +722,19 @@ coord_t Mobj_Radius(mobj_t const &mobj) coord_t Mobj_VisualRadius(mobj_t const &mobj) { #ifdef __CLIENT__ - - // If models are being used, use the model's radius. - if(ModelDef *mf = currentModelDefForMobj(mobj)) + // Is a model in effect? + if(useModels) { - return mf->visualRadius; + if(ModelDef *modef = Mobj_ModelDef(mobj)) + { + return modef->visualRadius; + } } - // Use the sprite frame's width? - if(Sprite *sprite = App_ResourceSystem().spritePtr(mobj.sprite, mobj.frame)) + // Is a sprite in effect? + if(Sprite *sprite = Mobj_Sprite(mobj)) { - if(sprite->hasViewAngle(0)) - { - Material *material = sprite->viewAngle(0).material; - MaterialSnapshot const &ms = material->prepare(Rend_SpriteMaterialSpec()); - return ms.width() / 2; - } + return sprite->visualRadius(); } #endif diff --git a/doomsday/client/src/world/p_particle.cpp b/doomsday/client/src/world/p_particle.cpp index 95e0e3d148..4e77bcb5a4 100644 --- a/doomsday/client/src/world/p_particle.cpp +++ b/doomsday/client/src/world/p_particle.cpp @@ -497,7 +497,7 @@ static void P_NewParticle(ptcgen_t *gen) // Check for model-only generators. if(gen->source) { - inter = App_ResourceSystem().modelDefForMobj(gen->source, &mf, &nextmf); + mf = Mobj_ModelDef(*gen->source, &nextmf, &inter); if(((!mf || !useModels) && def->flags & PGF_MODEL_ONLY) || (mf && useModels && mf->flags & MFF_NO_PARTICLES)) return;