diff --git a/doomsday/client/include/audio/s_environ.h b/doomsday/client/include/audio/s_environ.h index 39721798af..097b82f482 100644 --- a/doomsday/client/include/audio/s_environ.h +++ b/doomsday/client/include/audio/s_environ.h @@ -1,4 +1,4 @@ -/** @file s_environ.h Sound environment. +/** @file s_environ.h Audio environment management. * @ingroup audio * * @authors Copyright © 2003-2013 Jaakko Keränen @@ -24,24 +24,29 @@ #include "api_uri.h" -namespace de { -class Map; -} - class Sector; -typedef enum audioenvironmentclass_e { - AEC_UNKNOWN = -1, - AEC_FIRST = 0, - AEC_METAL = AEC_FIRST, - AEC_ROCK, - AEC_WOOD, - AEC_CLOTH, - NUM_AUDIO_ENVIRONMENT_CLASSES -} AudioEnvironmentClass; +enum AudioEnvironmentId +{ + AE_NONE = -1, + AE_FIRST = 0, + AE_METAL = AE_FIRST, + AE_ROCK, + AE_WOOD, + AE_CLOTH, + NUM_AUDIO_ENVIRONMENTS +}; -#define VALID_AUDIO_ENVIRONMENT_CLASS(val) (\ - (int)(val) >= AEC_FIRST && (int)(val) < NUM_AUDIO_ENVIRONMENT_CLASSES) +/** + * Defines the properties of an audio environment. + */ +struct AudioEnvironment +{ + char const name[9]; ///< Environment type name. + int volumeMul; + int decayMul; + int dampingMul; +}; /** * Requests re-calculation of the reverb properties of the given sector. Should @@ -68,16 +73,22 @@ void S_UpdateReverbForSector(Sector *sec); /** * Must be called when the map changes. */ -void S_ResetReverb(void); +void S_ResetReverb(); + +/** + * Lookup the symbolic name of the identified audio environment. + */ +char const *S_AudioEnvironmentName(AudioEnvironmentId id); /** - * @return Environment class name for identifier @a mclass. + * Lookup the identified audio environment. */ -char const *S_AudioEnvironmentName(AudioEnvironmentClass environment); +AudioEnvironment const &S_AudioEnvironment(AudioEnvironmentId id); /** - * @return Environment class associated with material @a uri else @c AEC_UNKNOWN. + * Lookup the audio environment associated with material @a uri. If no environment + * is defined then @c AE_NONE is returned. */ -AudioEnvironmentClass S_AudioEnvironmentForMaterial(Uri const *uri); +AudioEnvironmentId S_AudioEnvironmentId(Uri const *uri); #endif // DENG_SOUND_ENVIRON diff --git a/doomsday/client/include/resource/material.h b/doomsday/client/include/resource/material.h index 68e368fbf8..42c72bebd9 100644 --- a/doomsday/client/include/resource/material.h +++ b/doomsday/client/include/resource/material.h @@ -788,19 +788,6 @@ class Material : public de::MapElement, */ void setFlags(Flags flagsToChange, de::FlagOp operation = de::SetFlags); - /** - * Returns the environment audio class for the material. - */ - audioenvironmentclass_e audioEnvironment() const; - - /** - * Change the material's environment audio class. - * @param newEnvironment New environment to apply. - * - * @todo If attached to a map Surface update accordingly! - */ - void setAudioEnvironment(audioenvironmentclass_e newEnvironment); - /** * Add a new layer to the end of the material's layer stack. Ownership of the * layer is @em not transferred to the caller. @@ -876,6 +863,17 @@ class Material : public de::MapElement, ShineLayer const &shineLayer() const; #ifdef __CLIENT__ + /** + * Returns the identifier of the audio environment for the material. + */ + AudioEnvironmentId audioEnvironment() const; + + /** + * Change the audio environment for the material. + * + * @param newEnvironment New audio environment to apply. + */ + void setAudioEnvironment(AudioEnvironmentId newEnvironment); /** * Returns the number of material (light) decorations. diff --git a/doomsday/client/include/world/bspleaf.h b/doomsday/client/include/world/bspleaf.h index 17ebf7d2ad..b6b2fff2dc 100644 --- a/doomsday/client/include/world/bspleaf.h +++ b/doomsday/client/include/world/bspleaf.h @@ -287,6 +287,11 @@ class BspLeaf : public de::MapElement */ void applyBiasDigest(BiasDigest &changes); + /** + * Recalculate the environmental audio characteristics for the BSP leaf. + */ + bool updateReverb(); + /** * Clear the list of fake radio shadow line sides for the BSP leaf. */ diff --git a/doomsday/client/include/world/sector.h b/doomsday/client/include/world/sector.h index 1a0ba4f936..499441f776 100644 --- a/doomsday/client/include/world/sector.h +++ b/doomsday/client/include/world/sector.h @@ -108,9 +108,6 @@ class Sector : public de::MapElement struct mobj_s *_mobjList; #ifdef __CLIENT__ - /// @ref sectorFrameFlags - int _frameFlags; - /// Final environmental audio characteristics. AudioEnvironmentFactors _reverb; #endif diff --git a/doomsday/client/src/audio/s_environ.cpp b/doomsday/client/src/audio/s_environ.cpp index a7babc702f..7b1b8b5328 100644 --- a/doomsday/client/src/audio/s_environ.cpp +++ b/doomsday/client/src/audio/s_environ.cpp @@ -21,34 +21,22 @@ * 02110-1301 USA */ -#include -#include #include -#include - #include "de_base.h" #include "de_audio.h" #include "de_play.h" #include "de_resource.h" -#include "de_system.h" - -#include "Face" #include "BspLeaf" +#include "Sector" #include "audio/s_environ.h" using namespace de; -typedef struct { - char const name[9]; ///< Environment type name. - int volumeMul; - int decayMul; - int dampingMul; -} audioenvinfo_t; - -static audioenvinfo_t envInfo[NUM_AUDIO_ENVIRONMENT_CLASSES] = { +static AudioEnvironment envInfo[1 + NUM_AUDIO_ENVIRONMENTS] = { + {"", 0, 0, 0}, {"Metal", 255, 255, 25}, {"Rock", 200, 160, 100}, {"Wood", 80, 50, 200}, @@ -58,14 +46,19 @@ static audioenvinfo_t envInfo[NUM_AUDIO_ENVIRONMENT_CLASSES] = { typedef std::set ReverbUpdateRequested; ReverbUpdateRequested reverbUpdateRequested; -char const *S_AudioEnvironmentName(AudioEnvironmentClass env) +char const *S_AudioEnvironmentName(AudioEnvironmentId id) +{ + DENG_ASSERT(id >= AE_NONE && id < NUM_AUDIO_ENVIRONMENTS); + return envInfo[1 + int(id)].name; +} + +AudioEnvironment const &S_AudioEnvironment(AudioEnvironmentId id) { - if(VALID_AUDIO_ENVIRONMENT_CLASS(env)) - return envInfo[env - AEC_FIRST].name; - return ""; + DENG_ASSERT(id >= AE_NONE && id < NUM_AUDIO_ENVIRONMENTS); + return envInfo[1 + int(id)]; } -AudioEnvironmentClass S_AudioEnvironmentForMaterial(uri_s const *uri) +AudioEnvironmentId S_AudioEnvironmentId(uri_s const *uri) { if(uri) { @@ -77,122 +70,18 @@ AudioEnvironmentClass S_AudioEnvironmentForMaterial(uri_s const *uri) uri_s *ref = env->materials[k]; if(!ref || !Uri_Equality(ref, uri)) continue; - // See if we recognise the material name. - for(int l = 0; l < NUM_AUDIO_ENVIRONMENT_CLASSES; ++l) + // Is this a known environment? + for(int m = 0; m < NUM_AUDIO_ENVIRONMENTS; ++m) { - if(!stricmp(env->id, envInfo[l].name)) - return AudioEnvironmentClass(AEC_FIRST + l); + AudioEnvironment const &envInfo = S_AudioEnvironment(AudioEnvironmentId(m)); + if(!stricmp(env->id, envInfo.name)) + return AudioEnvironmentId(m); } - return AEC_UNKNOWN; + return AE_NONE; } } } - return AEC_UNKNOWN; -} - -static void accumReverbForWallSections(HEdge const *hedge, - float envSpaceAccum[NUM_AUDIO_ENVIRONMENT_CLASSES], float &total) -{ - // Edges with no map line segment implicitly have no surfaces. - if(!hedge || !hedge->mapElement()) - return; - - Line::Side::Segment *seg = hedge->mapElement()->as(); - if(!seg->lineSide().hasSections() || !seg->lineSide().middle().hasMaterial()) - return; - - Material &material = seg->lineSide().middle().material(); - AudioEnvironmentClass env = material.audioEnvironment(); - if(!(env >= 0 && env < NUM_AUDIO_ENVIRONMENT_CLASSES)) - env = AEC_WOOD; // Assume it's wood if unknown. - - total += seg->length(); - - envSpaceAccum[env] += seg->length(); -} - -static boolean calcBspLeafReverb(BspLeaf *bspLeaf) -{ - DENG2_ASSERT(bspLeaf); - - if(!bspLeaf->hasSector() || bspLeaf->isDegenerate() || isDedicated) - { - bspLeaf->_reverb[SRD_SPACE] = bspLeaf->_reverb[SRD_VOLUME] = - bspLeaf->_reverb[SRD_DECAY] = bspLeaf->_reverb[SRD_DAMPING] = 0; - return false; - } - - float envSpaceAccum[NUM_AUDIO_ENVIRONMENT_CLASSES]; - std::memset(&envSpaceAccum, 0, sizeof(envSpaceAccum)); - - // Space is the rough volume of the BSP leaf (bounding box). - bspLeaf->_reverb[SRD_SPACE] = - (int) (bspLeaf->sector().ceiling().height() - bspLeaf->sector().floor().height()) * - (bspLeaf->poly().aaBox().maxX - bspLeaf->poly().aaBox().minX) * - (bspLeaf->poly().aaBox().maxY - bspLeaf->poly().aaBox().minY); - - float total = 0; - - // The other reverb properties can be found out by taking a look at the - // materials of all surfaces in the BSP leaf. - HEdge *base = bspLeaf->poly().hedge(); - HEdge *hedge = base; - do - { - accumReverbForWallSections(hedge, envSpaceAccum, total); - } while((hedge = &hedge->next()) != base); - - foreach(Mesh *mesh, bspLeaf->extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) - { - accumReverbForWallSections(hedge, envSpaceAccum, total); - } - - if(!total) - { - // Huh? - bspLeaf->_reverb[SRD_VOLUME] = bspLeaf->_reverb[SRD_DECAY] = - bspLeaf->_reverb[SRD_DAMPING] = 0; - return false; - } - - // Average the results. - uint i, v; - for(i = 0; i < NUM_AUDIO_ENVIRONMENT_CLASSES; ++i) - { - envSpaceAccum[i] /= total; - } - - // Volume. - for(i = 0, v = 0; i < NUM_AUDIO_ENVIRONMENT_CLASSES; ++i) - { - v += envSpaceAccum[i] * envInfo[i].volumeMul; - } - if(v > 255) v = 255; - bspLeaf->_reverb[SRD_VOLUME] = v; - - // Decay time. - for(i = 0, v = 0; i < NUM_AUDIO_ENVIRONMENT_CLASSES; ++i) - { - v += envSpaceAccum[i] * envInfo[i].decayMul; - } - if(v > 255) v = 255; - bspLeaf->_reverb[SRD_DECAY] = v; - - // High frequency damping. - for(i = 0, v = 0; i < NUM_AUDIO_ENVIRONMENT_CLASSES; ++i) - { - v += envSpaceAccum[i] * envInfo[i].dampingMul; - } - if(v > 255) v = 255; - bspLeaf->_reverb[SRD_DAMPING] = v; - - /* DEBUG_Message(("bspLeaf %04i: vol:%3i sp:%3i dec:%3i dam:%3i\n", - bspLeaf->indexInMap(), bspLeaf->reverb[SRD_VOLUME], - bspLeaf->reverb[SRD_SPACE], bspLeaf->reverb[SRD_DECAY], - bspLeaf->reverb[SRD_DAMPING])); */ - - return true; + return AE_NONE; } static void calculateSectorReverb(Sector *sec) @@ -206,7 +95,7 @@ static void calculateSectorReverb(Sector *sec) foreach(BspLeaf *bspLeaf, sec->reverbBspLeafs()) { - if(calcBspLeafReverb(bspLeaf)) + if(bspLeaf->updateReverb()) { sec->_reverb[SRD_SPACE] += bspLeaf->_reverb[SRD_SPACE]; diff --git a/doomsday/client/src/def_main.cpp b/doomsday/client/src/def_main.cpp index e3d0250455..342393e944 100644 --- a/doomsday/client/src/def_main.cpp +++ b/doomsday/client/src/def_main.cpp @@ -1390,7 +1390,9 @@ static void interpretMaterialDef(ded_material_t const &def) material.setFlags(translateMaterialDefFlags(def.flags)); material.setDimensions(Vector2i(def.width, def.height)); - material.setAudioEnvironment(S_AudioEnvironmentForMaterial(def.uri)); +#ifdef __CLIENT__ + material.setAudioEnvironment(S_AudioEnvironmentId(def.uri)); +#endif rebuildMaterialLayers(material, def); #ifdef __CLIENT__ diff --git a/doomsday/client/src/resource/material.cpp b/doomsday/client/src/resource/material.cpp index 1f23f2b59c..4cd6d8b225 100644 --- a/doomsday/client/src/resource/material.cpp +++ b/doomsday/client/src/resource/material.cpp @@ -328,9 +328,6 @@ DENG2_PIMPL(Material) Material::Variants variants; #endif - /// Environment audio class. - AudioEnvironmentClass envClass; - /// World dimensions in map coordinate space units. Vector2i dimensions; @@ -343,6 +340,9 @@ DENG2_PIMPL(Material) Material::ShineLayer *shineLayer; #ifdef __CLIENT__ + /// Audio environment. + AudioEnvironmentId audioEnvironment; + /// Decorations (will be projected into the map relative to a surface). Material::Decorations decorations; #endif @@ -356,11 +356,13 @@ DENG2_PIMPL(Material) #ifdef __CLIENT__ animationsAreDirty(true), #endif - envClass(AEC_UNKNOWN), flags(0), detailLayer(0), shineLayer(0), valid(true) +#ifdef __CLIENT__ + ,audioEnvironment(AE_NONE) +#endif {} ~Instance() @@ -550,17 +552,6 @@ bool Material::hasGlow() const return false; } -AudioEnvironmentClass Material::audioEnvironment() const -{ - if(isDrawable()) return d->envClass; - return AEC_UNKNOWN; -} - -void Material::setAudioEnvironment(AudioEnvironmentClass envClass) -{ - d->envClass = envClass; -} - void Material::clearLayers() { d->maybeCancelTextureDimensionsChangeNotification(); @@ -680,6 +671,17 @@ void Material::textureBeingDeleted(de::Texture const &texture) #ifdef __CLIENT__ +AudioEnvironmentId Material::audioEnvironment() const +{ + if(isDrawable()) return d->audioEnvironment; + return AE_NONE; +} + +void Material::setAudioEnvironment(AudioEnvironmentId audioEnvironment) +{ + d->audioEnvironment = audioEnvironment; +} + void Material::addDecoration(Material::Decoration &decor) { if(d->decorations.contains(&decor)) return; @@ -812,11 +814,11 @@ String Material::description() const String Material::synopsis() const { - String str = String("Drawable:%1 EnvClass:\"%2\"") - .arg(isDrawable()? "yes" : "no") - .arg(audioEnvironment() == AEC_UNKNOWN? "N/A" : S_AudioEnvironmentName(audioEnvironment())); + String str = String("Drawable:%1").arg(isDrawable()? "yes" : "no"); #ifdef __CLIENT__ - str += String(" Decorated:%1").arg(isDecorated()? "yes" : "no"); + str += String(" EnvClass:\"%2\" Decorated:%1") + .arg(audioEnvironment() == AE_NONE? "N/A" : S_AudioEnvironmentName(audioEnvironment())) + .arg(isDecorated()? "yes" : "no"); #endif str += String("\nDetailed:%1 Glowing:%2 Shiny:%3 SkyMasked:%4") .arg(isDetailed() ? "yes" : "no") diff --git a/doomsday/client/src/world/bspleaf.cpp b/doomsday/client/src/world/bspleaf.cpp index d3e7587a5f..6b9b3093ea 100644 --- a/doomsday/client/src/world/bspleaf.cpp +++ b/doomsday/client/src/world/bspleaf.cpp @@ -553,6 +553,97 @@ void BspLeaf::lightBiasPoly(int group, Vector3f const *posCoords, Vector4f *colo geomGroup->biasTracker.markIllumUpdateCompleted(); } +static void accumReverbForWallSections(HEdge const *hedge, + float envSpaceAccum[NUM_AUDIO_ENVIRONMENTS], float &total) +{ + // Edges with no map line segment implicitly have no surfaces. + if(!hedge || !hedge->mapElement()) + return; + + Line::Side::Segment *seg = hedge->mapElement()->as(); + if(!seg->lineSide().hasSections() || !seg->lineSide().middle().hasMaterial()) + return; + + Material &material = seg->lineSide().middle().material(); + AudioEnvironmentId env = material.audioEnvironment(); + if(!(env >= 0 && env < NUM_AUDIO_ENVIRONMENTS)) + env = AE_WOOD; // Assume it's wood if unknown. + + total += seg->length(); + + envSpaceAccum[env] += seg->length(); +} + +bool BspLeaf::updateReverb() +{ + if(!d->sector || !d->poly) + { + _reverb[SRD_SPACE] = _reverb[SRD_VOLUME] = + _reverb[SRD_DECAY] = _reverb[SRD_DAMPING] = 0; + return false; + } + + float envSpaceAccum[NUM_AUDIO_ENVIRONMENTS]; + std::memset(&envSpaceAccum, 0, sizeof(envSpaceAccum)); + + // Space is the rough volume of the BSP leaf (bounding box). + AABoxd const &aaBox = d->poly->aaBox(); + _reverb[SRD_SPACE] = + int(d->sector->ceiling().height() - d->sector->floor().height()) * + (aaBox.maxX - aaBox.minX) * + (aaBox.maxY - aaBox.minY); + + float total = 0; + + // The other reverb properties can be found out by taking a look at the + // materials of all surfaces in the BSP leaf. + HEdge *base = d->poly->hedge(); + HEdge *hedge = base; + do + { + accumReverbForWallSections(hedge, envSpaceAccum, total); + } while((hedge = &hedge->next()) != base); + + foreach(Mesh *mesh, d->extraMeshes) + foreach(HEdge *hedge, mesh->hedges()) + { + accumReverbForWallSections(hedge, envSpaceAccum, total); + } + + if(!total) + { + // Huh? + _reverb[SRD_VOLUME] = _reverb[SRD_DECAY] = _reverb[SRD_DAMPING] = 0; + return false; + } + + // Average the results. + for(int i = AE_FIRST; i < NUM_AUDIO_ENVIRONMENTS; ++i) + { + envSpaceAccum[i] /= total; + } + + // Accumulate and clamp the final characteristics + int accum[NUM_REVERB_DATA]; zap(accum); + for(int i = int(AE_FIRST); i < NUM_AUDIO_ENVIRONMENTS; ++i) + { + AudioEnvironment const &envInfo = S_AudioEnvironment(AudioEnvironmentId(i)); + // Volume. + accum[SRD_VOLUME] += envSpaceAccum[i] * envInfo.volumeMul; + + // Decay time. + accum[SRD_DECAY] += envSpaceAccum[i] * envInfo.decayMul; + + // High frequency damping. + accum[SRD_DAMPING] += envSpaceAccum[i] * envInfo.dampingMul; + } + _reverb[SRD_VOLUME] = de::min(accum[SRD_VOLUME], 255); + _reverb[SRD_DECAY] = de::min(accum[SRD_DECAY], 255); + _reverb[SRD_DAMPING] = de::min(accum[SRD_DAMPING], 255); + + return true; +} + void BspLeaf::clearShadowLines() { d->shadowLines.clear(); diff --git a/doomsday/client/src/world/sector.cpp b/doomsday/client/src/world/sector.cpp index 35c1b56986..65cd5152c9 100644 --- a/doomsday/client/src/world/sector.cpp +++ b/doomsday/client/src/world/sector.cpp @@ -212,7 +212,6 @@ Sector::Sector(float lightLevel, Vector3f const &lightColor) { _mobjList = 0; #ifdef __CLIENT__ - _frameFlags = 0; zap(_reverb); #endif } diff --git a/doomsday/server/src/server_dummies.cpp b/doomsday/server/src/server_dummies.cpp index 13a1879902..c8baec7540 100644 --- a/doomsday/server/src/server_dummies.cpp +++ b/doomsday/server/src/server_dummies.cpp @@ -703,22 +703,3 @@ DENG_EXTERN_C void R_ProjectSprite(struct mobj_s *mo) { DENG_UNUSED(mo); } - -/* -DENG_EXTERN_C void Rend_ApplyLightAdaptation(float* val) -{ - DENG_UNUSED(val); -} -*/ - -char const *S_AudioEnvironmentName(AudioEnvironmentClass env) -{ - DENG_UNUSED(env); - return ""; -} - -AudioEnvironmentClass S_AudioEnvironmentForMaterial(Uri const *uri) -{ - DENG_UNUSED(uri); - return AEC_UNKNOWN; -}