tge/engine/game/audioEmitter.cc
2017-04-17 06:17:10 -06:00

1072 lines
35 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "game/audioEmitter.h"
#include "game/ambientAudioManager.h"
#include "console/consoleTypes.h"
#include "editor/editor.h"
#include "dgl/dgl.h"
#include "sceneGraph/sceneState.h"
#include "game/gameConnection.h"
//------------------------------------------------------------------------------
static MRandomLCG sgRandom(0xdeadbeef);
#define UPDATE_BUMP_MS 50
extern ALuint alxGetWaveLen(ALuint buffer);
extern ALuint alxFindSource(AUDIOHANDLE handle);
IMPLEMENT_CO_NETOBJECT_V1(AudioEmitter);
//------------------------------------------------------------------------------
class AudioEmitterLoopEvent : public SimEvent
{
public:
void process(SimObject * object) {
((AudioEmitter*)object)->processLoopEvent();
}
};
//------------------------------------------------------------------------------
AudioEmitter::AudioEmitter(bool client)
{
// ::packData depends on these values... make sure it is updated with changes
mTypeMask |= MarkerObjectType;
if(client)
mNetFlags = 0;
else
mNetFlags.set(Ghostable|ScopeAlways);
mAudioHandle = NULL_AUDIOHANDLE;
mAudioProfileId = 0;
mAudioDescriptionId = 0;
mLoopCount = smDefaultDescription.mLoopCount;
mEventID = 0;
mOwnedByClient = false;
// field defaults
mAudioProfile = 0;
mAudioDescription = 0;
mFilename = 0;
mUseProfileDescription = false;
mOutsideAmbient = true;
mDescription = smDefaultDescription;
mEnableVisualFeedback = true;
mAnimRotAngle=0.f;
}
Audio::Description AudioEmitter::smDefaultDescription;
//------------------------------------------------------------------------------
void AudioEmitter::processLoopEvent()
{
if(mLoopCount == 0)
return;
else if(mLoopCount > 0)
mLoopCount--;
if(mUseProfileDescription && mAudioProfileId)
return;
if(!mDescription.mIsLooping || ((mDescription.mLoopCount <= 0) && !mDescription.mMaxLoopGap))
return;
// check if still playing...
U32 source = alxFindSource(mAudioHandle);
if(source != -1)
{
ALint state = AL_STOPPED;
alGetSourcei(source, AL_SOURCE_STATE, &state);
if(state == AL_PLAYING)
{
mEventID = Sim::postEvent(this, new AudioEmitterLoopEvent, Sim::getCurrentTime() + UPDATE_BUMP_MS);
mLoopCount++; // this one doesn't count because we are still playing
return;
}
}
mEventID = 0;
mDirty.set(SourceMask);
update();
}
//------------------------------------------------------------------------------
bool AudioEmitter::update()
{
// object does not know it is really a client object if created through event
AssertFatal(isClientObject() || mOwnedByClient, "AudioEmitter::update: only clients can update!");
if(mDirty.test(LoopCount))
mLoopCount = mDescription.mLoopCount;
// updating source?
if(mDirty.test(SourceMask|UseProfileDescription) || (!mUseProfileDescription &&
((mDescription.mIsLooping && mDirty.test(LoopingMask)) || mDirty.test(IsLooping|Is3D|AudioType))))
{
if(mAudioHandle != NULL_AUDIOHANDLE)
{
alxStop(mAudioHandle);
mAudioHandle = NULL_AUDIOHANDLE;
}
// profile:
if(mDirty.test(Profile))
{
if(!mAudioProfileId)
mAudioProfile = 0;
else
mAudioProfile = dynamic_cast<AudioProfile*>(Sim::findObject(mAudioProfileId));
}
// description:
if(mDirty.test(Description))
{
if(!mAudioDescriptionId)
mAudioDescription = 0;
else
mAudioDescription = dynamic_cast<AudioDescription*>(Sim::findObject(mAudioDescriptionId));
}
MatrixF transform = getTransform();
// use the profile?
if(mUseProfileDescription && mAudioProfileId)
mAudioHandle = alxCreateSource(mAudioProfile, &transform);
else
{
// grab the filename
const char * fileName = 0;
if(mFilename && mFilename[0])
fileName = mFilename;
else if(mAudioProfile)
fileName = mAudioProfile->mFilename;
if(!fileName)
{
Con::errorf(ConsoleLogEntry::General, "AudioEmitter::update: invalid audio filename!");
return(false);
}
// use a description?
if(mAudioDescription)
mAudioHandle = alxCreateSource(mAudioDescription, fileName, &transform);
else
{
if(mDescription.mIsLooping)
{
S32 minGap = mClamp(mDescription.mMinLoopGap, 0, mDescription.mMaxLoopGap);
S32 maxGap = mClamp(mDescription.mMaxLoopGap, mDescription.mMinLoopGap, mDescription.mMaxLoopGap);
// controlling looping?
if((mDescription.mLoopCount != -1) || maxGap)
{
mDescription.mIsLooping = false;
mAudioHandle = alxCreateSource(&mDescription, fileName, &transform);
mDescription.mIsLooping = true;
// handle may come back as null.. (0 volume, no handles, ...), still
// want to try again (assume good buffer) since this is a looper
Resource<AudioBuffer> buffer = AudioBuffer::find(fileName);
S32 waveLen = 0;
if(bool(buffer))
{
ALuint alBuffer = buffer->getALBuffer();
waveLen = alxGetWaveLen(alBuffer);
}
if(waveLen)
{
S32 offset = waveLen + minGap + sgRandom.randI(minGap, maxGap);
if(mEventID)
Sim::cancelEvent(mEventID);
mEventID = Sim::postEvent(this, new AudioEmitterLoopEvent, Sim::getCurrentTime() + offset);
if(mAudioHandle == NULL_AUDIOHANDLE)
return(true);
}
}
else
mAudioHandle = alxCreateSource(&mDescription, fileName, &transform);
}
else
mAudioHandle = alxCreateSource(&mDescription, fileName, &transform);
}
}
if(mAudioHandle == NULL_AUDIOHANDLE)
{
Con::errorf(ConsoleLogEntry::General, "AudioEmitter::update: failed to create source!");
return(false);
}
alxPlay(mAudioHandle);
}
else
{
// don't clear dirty flags until there is a source
if(mAudioHandle == NULL_AUDIOHANDLE)
return(true);
if(mDirty.test(Transform))
{
// Update the position.
Point3F pos;
mObjToWorld.getColumn(3, &pos);
alxSource3f(mAudioHandle, AL_POSITION, pos.x, pos.y, pos.z);
//Make sure we update the direction also.
mObjToWorld.getColumn(1, &pos);
alSource3f(mAudioHandle, AL_DIRECTION, pos.x, pos.y, pos.z);
}
// grab the description
const Audio::Description * desc = 0;
if(mUseProfileDescription && mAudioProfile)
desc = mAudioProfile->getDescription();
else if(mAudioDescription)
desc = mAudioDescription->getDescription();
else
desc = &mDescription;
if(!desc)
return(true);
if(mDirty.test(Volume))
alxSourcef(mAudioHandle, AL_GAIN_LINEAR, desc->mVolume);
if(mDirty.test(ReferenceDistance|MaxDistance))
{
alxSourcef(mAudioHandle, AL_REFERENCE_DISTANCE, desc->mReferenceDistance);
alxSourcef(mAudioHandle, AL_MAX_DISTANCE, desc->mMaxDistance);
}
if(mDirty.test(ConeInsideAngle))
alxSourcei(mAudioHandle, AL_CONE_INNER_ANGLE, desc->mConeInsideAngle);
if(mDirty.test(ConeOutsideAngle))
alxSourcei(mAudioHandle, AL_CONE_OUTER_ANGLE, desc->mConeOutsideAngle);
if(mDirty.test(ConeOutsideVolume))
alxSourcef(mAudioHandle, AL_CONE_OUTER_GAIN, desc->mConeOutsideVolume);
}
mDirty.clear();
return(true);
}
//------------------------------------------------------------------------------
bool AudioEmitter::onAdd()
{
if(!Parent::onAdd())
return(false);
// object does not know it is really a client object if created through event
if(isServerObject() && !mOwnedByClient)
{
// validate the object data
mDescription.mVolume = mClampF(mDescription.mVolume, 0.0f, 1.0f);
mDescription.mLoopCount = mClamp(mDescription.mLoopCount, -1, mDescription.mLoopCount);
mDescription.mMaxLoopGap = mClamp(mDescription.mMaxLoopGap, mDescription.mMinLoopGap, mDescription.mMaxLoopGap);
mDescription.mMinLoopGap = mClamp(mDescription.mMinLoopGap, 0, mDescription.mMaxLoopGap);
if(mDescription.mIs3D)
{
mDescription.mReferenceDistance = mClampF(mDescription.mReferenceDistance, 0.f, mDescription.mReferenceDistance);
mDescription.mMaxDistance = (mDescription.mMaxDistance > mDescription.mReferenceDistance) ? mDescription.mMaxDistance : (mDescription.mReferenceDistance+0.01f);
mDescription.mConeInsideAngle = mClamp(mDescription.mConeInsideAngle, 0, 360);
mDescription.mConeOutsideAngle = mClamp(mDescription.mConeOutsideAngle, mDescription.mConeInsideAngle, 360);
mDescription.mConeOutsideVolume = mClampF(mDescription.mConeOutsideVolume, 0.0f, 1.0f);
mDescription.mConeVector.normalize();
}
}
else
{
if(!update())
{
Con::errorf(ConsoleLogEntry::General, "AudioEmitter::onAdd: client failed initial update!");
if(mOwnedByClient)
return(false);
}
gAmbientAudioManager.addEmitter(this);
}
//
mObjBox.max = mObjScale;
mObjBox.min = mObjScale;
mObjBox.min.neg();
resetWorldBox();
addToScene();
return(true);
}
void AudioEmitter::onRemove()
{
if(isClientObject())
{
gAmbientAudioManager.removeEmitter(this);
if(mAudioHandle != NULL_AUDIOHANDLE)
alxStop(mAudioHandle);
}
removeFromScene();
Parent::onRemove();
}
void AudioEmitter::setTransform(const MatrixF & mat)
{
// Set the transform directly from the matrix created by inspector
// Also, be sure to strip out the pointing vector and place it in the cone vector
Point3F pointing;
mat.getColumn(1, &pointing);
mDescription.mConeVector = pointing;
Parent::setTransform(mat);
setMaskBits(TransformUpdateMask);
}
void AudioEmitter::setScale(const VectorF &)
{
}
//------------------------------------------------------------------------------
namespace {
static Point3F cubePoints[8] = {
Point3F(-1, -1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, -1), Point3F(-1, 1, 1),
Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1)
};
static U32 cubeFaces[6][4] = {
{ 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
{ 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 }
};
static Point2F textureCoords[4] = {
Point2F(0, 0), Point2F(1, 0), Point2F(1, 1), Point2F(0, 1)
};
}
bool AudioEmitter::prepRenderImage(SceneState * state, const U32 stateKey, const U32, const bool)
{
if(!gEditingMission || isLastState(state, stateKey))
return(false);
setLastState(state, stateKey);
if(gEditingMission && state->isObjectRendered(this))
{
SceneRenderImage * image = new SceneRenderImage;
image->obj = this;
state->insertRenderImage(image);
}
return(false);
}
void AudioEmitter::render3DVisualFeedBack(void)
{
const float RADINC = 30.f;
const float SWPINC = 10.f;
const float FULLPNT = 6.f;
float OldPointSize = 1.f;
float OldLineWidth = 1.f;
float OldColor[4] = {1.f,1.f,1.f,1.f};
float fPointIncrement = (mDescription.mMaxDistance/10.f);
//
// Startup Ops
//
glGetFloatv(GL_POINT_SIZE, &OldPointSize);
glGetFloatv(GL_LINE_WIDTH, &OldLineWidth);
glGetFloatv(GL_CURRENT_COLOR, &OldColor[0]);
glPushMatrix();
glRotatef(mAnimRotAngle+=1.f,0.f,1.f,0.f);
if(mAnimRotAngle >= 360.f) mAnimRotAngle = 0.f;
//
// RenderOps
//
// 0. INNER CONE
{
glPointSize(FULLPNT);
// Radial Sweep - Sweeps about radius of current angular sweep angle
for(float fRadAngle=0.f; fRadAngle < 360.f; fRadAngle += RADINC) {
bool bDone = false;
float fCurAngle = mDescription.mConeInsideAngle/2;
float fStopAngle = 0;
// Angular Sweep - Sweeps from outer angle of INNER CONE to 0 DEG
while(!bDone)
{
glPushMatrix();
glRotatef(fRadAngle, 0.f, 1.f, 0.f);
glRotatef(fCurAngle, 1.f, 0.f, 0.f);
glBegin(GL_POINTS);
glColor4f(1.f,0.0f,0.0f,1.f);
for(float fY=0.f;
fY <= mDescription.mMaxDistance;
fY += fPointIncrement) {
if(fY > mDescription.mReferenceDistance) {
glColor4f(1.f - (1.f - (mDescription.mReferenceDistance/fY)), 0.f,0.f,1.f);
}
glVertex3f(0.f, fY, 0.f);
}
glEnd();
glPopMatrix();
if(fStopAngle == fCurAngle) bDone = true;
fCurAngle -= SWPINC;
if(fStopAngle > fCurAngle) fCurAngle = fStopAngle;
}
}
}
// Outer Cone and Outside volume only get rendered if mConeOutsideVolume > 0
if(mDescription.mConeOutsideVolume) {
// 1. OUTER CONE
{
glPointSize(FULLPNT-1);
// Radial Sweep - Sweeps about radius of current angular sweep angle
for(float fRadAngle=0.f; fRadAngle < 360.f; fRadAngle += RADINC) {
bool bDone = false;
float fCurAngle = mDescription.mConeOutsideAngle/2;
float fStopAngle = mDescription.mConeInsideAngle/2;
// Angular Sweep - Sweeps from outer angle of INNER CONE to 0 DEG
while(!bDone)
{
glPushMatrix();
glRotatef(fRadAngle, 0.f, 1.f, 0.f);
glRotatef(fCurAngle, 1.f, 0.f, 0.f);
glBegin(GL_POINTS);
glColor4f(1.f,
0.0f,
1.0f,
1.f);
for(float fY=0.f;
fY <= mDescription.mMaxDistance;
fY += fPointIncrement) {
glVertex3f(0.f, fY, 0.f);
}
glEnd();
glPopMatrix();
if(fStopAngle == fCurAngle) bDone = true;
fCurAngle -= SWPINC*2.f;
if(fStopAngle > fCurAngle) fCurAngle = fStopAngle;
}
}
}
// 1. OUTSIDE VOLUME
{
glPointSize(FULLPNT-2);
// Radial Sweep - Sweeps about radius of current angular sweep angle
for(float fRadAngle=0.f; fRadAngle < 360.f; fRadAngle += RADINC) {
bool bDone = false;
float fCurAngle = 180;
float fStopAngle = mDescription.mConeOutsideAngle/2;
// Angular Sweep - Sweeps from outer angle of INNER CONE to 0 DEG
while(!bDone)
{
glPushMatrix();
glRotatef(fRadAngle, 0.f, 1.f, 0.f);
glRotatef(fCurAngle, 1.f, 0.f, 0.f);
glBegin(GL_POINTS);
glColor4f(0.f, 0.0f, 1.0f,1.f);
for(float fY=0.f;
fY <= mDescription.mMaxDistance;
fY += fPointIncrement) {
glVertex3f(0.f, fY, 0.f);
}
glEnd();
glPopMatrix();
if(fStopAngle == fCurAngle) bDone = true;
fCurAngle -= SWPINC*2.f;
if(fStopAngle > fCurAngle) fCurAngle = fStopAngle;
}
}
}
}
//
// Cleanup Ops
//
glPopMatrix();
glColor4f(OldColor[0],OldColor[1],OldColor[2],OldColor[3]);
glLineWidth(OldLineWidth);
glPointSize(OldPointSize);
}
void AudioEmitter::renderObject(SceneState*, SceneRenderImage*)
{
glPushMatrix();
dglMultMatrix(&mObjToWorld);
glScalef(mObjScale.x, mObjScale.y, mObjScale.z);
glDisable(GL_CULL_FACE);
Point3F size(1.f, 1.f, 1.f);
// draw the cube
// Check to see if it is playing (green == playing; black == off)
glColor4f(0.f,0.f,0.f,1.f);
U32 source = alxFindSource(mAudioHandle);
if(source != -1)
{
ALint state = AL_STOPPED;
alGetSourcei(source, AL_SOURCE_STATE, &state);
if(state == AL_PLAYING)
{
Point3F listener;
alGetListener3f(AL_POSITION, &listener.x, &listener.y, &listener.z);
Point3F pos;
alGetSourcefv(source, AL_POSITION, (F32*)pos);
F32 dist = 0.f;
alGetSourcef(source, AL_MAX_DISTANCE, &dist);
pos -= listener;
dist -= pos.len();
if (dist > 0.f)
{
glColor4f(0.2f,1.f,0.2f,1.f);
}
}
}
for(int i = 0; i < 6; i++)
{
glBegin(GL_QUADS);
for(int vert = 0; vert < 4; vert++)
{
int idx = cubeFaces[i][vert];
glVertex3f(cubePoints[idx].x * size.x, cubePoints[idx].y * size.y, cubePoints[idx].z * size.z);
}
glEnd();
}
// Only render 3D visuals if in 3D Audio Mode
if(mDescription.mIs3D && ( mEnableVisualFeedback || isSelected() ) ) render3DVisualFeedBack();
glPopMatrix();
}
//------------------------------------------------------------------------------
namespace {
static AudioProfile * saveAudioProfile;
static AudioDescription * saveAudioDescription;
static StringTableEntry saveFilename;
static bool saveUseProfileDescription;
static Audio::Description saveDescription;
static Point3F savePos;
static bool saveOutsideAmbient;
}
void AudioEmitter::inspectPreApply()
{
if(isClientObject())
return;
Parent::inspectPreApply();
mObjToWorld.getColumn(3, &savePos);
saveAudioProfile = mAudioProfile;
saveAudioDescription = mAudioDescription;
saveFilename = mFilename;
saveUseProfileDescription = mUseProfileDescription;
saveDescription = mDescription;
saveOutsideAmbient = mOutsideAmbient;
}
void AudioEmitter::inspectPostApply()
{
if(isClientObject())
return;
Parent::inspectPostApply();
Point3F pos;
mObjToWorld.getColumn(3, &pos);
// set some dirty flags
mDirty.clear();
mDirty.set((savePos != pos) ? Transform : 0);
mDirty.set((saveAudioProfile != mAudioProfile) ? Profile : 0);
mDirty.set((saveAudioDescription != mAudioDescription) ? Description : 0);
mDirty.set((saveFilename != mFilename) ? Filename : 0);
mDirty.set((saveUseProfileDescription != mUseProfileDescription) ? UseProfileDescription : 0);
mDirty.set((saveDescription.mVolume != mDescription.mVolume) ? Volume : 0);
mDirty.set((saveDescription.mIsLooping != mDescription.mIsLooping) ? IsLooping : 0);
mDirty.set((saveDescription.mIs3D != mDescription.mIs3D) ? Is3D : 0);
mDirty.set((saveDescription.mReferenceDistance != mDescription.mReferenceDistance) ? ReferenceDistance : 0);
mDirty.set((saveDescription.mMaxDistance != mDescription.mMaxDistance) ? MaxDistance : 0);
mDirty.set((saveDescription.mConeInsideAngle != mDescription.mConeInsideAngle) ? ConeInsideAngle : 0);
mDirty.set((saveDescription.mConeOutsideAngle != mDescription.mConeOutsideAngle) ? ConeOutsideAngle : 0);
mDirty.set((saveDescription.mConeOutsideVolume != mDescription.mConeOutsideVolume) ? ConeOutsideVolume : 0);
mDirty.set((saveDescription.mConeVector != mDescription.mConeVector) ? ConeVector : 0);
mDirty.set((saveDescription.mLoopCount != mDescription.mLoopCount) ? LoopCount : 0);
mDirty.set((saveDescription.mMinLoopGap != mDescription.mMinLoopGap) ? MinLoopGap : 0);
mDirty.set((saveDescription.mMaxLoopGap != mDescription.mMaxLoopGap) ? MaxLoopGap : 0);
mDirty.set((saveDescription.mType != mDescription.mType) ? AudioType : 0);
mDirty.set((saveOutsideAmbient != mOutsideAmbient) ? OutsideAmbient : 0);
if(mDirty)
setMaskBits(DirtyUpdateMask);
}
void AudioEmitter::packData(NetConnection *, U32 mask, BitStream * stream)
{
// initial update
if(stream->writeFlag(mask & InitialUpdateMask))
{
mask |= TransformUpdateMask;
mDirty = AllDirtyMask;
// see if can remove some items from initial update: description checked below
if(!mAudioProfile)
mDirty.clear(Profile);
if(!mAudioDescription)
mDirty.clear(Description);
if(!mFilename || !mFilename[0])
mDirty.clear(Filename);
if(!mUseProfileDescription)
mDirty.clear(UseProfileDescription);
if(mOutsideAmbient)
mDirty.clear(OutsideAmbient);
}
// transform
if(stream->writeFlag(mask & TransformUpdateMask))
stream->writeAffineTransform(mObjToWorld);
// profile
if(stream->writeFlag(mDirty.test(Profile)))
if(stream->writeFlag(mAudioProfile))
stream->writeRangedU32(mAudioProfile->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
// description
if(stream->writeFlag(mDirty.test(Description)))
if(stream->writeFlag(mAudioDescription))
stream->writeRangedU32(mAudioDescription->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
// filename
if(stream->writeFlag(mDirty.test(Filename)))
stream->writeString(mFilename);
// useprofiledescription
if(stream->writeFlag(mDirty.test(UseProfileDescription)))
{
if(mUseProfileDescription)
{
if(!mAudioProfile)
mUseProfileDescription = false;
else
mDirty.clear(UseProfileDescriptionMask);
}
if(!mUseProfileDescription)
mDirty.set(UseProfileDescriptionMask);
stream->writeFlag(mUseProfileDescription);
}
if(mAudioDescription && !mUseProfileDescription)
mDirty.clear(UseProfileDescriptionMask);
// check initial update against the default description
if((mask & InitialUpdateMask) && !mUseProfileDescription)
{
if(mDescription.mVolume == smDefaultDescription.mVolume) mDirty.clear(Volume);
AssertFatal(smDefaultDescription.mIsLooping, "Doh!");
if(mDescription.mIsLooping != smDefaultDescription.mIsLooping)
mDirty.clear(LoopingMask);
else
{
mDirty.clear(IsLooping);
if(mDescription.mLoopCount == smDefaultDescription.mLoopCount) mDirty.clear(LoopCount);
if(mDescription.mMinLoopGap == smDefaultDescription.mMinLoopGap) mDirty.clear(MinLoopGap);
if(mDescription.mMaxLoopGap == smDefaultDescription.mMaxLoopGap) mDirty.clear(MaxLoopGap);
}
AssertFatal(smDefaultDescription.mIs3D, "Doh!");
if(mDescription.mIs3D != smDefaultDescription.mIs3D)
mDirty.clear(Is3DMask);
else
{
mDirty.clear(Is3D);
if(mDescription.mReferenceDistance == smDefaultDescription.mReferenceDistance) mDirty.clear(ReferenceDistance);
if(mDescription.mMaxDistance == smDefaultDescription.mMaxDistance) mDirty.clear(MaxDistance);
if(mDescription.mConeInsideAngle == smDefaultDescription.mConeInsideAngle) mDirty.clear(ConeInsideAngle);
if(mDescription.mConeOutsideAngle == smDefaultDescription.mConeOutsideAngle) mDirty.clear(ConeOutsideAngle);
if(mDescription.mConeOutsideVolume == smDefaultDescription.mConeOutsideVolume) mDirty.clear(ConeOutsideVolume);
if(mDescription.mConeVector == smDefaultDescription.mConeVector) mDirty.clear(ConeVector);
}
if(mDescription.mType == smDefaultDescription.mType) mDirty.clear(AudioType);
}
// volume
if(stream->writeFlag(mDirty.test(Volume)))
stream->write(mDescription.mVolume);
// islooping
if(stream->writeFlag(mDirty.test(IsLooping)))
{
mDescription.mIsLooping ? mDirty.set(LoopingMask) : mDirty.clear(LoopingMask);
stream->writeFlag(mDescription.mIsLooping);
}
// is3d
if(stream->writeFlag(mDirty.test(Is3D)))
{
mDescription.mIs3D ? mDirty.set(Is3DMask) : mDirty.clear(Is3DMask);
stream->writeFlag(mDescription.mIs3D);
}
// ReferenceDistance
if(stream->writeFlag(mDirty.test(ReferenceDistance)))
stream->write(mDescription.mReferenceDistance);
// maxdistance
if(stream->writeFlag(mDirty.test(MaxDistance)))
stream->write(mDescription.mMaxDistance);
// coneinsideangle
if(stream->writeFlag(mDirty.test(ConeInsideAngle)))
stream->write(mDescription.mConeInsideAngle);
// coneoutsideangle
if(stream->writeFlag(mDirty.test(ConeOutsideAngle)))
stream->write(mDescription.mConeOutsideAngle);
// coneoutsidevolume
if(stream->writeFlag(mDirty.test(ConeOutsideVolume)))
stream->write(mDescription.mConeOutsideVolume);
// conevector
if(stream->writeFlag(mDirty.test(ConeVector)))
{
stream->write(mDescription.mConeVector.x);
stream->write(mDescription.mConeVector.y);
stream->write(mDescription.mConeVector.z);
}
// loopcount
if(stream->writeFlag(mDirty.test(LoopCount)))
stream->write(mDescription.mLoopCount);
// minloopgap
if(stream->writeFlag(mDirty.test(MinLoopGap)))
stream->write(mDescription.mMinLoopGap);
// maxloopgap
if(stream->writeFlag(mDirty.test(MaxLoopGap)))
stream->write(mDescription.mMaxLoopGap);
// audiotype
if(stream->writeFlag(mDirty.test(AudioType)))
stream->write(mDescription.mType);
// outside ambient
if(stream->writeFlag(mDirty.test(OutsideAmbient)))
stream->writeFlag(mOutsideAmbient);
stream->write(mEnableVisualFeedback);
mDirty.clear();
}
//------------------------------------------------------------------------------
U32 AudioEmitter::packUpdate(NetConnection * con, U32 mask, BitStream * stream)
{
U32 retMask = Parent::packUpdate(con, mask, stream);
packData(con, mask, stream);
return(retMask);
}
bool AudioEmitter::readDirtyFlag(BitStream * stream, U32 mask)
{
bool flag = stream->readFlag();
if(flag)
mDirty.set(mask);
return(flag);
}
void AudioEmitter::unpackData(NetConnection *, BitStream * stream)
{
// initial update?
bool initialUpdate = stream->readFlag();
// transform
if(readDirtyFlag(stream, Transform))
{
MatrixF mat;
stream->readAffineTransform(&mat);
Parent::setTransform(mat);
}
// profile
if(readDirtyFlag(stream, Profile))
if(stream->readFlag())
mAudioProfileId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
mAudioProfileId = 0;
// description
if(readDirtyFlag(stream, Description))
if(stream->readFlag())
mAudioDescriptionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
mAudioDescriptionId = 0;
// filename
if(readDirtyFlag(stream, Filename))
mFilename = stream->readSTString();
// useprofiledescription
if(readDirtyFlag(stream, UseProfileDescription))
mUseProfileDescription = stream->readFlag();
// volume
if(readDirtyFlag(stream, Volume))
stream->read(&mDescription.mVolume);
// islooping
if(readDirtyFlag(stream, IsLooping))
mDescription.mIsLooping = stream->readFlag();
// is3d
if(readDirtyFlag(stream, Is3D))
mDescription.mIs3D = stream->readFlag();
// ReferenceDistance
if(readDirtyFlag(stream, ReferenceDistance))
stream->read(&mDescription.mReferenceDistance);
// maxdistance
if(readDirtyFlag(stream, MaxDistance))
stream->read(&mDescription.mMaxDistance);
// coneinsideangle
if(readDirtyFlag(stream, ConeInsideAngle))
stream->read(&mDescription.mConeInsideAngle);
// coneoutsideangle
if(readDirtyFlag(stream, ConeOutsideAngle))
stream->read(&mDescription.mConeOutsideAngle);
// coneoutsidevolume
if(readDirtyFlag(stream, ConeOutsideVolume))
stream->read(&mDescription.mConeOutsideVolume);
// conevector
if(readDirtyFlag(stream, ConeVector))
{
stream->read(&mDescription.mConeVector.x);
stream->read(&mDescription.mConeVector.y);
stream->read(&mDescription.mConeVector.z);
}
// loopcount
if(readDirtyFlag(stream, LoopCount))
stream->read(&mDescription.mLoopCount);
// minloopgap
if(readDirtyFlag(stream, MinLoopGap))
{
stream->read(&mDescription.mMinLoopGap);
if(mDescription.mMinLoopGap < 0)
mDescription.mMinLoopGap = 0;
}
// maxloopgap
if(readDirtyFlag(stream, MaxLoopGap))
{
stream->read(&mDescription.mMaxLoopGap);
if(mDescription.mMaxLoopGap < 0)
mDescription.mMaxLoopGap = 0;
}
// audiotype
if(readDirtyFlag(stream, AudioType))
{
stream->read(&mDescription.mType);
if(mDescription.mType >= Audio::NumAudioTypes)
mDescription.mType = 0;
}
// outside ambient
if(readDirtyFlag(stream, OutsideAmbient))
mOutsideAmbient = stream->readFlag();
stream->read(&mEnableVisualFeedback);
// update the emitter now?
if(!initialUpdate)
update();
}
void AudioEmitter::unpackUpdate(NetConnection * con, BitStream * stream)
{
Parent::unpackUpdate(con, stream);
unpackData(con, stream);
}
void AudioEmitter::initPersistFields()
{
Parent::initPersistFields();
addGroup("Profile");
addField("profile", TypeAudioProfilePtr, Offset(mAudioProfile, AudioEmitter));
addField("useProfileDescription", TypeBool, Offset(mUseProfileDescription, AudioEmitter));
endGroup("Profile");
addGroup("Media");
addField("description", TypeAudioDescriptionPtr, Offset(mAudioDescription, AudioEmitter));
addField("fileName", TypeFilename, Offset(mFilename, AudioEmitter));
addField("type", TypeS32, Offset(mDescription.mType, AudioEmitter));
endGroup("Media");
addGroup("Sound");
addField("volume", TypeF32, Offset(mDescription.mVolume, AudioEmitter));
addField("outsideAmbient", TypeBool, Offset(mOutsideAmbient, AudioEmitter));
addField("ReferenceDistance", TypeF32, Offset(mDescription.mReferenceDistance, AudioEmitter));
addField("maxDistance", TypeF32, Offset(mDescription.mMaxDistance, AudioEmitter));
endGroup("Sound");
addGroup("Looping");
addField("isLooping", TypeBool, Offset(mDescription.mIsLooping, AudioEmitter));
addField("loopCount", TypeS32, Offset(mDescription.mLoopCount, AudioEmitter));
addField("minLoopGap", TypeS32, Offset(mDescription.mMinLoopGap, AudioEmitter));
addField("maxLoopGap", TypeS32, Offset(mDescription.mMaxLoopGap, AudioEmitter));
endGroup("Looping");
addGroup("Advanced");
addField("enableVisualFeedback", TypeBool, Offset(mEnableVisualFeedback, AudioEmitter));
addField("is3D", TypeBool, Offset(mDescription.mIs3D, AudioEmitter));
addField("coneInsideAngle", TypeS32, Offset(mDescription.mConeInsideAngle, AudioEmitter));
addField("coneOutsideAngle", TypeS32, Offset(mDescription.mConeOutsideAngle, AudioEmitter));
addField("coneOutsideVolume", TypeF32, Offset(mDescription.mConeOutsideVolume, AudioEmitter));
addField("coneVector", TypePoint3F, Offset(mDescription.mConeVector, AudioEmitter));
endGroup("Advanced");
// create the static description
smDefaultDescription.mVolume = 1.0f;
smDefaultDescription.mIsLooping = true;
smDefaultDescription.mIs3D = true;
smDefaultDescription.mReferenceDistance = 1.0f;
smDefaultDescription.mMaxDistance = 100.0f;
smDefaultDescription.mConeInsideAngle = 360;
smDefaultDescription.mConeOutsideAngle = 360;
smDefaultDescription.mConeOutsideVolume = 1.0f;
smDefaultDescription.mConeVector.set(0, 0, 1);
smDefaultDescription.mLoopCount = -1;
smDefaultDescription.mMinLoopGap = 0;
smDefaultDescription.mMaxLoopGap = 0;
smDefaultDescription.mType = 0;
}
////--------------------------------------------------------------------------
//IMPLEMENT_CO_NETOBJECT_V1(ClientAudioEmitter);
//
//ClientAudioEmitter::ClientAudioEmitter()
//{
// mNetFlags = 0;
// mEventCount = 0;
//}
//
//bool ClientAudioEmitter::onAdd()
//{
// if(!SceneObject::onAdd())
// return(false);
//
// // create and send events to all clients
// bool postedEvent = false;
// for(NetConnection * conn = NetConnection::getConnectionList(); conn; conn = conn->getNext())
// {
// if(!conn->isConnectionToServer())
// {
// postedEvent = true;
// conn->postNetEvent(new AudioEmitterToEvent(this));
// }
// }
//
// return(postedEvent);
//}
//
//void ClientAudioEmitter::initPersistFields()
//{
// Parent::initPersistFields();
//}
//
////--------------------------------------------------------------------------
//IMPLEMENT_CO_NETEVENT_V1(AudioEmitterToEvent);
//
//AudioEmitterToEvent::AudioEmitterToEvent(ClientAudioEmitter * emitter)
//{
// mEmitter = emitter;
// if(bool(mEmitter))
// mEmitter->mEventCount++;
//}
//
//void AudioEmitterToEvent::notifyDelivered(NetConnection *, bool)
//{
// if(bool(mEmitter) && mEmitter->mEventCount && (--mEmitter->mEventCount == 0))
// {
// mEmitter->deleteObject();
// mEmitter = 0;
// }
//}
//
//void AudioEmitterToEvent::write(NetConnection * conn, BitStream * bstream)
//{
// if(bool(mEmitter))
// mEmitter->packData(conn, AudioEmitter::InitialUpdateMask, bstream);
//}
//
//void AudioEmitterToEvent::pack(NetConnection * conn, BitStream * bstream)
//{
// if(bool(mEmitter))
// mEmitter->packData(conn, AudioEmitter::InitialUpdateMask, bstream);
//}
//
//void AudioEmitterToEvent::unpack(NetConnection * conn, BitStream * bstream)
//{
// // clients are responsible for deleting these audioemitters!
// AudioEmitter * emitter = new AudioEmitter(true);
// emitter->mOwnedByClient = true;
// emitter->unpackData(conn, bstream);
// emitter->registerOb