2
3
mirror of https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git synced 2025-07-14 02:27:40 -04:00

CWE Directory Reorganization

Rearrange directory structure of CWE to be loosely equivalent to
the H'uru Plasma repository.

Part 1: Movement of directories and files.
This commit is contained in:
rarified
2021-05-15 12:49:46 -06:00
parent c3f4a640a3
commit 96903e8dca
4002 changed files with 159 additions and 644 deletions

View File

@ -0,0 +1,75 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAllCreatables_inc
#define plAllCreatables_inc
#include "../plGImage/plGImageCreatable.h"
#include "../plGLight/plGLightCreatable.h"
#include "../plInterp/plInterpCreatable.h"
#include "../plInputCore/plInputCoreCreatable.h"
#include "../plPipeline/plPipelineCreatable.h"
#include "../plResMgr/plResMgrCreatable.h"
#include "../plSurface/plSurfaceCreatable.h"
#include "../plNetClient/plNetClientCreatable.h"
#include "../plNetClientComm/plNetClientCommCreatable.h"
#include "../plMessage/plMessageCreatable.h"
#include "../plAudible/plAudibleCreatable.h"
#include "../plDrawable/plDrawableCreatable.h"
#include "../plPhysical/plPhysicalCreatable.h"
#include "../plModifier/plModifierCreatable.h"
#include "../plScene/plSceneCreatable.h"
#include "../plPhysX/plPhysXCreatable.h"
#include "../plAudio/plAudioCreatable.h"
#include "../plAudioCore/plAudioCoreCreatable.h"
#include "../plParticleSystem/plParticleCreatable.h"
#include "../plNetCommon/plNetCommonCreatable.h"
#include "../plVault/plVaultCreatable.h"
#include "../plAvatar/plAvatarCreatable.h"
#include "../plIntersect/plIntersectCreatable.h"
#include "../plNetMessage/plNetMessageCreatable.h"
#include "../plStatGather/plStatGatherCreatable.h"
#include "../plSDL/plSDLCreatable.h"
#include "../plAgeLoader/plAgeLoaderCreatable.h"
#endif // plAllCreatables_inc

View File

@ -0,0 +1,565 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsStream.h"
#include "plAgeDescription.h"
#include "hsUtils.h"
#include "hsStlUtils.h"
#include "../plFile/hsFiles.h"
#include "../plFile/plInitFileReader.h"
#include "../plFile/plEncryptedStream.h"
#include "hsStringTokenizer.h"
#include <functional>
#include <algorithm>
const UInt32 plAgePage::kInvalidSeqSuffix = (UInt32)-1;
plAgePage::plAgePage( const char *name, UInt32 seqSuffix, Byte flags )
{
fName = name != nil ? hsStrcpy( name ) : nil;
fSeqSuffix = seqSuffix;
fFlags = flags;
}
plAgePage::plAgePage( char *stringFrom ) : fName(nil)
{
SetFromString( stringFrom );
}
plAgePage::plAgePage()
{
fName = nil;
fFlags = 0;
fSeqSuffix = 0;
}
plAgePage::plAgePage( const plAgePage &src ) : fName(nil)
{
fName = src.fName != nil ? hsStrcpy( src.fName ) : nil;
fSeqSuffix = src.fSeqSuffix;
fFlags = src.fFlags;
}
plAgePage::~plAgePage()
{
delete [] fName;
}
plAgePage &plAgePage::operator=( const plAgePage &src )
{
delete [] fName;
fName = src.fName != nil ? hsStrcpy( src.fName ) : nil;
fSeqSuffix = src.fSeqSuffix;
fFlags = src.fFlags;
return *this;
}
void plAgePage::SetFlags(Byte f, bool on)
{
if (on)
hsSetBits(fFlags, f);
else
hsClearBits(fFlags, f);
}
// now preservs original string
hsBool plAgePage::SetFromString( const char *stringIn )
{
char *c, seps[] = ", \n";
std::string string = stringIn;
// Parse. Format is going to be "pageName[,seqSuffix[,flags]]"
c = strtok( (char*)string.c_str(), seps );
if( c == nil )
return false;
delete [] fName;
fName = hsStrcpy( c );
// Look for seqSuffix
c = strtok( nil, seps );
if( c != nil )
{
fSeqSuffix = atoi( c );
// Look for flags
c = strtok( nil, seps );
if( c != nil )
{
fFlags = atoi( c );
}
else
fFlags = 0;
}
else
{
fSeqSuffix = kInvalidSeqSuffix;
fFlags = 0;
}
return true;
}
char *plAgePage::GetAsString( void ) const
{
static char str[ 256 ];
// Format is "pageName[,seqSuffix[,flags]]"
if( fFlags != 0 )
sprintf( str, "%s,%d,%d", fName, fSeqSuffix, fFlags );
else
sprintf( str, "%s,%d", fName, fSeqSuffix );
return str;
}
//
// plAgeDescription
//
//
//
// static
char plAgeDescription::kAgeDescPath[]={"dat"PATH_SEPARATOR_STR};
char *plAgeDescription::fCommonPages[] = { "Textures", "BuiltIn" };
// Also gotta init the separators for our helper reading function
plAgeDescription::plAgeDescription() : plInitSectionTokenReader()
{
IInit();
}
plAgeDescription::~plAgeDescription()
{
IDeInit();
}
void plAgeDescription::IDeInit()
{
ClearPageList();
delete [] fName;
}
plAgeDescription::plAgeDescription( const char *fileNameToReadFrom ) : plInitSectionTokenReader()
{
ReadFromFile(fileNameToReadFrom);
}
//
// Reads from a file, returns false if failed.
//
bool plAgeDescription::ReadFromFile( const char *fileNameToReadFrom )
{
IInit();
hsStream* stream = plEncryptedStream::OpenEncryptedFile(fileNameToReadFrom);
if( !stream )
return false;
Read( stream );
stream->Close();
delete stream;
SetAgeNameFromPath( fileNameToReadFrom );
return true;
}
void plAgeDescription::SetAgeNameFromPath( const char *path )
{
delete [] fName;
if( path == nil )
{
fName = nil;
return;
}
// Construct our name from the path
const char *pathSep1 = strrchr( path, '\\' );
const char *pathSep2 = strrchr( path, '/' );
if( pathSep2 > pathSep1 )
pathSep1 = pathSep2;
if( pathSep1 == nil )
pathSep1 = path;
else
pathSep1++; // Get past the actual character we found
char temp[ 512 ];
strcpy( temp, pathSep1 );
char *end = strrchr( temp, '.' );
if( end != nil )
*end = 0;
fName = hsStrcpy( temp );
}
void plAgeDescription::IInit( void )
{
fName = nil;
fDayLength = 24.0f;
fMaxCapacity = -1;
fLingerTime = 180; // seconds
fSeqPrefix = 0;
fReleaseVersion = 0;
fStart.SetMode( plUnifiedTime::kLocal );
fPageIterator = -1;
}
struct SzDelete { void operator()(char * str) { delete [] str;} };
void plAgeDescription::ClearPageList()
{
fPages.Reset();
}
void plAgeDescription::AppendPage( const char *name, int seqSuffix, Byte flags )
{
fPages.Append( plAgePage( name, ( seqSuffix == -1 ) ? fPages.GetCount() : (UInt32)seqSuffix, flags ) );
}
void plAgeDescription::SeekFirstPage( void )
{
fPageIterator = 0;
}
plAgePage *plAgeDescription::GetNextPage( void )
{
plAgePage *ret = nil;
if( fPageIterator >= 0 && fPageIterator < fPages.GetCount() )
{
ret = &fPages[ fPageIterator++ ];
if( fPageIterator >= fPages.GetCount() )
fPageIterator = -1;
}
return ret;
}
void plAgeDescription::RemovePage( const char *page )
{
int i;
for( i = 0; i < fPages.GetCount(); i++ )
{
if( strcmp( page, fPages[ i ].GetName() ) == 0 )
{
fPages.Remove( i );
return;
}
}
}
plAgePage *plAgeDescription::FindPage( const char *name ) const
{
int i;
for( i = 0; i < fPages.GetCount(); i++ )
{
if( strcmp( name, fPages[ i ].GetName() ) == 0 )
return &fPages[ i ];
}
return nil;
}
plLocation plAgeDescription::CalcPageLocation( const char *page ) const
{
plAgePage *ap = FindPage( page );
if( ap != nil )
{
// Combine our sequence # together
Int32 combined;
hsAssert(abs(fSeqPrefix) < 0xFF, "Age sequence prefex is out of range!"); // sequence prefix can NOT be larger or equal to 1-byte max value
UInt32 suffix = ap->GetSeqSuffix();
hsAssert(suffix <= 0xFFFF, "Page sequence number is out of range!"); // page sequence number can NOT be larger then 2-byte max value
if( fSeqPrefix < 0 ) // we are a global age
combined = -(Int32)( ( ( -fSeqPrefix ) << 16 ) + suffix );
else
combined = ( fSeqPrefix << 16 ) + suffix;
// Now, our 32 bit number looks like the following:
// 0xRRAAPPPP
// - RR is FF when reserved, and 00 when normal
// - AA is the one byte for age sequence prefix (FF not allowed because 0xFFFFFFFFFF is reserved for invalid sequence number)
// - PPPP is the two bytes for page sequence number
if( IsGlobalAge() )
return plLocation::MakeReserved( (UInt32)combined );
else
{
plLocation ret = plLocation::MakeNormal( combined );
if (page && !stricmp(page, "builtin"))
ret.SetFlags(plLocation::kBuiltIn);
return ret;
}
}
// Just make a blank (invalid) one
plLocation loc;
return loc;
}
//
// Writes the Age Description File
//
void plAgeDescription::Write(hsStream* stream) const
{
char buf[256];
// Write the date/time
sprintf(buf, "StartDateTime=%010lu\n", (unsigned long)fStart.GetSecs());
stream->WriteString(buf);
// Write the day length
sprintf(buf, "DayLength=%f\n", fDayLength);
stream->WriteString(buf);
// Write the max capacity
sprintf(buf, "MaxCapacity=%d\n", fMaxCapacity);
stream->WriteString(buf);
// Write the linger time
sprintf(buf, "LingerTime=%d\n", fLingerTime);
stream->WriteString(buf);
// Write out the sequence prefix
sprintf( buf, "SequencePrefix=%d\n", fSeqPrefix );
stream->WriteString( buf );
// Write out the release version
sprintf( buf, "ReleaseVersion=%d\n", fReleaseVersion );
stream->WriteString( buf );
// Write out the pages
int i;
for( i = 0; i < fPages.GetCount(); i++ )
{
sprintf(buf, "Page=%s\n", fPages[ i ].GetAsString() );
stream->WriteString(buf);
}
}
// Somewhat of an overkill, but I created it, so I better use it.
// The really nifty (or scary, depending on your viewpoint) thing is that, since
// we only have one section, we can safely use ourselves as the section reader.
// Later I might just add section readers with function pointers to avoid this need entirely
const char *plAgeDescription::GetSectionName( void ) const
{
return "AgeInfo";
}
hsBool plAgeDescription::IParseToken( const char *token, hsStringTokenizer *tokenizer, UInt32 userData )
{
char *tok;
if( !stricmp( token, "StartDateTime" ) )
{
if( ( tok = tokenizer->next() ) != nil )
{
char buf[11];
strncpy(buf, tok, 10); buf[10] = '\0';
fStart.SetSecs(atoi(buf));
}
}
else if (!stricmp(token, "DayLength"))
{
if( ( tok = tokenizer->next() ) != nil )
fDayLength = (float)atof(tok);
}
else if (!stricmp(token, "Page"))
{
fPages.Append( plAgePage( tokenizer->GetRestOfString() ) );
if( fPages[ fPages.GetCount() - 1 ].GetSeqSuffix() == plAgePage::kInvalidSeqSuffix )
fPages[ fPages.GetCount() - 1 ].SetSeqSuffix( fPages.GetCount() );
}
else if (!stricmp(token, "MaxCapacity"))
{
if( ( tok = tokenizer->next() ) != nil )
fMaxCapacity = atoi(tok);
}
else if (!stricmp(token, "LingerTime"))
{
if( ( tok = tokenizer->next() ) != nil )
fLingerTime = atoi(tok);
}
else if( !stricmp(token, "SequencePrefix"))
{
if( ( tok = tokenizer->next() ) != nil )
fSeqPrefix = atoi(tok);
}
else if( !stricmp(token, "ReleaseVersion"))
{
if( ( tok = tokenizer->next() ) != nil )
fReleaseVersion = atoi(tok);
}
return true;
}
//
// Reads the Age Description File
//
void plAgeDescription::Read(hsStream* stream)
{
plInitSectionReader *sections[] = { (plInitSectionReader *)this, nil };
plInitFileReader reader( stream, sections );
if( !reader.IsOpen() )
{
hsAssert( false, "Unable to open age description file for reading" );
return;
}
reader.Parse();
reader.Close();
}
//
// What is the current time in the age (in secs), based on dayLength.
//
int plAgeDescription::GetAgeTimeOfDaySecs(const plUnifiedTime& earthCurrentTime) const
{
double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime);
int secsInADay = (int)(fDayLength * 60 * 60);
int ageTime = (int)elapsedSecs % secsInADay;
return ageTime;
}
//
// What is the current time in the age (from 0-1), based on dayLength.
//
float plAgeDescription::GetAgeTimeOfDayPercent(const plUnifiedTime& earthCurrentTime) const
{
double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime);
int secsInADay = (int)(fDayLength * 60 * 60);
double ageTime = fmod(elapsedSecs, secsInADay);
float percent=(float)(ageTime/secsInADay);
if (percent<0.f)
percent=0.f;
if (percent>1.f)
percent=1.f;
return percent;
}
//
// How old is the age in days.
//
double plAgeDescription::GetAgeElapsedDays(plUnifiedTime earthCurrentTime) const
{
earthCurrentTime.SetMicros(0); // Ignore micros for this calculation
double elapsedSecs = GetAgeElapsedSeconds(earthCurrentTime);
return elapsedSecs / 60.0 / 60.0 / fDayLength; // days and fractions
}
//
// How many seconds have elapsed since the age was born. or how old is the age (in secs)
//
double plAgeDescription::GetAgeElapsedSeconds(const plUnifiedTime & earthCurrentTime) const
{
plUnifiedTime elapsed = earthCurrentTime - fStart;
return elapsed.GetSecsDouble();
}
// Static functions for the available common pages
enum CommonPages
{
kTextures,
kGlobal
};
const char *plAgeDescription::GetCommonPage( int pageType )
{
hsAssert( pageType < kNumCommonPages, "Invalid page type in GetCommonPage()" );
return fCommonPages[ pageType ];
}
void plAgeDescription::AppendCommonPages( void )
{
UInt32 startSuffix = 0xffff, i;
if( IsGlobalAge() )
return;
for( i = 0; i < kNumCommonPages; i++ )
fPages.Append( plAgePage( fCommonPages[ i ], startSuffix - i, 0 ) );
}
void plAgeDescription::CopyFrom(const plAgeDescription& other)
{
IDeInit();
fName = hsStrcpy(other.GetAgeName());
int i;
for(i=0;i<other.fPages.GetCount(); i++)
fPages.Append( other.fPages[ i ] );
fStart = other.fStart;
fDayLength = other.fDayLength;
fMaxCapacity = other.fMaxCapacity;
fLingerTime = other.fLingerTime;
fSeqPrefix = other.fSeqPrefix;
fReleaseVersion = other.fReleaseVersion;
}
bool plAgeDescription::FindLocation(const plLocation& loc) const
{
int i;
for( i = 0; i < fPages.GetCount(); i++ )
{
plLocation pageLoc = CalcPageLocation(fPages[i].GetName());
if (pageLoc == loc)
return true;
}
return false;
}

View File

@ -0,0 +1,212 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef PL_AGE_DESCRIPTION_H
#define PL_AGE_DESCRIPTION_H
#include "hsTypes.h"
#include "hsTemplates.h"
#include "hsUtils.h"
#include "../plUnifiedTime/plUnifiedTime.h"
#include "../pnKeyedObject/plUoid.h"
#include "../plFile/plInitFileReader.h"
//
// Age Definition File Reader/Writer
//
class hsStream;
class plAgePage
{
protected:
char *fName;
UInt32 fSeqSuffix;
Byte fFlags;
public:
static const UInt32 kInvalidSeqSuffix;
enum Flags
{
kPreventAutoLoad = 0x01,
kLoadIfSDLPresent = 0x02,
kIsLocalOnly = 0x04,
kIsVolatile = 0x08,
};
plAgePage( const char *name, UInt32 seqSuffix, Byte flags );
plAgePage( char *stringFrom );
plAgePage( const plAgePage &src );
plAgePage();
~plAgePage();
const char *GetName( void ) const { return fName; }
UInt32 GetSeqSuffix( void ) const { return fSeqSuffix; }
Byte GetFlags( void ) const { return fFlags; }
void SetSeqSuffix( UInt32 s ) { fSeqSuffix = s; }
void SetFlags(Byte f, bool on=true);
hsBool SetFromString( const char *string );
char *GetAsString( void ) const;
plAgePage &operator=( const plAgePage &src );
};
// Derived from plInitSectionTokenReader so we can do nifty things with reading the files
class plAgeDescription : public plInitSectionTokenReader
{
private:
char *fName;
Int32 fPageIterator;
hsTArray<plAgePage> fPages;
plUnifiedTime fStart;
float fDayLength;
short fMaxCapacity;
short fLingerTime; // seconds game instance should linger after last player leaves. -1 means never exit.
Int32 fSeqPrefix;
UInt32 fReleaseVersion; // 0 for pre-release, 1+ for actual released ages
static char *fCommonPages[];
void IInit( void );
void IDeInit( void );
// Overload for plInitSectionTokenReader
virtual hsBool IParseToken( const char *token, hsStringTokenizer *tokenizer, UInt32 userData );
public:
static char kAgeDescPath[];
plAgeDescription();
plAgeDescription( const char *fileNameToReadFrom );
plAgeDescription( const plAgeDescription &src )
{
IInit();
CopyFrom( src );
}
~plAgeDescription();
bool ReadFromFile( const char *fileNameToReadFrom ) ;
void Read(hsStream* stream);
void Write(hsStream* stream) const;
// Overload for plInitSectionTokenReader
virtual const char *GetSectionName( void ) const;
const char *GetAgeName( void ) const { return fName; }
void SetAgeNameFromPath( const char *path );
void SetAgeName(const char* ageName) { delete [] fName; fName=hsStrcpy(ageName); }
// Page list
void ClearPageList();
void RemovePage( const char *page );
void AppendPage( const char *name, int seqSuffix = -1, Byte flags = 0 );
void SeekFirstPage( void );
plAgePage *GetNextPage( void );
int GetNumPages() const { return fPages.GetCount(); }
plAgePage *FindPage( const char *name ) const;
bool FindLocation(const plLocation& loc) const;
plLocation CalcPageLocation( const char *page ) const;
// Getters
short GetStartMonth() const { return fStart.GetMonth(); }
short GetStartDay() const { return fStart.GetDay(); }
short GetStartYear() const { return fStart.GetYear(); }
short GetStartHour() const { return fStart.GetHour(); }
short GetStartMinute() const { return fStart.GetMinute(); }
short GetStartSecond() const { return fStart.GetSecond(); }
short GetMaxCapacity() const { return fMaxCapacity; }
short GetLingerTime() const { return fLingerTime;}
float GetDayLength() const { return fDayLength; }
Int32 GetSequencePrefix( void ) const { return fSeqPrefix; }
UInt32 GetReleaseVersion( void ) const { return fReleaseVersion; }
hsBool IsGlobalAge( void ) const { return ( fSeqPrefix < 0 ) ? true : false; }
// Setters
hsBool SetStart(short year, short month, short day, short hour, short minute, short second)
{ return fStart.SetTime(year,month,day,hour,minute,second); }
void SetDayLength(const float l) { fDayLength = l; }
void SetMaxCapacity(const short m) { fMaxCapacity=m; }
void SetLingerTime(const short v) { fLingerTime=v;}
void SetSequencePrefix( Int32 p ) { fSeqPrefix = p; }
void SetReleaseVersion( UInt32 v ) { fReleaseVersion = v; }
// calculations
double GetAgeElapsedDays(plUnifiedTime earthCurrentTime) const;
double GetAgeElapsedSeconds(const plUnifiedTime & earthCurrentTime) const;
int GetAgeTimeOfDaySecs(const plUnifiedTime& earthCurrentTime) const;
float GetAgeTimeOfDayPercent(const plUnifiedTime& earthCurrentTime) const;
// Static functions for the available common pages
enum CommonPages
{
kTextures = 0,
kGlobal,
kNumCommonPages
};
static const char *GetCommonPage( int pageType );
void AppendCommonPages( void );
void CopyFrom(const plAgeDescription& other);
plAgeDescription &operator=( const plAgeDescription &src )
{
CopyFrom( src );
return *this;
}
};
#endif //PL_AGE_DESCRIPTION_H

View File

@ -0,0 +1,264 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plManifest - Collection of version-specific info about an age, such //
// as the actual files constructing it, timestamps, and //
// release versions. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "plAgeManifest.h"
#include "hsUtils.h"
#include "../plFile/hsFiles.h"
#include "../plFile/plFileUtils.h"
#include "../plFile/plInitFileReader.h"
#include "hsStringTokenizer.h"
//// plManifestFile ///////////////////////////////////////////////////////
plManifestFile::plManifestFile(const char* name, const char* serverPath, const plMD5Checksum& check, UInt32 size, UInt32 zippedSize, UInt32 flags, bool md5Now) :
fChecksum(check),
fSize(size),
fZippedSize(zippedSize),
fFlags(flags),
fMd5Checked(md5Now)
{
fName = name;
fServerPath = serverPath;
if (md5Now)
{
DoMd5Check();
}
}
plManifestFile::~plManifestFile()
{
}
void plManifestFile::DoMd5Check()
{
if (plFileUtils::FileExists(fName.c_str()))
{
plMD5Checksum localFile(fName.c_str());
fIsLocalUpToDate = (localFile == fChecksum);
fLocalExists = true;
}
else
{
fIsLocalUpToDate = false;
fLocalExists = false;
}
fMd5Checked = true;
}
bool plManifestFile::IsLocalUpToDate()
{
if (!fMd5Checked)
DoMd5Check();
return fIsLocalUpToDate;
}
bool plManifestFile::LocalExists()
{
if (!fMd5Checked)
DoMd5Check();
return fLocalExists;
}
//// plManifest ///////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
char* plManifest::fTimeFormat = "%m/%d/%y %H:%M:%S";
static const UInt32 kLatestFormatVersion = 5;
plManifest::plManifest()
{
fFormatVersion = kLatestFormatVersion;
fAgeName = nil;
}
plManifest::~plManifest()
{
IReset();
}
void plManifest::IReset()
{
fFormatVersion = 0;
delete [] fAgeName;
int i;
for (i = 0; i < fFiles.GetCount(); i++)
delete fFiles[i];
fFiles.Reset();
}
//// Read and helpers ////////////////////////////////////////////////////////
class plVersSection : public plInitSectionTokenReader
{
protected:
plManifest* fDest;
virtual const char* GetSectionName() const { return "version"; }
virtual hsBool IParseToken(const char* token, hsStringTokenizer* tokenizer, UInt32 userData)
{
if (stricmp(token, "format") == 0)
fDest->SetFormatVersion(atoi(tokenizer->next()));
return true;
}
public:
plVersSection(plManifest* dest) : plInitSectionTokenReader(), fDest(dest) {}
};
class plGenericSection : public plInitSectionTokenReader
{
protected:
plManifest* fDest;
virtual void AddFile(plManifestFile* file) = 0;
plManifestFile* IReadManifestFile(const char* token, hsStringTokenizer* tokenizer, UInt32 userData, bool isPage)
{
char name[256];
strcpy(name, token);
UInt32 size = atoi(tokenizer->next());
plMD5Checksum sum;
sum.SetFromHexString(tokenizer->next());
UInt32 flags = atoi(tokenizer->next());
UInt32 zippedSize = 0;
if (hsCheckBits(flags, plManifestFile::kFlagZipped))
zippedSize = atoi(tokenizer->next());
return TRACKED_NEW plManifestFile(name, "", sum, size, zippedSize, flags);
}
virtual hsBool IParseToken(const char* token, hsStringTokenizer* tokenizer, UInt32 userData)
{
plManifestFile* file = IReadManifestFile(token, tokenizer, userData, false);
AddFile(file);
return true;
}
public:
plGenericSection(plManifest* dest) : plInitSectionTokenReader(), fDest(dest) {}
};
class plBaseSection : public plGenericSection
{
public:
plBaseSection(plManifest* dest) : plGenericSection(dest) {}
protected:
virtual void AddFile(plManifestFile* file) { fDest->AddFile(file); }
virtual const char* GetSectionName() const { return "base"; }
};
bool plManifest::Read(hsStream* stream)
{
plVersSection versReader(this);
plBaseSection baseReader(this);
plInitSectionReader* readers[] = { &versReader, &baseReader, nil };
plInitFileReader reader(readers, 4096); // Allow extra long lines
reader.SetUnhandledSectionReader(&baseReader);
// manifests don't need to be encrypted
reader.SetRequireEncrypted(false);
if (!reader.Open(stream))
return false;
// Clear out before we read
IReset();
if (!reader.Parse())
return false;
return true;
}
bool plManifest::Read(const char* filename)
{
plVersSection versReader(this);
plBaseSection baseReader(this);
plInitSectionReader* readers[] = { &versReader, &baseReader, nil };
plInitFileReader reader(readers, 4096); // Allow extra long lines
reader.SetUnhandledSectionReader(&baseReader);
// manifests don't need to be encrypted
reader.SetRequireEncrypted(false);
if (!reader.Open(filename))
return false;
// Clear out before we read
IReset();
if (!reader.Parse())
return false;
return true;
}
void plManifest::AddFile(plManifestFile* file)
{
fFiles.Append(file);
}

View File

@ -0,0 +1,134 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plAgeManifest - Collection of version-specific info about an age, such //
// as the actual files constructing it, timestamps, and //
// release versions. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plAgeManifest_h
#define _plAgeManifest_h
#include "hsTypes.h"
#include "hsTemplates.h"
#include "hsUtils.h"
#include "../plUnifiedTime/plUnifiedTime.h"
#include "../plFile/plInitFileReader.h"
#include "../plEncryption/plChecksum.h"
//// Small Container Classes for a Single File ///////////////////////////////
class plManifestFile
{
protected:
std::string fName;
std::string fServerPath;
plMD5Checksum fChecksum;
UInt32 fSize;
UInt32 fZippedSize;
UInt32 fFlags;
bool fMd5Checked;
bool fIsLocalUpToDate;
bool fLocalExists;
public:
// fUser flags
enum
{
// Sound files only
kSndFlagCacheSplit = 1<<0,
kSndFlagStreamCompressed = 1<<1,
kSndFlagCacheStereo = 1<<2,
// Any file
kFlagZipped = 1<<3,
};
plManifestFile(const char* name, const char* serverPath, const plMD5Checksum& check, UInt32 size, UInt32 zippedSize, UInt32 flags, bool md5Now = true);
virtual ~plManifestFile();
const char* GetName() const { return fName.c_str(); }
const char* GetServerPath() const { return fServerPath.c_str(); }
const plMD5Checksum& GetChecksum() const { return fChecksum; }
UInt32 GetDiskSize() const { return fSize; }
UInt32 GetDownloadSize() const { return hsCheckBits(fFlags, kFlagZipped) ? fZippedSize : fSize; }
UInt32 GetFlags() const { return fFlags; }
void DoMd5Check();
bool IsLocalUpToDate();
bool LocalExists();
};
//// Actual Manifest Class ///////////////////////////////////////////////////
class plManifest
{
protected:
UInt32 fFormatVersion;
char* fAgeName; // Mostly just for debugging
hsTArray<plManifestFile*> fFiles;
void IReset();
public:
static char* fTimeFormat; // Standard string for the printed version of our timestamps
void SetFormatVersion(UInt32 v) { fFormatVersion = v; }
void AddFile(plManifestFile* file);
plManifest();
virtual ~plManifest();
bool Read(const char* filename);
bool Read(hsStream* stream);
UInt32 GetFormatVersion() const { return fFormatVersion; }
UInt32 GetNumFiles() const { return fFiles.GetCount(); }
const plManifestFile& GetFile(UInt32 i) const { return *fFiles[i]; }
};
#endif //_plAgeManifest_h

View File

@ -0,0 +1,471 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plAgeLoader.h"
#include "hsStream.h"
#include "plgDispatch.h"
#include "hsResMgr.h"
//#include "hsTimer.h"
#include "plResPatcher.h"
#include "plBackgroundDownloader.h"
#include "process.h" // for getpid()
#include "../pnProduct/pnProduct.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnKeyedObject/plFixedKey.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../pnMessage/plClientMsg.h"
#include "../pnNetCommon/plNetApp.h"
#include "../plScene/plRelevanceMgr.h"
#include "../plResMgr/plKeyFinder.h"
#include "../plAgeDescription/plAgeDescription.h"
#include "../plSDL/plSDL.h"
#include "../plNetClient/plNetClientMgr.h"
#include "../plResMgr/plRegistryHelpers.h"
#include "../plResMgr/plRegistryNode.h"
#include "../plResMgr/plResManager.h"
#include "../plFile/plEncryptedStream.h"
/// TEMP HACK TO LOAD CONSOLE INIT FILES ON AGE LOAD
#include "../plMessage/plConsoleMsg.h"
#include "../plMessage/plLoadAvatarMsg.h"
#include "../plMessage/plAgeLoadedMsg.h"
extern hsBool gDataServerLocal;
extern hsBool gUseBackgroundDownloader;
// static
plAgeLoader* plAgeLoader::fInstance=nil;
//
// CONSTRUCT
//
plAgeLoader::plAgeLoader() :
fInitialAgeState(nil),
fFlags(0)
{
}
//
// DESTRUCT
//
plAgeLoader::~plAgeLoader()
{
delete fInitialAgeState;
fInitialAgeState=nil;
if ( PendingAgeFniFiles().size() )
plNetClientApp::StaticErrorMsg( "~plAgeLoader(): %d pending age fni files", PendingAgeFniFiles().size() );
if ( PendingPageOuts().size() )
plNetClientApp::StaticErrorMsg( "~plAgeLoader(): %d pending page outs", PendingPageOuts().size() );
ClearPageExcludeList(); // Clear our debugging exclude list, just to be tidy
if (fInstance==this)
SetInstance(nil);
}
void plAgeLoader::Shutdown()
{
}
void plAgeLoader::Init()
{
RegisterAs( kAgeLoader_KEY );
plgDispatch::Dispatch()->RegisterForExactType(plInitialAgeStateLoadedMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plClientMsg::Index(), GetKey());
if (!gDataServerLocal && gUseBackgroundDownloader)
plBackgroundDownloader::StartThread();
}
//
// STATIC
//
plAgeLoader* plAgeLoader::GetInstance()
{
return fInstance;
}
//
// STATIC
//
void plAgeLoader::SetInstance(plAgeLoader* inst)
{
fInstance=inst;
}
//
// Plasma Msg Handler
//
hsBool plAgeLoader::MsgReceive(plMessage* msg)
{
plInitialAgeStateLoadedMsg *stateMsg = plInitialAgeStateLoadedMsg::ConvertNoRef( msg );
if( stateMsg != nil )
{
// done receiving the initial state of the age from the server
return true;
}
plClientMsg* clientMsg = plClientMsg::ConvertNoRef(msg);
if (clientMsg && clientMsg->GetClientMsgFlag()==plClientMsg::kInitComplete)
{
ExecPendingAgeFniFiles(); // exec age-specific fni files
return true;
}
return plReceiver::MsgReceive(msg);
}
//
// read in the age desc file and page in/out the rooms belonging to the specified age.
// return false on error
//
//============================================================================
bool plAgeLoader::LoadAge(const char ageName[])
{
return ILoadAge(ageName);
}
//============================================================================
bool plAgeLoader::UpdateAge(const char ageName[])
{
bool result = true;
if (!gDataServerLocal)
{
plResPatcher myPatcher(ageName);
result = myPatcher.Update();
}
return result;
}
//============================================================================
void plAgeLoader::NotifyAgeLoaded( bool loaded )
{
if ( loaded )
fFlags &= ~kLoadingAge;
else
fFlags &= ~kUnLoadingAge;
plAgeLoadedMsg * msg = TRACKED_NEW plAgeLoadedMsg;
msg->fLoaded = loaded;
msg->Send();
}
//// ILoadAge ////////////////////////////////////////////////////////////////
// Does the loading-specific stuff for queueing an age to load
bool plAgeLoader::ILoadAge(const char ageName[])
{
plNetClientApp* nc = plNetClientApp::GetInstance();
ASSERT(!nc->GetFlagsBit(plNetClientApp::kPlayingGame));
StrCopy(fAgeName, ageName, arrsize(fAgeName));
nc->DebugMsg( "Net: Loading age %s", fAgeName);
if ((fFlags & kLoadMask) != 0)
ErrorFatal(__LINE__, __FILE__, "Fatal Error:\nAlready loading or unloading an age.\n%S will now exit.", ProductShortName());
fFlags |= kLoadingAge;
plAgeBeginLoadingMsg* ageBeginLoading = TRACKED_NEW plAgeBeginLoadingMsg();
ageBeginLoading->Send();
///////////////////////////////////////////////////////
/// Step 1: Update all of the dat files for this age
/*
UpdateAge(fAgeName);
*/
/// Step 2: Load the keys for this age, so we can find sceneNodes for them
// exec age .fni file when data is done loading
char consoleIniName[ 256 ];
sprintf( consoleIniName, "dat\\%s.fni", fAgeName);
fPendingAgeFniFiles.push_back( consoleIniName );
char csvName[256];
sprintf(csvName, "dat\\%s.csv", fAgeName);
fPendingAgeCsvFiles.push_back(csvName);
plSynchEnabler p( false ); // turn off dirty tracking while in this function
hsStream* stream=GetAgeDescFileStream(fAgeName);
if (!stream)
{
nc->ErrorMsg("Failed loading age. Age desc file %s has nil stream", fAgeName);
fFlags &= ~kLoadingAge;
return false;
}
plAgeDescription ad;
ad.Read(stream);
ad.SetAgeName(fAgeName);
stream->Close();
delete stream;
ad.SeekFirstPage();
plAgePage *page;
plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY );
// Copy, exclude pages we want excluded, and collect our scene nodes
fCurAgeDescription.CopyFrom(ad);
while( ( page = ad.GetNextPage() ) != nil )
{
if( IsPageExcluded( page, fAgeName) )
continue;
plKey roomKey = plKeyFinder::Instance().FindSceneNodeKey( fAgeName, page->GetName() );
if( roomKey != nil )
AddPendingPageInRoomKey( roomKey );
}
ad.SeekFirstPage();
// Tell the client to load-and-hold all the keys for this age, to make the loading process work better
plClientMsg *loadAgeKeysMsg = TRACKED_NEW plClientMsg( plClientMsg::kLoadAgeKeys );
loadAgeKeysMsg->SetAgeName( fAgeName);
loadAgeKeysMsg->Send( clientKey );
//
// Load the Age's SDL Hook object (and it's python modifier)
//
plUoid oid=nc->GetAgeSDLObjectUoid(fAgeName);
plKey ageSDLObjectKey = hsgResMgr::ResMgr()->FindKey(oid);
if (ageSDLObjectKey)
hsgResMgr::ResMgr()->AddViaNotify(ageSDLObjectKey, TRACKED_NEW plGenRefMsg(nc->GetKey(), plRefMsg::kOnCreate, -1,
plNetClientMgr::kAgeSDLHook), plRefFlags::kActiveRef);
int nPages = 0;
plClientMsg* pMsg1 = TRACKED_NEW plClientMsg(plClientMsg::kLoadRoom);
pMsg1->SetAgeName(fAgeName);
// Loop and ref!
while( ( page = ad.GetNextPage() ) != nil )
{
if( IsPageExcluded( page, fAgeName) )
{
nc->DebugMsg( "\tExcluding page %s\n", page->GetName() );
continue;
}
nPages++;
pMsg1->AddRoomLoc(ad.CalcPageLocation(page->GetName()));
nc->DebugMsg("\tPaging in room %s\n", page->GetName());
}
pMsg1->Send(clientKey);
// Send the client a message to let go of the extra keys it was holding on to
plClientMsg *dumpAgeKeys = TRACKED_NEW plClientMsg( plClientMsg::kReleaseAgeKeys );
dumpAgeKeys->SetAgeName( fAgeName);
dumpAgeKeys->Send( clientKey );
if ( nPages==0 )
{
// age is done loading because it has no pages?
fFlags &= ~kLoadingAge;
}
return true;
}
//// plUnloadAgeCollector ////////////////////////////////////////////////////
// Registry page iterator to collect all the loaded pages of a given age
// Note: we have to do an IterateAllPages(), since we want to also catch
// pages that are partially loaded, which are skipped in the vanilla
// IteratePages() call.
class plUnloadAgeCollector : public plRegistryPageIterator
{
public:
hsTArray<plRegistryPageNode *> fPages;
const char *fAge;
plUnloadAgeCollector( const char *a ) : fAge( a ) {}
virtual hsBool EatPage( plRegistryPageNode *page )
{
if( fAge && stricmp( page->GetPageInfo().GetAge(), fAge ) == 0 )
{
fPages.Append( page );
}
return true;
}
};
//// IUnloadAge //////////////////////////////////////////////////////////////
// Does the UNloading-specific stuff for queueing an age to unload.
// Far simpler that ILoadAge :)
bool plAgeLoader::IUnloadAge()
{
plNetClientApp* nc = plNetClientApp::GetInstance();
nc->DebugMsg( "Net: Unloading age %s", fAgeName);
hsAssert( (fFlags & kLoadMask)==0, "already loading or unloading an age?");
fFlags |= kUnLoadingAge;
plAgeBeginLoadingMsg* msg = TRACKED_NEW plAgeBeginLoadingMsg();
msg->fLoading = false;
msg->Send();
// Note: instead of going from the .age file, we just want a list of what
// is REALLY paged in for this age. So ask the resMgr!
plUnloadAgeCollector collector( fAgeName);
// WARNING: unsafe cast here, but it's ok, until somebody is mean and makes a non-plResManager resMgr
( (plResManager *)hsgResMgr::ResMgr() )->IterateAllPages( &collector );
// Dat was easy...
plKey clientKey = hsgResMgr::ResMgr()->FindKey( kClient_KEY );
// Build up a list of all the rooms we're going to page out
plKeyVec newPageOuts;
int i;
for( i = 0; i < collector.fPages.GetCount(); i++ )
{
plRegistryPageNode *page = collector.fPages[ i ];
plKey roomKey = plKeyFinder::Instance().FindSceneNodeKey( page->GetPageInfo().GetLocation() );
if( roomKey != nil && roomKey->ObjectIsLoaded() )
{
nc->DebugMsg( "\tPaging out room %s\n", page->GetPageInfo().GetPage() );
newPageOuts.push_back(roomKey);
}
}
// Put them in our pending page outs
for( i = 0; i < newPageOuts.size(); i++ )
fPendingPageOuts.push_back(newPageOuts[i]);
// ...then send the unload messages. That way we ensure the list is complete
// before any messages get processed
for( i = 0; i < newPageOuts.size(); i++ )
{
plClientMsg *pMsg1 = TRACKED_NEW plClientMsg( plClientMsg::kUnloadRoom );
pMsg1->AddRoomLoc(newPageOuts[i]->GetUoid().GetLocation());
pMsg1->Send( clientKey );
}
if ( newPageOuts.size()==0 )
{
// age is done unloading because it has no pages?
NotifyAgeLoaded( false );
}
return true;
}
void plAgeLoader::ExecPendingAgeFniFiles()
{
int i;
for (i=0;i<PendingAgeFniFiles().size(); i++)
{
plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kExecuteFile, fPendingAgeFniFiles[i].c_str() );
plgDispatch::MsgSend( cMsg );
}
fPendingAgeFniFiles.clear();
}
void plAgeLoader::ExecPendingAgeCsvFiles()
{
int i;
for (i=0;i<PendingAgeCsvFiles().size(); i++)
{
hsStream* stream = plEncryptedStream::OpenEncryptedFile(fPendingAgeCsvFiles[i].c_str());
if (stream)
{
plRelevanceMgr::Instance()->ParseCsvInput(stream);
stream->Close();
delete stream;
}
}
fPendingAgeCsvFiles.clear();
}
//
// return alloced stream or nil
// static
//
hsStream* plAgeLoader::GetAgeDescFileStream(const char* ageName)
{
if (!ageName)
return nil;
char ageDescFileName[256];
sprintf(ageDescFileName, "dat\\%s.age", ageName);
hsStream* stream = plEncryptedStream::OpenEncryptedFile(ageDescFileName);
if (!stream)
{
char str[256];
sprintf(str, "Can't find age desc file %s", ageDescFileName);
hsAssert(false, str);
return nil;
}
return stream;
}
//
// sent from server with joinAck
//
void plAgeLoader::ISetInitialAgeState(plStateDataRecord* s)
{
hsAssert(fInitialAgeState != s, "duplicate initial age state");
delete fInitialAgeState;
fInitialAgeState=s;
}

View File

@ -0,0 +1,145 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAgeLoader_h
#define plAgeLoader_h
#include "hsTypes.h"
#include "hsStlUtils.h"
#include "../pnUtils/pnUtils.h"
#include "../pnNetBase/pnNetBase.h"
#include "../pnKeyedObject/hsKeyedObject.h"
#include "../pnKeyedObject/plKey.h"
#include "../plAgeDescription/plAgeDescription.h"
#include "../plUUID/plUUID.h"
//
// A singleton class which manages loading and unloading ages and operations associated with that
//
// fwd decls
class plStateDataRecord;
class plMessage;
class plOperationProgress;
class plAgeLoader : public hsKeyedObject
{
friend class plNetClientMsgHandler;
friend class plNetClientJoinTask;
private:
typedef std::vector<plKey> plKeyVec;
typedef std::vector<std::string> plStringVec;
enum Flags
{
kLoadingAge = 0x1,
kUnLoadingAge = 0x2,
kLoadMask = (kLoadingAge | kUnLoadingAge)
};
static plAgeLoader* fInstance;
UInt32 fFlags;
plStringVec fPendingAgeFniFiles; // list of age .fni files to be parsed
plStringVec fPendingAgeCsvFiles; // list of age .csv files to be parsed
plKeyVec fPendingPageIns; // keys of rooms which are currently being paged in.
plKeyVec fPendingPageOuts; // keys of rooms which are currently being paged out.
plAgeDescription fCurAgeDescription;
plStateDataRecord* fInitialAgeState;
char fAgeName[kMaxAgeNameLength];
bool ILoadAge(const char ageName[]);
bool IUnloadAge();
void ISetInitialAgeState(plStateDataRecord* s); // sent from server with joinAck
const plStateDataRecord* IGetInitialAgeState() const { return fInitialAgeState; }
public:
plAgeLoader();
~plAgeLoader();
CLASSNAME_REGISTER( plAgeLoader);
GETINTERFACE_ANY( plAgeLoader, hsKeyedObject);
static plAgeLoader* GetInstance();
static void SetInstance(plAgeLoader* inst);
static hsStream* GetAgeDescFileStream(const char* ageName);
void Init();
void Shutdown();
hsBool MsgReceive(plMessage* msg);
bool LoadAge(const char ageName[]);
bool UnloadAge() { return IUnloadAge(); }
bool UpdateAge(const char ageName[]);
void NotifyAgeLoaded( bool loaded );
const plKeyVec& PendingPageOuts() const { return fPendingPageOuts; }
const plKeyVec& PendingPageIns() const { return fPendingPageIns; }
const plStringVec& PendingAgeCsvFiles() const { return fPendingAgeCsvFiles; }
const plStringVec& PendingAgeFniFiles() const { return fPendingAgeFniFiles; }
void AddPendingPageInRoomKey(plKey r);
bool RemovePendingPageInRoomKey(plKey r);
bool IsPendingPageInRoomKey(plKey p, int* idx=nil);
void ExecPendingAgeFniFiles();
void ExecPendingAgeCsvFiles();
// Fun debugging exclude commands (to prevent certain pages from loading)
void ClearPageExcludeList( void );
void AddExcludedPage( const char *pageName, const char *ageName = nil );
bool IsPageExcluded( const plAgePage *page, const char *ageName = nil );
const plAgeDescription &GetCurrAgeDesc( void ) const { return fCurAgeDescription; }
// paging
void FinishedPagingInRoom(plKey* rmKey, int numRms); // call when finished paging in/out a room
void StartPagingOutRoom(plKey* rmKey, int numRms); // call when starting to page in/out a room
void FinishedPagingOutRoom(plKey* rmKey, int numRms);
// Called on page-in-hold rooms, since we don't want them actually paging out in the NCM (i.e. sending info to the server)
void IgnorePagingOutRoom(plKey* rmKey, int numRms);
bool IsLoadingAge(){ return (fFlags & (kUnLoadingAge | kLoadingAge)); }
};
#endif // plAgeLoader_h

View File

@ -0,0 +1,50 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAgeLoaderCreatable_inc
#define plAgeLoaderCreatable_inc
#include "../pnFactory/plCreator.h"
#include "plAgeLoader.h"
REGISTER_CREATABLE( plAgeLoader);
#endif // plAgeLoaderCreatable_inc

View File

@ -0,0 +1,337 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plAgeLoader.h"
#include "hsTimer.h"
#include "hsResMgr.h"
#include "plgDispatch.h"
#include <algorithm>
#include "../pnNetCommon/plNetApp.h"
#include "../pnKeyedObject/plKey.h"
#include "../plMessage/plAgeLoadedMsg.h"
#include "../plNetMessage/plNetMessage.h"
#include "../plProgressMgr/plProgressMgr.h"
#include "../plSDL/plSDL.h"
#include "../pnDispatch/plDispatch.h"
#include "../plResMgr/plResManager.h"
#include "../plNetClient/plNetClientMgr.h"
//
// if room is reserved or for animations, don't report it to the server.
// the server only cares about rooms which have real, networked objects in them.
// The server already assigns a global room to everyone for things like avatar and builtin state .
//
bool ReportRoomToServer(const plKey &key)
{
plLocation keyLoc=key->GetUoid().GetLocation();
bool skip=(keyLoc.IsReserved() || keyLoc.IsVirtual() ||
// HACK ALERT - replace with new uoid type flags
(key->GetName() &&
(!strnicmp(key->GetName(), "global", 6) ||
strstr(key->GetName(), "_Male") ||
strstr(key->GetName(), "_Female")
)
)
);
if (skip)
hsLogEntry(plNetApp::StaticDebugMsg("Not reporting room %s to server, reserved=%d, virtual=%d",
key->GetName(), keyLoc.IsReserved(), keyLoc.IsVirtual()));
return !skip;
}
//
// call when finished paging in a room.
//
void plAgeLoader::FinishedPagingInRoom(plKey* rmKey, int numRms)
{
if (numRms==0)
return;
unsigned pendingPageIns = PendingPageIns().size();
plNetClientApp* nc = plNetClientApp::GetInstance();
// Send a msg to the server indicating that we have this room paged in
plNetMsgPagingRoom * pagingMsg = TRACKED_NEW plNetMsgPagingRoom;
pagingMsg->SetNetProtocol(kNetProtocolCli2Game);
int i;
for(i=0;i<numRms;i++)
{
plKey key=rmKey[i];
if (!RemovePendingPageInRoomKey(key)) // room is done paging in
continue; // we didn't queue this room
if (!ReportRoomToServer(key))
continue;
pagingMsg->AddRoom(key);
hsLogEntry(nc->DebugMsg("\tSending PageIn/RequestState msg, room=%s\n", key->GetName()));
}
if( pagingMsg->GetNumRooms() > 0 ) // all rooms were reserved
{
plNetClientMgr * mgr = plNetClientMgr::GetInstance();
mgr->AddPendingPagingRoomMsg( pagingMsg );
}
else
delete pagingMsg;
// If any of these rooms were queued for load by us, then we may be done loading the age.
if (pendingPageIns != PendingPageIns().size())
{
bool ageLoaded = (PendingPageIns().size()==0) && (fFlags & kLoadingAge);
if (ageLoaded)
{
plAgeLoaded2Msg * msg = TRACKED_NEW plAgeLoaded2Msg;
msg->Send();
// join task will call NotifyAgeLoaded for us later
}
}
}
//
// called by the client when a room is finished paging out
//
void plAgeLoader::FinishedPagingOutRoom(plKey* rmKey, int numRms)
{
plNetClientApp* nc = plNetClientApp::GetInstance();
nc->StayAlive(hsTimer::GetSysSeconds()); // alive
int i;
for(i=0;i<numRms;i++)
{
plKeyVec::iterator found = std::find( fPendingPageOuts.begin(), fPendingPageOuts.end(), rmKey[ i ] );
if( found != fPendingPageOuts.end() )
{
fPendingPageOuts.erase( found );
nc->DebugMsg("Finished paging out room %s", rmKey[i]->GetName());
}
}
if (PendingPageOuts().size() == 0 && (fFlags & kUnLoadingAge))
{
NotifyAgeLoaded( false );
}
}
//
// call when starting to page out a room.
// allows server to transfer ownership of room objects to someone else.
//
void plAgeLoader::StartPagingOutRoom(plKey* rmKey, int numRms)
{
plNetClientApp* nc = plNetClientApp::GetInstance();
plNetMsgPagingRoom pagingMsg;
pagingMsg.SetNetProtocol(kNetProtocolCli2Game);
pagingMsg.SetPagingOut(true);
int i;
for(i=0;i<numRms;i++)
{
plKey key=rmKey[i];
if (!ReportRoomToServer(key))
continue;
pagingMsg.AddRoom(rmKey[i]);
nc->DebugMsg("\tSending PageOut msg, room=%s", rmKey[i]->GetName());
}
if (!pagingMsg.GetNumRooms()) // all rooms were reserved
return;
nc->SendMsg(&pagingMsg);
}
// Client telling us that this page isn't going to get the start/finish combo
// on page out, most likely because it was a load-and-hold, not a load. So take
// it from our pending list but don't actually process it
// Note: right now it's just a dup of FinishPagingOutRoom(), but since the latter
// might change later, we go ahead and dup to avoid unnecessary bugs later
void plAgeLoader::IgnorePagingOutRoom(plKey* rmKey, int numRms)
{
plNetClientApp* nc = plNetClientApp::GetInstance();
nc->StayAlive(hsTimer::GetSysSeconds()); // alive
int i;
for(i=0;i<numRms;i++)
{
plKeyVec::iterator found = std::find( fPendingPageOuts.begin(), fPendingPageOuts.end(), rmKey[ i ] );
if( found != fPendingPageOuts.end() )
{
fPendingPageOuts.erase( found );
nc->DebugMsg("Ignoring paged out room %s", rmKey[i]->GetName());
}
}
if (PendingPageOuts().size() == 0 && (fFlags & kUnLoadingAge))
{
NotifyAgeLoaded( false );
}
}
///////////////////////////////////
bool plAgeLoader::IsPendingPageInRoomKey(plKey pKey, int *idx)
{
if (pKey)
{
plKeyVec::iterator result=std::find(fPendingPageIns.begin(), fPendingPageIns.end(), pKey);
bool found = result!=fPendingPageIns.end();
if (idx)
*idx = found ? result-fPendingPageIns.begin() : -1;
return found;
}
return false;
}
void plAgeLoader::AddPendingPageInRoomKey(plKey pKey)
{
if (!IsPendingPageInRoomKey(pKey))
{
fPendingPageIns.push_back(pKey);
}
}
bool plAgeLoader::RemovePendingPageInRoomKey(plKey pKey)
{
int idx;
if (IsPendingPageInRoomKey(pKey, &idx))
{
fPendingPageIns.erase(fPendingPageIns.begin()+idx); // remove key from list
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////////
//// Page Exclusion //////////////////////////////////////////////////////////
// //
// Fun debugging exclude commands (to prevent certain pages from loading) //
// //
//////////////////////////////////////////////////////////////////////////////
class plExcludePage
{
public:
char *fPageName;
char *fAgeName;
plExcludePage() { fPageName = nil; fAgeName = nil; }
plExcludePage( char *p, char *a )
{
fPageName = p;
fAgeName = a;
}
};
static hsTArray<plExcludePage> sExcludeList;
void plAgeLoader::ClearPageExcludeList( void )
{
int i;
for( i = 0; i < sExcludeList.GetCount(); i++ )
{
delete [] sExcludeList[ i ].fPageName;
delete [] sExcludeList[ i ].fAgeName;
}
}
void plAgeLoader::AddExcludedPage( const char *pageName, const char *ageName )
{
char *p = hsStrcpy( pageName );
char *a = nil;
if( ageName != nil )
a = hsStrcpy( ageName );
sExcludeList.Append( plExcludePage( p, a ) );
}
bool plAgeLoader::IsPageExcluded( const plAgePage *page, const char *ageName )
{
// check page flags
if (page->GetFlags() & plAgePage::kPreventAutoLoad)
return true;
// check exclude list
const char* pageName = page->GetName();
int i;
for( i = 0; i < sExcludeList.GetCount(); i++ )
{
if( stricmp( pageName, sExcludeList[ i ].fPageName ) == 0 )
{
if( ageName == nil || sExcludeList[ i ].fAgeName == nil ||
stricmp( ageName, sExcludeList[ i ].fAgeName ) == 0 )
{
return true;
}
}
}
// Check if pages are excluded due to age SDL vars
if (page->GetFlags() & plAgePage::kLoadIfSDLPresent)
{
if (IGetInitialAgeState())
{
plSimpleStateVariable* sdVar = IGetInitialAgeState()->FindVar(pageName);
if (!sdVar)
return true; // no sdl var, exclude
bool value;
sdVar->Get(&value);
return value ? false : true; // exclude if var is false
}
else
return true; // no age state, exclude
}
return false;
}

View File

@ -0,0 +1,545 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plBackgroundDownloader.h"
#include <process.h>
#include "../pnUtils/pnUtils.h"
#include "../pnNetBase/pnNetBase.h"
#include "../plEncryption/plChecksum.h"
#include "../NucleusLib/inc/hsResMgr.h"
#include "../plAgeDescription/plAgeManifest.h"
#include "../plResMgr/plResManager.h"
#include "../plFile/plFileUtils.h"
#include "../plFile/plEncryptedStream.h"
#include "../plCompression/plZlibStream.h"
#include "../plAudioCore/plAudioFileReader.h"
#include "../plProgressMgr/plProgressMgr.h"
#include "../pnAsyncCore/pnAsyncCore.h"
#include "../pnNetCli/pnNetCli.h"
#include "../plNetGameLib/plNetGameLib.h"
#include "../pnDispatch/plDispatch.h"
#include "../plStatusLog/plStatusLog.h"
static const unsigned kMaxDownloadTries = 10;
static const wchar s_manifest[] = L"AllAges";
plBackgroundDownloader* plBackgroundDownloader::fInstance = NULL;
hsBool gUseBackgroundDownloader = false;
//============================================================================
enum DownloaderLogType
{
kHeader,
kInfo,
kMajorStatus,
kStatus,
kError,
};
void BackgroundDownloaderLog(DownloaderLogType type, const char* format, ...)
{
UInt32 color = 0;
switch (type)
{
case kHeader: color = plStatusLog::kWhite; break;
case kInfo: color = plStatusLog::kBlue; break;
case kMajorStatus: color = plStatusLog::kYellow; break;
case kStatus: color = plStatusLog::kGreen; break;
case kError: color = plStatusLog::kRed; break;
}
static plStatusLog* gStatusLog = nil;
if (!gStatusLog)
{
gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(
20,
"bgdownload.log",
plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kDeleteForMe);
}
va_list args;
va_start(args, format);
gStatusLog->AddLineV(color, format, args);
va_end(args);
}
//============================================================================
class plBGDownloadStream : public plZlibStream
{
public:
plBGDownloadStream() : plZlibStream() {}
virtual ~plBGDownloadStream() {}
virtual UInt32 Write(UInt32 byteCount, const void* buffer);
};
UInt32 plBGDownloadStream::Write(UInt32 byteCount, const void* buffer)
{
return plZlibStream::Write(byteCount, buffer);
}
//============================================================================
static void DownloadFileCallback(ENetError result, void* param, const wchar filename[], hsStream* writer)
{
plBackgroundDownloader* bgdownloader = (plBackgroundDownloader*)param;
// Retry download unless shutting down or file not found
switch (result) {
case kNetSuccess:
writer->Close();
bgdownloader->DoneWithFile(true);
break;
case kNetErrFileNotFound:
case kNetErrRemoteShutdown:
writer->Close();
bgdownloader->DoneWithFile(false);
break;
default:
writer->Rewind();
NetCliFileDownloadRequest(
filename,
writer,
DownloadFileCallback,
param
);
break;
}
}
static void ManifestCallback(ENetError result, void* param, const wchar group[], const NetCliFileManifestEntry manifest[], unsigned entryCount)
{
plBackgroundDownloader* bgdownloader = (plBackgroundDownloader*)param;
bgdownloader->DoneWithManifest(result == kNetSuccess, manifest, entryCount);
}
//============================================================================
plBackgroundDownloader* plBackgroundDownloader::GetInstance()
{
return fInstance;
}
void plBackgroundDownloader::ThreadMain(void * param)
{
Init();
plBackgroundDownloader::GetInstance()->Run();
plBackgroundDownloader::GetInstance()->CleanUp();
Shutdown();
}
void plBackgroundDownloader::StartThread()
{
_beginthread(plBackgroundDownloader::ThreadMain, 0, NULL);
}
void plBackgroundDownloader::Init()
{
fInstance = TRACKED_NEW plBackgroundDownloader();
}
void plBackgroundDownloader::Shutdown()
{
delete fInstance;
fInstance = NULL;
}
plBackgroundDownloader::plBackgroundDownloader()
{
BackgroundDownloaderLog(kHeader, "--- Starting background download ---");
fBGDownloaderRun = CreateEvent(
NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is signaled
NULL // unnamed
);
fBGDownloaderIsPaused = CreateEvent(
NULL, // default security attributes
FALSE, // manual-reset event
TRUE, // initial state is signaled
NULL // unnamed
);
}
plBackgroundDownloader::~plBackgroundDownloader()
{
HANDLE runHandle = fBGDownloaderRun;
fBGDownloaderRun = NULL;
CloseHandle(runHandle);
HANDLE pausedHandle = fBGDownloaderIsPaused;
fBGDownloaderIsPaused = NULL;
CloseHandle(pausedHandle);
BackgroundDownloaderLog(kHeader, "--- Background download done ---");
}
UInt32 plBackgroundDownloader::IGetDownloadSize()
{
if (!IGetDataManifest())
return 0;
UInt32 downloadSize = 0;
UInt32 downloadFiles = 0;
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
{
plManifestFile* mfsFile = (*i);
if (!mfsFile->IsLocalUpToDate())
{
downloadFiles++;
downloadSize += mfsFile->GetDownloadSize();
}
}
BackgroundDownloaderLog(kInfo, "Got download stats, %d files, %d bytes", downloadFiles, downloadSize);
return downloadSize;
}
bool plBackgroundDownloader::CheckFreeSpace(UInt32 bytesNeeded)
{
#ifdef HS_BUILD_FOR_WIN32
ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes;
if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL))
{
neededBytes.HighPart = 0;
neededBytes.LowPart = bytesNeeded;
if (neededBytes.QuadPart > freeBytesAvailable.QuadPart)
{
BackgroundDownloaderLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded);
return false;
}
}
#endif // HS_BUILD_FOR_WIN32
return true;
}
bool plBackgroundDownloader::IDecompressSound(plManifestFile* mfsFile, bool noOverwrite)
{
UInt32 flags = mfsFile->GetFlags();
if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "ogg") == 0)
{
plAudioFileReader* reader = plAudioFileReader::CreateReader(mfsFile->GetName(), plAudioCore::kAll, plAudioFileReader::kStreamNative);
if (!reader)
{
BackgroundDownloaderLog(kInfo, "Unable to create audio file reader for %s", mfsFile->GetName());
return false;
}
UInt32 size = reader->GetDataSize();
delete reader;
// Make sure we have enough free space
if (!CheckFreeSpace(size))
return false;
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit))
plAudioFileReader::CacheFile(mfsFile->GetName(), true, noOverwrite);
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo))
plAudioFileReader::CacheFile(mfsFile->GetName(), false, noOverwrite);
}
return true;
}
bool plBackgroundDownloader::Run()
{
// Wait to be signaled that we've gotten at least as far as the startup age
WaitForSingleObject(fBGDownloaderRun, INFINITE);
IGetDataManifest();
plFileUtils::CreateDir("dat");
plFileUtils::CreateDir("sfx");
bool result = true;
plResManager* resMgr = ((plResManager*)hsgResMgr::ResMgr());
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
{
plManifestFile* mfsFile = (*i);
if (!mfsFile->IsLocalUpToDate())
{
if (!CheckFreeSpace(mfsFile->GetDiskSize()))
return false;
FileType type = IGetFile(mfsFile);
if (type == kPrp)
{
// Checks for existence before attempting to remove
resMgr->RemoveSinglePage(mfsFile->GetName());
if (!resMgr->FindSinglePage(mfsFile->GetName()))
{
resMgr->AddSinglePage(mfsFile->GetName());
}
}
else if (type == kOther)
{
if (!IDecompressSound(mfsFile, false))
{
char text[MAX_PATH];
StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName());
BackgroundDownloaderLog(kInfo, text );
hsAssert(false, text);
result = false;
}
}
else
{
char text[MAX_PATH];
StrPrintf(text, arrsize(text), "Failed downloading file: %s", mfsFile->GetName());
BackgroundDownloaderLog(kInfo, text );
hsAssert(false, text);
result = false;
}
}
}
return result;
}
void plBackgroundDownloader::CleanUp()
{
BackgroundDownloaderLog(kMajorStatus, "Cleaning up background downloader..." );
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
{
plManifestFile* file = (*i);
delete file;
}
fMfsVec.clear();
}
void plBackgroundDownloader::Pause()
{
if (fBGDownloaderRun != NULL && fBGDownloaderIsPaused != NULL)
{
ResetEvent(fBGDownloaderRun);
WaitForSingleObject(fBGDownloaderIsPaused, INFINITE);
BackgroundDownloaderLog(kStatus, "--- Background download paused ---");
}
}
void plBackgroundDownloader::UnPause()
{
if (fBGDownloaderRun != NULL && fBGDownloaderIsPaused != NULL)
{
SetEvent(fBGDownloaderRun);
BackgroundDownloaderLog(kStatus, "--- Background download resumed ---");
}
}
plBackgroundDownloader::FileType plBackgroundDownloader::IGetFile(const plManifestFile* mfsFile)
{
BackgroundDownloaderLog(kInfo, " Setting up to download file %s", mfsFile->GetName());
bool downloadDone = false;
wchar* wServerPath = hsStringToWString(mfsFile->GetServerPath());
int numTries = 0;
while (!downloadDone)
{
if (WaitForSingleObject(fBGDownloaderRun, 0) == WAIT_TIMEOUT)
SignalObjectAndWait(fBGDownloaderIsPaused, fBGDownloaderRun, INFINITE, FALSE);
if (numTries >= kMaxDownloadTries)
{
BackgroundDownloaderLog(kInfo, " Max download tries exceeded (%d). Aborting download...", kMaxDownloadTries);
return kFail;
}
plBGDownloadStream* downloadStream = TRACKED_NEW plBGDownloadStream();
if (!downloadStream->Open(mfsFile->GetName(), "wb"))
{
BackgroundDownloaderLog(kInfo, " Unable to create file. Aborting download...");
return kFail;
}
BackgroundDownloaderLog(kInfo, " Downloading file %s...", mfsFile->GetName());
fSuccess = false;
fDoneWithFile = false;
NetCliFileDownloadRequest(
wServerPath,
downloadStream,
DownloadFileCallback,
this
);
while (!fDoneWithFile) {
AsyncSleep(100);
}
if (!fSuccess) {
// remove partial file and die (server didn't have the file or server is shutting down)
plFileUtils::RemoveFile(mfsFile->GetName(), true);
BackgroundDownloaderLog(kError, " File %s failed to download.", mfsFile->GetName());
}
else {
AsyncSleep(100);
if (downloadStream->DecompressedOk()) {
BackgroundDownloaderLog(kInfo, " Decompress successful." );
// download and decompress successful, do a md5 check on the resulting file
plMD5Checksum localMD5(mfsFile->GetName());
if (localMD5 != mfsFile->GetChecksum()) {
plFileUtils::RemoveFile(mfsFile->GetName(), true);
BackgroundDownloaderLog(kError, " File %s MD5 check FAILED.", mfsFile->GetName());
// don't set downloadDone so we attempt to re-download from the server
}
else {
BackgroundDownloaderLog(kInfo, " MD5 check succeeded.");
downloadDone = true;
}
}
else {
plFileUtils::RemoveFile(mfsFile->GetName(), true);
BackgroundDownloaderLog(kError, " File %s failed to decompress.", mfsFile->GetName());
// don't set downloadDone so we attempt to re-download from the server
}
}
delete downloadStream;
++numTries;
}
delete [] wServerPath;
if (!fSuccess)
return kFail;
if (stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0)
return kPrp;
return kOther;
}
bool plBackgroundDownloader::IGetDataManifest()
{
if (fMfsVec.size() > 0)
return true;
BackgroundDownloaderLog(kMajorStatus, "Downloading new manifest from data server..." );
fSuccess = false;
unsigned numTries = 0;
while (!fSuccess)
{
numTries++;
fDoneWithFile = false;
NetCliFileManifestRequest(ManifestCallback, this, s_manifest);
while (!fDoneWithFile)
{
NetClientUpdate();
plgDispatch::Dispatch()->MsgQueueProcess();
AsyncSleep(10);
}
if (!fSuccess)
{
fMfsVec.clear(); // clear out any bad data
if (numTries > kMaxDownloadTries)
break; // abort
}
}
if (fSuccess)
BackgroundDownloaderLog(kStatus, "New manifest read; number of files: %d", fMfsVec.size() );
else
BackgroundDownloaderLog(kStatus, "Failed to download manifest after trying %d times", kMaxDownloadTries);
return fSuccess;
}
void plBackgroundDownloader::DoneWithFile(bool success)
{
fDoneWithFile = true;
fSuccess = success;
}
void plBackgroundDownloader::DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount)
{
BackgroundDownloaderLog(kStatus, "New age manifest received. Reading...");
if (success)
{
for (unsigned i = 0; i < entryCount; i++)
{
char* name = hsWStringToString(manifestEntires[i].clientName);
char* serverPath = hsWStringToString(manifestEntires[i].downloadName);
char* md5Str = hsWStringToString(manifestEntires[i].md5);
int size = manifestEntires[i].fileSize;
int zipsize = manifestEntires[i].zipSize;
int flags = manifestEntires[i].flags;
if (stricmp(plFileUtils::GetFileExt(name), "gz"))
flags |= plManifestFile::kFlagZipped; // add zipped flag if necessary
plMD5Checksum sum;
sum.SetFromHexString(md5Str);
fMfsVec.push_back(TRACKED_NEW plManifestFile(name, serverPath, sum, size, zipsize, flags, false));
delete [] name;
delete [] serverPath;
delete [] md5Str;
}
}
fDoneWithFile = true;
fSuccess = success;
}

View File

@ -0,0 +1,98 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plBackgroundDownloader_h_inc
#define plBackgroundDownloader_h_inc
#include <vector>
#include "hsTypes.h"
class plManifestFile;
struct NetCliFileManifestEntry;
class plBackgroundDownloader
{
protected:
static plBackgroundDownloader* fInstance;
static void Init();
static void Shutdown();
static void ThreadMain(void * param);
plBackgroundDownloader();
~plBackgroundDownloader();
HANDLE fBGDownloaderRun;
HANDLE fBGDownloaderIsPaused;
enum FileType {kFail, kPrp, kOther};
typedef std::vector<plManifestFile*> MfsFileVec;
MfsFileVec fMfsVec;
public:
bool fDoneWithFile;
bool fSuccess;
bool IGetDataManifest();
FileType IGetFile(const plManifestFile* mfsFile);
UInt32 IGetDownloadSize();
bool IDecompressSound(plManifestFile* mfsFile, bool noOverwrite = false);
public:
static plBackgroundDownloader* GetInstance();
static void StartThread();
bool Run();
void CleanUp();
void Pause();
void UnPause();
static bool CheckFreeSpace(UInt32 bytesNeeded);
// called by download callbacks to tell it we are done with the current file
void DoneWithFile(bool success);
void DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount);
};
#endif //plBackgroundDownloader_h_inc

View File

@ -0,0 +1,518 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plResPatcher.h"
#include "../NucleusLib/inc/hsResMgr.h"
#include "../plAgeDescription/plAgeManifest.h"
#include "../plResMgr/plResManager.h"
#include "../plFile/plFileUtils.h"
#include "../plFile/plEncryptedStream.h"
#include "../plCompression/plZlibStream.h"
#include "../plAudioCore/plAudioFileReader.h"
#include "../plProgressMgr/plProgressMgr.h"
#include "../pnAsyncCore/pnAsyncCore.h"
#include "../pnNetCli/pnNetCli.h"
#include "../plNetGameLib/plNetGameLib.h"
#include "../pnDispatch/plDispatch.h"
#include "../plStatusLog/plStatusLog.h"
static const unsigned kMaxDownloadTries = 10;
//////////////////////////////////////////////////////////////////////////////
class plDownloadStream : public plZlibStream
{
private:
plOperationProgress* fProgress;
unsigned fBytesReceived;
public:
plDownloadStream(plOperationProgress* progress) : fProgress(progress), fBytesReceived(0), plZlibStream() {}
virtual ~plDownloadStream() {}
virtual UInt32 Write(UInt32 byteCount, const void* buffer);
void RewindProgress() {fProgress->Increment(-(hsScalar)fBytesReceived);} // rewind the progress bar by as far as we got
};
UInt32 plDownloadStream::Write(UInt32 byteCount, const void* buffer)
{
fProgress->Increment((hsScalar)byteCount);
fBytesReceived += byteCount;
return plZlibStream::Write(byteCount, buffer);
}
//////////////////////////////////////////////////////////////////////////////
static void DownloadFileCallback(ENetError result, void* param, const wchar filename[], hsStream* writer)
{
plResPatcher* patcher = (plResPatcher*)param;
// Retry download unless shutting down or file not found
switch (result) {
case kNetSuccess:
writer->Close();
patcher->DoneWithFile(true);
break;
case kNetErrFileNotFound:
case kNetErrRemoteShutdown:
writer->Close();
patcher->DoneWithFile(false);
break;
default:
writer->Rewind();
NetCliFileDownloadRequest(
filename,
writer,
DownloadFileCallback,
param
);
break;
}
}
static void ManifestCallback(ENetError result, void* param, const wchar group[], const NetCliFileManifestEntry manifest[], unsigned entryCount)
{
plResPatcher* patcher = (plResPatcher*)param;
patcher->DoneWithManifest(result == kNetSuccess, manifest, entryCount);
}
//// Constructor/Destructor //////////////////////////////////////////////////
plResPatcher::plResPatcher(const char* ageToPatch, bool showAgeName)
{
fAgeToPatch = ageToPatch;
fAlwaysShowAgeName = showAgeName;
IInit();
}
void plResPatcher::IInit()
{
PatcherLog(kHeader, "--- Starting patch process for %s ---", fAgeToPatch.c_str());
}
plResPatcher::~plResPatcher()
{
PatcherLog(kHeader, "--- Patch process done for %s ---", fAgeToPatch.c_str());
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
{
plManifestFile* file = (*i);
delete file;
}
fMfsVec.clear();
}
UInt32 plResPatcher::IGetDownloadSize()
{
if (!IGetAgeManifest())
return 0;
#ifdef PLASMA_EXTERNAL_RELEASE
bool showAgeName = fAlwaysShowAgeName;
#else
bool showAgeName = true;
#endif
char msg[128];
if (!fAgeToPatch.empty())
{
if (showAgeName)
sprintf(msg, "Checking age %s...", fAgeToPatch.c_str());
else
strcpy(msg, "Checking age...");
}
else
sprintf(msg, "Checking...");
plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOperation((hsScalar)(fMfsVec.size()), msg, plProgressMgr::kNone, false, true);
UInt32 downloadSize = 0;
UInt32 downloadFiles = 0;
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
{
plManifestFile* mfsFile = (*i);
if (!mfsFile->IsLocalUpToDate())
{
downloadFiles++;
downloadSize += mfsFile->GetDownloadSize();
}
progress->Increment(1.f);
}
delete progress;
PatcherLog(kInfo, "Got download stats, %d files, %d bytes", downloadFiles, downloadSize);
return downloadSize;
}
bool plResPatcher::CheckFreeSpace(UInt32 bytesNeeded)
{
#ifdef HS_BUILD_FOR_WIN32
ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, neededBytes;
if (GetDiskFreeSpaceEx(NULL, &freeBytesAvailable, &totalNumberOfBytes, NULL))
{
neededBytes.HighPart = 0;
neededBytes.LowPart = bytesNeeded;
if (neededBytes.QuadPart > freeBytesAvailable.QuadPart)
{
PatcherLog(kInfo, "Not enough disk space (asked for %d bytes)", bytesNeeded);
return false;
}
}
#endif // HS_BUILD_FOR_WIN32
return true;
}
bool plResPatcher::IDecompressSound(plManifestFile* mfsFile, bool noOverwrite)
{
UInt32 flags = mfsFile->GetFlags();
if ( (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit) || hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo)) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "ogg") == 0)
{
plAudioFileReader* reader = plAudioFileReader::CreateReader(mfsFile->GetName(), plAudioCore::kAll, plAudioFileReader::kStreamNative);
if (!reader)
{
PatcherLog(kInfo, "Unable to create audio file reader for %s", mfsFile->GetName());
return false;
}
UInt32 size = reader->GetDataSize();
delete reader;
// Make sure we have enough free space
if (!CheckFreeSpace(size))
return false;
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheSplit))
plAudioFileReader::CacheFile(mfsFile->GetName(), true, noOverwrite);
if (hsCheckBits(flags, plManifestFile::kSndFlagCacheStereo))
plAudioFileReader::CacheFile(mfsFile->GetName(), false, noOverwrite);
}
return true;
}
bool plResPatcher::Update()
{
UInt32 downloadSize = IGetDownloadSize();
// if download size is 0, nothing to download, but we still need to tell the res manager about the files
plFileUtils::CreateDir("dat");
plFileUtils::CreateDir("sfx");
if (!CheckFreeSpace(downloadSize))
return false;
#ifdef PLASMA_EXTERNAL_RELEASE
bool showAgeName = fAlwaysShowAgeName;
#else
bool showAgeName = true;
#endif
char msg[128];
if (!fAgeToPatch.empty())
{
if (showAgeName)
sprintf(msg, "Downloading %s data...", fAgeToPatch.c_str());
else
strcpy(msg, "Downloading age data...");
}
else
sprintf(msg, "Downloading...");
plOperationProgress* progress = plProgressMgr::GetInstance()->RegisterOverallOperation((hsScalar)downloadSize, msg, plProgressMgr::kUpdateText, true);
bool result = true;
plResManager* resMgr = ((plResManager*)hsgResMgr::ResMgr());
for (MfsFileVec::iterator i = fMfsVec.begin(); i != fMfsVec.end(); ++i)
{
plManifestFile* mfsFile = (*i);
if (!mfsFile->IsLocalUpToDate())
{
FileType type = IGetFile(mfsFile, progress);
if (type == kPrp)
{
// Checks for existence before attempting to remove
resMgr->RemoveSinglePage(mfsFile->GetName());
}
else if (type == kOther)
{
if (!IDecompressSound(mfsFile, false))
{
char text[MAX_PATH];
StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName());
PatcherLog(kInfo, text );
hsAssert(false, text);
result = false;
}
}
else
{
char text[MAX_PATH];
StrPrintf(text, arrsize(text), "Failed downloading file: %s", mfsFile->GetName());
PatcherLog(kInfo, text );
hsAssert(false, text);
result = false;
}
}
else
{
if (!IDecompressSound(mfsFile, true))
{
char text[MAX_PATH];
StrPrintf(text, arrsize(text), "%s could not be decompressed", mfsFile->GetName());
PatcherLog(kInfo, text );
hsAssert(false, text);
result = false;
}
}
if (!resMgr->FindSinglePage(mfsFile->GetName()) && stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0)
{
resMgr->AddSinglePage(mfsFile->GetName());
}
}
PatcherLog(kMajorStatus, "Cleaning up patcher..." );
delete progress;
return result;
}
plResPatcher::FileType plResPatcher::IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar)
{
PatcherLog(kInfo, " Setting up to download file %s", mfsFile->GetName());
bool downloadDone = false;
wchar* wServerPath = hsStringToWString(mfsFile->GetServerPath());
int numTries = 0;
while (!downloadDone)
{
if (numTries >= kMaxDownloadTries)
{
PatcherLog(kInfo, " Max download tries exceeded (%d). Aborting download...", kMaxDownloadTries);
return kFail;
}
plDownloadStream downloadStream(progressBar);
if (!downloadStream.Open(mfsFile->GetName(), "wb"))
{
PatcherLog(kInfo, " Unable to create file. Aborting download...");
return kFail;
}
PatcherLog(kInfo, " Downloading file %s...", mfsFile->GetName());
fSuccess = false;
fDoneWithFile = false;
NetCliFileDownloadRequest(
wServerPath,
&downloadStream,
DownloadFileCallback,
this
);
while (!fDoneWithFile) {
NetClientUpdate();
plgDispatch::Dispatch()->MsgQueueProcess();
AsyncSleep(10);
}
if (!fSuccess) {
// remove partial file and die (server didn't have the file or server is shutting down)
downloadStream.RewindProgress();
plFileUtils::RemoveFile(mfsFile->GetName(), true);
PatcherLog(kError, " File %s failed to download.", mfsFile->GetName());
downloadDone = true;
}
else {
if (downloadStream.DecompressedOk()) {
PatcherLog(kInfo, " Decompress successful." );
// download and decompress successful, do a md5 check on the resulting file
plMD5Checksum localMD5(mfsFile->GetName());
if (localMD5 != mfsFile->GetChecksum()) {
downloadStream.RewindProgress();
downloadStream.Close();
plFileUtils::RemoveFile(mfsFile->GetName(), true);
PatcherLog(kError, " File %s MD5 check FAILED.", mfsFile->GetName());
// don't set downloadDone so we attempt to re-download from the server
}
else {
downloadStream.Close();
PatcherLog(kInfo, " MD5 check succeeded.");
downloadDone = true;
}
}
else {
downloadStream.RewindProgress();
downloadStream.Close();
plFileUtils::RemoveFile(mfsFile->GetName(), true);
PatcherLog(kError, " File %s failed to decompress.", mfsFile->GetName());
// don't set downloadDone so we attempt to re-download from the server
}
}
++numTries;
}
FREE(wServerPath);
if (!fSuccess)
return kFail;
if (stricmp(plFileUtils::GetFileExt(mfsFile->GetName()), "prp") == 0)
return kPrp;
return kOther;
}
bool plResPatcher::IGetAgeManifest()
{
if (fMfsVec.size() > 0)
return true;
PatcherLog(kMajorStatus, "Downloading new manifest from data server..." );
fSuccess = false;
wchar* group = hsStringToWString(fAgeToPatch.c_str());
unsigned numTries = 0;
while (!fSuccess)
{
numTries++;
fDoneWithFile = false;
NetCliFileManifestRequest(ManifestCallback, this, group);
while (!fDoneWithFile)
{
NetClientUpdate();
plgDispatch::Dispatch()->MsgQueueProcess();
AsyncSleep(10);
}
if (!fSuccess)
{
fMfsVec.clear(); // clear out any bad data
if (numTries > kMaxDownloadTries)
break; // abort
}
}
delete [] group;
if (fSuccess)
PatcherLog(kStatus, "New age manifest read; number of files: %d", fMfsVec.size() );
else
PatcherLog(kStatus, "Failed to download manifest after trying %d times", kMaxDownloadTries);
return fSuccess;
}
void plResPatcher::DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount)
{
PatcherLog(kStatus, "New age manifest received. Reading...");
if (success)
{
for (unsigned i = 0; i < entryCount; i++)
{
char* name = hsWStringToString(manifestEntires[i].clientName);
char* serverPath = hsWStringToString(manifestEntires[i].downloadName);
char* md5Str = hsWStringToString(manifestEntires[i].md5);
int size = manifestEntires[i].fileSize;
int zipsize = manifestEntires[i].zipSize;
int flags = manifestEntires[i].flags;
if (stricmp(plFileUtils::GetFileExt(name), "gz"))
flags |= plManifestFile::kFlagZipped; // add zipped flag if necessary
plMD5Checksum sum;
sum.SetFromHexString(md5Str);
fMfsVec.push_back(TRACKED_NEW plManifestFile(name, serverPath, sum, size, zipsize, flags));
delete [] name;
delete [] serverPath;
delete [] md5Str;
}
}
fDoneWithFile = true;
fSuccess = success;
}
void PatcherLog(PatcherLogType type, const char* format, ...)
{
UInt32 color = 0;
switch (type)
{
case kHeader: color = plStatusLog::kWhite; break;
case kInfo: color = plStatusLog::kBlue; break;
case kMajorStatus: color = plStatusLog::kYellow; break;
case kStatus: color = plStatusLog::kGreen; break;
case kError: color = plStatusLog::kRed; break;
}
static plStatusLog* gStatusLog = nil;
if (!gStatusLog)
{
gStatusLog = plStatusLogMgr::GetInstance().CreateStatusLog(
20,
"patcher.log",
plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kDeleteForMe);
}
va_list args;
va_start(args, format);
gStatusLog->AddLineV(color, format, args);
va_end(args);
}

View File

@ -0,0 +1,103 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plResPatcher_h_inc
#define plResPatcher_h_inc
#include "hsStlUtils.h"
#include "../pnUtils/pnUtils.h"
#include "../pnNetBase/pnNetBase.h"
#include "../plEncryption/plChecksum.h"
class plManifest;
class plManifestFile;
class plOperationProgress;
struct NetCliFileManifestEntry;
class plResPatcher
{
protected:
enum FileType {kFail, kPrp, kOther};
std::string fAgeToPatch;
typedef std::vector<plManifestFile*> MfsFileVec;
MfsFileVec fMfsVec;
bool fDoneWithFile;
bool fSuccess;
bool fAlwaysShowAgeName;
void IInit();
static void ILog(UInt32 type, const char* format, ...);
FileType IGetFile(const plManifestFile* mfsFile, plOperationProgress* progressBar);
bool IGetAgeManifest();
UInt32 IGetDownloadSize();
bool IDecompressSound(plManifestFile* mfsFile, bool noOverwrite = false);
public:
plResPatcher(const char* ageToPatch, bool showAgeName = false);
~plResPatcher();
bool Update();
static bool CheckFreeSpace(UInt32 bytesNeeded);
// called by download callbacks to tell it we are done with the current file
void DoneWithFile(bool success) {fDoneWithFile = true; fSuccess = success;}
void DoneWithManifest(bool success, const NetCliFileManifestEntry manifestEntires[], unsigned entryCount);
};
enum PatcherLogType
{
kHeader,
kInfo,
kMajorStatus,
kStatus,
kError,
};
void PatcherLog(PatcherLogType type, const char* format, ...);
#endif // _plResPatcher_h

View File

@ -0,0 +1,58 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAudibleCreatable_inc
#define plAudibleCreatable_inc
#include "../pnFactory/plCreator.h"
#include "plAudibleNull.h"
REGISTER_CREATABLE( plAudibleNull );
#include "plWinAudible.h"
REGISTER_CREATABLE( plWinAudible );
REGISTER_CREATABLE( pl2WayWinAudible );
#endif // plAudibleCreatable_inc

View File

@ -0,0 +1,83 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsTypes.h"
#include "hsGeometry3.h"
#include "plAudibleNull.h"
#include "plgDispatch.h"
#include "hsResMgr.h"
#include "../pnMessage/plNodeRefMsg.h"
#include "../pnKeyedObject/plKey.h"
hsVector3 plAudibleNull::GetVelocity(int index) const
{
hsVector3 ret(0,0,0);
return(ret);
}
hsPoint3 plAudibleNull::GetPosition(int index)
{
hsPoint3 ret(0,0,0);
return(ret);
}
void plAudibleNull::SetSceneNode(plKey newNode)
{
plKey oldNode = GetSceneNode();
if( oldNode == newNode )
return;
if( newNode )
{
plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(newNode, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kAudible);
hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kActiveRef);
}
if( oldNode )
{
oldNode->Release(GetKey());
}
}

View File

@ -0,0 +1,109 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAudibleNull_inc
#define plAudibleNull_inc
struct hsVector3;
struct hsPoint3;
#include "plAudible.h"
class plAudibleNull : public plAudible
{
public:
plAudibleNull() : fSceneNode(nil),fSceneObj(nil) {;}
CLASSNAME_REGISTER( plAudibleNull );
GETINTERFACE_ANY( plAudibleNull, plAudible );
virtual plKey GetSceneNode() const { return fSceneNode; }
virtual void SetSceneNode(plKey newNode);
virtual plKey GetSceneObject() const { return fSceneObj; }
virtual void SetSceneObject(plKey newNode) { }
virtual plAudible& SetProperty(int prop, hsBool on) { return *this; }
virtual hsBool GetProperty(int prop) { return false; }
void Play(int index = -1){;}
void SynchedPlay(int index = -1) {;}
void Stop(int index = -1){;}
void FastForwardPlay(int index = -1){;}
void FastForwardToggle(int index = -1){;}
void SetMin(const hsScalar m,int index = -1){;} // sets minimum falloff distance
void SetMax(const hsScalar m,int index = -1){;} // sets maximum falloff distance
virtual plAudible& SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index = -1){return *this;}
hsScalar GetMin(int index = -1) const{return 0;}
hsScalar GetMax(int index = -1) const{return 0;}
void SetVelocity(const hsVector3 vel,int index = -1){;}
hsVector3 GetVelocity(int index = -1) const;
hsPoint3 GetPosition(int index = -1);
void SetLooping(hsBool loop,int index = -1){;} // sets continuous loop or stops looping
hsBool IsPlaying(int index = -1){return false;}
virtual void SetTime(double t, int index = -1) {}
virtual void Activate(){}
virtual void DeActivate(){}
virtual void GetStatus(plSoundMsg* pMsg) {;}
virtual int GetNumSounds() const {return 0;}
virtual plSound* GetSound(int i) const { return nil; }
virtual int GetSoundIndex(const char *keyname) const { return -1; }
virtual void SetVolume(const float volume,int index = -1) {;}
virtual void SetFilename(int index, const char *filename, hsBool isCompressed){}
virtual void RemoveCallbacks(plSoundMsg* pMsg) {}
virtual void AddCallbacks(plSoundMsg* pMsg) {}
virtual void SetMuted( hsBool muted, int index = -1 ) {;}
virtual void ToggleMuted( int index = -1 ) {;}
virtual void SetTalkIcon(int index, UInt32 str){;}
virtual void ClearTalkIcon(){;}
virtual void SetFadeIn( const int type, const float length, int index = -1 ) {}
virtual void SetFadeOut( const int type, const float length, int index = -1 ) {}
protected:
plKey fSceneNode, fSceneObj;
};
#endif // plAudibleNull_inc

View File

@ -0,0 +1,787 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsTypes.h"
#include "hsGeometry3.h"
#include "plWinAudible.h"
#include "hsMatrix44.h"
#include "hsTimer.h"
#include "../plAudio/plSound.h"
#include "../plAudio/plWin32Sound.h"
#include "../plAudio/plVoiceChat.h"
#include "../plAudio/plAudioSystem.h"
#include "../plAudio/plWin32StreamingSound.h"
#include "../pnMessage/plSoundMsg.h"
#include "../pnMessage/plTimeMsg.h"
#include "../pnMessage/plAudioSysMsg.h"
#include "../plMessage/plLinkToAgeMsg.h"
#include "hsResMgr.h"
#include "../pnKeyedObject/plKey.h"
#include "hsQuat.h"
#include "plgDispatch.h"
#include "../pnMessage/plNodeRefMsg.h"
#include "../pnMessage/plEventCallbackMsg.h"
#include "../pnMessage/plCmdIfaceModMsg.h"
#include "../pnMessage/plProxyDrawMsg.h"
#include "../plMessage/plInputEventMsg.h"
#include "../pnInputCore/plControlEventCodes.h"
#include "../plModifier/plSoundSDLModifier.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../plStatusLog/plStatusLog.h"
#define SND_INDEX_CHECK( index, ret ) \
if( index >= fSoundObjs.GetCount() ) \
{ \
hsStatusMessageF( "ERROR: Sound index out of range (index %d, count %d)\n", index, fSoundObjs.GetCount() ); \
return ret; \
}
#define SND_APPLY_LOOP( index, func, ret ) \
if( index != -1 ) \
{ \
SND_INDEX_CHECK( index, ret ); \
if( fSoundObjs[ index ] != nil ) \
fSoundObjs[ index ]->func; \
} \
else \
{ \
for( int i = 0; i < fSoundObjs.Count(); i++ ) \
{ \
if( fSoundObjs[ i ] != nil ) \
fSoundObjs[ i ]->func; \
} \
}
// Visualization
#include "plWinAudibleProxy.h"
plWinAudible::plWinAudible()
: fSceneNode(nil), fSceneObj(nil), fSDLMod(nil)
{
fProxyGen = TRACKED_NEW plWinAudibleProxy;
fProxyGen->Init(this);
fLocalToWorld.Reset();
}
plWinAudible::~plWinAudible()
{
// delete SDL modifier
if (fSceneObj)
{
plSceneObject* so=plSceneObject::ConvertNoRef(fSceneObj->ObjectIsLoaded());
if (so)
so->RemoveModifier(fSDLMod);
}
delete fSDLMod;
delete fProxyGen;
for (int i = 0; i < fSoundObjs.Count(); i++)
delete(fSoundObjs[i]);
fSoundObjs.SetCountAndZero(0);
}
void plWinAudible::SetSceneObject(plKey obj)
{
plKey oldKey = nil;
// remove old SDL mod
if (fSDLMod && fSceneObj && fSceneObj != obj)
{
oldKey = fSceneObj;
plSceneObject* so=plSceneObject::ConvertNoRef(fSceneObj->ObjectIsLoaded());
if (so)
so->RemoveModifier(fSDLMod);
delete fSDLMod;
fSDLMod=nil;
}
fSceneObj = obj;
plSceneObject* so=plSceneObject::ConvertNoRef(obj ? obj->ObjectIsLoaded() : nil);
if (so)
{
so->RemoveModifier(fSDLMod);
delete fSDLMod;
fSDLMod=TRACKED_NEW plSoundSDLModifier;
so->AddModifier(fSDLMod);
}
for( int i = 0; i < fSoundObjs.Count(); i++ )
{
if( fSoundObjs[ i ] != nil && fSoundObjs[ i ]->GetKey() != nil )
{
if( obj != nil )
{
plGenRefMsg *replaceMsg = TRACKED_NEW plGenRefMsg( fSoundObjs[ i ]->GetKey(), plRefMsg::kOnReplace, 0, plSound::kRefParentSceneObject );
hsgResMgr::ResMgr()->AddViaNotify( obj, replaceMsg, plRefFlags::kPassiveRef );
}
else if( oldKey != nil )
fSoundObjs[ i ]->GetKey()->Release( oldKey );
}
}
}
void plWinAudible::SetSceneNode(plKey newNode)
{
plKey oldNode = GetSceneNode();
if( oldNode == newNode )
return;
if( !oldNode )
Activate();
if( newNode )
{
plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(newNode, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kAudible);
hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef);
}
if( oldNode )
{
oldNode->Release(GetKey());
}
if( !newNode )
{
DeActivate();
}
fSceneNode = newNode;
}
void plWinAudible::GetStatus(plSoundMsg* pMsg)
{
if (pMsg->fIndex == -1)
{
for (int i = 0; i < fSoundObjs.Count(); i++)
{
plSoundMsg* pReply = fSoundObjs[i]->GetStatus(pMsg);
pReply->fIndex = i;
plgDispatch::MsgSend(pReply);
}
}
else
if (pMsg->fIndex < fSoundObjs.Count())
{
plSoundMsg* pReply = fSoundObjs[pMsg->fIndex]->GetStatus(pMsg);
pReply->fIndex = pMsg->fIndex;
plgDispatch::MsgSend(pReply);
}
}
hsBool plWinAudible::AddSound( plSound *pSnd, int index, hsBool is3D )
{
hsAssert(pSnd->GetKey() != nil, "Adding a new sound with no key.");
if (plgAudioSys::Active())
{
hsgResMgr::ResMgr()->AddViaNotify( pSnd->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, index, 0 ), plRefFlags::kActiveRef );
return true;
}
else
{
pSnd->SetProperty( plSound::kPropIs3DSound, is3D );
hsgResMgr::ResMgr()->AddViaNotify( pSnd->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, index, 0 ), plRefFlags::kActiveRef );
return true;
}
}
/* Unused
int plWinAudible::AddSoundFromResource(plSound *pSnd, void* addr, Int32 size, hsBool is3D )
{
//plWin32Sound* pSnd = TRACKED_NEW plWin32Sound;
//IAssignSoundKey( pSnd, GetKey() ? GetKeyName() : "", fSoundObjs.Count() - 1 );
if (plgAudioSys::Active())
{
int ret = pSnd->GetSoundFromMemory(addr, size, is3D );
if (ret)
{
fSoundObjs.Append(pSnd);
return (fSoundObjs.Count() -1 );
}
}
delete pSnd;
return -1;
}
*/
plAudible& plWinAudible::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index)
{
fLocalToWorld = l2w;
plAudible::SetTransform(l2w, w2l);
hsVector3 v = l2w.GetAxis(hsMatrix44::kUp);
v*=-1;
hsPoint3 pos = l2w.GetTranslate();
if (index != -1)
{
SND_INDEX_CHECK( index, (*this) );
if( fSoundObjs[ index ] != nil )
{
fSoundObjs[index]->SetPosition( pos );
fSoundObjs[index]->SetConeOrientation( v.fX,v.fY,v.fZ );
}
}
else
{
for (int i = 0; i < fSoundObjs.Count(); i++)
{
if( fSoundObjs[ i ] != nil )
{
fSoundObjs[i]->SetConeOrientation( v.fX,v.fY,v.fZ );
fSoundObjs[i]->SetPosition( pos );
}
}
}
fProxyGen->SetTransform(l2w, w2l);
return (*this);
}
void plWinAudible::SetTime(double t, int index)
{
SND_APPLY_LOOP( index, SetTime( t ), ; );
}
void plWinAudible::SynchedPlay(int index)
{
for(int i = 0; i < fSoundObjs.Count(); ++i)
{
if(fSoundObjs[i]->IsPlaying())
{
fSoundObjs[index]->SynchedPlay(fSoundObjs[i]->GetByteOffset());
return;
}
}
}
void plWinAudible::Play(int index )
{
// hsStatusMessageF( "Playing sound %s, index %d, time=%f\n", GetKeyName(), index, hsTimer::GetSeconds());
SND_APPLY_LOOP( index, Play(), ; );
}
void plWinAudible::FastForwardPlay(int index /* = -1 */)
{
SND_APPLY_LOOP(index, FastForwardPlay(), ; );
}
void plWinAudible::FastForwardToggle(int index /* = -1 */)
{
SND_APPLY_LOOP(index, FastForwardToggle(), ; );
}
void plWinAudible::SetOuterVol(const int v, int index)
{
SND_APPLY_LOOP( index, SetOuterVolume( v ), ; );
}
void plWinAudible::SetConeAngles(int inner, int outer, int index)
{
SND_APPLY_LOOP( index, SetConeAngles( inner, outer ), ; );
}
void plWinAudible::Stop(int index)
{
SND_APPLY_LOOP( index, Stop(), ; );
}
void plWinAudible::SetMin(const hsScalar m,int index)
{
SND_APPLY_LOOP( index, SetMin( (int)m ), ; );
}
void plWinAudible::SetMax(const hsScalar m,int index)
{
SND_APPLY_LOOP( index, SetMax( (int)m ), ; );
}
// Takes a 0-1.f as the volume (platform independent)
void plWinAudible::SetVolume(const float volume,int index )
{
SND_APPLY_LOOP( index, SetVolume( volume ), ; );
}
void plWinAudible::SetMuted( hsBool muted, int index )
{
SND_APPLY_LOOP( index, SetMuted( muted ), ; );
}
void plWinAudible::ToggleMuted( int index )
{
if( index != -1 )
{
SND_INDEX_CHECK( index, ; );
if( fSoundObjs[ index ] != nil )
fSoundObjs[ index ]->SetMuted( !fSoundObjs[ index ]->IsMuted() );
}
else
{
for( int i = 0; i < fSoundObjs.Count(); i++ )
{
if( fSoundObjs[ i ] != nil )
fSoundObjs[ i ]->SetMuted( !fSoundObjs[ i ]->IsMuted() );
}
}
}
hsScalar plWinAudible::GetMin(int index) const
{
return (hsScalar)(fSoundObjs[index]->GetMin());
}
hsScalar plWinAudible::GetMax(int index) const
{
return (hsScalar)(fSoundObjs[index]->GetMax());
}
void plWinAudible::SetVelocity(const hsVector3 vel,int index)
{
SND_APPLY_LOOP( index, SetVelocity( vel ), ; );
}
hsVector3 plWinAudible::GetVelocity(int index) const
{
return(fSoundObjs[index]->GetVelocity());
}
hsPoint3 plWinAudible::GetPosition(int index)
{
return( fSoundObjs[index]->GetPosition() );
}
void plWinAudible::SetLooping(hsBool loop,int index)
{
SND_APPLY_LOOP( index, SetProperty( plSound::kPropLooping, loop ), ; );
}
void plWinAudible::SetFadeIn( const int type, const float length, int index )
{
SND_APPLY_LOOP( index, SetFadeInEffect( (plSound::plFadeParams::Type)type, length ), ; );
}
void plWinAudible::SetFadeOut( const int type, const float length, int index )
{
SND_APPLY_LOOP( index, SetFadeOutEffect( (plSound::plFadeParams::Type)type, length ), ; );
}
void plWinAudible::SetFilename(int index, const char *filename, hsBool isCompressed)
{
if(index < 0 || index >= fSoundObjs.Count())
{
hsStatusMessageF( "ERROR: Sound index out of range (index %d, count %d)\n", index, fSoundObjs.GetCount() );
return;
}
plWin32StreamingSound *pStreamingSound = plWin32StreamingSound::ConvertNoRef(fSoundObjs[ index ]);
if(pStreamingSound)
{
pStreamingSound->SetFilename(filename, isCompressed);
}
else
{
plStatusLog::AddLineS("audio.log", "Cannot set filename of non-streaming sound. %s", fSoundObjs[ index ]->GetKeyName());
}
}
hsBool plWinAudible::IsPlaying(int index)
{
int count = fSoundObjs.Count();
if(index < count)
{
if (index == -1)
{
for (int i = 0; i < fSoundObjs.Count(); i++)
{
if( fSoundObjs[ i ] != nil && fSoundObjs[i]->IsPlaying() )
return true;
}
return false;
}
if( fSoundObjs[ index ] == nil )
return false;
return(fSoundObjs[index]->IsPlaying());
} else {
return false;
}
}
void plWinAudible::Read(hsStream* s, hsResMgr* mgr)
{
plAudible::Read(s, mgr);
int n = s->ReadSwap32();
fSoundObjs.SetCountAndZero(n);
for(int i = 0; i < n; i++ )
{
plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, 0);
mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef);
//plSound* pSnd = plSound::ConvertNoRef(mgr->ReadCreatable(s));
//IAssignSoundKey( pSnd, GetKey() ? GetKeyName() : "", i );
//if (plWin32LinkSound::ConvertNoRef(pSnd))
// plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), pSnd->GetKey());
//fSoundObjs[i] = pSnd;
}
plKey pSceneKey = mgr->ReadKey(s);
plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(pSceneKey, plRefMsg::kOnCreate, -1, plNodeRefMsg::kAudible);
mgr->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef);
}
void plWinAudible::IAssignSoundKey( plSound *sound, const char *name, UInt32 i )
{
char keyName[ 256 ];
sprintf( keyName, "%s_%d", name, i );
hsgResMgr::ResMgr()->NewKey( keyName, sound, GetKey() ? GetKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc );
}
void plWinAudible::Write(hsStream* s, hsResMgr* mgr)
{
plAudible::Write(s, mgr);
s->WriteSwap32(fSoundObjs.GetCount());
for(int i = 0; i < fSoundObjs.GetCount(); i++ )
// mgr->WriteCreatable( s, fSoundObjs[i] );
mgr->WriteKey(s, fSoundObjs[i]);
mgr->WriteKey(s, fSceneNode);
}
void plWinAudible::Activate()
{
for (int i = 0; i < fSoundObjs.Count(); i++)
if (fSoundObjs[i] != nil)
fSoundObjs[i]->Activate();
}
void plWinAudible::DeActivate()
{
for (int i = 0; i < fSoundObjs.Count(); i++)
{
if (fSoundObjs[i] != nil)
{
fSoundObjs[i]->DeActivate();
}
}
}
hsBool plWinAudible::MsgReceive(plMessage* msg)
{
plGenRefMsg *refMsg;
if (refMsg = plGenRefMsg::ConvertNoRef(msg))
{
plSound *snd;
if (snd = plSound::ConvertNoRef(refMsg->GetRef()))
{
int index = refMsg->fWhich;
if( refMsg->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest) )
{
int i = fSoundObjs.Count();
if (index >= i)
{
fSoundObjs.ExpandAndZero(index + 1);
}
fSoundObjs[index] = plSound::ConvertNoRef(refMsg->GetRef());
if (plgAudioSys::Active())
fSoundObjs[index]->Activate();
}
else if( refMsg->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) )
{
int i = fSoundObjs.Count();
if (index < i)
{
fSoundObjs[index] = nil;
}
}
return true;
}
}
plAudioSysMsg *sysMsg = plAudioSysMsg::ConvertNoRef( msg );
if( sysMsg )
{
if( sysMsg->GetAudFlag() == plAudioSysMsg::kChannelVolChanged )
{
SND_APPLY_LOOP( -1, RefreshVolume(), false; );
}
}
// proxyDrawMsg handling--just pass it on to the proxy object
plProxyDrawMsg *pdMsg = plProxyDrawMsg::ConvertNoRef( msg );
if( pdMsg != nil )
{
if( fProxyGen )
return fProxyGen->MsgReceive( pdMsg );
return true;
}
return plAudible::MsgReceive(msg);
}
void plWinAudible::RemoveCallbacks(plSoundMsg* pMsg)
{
// sanity check
if (pMsg->fIndex >= fSoundObjs.Count())
return;
if( pMsg->fIndex < 0 )
{
int i;
for( i = 0; i < fSoundObjs.Count(); i++ )
fSoundObjs[i]->RemoveCallbacks(pMsg);
}
else
{
fSoundObjs[pMsg->fIndex]->RemoveCallbacks(pMsg);
}
}
void plWinAudible::AddCallbacks(plSoundMsg* pMsg)
{
// sanity check
if (pMsg->fIndex >= fSoundObjs.Count())
return;
int i;
for( i = 0; i < pMsg->GetNumCallbacks(); i++ )
{
pMsg->GetEventCallback(i)->fIndex = pMsg->fIndex;
pMsg->GetEventCallback(i)->SetSender(GetKey());
}
if( pMsg->fIndex < 0 )
{
for( i = 0; i < fSoundObjs.Count(); i++ )
fSoundObjs[i]->AddCallbacks(pMsg);
}
else
{
fSoundObjs[pMsg->fIndex]->AddCallbacks(pMsg);
}
}
int plWinAudible::GetSoundIndex(const char *keyname) const
{
for( int i = 0; i < fSoundObjs.Count(); i++)
{
if(!fSoundObjs[i]) continue;
if(!strcmp(fSoundObjs[i]->GetKeyName(), keyname ))
{
return i;
}
}
return -1;
}
plSound* plWinAudible::GetSound(int i) const
{
return fSoundObjs[i];
}
// Visualization
plDrawableSpans* plWinAudible::CreateProxy(hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo)
{
plDrawableSpans* myDraw = addTo;
int i;
for( i = 0; i < fSoundObjs.Count(); i++ )
myDraw = fSoundObjs[i]->CreateProxy(fLocalToWorld, mat, idx, myDraw);
return myDraw;
}
//
// 2-way win audible (for local players)
//
pl2WayWinAudible::pl2WayWinAudible() :
fVoicePlayer(nil),
fVoiceRecorder(nil),
fActive(false)
{
}
pl2WayWinAudible::~pl2WayWinAudible()
{
DeActivate();
if (fVoicePlayer)
delete fVoicePlayer;
if(fVoiceRecorder)
delete fVoiceRecorder;
}
hsBool pl2WayWinAudible::MsgReceive(plMessage* msg)
{
plEvalMsg* pMsg = plEvalMsg::ConvertNoRef(msg);
if (pMsg && fVoiceRecorder)
{
fVoiceRecorder->Update(pMsg->GetTimeStamp());
// fVoxIO->Update();
return true;
}
plControlEventMsg* pCtrlMsg = plControlEventMsg::ConvertNoRef(msg);
if (pCtrlMsg)
{
if (pCtrlMsg->GetControlCode() == S_PUSH_TO_TALK && fVoiceRecorder)
fVoiceRecorder->SetMikeOpen(pCtrlMsg->ControlActivated());
return true;
}
return plWinAudible::MsgReceive(msg);
}
void pl2WayWinAudible::Init(hsBool isLocal)
{
if (!fVoicePlayer)
{
if(!isLocal)
fVoicePlayer = TRACKED_NEW plVoicePlayer;
}
if(!fVoiceRecorder)
{
if(isLocal)
{
fVoiceRecorder = TRACKED_NEW plVoiceRecorder;
}
}
Activate();
}
void pl2WayWinAudible::Activate()
{
if(fActive)
return;
plWinAudible::Activate();
if(fVoicePlayer)
{
fVoicePlayer->GetSoundPtr()->Activate();
fActive = true;
}
if (fVoiceRecorder)
{
plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg;
pModMsg->SetBCastFlag(plMessage::kBCastByExactType);
pModMsg->SetSender(GetKey());
pModMsg->SetCmd(plCmdIfaceModMsg::kAdd);
plgDispatch::MsgSend(pModMsg);
fActive = true;
}
}
void pl2WayWinAudible::DeActivate()
{
if(!fActive)
return;
plWinAudible::DeActivate();
if(fVoicePlayer)
{
fVoicePlayer->GetSoundPtr()->DeActivate();
fActive = false;
}
if (fVoiceRecorder)
{
plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg;
pModMsg->SetBCastFlag(plMessage::kBCastByExactType);
pModMsg->SetSender(GetKey());
pModMsg->SetCmd(plCmdIfaceModMsg::kRemove);
plgDispatch::MsgSend(pModMsg);
fActive = false;
}
}
void pl2WayWinAudible::Read(hsStream* s, hsResMgr* mgr)
{
plWinAudible::Read(s, mgr);
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
}
void pl2WayWinAudible::PlayNetworkedSpeech(const char* addr, Int32 size, int numFrames, BYTE flags)
{
if (fVoicePlayer)
{
if (!(flags & VOICE_ENCODED))
fVoicePlayer->PlaybackUncompressedVoiceMessage((BYTE*)addr, size);
else
fVoicePlayer->PlaybackVoiceMessage((BYTE*)addr, size, numFrames);
}
}
plAudible& pl2WayWinAudible::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index)
{
plWinAudible::SetTransform(l2w, w2l, index);
if (fVoicePlayer)
{
hsPoint3 pt = l2w.GetTranslate();
// adjust to head level
pt.fZ += 6.33f;
fVoicePlayer->SetPosition( pt );
hsPoint3 v(l2w.GetAxis(hsMatrix44::kView));
fVoicePlayer->SetOrientation(v);
}
return (*this);
}
void pl2WayWinAudible::SetVelocity(const hsVector3 vel,int index)
{
plWinAudible::SetVelocity(vel, index);
if (fVoicePlayer)
fVoicePlayer->SetVelocity( vel );
}
void pl2WayWinAudible::SetTalkIcon(int index, UInt32 str)
{
if (fVoicePlayer)
fVoicePlayer->SetTalkIcon(index,str);
}
void pl2WayWinAudible::ClearTalkIcon()
{
if (fVoicePlayer)
fVoicePlayer->ClearTalkIcon();
}

View File

@ -0,0 +1,167 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plWinAudible_inc
#define plWinAudible_inc
#include "plAudible.h"
#include "hsTemplates.h"
#include "hsMatrix44.h"
class plSound;
class hsResMgr;
class plWinAudibleProxy;
class plDrawableSpans;
class hsGMaterial;
class plMaxNode;
class plSoundSDLModifier;
class plVoiceRecorder;
class plVoicePlayer;
class plWinAudible : public plAudible
{
public:
plWinAudible();
~plWinAudible();
CLASSNAME_REGISTER( plWinAudible );
GETINTERFACE_ANY( plWinAudible, plAudible );
virtual plKey GetSceneNode() const { return fSceneNode; }
virtual void SetSceneNode(plKey newNode);
virtual plKey GetSceneObject() const { return fSceneObj; }
virtual void SetSceneObject(plKey obj);
virtual plAudible& SetProperty(int prop, hsBool on) { return *this; }
virtual hsBool GetProperty(int prop) { return false; }
virtual plAudible& SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index = -1);
void Play(int index = -1);
void SynchedPlay(int index = -1);
void Stop(int index = -1);
void FastForwardPlay(int index = -1);
void FastForwardToggle(int index = -1);
void SetMin(const hsScalar m,int index = -1); // sets minimum falloff distance
void SetMax(const hsScalar m,int index = -1); // sets maximum falloff distance
void SetPosition(const hsPoint3 p, int index = -1);
hsScalar GetMin(int index = -1) const;
hsScalar GetMax(int index = -1) const;
virtual void SetVelocity(const hsVector3 vel,int index = -1);
hsVector3 GetVelocity(int index = -1) const;
hsPoint3 GetPosition(int index = -1);
void SetLooping(hsBool loop,int index = -1); // sets continuous loop or stops looping
hsBool IsPlaying(int index = -1);
void SetTime(double t, int index = -1);
void SetOuterVol(const int v,int index = -1); // volume for the outer cone (if applicable)
void SetConeAngles(int inner, int outer,int index = -1);
void RemoveCallbacks(plSoundMsg* pMsg);
void AddCallbacks(plSoundMsg* pMsg);
hsBool AddSound(plSound *pSnd, int index,hsBool is3D);
int AddSoundFromResource(plSound *pSnd, void* addr, Int32 size, hsBool is3D);
virtual void GetStatus(plSoundMsg* pMsg);
virtual int GetNumSounds() const {return fSoundObjs.Count();}
virtual plSound* GetSound(int i) const;
virtual int GetSoundIndex(const char *keyname) const;
virtual void SetVolume(const float volume,int index = -1);
virtual void SetMuted( hsBool muted, int index = -1 );
virtual void ToggleMuted( int index = -1 );
virtual void SetTalkIcon(int index, UInt32 str){;}
virtual void ClearTalkIcon(){;}
void SetFilename(int index, const char *filename, hsBool isCompressed);
virtual void SetFadeIn( const int type, const float length, int index = -1 );
virtual void SetFadeOut( const int type, const float length, int index = -1 );
virtual hsBool MsgReceive(plMessage* pMsg);
virtual void Activate();
virtual void DeActivate();
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
// Visualization
virtual plDrawableSpans* CreateProxy(hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo);
private:
hsTArray<plSound *> fSoundObjs;
plKey fSceneNode;
plWinAudibleProxy* fProxyGen;
hsMatrix44 fLocalToWorld;
plSoundSDLModifier* fSDLMod;
plKey fSceneObj;
void IAssignSoundKey( plSound *sound, const char *name, UInt32 i );
};
class pl2WayWinAudible : public plWinAudible
{
public:
pl2WayWinAudible();
~pl2WayWinAudible();
CLASSNAME_REGISTER( pl2WayWinAudible );
GETINTERFACE_ANY( pl2WayWinAudible, plWinAudible );
virtual hsBool MsgReceive(plMessage* pMsg);
virtual void Init(hsBool isLocal);
virtual void Activate();
virtual void DeActivate();
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void PlayNetworkedSpeech(const char* addr, Int32 size, int numFrames, unsigned char flags);
virtual plAudible& SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index = -1);
virtual void SetVelocity(const hsVector3 vel,int index = -1);
virtual void SetTalkIcon(int index, UInt32 str);
virtual void ClearTalkIcon();
protected:
plVoicePlayer* fVoicePlayer;
plVoiceRecorder *fVoiceRecorder;
bool fActive;
};
#endif // plWinAudible_inc

View File

@ -0,0 +1,82 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsTypes.h"
#include "plWinAudibleProxy.h"
#include "plWinAudible.h"
#include "../plDrawable/plDrawableGenerator.h"
#include "../pnMessage/plProxyDrawMsg.h"
plWinAudibleProxy::plWinAudibleProxy()
: plProxyGen(hsColorRGBA().Set(0.2f,0.2f,0.8f,1.f), hsColorRGBA().Set(1.f,0.5f,0.5f,1.f), 0.2f),
fOwner(nil)
{
}
plWinAudibleProxy::~plWinAudibleProxy()
{
}
hsBool plWinAudibleProxy::Init(plWinAudible* aud)
{
plProxyGen::Init(aud);
fOwner = aud;
fProxyMsgType = plProxyDrawMsg::kAudible;
return fOwner != nil;
}
plKey plWinAudibleProxy::IGetNode() const
{
return fOwner ? fOwner->GetSceneNode() : nil;
}
plDrawableSpans* plWinAudibleProxy::ICreateProxy(hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo)
{
if( fOwner )
{
return fOwner->CreateProxy(mat, idx, addTo);
}
return nil;
}

View File

@ -0,0 +1,63 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plWinAudibleProxy_inc
#define plWinAudibleProxy_inc
#include "../plDrawable/plProxyGen.h"
class plWinAudible;
class plWinAudibleProxy : public plProxyGen
{
public:
plWinAudibleProxy();
virtual ~plWinAudibleProxy();
hsBool Init(plWinAudible* aud);
protected:
plWinAudible* fOwner;
virtual plDrawableSpans* ICreateProxy(hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo=nil);
virtual plKey IGetNode() const;
};
#endif // plWinAudibleProxy_inc

View File

@ -0,0 +1,243 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plAudioCaps - Utility class to query and detect available audio options.//
// //
//////////////////////////////////////////////////////////////////////////////
#include "HeadSpin.h"
#include "al.h"
#include "alc.h"
#include "plEAXEffects.h"
#include "plAudioCaps.h"
#ifdef EAX_SDK_AVAILABLE
#include <eax.h>
#include <eaxlegacy.h>
#endif
#include <DShow.h>
#include "../plStatusLog/plStatusLog.h"
#define MAX_NUM_SOURCES 128
#define kLogMe if( fLog != nil ) fLog->AddLineF(
#define MAX_AUDIOCARD_NAME 256
//////////////////////////////////////////////////////////////////////////////
//// Detector Functions //////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
plAudioCaps plAudioCapsDetector::fCaps;
hsBool plAudioCapsDetector::fGotCaps = false;
plStatusLog *plAudioCapsDetector::fLog = nil;
plAudioCapsDetector::plAudioCapsDetector()
{
}
plAudioCapsDetector::~plAudioCapsDetector()
{
}
//// Detect //////////////////////////////////////////////////////////////////
// Our big function that does all of our work
plAudioCaps &plAudioCapsDetector::Detect( hsBool logIt, hsBool init )
{
// If we already have the device capabilities, just return them
if(fGotCaps) return fCaps;
fCaps.fIsAvailable = true;
ALCdevice * device;
ALCcontext * context;
if(init)
{
device = alcOpenDevice(0);
if(!device)
{
fCaps.fIsAvailable = false;
}
context = alcCreateContext(device, 0);
if(alGetError() != AL_NO_ERROR)
{
fCaps.fIsAvailable = false;
}
alcMakeContextCurrent(context);
if(alGetError() != AL_NO_ERROR)
{
fCaps.fIsAvailable = false;
}
}
EnumerateAudioDevices();
if( logIt )
fLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "audioCaps.log" );
else
fLog = nil;
kLogMe 0xff00ff00, "Starting audio caps detection..." );
// find the max number of sources
ALuint sources[MAX_NUM_SOURCES];
ALuint i = 0;
for(; i < MAX_NUM_SOURCES; i++)
{
alGenSources(1, &sources[i]);
if(alGetError() != AL_NO_ERROR)
break;
fCaps.fMaxNumSources++;
}
alDeleteSources(i, sources);
kLogMe 0xffffffff, "Max Number of sources: %d", i);
plStatusLog::AddLineS("audio.log", "Max Number of sources: %d", i);
// Detect EAX support
kLogMe 0xff00ff00, "Attempting to detect EAX support..." );
fCaps.fEAXAvailable = IDetectEAX( );
kLogMe 0xff00ff00, "Audio caps detection COMPLETE." );
delete fLog;
fGotCaps = true; // We've got the device capabilities
if(init)
{
alcMakeContextCurrent(nil);
alcDestroyContext(context);
alcCloseDevice(device);
}
return fCaps;
}
void plAudioCapsDetector::EnumerateAudioDevices()
{
ICreateDevEnum *pDevEnum;
IEnumMoniker *pEnumMon;
IMoniker *pMoniker;
ULONG cFetched;
HRESULT hr;
char audioCardName[MAX_AUDIOCARD_NAME];
short *pShort;
// Enumerate audio devices
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void **)&pDevEnum);
if(SUCCEEDED(hr))
{
hr = pDevEnum->CreateClassEnumerator(CLSID_AudioRendererCategory, &pEnumMon, 0);
if(SUCCEEDED(hr))
{
while(pEnumMon->Next(1, &pMoniker, &cFetched) == S_OK)
{
if(pMoniker)
{
IPropertyBag *pPropBag;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
if(SUCCEEDED(hr))
{
VARIANT varName;
VariantInit(&varName);
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
memset(audioCardName, 0, MAX_AUDIOCARD_NAME);
pShort = varName.piVal;
// copy from wide character array to char array
for(int i = 0; *pShort != 0 && i < MAX_AUDIOCARD_NAME; pShort++, i++)
{
audioCardName[i] = (char)*pShort;
}
if(SUCCEEDED(hr))
{
plStatusLog::AddLineS("audiocaps.log", audioCardName );
}
VariantClear(&varName);
pPropBag->Release();
}
pMoniker->Release();
}
}
pEnumMon->Release();
}
pDevEnum->Release();
}
}
//// IDetectEAX //////////////////////////////////////////////////////////////
// Attempt to actually init the EAX listener.Note that we can potentially do
// this even if we didn't load the EAX Unified driver (we could just be
// running EAX 3.0 native), so you can really just think of the EAX Unified
// init code above as a way of trying to make sure this line here will
// succeed as often as possible.
hsBool plAudioCapsDetector::IDetectEAX( )
{
#ifdef EAX_SDK_AVAILABLE
hsBool gotSupport = true;
if(!alIsExtensionPresent((ALchar *)"EAX4.0")) // is eax 4 supported
{
if(!alIsExtensionPresent((ALchar *) "EAX4.0Emulated")) // is an earlier version of eax supported
{
kLogMe 0xff00ff00, "EAX not supported");
gotSupport = false;
}
else
{
fCaps.fEAXUnified = true;
kLogMe 0xff00ff00, "EAX 4 Emulated supported");
}
}
else
{
kLogMe 0xff00ff00, "EAX 4 available");
}
return gotSupport;
#else
kLogMe 0xff00ff00, "EAX disabled in this build");
return false;
#endif
}

View File

@ -0,0 +1,100 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plAudioCaps - Utility class to query and detect available audio options.//
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plAudioCaps_h
#define _plAudioCaps_h
#include "hsTypes.h"
#include "hsTemplates.h"
class plAudioCapsDetector;
class plStatusLog;
class plAudioCaps
{
public:
plAudioCaps() { Clear(); }
void Clear( void )
{
fIsAvailable = false;
fEAXAvailable = false;
fEAXUnified = false;
fMaxNumSources = 0;
}
hsBool IsAvailable( void ) const { return fIsAvailable; }
hsBool IsEAXAvailable( void ) const { return fEAXAvailable; }
hsBool UsingEAXUnified( void ) const { return fEAXUnified; }
unsigned GetMaxNumVoices() { return fMaxNumSources; }
protected:
friend class plAudioCapsDetector;
hsBool fIsAvailable, fEAXAvailable, fEAXUnified;
unsigned fMaxNumSources;
};
class plAudioCapsDetector
{
public:
plAudioCapsDetector();
virtual ~plAudioCapsDetector();
static plAudioCaps &Detect( hsBool log = false, hsBool init = false );
protected:
static plStatusLog *fLog;
static plAudioCaps fCaps;
static hsBool fGotCaps;
static hsBool IDetectEAX( );
static void EnumerateAudioDevices();
};
#endif //_plAudioCaps_h

View File

@ -0,0 +1,73 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAudioCreatable_inc
#define plAudioCreatable_inc
#include "../pnFactory/plCreator.h"
#include "plAudioSystem.h"
REGISTER_CREATABLE( plAudioSystem );
#include "plSound.h"
REGISTER_NONCREATABLE( plSound );
REGISTER_CREATABLE( plSoundVolumeApplicator );
#include "plWin32Sound.h"
#include "plWin32StaticSound.h"
#include "plWin32StreamingSound.h"
#include "plWin32GroupedSound.h"
REGISTER_NONCREATABLE( plWin32Sound );
REGISTER_CREATABLE( plWin32StaticSound );
REGISTER_CREATABLE( plWin32LinkSound );
REGISTER_CREATABLE( plWin32StreamingSound );
REGISTER_CREATABLE( plWin32GroupedSound );
#include "plEAXListenerMod.h"
REGISTER_CREATABLE( plEAXListenerMod );
#include "plAudioReaderCreatable.h"
#endif // plAudioCreatable_inc

View File

@ -0,0 +1,41 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,280 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAudioSystem_h
#define plAudioSystem_h
#include "hsStlUtils.h"
#include "hsWindowHndl.h"
#include "hsTemplates.h"
#include "hsGeometry3.h"
#include "../pnKeyedObject/hsKeyedObject.h"
#define DEFAULT_AUDIO_DEVICE_NAME "Generic Software"
typedef wchar_t WCHAR;
class plSound;
class plSoftSoundNode;
class plgAudioSys;
class plStatusLog;
class plEAXListenerMod;
typedef struct ALCdevice_struct ALCdevice;
typedef struct ALCcontext_struct ALCcontext;
class DeviceDescriptor
{
public:
DeviceDescriptor(const char *name, hsBool supportsEAX):
fDeviceName(name),
fSupportsEAX(supportsEAX)
{
}
const char *GetDeviceName() { return fDeviceName.c_str();}
hsBool SupportsEAX() { return fSupportsEAX; }
private:
std::string fDeviceName;
hsBool fSupportsEAX;
};
class plAudioSystem : public hsKeyedObject
{
public:
plAudioSystem();
~plAudioSystem();
CLASSNAME_REGISTER( plAudioSystem );
GETINTERFACE_ANY( plAudioSystem, hsKeyedObject );
enum
{
kThreadSndRef = 0,
kRefEAXRegion
};
hsBool Init(hsWindowHndl hWnd);
void Shutdown();
void SetActive( hsBool b );
void SetListenerPos(const hsPoint3 pos);
void SetListenerVelocity(const hsVector3 vel);
void SetListenerOrientation(const hsVector3 view, const hsVector3 up);
void SetMaxNumberOfActiveSounds(); // sets the max number of active sounds based on the priority cutoff
void SetDistanceModel(int i);
virtual hsBool MsgReceive(plMessage* msg);
double GetTime();
void NextDebugSound( void );
hsPoint3 GetCurrListenerPos( void ) const { return fCurrListenerPos; }
int GetNumAudioDevices();
const char *GetAudioDeviceName(int index);
hsBool SupportsEAX(const char *deviceName);
void SetFadeLength(float lengthSec);
protected:
friend class plgAudioSys;
ALCdevice * fDevice;
ALCcontext * fContext;
ALCdevice * fCaptureDevice;
plSoftSoundNode *fSoftRegionSounds;
plSoftSoundNode *fActiveSofts;
plStatusLog *fDebugActiveSoundDisplay;
static Int32 fMaxNumSounds, fNumSoundsSlop; // max number of sounds the engine is allowed to audibly play. Different than fMaxNumSources. That is the max number of sounds the audio card can play
plSoftSoundNode *fCurrDebugSound;
hsTArray<plKey> fPendingRegisters;
hsPoint3 fCurrListenerPos;//, fCommittedListenerPos;
hsBool fActive, fUsingEAX, fRestartOnDestruct, fWaitingForShutdown;
__int64 fStartTime;
hsTArray<hsKeyedObject *> fMyRefs;
hsTArray<plEAXListenerMod *> fEAXRegions;
hsPoint3 fLastPos;
hsBool fAvatarPosSet; // used for listener stuff
hsBool fDisplayNumBuffers;
std::vector<DeviceDescriptor> fDeviceList; // list of openal device names
double fStartFade;
float fFadeLength;
unsigned int fMaxNumSources; // max openal sources
double fLastUpdateTimeMs;
void RegisterSoftSound( const plKey soundKey );
void UnregisterSoftSound( const plKey soundKey );
void IUpdateSoftSounds( const hsPoint3 &newPosition );
UInt32 IScaleVolume(float volume);
void IEnumerateDevices();
public:
hsBool fListenerInit;
};
class plgAudioSys
{
public:
enum ASChannel
{
kSoundFX,
kAmbience,
kBgndMusic,
kGUI,
kNPCVoice,
kVoice,
kNumChannels
};
enum DebugFlags
{
kDisableRightSelect = 0x00000001,
kDisableLeftSelect = 0x00000002
};
enum AudioMode
{
kDisabled,
kSoftware,
kHardware,
kHardwarePlusEAX,
};
static void Init(hsWindowHndl hWnd);
static hsBool Hardware() { return fUseHardware; }
static void SetUseHardware(hsBool b);
static void SetActive(hsBool b);
static void SetMuted( hsBool b );
static void EnableEAX( hsBool b );
static hsBool Active() { return fInit; }
static void Shutdown();
static void Activate(hsBool b);
static hsBool IsMuted( void ) { return fMuted; }
static hsWindowHndl hWnd() { return fWnd; }
static plAudioSystem* Sys() { return fSys; }
static void Restart( void );
static hsBool UsingEAX( void ) { return fSys->fUsingEAX; }
static void NextDebugSound( void );
static void SetChannelVolume( ASChannel chan, hsScalar vol );
static hsScalar GetChannelVolume( ASChannel chan );
static void Set2D3DBias( hsScalar bias );
static hsScalar Get2D3Dbias();
static void SetGlobalFadeVolume( hsScalar vol );
static hsScalar GetGlobalFadeVolume( void ) { return fGlobalFadeVolume; }
static void SetDebugFlag( UInt32 flag, hsBool set = true ) { if( set ) fDebugFlags |= flag; else fDebugFlags &= ~flag; }
static hsBool IsDebugFlagSet( UInt32 flag ) { return fDebugFlags & flag; }
static void ClearDebugFlags( void ) { fDebugFlags = 0; }
static hsScalar GetStreamingBufferSize( void ) { return fStreamingBufferSize; }
static void SetStreamingBufferSize( hsScalar size ) { fStreamingBufferSize = size; }
static UInt8 GetPriorityCutoff( void ) { return fPriorityCutoff; }
static void SetPriorityCutoff( UInt8 cut ) { fPriorityCutoff = cut; if(fSys) fSys->SetMaxNumberOfActiveSounds(); }
static hsBool AreExtendedLogsEnabled( void ) { return fEnableExtendedLogs; }
static void EnableExtendedLogs( hsBool e ) { fEnableExtendedLogs = e; }
static hsScalar GetStreamFromRAMCutoff( void ) { return fStreamFromRAMCutoff; }
static void SetStreamFromRAMCutoff( hsScalar c ) { fStreamFromRAMCutoff = c; }
static void SetListenerPos(const hsPoint3 pos);
static void SetListenerVelocity(const hsVector3 vel);
static void SetListenerOrientation(const hsVector3 view, const hsVector3 up);
static void ShowNumBuffers(hsBool b) { if(fSys) fSys->fDisplayNumBuffers = b; }
static void SetAudioMode(AudioMode mode);
static int GetAudioMode();
static hsBool LogStreamingUpdates() { return fLogStreamingUpdates; }
static void SetLogStreamingUpdates(hsBool logUpdates) { fLogStreamingUpdates = logUpdates; }
static void SetDeviceName(const char *device, hsBool restart = false);
static const char *GetDeviceName() { return fDeviceName.c_str(); }
static int GetNumAudioDevices();
static const char *GetAudioDeviceName(int index);
static ALCdevice *GetCaptureDevice();
static hsBool SupportsEAX(const char *deviceName);
static void RegisterSoftSound( const plKey soundKey );
static void UnregisterSoftSound( const plKey soundKey );
static hsBool IsRestarting() {return fRestarting;}
private:
friend class plAudioSystem;
static plAudioSystem* fSys;
static hsBool fInit;
static hsBool fActive;
static hsBool fMuted;
static hsWindowHndl fWnd;
static hsBool fUseHardware;
static hsBool fDelayedActivate;
static hsScalar fChannelVolumes[ kNumChannels ];
static hsScalar fGlobalFadeVolume;
static UInt32 fDebugFlags;
static hsBool fEnableEAX;
static hsScalar fStreamingBufferSize;
static UInt8 fPriorityCutoff;
static hsBool fEnableExtendedLogs;
static hsScalar fStreamFromRAMCutoff;
static hsScalar f2D3DBias;
static hsBool fLogStreamingUpdates;
static std::string fDeviceName;
static hsBool fRestarting;
static hsBool fMutedStateChange;
};
#endif //plAudioSystem_h

View File

@ -0,0 +1,792 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plDSoundBuffer - Simple wrapper class for a DirectSound buffer. //
// Allows us to simplify all the work done behind the //
// scenes in plWin32BufferThread. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "hsThread.h"
#include "plDSoundBuffer.h"
#include "al.h"
#include "plgDispatch.h"
#include "plAudioSystem.h"
#include "../plAudioCore/plAudioCore.h"
#include "../plAudioCore/plAudioFileReader.h"
#include "plEAXEffects.h"
#include "plProfile.h"
#include "../plStatusLog/plStatusLog.h"
#include <dsound.h>
UInt32 plDSoundBuffer::fNumBuffers = 0;
plProfile_CreateCounterNoReset( "Playing", "Sound", SoundPlaying );
plProfile_CreateCounterNoReset( "Allocated", "Sound", NumAllocated );
//// Constructor/Destructor //////////////////////////////////////////////////
plDSoundBuffer::plDSoundBuffer( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool isLooping, hsBool tryStatic, bool streaming )
{
fLooping = isLooping;
fValid = false;
fBufferDesc = nil;
fLockPtr = nil;
fLockLength = 0;
fStreaming = streaming;
buffer = 0;
source = 0;
for(int i = 0; i < STREAMING_BUFFERS; ++i)
{
streamingBuffers[i] = 0;
}
IAllocate( size, bufferDesc, enable3D, tryStatic );
fNumBuffers++;
}
plDSoundBuffer::~plDSoundBuffer()
{
IRelease();
fNumBuffers--;
}
//// IAllocate ///////////////////////////////////////////////////////////////
void plDSoundBuffer::IAllocate( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool tryStatic )
{
// Create a DSound buffer description
fBufferDesc = new plWAVHeader;
*fBufferDesc = bufferDesc;
fBufferSize = size;
// Do we want to try EAX?
if( plgAudioSys::UsingEAX() )
fEAXSource.Init( this );
fValid = true;
plProfile_Inc( NumAllocated );
}
//// IRelease ////////////////////////////////////////////////////////////////
void plDSoundBuffer::IRelease( void )
{
if( IsPlaying() )
Stop();
// Release stuff
fEAXSource.Release();
alSourcei(source, AL_BUFFER, nil);
alDeleteSources(1, &source);
if(buffer)
alDeleteBuffers( 1, &buffer );
else
alDeleteBuffers(STREAMING_BUFFERS, streamingBuffers);
source = 0;
buffer = 0;
alGetError();
memset(streamingBuffers, 0, STREAMING_BUFFERS * sizeof(unsigned));
delete fBufferDesc;
fBufferDesc = nil;
fBufferSize = 0;
fValid = false;
plProfile_Dec( NumAllocated );
}
/*****************************************************************************
*
* OpenAL
*
***/
int plDSoundBuffer::IGetALFormat(unsigned bitsPerSample, unsigned int numChannels)
{
int format = 0;
switch(bitsPerSample)
{
case 8:
format = (numChannels == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
break;
case 16:
format = (numChannels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
break;
}
return format;
}
bool plDSoundBuffer::FillBuffer(void *data, unsigned bytes, plWAVHeader *header)
{
if(source)
{
alSourcei(source, AL_BUFFER, nil);
alDeleteSources(1, &source);
}
if(buffer)
alDeleteBuffers(1, &buffer);
source = 0;
buffer = 0;
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
ALenum error = alGetError();
alGenBuffers(1, &buffer);
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error);
return false;
}
alBufferData(buffer, format, data, bytes, header->fNumSamplesPerSec );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to fill sound buffer %d", error);
return false;
}
alGenSources(1, &source);
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source);
return false;
}
// Just make it quiet for now
SetScalarVolume(0);
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048);
alGetError();
if( error != AL_NO_ERROR )
{
return false;
}
alSourcei(source, AL_BUFFER, buffer);
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to attach buffer to source %d", error);
return false;
}
return true;
}
//============================================================================
// OpenAL Streaming functions
//============================================================================
// this function is used when restarting the audio system. It is needed to restart a streaming source from where it left off
bool plDSoundBuffer::SetupStreamingSource(plAudioFileReader *stream)
{
unsigned char data[STREAM_BUFFER_SIZE];
unsigned int size;
ALenum error;
alGetError();
int numBuffersToQueue = 0;
// fill buffers with data
for( int i = 0; i < STREAMING_BUFFERS; i++ )
{
size = stream->NumBytesLeft() < STREAM_BUFFER_SIZE ? stream->NumBytesLeft() : STREAM_BUFFER_SIZE;
if(!size)
{
if(IsLooping())
{
stream->SetPosition(0);
}
}
stream->Read(size, data);
numBuffersToQueue++;
alGenBuffers( 1, &streamingBuffers[i] );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error);
return false;
}
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
alBufferData( streamingBuffers[i], format, data, size, fBufferDesc->fNumSamplesPerSec );
if( (error = alGetError()) != AL_NO_ERROR )
plStatusLog::AddLineS("audio.log", "alBufferData");
}
// Generate AL Source
alGenSources( 1, &source );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source);
return false;
}
alSourcei(source, AL_BUFFER, nil);
SetScalarVolume(0);
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048);
alGetError();
if( error != AL_NO_ERROR )
{
return false;
}
alSourceQueueBuffers( source, numBuffersToQueue, streamingBuffers );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to queue buffers %d", error);
return false;
}
return true;
}
// this function is used when starting up a streaming sound, as opposed to restarting it due to an audio system restart.
bool plDSoundBuffer::SetupStreamingSource(void *data, unsigned bytes)
{
unsigned char bufferData[STREAM_BUFFER_SIZE];
unsigned int size;
ALenum error;
char *pData = (char *)data;
alGetError();
int numBuffersToQueue = 0;
// fill buffers with data
for( int i = 0; i < STREAMING_BUFFERS; i++ )
{
size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE;
if(!size)
break;
MemCopy(bufferData, pData, size);
pData += size;
bytes-= size;
numBuffersToQueue++;
alGenBuffers( 1, &streamingBuffers[i] );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error);
return false;
}
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
alBufferData( streamingBuffers[i], format, bufferData, size, fBufferDesc->fNumSamplesPerSec );
if( (error = alGetError()) != AL_NO_ERROR )
plStatusLog::AddLineS("audio.log", "alBufferData");
}
// Generate AL Source
alGenSources( 1, &source );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source);
return false;
}
alSourcei(source, AL_BUFFER, nil);
SetScalarVolume(0);
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048);
error = alGetError();
if( error != AL_NO_ERROR )
{
return false;
}
alSourceQueueBuffers( source, numBuffersToQueue, streamingBuffers );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to queue buffers %d", error);
return false;
}
return true;
}
//============================================================================
int plDSoundBuffer::BuffersProcessed( void )
{
if(alIsSource(source)==AL_FALSE)
{
plStatusLog::AddLineS("audio.log", "BuffersProcessed, source invalid");
return 0;
}
ALint processed = 0;
alGetSourcei( source, AL_BUFFERS_PROCESSED, &processed );
if(alGetError() != AL_NO_ERROR)
{
plStatusLog::AddLineS("audio.log", "alGetSourcei failed");
}
return processed;
}
//============================================================================
int plDSoundBuffer::BuffersQueued( void )
{
if(alIsSource(source)==AL_FALSE) return 0;
ALint queued = 0;
alGetSourcei( source, AL_BUFFERS_QUEUED, &queued );
alGetError();
return queued;
}
//============================================================================
bool plDSoundBuffer::StreamingFillBuffer(plAudioFileReader *stream)
{
if(!source)
return false;
ALenum error;
ALuint bufferId;
unsigned char data[STREAM_BUFFER_SIZE];
int buffersProcessed = BuffersProcessed();
hsBool finished = false;
for(int i = 0; i < buffersProcessed; i++)
{
alSourceUnqueueBuffers( source, 1, &bufferId );
if( (error = alGetError()) != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to unqueue buffer %d", error);
return false;
}
if(!finished)
{
if(stream->NumBytesLeft() == 0)
{
// if at anytime we run out of data, and we are looping, reset the data stream and continue to fill buffers
if(IsLooping())
{
stream->SetPosition(0); // we are looping, so reset data stream, and keep filling buffers
}
else
{
finished = true; // no more data, but we could still be playing, so we don't want to stop the sound yet
}
}
if(!finished)
{ unsigned int size = stream->NumBytesLeft() < STREAM_BUFFER_SIZE ? stream->NumBytesLeft() : STREAM_BUFFER_SIZE;
stream->Read(size, data);
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
alBufferData( bufferId, format, data, size, fBufferDesc->fNumSamplesPerSec );
if( (error = alGetError()) != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to copy data to sound buffer %d", error);
return false;
}
alSourceQueueBuffers( source, 1, &bufferId );
if( (error = alGetError()) != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to queue buffer %d", error);
return false;
}
}
}
}
if(!IsPlaying() && !finished)
{
alSourcePlay(source);
}
alGetError();
return true;
}
/*****************************************************************************
*
* Voice playback functions
*
***/
bool plDSoundBuffer::GetAvailableBufferId(unsigned *bufferId)
{
if(mAvailableBuffers.empty())
{
return false;
}
*bufferId = mAvailableBuffers.front();
mAvailableBuffers.pop_front();
return true;
}
bool plDSoundBuffer::SetupVoiceSource( void )
{
ALenum error = alGetError();
// Generate AL Buffers
alGenBuffers( STREAMING_BUFFERS, streamingBuffers );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to create sound buffer %d", error);
return false;
}
for( int i = 0; i < STREAMING_BUFFERS; i++ )
{
mAvailableBuffers.push_back(streamingBuffers[i]);
}
// Generate AL Source
alGenSources( 1, &source );
error = alGetError();
if( error != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to create audio source %d %d", error, source);
return false;
}
SetScalarVolume(0);
alSourcef(source, AL_ROLLOFF_FACTOR, 0.3048);
error = alGetError();
if( error != AL_NO_ERROR )
{
return false;
}
alSourcei(source, AL_BUFFER, nil);
error = alGetError();
//alSourcei(source, AL_PITCH, 0);
// dont queue any buffers here
return true;
}
//============================================================================
void plDSoundBuffer::UnQueueVoiceBuffers( void )
{
unsigned buffersProcessed = BuffersProcessed();
if(buffersProcessed)
plStatusLog::AddLineS("audio.log", "unqueuing buffers %d", buffersProcessed);
for(int i = 0; i < buffersProcessed; i++)
{
ALuint unQueued;
alSourceUnqueueBuffers( source, 1, &unQueued );
if(alGetError() == AL_NO_ERROR)
{
mAvailableBuffers.push_back(unQueued);
}
else
{
plStatusLog::AddLineS("audio.log", "Failed to unqueue buffer");
}
}
}
//============================================================================
bool plDSoundBuffer::VoiceFillBuffer(void *data, unsigned bytes, unsigned bufferId)
{
if(!source)
return false;
ALenum error;
unsigned int size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE;
ALenum format = IGetALFormat(fBufferDesc->fBitsPerSample, fBufferDesc->fNumChannels);
alBufferData( bufferId, format, data, size, fBufferDesc->fNumSamplesPerSec );
if( (error = alGetError()) != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to copy data to sound buffer %d", error);
return false;
}
alSourceQueueBuffers( source, 1, &bufferId );
if( (error = alGetError()) != AL_NO_ERROR )
{
plStatusLog::AddLineS("audio.log", "Failed to queue buffer %d", error);
return false;
}
if(!IsPlaying())
{
alSourcePlay(source);
}
alGetError();
return true;
}
//// SetLooping //////////////////////////////////////////////////////////////
void plDSoundBuffer::SetLooping( hsBool loop )
{
fLooping = loop;
}
void plDSoundBuffer::SetMinDistance( int dist )
{
alSourcei(source, AL_REFERENCE_DISTANCE, dist);
ALenum error;
if((error = alGetError()) != AL_NO_ERROR)
plStatusLog::AddLineS("audio.log", "Failed to set min distance");
}
void plDSoundBuffer::SetMaxDistance( int dist )
{
alSourcei(source, AL_MAX_DISTANCE, dist);
ALenum error;
if((error = alGetError()) != AL_NO_ERROR)
plStatusLog::AddLineS("audio.log", "Failed to set min distance");
}
//// Play ////////////////////////////////////////////////////////////////////
void plDSoundBuffer::Play( void )
{
if(!source)
return;
ALenum error = alGetError(); // clear error
// we dont want openal to loop our streaming buffers, or the buffer will loop back on itself. We will handle looping in the streaming sound
if(fLooping && !fStreaming)
alSourcei(source, AL_LOOPING, AL_TRUE);
else
alSourcei(source, AL_LOOPING, AL_FALSE);
error = alGetError();
alSourcePlay(source);
error = alGetError();
if(error != AL_NO_ERROR)
plStatusLog::AddLineS("voice.log", "Play failed");
plProfile_Inc( SoundPlaying );
}
//// Pause ////////////////////////////////////////////////////////////////////
void plDSoundBuffer::Pause( void )
{
if (!source)
return;
alSourcePause(source);
alGetError();
}
//// Stop ////////////////////////////////////////////////////////////////////
void plDSoundBuffer::Stop( void )
{
if(!source)
return;
alSourceStop(source);
alGetError();
plProfile_Dec( SoundPlaying );
}
//============================================================================
void plDSoundBuffer::SetPosition(float x, float y, float z)
{
alSource3f(source, AL_POSITION, x, y, -z); // negate z coord, since openal uses opposite handedness
alGetError();
}
//============================================================================
void plDSoundBuffer::SetOrientation(float x, float y, float z)
{
alSource3f(source, AL_ORIENTATION, x, y, -z); // negate z coord, since openal uses opposite handedness
alGetError();
}
//============================================================================
void plDSoundBuffer::SetVelocity(float x, float y, float z)
{
alSource3f(source, AL_VELOCITY, 0, 0, 0); // no doppler shift
alGetError();
}
//============================================================================
void plDSoundBuffer::SetConeAngles(int inner, int outer)
{
alSourcei(source, AL_CONE_INNER_ANGLE, inner);
alSourcei(source, AL_CONE_OUTER_ANGLE, outer);
alGetError();
}
//============================================================================
void plDSoundBuffer::SetConeOrientation(float x, float y, float z)
{
alSource3f(source, AL_DIRECTION, x, y, -z); // negate z coord, since openal uses opposite handedness
alGetError();
}
//============================================================================
// vol range: -5000 - 0
void plDSoundBuffer::SetConeOutsideVolume(int vol)
{
float volume = (float)vol / 5000.0f + 1.0f; // mb to scalar
alSourcef(source, AL_CONE_OUTER_GAIN, volume);
alGetError();
}
//============================================================================
void plDSoundBuffer::Rewind( void )
{
alSourceRewind(source);
alGetError();
}
//// IsPlaying ///////////////////////////////////////////////////////////////
hsBool plDSoundBuffer::IsPlaying( void )
{
ALint state = AL_STOPPED;
alGetSourcei(source, AL_SOURCE_STATE, &state);
alGetError();
return state == AL_PLAYING;
}
//// IsEAXAccelerated ////////////////////////////////////////////////////////
hsBool plDSoundBuffer::IsEAXAccelerated( void ) const
{
return fEAXSource.IsValid();
}
//// BytePosToMSecs //////////////////////////////////////////////////////////
UInt32 plDSoundBuffer::BytePosToMSecs( UInt32 bytePos ) const
{
return (UInt32)(bytePos * 1000 / (hsScalar)fBufferDesc->fAvgBytesPerSec);
}
//// GetBufferBytePos ////////////////////////////////////////////////////////
UInt32 plDSoundBuffer::GetBufferBytePos( hsScalar timeInSecs ) const
{
hsAssert( fBufferDesc != nil, "Nil buffer description when calling GetBufferBytePos()" );
UInt32 byte = (UInt32)( timeInSecs * (hsScalar)fBufferDesc->fNumSamplesPerSec );
byte *= fBufferDesc->fBlockAlign;
return byte;
}
//// GetLengthInBytes ////////////////////////////////////////////////////////
UInt32 plDSoundBuffer::GetLengthInBytes( void ) const
{
return fBufferSize;
}
//// SetEAXSettings //////////////////////////////////////////////////////////
void plDSoundBuffer::SetEAXSettings( plEAXSourceSettings *settings, hsBool force )
{
fEAXSource.SetFrom( settings, source, force );
}
//// GetBlockAlign ///////////////////////////////////////////////////////////
UInt8 plDSoundBuffer::GetBlockAlign( void ) const
{
return ( fBufferDesc != nil ) ? fBufferDesc->fBlockAlign : 0;
}
//// SetScalarVolume /////////////////////////////////////////////////////////
// Sets the volume, but on a range from 0 to 1
void plDSoundBuffer::SetScalarVolume( hsScalar volume )
{
if(source)
{
ALenum error;
alSourcef(source, AL_GAIN, volume);
if((error = alGetError()) != AL_NO_ERROR)
plStatusLog::AddLineS("audio.log", "failed to set volume on source %d", error);
}
}
unsigned plDSoundBuffer::GetByteOffset( void )
{
ALint bytes;
alGetSourcei(source, AL_BYTE_OFFSET, &bytes);
ALenum error = alGetError();
return bytes;
}
float plDSoundBuffer::GetTimeOffsetSec( void )
{
float time;
alGetSourcef(source, AL_SEC_OFFSET, &time);
ALenum error = alGetError();
return time;
}
void plDSoundBuffer::SetTimeOffsetSec(float seconds)
{
alSourcef(source, AL_SEC_OFFSET, seconds);
ALenum error = alGetError();
}
void plDSoundBuffer::SetTimeOffsetBytes(unsigned bytes)
{
alSourcef(source, AL_BYTE_OFFSET, bytes);
ALenum error = alGetError();
}

View File

@ -0,0 +1,167 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plDSoundBuffer - Simple wrapper class for a DirectSound buffer. //
// Allows us to simplify all the work done behind the //
// scenes in plWin32BufferThread. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plDSoundBuffer_h
#define _plDSoundBuffer_h
#include "hsStlUtils.h"
#include "hsTemplates.h"
#include "plEAXEffects.h"
#define STREAMING_BUFFERS 16
#define STREAM_BUFFER_SIZE 4608*4
//#define VOICE_BUFFERS 4
//#define VOICE_BUFFER_SIZE 4608
class plWAVHeader;
class plAudioFileReader;
// Ported to OpenAL from DirectSound May 2006. Idealy the openal sources would be seperate from this class.
// OpenAl sound buffer, and source.
class plDSoundBuffer
{
public:
plDSoundBuffer( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool looping, hsBool tryStatic = false, bool streaming = false );
~plDSoundBuffer();
void Play();
void Stop();
void Pause();
void Rewind() ;
UInt32 GetLengthInBytes() const;
void SetScalarVolume( hsScalar volume ); // Sets the volume, but on a range from 0 to 1
unsigned GetSource() { return source; }
void SetPosition(float x, float y, float z);
void SetOrientation(float x, float y, float z);
void SetVelocity(float x, float y, float z);
void SetConeAngles(int inner, int outer);
void SetConeOrientation(float x, float y, float z);
void SetConeOutsideVolume(int vol);
void SetLooping( hsBool loop );
void SetMinDistance( int dist);
void SetMaxDistance( int dist );
hsBool IsValid() const { return fValid; }
hsBool IsPlaying();
hsBool IsLooping() const { return fLooping; }
hsBool IsEAXAccelerated() const;
bool FillBuffer(void *data, unsigned bytes, plWAVHeader *header);
// Streaming support
bool SetupStreamingSource(plAudioFileReader *stream);
bool SetupStreamingSource(void *data, unsigned bytes);
int BuffersProcessed();
bool StreamingFillBuffer(plAudioFileReader *stream);
bool SetupVoiceSource();
bool VoiceFillBuffer(void *data, unsigned bytes, unsigned buferId);
void UnQueueVoiceBuffers();
unsigned GetByteOffset();
UInt32 GetBufferBytePos( hsScalar timeInSecs ) const;
UInt32 BytePosToMSecs( UInt32 bytePos ) const;
void SetEAXSettings( plEAXSourceSettings *settings, hsBool force = false );
void SetTimeOffsetBytes(unsigned bytes);
UInt8 GetBlockAlign( void ) const;
static UInt32 GetNumBuffers() { return fNumBuffers; }
float GetDefaultMinDistance() { return fDefaultMinDistance; }
bool GetAvailableBufferId(unsigned *bufferId);
unsigned GetNumQueuedBuffers(){ return fNumQueuedBuffers;} // returns the max number of buffers queued on a source
float GetTimeOffsetSec();
void SetTimeOffsetSec(float seconds);
int BuffersQueued();
protected:
enum BufferType
{
kStatic,
kStreaming,
kVoice,
};
BufferType fType;
hsBool fValid, fLooping;
UInt32 fLockLength;
void * fLockPtr;
hsTArray<UInt32> fPosNotifys;
bool fStreaming;
plWAVHeader* fBufferDesc;
UInt32 fBufferSize;
unsigned buffer; // used if this is not a streaming buffer
unsigned streamingBuffers[STREAMING_BUFFERS]; // used if this is a streaming buffer
std::list<unsigned> mAvailableBuffers; // used for doing our own buffer management. Specifically voice chat, since we dont want old buffers queued
unsigned source;
unsigned int fStreamingBufferSize;
plEAXSource fEAXSource;
static UInt32 fNumBuffers;
static float fDefaultMinDistance;
unsigned fNumQueuedBuffers;
hsScalar fPrevVolume;
void IAllocate( UInt32 size, plWAVHeader &bufferDesc, hsBool enable3D, hsBool tryStatic );
void IRelease();
int IGetALFormat(unsigned bitsPerSample, unsigned int numChannels);
};
#endif //_plDSoundBuffer_h

View File

@ -0,0 +1,745 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plEAXEffects - Various classes and wrappers to support EAX //
// acceleration. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifdef EAX_SDK_AVAILABLE
# pragma message("==>Compiling with EAX_SDK")
#if defined(_MSC_VER) && _MSC_VER < 1400
// name EAX libraries here rather than in the projects so that we don't try to
// link them unless EAX_SDK_AVAILABLE (avoids the need for separate projects
// or configurations for the cases with/without EAX)
#pragma comment(lib, "eax.lib")
#pragma comment(lib, "eaxguid.lib")
#endif // _MSC_VER < 1400
#endif // EAX_SDK_AVAILABLE
#include "hsTypes.h"
#include "hsThread.h"
#ifndef EAX_SDK_AVAILABLE
#include <EFX-Util.h>
#endif
#include "plEAXEffects.h"
#include "../plAudioCore/plAudioCore.h"
#include "plDSoundBuffer.h"
#include "hsTemplates.h"
#include "plEAXListenerMod.h"
#include "hsStream.h"
#include "plAudioSystem.h"
#include <al.h>
#include <dxerr.h>
#ifdef EAX_SDK_AVAILABLE
#include <eax.h>
#include <eax-util.h>
#include <eaxlegacy.h>
#endif
#include "../plStatusLog/plStatusLog.h"
#define kDebugLog if( myLog != nil ) myLog->AddLineF(
#ifdef EAX_SDK_AVAILABLE
static EAXGet s_EAXGet;
static EAXSet s_EAXSet;
#endif
//// GetInstance /////////////////////////////////////////////////////////////
plEAXListener &plEAXListener::GetInstance( void )
{
static plEAXListener instance;
return instance;
}
//// Constructor/Destructor //////////////////////////////////////////////////
plEAXListener::plEAXListener()
{
fInited = false;
ClearProcessCache();
}
plEAXListener::~plEAXListener()
{
Shutdown();
}
//// Init ////////////////////////////////////////////////////////////////////
hsBool plEAXListener::Init( void )
{
#ifdef EAX_SDK_AVAILABLE
if( fInited )
return true;
if(!alIsExtensionPresent((ALchar *)"EAX4.0")) // is eax 4 supported
{
if(!alIsExtensionPresent((ALchar *) "EAX4.0Emulated")) // is an earlier version of eax supported
{
plStatusLog::AddLineS("audio.log", "EAX not supported");
return false;
}
else
{
plStatusLog::AddLineS("audio.log", "EAX 4 Emulated supported");
}
}
else
{
plStatusLog::AddLineS("audio.log", "EAX 4 available");
}
// EAX is supported
s_EAXGet = (EAXGet)alGetProcAddress((ALchar *)"EAXGet");
s_EAXSet = (EAXSet)alGetProcAddress((ALchar *)"EAXSet");
if(!s_EAXGet || ! s_EAXSet)
{
IFail( "EAX initialization failed", true );
return false;
}
fInited = true;
#if 1
try
{
// Make an EAX call here to prevent problems on WDM driver
unsigned int lRoom = -10000;
SetGlobalEAXProperty(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ROOM, &lRoom, sizeof( unsigned int ));
}
catch ( ... )
{
plStatusLog::AddLineS("audio.log", "Unable to set EAX Property Set, disabling EAX...");
plgAudioSys::EnableEAX(false);
return false;
}
#endif
ClearProcessCache();
return true;
#else /* !EAX_SDK_AVAILABLE */
plStatusLog::AddLineS("audio.log", "EAX disabled in this build");
return false;
#endif
}
//// Shutdown ////////////////////////////////////////////////////////////////
void plEAXListener::Shutdown( void )
{
if( !fInited )
return;
#ifdef EAX_SDK_AVAILABLE
s_EAXSet = nil;
s_EAXGet = nil;
#endif
IRelease();
}
bool plEAXListener::SetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize )
{
if(fInited)
{
#ifdef EAX_SDK_AVAILABLE
return s_EAXSet(&guid, ulProperty, 0, pData, ulDataSize) == AL_NO_ERROR;
#endif
}
return false;
}
bool plEAXListener::GetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
{
if(fInited)
{
#ifdef EAX_SDK_AVAILABLE
return s_EAXGet(&guid, ulProperty, 0, pData, ulDataSize) == AL_NO_ERROR;
#endif
}
return false;
}
bool plEAXSource::SetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
{
#ifdef EAX_SDK_AVAILABLE
return s_EAXSet(&guid, ulProperty, source, pData, ulDataSize) == AL_NO_ERROR;
#else
return false;
#endif
}
bool plEAXSource::GetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize)
{
#ifdef EAX_SDK_AVAILABLE
return s_EAXGet(&guid, ulProperty, source, pData, ulDataSize) == AL_NO_ERROR;
#else
return false;
#endif
}
//// IRelease ////////////////////////////////////////////////////////////////
void plEAXListener::IRelease( void )
{
fInited = false;
}
//// IFail ///////////////////////////////////////////////////////////////////
void plEAXListener::IFail( hsBool major )
{
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed,
"ERROR in plEAXListener: Could not set global eax params");
if( major )
IRelease();
}
void plEAXListener::IFail( const char *msg, hsBool major )
{
plStatusLog::AddLineS( "audio.log", plStatusLog::kRed,
"ERROR in plEAXListener: %s", msg );
if( major )
IRelease();
}
//// IMuteProperties /////////////////////////////////////////////////////////
// Mutes the given properties, so if you have some props that you want
// half strength, this function will do it for ya.
void plEAXListener::IMuteProperties( EAXREVERBPROPERTIES *props, hsScalar percent )
{
// We only mute the room, roomHF and roomLF, since those control the overall effect
// application. All three are a direct linear blend as defined by eax-util.cpp, so
// this should be rather easy
hsScalar invPercent = 1.f - percent;
// The old way, as dictated by EAX sample code...
#ifdef EAX_SDK_AVAILABLE
props->lRoom = (int)( ( (float)EAXLISTENER_MINROOM * invPercent ) + ( (float)props->lRoom * percent ) );
#endif
// The new way, as suggested by EAX guys...
// props->lRoom = (int)( 2000.f * log( invPercent ) ) + props->lRoom;
// props->lRoomLF = (int)( ( (float)EAXLISTENER_MINROOMLF * invPercent ) + ( (float)props->lRoomLF * percent ) );
// props->lRoomHF = (int)( ( (float)EAXLISTENER_MINROOMHF * invPercent ) + ( (float)props->lRoomHF * percent ) );
}
//// ClearProcessCache ///////////////////////////////////////////////////////
// Clears the cache settings used to speed up ProcessMods(). Call whenever
// the list of mods changed.
void plEAXListener::ClearProcessCache( void )
{
fLastBigRegion = nil;
fLastModCount = -1;
fLastWasEmpty = false;
fLastSingleStrength = -1.f;
}
//// ProcessMods /////////////////////////////////////////////////////////////
// 9.13.02 mcn - Updated the caching method. Now fLastBigRegion will point
// to a region iff it's the only region from the last pass that had a
// strength > 0. The reason we can't do our trick before is because even if
// we have a region w/ strength 1, another region might power up from 1 and
// thus suddenly alter the total reverb settings. Thus, the only time we
// can wisely skip is if our current big region == fLastBigRegion *and*
// the total strength is the same.
void plEAXListener::ProcessMods( hsTArray<plEAXListenerMod *> &modArray )
{
#ifdef EAX_SDK_AVAILABLE
int i;
float totalStrength;
hsBool firstOne;
plEAXListenerMod *thisBigRegion = nil;
EAXLISTENERPROPERTIES finalProps;
static int oldTime = timeGetTime(); // Get starting time
int newTime;
hsBool bMorphing = false;
static plStatusLog *myLog = nil;
if( myLog == nil && plgAudioSys::AreExtendedLogsEnabled() )
myLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "EAX Reverbs", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kDontWriteFile );
else if( myLog != nil && !plgAudioSys::AreExtendedLogsEnabled() )
{
delete myLog;
myLog = nil;
}
if( myLog != nil )
myLog->Clear();
if( modArray.GetCount() != fLastModCount )
{
kDebugLog "Clearing cache..." );
ClearProcessCache(); // Code path changed, clear the entire cache
fLastModCount = modArray.GetCount();
}
else
{
kDebugLog "" );
}
if( modArray.GetCount() > 0 )
{
kDebugLog "%d regions to calc", modArray.GetCount() );
// Reset and find a new one if applicable
thisBigRegion = nil;
// Accumulate settings from all the active listener regions (shouldn't be too many, we hope)
totalStrength = 0.f;
firstOne = true;
for( i = 0; i < modArray.GetCount(); i++ )
{
float strength = modArray[ i ]->GetStrength();
kDebugLog "%4.2f - %s", strength, modArray[ i ]->GetKey()->GetUoid().GetObjectName() );
if( strength > 0.f )
{
// fLastBigRegion will point to a region iff it's the only region w/ strength > 0
if( totalStrength == 0.f )
thisBigRegion = modArray[ i ];
else
thisBigRegion = nil;
if( firstOne )
{
// First one, just init to it
memcpy( &finalProps, modArray[ i ]->GetListenerProps(), sizeof( finalProps ) );
totalStrength = strength;
firstOne = false;
}
else
{
hsScalar scale = strength / ( totalStrength + strength );
EAX3ListenerInterpolate( &finalProps, modArray[ i ]->GetListenerProps(), scale, &finalProps, false );
totalStrength += strength;
bMorphing = true;
}
if( totalStrength >= 1.f )
break;
}
}
if( firstOne )
{
// No regions of strength > 0, so just make it quiet
kDebugLog "Reverb should be quiet" );
if( fLastWasEmpty )
return;
memcpy( &finalProps, &EAX30_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXLISTENERPROPERTIES ) );
finalProps.lRoom = EAXLISTENER_MINROOM;
// finalProps.lRoomLF = EAXLISTENER_MINROOMLF;
// finalProps.lRoomHF = EAXLISTENER_MINROOMHF;
fLastWasEmpty = true;
fLastBigRegion = nil;
fLastSingleStrength = -1.f;
}
else
{
fLastWasEmpty = false;
if( thisBigRegion == fLastBigRegion && totalStrength == fLastSingleStrength )
// Cached values should be the same, so we can bail at this point
return;
fLastBigRegion = thisBigRegion;
fLastSingleStrength = ( thisBigRegion != nil ) ? totalStrength : -1.f;
if( totalStrength < 1.f )
{
kDebugLog "Total strength < 1; muting result" );
// All of them together is less than full strength, so mute our result
IMuteProperties( &finalProps, totalStrength );
}
}
}
else
{
kDebugLog "No regions at all; disabling reverb" );
// No regions whatsoever, so disable listener props entirely
if( fLastWasEmpty )
return;
memcpy( &finalProps, &EAX30_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXLISTENERPROPERTIES ) );
finalProps.lRoom = EAXLISTENER_MINROOM;
// finalProps.lRoomLF = EAXLISTENER_MINROOMLF;
// finalProps.lRoomHF = EAXLISTENER_MINROOMHF;
fLastWasEmpty = true;
}
// if were morphing between regions, do 10th of a second check, otherwise just let it
// change due to opt out(caching) feature.
if(bMorphing)
{
newTime = timeGetTime();
// Update, at most, ten times per second
if((newTime - oldTime) < 100) return;
oldTime = newTime; // update time
}
//finalProps.flAirAbsorptionHF *= 0.3048f; // Convert to feet
//kDebugLog "** Updating property set **" );
if(!SetGlobalEAXProperty(DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ALLPARAMETERS, &finalProps, sizeof( finalProps )))
{
IFail( false );
}
#endif /* EAX_SDK_AVAILABLE */
}
//////////////////////////////////////////////////////////////////////////////
//// Source Settings /////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//// Constructor/Destructor //////////////////////////////////////////////////
plEAXSourceSettings::plEAXSourceSettings()
{
fDirtyParams = kAll;
Enable( false );
}
plEAXSourceSettings::~plEAXSourceSettings()
{
}
//// Read/Write/Set //////////////////////////////////////////////////////////
void plEAXSourceSettings::Read( hsStream *s )
{
fEnabled = s->ReadBool();
if( fEnabled )
{
fRoom = s->ReadSwap16();
fRoomHF = s->ReadSwap16();
fRoomAuto = s->ReadBool();
fRoomHFAuto = s->ReadBool();
fOutsideVolHF = s->ReadSwap16();
fAirAbsorptionFactor = s->ReadSwapFloat();
fRoomRolloffFactor = s->ReadSwapFloat();
fDopplerFactor = s->ReadSwapFloat();
fRolloffFactor = s->ReadSwapFloat();
fSoftStarts.Read( s );
fSoftEnds.Read( s );
fOcclusionSoftValue = -1.f;
SetOcclusionSoftValue( s->ReadSwapFloat() );
fDirtyParams = kAll;
}
else
Enable( false ); // Force init of params
}
void plEAXSourceSettings::Write( hsStream *s )
{
s->WriteBool( fEnabled );
if( fEnabled )
{
s->WriteSwap16( fRoom );
s->WriteSwap16( fRoomHF );
s->WriteBool( fRoomAuto );
s->WriteBool( fRoomHFAuto );
s->WriteSwap16( fOutsideVolHF );
s->WriteSwapFloat( fAirAbsorptionFactor );
s->WriteSwapFloat( fRoomRolloffFactor );
s->WriteSwapFloat( fDopplerFactor );
s->WriteSwapFloat( fRolloffFactor );
fSoftStarts.Write( s );
fSoftEnds.Write( s );
s->WriteSwapFloat( fOcclusionSoftValue );
}
}
void plEAXSourceSettings::SetRoomParams( Int16 room, Int16 roomHF, hsBool roomAuto, hsBool roomHFAuto )
{
fRoom = room;
fRoomHF = roomHF;
fRoomAuto = roomAuto;
fRoomHFAuto = roomHFAuto;
fDirtyParams |= kRoom;
}
void plEAXSourceSettings::Enable( hsBool e )
{
fEnabled = e;
if( !e )
{
#ifdef EAX_SDK_AVAILABLE
fRoom = EAXBUFFER_MINROOM;
fRoomHF = EAXBUFFER_MINROOMHF;
#else
fRoom = 0;
fRoomHF = 0;
#endif
fRoomAuto = true;
fRoomHFAuto = true;
fOutsideVolHF = 0;
fAirAbsorptionFactor = 1.f;
fRoomRolloffFactor = 0.f;
fDopplerFactor = 0.f;
fRolloffFactor = 0.f;
fOcclusionSoftValue = 0.f;
fSoftStarts.Reset();
fSoftEnds.Reset();
fCurrSoftValues.Reset();
fDirtyParams = kAll;
}
}
void plEAXSourceSettings::SetOutsideVolHF( Int16 vol )
{
fOutsideVolHF = vol;
fDirtyParams |= kOutsideVolHF;
}
void plEAXSourceSettings::SetFactors( hsScalar airAbsorption, hsScalar roomRolloff, hsScalar doppler, hsScalar rolloff )
{
fAirAbsorptionFactor = airAbsorption;
fRoomRolloffFactor = roomRolloff;
fDopplerFactor = doppler;
fRolloffFactor = rolloff;
fDirtyParams |= kFactors;
}
void plEAXSourceSettings::SetOcclusionSoftValue( hsScalar value )
{
if( fOcclusionSoftValue != value )
{
fOcclusionSoftValue = value;
IRecalcSofts( kOcclusion );
fDirtyParams |= kOcclusion;
}
}
void plEAXSourceSettings::IRecalcSofts( UInt8 whichOnes )
{
hsScalar percent, invPercent;
if( whichOnes & kOcclusion )
{
percent = fOcclusionSoftValue;
invPercent = 1.f - percent;
Int16 occ = (Int16)( ( (float)fSoftStarts.GetOcclusion() * invPercent ) + ( (float)fSoftEnds.GetOcclusion() * percent ) );
hsScalar lfRatio = (hsScalar)( ( fSoftStarts.GetOcclusionLFRatio() * invPercent ) + ( fSoftEnds.GetOcclusionLFRatio() * percent ) );
hsScalar roomRatio = (hsScalar)( ( fSoftStarts.GetOcclusionRoomRatio() * invPercent ) + ( fSoftEnds.GetOcclusionRoomRatio() * percent ) );
hsScalar directRatio = (hsScalar)( ( fSoftStarts.GetOcclusionDirectRatio() * invPercent ) + ( fSoftEnds.GetOcclusionDirectRatio() * percent ) );
fCurrSoftValues.SetOcclusion( occ, lfRatio, roomRatio, directRatio );
}
}
//////////////////////////////////////////////////////////////////////////////
//// Source Soft Settings ////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void plEAXSourceSoftSettings::Reset( void )
{
fOcclusion = 0;
fOcclusionLFRatio = 0.25f;
fOcclusionRoomRatio = 1.5f;
fOcclusionDirectRatio = 1.f;
}
void plEAXSourceSoftSettings::Read( hsStream *s )
{
s->ReadSwap( &fOcclusion );
s->ReadSwap( &fOcclusionLFRatio );
s->ReadSwap( &fOcclusionRoomRatio );
s->ReadSwap( &fOcclusionDirectRatio );
}
void plEAXSourceSoftSettings::Write( hsStream *s )
{
s->WriteSwap( fOcclusion );
s->WriteSwap( fOcclusionLFRatio );
s->WriteSwap( fOcclusionRoomRatio );
s->WriteSwap( fOcclusionDirectRatio );
}
void plEAXSourceSoftSettings::SetOcclusion( Int16 occ, hsScalar lfRatio, hsScalar roomRatio, hsScalar directRatio )
{
fOcclusion = occ;
fOcclusionLFRatio = lfRatio;
fOcclusionRoomRatio = roomRatio;
fOcclusionDirectRatio = directRatio;
}
//// Constructor/Destructor //////////////////////////////////////////////////
plEAXSource::plEAXSource()
{
fInit = false;
}
plEAXSource::~plEAXSource()
{
Release();
}
//// Init/Release ////////////////////////////////////////////////////////////
void plEAXSource::Init( plDSoundBuffer *parent )
{
fInit = true;
// Init some default params
plEAXSourceSettings defaultParams;
SetFrom( &defaultParams, parent->GetSource() );
}
void plEAXSource::Release( void )
{
fInit = false;
}
hsBool plEAXSource::IsValid( void ) const
{
return true;
}
//// SetFrom /////////////////////////////////////////////////////////////////
void plEAXSource::SetFrom( plEAXSourceSettings *settings, unsigned source, hsBool force )
{
UInt32 dirtyParams;
if(source == 0 || !fInit)
return;
if( force )
dirtyParams = plEAXSourceSettings::kAll;
else
dirtyParams = settings->fDirtyParams;
// Do the params
#ifdef EAX_SDK_AVAILABLE
if( dirtyParams & plEAXSourceSettings::kRoom )
{
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOM, &settings->fRoom, sizeof(settings->fRoom));
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOMHF, &settings->fRoomHF, sizeof(settings->fRoomHF));
}
if( dirtyParams & plEAXSourceSettings::kOutsideVolHF )
{
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, &settings->fOutsideVolHF, sizeof(settings->fOutsideVolHF));
}
if( dirtyParams & plEAXSourceSettings::kFactors )
{
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, &settings->fDopplerFactor, sizeof(settings->fDopplerFactor));
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, &settings->fRolloffFactor, sizeof(settings->fRolloffFactor));
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, &settings->fRoomRolloffFactor, sizeof(settings->fRoomRolloffFactor));
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, &settings->fAirAbsorptionFactor, sizeof(settings->fAirAbsorptionFactor));
}
if( dirtyParams & plEAXSourceSettings::kOcclusion )
{
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSION, &settings->GetCurrSofts().fOcclusion, sizeof(settings->GetCurrSofts().fOcclusion));
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, &settings->GetCurrSofts().fOcclusionLFRatio, sizeof(settings->GetCurrSofts().fOcclusionLFRatio));
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, &settings->GetCurrSofts().fOcclusionRoomRatio, sizeof(settings->GetCurrSofts().fOcclusionRoomRatio));
SetSourceEAXProperty(source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, &settings->GetCurrSofts().fOcclusionDirectRatio, sizeof(settings->GetCurrSofts().fOcclusionDirectRatio));
}
#endif /* EAX_SDK_AVAILABLE */
settings->ClearDirtyParams();
// Do all the flags in one pass
#ifdef EAX_SDK_AVAILABLE
DWORD flags;
if( GetSourceEAXProperty( source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_FLAGS, &flags, sizeof( DWORD )) )
{
if( settings->GetRoomAuto() )
flags |= EAXBUFFERFLAGS_ROOMAUTO;
else
flags &= ~EAXBUFFERFLAGS_ROOMAUTO;
if( settings->GetRoomHFAuto() )
flags |= EAXBUFFERFLAGS_ROOMHFAUTO;
else
flags &= ~EAXBUFFERFLAGS_ROOMHFAUTO;
if( SetSourceEAXProperty( source, DSPROPSETID_EAX_BufferProperties, DSPROPERTY_EAXBUFFER_FLAGS, &flags, sizeof( DWORD ) ) )
{
return; // All worked, return here
}
// Flag setting failed somehow
hsAssert( false, "Unable to set EAX buffer flags" );
}
#endif /* EAX_SDK_AVAILABLE */
}

View File

@ -0,0 +1,212 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plEAXEffects - Various classes and wrappers to support EAX //
// acceleration. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plEAXEffects_h
#define _plEAXEffects_h
#include "hsTypes.h"
#include "hsTemplates.h"
//// Listener Settings Class Definition ///////////////////////////////////////
class plDSoundBuffer;
class plEAXListenerMod;
#ifdef EAX_SDK_AVAILABLE
typedef struct _EAXREVERBPROPERTIES EAXREVERBPROPERTIES;
#else
#include <EFX-Util.h>
#endif
class plEAXListener
{
public:
~plEAXListener();
static plEAXListener &GetInstance( void );
hsBool Init( void );
void Shutdown( void );
bool SetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize );
bool GetGlobalEAXProperty(GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize );
void ProcessMods( hsTArray<plEAXListenerMod *> &modArray );
void ClearProcessCache( void );
protected:
plEAXListener();
void IFail( hsBool major );
void IFail( const char *msg, hsBool major );
void IRelease( void );
void IMuteProperties( EAXREVERBPROPERTIES *props, hsScalar percent );
hsBool fInited;
// Cache info
Int32 fLastModCount;
hsBool fLastWasEmpty;
hsScalar fLastSingleStrength;
plEAXListenerMod *fLastBigRegion;
};
//// Soft Buffer Settings Class Definition ////////////////////////////////////
// Used to hold buffer settings that will be attenuated by a soft volume,
// to make the main settings class a bit cleaner
class hsStream;
class plEAXSourceSoftSettings
{
public:
Int16 fOcclusion;
hsScalar fOcclusionLFRatio, fOcclusionRoomRatio, fOcclusionDirectRatio;
void Read( hsStream *s );
void Write( hsStream *s );
void SetOcclusion( Int16 occ, hsScalar lfRatio, hsScalar roomRatio, hsScalar directRatio );
Int16 GetOcclusion( void ) const { return fOcclusion; }
hsScalar GetOcclusionLFRatio( void ) const { return fOcclusionLFRatio; }
hsScalar GetOcclusionRoomRatio( void ) const { return fOcclusionRoomRatio; }
hsScalar GetOcclusionDirectRatio( void ) const { return fOcclusionDirectRatio; }
void Reset( void );
};
//// Buffer Settings Class Definition /////////////////////////////////////////
class plEAXSource;
class plEAXSourceSettings
{
public:
plEAXSourceSettings();
virtual ~plEAXSourceSettings();
void Read( hsStream *s );
void Write( hsStream *s );
void Enable( hsBool e );
hsBool IsEnabled( void ) const { return fEnabled; }
void SetRoomParams( Int16 room, Int16 roomHF, hsBool roomAuto, hsBool roomHFAuto );
Int16 GetRoom( void ) const { return fRoom; }
Int16 GetRoomHF( void ) const { return fRoomHF; }
hsBool GetRoomAuto( void ) const { return fRoomAuto; }
hsBool GetRoomHFAuto( void ) const { return fRoomHFAuto; }
void SetOutsideVolHF( Int16 vol );
Int16 GetOutsideVolHF( void ) const { return fOutsideVolHF; }
void SetFactors( hsScalar airAbsorption, hsScalar roomRolloff, hsScalar doppler, hsScalar rolloff );
hsScalar GetAirAbsorptionFactor( void ) const { return fAirAbsorptionFactor; }
hsScalar GetRoomRolloffFactor( void ) const { return fRoomRolloffFactor; }
hsScalar GetDopplerFactor( void ) const { return fDopplerFactor; }
hsScalar GetRolloffFactor( void ) const { return fRolloffFactor; }
plEAXSourceSoftSettings &GetSoftStarts( void ) { return fSoftStarts; }
plEAXSourceSoftSettings &GetSoftEnds( void ) { return fSoftEnds; }
plEAXSourceSoftSettings &GetCurrSofts( void ) { return fCurrSoftValues; }
void SetOcclusionSoftValue( hsScalar value );
hsScalar GetOcclusionSoftValue( void ) const { return fOcclusionSoftValue; }
void ClearDirtyParams( void ) const { fDirtyParams = 0; }
protected:
friend class plEAXSource;
friend plEAXSourceSoftSettings;
hsBool fEnabled;
Int16 fRoom, fRoomHF;
hsBool fRoomAuto, fRoomHFAuto;
Int16 fOutsideVolHF;
hsScalar fAirAbsorptionFactor, fRoomRolloffFactor, fDopplerFactor, fRolloffFactor;
plEAXSourceSoftSettings fSoftStarts, fSoftEnds, fCurrSoftValues;
hsScalar fOcclusionSoftValue;
mutable UInt32 fDirtyParams;
enum ParamSets
{
kOcclusion = 0x01,
kRoom = 0x02,
kOutsideVolHF = 0x04,
kFactors = 0x08,
kAll = 0xff
};
void IRecalcSofts( UInt8 whichOnes );
};
//// Source Class Definition //////////////////////////////////////////////////
class plEAXSource
{
public:
friend plEAXSourceSettings;
friend plEAXSourceSoftSettings;
plEAXSource();
virtual ~plEAXSource();
void Init( plDSoundBuffer *parent );
void Release( void );
hsBool IsValid( void ) const;
bool SetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize);
bool GetSourceEAXProperty(unsigned source, GUID guid, unsigned long ulProperty, void *pData, unsigned long ulDataSize);
void SetFrom( plEAXSourceSettings *settings, unsigned source, hsBool force = false );
private:
hsBool fInit;
};
#endif //_plEAXEffects_h

View File

@ -0,0 +1,253 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plEAXListenerMod //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef EAX_SDK_AVAILABLE
#include <EFX-Util.h>
#endif
#include "hsTypes.h"
#include "plEAXListenerMod.h"
#include "../plIntersect/plSoftVolume.h"
#include "hsResMgr.h"
#include "plgDispatch.h"
#include "plAudioSystem.h"
#include "../pnMessage/plAudioSysMsg.h"
#ifdef EAX_SDK_AVAILABLE
#include <eax-util.h>
#endif
plEAXListenerMod::plEAXListenerMod()
{
fListenerProps = TRACKED_NEW EAXREVERBPROPERTIES;
fSoftRegion = nil;
fRegistered = false;
fGetsMessages = false;
#ifdef EAX_SDK_AVAILABLE
memcpy( fListenerProps, &REVERB_ORIGINAL_PRESETS[ ORIGINAL_GENERIC ], sizeof( EAXREVERBPROPERTIES ) );
#endif
}
plEAXListenerMod::~plEAXListenerMod()
{
// Tell the audio sys we're going away
IUnRegister();
// Unregister for audioSys messages
if( fGetsMessages )
{
plgDispatch::Dispatch()->UnRegisterForExactType( plAudioSysMsg::Index(), GetKey() );
fGetsMessages = false;
}
delete fListenerProps;
}
void plEAXListenerMod::IRegister( void )
{
if( !fGetsMessages )
{
plgDispatch::Dispatch()->RegisterForExactType( plAudioSysMsg::Index(), GetKey() );
fGetsMessages = true;
}
if( fRegistered || GetKey() == nil )
return;
plKey sysKey = hsgResMgr::ResMgr()->FindKey( plUoid( kAudioSystem_KEY ) );
if( sysKey != nil )
{
plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg( sysKey, plRefMsg::kOnCreate, 0, plAudioSystem::kRefEAXRegion );
hsgResMgr::ResMgr()->AddViaNotify( GetKey(), refMsg, plRefFlags::kPassiveRef );
fRegistered = true;
}
}
void plEAXListenerMod::IUnRegister( void )
{
if( !fRegistered || GetKey() == nil )
return;
plKey sysKey = hsgResMgr::ResMgr()->FindKey( plUoid( kAudioSystem_KEY ) );
if( sysKey != nil && GetKey() != nil )
sysKey->Release( GetKey() );
fRegistered = false;
}
hsBool plEAXListenerMod::IEval( double secs, hsScalar del, UInt32 dirty )
{
IRegister();
return false;
}
hsBool plEAXListenerMod::MsgReceive( plMessage* pMsg )
{
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef( pMsg );
if( refMsg != nil )
{
switch( refMsg->fType )
{
case kRefSoftRegion:
if( refMsg->GetContext() & ( plRefMsg::kOnCreate | plRefMsg::kOnRequest | plRefMsg::kOnReplace ) )
{
fSoftRegion = plSoftVolume::ConvertNoRef( refMsg->GetRef() );
fSoftRegion->SetCheckListener();
}
else if( refMsg->GetContext() & ( plRefMsg::kOnRemove | plRefMsg::kOnDestroy ) )
{
fSoftRegion = nil;
}
break;
}
}
plAudioSysMsg *sysMsg = plAudioSysMsg::ConvertNoRef( pMsg );
if( sysMsg != nil )
{
if( sysMsg->GetAudFlag() == plAudioSysMsg::kActivate )
{
IRegister();
}
else if( sysMsg->GetAudFlag() == plAudioSysMsg::kDeActivate )
{
IUnRegister();
}
return true;
}
return plSingleModifier::MsgReceive( pMsg );
}
void plEAXListenerMod::Read( hsStream* s, hsResMgr* mgr )
{
plSingleModifier::Read( s, mgr );
// Read in the soft region
mgr->ReadKeyNotifyMe( s, TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, 0, kRefSoftRegion ), plRefFlags::kActiveRef );
// Read the listener params
fListenerProps->ulEnvironment = s->ReadSwap32();
fListenerProps->flEnvironmentSize = s->ReadSwapFloat();
fListenerProps->flEnvironmentDiffusion = s->ReadSwapFloat();
fListenerProps->lRoom = s->ReadSwap32();
fListenerProps->lRoomHF = s->ReadSwap32();
fListenerProps->lRoomLF = s->ReadSwap32();
fListenerProps->flDecayTime = s->ReadSwapFloat();
fListenerProps->flDecayHFRatio = s->ReadSwapFloat();
fListenerProps->flDecayLFRatio = s->ReadSwapFloat();
fListenerProps->lReflections = s->ReadSwap32();
fListenerProps->flReflectionsDelay = s->ReadSwapFloat();
//fListenerProps->vReflectionsPan; // early reflections panning vector
fListenerProps->lReverb = s->ReadSwap32(); // late reverberation level relative to room effect
fListenerProps->flReverbDelay = s->ReadSwapFloat();
//fListenerProps->vReverbPan; // late reverberation panning vector
fListenerProps->flEchoTime = s->ReadSwapFloat();
fListenerProps->flEchoDepth = s->ReadSwapFloat();
fListenerProps->flModulationTime = s->ReadSwapFloat();
fListenerProps->flModulationDepth = s->ReadSwapFloat();
fListenerProps->flAirAbsorptionHF = s->ReadSwapFloat();
fListenerProps->flHFReference = s->ReadSwapFloat();
fListenerProps->flLFReference = s->ReadSwapFloat();
fListenerProps->flRoomRolloffFactor = s->ReadSwapFloat();
fListenerProps->ulFlags = s->ReadSwap32();
// Done reading, time to tell the audio sys we exist
IRegister();
}
void plEAXListenerMod::Write( hsStream* s, hsResMgr* mgr )
{
plSingleModifier::Write( s, mgr );
// Write the soft region key
mgr->WriteKey( s, fSoftRegion );
// Write the listener params
s->WriteSwap32( fListenerProps->ulEnvironment );
s->WriteSwapFloat( fListenerProps->flEnvironmentSize );
s->WriteSwapFloat( fListenerProps->flEnvironmentDiffusion );
s->WriteSwap32( fListenerProps->lRoom );
s->WriteSwap32( fListenerProps->lRoomHF );
s->WriteSwap32( fListenerProps->lRoomLF );
s->WriteSwapFloat( fListenerProps->flDecayTime );
s->WriteSwapFloat( fListenerProps->flDecayHFRatio );
s->WriteSwapFloat( fListenerProps->flDecayLFRatio );
s->WriteSwap32( fListenerProps->lReflections );
s->WriteSwapFloat( fListenerProps->flReflectionsDelay );
//s->WriteSwapFloat( fListenerProps->vReflectionsPan; // early reflections panning vector
s->WriteSwap32( fListenerProps->lReverb ); // late reverberation level relative to room effect
s->WriteSwapFloat( fListenerProps->flReverbDelay );
//s->WriteSwapFloat( fListenerProps->vReverbPan; // late reverberation panning vector
s->WriteSwapFloat( fListenerProps->flEchoTime );
s->WriteSwapFloat( fListenerProps->flEchoDepth );
s->WriteSwapFloat( fListenerProps->flModulationTime );
s->WriteSwapFloat( fListenerProps->flModulationDepth );
s->WriteSwapFloat( fListenerProps->flAirAbsorptionHF );
s->WriteSwapFloat( fListenerProps->flHFReference );
s->WriteSwapFloat( fListenerProps->flLFReference );
s->WriteSwapFloat( fListenerProps->flRoomRolloffFactor );
s->WriteSwap32( fListenerProps->ulFlags );
}
void plEAXListenerMod::SetFromPreset( UInt32 preset )
{
#ifdef EAX_SDK_AVAILABLE
memcpy( fListenerProps, &REVERB_ORIGINAL_PRESETS[ preset ], sizeof( EAXREVERBPROPERTIES ) );
#endif
}
float plEAXListenerMod::GetStrength( void )
{
if( fSoftRegion == nil )
return 0.f;
return fSoftRegion->GetListenerStrength();
}

View File

@ -0,0 +1,93 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plEAXListenerMod Header //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plEAXListenerMod_h
#define _plEAXListenerMod_h
#include "../pnModifier/plSingleModifier.h"
class plMessage;
class plSoftVolume;
#ifdef EAX_SDK_AVAILABLE
typedef struct _EAXREVERBPROPERTIES EAXREVERBPROPERTIES;
#endif
class plEAXListenerMod : public plSingleModifier
{
public:
plEAXListenerMod();
virtual ~plEAXListenerMod();
CLASSNAME_REGISTER( plEAXListenerMod );
GETINTERFACE_ANY( plEAXListenerMod, plSingleModifier );
enum Refs
{
kRefSoftRegion = 0,
};
virtual hsBool MsgReceive( plMessage* pMsg );
virtual void Read( hsStream* s, hsResMgr* mgr );
virtual void Write( hsStream* s, hsResMgr* mgr );
float GetStrength( void );
EAXREVERBPROPERTIES * GetListenerProps( void ) { return fListenerProps; }
void SetFromPreset( UInt32 preset );
protected:
plSoftVolume *fSoftRegion;
EAXREVERBPROPERTIES *fListenerProps;
hsBool fRegistered, fGetsMessages;
void IRegister( void );
void IUnRegister( void );
virtual hsBool IEval( double secs, hsScalar del, UInt32 dirty ); // called only by owner object's Eval()
};
#endif // _plEAXListenerMod_h

View File

@ -0,0 +1,358 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plOGGCodec - Plasma codec support for the OGG/Vorbis file format. //
// //
//// Notes ///////////////////////////////////////////////////////////////////
// //
// 2.7.2003 - Created by mcn. If only life were really this simple... //
// //
//////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#include "hsTypes.h"
#include "plOGGCodec.h"
#include "hsTimer.h"
#include "../pnNetCommon/plNetApp.h"
plOGGCodec::DecodeFormat plOGGCodec::fDecodeFormat = plOGGCodec::k16bitSigned;
UInt8 plOGGCodec::fDecodeFlags = 0;
//// Constructor/Destructor //////////////////////////////////////////////////
plOGGCodec::plOGGCodec( const char *path, plAudioCore::ChannelSelect whichChan ) : fFileHandle( nil )
{
fOggFile = nil;
IOpen( path, whichChan );
fCurHeaderPos = 0;
fHeadBuf = nil;
}
void plOGGCodec::BuildActualWaveHeader()
{
// Build an actual WAVE header for this ogg
int fmtSize = 16;
short fmt = 1;
int factsize = 4;
int factdata = 0;
int size = fDataSize+48; // size of data with header except for first four bytes
fHeadBuf = (UInt8 *) ALLOC(56);
memcpy(fHeadBuf, "RIFF", 4);
memcpy(fHeadBuf+4, &size, 4);
memcpy(fHeadBuf+8, "WAVE", 4);
memcpy(fHeadBuf+12, "fmt ", 4);
memcpy(fHeadBuf+16, &fmtSize, 4);
memcpy(fHeadBuf+20, &fmt, 2); /* format */
memcpy(fHeadBuf+22, &fHeader.fNumChannels, 2);
memcpy(fHeadBuf+24, &fHeader.fNumSamplesPerSec, 4);
memcpy(fHeadBuf+28, &fHeader.fAvgBytesPerSec, 4);
memcpy(fHeadBuf+32, &fHeader.fBlockAlign, 4);
memcpy(fHeadBuf+34, &fHeader.fBitsPerSample, 2);
memcpy(fHeadBuf+36, "fact", 4);
memcpy(fHeadBuf+40, &factsize, 4);
memcpy(fHeadBuf+44, &factdata, 4);
memcpy(fHeadBuf+48, "data", 4);
memcpy(fHeadBuf+52, &fDataSize, 4);
}
bool plOGGCodec::ReadFromHeader(int numBytes, void *data)
{
if(fCurHeaderPos < 56)
{
memcpy(data, fHeadBuf+fCurHeaderPos, numBytes);
fCurHeaderPos += numBytes;
return true;
}
return false;
}
void plOGGCodec::IOpen( const char *path, plAudioCore::ChannelSelect whichChan )
{
hsAssert( path != nil, "Invalid path specified in plOGGCodec reader" );
// plNetClientApp::StaticDebugMsg("Ogg Open %s, t=%f, start", path, hsTimer::GetSeconds());
strncpy( fFilename, path, sizeof( fFilename ) );
fWhichChannel = whichChan;
/// Open the file as a plain binary stream
fFileHandle = fopen( path, "rb" );
if( fFileHandle != nil )
{
/// Create the OGG data struct
fOggFile = TRACKED_NEW OggVorbis_File;
/// Open the OGG decompressor
if( ov_open( fFileHandle, fOggFile, NULL, 0 ) < 0 )
{
IError( "Unable to open OGG source file" );
return;
}
/// Construct some header info from the ogg info
vorbis_info *vInfo = ov_info( fOggFile, -1 );
fHeader.fFormatTag = 1;
fHeader.fNumChannels = vInfo->channels;
fHeader.fNumSamplesPerSec = vInfo->rate;
// Funny thing about the bits per sample: we get to CHOOSE. Go figure!
fHeader.fBitsPerSample = ( fDecodeFormat == k8bitUnsigned ) ? 8 : 16;
// Why WAV files hold this info when it can be calculated is beyond me...
fHeader.fBlockAlign = ( fHeader.fBitsPerSample * fHeader.fNumChannels ) >> 3;
fHeader.fAvgBytesPerSec = fHeader.fNumSamplesPerSec * fHeader.fBlockAlign;
/// The size in bytes of our PCM data stream
/// Note: OGG sometimes seems to be off by 1 sample, which causes our reads to suck
/// because we end up waiting for 1 more sample than we actually have. So, on the
/// assumption that OGG is just slightly wrong sometimes, we just subtract 1 sample
/// from what it tells us. As Brice put it, who's going to miss 1/40,000'ths of a second?
fDataSize = (UInt32)(( ov_pcm_total( fOggFile, -1 ) - 1 ) * fHeader.fBlockAlign);
/// Channel select
if( fWhichChannel != plAudioCore::kAll )
{
fChannelAdjust = 2;
fChannelOffset = ( fWhichChannel == plAudioCore::kLeft ) ? 0 : 1;
}
else
{
fChannelAdjust = 1;
fChannelOffset = 0;
}
/// Construct our fake header for channel adjustment
fFakeHeader = fHeader;
fFakeHeader.fAvgBytesPerSec /= fChannelAdjust;
fFakeHeader.fNumChannels /= (UInt16)fChannelAdjust;
fFakeHeader.fBlockAlign /= (UInt16)fChannelAdjust;
SetPosition( 0 );
}
// plNetClientApp::StaticDebugMsg("Ogg Open %s, t=%f, end", path, hsTimer::GetSeconds());
}
plOGGCodec::~plOGGCodec()
{
Close();
}
void plOGGCodec::Close( void )
{
// plNetClientApp::StaticDebugMsg("Ogg Close, t=%f, start", hsTimer::GetSeconds());
FREE(fHeadBuf);
fHeadBuf = nil;
if( fOggFile != nil )
{
ov_clear( fOggFile );
DEL(fOggFile);
fOggFile = nil;
}
if( fFileHandle != nil )
{
fclose( fFileHandle );
fFileHandle = nil;
}
// plNetClientApp::StaticDebugMsg("Ogg Close, t=%f, end", hsTimer::GetSeconds());
}
void plOGGCodec::IError( const char *msg )
{
hsAssert( false, msg );
Close();
}
plWAVHeader &plOGGCodec::GetHeader( void )
{
hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" );
return fFakeHeader;
}
float plOGGCodec::GetLengthInSecs( void )
{
hsAssert( IsValid(), "GetLengthInSecs() called on an invalid OGG file" );
// Just query ogg directly...starting to see how cool ogg is yet?
return (float)ov_time_total( fOggFile, -1 );
}
hsBool plOGGCodec::SetPosition( UInt32 numBytes )
{
hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" );
if( !ov_seekable( fOggFile ) )
{
hsAssert( false, "Trying to set position on an unseekable OGG stream!" );
return false;
}
// The numBytes position is in uncompressed space and should be sample-aligned anyway,
// so this should be just fine here.
ogg_int64_t newSample = ( numBytes / (fFakeHeader.fBlockAlign * fChannelAdjust) );
// Now please note how freaking easy it is here to do accurate or fast seeking...
// Also note that if we're doing our channel extraction, we MUST do it the accurate way
if( ( fDecodeFlags & kFastSeeking ) && fChannelAdjust == 1 )
{
if( ov_pcm_seek_page( fOggFile, newSample ) != 0 )
{
IError( "Unable to seek OGG stream" );
return false;
}
}
else
{
if( ov_pcm_seek( fOggFile, newSample ) != 0 )
{
IError( "Unable to seek OGG stream" );
return false;
}
}
return true;
}
hsBool plOGGCodec::Read( UInt32 numBytes, void *buffer )
{
hsAssert( IsValid(), "GetHeader() called on an invalid OGG file" );
// plNetClientApp::StaticDebugMsg("Ogg Read, t=%f, start", hsTimer::GetSeconds());
int bytesPerSample = ( fDecodeFormat == k16bitSigned ) ? 2 : 1;
int isSigned = ( fDecodeFormat == k16bitSigned ) ? 1 : 0;
int currSection;
if( fWhichChannel == plAudioCore::kAll )
{
// Easy, just a straight read
char *uBuffer = (char *)buffer;
while( numBytes > 0 )
{
// Supposedly we should pay attention to currSection in case of bitrate changes,
// but hopefully we'll never have those....
long bytesRead = ov_read( fOggFile, uBuffer, numBytes, 0, bytesPerSample, isSigned, &currSection );
// Since our job is so simple, do some extra error checking
if( bytesRead == OV_HOLE )
{
IError( "Unable to read from OGG file: missing data" );
return false;
}
else if( bytesRead == OV_EBADLINK )
{
IError( "Unable to read from OGG file: corrupt link" );
return false;
}
else if( bytesRead == 0 )
{
IError( "Unable to finish reading from OGG file: end of stream" );
return false;
}
else if( bytesRead < 0 )
{
IError( "Unable to read from OGG file: unknown error" );
return false;
}
numBytes -= bytesRead;
uBuffer += bytesRead;
}
}
else
{
/// Read in 4k chunks and extract
static char trashBuffer[ 4096 ];
long toRead, i, thisRead, sampleSize = fFakeHeader.fBlockAlign;
for( ; numBytes > 0; )
{
/// Read 4k worth of samples
toRead = ( sizeof( trashBuffer ) < numBytes * fChannelAdjust ) ? sizeof( trashBuffer ) : numBytes * fChannelAdjust;
thisRead = ov_read( fOggFile, (char *)trashBuffer, toRead, 0, bytesPerSample, isSigned, &currSection );
if( thisRead < 0 )
return false;
/// Copy every other sample out
int sampleOffset = (fChannelOffset == 1) ? sampleSize : 0;
for (i = 0; i < thisRead; i += sampleSize * 2)
{
memcpy(buffer, &trashBuffer[i + sampleOffset], sampleSize);
buffer = (void*)((UInt8*)buffer + sampleSize);
numBytes -= sampleSize;
}
}
}
// plNetClientApp::StaticDebugMsg("Ogg Read, t=%f, end", hsTimer::GetSeconds());
return true;
}
UInt32 plOGGCodec::NumBytesLeft( void )
{
if(!IsValid())
{
hsAssert( false, "GetHeader() called on an invalid OGG file" );
return 0;
}
return (UInt32)(( fDataSize - ( ov_pcm_tell( fOggFile ) * fHeader.fBlockAlign ) ) / fChannelAdjust);
}

View File

@ -0,0 +1,122 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plOGGCodec - Plasma codec support for the OGG/Vorbis file format. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plOGGCodec_h
#define _plOGGCodec_h
#include "../plAudioCore/plAudioFileReader.h"
//// Class Definition ////////////////////////////////////////////////////////
struct OggVorbis_File;
class plOGGCodec : public plAudioFileReader
{
public:
plOGGCodec( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll );
virtual ~plOGGCodec();
enum DecodeFormat
{
k8bitUnsigned,
k16bitSigned
};
enum DecodeFlags
{
kFastSeeking = 0x01
};
virtual plWAVHeader &GetHeader( void );
virtual void Close( void );
virtual UInt32 GetDataSize( void ) { return fDataSize / fChannelAdjust; }
virtual float GetLengthInSecs( void );
virtual hsBool SetPosition( UInt32 numBytes );
virtual hsBool Read( UInt32 numBytes, void *buffer );
virtual UInt32 NumBytesLeft( void );
virtual hsBool IsValid( void ) { return ( fOggFile != nil ) ? true : false; }
static void SetDecodeFormat( DecodeFormat f ) { fDecodeFormat = f; }
static void SetDecodeFlag( UInt8 flag, hsBool on ) { if( on ) fDecodeFlags |= flag; else fDecodeFlags &= ~flag; }
static UInt8 GetDecodeFlags( void ) { return fDecodeFlags; }
void ResetWaveHeaderRef() { fCurHeaderPos = 0; }
void BuildActualWaveHeader();
bool ReadFromHeader(int numBytes, void *data); // read from Actual wave header
protected:
enum
{
kPCMFormatTag = 1
};
char fFilename[ 512 ];
FILE *fFileHandle;
OggVorbis_File *fOggFile;
plWAVHeader fHeader, fFakeHeader;
UInt32 fDataStartPos, fCurrDataPos, fDataSize;
plAudioCore::ChannelSelect fWhichChannel;
UInt32 fChannelAdjust, fChannelOffset;
static DecodeFormat fDecodeFormat;
static UInt8 fDecodeFlags;
UInt8 * fHeadBuf;
int fCurHeaderPos;
void IError( const char *msg );
void IOpen( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll );
};
#endif //_plOGGCodec_h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,413 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plSound.h - Base sound class header //
// //
//// History /////////////////////////////////////////////////////////////////
// //
// 10.12.01 mcn - Added preliminary soft region (volume) support. //
// 7.12.02 mcn - Added EAX support //
// 7.15.02 mcn - Added support for animated volumes //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef plSound_h
#define plSound_h
#include "hsTemplates.h"
#include "hsGeometry3.h"
#include "plEAXEffects.h"
#include "../pnNetCommon/plSynchedObject.h"
#include "../plAvatar/plAGChannel.h"
#include "../plAvatar/plAGApplicator.h"
#include "../plAudioCore/plSoundBuffer.h"
class hsResMgr;
class hsStream;
class plSoundProxy;
class plDrawableSpans;
class hsGMaterial;
class plSoundMsg;
class plSoftVolume;
class plGraphPlate;
struct hsMatrix44;
class plSoundBuffer;
class plSceneObject;
class plSoundVolumeApplicator;
// Set this to 1 to do our own distance attenuation (model doesn't work yet tho)
#define MCN_HACK_OUR_ATTEN 0
#define MAX_INCIDENTALS 4
class plSound : public plSynchedObject
{
friend class plSoundSDLModifier;
friend class plSoundVolumeApplicator;
public:
plSound();
virtual ~plSound();
CLASSNAME_REGISTER( plSound );
GETINTERFACE_ANY( plSound, plSynchedObject );
enum Property
{
kPropIs3DSound = 0x00000001,
kPropDisableLOD = 0x00000002,
kPropLooping = 0x00000004,
kPropAutoStart = 0x00000008,
kPropLocalOnly = 0x00000010, // Disables network synching and triggering
kPropLoadOnlyOnCall = 0x00000020, // Only load and unload when we're told to
kPropFullyDisabled = 0x00000040, // This sound should never play while this is set
// Only plWin32LinkSound uses it. Placed here as a TODO though...
kPropDontFade = 0x00000080,
kPropIncidental = 0x00000100 // Incidental sound, will be played thru the incidental manager
};
enum Type
{
kStartType,
kSoundFX = kStartType, // For now, 3D sounds are always marked as this
kAmbience,
kBackgroundMusic,
kGUISound,
kNPCVoices,
kNumTypes
};
enum Refs
{
kRefSoftVolume = 0,
kRefDataBuffer, // plugins only
kRefParentSceneObject,
kRefSoftOcclusionRegion
};
enum
{
kSoftRegion = 0
};
enum StreamType
{
kNoStream,
kStreamFromRAM,
kStreamFromDisk,
kStreamCompressed
};
class plFadeParams
{
friend class plSound;
public:
enum Type
{
kLinear,
kLogarithmic,
kExponential
};
hsScalar fLengthInSecs; // Time to take to fade
hsScalar fVolStart; // Set one of these two for fade in/out,
hsScalar fVolEnd; // the other becomes the current volume
UInt8 fType;
hsBool fStopWhenDone; // Actually stop the sound once the fade is complete
hsBool fFadeSoftVol; // Fade the soft volume instead of fCurrVolume
plFadeParams() { fLengthInSecs = 0.f; fCurrTime = -1.f; fStopWhenDone = false; fFadeSoftVol = false; fVolStart = fVolEnd = 0.f; fType = kLinear; }
plFadeParams( Type type, hsScalar len, hsScalar start, hsScalar end )
{
fLengthInSecs = len; fVolStart = start; fVolEnd = end; fType = type;
fStopWhenDone = false;
fFadeSoftVol = false;
fCurrTime = -1.f;
}
void Read( hsStream *s );
void Write( hsStream *s );
hsScalar InterpValue();
protected:
hsScalar fCurrTime; // -1 if we aren't active, else it's how far we're into the animation
};
virtual hsBool LoadSound( hsBool is3D ) = 0;
hsScalar GetVirtualStartTime() const { return (hsScalar)fVirtualStartTime; }
virtual void Play();
void SynchedPlay( unsigned bytes );
void SynchedPlay( hsScalar virtualStartTime );
virtual void Stop();
virtual void FastForwardPlay();
virtual void FastForwardToggle();
virtual void SetMin(const int m); // sets minimum falloff distance
virtual void SetMax(const int m); // sets maximum falloff distance
virtual int GetMin() const;
virtual int GetMax() const;
virtual void SetVolume(const float volume);
virtual float GetVolume() const { return fCurrVolume; }
hsScalar GetMaxVolume() { return fMaxVolume; }
virtual hsBool IsPlaying() { return fPlaying; }
void SetTime(double t);
virtual double GetTime() { return 0.f; }
virtual void Activate(hsBool forcePlay = false);
virtual void DeActivate();
virtual void SetLength(double l) { fLength = l; }
virtual void SetMuted( hsBool muted );
virtual hsBool IsMuted() { return fMuted; }
void Disable() { fDistAttenuation = 0; }
virtual plSoundMsg* GetStatus(plSoundMsg* pMsg){return NULL;}
virtual void SetConeOrientation(hsScalar x, hsScalar y, hsScalar z);
virtual void SetOuterVolume( const int v ); // volume for the outer cone (if applicable)
virtual void SetConeAngles( int inner, int outer );
virtual void SetPosition(const hsPoint3 pos);
virtual void SetVelocity(const hsVector3 vel);
virtual hsPoint3 GetPosition() const;
virtual hsVector3 GetVelocity() const;
virtual void Update();
plSoundBuffer * GetDataBuffer() const { return (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded(); }
hsScalar QueryCurrVolume() const; // Returns the current volume, attenuated
const char * GetFileName() const;
virtual double GetLength();
void SetProperty( Property prop, hsBool on ) { if( on ) fProperties |= prop; else fProperties &= ~prop; }
hsBool IsPropertySet( Property prop ) const { return ( fProperties & prop ) ? true : false; }
virtual void RefreshVolume();
virtual void SetStartPos(unsigned bytes) = 0;
virtual unsigned GetByteOffset(){return 0;}
virtual float GetActualTimeSec() = 0;
virtual void AddCallbacks(plSoundMsg* pMsg) = 0;
virtual void RemoveCallbacks(plSoundMsg* pMsg) = 0;
virtual UInt8 GetChannelSelect() const { return 0; } // Only defined on Win32Sound right now, should be here tho
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
virtual void SetFadeInEffect( plFadeParams::Type type, hsScalar length );
virtual void SetFadeOutEffect( plFadeParams::Type type, hsScalar length );
virtual hsScalar CalcSoftVolume( hsBool enable, hsScalar distToListenerSquared );
virtual void UpdateSoftVolume( hsBool enable, hsBool firstTime = false );
virtual hsBool MsgReceive( plMessage* pMsg );
virtual hsBool DirtySynchState( const char *sdlName = nil, UInt32 sendFlags = 0 ); // call when state has changed
// Tests whether this sound is within range of the given position, not counting soft regions
hsBool IsWithinRange( const hsPoint3 &listenerPos, hsScalar *distSquared );
// Type setting and getting, from the Types enum
void SetType( UInt8 type ) { fType = type; }
UInt8 GetType() const { return fType; }
// Priority stuff
void SetPriority( UInt8 pri ) { fPriority = pri; }
UInt8 GetPriority() const { return fPriority; }
// Visualization
virtual plDrawableSpans* CreateProxy(const hsMatrix44& l2w, hsGMaterial* mat, hsTArray<UInt32>& idx, plDrawableSpans* addTo);
// Forced loading/unloading (for when the audio system's LOD just doesn't cut it)
virtual void ForceLoad( );
virtual void ForceUnload();
// Note: ONLY THE AUDIOSYS SHOULD CALL THIS. If you're not the audioSys, get lost.
static void SetCurrDebugPlate( const plKey soundKey );
void RegisterOnAudioSys();
void UnregisterOnAudioSys();
// Also only for the audio system
hsScalar GetVolumeRank();
void ForceUnregisterFromAudioSys();
static void SetLoadOnDemand( hsBool activate ) { fLoadOnDemandFlag = activate; }
static void SetLoadFromDiskOnDemand( hsBool activate ) { fLoadFromDiskOnDemand = activate; }
const plEAXSourceSettings &GetEAXSettings() const { return fEAXSettings; }
plEAXSourceSettings &GetEAXSettings() { return fEAXSettings; }
virtual StreamType GetStreamType() const { return kNoStream; }
virtual void FreeSoundData();
protected:
hsBool fPlaying;
hsBool fActive;
double fTime;
int fMaxFalloff;
int fMinFalloff;
hsScalar fCurrVolume;
hsScalar fDesiredVol; // Equal to fCurrVolume except when we're fading or muted
hsScalar fFadedVolume;
hsScalar fMaxVolume;
int fOuterVol;
int fInnerCone;
int fOuterCone;
double fLength;
int fProperties;
UInt8 fType;
UInt8 fPriority;
hsBool fMuted, fFading, fRegisteredForTime, fPlayOnReactivate, fFreeData;
hsBool fNotHighEnoughPriority; // Set whenever the audioSys calls UpdateSoftVolume() with enable=false,
// thus indicating that we slipped off the top 16 most wanted list.
// Do these need to be synched values? They weren't before...
hsVector3 fConeOrientation;
hsPoint3 f3DPosition;
hsVector3 f3DVelocity;
hsBool fPlayWhenLoaded;
double fSynchedStartTimeSec;
// Just around for reference and sending messages upward (synched state)
plSceneObject *fOwningSceneObject;
// EAX Settings storage here
plEAXSourceSettings fEAXSettings;
hsBool fQueued;
plFadeParams fFadeInParams, fFadeOutParams;
plFadeParams fCoolSoftVolumeTrickParams;
plFadeParams *fCurrFadeParams;
plSoftVolume *fSoftRegion;
hsScalar fSoftVolume;
hsScalar fDistAttenuation, fDistToListenerSquared;
double fVirtualStartTime;
hsBool fRegistered;
static unsigned fIncidentalsPlaying;
plSoftVolume *fSoftOcclusionRegion;
plSoundBuffer *fDataBuffer; // Not always around
hsBool fDataBufferLoaded;
plKey fDataBufferKey; // Always around
static plGraphPlate *fDebugPlate;
static plSound *fCurrDebugPlateSound;
static hsBool fLoadOnDemandFlag, fLoadFromDiskOnDemand;
hsBool fLoading;
void IUpdateDebugPlate();
void IPrintDbgMessage( const char *msg, hsBool isErr = false );
virtual void ISetActualVolume(float v) = 0;
virtual void IActuallyStop();
virtual hsBool IActuallyPlaying() = 0;
virtual void IActuallyPlay() = 0;
virtual void IFreeBuffers() = 0;
//NOTE: if isIncidental is true the entire sound will be loaded.
virtual plSoundBuffer::ELoadReturnVal IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental = false );
virtual void ISetActualTime( double t ) = 0;
virtual hsBool IActuallyLoaded() = 0;
virtual void IRefreshEAXSettings( hsBool force = false ) = 0;
virtual hsScalar IGetChannelVolume() const;
void ISynchToStartTime();
void ISynchedPlay( double virtualStartTime );
void IStartFade( plFadeParams *params, hsScalar offsetIntoFade = 0.f );
void IStopFade( hsBool shuttingDown = false, hsBool SetVolEnd = true);
hsBool IWillBeAbleToPlay();
void ISetSoftRegion( plSoftVolume *region );
hsScalar IAttenuateActualVolume( hsScalar volume ) const;
void ISetSoftOcclusionRegion( plSoftVolume *region );
// Override to make sure the buffer is available before the base class is called
virtual void IRefreshParams();
virtual bool ILoadDataBuffer();
virtual void IUnloadDataBuffer();
//virtual void ISetMinDistance( const int m ) = 0;
//virtual void ISetMaxDistance( const int m ) = 0;
//virtual void ISetOuterVolume( const int v ) = 0;
//virtual void ISetConeAngles( int inner, int outer ) = 0;
//virtual void ISetActualConeOrient( hsVector3 &vector ) = 0;
//virtual void ISetVelocity( const hsVector3 vel ) = 0;
//virtual void ISetPosition( const hsPoint3 pos ) = 0;
virtual void IRead( hsStream *s, hsResMgr *mgr );
virtual void IWrite( hsStream *s, hsResMgr *mgr );
};
//// plSoundVolumeApplicator /////////////////////////////////////////////////
// Tiny helper for handling animated volumes
class plSoundVolumeApplicator : public plAGApplicator
{
public:
plSoundVolumeApplicator() { }
plSoundVolumeApplicator( UInt32 index ) { fIndex = index; }
CLASSNAME_REGISTER( plSoundVolumeApplicator );
GETINTERFACE_ANY( plSoundVolumeApplicator, plAGApplicator );
virtual plAGApplicator *CloneWithChannel( plAGChannel *channel );
virtual void Write( hsStream *stream, hsResMgr *mgr );
virtual void Read( hsStream *s, hsResMgr *mgr );
protected:
UInt32 fIndex;
virtual void IApply( const plAGModifier *mod, double time );
};
#endif //plWin32Sound_h

View File

@ -0,0 +1,199 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plSoundEvent //
// //
//// Notes ///////////////////////////////////////////////////////////////////
// //
// 10.30.2001 - Created by mcn. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "plSoundEvent.h"
#include "plgDispatch.h"
#include "../pnMessage/plEventCallbackMsg.h"
#include "../pnMessage/plSoundMsg.h"
#include "plSound.h"
plSoundEvent::plSoundEvent( Types type, plSound *owner )
{
fType = type;
fBytePosTime = 0;
fOwner = owner;
fCallbacks.Reset();
fCallbackEndingFlags.Reset();
}
plSoundEvent::plSoundEvent( Types type, UInt32 bytePos, plSound *owner )
{
fType = type;
fBytePosTime = bytePos;
fOwner = owner;
fCallbacks.Reset();
fCallbackEndingFlags.Reset();
}
plSoundEvent::plSoundEvent()
{
fType = kStart;
fBytePosTime = 0;
fOwner = nil;
fCallbacks.Reset();
fCallbackEndingFlags.Reset();
}
plSoundEvent::~plSoundEvent()
{
int i;
for( i = 0; i < fCallbacks.GetCount(); i++ )
hsRefCnt_SafeUnRef( fCallbacks[ i ] );
}
void plSoundEvent::AddCallback( plEventCallbackMsg *msg )
{
hsRefCnt_SafeRef( msg );
fCallbacks.Append( msg );
fCallbackEndingFlags.Append( 0 );
}
hsBool plSoundEvent::RemoveCallback( plEventCallbackMsg *msg )
{
int idx = fCallbacks.Find( msg );
if( idx != fCallbacks.kMissingIndex )
{
hsRefCnt_SafeUnRef( msg );
fCallbacks.Remove( idx );
fCallbackEndingFlags.Remove( idx );
return true;
}
return false;
}
void plSoundEvent::SendCallbacks( void )
{
int j;
plSoundMsg *sMsg;
for( j = fCallbacks.GetCount() - 1; j >= 0; j-- )
{
plEventCallbackMsg *msg = fCallbacks[ j ];
if (!msg->HasBCastFlag(plMessage::kNetPropagate) || !fOwner ||
fOwner->IsLocallyOwned() == plSynchedObject::kYes )
{
/// Do this a bit differently so we can do our MsgSend last
sMsg = nil;
// Ref to make sure the dispatcher doesn't delete it on us
hsRefCnt_SafeRef( msg );
if( msg->fRepeats == 0 && fCallbackEndingFlags[ j ] == 0 )
{
// Note: we get fancy here. We never want to remove the callback directly,
// because the sound won't know about it. So instead, send it a message to
// remove the callback for us
sMsg = TRACKED_NEW plSoundMsg();
sMsg->SetBCastFlag( plMessage::kLocalPropagate, true );
sMsg->AddReceiver( fOwner->GetKey() );
sMsg->SetCmd( plSoundMsg::kRemoveCallbacks );
sMsg->AddCallback( msg );
}
// If this isn't infinite, decrement the number of repeats
if( msg->fRepeats > 0 )
msg->fRepeats--;
// And finally...
if( fCallbackEndingFlags[ j ] == 0 )
{
plgDispatch::MsgSend( msg, true );
}
if( sMsg != nil )
{
plgDispatch::MsgSend( sMsg, true );
fCallbackEndingFlags[ j ] = 0xff; // Our special flag to mean "hey, don't
// process this, just waiting for
// it to die"
}
}
}
}
UInt32 plSoundEvent::GetNumCallbacks( void ) const
{
return fCallbacks.GetCount();
}
int plSoundEvent::GetType( void ) const
{
return (int)fType;
}
void plSoundEvent::SetType( Types type )
{
fType = type;
}
UInt32 plSoundEvent::GetTime( void ) const
{
return fBytePosTime;
}
plSoundEvent::Types plSoundEvent::GetTypeFromCallbackMsg( plEventCallbackMsg *msg )
{
switch( msg->fEvent )
{
case ::kStart: return kStart;
case ::kTime: return kTime;
case ::kStop: return kStop;
case ::kLoop: return kLoop;
}
return kStop;
}

View File

@ -0,0 +1,99 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plSoundEvent - Event node for handling callback thread stuff //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plSoundEvent_h
#define _plSoundEvent_h
#include "hsTemplates.h"
class plEventCallbackMsg;
class plSound;
//// plSoundEvent ////////////////////////////////////////////////////////////
// Storage class for an event node.
class plSoundEvent
{
public:
enum Types
{
kStart,
kStop,
kTime,
kLoop
};
plSoundEvent( Types type, plSound *owner );
plSoundEvent( Types type, UInt32 bytePos, plSound *owner );
plSoundEvent();
~plSoundEvent();
void AddCallback( plEventCallbackMsg *msg );
hsBool RemoveCallback( plEventCallbackMsg *msg );
UInt32 GetNumCallbacks( void ) const;
int GetType( void ) const;
void SetType( Types type );
UInt32 GetTime( void ) const;
void SendCallbacks( void );
static Types GetTypeFromCallbackMsg( plEventCallbackMsg *msg );
protected:
Types fType;
UInt32 fBytePosTime;
plSound *fOwner;
hsTArray<plEventCallbackMsg *> fCallbacks;
hsTArray<UInt8> fCallbackEndingFlags;
};
#endif //_plSoundEvent_h

View File

@ -0,0 +1,718 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsTypes.h"
#include "hsWindows.h"
#include "hsTimer.h"
#include "hsResMgr.h"
#include "al.h"
#include "alc.h"
#include "plDSoundBuffer.h"
#include "speex.h"
#include "speex_bits.h"
#include "hsGeometry3.h"
#include "plVoiceChat.h"
#include "plAudioSystem.h"
#include "plgDispatch.h"
#include "../plAudible/plWinAudible.h"
#include "../plNetMessage/plNetMessage.h"
#include "../plPipeline/plPlates.h"
#include "hsConfig.h"
#include "../plAvatar/plAvatarMgr.h"
#include "../plAvatar/plArmatureMod.h"
#include "hsQuat.h"
#include "../plAudioCore/plAudioCore.h"
// DEBUG for printing to the console
#include "../plMessage/plConsoleMsg.h"
#include "../plPipeline/plDebugText.h"
#include "../plStatusLog/plStatusLog.h"
#define MICROPHONE "ui_microphone.png"
#define TALKING "ui_speaker.png"
#define NUM_CHANNELS 1
#define VOICE_STOP_MS 2000
#define MAX_DATA_SIZE 1024 * 4 // 4 KB
hsBool plVoiceRecorder::fCompress = true;
hsBool plVoiceRecorder::fRecording = true;
hsBool plVoiceRecorder::fNetVoice = false;
short plVoiceRecorder::fSampleRate = FREQUENCY;
hsScalar plVoiceRecorder::fRecordThreshhold = 200.0f;
hsBool plVoiceRecorder::fShowIcons = true;
hsBool plVoiceRecorder::fMicAlwaysOpen = false;
hsBool plVoicePlayer::fEnabled = true;
plVoiceRecorder::plVoiceRecorder()
{
plPlateManager::Instance().CreatePlate( &fDisabledIcon );
fDisabledIcon->CreateFromResource( MICROPHONE );
fDisabledIcon->SetPosition(-0.90, -0.90);
fDisabledIcon->SetSize(0.064, 0.064, true);
fDisabledIcon->SetVisible(false);
plPlateManager::Instance().CreatePlate( &fTalkIcon );
fTalkIcon->CreateFromResource( TALKING );
fTalkIcon->SetPosition(-0.9,-0.9);
fTalkIcon->SetSize(0.0675, 0.09);
fTalkIcon->SetVisible(false);
}
plVoiceRecorder::~plVoiceRecorder()
{
if(fDisabledIcon)
plPlateManager::Instance().DestroyPlate( fDisabledIcon);
fDisabledIcon = nil;
if (fTalkIcon)
plPlateManager::Instance().DestroyPlate( fTalkIcon );
fTalkIcon = nil;
}
void plVoiceRecorder::IncreaseRecordingThreshhold()
{
fRecordThreshhold += (100 * hsTimer::GetDelSysSeconds());
if (fRecordThreshhold >= 10000.0f)
fRecordThreshhold = 10000.0f;
plDebugText &txt = plDebugText::Instance();
char str[256];
sprintf(str, "RecordThreshhold %f\n", fRecordThreshhold);
txt.DrawString(400,300,str);
}
void plVoiceRecorder::DecreaseRecordingThreshhold()
{
fRecordThreshhold -= (100 * hsTimer::GetDelSysSeconds());
if (fRecordThreshhold <= 50.0f)
fRecordThreshhold = 50.0f;
plDebugText &txt = plDebugText::Instance();
char str[256];
sprintf(str, "RecordThreshhold %f\n", fRecordThreshhold);
txt.DrawString(400,300,str);
}
// Set the quality of speex encoder
void plVoiceRecorder::SetQuality(int quality)
{
char str[] = "Voice quality setting out of range. Must be between 1 and 10 inclusive";
if(quality < 1 || quality > 10)
{
plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, str );
plgDispatch::MsgSend( cMsg );
return;
}
if(plSpeex::GetInstance()->IsUsingVBR())
{
// Sets average bit rate between 4kb and 13kb
int AverageBitrate = quality * 1000 + 3000;
plSpeex::GetInstance()->SetABR(AverageBitrate);
}
else
{
plSpeex::GetInstance()->SetQuality(quality);
}
}
// toggle variable bit rate
void plVoiceRecorder::SetVBR(bool vbr)
{
plSpeex::GetInstance()->VBR(vbr);
SetQuality(plSpeex::GetInstance()->GetQuality()); // update proper quality param
}
void plVoiceRecorder::SetComplexity(int c)
{
char str[] = "Voice quality setting out of range. Must be between 1 and 10 inclusive";
if(c < 1 || c > 10)
{
plConsoleMsg *cMsg = TRACKED_NEW plConsoleMsg( plConsoleMsg::kAddLine, str );
plgDispatch::MsgSend( cMsg );
return;
}
plSpeex::GetInstance()->SetComplexity((UInt8) c);
}
void plVoiceRecorder::SetENH(hsBool b)
{
plSpeex::GetInstance()->SetENH(b);
}
void plVoiceRecorder::SetMikeOpen(hsBool b)
{
ALCdevice *device = plgAudioSys::GetCaptureDevice();
if (fRecording && device)
{
if (b)
{
alcCaptureStart(device);
}
else
{
alcCaptureStop(device);
}
DrawTalkIcon(b);
fMikeOpen = b;
}
else
{
DrawDisabledIcon(b); // voice recording is unavailable or disabled
}
}
void plVoiceRecorder::DrawDisabledIcon(hsBool b)
{
if (!fDisabledIcon)
{
// at least try and make one here...
plPlateManager::Instance().CreatePlate( &fDisabledIcon );
if (fDisabledIcon)
{
fDisabledIcon->CreateFromResource( MICROPHONE );
fDisabledIcon->SetPosition(-0.90, -0.90);
fDisabledIcon->SetSize(0.064, 0.064, true);
fDisabledIcon->SetVisible(false);
}
}
if (fDisabledIcon)
{
fDisabledIcon->SetSize(0.064, 0.064, true); // Re-compute plate size in case the aspect ratio has changed.
fDisabledIcon->SetVisible(b);
}
}
void plVoiceRecorder::DrawTalkIcon(hsBool b)
{
if (!fTalkIcon)
{
plPlateManager::Instance().CreatePlate( &fTalkIcon );
if (fTalkIcon)
{ fTalkIcon->CreateFromResource( TALKING );
fTalkIcon->SetPosition(-0.9,-0.9);
fTalkIcon->SetSize(0.064, 0.064, true);
fTalkIcon->SetVisible(false);
}
}
if (fTalkIcon)
{
fTalkIcon->SetSize(0.064, 0.064, true); // Re-compute plate size in case the aspect ratio has changed.
fTalkIcon->SetVisible(b);
}
}
void plVoiceRecorder::Update(double time)
{
if(!fRecording)
return;
int EncoderFrameSize = plSpeex::GetInstance()->GetFrameSize();
if(EncoderFrameSize == -1)
return;
ALCdevice *captureDevice = plgAudioSys::GetCaptureDevice();
if(!captureDevice)
return;
unsigned minSamples = EncoderFrameSize * 10;
ALCint samples;
alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples );
if (samples > 0)
{
if (samples >= minSamples)
{
int numFrames = (int)(samples / EncoderFrameSize); // the number of frames that have been captured
int totalSamples = numFrames * EncoderFrameSize;
// cap uncompressed data
if(totalSamples > MAX_DATA_SIZE)
totalSamples = MAX_DATA_SIZE;
// convert to correct units:
short *buffer = TRACKED_NEW short[totalSamples];
alcCaptureSamples(captureDevice, buffer, totalSamples);
if (!CompressionEnabled())
{
plNetMsgVoice pMsg;
pMsg.SetNetProtocol(kNetProtocolCli2Game);
pMsg.SetVoiceData((char *)buffer, totalSamples * sizeof(short));
// set frame size here;
pMsg.SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID());
//if (false) //plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kEchoVoice))
// pMsg.SetBit(plNetMessage::kEchoBackToSender);
plNetClientApp::GetInstance()->SendMsg(&pMsg);
}
else // use the speex voice compression lib
{
UInt8 *packet = TRACKED_NEW UInt8[totalSamples]; // packet to send encoded data in
int packedLength = 0; // the size of the packet that will be sent
hsRAMStream ram; // ram stream to hold output data from speex
UInt8 numFrames = totalSamples / EncoderFrameSize; // number of frames to be encoded
// encode the data using speex
plSpeex::GetInstance()->Encode(buffer, numFrames, &packedLength, &ram);
if (packedLength)
{
// extract data from ram stream into packet
ram.Rewind();
ram.Read(packedLength, packet);
plNetMsgVoice pMsg;
pMsg.SetNetProtocol(kNetProtocolCli2Game);
pMsg.SetVoiceData((char *)packet, packedLength);
pMsg.SetPlayerID(plNetClientApp::GetInstance()->GetPlayerID());
pMsg.SetFlag(VOICE_ENCODED); // Set encoded flag
pMsg.SetNumFrames(numFrames);
if (plNetClientApp::GetInstance()->GetFlagsBit(plNetClientApp::kEchoVoice))
pMsg.SetBit(plNetMessage::kEchoBackToSender);
plNetClientApp::GetInstance()->SendMsg(&pMsg);
}
delete[] packet;
}
delete[] buffer;
}
else if(!fMikeOpen)
{
short *buffer = TRACKED_NEW short[samples];
// the mike has since closed, and there isn't enough data to meet our minimum, so throw this data out
alcCaptureSamples(captureDevice, buffer, samples);
delete[] buffer;
}
}
}
plVoicePlayer::plVoicePlayer()
{
}
plVoicePlayer::~plVoicePlayer()
{
}
void plVoicePlayer::PlaybackUncompressedVoiceMessage(void* data, unsigned size)
{
if(fEnabled)
{
if(!fSound.IsPlaying())
{
fSound.Play();
}
fSound.AddVoiceData(data, size);
}
}
void plVoicePlayer::PlaybackVoiceMessage(void* data, unsigned size, int numFramesInBuffer)
{
if(fEnabled)
{
int numBytes; // the number of bytes that speex decompressed the data to.
int bufferSize = numFramesInBuffer * plSpeex::GetInstance()->GetFrameSize();
short *nBuff = TRACKED_NEW short[bufferSize];
memset(nBuff, 0, bufferSize);
// Decode the encoded voice data using speex
if(!plSpeex::GetInstance()->Decode((UInt8 *)data, size, numFramesInBuffer, &numBytes, nBuff))
{
delete[] nBuff;
return;
}
BYTE* newBuff;
newBuff = (BYTE*)nBuff; // Convert to byte data
PlaybackUncompressedVoiceMessage(newBuff, numBytes); // playback uncompressed data
delete[] nBuff;
}
}
void plVoicePlayer::SetVelocity(const hsVector3 vel)
{
fSound.SetVelocity(vel);
}
void plVoicePlayer::SetPosition(const hsPoint3 pos)
{
fSound.SetPosition(pos);
}
void plVoicePlayer::SetOrientation(const hsPoint3 pos)
{
fSound.SetConeOrientation(pos.fX, pos.fY, pos.fZ);
}
/*****************************************************************************
*
* plVoiceSound
*
***/
unsigned plVoiceSound::fCount = 0;
plVoiceSound::plVoiceSound()
{
fInnerCone = 90;
fOuterCone = 240;
fOuterVol = -2000;
fMinFalloff = 15;
fMaxFalloff = 75;
fProperties = 0;
fCurrVolume = 1.0;
fDesiredVol = 1.0;
fPriority = 1;
fType = plgAudioSys::kVoice;
fEAXSettings.SetRoomParams(-1200, -100, 0, 0);
fLastUpdate = 0;
char keyName[32];
StrPrintf(keyName, arrsize(keyName), "VoiceSound_%d", fCount);
fCount++;
hsgResMgr::ResMgr()->NewKey(keyName, this, plLocation::kGlobalFixedLoc);
}
plVoiceSound::~plVoiceSound()
{
}
hsBool plVoiceSound::LoadSound( hsBool is3D )
{
if( fFailed )
return false;
if( !plgAudioSys::Active() || fDSoundBuffer )
return false;
if( fPriority > plgAudioSys::GetPriorityCutoff() )
return false; // Don't set the failed flag, just return
plWAVHeader header;
header.fFormatTag = WAVE_FORMAT_PCM;
header.fBitsPerSample = 16;
header.fNumChannels = 1;
header.fNumSamplesPerSec = FREQUENCY;
header.fBlockAlign = header.fNumChannels * header.fBitsPerSample / 2;
header.fAvgBytesPerSec = header.fNumSamplesPerSec * header.fBlockAlign;
fDSoundBuffer = TRACKED_NEW plDSoundBuffer(0, header, true, false, false, true);
if(!fDSoundBuffer)
return false;
fDSoundBuffer->SetupVoiceSource();
IRefreshParams();
IRefreshEAXSettings( true );
fDSoundBuffer->SetScalarVolume(1.0);
return true;
}
void plVoiceSound::Play()
{
fPlaying = true;
if( IWillBeAbleToPlay() )
{
IRefreshParams();
SetVolume( fDesiredVol );
IActuallyPlay();
}
}
void plVoiceSound::IDerivedActuallyPlay( void )
{
if( !fReallyPlaying )
{
fDSoundBuffer->Play();
fReallyPlaying = true;
}
}
void plVoiceSound::AddVoiceData(void *data, unsigned bytes)
{
unsigned size;
unsigned bufferId;
if(!fDSoundBuffer)
{
if(!LoadSound(true))
{
return;
}
}
fDSoundBuffer->UnQueueVoiceBuffers(); // attempt to unque any buffers that have finished
while(bytes > 0)
{
size = bytes < STREAM_BUFFER_SIZE ? bytes : STREAM_BUFFER_SIZE;
if(!fDSoundBuffer->GetAvailableBufferId(&bufferId))
break; // if there isn't any room for the data, it is currently thrown out
fDSoundBuffer->VoiceFillBuffer(data, size, bufferId);
bytes -= size;
}
fLastUpdate = hsTimer::GetMilliSeconds();
}
void plVoiceSound::Update()
{
if(IsPlaying())
{
if((hsTimer::GetMilliSeconds() - fLastUpdate) > VOICE_STOP_MS)
{
Stop(); // terminating case for playback. Wait for x number of milliseconds, and stop.
}
}
}
void plVoiceSound::IRefreshParams()
{
plSound::IRefreshParams();
}
/*****************************************************************************
*
* Speex Voice Encoding/Decoding
*
***/
plSpeex::plSpeex() :
fBits(nil),
fEncoderState(nil),
fDecoderState(nil),
fSampleRate(plVoiceRecorder::GetSampleRate()),
fFrameSize(-1),
fQuality(7),
fVBR(true), // variable bit rate on
fAverageBitrate(8000), // 8kb bitrate
fComplexity(3),
fENH(false),
fInitialized(false)
{
fBits = TRACKED_NEW SpeexBits;
Init(kNarrowband); // if no one initialized us initialize using a narrowband encoder
}
plSpeex::~plSpeex()
{
Shutdown();
delete fBits;
fBits = nil;
}
hsBool plSpeex::Init(Mode mode)
{
int enh = 1;
// setup speex
speex_bits_init(fBits);
fBitsInit = true;
if(mode == kNarrowband)
{
fEncoderState = speex_encoder_init(&speex_nb_mode); // narrowband
fDecoderState = speex_decoder_init(&speex_nb_mode);
}
else if(mode == kWideband)
{
fEncoderState = speex_encoder_init(&speex_wb_mode);
fDecoderState = speex_decoder_init(&speex_wb_mode);
}
speex_encoder_ctl(fEncoderState, SPEEX_GET_FRAME_SIZE, &fFrameSize); // get frame size
speex_encoder_ctl(fEncoderState, SPEEX_SET_COMPLEXITY, &fComplexity); // 3
speex_encoder_ctl(fEncoderState, SPEEX_SET_SAMPLING_RATE, &fSampleRate); // 8 khz
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR_QUALITY, &fQuality); // 7
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR, &fVBR); // use variable bit rate
speex_encoder_ctl(fEncoderState, SPEEX_SET_ABR, &fAverageBitrate); // default to 8kb
speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &fENH); // perceptual enhancement
fInitialized = true;
return true;
}
hsBool plSpeex::Shutdown()
{
//shutdown speex
if(fDecoderState)
{
speex_decoder_destroy(fDecoderState);
fDecoderState = nil;
}
if(fEncoderState)
{
speex_encoder_destroy(fEncoderState);
fEncoderState = nil;
}
if(fBitsInit)
{
speex_bits_destroy(fBits);
fBitsInit = false;
}
fInitialized = false;
return true;
}
hsBool plSpeex::Encode(short *data, int numFrames, int *packedLength, hsRAMStream *out)
{
*packedLength = 0;
short *pData = data; // pointer to input data
float *input = TRACKED_NEW float[fFrameSize]; // input to speex - used as am intermediate array since speex requires float data
BYTE frameLength; // number of bytes speex compressed frame to
BYTE *frameData = TRACKED_NEW BYTE[fFrameSize]; // holds one frame of encoded data
// encode data
for( int i = 0; i < numFrames; i++ )
{
// convert input data to floats
for( int j = 0; j < fFrameSize; j++ )
{
input[j] = pData[j];
}
speex_bits_reset(fBits); // reset bit structure
// encode data using speex
speex_encode(fEncoderState, input, fBits);
frameLength = speex_bits_write(fBits, (char *)frameData, fFrameSize);
// write data - length and bytes
out->WriteSwap(frameLength);
*packedLength += sizeof(frameLength); // add length of encoded frame
out->Write(frameLength, frameData);
*packedLength += frameLength; // update length
pData += fFrameSize; // move input pointer
}
delete[] frameData;
delete[] input;
return true;
}
hsBool plSpeex::Decode(UInt8 *data, int size, int numFrames, int *numOutputBytes, short *out)
{
if(!fInitialized) return false;
*numOutputBytes = 0;
hsReadOnlyStream stream( size, data );
float *speexOutput = TRACKED_NEW float[fFrameSize]; // holds output from speex
short *pOut = out; // pointer to output short buffer
// create buffer for input data
BYTE *frameData = TRACKED_NEW BYTE[fFrameSize]; // holds the current frames data to be decoded
BYTE frameLen; // holds the length of the current frame being decoded.
// Decode data
for (int i = 0; i < numFrames; i++)
{
stream.ReadSwap( &frameLen ); // read the length of the current frame to be decoded
stream.Read( frameLen, frameData ); // read the data
memset(speexOutput, 0, fFrameSize * sizeof(float));
speex_bits_read_from(fBits, (char *)frameData, frameLen); // give data to speex
speex_decode(fDecoderState, fBits, speexOutput); // decode data
for(int j = 0; j < fFrameSize; j++)
{
pOut[j] = (short)(speexOutput[j]); // convert floats to shorts
}
pOut += fFrameSize;
}
delete[] frameData;
delete[] speexOutput;
*numOutputBytes = (numFrames * fFrameSize) * sizeof(short); // length of decoded voice data(out) in bytes
if(*numOutputBytes == 0)
return false;
return true;
}
// Sets variable bit rate on/off
void plSpeex::VBR(hsBool b)
{
fVBR = b;
speex_encoder_ctl(fEncoderState, SPEEX_SET_VBR, &fVBR);
}
// Sets the average bit rate
void plSpeex::SetABR(UInt32 abr)
{
fAverageBitrate = abr;
speex_encoder_ctl(fEncoderState, SPEEX_SET_ABR, &fAverageBitrate);
}
// Sets the quality of encoding
void plSpeex::SetQuality(UInt32 quality)
{
fQuality = quality;
speex_encoder_ctl(fEncoderState, SPEEX_SET_QUALITY, &fQuality);
}
void plSpeex::SetENH(hsBool b)
{
fENH = b;
speex_decoder_ctl(fDecoderState, SPEEX_SET_ENH, &fENH);
}
void plSpeex::SetComplexity(UInt8 c)
{
fComplexity = c;
speex_encoder_ctl(fEncoderState, SPEEX_SET_COMPLEXITY, &fComplexity);
}

View File

@ -0,0 +1,212 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plVoiceChat_h
#define plVoiceChat_h
#include "hsTemplates.h"
#include "plWin32Sound.h"
#include "hsThread.h"
// voice flags
#define VOICE_ENCODED ( 1 << 0 )
#define VOICE_NARROWBAND ( 1 << 1 )
#define VOICE_ENH ( 1 << 2 )
#define BUFFER_LEN_SECONDS 4
#define FREQUENCY 8000
struct hsVector3;
struct SpeexBits;
class plWinAudible;
class plPlate;
class plStatusLog;
class plSpeex;
typedef struct ALCdevice_struct ALCdevice;
// Sound used for playing back dynamic voice chat data. this allows us to hook voice chat into the audio system
class plVoiceSound : public plWin32Sound
{
public:
plVoiceSound();
~plVoiceSound();
hsBool LoadSound( hsBool is3D );
void AddVoiceData(void *data, unsigned bytes);
void Update();
void Play();
virtual void SetStartPos(unsigned bytes){}
private:
virtual bool ILoadDataBuffer( void ){ return true; }
virtual void IUnloadDataBuffer( void ){}
virtual void IDerivedActuallyPlay( void );
virtual void ISetActualTime( double t ){}
virtual float GetActualTimeSec() { return 0.0f; }
virtual void IRefreshParams( void );
static unsigned fCount;
double fLastUpdate;
};
class plVoicePlayer
{
public:
plVoicePlayer();
~plVoicePlayer();
void PlaybackVoiceMessage(void* data, unsigned size, int numFramesInBuffer);
void PlaybackUncompressedVoiceMessage(void* data, unsigned size);
void SetVelocity(const hsVector3 vel);
void SetPosition(const hsPoint3 pos);
void SetOrientation(const hsPoint3 pos);
void SetTalkIcon(int index, UInt32 str){}
void ClearTalkIcon(){}
plVoiceSound *GetSoundPtr() { return &fSound; }
static void Enable(hsBool enable) { fEnabled = enable; }
private:
plVoiceSound fSound;
static hsBool fEnabled;
};
class plVoiceRecorder
{
public:
plVoiceRecorder();
~plVoiceRecorder();
void Update(double time);
void SetMikeOpen(hsBool b);
void DrawTalkIcon(hsBool b);
void DrawDisabledIcon(hsBool b);
void SetTalkIcon(int index, UInt32 str);
void ClearTalkIcon();
static hsBool RecordingEnabled() { return fRecording; }
static hsBool NetVoiceEnabled() { return fNetVoice; }
static hsBool CompressionEnabled() { return fCompress; }
static void EnablePushToTalk(hsBool b) { fMicAlwaysOpen = !b; }
static void EnableIcons(hsBool b) { fShowIcons = b; }
static void EnableRecording(hsBool b) { fRecording = b; }
static void EnableNetVoice(hsBool b) { fNetVoice = b; }
static void EnableCompression(hsBool b) { fCompress = b; }
static void SetSampleRate(short s) { fSampleRate = s; }
static void SetSquelch(hsScalar f) { fRecordThreshhold = f; }
static void IncreaseRecordingThreshhold();
static void DecreaseRecordingThreshhold();
static void SetQuality(int quality); // sets the quality of encoding
static void SetMode(int mode); // sets nb or wb mode
static void SetVBR(bool vbr);
static void SetComplexity(int c);
static void SetENH(hsBool b);
static short GetSampleRate() { return fSampleRate; }
private:
hsBool fMikeOpen;
hsBool fMikeJustClosed;
static hsBool fMicAlwaysOpen;
static hsBool fShowIcons;
static hsBool fCompress;
static hsBool fNetVoice;
static hsBool fRecording;
static short fSampleRate;
plPlate* fDisabledIcon;
plPlate* fTalkIcon;
static hsScalar fRecordThreshhold;
};
// Speex voice encoder/decoder class
class plSpeex
{
public:
~plSpeex();
enum Mode
{
kNarrowband,
kWideband,
kUltraWideband
};
static plSpeex *GetInstance()
{
static plSpeex instance;
return &instance;
}
hsBool Init(Mode mode);
hsBool Shutdown();
hsBool Encode(short *data, int numFrames, int *packedLength, hsRAMStream *out);
hsBool Decode(UInt8 *data, int size, int numFrames, int *numOutputBytes, short *out);
int GetFrameSize() { return fFrameSize; }
void VBR(hsBool b); // turn variable bit rate on/off
void SetVBR(UInt32 vbr); // Set variable bit rate quality
void ABR(hsBool b); // turn average bit rate on/off
void SetABR(UInt32 abr); // Set average bit rate quality
void SetQuality(UInt32 quality); // Set encoder quality
hsBool IsUsingVBR() { return fVBR; }
int GetQuality() { return fQuality; }
void SetENH(hsBool b);
void SetComplexity(UInt8 c);
hsBool Initialized() { return fInitialized; }
private:
plSpeex();
SpeexBits* fBits; // main speex structure
hsBool fBitsInit;
void* fEncoderState;
void* fDecoderState;
int fSampleRate;
int fFrameSize; // frame size from speex - 160 for nb
int fQuality; // 0-10 speex encode quality
hsBool fVBR; // toggle variable bit rate
int fAverageBitrate; // n-bits per second
UInt8 fComplexity; // 1-10 sets cpu resources allowed for encoder
hsBool fENH; // perceptual enhancement
hsBool fInitialized;
};
#endif //plVoiceChat_h

View File

@ -0,0 +1,226 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plWAVClipBuffer - Helper class for writing out WAV data in a buffered //
// manner, with support for clipping off the specified //
// amount at the end, but without knowing beforehand //
// exactly how much data we'll have. //
// //
// The algorithm goes something like this: we keep two buffers, both the //
// size of the amount we want to clip. We then start filling in the first //
// buffer, overflowing into the second buffer and wrapping back to the //
// first again in a circular fashion. When we fill up one buffer and are //
// about to advance to the next, we write that next buffer out. Why? //
// Because we know that, even if we got no more data in, we have enough //
// data in the first buffer to clip out the amount we want, so the other //
// half (which will have older data, being a circular buffer) can be //
// written out safely. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "plWAVClipBuffer.h"
#include "hsStream.h"
#include "hsUtils.h"
#include "plWavFile.h"
//// Constructor/Destructor //////////////////////////////////////////////////
plWAVClipBuffer::plWAVClipBuffer( UInt32 clipSize, CWaveFile *outFile )
{
fBuffers[ 0 ] = fBuffers[ 1 ] = nil;
fFlushCalled = true;
Init( clipSize, outFile );
}
plWAVClipBuffer::~plWAVClipBuffer()
{
IShutdown();
}
//// Init & IShutdown ////////////////////////////////////////////////////////
void plWAVClipBuffer::Init( UInt32 clipSize, CWaveFile *outFile )
{
IShutdown();
if( clipSize > 0 )
{
fBuffers[ 0 ] = TRACKED_NEW UInt8[ clipSize ];
fBuffers[ 1 ] = TRACKED_NEW UInt8[ clipSize ];
memset( fBuffers[ 0 ], 0, clipSize );
memset( fBuffers[ 1 ], 0, clipSize );
}
fWhichBuffer = 0;
fBufferSize = clipSize;
fCursor = 0;
fFirstFlip = true;
fOutFile = outFile;
fFlushCalled = false;
}
void plWAVClipBuffer::IShutdown( void )
{
hsAssert( fFlushCalled, "WAVClipBuffer shut down without flushing it!!!" );
delete [] fBuffers[ 0 ];
delete [] fBuffers[ 1 ];
}
//// WriteData ///////////////////////////////////////////////////////////////
// The main workhorse; call this to add data to the buffer.
hsBool plWAVClipBuffer::WriteData( UInt32 size, UInt8 *data )
{
while( size > 0 )
{
UInt32 toWrite = fBufferSize - fCursor;
if( size < toWrite )
{
// Just write, haven't filled a buffer yet
memcpy( fBuffers[ fWhichBuffer ] + fCursor, data, size );
data += size;
fCursor += size;
return true; // All done!
}
// Fill up to the end of a buffer, then flip
memcpy( fBuffers[ fWhichBuffer ] + fCursor, data, toWrite );
data += toWrite;
fCursor += toWrite;
size -= toWrite;
// Flip now...
fWhichBuffer = 1 - fWhichBuffer;
fCursor = 0;
// Now we can write out this buffer, since it'll be old data and
// we have enough in the other buffer to clip with. The *only*
// time we don't want to do this is the first time we flip, since
// at that point, the buffer we just flipped to hasn't been filled yet.
// (Every time afterwards, we'll always be flipping to a buffer with old
// data).
if( fFirstFlip )
fFirstFlip = false;
else
{
// Write it out before we overwrite it!
UINT written;
HRESULT hr = fOutFile->Write( fBufferSize, fBuffers[ fWhichBuffer ], &written );
if( FAILED( hr ) )
{
hsAssert( false, "ERROR writing WMA stream to WAV file" );
return false;
}
else if( written != fBufferSize )
{
hsAssert( false, "Unable to write all of WMA stream to WAV file" );
return false;
}
}
}
// Cleanly got here, so just return success
return true;
}
//// Flush ///////////////////////////////////////////////////////////////////
// Writes out the remaining data, minus our clip value (which is fBufferSize)
// So here's our situation: at this point, one of two things could be true:
//
// 1) We haven't received enough data to clip by, at which point we don't
// write any more and bail (this will be true if fFirstFlip is still true)
//
// 2) Our cursor is at 0, which means we have one filled buffer that hasn't been
// written out and our current buffer is empty. At this point, we discard the
// filled buffer (which is precisely the length we want to clip by) and we're done.
//
// 3) The buffer we're on should be partially filled, while the other one is older
// data. So, we want to write out the older data and the partial buffer all the way,
// except for the clip size. Since we can therefore never write out any data in the
// partial buffer (since that count will always be less than the clip size and thus be
// the second half of what we clip), we simply figure out how much of the other one we
// clip and write out the rest.
hsBool plWAVClipBuffer::Flush( void )
{
fFlushCalled = true;
if( fFirstFlip )
return false; // We failed--not enough data to clip with
if( fCursor == 0 )
{
// Our current buffer is empty, so the other buffer is precisely what we clip.
// So just discard and return successfully
return true;
}
// The hard case--we always discard the partial buffer we're on, so figure out
// how much we want to save of the other buffer. The math is:
// Partial buffer amount we're clipping = fCursor
// Amount of other buffer we're clipping = fBufferSize - fCursor
// Amount of other buffer we're writing = fBufferSize - ( fBufferSize - fCursor ) = fCursor
// Go figure :)
UInt32 toWrite = fCursor;
UINT written;
HRESULT hr = fOutFile->Write( toWrite, fBuffers[ 1 - fWhichBuffer ], &written );
if( FAILED( hr ) )
{
hsAssert( false, "ERROR writing WMA stream to WAV file" );
return false;
}
else if( written != toWrite )
{
hsAssert( false, "Unable to write all of WMA stream to WAV file" );
return false;
}
// All done!
return true;
}

View File

@ -0,0 +1,94 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plWAVClipBuffer - Helper class for writing out WAV data in a buffered //
// manner, with support for clipping off the specified //
// amount at the end, but without knowing beforehand //
// exactly how much data we'll have. //
// //
// The algorithm goes something like this: we keep two buffers, both the //
// size of the amount we want to clip. We then start filling in the first //
// buffer, overflowing into the second buffer and wrapping back to the //
// first again in a circular fashion. When we fill up one buffer and are //
// about to advance to the next, we write that next buffer out. Why? //
// Because we know that, even if we got no more data in, we have enough //
// data in the first buffer to clip out the amount we want, so the other //
// half (which will have older data, being a circular buffer) can be //
// written out safely. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plWAVClipBuffer_h
#define _plWAVClipBuffer_h
//// Class Definition ////////////////////////////////////////////////////////
class CWaveFile;
class plWAVClipBuffer
{
public:
plWAVClipBuffer( UInt32 clipSize, CWaveFile *outFile );
~plWAVClipBuffer();
// Inits the buffer. Can re-init if you wish
void Init( UInt32 clipSize, CWaveFile *outFile );
// Writes/adds data to the buffer
hsBool WriteData( UInt32 size, UInt8 *data );
// Call at the end, flushes the buffer and performs the clipping
hsBool Flush( void );
protected:
UInt8 *fBuffers[ 2 ];
UInt8 fWhichBuffer; // 0 or 1
UInt32 fCursor, fBufferSize;
hsBool fFirstFlip, fFlushCalled;
CWaveFile *fOutFile;
void IShutdown( void );
};
#endif //_plWAVClipBuffer_h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,130 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plWavFile_H
#define plWavFile_H
#define WAVEFILE_READ 1
#define WAVEFILE_WRITE 2
#include "hsTypes.h"
#include "hsWindows.h"
#include "hsStlUtils.h"
#include <mmsystem.h>
#include "../plAudioCore/plAudioFileReader.h"
struct plSoundMarker
{
char *fName;
double fOffset; // in Secs
plSoundMarker () { fName = NULL;fOffset = 0.0; }
};
//-----------------------------------------------------------------------------
// Name: class CWaveFile
// Desc: Encapsulates reading or writing sound data to or from a wave file
//-----------------------------------------------------------------------------
class CWaveFile : public plAudioFileReader
{
public:
CWaveFile();
~CWaveFile();
HRESULT Open(const char *strFileName, WAVEFORMATEX* pwfx, DWORD dwFlags );
HRESULT OpenFromMemory( BYTE* pbData, ULONG ulDataSize, WAVEFORMATEX* pwfx, DWORD dwFlags );
HRESULT Read( BYTE* pBuffer, DWORD dwSizeToRead, DWORD* pdwSizeRead );
HRESULT AdvanceWithoutRead( DWORD dwSizeToRead, DWORD* pdwSizeRead );
HRESULT Write( UINT nSizeToWrite, BYTE* pbData, UINT* pnSizeWrote );
DWORD GetSize();
HRESULT ResetFile();
WAVEFORMATEX* GetFormat() { return m_pwfx; };
DWORD GetNumMarkers() { return fMarkers.size() ; };
plSoundMarker *GetSoundMarker(int i) { return fMarkers[i]; }
// Overloads for plAudioFileReader
CWaveFile( const char *path, plAudioCore::ChannelSelect whichChan );
virtual hsBool OpenForWriting( const char *path, plWAVHeader &header );
virtual plWAVHeader &GetHeader( void );
virtual void Close( void );
virtual UInt32 GetDataSize( void );
virtual float GetLengthInSecs( void );
virtual hsBool SetPosition( UInt32 numBytes );
virtual hsBool Read( UInt32 numBytes, void *buffer );
virtual UInt32 NumBytesLeft( void );
virtual UInt32 Write( UInt32 bytes, void *buffer );
virtual hsBool IsValid( void );
WAVEFORMATEX* m_pwfx; // Pointer to WAVEFORMATEX structure
HMMIO m_hmmio; // MM I/O handle for the WAVE
MMCKINFO m_ck; // Multimedia RIFF chunk
MMCKINFO m_ckRiff; // Use in opening a WAVE file
DWORD m_dwSize; // The size of the wave file
MMIOINFO m_mmioinfoOut;
DWORD m_dwFlags;
BOOL m_bIsReadingFromMemory;
BYTE* m_pbData;
BYTE* m_pbDataCur;
ULONG m_ulDataSize;
plWAVHeader fHeader;
std::vector<plSoundMarker*> fMarkers;
double fSecsPerSample;
protected:
HRESULT ReadMMIO();
HRESULT WriteMMIO( WAVEFORMATEX *pwfxDest );
HRESULT IClose();
};
#endif // plWavFile_H

View File

@ -0,0 +1,409 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plWin32GroupedSound - Grouped version of a static sound. Lots of short //
// sounds stored in the buffer, all share the same //
// DSound playback buffer and only one plays at a //
// time. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "plWin32GroupedSound.h"
#include "plDSoundBuffer.h"
#include "plAudioSystem.h"
#include "../plAudioCore/plSoundBuffer.h"
#include "../plAudioCore/plSoundDeswizzler.h"
#include "plgDispatch.h"
#include "../pnMessage/plSoundMsg.h"
#include "../plStatusLog/plStatusLog.h"
#include "plProfile.h"
#include "hsResMgr.h"
plProfile_Extern( MemSounds );
plProfile_Extern( StaticSndShoveTime );
plProfile_Extern( StaticSwizzleTime );
/////////////////////////////////////////////////////////////////////////////////////////////////
plWin32GroupedSound::plWin32GroupedSound()
{
fCurrentSound = 0;
}
plWin32GroupedSound::~plWin32GroupedSound()
{
DeActivate();
}
void plWin32GroupedSound::SetPositionArray( UInt16 numSounds, UInt32 *posArray, hsScalar *volumeArray )
{
UInt16 i;
fStartPositions.SetCountAndZero( numSounds );
fVolumes.SetCountAndZero( numSounds );
for( i = 0; i < numSounds; i++ )
{
fStartPositions[ i ] = posArray[ i ];
fVolumes[ i ] = volumeArray[ i ];
}
}
//// IRead/IWrite ////////////////////////////////////////////////////////////
void plWin32GroupedSound::IRead( hsStream *s, hsResMgr *mgr )
{
plWin32StaticSound::IRead( s, mgr );
UInt16 i, n = s->ReadSwap16();
fStartPositions.SetCountAndZero( n );
fVolumes.SetCountAndZero( n );
for( i = 0; i < n; i++ )
{
fStartPositions[ i ] = s->ReadSwap32();
fVolumes[ i ] = s->ReadSwapScalar();
}
}
void plWin32GroupedSound::IWrite( hsStream *s, hsResMgr *mgr )
{
plWin32StaticSound::IWrite( s, mgr );
s->WriteSwap16( fStartPositions.GetCount() );
UInt16 i;
for( i = 0; i < fStartPositions.GetCount(); i++ )
{
s->WriteSwap32( fStartPositions[ i ] );
s->WriteSwapScalar( fVolumes[ i ] );
}
}
//// LoadSound ///////////////////////////////////////////////////////////////
hsBool plWin32GroupedSound::LoadSound( hsBool is3D )
{
if( fFailed )
return false;
if( fPriority > plgAudioSys::GetPriorityCutoff() )
return false; // Don't set the failed flag, just return
if( !plgAudioSys::Active() || fDSoundBuffer != nil )
return false;
// Debug flag #1
if( fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) )
{
// Force a fail
fFailed = true;
return false;
}
// We need it to be resident to read in
plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true);
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
if(!buffer)
{
return plSoundBuffer::kError;
}
if( retVal == plSoundBuffer::kPending) // we are still reading data.
{
return true;
}
// We need it to be resident to read in
if( retVal == plSoundBuffer::kError)
{
char str[ 256 ];
sprintf( str, "Unable to open .wav file %s", fDataBufferKey ? fDataBufferKey->GetName() : "nil");
IPrintDbgMessage( str, true );
fFailed = true;
return false;
}
SetProperty( kPropIs3DSound, is3D );
plWAVHeader header = buffer->GetHeader();
// Debug flag #2
if( fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) )
{
// Force a fail
fFailed = true;
return false;
}
// Calculate the maximum size for our buffer. This will be the length of the longest sound we're going to
// have to play.
UInt16 i;
UInt32 maxSoundSize, len;
for( i = 1, maxSoundSize = 0; i < fStartPositions.GetCount(); i++ )
{
len = fStartPositions[ i ] - fStartPositions[ i - 1 ];
if( len > maxSoundSize )
maxSoundSize = len;
}
len = buffer->GetDataLength() - fStartPositions[ fStartPositions.GetCount() - 1 ];
if( len > maxSoundSize )
maxSoundSize = len;
// Based on that, allocate our buffer
UInt32 bufferSize = maxSoundSize - ( maxSoundSize % header.fBlockAlign );
if( header.fNumChannels > 1 && is3D )
{
// We can only do a single channel of 3D sound. So copy over one (later)
bufferSize /= header.fNumChannels;
header.fBlockAlign /= header.fNumChannels;
header.fAvgBytesPerSec /= header.fNumChannels;
header.fNumChannels = 1;
}
fNumDestChannels = (UInt8)(header.fNumChannels);
fNumDestBytesPerSample = (UInt8)(header.fBlockAlign);
// Create our DSound buffer (or rather, the wrapper around it)
fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet( kPropLooping ), true );
if( !fDSoundBuffer->IsValid() )
{
char str[256];
sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName());
IPrintDbgMessage( str, true );
fFailed = true;
delete fDSoundBuffer;
fDSoundBuffer = nil;
return false;
}
IRefreshEAXSettings( true );
// Fill the buffer with whatever our current sound is.
IFillCurrentSound( 0 );
// Logging
char str[ 256 ];
sprintf( str, " Grouped %s %s allocated (%d msec).", buffer->GetFileName() != nil ? "file" : "buffer",
buffer->GetFileName() != nil ? buffer->GetFileName() : buffer->GetKey()->GetUoid().GetObjectName(),
//fDSoundBuffer->IsHardwareAccelerated() ? "hardware" : "software",
//fDSoundBuffer->IsStaticVoice() ? "static" : "dynamic",
#ifdef PL_PROFILE_ENABLED
gProfileVarStaticSndShoveTime.GetValue() );
#else
0 );
#endif
IPrintDbgMessage( str );
if( GetKey() != nil && GetKeyName() != nil && strstr( GetKeyName(), "Footstep" ) != nil )
;
else
plStatusLog::AddLineS( "audioTimes.log", "%s (%s)", str, GetKey() ? GetKeyName() : "unkeyed" );
fTotalBytes = bufferSize;
plProfile_NewMem( MemSounds, fTotalBytes );
// All done!
// if( fLoadFromDiskOnDemand )
// IUnloadDataBuffer();
FreeSoundData();
return true;
}
//// GetSoundLength //////////////////////////////////////////////////////////
// Gets the length (in seconds) of the given sound index from the group.
hsScalar plWin32GroupedSound::GetSoundLength( Int16 soundIndex )
{
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
if(buffer)
{
return (hsScalar)IGetSoundByteLength( soundIndex ) / buffer->GetHeader().fAvgBytesPerSec;
}
return 0;
}
//// IGetSoundByteLength /////////////////////////////////////////////////////
// Byte version of above.
UInt32 plWin32GroupedSound::IGetSoundByteLength( Int16 soundIndex )
{
if( soundIndex == fStartPositions.GetCount() - 1 )
return ((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetDataLength() - fStartPositions[ soundIndex ];
else
return fStartPositions[ soundIndex + 1 ] - fStartPositions[ soundIndex ];
}
//// IGetDataPointer/Length //////////////////////////////////////////////////
// Abstracting a few things here for the incidentalMgr
void *plWin32GroupedSound::IGetDataPointer( void ) const
{
return ( fDataBufferKey->ObjectIsLoaded() ) ? (void *)( (UInt8 *)((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetData() + fStartPositions[ fCurrentSound ] ) : nil;
}
UInt32 plWin32GroupedSound::IGetDataLength( void ) const
{
return ( fDataBufferKey->ObjectIsLoaded() ) ? fCurrentSoundLength : 0;
}
//// IFillCurrentSound ///////////////////////////////////////////////////////
// Fills the DSoundBuffer with data from the current sound from our sound
// group, optionally switching what our current sound is.
void plWin32GroupedSound::IFillCurrentSound( Int16 newCurrent /*= -1*/ )
{
//void *dataPtr;
//UInt32 dataLength;
if( !fDSoundBuffer && plgAudioSys::Active() )
LoadSound( IsPropertySet( kPropIs3DSound ) );
plProfile_BeginTiming( StaticSndShoveTime );
// Make sure we're stopped first. Don't want to be filling while we're playing
Stop();
if( newCurrent != -1 )
{
fCurrentSound = (UInt16)newCurrent;
if( fCurrentSound >= fStartPositions.GetCount() )
{
// Invalid index!
hsAssert( false, "Invalid index in plWin32GroupedSound::IFillCurrentSound()" );
fCurrentSound = -1;
return;
}
// Set our length based on the current sound
fCurrentSoundLength = IGetSoundByteLength( fCurrentSound );
if( fDataBufferKey->ObjectIsLoaded() )
SetLength( fCurrentSoundLength / ((plSoundBuffer *)fDataBufferKey->ObjectIsLoaded())->GetHeader().fAvgBytesPerSec );
// Update our volume as well
SetVolume( fVolumes[ fCurrentSound ] );
}
if( fDSoundBuffer != nil )
{
/// Lock our buffer
//fDSoundBuffer->Lock( dataLength, dataPtr );
/// Copy or de-swizzle?
//if( fDataBuffer->GetHeader().fNumChannels == fNumDestChannels )
{
// Just copy
//memcpy( dataPtr, (Byte *)fDataBuffer->GetData() + fStartPositions[ fCurrentSound ], fCurrentSoundLength );
//dataPtr = (Byte *)dataPtr + fCurrentSoundLength;
//dataLength -= fCurrentSoundLength;
}
//else
{
// We're extracting a single channel of sound into our sound buffer, so it isn't a straight copy...
/*plProfile_BeginTiming( StaticSwizzleTime );
plSoundDeswizzler deswiz( (Byte *)fDataBuffer->GetData() + fStartPositions[ fCurrentSound ], fCurrentSoundLength,
(UInt8)(fDataBuffer->GetHeader().fNumChannels), fNumDestBytesPerSample );
deswiz.Extract( fChannelSelect, dataPtr );
dataPtr = (Byte *)dataPtr + fCurrentSoundLength / fDataBuffer->GetHeader().fNumChannels;
dataLength -= fCurrentSoundLength / fDataBuffer->GetHeader().fNumChannels;
plProfile_EndTiming( StaticSwizzleTime );*/
}
/// Fill the remaining part with empty space
//memset( dataPtr, 0, dataLength );
/// Finally, unlock!
//fDSoundBuffer->Unlock();
}
/// All done!
plProfile_EndTiming( StaticSndShoveTime );
}
void plWin32GroupedSound::IDerivedActuallyPlay( void )
{
// Ensure there's a stop notify for us
if( !fReallyPlaying )
{
fDSoundBuffer->Play();
fReallyPlaying = true;
}
plSoundEvent *event = IFindEvent( plSoundEvent::kStart );
if( event != nil )
event->SendCallbacks();
}
hsBool plWin32GroupedSound::MsgReceive( plMessage* pMsg )
{
plSoundMsg *soundMsg = plSoundMsg::ConvertNoRef( pMsg );
if( soundMsg != nil && soundMsg->Cmd( plSoundMsg::kSelectFromGroup ) )
{
IFillCurrentSound( soundMsg->fIndex );
return true;
}
else if( soundMsg != nil && soundMsg->Cmd( plSoundMsg::kPlay ) )
{
Play();
//plIncidentalMgr::GetInstance()->Play( this, plIncidentalMgr::kNormal );
return true;
}
return plWin32StaticSound::MsgReceive( pMsg );
}

View File

@ -0,0 +1,99 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plWin32GroupedSound - Grouped version of a static sound. Lots of short //
// sounds stored in the buffer, all share the same //
// DSound playback buffer and only one plays at a //
// time. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef plWin32GroupedSound_h
#define plWin32GroupedSound_h
#include "plWin32StaticSound.h"
class hsResMgr;
class plDSoundBuffer;
class plEventCallbackMsg;
#include "plSoundEvent.h"
class plWin32GroupedSound : public plWin32StaticSound
{
public:
plWin32GroupedSound();
~plWin32GroupedSound();
CLASSNAME_REGISTER( plWin32GroupedSound );
GETINTERFACE_ANY( plWin32GroupedSound, plWin32StaticSound );
virtual hsBool LoadSound( hsBool is3D );
virtual hsBool MsgReceive( plMessage *pMsg );
void SetPositionArray( UInt16 numSounds, UInt32 *posArray, hsScalar *volumeArray );
hsScalar GetSoundLength( Int16 soundIndex );
virtual double GetLength() { return GetSoundLength( fCurrentSound ); }
protected:
UInt16 fCurrentSound;
UInt32 fCurrentSoundLength;
hsTArray<UInt32> fStartPositions; // In bytes
hsTArray<hsScalar> fVolumes;
// Some extra handy info for us
UInt8 fNumDestChannels, fNumDestBytesPerSample;
virtual void IDerivedActuallyPlay( void );
virtual void IRead( hsStream *s, hsResMgr *mgr );
virtual void IWrite( hsStream *s, hsResMgr *mgr );
UInt32 IGetSoundByteLength( Int16 soundIndex );
void IFillCurrentSound( Int16 newCurrent = -1 );
// Abstracting a few things here for the incidentalMgr
virtual void * IGetDataPointer( void ) const;
virtual UInt32 IGetDataLength( void ) const;
};
#endif //plWin32GroupedSound_h

View File

@ -0,0 +1,445 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include <direct.h>
#include "HeadSpin.h"
#include "hsGeometry3.h"
#include "hsTimer.h"
#include "hsResMgr.h"
#include "plgDispatch.h"
#include "plProfile.h"
#include "plWin32Sound.h"
#include "plAudioSystem.h"
#include "plDSoundBuffer.h"
#include "plWavFile.h"
#include "../plAudible/plWinAudible.h"
#include "../plNetMessage/plNetMessage.h"
#include "../pnNetCommon/plNetApp.h"
#include "../pnMessage/plSoundMsg.h"
#include "../pnMessage/plEventCallbackMsg.h"
#include "../plPipeline/plPlates.h"
#include "../plStatusLog/plStatusLog.h"
plProfile_CreateMemCounter("Sounds", "Memory", MemSounds);
plProfile_Extern(SoundPlaying);
plWin32Sound::plWin32Sound() :
fFailed(false),
fPositionInited(false),
fAwaitingPosition(false),
fTotalBytes(0),
fReallyPlaying(false),
fChannelSelect(0),
fDSoundBuffer(nil)
{
}
plWin32Sound::~plWin32Sound()
{
}
void plWin32Sound::Activate( hsBool forcePlay )
{
if( fFailed )
return;
plSound::Activate( forcePlay );
}
void plWin32Sound::DeActivate()
{
plSound::DeActivate();
IFreeBuffers();
}
void plWin32Sound::IFreeBuffers( void )
{
if( fDSoundBuffer != nil )
{
delete fDSoundBuffer;
fDSoundBuffer = nil;
plProfile_DelMem(MemSounds, fTotalBytes);
fTotalBytes = 0;
}
fPositionInited = false;
fAwaitingPosition = false;
}
void plWin32Sound::Update()
{
plSound::Update();
}
void plWin32Sound::IActuallyPlay( void )
{
//plSound::Play();
if (!fDSoundBuffer && plgAudioSys::Active())
LoadSound( IsPropertySet( kPropIs3DSound ) );
if(!fLoading )
{
if (fDSoundBuffer && plgAudioSys::Active() )
{
// Sometimes base/derived classes can be annoying
IDerivedActuallyPlay();
RefreshVolume();
}
else
{
// If we can't load (for ex., if audio is off), then we act like we played and then stopped
// really fast. Thus, we need to send *all* of our callbacks off immediately and then Stop().
UInt32 i;
for( i = 0; i < fSoundEvents.GetCount(); i++ )
{
fSoundEvents[ i ]->SendCallbacks();
}
// Now stop, 'cause we played really really really really fast
fPlaying = false;
fPlayOnReactivate = IsPropertySet( kPropLooping );
IActuallyStop();
}
}
}
void plWin32Sound::IActuallyStop()
{
if( fReallyPlaying )
{
if( fDSoundBuffer != nil )
{
if(IsPropertySet(kPropIncidental))
{
--fIncidentalsPlaying;
}
fDSoundBuffer->Stop();
plStatusLog::AddLineS("impacts.log", "Stopping %s", GetKeyName());
}
fReallyPlaying = false;
}
else
{
if( fDSoundBuffer != nil && fDSoundBuffer->IsPlaying() )
{
plStatusLog::AddLineS( "audio.log", 0xffff0000, "WARNING: BUFFER FLAGGED AS STOPPED BUT NOT STOPPED - %s", GetKey() ? GetKeyName() : nil );
fDSoundBuffer->Stop();
}
}
// send callbacks
plSoundEvent *event = IFindEvent( plSoundEvent::kStop );
if( event != nil )
{
event->SendCallbacks();
}
plSound::IActuallyStop();
}
plSoundMsg* plWin32Sound::GetStatus(plSoundMsg* pMsg)
{
plSoundMsg* pReply = TRACKED_NEW plSoundMsg;
pReply->AddReceiver( pMsg->GetSender() );
pReply->SetCmd(plSoundMsg::kStatusReply);
pReply->fLoop = IsPropertySet( kPropLooping );
pReply->fPlaying = IsPlaying();
return pReply;
}
void plWin32Sound::SetMin( const int m )
{
plSound::SetMin(m);
if(fDSoundBuffer)
{
fDSoundBuffer->SetMinDistance(m);
}
}
void plWin32Sound::SetMax( const int m )
{
plSound::SetMax(m);
if( fDSoundBuffer )
{
fDSoundBuffer->SetMaxDistance( m );
}
}
void plWin32Sound::SetOuterVolume( const int v )
{
plSound::SetOuterVolume(v);
if(fDSoundBuffer)
{
fDSoundBuffer->SetConeOutsideVolume(v);
}
}
void plWin32Sound::SetConeAngles( int inner, int outer )
{
plSound::SetConeAngles(inner, outer);
if(fDSoundBuffer)
{
fDSoundBuffer->SetConeAngles(inner, outer);
}
}
void plWin32Sound::SetConeOrientation( hsScalar x, hsScalar y, hsScalar z )
{
plSound::SetConeOrientation(x, y, z);
if(fDSoundBuffer)
{
fDSoundBuffer->SetConeOrientation(x, z, y);
}
}
void plWin32Sound::SetVelocity( const hsVector3 vel )
{
plSound::SetVelocity(vel);
if( fDSoundBuffer)
fDSoundBuffer->SetVelocity(vel.fX, vel.fZ, vel.fY);
}
void plWin32Sound::SetPosition( const hsPoint3 pos )
{
plSound::SetPosition(pos);
if(fDSoundBuffer)
{
// in openal sounds that are mono are played as positional. Since gui's may be positioned way off in space, the sound may not be audible.
// doing this allows us to play mono gui sounds and still hear them, since this attaches the sound to the listener.
if(fType == kGUISound)
{
hsPoint3 listenerPos = plgAudioSys::Sys()->GetCurrListenerPos();
fDSoundBuffer->SetPosition(listenerPos.fX, listenerPos.fZ, listenerPos.fY);
}
else
{
fDSoundBuffer->SetPosition(pos.fX, pos.fZ, pos.fY);
}
}
fPositionInited = true;
if( fAwaitingPosition )
{
// If this is set, then we tried to set the volume before the position. Since
// this results in some ghastly sound popping, we wait until we set the position
// (here), and then call our volume again
RefreshVolume();
fAwaitingPosition = false;
}
}
void plWin32Sound::ISetActualVolume(float volume)
{
float vol = IAttenuateActualVolume( volume ) * IGetChannelVolume();
if( fDSoundBuffer )
{
if( fPositionInited || !IsPropertySet( kPropIs3DSound ) )
{
fDSoundBuffer->SetScalarVolume( vol );
}
else
{
// If position isn't inited, we don't want to set the volume yet,
// so set this flag so we know to set the volume once we DO get the position
fAwaitingPosition = true;
}
}
IUpdateDebugPlate(); // Byte me.
}
//////////////////////////////////////////////////////////////
// The base class will make sure all our params are correct and up-to-date,
// but before it does its work, we have to make sure our buffer is ready to
// be updated.
void plWin32Sound::IRefreshParams( void )
{
if (!fDSoundBuffer && plgAudioSys::Active())
LoadSound( IsPropertySet( kPropIs3DSound ) );
else
{
// There is a gap between the ds buffer stopping and the sound being marked as stopped.
// If the sound is asked to play again during this time frame it will never actually be
// played because it is still marked as playing. Not only that, but we'll lose a hardware voice too.
// This will fix that by starting it up again.
if(plgAudioSys::Active())
fDSoundBuffer->Play();
}
plSound::IRefreshParams();
}
void plWin32Sound::IRefreshEAXSettings( hsBool force )
{
if( fDSoundBuffer != nil )
fDSoundBuffer->SetEAXSettings( &GetEAXSettings(), force );
}
void plWin32Sound::IAddCallback( plEventCallbackMsg *pMsg )
{
plSoundEvent::Types type = plSoundEvent::GetTypeFromCallbackMsg( pMsg );
plSoundEvent *event;
if( type == plSoundEvent::kTime )
{
UInt32 byteTime = ( fDSoundBuffer != nil ) ? fDSoundBuffer->GetBufferBytePos( pMsg->fEventTime ) : 0;
event = IFindEvent( type, byteTime );
if( event == nil )
{
// Add a new sound event for this guy
event = TRACKED_NEW plSoundEvent( type, byteTime, this );
//fDSoundBuffer->AddPosNotify( byteTime );
fSoundEvents.Append( event );
}
}
else
{
event = IFindEvent( type );
if( event == nil )
{
// Add a new sound event for this guy
event = TRACKED_NEW plSoundEvent( type, this );
fSoundEvents.Append( event );
}
}
event->AddCallback( pMsg );
}
void plWin32Sound::IRemoveCallback( plEventCallbackMsg *pMsg )
{
plSoundEvent::Types type = plSoundEvent::GetTypeFromCallbackMsg( pMsg );
for(int i = 0; i < fSoundEvents.GetCount(); ++i)
{
if( fSoundEvents[ i ]->GetType() == type )
{
if( fSoundEvents[ i ]->RemoveCallback( pMsg ) )
{
if( fSoundEvents[ i ]->GetNumCallbacks() == 0 )
{
//if( fSoundEvents[ i ]->GetType() == plSoundEvent::kTime )
//fDSoundBuffer->RemovePosNotify( fSoundEvents[ i ]->GetTime() );
delete fSoundEvents[ i ];
fSoundEvents.Remove( i );
}
break;
}
}
}
}
plSoundEvent *plWin32Sound::IFindEvent( plSoundEvent::Types type, UInt32 bytePos )
{
for(int i = 0; i < fSoundEvents.GetCount(); ++i )
{
if( fSoundEvents[ i ]->GetType() == type )
{
if( type != plSoundEvent::kTime || bytePos == fSoundEvents[ i ]->GetTime() )
return fSoundEvents[ i ];
}
}
return nil;
}
void plWin32Sound::RemoveCallbacks(plSoundMsg* pSoundMsg)
{
for(int i = 0; i < pSoundMsg->GetNumCallbacks(); ++i )
IRemoveCallback( pSoundMsg->GetEventCallback( i ) );
}
void plWin32Sound::AddCallbacks(plSoundMsg* pSoundMsg)
{
for(int i = 0; i < pSoundMsg->GetNumCallbacks(); ++i )
IAddCallback( pSoundMsg->GetEventCallback( i ) );
}
hsBool plWin32Sound::MsgReceive( plMessage* pMsg )
{
plEventCallbackMsg *e = plEventCallbackMsg::ConvertNoRef( pMsg );
if( e != nil )
{
if( e->fEvent == kStop )
{
fPlaying = false;
fPlayOnReactivate = false; // Just to make sure...
// Do we have an ending fade?
if( fFadeOutParams.fLengthInSecs > 0 )
{
IStartFade( &fFadeOutParams );
plSoundEvent *event = IFindEvent( plSoundEvent::kStop );
if( event != nil )
event->SendCallbacks();
}
else
{
if( fFading )
IStopFade();
fCurrVolume = 0.f;
this->ISetActualVolume( fCurrVolume );
}
this->IActuallyStop();
return true;
}
}
return plSound::MsgReceive( pMsg );
}
void plWin32Sound::IRead( hsStream *s, hsResMgr *mgr )
{
plSound::IRead( s, mgr );
fChannelSelect = s->ReadByte();
}
void plWin32Sound::IWrite( hsStream *s, hsResMgr *mgr )
{
plSound::IWrite( s, mgr );
s->WriteByte( fChannelSelect );
}

View File

@ -0,0 +1,136 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plWin32Sound_h
#define plWin32Sound_h
#include "hsTemplates.h"
#include "plSound.h"
#include "hsThread.h"
#include "plSoundEvent.h"
#define NUM_MAX_HANDLES 16
#define REPEAT_INFINITE 0xffffffff
struct hsVector3;
class hsResMgr;
class plEventCallbackMsg;
class plSoundMsg;
class DSoundCallbackHandle;
class plSoundEvent;
class plDSoundBuffer;
class plWin32Sound : public plSound
{
public:
plWin32Sound();
virtual ~plWin32Sound();
CLASSNAME_REGISTER( plWin32Sound );
GETINTERFACE_ANY( plWin32Sound, plSound );
virtual void Activate(hsBool forcePlay = false);
virtual void DeActivate();
virtual void AddCallbacks(plSoundMsg* pMsg);
virtual void RemoveCallbacks(plSoundMsg* pMsg);
virtual plSoundMsg* GetStatus(plSoundMsg* pMsg);
virtual hsBool MsgReceive(plMessage* pMsg);
virtual void Update();
virtual void SetMin(const int m); // sets minimum falloff distance
virtual void SetMax(const int m); // sets maximum falloff distance
virtual void SetConeOrientation(hsScalar x, hsScalar y, hsScalar z);
virtual void SetOuterVolume( const int v ); // volume for the outer cone (if applicable)
virtual void SetConeAngles( int inner, int outer );
virtual void SetPosition(const hsPoint3 pos);
virtual void SetVelocity(const hsVector3 vel);
enum ChannelSelect
{
kLeftChannel,
kRightChannel
};
// Selects a channel source from a multi-channel (stereo) file. Ignored if source is mono
void SetChannelSelect( ChannelSelect source ) { fChannelSelect = (UInt8)source; }
virtual UInt8 GetChannelSelect( void ) const { return fChannelSelect; }
protected:
plDSoundBuffer * fDSoundBuffer;
hsBool fFailed;
hsBool fPositionInited, fAwaitingPosition;
hsBool fReallyPlaying;
UInt32 fTotalBytes;
hsBool fWasPlaying;
UInt8 fChannelSelect; // For selecting a mono channel from a stereo file
hsTArray<plSoundEvent *> fSoundEvents;
virtual void ISetActualVolume(float v);
virtual void IActuallyStop( void );
virtual hsBool IActuallyPlaying( void ) { return fReallyPlaying; }
virtual void IActuallyPlay( void );
virtual void IFreeBuffers( void );
virtual hsBool IActuallyLoaded( void ) { return ( fDSoundBuffer != nil ) ? true : false; }
// Override to make sure the buffer is available before the base class is called
virtual void IRefreshParams( void );
virtual void IDerivedActuallyPlay( void ) = 0;
virtual void IAddCallback( plEventCallbackMsg *pMsg );
virtual void IRemoveCallback( plEventCallbackMsg *pMsg );
plSoundEvent *IFindEvent( plSoundEvent::Types type, UInt32 bytePos = 0 );
virtual void IRead( hsStream *s, hsResMgr *mgr );
virtual void IWrite( hsStream *s, hsResMgr *mgr );
virtual void IRefreshEAXSettings( hsBool force = false );
};
#endif //plWin32Sound_h

View File

@ -0,0 +1,343 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "HeadSpin.h"
#include "hsGeometry3.h"
#include "hsResMgr.h"
#include "plgDispatch.h"
#include "plProfile.h"
#include "plWin32StaticSound.h"
#include "plWin32Sound.h"
#include "plDSoundBuffer.h"
#include "plAudioSystem.h"
#include "../plAudioCore/plSoundBuffer.h"
#include "../plAudioCore/plSoundDeswizzler.h"
#include "../pnMessage/plEventCallbackMsg.h"
#include "../pnMessage/plAudioSysMsg.h"
#include "../plMessage/plLinkToAgeMsg.h"
#include "../plMessage/plAvatarMsg.h"
#include "../plPipeline/plPlates.h"
#include "../plStatusLog/plStatusLog.h"
plProfile_Extern(MemSounds);
plProfile_CreateAsynchTimer( "Static Shove Time", "Sound", StaticSndShoveTime );
plProfile_CreateAsynchTimer( "Static Swizzle Time", "Sound", StaticSwizzleTime );
plWin32StaticSound::plWin32StaticSound()
{
}
plWin32StaticSound::~plWin32StaticSound()
{
DeActivate();
IUnloadDataBuffer();
}
void plWin32StaticSound::Activate( hsBool forcePlay )
{
plWin32Sound::Activate( forcePlay );
}
void plWin32StaticSound::DeActivate()
{
plWin32Sound::DeActivate();
}
hsBool plWin32StaticSound::LoadSound( hsBool is3D )
{
if (fFailed)
return false;
if( fPriority > plgAudioSys::GetPriorityCutoff() )
return false; // Don't set the failed flag, just return
if (plgAudioSys::Active() && !fDSoundBuffer)
{
// Debug flag #1
if( fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) )
{
// Force a fail
fFailed = true;
return false;
}
// We need it to be resident to read in
plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true);
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
if(!buffer)
{
return plSoundBuffer::kError;
}
if( retVal == plSoundBuffer::kPending) // we are still reading data.
{
return true;
}
if( retVal == plSoundBuffer::kError )
{
char str[ 256 ];
sprintf( str, "Unable to open .wav file %s", fDataBufferKey ? fDataBufferKey->GetName() : "nil");
IPrintDbgMessage( str, true );
fFailed = true;
return false;
}
SetProperty( kPropIs3DSound, is3D );
plWAVHeader header = buffer->GetHeader();
// Debug flag #2
if( fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) )
{
// Force a fail
fFailed = true;
return false;
}
UInt32 bufferSize = buffer->GetDataLength();
if( header.fNumChannels > 1 && is3D )
{
// We can only do a single channel of 3D sound. So copy over one (later)
bufferSize /= header.fNumChannels;
header.fBlockAlign /= header.fNumChannels;
header.fAvgBytesPerSec /= header.fNumChannels;
header.fNumChannels = 1;
}
hsBool tryStatic = true;
// If we want FX, we can't use a static voice, but EAX doesn't fit under that limitation :)
if( 0 )
tryStatic = false;
// Create our DSound buffer (or rather, the wrapper around it)
fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet( kPropLooping ), tryStatic );
if( !fDSoundBuffer->IsValid() )
{
char str[256];
sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName());
IPrintDbgMessage( str, true );
fFailed = true;
delete fDSoundBuffer;
fDSoundBuffer = nil;
return false;
}
plProfile_BeginTiming( StaticSndShoveTime );
if(!fDSoundBuffer->FillBuffer(buffer->GetData(), buffer->GetDataLength(), &header))
{
delete fDSoundBuffer;
fDSoundBuffer = nil;
plStatusLog::AddLineS("audio.log", "Could not play static sound, no voices left %s", GetKeyName());
return false;
}
plProfile_EndTiming( StaticSndShoveTime );
IRefreshEAXSettings( true );
fTotalBytes = bufferSize;
plProfile_NewMem(MemSounds, fTotalBytes);
// get pertinent info
hsScalar length = (hsScalar)bufferSize / (hsScalar)header.fAvgBytesPerSec;
SetLength(length);
if( fLoadFromDiskOnDemand && !IsPropertySet( kPropLoadOnlyOnCall ) )
FreeSoundData();
return true;
}
return false;
}
void plWin32StaticSound::Update()
{
plWin32Sound::Update();
if(fDSoundBuffer)
{
if(fPlaying) // we think we are playing
{
if(!fDSoundBuffer->IsPlaying()) // are we actually playing
{
Stop();
}
}
}
}
void plWin32StaticSound::IDerivedActuallyPlay( void )
{
// Ensure there's a stop notify for us
if( !fReallyPlaying )
{
for(;;)
{
if(IsPropertySet(kPropIncidental))
{
if(fIncidentalsPlaying >= MAX_INCIDENTALS)
break;
++fIncidentalsPlaying;
}
fDSoundBuffer->Play();
fReallyPlaying = true;
break;
}
}
plSoundEvent *event = IFindEvent( plSoundEvent::kStart );
if( event != nil )
event->SendCallbacks();
}
float plWin32StaticSound::GetActualTimeSec()
{
if(fDSoundBuffer)
return fDSoundBuffer->GetTimeOffsetSec();
return 0.0f;
}
void plWin32StaticSound::ISetActualTime(double t)
{
if( !fDSoundBuffer && plgAudioSys::Active())
LoadSound( IsPropertySet( kPropIs3DSound ) );
if( fDSoundBuffer )
{
if(!t)
fDSoundBuffer->SetTimeOffsetSec((float)t);
}
}
hsBool plWin32StaticSound::MsgReceive( plMessage* pMsg )
{
return plWin32Sound::MsgReceive( pMsg );
}
void plWin32StaticSound::IRemoveCallback( plEventCallbackMsg *pCBMsg )
{
plWin32Sound::IRemoveCallback( pCBMsg );
}
void plWin32StaticSound::IAddCallback( plEventCallbackMsg *pCBMsg )
{
if( plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStop &&
plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStart )
{
hsAssert( false, "Static sounds only support start and stop callbacks at this time." );
return;
}
plWin32Sound::IAddCallback( pCBMsg );
}
plWin32LinkSound::plWin32LinkSound()
{
SetLocalOnly(true); // linking sounds already synch at a higher level
SetProperty( kPropDontFade, true );
}
void plWin32LinkSound::Read(hsStream* s, hsResMgr* mgr)
{
plWin32StaticSound::Read(s, mgr);
plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey());
plgDispatch::Dispatch()->RegisterForExactType(plPseudoLinkEffectMsg::Index(), GetKey());
SetLocalOnly(true); // linking sounds already synch at a higher level
SetProperty( kPropDontFade, true );
}
void plWin32LinkSound::Write(hsStream* s, hsResMgr* mgr)
{
plWin32StaticSound::Write(s, mgr);
}
hsBool plWin32LinkSound::MsgReceive( plMessage* pMsg )
{
plLinkEffectBCMsg *msg = plLinkEffectBCMsg::ConvertNoRef( pMsg );
if( msg != nil && !msg->HasLinkFlag(plLinkEffectBCMsg::kMute))
{
if (msg->fLinkKey->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID())
{
if (!IsPropertySet(kPropFullyDisabled))
{
ISetActualTime(0);
Play();
//Activate(true);
}
}
return true;
}
plPseudoLinkEffectMsg *psmsg = plPseudoLinkEffectMsg::ConvertNoRef( pMsg );
if( psmsg != nil)
{
if (psmsg->fAvatarKey->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID())
{
if (!IsPropertySet(kPropFullyDisabled))
{
ISetActualTime(0);
//Play();
Activate(true);
}
}
return true;
}
plAvatarStealthModeMsg *sMsg = plAvatarStealthModeMsg::ConvertNoRef(pMsg);
if (sMsg)
{
if (sMsg->GetSender()->GetUoid().GetClonePlayerID() == GetKey()->GetUoid().GetClonePlayerID())
{
SetProperty(kPropFullyDisabled, (sMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked));
plNetApp::StaticDebugMsg("plWin32LinkSound: rcvd avatarStealth msg, cloaked=%d", sMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked);
}
return true;
}
return plWin32StaticSound::MsgReceive( pMsg );
}

View File

@ -0,0 +1,96 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plWin32StaticSound_h
#define plWin32StaticSound_h
#include "plWin32Sound.h"
class hsResMgr;
class plDSoundBuffer;
class plEventCallbackMsg;
#include "plSoundEvent.h"
class plWin32StaticSound : public plWin32Sound
{
public:
plWin32StaticSound();
~plWin32StaticSound();
CLASSNAME_REGISTER( plWin32StaticSound );
GETINTERFACE_ANY( plWin32StaticSound, plWin32Sound );
virtual void Activate( hsBool forcePlay = false );
virtual void DeActivate();
virtual hsBool LoadSound( hsBool is3D );
virtual void Update();
virtual hsBool MsgReceive(plMessage* pMsg);
virtual void SetStartPos(unsigned bytes){}
protected:
hsBool fRegisteredOnThread;
virtual void IDerivedActuallyPlay( void );
virtual void ISetActualTime( double t );
virtual float GetActualTimeSec();
virtual void IAddCallback( plEventCallbackMsg *pCBMsg );
virtual void IRemoveCallback( plEventCallbackMsg *pCBMsg );
};
// Same as a plWin32StaticSound, except this registers for a plLinkEffectBCMsg to play the sound on linking.
class plWin32LinkSound : public plWin32StaticSound
{
public:
plWin32LinkSound();
~plWin32LinkSound() { }
CLASSNAME_REGISTER( plWin32LinkSound );
GETINTERFACE_ANY( plWin32LinkSound, plWin32StaticSound );
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
virtual hsBool MsgReceive(plMessage* pMsg);
};
#endif //plWin32StaticSound_h

View File

@ -0,0 +1,509 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include <direct.h>
#include "HeadSpin.h"
#include "hsTimer.h"
#include "hsGeometry3.h"
#include "plgDispatch.h"
#include "plProfile.h"
#include "plWin32Sound.h"
#include "plWin32StreamingSound.h"
#include "plDSoundBuffer.h"
#include "plAudioSystem.h"
#include "../plAudioCore/plAudioFileReader.h"
#include "../plAudioCore/plSoundBuffer.h"
#include "../plAudioCore/plSoundDeswizzler.h"
#include "../pnMessage/plSoundMsg.h"
#include "../pnMessage/plEventCallbackMsg.h"
#include "../plStatusLog/plStatusLog.h"
#define STREAMING_UPDATE_MS 200
plProfile_Extern(MemSounds);
plProfile_CreateAsynchTimer( "Stream Shove Time", "Sound", StreamSndShoveTime );
plProfile_CreateAsynchTimer( "Stream Swizzle Time", "Sound", StreamSwizzleTime );
plProfile_Extern( SoundLoadTime );
plWin32StreamingSound::plWin32StreamingSound() :
fDataStream(nil),
fBlankBufferFillCounter(0),
fDeswizzler(nil),
fStreamType(kNoStream),
fLastStreamingUpdate(0),
fStopping(false),
fPlayWhenStopped(false),
fStartPos(0)
{
fBufferLengthInSecs = plgAudioSys::GetStreamingBufferSize();
}
plWin32StreamingSound::~plWin32StreamingSound()
{
/// Call before we delete our dataStream
DeActivate();
IUnloadDataBuffer();
delete fDataStream;
fDataStream = nil;
fSrcFilename[ 0 ] = 0;
delete fDeswizzler;
}
void plWin32StreamingSound::DeActivate()
{
plWin32Sound::DeActivate();
}
// Change the filename used by this streaming sound, so we can play a different file
void plWin32StreamingSound::SetFilename(const char *filename, bool isCompressed)
{
fNewFilename = filename;
fIsCompressed = isCompressed;
fFailed = false; // just in case the last sound failed to load turn this off so it can try to load the new sound
}
//////////////////////////////////////////////////////////////
// Override, 'cause we don't want to actually LOAD the sound for streaming,
// just make sure it's decompressed and such and ready to stream.
plSoundBuffer::ELoadReturnVal plWin32StreamingSound::IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental /* = false */ )
{
if(fPlayWhenStopped)
return plSoundBuffer::kPending;
hsBool sfxPath = fNewFilename.size() ? false : true;
if( fDataStream != nil && fNewFilename.size() == 0)
return plSoundBuffer::kSuccess; // Already loaded
if(!ILoadDataBuffer())
{
return plSoundBuffer::kError;
}
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
if(!buffer)
return plSoundBuffer::kError;
// The databuffer also needs to know if the source is compressed or not
if(fNewFilename.length())
{
buffer->SetFileName(fNewFilename.c_str());
buffer->SetFlag(plSoundBuffer::kStreamCompressed, fIsCompressed);
fNewFilename.clear();
if(fReallyPlaying)
{
fPlayWhenStopped = true;
return plSoundBuffer::kPending;
}
}
if( buffer->IsValid() )
{
plAudioFileReader::StreamType type = plAudioFileReader::kStreamNative;
fStreamType = kStreamCompressed;
if(!buffer->HasFlag(plSoundBuffer::kStreamCompressed))
{
if(buffer->GetDataLengthInSecs() > plgAudioSys::GetStreamFromRAMCutoff( ))
{
fStreamType = kStreamFromDisk;
type = plAudioFileReader::kStreamWAV;
}
else
{
fStreamType = kStreamFromRAM;
type = plAudioFileReader::kStreamRAM;
}
}
if(!fStartPos)
{
if(buffer->AsyncLoad(type, isIncidental ? 0 : STREAMING_BUFFERS * STREAM_BUFFER_SIZE ) == plSoundBuffer::kPending)
{
fPlayWhenLoaded = playWhenLoaded;
fLoading = true;
return plSoundBuffer::kPending;
}
}
char str[ 256 ];
strncpy( fSrcFilename, buffer->GetFileName(), sizeof( fSrcFilename ) );
bool streamCompressed = (buffer->HasFlag(plSoundBuffer::kStreamCompressed) != 0);
delete fDataStream;
fDataStream = buffer->GetAudioReader();
if(!fDataStream)
{
plAudioCore::ChannelSelect select = buffer->GetReaderSelect();
bool streamCompressed = (buffer->HasFlag(plSoundBuffer::kStreamCompressed) != 0);
/// Open da file
CHAR strPath[ MAX_PATH ];
_getcwd(strPath, MAX_PATH);
if(sfxPath)
strcat( strPath, "\\sfx\\" );
else
{
// if we've changing the filename don't append 'sfx', just append a '\' since a path to the folder is expected
strcat( strPath, "\\");
}
strcat( strPath, fSrcFilename );
fDataStream = plAudioFileReader::CreateReader(strPath, select,type);
}
if( fDataStream == nil || !fDataStream->IsValid() )
{
delete fDataStream;
fDataStream = nil;
return plSoundBuffer::kError;
}
sprintf( str, " Readied file %s for streaming", fSrcFilename );
IPrintDbgMessage( str );
// dont free sound data until we have a chance to use it in load sound
return fDataStream ? plSoundBuffer::kSuccess : plSoundBuffer::kError;
}
plStatusLog::AddLineS("audio.log", "EnsureLoadable failed for streaming sound %d", fDataBufferKey->GetName());
return plSoundBuffer::kError;
}
void plWin32StreamingSound::IFreeBuffers( void )
{
plWin32Sound::IFreeBuffers();
if( fLoadFromDiskOnDemand && !IsPropertySet( kPropLoadOnlyOnCall ) )
{
// if the audio system has just restarted, dont delete the datastream. This way we can pick up where we left off instead of restarting the sound.
if(!plgAudioSys::IsRestarting())
{
// we are deleting the stream, we must release the sound data.
FreeSoundData();
delete fDataStream;
fDataStream = nil;
}
fSrcFilename[ 0 ] = 0;
}
}
///////////////////////////////////////////////////////////////////
// Overload from plSound. Basically sets up the streaming file and fills the
// first half of our buffer. We'll fill the rest of the buffer as we get
// notifications for such.
hsBool plWin32StreamingSound::LoadSound( hsBool is3D )
{
if( fFailed )
return false;
if( !plgAudioSys::Active() || fDSoundBuffer )
return false;
if( fPriority > plgAudioSys::GetPriorityCutoff() )
return false; // Don't set the failed flag, just return
// Debug flag #1
if( is3D && fChannelSelect > 0 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableRightSelect ) )
{
// Force a fail
fFailed = true;
return false;
}
plSoundBuffer::ELoadReturnVal retVal = IPreLoadBuffer(true);
if(retVal == plSoundBuffer::kPending)
return true;
if( retVal == plSoundBuffer::kError )
{
char str[ 256 ];
sprintf( str, "Unable to open streaming source %s", fDataBufferKey->GetName() );
IPrintDbgMessage( str, true );
fFailed = true;
return false;
}
SetProperty( kPropIs3DSound, is3D );
plWAVHeader header = fDataStream->GetHeader();
UInt32 bufferSize = (UInt32)(fBufferLengthInSecs * header.fAvgBytesPerSec);
// Debug flag #2
if( is3D && fChannelSelect == 0 && header.fNumChannels > 1 && plgAudioSys::IsDebugFlagSet( plgAudioSys::kDisableLeftSelect ) )
{
// Force a fail
fFailed = true;
return false;
}
if( header.fNumChannels > 1 && is3D )
{
// We can only do a single channel of 3D sound. So copy over one (later)
bufferSize /= header.fNumChannels;
header.fBlockAlign /= header.fNumChannels;
header.fAvgBytesPerSec /= header.fNumChannels;
header.fNumChannels = 1;
}
// Actually create the buffer now (always looping)
fDSoundBuffer = TRACKED_NEW plDSoundBuffer( bufferSize, header, is3D, IsPropertySet(kPropLooping), false, true );
if( !fDSoundBuffer->IsValid() )
{
fDataStream->Close();
delete fDataStream;
fDataStream = nil;
delete fDSoundBuffer;
fDSoundBuffer = nil;
char str[256];
sprintf(str, "Can't create sound buffer for %s.wav. This could happen if the wav file is a stereo file. Stereo files are not supported on 3D sounds. If the file is not stereo then please report this error.", GetFileName());
IPrintDbgMessage( str, true );
fFailed = true;
return false;
}
fTotalBytes = (UInt32)bufferSize;
plProfile_NewMem(MemSounds, fTotalBytes);
plSoundBuffer *buffer = (plSoundBuffer *)fDataBufferKey->ObjectIsLoaded();
if(!buffer)
return false;
bool setupSource = true;
if(!buffer->GetData() || fStartPos)
{
if(fStartPos && fStartPos <= fDataStream->NumBytesLeft())
{
fDataStream->SetPosition(fStartPos);
plStatusLog::AddLineS("syncaudio.log", "startpos %d", fStartPos);
}
// if we get here we are not starting from the beginning of the sound. We still have an audio loaded and need to pick up where we left off
if(!fDSoundBuffer->SetupStreamingSource(fDataStream))
{
setupSource = false;
}
}
else
{
// this sound is starting from the beginning. Get the data and start it.
if(!fDSoundBuffer->SetupStreamingSource(buffer->GetData(), buffer->GetAsyncLoadLength()))
{
setupSource = false;
}
}
if(!setupSource)
{
fDataStream->Close();
delete fDataStream;
fDataStream = nil;
delete fDSoundBuffer;
fDSoundBuffer = nil;
plStatusLog::AddLineS("audio.log", "Could not play streaming sound, no voices left %s", GetKeyName());
return false;
}
FreeSoundData();
IRefreshEAXSettings( true );
// Debug info
char str[ 256 ];
sprintf( str, " Streaming %s.", fSrcFilename);
IPrintDbgMessage( str );
plStatusLog::AddLineS( "audioTimes.log", 0xffffffff, "Streaming %4.2f secs of %s", fDataStream->GetLengthInSecs(), GetKey()->GetUoid().GetObjectName() );
// Get pertinent info
SetLength( (hsScalar)fDataStream->GetLengthInSecs() );
// Set up our deswizzler, if necessary
delete fDeswizzler;
if( fDataStream->GetHeader().fNumChannels != header.fNumChannels )
fDeswizzler = TRACKED_NEW plSoundDeswizzler( (UInt32)(fBufferLengthInSecs * fDataStream->GetHeader().fAvgBytesPerSec),
(UInt8)(fDataStream->GetHeader().fNumChannels),
header.fBitsPerSample / 8 );
else
fDeswizzler = nil;
// LEAVE THE WAV FILE OPEN! (We *are* streaming, after all :)
return true;
}
void plWin32StreamingSound::IStreamUpdate()
{
if(!fReallyPlaying)
return;
if(hsTimer::GetMilliSeconds() - fLastStreamingUpdate < STREAMING_UPDATE_MS) // filter out update requests so we aren't doing this more that we need to
return;
plProfile_BeginTiming( StreamSndShoveTime );
if(fDSoundBuffer)
{
if(fDSoundBuffer->BuffersQueued() == 0 && fDataStream->NumBytesLeft() == 0 && !fDSoundBuffer->IsLooping())
{
// If we are fading out it's possible that we will hit this multiple times causing this sound to try and fade out multiple times, never allowing it to actually stop
if(!fStopping)
{
fStopping = true;
Stop();
plProfile_EndTiming( StreamSndShoveTime );
}
return;
}
if(!fDSoundBuffer->StreamingFillBuffer(fDataStream))
{
plStatusLog::AddLineS("audio.log", "%s Streaming buffer fill failed", GetKeyName());
}
}
plProfile_EndTiming( StreamSndShoveTime );
}
void plWin32StreamingSound::Update()
{
plWin32Sound::Update();
IStreamUpdate();
}
void plWin32StreamingSound::IDerivedActuallyPlay( void )
{
fStopping = false;
if( !fReallyPlaying )
{
for(;;)
{
if(fSynchedStartTimeSec)
{
// if we are synching to another sound this is our latency time
fDSoundBuffer->SetTimeOffsetSec((float)(hsTimer::GetSeconds() - fSynchedStartTimeSec));
fSynchedStartTimeSec = 0;
}
if(IsPropertySet(kPropIncidental))
{
if(fIncidentalsPlaying >= MAX_INCIDENTALS)
break;
++fIncidentalsPlaying;
}
fDSoundBuffer->Play();
fReallyPlaying = true;
break;
}
}
/// Send start callbacks
plSoundEvent *event = IFindEvent( plSoundEvent::kStart );
if( event != nil )
event->SendCallbacks();
}
void plWin32StreamingSound::IActuallyStop()
{
fStopping = false;
plWin32Sound::IActuallyStop();
if(fPlayWhenStopped)
{
fPlayWhenStopped = false;
Play();
}
}
unsigned plWin32StreamingSound::GetByteOffset()
{
if(fDataStream && fDSoundBuffer)
{
unsigned bytesQueued = fDSoundBuffer->BuffersQueued() * STREAM_BUFFER_SIZE;
unsigned offset = fDSoundBuffer->GetByteOffset();
long byteoffset = ((fDataStream->GetDataSize() - fDataStream->NumBytesLeft()) - bytesQueued) + offset;
return byteoffset < 0 ? fDataStream->GetDataSize() - abs(byteoffset) : byteoffset;
}
return 0;
}
float plWin32StreamingSound::GetActualTimeSec()
{
if(fDataStream && fDSoundBuffer)
return fDSoundBuffer->BytePosToMSecs(fDataStream->NumBytesLeft()) / 1000.0f;
return 0.0f;
}
void plWin32StreamingSound::SetStartPos(unsigned bytes)
{
fStartPos = bytes;
}
void plWin32StreamingSound::ISetActualTime( double t )
{
//fStartTimeSec = 0;
if(fDataStream && fDSoundBuffer)
fDataStream->SetPosition(fDSoundBuffer->GetBufferBytePos((float)t));
//else
// fStartTimeSec = t;
}
hsBool plWin32StreamingSound::MsgReceive( plMessage* pMsg )
{
return plWin32Sound::MsgReceive( pMsg );
}
void plWin32StreamingSound::IRemoveCallback( plEventCallbackMsg *pCBMsg )
{
plWin32Sound::IRemoveCallback( pCBMsg );
}
void plWin32StreamingSound::IAddCallback( plEventCallbackMsg *pCBMsg )
{
if( plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStop &&
plSoundEvent::GetTypeFromCallbackMsg( pCBMsg ) != plSoundEvent::kStart )
{
hsAssert( false, "Streaming sounds only support start and stop callbacks at this time." );
return;
}
plWin32Sound::IAddCallback( pCBMsg );
}

View File

@ -0,0 +1,106 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plWin32StreamingSound_h
#define plWin32StreamingSound_h
#include "plWin32Sound.h"
#include "../pnUtils/pnUtils.h"
class plDSoundBuffer;
class DSoundCallbackHandle;
class plAudioFileReader;
class plStreamingSoundThread;
enum CallbackHandleType;
class plSoundDeswizzler;
class plWin32StreamingSound : public plWin32Sound
{
public:
plWin32StreamingSound();
~plWin32StreamingSound();
CLASSNAME_REGISTER( plWin32StreamingSound );
GETINTERFACE_ANY( plWin32StreamingSound, plWin32Sound );
virtual void DeActivate();
virtual hsBool LoadSound( hsBool is3D );
virtual float GetActualTimeSec();
virtual unsigned GetByteOffset();
virtual StreamType GetStreamType() const { return fStreamType; }
virtual void SetFilename(const char *filename, bool isCompressed);
virtual void Update(); // temp
void StreamUpdate();
virtual hsBool MsgReceive( plMessage *pMsg );
protected:
hsScalar fTimeAtBufferStart;
plAudioFileReader *fDataStream;
hsScalar fBufferLengthInSecs;
UInt8 fBlankBufferFillCounter;
plSoundDeswizzler *fDeswizzler;
char fSrcFilename[ 256 ];
StreamType fStreamType;
bool fIsCompressed; // this applies only to the new sound file specified in fNewFilename, so we can play both ogg's and wav's
std::string fNewFilename; // allow the filename to be changed so we can play from a different source.
// ultimately this filename will be given to fDataBuffer, but since it's not always around we'll store it here
hsBool fStopping;
double fLastStreamingUpdate;
bool fPlayWhenStopped;
unsigned fStartPos;
hsScalar IGetTimeAtBufferStart( void ) { return fTimeAtBufferStart; }
virtual void SetStartPos(unsigned bytes);
virtual void IDerivedActuallyPlay( void );
void IActuallyStop();
virtual void ISetActualTime( double t );
virtual void IAddCallback( plEventCallbackMsg *pMsg );
virtual void IRemoveCallback( plEventCallbackMsg *pMsg );
virtual void IFreeBuffers( void );
void IStreamUpdate();
virtual plSoundBuffer::ELoadReturnVal IPreLoadBuffer( hsBool playWhenLoaded, hsBool isIncidental = false );
};
#endif //plWin32StreamingSound_h

View File

@ -0,0 +1,122 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plWin32VideoSound.h"
#include "hsResMgr.h"
#include "plDSoundBuffer.h"
static int uniqueID = 0;
plWin32VideoSound::plWin32VideoSound(const plWAVHeader& header) : plWin32Sound()
{
fCurrVolume = 1.0f;
fDesiredVol = 1.0f;
fSoftVolume = 1.0f;
fType = kGUISound;
fWAVHeader = header;
fDSoundBuffer = new plDSoundBuffer(0, fWAVHeader, false, false);
uniqueID++;
char keyName[32];
StrPrintf(keyName, arrsize(keyName), "VideoSound_%d", uniqueID);
hsgResMgr::ResMgr()->NewKey(keyName, this, plLocation::kGlobalFixedLoc);
}
plWin32VideoSound::~plWin32VideoSound()
{
if (fDSoundBuffer)
delete fDSoundBuffer;
}
void plWin32VideoSound::Play()
{
IActuallyPlay();
}
void plWin32VideoSound::Pause(bool on)
{
if (on)
fDSoundBuffer->Pause();
else if (!fReallyPlaying)
fDSoundBuffer->Play();
fReallyPlaying = !on;
}
void plWin32VideoSound::FillSoundBuffer(void* buffer, size_t size)
{
fDSoundBuffer->FillBuffer(buffer, size, &fWAVHeader);
fDSoundBuffer->SetScalarVolume(1.0f);
}
void plWin32VideoSound::IDerivedActuallyPlay()
{
if (!fReallyPlaying) {
fDSoundBuffer->Play();
fReallyPlaying = true;
}
}
hsBool plWin32VideoSound::LoadSound(hsBool is3D)
{
hsAssert(false, "unimplemented cause unnecessary for this class");
return false;
}
void plWin32VideoSound::SetStartPos(unsigned bytes)
{
//do nothing
hsAssert(false, "unimplemented cause unnecessary for this class");
}
float plWin32VideoSound::GetActualTimeSec()
{
hsAssert(false, "unimplemented cause unnecessary for this class");
return 0;
}
void plWin32VideoSound::ISetActualTime(double t)
{
hsAssert(false, "unimplemented cause unnecessary for this class");
}

View File

@ -0,0 +1,67 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plWin32VideoSound_h
#define plWin32VideoSound_h
#include "plWin32Sound.h"
class plWin32VideoSound : public plWin32Sound
{
public:
plWin32VideoSound(const plWAVHeader& header);
virtual ~plWin32VideoSound();
virtual void Play();
virtual void Pause(bool on);
void FillSoundBuffer(void* buffer, size_t size);
protected:
void IDerivedActuallyPlay();
hsBool LoadSound(hsBool is3D);
void SetStartPos(unsigned bytes);
float GetActualTimeSec();
void ISetActualTime(double t);
plWAVHeader fWAVHeader;
};
#endif

View File

@ -0,0 +1,377 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plWinMicLevel - Annoying class to deal with the annoying problem of //
// setting the microphone recording volume in Windows. //
// Yeah, you'd THINK there'd be some easier way... //
// //
//// Notes ///////////////////////////////////////////////////////////////////
// //
// 5.8.2001 - Created by mcn. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "plWinMicLevel.h"
#include "hsWindows.h"
#include <mmsystem.h>
//// Our Local Static Data ///////////////////////////////////////////////////
int sNumMixers = 0;
HMIXER sMixerHandle = nil;
MIXERCAPS sMixerCaps;
DWORD sMinValue = 0, sMaxValue = 0;
DWORD sVolControlID = 0;
//// Local Static Helpers ////////////////////////////////////////////////////
hsBool IGetMuxMicVolumeControl( void );
hsBool IGetBaseMicVolumeControl( void );
hsBool IGetControlValue( DWORD &value );
hsBool ISetControlValue( DWORD value );
MIXERLINE *IGetLineByType( DWORD type );
MIXERLINE *IGetLineByID( DWORD id );
MIXERCONTROL *IGetControlByType( MIXERLINE *line, DWORD type );
MIXERLINE *IGetMixerSubLineByType( MIXERCONTROL *mux, DWORD type );
//// The Publics /////////////////////////////////////////////////////////////
hsScalar plWinMicLevel::GetLevel( void )
{
if( !CanSetLevel() )
return -1;
DWORD rawValue;
if( !IGetControlValue( rawValue ) )
return -1;
return (hsScalar)( rawValue - sMinValue ) / (hsScalar)( sMaxValue - sMinValue );
}
void plWinMicLevel::SetLevel( hsScalar level )
{
if( !CanSetLevel() )
return;
DWORD rawValue = (DWORD)(( level * ( sMaxValue - sMinValue ) ) + sMinValue);
ISetControlValue( rawValue );
}
hsBool plWinMicLevel::CanSetLevel( void )
{
// Just to init
plWinMicLevel &instance = IGetInstance();
return ( sMixerHandle != nil ) ? true : false;
}
//// Protected Init Stuff ////////////////////////////////////////////////////
plWinMicLevel &plWinMicLevel::IGetInstance( void )
{
static plWinMicLevel sInstance;
return sInstance;
}
plWinMicLevel::plWinMicLevel()
{
sMixerHandle = nil;
memset( &sMixerCaps, 0, sizeof( sMixerCaps ) );
// Get the number of mixers in the system
sNumMixers = ::mixerGetNumDevs();
// So long as we have one, open the first one
if( sNumMixers == 0 )
return;
if( ::mixerOpen( &sMixerHandle, 0,
nil, // Window handle to receive callback messages
nil, MIXER_OBJECTF_MIXER ) != MMSYSERR_NOERROR )
{
sMixerHandle = nil; // Just to be sure
return;
}
if( ::mixerGetDevCaps( (UINT)sMixerHandle, &sMixerCaps, sizeof( sMixerCaps ) ) != MMSYSERR_NOERROR )
{
// Oh well, who cares
}
// Try to get the Mux/mixer-based mic volume control first, since that seems to work better/more often/at all
if( !IGetMuxMicVolumeControl() )
{
// Failed, so try getting the volume control from the base mic-in line
if( !IGetBaseMicVolumeControl() )
{
IShutdown();
return;
}
}
}
plWinMicLevel::~plWinMicLevel()
{
IShutdown();
}
void plWinMicLevel::IShutdown( void )
{
if( sMixerHandle != nil )
::mixerClose( sMixerHandle );
sMixerHandle = nil;
}
//// IGetMuxMicVolumeControl /////////////////////////////////////////////////
// Tries to get the volume control of the microphone subline of the MUX or
// mixer control of the WaveIn destination line of the audio system (whew!)
// Note: testing indcates that this works but the direct SRC_MICROPHONE
// doesn't, hence we try this one first.
hsBool IGetMuxMicVolumeControl( void )
{
if( sMixerHandle == nil )
return false;
// Get the WaveIn destination line
MIXERLINE *waveInLine = IGetLineByType( MIXERLINE_COMPONENTTYPE_DST_WAVEIN );
if( waveInLine == nil )
return false;
// Get the mixer or MUX controller from the line
MIXERCONTROL *control = IGetControlByType( waveInLine, MIXERCONTROL_CONTROLTYPE_MIXER );
if( control == nil )
control = IGetControlByType( waveInLine, MIXERCONTROL_CONTROLTYPE_MUX );
if( control == nil )
return false;
// Get the microphone sub-component
// Note: this eventually calls IGetLineByType(), which destroys the waveInLine pointer we had before
MIXERLINE *micLine = IGetMixerSubLineByType( control, MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE );
if( micLine == nil )
return false;
// Get the volume subcontroller
MIXERCONTROL *micVolCtrl = IGetControlByType( micLine, MIXERCONTROL_CONTROLTYPE_VOLUME );
if( micVolCtrl == nil )
return false;
// Found it! store our values
char *dbgLineName = micLine->szName;
char *dbgControlName = micVolCtrl->szName;
sMinValue = micVolCtrl->Bounds.dwMinimum;
sMaxValue = micVolCtrl->Bounds.dwMaximum;
sVolControlID = micVolCtrl->dwControlID;
return true;
}
//// IGetBaseMicVolumeControl ////////////////////////////////////////////////
// Tries to get the volume control of the mic-in line. See
// IGetMuxMicVolumeControl for why we don't do this one first.
hsBool IGetBaseMicVolumeControl( void )
{
if( sMixerHandle == nil )
return false;
// Get the mic source line
MIXERLINE *micLine = IGetLineByType( MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE );
if( micLine == nil )
return false;
// Get the volume subcontroller
MIXERCONTROL *micVolCtrl = IGetControlByType( micLine, MIXERCONTROL_CONTROLTYPE_VOLUME );
if( micVolCtrl == nil )
return false;
// Found it! store our values
char *dbgLineName = micLine->szName;
char *dbgControlName = micVolCtrl->szName;
sMinValue = micVolCtrl->Bounds.dwMinimum;
sMaxValue = micVolCtrl->Bounds.dwMaximum;
sVolControlID = micVolCtrl->dwControlID;
return true;
}
//// IGetControlValue ////////////////////////////////////////////////////////
// Gets the raw value of the current volume control.
hsBool IGetControlValue( DWORD &value )
{
if( sMixerHandle == nil )
return false;
MIXERCONTROLDETAILS_UNSIGNED mxcdVolume;
MIXERCONTROLDETAILS mxcd;
mxcd.cbStruct = sizeof( MIXERCONTROLDETAILS );
mxcd.dwControlID = sVolControlID;
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof( MIXERCONTROLDETAILS_UNSIGNED );
mxcd.paDetails = &mxcdVolume;
if( ::mixerGetControlDetails( (HMIXEROBJ)sMixerHandle, &mxcd,
MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
return false;
value = mxcdVolume.dwValue;
return true;
}
//// ISetControlValue ////////////////////////////////////////////////////////
// Sets the raw value of the current volume control.
hsBool ISetControlValue( DWORD value )
{
if( sMixerHandle == nil )
return false;
MIXERCONTROLDETAILS_UNSIGNED mxcdVolume = { value };
MIXERCONTROLDETAILS mxcd;
mxcd.cbStruct = sizeof( MIXERCONTROLDETAILS );
mxcd.dwControlID = sVolControlID;
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof( MIXERCONTROLDETAILS_UNSIGNED );
mxcd.paDetails = &mxcdVolume;
if( ::mixerSetControlDetails( (HMIXEROBJ)sMixerHandle, &mxcd,
MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE ) != MMSYSERR_NOERROR )
return false;
return true;
}
//// Helper Functions ////////////////////////////////////////////////////////
MIXERLINE *IGetLineByType( DWORD type )
{
static MIXERLINE mxl;
mxl.cbStruct = sizeof( MIXERLINE );
mxl.dwComponentType = type;
if( ::mixerGetLineInfo( (HMIXEROBJ)sMixerHandle, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE ) != MMSYSERR_NOERROR )
return nil;
return &mxl;
}
MIXERLINE *IGetLineByID( DWORD id )
{
static MIXERLINE mxl;
mxl.cbStruct = sizeof( MIXERLINE );
mxl.dwLineID = id;
if( ::mixerGetLineInfo( (HMIXEROBJ)sMixerHandle, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID ) != MMSYSERR_NOERROR )
return nil;
return &mxl;
}
MIXERCONTROL *IGetControlByType( MIXERLINE *line, DWORD type )
{
static MIXERCONTROL mxc;
MIXERLINECONTROLS mxlc;
mxlc.cbStruct = sizeof( MIXERLINECONTROLS );
mxlc.dwLineID = line->dwLineID;
mxlc.dwControlType = type;
mxlc.cControls = 1;
mxlc.cbmxctrl = sizeof( MIXERCONTROL );
mxlc.pamxctrl = &mxc;
if( ::mixerGetLineControls( (HMIXEROBJ)sMixerHandle, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE ) != MMSYSERR_NOERROR )
return nil;
return &mxc;
}
MIXERLINE *IGetMixerSubLineByType( MIXERCONTROL *mux, DWORD type )
{
// A mixer or MUX is really a combination of MORE lines. And beautifully, you can't
// just ask for a single one off of it, you have to ask for them all and search through yourself
MIXERCONTROLDETAILS_LISTTEXT *lineInfo = TRACKED_NEW MIXERCONTROLDETAILS_LISTTEXT[ mux->cMultipleItems ];
if( lineInfo == nil )
return nil;
MIXERCONTROLDETAILS details;
details.cbStruct = sizeof( MIXERCONTROLDETAILS );
details.dwControlID = mux->dwControlID;
details.cChannels = 1;
details.cMultipleItems = mux->cMultipleItems;
details.cbDetails = sizeof( MIXERCONTROLDETAILS_LISTTEXT );
details.paDetails = lineInfo;
if( ::mixerGetControlDetails( (HMIXEROBJ)sMixerHandle, &details, MIXER_OBJECTF_HMIXER | MIXER_GETCONTROLDETAILSF_LISTTEXT ) != MMSYSERR_NOERROR )
{
delete [] lineInfo;
return nil;
}
// Loop through and find the one with the right component type. But of course it doesn't give us that offhand...
for( unsigned int i = 0; i < mux->cMultipleItems; i++ )
{
MIXERLINE *line = IGetLineByID( lineInfo[ i ].dwParam1 );
if( line->dwComponentType == type )
{
delete [] lineInfo;
return line;
}
}
delete [] lineInfo;
return nil;
}

View File

@ -0,0 +1,80 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef _plWinMicLevel_h
#define _plWinMicLevel_h
//////////////////////////////////////////////////////////////////////////////
// //
// plWinMicLevel - Annoying class to deal with the annoying problem of //
// setting the microphone recording volume in Windows. //
// Yeah, you'd THINK there'd be some easier way... //
// //
//// Notes ///////////////////////////////////////////////////////////////////
// //
// 5.8.2001 - Created by mcn. //
// //
//////////////////////////////////////////////////////////////////////////////
//// Class Definition ////////////////////////////////////////////////////////
class plWinMicLevel
{
public:
~plWinMicLevel();
// Gets the microphone volume, range 0-1, -1 if error
static hsScalar GetLevel( void );
// Sets the microphone volume, range 0-1
static void SetLevel( hsScalar level );
// Returns whether we can set the level
static hsBool CanSetLevel( void );
protected:
plWinMicLevel(); // Protected constructor for IGetInstance. Just to init some stuff
static plWinMicLevel &IGetInstance( void );
void IShutdown( void );
};
#endif // _plWinMicLevel_h

View File

@ -0,0 +1,84 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plAudioCore - Core, common stuff that all of the audio system needs //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plAudioCore_h
#define _plAudioCore_h
//// plWAVHeader Class ///////////////////////////////////////////////////////
// Just a small info class about WAV sound
class plWAVHeader
{
public:
UInt16 fFormatTag;
UInt16 fNumChannels;
UInt32 fNumSamplesPerSec;
UInt32 fAvgBytesPerSec;
UInt16 fBlockAlign;
UInt16 fBitsPerSample;
enum
{
kPCMFormatTag = 1
};
};
//// plAudioCore Konstants ///////////////////////////////////////////////////
namespace plAudioCore
{
enum ChannelSelect
{
kAll = 0,
kLeft,
kRight
};
};
#endif //_plAudioCore_h

View File

@ -0,0 +1,52 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAudioCoreCreatable_inc
#define plAudioCoreCreatable_inc
#include "../pnFactory/plCreator.h"
#include "plSoundBuffer.h"
REGISTER_CREATABLE( plSoundBuffer );
#endif // plAudioCoreCreatable_inc

View File

@ -0,0 +1,185 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plAudioFileReader //
// //
//// Notes ///////////////////////////////////////////////////////////////////
// //
// 3.5.2001 - Created by mcn. //
// //
//////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include "hsTypes.h"
#include "plAudioFileReader.h"
#include "plAudioCore.h"
//#include "hsTimer.h"
#include "hsUtils.h"
#include "../plFile/hsFiles.h"
#include "../plFile/plFileUtils.h"
#include "../plUnifiedTime/plUnifiedTime.h"
#include "plBufferedFileReader.h"
#include "plFastWavReader.h"
#include "../plAudio/plOGGCodec.h"
#include "../plAudio/plWavFile.h"
#define kCacheDirName "streamingCache"
static void hsStrUpper(char *s)
{
if (s)
{
int len = hsStrlen(s);
for (int i = 0; i < len; i++)
s[i] = toupper(s[i]);
}
}
plAudioFileReader* plAudioFileReader::CreateReader(const char* path, plAudioCore::ChannelSelect whichChan, StreamType type)
{
const char* ext = plFileUtils::GetFileExt(path);
if (type == kStreamWAV)
{
bool isWav = (stricmp(ext, "wav") == 0);
// We want to stream a wav off disk, but this is a compressed file.
// Get the uncompressed path. Ignore the requested channel, since it
// will have already been split into two files if that is necessary.
if (!isWav)
{
char cachedPath[256];
IGetCachedPath(path, cachedPath, whichChan);
plAudioFileReader *r = TRACKED_NEW plFastWAV(cachedPath, plAudioCore::kAll);
return r;
}
plAudioFileReader *r = TRACKED_NEW plFastWAV(path, whichChan);
return r;
}
else if (type == kStreamRAM)
return TRACKED_NEW plBufferedFileReader(path, whichChan);
else if (type == kStreamNative)
return TRACKED_NEW plOGGCodec(path, whichChan);
return nil;
}
plAudioFileReader* plAudioFileReader::CreateWriter(const char* path, plWAVHeader& header)
{
const char* ext = plFileUtils::GetFileExt(path);
plAudioFileReader* writer = TRACKED_NEW CWaveFile(path, plAudioCore::kAll);
writer->OpenForWriting(path, header);
return writer;
}
void plAudioFileReader::IGetCachedPath(const char* path, char* cachedPath, plAudioCore::ChannelSelect whichChan)
{
// Get the file's path and add our streaming cache folder to it
strcpy(cachedPath, path);
plFileUtils::StripFile(cachedPath);
strcat(cachedPath, kCacheDirName"\\");
// Create the directory first
plFileUtils::CreateDir(cachedPath);
// Get the path to the cached version of the file, without the extension
const char* fileName = plFileUtils::GetFileName(path);
const char* fileExt = plFileUtils::GetFileExt(fileName);
strncat(cachedPath, fileName, fileExt-fileName-1);
if (whichChan == plAudioCore::kLeft)
strcat(cachedPath, "-Left.wav");
else if (whichChan == plAudioCore::kRight)
strcat(cachedPath, "-Right.wav");
else if (whichChan == plAudioCore::kAll)
strcat(cachedPath, ".wav");
}
void plAudioFileReader::ICacheFile(const char* path, bool noOverwrite, plAudioCore::ChannelSelect whichChan)
{
char cachedPath[256];
IGetCachedPath(path, cachedPath, whichChan);
if (!noOverwrite || !plFileUtils::FileExists(cachedPath))
{
plAudioFileReader* reader = plAudioFileReader::CreateReader(path, whichChan, kStreamNative);
if (!reader || !reader->IsValid())
{
delete reader;
return;
}
plAudioFileReader* writer = CreateWriter(cachedPath, reader->GetHeader());
if (!writer || !writer->IsValid())
{
delete reader;
delete writer;
return;
}
UInt8 buffer[4096];
UInt32 numLeft;
while ((numLeft = reader->NumBytesLeft()) > 0)
{
UInt32 toRead = (numLeft < sizeof(buffer)) ? numLeft : sizeof(buffer);
reader->Read(toRead, buffer);
writer->Write(toRead, buffer);
}
writer->Close();
delete writer;
delete reader;
}
}
void plAudioFileReader::CacheFile(const char* path, bool splitChannels, bool noOverwrite)
{
if (splitChannels)
{
ICacheFile(path, noOverwrite, plAudioCore::kLeft);
ICacheFile(path, noOverwrite, plAudioCore::kRight);
}
else
{
ICacheFile(path, noOverwrite, plAudioCore::kAll);
}
}

View File

@ -0,0 +1,99 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plAudioFileReader - Microsoft's way of making our lives difficult when //
// reading in .WMA files. Hacking Winamp's plugins is //
// probably easier... //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plAudioFileReader_h
#define _plAudioFileReader_h
#include "plAudioCore.h"
#include "hsTemplates.h"
//// Class Definition ////////////////////////////////////////////////////////
class plUnifiedTime;
class plWAVHeader;
class plAudioFileReader
{
public:
virtual ~plAudioFileReader() {}
virtual plWAVHeader &GetHeader( void ) = 0;
enum StreamType
{
kStreamRAM, // Stream from a WAV loaded into RAM
kStreamWAV, // Stream from a WAV on disk
kStreamNative, // Stream from the native type (ie, an Ogg on disk)
};
virtual void Open(){}
virtual void Close( void ) = 0;
virtual UInt32 GetDataSize( void ) = 0;
virtual float GetLengthInSecs( void ) = 0;
virtual hsBool SetPosition( UInt32 numBytes ) = 0;
virtual hsBool Read( UInt32 numBytes, void *buffer ) = 0;
virtual UInt32 NumBytesLeft( void ) = 0;
virtual hsBool OpenForWriting( const char *path, plWAVHeader &header ) { return false; }
virtual UInt32 Write( UInt32 bytes, void *buffer ) { return 0; }
virtual hsBool IsValid( void ) = 0;
static plAudioFileReader* CreateReader(const char* path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll, StreamType type = kStreamWAV);
static plAudioFileReader* CreateWriter(const char* path, plWAVHeader& header);
// Decompresses a compressed file to the cache directory
static void CacheFile(const char* path, bool splitChannels=false, bool noOverwrite=false);
protected:
static void IGetCachedPath(const char* path, char* cachedPath, plAudioCore::ChannelSelect whichChan);
static void ICacheFile(const char* path, bool noOverwrite, plAudioCore::ChannelSelect whichChan);
};
#endif //_plAudioFileReader_h

View File

@ -0,0 +1,182 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plBufferedFileReader - Reads in a given file into a RAM buffer, then //
// "reads" from that buffer as requested. Useless //
// for normal sounds, but perfect for streaming //
// from RAM. //
// //
//// Notes ///////////////////////////////////////////////////////////////////
// //
// 11.1.2002 - Created by mcn. //
// //
//////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "hsTypes.h"
#include "plBufferedFileReader.h"
//#include "plProfile.h"
//plProfile_CreateMemCounter( "BufferedRAM", "Sound", SndBufferedMem );
//// Constructor/Destructor //////////////////////////////////////////////////
plBufferedFileReader::plBufferedFileReader( const char *path, plAudioCore::ChannelSelect whichChan )
{
// Init some stuff
fBufferSize = 0;
fBuffer = nil;
fCursor = 0;
hsAssert( path != nil, "Invalid path specified in plBufferedFileReader" );
// Ask plAudioFileReader for another reader to get this file
// Note: have this reader do the chanSelect for us
plAudioFileReader *reader = plAudioFileReader::CreateReader( path, whichChan );
if( reader == nil || !reader->IsValid() )
{
delete reader;
IError( "Unable to open file to read in to RAM buffer" );
return;
}
fHeader = reader->GetHeader();
fBufferSize = reader->GetDataSize();
fBuffer = TRACKED_NEW UInt8[ fBufferSize ];
//plProfile_NewMem( SndBufferedMem, fBufferSize );
if( fBuffer == nil )
{
delete reader;
IError( "Unable to allocate RAM buffer" );
return;
}
if( !reader->Read( fBufferSize, fBuffer ) )
{
delete reader;
IError( "Unable to read file into RAM buffer" );
return;
}
// All done!
delete reader;
}
plBufferedFileReader::~plBufferedFileReader()
{
Close();
}
void plBufferedFileReader::Close( void )
{
//plProfile_DelMem( SndBufferedMem, fBufferSize );
DEL(fBuffer);;
fBuffer = nil;
fBufferSize = 0;
fCursor = 0;
}
void plBufferedFileReader::IError( const char *msg )
{
hsAssert( false, msg );
Close();
}
plWAVHeader &plBufferedFileReader::GetHeader( void )
{
hsAssert( IsValid(), "GetHeader() called on an invalid RAM buffer" );
return fHeader;
}
float plBufferedFileReader::GetLengthInSecs( void )
{
hsAssert( IsValid(), "GetLengthInSecs() called on an invalid RAM buffer" );
return (float)fBufferSize / (float)fHeader.fAvgBytesPerSec;
}
hsBool plBufferedFileReader::SetPosition( UInt32 numBytes )
{
hsAssert( IsValid(), "SetPosition() called on an invalid RAM buffer" );
if( numBytes > fBufferSize )
{
hsAssert( false, "Invalid position in SetPosition()" );
return false;
}
fCursor = numBytes;
return true;
}
hsBool plBufferedFileReader::Read( UInt32 numBytes, void *buffer )
{
hsAssert( IsValid(), "Read() called on an invalid RAM buffer" );
hsBool valid = true;
if( fCursor + numBytes > fBufferSize )
{
numBytes = fBufferSize - fCursor;
valid = false;
}
memcpy( buffer, fBuffer + fCursor, numBytes );
fCursor += numBytes;
return valid;
}
UInt32 plBufferedFileReader::NumBytesLeft( void )
{
hsAssert( IsValid(), "NumBytesLeft() called on an invalid RAM buffer" );
return fBufferSize - fCursor;
}

View File

@ -0,0 +1,82 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plBufferedFileReader - Reads in a given file into a RAM buffer, then //
// "reads" from that buffer as requested. Useless //
// for normal sounds, but perfect for streaming //
// from RAM. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plBufferedFileReader_h
#define _plBufferedFileReader_h
#include "../plAudioCore/plAudioFileReader.h"
//// Class Definition ////////////////////////////////////////////////////////
class plBufferedFileReader : public plAudioFileReader
{
public:
plBufferedFileReader( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll );
virtual ~plBufferedFileReader();
virtual plWAVHeader &GetHeader( void );
virtual void Close( void );
virtual UInt32 GetDataSize( void ) { return fBufferSize; }
virtual float GetLengthInSecs( void );
virtual hsBool SetPosition( UInt32 numBytes );
virtual hsBool Read( UInt32 numBytes, void *buffer );
virtual UInt32 NumBytesLeft( void );
virtual hsBool IsValid( void ) { return ( fBuffer != nil ) ? true : false; }
protected:
UInt32 fBufferSize;
UInt8 *fBuffer;
plWAVHeader fHeader;
UInt32 fCursor;
void IError( const char *msg );
};
#endif //_plBufferedFileReader_h

View File

@ -0,0 +1,350 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plFastWavReader - Quick, dirty, and highly optimized class for reading //
// in the samples of a WAV file when you're in a hurry. //
// ONLY WORKS WITH PCM (i.e. uncompressed) DATA //
// //
//// Notes ///////////////////////////////////////////////////////////////////
// //
// 11.5.2001 - Created by mcn. //
// //
//////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "hsTypes.h"
#include "plFastWavReader.h"
//// Local Helpers ///////////////////////////////////////////////////////////
class plRIFFChunk
{
public:
char fID[ 4 ];
UInt32 fSize;
void Read( FILE *fp )
{
fread( fID, 1, 4, fp );
fread( &fSize, sizeof( UInt32 ), 1, fp );
}
bool IsA( const char *type )
{
return ( memcmp( fID, type, 4 ) == 0 ) ? true : false;
}
};
class plRIFFHeader
{
protected:
plRIFFChunk fChunk;
bool fValid;
char fFormat[ 4 ];
public:
plRIFFHeader( FILE *fp )
{
fValid = false;
fChunk.Read( fp );
if( fChunk.IsA( "RIFF" ) )
{
if( fread( &fFormat, 1, 4, fp ) == 4 )
{
fValid = true;
}
}
}
bool IsValid( void )
{
return fValid;
}
bool IsA( const char *type )
{
return ( memcmp( fFormat, type, 4 ) == 0 ) ? true : false;
}
};
//// Constructor/Destructor //////////////////////////////////////////////////
plFastWAV::plFastWAV( const char *path, plAudioCore::ChannelSelect whichChan ) : fFileHandle( nil )
{
hsAssert( path != nil, "Invalid path specified in plFastWAV reader" );
strncpy( fFilename, path, sizeof( fFilename ) );
fWhichChannel = whichChan;
fFileHandle = fopen( path, "rb" );
if( fFileHandle != nil )
{
/// Read in our header and calc our start position
plRIFFHeader riffHdr( fFileHandle );
if( !riffHdr.IsValid() )
{
IError( "Invalid RIFF file header in plFastWAV" );
return;
}
if( !riffHdr.IsA( "WAVE" ) )
{
IError( "Invalid RIFF file type in plFastWAV" );
return;
}
fChunkStart = ftell( fFileHandle );
// Seek and read the "fmt " header
plRIFFChunk chunk;
if( !ISeekToChunk( "fmt ", &chunk ) )
{
IError( "Unable to find fmt chunk in WAV file" );
return;
}
if( fread( &fHeader, 1, sizeof( plWAVHeader ), fFileHandle ) != sizeof( plWAVHeader ) )
{
IError( "Invalid WAV file header in plFastWAV" );
return;
}
// Check format
if( fHeader.fFormatTag != kPCMFormatTag )
{
IError( "Invalid format in plFastWAV" );
return;
}
// Seek to and get the position of the data chunk
if( !ISeekToChunk( "data", &chunk ) )
{
IError( "Unable to find data chunk in WAV file" );
return;
}
fDataStartPos = ftell( fFileHandle );
fDataSize = chunk.fSize;
// HACKY FIX FOR BAD WAV FILES
fDataSize -= ( fDataSize & ( fHeader.fBlockAlign - 1 ) );
if( fWhichChannel != plAudioCore::kAll )
{
fChannelAdjust = 2;
fChannelOffset = ( fWhichChannel == plAudioCore::kLeft ) ? 0 : 1;
}
else
{
fChannelAdjust = 1;
fChannelOffset = 0;
}
fFakeHeader = fHeader;
fFakeHeader.fAvgBytesPerSec /= fChannelAdjust;
fFakeHeader.fNumChannels /= (UInt16)fChannelAdjust;
fFakeHeader.fBlockAlign /= (UInt16)fChannelAdjust;
SetPosition( 0 );
// fCurrDataPos = 0;
}
}
plFastWAV::~plFastWAV()
{
if( fFileHandle != nil )
fclose( fFileHandle );
}
bool plFastWAV::ISeekToChunk( const char *type, plRIFFChunk *c )
{
plRIFFChunk chunk;
// Start from chunk start and search through all the, well, chunks :)
fseek( fFileHandle, fChunkStart, SEEK_SET );
while( !feof( fFileHandle ) )
{
chunk.Read( fFileHandle );
if( chunk.IsA( type ) )
{
*c = chunk;
return true;
}
// Seek past this one
fseek( fFileHandle, chunk.fSize, SEEK_CUR );
}
return false;
}
void plFastWAV::Open()
{
if(fFileHandle)
return;
fFileHandle = fopen( fFilename, "rb" );
if(!fFileHandle)
return;
fCurrDataPos = 0;
fseek( fFileHandle, fDataStartPos, SEEK_SET );
}
void plFastWAV::Close( void )
{
if( fFileHandle != nil )
{
fclose( fFileHandle );
fFileHandle = nil;
}
}
void plFastWAV::IError( const char *msg )
{
hsAssert( false, msg );
Close();
}
plWAVHeader &plFastWAV::GetHeader( void )
{
hsAssert( IsValid(), "GetHeader() called on an invalid WAV file" );
return fFakeHeader;
}
float plFastWAV::GetLengthInSecs( void )
{
hsAssert( IsValid(), "GetLengthInSecs() called on an invalid WAV file" );
return (float)( fDataSize / fChannelAdjust ) / (float)fHeader.fAvgBytesPerSec;
}
hsBool plFastWAV::SetPosition( UInt32 numBytes )
{
hsAssert( IsValid(), "GetHeader() called on an invalid WAV file" );
fCurrDataPos = numBytes * fChannelAdjust + ( fChannelOffset * fHeader.fBlockAlign / fChannelAdjust );
hsAssert( fCurrDataPos <= fDataSize, "Invalid new position while seeking WAV file" );
return ( fseek( fFileHandle, fDataStartPos + fCurrDataPos, SEEK_SET ) == 0 ) ? true : false;
}
hsBool plFastWAV::Read( UInt32 numBytes, void *buffer )
{
hsAssert( IsValid(), "GetHeader() called on an invalid WAV file" );
if( fWhichChannel != plAudioCore::kAll )
{
size_t numRead, sampleSize = fHeader.fBlockAlign / fChannelAdjust;
static UInt8 trashBuffer[ 32 ];
UInt32 numBytesFull = numBytes;
if( fCurrDataPos + ( numBytes * fChannelAdjust ) > fDataSize )
numBytesFull -= sampleSize;
for( numRead = 0; numRead < numBytesFull; numRead += sampleSize )
{
size_t thisRead = fread( buffer, 1, sampleSize, fFileHandle );
if( thisRead != sampleSize )
return false;
// fseek( fFileHandle, sampleSize * ( fChannelAdjust - 1 ), SEEK_CUR );
thisRead = fread( trashBuffer, 1, sampleSize, fFileHandle );
if( thisRead != sampleSize )
return false;
buffer = (void *)( (UInt8 *)buffer + sampleSize );
fCurrDataPos += sampleSize * fChannelAdjust;
}
if( numRead < numBytes )
{
if( numBytes - numRead > sampleSize )
{
hsAssert( false, "Invalid logic in plFastWAV::Read()" );
return false;
}
// Must not have enough room left, so no more skipping
size_t thisRead = fread( buffer, 1, sampleSize, fFileHandle );
if( thisRead != sampleSize )
return false;
buffer = (void *)( (UInt8 *)buffer + sampleSize );
fCurrDataPos += sampleSize;
}
hsAssert( fCurrDataPos <= fDataSize, "Invalid new position while reading WAV file" );
}
else
{
size_t numRead = fread( buffer, 1, numBytes, fFileHandle );
fCurrDataPos += numRead;
hsAssert( fCurrDataPos <= fDataSize, "Invalid new position while reading WAV file" );
if( numRead < numBytes )
return false;
}
return true;
}
UInt32 plFastWAV::NumBytesLeft( void )
{
hsAssert( IsValid(), "GetHeader() called on an invalid WAV file" );
hsAssert( fCurrDataPos <= fDataSize, "Invalid current position while reading WAV file" );
return ( fDataSize - fCurrDataPos ) / fChannelAdjust;
}

View File

@ -0,0 +1,98 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plFastWavReader - Quick, dirty, and highly optimized class for reading //
// in the samples of a WAV file when you're in a hurry. //
// ONLY WORKS WITH PCM (i.e. uncompressed) DATA //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plFastWavReader_h
#define _plFastWavReader_h
#include "plAudioFileReader.h"
//// Class Definition ////////////////////////////////////////////////////////
class plRIFFChunk;
class plFastWAV : public plAudioFileReader
{
public:
plFastWAV( const char *path, plAudioCore::ChannelSelect whichChan = plAudioCore::kAll );
virtual ~plFastWAV();
virtual plWAVHeader &GetHeader( void );
virtual void Open();
virtual void Close( void );
virtual UInt32 GetDataSize( void ) { return fDataSize / fChannelAdjust; }
virtual float GetLengthInSecs( void );
virtual hsBool SetPosition( UInt32 numBytes );
virtual hsBool Read( UInt32 numBytes, void *buffer );
virtual UInt32 NumBytesLeft( void );
virtual hsBool IsValid( void ) { return ( fFileHandle != nil ) ? true : false; }
protected:
enum
{
kPCMFormatTag = 1
};
char fFilename[ 512 ];
FILE * fFileHandle;
plWAVHeader fHeader, fFakeHeader;
UInt32 fDataStartPos, fCurrDataPos, fDataSize;
UInt32 fChunkStart;
plAudioCore::ChannelSelect fWhichChannel;
UInt32 fChannelAdjust, fChannelOffset;
void IError( const char *msg );
bool ISeekToChunk( const char *type, plRIFFChunk *c );
};
#endif //_plFastWavReader_h

View File

@ -0,0 +1,581 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include <process.h>
#include "hsTypes.h"
#include "plSoundBuffer.h"
#include "hsStream.h"
#include "hsUtils.h"
#include "plgDispatch.h"
#include "hsResMgr.h"
#include "../pnMessage/plRefMsg.h"
#include "../plFile/plFileUtils.h"
#include "../plFile/hsFiles.h"
#include "../plUnifiedTime/plUnifiedTime.h"
#include "../pnUtils/pnUtils.h"
#include "../plStatusLog/plStatusLog.h"
#include "hsTimer.h"
static bool s_running;
static LISTDECL(plSoundBuffer, link) s_loading;
static CCritSect s_critsect;
static CEvent s_event(kEventAutoReset);
static void GetFullPath( const char filename[], char *destStr )
{
char path[ kFolderIterator_MaxPath ];
if( strchr( filename, '\\' ) != nil )
strcpy( path, filename );
else
sprintf( path, "sfx\\%s", filename );
strcpy( destStr, path );
}
//// IGetReader //////////////////////////////////////////////////////////////
// Makes sure the sound is ready to load without any extra processing (like
// decompression or the like), then opens a reader for it.
// fullpath tells the function whether to append 'sfx' to the path or not (we don't want to do this if were providing the full path)
static plAudioFileReader *CreateReader( hsBool fullpath, const char filename[], plAudioFileReader::StreamType type, plAudioCore::ChannelSelect channel )
{
char path[512];
if(fullpath) GetFullPath(filename, path);
else
strcpy(path, filename);
plAudioFileReader* reader = plAudioFileReader::CreateReader(path, channel, type);
if( reader == nil || !reader->IsValid() )
{
delete reader;
return nil;
}
return reader;
}
// our loading thread
static void LoadCallback(void *)
{
LISTDECL(plSoundBuffer, link) templist;
while(s_running)
{
s_critsect.Enter();
{
while(plSoundBuffer *buffer = s_loading.Head())
{
templist.Link(buffer);
}
}
s_critsect.Leave();
if(!templist.Head())
{
s_event.Wait(kEventWaitForever);
}
else
{
plAudioFileReader *reader = nil;
while(plSoundBuffer *buffer = templist.Head())
{
if(buffer->GetData())
{
reader = CreateReader(true, buffer->GetFileName(), buffer->GetAudioReaderType(), buffer->GetReaderSelect());
if( reader )
{
unsigned readLen = buffer->GetAsyncLoadLength() ? buffer->GetAsyncLoadLength() : buffer->GetDataLength();
reader->Read( readLen, buffer->GetData() );
buffer->SetAudioReader(reader); // give sound buffer reader, since we may need it later
}
else
buffer->SetError();
}
templist.Unlink(buffer);
buffer->SetLoaded(true);
}
}
}
// we need to be sure that all buffers are removed from our load list when shutting this thread down or we will hang,
// since the sound buffer will wait to be destroyed until it is marked as loaded
s_critsect.Enter();
{
while(plSoundBuffer *buffer = s_loading.Head())
{
buffer->SetLoaded(true);
s_loading.Unlink(buffer);
}
}
s_critsect.Leave();
}
void plSoundBuffer::Init()
{
s_running = true;
_beginthread(LoadCallback, 0, 0);
}
void plSoundBuffer::Shutdown()
{
s_running = false;
s_event.Signal();
}
//// Constructor/Destructor //////////////////////////////////////////////////
plSoundBuffer::plSoundBuffer()
{
IInitBuffer();
}
plSoundBuffer::plSoundBuffer( const char *fileName, UInt32 flags )
{
IInitBuffer();
SetFileName( fileName );
fFlags = flags;
fValid = IGrabHeaderInfo();
}
plSoundBuffer::~plSoundBuffer()
{
// if we are loading a sound we need to wait for the loading thread to be completely done processing this buffer.
// otherwise it may try to access this buffer after it's been deleted
if(fLoading)
{
while(!fLoaded)
{
Sleep(10);
}
}
ASSERT(!link.IsLinked());
delete [] fFileName;
UnLoad();
}
void plSoundBuffer::IInitBuffer()
{
fError = false;
fValid = false;
fFileName = nil;
fData = nil;
fDataLength = 0;
fFlags = 0;
fDataRead = 0;
fReader = nil;
fLoaded = 0;
fLoading = false;
fHeader.fFormatTag = 0;
fHeader.fNumChannels = 0;
fHeader.fNumSamplesPerSec = 0;
fHeader.fAvgBytesPerSec = 0;
fHeader.fBlockAlign = 0;
fHeader.fBitsPerSample = 0;
}
//// GetDataLengthInSecs /////////////////////////////////////////////////////
hsScalar plSoundBuffer::GetDataLengthInSecs( void ) const
{
return (hsScalar)fDataLength / (hsScalar)fHeader.fAvgBytesPerSec;
}
//// Read/Write //////////////////////////////////////////////////////////////
void plSoundBuffer::Read( hsStream *s, hsResMgr *mgr )
{
hsKeyedObject::Read( s, mgr );
s->ReadSwap( &fFlags );
s->ReadSwap( &fDataLength );
fFileName = s->ReadSafeString();
s->ReadSwap( &fHeader.fFormatTag );
s->ReadSwap( &fHeader.fNumChannels );
s->ReadSwap( &fHeader.fNumSamplesPerSec );
s->ReadSwap( &fHeader.fAvgBytesPerSec );
s->ReadSwap( &fHeader.fBlockAlign );
s->ReadSwap( &fHeader.fBitsPerSample );
fValid = false;
if( !( fFlags & kIsExternal ) )
{
fData = TRACKED_NEW UInt8[ fDataLength ];
if( fData == nil )
fFlags |= kIsExternal;
else
{
s->Read( fDataLength, fData );
fValid = true;
SetLoaded(true);
}
}
else
{
fData = nil;
// fValid = EnsureInternal();
fValid = true;
}
}
void plSoundBuffer::Write( hsStream *s, hsResMgr *mgr )
{
hsKeyedObject::Write( s, mgr );
if( fData == nil )
fFlags |= kIsExternal;
s->WriteSwap( fFlags );
s->WriteSwap( fDataLength );
// Truncate the path to just a file name on write
if( fFileName != nil )
{
char *nameOnly = strrchr( fFileName, '\\' );
if( nameOnly != nil )
s->WriteSafeString( nameOnly + 1 );
else
s->WriteSafeString( fFileName );
}
else
s->WriteSafeString( nil );
s->WriteSwap( fHeader.fFormatTag );
s->WriteSwap( fHeader.fNumChannels );
s->WriteSwap( fHeader.fNumSamplesPerSec );
s->WriteSwap( fHeader.fAvgBytesPerSec );
s->WriteSwap( fHeader.fBlockAlign );
s->WriteSwap( fHeader.fBitsPerSample );
if( !( fFlags & kIsExternal ) )
s->Write( fDataLength, fData );
}
//// SetFileName /////////////////////////////////////////////////////////////
void plSoundBuffer::SetFileName( const char *name )
{
if(fLoading)
{
hsAssert(false, "Unable to set SoundBuffer filename");
return;
}
delete [] fFileName;
if( name != nil )
fFileName = hsStrcpy( name );
else
fFileName = nil;
// Data is no longer valid
UnLoad();
}
//// GetReaderSelect /////////////////////////////////////////////////////////
// Translates our flags into the ChannelSelect enum for plAudioFileReader
plAudioCore::ChannelSelect plSoundBuffer::GetReaderSelect( void ) const
{
if( fFlags & kOnlyLeftChannel )
return plAudioCore::kLeft;
else if( fFlags & kOnlyRightChannel )
return plAudioCore::kRight;
else
return plAudioCore::kAll;
}
//// IGetFullPath ////////////////////////////////////////////////////////////
// Construct our current full path to our sound.
void plSoundBuffer::IGetFullPath( char *destStr )
{
if(!fFileName)
{
*destStr = 0;
return;
}
char path[ kFolderIterator_MaxPath ];
if( strchr( fFileName, '\\' ) != nil )
strcpy( path, fFileName );
else
sprintf( path, "sfx\\%s", fFileName );
strcpy( destStr, path );
}
//============================================================================
// Asyncload will queue up a buffer for loading in our loading list the first time it is called.
// It will load in "length" number of bytes, if length is non zero. If length is zero the entire file will be loaded
// When called subsequent times it will check to see if the data has been loaded.
// Returns kPending while still loading the file. Returns kSuccess when the data has been loaded.
// While a file is loading(fLoading == true, and fLoaded == false) a buffer, no paremeters of the buffer should be modified.
plSoundBuffer::ELoadReturnVal plSoundBuffer::AsyncLoad(plAudioFileReader::StreamType type, unsigned length /* = 0 */ )
{
if(!s_running)
return kError; // we cannot load the data since the load thread is no longer running
if(!fLoading && !fLoaded)
{
fAsyncLoadLength = length;
fStreamType = type;
if(fData == nil )
{
fData = TRACKED_NEW UInt8[ fAsyncLoadLength ? fAsyncLoadLength : fDataLength ];
if( fData == nil )
return kError;
}
s_critsect.Enter();
{
fLoading = true;
s_loading.Link(this);
}
s_critsect.Leave();
s_event.Signal();
}
if(fLoaded)
{
if(fLoading) // ensures we only do this stuff one time
{
ELoadReturnVal retVal = kSuccess;
if(fError)
{
retVal = kError;
fError = false;
}
if(fReader)
{
fHeader = fReader->GetHeader();
SetDataLength(fReader->GetDataSize());
}
fFlags &= ~kIsExternal;
fLoading = false;
return retVal;
}
return kSuccess;
}
return kPending;
}
//// ForceNonInternal ////////////////////////////////////////////////////////
// destroys loaded, and frees data
void plSoundBuffer::UnLoad( void )
{
if(fLoaded)
int i = 0;
if(fLoading)
return;
if(fReader)
fReader->Close();
delete fReader;
fReader = nil;
delete [] fData;
fData = nil;
SetLoaded(false);
fFlags |= kIsExternal;
}
//// IRoundDataPos ///////////////////////////////////////////////////////////
void plSoundBuffer::RoundDataPos( UInt32 &pos )
{
UInt32 extra = pos & ( fHeader.fBlockAlign - 1 );
pos -= extra;
}
// transfers ownership to caller
plAudioFileReader *plSoundBuffer::GetAudioReader()
{
plAudioFileReader * reader = fReader;
fReader = nil;
return reader;
}
// WARNING: called by the loader thread(only)
// the reader will be handed off for later use. This is useful for streaming sound if we want to load the first chunk of data
// and the continue streaming the file from disk.
void plSoundBuffer::SetAudioReader(plAudioFileReader *reader)
{
if(fReader)
fReader->Close();
delete fReader;
fReader = reader;
}
void plSoundBuffer::SetLoaded(bool loaded)
{
fLoaded = loaded;
}
/*****************************************************************************
*
* for plugins only
*
***/
//// SetInternalData /////////////////////////////////////////////////////////
void plSoundBuffer::SetInternalData( plWAVHeader &header, UInt32 length, UInt8 *data )
{
if(fLoading) return;
fFileName = nil;
fHeader = header;
fFlags = 0;
fDataLength = length;
fData = TRACKED_NEW UInt8[ length ];
memcpy( fData, data, length );
fValid = true;
}
//// EnsureInternal //////////////////////////////////////////////////////////
// for plugins only
plSoundBuffer::ELoadReturnVal plSoundBuffer::EnsureInternal()
{
if( fData == nil )
{
fData = TRACKED_NEW UInt8[fDataLength ];
if( fData == nil )
return kError;
}
if(!fReader)
fReader = IGetReader(true);
//else
// fReader->Open();
if( fReader == nil )
return kError;
unsigned readLen = fDataLength;
if( !fReader->Read( readLen, fData ) )
{
delete [] fData;
fData = nil;
return kError;
}
if(fReader)
{
fReader->Close();
delete fReader;
fReader = nil;
}
return kSuccess;
}
//// IGrabHeaderInfo /////////////////////////////////////////////////////////
hsBool plSoundBuffer::IGrabHeaderInfo( void )
{
static char path[ 512 ];
if( fFileName != nil )
{
IGetFullPath( path );
// Go grab from the WAV file
if(!fReader)
{
fReader = plAudioFileReader::CreateReader(path, GetReaderSelect(), plAudioFileReader::kStreamNative);
if( fReader == nil || !fReader->IsValid() )
{
delete fReader;
fReader = nil;
return false;
}
}
fHeader = fReader->GetHeader();
fDataLength = fReader->GetDataSize();
RoundDataPos( fDataLength );
fReader->Close();
delete fReader;
fReader = nil;
}
return true;
}
//// IGetReader //////////////////////////////////////////////////////////////
// Makes sure the sound is ready to load without any extra processing (like
// decompression or the like), then opens a reader for it.
// fullpath tells the function whether to append 'sfx' to the path or not (we don't want to do this if were providing the full path)
plAudioFileReader *plSoundBuffer::IGetReader( hsBool fullpath )
{
char path[512];
if(fullpath) IGetFullPath(path);
else
strcpy(path, fFileName);
// Go grab from the WAV file
plAudioFileReader::StreamType type = plAudioFileReader::kStreamWAV;
if (HasFlag(kStreamCompressed))
type = plAudioFileReader::kStreamNative;
plAudioFileReader* reader = plAudioFileReader::CreateReader(path, GetReaderSelect(), type);
if( reader == nil || !reader->IsValid() )
{
delete reader;
return nil;
}
fHeader = reader->GetHeader();
fDataLength = reader->GetDataSize();
RoundDataPos( fDataLength );
return reader;
}

View File

@ -0,0 +1,162 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plSoundBuffer - Quick, dirty, and highly optimized class for reading //
// in the samples of a WAV file when you're in a hurry. //
// ONLY WORKS WITH PCM (i.e. uncompressed) DATA //
// //
// Now loads data asynchronously. When fLoading is true //
// do not touch any data in the soundbuffer //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plSoundBuffer_h
#define _plSoundBuffer_h
#include "../pnKeyedObject/hsKeyedObject.h"
#include "plAudioCore.h"
#include "plAudioFileReader.h"
#include "../pnUtils/pnUtils.h"
//// Class Definition ////////////////////////////////////////////////////////
class plUnifiedTime;
class plAudioFileReader;
class plSoundBuffer : public hsKeyedObject
{
public:
plSoundBuffer();
plSoundBuffer( const char *fileName, UInt32 flags = 0 );
~plSoundBuffer();
CLASSNAME_REGISTER( plSoundBuffer );
GETINTERFACE_ANY( plSoundBuffer, hsKeyedObject );
LINK(plSoundBuffer) link;
enum Flags
{
kIsExternal = 0x0001,
kAlwaysExternal = 0x0002,
kOnlyLeftChannel = 0x0004,
kOnlyRightChannel = 0x0008,
kStreamCompressed = 0x0010,
};
enum ELoadReturnVal
{
kSuccess,
kError,
kPending,
};
void RoundDataPos( UInt32 &pos );
virtual void Read( hsStream *s, hsResMgr *mgr );
virtual void Write( hsStream *s, hsResMgr *mgr );
plWAVHeader &GetHeader( void ) { return fHeader; }
UInt32 GetDataLength( void ) const { return fDataLength; }
void SetDataLength(unsigned length) { fDataLength = length; }
void *GetData( void ) const { return fData; }
const char *GetFileName( void ) const { return fFileName; }
hsBool IsValid( void ) const { return fValid; }
hsScalar GetDataLengthInSecs( void ) const;
void SetFileName( const char *name );
hsBool HasFlag( UInt32 flag ) { return ( fFlags & flag ) ? true : false; }
void SetFlag( UInt32 flag, hsBool yes = true ) { if( yes ) fFlags |= flag; else fFlags &= ~flag; }
// Must be called until return value is kSuccess. starts an asynchronous load first time called. returns kSuccess when finished.
ELoadReturnVal AsyncLoad( plAudioFileReader::StreamType type, unsigned length = 0 );
void UnLoad( );
plAudioCore::ChannelSelect GetReaderSelect( void ) const;
static void Init();
static void Shutdown();
plAudioFileReader * GetAudioReader(); // transfers ownership to caller
void SetAudioReader(plAudioFileReader *reader);
void SetLoaded(bool loaded);
plAudioFileReader::StreamType GetAudioReaderType() { return fStreamType; }
unsigned GetAsyncLoadLength() { return fAsyncLoadLength ? fAsyncLoadLength : fDataLength; }
// for plugins only
void SetInternalData( plWAVHeader &header, UInt32 length, UInt8 *data );
ELoadReturnVal EnsureInternal( );
void SetError() { fError = true; }
protected:
// plSoundBuffers can be two ways--they can either have a filename and no
// data, in which case they reference a file in the sfx folder, or they
// can store the data directly
void IInitBuffer();
hsBool IGrabHeaderInfo( void );
void IAddBuffers( void *base, void *toAdd, UInt32 lengthInBytes, UInt8 bitsPerSample );
void IGetFullPath( char *destStr );
UInt32 fFlags;
hsBool fValid;
UInt32 fDataRead;
char *fFileName;
bool fLoaded;
bool fLoading;
bool fError;
plAudioFileReader * fReader;
UInt8 * fData;
plWAVHeader fHeader;
UInt32 fDataLength;
UInt32 fAsyncLoadLength;
plAudioFileReader::StreamType fStreamType;
// for plugins only
plAudioFileReader *IGetReader( hsBool fullpath );
};
#endif //_plSoundBuffer_h

View File

@ -0,0 +1,97 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plSoundDeswizzler - Quick helper class to extract a single channel of //
// data from stereo (or more)-channel data. //
// //
//////////////////////////////////////////////////////////////////////////////
#include "hsTypes.h"
#include "plSoundDeswizzler.h"
#include <string.h>
plSoundDeswizzler::plSoundDeswizzler( void *srcPtr, UInt32 srcLength, UInt8 numChannels, UInt32 sampleSize )
{
fNumSamples = srcLength / sampleSize;
fSampleSize = sampleSize;
fStride = fSampleSize * numChannels;
fData = (UInt8 *)srcPtr;
fOwnsData = false;
}
plSoundDeswizzler::plSoundDeswizzler( UInt32 srcLength, UInt8 numChannels, UInt32 sampleSize )
{
fNumSamples = srcLength / sampleSize;
fSampleSize = sampleSize;
fStride = fSampleSize * numChannels;
fData = TRACKED_NEW UInt8[ srcLength ];
fOwnsData = true;
}
plSoundDeswizzler::~plSoundDeswizzler()
{
if( fOwnsData )
delete [] fData;
}
void plSoundDeswizzler::Extract( UInt8 channelSelect, void *dest, UInt32 numBytesToProcess )
{
UInt8 *srcPtr = fData + channelSelect * fSampleSize;
UInt8 *destPtr = (UInt8 *)dest;
UInt32 i;
if( numBytesToProcess == 0 )
numBytesToProcess = fNumSamples;
else
numBytesToProcess /= fStride;
// Extract!
for( i = 0; i < numBytesToProcess; i++ )
{
memcpy( destPtr, srcPtr, fSampleSize );
destPtr += fSampleSize;
srcPtr += fStride;
}
}

View File

@ -0,0 +1,73 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
//////////////////////////////////////////////////////////////////////////////
// //
// plSoundDeswizzler - Quick helper class to extract a single channel of //
// data from stereo (or more)-channel data. //
// //
//////////////////////////////////////////////////////////////////////////////
#ifndef _plSoundDeswizzler_h
#define _plSoundDeswizzler_h
#include "hsTypes.h"
//// Class Definition ////////////////////////////////////////////////////////
class plSoundDeswizzler
{
public:
plSoundDeswizzler( void *srcPtr, UInt32 srcLength, UInt8 numChannels, UInt32 sampleSize );
plSoundDeswizzler( UInt32 srcLength, UInt8 numChannels, UInt32 sampleSize );
~plSoundDeswizzler();
void *GetSourceBuffer( void ) const { return fData; }
void Extract( UInt8 channelSelect, void *destPtr, UInt32 numBytesToProcess = 0 );
protected:
UInt8 *fData;
UInt32 fNumSamples, fSampleSize, fStride;
hsBool fOwnsData;
};
#endif //_plSoundDeswizzler_h

View File

@ -0,0 +1,782 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
// singular
#include "plAGAnim.h"
// local
#include "plMatrixChannel.h"
// global
#include "hsResMgr.h"
// other
#include "../plMessage/plAnimCmdMsg.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// STATICS
//
/////////////////////////////////////////////////////////////////////////////////////////
plAGAnim::plAnimMap plAGAnim::fAllAnims;
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAGAnim
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor ------------
// -----
plAGAnim::plAGAnim()
: plSynchedObject()
{
fName = nil;
}
// ctor ------------------------------------------------------
// -----
plAGAnim::plAGAnim(const char *name, double start, double end)
: fStart((hsScalar)start),
fEnd((hsScalar)end)
{
if (name == nil)
name = "";
fName = TRACKED_NEW char[strlen(name) + 1];
strcpy(fName, name);
}
// dtor -------------
// -----
plAGAnim::~plAGAnim()
{
if (fName)
{
RemoveAnim(fName);
delete[] fName;
fName = nil;
}
//int numChannels = fChannels.size();
int numApps = fApps.size();
for (int i = 0; i < numApps; i++)
{
plAGApplicator *app = fApps[i];
if (app)
{
plAGChannel *channel = app->GetChannel();
if(channel)
delete channel;
delete app;
}
}
}
// GetChannelCount ------------------
// ----------------
int plAGAnim::GetChannelCount() const
{
return fApps.size();
}
// GetChannel -------------------------------------
// -----------
plAGChannel * plAGAnim::GetChannel(int index) const
{
plAGApplicator *app = fApps[index];
return (app ? app->GetChannel() : nil);
}
// GetChannel --------------------------------------------
// -----------
plAGChannel * plAGAnim::GetChannel(const char *name) const
{
int appCount = fApps.size();
for(int i = 0; i < appCount; i++)
{
plAGApplicator *app = fApps[i];
plAGChannel *channel = app->GetChannel();
const char *channelName = app->GetChannelName();
if(stricmp(name, channelName) == 0)
{
return channel;
}
}
return nil;
}
// GetApplicatorCount ------------------
// -------------------
int plAGAnim::GetApplicatorCount() const
{
return fApps.size();
}
// GetApplicator -----------------------------------
// --------------
plAGApplicator *plAGAnim::GetApplicator(int i) const
{
return fApps[i];
}
// AddApplicator -------------------------------
// --------------
int plAGAnim::AddApplicator(plAGApplicator *app)
{
hsAssert(app->GetChannel(), "Adding an applicator with no channel");
fApps.push_back(app);
// return the index of the channel
return(fApps.size() - 1);
}
// RemoveApplicator ------------------------
// -----------------
hsBool plAGAnim::RemoveApplicator(int index)
{
hsAssert(index < fApps.size(), "Out of range index for plAGAnim::RemoveApp()");
if(index < fApps.size())
{
fApps.erase(fApps.begin() + index);
return true;
} else {
return false;
}
}
// ExtendToLength ----------------------------
// ---------------
void plAGAnim::ExtendToLength(hsScalar length)
{
if (length > GetEnd())
SetEnd(length);
}
// GetChannelName ------------------------------
// ---------------
const char * plAGAnim::GetChannelName(int index)
{
hsAssert(index < fApps.size(), "Out of range index for plAGAnim::GetChannelName()");
if(index < fApps.size())
{
return fApps[index]->GetChannel()->GetName();
} else {
return nil;
}
}
// Read --------------------------------------------
// -----
void plAGAnim::Read(hsStream *stream, hsResMgr *mgr)
{
plSynchedObject::Read(stream, mgr);
// read in the name of the animation itself
fName = stream->ReadSafeString();
fStart = stream->ReadSwapScalar();
fEnd = stream->ReadSwapScalar();
int numApps = stream->ReadSwap32();
fApps.reserve(numApps); // pre-allocate for performance
int i;
for (i = 0; i < numApps; i++)
{
plAGApplicator * app = plAGApplicator::ConvertNoRef(mgr->ReadCreatable(stream));
app->SetChannel(plAGChannel::ConvertNoRef(mgr->ReadCreatable(stream)));
fApps.push_back(app);
}
plAGAnim::AddAnim(fName, this);
}
// Write --------------------------------------------
// ------
void plAGAnim::Write(hsStream *stream, hsResMgr *mgr)
{
plSynchedObject::Write(stream, mgr);
stream->WriteSafeString(fName);
stream->WriteSwapScalar(fStart);
stream->WriteSwapScalar(fEnd);
int numApps = fApps.size();
stream->WriteSwap32(numApps);
int i;
for (i = 0; i < numApps; i++)
{
plAGApplicator *app = fApps[i];
hsAssert(app, "Missing applicator during write.");
plAGChannel *channel = nil;
if (app)
channel = app->GetChannel();
hsAssert(channel, "Missing channel during write.");
mgr->WriteCreatable(stream, app);
mgr->WriteCreatable(stream, channel);
}
}
void plAGAnim::ClearAnimationRegistry()
{
fAllAnims.clear();
}
// AddAnim ----------------------------------------------
// --------
void plAGAnim::AddAnim(const char * name, plAGAnim *anim)
{
// Only register the animation if it's got a "real" name. Unnamed animations
// all get the same standard name.
if(strcmp(name, ENTIRE_ANIMATION_NAME) != 0)
{
hsAssert(anim, "registering nil anim");
fAllAnims[name] = anim;
}
}
// FindAnim -----------------------------------
// ---------
plAGAnim * plAGAnim::FindAnim(const char *name)
{
plAnimMap::iterator i = fAllAnims.find(name);
if(i != fAllAnims.end())
{
return (*i).second;
} else {
return nil;
}
}
// RemoveAnim -------------------------------
// -----------
hsBool plAGAnim::RemoveAnim(const char *name)
{
plAnimMap::iterator i = fAllAnims.find(name);
if(i != fAllAnims.end())
{
fAllAnims.erase(i);
return true;
} else {
return false;
}
}
// DumpAnimationRegistry -------------
// ----------------------
void plAGAnim::DumpAnimationRegistry()
{
plAnimMap::iterator i = fAllAnims.begin();
int j = 0;
do {
plAGAnim *anim = (*i).second;
const char *name = anim->GetName();
hsStatusMessageF("GLOBAL ANIMS [%d]: <%s>", j++, name);
} while(++i != fAllAnims.end());
}
// SharesPinsWith -----------------------------------------
// ---------------
hsBool plAGAnim::SharesPinsWith(const plAGAnim *anim) const
{
int i, j;
for (i = 0; i < fApps.size(); i++)
{
for (j = 0; j < anim->fApps.size(); j++)
{
if (!strcmp(fApps[i]->GetChannelName(), anim->fApps[j]->GetChannelName()) &&
fApps[i]->CanBlend(anim->fApps[j]))
{
return true;
}
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plATCAnim
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------
// -----
plATCAnim::plATCAnim()
: plAGAnim()
{
}
// ctor --------------------------------------------------------
// -----
plATCAnim::plATCAnim(const char *name, double start, double end)
: plAGAnim(name, start, end),
fInitial(-1),
fAutoStart(true),
fLoopStart((hsScalar)start),
fLoopEnd((hsScalar)end),
fLoop(false),
fEaseInType(plAnimEaseTypes::kNoEase),
fEaseOutType(plAnimEaseTypes::kNoEase),
fEaseInLength(0),
fEaseOutLength(0),
fEaseInMin(0.f),
fEaseInMax(0.f),
fEaseOutMin(0.f),
fEaseOutMax(0.f)
{
}
// dtor ---------------
// -----
plATCAnim::~plATCAnim()
{
for (MarkerMap::iterator it = fMarkers.begin(); it != fMarkers.end(); it++)
delete [] (char*)it->first;
fMarkers.clear();
for( LoopMap::iterator it2 = fLoops.begin(); it2 != fLoops.end(); it2++ )
delete [] (char *)it2->first;
fLoops.clear();
fStopPoints.clear();
}
// Read ---------------------------------------------
// -----
void plATCAnim::Read(hsStream *stream, hsResMgr *mgr)
{
plAGAnim::Read(stream, mgr);
fInitial = stream->ReadSwapScalar();
fAutoStart = stream->Readbool();
fLoopStart = stream->ReadSwapScalar();
fLoopEnd = stream->ReadSwapScalar();
fLoop = stream->Readbool();
fEaseInType = stream->ReadByte();
fEaseInMin = stream->ReadSwapScalar();
fEaseInMax = stream->ReadSwapScalar();
fEaseInLength = stream->ReadSwapScalar();
fEaseOutType = stream->ReadByte();
fEaseOutMin = stream->ReadSwapScalar();
fEaseOutMax = stream->ReadSwapScalar();
fEaseOutLength = stream->ReadSwapScalar();
int i;
int numMarkers = stream->ReadSwap32();
for (i = 0; i < numMarkers; i++)
{
char *name = stream->ReadSafeString();
float time = stream->ReadSwapFloat();
fMarkers[name] = time;
}
int numLoops = stream->ReadSwap32();
for (i = 0; i < numLoops; i++)
{
char *name = stream->ReadSafeString();
float begin = stream->ReadSwapScalar();
float end = stream->ReadSwapScalar();
fLoops[name] = std::pair<float,float>(begin,end);
}
int numStops = stream->ReadSwap32();
for (i = 0; i < numStops; i++)
fStopPoints.push_back(stream->ReadSwapScalar());
}
// Write ---------------------------------------------
// ------
void plATCAnim::Write(hsStream *stream, hsResMgr *mgr)
{
plAGAnim::Write(stream, mgr);
stream->WriteSwapScalar(fInitial);
stream->Writebool(fAutoStart);
stream->WriteSwapScalar(fLoopStart);
stream->WriteSwapScalar(fLoopEnd);
stream->Writebool(fLoop);
stream->WriteByte(fEaseInType);
stream->WriteSwapScalar(fEaseInMin);
stream->WriteSwapScalar(fEaseInMax);
stream->WriteSwapScalar(fEaseInLength);
stream->WriteByte(fEaseOutType);
stream->WriteSwapScalar(fEaseOutMin);
stream->WriteSwapScalar(fEaseOutMax);
stream->WriteSwapScalar(fEaseOutLength);
stream->WriteSwap32(fMarkers.size());
for (MarkerMap::iterator it = fMarkers.begin(); it != fMarkers.end(); it++)
{
stream->WriteSafeString(it->first);
stream->WriteSwapFloat(it->second);
}
stream->WriteSwap32(fLoops.size());
for (LoopMap::iterator loopIt = fLoops.begin(); loopIt != fLoops.end(); loopIt++)
{
stream->WriteSafeString(loopIt->first);
std::pair<float,float>& loop = loopIt->second;
stream->WriteSwapFloat(loop.first);
stream->WriteSwapFloat(loop.second);
}
int i;
stream->WriteSwap32(fStopPoints.size());
for (i = 0; i < fStopPoints.size(); i++)
stream->WriteSwapScalar(fStopPoints[i]);
}
// CheckLoop --------------
// ----------
void plATCAnim::CheckLoop()
{
if (fLoopStart == fLoopEnd)
{
fLoopStart = fStart;
fLoopEnd = fEnd;
}
}
// AddLoop ------------------------------------------------------
// --------
void plATCAnim::AddLoop(const char *name, float start, float end)
{
char *nameCpy = hsStrcpy(name);
fLoops[nameCpy] = std::pair<float,float>(start, end);
}
// GetLoop --------------------------------------------------------------
// --------
bool plATCAnim::GetLoop(const char *name, float &start, float &end) const
{
LoopMap::const_iterator it = fLoops.find(name);
if (it != fLoops.end())
{
const std::pair<float,float>& loop = (*it).second;
start = loop.first;
end = loop.second;
return true;
}
return false;
}
// GetLoop --------------------------------------------------------
// --------
bool plATCAnim::GetLoop(UInt32 num, float &start, float &end) const
{
if (num >= fLoops.size())
return false;
LoopMap::const_iterator it = fLoops.begin();
while (num > 0)
{
it++;
num--;
}
const std::pair<float,float>& loop = (*it).second;
start = loop.first;
end = loop.second;
return true;
}
// GetNumLoops ----------------------
// ------------
UInt32 plATCAnim::GetNumLoops() const
{
return fLoops.size();
}
// AddMarker ------------------------------------------
// ----------
void plATCAnim::AddMarker(const char *name, float time)
{
char *nameCpy = hsStrcpy(name);
fMarkers[nameCpy] = time;
}
// GetMarker -------------------------------------
// ----------
float plATCAnim::GetMarker(const char *name) const
{
if (fMarkers.find(name) != fMarkers.end())
return (*fMarkers.find(name)).second;
return -1;
}
// CopyMarkerNames -------------------------------------
// ----------------
void plATCAnim::CopyMarkerNames(std::vector<char*> &out)
{
MarkerMap::iterator it = fMarkers.begin();
for (; it != fMarkers.end(); it++)
{
out.push_back(hsStrcpy((*it).first));
}
}
// AddStopPoint ---------------------------
// -------------
void plATCAnim::AddStopPoint(hsScalar time)
{
fStopPoints.push_back(time);
}
// NumStopPoints ----------------
// --------------
UInt32 plATCAnim::NumStopPoints()
{
return fStopPoints.size();
}
// GetStopPoint --------------------------
// -------------
hsScalar plATCAnim::GetStopPoint(UInt32 i)
{
hsAssert(i < fStopPoints.size(), "Invalid index for GetStopPoint");
return fStopPoints[i];
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plEmoteAnim
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor ------------------
// -----
plEmoteAnim::plEmoteAnim()
: fBodyUsage(kBodyFull)
{
}
// ctor ------------------------------------------------------------------------------
// -----
plEmoteAnim::plEmoteAnim(const char *animName, double begin, double end, float fadeIn,
float fadeOut, BodyUsage bodyUsage)
: plATCAnim(animName, begin, end),
fFadeIn(fadeIn),
fFadeOut(fadeOut),
fBodyUsage(bodyUsage)
{
}
// Read -----------------------------------------------
// -----
void plEmoteAnim::Read(hsStream *stream, hsResMgr *mgr)
{
plATCAnim::Read(stream, mgr);
// plAGAnim::RegisterEmote(fName, this);
fFadeIn = stream->ReadSwapScalar();
fFadeOut = stream->ReadSwapScalar();
fBodyUsage = static_cast<BodyUsage>(stream->ReadByte());
}
// Write -----------------------------------------------
// ------
void plEmoteAnim::Write(hsStream *stream, hsResMgr *mgr)
{
plATCAnim::Write(stream, mgr);
stream->WriteSwapScalar(fFadeIn);
stream->WriteSwapScalar(fFadeOut);
stream->WriteByte(static_cast<UInt8>(fBodyUsage));
}
// GetBodyUsage ----------------------------------------
// -------------
plEmoteAnim::BodyUsage plEmoteAnim::GetBodyUsage() const
{
return fBodyUsage;
}
// GetFadeIn -----------------------
// ----------
float plEmoteAnim::GetFadeIn() const
{
return fFadeIn;
}
// GetFadeOut -----------------------
// -----------
float plEmoteAnim::GetFadeOut() const
{
return fFadeOut;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAgeGlobalAnim
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor --------------------------
// -----
plAgeGlobalAnim::plAgeGlobalAnim()
: plAGAnim()
{
fGlobalVarName = nil;
}
// ctor --------------------------------------------------------------------
// -----
plAgeGlobalAnim::plAgeGlobalAnim(const char *name, double start, double end)
: plAGAnim(name, start, end),
fGlobalVarName(nil)
{
}
// dtor ---------------------------
// -----
plAgeGlobalAnim::~plAgeGlobalAnim()
{
delete [] fGlobalVarName;
}
void plAgeGlobalAnim::SetGlobalVarName(char *name)
{
delete [] fGlobalVarName;
fGlobalVarName = hsStrcpy(name);
}
// Read ---------------------------------------------------
// -----
void plAgeGlobalAnim::Read(hsStream *stream, hsResMgr *mgr)
{
plAGAnim::Read(stream, mgr);
fGlobalVarName = stream->ReadSafeString();
}
// Write ---------------------------------------------------
// ------
void plAgeGlobalAnim::Write(hsStream *stream, hsResMgr *mgr)
{
plAGAnim::Write(stream, mgr);
stream->WriteSafeString(fGlobalVarName);
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// UTILITIES
//
/////////////////////////////////////////////////////////////////////////////////////////
// GetStartToEndTransform -----------------------------------------------
bool GetStartToEndTransform(const plAGAnim *anim, hsMatrix44 *startToEnd,
hsMatrix44 *endToStart, const char *channelName)
{
double start = 0.0f; // assumed
double end = anim->GetEnd();
GetRelativeTransform(anim, start, end, startToEnd, endToStart, channelName);
return true;
}
// GetRelativeTransform ---------------------------------------------------
bool GetRelativeTransform(const plAGAnim *anim, double timeA, double timeB,
hsMatrix44 *a2b, hsMatrix44 *b2a, const char *channelName)
{
bool result = false;
plAGChannel *maybeChannel = anim->GetChannel(channelName);
hsAssert(maybeChannel, "Couldn't find channel with given name.");
if(maybeChannel)
{
plMatrixChannel *channel = plMatrixChannel::ConvertNoRef(maybeChannel);
hsAssert(channel, "Found channel, but it's not a matrix channel.");
if(channel)
{
hsMatrix44 matA;
hsMatrix44 matB;
channel->Value(matA, timeA);
channel->Value(matB, timeB);
if(a2b) // requested a transform from point A to point B
{
hsMatrix44 invA;
matA.GetInverse(&invA);
*a2b = invA * matB;
}
if(b2a) // requested a transform from point B to point A
{
hsMatrix44 invB;
matB.GetInverse(&invB);
*b2a = invB * matA;
if(a2b)
{
hsMatrix44 invB2;
a2b->GetInverse(&invB2);
}
}
}
}
return result;
}

View File

@ -0,0 +1,446 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef PLAGANIM_INC
#define PLAGANIM_INC
/** \file plAGAnim.h
\brief The animation class for the AniGraph animation system
\ingroup Avatar
\ingroup AniGraph
*/
#pragma warning(disable: 4786) // don't care if mangled names are longer than 255 characters
#include "../pnNetCommon/plSynchedObject.h"
#include "hsStlUtils.h"
#include "hsStlSortUtils.h"
class plTMController;
class hsAffineParts;
class plAnimTimeConvert;
struct hsMatrix44;
class plEmoteAnim;
class plAGApplicator;
class plAGChannel;
/** \class plAGAnim
This class holds reusable animation data. A single plAGAnim can be instanced
any number of times simultaneously.
In order to use a plAGAnim, you need a plAGMasterMod, which is a special type of
modifier which can be attached to multiple scene objects. A master mod is typically
applied to the root of a scene branch, but there is no requirement that all the scene
objects animated be children.
Each plAGAnim has a number of channels, each of which can animate a single parameter
of a single object. Channels are parametric, i.e. they can carry any data type.
A plAGAnim carries a name for the animation, and each channel is named as well.
Instancing a plAGAnim is done via a plAGAnimInstance.
\sa plAGAnimInstance plAGMasterMod plAGModifier
*/
class plAGAnim : public plSynchedObject
{
public:
/** How much of the body does this emote use? This is handy information for
figuring out whether you can, say, wave while sitting down. */
enum BodyUsage {
kBodyUnknown,
kBodyUpper,
kBodyFull,
kBodyLower,
kBodyMax,
kForceSize = 0xff
};
plAGAnim();
/** Construct with name, start time, and end time (within the max note track)
*/
plAGAnim(const char *name, double begin, double end);
/** Destruct, freeing the underlying animation data. */
virtual ~plAGAnim();
/** Return the total of number of channels supplied by this animation.
An object being animated by this animation does not have to have this
many channels; any which are not available will be ignored.
This is syntactic sugar: GetApplicatorCount will return the exact
same number, but some code is only interested in the channels and not
the applicators. */
int GetChannelCount() const;
/** Return the ith channel of the animation. Ordering is arbitrary but consistent.
It's currently breadth first base on the export algorithm, but don't count on this
remaining true. */
plAGChannel * GetChannel(int i) const;
/** Get the name of the channel having the given index. Useful for talking to an
an animation before it is applied and finding out what channels it's going to
affect. */
virtual const char * GetChannelName(int index);
/** Get channel by name. This corresponds to the name of the scene object this channel
will be attached to when the animation is applied.
This function is fairly slow and shouldn't be used often. */
plAGChannel * GetChannel(const char *name) const;
/** Return the number of applicators held by this animation. An applicator is used
to attach a channel to a sceneobject. */
int GetApplicatorCount() const;
/** Return the ith applicator in the channel.
Order is arbitrary but consistent, corresponding to processing order in the exporter. */
plAGApplicator * GetApplicator(int i) const; // get applicator by index
/** Add an applicator -- which must have a channel behind it.
Applicators are translator object which take the output of a
channel and apply it to a scene object. */
int AddApplicator(plAGApplicator * app);
/** Remove the ith applicator and its associated channel. Existing applicators
will be renumbered. */
hsBool RemoveApplicator(int appNum);
/** The name of the animation. This name is used in the avatar manager to reference
animations. Animations are generally indexed by name when they are loaded
by the avatar or from script, but most of the functions which take an animation
name (such as AttachAnimation on the plAGMasterMod) will also take an pointer
to a plAGAnim. */
virtual const char * GetName() const { return fName; }
/** Return the length of the animation; end - start. */
virtual hsScalar GetLength() const { return fEnd - fStart; }
/** Hacky function to extend the length of the animation to some minimum
length. Does nothing if the animation is already longer than this. */
void ExtendToLength(hsScalar length);
/** Return the start time for the beginning of the animation. The animation
contains no data prior to this time.
In practice, this always returns 0.0f and this function may be deprecated. */
virtual hsScalar GetStart() const { return fStart; }
void SetStart(hsScalar start) { fStart = start; }
/** Return the end time of the animation. Since start is typically 0, this usually
serves as the length of the animation as well. */
virtual hsScalar GetEnd() const { return fEnd; }
void SetEnd(hsScalar end) { fEnd = end; }
/** Returns true if any applicator on the arg anim tries to use the
same pin (on the same object) as we do. */
hsBool SharesPinsWith(const plAGAnim *anim) const;
// PLASMA PROTOCOL
// rtti
CLASSNAME_REGISTER( plAGAnim );
GETINTERFACE_ANY( plAGAnim, plSynchedObject );
// *** temp hack to manage animation instances
/** Add the animation by name to a global static registry.
This functionality will possibly be added to the resource
manager. */
static void AddAnim(const char * name, plAGAnim *anim);
/** See if there is an animation with the given name in the
global animation registry. */
static plAGAnim *FindAnim(const char *name);
/** Remove the given animation from the registry. */
static hsBool RemoveAnim(const char *name);
/** Clear the animation cache. Used when resetting the client
to a vanilla state, as when clearing the scene while
exporting. */
static void ClearAnimationRegistry();
/** Debugging utility. Prints out a list of all the animations
in the registry */
static void DumpAnimationRegistry();
// persistance
virtual void Read(hsStream* stream, hsResMgr* mgr);
virtual void Write(hsStream* stream, hsResMgr* mgr);
protected:
typedef std::vector<plAGApplicator*> ApplicatorVec;
ApplicatorVec fApps; /// our animation channels
float fBlend; /// requested blend factor
hsScalar fStart; /// the start time of the beginning of the animation (usually 0)
hsScalar fEnd; /// the end time of the animation
char *fName; /// the name of our animation
// ??? Can this be moved to the resource manager? If it can manage an efficient
// string-based namespace per class, we could get rid of this.
typedef std::map<const char *, plAGAnim *, stringISorter> plAnimMap; //
static plAnimMap fAllAnims; /// map of animation names to animations
typedef std::map<const char *, plEmoteAnim *, stringISorter> plEmoteMap;
static plEmoteMap fAllEmotes;
};
///////////////
// PLATCANIM
///////////////
/** \class plATCAnim
The most common subclass of plAGAnim.
Represents an animation with a standard AnimTimeConvert
(i.e. stop/start/loop/etc animation)
*/
class plATCAnim : public plAGAnim
{
public:
plATCAnim();
/** Construct with name, start time, and end time (within the max note track)
Default is to start automatically, not loop, with no ease curves. */
plATCAnim(const char *name, double begin, double end);
/** Destruct, freeing the underlying animation data. */
virtual ~plATCAnim();
/** Returns the initial position of the "playback head" for this animation.
Animations are not required to start at their actual beginning but can,
for instance, start in the middle, play to the end, and then loop to the
beginning or to their loop start point. */
virtual hsScalar GetInitial() const { return fInitial; }
void SetInitial(hsScalar initial) { fInitial = initial; }
/** Does this animation start automatically when it's applied? */
virtual bool GetAutoStart() const { return fAutoStart; }
void SetAutoStart(hsBool start) { fAutoStart = (start != 0); }
/** If the animation loops, this is where it will restart the loop. Note that
loops do not have to start at the beginning of the animation. */
virtual hsScalar GetLoopStart() const { return fLoopStart; }
void SetLoopStart(hsScalar start) { fLoopStart = start; }
/** If the animation loops, this is the end point of the loop. After passing
this point, the animation will cycle around to GetLoopStart */
virtual hsScalar GetLoopEnd() const { return fLoopEnd; }
void SetLoopEnd(hsScalar end) { fLoopEnd = end; }
/** Does this animation loop?. Note that there may be multiple loop segments defined
within a given animation. */
virtual bool GetLoop() const { return fLoop; }
void SetLoop(hsBool loop) { fLoop = (loop != 0); }
/** Set the curve type for easing in. Easing is an optional feature which allows you
to make an animation slow down gradually when you stop it.
The types are defined in plAnimEaseTypes.h
*/
virtual UInt8 GetEaseInType() const { return fEaseInType; }
void SetEaseInType(UInt8 type) { fEaseInType = type; }
/** Set the length of time the ease-in should take. */
virtual hsScalar GetEaseInLength() const { return fEaseInLength; }
/** Set the length of time the ease-in should take. */
void SetEaseInLength(hsScalar length) { fEaseInLength = length; }
/** The minimum value used at the start of the ease in. */
virtual hsScalar GetEaseInMin() const { return fEaseInMin; }
/** The minimum value used at the start of the ease in. */
void SetEaseInMin(hsScalar length) { fEaseInMin = length; }
/** The maximum value reached at the end of the ease in. */
virtual hsScalar GetEaseInMax() const { return fEaseInMax; }
/** The maximum value reached at the end of the ease in. */
void SetEaseInMax(hsScalar length) { fEaseInMax = length; }
/** The curve type for the ease out. */
virtual UInt8 GetEaseOutType() const { return fEaseOutType; }
/** The curve type for the ease out. */
void SetEaseOutType(UInt8 type) { fEaseOutType = type; }
/** The length of time for the ease out. */
virtual hsScalar GetEaseOutLength() const { return fEaseOutLength; }
/** The length of time for the ease out. */
void SetEaseOutLength(hsScalar length) { fEaseOutLength = length; }
/** Minimum value reached in ease-out */
virtual hsScalar GetEaseOutMin() const { return fEaseOutMin; }
/** Minimum value reached in ease-out */
void SetEaseOutMin(hsScalar length) { fEaseOutMin = length; }
/** Maximum value reached in ease-in */
virtual hsScalar GetEaseOutMax() const { return fEaseOutMax; }
/** Maximum value reached in ease-in */
void SetEaseOutMax(hsScalar length) { fEaseOutMax = length; }
/** Animations can have multiple defined loop segments; these
are selected using animation control messages.
Each loop segment is named using markers in the notetrack. */
void AddLoop(const char *name, float start, float end);
/** Get the loop having the given name.
\param start will return the start time of the loop.
\param end will hold the end time of the loop */
bool GetLoop(const char *name, float &start, float &end) const;
/** Lets you get a loop by index instead of name. */
bool GetLoop(UInt32 num, float &start, float &end) const;
/** Returns the number of loops defined on this anim. */
UInt32 GetNumLoops() const;
/** Add a marker to the animation. Markers can be used
for callbacks or for goto comands. A marker is a simple
name/time tuple. */
void AddMarker(const char *name, float time);
/** Returns the time value of the marker named by name. */
float GetMarker(const char *name) const;
void CopyMarkerNames(std::vector<char*> &out);
/** Add a stop point to the animation. A stop point is a
"detent" for playback - if the animation is stopping
near a stop point and fading out, the stop point will
override the fade, so that the animation stops precisely
at the defined time. */
void AddStopPoint(hsScalar time);
/** Return the number of stop points defined for this animation. */
UInt32 NumStopPoints();
/** Get the time corresponding to the given stop point. Stop points
are numbered in the order they were added. */
hsScalar GetStopPoint(UInt32 i);
/** Function to check for a zero-length loop, and set it to
the anim's start/end instead */
void CheckLoop();
// PLASMA PROTOCOL
// rtti
CLASSNAME_REGISTER( plATCAnim );
GETINTERFACE_ANY( plATCAnim, plAGAnim );
// persistance
virtual void Read(hsStream* stream, hsResMgr* mgr);
virtual void Write(hsStream* stream, hsResMgr* mgr);
protected:
hsScalar fInitial; /// the position of the playback head
bool fAutoStart; /// does the animation start automatically?
hsScalar fLoopStart; /// when wrapping a loop, start here
hsScalar fLoopEnd; /// when you reach this point, loop back
bool fLoop; /// do we loop?
UInt8 fEaseInType; /// the type (none/linear/spline) of our ease-in curve, if any
UInt8 fEaseOutType; /// the type (none/linear/spline) of our ease-out curve, if any
hsScalar fEaseInLength; /// the length of time our ease-in curve takes
hsScalar fEaseInMin; /// minimum (initial) value of our ease-in
hsScalar fEaseInMax; /// maximum (final) value of our ease-in
hsScalar fEaseOutLength; /// the length of time our ease-out curve takes
hsScalar fEaseOutMin; /// minimum (final) value of our ease-out
hsScalar fEaseOutMax; /// maximum (initial) value of our ease-out
// a map from segment names to times
typedef std::map<const char *, float, stringSorter> MarkerMap;
MarkerMap fMarkers;
typedef std::map<const char *, std::pair<float,float>, stringSorter> LoopMap;
LoopMap fLoops;
typedef std::vector<hsScalar> ScalarMap;
ScalarMap fStopPoints; /// vector of stop points
};
/** \class plEmoteAnim
An animation to be used for emotes.
Automatically registers so that it can be played from the chat field.
*/
class plEmoteAnim : public plATCAnim
{
public:
plEmoteAnim();
plEmoteAnim(const char *animName, double begin, double end, float fadeIn, float fadeOut, BodyUsage bodyUsage);
BodyUsage GetBodyUsage() const;
hsScalar GetFadeIn() const;
hsScalar GetFadeOut() const;
CLASSNAME_REGISTER( plEmoteAnim );
GETINTERFACE_ANY( plEmoteAnim, plATCAnim );
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual void Write(hsStream *stream, hsResMgr *mgr);
protected:
BodyUsage fBodyUsage; // how much of the body is used by this emote?
hsScalar fFadeIn; // how fast to fade in the emote
hsScalar fFadeOut; // how fast to fade out the emote
};
//////////////////
// PLAGEGLOBALANIM
//////////////////
/** \class plAgeGlobalAnim
An animation that bases its current position on a variable that global to the age,
like weather, time of day, etc.
*/
class plAgeGlobalAnim : public plAGAnim
{
public:
plAgeGlobalAnim();
/** Construct with name, start time, and end time (within the max note track)
*/
plAgeGlobalAnim(const char *name, double begin, double end);
/** Destruct, freeing the underlying animation data. */
virtual ~plAgeGlobalAnim();
const char * GetGlobalVarName() const { return fGlobalVarName; }
void SetGlobalVarName(char *name);
// PLASMA PROTOCOL
// rtti
CLASSNAME_REGISTER( plAgeGlobalAnim );
GETINTERFACE_ANY( plAgeGlobalAnim, plAGAnim );
// persistance
virtual void Read(hsStream* stream, hsResMgr* mgr);
virtual void Write(hsStream* stream, hsResMgr* mgr);
protected:
char *fGlobalVarName; // Name of the SDL variable we animate on.
};
// USEFUL HELPER FUNCTIONS
bool GetStartToEndTransform(const plAGAnim *anim, hsMatrix44 *startToEnd, hsMatrix44 *endToStart, const char *channelName);
bool GetRelativeTransform(const plAGAnim *anim, double timeA, double timeB, hsMatrix44 *a2b, hsMatrix44 *b2a, const char *channelName);
#endif

View File

@ -0,0 +1,622 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
// singular
#include "plAGAnimInstance.h"
// local
#include "plAGAnim.h"
#include "plAGModifier.h"
#include "plAGMasterMod.h"
// global
#include "hsTimer.h" // just when debugging for GetSysSeconds
// other
#include "../pnNetCommon/plSDLTypes.h"
#include "../plMessage/plAnimCmdMsg.h"
#include "../plMessage/plOneShotCallbacks.h"
#include "../plModifier/plSDLModifier.h"
#include "../plSDL/plSDL.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// FLAGS
//
/////////////////////////////////////////////////////////////////////////////////////////
// enable this to show blend trees before and after attaches and detaches
// #define SHOW_AG_CHANGES
/////////////////////////////////////////////////////////////////////////////////////////
//
// STATIC
//
/////////////////////////////////////////////////////////////////////////////////////////
#ifdef TRACK_AG_ALLOCS
extern const char *gGlobalAnimName = nil;
extern const char *gGlobalChannelName = nil;
#endif // TRACK_AG_ALLOCS
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAGAnimInstance
//
/////////////////////////////////////////////////////////////////////////////////////////
// ctor -------------------------------------------------------------------
// -----
plAGAnimInstance::plAGAnimInstance(plAGAnim * anim, plAGMasterMod * master,
hsScalar blend, UInt16 blendPriority, hsBool cache,
bool useAmplitude)
: fAnimation(anim),
fMaster(master),
fBlend(blend),
fAmplitude(useAmplitude ? 1.0f : -1.0f)
{
int i;
fTimeConvert = nil;
plScalarChannel *timeChan = nil;
#ifdef TRACK_AG_ALLOCS
gGlobalAnimName = anim->GetName(); // for debug tracking...
#endif // TRACK_AG_ALLOCS
plATCAnim *atcAnim = plATCAnim::ConvertNoRef(anim);
if (atcAnim)
{
fTimeConvert = TRACKED_NEW plAnimTimeConvert();
fTimeConvert->Init(atcAnim, this, master);
timeChan = TRACKED_NEW plATCChannel(fTimeConvert);
}
else
{
timeChan = TRACKED_NEW plScalarSDLChannel(anim->GetLength());
fSDLChannels.push_back((plScalarSDLChannel *)timeChan);
}
int nInChannels = anim->GetChannelCount();
fCleanupChannels.push_back(timeChan);
#ifdef SHOW_AG_CHANGES
hsStatusMessageF("\nAbout to Attach anim <%s>", GetName());
fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds());
#endif
for (i = 0; i < nInChannels; i++)
{
plAGApplicator * app = fAnimation->GetApplicator(i);
plAGChannel * inChannel = app->GetChannel();
const char * channelName = app->GetChannelName();
plAGModifier * channelMod = master->GetChannelMod(channelName);
if(channelMod) {
#ifdef TRACK_AG_ALLOCS
gGlobalChannelName = channelName;
#endif // TRACK_AG_ALLOCS
// we're going to be accumulating a chain of channels.
// curChannel will always point to the top one...
plAGChannel *topNode = inChannel;
if(cache)
{
topNode = topNode->MakeCacheChannel(fTimeConvert);
IRegisterDetach(channelName, topNode);
}
if(useAmplitude)
{
// amplitude is rarely used and expensive, so only alloc if asked
// first build a static copy of the incoming channel...
plAGChannel *zeroState = inChannel->MakeZeroState();
IRegisterDetach(channelName, zeroState);
// now make a blend node to blend the anim with its static copy
topNode = zeroState->MakeBlend(topNode, &fAmplitude, -1);
}
// make a time scaler to localize time for this instance
topNode = topNode->MakeTimeScale(timeChan);
IRegisterDetach(channelName, topNode);
channelMod->MergeChannel(app, topNode, &fBlend, this, blendPriority);
}
else
hsAssert(false, "Adding an animation with an invalid channel.");
}
fFadeBlend = fFadeAmp = false;
#ifdef TRACK_AG_ALLOCS
gGlobalAnimName = nil;
#endif // TRACK_AG_ALLOCS
}
// dtor -----------------------------
// -----
plAGAnimInstance::~plAGAnimInstance()
{
delete fTimeConvert;
}
// SearchForGlobals ---------------------
// -----------------
void plAGAnimInstance::SearchForGlobals()
{
const plAgeGlobalAnim *ageAnim = plAgeGlobalAnim::ConvertNoRef(fAnimation);
if (ageAnim != nil && fSDLChannels.size() > 0)
{
extern const plSDLModifier *ExternFindAgeSDL();
const plSDLModifier *sdlMod = ExternFindAgeSDL();
if (!sdlMod)
return;
plSimpleStateVariable *var = sdlMod->GetStateCache()->FindVar(ageAnim->GetGlobalVarName());
if (!var)
return;
sdlMod->AddNotifyForVar(fMaster->GetKey(), ageAnim->GetGlobalVarName(), 0);
int i;
for (i = 0; i < fSDLChannels.size(); i++)
fSDLChannels[i]->SetVar(var);
}
}
void plAGAnimInstance::IRegisterDetach(const char *channelName, plAGChannel *channel)
{
plDetachMap::value_type newPair(channelName, channel);
fManualDetachChannels.insert(newPair);
}
// SetCurrentTime ---------------------------------------------------------------
// ---------------
void plAGAnimInstance::SetCurrentTime(hsScalar localT, hsBool jump /* = false */)
{
if (fTimeConvert)
fTimeConvert->SetCurrentAnimTime(localT, jump);
}
// SeekRelative ------------------------------------
// -------------
void plAGAnimInstance::SeekRelative (hsScalar delta, hsBool jump)
{
if(fTimeConvert)
{
hsScalar now = fTimeConvert->CurrentAnimTime();
fTimeConvert->SetCurrentAnimTime (now + delta, jump);
}
}
// Detach ---------------------
// -------
void plAGAnimInstance::Detach()
{
fMaster->DetachAnimation(this);
}
// DetachChannels ---------------------
// ---------------
void plAGAnimInstance::DetachChannels()
{
#ifdef SHOW_AG_CHANGES
hsStatusMessageF("\nAbout to DETACH anim <%s>", GetName());
fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds());
#endif
plDetachMap::iterator i = fManualDetachChannels.begin();
while(i != fManualDetachChannels.end())
{
const char *channelName = (*i).first;
plAGModifier *channelMod = fMaster->GetChannelMod(channelName, true);
if(channelMod)
{
do {
plAGChannel *channel = (*i).second;
channelMod->DetachChannel(channel);
} while (++i != fManualDetachChannels.end() && i->first == channelName);
} else {
do {
} while (++i != fManualDetachChannels.end() && i->first == channelName);
}
}
int cleanCount = fCleanupChannels.size();
hsAssert(cleanCount, "No time controls when deleting animation");
for (int j = 0; j < cleanCount; j++)
{
delete fCleanupChannels[j];
}
fCleanupChannels.clear();
#ifdef SHOW_AG_CHANGES
hsStatusMessageF("\nFinished DETACHING anim <%s>", GetName());
fMaster->DumpAniGraph("bone_pelvis", false, hsTimer::GetSysSeconds());
#endif
}
// SetBlend ---------------------------------------
// ---------
hsScalar plAGAnimInstance::SetBlend(hsScalar blend)
{
float oldBlend = fBlend.Value(0.0, true);
if(oldBlend != blend &&
(oldBlend == 0.0f ||
blend == 0.0f ||
oldBlend == 1.0f ||
blend == 1.0f))
{
fMaster->SetNeedCompile(true);
}
fBlend.Set(blend);
return blend;
}
// GetBlend -------------------------
// ---------
hsScalar plAGAnimInstance::GetBlend()
{
return fBlend.Value(0);
}
// SetAmplitude -------------------------------------
// -------------
hsScalar plAGAnimInstance::SetAmplitude(hsScalar amp)
{
if(fAmplitude.Get() != -1.0f)
{
fAmplitude.Set(amp);
}
return amp;
}
// GetAmplitude -------------------------
// -------------
hsScalar plAGAnimInstance::GetAmplitude()
{
return fAmplitude.Value(0);
}
// GetName -----------------------------
// --------
const char * plAGAnimInstance::GetName()
{
if(fAnimation)
return fAnimation->GetName();
else
return nil;
}
// SetLoop ----------------------------------
// --------
void plAGAnimInstance::SetLoop(hsBool status)
{
if (fTimeConvert)
fTimeConvert->Loop(status);
}
// HandleCmd ----------------------------------------
// ----------
hsBool plAGAnimInstance::HandleCmd(plAnimCmdMsg *msg)
{
if (fTimeConvert)
return fTimeConvert->HandleCmd(msg);
return false;
}
// IsFinished -----------------------
// -----------
hsBool plAGAnimInstance::IsFinished()
{
if (fTimeConvert)
return fTimeConvert->IsStopped();
return false;
}
// IsAtEnd -----------------------
// --------
hsBool plAGAnimInstance::IsAtEnd()
{
if(fTimeConvert)
{
return fTimeConvert->CurrentAnimTime() == fTimeConvert->GetEnd();
}
else
return false;
}
// Start -----------------------------------
// ------
void plAGAnimInstance::Start(double timeNow)
{
if (fTimeConvert)
{
if (timeNow < 0)
fTimeConvert->Start();
else
fTimeConvert->Start(timeNow);
}
}
// Stop ---------------------
// -----
void plAGAnimInstance::Stop()
{
if (fTimeConvert)
fTimeConvert->Stop();
}
// AttachCallbacks --------------------------------------------------
// ----------------
void plAGAnimInstance::AttachCallbacks(plOneShotCallbacks *callbacks)
{
const plATCAnim *anim = plATCAnim::ConvertNoRef(fAnimation);
if (callbacks && anim)
{
plAnimCmdMsg animMsg;
animMsg.SetCmd(plAnimCmdMsg::kAddCallbacks);
for (int i = 0; i < callbacks->GetNumCallbacks(); i++)
{
plOneShotCallbacks::plOneShotCallback& cb = callbacks->GetCallback(i);
plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg;
eventMsg->AddReceiver(cb.fReceiver);
eventMsg->fRepeats = 0;
eventMsg->fUser = cb.fUser;
if (cb.fMarker)
{
float marker = anim->GetMarker(cb.fMarker);
hsAssert(marker != -1, "Bad marker name");
eventMsg->fEventTime = marker;
eventMsg->fEvent = kTime;
}
else
{
eventMsg->fEvent = kStop;
}
animMsg.AddCallback(eventMsg);
hsRefCnt_SafeUnRef(eventMsg);
}
fTimeConvert->HandleCmd(&animMsg);
}
}
// ProcessFade -------------------------------------
// ------------
void plAGAnimInstance::ProcessFade(hsScalar elapsed)
{
if (fFadeBlend) {
hsScalar newBlend = ICalcFade(fFadeBlend, GetBlend(), fFadeBlendGoal, fFadeBlendRate, elapsed);
SetBlend(newBlend);
if(fFadeDetach && (newBlend == fFadeBlendGoal) && (fFadeBlendGoal == 0.0f) )
{
fMaster->DetachAnimation(this);
return;
}
}
if (fFadeAmp && fAmplitude.Get() != -1.0f) {
hsScalar curAmp = GetAmplitude();
hsScalar newAmp = ICalcFade(fFadeAmp, curAmp, fFadeAmpGoal, fFadeAmpRate, elapsed);
SetAmplitude(newAmp);
}
}
// ICalcFade ---------------------------------------------------------------------
// ----------
hsScalar plAGAnimInstance::ICalcFade(hsBool &fade, hsScalar curVal, hsScalar goal,
hsScalar rate, hsScalar elapsed)
{
hsScalar newVal;
hsScalar curStep = rate * elapsed;
if(rate > 0) {
newVal = __min(goal, curVal + curStep);
} else {
newVal = __max(goal, curVal + curStep);
}
if(newVal == goal)
{
fade = false;
fMaster->DirtySynchState(kSDLAGMaster, 0); // send SDL state update to server
}
return newVal;
}
// FadeAndDetach -------------------------------------------------
// --------------
void plAGAnimInstance::FadeAndDetach(hsScalar goal, hsScalar rate)
{
ISetupFade(goal, rate, true, kFadeBlend);
}
// Fade --------------------------------------------------------------------------------
// -----
void plAGAnimInstance::Fade(hsScalar goal, hsScalar rate, UInt8 type /* = kFadeBlend */)
{
ISetupFade(goal, rate, false, type);
}
// ISetupFade --------------------------------------------------------------------------
// -----------
void plAGAnimInstance::ISetupFade(hsScalar goal, hsScalar rate, bool detach, UInt8 type)
{
if (rate == 0)
{
if (type == kFadeBlend)
{
SetBlend(goal);
fFadeBlend = false;
if(detach) {
fMaster->DetachAnimation(this);
}
}
else if (type == kFadeAmp)
{
SetAmplitude(goal);
fFadeAmp = false;
}
return;
}
rate = (rate > 0 ? rate : -rate); // For old code that sends negative values
hsScalar curVal = 0;
switch (type)
{
case kFadeBlend:
curVal = GetBlend();
break;
case kFadeAmp:
curVal = GetAmplitude();
break;
}
if (curVal > goal)
rate = -rate;
switch (type)
{
case kFadeBlend:
fFadeBlend = true;
fFadeBlendGoal = goal;
fFadeBlendRate = rate;
fFadeDetach = detach;
break;
case kFadeAmp:
fFadeAmp = true;
fFadeAmpGoal = goal;
fFadeAmpRate = rate;
fFadeDetach = false; // only detach on blend fades, for the moment.
break;
}
}
class agAlloc
{
public:
agAlloc(plAGChannel *object, const char *chanName, const char *animName, UInt16 classIndex)
: fObject(object),
fClassIndex(classIndex)
{
fChannelName = hsStrcpy(chanName);
fAnimName = hsStrcpy(animName);
}
~agAlloc()
{
delete[] fChannelName;
delete[] fAnimName;
}
plAGChannel *fObject;
char *fChannelName;
char *fAnimName;
UInt16 fClassIndex;
};
typedef std::map<plAGChannel *, agAlloc *> agAllocMap;
static agAllocMap gAGAllocs;
void RegisterAGAlloc(plAGChannel *object, const char *chanName, const char *animName, UInt16 classIndex)
{
gAGAllocs[object] = TRACKED_NEW agAlloc(object, chanName, animName, classIndex);
}
void DumpAGAllocs()
{
agAllocMap::iterator i = gAGAllocs.begin();
agAllocMap::iterator theEnd = gAGAllocs.end();
hsStatusMessage("DUMPING AG ALLOCATIONS ================================================");
for ( ; i != theEnd; i++)
{
agAlloc * al = (*i).second;
UInt16 realClassIndex = al->fObject->ClassIndex();
hsStatusMessageF("agAlloc: an: %s ch: %s, cl: %s", al->fAnimName, al->fChannelName, plFactory::GetNameOfClass(realClassIndex));
}
// it's not fast but it's safe and simple..
i = gAGAllocs.begin();
while(i != gAGAllocs.end())
{
agAlloc * al = (*i).second;
delete al;
i = gAGAllocs.erase(i);
}
hsStatusMessage("FINISHED DUMPING AG ALLOCATIONS *********************************************");
}
void UnRegisterAGAlloc(plAGChannel *object)
{
agAllocMap::iterator i = gAGAllocs.find(object);
if(i != gAGAllocs.end())
{
agAlloc * al = (*i).second;
gAGAllocs.erase(i);
delete al;
}
}

View File

@ -0,0 +1,294 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/** \file plAGAnimInstance.h
\brief The animation class for the AniGraph animation system
\ingroup Avatar
\ingroup AniGraph
*/
#ifndef PLAGANIMINSTANCE_INC
#define PLAGANIMINSTANCE_INC
// disable warning C4503: dcorated name length exceeded, name was truncated
// disable warning C4786: symbol greater than 255 characters,
#pragma warning(disable: 4503 4786)
// templates
#include "hsStlUtils.h"
#include "hsStlSortUtils.h"
// local
#include "plScalarChannel.h"
// other
#include "../plInterp/plAnimTimeConvert.h"
// declarations
class plAGChannel;
class plAGAnim;
class plAGMasterMod;
class plAGChannelApplicator;
class plOneShotCallbacks;
/////////////////
// PLAGANIMINSTANCE
/////////////////
/** \class plAGAnimInstance
Whenever we attach an animation to a scene object hierarchy, we
create an activation record -- a plAGAnimInstance -- that remembers
all the ephemeral state associated with animation
Since animations have many channels and may involve blend operations,
one of the primary responsibilities of this class is to keep track of
all the animation node graphs that were created by the invocation of
this animation.
*/
class plAGAnimInstance {
public:
/** Used for the fade commands to select what to fade. */
enum
{
kFadeBlend, /// Fade the blend strength
kFadeAmp, /// Fade the amplitude
} FadeType;
/** Default constructor. */
plAGAnimInstance();
/** Construct from an animation and a master modifier.
This attaches the animation channels to the channels of
the master modifier and creates all the bookkeeping structures
necessary to undo it later. */
plAGAnimInstance(plAGAnim * anim, plAGMasterMod * master, hsScalar blend, UInt16 blendPriority, hsBool cache, bool useAmplitude);
/** Destructor. Removes the animation from the scene objects it's attached to. */
virtual ~plAGAnimInstance();
/** Returns the animation that this instance mediates. */
const plAGAnim * GetAnimation() { return fAnimation; };
/** Returns the timeconvert object that controls the progress of time
in this animation. */
plAnimTimeConvert *GetTimeConvert() { return fTimeConvert; }
/** Set the speed of the animation. This is expressed as a fraction of
the speed with which the animation was defined. */
void SetSpeed(hsScalar speed) { if (fTimeConvert) fTimeConvert->SetSpeed(speed); };
// \{
/**
The current blend factor of the animation. This indicates the
priority of this animation as opposed to other animations which
were attached before it. Conceptually it may help to think of this
as a layer in an stack of animations, where the blend value is the
'opacity' of this animation relative to the ones below it.
1.0 represents full strength.
You may use values higher than 1.0, but this has not
yet been seen to have any practical utility whatsoever. Note that
even if an animation has a blend strength of 1.0, it may have another
animation on top/downstream from it that is masking it completely. */
hsScalar SetBlend(hsScalar blend);
hsScalar GetBlend();
// \}
/** Set the strength of the animation with respect to its 0th frame.
This can be used to dampen the motion of the animation.
Animations must be designed to use this: frame 0 of the animation
must be a reasonable "default pose" as it will be blended with the
current frame of the animation to produce the result. */
hsScalar SetAmplitude(hsScalar amp);
/** Get the current strength of the animation. */
hsScalar GetAmplitude();
/** Make this animation loop (or not.) Note that the instance can loop
or not without regard to whether the plAGAnim it is based on loops. */
void SetLoop(hsBool status);
/** Interpret and respond to an animation command message. /sa plAnimCmdMsg */
hsBool HandleCmd(plAnimCmdMsg *msg);
/** Start playback of the animation. You may optionally provide the a world
time, which is needed for synchronizing the animation's timeline
with the global timeline. If timeNow is -1 (the default,) the system
time will be polled */
void Start(double worldTimeNow = -1);
/** Stop playback of the animation. */
void Stop();
/** Move the playback head of the animation to a specific time.
Note that this time is in animation local time, not global time.
The "jump" parameter specifies whether or not to fire callbacks
that occur between the current time and the target time. */
void SetCurrentTime(hsScalar newLocalTime, hsBool jump = false);
/** Move the playback head by the specified relative amount within
the animation. This may cause looping. If the beginning or end
of the animation is reached an looping is not on, the movement
will pin.
\param jump if true, don't look for callbacks between old time and TRACKED_NEW */
void SeekRelative(hsScalar delta, hsBool jump);
/** Gradually fade the blend strength or amplitude of the animation.
\param goal is the desired blend strength
\param rate is in blend units per second
\type is either kFadeBlend or kFadeAmp */
void Fade(hsScalar goal, hsScalar rate, UInt8 type = kFadeBlend);
/** Fade the animation and detach it after the fade is complete.
Extremely useful for situations where the controlling logic
is terminating immediately but you want the animation to fade
out gradually.
\deprecated
*/
void FadeAndDetach(hsScalar goal, hsScalar rate);
/** Has the animation terminated of natural causes?
Primarily used to see if an animation has played all the
way to the end, but will also return true if the animation
was stopped with a stop command */
hsBool IsFinished();
/** Is the animation playback head positioned at the end. */
hsBool IsAtEnd();
/** Get the name of the underlying animation. */
const char * GetName();
/** Remove all channels from the master mode and remove us from
our master modifier.
Destructs the object! */
void Detach();
/** Remove all the instance's channels from the modifiers they're attached to.
Typically called by the master mod prior to destructing the instance. */
void DetachChannels();
/** Prune any unused branches out of the animation graph; add any
newly active branches back in. */
void Optimize();
/** Convert the given world time to local animation time.
May include the effects of looping or wraparound.
If the local time passes the end of the animation, the returned
time will be pinned appropriately. */
double WorldToAnimTime(double foo) { return (fTimeConvert ? fTimeConvert->WorldToAnimTimeNoUpdate(foo) : 0); };
/** Attach a sequence of callback messages to the animation instance.
Messages are each associated with a specific (local) time
in the animation and will be sent when playback passes that time. */
void AttachCallbacks(plOneShotCallbacks *callbacks);
void ProcessFade(hsScalar elapsed); // process any outstanding fades
void SearchForGlobals(); // Util function to setup SDL channels
protected:
/** Set up bookkeeping for a fade. */
void ISetupFade(hsScalar goal, hsScalar rate, bool detach, UInt8 type);
void IRegisterDetach(const char *channelName, plAGChannel *channel);
const plAGAnim * fAnimation;
plAGMasterMod * fMaster;
std::map<char *, plAGChannelApplicator *, stringISorter> fChannels;
typedef std::multimap<const char *, plAGChannel *> plDetachMap;
plDetachMap fManualDetachChannels;
std::vector<plAGChannel*> fCleanupChannels;
std::vector<plScalarSDLChannel*> fSDLChannels;
plScalarConstant fBlend; // blend factor vs. previous animations
plScalarConstant fAmplitude; // for animation scaling
// Each activation gets its own timeline.
plAnimTimeConvert *fTimeConvert;
hsBool fFadeBlend; /// we are fading the blend
hsScalar fFadeBlendGoal; /// what blend level we're trying to reach
hsScalar fFadeBlendRate; /// how fast are we fading in blend units per second (1 blend unit = full)
hsBool fFadeDetach; /// detach after fade is finished? (only used for blend fades)
hsBool fFadeAmp; /// we are fading the amplitude
hsScalar fFadeAmpGoal; /// amplitude we're trying to reach
hsScalar fFadeAmpRate; /// how faster we're fading in blend units per second
hsScalar ICalcFade(hsBool &fade, hsScalar curVal, hsScalar goal, hsScalar rate, hsScalar elapsed);
};
//#ifdef _DEBUG
//#define TRACK_AG_ALLOCS // for now, automatically track AG allocations in debug
//#endif
#ifdef TRACK_AG_ALLOCS
extern const char *gGlobalAnimName;
extern const char *gGlobalChannelName;
void RegisterAGAlloc(plAGChannel *object, const char *chanName, const char *animName, UInt16 classIndex);
void UnRegisterAGAlloc(plAGChannel *object);
void DumpAGAllocs();
#endif // TRACK_AG_ALLOCS
#endif // PLAGANIMINSTANCE_INC

View File

@ -0,0 +1,181 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plAGApplicator.h"
#include "plAGModifier.h"
#include "hsResMgr.h"
#include "hsUtils.h"
// ctor --------
// -----
plAGApplicator::plAGApplicator()
: fChannel(nil),
fChannelName(nil),
fEnabled(true)
{
};
// ctor -------------------------------
// -----
plAGApplicator::plAGApplicator(const char *channelName)
: fChannel(nil),
fEnabled(true)
{
fChannelName = hsStrcpy(channelName);
};
plAGApplicator::~plAGApplicator()
{
if(fChannelName)
delete[] fChannelName;
}
void plAGApplicator::Apply(const plAGModifier *mod, double time, hsBool force)
{
if (fEnabled || force)
IApply(mod, time);
}
void plAGApplicator::SetChannelName(const char *name)
{
if(name)
fChannelName = hsStrcpy(name);
};
const char * plAGApplicator::GetChannelName()
{
return fChannelName;
};
plAGChannel *plAGApplicator::MergeChannel(plAGApplicator *app, plAGChannel *channel,
plScalarChannel *blend, int blendPriority)
{
plAGChannel *result = nil;
if(fChannel)
{
if (CanCombine(app))
result = fChannel->MakeCombine(channel);
else if (CanBlend(app))
result = fChannel->MakeBlend(channel, blend, blendPriority);
} else {
result = channel;
}
if (result && result != fChannel)
SetChannel(result);
return result;
}
plAGApplicator *plAGApplicator::CloneWithChannel(plAGChannel *channel)
{
plAGApplicator *app = plAGApplicator::ConvertNoRef(plFactory::Create(ClassIndex()));
app->SetChannel(channel);
app->Enable(fEnabled);
app->SetChannelName(fChannelName);
return app;
}
hsBool plAGApplicator::CanBlend(plAGApplicator *app)
{
UInt16 ourClass = ClassIndex();
UInt16 theirClass = app->ClassIndex();
return(ourClass == theirClass);
// return(this->HasBaseClass(theirClass)
// || app->HasBaseClass(ourClass));
}
void plAGApplicator::Write(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Write(stream, mgr);
stream->WriteBool(fEnabled);
stream->WriteSafeString(fChannelName);
}
void plAGApplicator::Read(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Read(stream, mgr);
fEnabled = stream->ReadBool();
fChannel = nil; // Whatever is reading this applicator in should know what channel to assign it
fChannelName = stream->ReadSafeString();
}
// IGETxI
// Gain access to of our modifier's target's interfaces.
// This is technically in violation of the principle that only modifiers can get non-const
// reference to their target's interfaces,
// BUT since the plAGApplicator architecture is wholly "owned" by the AGModifier, this
// seemed the most graceful way to do it without const_cast or modifying plModifier or plSceneObject
// IGETAI
plAudioInterface * plAGApplicator::IGetAI(const plAGModifier *modifier) const
{
return modifier->LeakAI();
}
// IGETCI
plCoordinateInterface * plAGApplicator::IGetCI(const plAGModifier* modifier) const
{
return modifier->LeakCI();
}
// IGETDI
plDrawInterface * plAGApplicator::IGetDI(const plAGModifier * modifier) const
{
return modifier->LeakDI();
}
// IGETSI
plSimulationInterface * plAGApplicator::IGetSI(const plAGModifier * modifier) const
{
return modifier->LeakSI();
}
plObjInterface * plAGApplicator::IGetGI(const plAGModifier * modifier, UInt16 classIdx) const
{
return modifier->LeakGI(classIdx);
}

View File

@ -0,0 +1,173 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAGApplicator_h
#define plAGApplicator_h
/////////////////////////////////////////////////////////////////////////////////////////
//
// FORWARDS
//
/////////////////////////////////////////////////////////////////////////////////////////
class plAudioInterface;
class plCoordinateInterface;
class plDrawInterface;
class plSimulationInterface;
class plObjInterface;
class plAGModifier;
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include "../pnFactory/plCreatable.h"
#include "plAvDefs.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
class plAGChannel;
class plScalarChannel;
/////////////////////////////////////////////////////////////////////////////////////////
//
// DEFINITIONS
//
/////////////////////////////////////////////////////////////////////////////////////////
/** \class plAGApplicator
Takes the end of a channel tree and applies it to a scene object.
A transform applicator takes a matrix-typed-tree and applies it to
the transform of the scene object.
Other applicators might take floats and use them to animate alpha channels,
etc. */
class plAGApplicator : public plCreatable
{
public:
// -- methods --
/** Base constructor. */
plAGApplicator();
plAGApplicator(const char *channelName);
virtual ~plAGApplicator();
/** Return our single input channel. Applicators only ever
have a single input channel; you can always use blend
or combine nodes to merge channels before routing
them into the applicator. */
plAGChannel *GetChannel() { return fChannel; }
/** Set our input channel. Does not free the previous input channel. */
void SetChannel(plAGChannel *channel) { fChannel = channel; }
void SetChannelName(const char *name);
const char * GetChannelName();
/** Optionally suppress the action of this applicator.
The applicator can still be forced to apply using the force
paramater of the Apply function. */
void Enable(hsBool on) { fEnabled = on; }
/** Make a shallow copy of the applicator. Keep the same input channel
but do not clone the input channel. */
virtual plAGApplicator *CloneWithChannel(plAGChannel *channel);
/** What animation type do we have? Used to determine whether two
applicators are trying to animate the same thing or whether they
can coexist peacefully. */
virtual plAGPinType GetPinType() { return kAGPinUnknown; }
// Join the incoming channel (if possible) to ours
/** Determine whether the given applicator can be blended to ours. If so, this
would be effected by pulling the input channel from the other applicator,
blending it with our input channel via a new blend node, attaching that blend
node as our new input, and throwing the other applicator away. */
virtual hsBool CanBlend(plAGApplicator *app);
/** Combine the two applicators if possible. \sa CanBlend */
virtual plAGChannel * MergeChannel(plAGApplicator *app, plAGChannel *channel,
plScalarChannel *blend, int blendPriority);
/** \bug It makes no sense for an applicator to combine because combination always
results in a different data type, which would require a different applicator. */
virtual hsBool CanCombine(plAGApplicator *app) { return false; }
/** Apply our channel's data to the scene object, via the modifier.
This is the only function that actually changes perceivable scene state. */
void Apply(const plAGModifier *mod, double time, hsBool force = false); // Apply our channel's data to the modifier
// this is pretty much a HACK to support applicators that want to stick around when
// their channel is gone so they can operate on the next channel that comes in
// the RIGHT way to do this is to make applicators support the Detach() protocol just
// like channels...
virtual hsBool AutoDelete() { return true; } // should we remove it when its input channel is gone?
// PlOP
CLASSNAME_REGISTER( plAGApplicator );
GETINTERFACE_ANY( plAGApplicator, plCreatable );
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *s, hsResMgr *mgr);
protected:
// -- methods --
virtual void IApply(const plAGModifier *mod, double time) = 0;
// give derived classes access to the object interfaces
plAudioInterface * IGetAI(const plAGModifier *modifier) const;
plCoordinateInterface * IGetCI(const plAGModifier *modifier) const;
plDrawInterface * IGetDI(const plAGModifier *modifier) const;
plSimulationInterface * IGetSI(const plAGModifier *modifier) const;
plObjInterface * IGetGI(const plAGModifier *modifier, UInt16 classIdx) const;
// Allow plAGModifier to declare IGet?? as friends
friend class plAGModifier;
// -- members --
plAGChannel *fChannel;
hsBool fEnabled;
char *fChannelName;
};
#endif

View File

@ -0,0 +1,128 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
// singular
#include "plAGChannel.h"
// local
#include "plAGModifier.h"
// global
#include "hsTypes.h"
#include "hsResMgr.h"
#include "plAGAnimInstance.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAGChannel
//
/////////////////////////////////////////////////////////////////////////////////////////
plAGChannel::plAGChannel()
{
#ifdef TRACK_AG_ALLOCS
fName = gGlobalAnimName;
RegisterAGAlloc(this, gGlobalChannelName, gGlobalAnimName, this->ClassIndex());
#else // TRACK_AG_ALLOCS
fName = nil;
#endif // TRACK_AG_ALLOCS
}
// DTOR
plAGChannel::~plAGChannel()
{
// we do not own the "fName" string, so don't delete it!
#ifdef TRACK_AG_ALLOCS
UnRegisterAGAlloc(this);
#endif // TRACK_AG_ALLOCS
}
// MAKECOMBINE
plAGChannel * plAGChannel::MakeCombine(plAGChannel *channelA)
{
return nil;
}
// MAKEBLEND
plAGChannel * plAGChannel::MakeBlend(plAGChannel *channelA, plScalarChannel *blend, int blendPriority)
{
return nil;
}
// DETACH
// If the channel being detached is us, let our caller know to replace us
// by return NIL.
plAGChannel * plAGChannel::Detach(plAGChannel *channel)
{
if (this == channel)
{
return nil;
} else {
return this;
}
}
// OPTIMIZE
plAGChannel * plAGChannel::Optimize(double time)
{
// the basic channel can't optimize...
return this;
}
// WRITE
void plAGChannel::Write(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Write(stream, mgr);
stream->WriteSafeString(fName);
}
// READ
void plAGChannel::Read(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Read(stream, mgr);
fName = stream->ReadSafeString();
}
////////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,198 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/** \file plAGChannel.h
\brief The animation class for the AniGraph animation system
WHAT'S AG STAND FOR?
AG stands for animation graph. It's a directed acyclic graph of animation data sources and operations.
WHAT'S A CHANNEL?
A channel is a stream of animation data.
A channel is a node in an animation graph.
Animation data can come from anything; keyframes, IK, physics, etc.
Channels are often grouped together using blending nodes.
Blending nodes are also a type of channel.
See where I'm going with this?
HOW IS THIS DIFFERENT FROM PLCONTROLLER?
It's very similar, but it's designed to be extremely lightweight; animation graphs are set up and torn down
quickly and with great frequency. They are not persistent, and their only state (besides cache)
is their interconnectedness.
HOW DO THEY GET SAVED?
Their client must recreate them at read/load time. Since they are stateless, all information necessary
to recreate them is necessarily held by the client.
ARE THEY REFERENCE COUNTED?
No. The entire graph is owned by the creator.
Deleting the top node of a graph deletes the entire graph.
If we decide we want to share subgraphs, we'll add reference counting -- easy since cycles are illegal
HOW DO THEY INTEGRATE WITH PLCONTROLLERS?
Once we decide the animation graph approach is workable and effective, we'll combine the plController
concept with the plAGChannel concept.
Until then, there will be a handful of channel types that adapt controllers into animation graphs.
WHAT DOES "COMBINE" MEAN?
Merging more than one types of animation data into a single output that preserves the value
of both.
WHAT DOES "BLEND" MEAN?
Merging two similar types of animation data using a weight to "fade" from one to the other
WHAT IS ZEROSTATE?
This is a static channel that always returns the value at time zero. It is used as a reference for animation
scaling (amplitude).
WHAT'S AN APPLICATOR?
In order to cut down on the number of derived channel classes for different Value() and Apply() behaviors,
applicators were created. At the end of each graph is an applicator, which specifies what should be done
with the result of a call to Value().
\ingroup Avatar
\ingroup AniGraph
*/
#ifndef PLAGCHANNEL_H
#define PLAGCHANNEL_H
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include "../pnFactory/plCreatable.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// FORWARDS
//
/////////////////////////////////////////////////////////////////////////////////////////
class plAGModifier;
class plAnimTimeConvert;
class plAGChannel;
class plScalarChannel;
/////////////////////////////////////////////////////////////////////////////////////////
//
// DEFINITIONS
//
/////////////////////////////////////////////////////////////////////////////////////////
/** \class plAGChannel
An object that emits data of a specific type. Fundamental building
block of the animation graph. */
class plAGChannel : public plCreatable
{
public:
// -- methods --
/** Default constructor for the base class. */
plAGChannel();
/** Free an allocated name. Does not release any upstream nodes. */
virtual ~plAGChannel();
// AG PROTOCOL
/** Combine the given channel with this channel, allocating and returning
a new node which does the combination. It's up to the caller to
manage the lifetime of the new node. */
virtual plAGChannel * MakeCombine(plAGChannel * channelB);
/** Blend the given channel with this channel, using a third channel (which
must output a float/scalar value) to crossfade between the two.
As the blendChannel varies, the blend will vary. */
virtual plAGChannel * MakeBlend(plAGChannel * channelB, plScalarChannel * blendChannel, int blendPriority);
/** Create a "static clone" of this channel which always returns this channel's
value at time zero. */
virtual plAGChannel * MakeZeroState() = 0;
/** If we're potentially sharing this channel with other plAGMasterMods, we'll
want to insert a channel in the graph for cache info. This function returns
either the cache channel (replacing us) or ourself. */
virtual plAGChannel * MakeCacheChannel(plAnimTimeConvert *atc) { return this; }
/** Create a new channel which converts global time to local time
and attach it downstream from this channel. This allows you to
convert an animation from one timespace to another - critical for
blending.
local-time-animation <-- timescale <-- world-time-animation */
virtual plAGChannel * MakeTimeScale(plScalarChannel *timeSource) = 0;
/** Is the animation moving at the given world time? Takes into account
start/stop messages that haven't been applied yet, ease curves, etc. */
virtual hsBool IsStoppedAt(double wSecs) { return true; }
/** Detach the given channel from our graph. If this is the channel in
question, returns any upstream channels so they can be reattached.
If this is not the channel in question, passes the request upstream
and does any reattachment necessary. */
virtual plAGChannel * Detach(plAGChannel * channel);
/** Return the optimized version of this channel. May be a completely
different channel; will collapse out inactive subgraphs. */
virtual plAGChannel * Optimize(double time);
// \{
/** The name of the channel is used to dynamically attach to sub-parts of an
object. */
virtual const char * GetName() { return fName; };
virtual void SetName(char * name) { fName = name; };
// \}
// PLASMA PROTOCOL
// rtti
CLASSNAME_REGISTER( plAGChannel );
GETINTERFACE_ANY( plAGChannel, plCreatable );
// persistence
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *s, hsResMgr *mgr);
protected:
const char * fName;
};
#endif PLAGCHANNEL_H

View File

@ -0,0 +1,822 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
// singular
#include "plAGMasterMod.h"
// local
#include "plAGAnim.h"
#include "plAGAnimInstance.h"
#include "plAGModifier.h"
// #include "plAvatarAnim.h"
#include "plAGMasterSDLModifier.h"
#include "plMatrixChannel.h"
// global
#include "hsResMgr.h"
#include "plgDispatch.h"
// other
// #include "../pnMessage/plRefMsg.h"
#include "../plMessage/plAnimCmdMsg.h"
#include "../pnMessage/plSDLModifierMsg.h"
#include "../pnMessage/plSDLNotificationMsg.h"
#include "../pnMessage/plTimeMsg.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../pnSceneObject/plCoordinateInterface.h"
////////////////
// PLAGMASTERMOD
////////////////
// Coordinates the activities of a bunch of plAGModifiers
// std::map<char *, plAGMasterMod *, stringISorter> plAGMasterMod::fInstances;
// CTOR
plAGMasterMod::plAGMasterMod()
: fTarget(nil),
fNeedEval(false),
fFirstEval(true),
fAGMasterSDLMod(nil),
fNeedCompile(false),
fIsGrouped(false),
fIsGroupMaster(false),
fMsgForwarder(nil)
{
}
// DTOR
plAGMasterMod::~plAGMasterMod()
{
}
void plAGMasterMod::Write(hsStream *stream, hsResMgr *mgr)
{
plModifier::Write(stream, mgr);
int length = 0;
stream->WriteSwap32(length); // backwards compatability. Nuke on next format change.
stream->WriteSwap32(fPrivateAnims.size());
int i;
for (i = 0; i < fPrivateAnims.size(); i++)
{
mgr->WriteKey(stream, fPrivateAnims[i]->GetKey());
}
stream->Writebool(fIsGrouped);
stream->Writebool(fIsGroupMaster);
if (fIsGroupMaster)
mgr->WriteKey(stream, fMsgForwarder->GetKey());
// maybe later... WriteCachedMessages(stream, mgr);
}
void plAGMasterMod::Read(hsStream * stream, hsResMgr *mgr)
{
plModifier::Read(stream, mgr);
//////////////////////////////////////////
int nameLength = stream->ReadSwap32(); // Unused. Nuke next format change.
char *junk = TRACKED_NEW char[nameLength+1]; //
stream->Read(nameLength, junk); //
junk[nameLength] = 0; //
delete [] junk; //
//////////////////////////////////////////
int numPrivateAnims = stream->ReadSwap32();
fPrivateAnims.reserve(numPrivateAnims); // pre-allocate for performance
int i;
for (i = 0; i < numPrivateAnims; i++)
{
plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPrivateAnim);
mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef);
}
fIsGrouped = stream->Readbool();
fIsGroupMaster = stream->Readbool();
if (fIsGroupMaster)
{
plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0);
mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef);
}
// maybe later... ReadCachedMessages(stream, mgr);
}
// ADDTARGET
// Collect all the plAGModifiers from our children and attach private anims.
void plAGMasterMod::AddTarget(plSceneObject * object)
{
plSynchEnabler p(false); // turn off dirty tracking while in this function
fTarget = object;
int autoIdx = -1;
int initialIdx = -1;
int timeIdx = 0;
int i;
for (i = 0; i < fPrivateAnims.size(); i++)
{
plATCAnim *atcAnim = plATCAnim::ConvertNoRef(fPrivateAnims[i]);
if (!atcAnim)
continue;
if (atcAnim->GetAutoStart())
autoIdx = i;
if (atcAnim->GetInitial() != -1)
initialIdx = i;
if (atcAnim->GetStart() < fPrivateAnims[timeIdx]->GetStart())
timeIdx = i;
}
int masterIdx;
if (autoIdx != -1)
masterIdx = autoIdx; // If something autostarts, it wins.
else if (initialIdx != -1)
masterIdx = initialIdx; // Otherwise, the fellow with the @initial point wins
else
masterIdx = timeIdx; // Default case: the earliest anim wins
for (i = 0; i < fPrivateAnims.size(); i++)
{
AttachAnimationBlended(fPrivateAnims[i], i == masterIdx ? 1.f : 0.f);
}
// Force one eval after we init
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
if (!fIsGrouped || fIsGroupMaster)
{
// add sdl modifier
delete fAGMasterSDLMod;
fAGMasterSDLMod = TRACKED_NEW plAGMasterSDLModifier;
object->AddModifier(fAGMasterSDLMod);
}
}
void plAGMasterMod::RemoveTarget(plSceneObject* o)
{
hsAssert(o == fTarget, "Removing target I don't have");
DetachAllAnimations();
// remove sdl modifier
if (o)
{
if (fAGMasterSDLMod)
o->RemoveModifier(fAGMasterSDLMod);
}
delete fAGMasterSDLMod;
fAGMasterSDLMod=nil;
fTarget = nil;
}
#include "plProfile.h"
plProfile_CreateTimer("ApplyAnimation", "Animation", ApplyAnimation);
plProfile_CreateTimer(" AffineValue", "Animation", AffineValue);
plProfile_CreateTimer(" AffineInterp", "Animation", AffineInterp);
plProfile_CreateTimer(" AffineBlend", "Animation", AffineBlend);
plProfile_CreateTimer(" AffineCompose", "Animation", AffineCompose);
plProfile_CreateTimer(" AffineApplicator", "Animation", MatrixApplicator);
plProfile_CreateTimer("AnimatingPhysicals", "Animation", AnimatingPhysicals);
plProfile_CreateTimer("StoppedAnimPhysicals", "Animation", StoppedAnimPhysicals);
// IEVAL
hsBool plAGMasterMod::IEval(double secs, hsScalar del, UInt32 dirty)
{
if (fFirstEval)
{
int i;
for (i = 0; i < fAnimInstances.size(); i++)
fAnimInstances[i]->SearchForGlobals();
fFirstEval = false;
}
ApplyAnimations(secs, del);
// We might get registered for just a single eval. If we don't need to eval anymore, unregister
if (!fNeedEval)
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
return true;
}
// APPLYANIMATIONS
void plAGMasterMod::ApplyAnimations(double time, hsScalar elapsed)
{
plProfile_BeginLap(ApplyAnimation, this->GetKey()->GetUoid().GetObjectName());
// update any fades
for (int i = 0; i < fAnimInstances.size(); i++)
{
fAnimInstances[i]->ProcessFade(elapsed);
}
AdvanceAnimsToTime(time);
plProfile_EndLap(ApplyAnimation,this->GetKey()->GetUoid().GetObjectName());
}
void plAGMasterMod::AdvanceAnimsToTime(double time)
{
if(fNeedCompile)
Compile(time);
for(plChannelModMap::iterator j = fChannelMods.begin(); j != fChannelMods.end(); j++)
{
plAGModifier *mod = (*j).second;
mod->Apply(time);
}
}
void plAGMasterMod::SetNeedCompile(bool needCompile)
{
fNeedCompile = true;
}
void plAGMasterMod::Compile(double time)
{
plChannelModMap::iterator end = fChannelMods.end();
fNeedCompile = false;
for(plChannelModMap::iterator j = fChannelMods.begin(); j != end; j++)
{
plAGModifier *mod = (*j).second;
plAGApplicator *app = mod->GetApplicator(kAGPinTransform);
if(app) {
plAGChannel *channel = app->GetChannel();
if(channel)
{
plMatrixChannel *topChannel = plMatrixChannel::ConvertNoRef(channel);
if(topChannel)
topChannel->Optimize(time);
}
}
}
}
void plAGMasterMod::DumpAniGraph(const char *justThisChannel, bool optimized, double time)
{
plChannelModMap::iterator end = fChannelMods.end();
fNeedCompile = false;
for(plChannelModMap::iterator j = fChannelMods.begin(); j != end; j++)
{
plAGModifier *mod = (*j).second;
if(!justThisChannel || stricmp(justThisChannel, mod->GetChannelName()) == 0)
{
plAGApplicator *app = mod->GetApplicator(kAGPinTransform);
if(app) {
plAGChannel *channel = app->GetChannel();
if(channel)
{
plMatrixChannel *topChannel = plMatrixChannel::ConvertNoRef(channel);
if(topChannel)
{
hsStatusMessageF("AGModifier: <%s>", mod->GetChannelName());
topChannel->Dump(1, optimized, time);
}
}
}
if(justThisChannel)
break;
}
}
}
// GETCHANNELMOD(name)
// Get the modifier that controls the channel with the given name
plAGModifier * plAGMasterMod::GetChannelMod(const char * name, hsBool dontCache ) const
{
plAGModifier * result = nil;
std::map<const char *, plAGModifier *, stringSorter>::const_iterator i = fChannelMods.find(name);
if (i != fChannelMods.end()) {
result = (*i).second;
} else {
plSceneObject *SO = GetTarget(0);
if(SO) {
result = IFindChannelMod(SO, name);
if(result && !dontCache) {
ICacheChannelMod(result);
}
}
}
return result;
}
// CACHECHANNELMOD
plAGModifier * plAGMasterMod::ICacheChannelMod(plAGModifier *mod) const
{
plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0);
hsgResMgr::ResMgr()->SendRef(mod, msg, plRefFlags::kActiveRef);
return mod;
}
// IFINDAGMOD (sceneObject)
// See if there's an ag modifier on this sceneobject.
// Doesn't check for multiples; just returns the first one.
plAGModifier * plAGMasterMod::IFindChannelMod(const plSceneObject *SO, const char *name) const
{
const plCoordinateInterface * CI = SO->GetCoordinateInterface();
const plAGModifier * constMod = static_cast<const plAGModifier *>(FindModifierByClass(SO, plAGModifier::Index()));
plAGModifier * mod = const_cast<plAGModifier *>(constMod);
if(mod)
{
const char *modName = mod->GetChannelName();
if(stricmp(name, modName) == 0)
return mod;
}
if(CI)
{
int childCount = CI->GetNumChildren();
for (int i = 0; i < childCount; i++)
{
const plSceneObject * subChild = CI->GetChild(i)->GetOwner();
plAGModifier * mod = IFindChannelMod(subChild, name);
if(mod)
return mod;
}
}
return nil;
}
// ATTACHANIMATIONBLENDED(anim, blend)
plAGAnimInstance * plAGMasterMod::AttachAnimationBlended(plAGAnim *anim,
hsScalar blendFactor /* = 0 */,
UInt16 blendPriority /* plAGMedBlendPriority */,
hsBool cache /* = false */)
{
plAGAnimInstance *instance = nil;
plAnimVector::iterator i;
if(anim)
{
fNeedCompile = true; // need to recompile the graph since we're editing it...
for (i = fPrivateAnims.begin(); i != fPrivateAnims.end(); i++)
{
if (*i == anim)
break;
}
if (i == fPrivateAnims.end()) // Didn't find it. Ref it!
{
plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPublicAnim);
hsgResMgr::ResMgr()->SendRef(anim, msg, plRefFlags::kActiveRef);
}
instance = TRACKED_NEW plAGAnimInstance(anim, this, blendFactor, blendPriority, cache, false);
fAnimInstances.push_back(instance);
plATCAnim *atcAnim = plATCAnim::ConvertNoRef(anim);
if (atcAnim)
{
fATCAnimInstances.push_back(instance);
ISetupMarkerCallbacks(atcAnim, instance->GetTimeConvert());
}
IRegForEval(HasRunningAnims());
}
return instance;
}
// ATTACHANIMATIONBLENDED(name, blend)
plAGAnimInstance * plAGMasterMod::AttachAnimationBlended(const char *name, hsScalar blendFactor /* = 0 */, UInt16 blendPriority, hsBool cache /* = false */)
{
plAGAnimInstance *instance = nil;
plAGAnim *anim = plAGAnim::FindAnim(name);
if(anim)
{
instance = AttachAnimationBlended(anim, blendFactor, blendPriority, cache);
}
return instance;
}
void plAGMasterMod::PlaySimpleAnim(const char *name)
{
plATCAnim *anim = plATCAnim::ConvertNoRef(plAGAnim::FindAnim(name));
plAGAnimInstance *instance = nil;
if (anim)
{
if (FindAnimInstance(name))
return;
instance = AttachAnimationBlended(anim, 1.f, (UInt16)kAGMaxBlendPriority, false);
}
if (instance)
{
instance->SetLoop(false);
instance->Start();
plAGDetachCallbackMsg *msg = TRACKED_NEW plAGDetachCallbackMsg(GetKey(), kStop);
msg->SetAnimName(name);
instance->GetTimeConvert()->AddCallback(msg);
hsRefCnt_SafeUnRef(msg);
}
}
// FINDANIMINSTANCE
// Look for an animation instance of the given name on the modifier.
// If we need this to be fast, should make it a map rather than a vector
plAGAnimInstance * plAGMasterMod::FindAnimInstance(const char *name)
{
plAGAnimInstance *result = nil;
if (name)
{
for (int i = 0; i < fAnimInstances.size(); i++)
{
plAGAnimInstance *act = fAnimInstances[i];
const char *eachName = act->GetName();
if( stricmp(eachName, name) == 0)
{
result = act;
break;
}
}
}
return result;
}
// FINDORATTACHINSTANCE
plAGAnimInstance * plAGMasterMod::FindOrAttachInstance(const char *name, hsScalar blendFactor)
{
plAGAnimInstance *result = FindAnimInstance(name);
if(result)
{
// if it's already attached, we need to set the blend
result->SetBlend(blendFactor);
} else {
result = AttachAnimationBlended(name, blendFactor);
}
return result;
}
// GETANIMINSTANCE
plAGAnimInstance * plAGMasterMod::GetAnimInstance(int i)
{
return fAnimInstances[i];
}
// GETNUMANIMATIONS
int plAGMasterMod::GetNumAnimations()
{
return fAnimInstances.size();
}
// GETNUMPRIVATEANIMATIONS
int plAGMasterMod::GetNumPrivateAnimations()
{
return fPrivateAnims.size();
}
int plAGMasterMod::GetNumATCAnimations()
{
return fATCAnimInstances.size();
}
plAGAnimInstance *plAGMasterMod::GetATCAnimInstance(int i)
{
return fATCAnimInstances[i];
}
void plAGMasterMod::DetachAllAnimations()
{
int nInstances = fAnimInstances.size();
for (int i = nInstances - 1; i >= 0; i--)
{
plAGAnimInstance * instance = fAnimInstances[i];
if(instance)
{
DetachAnimation(instance);
// delete instance;
}
}
fAnimInstances.clear();
fPrivateAnims.clear();
fATCAnimInstances.clear();
}
// DETACHANIMATION(plAGAnimInstance *)
void plAGMasterMod::DetachAnimation(plAGAnimInstance *anim)
{
plInstanceVector::iterator i;
plAnimVector::iterator j;
fNeedCompile = true; // need to recompile the graph since we're editing it...
for ( i = fAnimInstances.begin(); i != fAnimInstances.end(); i++)
{
plAGAnimInstance *instance = *i;
if(instance == anim)
{
// DetachAnimation(instance);
instance->DetachChannels();
// Need to release it if it's not a private anim
const plAGAnim *agAnim = instance->GetAnimation();
for (j = fPrivateAnims.begin(); j != fPrivateAnims.end(); j++)
{
if (*j == agAnim)
break;
}
if (j == fPrivateAnims.end()) // We didn't find it
GetKey()->Release(agAnim->GetKey());
delete instance;
i = fAnimInstances.erase(i);
break;
}
}
for ( i = fATCAnimInstances.begin(); i != fATCAnimInstances.end(); i++)
{
plAGAnimInstance *instance = *i;
if(instance == anim)
{
i = fATCAnimInstances.erase(i);
break;
}
}
}
// DETACHANIMATION(name)
void plAGMasterMod::DetachAnimation(const char *name)
{
plAGAnimInstance *anim = FindAnimInstance(name);
if(anim) {
DetachAnimation(anim);
}
}
void plAGMasterMod::DumpCurrentAnims(const char *header)
{
if(header)
hsStatusMessageF("Dumping Armature Anim Stack: %s", header);
int nAnims = fAnimInstances.size();
for(int i = nAnims - 1; i >= 0; i--)
{
plAGAnimInstance *inst = fAnimInstances[i];
const char *name = inst->GetName();
float blend = inst->GetBlend();
hsStatusMessageF("%d: %s with blend of %f\n", i, name, blend);
}
}
// MSGRECEIVE
// receive trigger messages
hsBool plAGMasterMod::MsgReceive(plMessage* msg)
{
plAnimCmdMsg* cmdMsg;
plAGCmdMsg* agMsg;
int i;
plSDLNotificationMsg* nMsg = plSDLNotificationMsg::ConvertNoRef(msg);
if (nMsg)
{
// Force a single eval
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
return true;
}
if (cmdMsg = plAnimCmdMsg::ConvertNoRef(msg))
{
const char *targetName = cmdMsg->GetAnimName();
if (!targetName)
targetName = ENTIRE_ANIMATION_NAME;
plAGAnimInstance *inst = FindAnimInstance(targetName);
if (inst != nil)
{
if (cmdMsg->CmdChangesAnimTime())
{
for (i = 0; i < GetNumAnimations(); i++)
{
plAGAnimInstance *currInst = GetAnimInstance(i);
if (currInst != inst && currInst->GetAnimation()->SharesPinsWith(inst->GetAnimation()))
currInst->SetBlend(0);
}
inst->SetBlend(1);
}
inst->HandleCmd(cmdMsg);
}
return true;
}
if (agMsg = plAGCmdMsg::ConvertNoRef(msg))
{
if (agMsg->Cmd(plAGCmdMsg::kSetAnimTime))
{
for (int i = 0; i < fAnimInstances.size(); i++)
{
plAGAnimInstance *inst = fAnimInstances[i];
inst->SetCurrentTime(agMsg->fAnimTime, true);
}
return true;
}
plAGAnimInstance *inst = FindAnimInstance(agMsg->GetAnimName());
if (inst != nil)
{
if (agMsg->Cmd(plAGCmdMsg::kSetBlend))
inst->Fade(agMsg->fBlend, agMsg->fBlendRate, plAGAnimInstance::kFadeBlend);
if (agMsg->Cmd(plAGCmdMsg::kSetAmp))
inst->Fade(agMsg->fAmp, agMsg->fAmpRate, plAGAnimInstance::kFadeAmp);
}
return true;
}
plAGInstanceCallbackMsg *agicMsg = plAGInstanceCallbackMsg::ConvertNoRef(msg);
if (agicMsg != nil)
{
if (agicMsg->fEvent == kStart)
{
IRegForEval(true);
}
else if (agicMsg->fEvent == kStop)
{
if (!HasRunningAnims())
IRegForEval(false);
}
else // Just force a single eval
{
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
}
return true;
}
plAGDetachCallbackMsg *detachMsg = plAGDetachCallbackMsg::ConvertNoRef(msg);
if (detachMsg != nil)
{
DetachAnimation(detachMsg->GetAnimName());
}
plGenRefMsg *genRefMsg;
if (genRefMsg = plGenRefMsg::ConvertNoRef(msg))
{
plAGAnim *anim;
if (anim = plAGAnim::ConvertNoRef(genRefMsg->GetRef()))
{
if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
{
if (genRefMsg->fType == kPrivateAnim)
fPrivateAnims.push_back(anim);
}
else
{
if (genRefMsg->fType == kPrivateAnim)
{
plAnimVector::iterator i = fPrivateAnims.begin();
for (i; i != fPrivateAnims.end(); i++)
{
plAGAnim *currAnim = *i;
if(currAnim == anim)
{
i = fPrivateAnims.erase(i);
break;
}
}
}
}
return true;
}
plAGModifier *agmod;
if (agmod = plAGModifier::ConvertNoRef(genRefMsg->GetRef()))
{
if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
fChannelMods[agmod->GetChannelName()] = agmod;
else
fChannelMods.erase(agmod->GetChannelName());
return true;
}
plMsgForwarder *msgfwd;
if (msgfwd = plMsgForwarder::ConvertNoRef(genRefMsg->GetRef()))
{
if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
fMsgForwarder = msgfwd;
else
fMsgForwarder = nil;
return true;
}
}
plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg);
if( refMsg )
{
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
AddTarget(plSceneObject::ConvertNoRef(refMsg->GetRef()));
else
RemoveTarget(plSceneObject::ConvertNoRef(refMsg->GetRef()));
return true;
}
return plModifier::MsgReceive(msg);
}
void plAGMasterMod::IRegForEval(hsBool val)
{
if (fNeedEval == val)
return;
fNeedEval = val;
if (val)
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
else
plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
}
hsBool plAGMasterMod::HasRunningAnims()
{
int i;
hsBool needEval = false;
for (i = 0; i < fAnimInstances.size(); i++)
{
if (!fAnimInstances[i]->IsFinished())
{
needEval = true;
break;
}
}
return needEval;
}
//
// Send SDL sendState msg to object's plAGMasterSDLModifier
//
hsBool plAGMasterMod::DirtySynchState(const char* SDLStateName, UInt32 synchFlags)
{
if(GetNumTargets() > 0 && (!fIsGrouped || fIsGroupMaster))
{
plSceneObject *sObj = GetTarget(0);
if(sObj)
return sObj->DirtySynchState(SDLStateName, synchFlags);
}
return false;
}
void plAGMasterMod::SetIsGroupMaster(bool master, plMsgForwarder* msgForwarder)
{
fIsGroupMaster = master;
fMsgForwarder = msgForwarder;
}
void plAGMasterMod::SetIsGrouped(bool grouped)
{
fIsGrouped = true;
}

View File

@ -0,0 +1,268 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/** \file plAGMasterMod.h
\brief The animation class for the AniGraph animation system
\ingroup Avatar
\ingroup AniGraph
*/
#ifndef PLAGMASTERMOD_INC
#define PLAGMASTERMOD_INC
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include "../pnModifier/plModifier.h"
#include "plAGChannel.h"
#include "plAvDefs.h"
#include "../pnKeyedObject/plMsgForwarder.h"
// templates
#include "hsStlUtils.h"
#include "hsStlSortUtils.h"
class plAGModifier;
class plAGAnimInstance;
class plAGAnim;
class plATCAnim;
class plAGMasterSDLModifier;
////////////////
//
// PLAGMASTERMOD
//
////////////////
/** \class plAGMasterMod
A modifier which can apply animations to scene objects.
Works together with a plAGModifier, which can apply animation to a
single scene object. A master modifier hooks up to a family of
ag modifiers to do its job.
An animation (plAGAnim) can have many different channels, but the
user can just pass it to a single master mod to apply it to all the
channels owned by that master. The master will take care of hooking
each channel up to an ag modifier.
The goal is to make an animation a conceptually (and practically) simple
object to work with, whether or not it has multiple channels.
\sa plAGAnim, plAGAnimInstance, plAGModifier
*/
class plAGMasterMod : public plModifier
{
friend class plAGMasterSDLModifier;
public:
/** Default constructor. Primarily for use by the class factory. */
plAGMasterMod();
/** Free the name and any other miscellany. */
virtual ~plAGMasterMod();
/** Find an individual plAGModifier of the given name under our control. */
plAGModifier * GetChannelMod(const char * name, hsBool dontCache = false) const;
/** \name Managing Animations */
// \{
// AGANIM PROTOCOL
/** Attach the given animation object with the given blend factor.
If there's no animation already attached to blend with, the
animation will be attached at full strength. */
plAGAnimInstance *AttachAnimationBlended(plAGAnim *anim, hsScalar blendFactor = 0,
UInt16 blendPriority = kAGMedBlendPriority,
hsBool cache = false);
/** Look up the given animation by name and attach it
with the given blend factor. */
plAGAnimInstance *AttachAnimationBlended(const char *name, hsScalar blendFactor = 0,
UInt16 blendPriority = kAGMedBlendPriority,
hsBool cache = false);
/** Play a simple anim (one that doesn't affect root) once and auto detach.
Intended for Zandi's facial animations that run seperate from the behaviors. */
void PlaySimpleAnim(const char *name);
/** Detach the given animation instance. Does nothing
if the instance is not managed by this master mod. */
void DetachAnimation(plAGAnimInstance *instance);
void DetachAllAnimations();
/** Detach the given animation by name. Searches for
any instances derived from animations with the
given name and removes them. */
void DetachAnimation(const char *name);
// \}
/** Print the current animation stack to the console.
Will list all the animations and their blend strengths.
Animations later in the list will mask animations earlier
in the list. */
void DumpCurrentAnims(const char *header);
/** Find and return any animation instances with the
given name on this master modifer. */
plAGAnimInstance *FindAnimInstance(const char *name);
/** Return the Ith animation instance, based on blend
order. Of dubious utility, but, y'know. */
plAGAnimInstance *GetAnimInstance(int i);
/** Attach the animation if it's not already attached. If
it is attached, return the instance.
Note that if it's attached by this function, it
will be on top of the stack, but if it was already
attached, it could be anywhere, including buried under
a bunch of other animations. If it's important that it be
on top of the stack, you may need to detach it first. */
plAGAnimInstance *FindOrAttachInstance(const char *name, hsScalar blendFactor);
/** Return the number of animations available. */
int GetNumAnimations();
/** Return the number of animations that are privately
owned by this modifier.
Animations may be either shared in a general pool,
or privately owned by a mastermod. */
int GetNumPrivateAnimations();
int GetNumATCAnimations();
plAGAnimInstance *GetATCAnimInstance(int i);
/** Apply all our animations to all our parts.
\param timeNow is the current world time
\param elapsed is the time since the previous frame */
void ApplyAnimations(double timeNow, hsScalar elapsed);
/** Runs through our anims and applies them, without
processing fades. This is used when we load in anim
state from the server, and need to advance it to a
certain point before enabling callbacks */
void AdvanceAnimsToTime(double time);
/** Change the connectivity in the graph so that inactive animations are bypassed.
The original connectivity information is kept, so if the activity of different
animations is changed (such as by changing blend biases or adding new animations,
the graph can be compiled again to the correct state. */
void Compile(double time);
/** We've done something that invalidates the cached connectivity in the graph.
Mark this for fixup. */
void SetNeedCompile(bool needCompile);
/** List the animationg graph to stdOut, with a ASCII representation of the tree
structure. Done by recursively dumping the graph; some types of nodes will have
more output information than others.
*/
void DumpAniGraph(const char *channel, bool optimized, double time);
/** Set whether or not this is the "group master" so grouped animations will only have
one member getting/setting sdl animation state in order to synch the anims
*/
void SetIsGrouped(bool grouped);
void SetIsGroupMaster(bool master, plMsgForwarder* msgForwarder);
// PLASMA PROTOCOL
virtual int GetNumTargets() const { return fTarget ? 1 : 0; }
virtual plSceneObject* GetTarget(int w) const { /* hsAssert(w < GetNumTargets(), "Bad target"); */ return fTarget; }
virtual void AddTarget(plSceneObject * object);
virtual void RemoveTarget(plSceneObject * object);
hsBool plAGMasterMod::MsgReceive(plMessage* msg);
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream * stream, hsResMgr *mgr);
hsBool HasRunningAnims();
hsBool DirtySynchState(const char* SDLStateName, UInt32 synchFlags);
CLASSNAME_REGISTER( plAGMasterMod );
GETINTERFACE_ANY( plAGMasterMod, plModifier );
protected:
// -- methods --
plAGModifier * ICacheChannelMod(plAGModifier *mod) const;
plAGModifier * IFindChannelMod(const plSceneObject *obj, const char *name) const;
virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty);
virtual void IApplyDynamic() {}; // dummy function required by base class
// Find markers in an anim for environment effects (footsteps)
virtual void ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc) {}
// -- members
plSceneObject* fTarget;
// a map from channel names to ag modifiers within our domain
typedef std::map<const char *, plAGModifier*, stringSorter> plChannelModMap;
plChannelModMap fChannelMods;
// instances which are currently attached to this master
typedef std::vector<plAGAnimInstance*> plInstanceVector;
plInstanceVector fAnimInstances; // animation instances
// animations which are owned exclusively by this master
typedef std::vector<plAGAnim*> plAnimVector;
plAnimVector fPrivateAnims;
// animations that require AnimTimeConvert state to be synched
typedef std::vector<plAGAnim*> plAnimVector;
plInstanceVector fATCAnimInstances;
hsBool fFirstEval;
hsBool fNeedEval;
void IRegForEval(hsBool val);
// SDL modifier which sends/recvs dynamics state
plAGMasterSDLModifier *fAGMasterSDLMod;
bool fNeedCompile;
bool fIsGrouped;
bool fIsGroupMaster;
plMsgForwarder* fMsgForwarder;
enum {
kPrivateAnim,
kPublicAnim,
};
};
#endif // PLAGMASTERMOD_INC

View File

@ -0,0 +1,211 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plAGMasterSDLModifier.h"
#include "../plSDL/plSDL.h"
#include "../plInterp/plAnimTimeConvert.h"
#include "../pnSceneObject/plSceneObject.h"
#include "plAGMasterMod.h"
#include "plAGAnimInstance.h"
#include "plgDispatch.h"
#include "../pnMessage/plTimeMsg.h"
#include "hsTimer.h"
#include "../plMessage/plAnimCmdMsg.h"
// static vars
char plAGMasterSDLModifier::AGMasterVarNames::kStrAtcs[]="atcs";
char plAGMasterSDLModifier::AGMasterVarNames::kStrBlends[]="blends";
UInt32 plAGMasterSDLModifier::IApplyModFlags(UInt32 sendFlags)
{
// ugly hack so bug light animation state isn't stored on the server
if (stricmp(GetTarget()->GetKeyName(), "RTOmni-BugLightTest") == 0)
return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState);
// ditto for the KI light
if (stricmp(GetTarget()->GetKeyName(), "RTOmniKILight") == 0)
return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState);
return sendFlags;
}
//
// copy blends values from current state into sdl
//
void plAGMasterSDLModifier::IPutBlends(plStateDataRecord* state, plAGMasterMod* agMaster)
{
int numBlends = agMaster->GetNumPrivateAnimations(); // each private anim has a blend value
plSimpleStateVariable* blendsVar = state->FindVar(AGMasterVarNames::kStrBlends);
if (blendsVar->GetCount() != numBlends)
blendsVar->Alloc(numBlends);
// sdl copy
int i;
for(i=0;i<numBlends; i++)
{
blendsVar->Set((UInt8)(agMaster->GetAnimInstance(i)->GetBlend() * 255), i);
}
}
//
// Copy atcs from current state into sdl
//
void plAGMasterSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState)
{
plSceneObject* sobj=GetTarget();
hsAssert(sobj, "plAGMasterSDLModifier, nil target");
plAGMasterMod* agMaster=IGetObjectsAGMasterMod(sobj);
hsAssert(agMaster, "nil AGMasterMod");
if (agMaster)
{
IPutBlends(dstState, agMaster);
int numAnims = agMaster->GetNumATCAnimations();
plSDStateVariable* atcsVar = dstState->FindSDVar(AGMasterVarNames::kStrAtcs);
if (atcsVar->GetCount() != numAnims)
atcsVar->Resize(numAnims);
// copy atcs to sdl
int i;
for(i=0;i<numAnims; i++)
{
plStateDataRecord* atcStateDataRec = atcsVar->GetStateDataRecord(i);
plAnimTimeConvert* animTimeConvert = agMaster->GetATCAnimInstance(i)->GetTimeConvert();
IPutATC(atcStateDataRec, animTimeConvert);
}
}
}
//
// Given a scene object, find and return it's AGMasterMod
//
plAGMasterMod* plAGMasterSDLModifier::IGetObjectsAGMasterMod(plSceneObject* obj)
{
int count = obj->GetNumModifiers();
for (int i = 0; i < count; i++)
{
plAGMasterMod * avMod = const_cast<plAGMasterMod*>(plAGMasterMod::ConvertNoRef(obj->GetModifier(i)));
if(avMod)
return avMod;
}
return nil;
}
//
// Apply state in SDL record to current animation state
//
void plAGMasterSDLModifier::ISetCurrentBlends(const plStateDataRecord* state, plAGMasterMod* objAGMaster)
{
// Check Blends
plSimpleStateVariable* blendsVar = state->FindVar(AGMasterVarNames::kStrBlends);
if (blendsVar->IsUsed())
{
int i;
if (blendsVar->GetCount() != objAGMaster->GetNumPrivateAnimations())
return; // bogus state
for (i=0;i<blendsVar->GetCount();i++)
{
UInt8 blend;
blendsVar->Get(&blend, i);
objAGMaster->GetAnimInstance(i)->SetBlend(blend / 255.f);
}
}
}
//
// Change the object's animation state to reflect what is specified in the
// stateDataRecord.
//
void plAGMasterSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState)
{
plSceneObject* sobj=GetTarget();
hsAssert(sobj, "plAGMasterSDLModifier, nil target");
plAGMasterMod* objAGMaster=IGetObjectsAGMasterMod(sobj);
hsAssert(objAGMaster, "can't find object's AGMasterSDLState");
ISetCurrentBlends(srcState, objAGMaster);
plSDStateVariable* atcsVar = srcState->FindSDVar(AGMasterVarNames::kStrAtcs);
if (atcsVar->IsUsed())
{
if (objAGMaster->GetNumATCAnimations() != atcsVar->GetCount())
return;
int i;
for(i=0;i<atcsVar->GetCount(); i++)
{
plStateDataRecord* atcStateDataRec = atcsVar->GetStateDataRecord(i);
plAnimTimeConvert* objAtc = objAGMaster->GetATCAnimInstance(i)->GetTimeConvert(); // dst
ISetCurrentATC(atcStateDataRec, objAtc);
objAtc->EnableCallbacks(false);
}
objAGMaster->IRegForEval(objAGMaster->HasRunningAnims());
// Force one eval, then re-enable all the callbacks
double time = (hsTimer::GetSysSeconds() - hsTimer::GetDelSysSeconds());
if (objAGMaster->fIsGrouped && objAGMaster->fMsgForwarder)
{
hsScalar animTimeFromWorldTime = (objAGMaster->GetNumATCAnimations() > 0) ? objAGMaster->GetATCAnimInstance(0)->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) : 0.0f;
plAGCmdMsg *msg = TRACKED_NEW plAGCmdMsg();
msg->SetCmd(plAGCmdMsg::kSetAnimTime);
msg->fAnimTime = animTimeFromWorldTime;
msg->AddReceiver(objAGMaster->fMsgForwarder->GetKey());
plgDispatch::MsgSend(msg);
}
else
{
objAGMaster->AdvanceAnimsToTime(time);
}
for (i = 0; i < objAGMaster->GetNumATCAnimations(); i++)
objAGMaster->GetATCAnimInstance(i)->GetTimeConvert()->EnableCallbacks(true);
}
}

View File

@ -0,0 +1,86 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAGMasterSDLModifier_inc
#define plAGMasterSDLModifier_inc
#include "../plModifier/plAnimTimeConvertSDLModifier.h"
//
// This modifier is responsible for sending and recving
// an object's animation state
//
class plAGMasterMod;
class plSceneObject;
class plStateDataRecord;
class plAGMasterSDLModifier : public plAnimTimeConvertSDLModifier
{
protected:
// var labels
struct AGMasterVarNames
{
static char kStrAtcs[]; // animTimeConverts
static char kStrBlends[];
};
plAGMasterMod* IGetObjectsAGMasterMod(plSceneObject* obj);
void IPutBlends(plStateDataRecord* state, plAGMasterMod* objAGMaster);
void ISetCurrentBlends(const plStateDataRecord* state, plAGMasterMod* objAGMaster);
void IPutCurrentStateIn(plStateDataRecord* dstState);
void ISetCurrentStateFrom(const plStateDataRecord* srcState);
UInt32 IApplyModFlags(UInt32 sendFlags);
public:
CLASSNAME_REGISTER( plAGMasterSDLModifier);
GETINTERFACE_ANY( plAGMasterSDLModifier, plAnimTimeConvertSDLModifier);
plAGMasterSDLModifier() {}
~plAGMasterSDLModifier() {}
const char* GetSDLName() const { return kSDLAGMaster; }
};
#endif // plAGMasterSDLModifier_inc

View File

@ -0,0 +1,320 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
// local
#include "plAGModifier.h"
#include "plMatrixChannel.h"
// global
#include "hsResMgr.h"
#include "hsTimer.h"
// other
#include "../pnSceneObject/plSceneObject.h"
#include "../pnSceneObject/plCoordinateInterface.h"
#include "../pnSceneObject/plSimulationInterface.h"
/////////////////
//
// PLAGMODIFIER
//
/////////////////
// Applies animation graph output to a single scene object.
// CTOR
plAGModifier::plAGModifier()
: plSingleModifier()
{
fChannelName = nil;
fAutoApply = true;
fEnabled = true;
}
// CTOR(name)
plAGModifier::plAGModifier(const char *name, hsBool autoApply)
: plSingleModifier(), fAutoApply(autoApply)
{
fChannelName = hsStrcpy(name);
fEnabled = true;
}
// DTOR
plAGModifier::~plAGModifier()
{
if(fChannelName)
{
delete[] fChannelName;
fChannelName = nil;
}
int i;
for (i = 0; i < fApps.size(); i++)
{
delete fApps[i];
}
}
// GETCHANNELNAME
const char * plAGModifier::GetChannelName() const
{
return fChannelName;
}
// ENABLE
void plAGModifier::Enable(hsBool val)
{
fEnabled = val;
}
// SETCHANNELNAME
void plAGModifier::SetChannelName(char * name)
{
fChannelName = hsStrcpy(name);
}
// IAPPLYCHANNELS (time)
// One AGModifier, although applied to a single scene object, can have
// multiple channels on it affecting, say, position, color, aroma, etc.
//
// There are cases where we want to call this and won't know the delta,
// we don't seem to ever need it for this function, so I'm taking it out.
// If you run into a case where you think it's necessary, see me. -Bob
void plAGModifier::Apply(double time) const
{
if (!fEnabled)
return;
for (int i = 0; i < fApps.size(); i++)
{
plAGApplicator *app = fApps[i];
app->Apply(this, time);
}
}
// IEVAL
// Apply our channels to our scene object
hsBool plAGModifier::IEval(double time, hsScalar delta, UInt32 dirty)
{
if(fAutoApply) {
// Apply(time, delta);
}
return true;
}
// GETAPPLICATOR
plAGApplicator * plAGModifier::GetApplicator(plAGPinType pinType) const
{
int numApps = fApps.size();
for (int i = 0; i < numApps; i++)
{
plAGApplicator *app = fApps[i];
plAGPinType otherType = app->GetPinType();
if(otherType == pinType)
return app;
}
return nil;
}
// SETAPPLICATOR
void plAGModifier::SetApplicator(plAGApplicator *newApp)
{
int numApps = fApps.size();
plAGPinType newPinType = newApp->GetPinType();
// *** NOTE: this code is completely untested. Since I happened to be here
// I sketched out how it *should* work and implemented the base protocol.
// In reality, most of these code paths are not accessed now...
// -- mm
for(int i = 0; i < numApps; i++)
{
plAGApplicator *existingApp = fApps[i];
plAGPinType extPinType = existingApp->GetPinType();
if(extPinType == newPinType)
{
hsStatusMessage("Two applicators accessing same pin type...congratulations for being the first to test this.");
// these applicators both try to set the same thing; try to merge them
plAGChannel *newChannel = newApp->GetChannel();
hsAssert(newChannel = nil, "Trying to merge in new applicator which already has channel. Incomplete.");
// *** right now I just want to support the case of putting in a new applicator - not merging animations
plAGChannel *extChannel = existingApp->GetChannel();
newApp->SetChannel(extChannel);
existingApp->SetChannel(nil);
fApps[i] = newApp;
delete existingApp;
return;
// NOTE: we should make these arbitrate, but I'm not going to right now because
// there's not currently an (easy) way to merge two applicators without allowing a blend.
// if(existingApp->CanCombine(newApp))
// {
// // the existing applicator promises to provide the functionality we need...merge into it.
// existingApp->MergeChannel(newApp);
// } else {
// // couldn't merge into the existing channel; can we merge it into us instead?
// if(newApp->CanCombine(extApp))
// {
// // okay, WE can provide the functionality of the existing applicator.
// fApps[i] = newApp; // take over its spot in the applicators
// newApp->MergeChannel(existingApp); // and merge it into us
// }
// }
}
}
// didn't find any conflicts; just add our app on the end
fApps.push_back(newApp);
}
// MERGECHANNEL
// Intended as a replacement for attach/blend channel. You want to add a channel to this node,
// we do that for you. Don't ask us how, you shouldn't have to know.
plAGChannel * plAGModifier::MergeChannel(plAGApplicator *app,
plAGChannel *channel,
plScalarChannel *blend,
plAGAnimInstance *anim,
int priority)
{
int numApps = fApps.size();
plAGChannel * result = nil;
for (int i = 0; i < numApps; i++)
{
plAGApplicator *existingApp = fApps[i];
result = existingApp->MergeChannel(app, channel, blend, priority);
if (result)
return result;
}
if (!result)
{
// didn't blend or combine with an existing channel; add a new channel
plAGApplicator *newApp = app->CloneWithChannel(channel);
fApps.push_back(newApp);
}
return result;
}
// DETACHCHANNEL
hsBool plAGModifier::DetachChannel(plAGChannel * channel)
{
plAppTable::iterator i = fApps.begin();
while( i != fApps.end() )
{
plAGApplicator *app = *i;
plAGChannel *existingChannel = app->GetChannel();
if(existingChannel)
{
plAGChannel *replacementChannel = existingChannel->Detach(channel);
if (existingChannel != replacementChannel)
{
app->SetChannel(replacementChannel);
if( ! replacementChannel && app->AutoDelete())
{
// Don't need to adjust the iterator since we're about to exit the loop
fApps.erase(i);
delete app;
}
return true;
}
}
++i;
}
return false;
}
// READ
void plAGModifier::Read(hsStream *stream, hsResMgr *mgr)
{
plSingleModifier::Read(stream, mgr);
// read in the name of the modifier
fChannelName = stream->ReadSafeString();
}
// WRITE
void plAGModifier::Write(hsStream *stream, hsResMgr *mgr)
{
plSingleModifier::Write(stream, mgr);
// write out the name of the modifier
stream->WriteSafeString(fChannelName);
}
/////////////////////////////////////////
/////////////////////////////////////////
//
// MOVE
//
/////////////////////////////////////////
/////////////////////////////////////////
const plModifier * FindModifierByClass(const plSceneObject *obj, int classID)
{
if(obj)
{
int modCount = obj->GetNumModifiers();
for (int i = 0; i < modCount; i++)
{
const plModifier *mod = obj->GetModifier(i);
if(mod) // modifier might not be loaded yet
{
if(mod->ClassIndex() == classID)
{
return mod;
}
}
}
}
return nil;
}

View File

@ -0,0 +1,170 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/** \file plAGMasterMod.h
\ingroup Avatar
\ingroup AniGraph
*/
#ifndef PLAGMODIFIER_H
#define PLAGMODIFIER_H
#include "hsTypes.h" // need for plSingleModifier
#include "../pnModifier/plSingleModifier.h" // inherited
// local
#include "../plAvatar/plScalarChannel.h"
// stl
#include "hsStlUtils.h"
#include "hsStlSortUtils.h"
class plSceneObject;
class plAGAnimInstance;
class plAGAnim;
class plAvatarAnim;
class plAnimCmdMsg;
class plOneShotCallbacks;
///////////////
//
// PLAGMODIFIER
//
///////////////
/** \class plAGModifier
Animates a single scene object.
May apply multiple animations to that scene object, animating
the transform with one, colors with another, etc.
The basic model is that the ag modifier holds an array of
applicators, each of which is the root of a tree of animation
operations, such as a blend tree, etc. Each frame it fires
all its applicators, or (ideally) only those that need to be fired.
*/
class plAGModifier : public plSingleModifier
{
public:
/** Default constructor, primarily for the class factory. */
plAGModifier();
/** Construct given a name. This name will be used to match up
incoming channels with this modifier. You may also supply an
autoApply parameter, which indicates whether this modifier
should apply itself every frame, or only when explicitly asked to. */
plAGModifier(const char *name, hsBool autoApply = true);
/** It's a destructor. Destroys the name passed into the constructor,
and a bunch of other stuff you don't need to know anything about. */
virtual ~plAGModifier();
/** Get the name of the channel controlled by this modifier. */
const char * GetChannelName() const;
/** Change the channel name of the modifier. Will delete the previous
name. Will NOT remove any channels that are already attached, so
you could wind up with a modifier named "Fred" and a bunch of
channels attached to it that were intended for "Lamont." */
void SetChannelName(char * name);
/** Attach a TRACKED_NEW applicator to our modifier. Will arbitrate with existing
modifiers if necessary, based on pin type. May destruct existing applicators. */
plAGChannel *MergeChannel(plAGApplicator *app, plAGChannel *chan, plScalarChannel *blend,
plAGAnimInstance *anim, int priority);
/** Remove the given channel. Will also remove the channel's applicator. */
hsBool DetachChannel(plAGChannel * channel);
/** Set the applicator for a modifier.
\deprecated */
void SetApplicator(plAGApplicator *app);
/** Get the applicator for a given pintype. Note that an ag modifier can
only have one applicator of a given pin type attached at a time.
The exception is for the unknown pin type, which never conflicts
with any other pin type, including itself. */
plAGApplicator *GetApplicator(plAGPinType pin) const;
/** Apply the animation for our scene object. */
void Apply(double time) const;
/** Get the channel tied to our ith applicator */
plAGChannel * GetChannel(int i) { return fApps[i]->GetChannel(); }
void Enable(hsBool val);
// PERSISTENCE
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual void Write(hsStream *stream, hsResMgr *mgr);
// PLASMA PROTOCOL
CLASSNAME_REGISTER( plAGModifier );
GETINTERFACE_ANY( plAGModifier, plSingleModifier );
protected:
typedef std::vector<plAGApplicator*> plAppTable;
plAppTable fApps; // the applicators (with respective channels) that we're applying to our scene object
char *fChannelName; // name used for matching animation channels to this modifier
hsBool fAutoApply; // evaluate animation automatically during IEval call
hsBool fEnabled; // if not enabled, we don't eval any of our anims
// APPLYING THE ANIMATION
virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty);
virtual hsBool IHandleCmd(plAnimCmdMsg* modMsg) { return false; } // only plAGMasterMod should handle these
virtual void IApplyDynamic() {}; // dummy function required by base class
// INTERNAL ACCESSORS FOR SCENE OBJECT INTERFACES
plAudioInterface * LeakAI() const { return IGetTargetAudioInterface(0); };
plCoordinateInterface * LeakCI() const { return IGetTargetCoordinateInterface(0); };
plDrawInterface * LeakDI() const { return IGetTargetDrawInterface(0); };
plSimulationInterface * LeakSI() const { return IGetTargetSimulationInterface(0); };
plObjInterface * LeakGI(UInt32 classIdx) const { return IGetTargetGenericInterface(0, classIdx); }
friend plAudioInterface * plAGApplicator::IGetAI(const plAGModifier * modifier) const;
friend plCoordinateInterface * plAGApplicator::IGetCI(const plAGModifier * modifier) const;
friend plDrawInterface * plAGApplicator::IGetDI(const plAGModifier * modifier) const;
friend plSimulationInterface * plAGApplicator::IGetSI(const plAGModifier * modifier) const;
friend plObjInterface * plAGApplicator::IGetGI(const plAGModifier * modifier, UInt16 classIdx) const;
};
const plModifier * FindModifierByClass(const plSceneObject *obj, int classID);
#endif

View File

@ -0,0 +1,795 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
// singular
#include "plAnimStage.h"
// local
#include "plAvatarMgr.h"
#include "plAGAnim.h"
#include "plArmatureMod.h"
#include "plAGAnimInstance.h"
#include "plMatrixChannel.h"
#include "plAvBrainGeneric.h"
#include "plMultiStageBehMod.h"
// global
#include "hsUtils.h"
#include "hsStlUtils.h"
#include "hsResMgr.h"
#include "hsTimer.h"
#include <cstdio>
// other
#include "../pnSceneObject/plSceneObject.h"
#include "../plMessage/plSimStateMsg.h"
#include "../plStatusLog/plStatusLog.h"
#include "../pnMessage/plNotifyMsg.h"
#include "../plPipeline/plDebugText.h"
#ifdef DEBUG_MULTISTAGE
#include "plAvatarMgr.h"
#include "../plStatusLog/plStatusLog.h"
#endif
class plAGAnim;
// PLANIMSTAGE default ctor
plAnimStage::plAnimStage()
: fAnimName(nil),
fNotify(0),
fArmature(nil),
fBrain(nil),
fForwardType(kForwardNone),
fBackType(kBackNone),
fAdvanceType(kAdvanceNone),
fRegressType(kRegressNone),
fLoops(0),
fAnimInstance(nil),
fLocalTime(0.0f),
fLength(0.0f),
fCurLoop(0),
fAttached(false),
fDoAdvanceTo(false),
fAdvanceTo(0),
fDoRegressTo(false),
fRegressTo(0),
fMod(nil),
fSentNotifies(0),
fReverseOnIdle(false),
fDone(false)
{
}
plAnimStage::plAnimStage(const char *animName, UInt8 notify)
: fNotify(notify),
fArmature(nil),
fBrain(nil),
fForwardType(kForwardAuto), // different from default
fBackType(kBackNone),
fAdvanceType(kAdvanceAuto), // different from default
fRegressType(kRegressNone),
fLoops(0),
fAnimInstance(nil),
fLocalTime(0.0f),
fLength(0.0f),
fCurLoop(0),
fAttached(false),
fDoAdvanceTo(false),
fAdvanceTo(0),
fDoRegressTo(false),
fRegressTo(0),
fMod(nil),
fSentNotifies(0),
fReverseOnIdle(false),
fDone(false)
{
fAnimName = hsStrcpy(animName);
}
// PLANIMSTAGE canonical ctor
plAnimStage::plAnimStage(const char *animName,
UInt8 notify,
ForwardType forward,
BackType back,
AdvanceType advance,
RegressType regress,
int loops)
: fArmature(nil),
fBrain(nil),
fNotify(notify),
fForwardType(forward),
fBackType(back),
fAdvanceType(advance),
fRegressType(regress),
fLoops(loops),
fAnimInstance(nil),
fLocalTime(0.0f),
fLength(0.0f),
fCurLoop(0),
fAttached(false),
fDoAdvanceTo(false),
fAdvanceTo(0),
fDoRegressTo(false),
fRegressTo(0),
fMod(nil),
fSentNotifies(0),
fReverseOnIdle(false),
fDone(false)
{
fAnimName = hsStrcpy(animName);
}
plAnimStage::plAnimStage(const char *animName,
UInt8 notify,
ForwardType forward,
BackType back,
AdvanceType advance,
RegressType regress,
int loops,
bool doAdvanceTo,
int advanceTo,
bool doRegressTo,
int regressTo)
: fArmature(nil),
fBrain(nil),
fNotify(notify),
fForwardType(forward),
fBackType(back),
fAdvanceType(advance),
fRegressType(regress),
fLoops(loops),
fAnimInstance(nil),
fLocalTime(0.0f),
fLength(0.0f),
fCurLoop(0),
fAttached(false),
fDoAdvanceTo(doAdvanceTo),
fAdvanceTo(advanceTo),
fDoRegressTo(doRegressTo),
fRegressTo(regressTo),
fMod(nil),
fSentNotifies(0),
fReverseOnIdle(false),
fDone(false)
{
fAnimName = hsStrcpy(animName);
}
// PLANIMSTAGE dtor
plAnimStage::~plAnimStage()
{
if(fAnimName)
delete[] fAnimName;
hsAssert(fAnimInstance == nil, "plAnimStage still has anim instance during destruction. (that's bad.)");
// we could delete the animation instance here, but it should have been deleted already...
// *** check back in a while....
}
// operator= ----------------------------------------------------
// ----------
const plAnimStage& plAnimStage::operator=(const plAnimStage& src)
{
fAnimName = hsStrcpy(src.fAnimName);
fNotify = src.fNotify;
fForwardType = src.fForwardType;
fBackType = src.fBackType;
fAdvanceType = src.fAdvanceType;
fRegressType = src.fRegressType;
fLoops = src.fLoops;
fDoAdvanceTo = src.fDoAdvanceTo;
fAdvanceTo = src.fAdvanceTo;
fDoRegressTo = src.fDoRegressTo;
fRegressTo = src.fRegressTo;
fMod = src.fMod;
fAnimInstance = nil;
fLocalTime = 0.0f;
fLength = 0.0f;
fCurLoop = 0;
fAttached = false;
fReverseOnIdle = src.fReverseOnIdle;
return *this;
}
// attach --------------------------------------------------------------------------------------------------------
// -------
plAGAnimInstance * plAnimStage::Attach(plArmatureMod *armature, plArmatureBrain *brain, float initialBlend, double time)
{
// NOTE: you need to be able to detach an animstage and then re-attach it and have
// it wind up in exactly the same state it was in before - for loading and saving.
fBrain = brain;
fSentNotifies = 0;
fArmature = armature;
if(fAnimInstance)
{
fAnimInstance->SetBlend(initialBlend);
} else {
plAGAnim *anim = armature->FindCustomAnim(fAnimName);
if(anim)
{
fLength = anim->GetEnd();
fAnimInstance = armature->AttachAnimationBlended(anim, initialBlend);
fAnimInstance->SetCurrentTime(fLocalTime);
#ifdef DEBUG_MULTISTAGE
char sbuf[256];
sprintf(sbuf,"AnimStage::Attach - attaching stage %s",fAnimName);
plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf);
#endif
} else {
char buf[256];
sprintf(buf, "Can't find animation <%s> for animation stage. Anything could happen.", fAnimName);
hsAssert(false, buf);
#ifdef DEBUG_MULTISTAGE
plAvatarMgr::GetInstance()->GetLog()->AddLine(buf);
#endif
}
}
if(fAnimInstance)
{
fAnimInstance->Stop(); // we'll be setting the time directly.
fAnimatedHandle = (fAnimInstance->GetAnimation()->GetChannel("Handle") != nil);
fAttached = true;
// this is too early to send the enter notify. we're attached, but we may not
// have faded in yet.
// XXX ISendNotify(kNotifyEnter, proEventData::kEnterStage, armature, brain);
}
return fAnimInstance;
}
// SENDNOTIFY
hsBool plAnimStage::ISendNotify(UInt32 notifyMask, UInt32 notifyType, plArmatureMod *armature, plArmatureBrain *brain)
{
// make sure the user has requested this type of notify
if(fNotify & notifyMask)
{
plKey avKey = armature->GetTarget(0)->GetKey();
if (fMod)
avKey = fMod->GetKey();
plNotifyMsg *msg = TRACKED_NEW plNotifyMsg();
msg->SetSender(avKey);
if (fMod)
{
msg->SetBCastFlag(plMessage::kNetPropagate, fMod->NetProp());
msg->SetBCastFlag(plMessage::kNetForce, fMod->NetForce());
}
else
{
msg->SetBCastFlag(plMessage::kNetPropagate, false);
msg->SetBCastFlag(plMessage::kNetForce, false);
}
plAvBrainGeneric *genBrain = plAvBrainGeneric::ConvertNoRef(brain);
int stageNum = genBrain ? genBrain->GetStageNum(this) : -1;
msg->AddMultiStageEvent(stageNum, notifyType, armature->GetTarget(0)->GetKey());
if (! genBrain->RelayNotifyMsg(msg) )
{
msg->UnRef(); // couldn't send; destroy...
}
return true;
}
return false;
}
// DETACH
hsBool plAnimStage::Detach(plArmatureMod *armature)
{
hsBool result = false;
#ifdef DEBUG_MULTISTAGE
char sbuf[256];
sprintf(sbuf,"AnimStage::Detach - detaching stage %s",fAnimName);
plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf);
#endif
// hsStatusMessageF("Detaching plAnimStage <%s>", fAnimName);
if(fArmature) {
fArmature = nil;
if(fAnimInstance) {
armature->DetachAnimation(fAnimInstance); // detach instantly
fAnimInstance = nil;
result = true;
}
#ifdef DEBUG_MULTISTAGE
} else {
char sbuf[256];
sprintf(sbuf,"AnimStage::Detach: stage already detached");
plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf);
#endif
// hsStatusMessageF("Detach: stage already detached.");
}
fBrain = nil;
fAttached = false;
return result;
}
void plAnimStage::Reset(double time, plArmatureMod *avMod, bool atStart)
{
if(atStart)
SetLocalTime(0.0f, true);
else
SetLocalTime(fLength, true);
avMod->GetRootAnimator()->Reset(time);
}
void plAnimStage::ResetAtTime(double globalTime, float localTime, plArmatureMod *avMod)
{
SetLocalTime(localTime, true);
avMod->GetRootAnimator()->Reset(globalTime);
}
// MoveRelative ------------------------------
// -------------
// A true result means that the stage is done.
bool plAnimStage::MoveRelative(double time, float delta, float &overage, plArmatureMod *avMod)
{
bool result; // true means the stage is done
if(fLocalTime == 0.0f && delta >= 0.0f && !hsCheckBits(fSentNotifies, kNotifyEnter))
{
// we send the "enter" notify if we're at the start and either moving forward
// or standing still.
ISendNotify(kNotifyEnter, proEventData::kEnterStage, avMod, fBrain);
hsSetBits(fSentNotifies, kNotifyEnter);
}
// aborting...
if( fAdvanceType == kAdvanceOnMove && (avMod->HasMovementFlag() || avMod->ExitModeKeyDown()))
{ // special case: advance when any key is pressed, regardless of position in stage.
ISendNotify(kNotifyAdvance, proEventData::kAdvanceNextStage, avMod, fBrain);
result = true;
} else {
if(delta == 0.0f)
{
return false;
}
else
if(delta < 0.0f)
result = IMoveBackward(time, delta, overage, avMod);
else
result = IMoveForward(time, delta, overage, avMod);
}
return result;
}
// IMoveBackward ------------------------------------------------------------------------------
// --------------
bool plAnimStage::IMoveBackward(double time, float delta, float &overrun, plArmatureMod *avMod)
{
if (fLocalTime <= 0 && fRegressType == kRegressNone)
{
// If we're at the beginning, but not allowed to regress, we don't want to keep processing
// (and firing triggers).
return false;
}
float target = fLocalTime + delta;
bool infiniteLoop = fLoops == -1;
bool loopsRemain = fCurLoop > 0 || infiniteLoop;
// This must be here before we set the local time.
if (fAnimInstance->GetTimeConvert())
fAnimInstance->GetTimeConvert()->Backwards();
if(target < 0)
{
SetLocalTime(0); // animation to beginning
avMod->GetRootAGMod()->Apply(time); // move avatar to beginning
if(loopsRemain)
{
// If a callback is on the last frame, it'll get triggered twice. Once for setting
// the anim at the end, and once when we play again. So we don't fire callbacks here.
SetLocalTime(fLength, true); // animation wraps to end
avMod->GetRootAnimator()->Reset(time); // reset the root animator at the end
fCurLoop = infiniteLoop ? 0 : fCurLoop - 1;
target = fLength - (fmodf(-target, fLength)); // modularize negative number to discard
// extra loops (only one allowed)
} else {
// overrun = target + fLength;
// now we want to make sure that overrun goes negative when appropriate, rather than modularizing
overrun = target;
fDone = true;
return ITryRegress(avMod);
}
}
overrun = 0.0f;
fLocalTime = target;
fAnimInstance->SetCurrentTime(fLocalTime);
return false; // not done
}
// IMoveForward ------------------------------------------------------------------------------
// -------------
// It's currently not supported to advance the animation by more than its length in one frame.
// It wouldn't be too hard to add, but it clutters things up and it hasn't been shown to
// be necessary.
bool plAnimStage::IMoveForward(double time, float delta, float &overrun, plArmatureMod *avMod)
{
if (fLocalTime >= fLength && fAdvanceType == kAdvanceNone)
{
// If we're at the end, but not allowed to advance, we don't want to keep processing
// (and firing triggers).
return false;
}
// first get the target time in local time, ignoring overruns
float target = fLocalTime + delta;
if (fAnimInstance->GetTimeConvert())
fAnimInstance->GetTimeConvert()->Forewards();
if (target > fLength)
{
// we're going to the end for sure, so do that first
SetLocalTime(fLength);
// we're going to swap in a new animation before the next eval, so force
// an apply of this one to make sure the avatar gets the necessary movement.
// we only apply on the root node to get the movement -- no need to animate
// the fingers as they'll be overwritten when the next animation is swapped in.
avMod->GetRootAGMod()->Apply(time);
// are there *any* loops to be had?
bool loopsRemain = fCurLoop < fLoops || fLoops == -1;
if(loopsRemain)
{
SetLocalTime(0.0f, true); // animation back to beginning
avMod->GetRootAnimator()->Reset(time); // reset the root animator's frame cache
fCurLoop++;
target = fmodf(target, fLength); // discard extra loops (only one at a time allowed)
// target -= fLength;
} else {
overrun = target - fLength;
fDone = true;
return ITryAdvance(avMod);
}
}
overrun = 0.0f;
fLocalTime = target;
avMod->GetRootAGMod()->Apply(time);
fAnimInstance->SetCurrentTime(fLocalTime);
return false; // not done
}
bool plAnimStage::ITryAdvance(plArmatureMod *avMod)
{
bool stageDone = false;
// hsStatusMessageF("Sending advance message for stage <%s>\n", fAnimName);
if(fAdvanceType == kAdvanceAuto || fAdvanceType == kAdvanceOnMove) {
stageDone = true;
}
if(!hsCheckBits(fSentNotifies, kNotifyAdvance))
{
// we send the advance message at the point where we *would* advance, whether
// or not we actually do. this is misleading but better suited to actual current usage.
// we may want to rename this to "ReachedStageEnd"
ISendNotify(kNotifyAdvance, proEventData::kAdvanceNextStage, avMod, fBrain);
hsSetBits(fSentNotifies, kNotifyAdvance);
}
return stageDone;
}
bool plAnimStage::ITryRegress(plArmatureMod *avMod)
{
bool stageDone = false;
// we send the advance message at the point where we *would* advance, whether
// or not we actually do. this is misleading but better suited to actual current usage.
// we may want to rename this to "ReachedStageEnd"
ISendNotify(kNotifyRegress, proEventData::kRegressPrevStage, avMod, fBrain);
// hsStatusMessageF("Sending regress message for stage <%s>\n", fAnimName);
if(fRegressType == kRegressAuto) {
stageDone = true;
}
return stageDone;
}
// GETANIMNAME
const char * plAnimStage::GetAnimName()
{
return fAnimName;
}
// GETFORWARDTYPE
plAnimStage::ForwardType plAnimStage::GetForwardType()
{
return fForwardType;
}
// SETFORWARDTYPE
void plAnimStage::SetForwardType(ForwardType t)
{
fForwardType = t;
}
// GETBACKTYPE
plAnimStage::BackType plAnimStage::GetBackType()
{
return fBackType;
}
// SETBACKTYPE
void plAnimStage::SetBackType(BackType t)
{
fBackType = t;
}
// GETADVANCETYPE
plAnimStage::AdvanceType plAnimStage::GetAdvanceType()
{
return fAdvanceType;
}
// SETADVANCETYPE
void plAnimStage::SetAdvanceType(AdvanceType t)
{
fAdvanceType = t;
}
// GETREGRESSTYPE
plAnimStage::RegressType plAnimStage::GetRegressType()
{
return fRegressType;
}
// SETREGRESSTYPE
void plAnimStage::SetRegresstype(RegressType t)
{
fRegressType = t;
}
// GETNOTIFYFLAGS
UInt32 plAnimStage::GetNotifyFlags()
{
return fNotify;
}
// SETNOTIFYFLAGS
void plAnimStage::SetNotifyFlags(UInt32 newFlags)
{
fNotify = (UInt8)newFlags;
}
// GETNUMLOOPS
int plAnimStage::GetNumLoops()
{
return fLoops;
}
// SETNUMLOOPS
void plAnimStage::SetNumLoops(int loops)
{
// I'm very suspicious of this if statement...
// 1. It's preceded by an assert whose condition is the opposite the error message
// (which I've now commented out)
// 2. It only matters if the avatar is currently in the stage.
// 3. It doesn't seem intuitive that if I'm currently on loop 3 and you
// change the number of loops to 6, that I should jump to the end.
// BUT...
// It's been like this for ages, so I'm not touching it until a break shows a problem.
//
//hsAssert(loops < fCurLoop, "Setting loopcount below current loop");
if(loops >= fCurLoop) {
fCurLoop = loops;
}
fLoops = loops;
}
// GETLOOPVALUE
int plAnimStage::GetLoopValue()
{
return fCurLoop;
}
// SETLOOPVALUE
void plAnimStage::SetLoopValue(int value)
{
fCurLoop = value;
}
// GETLOCALTIME
float plAnimStage::GetLocalTime()
{
return fLocalTime;
}
// SETLOCALTIME
void plAnimStage::SetLocalTime(float time, hsBool noCallbacks /* = false */)
{
fLocalTime = time;
if(fAnimInstance)
fAnimInstance->SetCurrentTime(time, noCallbacks);
}
// GETLENGTH
float plAnimStage::GetLength()
{
return fLength;
}
// GETISATTACHED
bool plAnimStage::GetIsAttached()
{
return fAttached;
}
// SETISATTACHED
void plAnimStage::SetIsAttached(bool status)
{
fAttached = status;
}
// GETNEXTSTAGE
int plAnimStage::GetNextStage(int curStage)
{
if(fDoAdvanceTo)
{
return fAdvanceTo;
} else {
return curStage + 1;
}
}
// GETPREVSTAGE
int plAnimStage::GetPrevStage(int curStage)
{
if(fDoRegressTo)
{
return fRegressTo;
} else {
return curStage - 1;
}
}
// DUMPDEBUG
void plAnimStage::DumpDebug(bool active, int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt)
{
std::string str;
str += fAnimName;
str += " ";
if(fLoops) {
sprintf(strBuf, "loop(%d/%d)", fCurLoop, fLoops);
str += strBuf;
}
sprintf(strBuf, "time: (%f/%f)", fLocalTime, fLength);
str += strBuf;
if(active)
debugTxt.DrawString(x, y, str.c_str(), 0, 255, 0);
else if(fAnimInstance)
debugTxt.DrawString(x, y, str.c_str());
else
debugTxt.DrawString(x, y, str.c_str(), 255, 255, 0);
y += lineHeight;
}
// READ
void plAnimStage::Read(hsStream *stream, hsResMgr *mgr)
{
delete [] fAnimName;
fAnimName = stream->ReadSafeString();
fNotify = stream->ReadByte();
fForwardType = (ForwardType)stream->ReadSwap32();
fBackType = (BackType)stream->ReadSwap32();
fAdvanceType = (AdvanceType)stream->ReadSwap32();
fRegressType = (RegressType)stream->ReadSwap32();
fLoops = stream->ReadSwap32();
fDoAdvanceTo = stream->Readbool();
fAdvanceTo = stream->ReadSwap32();
fDoRegressTo = stream->Readbool();
fRegressTo = stream->ReadSwap32();
}
void plAnimStage::Write(hsStream *stream, hsResMgr *mgr)
{
stream->WriteSafeString(fAnimName);
stream->WriteByte(fNotify);
stream->WriteSwap32(fForwardType);
stream->WriteSwap32(fBackType);
stream->WriteSwap32(fAdvanceType);
stream->WriteSwap32(fRegressType);
stream->WriteSwap32(fLoops);
stream->Writebool(fDoAdvanceTo);
stream->WriteSwap32(fAdvanceTo);
stream->Writebool(fDoRegressTo);
stream->WriteSwap32(fRegressTo);
}
// SAVEAUX
void plAnimStage::SaveAux(hsStream *stream, hsResMgr *mgr)
{
stream->WriteSwapScalar(fLocalTime);
stream->WriteSwapScalar(fLength);
stream->WriteSwap32(fCurLoop);
stream->Writebool(fAttached);
// no ephemeral stage at the moment
}
// LOADAUX
void plAnimStage::LoadAux(hsStream *stream, hsResMgr *mgr, double time)
{
fLocalTime = stream->ReadSwapScalar();
fLength = stream->ReadSwapScalar();
fCurLoop = stream->ReadSwap32();
// This should actually be Readbool (lowercase), but I won't fix it since that
// would require a version change
fAttached = (stream->Readbool() != 0);
}

View File

@ -0,0 +1,289 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/** \file plAnimStage.h
An intelligent avatar animation for use in a generic brain.
\ingroup Avatar
*/
#ifndef PL_ANIM_STAGE_INC
#define PL_ANIM_STAGE_INC
#include "hsMatrix44.h"
#include "hsStlUtils.h"
#include "../pnFactory/plCreatable.h"
class plMessage;
class plAGAnimInstance;
class plArmatureMod;
class plArmatureMod;
class hsStream;
class hsResMgr;
class plArmatureBrain;
class plDebugText;
class plMultistageBehMod;
// PLANIMSTAGE
// In a multi-stage behavior, each stage is specified in one of these classes
/** \class plAnimStage
An animation stage is an intelligent wrapper for an avatar animation.
It provides specific control over:
- how the progress of the animation is controlled
- when the animation optionally sends messages about its progress
- whether the animation naturally progresses to the next animation after finishing
- how many times the animation loops
- whether the animation works in world space or a local space
*/
class plAnimStage : public plCreatable
{
public:
// -- forward declarations of types --
// Points to send notifications at
/** \enum NotifyType
Controls when notification messages are sent by the stage.
These can be used instead of or in combination with
normal animation callback messages.
Where the messages are sent is determined by the controlling
brain.
These are mask bits; you can have any number of them on at a time.
*/
enum NotifyType
{
kNotifyEnter = 0x1, /// Send when we enter the stage from
kNotifyLoop = 0x2, /// Sent each time the stage loops
kNotifyAdvance = 0x4, /// Sent when we advance out of the stage
kNotifyRegress = 0x8 /// Sent when we regress out of the stage
};
/** \enum ForwardType
What makes the stage progress in a forward direction?
You can only choose one of these, although a stage will always respond
to forward movement messages. */
enum ForwardType {
kForwardNone, /// move forward only in response to messages
kForwardKey, /// move forward when the user presses the move forward key
kForwardAuto, /// move forward automatically
kForwardMax,
} fForwardType;
/** \enum BackType
What makes the stage progress in a backward direction?
You can only choose one of these, although a stage will always respond
to backward movement message. */
enum BackType {
kBackNone, /// move backward only in response to messages
kBackKey, /// move backward when the user presses the move backward key
kBackAuto, /// move backward automatically (only when entered from end)
kBackMax,
} fBackType;
/** When do we advance to the next stage?
This is typically asked when we're reached the end of the current stage,
but for some advance types, you can interrupt the current stage and
advance automatically on certain events. */
enum AdvanceType {
kAdvanceNone, /// advance only when told to via a message
kAdvanceOnMove, /// advance whenever the user hits a movement key
kAdvanceAuto, /// advance automatically at the end of the current stage
kAdvanceOnAnyKey, /// advance when any key is pressed
kAdvanceMax,
} fAdvanceType;
/** When do we regress to the previous stage?
This is typically asked when we've reached the beginning of the current stage
and we still trying to move backwards.
For some regress types, however, you can abort this stage automatically when
certain events occur. */
enum RegressType {
kRegressNone, // regress only when told to via a message
kRegressOnMove, // regress whenever the user hits a movement key
kRegressAuto, // regress automatically when we reach the beginning of the current
/// stage and we're still moving backwards
kRegressOnAnyKey, // regress when any key is pressed
kRegressMax,
} fRegressType;
/** Default constructor, used primarily by the class factory. */
plAnimStage();
/** Canonical constructor.
\param animName The name of the animation controlled by this stage.
\param notify Flags for when to send notify messages
\param forward How to control forward movement
\param backward How to control backward movement
\param regress When to regress to the previous stage
\param advance When to advance to the next stage
\param loops How many times to loop this stage after its initial playthrough.
i.e. If you only want it to play once, set loops to 0
To make it play forever, pass in -1 for loops.
\param localize Align the origin of the animation to the avatar's position
when the stage start's playing. Only relevant if the
animation attempts to reposition the avatar by having a
channel attached to the avatar's handle.
*/
plAnimStage(const char *animName,
UInt8 notify,
ForwardType forward,
BackType backward,
AdvanceType advance,
RegressType regress,
int loops);
/** Most complete constructor. Specifies every aspect of the stage. Differs from
the next-most-complete constructor by the presence of doAdvanceTo, advanceTo,
doRegressTo, and regressTo. These parameters allow you to specify what stages
will be played after this one, depending on which direction the stage is
moving.
*/
plAnimStage(const char *animName,
UInt8 notify,
ForwardType forward,
BackType back,
AdvanceType advance,
RegressType regress,
int loops,
bool doAdvanceTo,
int advanceTo,
bool doRegressTo,
int regressTo);
/** Simple and common constructor: moves forward automatically, advances automatically,
no reverse, no regress.
\param animName The name of the animation controlled by this stage.
\param notify Flags for when to send notify messages
*/
plAnimStage(const char *animName, UInt8 notify);
virtual ~plAnimStage();
const plAnimStage& operator=(const plAnimStage& src);
plAGAnimInstance * Attach(plArmatureMod *armature, plArmatureBrain *brain, float initialBlend, double time);
int Detach(plArmatureMod *armature);
void Reset(double time, plArmatureMod *avMod, bool atStart);
void ResetAtTime(double time, float localTime, plArmatureMod *avMod);
bool MoveRelative(double worldTime, float delta, float &overage, plArmatureMod *avMod);
/** The name of the animation associated with this stage. */
const char * GetAnimName();
ForwardType GetForwardType();
void SetForwardType(ForwardType t);
BackType GetBackType();
void SetBackType(BackType t);
AdvanceType GetAdvanceType();
void SetAdvanceType(AdvanceType t);
RegressType GetRegressType();
void SetRegresstype(RegressType t);
UInt32 GetNotifyFlags();
void SetNotifyFlags(UInt32 newFlags);
int GetNumLoops();
void SetNumLoops(int loops);
int GetLoopValue();
void SetLoopValue(int loop);
float GetLocalTime();
void SetLocalTime(float time, hsBool noCallbacks = false);
float GetLength();
void SetMod(plMultistageBehMod* p) { fMod = p; } // for returning ID#'s to python scripts:
plMultistageBehMod* GetMod() { return fMod; }
bool GetIsAttached();
void SetIsAttached(bool status);
int GetNextStage(int curStage);
int GetPrevStage(int curStage);
plAGAnimInstance *GetAnimInstance() const { return fAnimInstance; };
bool GetReverseOnIdle() { return fReverseOnIdle; }
void SetReverseOnIdle(bool onOff) { fReverseOnIdle = onOff; }
void DumpDebug(bool active, int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt);
// STANDARD PLASMA PROTOCOL
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void SaveAux(hsStream *stream, hsResMgr *mgr);
virtual void LoadAux(hsStream *stream, hsResMgr *mgr, double time);
CLASSNAME_REGISTER( plAnimStage );
GETINTERFACE_ANY( plAnimStage, plCreatable);
protected:
bool IMoveBackward(double worldTime, float delta, float &underrun, plArmatureMod *avMod);
bool IMoveForward(double worldTime, float delta, float &overrun, plArmatureMod *avMod);
bool INeedFadeIn();
bool INeedFadeOut();
bool ITryAdvance(plArmatureMod *avMod);
bool ITryRegress(plArmatureMod *avMod);
hsBool ISendNotify(UInt32 notifyMask, UInt32 notifyType, plArmatureMod *armature, plArmatureBrain *brain);
char *fAnimName; // the name of our animation
UInt8 fNotify; // flags for which events will cause notification events
int fLoops; // how many times will this animation loop (after initial playthrough?)
bool fDoAdvanceTo; // advance to a specific stage instead of n + 1
UInt32 fAdvanceTo; // the stage to advance to, provided fDoAdvanceTo is true
bool fDoRegressTo; // regress to a specific stage instaed of n - 1
UInt32 fRegressTo; // the stage to regress true, provided fDoRegressTo is true
// --- these are derived & kept for bookkeeping
plAGAnimInstance *fAnimInstance;
plArmatureMod *fArmature;
plArmatureBrain *fBrain; // only valid while attached
plMultistageBehMod *fMod; // where we came from
float fLocalTime; // our time within the animation
float fLength; // the length of our animation?
int fCurLoop; // which loop are we currently in?
bool fAttached; // in the middle of reloading
bool fAnimatedHandle; // this animation moves the handle
UInt8 fSentNotifies; // which notifies have we sent?
bool fReverseOnIdle; // reverse our key interpretation if we stop. this is a special
// case for down ladders, for which the forward button means "keep going down"
// if you hold it down the whole time, but means "go up" if you press it
// again after releasing it.
bool fDone; // We've reached the end of the anim -- may be fading out, or paused.
};
class plAnimStageVec : public std::vector<plAnimStage*>
{
};
#endif // PL_ANIM_STAGE_INC

View File

@ -0,0 +1,340 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "../plStatusLog/plStatusLog.h"
#include "plArmatureEffects.h"
#include "../pfMessage/plArmatureEffectMsg.h"
#include "../plMessage/plAnimCmdMsg.h"
#include "../plMessage/plAvatarMsg.h"
#include "plArmatureMod.h"
#include "../pnSceneObject/plAudioInterface.h"
#include "../plAudio/plSound.h"
#include "../plAudio/plAudioSystem.h"
#include "../pfAudio/plRandomSoundMod.h"
#include "hsResMgr.h"
#include "plgDispatch.h"
const char *plArmatureEffectsMgr::SurfaceStrings[] =
{
"Dirt",
"Puddle",
"Water",
"Tile",
"Metal",
"WoodBridge",
"RopeLadder",
"Grass",
"Brush",
"HardWood",
"Rug",
"Stone",
"Mud",
"MetalLadder",
"WoodLadder",
"DeepWater",
"Maintainer(Glass)",
"Maintainer(Stone)",
"Swimming",
"(none)" // Keep this one last
};
void plArmatureEffectsMgr::Read(hsStream *s, hsResMgr *mgr)
{
hsKeyedObject::Read(s, mgr);
int numEffects = s->ReadSwap32();
while (numEffects > 0)
{
plRefMsg *msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1);
hsgResMgr::ResMgr()->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef);
numEffects--;
}
plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey());
}
void plArmatureEffectsMgr::Write(hsStream *s, hsResMgr *mgr)
{
hsKeyedObject::Write(s, mgr);
s->WriteSwap32(fEffects.GetCount());
int i;
for (i = 0; i < fEffects.GetCount(); i++)
mgr->WriteKey(s, fEffects[i]->GetKey());
}
hsBool plArmatureEffectsMgr::MsgReceive(plMessage* msg)
{
plEventCallbackInterceptMsg *iMsg = plEventCallbackInterceptMsg::ConvertNoRef(msg);
if (iMsg)
{
if (fEnabled)
iMsg->SendMessage();
}
plArmatureEffectMsg *eMsg = plArmatureEffectMsg::ConvertNoRef(msg);
plArmatureEffectStateMsg *sMsg = plArmatureEffectStateMsg::ConvertNoRef(msg);
if (eMsg || sMsg)
{
// Always handle state messages, but only trigger actual effects if we're enabled
if (sMsg || fEnabled)
{
int i;
for (i = 0; i < fEffects.GetCount(); i++)
fEffects[i]->HandleTrigger(msg);
}
return true;
}
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg);
if (refMsg)
{
plArmatureEffect *effect = plArmatureEffect::ConvertNoRef(refMsg->GetRef());
if (effect)
{
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
fEffects.Append(effect);
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
fEffects.RemoveItem(effect);
return true;
}
}
plAvatarStealthModeMsg *stealthMsg = plAvatarStealthModeMsg::ConvertNoRef(msg);
if (stealthMsg && stealthMsg->GetSender() == fArmature->GetTarget(0)->GetKey())
{
if (stealthMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked)
fEnabled = false;
else
fEnabled = true;
return true;
}
return hsKeyedObject::MsgReceive(msg);
}
UInt32 plArmatureEffectsMgr::GetNumEffects()
{
return fEffects.GetCount();
}
plArmatureEffect *plArmatureEffectsMgr::GetEffect(UInt32 num)
{
return fEffects[num];
}
void plArmatureEffectsMgr::ResetEffects()
{
int i;
for (i = 0; i < fEffects.GetCount(); i++)
fEffects[i]->Reset();
}
/////////////////////////////////////////////////////////////////////////////
//
// Actual Effects
//
/////////////////////////////////////////////////////////////////////////////
plArmatureEffectFootSound::plArmatureEffectFootSound()
{
plArmatureEffectFootSurface *surface = TRACKED_NEW plArmatureEffectFootSurface;
surface->fID = plArmatureEffectsMgr::kFootNoSurface;
surface->fTrigger = nil;
fSurfaces.Append(surface);
int i;
for (i = 0; i < plArmatureEffectsMgr::kMaxSurface; i++)
{
fMods[i] = nil;
}
SetFootType(kFootTypeShoe);
}
plArmatureEffectFootSound::~plArmatureEffectFootSound()
{
int i;
for (i = 0; i < fSurfaces.GetCount(); i++)
delete fSurfaces[i];
}
void plArmatureEffectFootSound::Read(hsStream* s, hsResMgr* mgr)
{
plArmatureEffect::Read(s, mgr);
int count = s->ReadByte();
int i;
for (i = 0; i < count; i++)
{
plGenRefMsg *msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, -1);
mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef);
}
}
UInt32 plArmatureEffectFootSound::IFindSurfaceByTrigger(plKey trigger)
{
UInt32 i;
// Skip index 0. It's the special "NoSurface" that should always be at the stack bottom
for (i = 1; i < fSurfaces.GetCount(); i++)
{
if (fSurfaces[i]->fTrigger == trigger)
return i;
}
return -1;
}
void plArmatureEffectFootSound::Write(hsStream* s, hsResMgr* mgr)
{
plArmatureEffect::Write(s, mgr);
s->WriteByte(plArmatureEffectsMgr::kMaxSurface);
int i;
for (i = 0; i < plArmatureEffectsMgr::kMaxSurface; i++)
mgr->WriteKey(s, (fMods[i] ? fMods[i]->GetKey() : nil));
}
hsBool plArmatureEffectFootSound::MsgReceive(plMessage* msg)
{
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg);
if (refMsg)
{
plRandomSoundMod *rsMod = plRandomSoundMod::ConvertNoRef(refMsg->GetRef());
if (rsMod)
{
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
{
fMods[refMsg->fWhich] = rsMod;
}
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
fMods[refMsg->fWhich] = nil;
return true;
}
}
return plArmatureEffect::MsgReceive(msg);
}
hsBool plArmatureEffectFootSound::HandleTrigger(plMessage* msg)
{
plArmatureEffectMsg *eMsg = plArmatureEffectMsg::ConvertNoRef(msg);
if (eMsg)
{
UInt32 curSurfaceIndex = fSurfaces[fSurfaces.GetCount() - 1]->fID;
if (curSurfaceIndex < plArmatureEffectsMgr::kMaxSurface && fMods[curSurfaceIndex] != nil)
{
if (plgAudioSys::Active() && fActiveSurfaces.IsBitSet(curSurfaceIndex))
{
fMods[curSurfaceIndex]->SetCurrentGroup(eMsg->fTriggerIdx);
plAnimCmdMsg *animMsg = TRACKED_NEW plAnimCmdMsg;
animMsg->AddReceiver(fMods[curSurfaceIndex]->GetKey());
animMsg->SetCmd(plAnimCmdMsg::kContinue);
plgDispatch::MsgSend(animMsg);
}
}
return true;
}
plArmatureEffectStateMsg *sMsg = plArmatureEffectStateMsg::ConvertNoRef(msg);
if (sMsg)
{
// It doesn't matter if we're adding or removing a surface, our load/unload is the same:
// unload the old surface and load the new one
if (sMsg->fAddSurface)
{
if (IFindSurfaceByTrigger(sMsg->GetSender()) == -1) // Check that it's not a repeat msg
{
plStatusLog::AddLineS("audio.log", "FTSP: Switching to surface - %s",
plArmatureEffectsMgr::SurfaceStrings[sMsg->fSurface]);
plArmatureEffectFootSurface *surface = TRACKED_NEW plArmatureEffectFootSurface;
surface->fID = sMsg->fSurface;
surface->fTrigger = sMsg->GetSender();
fSurfaces.Append(surface);
}
}
else
{
UInt32 index = IFindSurfaceByTrigger(sMsg->GetSender());
if (index != -1)
{
if (index == fSurfaces.GetCount() - 1) // It's the top on the stack
plStatusLog::AddLineS("audio.log", "FTSP: Switching to surface - %s",
plArmatureEffectsMgr::SurfaceStrings[fSurfaces[index - 1]->fID]);
delete fSurfaces[index];
fSurfaces.Remove(index);
}
}
return true;
}
return false;
}
void plArmatureEffectFootSound::Reset()
{
while (fSurfaces.GetCount() > 1)
delete fSurfaces.Pop();
}
void plArmatureEffectFootSound::SetFootType(UInt8 type)
{
if (type == kFootTypeBare)
{
fActiveSurfaces.Clear();
fActiveSurfaces.SetBit(plArmatureEffectsMgr::kFootDeepWater);
fActiveSurfaces.SetBit(plArmatureEffectsMgr::kFootPuddle);
fActiveSurfaces.SetBit(plArmatureEffectsMgr::kFootWater);
fActiveSurfaces.SetBit(plArmatureEffectsMgr::kFootSwimming);
}
else
{
fActiveSurfaces.Set(plArmatureEffectsMgr::kMaxSurface);
}
}

View File

@ -0,0 +1,164 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/** \file plArmatureEffects.h
Manages environmental effects for the avatar.
*/
#ifndef plArmatureEffects_inc
#define plArmatureEffects_inc
#include "../pnKeyedObject/hsKeyedObject.h"
#include "hsTemplates.h"
#include "hsBitVector.h"
class plArmatureMod;
class plArmatureEffect;
class plRandomSoundMod;
/** \class plArmatureEffects
Passes key avatar events to external effects generators.
Currently used for footstep sounds only, but should eventually
generalize to water splashes, etc.
More to come...*/
class plArmatureEffectsMgr : public hsKeyedObject
{
protected:
hsTArray<plArmatureEffect *> fEffects;
hsBool fEnabled;
public:
plArmatureEffectsMgr() : fArmature(nil), fEnabled(true) {}
virtual ~plArmatureEffectsMgr() {}
CLASSNAME_REGISTER( plArmatureEffectsMgr );
GETINTERFACE_ANY( plArmatureEffectsMgr, hsKeyedObject );
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
virtual hsBool MsgReceive(plMessage* msg);
UInt32 GetNumEffects();
plArmatureEffect *GetEffect(UInt32 num);
void ResetEffects();
plArmatureMod *fArmature;
enum
{
kFootDirt,
kFootPuddle,
kFootWater,
kFootTile,
kFootMetal,
kFootWoodBridge,
kFootRopeLadder,
kFootGrass,
kFootBrush,
kFootHardWood,
kFootRug,
kFootStone,
kFootMud,
kFootMetalLadder,
kFootWoodLadder,
kFootDeepWater,
kFootMaintainerGlass,
kFootMaintainerStone,
kFootSwimming,
kMaxSurface,
kFootNoSurface = kMaxSurface,
};
static const char *SurfaceStrings[];
};
class plArmatureEffect : public hsKeyedObject
{
public:
plArmatureEffect() {}
~plArmatureEffect() {}
CLASSNAME_REGISTER( plArmatureEffect );
GETINTERFACE_ANY( plArmatureEffect, hsKeyedObject );
virtual hsBool HandleTrigger(plMessage* msg) = 0;
virtual void Reset() {}
};
class plArmatureEffectFootSurface
{
public:
UInt8 fID;
plKey fTrigger;
};
class plArmatureEffectFootSound : public plArmatureEffect
{
protected:
hsTArray<plArmatureEffectFootSurface *> fSurfaces;
hsBitVector fActiveSurfaces;
plRandomSoundMod *fMods[plArmatureEffectsMgr::kMaxSurface];
UInt32 IFindSurfaceByTrigger(plKey trigger);
public:
plArmatureEffectFootSound();
~plArmatureEffectFootSound();
CLASSNAME_REGISTER( plArmatureEffectFootSound );
GETINTERFACE_ANY( plArmatureEffectFootSound, plArmatureEffect );
virtual void Read(hsStream* s, hsResMgr* mgr);
virtual void Write(hsStream* s, hsResMgr* mgr);
virtual hsBool MsgReceive(plMessage* msg);
virtual hsBool HandleTrigger(plMessage* msg);
virtual void Reset();
void SetFootType(UInt8);
enum
{
kFootTypeShoe,
kFootTypeBare,
};
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,488 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/** \file plArmatureMod.h
A modifier which manages multi-channel animation and has a physical body.
Designed for avatars; also good for vehicles, creatures, etc.
*/
// PLARMATUREMOD
//
// An armature is an object with both a physical presence (physics behavior) and articulated animated parts.
// (The parts are not themselves physical)
// An avatar is a type of armature, as is a critter, and anything else that moves around.
//
// This modifier combines multi-channel animation with blending (inherited from plAGMasterMod)
// with convenience functions for moving a physical body around and some specialized animation support
#ifndef plArmatureMod_inc
#define plArmatureMod_inc
#include "plAGMasterMod.h"
// other local
#include "plAvDefs.h"
#include "../pnSceneObject/plSimulationInterface.h"
#include "hsMatrix44.h"
#include "../plNetCommon/plNetCommon.h"
#include <float.h>
/////////////////////////////////////////////////////////////////////////////////////////
//
// FORWARDS
//
/////////////////////////////////////////////////////////////////////////////////////////
class hsQuat;
class plMatrixChannel;
class plAGModifier;
class plAGMasterMod;
class plAGChannel;
class plClothingOutfit;
class plClothingSDLModifier;
class plAvatarSDLModifier;
class plMatrixDelayedCorrectionApplicator;
class plMatrixDifferenceApp;
class plDebugText;
class plArmatureEffectsMgr;
class plAvBoneMap; // below
class plDrawable;
class plControlEventMsg;
class plAvatarInputStateMsg;
class plLayerLinkAnimation;
class plPipeline;
class plArmatureBrain;
class plArmatureUpdateMsg;
class plPhysicalControllerCore;
typedef std::vector<plKey> plKeyVector;
typedef std::vector<plArmatureBrain*> plBrainStack;
class plArmatureModBase : public plAGMasterMod
{
public:
plArmatureModBase();
virtual ~plArmatureModBase();
CLASSNAME_REGISTER( plArmatureModBase );
GETINTERFACE_ANY( plArmatureModBase, plAGMasterMod );
virtual hsBool MsgReceive(plMessage* msg);
virtual void AddTarget(plSceneObject* so);
virtual void RemoveTarget(plSceneObject* so);
virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty);
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual void Write(hsStream *stream, hsResMgr *mgr);
plMatrixDifferenceApp *GetRootAnimator() { return fRootAnimator; }
plPhysicalControllerCore* GetController() const { return fController; }
plKey GetWorldKey() const;
virtual hsBool ValidatePhysics();
virtual hsBool ValidateMesh();
virtual void PushBrain(plArmatureBrain *brain);
virtual void PopBrain();
plArmatureBrain *GetCurrentBrain() const;
plDrawable *FindDrawable() const;
virtual void LeaveAge();
virtual hsBool IsFinal();
// LOD stuff
void AdjustLOD(); // see if we need to switch to a different resolution
hsBool SetLOD(int newLOD); // switch to a different resolution
void RefreshTree(); // Resend an LOD update to all our nodes (for when geometry changes)
int AppendMeshKey(plKey meshKey);
int AppendBoneVec(plKeyVector *boneVec);
UInt8 GetNumLOD() const;
// A collection of reasons (flags) that things might be disabled. When all flags are gone
// The object is re-enabled.
enum
{
kDisableReasonUnknown = 0x0001,
kDisableReasonRelRegion = 0x0002,
kDisableReasonLinking = 0x0004,
kDisableReasonCCR = 0x0008,
kDisableReasonVehicle = 0x0010,
kDisableReasonGenericBrain = 0x0020,
kDisableReasonKinematic = 0x0040
};
void EnablePhysics(hsBool status, UInt16 reason = kDisableReasonUnknown);
void EnablePhysicsKinematic(hsBool status);
void EnableDrawing(hsBool status, UInt16 reason = kDisableReasonUnknown);
hsBool IsPhysicsEnabled() { return fDisabledPhysics == 0; }
hsBool IsDrawEnabled() { return fDisabledDraw == 0; }
static void AddressMessageToDescendants(const plCoordinateInterface * CI, plMessage *msg);
static void EnableDrawingTree(const plSceneObject *object, hsBool status);
static int fMinLOD; // throttle for lowest-indexed LOD
static double fLODDistance; // Distance for first LOD switch 2nd is 2x this distance (for now)
protected:
virtual void IFinalize();
virtual void ICustomizeApplicator();
void IEnableBones(int lod, hsBool enable);
// Some of these flags are only needed by derived classes, but I just want
// the one waitFlags variable.
enum
{
kNeedMesh = 0x01,
kNeedPhysics = 0x02,
kNeedAudio = 0x04,
kNeedCamera = 0x08,
kNeedSpawn = 0x10,
kNeedApplicator = 0x20,
kNeedBrainActivation = 0x40,
};
UInt16 fWaitFlags;
int fCurLOD;
plPhysicalControllerCore* fController;
plKeyVector fMeshKeys;
plBrainStack fBrains;
plMatrixDifferenceApp *fRootAnimator;
std::vector<plKeyVector*> fUnusedBones;
UInt16 fDisabledPhysics;
UInt16 fDisabledDraw;
};
class plArmatureMod : public plArmatureModBase
{
friend class plHBehavior;
friend class plAvatarSDLModifier;
friend class plAvatarPhysicalSDLModifier;
friend class plClothingSDLModifier;
friend class plAvOneShotLinkTask;
public:
plArmatureMod();
virtual ~plArmatureMod();
CLASSNAME_REGISTER( plArmatureMod );
GETINTERFACE_ANY( plArmatureMod, plArmatureModBase );
virtual hsBool MsgReceive(plMessage* msg);
virtual void AddTarget(plSceneObject* so);
virtual void RemoveTarget(plSceneObject* so);
virtual hsBool IEval(double secs, hsScalar del, UInt32 dirty);
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual hsBool ValidatePhysics();
virtual hsBool ValidateMesh();
// Get or set the position of the avatar in simulation space. Set any
// arguments you don't care about to nil.
void SetPositionAndRotationSim(const hsPoint3* position, const hsQuat* rotation);
void GetPositionAndRotationSim(hsPoint3* position, hsQuat* rotation);
hsBool IsLocalAvatar();
hsBool IsLocalAI();
virtual const plSceneObject *FindBone(const char * name) const;
virtual const plSceneObject *FindBone(UInt32 id) const; // use an id from an appropriate taxonomy, such as plAvBrainHuman::BoneID
virtual void AddBoneMapping(UInt32 id, const plSceneObject *bone);
plAGModifier *GetRootAGMod();
plAGAnim *FindCustomAnim(const char *baseName) const;
virtual void Spawn(double timeNow);
virtual void SpawnAt(int which, double timeNow);
virtual void EnterAge(hsBool reSpawn);
virtual void LeaveAge();
virtual void PanicLink(hsBool playLinkOutAnim = true);
virtual void PersonalLink();
virtual bool ToggleDontPanicLinkFlag() { fDontPanicLink = fDontPanicLink ? false : true; return fDontPanicLink; }
int GetBrainCount();
plArmatureBrain *GetNextBrain(plArmatureBrain *brain);
plArmatureBrain *GetBrain(int index) { if(index <= fBrains.size()) return fBrains.at(index); else return nil; }
plArmatureBrain *FindBrainByClass(UInt32 classID) const;
void TurnToPoint(hsPoint3 &point);
void SuspendInput();
void ResumeInput();
UInt8 IsInputSuspended() { return fSuspendInputCount; }
void IProcessQueuedInput();
void PreserveInputState();
void RestoreInputState();
hsBool GetInputFlag(int f) const;
void SetInputFlag(int which, hsBool status);
void ClearInputFlags(bool saveAlwaysRun, bool clearBackup);
hsBool HasMovementFlag() const; // Is any *movement* input flag on?
hsScalar GetTurnStrength() const;
hsScalar GetKeyTurnStrength() const;
hsScalar GetAnalogTurnStrength() const;
void SetReverseFBOnIdle(bool val);
hsBool IsFBReversed();
bool ForwardKeyDown() const;
bool BackwardKeyDown() const;
bool StrafeLeftKeyDown() const;
bool StrafeRightKeyDown() const;
bool StrafeKeyDown() const;
bool FastKeyDown() const;
bool TurnLeftKeyDown() const;
bool TurnRightKeyDown() const;
bool JumpKeyDown() const;
bool ExitModeKeyDown() const;
void SetForwardKeyDown();
void SetBackwardKeyDown();
void SetStrafeLeftKeyDown(bool on = true);
void SetStrafeRightKeyDown(bool on = true);
void SetFastKeyDown();
void SetTurnLeftKeyDown(bool status = true);
void SetTurnRightKeyDown(bool status = true);
void SetJumpKeyDown();
void DebugDumpMoveKeys(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt);
void GetMoveKeyString(char *buff);
void SynchIfLocal(double timeNow, int force); // Just physical state
void SynchInputState(UInt32 rcvID = kInvalidPlayerID);
hsBool DirtySynchState(const char* SDLStateName, UInt32 synchFlags );
hsBool DirtyPhysicalSynchState(UInt32 synchFlags);
plClothingOutfit *GetClothingOutfit() const { return fClothingOutfit; }
plClothingSDLModifier *GetClothingSDLMod() const { return fClothingSDLMod; }
const plSceneObject *GetClothingSO(UInt8 lod) const;
plArmatureEffectsMgr *GetArmatureEffects() const { return fEffects; }
enum
{
kWalk,
kRun,
kTurn,
kImpact,
kSwim,
};
const char *plArmatureMod::GetAnimRootName(const char *name);
Int8 AnimNameToIndex(const char *name);
void SetBodyType(int type) { fBodyType = type; }
int GetBodyType(int type) { return fBodyType; }
int GetCurrentGenericType();
bool FindMatchingGenericBrain(const char *names[], int count);
char *MakeAnimationName(const char * baseName) const;
char *GetRootName();
void SetRootName(const char *name);
int RefreshDebugDisplay();
void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt);
void SetDebugState(hsBool state) { fDebugOn = (state != 0); }
bool GetDebugState() { return fDebugOn; }
virtual void RefreshTree() {}
hsBool IsInStealthMode() const;
int GetStealthLevel() const { return fStealthLevel; }
bool IsOpaque();
bool IsLinkedIn();
bool IsMidLink();
hsBool ConsumeJump(); // returns true if the jump keypress was available to consume
void SendBehaviorNotify(UInt32 type, hsBool start = true) { IFireBehaviorNotify(type,start); }
// Discovered a bug which makes these values horribly out of scale. So we do the rescale
// in the Get/Set functions for backwards compatability.
static void SetMouseTurnSensitivity(hsScalar val) { fMouseTurnSensitivity = val / 150.f; }
static hsScalar GetMouseTurnSensitivity() { return fMouseTurnSensitivity * 150.f; }
static void SetSpawnPointOverride( const char *overrideObjName );
static void WindowActivate(bool active);
void SetFollowerParticleSystemSO(plSceneObject *follower);
plSceneObject *GetFollowerParticleSystemSO();
void RegisterForBehaviorNotify(plKey key);
void UnRegisterForBehaviorNotify(plKey key);
const hsBitVector& GetRelRegionCareAbout() const { return fRegionsICareAbout; }
const hsBitVector& GetRelRegionImIn() const { return fRegionsImIn; }
bool IsKILowestLevel();
int GetKILevel();
void SetLinkInAnim(const char *animName);
plKey GetLinkInAnimKey() const;
enum
{
kSwapTargetShadow,
kMaxSwapType
};
enum
{
kBoneBaseMale = 0,
kBoneBaseFemale,
kBoneBaseCritter, // AI controlled avatar
kBoneBaseActor, // human controlled, non human avatar
kMaxBoneBase
};
enum
{
kAvatarLOSGround,
kAvatarLOSSwimSurface,
};
plMatrixDelayedCorrectionApplicator *fBoneRootAnimator;
static const hsScalar kAvatarInputSynchThreshold;
static hsBool fClickToTurn;
static const char *BoneStrings[];
void SetPhysicalDims(hsScalar height, hsScalar width) { fPhysHeight = height; fPhysWidth = width; }
void SetBodyAgeName(const char* ageName) {if (ageName) fBodyAgeName = ageName; else fBodyAgeName = "";}
void SetBodyFootstepSoundPage(const char* pageName) {if (pageName) fBodyFootstepSoundPage = pageName; else fBodyFootstepSoundPage = "";}
void SetAnimationPrefix(const char* prefix) {if (prefix) fAnimationPrefix = prefix; else fAnimationPrefix = "";}
const char* GetUserStr() {return fUserStr.c_str();}
protected:
void IInitDefaults();
virtual void IFinalize();
virtual void ICustomizeApplicator();
virtual void ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc);
void NetworkSynch(double timeNow, int force = 0);
hsBool IHandleControlMsg(plControlEventMsg* pMsg);
void IFireBehaviorNotify(UInt32 type, hsBool behaviorStart = true);
void IHandleInputStateMsg(plAvatarInputStateMsg *msg);
void ILinkToPersonalAge();
int IFindSpawnOverride(void);
void ISetTransparentDrawOrder(bool val);
plLayerLinkAnimation *IFindLayerLinkAnim();
char *fRootName; // the name of the player root (from the max file)
hsBitVector fMoveFlags; // which keys/buttons are currently pressed
hsBitVector fMoveFlagsBackup; // a copy of fMoveFlags
typedef std::vector<plControlEventMsg*> CtrlMessageVec;
CtrlMessageVec fQueuedCtrlMessages; // input messages we haven't processed
hsScalar fMouseFrameTurnStrength; // Sum turnage from mouse delta messages since last eval.
plKey fFootSoundSOKey; // The Scene Object we attach to targets for footstep sounds
plKey fLinkSoundSOKey; // Same thing for linking... wwwwawAWAWAwawa...
plKey fLinkInAnimKey; // Set when we link out, this is the anim to play (backwards) when we link in.
static hsScalar fMouseTurnSensitivity;
plArmatureUpdateMsg *fUpdateMsg;
// Trying to be a good lad here and align all our bools and UInt8s...
bool fIsLinkedIn; // We have finished playing the LinkEffects and are properly in the age
bool fMidLink; // We're in between a LeaveAge and an EnterAge
bool fAlreadyPanicLinking; // Cleared when you enter an age. Prevents spamming the server with panic link requests.
bool fUnconsumedJump; // We've pressed the jump key, but haven't jumped yet
bool fReverseFBOnIdle; // see set/getters for comments
bool fPendingSynch;
bool fDebugOn;
bool fOpaque;
UInt8 fSuspendInputCount;
UInt8 fStealthMode;
int fStealthLevel; // you are invisible to other players/CCRs of lower stealthLevel
double fLastInputSynch;
plAGModifier * fRootAGMod;
plAvBoneMap * fBoneMap; // uses id codes to look up bones. set up by the brain as needed.
double fLastSynch;
int fBodyType;
plClothingOutfit *fClothingOutfit;
plClothingSDLModifier *fClothingSDLMod;
plAvatarSDLModifier *fAvatarSDLMod;
plAvatarPhysicalSDLModifier *fAvatarPhysicalSDLMod;
hsTArray<const plSceneObject*> fClothToSOMap;
plArmatureEffectsMgr *fEffects;
plSceneObject *fFollowerParticleSystemSO;
static char *fSpawnPointOverride;
// These vectors are used with relevance regions for culling out other objects
hsBitVector fRegionsImIn;
hsBitVector fRegionsICareAbout;
hsBitVector fOldRegionsImIn;
hsBitVector fOldRegionsICareAbout;
hsTArray<plKey> fNotifyKeys;
// Extra info for creating our special physical at runtime
float fPhysHeight;
float fPhysWidth;
bool fDontPanicLink;
// strings for animations, age names, footstep sounds, etc
std::string fBodyAgeName;
std::string fBodyFootstepSoundPage;
std::string fAnimationPrefix;
// user-defined string assigned to this avatar
std::string fUserStr;
};
// PLARMATURELOD
// This class has been phased into plArmatureModBase. It's left behind
// for backwards compatability.
class plArmatureLODMod : public plArmatureMod
{
public:
// tors
plArmatureLODMod();
plArmatureLODMod(const char * root_name);
virtual ~plArmatureLODMod();
CLASSNAME_REGISTER( plArmatureLODMod );
GETINTERFACE_ANY( plArmatureLODMod, plArmatureMod );
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual void Write(hsStream *stream, hsResMgr *mgr);
};
class plAvBoneMap
{
public:
plAvBoneMap();
virtual ~plAvBoneMap();
const plSceneObject * FindBone(UInt32 boneID); // you probably want to use plAvBrainHuman::BoneID;
void AddBoneMapping(UInt32 boneID, const plSceneObject *SO);
protected:
class BoneMapImp; // forward declaration to keep the header clean: see .cpp for implementation
BoneMapImp *fImp; // the thing that actually holds our map
};
#define TWO_PI (hsScalarPI * 2)
#endif plArmatureMod_inc

View File

@ -0,0 +1,137 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plAvBehaviors.h"
#include "plAvBrainHuman.h"
#include "plArmatureMod.h"
#include "plAGAnimInstance.h"
#include "../plMessage/plAvatarMsg.h"
#include "../plPipeline/plDebugText.h"
plArmatureBehavior::plArmatureBehavior() : fAnim(nil), fArmature(nil), fBrain(nil), fIndex((UInt8)-1), fFlags(0) {}
plArmatureBehavior::~plArmatureBehavior()
{
if (fAnim)
fAnim->Detach();
}
void plArmatureBehavior::Init(plAGAnim *anim, hsBool loop, plArmatureBrain *brain, plArmatureModBase *armature, UInt8 index)
{
fArmature = armature;
fBrain = brain;
fIndex = index;
fStrength.Set(0);
if (anim)
{
fAnim = fArmature->AttachAnimationBlended(anim, 0, 0, true);
fAnim->SetLoop(loop);
}
}
void plArmatureBehavior::Process(double time, float elapsed)
{
}
void plArmatureBehavior::SetStrength(hsScalar val, hsScalar rate /* = 0.f */)
{
hsScalar oldStrength = GetStrength();
if (rate == 0)
fStrength.Set(val);
else
fStrength.Set(val, fabs(val - oldStrength) / rate);
if (fAnim)
fAnim->Fade(val, rate);
if (val > 0 && oldStrength == 0)
IStart();
else if (val == 0 && oldStrength > 0)
IStop();
}
hsScalar plArmatureBehavior::GetStrength()
{
return fStrength.Value();
}
void plArmatureBehavior::Rewind()
{
if (fAnim)
fAnim->SetCurrentTime(0.0f, true);
}
void plArmatureBehavior::DumpDebug(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt)
{
hsScalar strength = GetStrength();
const char *onOff = strength > 0 ? "on" : "off";
char blendBar[11] = "||||||||||";
int bars = (int)__min(10 * strength, 10);
blendBar[bars] = '\0';
if (fAnim)
{
const char *animName = fAnim->GetName();
float time = fAnim->GetTimeConvert()->CurrentAnimTime();
sprintf(strBuf, "%20s %3s time: %5.2f %s", animName, onOff, time, blendBar);
}
else
sprintf(strBuf, " Behavior %2d %3s %s", fIndex, onOff, blendBar);
debugTxt.DrawString(x, y, strBuf);
y += lineHeight;
}
void plArmatureBehavior::IStart()
{
if (fAnim)
{
fAnim->SetCurrentTime(0.0f, true);
fAnim->Start();
}
}
void plArmatureBehavior::IStop()
{
if (fFlags & kBehaviorFlagNotifyOnStop)
fBrain->OnBehaviorStop(fIndex);
}

View File

@ -0,0 +1,87 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef PL_AV_BEHAVIORS_H
#define PL_AV_BEHAVIORS_H
#include "hsTypes.h"
#include "hsTemplates.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnTimer/plTimedValue.h"
class plAGAnim;
class plAGAnimInstance;
class plArmatureModBase;
class plArmatureBrain;
class plDebugText;
class plArmatureBehavior
{
public:
plArmatureBehavior();
virtual ~plArmatureBehavior();
void Init(plAGAnim *anim, hsBool loop, plArmatureBrain *brain, plArmatureModBase *armature, UInt8 index);
virtual void Process(double time, float elapsed);
virtual void SetStrength(hsScalar val, hsScalar rate = 0.f); // default instant change
virtual hsScalar GetStrength();
virtual void Rewind();
void DumpDebug(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt);
enum
{
kBehaviorFlagNotifyOnStop = 0x01,
};
UInt32 fFlags;
protected:
plAGAnimInstance *fAnim;
plArmatureModBase *fArmature;
plArmatureBrain *fBrain;
plTimedValue<hsScalar> fStrength;
UInt8 fIndex;
virtual void IStart();
virtual void IStop();
};
#endif

View File

@ -0,0 +1,200 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
// local
#include "plAvBrain.h"
#include "plAvBehaviors.h"
#include "plArmatureMod.h"
#include "plAvatarMgr.h"
#include "plAvatarTasks.h"
// global
#include "hsGeometry3.h"
#include "hsQuat.h"
// other
#include "../pnSceneObject/plSceneObject.h"
#include "../plPipeline/plDebugText.h"
// messages
#include "../plMessage/plAvatarMsg.h"
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
plArmatureBrain::plArmatureBrain() :
fCurTask(nil),
fArmature(nil),
fAvMod(nil)
{
}
plArmatureBrain::~plArmatureBrain()
{
while (fTaskQueue.size() > 0)
{
plAvTask *task = fTaskQueue.front();
delete task;
fTaskQueue.pop_front();
}
if (fCurTask)
delete fCurTask;
}
hsBool plArmatureBrain::Apply(double timeNow, hsScalar elapsed)
{
IProcessTasks(timeNow, elapsed);
fArmature->ApplyAnimations(timeNow, elapsed);
return true;
}
void plArmatureBrain::Activate(plArmatureModBase *armature)
{
fArmature = armature;
fAvMod = plArmatureMod::ConvertNoRef(armature);
}
void plArmatureBrain::QueueTask(plAvTask *task)
{
if (task)
fTaskQueue.push_back(task);
}
hsBool plArmatureBrain::LeaveAge()
{
if (fCurTask)
fCurTask->LeaveAge(plArmatureMod::ConvertNoRef(fArmature));
plAvTaskQueue::iterator i = fTaskQueue.begin();
for (; i != fTaskQueue.end(); i++)
{
plAvTask *task = *i;
task->LeaveAge(plArmatureMod::ConvertNoRef(fArmature)); // Give it a chance to do something before we nuke it.
delete task;
}
fTaskQueue.clear();
return true;
}
hsBool plArmatureBrain::IsRunningTask() const
{
if (fCurTask)
return true;
if(fTaskQueue.size() > 0)
return true;
return false;
}
// Nothing for this class to read/write. These methods exist
// for backwards compatability with plAvBrain and plAvBrainUser
void plArmatureBrain::Write(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Write(stream, mgr);
// plAvBrain
stream->WriteSwap32(0);
stream->WriteBool(false);
// plAvBrainUser
stream->WriteSwap32(0);
stream->WriteSwapScalar(0.f);
stream->WriteSwapDouble(0.f);
}
void plArmatureBrain::Read(hsStream *stream, hsResMgr *mgr)
{
plCreatable::Read(stream, mgr);
// plAvBrain
stream->ReadSwap32();
if (stream->ReadBool())
mgr->ReadKey(stream);
// plAvBrainUser
stream->ReadSwap32();
stream->ReadSwapScalar();
stream->ReadSwapDouble();
}
// MSGRECEIVE
hsBool plArmatureBrain::MsgReceive(plMessage * msg)
{
plAvTaskMsg *taskMsg = plAvTaskMsg::ConvertNoRef(msg);
if (taskMsg)
{
return IHandleTaskMsg(taskMsg);
}
return false;
}
void plArmatureBrain::IProcessTasks(double time, hsScalar elapsed)
{
if (!fCurTask || !fCurTask->Process(plArmatureMod::ConvertNoRef(fArmature), this, time, elapsed))
{
if (fCurTask)
{
fCurTask->Finish(plArmatureMod::ConvertNoRef(fArmature), this, time, elapsed);
delete fCurTask;
fCurTask = nil;
}
// need a new task
if (fTaskQueue.size() > 0)
{
plAvTask *newTask = fTaskQueue.front();
if (newTask && newTask->Start(plArmatureMod::ConvertNoRef(fArmature), this, time, elapsed))
{
fCurTask = newTask;
fTaskQueue.pop_front();
}
// if we couldn't start the task, we'll keep trying until we can.
}
}
}
hsBool plArmatureBrain::IHandleTaskMsg(plAvTaskMsg *msg)
{
plAvTask *task = msg->GetTask();
QueueTask(task);
return true;
}

View File

@ -0,0 +1,103 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef PLAVBRAIN_INC
#define PLAVBRAIN_INC
#include "plAGModifier.h"
#include "hsTypes.h"
#include "hsTemplates.h"
#include "hsBitVector.h"
#include "hsGeometry3.h"
#include "hsResMgr.h"
#include "../pnNetCommon/plSynchedObject.h"
#pragma warning(disable: 4284)
#include <deque>
class plArmatureModBase;
class plArmatureBehavior;
class plHKAction;
class plAvTask;
class plAvTaskMsg;
class plDebugText;
class plArmatureBrain : public plCreatable
{
public:
plArmatureBrain();
virtual ~plArmatureBrain();
CLASSNAME_REGISTER( plArmatureBrain );
GETINTERFACE_ANY( plArmatureBrain, plCreatable );
virtual hsBool Apply(double timeNow, hsScalar elapsed);
virtual void Activate(plArmatureModBase *armature);
virtual void Deactivate() {}
virtual void Suspend() {}
virtual void Resume() {}
virtual void Spawn(double timeNow) {}
virtual void OnBehaviorStop(UInt8 index) {}
virtual hsBool LeaveAge();
virtual hsBool IsRunningTask() const;
virtual void QueueTask(plAvTask *task);
virtual void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) {}
virtual void Write(hsStream *stream, hsResMgr *mgr);
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual hsBool MsgReceive(plMessage *msg);
protected:
virtual void IProcessTasks(double time, hsScalar elapsed);
virtual hsBool IHandleTaskMsg(plAvTaskMsg *msg);
typedef std::deque<plAvTask *> plAvTaskQueue;
plAvTaskQueue fTaskQueue; // FIFO queue of tasks we're working on
plAvTask *fCurTask; // the task we're working on right now
plArmatureModBase *fArmature;
plArmatureMod *fAvMod;
hsTArray<plArmatureBehavior*> fBehaviors;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,227 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef plAvBrainClimb_Include
#define plAvBrainClimb_Include
#pragma once
/////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////
#include "plAvBrain.h"
#include "../plMessage/plClimbMsg.h"
/////////////////////////////////////////////////////////////////
//
// PROTOTYPES
//
/////////////////////////////////////////////////////////////////
class plAnimStage;
class plLOSHitMsg;
class plStateDataRecord;
/////////////////////////////////////////////////////////////////
//
// DECLARATION
//
/////////////////////////////////////////////////////////////////
class plAvBrainClimb : public plArmatureBrain
{
public:
enum Mode {
kInactive,
kUnknown,
kFinishing,
kDone,
kClimbingUp,
kClimbingDown,
kClimbingLeft,
kClimbingRight,
kMountingUp,
kMountingDown,
kMountingLeft,
kMountingRight,
kDismountingUp,
kDismountingDown,
kDismountingLeft,
kDismountingRight,
kIdle,
kReleasing,
kFallingOff
};
plAvBrainClimb();
plAvBrainClimb(Mode initialMode);
virtual ~plAvBrainClimb();
virtual void Activate(plArmatureModBase *avMod);
virtual void Deactivate();
virtual hsBool Apply(double timeNow, hsScalar elapsed);
virtual void SaveToSDL(plStateDataRecord *sdl);
virtual void LoadFromSDL(const plStateDataRecord *sdl);
void DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt);
const char * plAvBrainClimb::WorldDirStr(plClimbMsg::Direction dir);
const char *plAvBrainClimb::ModeStr(Mode mode);
// plasma protocol
virtual hsBool MsgReceive(plMessage *msg);
CLASSNAME_REGISTER( plAvBrainClimb );
GETINTERFACE_ANY( plAvBrainClimb, plArmatureBrain);
private:
bool IAdvanceCurrentStage(double time, float elapsed, float &overage);
bool ITryStageTransition(double time, float overage);
bool IChooseNextMode();
/** Handle a climb message. Note that the "start climbing" climb message is handled
by the human brain, since there's no climb brain there to hear it, since you
(by definition) haven't started climbing yet... */
inline hsBool IHandleClimbMsg(plClimbMsg *msg);
inline hsBool IHandleLOSMsg(plLOSHitMsg *msg);
/** Allow or block dismounting in the specified direction. */
void IEnableDismount(plClimbMsg::Direction dir, bool status);
/** Allow or block climbing in the specified direction. */
void IEnableClimb(plClimbMsg::Direction dir, bool status);
/** Figure out which directions we can go from our current position */
void ICheckAllowedDirections();
/** Look left, right, up, and down to see which directions are clear
for our movement. We could do this by positioning our actual collision
body and testing for hits, but it gives a lot more false positives *and*
we won't get the normals of intersection, so it will be more complex
to figure out which directions are actually blocked.
The approach here is to do a raycast in the aforementioned directions
and fail that direction if the raycast hits anything. */
void IProbeEnvironment();
/** When we probe to see if we can climb in some direction, we need to know
how much room we need in each direction. Right now this is hardcoded,
but we may switch to using the actual animation climb distances. */
void ICalcProbeLengths();
/** Which direction are we trying to go? (up, down, left, or right)
Just looks at keyboard input. */
void IGetDesiredDirection();
/** Let go of the wall and fall. */
void IRelease(bool intentional);
/** Decide how far forward or backward to move in the animation */
float IGetAnimDelta(double time, float elapsed);
/** Create all our animation stage objects. Doesn't actually apply any of the animations
to the avatar yet. */
virtual hsBool IInitAnimations();
/** Find the animation stage corresponding with a mode */
plAnimStage * IGetStageFromMode(Mode mode);
Mode IGetModeFromStage(plAnimStage *stage);
/** The exit stage is a special second stage that runs concurrently with the others.
It's currently used to blend the "falloff" and "release" animations seamlessly
with the others. It has a few limitations:
- it's always expected to play forwards
- it doesn't pay attention to the keyboard
*/
bool IProcessExitStage(double time, float elapsed);
void IDumpClimbDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt);
void IDumpDismountDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt);
void IDumpBlockedDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt);
////////////////////////////
//
// MEMBERS
//
////////////////////////////
Mode fCurMode;
Mode fNextMode;
plClimbMsg::Direction fDesiredDirection; // up / down / left / right
float fControlDir; // 1.0 = move current stage forward -1.0 = move current stage back
UInt32 fAllowedDirections;
UInt32 fPhysicallyBlockedDirections;
UInt32 fOldPhysicallyBlockedDirections; // for debug display convenience
UInt32 fAllowedDismounts;
float fVerticalProbeLength;
float fHorizontalProbeLength;
// climbing stages
plAnimStage *fUp;
plAnimStage *fDown;
plAnimStage *fLeft;
plAnimStage *fRight;
plAnimStage *fMountUp;
plAnimStage *fMountDown;
plAnimStage *fMountLeft;
plAnimStage *fMountRight;
plAnimStage *fDismountUp;
plAnimStage *fDismountDown;
plAnimStage *fDismountLeft;
plAnimStage *fDismountRight;
plAnimStage *fIdle;
plAnimStage *fRelease;
plAnimStage *fFallOff;
// /** Current position on the climbing grid. */
// int fX;
// int fY;
/** The stage that is currently executing. */
plAnimStage *fCurStage;
/** A second stage we use (simultaneously) when we need to blend (fall or release) animations. */
plAnimStage *fExitStage;
};
#endif

View File

@ -0,0 +1,257 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
// singular
#include "plAvBrainCoop.h"
// local
#include "plArmatureMod.h"
#include "plAnimStage.h"
#include "plAvTaskSeek.h"
#include "plAvatarMgr.h"
// other
#include "../plScene/plSceneNode.h"
#include "../pnNetCommon/plNetApp.h"
// messages
#include "../plMessage/plAvatarMsg.h"
#include "../plMessage/plAvCoopMsg.h"
#include "../plMessage/plPickedMsg.h"
#include "../pnMessage/plNotifyMsg.h"
/////////////////////////////////////////////////////////////////////////////////////////
//
// STATIC
//
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//
// CODE
//
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
//
// plAvBrainCoop
//
/////////////////////////////////////////////////////////////////////////////////////////
plAvBrainCoop::plAvBrainCoop()
: fInitiatorID(0),
fInitiatorSerial(0)
{
}
// plAvBrainCoop ----------------------------------------------------------
// --------------
plAvBrainCoop::plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut,
MoveMode moveMode, plKey guestKey)
: plAvBrainGeneric(exitFlags, fadeIn, fadeOut, moveMode),
fGuestKey(guestKey)
{
static UInt16 coopSerial = 0;
// this particular constructor is only called by the initiator...
fInitiatorID = plNetClientApp::GetInstance()->GetPlayerID();
fInitiatorSerial = coopSerial++;
}
// plAvBrainCoop ----------------------------------------------------------
// --------------
plAvBrainCoop::plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut,
MoveMode moveMode, UInt32 initiatorID, UInt16 initiatorSerial,
plKey hostKey)
: plAvBrainGeneric(exitFlags, fadeIn, fadeOut, moveMode),
fInitiatorID(initiatorID), fInitiatorSerial(initiatorSerial),
fHostKey(hostKey)
{
}
// MsgReceive ----------------------------------
// -----------
hsBool plAvBrainCoop::MsgReceive(plMessage *msg)
{
plPickedMsg *pickM = plPickedMsg::ConvertNoRef(msg);
if(pickM)
{
if(fWaitingForClick)
{
fWaitingForClick = false;
// clicks are never network propagated, so we can be sure that the
// click was performed by the local player.
plKey localPlayer = plNetClientApp::GetInstance()->GetLocalPlayerKey();
if(localPlayer == fGuestKey)
{
plAvCoopMsg *coopM = TRACKED_NEW plAvCoopMsg(plAvCoopMsg::kGuestAccepted, fInitiatorID, fInitiatorSerial);
coopM->SetBCastFlag(plMessage::kNetPropagate);
coopM->Send();
}
}
}
return plAvBrainGeneric::MsgReceive(msg);
}
// RelayNotifyMsg ----------------------------------
// ---------------
bool plAvBrainCoop::RelayNotifyMsg(plNotifyMsg *msg)
{
// add a coop event so the receiver will know what coop this message is from
msg->AddCoopEvent(fInitiatorID, fInitiatorSerial);
proMultiStageEventData * mtevt = static_cast<proMultiStageEventData *>(msg->FindEventRecord(proEventData::kMultiStage));
if(mtevt)
DebugMsg("COOP: Relaying multi-stage event to %d recipients (via plAvBrainCoop)", fRecipients.size());
if(fRecipients.size() != 0)
{
bool foundARecipient = false;
for (unsigned curRecipient = 0; curRecipient < fRecipients.size(); curRecipient++)
{
if (fRecipients[curRecipient])
{
foundARecipient = true;
msg->AddReceiver(fRecipients[curRecipient]);
}
}
if (!foundARecipient)
return false;
msg->Send();
return true;
}
else
return false;
}
void plAvBrainCoop::EnableGuestClick()
{
fWaitingForClick = true;
}
// GetInitiatorID --------------------
// ---------------
UInt32 plAvBrainCoop::GetInitiatorID()
{
return fInitiatorID;
}
// GetInitiatorSerial --------------------
// -------------------
UInt16 plAvBrainCoop::GetInitiatorSerial()
{
return fInitiatorSerial;
}
// Read -------------------------------------------------
// -----
void plAvBrainCoop::Read(hsStream *stream, hsResMgr *mgr)
{
plAvBrainGeneric::Read(stream, mgr);
fInitiatorID = stream->ReadSwap32();
fInitiatorSerial = stream->ReadSwap16();
if(stream->Readbool())
{
fHostKey = mgr->ReadKey(stream);
}
if(stream->Readbool())
{
fGuestKey = mgr->ReadKey(stream);
}
fWaitingForClick = stream->Readbool();
unsigned numRecipients = stream->ReadSwap16();
for (unsigned i = 0; i < numRecipients; i++)
fRecipients.push_back(mgr->ReadKey(stream));
}
// Write -------------------------------------------------
// ------
void plAvBrainCoop::Write(hsStream *stream, hsResMgr *mgr)
{
plAvBrainGeneric::Write(stream, mgr);
stream->WriteSwap32(fInitiatorID);
stream->WriteSwap16(fInitiatorSerial);
bool hasHostKey = (fHostKey != nil);
bool hasGuestKey = (fGuestKey != nil);
stream->Writebool(hasHostKey);
if(hasHostKey)
mgr->WriteKey(stream, fHostKey);
stream->Writebool(hasGuestKey);
if(hasGuestKey)
mgr->WriteKey(stream, fGuestKey);
stream->Writebool(fWaitingForClick);
stream->WriteSwap16(fRecipients.size());
for (unsigned i = 0; i < fRecipients.size(); i++)
mgr->WriteKey(stream, fRecipients[i]);
}
plKey plAvBrainCoop::GetRecipient()
{
if (fRecipients.size() == 0)
return nil;
return fRecipients[0];
}
void plAvBrainCoop::SetRecipient(plKey &recipient)
{
fRecipients.push_back(recipient);
}

View File

@ -0,0 +1,97 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef PLAVBRAINGENERIC_H
#define PLAVBRAINGENERIC_H
#include "hsStlUtils.h"
#include "plAvBrainGeneric.h"
/** \class plAvBrainCoop
This is currently no different whatsoever from plAvBrainGeneric,
but it's quite possible that it will need to vary, so we're
keeping it around for the moment. */
class plAvBrainCoop : public plAvBrainGeneric
{
public:
// used only by the class factory...
plAvBrainCoop();
// use this constructor for a host brain; it sets up the unique ID
plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut, MoveMode moveMode, plKey guestKey);
// use this constructor for the guest brain, when you already have the unique ID
plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut, MoveMode moveMode,
UInt32 initiatorID, UInt16 initiatorSerial, plKey hostKey);
hsBool MsgReceive(plMessage *msg);
virtual bool RelayNotifyMsg(plNotifyMsg *msg);
void EnableGuestClick();
// rtti
CLASSNAME_REGISTER( plAvBrainCoop );
GETINTERFACE_ANY( plAvBrainCoop, plAvBrainGeneric);
// i/o
virtual void Read(hsStream *stream, hsResMgr *mgr);
virtual void Write(hsStream *stream, hsResMgr *mgr);
// stuff
UInt32 GetInitiatorID();
UInt16 GetInitiatorSerial();
virtual plKey GetRecipient();
virtual void SetRecipient(plKey &recipient);
private:
UInt32 fInitiatorID;
UInt16 fInitiatorSerial;
plKey fGuestKey; // only filled out if we are the host
plKey fHostKey; // only filled out if we are the guest
bool fWaitingForClick;
std::vector<plKey> fRecipients; // we have an array for a slight hack so relto book sharing works
};
#endif

View File

@ -0,0 +1,742 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsConfig.h"
#include "hsWindows.h"
#include "plPhysicalControllerCore.h"
#include "plAvBrainCritter.h"
#include "plAvBrainHuman.h"
#include "plArmatureMod.h"
#include "plAvBehaviors.h"
#include "plAGAnim.h"
#include "plAGAnimInstance.h"
#include "plAvatarMgr.h"
#include "plgDispatch.h"
#include "../plMessage/plAIMsg.h"
#include "../plPipeline/plDebugText.h"
#include "../pnSceneObject/plCoordinateInterface.h"
#include "../plMath/plRandom.h"
#include "../plNetClient/plNetClientMgr.h"
#include "../plNetTransport/plNetTransportMember.h"
///////////////////////////////////////////////////////////////////////////////
static plRandom sRandom; // random number generator
const char kDefaultIdleAnimName[] = "Idle";
const char kDefaultIdleBehName[] = "Idle";
const char kDefaultRunAnimName[] = "Run";
const char kDefaultRunBehName[] = "Run";
const float kLoudSoundMultiplyer = 2.0f;
///////////////////////////////////////////////////////////////////////////////
class CritterBehavior : public plArmatureBehavior
{
friend class plAvBrainCritter;
public:
CritterBehavior(const std::string& name, bool randomStart = false, float fadeInLength = 2.f, float fadeOutLength = 2.f) : plArmatureBehavior(),
fAvMod(nil), fCritterBrain(nil), fName(name), fRandomStartPoint(randomStart), fFadeInLength(fadeInLength), fFadeOutLength(fadeOutLength) {}
virtual ~CritterBehavior() {}
void Init(plAGAnim* anim, hsBool loop, plAvBrainCritter* brain, plArmatureMod* body, UInt8 index)
{
plArmatureBehavior::Init(anim, loop, brain, body, index);
fAvMod = body;
fCritterBrain = brain;
fAnimName = anim->GetName();
}
virtual hsBool PreCondition(double time, float elapsed) {return true;}
hsScalar GetAnimLength() {return (fAnim->GetAnimation()->GetLength());}
void SetAnimTime(hsScalar time) {fAnim->SetCurrentTime(time, true);}
std::string Name() const {return fName;}
std::string AnimName() const {return fAnimName;}
bool RandomStartPoint() const {return fRandomStartPoint;}
float FadeInLength() const {return fFadeInLength;}
float FadeOutLength() const {return fFadeOutLength;}
protected:
virtual void IStart()
{
plArmatureBehavior::IStart();
fAvMod->SynchIfLocal(hsTimer::GetSysSeconds(), false);
}
virtual void IStop()
{
plArmatureBehavior::IStop();
fAvMod->SynchIfLocal(hsTimer::GetSysSeconds(), false);
}
plArmatureMod *fAvMod;
plAvBrainCritter *fCritterBrain;
std::string fName; // user-created name for this behavior, also used as the index into the brain's behavior map
std::string fAnimName; // physical animation's name, for reference
bool fRandomStartPoint; // do we want this behavior to start at a random frame every time we start it?
float fFadeInLength; // how long to fade in this behavior
float fFadeOutLength; // how long to fade out this behavior
};
///////////////////////////////////////////////////////////////////////////////
plAvBrainCritter::plAvBrainCritter(): fWalkingStrategy(nil), fCurMode(kIdle), fNextMode(kIdle), fFadingNextBehavior(true),
fLocallyControlled(false), fAvoidingAvatars(false), fFinalGoalPos(0, 0, 0), fImmediateGoalPos(0, 0, 0), fDotGoal(0),
fAngRight(0)
{
SightCone(hsScalarPI/2); // 90deg
StopDistance(1);
SightDistance(10);
HearingDistance(10);
}
plAvBrainCritter::~plAvBrainCritter()
{
for (int i = 0; i < fBehaviors.GetCount(); ++i)
{
delete fBehaviors[i];
fBehaviors[i] = nil;
}
delete fWalkingStrategy;
fWalkingStrategy = nil;
fUserBehaviors.clear();
fReceivers.clear();
}
///////////////////////////////////////////////////////////////////////////////
hsBool plAvBrainCritter::Apply(double time, hsScalar elapsed)
{
// update internal pathfinding variables
IEvalGoal();
if (fNextMode >= kIdle)
{
// next mode is set, fade out the previous mode and start up the new one
IFadeOutBehavior();
IStartBehavior();
}
else
IProcessBehavior(time, elapsed); // just continue with the currently running one
// update our controller to keep us turned and moving to where we want to go
fWalkingStrategy->SetTurnStrength(IGetTurnStrength(time));
fWalkingStrategy->RecalcVelocity(time, elapsed);
return plArmatureBrain::Apply(time, elapsed);
}
hsBool plAvBrainCritter::MsgReceive(plMessage* msg)
{
return plArmatureBrain::MsgReceive(msg);
}
///////////////////////////////////////////////////////////////////////////////
void plAvBrainCritter::Activate(plArmatureModBase* avMod)
{
plArmatureBrain::Activate(avMod);
// initialize our base "Run" and "Idle" behaviors
IInitBaseAnimations();
// create the controller if we haven't done so already
if (!fWalkingStrategy)
{
plSceneObject* avObj = fArmature->GetTarget(0);
plAGModifier* agMod = const_cast<plAGModifier*>(plAGModifier::ConvertNoRef(FindModifierByClass(avObj, plAGModifier::Index())));
plPhysicalControllerCore* controller = avMod->GetController();
fWalkingStrategy = TRACKED_NEW plWalkingStrategy(agMod->GetApplicator(kAGPinTransform), controller);
controller->SetMovementStrategy(fWalkingStrategy);
}
// tell people that care that we are good to go
plAIBrainCreatedMsg* brainCreated = TRACKED_NEW plAIBrainCreatedMsg(fArmature->GetKey());
plgDispatch::MsgSend(brainCreated);
}
void plAvBrainCritter::Deactivate()
{
plArmatureBrain::Deactivate();
}
void plAvBrainCritter::Suspend()
{
// fade out the previous behavior
CritterBehavior *behavior = (CritterBehavior*)fBehaviors[fCurMode];
behavior->SetStrength(0.f, fFadingNextBehavior ? behavior->FadeOutLength() : 0.f);
// fade in the idle
fNextMode = kIdle;
plArmatureBrain::Suspend();
}
void plAvBrainCritter::Resume()
{
// fade in the idle
fNextMode = kIdle;
fWalkingStrategy->Reset(false);
plArmatureBrain::Resume();
}
void plAvBrainCritter::AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop /* = true */, bool randomStartPos /* = true */,
float fadeInLen /* = 2.f */, float fadeOutLen /* = 2.f */)
{
// grab the animations
plAGAnim* anim = fAvMod->FindCustomAnim(animationName.c_str());
if (!anim)
return; // can't find it, die
// create the behavior and set it up
CritterBehavior* behavior = TRACKED_NEW CritterBehavior(behaviorName, randomStartPos, fadeInLen, fadeOutLen);
fBehaviors.Push(behavior);
behavior->Init(anim, loop, this, fAvMod, fBehaviors.Count() - 1);
fUserBehaviors[behaviorName].push_back(fBehaviors.Count() - 1);
}
void plAvBrainCritter::StartBehavior(const std::string& behaviorName, bool fade /* = true */)
{
// make sure the new behavior exists
if (fUserBehaviors.find(behaviorName) == fUserBehaviors.end())
return;
else
{
if (fUserBehaviors[behaviorName].size() == 0)
return;
}
// remember the fade request
fFadingNextBehavior = fade;
// pick our next behavior
fNextMode = IPickBehavior(behaviorName);
}
bool plAvBrainCritter::RunningBehavior(const std::string& behaviorName) const
{
// make sure the behavior exists
std::map<std::string, std::vector<int> >::const_iterator behaviorIterator = fUserBehaviors.find(behaviorName);
if (behaviorIterator == fUserBehaviors.end())
return false;
else
{
if (behaviorIterator->second.size() == 0)
return false;
}
// check all behaviors that use this tag and return true if we are running one of them
for (unsigned i = 0; i < behaviorIterator->second.size(); ++i)
{
if (fCurMode == behaviorIterator->second[i])
return true;
}
return false;
}
std::string plAvBrainCritter::BehaviorName(int behavior) const
{
if ((behavior >= fBehaviors.Count()) || (behavior < 0))
return "";
return ((CritterBehavior*)fBehaviors[behavior])->Name();
}
std::string plAvBrainCritter::AnimationName(int behavior) const
{
if ((behavior >= fBehaviors.Count()) || (behavior < 0))
return "";
return ((CritterBehavior*)fBehaviors[behavior])->AnimName();
}
std::string plAvBrainCritter::IdleBehaviorName() const
{
return kDefaultIdleBehName;
}
std::string plAvBrainCritter::RunBehaviorName() const
{
return kDefaultRunBehName;
}
void plAvBrainCritter::GoToGoal(hsPoint3 newGoal, bool avoidingAvatars /* = false */)
{
fFinalGoalPos = newGoal;
fAvoidingAvatars = avoidingAvatars;
fNextMode = IPickBehavior(kRun);
// TODO: Pathfinding here!
}
bool plAvBrainCritter::AtGoal() const
{
// we are at our goal if our distance from it is less then or equal to our stopping distance
hsPoint3 creaturePos;
hsQuat creatureRot;
fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot);
hsVector3 finalGoalVec(creaturePos - fFinalGoalPos);
return (finalGoalVec.MagnitudeSquared() <= fStopDistanceSquared);
}
void plAvBrainCritter::SightCone(hsScalar coneRad)
{
fSightConeAngle = coneRad;
// calculate the minimum dot product for the cone of sight (angle/2 vector dotted with straight ahead)
hsVector3 straightVector(1, 0, 0), viewVector(1, 0, 0);
hsQuat rotation(fSightConeAngle/2, &hsVector3(0, 1, 0));
viewVector = hsVector3(rotation.Rotate(&viewVector));
viewVector.Normalize();
fSightConeDotMin = straightVector * viewVector;
}
void plAvBrainCritter::HearingDistance(hsScalar hearDis)
{
fHearingDistance = hearDis;
fHearingDistanceSquared = fHearingDistance * fHearingDistance;
fLoudHearingDistanceSquared = (fHearingDistance * kLoudSoundMultiplyer) * (fHearingDistance * kLoudSoundMultiplyer);
}
bool plAvBrainCritter::CanSeeAvatar(unsigned long id) const
{
plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(id);
if (avatar)
return ICanSeeAvatar(avatar);
return false;
}
bool plAvBrainCritter::CanHearAvatar(unsigned long id) const
{
plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(id);
if (avatar)
return ICanHearAvatar(avatar);
return false;
}
std::vector<unsigned long> plAvBrainCritter::PlayersICanSee() const
{
std::vector<unsigned long> allPlayers = IGetAgePlayerIDList();
std::vector<unsigned long> onesICanSee;
for (unsigned i = 0; i < allPlayers.size(); ++i)
{
if (CanSeeAvatar(allPlayers[i]))
onesICanSee.push_back(allPlayers[i]);
}
return onesICanSee;
}
std::vector<unsigned long> plAvBrainCritter::PlayersICanHear() const
{
std::vector<unsigned long> allPlayers = IGetAgePlayerIDList();
std::vector<unsigned long> onesICanHear;
for (unsigned i = 0; i < allPlayers.size(); ++i)
{
if (CanHearAvatar(allPlayers[i]))
onesICanHear.push_back(allPlayers[i]);
}
return onesICanHear;
}
hsVector3 plAvBrainCritter::VectorToPlayer(unsigned long id) const
{
plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(id);
if (!avatar)
return hsVector3(0, 0, 0);
hsPoint3 avPos;
hsQuat avRot;
avatar->GetPositionAndRotationSim(&avPos, &avRot);
hsPoint3 creaturePos;
hsQuat creatureRot;
fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot);
return hsVector3(creaturePos - avPos);
}
void plAvBrainCritter::AddReceiver(const plKey key)
{
for (unsigned i = 0; i < fReceivers.size(); ++i)
{
if (fReceivers[i] == key)
return; // already in our list
}
fReceivers.push_back(key);
}
void plAvBrainCritter::RemoveReceiver(const plKey key)
{
for (unsigned i = 0; i < fReceivers.size(); ++i)
{
if (fReceivers[i] == key)
{
fReceivers.erase(fReceivers.begin() + i);
return;
}
}
return; // not found, do nothing
}
void plAvBrainCritter::DumpToDebugDisplay(int& x, int& y, int lineHeight, char* strBuf, plDebugText& debugTxt)
{
sprintf(strBuf, "Brain type: Critter");
debugTxt.DrawString(x, y, strBuf, 0, 255, 255);
y += lineHeight;
// extract the name from the behavior running
if (fBehaviors[fCurMode])
sprintf(strBuf, "Mode: %s", ((CritterBehavior*)(fBehaviors[fCurMode]))->Name().c_str());
else
sprintf(strBuf, "Mode: Unknown");
// draw it
debugTxt.DrawString(x, y, strBuf);
y += lineHeight;
for (int i = 0; i < fBehaviors.GetCount(); ++i)
fBehaviors[i]->DumpDebug(x, y, lineHeight, strBuf, debugTxt);
}
///////////////////////////////////////////////////////////////////////////////
hsBool plAvBrainCritter::IInitBaseAnimations()
{
// create the basic idle and run behaviors, and put them into our appropriate structures
plAGAnim* idle = fAvMod->FindCustomAnim(kDefaultIdleAnimName);
plAGAnim* run = fAvMod->FindCustomAnim(kDefaultRunAnimName);
hsAssert(idle, "Creature is missing idle animation");
hsAssert(run, "Creature is missing run animation");
fBehaviors.SetCountAndZero(kNumDefaultModes);
CritterBehavior* behavior;
if (idle)
{
fBehaviors[kIdle] = behavior = TRACKED_NEW CritterBehavior(kDefaultIdleBehName, true); // starts at a random start point each time
behavior->Init(idle, true, this, fAvMod, kIdle);
fUserBehaviors[kDefaultIdleBehName].push_back(kIdle);
}
if (run)
{
fBehaviors[kRun] = behavior = TRACKED_NEW CritterBehavior(kDefaultRunBehName);
behavior->Init(run, true, this, fAvMod, kRun);
fUserBehaviors[kDefaultRunBehName].push_back(kRun);
}
return true;
}
int plAvBrainCritter::IPickBehavior(int behavior) const
{
if ((behavior >= fBehaviors.Count()) || (behavior < 0))
return IPickBehavior(kDefaultIdleBehName); // do an idle if the behavior is invalid
CritterBehavior* behaviorObj = (CritterBehavior*)(fBehaviors[behavior]);
return IPickBehavior(behaviorObj->Name());
}
int plAvBrainCritter::IPickBehavior(const std::string& behavior) const
{
// make sure the behavior exists
std::map<std::string, std::vector<int> >::const_iterator behaviorIterator = fUserBehaviors.find(behavior);
if (behaviorIterator == fUserBehaviors.end())
{
if (behavior != kDefaultIdleBehName)
return IPickBehavior(kDefaultIdleBehName); // do an idle if the behavior is invalid
return -1; // can't recover from being unable to find an idle!
}
else
{
unsigned numBehaviors = behaviorIterator->second.size();
if (numBehaviors == 0)
{
if (behavior != kDefaultIdleBehName)
return IPickBehavior(kDefaultIdleBehName); // do an idle if the behavior is invalid
return -1; // can't recover from being unable to find an idle!
}
// pick our behavior
unsigned index = sRandom.RandRangeI(0, numBehaviors - 1);
return behaviorIterator->second[index];
}
}
void plAvBrainCritter::IFadeOutBehavior()
{
if ((fCurMode >= fBehaviors.Count()) || (fCurMode < 0))
return; // invalid fCurMode
// fade out currently playing behavior
CritterBehavior* behavior = (CritterBehavior*)fBehaviors[fCurMode];
behavior->SetStrength(0.f, fFadingNextBehavior ? behavior->FadeOutLength() : 0.f);
}
void plAvBrainCritter::IStartBehavior()
{
if ((fNextMode >= fBehaviors.Count()) || (fNextMode < 0))
return; // invalid fNextMode
// fade in our behavior
CritterBehavior* behavior = (CritterBehavior*)fBehaviors[fNextMode];
behavior->SetStrength(1.f, fFadingNextBehavior ? behavior->FadeInLength() : 0.f);
// if we start at a random point, do so
if (behavior->RandomStartPoint())
{
hsScalar newStart = sRandom.RandZeroToOne() * behavior->GetAnimLength();
behavior->SetAnimTime(newStart);
}
// clean up the internal variables
fCurMode = fNextMode;
fNextMode = -1;
}
void plAvBrainCritter::IProcessBehavior(double time, float elapsed)
{
// run the currently running behavior
CritterBehavior* behavior = (CritterBehavior*)fBehaviors[fCurMode];
behavior->SetStrength(1.f, fFadingNextBehavior ? behavior->FadeInLength() : 0.f);
behavior->Process(time, elapsed);
}
void plAvBrainCritter::IEvalGoal()
{
// TODO: Implement pathfinding logic here
// (for now, this runs directly towards the goal)
fImmediateGoalPos = fFinalGoalPos;
// where am I relative to my goal?
const plSceneObject* creatureObj = fArmature->GetTarget(0);
hsVector3 view(creatureObj->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView));
hsPoint3 creaturePos;
hsQuat creatureRot;
fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot);
hsVector3 goalVec(creaturePos - fImmediateGoalPos);
goalVec.Normalize();
fDotGoal = goalVec * view; // 1 = directly facing, 0 = 90 deg off, -1 = facing away
// calculate a vector pointing to the creature's right
hsQuat invRot = creatureRot.Conjugate();
hsPoint3 globRight = invRot.Rotate(&kAvatarRight);
fAngRight = globRight.InnerProduct(goalVec); // dot product, 1 = goal is 90 to the right, 0 = goal is in front or behind, -1 = goal is 90 to the left
if (fAvoidingAvatars)
{
// check to see we can see anyone in our way (if we can't see them, we can't avoid them)
std::vector<plArmatureMod*> playersICanSee = IAvatarsICanSee();
for (unsigned i = 0; i < playersICanSee.size(); ++i)
{
hsPoint3 avPos;
hsQuat avRot;
playersICanSee[i]->GetPositionAndRotationSim(&avPos, &avRot);
hsVector3 avVec(creaturePos - avPos);
avVec.Normalize();
hsScalar dotAv = avVec * goalVec;
if (dotAv > 0.5f) // within a 45deg angle in front of us
{
// a player is in the way, so we will change our "goal" to a 90deg angle from the player
// then we stop searching, since any other players in the way will just produce the same (or similar) result
avVec.fZ = goalVec.fZ = 0.f;
goalVec = goalVec % avVec;
fAngRight = globRight.InnerProduct(goalVec);
break;
}
}
}
// are we at our final goal?
if (AtGoal())
{
if (RunningBehavior(kDefaultRunBehName)) // don't do anything if we're not running!
{
// we're close enough, stop running and pick an idle
fNextMode = IPickBehavior(kIdle);
// tell everyone who cares that we have arrived
for (unsigned i = 0; i < fReceivers.size(); ++i)
{
plAIArrivedAtGoalMsg* msg = TRACKED_NEW plAIArrivedAtGoalMsg(fArmature->GetKey(), fReceivers[i]);
msg->Goal(fFinalGoalPos);
msg->Send();
}
}
}
}
hsScalar plAvBrainCritter::IGetTurnStrength(double time) const
{
if (!RunningBehavior(kDefaultRunBehName))
return 0.0f;
// am I directly facing my goal?
if (fDotGoal < -0.98)
return 0.f;
if (fAngRight > 0.f)
return 1.f;
return -1.f;
}
std::vector<unsigned long> plAvBrainCritter::IGetAgePlayerIDList() const
{
// make a list of non-local players
std::vector<unsigned long> playerIDs;
std::map<unsigned long, bool> tempMap; // slightly hacky way to remove dups
plNetClientMgr* nc = plNetClientMgr::GetInstance();
for (int i = 0; i < nc->TransportMgr().GetNumMembers(); ++i)
{
plNetTransportMember* mbr = nc->TransportMgr().GetMember(i);
unsigned long id = mbr->GetPlayerID();
if (tempMap.find(id) == tempMap.end())
{
playerIDs.push_back(id);
tempMap[id] = true;
}
}
// add the local player if he isn't already in the list
unsigned long localID = nc->GetPlayerID();
if (tempMap.find(localID) == tempMap.end())
playerIDs.push_back(localID);
// return result
return playerIDs;
}
bool plAvBrainCritter::ICanSeeAvatar(plArmatureMod* avatar) const
{
// sight is a x deg cone in front of the critter, cuts off at a certain distance
hsPoint3 avPos;
hsQuat avRot;
avatar->GetPositionAndRotationSim(&avPos, &avRot);
hsPoint3 creaturePos;
hsQuat creatureRot;
fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot);
hsVector3 avVec(creaturePos - avPos);
if (avVec.MagnitudeSquared() > fSightDistanceSquared)
return false; // too far away
avVec.Normalize();
const plSceneObject* creatureObj = fArmature->GetTarget(0);
hsVector3 view(creatureObj->GetCoordinateInterface()->GetLocalToWorld().GetAxis(hsMatrix44::kView));
hsScalar avDot = view * avVec;
if (avDot < fSightConeDotMin)
return false; // out of our cone of view
return true;
}
bool plAvBrainCritter::ICanHearAvatar(plArmatureMod* avatar) const
{
// check to see if the avatar is being loud (running or jumping)
bool isLoud = false;
plAvBrainHuman* humanBrain = plAvBrainHuman::ConvertNoRef(avatar->FindBrainByClass(plAvBrainHuman::Index()));
if (humanBrain)
{
isLoud = humanBrain->IsBehaviorPlaying(plAvBrainHuman::kRun) || humanBrain->IsBehaviorPlaying(plAvBrainHuman::kStandingJump) ||
humanBrain->IsBehaviorPlaying(plAvBrainHuman::kWalkingJump) || humanBrain->IsBehaviorPlaying(plAvBrainHuman::kRunningJump) ||
humanBrain->IsBehaviorPlaying(plAvBrainHuman::kGroundImpact) || humanBrain->IsBehaviorPlaying(plAvBrainHuman::kRunningImpact);
}
// hearing is 360 degrees around the critter, cuts off at a certain distance
hsPoint3 avPos;
hsQuat avRot;
avatar->GetPositionAndRotationSim(&avPos, &avRot);
hsPoint3 creaturePos;
hsQuat creatureRot;
fAvMod->GetPositionAndRotationSim(&creaturePos, &creatureRot);
hsVector3 avVec(creaturePos - avPos);
hsScalar distSq = avVec.MagnitudeSquared();
if (distSq <= fHearingDistanceSquared)
return true; // within our normal hearing distance
else if (isLoud && (distSq <= fLoudHearingDistanceSquared))
return true; // they are being loud, and within our loud hearing distance
return false;
}
std::vector<plArmatureMod*> plAvBrainCritter::IAvatarsICanSee() const
{
std::vector<unsigned long> allPlayers = IGetAgePlayerIDList();
std::vector<plArmatureMod*> onesICanSee;
for (unsigned i = 0; i < allPlayers.size(); ++i)
{
plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(allPlayers[i]);
if (!avatar)
continue;
if (ICanSeeAvatar(avatar))
onesICanSee.push_back(avatar);
}
return onesICanSee;
}
std::vector<plArmatureMod*> plAvBrainCritter::IAvatarsICanHear() const
{
std::vector<unsigned long> allPlayers = IGetAgePlayerIDList();
std::vector<plArmatureMod*> onesICanHear;
for (unsigned i = 0; i < allPlayers.size(); ++i)
{
plArmatureMod* avatar = plAvatarMgr::GetInstance()->FindAvatarByPlayerID(allPlayers[i]);
if (!avatar)
continue;
if (ICanHearAvatar(avatar))
onesICanHear.push_back(avatar);
}
return onesICanHear;
}

View File

@ -0,0 +1,182 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#ifndef PL_AV_BRAIN_CRITTER_H
#define PL_AV_BRAIN_CRITTER_H
#include "plAvBrain.h"
#include "hsTemplates.h"
#include "../pnKeyedObject/plKey.h"
class plArmatureMod;
class plWalkingStrategy;
class plAIMsg;
class plAvBrainCritter : public plArmatureBrain
{
public:
enum CritterMode
{
kIdle = 0,
kRun,
kNumDefaultModes
// anything >= kNumDefaultModes is user
};
enum Anims
{
kIdleAnim = 0,
kRunAnim,
kNumDefaultAnims
// anything >= kNumDefaultAnims this is user
};
plAvBrainCritter();
virtual ~plAvBrainCritter();
CLASSNAME_REGISTER(plAvBrainCritter);
GETINTERFACE_ANY(plAvBrainCritter, plArmatureBrain);
hsBool Apply(double time, hsScalar elapsed);
hsBool MsgReceive(plMessage* msg);
virtual void Activate(plArmatureModBase* avMod);
virtual void Deactivate();
virtual void Suspend();
virtual void Resume();
void AddBehavior(const std::string& animationName, const std::string& behaviorName, bool loop = true, bool randomStartPos = true,
float fadeInLen = 2.f, float fadeOutLen = 2.f);
void StartBehavior(const std::string& behaviorName, bool fade = true);
bool RunningBehavior(const std::string& behaviorName) const;
void LocallyControlled(bool local) {fLocallyControlled = local;}
bool LocallyControlled() const {return fLocallyControlled;}
std::string BehaviorName(int behavior) const;
std::string AnimationName(int behavior) const;
int CurBehavior() const {return fCurMode;}
int NextBehavior() const {return fNextMode;}
std::string IdleBehaviorName() const;
std::string RunBehaviorName() const;
void GoToGoal(hsPoint3 newGoal, bool avoidingAvatars = false);
hsPoint3 CurrentGoal() const {return fFinalGoalPos;}
bool AvoidingAvatars() const {return fAvoidingAvatars;}
bool AtGoal() const;
void StopDistance(hsScalar stopDistance) {fStopDistance = stopDistance; fStopDistanceSquared = fStopDistance * fStopDistance;}
hsScalar StopDistance() const {return fStopDistance;}
void SightCone(hsScalar coneRad);
hsScalar SightCone() const {return fSightConeAngle;}
void SightDistance(hsScalar sightDis) {fSightDistance = sightDis; fSightDistanceSquared = fSightDistance * fSightDistance;}
hsScalar SightDistance() const {return fSightDistance;}
void HearingDistance(hsScalar hearDis);
hsScalar HearingDistance() const {return fHearingDistance;}
bool CanSeeAvatar(unsigned long id) const;
bool CanHearAvatar(unsigned long id) const;
std::vector<unsigned long> PlayersICanSee() const;
std::vector<unsigned long> PlayersICanHear() const;
hsVector3 VectorToPlayer(unsigned long id) const;
void AddReceiver(const plKey key);
void RemoveReceiver(const plKey key);
virtual void DumpToDebugDisplay(int& x, int& y, int lineHeight, char* strBuf, plDebugText& debugTxt);
// For the console
static bool fDrawDebug;
protected:
virtual hsBool IInitBaseAnimations();
int IPickBehavior(int behavior) const;
int IPickBehavior(const std::string& behavior) const;
void IFadeOutBehavior(); // fades out fCurMode
void IStartBehavior(); // fades in and initializes fNextMode, then sets fCurMode
void IProcessBehavior(double time, float elapsed); // processes fCurMode
void IEvalGoal();
hsScalar IGetTurnStrength(double time) const;
std::vector<unsigned long> IGetAgePlayerIDList() const;
bool ICanSeeAvatar(plArmatureMod* avatar) const;
bool ICanHearAvatar(plArmatureMod* avatar) const;
std::vector<plArmatureMod*> IAvatarsICanSee() const;
std::vector<plArmatureMod*> IAvatarsICanHear() const;
plWalkingStrategy* fWalkingStrategy;
int fCurMode; // current behavior we are running
int fNextMode; // the next behavior to run (-1 if we aren't switching on next eval)
bool fFadingNextBehavior; // is the next behavior supposed to blend?
bool fLocallyControlled; // is our local AI script the one making all the choices?
bool fAvoidingAvatars; // are we avoiding avatars to the best of our ability when pathfinding?
hsPoint3 fFinalGoalPos; // the location we are pathfinding to
hsPoint3 fImmediateGoalPos; // the location of the point we are immediately going towards (not necessarily our final goal)
hsScalar fDotGoal; // dot product to our goal
hsScalar fAngRight; // dot product of our local right-hand vector to our goal
hsScalar fStopDistance; // how close we need to get to our goal before stopping
hsScalar fStopDistanceSquared; // the above, squared, for faster calculation
hsScalar fSightConeAngle; // in radians, the width of the cone we can see (/2 on each side of where we face, so 90deg cone is 45deg on each side)
hsScalar fSightConeDotMin; // the minimum dot-product of the cone we can see (1 - straight ahead only, 0 - 90deg either side, -1 - 180 behind, or full 360)
hsScalar fSightDistance; // how far away we can see (cone in front of us)
hsScalar fSightDistanceSquared; // the above, squared, for faster calculation
hsScalar fHearingDistance; // how far away we can hear (360 degrees)
hsScalar fHearingDistanceSquared; // the above, squared, for faster calculation
hsScalar fLoudHearingDistanceSquared; // how far away we can hear loud noises, squared, for faster calculation
std::map<std::string, std::vector<int> > fUserBehaviors; // string is behavior name, internal vector is the list of behaviors that are randomly picked from
std::vector<plKey> fReceivers; // list of people that want messages from us
};
#endif // PL_AV_BRAIN_CRITTER_H

View File

@ -0,0 +1,198 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
// local includes
#include "plAvBrainDrive.h"
#include "plArmatureMod.h"
// global includes
#include "hsTimer.h"
#include "hsGeometry3.h"
// other includes
#include "hsQuat.h"
#include "../plMessage/plSimStateMsg.h"
#include "../pnMessage/plCameraMsg.h"
// messages
#include "../plMessage/plInputEventMsg.h"
// CTOR default
plAvBrainDrive::plAvBrainDrive()
: fMaxVelocity(20), fTurnRate(1)
{
}
// CTOR max velocity, turn rate
plAvBrainDrive::plAvBrainDrive(hsScalar maxVelocity, hsScalar turnRate)
: fMaxVelocity(maxVelocity), fTurnRate(turnRate)
{
}
// ACTIVATE
void plAvBrainDrive::Activate(plArmatureModBase *avMod)
{
plArmatureBrain::Activate(avMod);
IEnablePhysics(false, avMod->GetTarget(0)->GetKey());
plCameraMsg* pMsg = TRACKED_NEW plCameraMsg;
pMsg->SetCmd(plCameraMsg::kNonPhysOn);
pMsg->SetBCastFlag(plMessage::kBCastByExactType);
pMsg->Send();
}
// DEACTIVATE
void plAvBrainDrive::Deactivate()
{
if (fAvMod)
{
IEnablePhysics(true, fAvMod->GetTarget(0)->GetKey());
plCameraMsg* pMsg = TRACKED_NEW plCameraMsg;
pMsg->SetCmd(plCameraMsg::kNonPhysOff);
pMsg->SetBCastFlag(plMessage::kBCastByExactType);
pMsg->Send();
}
}
void plAvBrainDrive::IEnablePhysics(bool enable, plKey avKey)
{
fAvMod->EnablePhysics(enable);
}
// APPLY
hsBool plAvBrainDrive::Apply(double timeNow, hsScalar elapsed)
{
plSceneObject * avSO = fAvMod->GetTarget(0);
hsScalar eTime = hsTimer::GetDelSysSeconds();
hsMatrix44 targetMatrix = avSO->GetLocalToWorld();
hsPoint3 playerPos = targetMatrix.GetTranslate();
hsVector3 view, up, right;
targetMatrix.GetAxis(&view, &up, &right);
hsScalar speed = fMaxVelocity;
hsScalar turn = fTurnRate;
if (fAvMod->FastKeyDown())
{
turn *= 0.25;
speed *= 3.5;
}
if (fAvMod->GetInputFlag(B_CONTROL_MOVE_FORWARD))
{
playerPos += view * speed * eTime;
}
if (fAvMod->GetInputFlag(B_CONTROL_MOVE_BACKWARD))
{
playerPos += view * speed * eTime * -1;
}
if (fAvMod->StrafeLeftKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnLeftKeyDown()))
{
playerPos += right * speed * eTime * -1;
}
if (fAvMod->StrafeRightKeyDown() || (fAvMod->StrafeKeyDown() && fAvMod->TurnRightKeyDown()))
{
playerPos += right * speed * eTime;
}
if (fAvMod->GetInputFlag(B_CONTROL_MOVE_DOWN))
{
playerPos += up * speed * eTime * -1;
}
if (fAvMod->GetInputFlag(B_CONTROL_MOVE_UP))
{
playerPos += up * speed * eTime;
}
hsPoint3 desiredPosition = playerPos;
// calculate rotation matrix
hsVector3 rotUp(0,0,1);
hsVector3 rotRight(1,0,0);
hsMatrix44 rot;
hsScalar angle = 0;
if ( fAvMod->GetInputFlag( B_CONTROL_ROTATE_RIGHT ) || fAvMod->GetInputFlag( B_CONTROL_ROTATE_LEFT ) || fAvMod->GetInputFlag( A_CONTROL_TURN ) )
{
angle = fTurnRate * eTime * fAvMod->GetTurnStrength();
}
hsMatrix44 justRot(targetMatrix);
hsPoint3 zero(0,0,0);
justRot.SetTranslate(&zero);
if( angle ) {
hsQuat q( angle, &rotUp );
q.NormalizeIfNeeded();
q.MakeMatrix( &rot );
justRot = rot * justRot;
targetMatrix = rot * targetMatrix;
}
// use the desired rotation matrix to set position and rotation:
hsMatrix44 inv;
targetMatrix.SetTranslate( &desiredPosition );
targetMatrix.GetInverse( &inv );
avSO->SetTransform( targetMatrix, inv );
avSO->FlushTransform();
return true;
}
// IHANDLECONTROLMSG
hsBool plAvBrainDrive::MsgReceive(plMessage* msg)
{
plControlEventMsg *ctrlMsg = plControlEventMsg::ConvertNoRef(msg);
if(ctrlMsg)
{
if( ctrlMsg->ControlActivated() )
{
if(ctrlMsg->GetControlCode() == B_CONTROL_TOGGLE_PHYSICAL)
{
fAvMod->PopBrain();
return true;
}
}
}
return false;
}

View File

@ -0,0 +1,101 @@
/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/** \file plAvBrainDrive.h
*/
#ifndef AVBRAINDRIVE_INC
#define AVBRAINDRIVE_INC
// base class
#include "plAvBrain.h"
/** A simple brain used to steer the player around without regard
for physics. Used in production to quickly fly through a scene
without having to actually solve puzzles, jump, etc.
The 'Drive Brain' uses the same input keys as the avatar, with
a few secret additions for convenience. At the time of this
writing, you invoke the drive brain by pressing shift-P, and
then use the forward and back arrows to move and th e left and
right arrows to rotate. The 'u' and 'j' keys will move your avatar
vertically.
Gravity and collision are completely suspended for avatars in
drive mode, but they *can* still trigger region detectors if
you need to fire off triggers, puzzles, or whatnot.
Note that the drive brain inherits from the user brain, which
parses control input for us.
*/
class plAvBrainDrive : public plArmatureBrain
{
public:
plAvBrainDrive();
/** Canonical constructer. Use this one.
\param maxVelocity The highest speed this avatar can fly at.
\param turnRate The speed at which we will turn, in radians per second.
*/
plAvBrainDrive(hsScalar maxVelocity, hsScalar turnRate);
// BRAIN PROTOCOL
/** Suspend physics and get in line to receive keyboard control messages. */
virtual void Activate(plArmatureModBase *avMod);
/** Restore physical reality and stop handling input messages */
virtual void Deactivate();
/** Look at the key states and figure out if and how we should move */
virtual hsBool Apply(double timeNow, hsScalar elapsed); // main control loop. called by avatar eval()
// the user brain base handles most of the details of control messages,
// so this function just looks for the special command which gets us out
// of drive mode.
virtual hsBool MsgReceive(plMessage* pMsg); // handle control input from the user
CLASSNAME_REGISTER( plAvBrainDrive );
GETINTERFACE_ANY( plAvBrainDrive, plArmatureBrain );
protected:
void IEnablePhysics(bool enable, plKey avKey);
hsScalar fMaxVelocity;
hsScalar fTurnRate;
};
#endif // AVBRAINDRIVE_INC

Some files were not shown because too many files have changed in this diff Show More