Skip to content

Commit

Permalink
Refactor|Resources: Began remodeling Sprite resource management
Browse files Browse the repository at this point in the history
  • Loading branch information
danij-deng committed Nov 18, 2013
1 parent 8e40259 commit 22cb983
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 305 deletions.
1 change: 0 additions & 1 deletion doomsday/api/dd_share.h
Expand Up @@ -786,7 +786,6 @@ typedef struct {
int flip;
RectRaw geometry;
float texCoord[2]; // Prepared texture coordinates.
int numFrames; // Number of frames the sprite has.
} spriteinfo_t;

/**
Expand Down
150 changes: 70 additions & 80 deletions doomsday/client/include/resource/sprites.h
@@ -1,4 +1,4 @@
/** @file sprites.h Logical Sprite Management.
/** @file sprites.h Sprite resource management.
*
* @authors Copyright © 2003-2013 Jaakko Keränen <jaakko.keranen@iki.fi>
* @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
Expand All @@ -24,105 +24,95 @@
#define DENG_RESOURCE_SPRITES_H

#include <de/libdeng1.h>
#include <de/String>
#include <QList>

class Material;
#ifdef __CLIENT__
class Lumobj;
#endif

/**
* Sprites are patches with a special naming convention so they can be
* recognized by R_InitSprites. The sprite and frame specified by a
* mobj is range checked at run time.
* A sprite is a map entity visualization which approximates a 3D object using
* a set of 2D images. Each image represents a view of the entity, from a specific
* view-angle. The illusion of 3D is successfully achieved through matching the
* relative angle to the viewer with an image which depicts the entity from this
* angle.
*
* A sprite is a patch_t that is assumed to represent a three dimensional
* object and may have multiple rotations pre drawn. Horizontal flipping
* is used to save space. Some sprites will only have one picture used
* for all views.
*/

#define SPRITEFRAME_MAX_ANGLES 8

struct spriteframe_t
{
byte rotate; // 0= no rotations, 1= only front, 2= more...
Material *mats[8]; // Material to use for view angles 0-7
byte flip[8]; // Flip (1 = flip) to use for view angles 0-7
};

/**
* Select an appropriate material for visualizing the sprite given a mobj's
* angle and relative angle with the viewer (the 'eye').
*
* @param sprFrame spriteframe_t instance.
* @param mobjAngle Angle of the mobj in the map coordinate space.
* @param angleToEye Relative angle of the mobj from the view position.
* @param noRotation @c true= Ignore rotations and always use the "front".
*
* Return values:
* @param flipX @c true= chosen material should be flipped on the X axis.
* @param flipY @c true= chosen material should be flipped on the Y axis.
*
* @return The chosen material otherwise @c 0.
*/
Material *SpriteFrame_Material(spriteframe_t &sprFrame, angle_t mobjAngle,
angle_t angleToEye, bool noRotation = false, bool *flipX = 0, bool *flipY = 0);

/**
* Returns the material attributed to the specified rotation.
*
* @param sprFrame spriteframe_t instance.
* @param rotation Rotation index/identifier to lookup the material for. The
* valid range is [0...SPRITEFRAME_MAX_ANGLES)
*
* Return values:
* @param flipX @c true= chosen material should be flipped on the X axis.
* @param flipY @c true= chosen material should be flipped on the Y axis.
* Sprite animation sequences are defined elsewhere.
*
* @return The attributed material otherwise @c 0.
* @ingroup resource
*/
Material *SpriteFrame_Material(spriteframe_t &sprFrame, int rotation = 0,
bool *flipX = 0, bool *flipY = 0);

struct spritedef_t
class Sprite
{
char name[5];
int numFrames;
spriteframe_t *spriteFrames;
};

/**
* Lookup a sprite frame by unique @a frame index.
*/
spriteframe_t *SpriteDef_Frame(spritedef_t const &sprDef, int frame);
public:
static int const max_angles = 8;

public:
byte _rotate; ///< 0= no rotations, 1= only front, 2= more...
Material *_mats[8]; ///< Material to use for view angles 0-7
byte _flip[8]; ///< Flip (1 = flip) to use for view angles 0-7

public:
Sprite();

/**
* Select an appropriate material for visualizing the sprite given a mobj's
* angle and relative angle with the viewer (the 'eye').
*
* @param mobjAngle Angle of the mobj in the map coordinate space.
* @param angleToEye Relative angle of the mobj from the view position.
* @param noRotation @c true= Ignore rotations and always use the "front".
*
* Return values:
* @param flipX @c true= chosen material should be flipped on the X axis.
* @param flipY @c true= chosen material should be flipped on the Y axis.
*
* @return The chosen material otherwise @c 0.
*/
Material *material(angle_t mobjAngle, angle_t angleToEye, bool noRotation = false,
bool *flipX = 0, bool *flipY = 0) const;

/**
* Returns the material attributed to the specified rotation.
*
* @param rotation Rotation index/identifier to lookup the material for. The
* valid range is [0...SPRITEFRAME_MAX_ANGLES)
*
* Return values:
* @param flipX @c true= chosen material should be flipped on the X axis.
* @param flipY @c true= chosen material should be flipped on the Y axis.
*
* @return The attributed material otherwise @c 0.
*/
Material *material(int rotation = 0, bool *flipX = 0, bool *flipY = 0) const;

#ifdef __CLIENT__

/**
* Produce a luminous object from the sprite configuration. The properties of
* any resultant lumobj are configured in "sprite-local" space. This means that
* it will positioned relative to the center of the sprite and must be further
* configured before adding to the map (i.e., translated to the origin in map
* space).
*
* @param sprDef SpriteDef instance.
* @param frame Animation frame to produce a lumobj for.
*
* @return Newly generated lumobj otherwise @c 0.
*/
Lumobj *SpriteDef_GenerateLumobj(spritedef_t const &sprDef, int frame);

/**
* Produce a luminous object from the sprite configuration. The properties
* of any resultant lumobj are configured in "sprite-local" space. This means
* that it will positioned relative to the center of the sprite and must be
* further configured before adding to the map (i.e., translated to the origin
* in map space).
*
* @return Newly generated lumobj otherwise @c 0.
*/
Lumobj *generateLumobj() const;
#endif
};

DENG_EXTERN_C spritedef_t *sprites;
DENG_EXTERN_C int numSprites;
typedef QList<Sprite *> SpriteSet;

void R_InitSprites();

void R_ShutdownSprites();

spritedef_t *R_SpriteDef(int sprite);
int R_SpriteCount();

Sprite *R_SpritePtr(int spriteId, int frame);

Sprite &R_Sprite(int spriteId, int frame);

Material *R_MaterialForSprite(int sprite, int frame);
SpriteSet &R_SpriteSet(int spriteId);

#endif // DENG_RESOURCE_SPRITES_H
62 changes: 36 additions & 26 deletions doomsday/client/src/render/r_main.cpp
Expand Up @@ -1516,39 +1516,53 @@ void R_ViewerClipLumobjBySight(Lumobj *lum, BspLeaf *bspLeaf)
}
}

static state_t *toState(int index)
{
if(index >= 0 && index < defs.count.states.num)
{
return states + index;
}
return 0; // Not found.
}

static int findSpriteOwner(thinker_t *th, void *context)
{ mobj_t *mo = (mobj_t *) th;
spritedef_t *sprDef = (spritedef_t *) context;
{
mobj_t *mo = reinterpret_cast<mobj_t *>(th);
int const sprite = *static_cast<int *>(context);

if(mo->type >= 0 && mo->type < defs.count.mobjs.num)
{
/// @todo optimize: traverses the entire state list!
for(int i = 0; i < defs.count.states.num; ++i)
{
if(stateOwners[i] != &mobjInfo[mo->type]) continue;
if(stateOwners[i] != &mobjInfo[mo->type])
{
continue;
}

state_t *state = toState(i);
DENG2_ASSERT(state != 0);

if(&sprites[states[i].sprite] == sprDef)
if(state->sprite == sprite)
{
return true; // Found one.
}
}
}

return false; // Continue iteration.
}

static void cacheSpritesForState(int stateIndex, MaterialVariantSpec const &spec)
static void cacheSpritesForState(int stateIdx, MaterialVariantSpec const &spec)
{
if(stateIndex < 0 || stateIndex >= defs.count.states.num) return;
state_t *state = toState(stateIdx);
DENG2_ASSERT(state != 0);

state_t *state = &states[stateIndex];
spritedef_t *sprDef = &sprites[state->sprite];

for(int i = 0; i < sprDef->numFrames; ++i)
SpriteSet const &sprites = R_SpriteSet(state->sprite);
foreach(Sprite *sprite, sprites)
for(int i = 0; i < Sprite::max_angles; ++i)
{
spriteframe_t *sprFrame = &sprDef->spriteFrames[i];
for(int k = 0; k < 8; ++k)
{
App_Materials().cache(*sprFrame->mats[k], spec);
}
App_Materials().cache(*sprite->_mats[i], spec);
}
}

Expand Down Expand Up @@ -1623,24 +1637,20 @@ void Rend_CacheForMap()
{
MaterialVariantSpec const &spec = Rend_SpriteMaterialSpec();

for(int i = 0; i < numSprites; ++i)
for(int i = 0; i < R_SpriteCount(); ++i)
{
spritedef_t *sprDef = &sprites[i];

if(map.thinkers().iterate(reinterpret_cast<thinkfunc_t>(gx.MobjThinker),
0x1/*mobjs are public*/,
findSpriteOwner, sprDef))
findSpriteOwner, &i))
{
// This sprite is used by some state of at least one mobj.

// Precache all the frames.
for(int k = 0; k < sprDef->numFrames; ++k)
// Precache all angles for all frames.
SpriteSet const & sprites = R_SpriteSet(i);
foreach(Sprite *sprite, sprites)
for(int k = 0; k < Sprite::max_angles; ++k)
{
spriteframe_t *sprFrame = &sprDef->spriteFrames[k];
for(int m = 0; m < 8; ++m)
{
App_Materials().cache(*sprFrame->mats[m], spec);
}
App_Materials().cache(*sprite->_mats[k], spec);
}
}
}
Expand Down
25 changes: 10 additions & 15 deletions doomsday/client/src/render/r_things.cpp
Expand Up @@ -114,15 +114,6 @@ static Vector3d mobjOriginSmoothed(mobj_t *mo)
return moPos;
}

static inline spriteframe_t *spriteFrame(int sprite, int frame)
{
if(spritedef_t *sprDef = R_SpriteDef(sprite))
{
return SpriteDef_Frame(*sprDef, frame);
}
return 0;
}

struct findmobjzoriginworker_params_t
{
vissprite_t *vis;
Expand Down Expand Up @@ -182,8 +173,8 @@ void R_ProjectSprite(mobj_t *mo)
// ...in an invalid state?
if(!mo->state || mo->state == states) return;
// ...no sprite frame is defined?
spriteframe_t *sprFrame = spriteFrame(mo->sprite, mo->frame);
if(!sprFrame) return;
Sprite *sprite = R_SpritePtr(mo->sprite, mo->frame);
if(!sprite) return;
// ...fully transparent?
float const alpha = Mobj_Alpha(mo);
if(alpha <= 0) return;
Expand Down Expand Up @@ -216,18 +207,22 @@ void R_ProjectSprite(mobj_t *mo)
// Decide which material to use according to the sprite's angle and position
// relative to that of the viewer.
bool matFlipS, matFlipT;
Material *mat = SpriteFrame_Material(*sprFrame, mo->angle,
R_ViewPointToAngle(mo->origin),
mf != 0, &matFlipS, &matFlipT);
Material *mat = sprite->material(mo->angle, R_ViewPointToAngle(mo->origin),
mf != 0, &matFlipS, &matFlipT);
if(!mat) return;

// A valid sprite texture in the "Sprites" scheme is required.
MaterialSnapshot const &ms = mat->prepare(Rend_SpriteMaterialSpec(mo->tclass, mo->tmap));
if(!ms.hasTexture(MTU_PRIMARY))
{
return;
}

Texture &tex = ms.texture(MTU_PRIMARY).generalCase();
if(tex.manifest().schemeName().compareWithoutCase("Sprites"))
{
return;
}

bool const fullbright = ((mo->state->flags & STF_FULLBRIGHT) != 0 || levelFullBright);
// Align to the view plane? (Means scaling down Z with models)
Expand Down Expand Up @@ -433,7 +428,7 @@ void R_ProjectSprite(mobj_t *mo)
// Do we need to project a flare source too?
if(mo->lumIdx != Lumobj::NoIndex)
{
Material *mat = SpriteFrame_Material(*sprFrame, mo->angle, R_ViewPointToAngle(mo->origin));
Material *mat = sprite->material(mo->angle, R_ViewPointToAngle(mo->origin));
if(!mat) return;

// A valid sprite texture in the "Sprites" scheme is required.
Expand Down
3 changes: 2 additions & 1 deletion doomsday/client/src/render/rend_main.cpp
Expand Up @@ -2669,7 +2669,8 @@ static int projectSpriteWorker(mobj_t &mo, void * /*context*/)

if(cluster.visCeiling().surface().hasSkyMaskedMaterial())
{
if(Material *material = R_MaterialForSprite(mo.sprite, mo.frame))
Sprite *sprite = R_SpritePtr(mo.sprite, mo.frame);
if(Material *material = sprite->material())
{
if(!(mo.dPlayer && (mo.dPlayer->flags & DDPF_CAMERA))
&& mo.origin[VZ] <= cluster.visCeiling().heightSmoothed()
Expand Down

0 comments on commit 22cb983

Please sign in to comment.