diff --git a/doomsday/client/include/resource/animgroups.h b/doomsday/client/include/resource/animgroups.h index 90f7887993..03c06ce4f7 100644 --- a/doomsday/client/include/resource/animgroups.h +++ b/doomsday/client/include/resource/animgroups.h @@ -1,8 +1,7 @@ -/** - * @file animgroups.h (Material) Animation groups. +/** @file animgroups.h Material animation group. * - * @author Copyright © 2003-2013 Jaakko Keränen - * @author Copyright © 2005-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -19,52 +18,126 @@ * 02110-1301 USA */ -#ifndef LIBDENG_RESOURCE_ANIMGROUPS_H -#define LIBDENG_RESOURCE_ANIMGROUPS_H +#ifndef DENG_RESOURCE_ANIMATIONGROUP_H +#define DENG_RESOURCE_ANIMATIONGROUP_H -#include "Texture" +#include "dd_types.h" +#include "TextureManifest" +#include -#ifdef __cplusplus -extern "C" { -#endif +namespace de { /** - * (Material) Animation group frame. + * Material Animation group. + * * @ingroup resource */ -typedef struct animframe_s +class AnimGroup { - void *textureManifest; - ushort tics; - ushort randomTics; -} animframe_t; +public: + /** + * A single frame in the animation. + */ + struct Frame + { + public: + /** + * Returns the texture manifest for the frame. + */ + TextureManifest &textureManifest() const; -/** - * (Material) Animation group. - * @ingroup resource - */ -typedef struct animgroup_s -{ - int id; - int flags; - int count; - animframe_t *frames; -} animgroup_t; + /** + * Returns the duration of the frame in tics. + */ + ushort tics() const; + + /** + * Returns the additional duration of the frame tics. + */ + ushort randomTics() const; + + friend class AnimGroup; + + private: + Frame(TextureManifest &textureManifest, ushort tics, ushort randomTics); + + TextureManifest *_textureManifest; + ushort _tics; + ushort _randomTics; + }; + + typedef QList Frames; + +public: + /** + * Construct a new animation group. + * + * @param uniqueId Unique identifier to associate with the group. + * @param flags @ref animationGroupFlags + */ + AnimGroup(int uniqueId, int flags = 0); + + /** + * Returns the unique identifier associated with the animation. + */ + int id() const; + + /** + * @return @ref animationGroupFlags + */ + int flags() const; + + /** + * Returns @c true iff at least one frame in the animation uses the specified + * @a textureManifest + * + * @see frames() + */ + bool hasFrameFor(TextureManifest const &textureManifest) const; + + /** + * Append a new frame to the animation. + * + * @param texture Manifest for the texture to use during the frame. + * @param tics Duration of the frame in tics. + * @param randomTics Random duration of the frame in tics. + * + * @return The new frame. + */ + Frame &newFrame(TextureManifest &textureManifest, ushort tics, + ushort randomTics = 0); + + /** + * Clear all frames in the animation. + */ + void clearAllFrames(); + + /** + * Returns the total number of frames in the animation. + */ + inline int frameCount() const { return allFrames().count(); } -/// @return Number of animation/precache groups. -int R_AnimGroupCount(void); + /** + * Convenient method of returning a frame in the animation by @a index. + * It is assumed that the index is within valid [0..frameCount) range. + * + * @see frameCount() + */ + inline Frame &frame(int index) const { return *allFrames().at(index); } -/// To be called to destroy all animation groups when they are no longer needed. -void R_ClearAnimGroups(void); + /** + * Provides access to the frame list for efficient traversal. + * + * @see frame() + */ + Frames const &allFrames() const; -/// @return AnimGroup associated with @a animGroupNum else @c NULL -animgroup_t const *R_ToAnimGroup(int animGroupNum); +private: + DENG2_PRIVATE(d) +}; -/// @return @c true iff @a texture is linked to the identified @a animGroupNum. -boolean R_IsTextureInAnimGroup(Uri const *texture, int animGroupNum); +typedef AnimGroup::Frame AnimGroupFrame; -#ifdef __cplusplus -} // extern "C" -#endif +} // namespace de -#endif /* LIBDENG_RESOURCE_ANIMGROUPS_H */ +#endif // DENG_RESOURCE_ANIMATIONGROUP_H diff --git a/doomsday/client/include/resource/resourcesystem.h b/doomsday/client/include/resource/resourcesystem.h index 12e8c50a1e..80dd98c1b0 100644 --- a/doomsday/client/include/resource/resourcesystem.h +++ b/doomsday/client/include/resource/resourcesystem.h @@ -21,6 +21,7 @@ #include "def_data.h" #include "resourceclass.h" +#include "resource/animgroups.h" #ifdef __CLIENT__ # include "Fonts" #endif @@ -133,6 +134,28 @@ class ResourceSystem : public de::System #endif + /** + * Returns the total number of animation/precache groups. + */ + int animGroupCount(); + + /** + * Destroys all the animation groups. + */ + void clearAllAnimGroups(); + + /** + * Returns the AnimGroup associated with @a index; otherwise @c 0. + */ + de::AnimGroup *animGroup(int index); + + /** + * Construct a new animation group. + * + * @param flags @ref animationGroupFlags + */ + de::AnimGroup &newAnimGroup(int flags); + public: /// @todo Should be private: void initCompositeTextures(); void initFlatTextures(); diff --git a/doomsday/client/src/dd_main.cpp b/doomsday/client/src/dd_main.cpp index 1e542824e4..3df8509b98 100644 --- a/doomsday/client/src/dd_main.cpp +++ b/doomsday/client/src/dd_main.cpp @@ -1410,7 +1410,7 @@ bool App_ChangeGame(Game &game, bool allowReload) B_BindDefaults(); B_InitialContextActivations(); #endif - R_ClearAnimGroups(); + App_ResourceSystem().clearAllAnimGroups(); // Reset the world back to it's initial state (unload the map, reset players, etc...). App_World().reset(); diff --git a/doomsday/client/src/def_main.cpp b/doomsday/client/src/def_main.cpp index 902a230a71..f4b26c7fd4 100644 --- a/doomsday/client/src/def_main.cpp +++ b/doomsday/client/src/def_main.cpp @@ -920,22 +920,16 @@ static void readAllDefinitions() LOG_INFO(String("readAllDefinitions: Completed in %1 seconds.").arg(begunAt.since(), 0, 'g', 2)); } -static animgroup_t const *findAnimGroupForTexture(TextureManifest &manifest) +static AnimGroup const *findAnimGroupForTexture(TextureManifest &textureManifest) { // Group ids are 1-based. // Search backwards to allow patching. - for(int i = R_AnimGroupCount(); i > 0; i--) + for(int i = App_ResourceSystem().animGroupCount(); i > 0; i--) { - animgroup_t const *anim = R_ToAnimGroup(i); - for(int j = 0; j < anim->count; ++j) + AnimGroup *animGroup = App_ResourceSystem().animGroup(i); + if(animGroup->hasFrameFor(textureManifest)) { - animframe_t const *frame = &anim->frames[j]; - if(!frame->textureManifest) continue; - - if(&manifest == frame->textureManifest) - { - return anim; - } + return animGroup; } } return 0; // Not found. @@ -999,47 +993,46 @@ static void generateMaterialDefForTexture(TextureManifest &manifest) st->texture = reinterpret_cast(new de::Uri(texUri)); // Is there an animation for this? - animgroup_t const *anim = findAnimGroupForTexture(manifest); - if(anim && anim->count > 1) + AnimGroup const *anim = findAnimGroupForTexture(manifest); + if(anim && anim->frameCount() > 1) { - animframe_t const *animFrame; + AnimGroupFrame const *animFrame; // Determine the start frame. int startFrame = 0; - while(anim->frames[startFrame].textureManifest != &manifest) + while(&anim->frame(startFrame).textureManifest() != &manifest) { startFrame++; } // Just animate the first in the sequence? - if(startFrame && (anim->flags & AGF_FIRST_ONLY)) + if(startFrame && (anim->flags() & AGF_FIRST_ONLY)) return; // Complete configuration of the first stage. - animFrame = &anim->frames[startFrame]; - st->tics = animFrame->tics + animFrame->randomTics; - if(animFrame->randomTics) + animFrame = &anim->frame(startFrame); + st->tics = animFrame->tics() + animFrame->randomTics(); + if(animFrame->randomTics()) { - st->variance = animFrame->randomTics / float( st->tics ); + st->variance = animFrame->randomTics() / float( st->tics ); } // Add further stages according to the animation group. startFrame++; - for(int i = 0; i < anim->count - 1; ++i) + for(int i = 0; i < anim->frameCount() - 1; ++i) { - int frame = de::wrap(startFrame + i, 0, anim->count); + int frame = de::wrap(startFrame + i, 0, anim->frameCount()); - animFrame = &anim->frames[frame]; - if(!animFrame->textureManifest) continue; - TextureManifest &frameManifest = *reinterpret_cast(animFrame->textureManifest); + animFrame = &anim->frame(frame); + TextureManifest &frameManifest = animFrame->textureManifest(); int layerIdx = DED_AddMaterialLayerStage(&mat->layers[0]); ded_material_layer_stage_t *st = &mat->layers[0].stages[layerIdx]; st->texture = reinterpret_cast(new de::Uri(frameManifest.composeUrn())); - st->tics = animFrame->tics + animFrame->randomTics; - if(animFrame->randomTics) + st->tics = animFrame->tics() + animFrame->randomTics(); + if(animFrame->randomTics()) { - st->variance = animFrame->randomTics / float( st->tics ); + st->variance = animFrame->randomTics() / float( st->tics ); } } } diff --git a/doomsday/client/src/render/r_main.cpp b/doomsday/client/src/render/r_main.cpp index dde150239d..62e3e54d9c 100644 --- a/doomsday/client/src/render/r_main.cpp +++ b/doomsday/client/src/render/r_main.cpp @@ -581,7 +581,6 @@ void R_Update() void R_Shutdown() { - R_ClearAnimGroups(); R_ShutdownSprites(); Models_Shutdown(); R_ShutdownSvgs(); diff --git a/doomsday/client/src/resource/animgroups.cpp b/doomsday/client/src/resource/animgroups.cpp index 0a6320f82a..ae7c9c9e75 100644 --- a/doomsday/client/src/resource/animgroups.cpp +++ b/doomsday/client/src/resource/animgroups.cpp @@ -1,7 +1,7 @@ -/** @file animgroups.cpp (Material) Animation groups +/** @file animgroup.cpp Material animation group. * - * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2005-2013 Daniel Swanson + * @authors Copyright © 2003-2013 Jaakko Keränen + * @authors Copyright © 2005-2013 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -18,127 +18,92 @@ * 02110-1301 USA */ -#include +#include "resource/animgroups.h" -#include "de_base.h" -#include "de_console.h" -#include "de_resource.h" -#include +#include +#include using namespace de; -static int numgroups; -static animgroup_t *groups; +AnimGroup::Frame::Frame(TextureManifest &textureManifest, ushort tics, ushort randomTics) + : _textureManifest(&textureManifest) + , _tics(tics) + , _randomTics(randomTics) +{} -static animgroup_t *getAnimGroup(int number) +TextureManifest &AnimGroup::Frame::textureManifest() const { - if(--number < 0 || number >= numgroups) return 0; - return &groups[number]; + return *_textureManifest; } -static bool isInAnimGroup(animgroup_t const &group, TextureManifest &manifest) +ushort AnimGroup::Frame::tics() const { - for(int i = 0; i < group.count; ++i) - { - if(group.frames[i].textureManifest == &manifest) - return true; - } - return false; + return _tics; } -void R_ClearAnimGroups() +ushort AnimGroup::Frame::randomTics() const { - if(numgroups <= 0) return; + return _randomTics; +} + +DENG2_PIMPL(AnimGroup) +{ + Frames frames; + int uniqueId; + int flags; ///< @ref animationGroupFlags + + Instance(Public *i) + : Base(i) + , uniqueId(0) + , flags(0) + {} - for(int i = 0; i < numgroups; ++i) + ~Instance() { - animgroup_t &grp = groups[i]; - if(grp.frames) Z_Free(grp.frames); + self.clearAllFrames(); } - Z_Free(groups); groups = 0; - numgroups = 0; -} +}; -animgroup_t const *R_ToAnimGroup(int animGroupNum) +AnimGroup::AnimGroup(int uniqueId, int flags) : d(new Instance(this)) { - LOG_AS("R_ToAnimGroup"); - animgroup_t *grp = getAnimGroup(animGroupNum); - if(!grp) LOG_DEBUG("Invalid group #%i, returning NULL.") << animGroupNum; - return grp; + d->uniqueId = uniqueId; + d->flags = flags; } -int R_AnimGroupCount() +void AnimGroup::clearAllFrames() { - return numgroups; + qDeleteAll(d->frames); + d->frames.clear(); } -#undef R_CreateAnimGroup -DENG_EXTERN_C int R_CreateAnimGroup(int flags) +int AnimGroup::id() const { - // Allocating one by one is inefficient but it doesn't really matter. - groups = (animgroup_t *) Z_Realloc(groups, sizeof(*groups) * ++numgroups, PU_APPSTATIC); - if(!groups) Con_Error("R_CreateAnimGroup: Failed on (re)allocation of %lu bytes enlarging AnimGroup list.", (unsigned long) sizeof(*groups) * numgroups); - - animgroup_t *group = &groups[numgroups-1]; - - // Init the new group. - std::memset(group, 0, sizeof *group); - group->id = numgroups; // 1-based index. - group->flags = flags & ~AGF_PRECACHE; - - return group->id; + return d->uniqueId; } -#undef R_AddAnimGroupFrame -DENG_EXTERN_C void R_AddAnimGroupFrame(int groupNum, uri_s const *textureUri, int tics, int randomTics) +int AnimGroup::flags() const { - LOG_AS("R_AddAnimGroupFrame"); - - if(!textureUri) return; - - animgroup_t *group = getAnimGroup(groupNum); - if(!group) - { - LOG_DEBUG("Unknown anim group #%i, ignoring.") << groupNum; - return; - } - - try - { - TextureManifest &manifest = App_Textures().find(reinterpret_cast(*textureUri)); - - // Allocate a new animframe. - group->frames = (animframe_t *) Z_Realloc(group->frames, sizeof(*group->frames) * ++group->count, PU_APPSTATIC); - if(!group->frames) Con_Error("R_AddAnimGroupFrame: Failed on (re)allocation of %lu bytes enlarging AnimFrame list for group #%i.", (unsigned long) sizeof(*group->frames) * group->count, groupNum); + return d->flags; +} - animframe_t *frame = &group->frames[group->count - 1]; - frame->textureManifest = &manifest; - frame->tics = tics; - frame->randomTics = randomTics; - } - catch(Textures::NotFoundError const &er) +bool AnimGroup::hasFrameFor(TextureManifest const &textureManifest) const +{ + foreach(Frame *frame, d->frames) { - // Log but otherwise ignore this error. - LOG_WARNING(er.asText() + ". Failed adding texture \"%s\" to group #%i, ignoring.") - << reinterpret_cast(*textureUri) << groupNum; + if(&frame->textureManifest() == &textureManifest) + return true; } + return false; } -boolean R_IsTextureInAnimGroup(uri_s const *textureUri, int groupNum) +AnimGroup::Frame &AnimGroup::newFrame(TextureManifest &textureManifest, + ushort tics, ushort randomTics) { - if(!textureUri) return false; - animgroup_t *group = getAnimGroup(groupNum); - if(!group) return false; + d->frames.append(new Frame(textureManifest, tics, randomTics)); + return *d->frames.last(); +} - try - { - TextureManifest &manifest = App_Textures().find(reinterpret_cast(*textureUri)); - return isInAnimGroup(*group, manifest); - } - catch(Textures::NotFoundError const &er) - { - // Log but otherwise ignore this error. - LOG_WARNING(er.asText() + ", ignoring."); - } - return false; +AnimGroup::Frames const &AnimGroup::allFrames() const +{ + return d->frames; } diff --git a/doomsday/client/src/resource/api_resource.cpp b/doomsday/client/src/resource/api_resource.cpp index 11234b2ae8..82e7cbcfb7 100644 --- a/doomsday/client/src/resource/api_resource.cpp +++ b/doomsday/client/src/resource/api_resource.cpp @@ -2,7 +2,7 @@ #include "de_base.h" #include "api_resource.h" #include "Textures" - +#include "resource/animgroups.h" #include "gl/gl_tex.h" // averagealpha_analysis_t, etc... #ifdef __CLIENT__ # include "render/r_draw.h" // Rend_PatchTextureSpec() @@ -36,13 +36,38 @@ DENG_EXTERN_C int Textures_UniqueId(Uri const *uri) return Textures_UniqueId2(uri, false); } -/* - * animgroups.cpp: - */ #undef R_CreateAnimGroup -DENG_EXTERN_C int R_CreateAnimGroup(int flags); +DENG_EXTERN_C int R_CreateAnimGroup(int flags) +{ + return App_ResourceSystem().newAnimGroup(flags & ~AGF_PRECACHE).id(); +} + #undef R_AddAnimGroupFrame -DENG_EXTERN_C void R_AddAnimGroupFrame(int animGroupNum, Uri const *texture, int tics, int randomTics); +DENG_EXTERN_C void R_AddAnimGroupFrame(int groupId, uri_s const *textureUri_, int tics, int randomTics) +{ + LOG_AS("R_AddAnimGroupFrame"); + + if(!textureUri_) return; + de::Uri const &textureUri = reinterpret_cast(*textureUri_); + + try + { + if(de::AnimGroup *group = App_ResourceSystem().animGroup(groupId)) + { + group->newFrame(App_Textures().find(textureUri), tics, randomTics); + } + else + { + LOG_DEBUG("Unknown anim group #%i, ignoring.") << groupId; + } + } + catch(de::Textures::NotFoundError const &er) + { + // Log but otherwise ignore this error. + LOG_WARNING(er.asText() + ". Failed adding texture \"%s\" to group #%i, ignoring.") + << textureUri << groupId; + } +} /* * colorpalettes.cpp: diff --git a/doomsday/client/src/resource/resourcesystem.cpp b/doomsday/client/src/resource/resourcesystem.cpp index 9f84ecb6e2..666b0869cd 100644 --- a/doomsday/client/src/resource/resourcesystem.cpp +++ b/doomsday/client/src/resource/resourcesystem.cpp @@ -78,8 +78,11 @@ static Texture *deriveTexture(TextureManifest &manifest) DENG2_PIMPL(ResourceSystem) { typedef QList ResourceClasses; - NullResourceClass nullResourceClass; ResourceClasses resClasses; + NullResourceClass nullResourceClass; + + typedef QList AnimGroups; + AnimGroups animGroups; QList patchNames; Textures textures; @@ -134,6 +137,7 @@ DENG2_PIMPL(ResourceSystem) ~Instance() { qDeleteAll(resClasses); + self.clearAllAnimGroups(); } #ifdef __CLIENT__ @@ -1169,6 +1173,37 @@ AbstractFont *ResourceSystem::createFontFromFile(de::Uri const &uri, #endif // __CLIENT__ +void ResourceSystem::clearAllAnimGroups() +{ + qDeleteAll(d->animGroups); + d->animGroups.clear(); +} + +int ResourceSystem::animGroupCount() +{ + return d->animGroups.count(); +} + +AnimGroup *ResourceSystem::animGroup(int index) +{ + LOG_AS("ResourceSystem::animGroup"); + if(index >= 0 && index < d->animGroups.count()) + { + return d->animGroups.at(index); + } + LOG_DEBUG("Invalid group #%i, returning NULL.") << index; + return 0; +} + +AnimGroup &ResourceSystem::newAnimGroup(int flags) +{ + LOG_AS("ResourceSystem"); + int const uniqueId = d->animGroups.count() + 1; // 1-based index. + // Allocating one by one is inefficient but it doesn't really matter. + d->animGroups.append(new AnimGroup(uniqueId, flags)); + return *d->animGroups.last(); +} + void ResourceSystem::consoleRegister() // static { Textures::consoleRegister();