Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • alchemy/viewer
  • Miezhiko/alchemy-next
  • JennaHuntsman/xdg-integration
  • logue/alchemy-next
  • FelixWolf/alchemy-viewer
  • XenHat/xdg-integration
6 results
Show changes
/**
/**
* @file listener_openal.h
* @brief Description of LISTENER class abstracting the audio support
* as an OpenAL implementation
......@@ -6,21 +6,21 @@
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -32,26 +32,27 @@
#include "AL/al.h"
#include "AL/alut.h"
#include "AL/alext.h"
class LLListener_OpenAL : public LLListener
class LLListener_OpenAL final : public LLListener
{
public:
LLListener_OpenAL();
virtual ~LLListener_OpenAL();
public:
LLListener_OpenAL();
virtual ~LLListener_OpenAL() = default;
virtual void translate(LLVector3 offset);
virtual void setPosition(LLVector3 pos);
virtual void setVelocity(LLVector3 vel);
virtual void orient(LLVector3 up, LLVector3 at);
virtual void commitDeferredChanges();
virtual void translate(const LLVector3& offset) override;
virtual void setPosition(const LLVector3& pos) override;
virtual void setVelocity(const LLVector3& vel) override;
virtual void orient(const LLVector3& up, const LLVector3& at) override;
virtual void commitDeferredChanges() override;
virtual void setDopplerFactor(F32 factor);
virtual F32 getDopplerFactor();
virtual void setRolloffFactor(F32 factor);
virtual F32 getRolloffFactor();
virtual void setDopplerFactor(F32 factor) override;
virtual F32 getDopplerFactor() override;
virtual void setRolloffFactor(F32 factor) override;
virtual F32 getRolloffFactor() override;
protected:
F32 mRolloffFactor;
F32 mRolloffFactor;
};
#endif
......
/**
/**
* @file streamingaudio.h
* @author Tofu Linden
* @brief Definition of LLStreamingAudioInterface base class abstracting the streaming audio interface
......@@ -6,21 +6,21 @@
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -30,23 +30,44 @@
#include "stdtypes.h" // from llcommon
#include "llsd.h"
#include "boost/signals2.hpp"
class LLSD;
// Entirely abstract. Based exactly on the historic API.
class LLStreamingAudioInterface
{
public:
virtual ~LLStreamingAudioInterface() {}
virtual void start(const std::string& url) = 0;
virtual void stop() = 0;
virtual void pause(int pause) = 0;
virtual void update() = 0;
virtual int isPlaying() = 0;
// use a value from 0.0 to 1.0, inclusive
virtual void setGain(F32 vol) = 0;
virtual F32 getGain() = 0;
virtual std::string getURL() = 0;
virtual bool supportsAdjustableBufferSizes(){return false;}
virtual void setBufferSizes(U32 streambuffertime, U32 decodebuffertime){};
virtual ~LLStreamingAudioInterface() = default;
virtual void start(const std::string& url) = 0;
virtual void stop() = 0;
virtual void pause(int pause) = 0;
virtual void update() = 0;
virtual int isPlaying() = 0;
// use a value from 0.0 to 1.0, inclusive
virtual void setGain(F32 vol) = 0;
virtual F32 getGain() = 0;
virtual std::string getURL() = 0;
virtual bool supportsAdjustableBufferSizes() = 0;
virtual void setBufferSizes(U32 streambuffertime, U32 decodebuffertime) = 0;
virtual bool supportsMetaData() = 0;
using metadata_signal_t = boost::signals2::signal<void(const LLSD& metadata)>;
virtual boost::signals2::connection setMetadataUpdatedCallback(const metadata_signal_t::slot_type& cb)
{
return mMetadataUpdateSignal.connect(cb);
}
virtual LLSD getMetadata() const { return LLSD(); }
virtual bool supportsWaveData() = 0;
virtual bool getWaveData(float* arr, S32 count, S32 stride = 1) = 0;
protected:
metadata_signal_t mMetadataUpdateSignal;
};
#endif // LL_STREAMINGAUDIO_H
/**
/**
* @file streamingaudio_fmodstudio.cpp
* @brief LLStreamingAudio_FMODSTUDIO implementation
*
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2020, Linden Research, Inc.
*
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llstreamingaudio_fmodstudio.h"
#include "llsd.h"
#include "llmath.h"
#include "llmutex.h"
#include "fmodstudio/fmod.hpp"
#include "fmodstudio/fmod_errors.h"
#include "llstreamingaudio_fmodstudio.h"
#include "fmod.hpp"
#include "fmod_errors.h"
inline bool Check_FMOD_Stream_Error(FMOD_RESULT result, const char *string)
{
if (result == FMOD_OK)
return false;
LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << LL_ENDL;
return true;
}
class LLAudioStreamManagerFMODSTUDIO
{
public:
LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, const std::string& url);
LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url);
FMOD::Channel* startStream();
bool stopStream(); // Returns true if the stream was successfully stopped.
bool ready();
const std::string& getURL() { return mInternetStreamURL; }
const std::string& getURL() { return mInternetStreamURL; }
FMOD_OPENSTATE getOpenState(unsigned int* percentbuffered = NULL, bool* starving = NULL, bool* diskbusy = NULL);
FMOD_RESULT getOpenState(FMOD_OPENSTATE& openstate, unsigned int* percentbuffered = nullptr, bool* starving = nullptr, bool* diskbusy = nullptr);
protected:
FMOD::System* mSystem;
FMOD::ChannelGroup* mChannelGroup;
FMOD::Channel* mStreamChannel;
FMOD::Sound* mInternetStream;
bool mReady;
......@@ -54,105 +62,99 @@ class LLAudioStreamManagerFMODSTUDIO
std::string mInternetStreamURL;
};
LLMutex gWaveDataMutex; //Just to be extra strict.
const U32 WAVE_BUFFER_SIZE = 1024;
U32 gWaveBufferMinSize = 0;
F32 gWaveDataBuffer[WAVE_BUFFER_SIZE] = { 0.f };
U32 gWaveDataBufferSize = 0;
//---------------------------------------------------------------------------
// Internet Streaming
//---------------------------------------------------------------------------
LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) :
mSystem(system),
mCurrentInternetStreamp(NULL),
mFMODInternetStreamChannelp(NULL),
mGain(1.0f),
mRetryCount(0)
FMOD_RESULT F_CALLBACK waveDataCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int *outchannels)
{
// Number of milliseconds of audio to buffer for the audio card.
// Must be larger than the usual Second Life frame stutter time.
const U32 buffer_seconds = 10; //sec
const U32 estimated_bitrate = 128; //kbit/sec
FMOD_RESULT result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << "setStreamBufferSize error: " << FMOD_ErrorString(result) << LL_ENDL;
}
// Here's where we set the size of the network buffer and some buffering
// parameters. In this case we want a network buffer of 16k, we want it
// to prebuffer 40% of that when we first connect, and we want it
// to rebuffer 80% of that whenever we encounter a buffer underrun.
if (!length || !inchannels)
return FMOD_OK;
memcpy(outbuffer, inbuffer, length * inchannels * sizeof(float));
// Leave the net buffer properties at the default.
//FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80);
}
static std::vector<F32> local_buf;
if (local_buf.size() < length)
local_buf.resize(length, 0.f);
LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO()
{
if (mCurrentInternetStreamp)
for (U32 i = 0; i < length; ++i)
{
// Isn't supposed to hapen, stream should be clear by now,
// and if it does, we are likely going to crash.
LL_WARNS("FMOD") << "mCurrentInternetStreamp not null on shutdown!" << LL_ENDL;
stop();
F32 total = 0.f;
for (S32 j = 0; j < inchannels; ++j)
{
total += inbuffer[i*inchannels + j];
}
local_buf[i] = total / inchannels;
}
// Kill dead internet streams, if possible
killDeadStreams();
if (!mDeadStreams.empty())
{
// LLStreamingAudio_FMODSTUDIO was inited on startup
// and should be destroyed on shutdown, it should
// wait for streams to die to not cause crashes or
// leaks.
// Ideally we need to wait on some kind of callback
// to release() streams correctly, but 200 ms should
// be enough and we can't wait forever.
LL_INFOS("FMOD") << "Waiting for " << (S32)mDeadStreams.size() << " streams to stop" << LL_ENDL;
for (S32 i = 0; i < 20; i++)
LLMutexLock lock(&gWaveDataMutex);
for (U32 i = length; i > 0; --i)
{
const U32 ms_delay = 10;
ms_sleep(ms_delay); // rude, but not many options here
killDeadStreams();
if (mDeadStreams.empty())
if (++gWaveDataBufferSize > WAVE_BUFFER_SIZE)
{
LL_INFOS("FMOD") << "All streams stopped after " << (S32)((i + 1) * ms_delay) << "ms" << LL_ENDL;
break;
if (gWaveBufferMinSize)
memcpy(gWaveDataBuffer + WAVE_BUFFER_SIZE - gWaveBufferMinSize, gWaveDataBuffer, gWaveBufferMinSize * sizeof(float));
gWaveDataBufferSize = 1 + gWaveBufferMinSize;
}
gWaveDataBuffer[WAVE_BUFFER_SIZE - gWaveDataBufferSize] = local_buf[i - 1];
}
}
if (!mDeadStreams.empty())
{
LL_WARNS("FMOD") << "Failed to kill some audio streams" << LL_ENDL;
}
return FMOD_OK;
}
void LLStreamingAudio_FMODSTUDIO::killDeadStreams()
//---------------------------------------------------------------------------
// Internet Streaming
//---------------------------------------------------------------------------
LLStreamingAudio_FMODSTUDIO::LLStreamingAudio_FMODSTUDIO(FMOD::System *system) :
mSystem(system),
mCurrentInternetStreamp(nullptr),
mStreamDSP(nullptr),
mStreamGroup(nullptr),
mFMODInternetStreamChannelp(nullptr),
mGain(1.0f),
mWasAlreadyPlaying(false),
mMetadata(LLSD::emptyMap())
{
std::list<LLAudioStreamManagerFMODSTUDIO *>::iterator iter;
for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
// Number of milliseconds of audio to buffer for the audio card.
// Must be larger than the usual Second Life frame stutter time.
const U32 buffer_seconds = 10; //sec
const U32 estimated_bitrate = 128; //kbit/sec
Check_FMOD_Stream_Error(mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize");
Check_FMOD_Stream_Error(system->createChannelGroup("stream", &mStreamGroup), "FMOD::System::createChannelGroup");
FMOD_DSP_DESCRIPTION dspdesc = { };
dspdesc.pluginsdkversion = FMOD_PLUGIN_SDK_VERSION;
strncpy(dspdesc.name, "Waveform", sizeof(dspdesc.name));
dspdesc.numoutputbuffers = 1;
dspdesc.read = &waveDataCallback; //Assign callback.
Check_FMOD_Stream_Error(system->createDSP(&dspdesc, &mStreamDSP), "FMOD::System::createDSP");
}
LLStreamingAudio_FMODSTUDIO::~LLStreamingAudio_FMODSTUDIO()
{
stop();
for (U32 i = 0; i < 100; ++i)
{
LLAudioStreamManagerFMODSTUDIO *streamp = *iter;
if (streamp->stopStream())
{
LL_INFOS("FMOD") << "Closed dead stream" << LL_ENDL;
delete streamp;
iter = mDeadStreams.erase(iter);
}
else
{
iter++;
}
if (releaseDeadStreams())
break;
ms_sleep(10);
}
cleanupWaveData();
}
void LLStreamingAudio_FMODSTUDIO::start(const std::string& url)
{
//if (!mInited)
//{
// LL_WARNS() << "startInternetStream before audio initialized" << LL_ENDL;
// return;
// LL_WARNS() << "startInternetStream before audio initialized" << LL_ENDL;
// return;
//}
// "stop" stream but don't clear url, etc. in case url == mInternetStreamURL
......@@ -160,24 +162,83 @@ void LLStreamingAudio_FMODSTUDIO::start(const std::string& url)
if (!url.empty())
{
LL_INFOS("FMOD") << "Starting internet stream: " << url << LL_ENDL;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, url);
mURL = url;
if (mDeadStreams.empty())
{
LL_INFOS() << "Starting internet stream: " << url << LL_ENDL;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, url);
mURL = url;
}
else
{
LL_INFOS() << "Deferring stream load until buffer release: " << url << LL_ENDL;
mPendingURL = url;
}
}
else
{
LL_INFOS("FMOD") << "Set internet stream to null" << LL_ENDL;
LL_INFOS() << "Set internet stream to null" << LL_ENDL;
mURL.clear();
}
mRetryCount = 0;
}
enum utf_endian_type_t
{
UTF16LE,
UTF16BE,
UTF16
};
std::string utf16input_to_utf8(unsigned char* input, U32 len, utf_endian_type_t type)
{
if (type == UTF16)
{
type = UTF16BE; //Default
if (len > 2)
{
//Parse and strip BOM.
if ((input[0] == 0xFE && input[1] == 0xFF) ||
(input[0] == 0xFF && input[1] == 0xFE))
{
input += 2;
len -= 2;
type = input[0] == 0xFE ? UTF16BE : UTF16LE;
}
}
}
llutf16string out_16((llutf16string::value_type*)input, len / 2);
if (len % 2)
{
out_16.push_back((input)[len - 1] << 8);
}
if (type == UTF16BE)
{
for (llutf16string::iterator i = out_16.begin(); i < out_16.end(); ++i)
{
llutf16string::value_type v = *i;
*i = ((v & 0x00FF) << 8) | ((v & 0xFF00) >> 8);
}
}
return utf16str_to_utf8str(out_16);
}
void LLStreamingAudio_FMODSTUDIO::update()
{
// Kill dead internet streams, if possible
killDeadStreams();
if (!releaseDeadStreams())
{
llassert_always(mCurrentInternetStreamp == NULL);
return;
}
if (!mPendingURL.empty())
{
llassert_always(mCurrentInternetStreamp == NULL);
LL_INFOS() << "Starting internet stream: " << mPendingURL << LL_ENDL;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, mStreamGroup, mPendingURL);
mURL = mPendingURL;
mMetadata = LLSD::emptyMap();
mMetadataUpdateSignal(mMetadata);
mPendingURL.clear();
}
// Don't do anything if there are no streams playing
if (!mCurrentInternetStreamp)
......@@ -188,9 +249,22 @@ void LLStreamingAudio_FMODSTUDIO::update()
unsigned int progress;
bool starving;
bool diskbusy;
FMOD_OPENSTATE open_state = mCurrentInternetStreamp->getOpenState(&progress, &starving, &diskbusy);
FMOD_OPENSTATE open_state;
if (open_state == FMOD_OPENSTATE_READY)
if (Check_FMOD_Stream_Error(mCurrentInternetStreamp->getOpenState(open_state, &progress, &starving, &diskbusy), "FMOD::Sound::getOpenState") || open_state == FMOD_OPENSTATE_ERROR)
{
LL_WARNS() << "Internet stream openstate error: open_state = " << open_state << " - progress = " << progress << " - starving = " << starving << " - diskbusy = " << diskbusy << LL_ENDL;
bool was_playing = mWasAlreadyPlaying;
stop();
// Try to restart previously playing stream on socket error
if (open_state == FMOD_OPENSTATE_ERROR && was_playing)
{
LL_WARNS() << "Stream was playing before - trying to restart" << LL_ENDL;
start(mURL);
}
return;
}
else if (open_state == FMOD_OPENSTATE_READY)
{
// Stream is live
......@@ -200,81 +274,147 @@ void LLStreamingAudio_FMODSTUDIO::update()
{
// Reset volume to previously set volume
setGain(getGain());
mFMODInternetStreamChannelp->setPaused(false);
}
mRetryCount = 0;
}
else if (open_state == FMOD_OPENSTATE_ERROR)
{
LL_INFOS("FMOD") << "State: FMOD_OPENSTATE_ERROR"
<< " Progress: " << U32(progress)
<< " Starving: " << S32(starving)
<< " Diskbusy: " << S32(diskbusy) << LL_ENDL;
if (mRetryCount < 2)
{
// Retry
std::string url = mURL;
stop(); // might drop mURL, drops mCurrentInternetStreamp
mRetryCount++;
if (!url.empty())
if (mStreamDSP)
{
LL_INFOS("FMOD") << "Restarting internet stream: " << url << ", attempt " << (mRetryCount + 1) << LL_ENDL;
mCurrentInternetStreamp = new LLAudioStreamManagerFMODSTUDIO(mSystem, url);
mURL = url;
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->addDSP(FMOD_CHANNELCONTROL_DSP_TAIL, mStreamDSP), "FMOD::Channel::addDSP");
}
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused");
mWasAlreadyPlaying = true;
}
else
}
else if (open_state == FMOD_OPENSTATE_PLAYING)
{
if (!mWasAlreadyPlaying)
{
stop();
mWasAlreadyPlaying = true;
}
return;
}
if (mFMODInternetStreamChannelp)
{
FMOD::Sound *sound = NULL;
FMOD::Sound *sound = nullptr;
if (mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound)
if(mFMODInternetStreamChannelp->getCurrentSound(&sound) == FMOD_OK && sound)
{
FMOD_TAG tag;
S32 tagcount, dirtytagcount;
if (sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount)
if(sound->getNumTags(&tagcount, &dirtytagcount) == FMOD_OK && dirtytagcount > 0)
{
for (S32 i = 0; i < tagcount; ++i)
mMetadata = LLSD::emptyMap();
for(S32 i = 0; i < tagcount; ++i)
{
if (sound->getTag(NULL, i, &tag) != FMOD_OK)
if(sound->getTag(nullptr, i, &tag)!=FMOD_OK)
continue;
if (tag.type == FMOD_TAGTYPE_FMOD)
std::string name = tag.name;
switch (tag.type)
{
if (!strcmp(tag.name, "Sample Rate Change"))
case FMOD_TAGTYPE_ID3V2:
{
LL_INFOS("FMOD") << "Stream forced changing sample rate to " << *((float *)tag.data) << LL_ENDL;
mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data));
if (!LLStringUtil::compareInsensitive(name, "TIT2")) name = "TITLE";
else if(!LLStringUtil::compareInsensitive(name, "TPE1")) name = "ARTIST";
break;
}
continue;
case FMOD_TAGTYPE_ASF:
{
if (!LLStringUtil::compareInsensitive(name, "Title")) name = "TITLE";
else if (!LLStringUtil::compareInsensitive(name, "WM/AlbumArtist")) name = "ARTIST";
break;
}
case FMOD_TAGTYPE_VORBISCOMMENT:
{
if (!LLStringUtil::compareInsensitive(name, "title")) name = "TITLE";
else if (!LLStringUtil::compareInsensitive(name, "artist")) name = "ARTIST";
break;
}
case FMOD_TAGTYPE_FMOD:
{
if (!LLStringUtil::compareInsensitive(name, "Sample Rate Change"))
{
LL_INFOS() << "Stream forced changing sample rate to " << *((float *)tag.data) << LL_ENDL;
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setFrequency(*((float *)tag.data)), "FMOD::Channel::setFrequency");
}
continue;
}
default:
if (!LLStringUtil::compareInsensitive(name, "TITLE") ||
!LLStringUtil::compareInsensitive(name, "ARTIST"))
LLStringUtil::toUpper(name);
break;
}
switch (tag.datatype)
{
case(FMOD_TAGDATATYPE_INT):
mMetadata[name]=*(LLSD::Integer*)(tag.data);
LL_INFOS() << tag.name << ": " << *(int*)(tag.data) << LL_ENDL;
break;
case(FMOD_TAGDATATYPE_FLOAT):
mMetadata[name]=*(LLSD::Real*)(tag.data);
LL_INFOS() << tag.name << ": " << *(float*)(tag.data) << LL_ENDL;
break;
case(FMOD_TAGDATATYPE_STRING):
{
std::string out = rawstr_to_utf8(std::string((char*)tag.data,tag.datalen));
if (out.length() && out[out.size() - 1] == 0)
out.erase(out.size() - 1);
mMetadata[name]=out;
LL_INFOS() << tag.name << "(RAW): " << out << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING_UTF8) :
{
U8 offs = 0;
if (tag.datalen > 3 && ((unsigned char*)tag.data)[0] == 0xEF && ((unsigned char*)tag.data)[1] == 0xBB && ((unsigned char*)tag.data)[2] == 0xBF)
offs = 3;
std::string out((char*)tag.data + offs, tag.datalen - offs);
if (out.length() && out[out.size() - 1] == 0)
out.erase(out.size() - 1);
mMetadata[name] = out;
LL_INFOS() << tag.name << "(UTF8): " << out << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING_UTF16):
{
std::string out = utf16input_to_utf8((unsigned char*)tag.data, tag.datalen, UTF16);
if (out.length() && out[out.size() - 1] == 0)
out.erase(out.size() - 1);
mMetadata[name] = out;
LL_INFOS() << tag.name << "(UTF16): " << out << LL_ENDL;
break;
}
case(FMOD_TAGDATATYPE_STRING_UTF16BE):
{
std::string out = utf16input_to_utf8((unsigned char*)tag.data, tag.datalen, UTF16BE);
if (out.length() && out[out.size() - 1] == 0)
out.erase(out.size() - 1);
mMetadata[name] = out;
LL_INFOS() << tag.name << "(UTF16BE): " << out << LL_ENDL;
break;
}
default:
break;
}
}
mMetadataUpdateSignal(mMetadata);
}
if (starving)
{
bool paused = false;
mFMODInternetStreamChannelp->getPaused(&paused);
if (!paused)
if (!Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->getPaused(&paused), "FMOD:Channel::getPaused") && !paused)
{
LL_INFOS("FMOD") << "Stream starvation detected! Pausing stream until buffer nearly full." << LL_ENDL;
LL_INFOS("FMOD") << " (diskbusy=" << diskbusy << ")" << LL_ENDL;
LL_INFOS("FMOD") << " (progress=" << progress << ")" << LL_ENDL;
mFMODInternetStreamChannelp->setPaused(true);
LL_INFOS() << "Stream starvation detected! Pausing stream until buffer nearly full." << LL_ENDL;
LL_INFOS() << " (diskbusy=" << diskbusy << ")" << LL_ENDL;
LL_INFOS() << " (progress=" << progress << ")" << LL_ENDL;
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused");
}
}
else if (progress > 80)
else if(progress > 80)
{
mFMODInternetStreamChannelp->setPaused(false);
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(false), "FMOD::Channel::setPaused");
}
}
}
......@@ -282,11 +422,21 @@ void LLStreamingAudio_FMODSTUDIO::update()
void LLStreamingAudio_FMODSTUDIO::stop()
{
mPendingURL.clear();
mWasAlreadyPlaying = false;
mMetadata = LLSD::emptyMap();
mMetadataUpdateSignal(mMetadata);
if (mFMODInternetStreamChannelp)
{
mFMODInternetStreamChannelp->setPaused(true);
mFMODInternetStreamChannelp->setPriority(0);
mFMODInternetStreamChannelp = NULL;
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPaused(true), "FMOD::Channel::setPaused");
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setPriority(0), "FMOD::Channel::setPriority");
if (mStreamDSP)
{
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->removeDSP(mStreamDSP), "FMOD::Channel::removeDSP");
}
mFMODInternetStreamChannelp = nullptr;
}
if (mCurrentInternetStreamp)
......@@ -301,12 +451,11 @@ void LLStreamingAudio_FMODSTUDIO::stop()
LL_WARNS("FMOD") << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << LL_ENDL;
mDeadStreams.push_back(mCurrentInternetStreamp);
}
mCurrentInternetStreamp = NULL;
//mURL.clear();
mCurrentInternetStreamp = nullptr;
}
}
void LLStreamingAudio_FMODSTUDIO::pause(int pauseopt)
void LLStreamingAudio_FMODSTUDIO::pause(S32 pauseopt)
{
if (pauseopt < 0)
{
......@@ -317,7 +466,6 @@ void LLStreamingAudio_FMODSTUDIO::pause(int pauseopt)
{
if (mCurrentInternetStreamp)
{
LL_INFOS("FMOD") << "Pausing internet stream" << LL_ENDL;
stop();
}
}
......@@ -336,7 +484,7 @@ int LLStreamingAudio_FMODSTUDIO::isPlaying()
{
return 1; // Active and playing
}
else if (!mURL.empty())
else if (!mURL.empty() || !mPendingURL.empty())
{
return 2; // "Paused"
}
......@@ -365,24 +513,53 @@ void LLStreamingAudio_FMODSTUDIO::setGain(F32 vol)
if (mFMODInternetStreamChannelp)
{
vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here?
vol = llclamp(vol * vol, 0.f, 1.f); //should vol be squared here?
mFMODInternetStreamChannelp->setVolume(vol);
Check_FMOD_Stream_Error(mFMODInternetStreamChannelp->setVolume(vol), "FMOD::Channel::setVolume");
}
}
/* virtual */
bool LLStreamingAudio_FMODSTUDIO::getWaveData(float* arr, S32 count, S32 stride/*=1*/)
{
if (count > (WAVE_BUFFER_SIZE / 2))
LL_ERRS("AudioImpl") << "Count=" << count << " exceeds WAVE_BUFFER_SIZE/2=" << WAVE_BUFFER_SIZE << LL_ENDL;
if(!mFMODInternetStreamChannelp || !mCurrentInternetStreamp)
return false;
bool muted = false;
FMOD_RESULT res = mFMODInternetStreamChannelp->getMute(&muted);
if(res != FMOD_OK || muted)
return false;
{
U32 buff_size;
{
LLMutexLock lock(&gWaveDataMutex);
gWaveBufferMinSize = count;
buff_size = gWaveDataBufferSize;
if (!buff_size)
return false;
memcpy(arr, gWaveDataBuffer + WAVE_BUFFER_SIZE - buff_size, llmin(U32(count), buff_size) * sizeof(float));
}
if (buff_size < U32(count))
memset(arr + buff_size, 0, (count - buff_size) * sizeof(float));
}
return true;
}
///////////////////////////////////////////////////////
// manager of possibly-multiple internet audio streams
LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, const std::string& url) :
mSystem(system),
mStreamChannel(NULL),
mInternetStream(NULL),
mReady(false)
LLAudioStreamManagerFMODSTUDIO::LLAudioStreamManagerFMODSTUDIO(FMOD::System *system, FMOD::ChannelGroup *group, const std::string& url) :
mSystem(system),
mChannelGroup(group),
mStreamChannel(nullptr),
mInternetStream(nullptr),
mReady(false),
mInternetStreamURL(url)
{
mInternetStreamURL = url;
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, 0, &mInternetStream);
FMOD_RESULT result = mSystem->createStream(url.c_str(), FMOD_2D | FMOD_NONBLOCKING | FMOD_IGNORETAGS, nullptr, &mInternetStream);
if (result != FMOD_OK)
{
......@@ -399,20 +576,17 @@ mReady(false)
FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream()
{
// We need a live and opened stream before we try and play it.
if (!mInternetStream || getOpenState() != FMOD_OPENSTATE_READY)
FMOD_OPENSTATE open_state;
if (!mInternetStream || Check_FMOD_Stream_Error(getOpenState(open_state), "FMOD::Sound::getOpenState") || open_state != FMOD_OPENSTATE_READY)
{
LL_WARNS("FMOD") << "No internet stream to start playing!" << LL_ENDL;
return NULL;
return nullptr;
}
if (mStreamChannel)
return mStreamChannel; //Already have a channel for this stream.
return mStreamChannel; //Already have a channel for this stream.
FMOD_RESULT result = mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << FMOD_ErrorString(result) << LL_ENDL;
}
Check_FMOD_Stream_Error(mSystem->playSound(mInternetStream, mChannelGroup, true, &mStreamChannel), "FMOD::System::playSound");
return mStreamChannel;
}
......@@ -420,23 +594,24 @@ bool LLAudioStreamManagerFMODSTUDIO::stopStream()
{
if (mInternetStream)
{
bool close = true;
switch (getOpenState())
FMOD_OPENSTATE open_state;
if (getOpenState(open_state) == FMOD_OK)
{
case FMOD_OPENSTATE_CONNECTING:
close = false;
break;
default:
close = true;
switch (open_state)
{
case FMOD_OPENSTATE_CONNECTING:
close = false;
break;
default:
close = true;
}
}
if (close)
if (close && mInternetStream->release() == FMOD_OK)
{
mInternetStream->release();
mStreamChannel = NULL;
mInternetStream = NULL;
mStreamChannel = nullptr;
mInternetStream = nullptr;
return true;
}
else
......@@ -450,32 +625,54 @@ bool LLAudioStreamManagerFMODSTUDIO::stopStream()
}
}
FMOD_OPENSTATE LLAudioStreamManagerFMODSTUDIO::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy)
FMOD_RESULT LLAudioStreamManagerFMODSTUDIO::getOpenState(FMOD_OPENSTATE& state, unsigned int* percentbuffered, bool* starving, bool* diskbusy)
{
FMOD_OPENSTATE state;
if (!mInternetStream)
return FMOD_ERR_INVALID_HANDLE;
FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << FMOD_ErrorString(result) << LL_ENDL;
}
return state;
Check_FMOD_Stream_Error(result, "FMOD::Sound::getOpenState");
return result;
}
void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime)
{
FMOD_RESULT result = mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES);
if (result != FMOD_OK)
{
LL_WARNS("FMOD") << "setStreamBufferSize error: " << FMOD_ErrorString(result) << LL_ENDL;
return;
}
FMOD_ADVANCEDSETTINGS settings;
memset(&settings, 0, sizeof(settings));
Check_FMOD_Stream_Error(mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES), "FMOD::System::setStreamBufferSize");
FMOD_ADVANCEDSETTINGS settings = { };
settings.cbSize = sizeof(settings);
settings.defaultDecodeBufferSize = decodebuffertime;//ms
result = mSystem->setAdvancedSettings(&settings);
if (result != FMOD_OK)
Check_FMOD_Stream_Error(mSystem->setAdvancedSettings(&settings), "FMOD::System::setAdvancedSettings");
}
bool LLStreamingAudio_FMODSTUDIO::releaseDeadStreams()
{
// Kill dead internet streams, if possible
for (auto iter = mDeadStreams.begin(); iter != mDeadStreams.end();)
{
LLAudioStreamManagerFMODSTUDIO *streamp = *iter;
if (streamp->stopStream())
{
LL_INFOS() << "Closed dead stream" << LL_ENDL;
delete streamp;
iter = mDeadStreams.erase(iter);
}
else
{
++iter;
}
}
return mDeadStreams.empty();
}
void LLStreamingAudio_FMODSTUDIO::cleanupWaveData()
{
if (mStreamGroup)
{
LL_WARNS("FMOD") << "setAdvancedSettings error: " << FMOD_ErrorString(result) << LL_ENDL;
Check_FMOD_Stream_Error(mStreamGroup->release(), "FMOD::ChannelGroup::release");
mStreamGroup = nullptr;
}
if(mStreamDSP)
Check_FMOD_Stream_Error(mStreamDSP->release(), "FMOD::DSP::release");
mStreamDSP = nullptr;
}
/**
/**
* @file streamingaudio_fmodstudio.h
* @brief Definition of LLStreamingAudio_FMODSTUDIO implementation
*
* $LicenseInfo:firstyear=2020&license=viewerlgpl$
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2020, Linden Research, Inc.
*
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -31,45 +31,62 @@
#include "llstreamingaudio.h"
#include "lltimer.h"
#include "llsd.h"
#include "boost/signals2.hpp"
//Stubs
class LLAudioStreamManagerFMODSTUDIO;
namespace FMOD
{
class System;
class Channel;
class System;
class Channel;
class ChannelGroup;
class DSP;
}
//Interfaces
class LLStreamingAudio_FMODSTUDIO : public LLStreamingAudioInterface
class LLStreamingAudio_FMODSTUDIO final : public LLStreamingAudioInterface
{
public:
public:
LLStreamingAudio_FMODSTUDIO(FMOD::System *system);
/*virtual*/ ~LLStreamingAudio_FMODSTUDIO();
/*virtual*/ void start(const std::string& url);
/*virtual*/ void stop();
/*virtual*/ void pause(S32 pause);
/*virtual*/ void update();
/*virtual*/ S32 isPlaying();
/*virtual*/ void setGain(F32 vol);
/*virtual*/ F32 getGain();
/*virtual*/ std::string getURL();
/*virtual*/ bool supportsAdjustableBufferSizes(){return true;}
/*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime);
/*virtual*/ void start(const std::string& url) override;
/*virtual*/ void stop() override;
/*virtual*/ void pause(S32 pause) override;
/*virtual*/ void update() override;
/*virtual*/ S32 isPlaying() override;
/*virtual*/ void setGain(F32 vol) override;
/*virtual*/ F32 getGain() override;
/*virtual*/ std::string getURL() override;
/*virtual*/ bool supportsAdjustableBufferSizes() override {return true;}
/*virtual*/ void setBufferSizes(U32 streambuffertime, U32 decodebuffertime) override;
/*virtual*/ bool supportsMetaData() override {return true;}
/*virtual*/ LLSD getMetadata() const override { return mMetadata; } //return NULL if not playing.
/*virtual*/ bool supportsWaveData() override {return true;}
/*virtual*/ bool getWaveData(float* arr, S32 count, S32 stride = 1) override;
private:
void killDeadStreams();
bool releaseDeadStreams();
void cleanupWaveData();
FMOD::System *mSystem;
LLAudioStreamManagerFMODSTUDIO *mCurrentInternetStreamp;
FMOD::DSP* mStreamDSP;
FMOD::ChannelGroup* mStreamGroup;
FMOD::Channel *mFMODInternetStreamChannelp;
std::list<LLAudioStreamManagerFMODSTUDIO *> mDeadStreams;
std::string mURL;
std::string mPendingURL;
F32 mGain;
S32 mRetryCount;
bool mWasAlreadyPlaying;
LLSD mMetadata;
};
......
/**
/**
* @file vorbisencode.cpp
* @brief Vorbis encoding routine routine for Indra.
*
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -39,161 +39,161 @@
#if 0
#include "VorbisFramework.h"
#define vorbis_analysis mac_vorbis_analysis
#define vorbis_analysis_headerout mac_vorbis_analysis_headerout
#define vorbis_analysis_init mac_vorbis_analysis_init
#define vorbis_encode_ctl mac_vorbis_encode_ctl
#define vorbis_encode_setup_init mac_vorbis_encode_setup_init
#define vorbis_encode_setup_managed mac_vorbis_encode_setup_managed
#define vorbis_info_init mac_vorbis_info_init
#define vorbis_info_clear mac_vorbis_info_clear
#define vorbis_comment_init mac_vorbis_comment_init
#define vorbis_comment_clear mac_vorbis_comment_clear
#define vorbis_block_init mac_vorbis_block_init
#define vorbis_block_clear mac_vorbis_block_clear
#define vorbis_dsp_clear mac_vorbis_dsp_clear
#define vorbis_analysis_buffer mac_vorbis_analysis_buffer
#define vorbis_analysis_wrote mac_vorbis_analysis_wrote
#define vorbis_analysis_blockout mac_vorbis_analysis_blockout
#define ogg_stream_packetin mac_ogg_stream_packetin
#define ogg_stream_init mac_ogg_stream_init
#define ogg_stream_flush mac_ogg_stream_flush
#define ogg_stream_pageout mac_ogg_stream_pageout
#define ogg_page_eos mac_ogg_page_eos
#define ogg_stream_clear mac_ogg_stream_clear
#define vorbis_analysis mac_vorbis_analysis
#define vorbis_analysis_headerout mac_vorbis_analysis_headerout
#define vorbis_analysis_init mac_vorbis_analysis_init
#define vorbis_encode_ctl mac_vorbis_encode_ctl
#define vorbis_encode_setup_init mac_vorbis_encode_setup_init
#define vorbis_encode_setup_managed mac_vorbis_encode_setup_managed
#define vorbis_info_init mac_vorbis_info_init
#define vorbis_info_clear mac_vorbis_info_clear
#define vorbis_comment_init mac_vorbis_comment_init
#define vorbis_comment_clear mac_vorbis_comment_clear
#define vorbis_block_init mac_vorbis_block_init
#define vorbis_block_clear mac_vorbis_block_clear
#define vorbis_dsp_clear mac_vorbis_dsp_clear
#define vorbis_analysis_buffer mac_vorbis_analysis_buffer
#define vorbis_analysis_wrote mac_vorbis_analysis_wrote
#define vorbis_analysis_blockout mac_vorbis_analysis_blockout
#define ogg_stream_packetin mac_ogg_stream_packetin
#define ogg_stream_init mac_ogg_stream_init
#define ogg_stream_flush mac_ogg_stream_flush
#define ogg_stream_pageout mac_ogg_stream_pageout
#define ogg_page_eos mac_ogg_page_eos
#define ogg_stream_clear mac_ogg_stream_clear
#endif
S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg)
{
U16 num_channels = 0;
U32 sample_rate = 0;
U32 bits_per_sample = 0;
U32 physical_file_size = 0;
U32 chunk_length = 0;
U32 raw_data_length = 0;
U32 bytes_per_sec = 0;
BOOL uncompressed_pcm = FALSE;
U16 num_channels = 0;
U32 sample_rate = 0;
U32 bits_per_sample = 0;
U32 physical_file_size = 0;
U32 chunk_length = 0;
U32 raw_data_length = 0;
U32 bytes_per_sec = 0;
BOOL uncompressed_pcm = FALSE;
unsigned char wav_header[44]; /*Flawfinder: ignore*/
unsigned char wav_header[44]; /*Flawfinder: ignore*/
error_msg.clear();
error_msg.clear();
//********************************
LLAPRFile infile ;
//********************************
LLAPRFile infile ;
infile.open(in_fname,LL_APR_RB);
//********************************
if (!infile.getFileHandle())
{
error_msg = "CannotUploadSoundFile";
return(LLVORBISENC_SOURCE_OPEN_ERR);
}
infile.read(wav_header, 44);
physical_file_size = infile.seek(APR_END,0);
if (strncmp((char *)&(wav_header[0]),"RIFF",4))
{
error_msg = "SoundFileNotRIFF";
return(LLVORBISENC_WAV_FORMAT_ERR);
}
if (strncmp((char *)&(wav_header[8]),"WAVE",4))
{
error_msg = "SoundFileNotRIFF";
return(LLVORBISENC_WAV_FORMAT_ERR);
}
// parse the chunks
U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
while ((file_pos + 8)< physical_file_size)
{
infile.seek(APR_SET,file_pos);
infile.read(wav_header, 44);
chunk_length = ((U32) wav_header[7] << 24)
+ ((U32) wav_header[6] << 16)
+ ((U32) wav_header[5] << 8)
+ wav_header[4];
if (chunk_length > physical_file_size - file_pos - 4)
{
infile.close();
error_msg = "SoundFileInvalidChunkSize";
return(LLVORBISENC_CHUNK_SIZE_ERR);
}
// LL_INFOS() << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << LL_ENDL;
if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
{
if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
{
uncompressed_pcm = TRUE;
}
num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
sample_rate = ((U32) wav_header[15] << 24)
+ ((U32) wav_header[14] << 16)
+ ((U32) wav_header[13] << 8)
+ wav_header[12];
bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
bytes_per_sec = ((U32) wav_header[19] << 24)
+ ((U32) wav_header[18] << 16)
+ ((U32) wav_header[17] << 8)
+ wav_header[16];
}
else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
{
raw_data_length = chunk_length;
}
file_pos += (chunk_length + 8);
chunk_length = 0;
}
//****************
infile.close();
//****************
if (!uncompressed_pcm)
{
error_msg = "SoundFileNotPCM";
return(LLVORBISENC_PCM_FORMAT_ERR);
}
if ((num_channels < 1) || (num_channels > LLVORBIS_CLIP_MAX_CHANNELS))
{
error_msg = "SoundFileInvalidChannelCount";
return(LLVORBISENC_MULTICHANNEL_ERR);
}
if (sample_rate != LLVORBIS_CLIP_SAMPLE_RATE)
{
error_msg = "SoundFileInvalidSampleRate";
return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
}
if ((bits_per_sample != 16) && (bits_per_sample != 8))
{
error_msg = "SoundFileInvalidWordSize";
return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
}
if (!raw_data_length)
{
error_msg = "SoundFileInvalidHeader";
return(LLVORBISENC_CLIP_TOO_LONG);
}
F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
if (clip_length > LLVORBIS_CLIP_MAX_TIME)
{
error_msg = "SoundFileInvalidTooLong";
return(LLVORBISENC_CLIP_TOO_LONG);
}
//********************************
if (!infile.getFileHandle())
{
error_msg = "CannotUploadSoundFile";
return(LLVORBISENC_SOURCE_OPEN_ERR);
}
infile.read(wav_header, 44);
physical_file_size = infile.seek(APR_END,0);
if (strncmp((char *)&(wav_header[0]),"RIFF",4))
{
error_msg = "SoundFileNotRIFF";
return(LLVORBISENC_WAV_FORMAT_ERR);
}
if (strncmp((char *)&(wav_header[8]),"WAVE",4))
{
error_msg = "SoundFileNotRIFF";
return(LLVORBISENC_WAV_FORMAT_ERR);
}
// parse the chunks
U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
while ((file_pos + 8)< physical_file_size)
{
infile.seek(APR_SET,file_pos);
infile.read(wav_header, 44);
chunk_length = ((U32) wav_header[7] << 24)
+ ((U32) wav_header[6] << 16)
+ ((U32) wav_header[5] << 8)
+ wav_header[4];
if (chunk_length > physical_file_size - file_pos - 4)
{
infile.close();
error_msg = "SoundFileInvalidChunkSize";
return(LLVORBISENC_CHUNK_SIZE_ERR);
}
// LL_INFOS() << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << LL_ENDL;
if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
{
if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00))
{
uncompressed_pcm = TRUE;
}
num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
sample_rate = ((U32) wav_header[15] << 24)
+ ((U32) wav_header[14] << 16)
+ ((U32) wav_header[13] << 8)
+ wav_header[12];
bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
bytes_per_sec = ((U32) wav_header[19] << 24)
+ ((U32) wav_header[18] << 16)
+ ((U32) wav_header[17] << 8)
+ wav_header[16];
}
else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
{
raw_data_length = chunk_length;
}
file_pos += (chunk_length + 8);
chunk_length = 0;
}
//****************
infile.close();
//****************
if (!uncompressed_pcm)
{
error_msg = "SoundFileNotPCM";
return(LLVORBISENC_PCM_FORMAT_ERR);
}
if ((num_channels < 1) || (num_channels > LLVORBIS_CLIP_MAX_CHANNELS))
{
error_msg = "SoundFileInvalidChannelCount";
return(LLVORBISENC_MULTICHANNEL_ERR);
}
if (sample_rate != LLVORBIS_CLIP_SAMPLE_RATE)
{
error_msg = "SoundFileInvalidSampleRate";
return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE);
}
if ((bits_per_sample != 16) && (bits_per_sample != 8))
{
error_msg = "SoundFileInvalidWordSize";
return(LLVORBISENC_UNSUPPORTED_WORD_SIZE);
}
if (!raw_data_length)
{
error_msg = "SoundFileInvalidHeader";
return(LLVORBISENC_CLIP_TOO_LONG);
}
F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec;
if (clip_length > LLVORBIS_CLIP_MAX_TIME)
{
error_msg = "SoundFileInvalidTooLong";
return(LLVORBISENC_CLIP_TOO_LONG);
}
return(LLVORBISENC_NOERR);
}
......@@ -201,306 +201,306 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro
S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname)
{
#define READ_BUFFER 1024
unsigned char readbuffer[READ_BUFFER*4+44]; /* out of the data segment, not the stack */ /*Flawfinder: ignore*/
ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
ogg_packet op; /* one raw packet of data for decode */
vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
vorbis_comment vc; /* struct that stores all the user comments */
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
vorbis_block vb; /* local working space for packet->PCM decode */
int eos=0;
int result;
U16 num_channels = 0;
U32 sample_rate = 0;
U32 bits_per_sample = 0;
S32 format_error = 0;
std::string error_msg;
if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
{
LL_WARNS() << error_msg << ": " << in_fname << LL_ENDL;
return(format_error);
}
unsigned char readbuffer[READ_BUFFER*4+44]; /* out of the data segment, not the stack */ /*Flawfinder: ignore*/
ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */
ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
ogg_packet op; /* one raw packet of data for decode */
vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */
vorbis_comment vc; /* struct that stores all the user comments */
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
vorbis_block vb; /* local working space for packet->PCM decode */
int eos=0;
int result;
U16 num_channels = 0;
U32 sample_rate = 0;
U32 bits_per_sample = 0;
S32 format_error = 0;
std::string error_msg;
if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg)))
{
LL_WARNS() << error_msg << ": " << in_fname << LL_ENDL;
return(format_error);
}
#if 1
unsigned char wav_header[44]; /*Flawfinder: ignore*/
S32 data_left = 0;
LLAPRFile infile ;
infile.open(in_fname,LL_APR_RB);
if (!infile.getFileHandle())
{
LL_WARNS() << "Couldn't open temporary ogg file for writing: " << in_fname
<< LL_ENDL;
return(LLVORBISENC_SOURCE_OPEN_ERR);
}
LLAPRFile outfile ;
outfile.open(out_fname,LL_APR_WPB);
if (!outfile.getFileHandle())
{
LL_WARNS() << "Couldn't open upload sound file for reading: " << in_fname
<< LL_ENDL;
return(LLVORBISENC_DEST_OPEN_ERR);
}
// parse the chunks
U32 chunk_length = 0;
U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
while (infile.eof() != APR_EOF)
{
infile.seek(APR_SET,file_pos);
infile.read(wav_header, 44);
chunk_length = ((U32) wav_header[7] << 24)
+ ((U32) wav_header[6] << 16)
+ ((U32) wav_header[5] << 8)
+ wav_header[4];
// LL_INFOS() << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << LL_ENDL;
if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
{
num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
sample_rate = ((U32) wav_header[15] << 24)
+ ((U32) wav_header[14] << 16)
+ ((U32) wav_header[13] << 8)
+ wav_header[12];
bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
}
else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
{
infile.seek(APR_SET,file_pos+8);
// leave the file pointer at the beginning of the data chunk data
data_left = chunk_length;
break;
}
file_pos += (chunk_length + 8);
chunk_length = 0;
}
/********** Encode setup ************/
/* choose an encoding mode */
/* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
vorbis_info_init(&vi);
// always encode to mono
// SL-52913 & SL-53779 determined this quality level to be our 'good
// enough' general-purpose quality level with a nice low bitrate.
// Equivalent to oggenc -q0.5
F32 quality = 0.05f;
// quality = (bitrate==128000 ? 0.4f : 0.1);
// if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
// if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
// vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
// vorbis_encode_setup_init(&vi))
{
LL_WARNS() << "unable to initialize vorbis codec at quality " << quality << LL_ENDL;
// LL_WARNS() << "unable to initialize vorbis codec at bitrate " << bitrate << LL_ENDL;
return(LLVORBISENC_DEST_OPEN_ERR);
}
/* add a comment */
vorbis_comment_init(&vc);
// vorbis_comment_add(&vc,"Linden");
/* set up the analysis state and auxiliary encoding storage */
vorbis_analysis_init(&vd,&vi);
vorbis_block_init(&vd,&vb);
/* set up our packet->stream encoder */
/* pick a random serial number; that way we can more likely build
chained streams just by concatenation */
ogg_stream_init(&os, ll_rand());
/* Vorbis streams begin with three headers; the initial header (with
most of the codec setup parameters) which is mandated by the Ogg
bitstream spec. The second header holds any comment fields. The
third header holds the bitstream codebook. We merely need to
make the headers, then pass them to libvorbis one at a time;
libvorbis handles the additional Ogg bitstream constraints */
{
ogg_packet header;
ogg_packet header_comm;
ogg_packet header_code;
vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
ogg_stream_packetin(&os,&header); /* automatically placed in its own
page */
ogg_stream_packetin(&os,&header_comm);
ogg_stream_packetin(&os,&header_code);
/* We don't have to write out here, but doing so makes streaming
* much easier, so we do, flushing ALL pages. This ensures the actual
* audio data will start on a new page
*/
while(!eos){
int result=ogg_stream_flush(&os,&og);
if(result==0)break;
outfile.write(og.header, og.header_len);
outfile.write(og.body, og.body_len);
}
}
while(!eos)
{
long bytes_per_sample = bits_per_sample/8;
long bytes=(long)infile.read(readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
if (bytes==0)
{
/* end of file. this can be done implicitly in the mainline,
but it's easier to see here in non-clever fashion.
Tell the library we're at end of stream so that it can handle
the last frame and mark end of stream in the output properly */
vorbis_analysis_wrote(&vd,0);
// eos = 1;
}
else
{
long i;
long samples;
int temp;
data_left -= bytes;
unsigned char wav_header[44]; /*Flawfinder: ignore*/
S32 data_left = 0;
LLAPRFile infile ;
infile.open(in_fname,LL_APR_RB);
if (!infile.getFileHandle())
{
LL_WARNS() << "Couldn't open temporary ogg file for writing: " << in_fname
<< LL_ENDL;
return(LLVORBISENC_SOURCE_OPEN_ERR);
}
LLAPRFile outfile ;
outfile.open(out_fname,LL_APR_WPB);
if (!outfile.getFileHandle())
{
LL_WARNS() << "Couldn't open upload sound file for reading: " << in_fname
<< LL_ENDL;
return(LLVORBISENC_DEST_OPEN_ERR);
}
// parse the chunks
U32 chunk_length = 0;
U32 file_pos = 12; // start at the first chunk (usually fmt but not always)
while (infile.eof() != APR_EOF)
{
infile.seek(APR_SET,file_pos);
infile.read(wav_header, 44);
chunk_length = ((U32) wav_header[7] << 24)
+ ((U32) wav_header[6] << 16)
+ ((U32) wav_header[5] << 8)
+ wav_header[4];
// LL_INFOS() << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << LL_ENDL;
if (!(strncmp((char *)&(wav_header[0]),"fmt ",4)))
{
num_channels = ((U16) wav_header[11] << 8) + wav_header[10];
sample_rate = ((U32) wav_header[15] << 24)
+ ((U32) wav_header[14] << 16)
+ ((U32) wav_header[13] << 8)
+ wav_header[12];
bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22];
}
else if (!(strncmp((char *)&(wav_header[0]),"data",4)))
{
infile.seek(APR_SET,file_pos+8);
// leave the file pointer at the beginning of the data chunk data
data_left = chunk_length;
break;
}
file_pos += (chunk_length + 8);
chunk_length = 0;
}
/********** Encode setup ************/
/* choose an encoding mode */
/* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */
vorbis_info_init(&vi);
// always encode to mono
// SL-52913 & SL-53779 determined this quality level to be our 'good
// enough' general-purpose quality level with a nice low bitrate.
// Equivalent to oggenc -q0.5
F32 quality = 0.05f;
// quality = (bitrate==128000 ? 0.4f : 0.1);
// if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1))
if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality))
// if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) ||
// vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) ||
// vorbis_encode_setup_init(&vi))
{
LL_WARNS() << "unable to initialize vorbis codec at quality " << quality << LL_ENDL;
// LL_WARNS() << "unable to initialize vorbis codec at bitrate " << bitrate << LL_ENDL;
return(LLVORBISENC_DEST_OPEN_ERR);
}
/* add a comment */
vorbis_comment_init(&vc);
// vorbis_comment_add(&vc,"Linden");
/* set up the analysis state and auxiliary encoding storage */
vorbis_analysis_init(&vd,&vi);
vorbis_block_init(&vd,&vb);
/* set up our packet->stream encoder */
/* pick a random serial number; that way we can more likely build
chained streams just by concatenation */
ogg_stream_init(&os, ll_rand());
/* Vorbis streams begin with three headers; the initial header (with
most of the codec setup parameters) which is mandated by the Ogg
bitstream spec. The second header holds any comment fields. The
third header holds the bitstream codebook. We merely need to
make the headers, then pass them to libvorbis one at a time;
libvorbis handles the additional Ogg bitstream constraints */
{
ogg_packet header;
ogg_packet header_comm;
ogg_packet header_code;
vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
ogg_stream_packetin(&os,&header); /* automatically placed in its own
page */
ogg_stream_packetin(&os,&header_comm);
ogg_stream_packetin(&os,&header_code);
/* We don't have to write out here, but doing so makes streaming
* much easier, so we do, flushing ALL pages. This ensures the actual
* audio data will start on a new page
*/
while(!eos){
int result=ogg_stream_flush(&os,&og);
if(result==0)break;
outfile.write(og.header, og.header_len);
outfile.write(og.body, og.body_len);
}
}
while(!eos)
{
long bytes_per_sample = bits_per_sample/8;
long bytes=(long)infile.read(readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */
if (bytes==0)
{
/* end of file. this can be done implicitly in the mainline,
but it's easier to see here in non-clever fashion.
Tell the library we're at end of stream so that it can handle
the last frame and mark end of stream in the output properly */
vorbis_analysis_wrote(&vd,0);
// eos = 1;
}
else
{
long i;
long samples;
int temp;
data_left -= bytes;
/* data to encode */
/* expose the buffer to submit data */
float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
i = 0;
samples = bytes / (num_channels * bytes_per_sample);
if (num_channels == 2)
{
if (bytes_per_sample == 2)
{
/* uninterleave samples */
for(i=0; i<samples ;i++)
{
temp = ((signed char *)readbuffer)[i*4+1]; /*Flawfinder: ignore*/
temp += ((signed char *)readbuffer)[i*4+3]; /*Flawfinder: ignore*/
temp <<= 8;
temp += readbuffer[i*4];
temp += readbuffer[i*4+2];
buffer[0][i] = ((float)temp) / 65536.f;
}
}
else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
{
/* uninterleave samples */
for(i=0; i<samples ;i++)
{
temp = readbuffer[i*2+0];
temp += readbuffer[i*2+1];
temp -= 256;
buffer[0][i] = ((float)temp) / 256.f;
}
}
}
else if (num_channels == 1)
{
if (bytes_per_sample == 2)
{
for(i=0; i < samples ;i++)
{
temp = ((signed char*)readbuffer)[i*2+1];
temp <<= 8;
temp += readbuffer[i*2];
buffer[0][i] = ((float)temp) / 32768.f;
}
}
else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
{
for(i=0; i < samples ;i++)
{
temp = readbuffer[i];
temp -= 128;
buffer[0][i] = ((float)temp) / 128.f;
}
}
}
/* tell the library how much we actually submitted */
vorbis_analysis_wrote(&vd,i);
}
/* vorbis does some data preanalysis, then divvies up blocks for
more involved (potentially parallel) processing. Get a single
block for encoding now */
while(vorbis_analysis_blockout(&vd,&vb)==1)
{
/* analysis */
/* Do the main analysis, creating a packet */
vorbis_analysis(&vb, NULL);
vorbis_bitrate_addblock(&vb);
while(vorbis_bitrate_flushpacket(&vd, &op))
{
/* weld the packet into the bitstream */
ogg_stream_packetin(&os,&op);
/* write out pages (if any) */
while(!eos)
{
result = ogg_stream_pageout(&os,&og);
if(result==0)
break;
outfile.write(og.header, og.header_len);
outfile.write(og.body, og.body_len);
/* this could be set above, but for illustrative purposes, I do
it here (to show that vorbis does know where the stream ends) */
if(ogg_page_eos(&og))
eos=1;
}
}
}
}
/* clean up and exit. vorbis_info_clear() must be called last */
ogg_stream_clear(&os);
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi);
/* ogg_page and ogg_packet structs always point to storage in
libvorbis. They're never freed or manipulated directly */
// fprintf(stderr,"Vorbis encoding: Done.\n");
LL_INFOS() << "Vorbis encoding: Done." << LL_ENDL;
/* expose the buffer to submit data */
float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER);
i = 0;
samples = bytes / (num_channels * bytes_per_sample);
if (num_channels == 2)
{
if (bytes_per_sample == 2)
{
/* uninterleave samples */
for(i=0; i<samples ;i++)
{
temp = ((signed char *)readbuffer)[i*4+1]; /*Flawfinder: ignore*/
temp += ((signed char *)readbuffer)[i*4+3]; /*Flawfinder: ignore*/
temp <<= 8;
temp += readbuffer[i*4];
temp += readbuffer[i*4+2];
buffer[0][i] = ((float)temp) / 65536.f;
}
}
else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
{
/* uninterleave samples */
for(i=0; i<samples ;i++)
{
temp = readbuffer[i*2+0];
temp += readbuffer[i*2+1];
temp -= 256;
buffer[0][i] = ((float)temp) / 256.f;
}
}
}
else if (num_channels == 1)
{
if (bytes_per_sample == 2)
{
for(i=0; i < samples ;i++)
{
temp = ((signed char*)readbuffer)[i*2+1];
temp <<= 8;
temp += readbuffer[i*2];
buffer[0][i] = ((float)temp) / 32768.f;
}
}
else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard")
{
for(i=0; i < samples ;i++)
{
temp = readbuffer[i];
temp -= 128;
buffer[0][i] = ((float)temp) / 128.f;
}
}
}
/* tell the library how much we actually submitted */
vorbis_analysis_wrote(&vd,i);
}
/* vorbis does some data preanalysis, then divvies up blocks for
more involved (potentially parallel) processing. Get a single
block for encoding now */
while(vorbis_analysis_blockout(&vd,&vb)==1)
{
/* analysis */
/* Do the main analysis, creating a packet */
vorbis_analysis(&vb, NULL);
vorbis_bitrate_addblock(&vb);
while(vorbis_bitrate_flushpacket(&vd, &op))
{
/* weld the packet into the bitstream */
ogg_stream_packetin(&os,&op);
/* write out pages (if any) */
while(!eos)
{
result = ogg_stream_pageout(&os,&og);
if(result==0)
break;
outfile.write(og.header, og.header_len);
outfile.write(og.body, og.body_len);
/* this could be set above, but for illustrative purposes, I do
it here (to show that vorbis does know where the stream ends) */
if(ogg_page_eos(&og))
eos=1;
}
}
}
}
/* clean up and exit. vorbis_info_clear() must be called last */
ogg_stream_clear(&os);
vorbis_block_clear(&vb);
vorbis_dsp_clear(&vd);
vorbis_comment_clear(&vc);
vorbis_info_clear(&vi);
/* ogg_page and ogg_packet structs always point to storage in
libvorbis. They're never freed or manipulated directly */
// fprintf(stderr,"Vorbis encoding: Done.\n");
LL_INFOS() << "Vorbis encoding: Done." << LL_ENDL;
#endif
return(LLVORBISENC_NOERR);
return(LLVORBISENC_NOERR);
}
/**
/**
* @file vorbisencode.h
* @brief Vorbis encoding routine routine for Indra.
*
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -40,13 +40,13 @@ const S32 LLVORBISENC_UNSUPPORTED_WORD_SIZE = 9; // unsupported word size
const S32 LLVORBISENC_CLIP_TOO_LONG = 10; // source file is too long
const S32 LLVORBISENC_CHUNK_SIZE_ERR = 11; // chunk size is wrong
const F32 LLVORBIS_CLIP_MAX_TIME = 30.0f;
const F32 LLVORBIS_CLIP_MAX_TIME = 60.0f;
const U8 LLVORBIS_CLIP_MAX_CHANNELS = 2;
const U32 LLVORBIS_CLIP_SAMPLE_RATE = 44100;
const U32 LLVORBIS_CLIP_MAX_SAMPLES_PER_CHANNEL = (U32)(LLVORBIS_CLIP_MAX_TIME * LLVORBIS_CLIP_SAMPLE_RATE);
const U32 LLVORBIS_CLIP_MAX_SAMPLES = LLVORBIS_CLIP_MAX_SAMPLES_PER_CHANNEL * LLVORBIS_CLIP_MAX_CHANNELS;
const size_t LLVORBIS_CLIP_MAX_SAMPLE_DATA = LLVORBIS_CLIP_MAX_SAMPLES * 2; // 2 = 16-bit
// Treat anything this long as a bad asset. A little fudge factor at the end:
// Make that a lot of fudge factor. We're allowing 30 sec for now - 3x legal upload
const size_t LLVORBIS_CLIP_REJECT_SAMPLES = LLVORBIS_CLIP_MAX_SAMPLES * 3;
......
/**
/**
* @file windgen.h
* @brief Templated wind noise generation
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -33,137 +33,137 @@ template <class MIXBUFFERFORMAT_T>
class LLWindGen
{
public:
LLWindGen(const U32 sample_rate = 44100) :
mTargetGain(0.f),
mTargetFreq(100.f),
mTargetPanGainR(0.5f),
mInputSamplingRate(sample_rate),
mSubSamples(2),
mFilterBandWidth(50.f),
mBuf0(0.0f),
mBuf1(0.0f),
mBuf2(0.0f),
mY0(0.0f),
mY1(0.0f),
mCurrentGain(0.f),
mCurrentFreq(100.f),
mCurrentPanGainR(0.5f),
mLastSample(0.f)
{
mSamplePeriod = (F32)mSubSamples / (F32)mInputSamplingRate;
mB2 = expf(-F_TWO_PI * mFilterBandWidth * mSamplePeriod);
}
const U32 getInputSamplingRate() { return mInputSamplingRate; }
const F32 getNextSample();
const F32 getClampedSample(bool clamp, F32 sample);
// newbuffer = the buffer passed from the previous DSP unit.
// numsamples = length in samples-per-channel at this mix time.
// NOTE: generates L/R interleaved stereo
MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples)
{
MIXBUFFERFORMAT_T *cursamplep = newbuffer;
// Filter coefficients
F32 a0 = 0.0f, b1 = 0.0f;
// No need to clip at normal volumes
bool clip = mCurrentGain > 2.0f;
bool interp_freq = false;
//if the frequency isn't changing much, we don't need to interpolate in the inner loop
if (llabs(mTargetFreq - mCurrentFreq) < (mCurrentFreq * 0.112))
{
// calculate resonant filter coefficients
mCurrentFreq = mTargetFreq;
b1 = (-4.0f * mB2) / (1.0f + mB2) * cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod));
a0 = (1.0f - mB2) * sqrtf(1.0f - (b1 * b1) / (4.0f * mB2));
}
else
{
interp_freq = true;
}
while (numsamples)
{
F32 next_sample;
// Start with white noise
// This expression is fragile, rearrange it and it will break!
next_sample = getNextSample();
// Apply a pinking filter
// Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/
mBuf0 = mBuf0 * 0.99765f + next_sample * 0.0990460f;
mBuf1 = mBuf1 * 0.96300f + next_sample * 0.2965164f;
mBuf2 = mBuf2 * 0.57000f + next_sample * 1.0526913f;
next_sample = mBuf0 + mBuf1 + mBuf2 + next_sample * 0.1848f;
if (interp_freq)
{
// calculate and interpolate resonant filter coefficients
mCurrentFreq = (0.999f * mCurrentFreq) + (0.001f * mTargetFreq);
b1 = (-4.0f * mB2) / (1.0f + mB2) * cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod));
a0 = (1.0f - mB2) * sqrtf(1.0f - (b1 * b1) / (4.0f * mB2));
}
// Apply a resonant low-pass filter on the pink noise
next_sample = a0 * next_sample - b1 * mY0 - mB2 * mY1;
mY1 = mY0;
mY0 = next_sample;
mCurrentGain = (0.999f * mCurrentGain) + (0.001f * mTargetGain);
mCurrentPanGainR = (0.999f * mCurrentPanGainR) + (0.001f * mTargetPanGainR);
// For a 3dB pan law use:
// next_sample *= mCurrentGain * ((mCurrentPanGainR*(mCurrentPanGainR-1)*1.652+1.413);
next_sample *= mCurrentGain;
// delta is used to interpolate between synthesized samples
F32 delta = (next_sample - mLastSample) / (F32)mSubSamples;
// Fill the audio buffer, clipping if necessary
for (U8 i=mSubSamples; i && numsamples; --i, --numsamples)
{
mLastSample = mLastSample + delta;
MIXBUFFERFORMAT_T sample_right = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample * mCurrentPanGainR);
MIXBUFFERFORMAT_T sample_left = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample - (F32)sample_right);
*cursamplep = sample_left;
++cursamplep;
*cursamplep = sample_right;
++cursamplep;
}
}
return newbuffer;
}
LLWindGen(const U32 sample_rate = 44100) :
mTargetGain(0.f),
mTargetFreq(100.f),
mTargetPanGainR(0.5f),
mInputSamplingRate(sample_rate),
mSubSamples(2),
mFilterBandWidth(50.f),
mBuf0(0.0f),
mBuf1(0.0f),
mBuf2(0.0f),
mY0(0.0f),
mY1(0.0f),
mCurrentGain(0.f),
mCurrentFreq(100.f),
mCurrentPanGainR(0.5f),
mLastSample(0.f)
{
mSamplePeriod = (F32)mSubSamples / (F32)mInputSamplingRate;
mB2 = expf(-F_TWO_PI * mFilterBandWidth * mSamplePeriod);
}
const U32 getInputSamplingRate() { return mInputSamplingRate; }
const F32 getNextSample();
const F32 getClampedSample(bool clamp, F32 sample);
// newbuffer = the buffer passed from the previous DSP unit.
// numsamples = length in samples-per-channel at this mix time.
// NOTE: generates L/R interleaved stereo
MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples)
{
MIXBUFFERFORMAT_T *cursamplep = newbuffer;
// Filter coefficients
F32 a0 = 0.0f, b1 = 0.0f;
// No need to clip at normal volumes
bool clip = mCurrentGain > 2.0f;
bool interp_freq = false;
//if the frequency isn't changing much, we don't need to interpolate in the inner loop
if (llabs(mTargetFreq - mCurrentFreq) < (mCurrentFreq * 0.112f))
{
// calculate resonant filter coefficients
mCurrentFreq = mTargetFreq;
b1 = (-4.0f * mB2) / (1.0f + mB2) * cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod));
a0 = (1.0f - mB2) * sqrtf(1.0f - (b1 * b1) / (4.0f * mB2));
}
else
{
interp_freq = true;
}
while (numsamples)
{
F32 next_sample;
// Start with white noise
// This expression is fragile, rearrange it and it will break!
next_sample = getNextSample();
// Apply a pinking filter
// Magic numbers taken from PKE method at http://www.firstpr.com.au/dsp/pink-noise/
mBuf0 = mBuf0 * 0.99765f + next_sample * 0.0990460f;
mBuf1 = mBuf1 * 0.96300f + next_sample * 0.2965164f;
mBuf2 = mBuf2 * 0.57000f + next_sample * 1.0526913f;
next_sample = mBuf0 + mBuf1 + mBuf2 + next_sample * 0.1848f;
if (interp_freq)
{
// calculate and interpolate resonant filter coefficients
mCurrentFreq = (0.999f * mCurrentFreq) + (0.001f * mTargetFreq);
b1 = (-4.0f * mB2) / (1.0f + mB2) * cosf(F_TWO_PI * (mCurrentFreq * mSamplePeriod));
a0 = (1.0f - mB2) * sqrtf(1.0f - (b1 * b1) / (4.0f * mB2));
}
// Apply a resonant low-pass filter on the pink noise
next_sample = a0 * next_sample - b1 * mY0 - mB2 * mY1;
mY1 = mY0;
mY0 = next_sample;
mCurrentGain = (0.999f * mCurrentGain) + (0.001f * mTargetGain);
mCurrentPanGainR = (0.999f * mCurrentPanGainR) + (0.001f * mTargetPanGainR);
// For a 3dB pan law use:
// next_sample *= mCurrentGain * ((mCurrentPanGainR*(mCurrentPanGainR-1)*1.652+1.413);
next_sample *= mCurrentGain;
// delta is used to interpolate between synthesized samples
F32 delta = (next_sample - mLastSample) / (F32)mSubSamples;
// Fill the audio buffer, clipping if necessary
for (U8 i=mSubSamples; i && numsamples; --i, --numsamples)
{
mLastSample = mLastSample + delta;
MIXBUFFERFORMAT_T sample_right = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample * mCurrentPanGainR);
MIXBUFFERFORMAT_T sample_left = (MIXBUFFERFORMAT_T)getClampedSample(clip, mLastSample - (F32)sample_right);
*cursamplep = sample_left;
++cursamplep;
*cursamplep = sample_right;
++cursamplep;
}
}
return newbuffer;
}
public:
F32 mTargetGain;
F32 mTargetFreq;
F32 mTargetPanGainR;
F32 mTargetGain;
F32 mTargetFreq;
F32 mTargetPanGainR;
private:
U32 mInputSamplingRate;
U8 mSubSamples;
F32 mSamplePeriod;
F32 mFilterBandWidth;
F32 mB2;
F32 mBuf0;
F32 mBuf1;
F32 mBuf2;
F32 mY0;
F32 mY1;
F32 mCurrentGain;
F32 mCurrentFreq;
F32 mCurrentPanGainR;
F32 mLastSample;
U32 mInputSamplingRate;
U8 mSubSamples;
F32 mSamplePeriod;
F32 mFilterBandWidth;
F32 mB2;
F32 mBuf0;
F32 mBuf1;
F32 mBuf2;
F32 mY0;
F32 mY1;
F32 mCurrentGain;
F32 mCurrentFreq;
F32 mCurrentPanGainR;
F32 mLastSample;
};
template<class T> inline const F32 LLWindGen<T>::getNextSample() { return (F32)rand() * (1.0f / (F32)(RAND_MAX / (U16_MAX / 8))) + (F32)(S16_MIN / 8); }
......
......@@ -4,22 +4,6 @@ project(llcharacter)
include(00-Common)
include(LLCommon)
include(LLMath)
include(LLMessage)
include(LLFileSystem)
include(LLXML)
include_directories(
${LLCOMMON_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
${LLMESSAGE_INCLUDE_DIRS}
${LLFILESYSTEM_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
)
include_directories(SYSTEM
${LLCOMMON_SYSTEM_INCLUDE_DIRS}
${LLXML_SYSTEM_INCLUDE_DIRS}
)
set(llcharacter_SOURCE_FILES
llanimationstates.cpp
......@@ -33,14 +17,12 @@ set(llcharacter_SOURCE_FILES
lljointsolverrp3.cpp
llkeyframefallmotion.cpp
llkeyframemotion.cpp
llkeyframemotionparam.cpp
llkeyframestandmotion.cpp
llkeyframewalkmotion.cpp
llmotioncontroller.cpp
llmotion.cpp
llmultigesture.cpp
llpose.cpp
llstatemachine.cpp
lltargetingmotion.cpp
llvisualparam.cpp
)
......@@ -61,30 +43,33 @@ set(llcharacter_HEADER_FILES
lljointstate.h
llkeyframefallmotion.h
llkeyframemotion.h
llkeyframemotionparam.h
llkeyframestandmotion.h
llkeyframewalkmotion.h
llmotion.h
llmotioncontroller.h
llmultigesture.h
llpose.h
llstatemachine.h
lltargetingmotion.h
llvisualparam.h
)
set_source_files_properties(${llcharacter_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
list(APPEND llcharacter_SOURCE_FILES ${llcharacter_HEADER_FILES})
add_library (llcharacter ${llcharacter_SOURCE_FILES})
target_include_directories( llcharacter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(
llcharacter
${LLCOMMON_LIBRARIES}
${LLMATH_LIBRARIES}
${LLMESSAGE_LIBRARIES}
${LLFILESYSTEM_LIBRARIES}
${LLXML_LIBRARIES}
llcharacter
llcommon
llmath
llmessage
llfilesystem
llxml
)
if(USE_PRECOMPILED_HEADERS AND ${CMAKE_VERSION} VERSION_GREATER "3.15.0")
target_precompile_headers(llcharacter
PRIVATE
[["linden_common.h"]]
)
endif()
/**
/**
* @file llanimationstates.cpp
* @brief Implementation of animation state related functions.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -195,155 +195,148 @@ LLAnimationLibrary gAnimLibrary;
// LLAnimationLibrary()
//-----------------------------------------------------------------------------
LLAnimationLibrary::LLAnimationLibrary() :
mAnimStringTable(16384)
{
//add animation names to animmap
mAnimMap[ANIM_AGENT_AFRAID]= mAnimStringTable.addString("express_afraid");
mAnimMap[ANIM_AGENT_AIM_BAZOOKA_R]= mAnimStringTable.addString("aim_r_bazooka");
mAnimMap[ANIM_AGENT_AIM_BOW_L]= mAnimStringTable.addString("aim_l_bow");
mAnimMap[ANIM_AGENT_AIM_HANDGUN_R]= mAnimStringTable.addString("aim_r_handgun");
mAnimMap[ANIM_AGENT_AIM_RIFLE_R]= mAnimStringTable.addString("aim_r_rifle");
mAnimMap[ANIM_AGENT_ANGRY]= mAnimStringTable.addString("express_anger");
mAnimMap[ANIM_AGENT_AWAY]= mAnimStringTable.addString("away");
mAnimMap[ANIM_AGENT_BACKFLIP]= mAnimStringTable.addString("backflip");
mAnimMap[ANIM_AGENT_BELLY_LAUGH]= mAnimStringTable.addString("express_laugh");
mAnimMap[ANIM_AGENT_BLOW_KISS]= mAnimStringTable.addString("blowkiss");
mAnimMap[ANIM_AGENT_BORED]= mAnimStringTable.addString("express_bored");
mAnimMap[ANIM_AGENT_BOW]= mAnimStringTable.addString("bow");
mAnimMap[ANIM_AGENT_BRUSH]= mAnimStringTable.addString("brush");
mAnimMap[ANIM_AGENT_DO_NOT_DISTURB]= mAnimStringTable.addString("busy");
mAnimMap[ANIM_AGENT_CLAP]= mAnimStringTable.addString("clap");
mAnimMap[ANIM_AGENT_COURTBOW]= mAnimStringTable.addString("courtbow");
mAnimMap[ANIM_AGENT_CROUCH]= mAnimStringTable.addString("crouch");
mAnimMap[ANIM_AGENT_CROUCHWALK]= mAnimStringTable.addString("crouchwalk");
mAnimMap[ANIM_AGENT_CRY]= mAnimStringTable.addString("express_cry");
mAnimMap[ANIM_AGENT_CUSTOMIZE]= mAnimStringTable.addString("turn_180");
mAnimMap[ANIM_AGENT_CUSTOMIZE_DONE]= mAnimStringTable.addString("turnback_180");
mAnimMap[ANIM_AGENT_DANCE1]= mAnimStringTable.addString("dance1");
mAnimMap[ANIM_AGENT_DANCE2]= mAnimStringTable.addString("dance2");
mAnimMap[ANIM_AGENT_DANCE3]= mAnimStringTable.addString("dance3");
mAnimMap[ANIM_AGENT_DANCE4]= mAnimStringTable.addString("dance4");
mAnimMap[ANIM_AGENT_DANCE5]= mAnimStringTable.addString("dance5");
mAnimMap[ANIM_AGENT_DANCE6]= mAnimStringTable.addString("dance6");
mAnimMap[ANIM_AGENT_DANCE7]= mAnimStringTable.addString("dance7");
mAnimMap[ANIM_AGENT_DANCE8]= mAnimStringTable.addString("dance8");
mAnimMap[ANIM_AGENT_DEAD]= mAnimStringTable.addString("dead");
mAnimMap[ANIM_AGENT_DRINK]= mAnimStringTable.addString("drink");
mAnimMap[ANIM_AGENT_EMBARRASSED]= mAnimStringTable.addString("express_embarrased");
mAnimMap[ANIM_AGENT_EXPRESS_AFRAID]= mAnimStringTable.addString("express_afraid_emote");
mAnimMap[ANIM_AGENT_EXPRESS_ANGER]= mAnimStringTable.addString("express_anger_emote");
mAnimMap[ANIM_AGENT_EXPRESS_BORED]= mAnimStringTable.addString("express_bored_emote");
mAnimMap[ANIM_AGENT_EXPRESS_CRY]= mAnimStringTable.addString("express_cry_emote");
mAnimMap[ANIM_AGENT_EXPRESS_DISDAIN]= mAnimStringTable.addString("express_disdain");
mAnimMap[ANIM_AGENT_EXPRESS_EMBARRASSED]= mAnimStringTable.addString("express_embarrassed_emote");
mAnimMap[ANIM_AGENT_EXPRESS_FROWN]= mAnimStringTable.addString("express_frown");
mAnimMap[ANIM_AGENT_EXPRESS_KISS]= mAnimStringTable.addString("express_kiss");
mAnimMap[ANIM_AGENT_EXPRESS_LAUGH]= mAnimStringTable.addString("express_laugh_emote");
mAnimMap[ANIM_AGENT_EXPRESS_OPEN_MOUTH]= mAnimStringTable.addString("express_open_mouth");
mAnimMap[ANIM_AGENT_EXPRESS_REPULSED]= mAnimStringTable.addString("express_repulsed_emote");
mAnimMap[ANIM_AGENT_EXPRESS_SAD]= mAnimStringTable.addString("express_sad_emote");
mAnimMap[ANIM_AGENT_EXPRESS_SHRUG]= mAnimStringTable.addString("express_shrug_emote");
mAnimMap[ANIM_AGENT_EXPRESS_SMILE]= mAnimStringTable.addString("express_smile");
mAnimMap[ANIM_AGENT_EXPRESS_SURPRISE]= mAnimStringTable.addString("express_surprise_emote");
mAnimMap[ANIM_AGENT_EXPRESS_TONGUE_OUT]= mAnimStringTable.addString("express_tongue_out");
mAnimMap[ANIM_AGENT_EXPRESS_TOOTHSMILE]= mAnimStringTable.addString("express_toothsmile");
mAnimMap[ANIM_AGENT_EXPRESS_WINK]= mAnimStringTable.addString("express_wink_emote");
mAnimMap[ANIM_AGENT_EXPRESS_WORRY]= mAnimStringTable.addString("express_worry_emote");
mAnimMap[ANIM_AGENT_FALLDOWN]= mAnimStringTable.addString("falldown");
mAnimMap[ANIM_AGENT_FEMALE_RUN_NEW]= mAnimStringTable.addString("female_run_new");
mAnimMap[ANIM_AGENT_FEMALE_WALK]= mAnimStringTable.addString("female_walk");
mAnimMap[ANIM_AGENT_FEMALE_WALK_NEW]= mAnimStringTable.addString("female_walk_new");
mAnimMap[ANIM_AGENT_FINGER_WAG]= mAnimStringTable.addString("angry_fingerwag");
mAnimMap[ANIM_AGENT_FIST_PUMP]= mAnimStringTable.addString("fist_pump");
mAnimMap[ANIM_AGENT_FLY]= mAnimStringTable.addString("fly");
mAnimMap[ANIM_AGENT_FLYSLOW]= mAnimStringTable.addString("flyslow");
mAnimMap[ANIM_AGENT_HELLO]= mAnimStringTable.addString("hello");
mAnimMap[ANIM_AGENT_HOLD_BAZOOKA_R]= mAnimStringTable.addString("hold_r_bazooka");
mAnimMap[ANIM_AGENT_HOLD_BOW_L]= mAnimStringTable.addString("hold_l_bow");
mAnimMap[ANIM_AGENT_HOLD_HANDGUN_R]= mAnimStringTable.addString("hold_r_handgun");
mAnimMap[ANIM_AGENT_HOLD_RIFLE_R]= mAnimStringTable.addString("hold_r_rifle");
mAnimMap[ANIM_AGENT_HOLD_THROW_R]= mAnimStringTable.addString("hold_throw_r");
mAnimMap[ANIM_AGENT_HOVER]= mAnimStringTable.addString("hover");
mAnimMap[ANIM_AGENT_HOVER_DOWN]= mAnimStringTable.addString("hover_down");
mAnimMap[ANIM_AGENT_HOVER_UP]= mAnimStringTable.addString("hover_up");
mAnimMap[ANIM_AGENT_IMPATIENT]= mAnimStringTable.addString("impatient");
mAnimMap[ANIM_AGENT_JUMP]= mAnimStringTable.addString("jump");
mAnimMap[ANIM_AGENT_JUMP_FOR_JOY]= mAnimStringTable.addString("jumpforjoy");
mAnimMap[ANIM_AGENT_KISS_MY_BUTT]= mAnimStringTable.addString("kissmybutt");
mAnimMap[ANIM_AGENT_LAND]= mAnimStringTable.addString("land");
mAnimMap[ANIM_AGENT_LAUGH_SHORT]= mAnimStringTable.addString("laugh_short");
mAnimMap[ANIM_AGENT_MEDIUM_LAND]= mAnimStringTable.addString("soft_land");
mAnimMap[ANIM_AGENT_MOTORCYCLE_SIT]= mAnimStringTable.addString("motorcycle_sit");
mAnimMap[ANIM_AGENT_MUSCLE_BEACH]= mAnimStringTable.addString("musclebeach");
mAnimMap[ANIM_AGENT_NO]= mAnimStringTable.addString("no_head");
mAnimMap[ANIM_AGENT_NO_UNHAPPY]= mAnimStringTable.addString("no_unhappy");
mAnimMap[ANIM_AGENT_NYAH_NYAH]= mAnimStringTable.addString("nyanya");
mAnimMap[ANIM_AGENT_ONETWO_PUNCH]= mAnimStringTable.addString("punch_onetwo");
mAnimMap[ANIM_AGENT_PEACE]= mAnimStringTable.addString("peace");
mAnimMap[ANIM_AGENT_POINT_ME]= mAnimStringTable.addString("point_me");
mAnimMap[ANIM_AGENT_POINT_YOU]= mAnimStringTable.addString("point_you");
mAnimMap[ANIM_AGENT_PRE_JUMP]= mAnimStringTable.addString("prejump");
mAnimMap[ANIM_AGENT_PUNCH_LEFT]= mAnimStringTable.addString("punch_l");
mAnimMap[ANIM_AGENT_PUNCH_RIGHT]= mAnimStringTable.addString("punch_r");
mAnimMap[ANIM_AGENT_REPULSED]= mAnimStringTable.addString("express_repulsed");
mAnimMap[ANIM_AGENT_ROUNDHOUSE_KICK]= mAnimStringTable.addString("kick_roundhouse_r");
mAnimMap[ANIM_AGENT_RPS_COUNTDOWN]= mAnimStringTable.addString("rps_countdown");
mAnimMap[ANIM_AGENT_RPS_PAPER]= mAnimStringTable.addString("rps_paper");
mAnimMap[ANIM_AGENT_RPS_ROCK]= mAnimStringTable.addString("rps_rock");
mAnimMap[ANIM_AGENT_RPS_SCISSORS]= mAnimStringTable.addString("rps_scissors");
mAnimMap[ANIM_AGENT_RUN]= mAnimStringTable.addString("run");
mAnimMap[ANIM_AGENT_RUN_NEW]= mAnimStringTable.addString("run_new");
mAnimMap[ANIM_AGENT_SAD]= mAnimStringTable.addString("express_sad");
mAnimMap[ANIM_AGENT_SALUTE]= mAnimStringTable.addString("salute");
mAnimMap[ANIM_AGENT_SHOOT_BOW_L]= mAnimStringTable.addString("shoot_l_bow");
mAnimMap[ANIM_AGENT_SHOUT]= mAnimStringTable.addString("shout");
mAnimMap[ANIM_AGENT_SHRUG]= mAnimStringTable.addString("express_shrug");
mAnimMap[ANIM_AGENT_SIT]= mAnimStringTable.addString("sit");
mAnimMap[ANIM_AGENT_SIT_FEMALE]= mAnimStringTable.addString("sit_female");
mAnimMap[ANIM_AGENT_SIT_GROUND]= mAnimStringTable.addString("sit_ground");
mAnimMap[ANIM_AGENT_SIT_GROUND_CONSTRAINED]= mAnimStringTable.addString("sit_ground_constrained");
mAnimMap[ANIM_AGENT_SIT_GENERIC]= mAnimStringTable.addString("sit_generic");
mAnimMap[ANIM_AGENT_SIT_TO_STAND]= mAnimStringTable.addString("sit_to_stand");
mAnimMap[ANIM_AGENT_SLEEP]= mAnimStringTable.addString("sleep");
mAnimMap[ANIM_AGENT_SMOKE_IDLE]= mAnimStringTable.addString("smoke_idle");
mAnimMap[ANIM_AGENT_SMOKE_INHALE]= mAnimStringTable.addString("smoke_inhale");
mAnimMap[ANIM_AGENT_SMOKE_THROW_DOWN]= mAnimStringTable.addString("smoke_throw_down");
mAnimMap[ANIM_AGENT_SNAPSHOT]= mAnimStringTable.addString("snapshot");
mAnimMap[ANIM_AGENT_STAND]= mAnimStringTable.addString("stand");
mAnimMap[ANIM_AGENT_STANDUP]= mAnimStringTable.addString("standup");
mAnimMap[ANIM_AGENT_STAND_1]= mAnimStringTable.addString("stand_1");
mAnimMap[ANIM_AGENT_STAND_2]= mAnimStringTable.addString("stand_2");
mAnimMap[ANIM_AGENT_STAND_3]= mAnimStringTable.addString("stand_3");
mAnimMap[ANIM_AGENT_STAND_4]= mAnimStringTable.addString("stand_4");
mAnimMap[ANIM_AGENT_STRETCH]= mAnimStringTable.addString("stretch");
mAnimMap[ANIM_AGENT_STRIDE]= mAnimStringTable.addString("stride");
mAnimMap[ANIM_AGENT_SURF]= mAnimStringTable.addString("surf");
mAnimMap[ANIM_AGENT_SURPRISE]= mAnimStringTable.addString("express_surprise");
mAnimMap[ANIM_AGENT_SWORD_STRIKE]= mAnimStringTable.addString("sword_strike_r");
mAnimMap[ANIM_AGENT_TALK]= mAnimStringTable.addString("talk");
mAnimMap[ANIM_AGENT_TANTRUM]= mAnimStringTable.addString("angry_tantrum");
mAnimMap[ANIM_AGENT_THROW_R]= mAnimStringTable.addString("throw_r");
mAnimMap[ANIM_AGENT_TRYON_SHIRT]= mAnimStringTable.addString("tryon_shirt");
mAnimMap[ANIM_AGENT_TURNLEFT]= mAnimStringTable.addString("turnleft");
mAnimMap[ANIM_AGENT_TURNRIGHT]= mAnimStringTable.addString("turnright");
mAnimMap[ANIM_AGENT_TYPE]= mAnimStringTable.addString("type");
mAnimMap[ANIM_AGENT_WALK]= mAnimStringTable.addString("walk");
mAnimMap[ANIM_AGENT_WALK_NEW]= mAnimStringTable.addString("walk_new");
mAnimMap[ANIM_AGENT_WHISPER]= mAnimStringTable.addString("whisper");
mAnimMap[ANIM_AGENT_WHISTLE]= mAnimStringTable.addString("whistle");
mAnimMap[ANIM_AGENT_WINK]= mAnimStringTable.addString("express_wink");
mAnimMap[ANIM_AGENT_WINK_HOLLYWOOD]= mAnimStringTable.addString("wink_hollywood");
mAnimMap[ANIM_AGENT_WORRY]= mAnimStringTable.addString("express_worry");
mAnimMap[ANIM_AGENT_YES]= mAnimStringTable.addString("yes_head");
mAnimMap[ANIM_AGENT_YES_HAPPY]= mAnimStringTable.addString("yes_happy");
mAnimMap[ANIM_AGENT_YOGA_FLOAT]= mAnimStringTable.addString("yoga_float");
}
//-----------------------------------------------------------------------------
// ~LLAnimationLibrary()
//-----------------------------------------------------------------------------
LLAnimationLibrary::~LLAnimationLibrary()
mAnimStringTable(16384)
{
//add animation names to animmap
mAnimMap[ANIM_AGENT_AFRAID]= mAnimStringTable.addString("express_afraid");
mAnimMap[ANIM_AGENT_AIM_BAZOOKA_R]= mAnimStringTable.addString("aim_r_bazooka");
mAnimMap[ANIM_AGENT_AIM_BOW_L]= mAnimStringTable.addString("aim_l_bow");
mAnimMap[ANIM_AGENT_AIM_HANDGUN_R]= mAnimStringTable.addString("aim_r_handgun");
mAnimMap[ANIM_AGENT_AIM_RIFLE_R]= mAnimStringTable.addString("aim_r_rifle");
mAnimMap[ANIM_AGENT_ANGRY]= mAnimStringTable.addString("express_anger");
mAnimMap[ANIM_AGENT_AWAY]= mAnimStringTable.addString("away");
mAnimMap[ANIM_AGENT_BACKFLIP]= mAnimStringTable.addString("backflip");
mAnimMap[ANIM_AGENT_BELLY_LAUGH]= mAnimStringTable.addString("express_laugh");
mAnimMap[ANIM_AGENT_BLOW_KISS]= mAnimStringTable.addString("blowkiss");
mAnimMap[ANIM_AGENT_BORED]= mAnimStringTable.addString("express_bored");
mAnimMap[ANIM_AGENT_BOW]= mAnimStringTable.addString("bow");
mAnimMap[ANIM_AGENT_BRUSH]= mAnimStringTable.addString("brush");
mAnimMap[ANIM_AGENT_DO_NOT_DISTURB]= mAnimStringTable.addString("busy");
mAnimMap[ANIM_AGENT_CLAP]= mAnimStringTable.addString("clap");
mAnimMap[ANIM_AGENT_COURTBOW]= mAnimStringTable.addString("courtbow");
mAnimMap[ANIM_AGENT_CROUCH]= mAnimStringTable.addString("crouch");
mAnimMap[ANIM_AGENT_CROUCHWALK]= mAnimStringTable.addString("crouchwalk");
mAnimMap[ANIM_AGENT_CRY]= mAnimStringTable.addString("express_cry");
mAnimMap[ANIM_AGENT_CUSTOMIZE]= mAnimStringTable.addString("turn_180");
mAnimMap[ANIM_AGENT_CUSTOMIZE_DONE]= mAnimStringTable.addString("turnback_180");
mAnimMap[ANIM_AGENT_DANCE1]= mAnimStringTable.addString("dance1");
mAnimMap[ANIM_AGENT_DANCE2]= mAnimStringTable.addString("dance2");
mAnimMap[ANIM_AGENT_DANCE3]= mAnimStringTable.addString("dance3");
mAnimMap[ANIM_AGENT_DANCE4]= mAnimStringTable.addString("dance4");
mAnimMap[ANIM_AGENT_DANCE5]= mAnimStringTable.addString("dance5");
mAnimMap[ANIM_AGENT_DANCE6]= mAnimStringTable.addString("dance6");
mAnimMap[ANIM_AGENT_DANCE7]= mAnimStringTable.addString("dance7");
mAnimMap[ANIM_AGENT_DANCE8]= mAnimStringTable.addString("dance8");
mAnimMap[ANIM_AGENT_DEAD]= mAnimStringTable.addString("dead");
mAnimMap[ANIM_AGENT_DRINK]= mAnimStringTable.addString("drink");
mAnimMap[ANIM_AGENT_EMBARRASSED]= mAnimStringTable.addString("express_embarrased");
mAnimMap[ANIM_AGENT_EXPRESS_AFRAID]= mAnimStringTable.addString("express_afraid_emote");
mAnimMap[ANIM_AGENT_EXPRESS_ANGER]= mAnimStringTable.addString("express_anger_emote");
mAnimMap[ANIM_AGENT_EXPRESS_BORED]= mAnimStringTable.addString("express_bored_emote");
mAnimMap[ANIM_AGENT_EXPRESS_CRY]= mAnimStringTable.addString("express_cry_emote");
mAnimMap[ANIM_AGENT_EXPRESS_DISDAIN]= mAnimStringTable.addString("express_disdain");
mAnimMap[ANIM_AGENT_EXPRESS_EMBARRASSED]= mAnimStringTable.addString("express_embarrassed_emote");
mAnimMap[ANIM_AGENT_EXPRESS_FROWN]= mAnimStringTable.addString("express_frown");
mAnimMap[ANIM_AGENT_EXPRESS_KISS]= mAnimStringTable.addString("express_kiss");
mAnimMap[ANIM_AGENT_EXPRESS_LAUGH]= mAnimStringTable.addString("express_laugh_emote");
mAnimMap[ANIM_AGENT_EXPRESS_OPEN_MOUTH]= mAnimStringTable.addString("express_open_mouth");
mAnimMap[ANIM_AGENT_EXPRESS_REPULSED]= mAnimStringTable.addString("express_repulsed_emote");
mAnimMap[ANIM_AGENT_EXPRESS_SAD]= mAnimStringTable.addString("express_sad_emote");
mAnimMap[ANIM_AGENT_EXPRESS_SHRUG]= mAnimStringTable.addString("express_shrug_emote");
mAnimMap[ANIM_AGENT_EXPRESS_SMILE]= mAnimStringTable.addString("express_smile");
mAnimMap[ANIM_AGENT_EXPRESS_SURPRISE]= mAnimStringTable.addString("express_surprise_emote");
mAnimMap[ANIM_AGENT_EXPRESS_TONGUE_OUT]= mAnimStringTable.addString("express_tongue_out");
mAnimMap[ANIM_AGENT_EXPRESS_TOOTHSMILE]= mAnimStringTable.addString("express_toothsmile");
mAnimMap[ANIM_AGENT_EXPRESS_WINK]= mAnimStringTable.addString("express_wink_emote");
mAnimMap[ANIM_AGENT_EXPRESS_WORRY]= mAnimStringTable.addString("express_worry_emote");
mAnimMap[ANIM_AGENT_FALLDOWN]= mAnimStringTable.addString("falldown");
mAnimMap[ANIM_AGENT_FEMALE_RUN_NEW]= mAnimStringTable.addString("female_run_new");
mAnimMap[ANIM_AGENT_FEMALE_WALK]= mAnimStringTable.addString("female_walk");
mAnimMap[ANIM_AGENT_FEMALE_WALK_NEW]= mAnimStringTable.addString("female_walk_new");
mAnimMap[ANIM_AGENT_FINGER_WAG]= mAnimStringTable.addString("angry_fingerwag");
mAnimMap[ANIM_AGENT_FIST_PUMP]= mAnimStringTable.addString("fist_pump");
mAnimMap[ANIM_AGENT_FLY]= mAnimStringTable.addString("fly");
mAnimMap[ANIM_AGENT_FLYSLOW]= mAnimStringTable.addString("flyslow");
mAnimMap[ANIM_AGENT_HELLO]= mAnimStringTable.addString("hello");
mAnimMap[ANIM_AGENT_HOLD_BAZOOKA_R]= mAnimStringTable.addString("hold_r_bazooka");
mAnimMap[ANIM_AGENT_HOLD_BOW_L]= mAnimStringTable.addString("hold_l_bow");
mAnimMap[ANIM_AGENT_HOLD_HANDGUN_R]= mAnimStringTable.addString("hold_r_handgun");
mAnimMap[ANIM_AGENT_HOLD_RIFLE_R]= mAnimStringTable.addString("hold_r_rifle");
mAnimMap[ANIM_AGENT_HOLD_THROW_R]= mAnimStringTable.addString("hold_throw_r");
mAnimMap[ANIM_AGENT_HOVER]= mAnimStringTable.addString("hover");
mAnimMap[ANIM_AGENT_HOVER_DOWN]= mAnimStringTable.addString("hover_down");
mAnimMap[ANIM_AGENT_HOVER_UP]= mAnimStringTable.addString("hover_up");
mAnimMap[ANIM_AGENT_IMPATIENT]= mAnimStringTable.addString("impatient");
mAnimMap[ANIM_AGENT_JUMP]= mAnimStringTable.addString("jump");
mAnimMap[ANIM_AGENT_JUMP_FOR_JOY]= mAnimStringTable.addString("jumpforjoy");
mAnimMap[ANIM_AGENT_KISS_MY_BUTT]= mAnimStringTable.addString("kissmybutt");
mAnimMap[ANIM_AGENT_LAND]= mAnimStringTable.addString("land");
mAnimMap[ANIM_AGENT_LAUGH_SHORT]= mAnimStringTable.addString("laugh_short");
mAnimMap[ANIM_AGENT_MEDIUM_LAND]= mAnimStringTable.addString("soft_land");
mAnimMap[ANIM_AGENT_MOTORCYCLE_SIT]= mAnimStringTable.addString("motorcycle_sit");
mAnimMap[ANIM_AGENT_MUSCLE_BEACH]= mAnimStringTable.addString("musclebeach");
mAnimMap[ANIM_AGENT_NO]= mAnimStringTable.addString("no_head");
mAnimMap[ANIM_AGENT_NO_UNHAPPY]= mAnimStringTable.addString("no_unhappy");
mAnimMap[ANIM_AGENT_NYAH_NYAH]= mAnimStringTable.addString("nyanya");
mAnimMap[ANIM_AGENT_ONETWO_PUNCH]= mAnimStringTable.addString("punch_onetwo");
mAnimMap[ANIM_AGENT_PEACE]= mAnimStringTable.addString("peace");
mAnimMap[ANIM_AGENT_POINT_ME]= mAnimStringTable.addString("point_me");
mAnimMap[ANIM_AGENT_POINT_YOU]= mAnimStringTable.addString("point_you");
mAnimMap[ANIM_AGENT_PRE_JUMP]= mAnimStringTable.addString("prejump");
mAnimMap[ANIM_AGENT_PUNCH_LEFT]= mAnimStringTable.addString("punch_l");
mAnimMap[ANIM_AGENT_PUNCH_RIGHT]= mAnimStringTable.addString("punch_r");
mAnimMap[ANIM_AGENT_REPULSED]= mAnimStringTable.addString("express_repulsed");
mAnimMap[ANIM_AGENT_ROUNDHOUSE_KICK]= mAnimStringTable.addString("kick_roundhouse_r");
mAnimMap[ANIM_AGENT_RPS_COUNTDOWN]= mAnimStringTable.addString("rps_countdown");
mAnimMap[ANIM_AGENT_RPS_PAPER]= mAnimStringTable.addString("rps_paper");
mAnimMap[ANIM_AGENT_RPS_ROCK]= mAnimStringTable.addString("rps_rock");
mAnimMap[ANIM_AGENT_RPS_SCISSORS]= mAnimStringTable.addString("rps_scissors");
mAnimMap[ANIM_AGENT_RUN]= mAnimStringTable.addString("run");
mAnimMap[ANIM_AGENT_RUN_NEW]= mAnimStringTable.addString("run_new");
mAnimMap[ANIM_AGENT_SAD]= mAnimStringTable.addString("express_sad");
mAnimMap[ANIM_AGENT_SALUTE]= mAnimStringTable.addString("salute");
mAnimMap[ANIM_AGENT_SHOOT_BOW_L]= mAnimStringTable.addString("shoot_l_bow");
mAnimMap[ANIM_AGENT_SHOUT]= mAnimStringTable.addString("shout");
mAnimMap[ANIM_AGENT_SHRUG]= mAnimStringTable.addString("express_shrug");
mAnimMap[ANIM_AGENT_SIT]= mAnimStringTable.addString("sit");
mAnimMap[ANIM_AGENT_SIT_FEMALE]= mAnimStringTable.addString("sit_female");
mAnimMap[ANIM_AGENT_SIT_GROUND]= mAnimStringTable.addString("sit_ground");
mAnimMap[ANIM_AGENT_SIT_GROUND_CONSTRAINED]= mAnimStringTable.addString("sit_ground_constrained");
mAnimMap[ANIM_AGENT_SIT_GENERIC]= mAnimStringTable.addString("sit_generic");
mAnimMap[ANIM_AGENT_SIT_TO_STAND]= mAnimStringTable.addString("sit_to_stand");
mAnimMap[ANIM_AGENT_SLEEP]= mAnimStringTable.addString("sleep");
mAnimMap[ANIM_AGENT_SMOKE_IDLE]= mAnimStringTable.addString("smoke_idle");
mAnimMap[ANIM_AGENT_SMOKE_INHALE]= mAnimStringTable.addString("smoke_inhale");
mAnimMap[ANIM_AGENT_SMOKE_THROW_DOWN]= mAnimStringTable.addString("smoke_throw_down");
mAnimMap[ANIM_AGENT_SNAPSHOT]= mAnimStringTable.addString("snapshot");
mAnimMap[ANIM_AGENT_STAND]= mAnimStringTable.addString("stand");
mAnimMap[ANIM_AGENT_STANDUP]= mAnimStringTable.addString("standup");
mAnimMap[ANIM_AGENT_STAND_1]= mAnimStringTable.addString("stand_1");
mAnimMap[ANIM_AGENT_STAND_2]= mAnimStringTable.addString("stand_2");
mAnimMap[ANIM_AGENT_STAND_3]= mAnimStringTable.addString("stand_3");
mAnimMap[ANIM_AGENT_STAND_4]= mAnimStringTable.addString("stand_4");
mAnimMap[ANIM_AGENT_STRETCH]= mAnimStringTable.addString("stretch");
mAnimMap[ANIM_AGENT_STRIDE]= mAnimStringTable.addString("stride");
mAnimMap[ANIM_AGENT_SURF]= mAnimStringTable.addString("surf");
mAnimMap[ANIM_AGENT_SURPRISE]= mAnimStringTable.addString("express_surprise");
mAnimMap[ANIM_AGENT_SWORD_STRIKE]= mAnimStringTable.addString("sword_strike_r");
mAnimMap[ANIM_AGENT_TALK]= mAnimStringTable.addString("talk");
mAnimMap[ANIM_AGENT_TANTRUM]= mAnimStringTable.addString("angry_tantrum");
mAnimMap[ANIM_AGENT_THROW_R]= mAnimStringTable.addString("throw_r");
mAnimMap[ANIM_AGENT_TRYON_SHIRT]= mAnimStringTable.addString("tryon_shirt");
mAnimMap[ANIM_AGENT_TURNLEFT]= mAnimStringTable.addString("turnleft");
mAnimMap[ANIM_AGENT_TURNRIGHT]= mAnimStringTable.addString("turnright");
mAnimMap[ANIM_AGENT_TYPE]= mAnimStringTable.addString("type");
mAnimMap[ANIM_AGENT_WALK]= mAnimStringTable.addString("walk");
mAnimMap[ANIM_AGENT_WALK_NEW]= mAnimStringTable.addString("walk_new");
mAnimMap[ANIM_AGENT_WHISPER]= mAnimStringTable.addString("whisper");
mAnimMap[ANIM_AGENT_WHISTLE]= mAnimStringTable.addString("whistle");
mAnimMap[ANIM_AGENT_WINK]= mAnimStringTable.addString("express_wink");
mAnimMap[ANIM_AGENT_WINK_HOLLYWOOD]= mAnimStringTable.addString("wink_hollywood");
mAnimMap[ANIM_AGENT_WORRY]= mAnimStringTable.addString("express_worry");
mAnimMap[ANIM_AGENT_YES]= mAnimStringTable.addString("yes_head");
mAnimMap[ANIM_AGENT_YES_HAPPY]= mAnimStringTable.addString("yes_happy");
mAnimMap[ANIM_AGENT_YOGA_FLOAT]= mAnimStringTable.addString("yoga_float");
}
//-----------------------------------------------------------------------------
......@@ -351,16 +344,17 @@ LLAnimationLibrary::~LLAnimationLibrary()
//-----------------------------------------------------------------------------
const char *LLAnimationLibrary::animStateToString( const LLUUID& state )
{
if (state.isNull())
{
return NULL;
}
if (mAnimMap.count(state))
{
return mAnimMap[state];
}
if (state.isNull())
{
return NULL;
}
auto it = mAnimMap.find(state);
if (it != mAnimMap.end())
{
return it->second;
}
return NULL;
return NULL;
}
......@@ -369,33 +363,32 @@ const char *LLAnimationLibrary::animStateToString( const LLUUID& state )
//-----------------------------------------------------------------------------
LLUUID LLAnimationLibrary::stringToAnimState( const std::string& name, BOOL allow_ids )
{
std::string lower_case_name(name);
LLStringUtil::toLower(lower_case_name);
std::string lower_case_name(name);
LLStringUtil::toLower(lower_case_name);
char *true_name = mAnimStringTable.checkString(lower_case_name.c_str());
char *true_name = mAnimStringTable.checkString(lower_case_name.c_str());
LLUUID id;
id.setNull();
LLUUID id;
id.setNull();
if (true_name)
{
for (anim_map_t::iterator iter = mAnimMap.begin();
iter != mAnimMap.end(); iter++)
{
if (iter->second == true_name)
{
id = iter->first;
break;
}
}
}
else if (allow_ids)
{
// try to convert string to LLUUID
id.set(name, FALSE);
}
if (true_name)
{
for (anim_map_t::value_type& anim_pair : mAnimMap)
{
if (anim_pair.second == true_name)
{
id = anim_pair.first;
break;
}
}
}
else if (allow_ids)
{
// try to convert string to LLUUID
id.set(name, FALSE);
}
return id;
return id;
}
//-----------------------------------------------------------------------------
......@@ -403,90 +396,90 @@ LLUUID LLAnimationLibrary::stringToAnimState( const std::string& name, BOOL allo
//-----------------------------------------------------------------------------
void LLAnimationLibrary::animStateSetString( const LLUUID& state, const std::string& name)
{
mAnimMap[state] = mAnimStringTable.addString(name);
mAnimMap[state] = mAnimStringTable.addString(name);
}
std::string LLAnimationLibrary::animationName( const LLUUID& id ) const
{
const char *cptr = gAnimLibrary.animStateToString(id);
if (cptr)
return std::string(cptr);
else
return std::string("[") + id.asString() + std::string("]");
const char *cptr = gAnimLibrary.animStateToString(id);
if (cptr)
return std::string(cptr);
else
return std::string("[") + id.asString() + std::string("]");
}
// Animation states that the user can trigger as part of a gesture
// See struct LLAnimStateEntry in header for label location information
const LLAnimStateEntry gUserAnimStates[] = {
LLAnimStateEntry("express_afraid", ANIM_AGENT_AFRAID),
LLAnimStateEntry("express_anger", ANIM_AGENT_ANGRY),
LLAnimStateEntry("away", ANIM_AGENT_AWAY),
LLAnimStateEntry("backflip", ANIM_AGENT_BACKFLIP),
LLAnimStateEntry("express_laugh", ANIM_AGENT_BELLY_LAUGH),
LLAnimStateEntry("express_toothsmile", ANIM_AGENT_EXPRESS_TOOTHSMILE),
LLAnimStateEntry("blowkiss", ANIM_AGENT_BLOW_KISS),
LLAnimStateEntry("express_bored", ANIM_AGENT_BORED),
LLAnimStateEntry("bow", ANIM_AGENT_BOW),
LLAnimStateEntry("clap", ANIM_AGENT_CLAP),
LLAnimStateEntry("courtbow", ANIM_AGENT_COURTBOW),
LLAnimStateEntry("express_cry", ANIM_AGENT_CRY),
LLAnimStateEntry("dance1", ANIM_AGENT_DANCE1),
LLAnimStateEntry("dance2", ANIM_AGENT_DANCE2),
LLAnimStateEntry("dance3", ANIM_AGENT_DANCE3),
LLAnimStateEntry("dance4", ANIM_AGENT_DANCE4),
LLAnimStateEntry("dance5", ANIM_AGENT_DANCE5),
LLAnimStateEntry("dance6", ANIM_AGENT_DANCE6),
LLAnimStateEntry("dance7", ANIM_AGENT_DANCE7),
LLAnimStateEntry("dance8", ANIM_AGENT_DANCE8),
LLAnimStateEntry("express_disdain", ANIM_AGENT_EXPRESS_DISDAIN),
LLAnimStateEntry("drink", ANIM_AGENT_DRINK),
LLAnimStateEntry("express_embarrased", ANIM_AGENT_EMBARRASSED),
LLAnimStateEntry("angry_fingerwag", ANIM_AGENT_FINGER_WAG),
LLAnimStateEntry("fist_pump", ANIM_AGENT_FIST_PUMP),
LLAnimStateEntry("yoga_float", ANIM_AGENT_YOGA_FLOAT),
LLAnimStateEntry("express_frown", ANIM_AGENT_EXPRESS_FROWN),
LLAnimStateEntry("impatient", ANIM_AGENT_IMPATIENT),
LLAnimStateEntry("jumpforjoy", ANIM_AGENT_JUMP_FOR_JOY),
LLAnimStateEntry("kissmybutt", ANIM_AGENT_KISS_MY_BUTT),
LLAnimStateEntry("express_kiss", ANIM_AGENT_EXPRESS_KISS),
LLAnimStateEntry("laugh_short", ANIM_AGENT_LAUGH_SHORT),
LLAnimStateEntry("musclebeach", ANIM_AGENT_MUSCLE_BEACH),
LLAnimStateEntry("no_unhappy", ANIM_AGENT_NO_UNHAPPY),
LLAnimStateEntry("no_head", ANIM_AGENT_NO),
LLAnimStateEntry("nyanya", ANIM_AGENT_NYAH_NYAH),
LLAnimStateEntry("punch_onetwo", ANIM_AGENT_ONETWO_PUNCH),
LLAnimStateEntry("express_open_mouth", ANIM_AGENT_EXPRESS_OPEN_MOUTH),
LLAnimStateEntry("peace", ANIM_AGENT_PEACE),
LLAnimStateEntry("point_you", ANIM_AGENT_POINT_YOU),
LLAnimStateEntry("point_me", ANIM_AGENT_POINT_ME),
LLAnimStateEntry("punch_l", ANIM_AGENT_PUNCH_LEFT),
LLAnimStateEntry("punch_r", ANIM_AGENT_PUNCH_RIGHT),
LLAnimStateEntry("rps_countdown", ANIM_AGENT_RPS_COUNTDOWN),
LLAnimStateEntry("rps_paper", ANIM_AGENT_RPS_PAPER),
LLAnimStateEntry("rps_rock", ANIM_AGENT_RPS_ROCK),
LLAnimStateEntry("rps_scissors", ANIM_AGENT_RPS_SCISSORS),
LLAnimStateEntry("express_repulsed", ANIM_AGENT_EXPRESS_REPULSED),
LLAnimStateEntry("kick_roundhouse_r", ANIM_AGENT_ROUNDHOUSE_KICK),
LLAnimStateEntry("express_sad", ANIM_AGENT_SAD),
LLAnimStateEntry("salute", ANIM_AGENT_SALUTE),
LLAnimStateEntry("shout", ANIM_AGENT_SHOUT),
LLAnimStateEntry("express_shrug", ANIM_AGENT_SHRUG),
LLAnimStateEntry("express_smile", ANIM_AGENT_EXPRESS_SMILE),
LLAnimStateEntry("smoke_idle", ANIM_AGENT_SMOKE_IDLE),
LLAnimStateEntry("smoke_inhale", ANIM_AGENT_SMOKE_INHALE),
LLAnimStateEntry("smoke_throw_down", ANIM_AGENT_SMOKE_THROW_DOWN),
LLAnimStateEntry("express_surprise", ANIM_AGENT_SURPRISE),
LLAnimStateEntry("sword_strike_r", ANIM_AGENT_SWORD_STRIKE),
LLAnimStateEntry("angry_tantrum", ANIM_AGENT_TANTRUM),
LLAnimStateEntry("express_tongue_out", ANIM_AGENT_EXPRESS_TONGUE_OUT),
LLAnimStateEntry("hello", ANIM_AGENT_HELLO),
LLAnimStateEntry("whisper", ANIM_AGENT_WHISPER),
LLAnimStateEntry("whistle", ANIM_AGENT_WHISTLE),
LLAnimStateEntry("express_wink", ANIM_AGENT_WINK),
LLAnimStateEntry("wink_hollywood", ANIM_AGENT_WINK_HOLLYWOOD),
LLAnimStateEntry("express_worry", ANIM_AGENT_EXPRESS_WORRY),
LLAnimStateEntry("yes_happy", ANIM_AGENT_YES_HAPPY),
LLAnimStateEntry("yes_head", ANIM_AGENT_YES),
LLAnimStateEntry("express_afraid", ANIM_AGENT_AFRAID),
LLAnimStateEntry("express_anger", ANIM_AGENT_ANGRY),
LLAnimStateEntry("away", ANIM_AGENT_AWAY),
LLAnimStateEntry("backflip", ANIM_AGENT_BACKFLIP),
LLAnimStateEntry("express_laugh", ANIM_AGENT_BELLY_LAUGH),
LLAnimStateEntry("express_toothsmile", ANIM_AGENT_EXPRESS_TOOTHSMILE),
LLAnimStateEntry("blowkiss", ANIM_AGENT_BLOW_KISS),
LLAnimStateEntry("express_bored", ANIM_AGENT_BORED),
LLAnimStateEntry("bow", ANIM_AGENT_BOW),
LLAnimStateEntry("clap", ANIM_AGENT_CLAP),
LLAnimStateEntry("courtbow", ANIM_AGENT_COURTBOW),
LLAnimStateEntry("express_cry", ANIM_AGENT_CRY),
LLAnimStateEntry("dance1", ANIM_AGENT_DANCE1),
LLAnimStateEntry("dance2", ANIM_AGENT_DANCE2),
LLAnimStateEntry("dance3", ANIM_AGENT_DANCE3),
LLAnimStateEntry("dance4", ANIM_AGENT_DANCE4),
LLAnimStateEntry("dance5", ANIM_AGENT_DANCE5),
LLAnimStateEntry("dance6", ANIM_AGENT_DANCE6),
LLAnimStateEntry("dance7", ANIM_AGENT_DANCE7),
LLAnimStateEntry("dance8", ANIM_AGENT_DANCE8),
LLAnimStateEntry("express_disdain", ANIM_AGENT_EXPRESS_DISDAIN),
LLAnimStateEntry("drink", ANIM_AGENT_DRINK),
LLAnimStateEntry("express_embarrased", ANIM_AGENT_EMBARRASSED),
LLAnimStateEntry("angry_fingerwag", ANIM_AGENT_FINGER_WAG),
LLAnimStateEntry("fist_pump", ANIM_AGENT_FIST_PUMP),
LLAnimStateEntry("yoga_float", ANIM_AGENT_YOGA_FLOAT),
LLAnimStateEntry("express_frown", ANIM_AGENT_EXPRESS_FROWN),
LLAnimStateEntry("impatient", ANIM_AGENT_IMPATIENT),
LLAnimStateEntry("jumpforjoy", ANIM_AGENT_JUMP_FOR_JOY),
LLAnimStateEntry("kissmybutt", ANIM_AGENT_KISS_MY_BUTT),
LLAnimStateEntry("express_kiss", ANIM_AGENT_EXPRESS_KISS),
LLAnimStateEntry("laugh_short", ANIM_AGENT_LAUGH_SHORT),
LLAnimStateEntry("musclebeach", ANIM_AGENT_MUSCLE_BEACH),
LLAnimStateEntry("no_unhappy", ANIM_AGENT_NO_UNHAPPY),
LLAnimStateEntry("no_head", ANIM_AGENT_NO),
LLAnimStateEntry("nyanya", ANIM_AGENT_NYAH_NYAH),
LLAnimStateEntry("punch_onetwo", ANIM_AGENT_ONETWO_PUNCH),
LLAnimStateEntry("express_open_mouth", ANIM_AGENT_EXPRESS_OPEN_MOUTH),
LLAnimStateEntry("peace", ANIM_AGENT_PEACE),
LLAnimStateEntry("point_you", ANIM_AGENT_POINT_YOU),
LLAnimStateEntry("point_me", ANIM_AGENT_POINT_ME),
LLAnimStateEntry("punch_l", ANIM_AGENT_PUNCH_LEFT),
LLAnimStateEntry("punch_r", ANIM_AGENT_PUNCH_RIGHT),
LLAnimStateEntry("rps_countdown", ANIM_AGENT_RPS_COUNTDOWN),
LLAnimStateEntry("rps_paper", ANIM_AGENT_RPS_PAPER),
LLAnimStateEntry("rps_rock", ANIM_AGENT_RPS_ROCK),
LLAnimStateEntry("rps_scissors", ANIM_AGENT_RPS_SCISSORS),
LLAnimStateEntry("express_repulsed", ANIM_AGENT_EXPRESS_REPULSED),
LLAnimStateEntry("kick_roundhouse_r", ANIM_AGENT_ROUNDHOUSE_KICK),
LLAnimStateEntry("express_sad", ANIM_AGENT_SAD),
LLAnimStateEntry("salute", ANIM_AGENT_SALUTE),
LLAnimStateEntry("shout", ANIM_AGENT_SHOUT),
LLAnimStateEntry("express_shrug", ANIM_AGENT_SHRUG),
LLAnimStateEntry("express_smile", ANIM_AGENT_EXPRESS_SMILE),
LLAnimStateEntry("smoke_idle", ANIM_AGENT_SMOKE_IDLE),
LLAnimStateEntry("smoke_inhale", ANIM_AGENT_SMOKE_INHALE),
LLAnimStateEntry("smoke_throw_down", ANIM_AGENT_SMOKE_THROW_DOWN),
LLAnimStateEntry("express_surprise", ANIM_AGENT_SURPRISE),
LLAnimStateEntry("sword_strike_r", ANIM_AGENT_SWORD_STRIKE),
LLAnimStateEntry("angry_tantrum", ANIM_AGENT_TANTRUM),
LLAnimStateEntry("express_tongue_out", ANIM_AGENT_EXPRESS_TONGUE_OUT),
LLAnimStateEntry("hello", ANIM_AGENT_HELLO),
LLAnimStateEntry("whisper", ANIM_AGENT_WHISPER),
LLAnimStateEntry("whistle", ANIM_AGENT_WHISTLE),
LLAnimStateEntry("express_wink", ANIM_AGENT_WINK),
LLAnimStateEntry("wink_hollywood", ANIM_AGENT_WINK_HOLLYWOOD),
LLAnimStateEntry("express_worry", ANIM_AGENT_EXPRESS_WORRY),
LLAnimStateEntry("yes_happy", ANIM_AGENT_YES_HAPPY),
LLAnimStateEntry("yes_head", ANIM_AGENT_YES),
};
const S32 gUserAnimStatesCount = LL_ARRAY_SIZE(gUserAnimStates);
......
/**
/**
* @file llanimationstates.h
* @brief Implementation of animation state support.
*
* $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -28,6 +28,7 @@
#define LL_LLANIMATIONSTATES_H
#include <map>
#include "boost/unordered/unordered_flat_map.hpp"
#include "llstringtable.h"
#include "lluuid.h"
......@@ -201,54 +202,54 @@ extern S32 NUM_AGENT_STAND_ANIMS;
class LLAnimationLibrary
{
private:
LLStringTable mAnimStringTable;
LLStringTable mAnimStringTable;
typedef std::map<LLUUID, char *> anim_map_t;
anim_map_t mAnimMap;
typedef boost::unordered_flat_map<LLUUID, char *> anim_map_t;
anim_map_t mAnimMap;
public:
LLAnimationLibrary();
~LLAnimationLibrary();
LLAnimationLibrary();
~LLAnimationLibrary() = default;
//-----------------------------------------------------------------------------
// Return the text name of a single animation state,
// Return NULL if the state is invalid
//-----------------------------------------------------------------------------
const char *animStateToString( const LLUUID& state );
//-----------------------------------------------------------------------------
// Return the text name of a single animation state,
// Return NULL if the state is invalid
//-----------------------------------------------------------------------------
const char *animStateToString( const LLUUID& state );
//-----------------------------------------------------------------------------
// Return the animation state for the given name.
// Retun NULL if the name is invalid.
//-----------------------------------------------------------------------------
LLUUID stringToAnimState( const std::string& name, BOOL allow_ids = TRUE );
//-----------------------------------------------------------------------------
// Return the animation state for the given name.
// Retun NULL if the name is invalid.
//-----------------------------------------------------------------------------
LLUUID stringToAnimState( const std::string& name, BOOL allow_ids = TRUE );
//-----------------------------------------------------------------------------
// Associate an anim state with a name
//-----------------------------------------------------------------------------
void animStateSetString( const LLUUID& state, const std::string& name);
//-----------------------------------------------------------------------------
// Associate an anim state with a name
//-----------------------------------------------------------------------------
void animStateSetString( const LLUUID& state, const std::string& name);
//-----------------------------------------------------------------------------
// Find the name for a given animation, or UUID string if none defined.
//-----------------------------------------------------------------------------
std::string animationName( const LLUUID& id ) const;
//-----------------------------------------------------------------------------
// Find the name for a given animation, or UUID string if none defined.
//-----------------------------------------------------------------------------
std::string animationName( const LLUUID& id ) const;
};
struct LLAnimStateEntry
{
LLAnimStateEntry(const char* name, const LLUUID& id) :
mName(name),
mID(id)
{
// LABELS:
// Look to newview/LLAnimStateLabels.* for how to get the labels.
// The labels should no longer be stored in this structure. The server
// shouldn't care about the local friendly name of an animation, and
// this is common code.
}
LLAnimStateEntry(const char* name, const LLUUID& id) :
mName(name),
mID(id)
{
// LABELS:
// Look to newview/LLAnimStateLabels.* for how to get the labels.
// The labels should no longer be stored in this structure. The server
// shouldn't care about the local friendly name of an animation, and
// this is common code.
}
const char* mName;
const LLUUID mID;
const char* mName;
const LLUUID mID;
};
// Animation states that the user can trigger
......
/**
/**
* @file llbvhconsts.h
* @brief Consts and types useful to BVH files and LindenLabAnimation format.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library 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
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -30,17 +30,52 @@
const F32 MAX_ANIM_DURATION = 60.f;
typedef enum e_constraint_type
{
CONSTRAINT_TYPE_POINT,
CONSTRAINT_TYPE_PLANE,
NUM_CONSTRAINT_TYPES
} EConstraintType;
{
CONSTRAINT_TYPE_POINT,
CONSTRAINT_TYPE_PLANE,
NUM_CONSTRAINT_TYPES
} EConstraintType;
typedef enum e_constraint_target_type
{
CONSTRAINT_TARGET_TYPE_BODY,
CONSTRAINT_TARGET_TYPE_GROUND,
NUM_CONSTRAINT_TARGET_TYPES
} EConstraintTargetType;
{
CONSTRAINT_TARGET_TYPE_BODY,
CONSTRAINT_TARGET_TYPE_GROUND,
NUM_CONSTRAINT_TARGET_TYPES
} EConstraintTargetType;
const std::string BVHSTATUS[] = {
"E_ST_OK",
"E_ST_EOF",
"E_ST_NO_CONSTRAINT",
"E_ST_NO_FILE",
"E_ST_NO_HIER",
"E_ST_NO_JOINT",
"E_ST_NO_NAME",
"E_ST_NO_OFFSET",
"E_ST_NO_CHANNELS",
"E_ST_NO_ROTATION",
"E_ST_NO_AXIS",
"E_ST_NO_MOTION",
"E_ST_NO_FRAMES",
"E_ST_NO_FRAME_TIME",
"E_ST_NO_POS",
"E_ST_NO_ROT",
"E_ST_NO_XLT_FILE",
"E_ST_NO_XLT_HEADER",
"E_ST_NO_XLT_NAME",
"E_ST_NO_XLT_IGNORE",
"E_ST_NO_XLT_RELATIVE",
"E_ST_NO_XLT_OUTNAME",
"E_ST_NO_XLT_MATRIX",
"E_ST_NO_XLT_MERGECHILD",
"E_ST_NO_XLT_MERGEPARENT",
"E_ST_NO_XLT_PRIORITY",
"E_ST_NO_XLT_LOOP",
"E_ST_NO_XLT_EASEIN",
"E_ST_NO_XLT_EASEOUT",
"E_ST_NO_XLT_HAND",
"E_ST_NO_XLT_EMOTE",
"E_ST_BAD_ROOT"
};
#endif // LL_LLBVHCONSTS_H
/**
/**
* @file llbvhloader.cpp
* @brief Translates a BVH files to LindenLabAnimation format.
*
* $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
*
* This library is distributed in the hope that it will be useful,7
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
......@@ -58,43 +58,43 @@ const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f;
const F32 POSITION_MOTION_THRESHOLD_SQUARED = 0.001f * 0.001f;
const F32 ROTATION_MOTION_THRESHOLD = 0.001f;
char gInFile[1024]; /* Flawfinder: ignore */
char gOutFile[1024]; /* Flawfinder: ignore */
char gInFile[1024]; /* Flawfinder: ignore */
char gOutFile[1024]; /* Flawfinder: ignore */
/*
//------------------------------------------------------------------------
// Status Codes
//------------------------------------------------------------------------
const char *LLBVHLoader::ST_OK = "Ok";
const char *LLBVHLoader::ST_EOF = "Premature end of file.";
const char *LLBVHLoader::ST_NO_CONSTRAINT = "Can't read constraint definition.";
const char *LLBVHLoader::ST_NO_FILE = "Can't open BVH file.";
const char *LLBVHLoader::ST_NO_HIER = "Invalid HIERARCHY header.";
const char *LLBVHLoader::ST_NO_JOINT = "Can't find ROOT or JOINT.";
const char *LLBVHLoader::ST_NO_NAME = "Can't get JOINT name.";
const char *LLBVHLoader::ST_NO_OFFSET = "Can't find OFFSET.";
const char *LLBVHLoader::ST_NO_CHANNELS = "Can't find CHANNELS.";
const char *LLBVHLoader::ST_NO_ROTATION = "Can't get rotation order.";
const char *LLBVHLoader::ST_NO_AXIS = "Can't get rotation axis.";
const char *LLBVHLoader::ST_NO_MOTION = "Can't find MOTION.";
const char *LLBVHLoader::ST_NO_FRAMES = "Can't get number of frames.";
const char *LLBVHLoader::ST_NO_FRAME_TIME = "Can't get frame time.";
const char *LLBVHLoader::ST_NO_POS = "Can't get position values.";
const char *LLBVHLoader::ST_NO_ROT = "Can't get rotation values.";
const char *LLBVHLoader::ST_NO_XLT_FILE = "Can't open translation file.";
const char *LLBVHLoader::ST_NO_XLT_HEADER = "Can't read translation header.";
const char *LLBVHLoader::ST_NO_XLT_NAME = "Can't read translation names.";
const char *LLBVHLoader::ST_NO_XLT_IGNORE = "Can't read translation ignore value.";
const char *LLBVHLoader::ST_NO_XLT_RELATIVE = "Can't read translation relative value.";
const char *LLBVHLoader::ST_NO_XLT_OUTNAME = "Can't read translation outname value.";
const char *LLBVHLoader::ST_NO_XLT_MATRIX = "Can't read translation matrix.";
const char *LLBVHLoader::ST_OK = "Ok";
const char *LLBVHLoader::ST_EOF = "Premature end of file.";
const char *LLBVHLoader::ST_NO_CONSTRAINT = "Can't read constraint definition.";
const char *LLBVHLoader::ST_NO_FILE = "Can't open BVH file.";
const char *LLBVHLoader::ST_NO_HIER = "Invalid HIERARCHY header.";
const char *LLBVHLoader::ST_NO_JOINT = "Can't find ROOT or JOINT.";
const char *LLBVHLoader::ST_NO_NAME = "Can't get JOINT name.";
const char *LLBVHLoader::ST_NO_OFFSET = "Can't find OFFSET.";
const char *LLBVHLoader::ST_NO_CHANNELS = "Can't find CHANNELS.";
const char *LLBVHLoader::ST_NO_ROTATION = "Can't get rotation order.";
const char *LLBVHLoader::ST_NO_AXIS = "Can't get rotation axis.";
const char *LLBVHLoader::ST_NO_MOTION = "Can't find MOTION.";
const char *LLBVHLoader::ST_NO_FRAMES = "Can't get number of frames.";
const char *LLBVHLoader::ST_NO_FRAME_TIME = "Can't get frame time.";
const char *LLBVHLoader::ST_NO_POS = "Can't get position values.";
const char *LLBVHLoader::ST_NO_ROT = "Can't get rotation values.";
const char *LLBVHLoader::ST_NO_XLT_FILE = "Can't open translation file.";
const char *LLBVHLoader::ST_NO_XLT_HEADER = "Can't read translation header.";
const char *LLBVHLoader::ST_NO_XLT_NAME = "Can't read translation names.";
const char *LLBVHLoader::ST_NO_XLT_IGNORE = "Can't read translation ignore value.";
const char *LLBVHLoader::ST_NO_XLT_RELATIVE = "Can't read translation relative value.";
const char *LLBVHLoader::ST_NO_XLT_OUTNAME = "Can't read translation outname value.";
const char *LLBVHLoader::ST_NO_XLT_MATRIX = "Can't read translation matrix.";
const char *LLBVHLoader::ST_NO_XLT_MERGECHILD = "Can't get mergechild name.";
const char *LLBVHLoader::ST_NO_XLT_MERGEPARENT = "Can't get mergeparent name.";
const char *LLBVHLoader::ST_NO_XLT_PRIORITY = "Can't get priority value.";
const char *LLBVHLoader::ST_NO_XLT_LOOP = "Can't get loop value.";
const char *LLBVHLoader::ST_NO_XLT_EASEIN = "Can't get easeIn values.";
const char *LLBVHLoader::ST_NO_XLT_EASEOUT = "Can't get easeOut values.";
const char *LLBVHLoader::ST_NO_XLT_HAND = "Can't get hand morph value.";
const char *LLBVHLoader::ST_NO_XLT_EMOTE = "Can't read emote name.";
const char *LLBVHLoader::ST_NO_XLT_PRIORITY = "Can't get priority value.";
const char *LLBVHLoader::ST_NO_XLT_LOOP = "Can't get loop value.";
const char *LLBVHLoader::ST_NO_XLT_EASEIN = "Can't get easeIn values.";
const char *LLBVHLoader::ST_NO_XLT_EASEOUT = "Can't get easeOut values.";
const char *LLBVHLoader::ST_NO_XLT_HAND = "Can't get hand morph value.";
const char *LLBVHLoader::ST_NO_XLT_EMOTE = "Can't read emote name.";
const char *LLBVHLoader::ST_BAD_ROOT = "Illegal ROOT joint.";
*/
......@@ -103,9 +103,9 @@ const char *LLBVHLoader::ST_BAD_ROOT = "Illegal ROOT joint.";
//------------------------------------------------------------------------
const char *find_next_whitespace(const char *p)
{
while(*p && isspace(*p)) p++;
while(*p && !isspace(*p)) p++;
return p;
while(*p && isspace(*p)) p++;
while(*p && !isspace(*p)) p++;
return p;
}
......@@ -118,13 +118,13 @@ const char *find_next_whitespace(const char *p)
//------------------------------------------------------------------------
LLQuaternion::Order bvhStringToOrder( char *str )
{
char order[4]; /* Flawfinder: ignore */
order[0] = str[2];
order[1] = str[1];
order[2] = str[0];
order[3] = 0;
LLQuaternion::Order retVal = StringToOrder( order );
return retVal;
char order[4]; /* Flawfinder: ignore */
order[0] = str[2];
order[1] = str[1];
order[2] = str[0];
order[3] = 0;
LLQuaternion::Order retVal = StringToOrder( order );
return retVal;
}
//-----------------------------------------------------------------------------
......@@ -133,66 +133,65 @@ LLQuaternion::Order bvhStringToOrder( char *str )
LLBVHLoader::LLBVHLoader(const char* buffer, ELoadStatus &loadStatus, S32 &errorLine, std::map<std::string, std::string>& joint_alias_map )
{
reset();
errorLine = 0;
mStatus = loadTranslationTable("anim.ini");
loadStatus = mStatus;
LL_INFOS("BVH") << "Load Status 00 : " << loadStatus << LL_ENDL;
if (mStatus == E_ST_NO_XLT_FILE)
{
LL_WARNS("BVH") << "NOTE: No translation table found." << LL_ENDL;
loadStatus = mStatus;
return;
}
else
{
if (mStatus != E_ST_OK)
{
LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
errorLine = getLineNumber();
loadStatus = mStatus;
return;
}
}
reset();
errorLine = 0;
mStatus = loadTranslationTable("anim.ini");
loadStatus = mStatus;
LL_INFOS("BVH") << "Load Status 00 : " << loadStatus << LL_ENDL;
if (mStatus == E_ST_NO_XLT_FILE)
{
LL_WARNS("BVH") << "NOTE: No translation table found." << LL_ENDL;
loadStatus = mStatus;
return;
}
else
{
if (mStatus != E_ST_OK)
{
LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
errorLine = getLineNumber();
loadStatus = mStatus;
return;
}
}
// Recognize all names we've been told are legal.
std::map<std::string, std::string>::iterator iter;
for (iter = joint_alias_map.begin(); iter != joint_alias_map.end(); iter++)
for (std::map<std::string, std::string>::value_type& alias_pair : joint_alias_map)
{
makeTranslation( alias_pair.first , alias_pair.second );
}
char error_text[128]; /* Flawfinder: ignore */
S32 error_line;
mStatus = loadBVHFile(buffer, error_text, error_line); //Reads all joints in BVH file.
LL_DEBUGS("BVH") << "============================================================" << LL_ENDL;
LL_DEBUGS("BVH") << "Raw data from file" << LL_ENDL;
dumpBVHInfo();
if (mStatus != E_ST_OK)
{
makeTranslation( iter->first , iter->second );
LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
loadStatus = mStatus;
errorLine = getLineNumber();
return;
}
char error_text[128]; /* Flawfinder: ignore */
S32 error_line;
mStatus = loadBVHFile(buffer, error_text, error_line); //Reads all joints in BVH file.
LL_DEBUGS("BVH") << "============================================================" << LL_ENDL;
LL_DEBUGS("BVH") << "Raw data from file" << LL_ENDL;
dumpBVHInfo();
if (mStatus != E_ST_OK)
{
LL_WARNS("BVH") << "ERROR: [line: " << getLineNumber() << "] " << mStatus << LL_ENDL;
loadStatus = mStatus;
errorLine = getLineNumber();
return;
}
applyTranslations(); //Maps between joints found in file and the aliased names.
optimize();
LL_DEBUGS("BVH") << "============================================================" << LL_ENDL;
LL_DEBUGS("BVH") << "After translations and optimize" << LL_ENDL;
dumpBVHInfo();
mInitialized = TRUE;
applyTranslations(); //Maps between joints found in file and the aliased names.
optimize();
LL_DEBUGS("BVH") << "============================================================" << LL_ENDL;
LL_DEBUGS("BVH") << "After translations and optimize" << LL_ENDL;
dumpBVHInfo();
mInitialized = TRUE;
}
LLBVHLoader::~LLBVHLoader()
{
std::for_each(mJoints.begin(),mJoints.end(),DeletePointer());
mJoints.clear();
std::for_each(mJoints.begin(),mJoints.end(),DeletePointer());
mJoints.clear();
}
//------------------------------------------------------------------------
......@@ -200,301 +199,301 @@ LLBVHLoader::~LLBVHLoader()
//------------------------------------------------------------------------
ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName)
{
//--------------------------------------------------------------------
// open file
//--------------------------------------------------------------------
std::string path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName);
LLAPRFile infile ;
infile.open(path, LL_APR_R);
apr_file_t *fp = infile.getFileHandle();
if (!fp)
return E_ST_NO_XLT_FILE;
LL_INFOS("BVH") << "NOTE: Loading translation table: " << fileName << LL_ENDL;
//--------------------------------------------------------------------
// register file to be closed on function exit
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// load header
//--------------------------------------------------------------------
if ( ! getLine(fp) )
return E_ST_EOF;
if ( strncmp(mLine, "Translations 1.0", 16) )
return E_ST_NO_XLT_HEADER;
//--------------------------------------------------------------------
// load data one line at a time
//--------------------------------------------------------------------
BOOL loadingGlobals = FALSE;
while ( getLine(fp) )
{
//----------------------------------------------------------------
// check the 1st token on the line to determine if it's empty or a comment
//----------------------------------------------------------------
char token[128]; /* Flawfinder: ignore */
if ( sscanf(mLine, " %127s", token) != 1 ) /* Flawfinder: ignore */
continue;
if (token[0] == '#')
continue;
//----------------------------------------------------------------
// check if a [jointName] or [GLOBALS] was specified.
//----------------------------------------------------------------
if (token[0] == '[')
{
char name[128]; /* Flawfinder: ignore */
if ( sscanf(mLine, " [%127[^]]", name) != 1 )
return E_ST_NO_XLT_NAME;
if (strcmp(name, "GLOBALS")==0)
{
loadingGlobals = TRUE;
continue;
}
}
//----------------------------------------------------------------
// check for optional emote
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "emote")==0)
{
char emote_str[1024]; /* Flawfinder: ignore */
if ( sscanf(mLine, " %*s = %1023s", emote_str) != 1 ) /* Flawfinder: ignore */
return E_ST_NO_XLT_EMOTE;
mEmoteName.assign( emote_str );
// LL_INFOS() << "NOTE: Emote: " << mEmoteName.c_str() << LL_ENDL;
continue;
}
//----------------------------------------------------------------
// check for global priority setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "priority")==0)
{
S32 priority;
if ( sscanf(mLine, " %*s = %d", &priority) != 1 )
return E_ST_NO_XLT_PRIORITY;
mPriority = priority;
// LL_INFOS() << "NOTE: Priority: " << mPriority << LL_ENDL;
continue;
}
//----------------------------------------------------------------
// check for global loop setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "loop")==0)
{
char trueFalse[128]; /* Flawfinder: ignore */
trueFalse[0] = '\0';
F32 loop_in = 0.f;
F32 loop_out = 1.f;
if ( sscanf(mLine, " %*s = %f %f", &loop_in, &loop_out) == 2 )
{
mLoop = TRUE;
}
else if ( sscanf(mLine, " %*s = %127s", trueFalse) == 1 ) /* Flawfinder: ignore */
{
mLoop = (LLStringUtil::compareInsensitive(trueFalse, "true")==0);
}
else
{
return E_ST_NO_XLT_LOOP;
}
mLoopInPoint = loop_in * mDuration;
mLoopOutPoint = loop_out * mDuration;
continue;
}
//----------------------------------------------------------------
// check for global easeIn setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "easein")==0)
{
F32 duration;
char type[128]; /* Flawfinder: ignore */
if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 ) /* Flawfinder: ignore */
return E_ST_NO_XLT_EASEIN;
mEaseIn = duration;
continue;
}
//----------------------------------------------------------------
// check for global easeOut setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "easeout")==0)
{
F32 duration;
char type[128]; /* Flawfinder: ignore */
if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 ) /* Flawfinder: ignore */
return E_ST_NO_XLT_EASEOUT;
mEaseOut = duration;
continue;
}
//----------------------------------------------------------------
// check for global handMorph setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "hand")==0)
{
S32 handMorph;
if (sscanf(mLine, " %*s = %d", &handMorph) != 1)
return E_ST_NO_XLT_HAND;
mHand = handMorph;
continue;
}
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "constraint")==0)
{
Constraint constraint;
// try reading optional target direction
if(sscanf( /* Flawfinder: ignore */
mLine,
" %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f",
&constraint.mChainLength,
&constraint.mEaseInStart,
&constraint.mEaseInStop,
&constraint.mEaseOutStart,
&constraint.mEaseOutStop,
constraint.mSourceJointName,
&constraint.mSourceOffset.mV[VX],
&constraint.mSourceOffset.mV[VY],
&constraint.mSourceOffset.mV[VZ],
constraint.mTargetJointName,
&constraint.mTargetOffset.mV[VX],
&constraint.mTargetOffset.mV[VY],
&constraint.mTargetOffset.mV[VZ],
&constraint.mTargetDir.mV[VX],
&constraint.mTargetDir.mV[VY],
&constraint.mTargetDir.mV[VZ]) != 16)
{
if(sscanf( /* Flawfinder: ignore */
mLine,
" %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f",
&constraint.mChainLength,
&constraint.mEaseInStart,
&constraint.mEaseInStop,
&constraint.mEaseOutStart,
&constraint.mEaseOutStop,
constraint.mSourceJointName,
&constraint.mSourceOffset.mV[VX],
&constraint.mSourceOffset.mV[VY],
&constraint.mSourceOffset.mV[VZ],
constraint.mTargetJointName,
&constraint.mTargetOffset.mV[VX],
&constraint.mTargetOffset.mV[VY],
&constraint.mTargetOffset.mV[VZ]) != 13)
{
return E_ST_NO_CONSTRAINT;
}
}
else
{
// normalize direction
if (!constraint.mTargetDir.isExactlyZero())
{
constraint.mTargetDir.normVec();
}
}
constraint.mConstraintType = CONSTRAINT_TYPE_POINT;
mConstraints.push_back(constraint);
continue;
}
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "planar_constraint")==0)
{
Constraint constraint;
// try reading optional target direction
if(sscanf( /* Flawfinder: ignore */
mLine,
" %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f",
&constraint.mChainLength,
&constraint.mEaseInStart,
&constraint.mEaseInStop,
&constraint.mEaseOutStart,
&constraint.mEaseOutStop,
constraint.mSourceJointName,
&constraint.mSourceOffset.mV[VX],
&constraint.mSourceOffset.mV[VY],
&constraint.mSourceOffset.mV[VZ],
constraint.mTargetJointName,
&constraint.mTargetOffset.mV[VX],
&constraint.mTargetOffset.mV[VY],
&constraint.mTargetOffset.mV[VZ],
&constraint.mTargetDir.mV[VX],
&constraint.mTargetDir.mV[VY],
&constraint.mTargetDir.mV[VZ]) != 16)
{
if(sscanf( /* Flawfinder: ignore */
mLine,
" %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f",
&constraint.mChainLength,
&constraint.mEaseInStart,
&constraint.mEaseInStop,
&constraint.mEaseOutStart,
&constraint.mEaseOutStop,
constraint.mSourceJointName,
&constraint.mSourceOffset.mV[VX],
&constraint.mSourceOffset.mV[VY],
&constraint.mSourceOffset.mV[VZ],
constraint.mTargetJointName,
&constraint.mTargetOffset.mV[VX],
&constraint.mTargetOffset.mV[VY],
&constraint.mTargetOffset.mV[VZ]) != 13)
{
return E_ST_NO_CONSTRAINT;
}
}
else
{
// normalize direction
if (!constraint.mTargetDir.isExactlyZero())
{
constraint.mTargetDir.normVec();
}
}
constraint.mConstraintType = CONSTRAINT_TYPE_PLANE;
mConstraints.push_back(constraint);
continue;
}
}
infile.close() ;
return E_ST_OK;
//--------------------------------------------------------------------
// open file
//--------------------------------------------------------------------
std::string path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName);
LLAPRFile infile ;
infile.open(path, LL_APR_R);
apr_file_t *fp = infile.getFileHandle();
if (!fp)
return E_ST_NO_XLT_FILE;
LL_INFOS("BVH") << "NOTE: Loading translation table: " << fileName << LL_ENDL;
//--------------------------------------------------------------------
// register file to be closed on function exit
//--------------------------------------------------------------------
//--------------------------------------------------------------------
// load header
//--------------------------------------------------------------------
if ( ! getLine(fp) )
return E_ST_EOF;
if ( strncmp(mLine, "Translations 1.0", 16) )
return E_ST_NO_XLT_HEADER;
//--------------------------------------------------------------------
// load data one line at a time
//--------------------------------------------------------------------
BOOL loadingGlobals = FALSE;
while ( getLine(fp) )
{
//----------------------------------------------------------------
// check the 1st token on the line to determine if it's empty or a comment
//----------------------------------------------------------------
char token[128]; /* Flawfinder: ignore */
if ( sscanf(mLine, " %127s", token) != 1 ) /* Flawfinder: ignore */
continue;
if (token[0] == '#')
continue;
//----------------------------------------------------------------
// check if a [jointName] or [GLOBALS] was specified.
//----------------------------------------------------------------
if (token[0] == '[')
{
char name[128]; /* Flawfinder: ignore */
if ( sscanf(mLine, " [%127[^]]", name) != 1 )
return E_ST_NO_XLT_NAME;
if (strcmp(name, "GLOBALS")==0)
{
loadingGlobals = TRUE;
continue;
}
}
//----------------------------------------------------------------
// check for optional emote
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "emote")==0)
{
char emote_str[1024]; /* Flawfinder: ignore */
if ( sscanf(mLine, " %*s = %1023s", emote_str) != 1 ) /* Flawfinder: ignore */
return E_ST_NO_XLT_EMOTE;
mEmoteName.assign( emote_str );
// LL_INFOS() << "NOTE: Emote: " << mEmoteName.c_str() << LL_ENDL;
continue;
}
//----------------------------------------------------------------
// check for global priority setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "priority")==0)
{
S32 priority;
if ( sscanf(mLine, " %*s = %d", &priority) != 1 )
return E_ST_NO_XLT_PRIORITY;
mPriority = priority;
// LL_INFOS() << "NOTE: Priority: " << mPriority << LL_ENDL;
continue;
}
//----------------------------------------------------------------
// check for global loop setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "loop")==0)
{
char trueFalse[128]; /* Flawfinder: ignore */
trueFalse[0] = '\0';
F32 loop_in = 0.f;
F32 loop_out = 1.f;
if ( sscanf(mLine, " %*s = %f %f", &loop_in, &loop_out) == 2 )
{
mLoop = TRUE;
}
else if ( sscanf(mLine, " %*s = %127s", trueFalse) == 1 ) /* Flawfinder: ignore */
{
mLoop = (LLStringUtil::compareInsensitive(trueFalse, "true")==0);
}
else
{
return E_ST_NO_XLT_LOOP;
}
mLoopInPoint = loop_in * mDuration;
mLoopOutPoint = loop_out * mDuration;
continue;
}
//----------------------------------------------------------------
// check for global easeIn setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "easein")==0)
{
F32 duration;
char type[128]; /* Flawfinder: ignore */
if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 ) /* Flawfinder: ignore */
return E_ST_NO_XLT_EASEIN;
mEaseIn = duration;
continue;
}
//----------------------------------------------------------------
// check for global easeOut setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "easeout")==0)
{
F32 duration;
char type[128]; /* Flawfinder: ignore */
if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 ) /* Flawfinder: ignore */
return E_ST_NO_XLT_EASEOUT;
mEaseOut = duration;
continue;
}
//----------------------------------------------------------------
// check for global handMorph setting
//----------------------------------------------------------------
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "hand")==0)
{
S32 handMorph;
if (sscanf(mLine, " %*s = %d", &handMorph) != 1)
return E_ST_NO_XLT_HAND;
mHand = handMorph;
continue;
}
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "constraint")==0)
{
Constraint constraint;
// try reading optional target direction
if(sscanf( /* Flawfinder: ignore */
mLine,
" %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f",
&constraint.mChainLength,
&constraint.mEaseInStart,
&constraint.mEaseInStop,
&constraint.mEaseOutStart,
&constraint.mEaseOutStop,
constraint.mSourceJointName,
&constraint.mSourceOffset.mV[VX],
&constraint.mSourceOffset.mV[VY],
&constraint.mSourceOffset.mV[VZ],
constraint.mTargetJointName,
&constraint.mTargetOffset.mV[VX],
&constraint.mTargetOffset.mV[VY],
&constraint.mTargetOffset.mV[VZ],
&constraint.mTargetDir.mV[VX],
&constraint.mTargetDir.mV[VY],
&constraint.mTargetDir.mV[VZ]) != 16)
{
if(sscanf( /* Flawfinder: ignore */
mLine,
" %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f",
&constraint.mChainLength,
&constraint.mEaseInStart,
&constraint.mEaseInStop,
&constraint.mEaseOutStart,
&constraint.mEaseOutStop,
constraint.mSourceJointName,
&constraint.mSourceOffset.mV[VX],
&constraint.mSourceOffset.mV[VY],
&constraint.mSourceOffset.mV[VZ],
constraint.mTargetJointName,
&constraint.mTargetOffset.mV[VX],
&constraint.mTargetOffset.mV[VY],
&constraint.mTargetOffset.mV[VZ]) != 13)
{
return E_ST_NO_CONSTRAINT;
}
}
else
{
// normalize direction
if (!constraint.mTargetDir.isExactlyZero())
{
constraint.mTargetDir.normVec();
}
}
constraint.mConstraintType = CONSTRAINT_TYPE_POINT;
mConstraints.push_back(constraint);
continue;
}
if (loadingGlobals && LLStringUtil::compareInsensitive(token, "planar_constraint")==0)
{
Constraint constraint;
// try reading optional target direction
if(sscanf( /* Flawfinder: ignore */
mLine,
" %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f",
&constraint.mChainLength,
&constraint.mEaseInStart,
&constraint.mEaseInStop,
&constraint.mEaseOutStart,
&constraint.mEaseOutStop,
constraint.mSourceJointName,
&constraint.mSourceOffset.mV[VX],
&constraint.mSourceOffset.mV[VY],
&constraint.mSourceOffset.mV[VZ],
constraint.mTargetJointName,
&constraint.mTargetOffset.mV[VX],
&constraint.mTargetOffset.mV[VY],
&constraint.mTargetOffset.mV[VZ],
&constraint.mTargetDir.mV[VX],
&constraint.mTargetDir.mV[VY],
&constraint.mTargetDir.mV[VZ]) != 16)
{
if(sscanf( /* Flawfinder: ignore */
mLine,
" %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f",
&constraint.mChainLength,
&constraint.mEaseInStart,
&constraint.mEaseInStop,
&constraint.mEaseOutStart,
&constraint.mEaseOutStop,
constraint.mSourceJointName,
&constraint.mSourceOffset.mV[VX],
&constraint.mSourceOffset.mV[VY],
&constraint.mSourceOffset.mV[VZ],
constraint.mTargetJointName,
&constraint.mTargetOffset.mV[VX],
&constraint.mTargetOffset.mV[VY],
&constraint.mTargetOffset.mV[VZ]) != 13)
{
return E_ST_NO_CONSTRAINT;
}
}
else
{
// normalize direction
if (!constraint.mTargetDir.isExactlyZero())
{
constraint.mTargetDir.normVec();
}
}
constraint.mConstraintType = CONSTRAINT_TYPE_PLANE;
mConstraints.push_back(constraint);
continue;
}
}
infile.close() ;
return E_ST_OK;
}
void LLBVHLoader::makeTranslation(std::string alias_name, std::string joint_name)
{
//Translation &newTrans = (foomap.insert(value_type(alias_name, Translation()))).first();
Translation &newTrans = mTranslations[ alias_name ]; //Uses []'s implicit call to ctor.
newTrans.mOutName = joint_name;
LLMatrix3 fm;
LLVector3 vect1(0, 1, 0);
LLVector3 vect2(0, 0, 1);
LLVector3 vect3(1, 0, 0);
fm.setRows(vect1, vect2, vect3);
newTrans.mFrameMatrix = fm;
if (joint_name == "mPelvis")
{
newTrans.mRelativePositionKey = TRUE;
......@@ -506,24 +505,22 @@ if (joint_name == "mPelvis")
ELoadStatus LLBVHLoader::loadAliases(const char * filename)
{
LLSD aliases_sd;
std::string fullpath = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,filename);
llifstream input_stream;
input_stream.open(fullpath.c_str(), std::ios::in | std::ios::binary);
if(input_stream.is_open())
{
if ( LLSDSerialize::fromXML(aliases_sd, input_stream) )
{
for(LLSD::map_iterator alias_iter = aliases_sd.beginMap();
alias_iter != aliases_sd.endMap();
++alias_iter)
for(const auto& alias_pair : aliases_sd.asMap())
{
LLSD::String alias_name = alias_iter->first;
LLSD::String joint_name = alias_iter->second;
LLSD::String alias_name = alias_pair.first;
LLSD::String joint_name = alias_pair.second;
makeTranslation(alias_name, joint_name);
}
}
else
......@@ -543,12 +540,12 @@ ELoadStatus LLBVHLoader::loadAliases(const char * filename)
void LLBVHLoader::dumpBVHInfo()
{
for (U32 j=0; j<mJoints.size(); j++)
{
Joint *joint = mJoints[j];
LL_DEBUGS("BVH") << joint->mName << LL_ENDL;
for (S32 i=0; i<mNumFrames; i++)
{
for (U32 j=0; j<mJoints.size(); j++)
{
Joint *joint = mJoints[j];
LL_DEBUGS("BVH") << joint->mName << LL_ENDL;
for (S32 i=0; i<mNumFrames; i++)
{
if (i<joint->mKeys.size()) // Check this in case file load failed.
{
Key &prevkey = joint->mKeys[llmax(i-1,0)];
......@@ -562,13 +559,13 @@ void LLBVHLoader::dumpBVHInfo()
(key.mRot[2] != prevkey.mRot[2])
)
{
LL_DEBUGS("BVH") << "FRAME " << i
LL_DEBUGS("BVH") << "FRAME " << i
<< " POS " << key.mPos[0] << "," << key.mPos[1] << "," << key.mPos[2]
<< " ROT " << key.mRot[0] << "," << key.mRot[1] << "," << key.mRot[2] << LL_ENDL;
}
}
}
}
}
}
}
......@@ -577,331 +574,331 @@ void LLBVHLoader::dumpBVHInfo()
//------------------------------------------------------------------------
ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &err_line)
{
std::string line;
err_line = 0;
error_text[127] = '\0';
std::string str(buffer);
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep("\r\n");
tokenizer tokens(str, sep);
tokenizer::iterator iter = tokens.begin();
mLineNumber = 0;
mJoints.clear();
std::vector<S32> parent_joints;
//--------------------------------------------------------------------
// consume hierarchy
//--------------------------------------------------------------------
if (iter == tokens.end())
return E_ST_EOF;
line = (*(iter++));
err_line++;
if ( !strstr(line.c_str(), "HIERARCHY") )
{
// LL_INFOS() << line << LL_ENDL;
return E_ST_NO_HIER;
}
//--------------------------------------------------------------------
// consume joints
//--------------------------------------------------------------------
while (TRUE)
{
//----------------------------------------------------------------
// get next line
//----------------------------------------------------------------
if (iter == tokens.end())
return E_ST_EOF;
line = (*(iter++));
err_line++;
//----------------------------------------------------------------
// consume }
//----------------------------------------------------------------
if ( strstr(line.c_str(), "}") )
{
if (parent_joints.size() > 0)
{
parent_joints.pop_back();
}
continue;
}
//----------------------------------------------------------------
// if MOTION, break out
//----------------------------------------------------------------
if ( strstr(line.c_str(), "MOTION") )
break;
//----------------------------------------------------------------
// it must be either ROOT or JOINT or EndSite
//----------------------------------------------------------------
if ( strstr(line.c_str(), "ROOT") )
{
}
else if ( strstr(line.c_str(), "JOINT") )
{
}
else if ( strstr(line.c_str(), "End Site") )
{
iter++; // {
iter++; // OFFSET
iter++; // }
S32 depth = 0;
for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
{
Joint *joint = mJoints[parent_joints[j]];
if (depth > joint->mChildTreeMaxDepth)
{
joint->mChildTreeMaxDepth = depth;
}
depth++;
}
continue;
}
else
{
strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
return E_ST_NO_JOINT;
}
//----------------------------------------------------------------
// get the joint name
//----------------------------------------------------------------
char jointName[80]; /* Flawfinder: ignore */
if ( sscanf(line.c_str(), "%*s %79s", jointName) != 1 ) /* Flawfinder: ignore */
{
strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
return E_ST_NO_NAME;
}
//---------------------------------------------------------------
// we require the root joint be "hip" - DEV-26188
//---------------------------------------------------------------
std::string line;
err_line = 0;
error_text[127] = '\0';
std::string str(buffer);
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep("\r\n");
tokenizer tokens(str, sep);
tokenizer::iterator iter = tokens.begin();
mLineNumber = 0;
mJoints.clear();
std::vector<S32> parent_joints;
//--------------------------------------------------------------------
// consume hierarchy
//--------------------------------------------------------------------
if (iter == tokens.end())
return E_ST_EOF;
line = (*(iter++));
err_line++;
if ( !strstr(line.c_str(), "HIERARCHY") )
{
// LL_INFOS() << line << LL_ENDL;
return E_ST_NO_HIER;
}
//--------------------------------------------------------------------
// consume joints
//--------------------------------------------------------------------
while (TRUE)
{
//----------------------------------------------------------------
// get next line
//----------------------------------------------------------------
if (iter == tokens.end())
return E_ST_EOF;
line = (*(iter++));
err_line++;
//----------------------------------------------------------------
// consume }
//----------------------------------------------------------------
if ( strstr(line.c_str(), "}") )
{
if (parent_joints.size() > 0)
{
parent_joints.pop_back();
}
continue;
}
//----------------------------------------------------------------
// if MOTION, break out
//----------------------------------------------------------------
if ( strstr(line.c_str(), "MOTION") )
break;
//----------------------------------------------------------------
// it must be either ROOT or JOINT or EndSite
//----------------------------------------------------------------
if ( strstr(line.c_str(), "ROOT") )
{
}
else if ( strstr(line.c_str(), "JOINT") )
{
}
else if ( strstr(line.c_str(), "End Site") )
{
iter++; // {
iter++; // OFFSET
iter++; // }
S32 depth = 0;
for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
{
Joint *joint = mJoints[parent_joints[j]];
if (depth > joint->mChildTreeMaxDepth)
{
joint->mChildTreeMaxDepth = depth;
}
depth++;
}
continue;
}
else
{
strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
return E_ST_NO_JOINT;
}
//----------------------------------------------------------------
// get the joint name
//----------------------------------------------------------------
char jointName[80]; /* Flawfinder: ignore */
if ( sscanf(line.c_str(), "%*s %79s", jointName) != 1 ) /* Flawfinder: ignore */
{
strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
return E_ST_NO_NAME;
}
//---------------------------------------------------------------
// we require the root joint be "hip" - DEV-26188
//---------------------------------------------------------------
if (mJoints.size() == 0 )
{
//The root joint of the BVH file must be hip (mPelvis) or an alias of mPelvis.
const char* FORCED_ROOT_NAME = "hip";
TranslationMap::iterator hip_joint = mTranslations.find( FORCED_ROOT_NAME );
TranslationMap::iterator root_joint = mTranslations.find( jointName );
if ( hip_joint == mTranslations.end() || root_joint == mTranslations.end() || root_joint->second.mOutName != hip_joint->second.mOutName )
{
strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
return E_ST_BAD_ROOT;
}
}
//----------------------------------------------------------------
// add a set of keyframes for this joint
//----------------------------------------------------------------
mJoints.push_back( new Joint( jointName ) );
Joint *joint = mJoints.back();
LL_DEBUGS("BVH") << "Created joint " << jointName << LL_ENDL;
LL_DEBUGS("BVH") << "- index " << mJoints.size()-1 << LL_ENDL;
S32 depth = 1;
for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
{
Joint *pjoint = mJoints[parent_joints[j]];
LL_DEBUGS("BVH") << "- ancestor " << pjoint->mName << LL_ENDL;
if (depth > pjoint->mChildTreeMaxDepth)
{
pjoint->mChildTreeMaxDepth = depth;
}
depth++;
}
//----------------------------------------------------------------
// get next line
//----------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
//----------------------------------------------------------------
// it must be {
//----------------------------------------------------------------
if ( !strstr(line.c_str(), "{") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_OFFSET;
}
else
{
parent_joints.push_back((S32)mJoints.size() - 1);
}
//----------------------------------------------------------------
// get next line
//----------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
//----------------------------------------------------------------
// it must be OFFSET
//----------------------------------------------------------------
if ( !strstr(line.c_str(), "OFFSET") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_OFFSET;
}
//----------------------------------------------------------------
// get next line
//----------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
//----------------------------------------------------------------
// it must be CHANNELS
//----------------------------------------------------------------
if ( !strstr(line.c_str(), "CHANNELS") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_CHANNELS;
}
//----------------------------------------------------------------
// add a set of keyframes for this joint
//----------------------------------------------------------------
mJoints.push_back( new Joint( jointName ) );
Joint *joint = mJoints.back();
LL_DEBUGS("BVH") << "Created joint " << jointName << LL_ENDL;
LL_DEBUGS("BVH") << "- index " << mJoints.size()-1 << LL_ENDL;
S32 depth = 1;
for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
{
Joint *pjoint = mJoints[parent_joints[j]];
LL_DEBUGS("BVH") << "- ancestor " << pjoint->mName << LL_ENDL;
if (depth > pjoint->mChildTreeMaxDepth)
{
pjoint->mChildTreeMaxDepth = depth;
}
depth++;
}
//----------------------------------------------------------------
// get next line
//----------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
//----------------------------------------------------------------
// it must be {
//----------------------------------------------------------------
if ( !strstr(line.c_str(), "{") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_OFFSET;
}
else
{
parent_joints.push_back((S32)mJoints.size() - 1);
}
//----------------------------------------------------------------
// get next line
//----------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
//----------------------------------------------------------------
// it must be OFFSET
//----------------------------------------------------------------
if ( !strstr(line.c_str(), "OFFSET") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_OFFSET;
}
//----------------------------------------------------------------
// get next line
//----------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
//----------------------------------------------------------------
// it must be CHANNELS
//----------------------------------------------------------------
if ( !strstr(line.c_str(), "CHANNELS") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_CHANNELS;
}
// Animating position (via mNumChannels = 6) is only supported for mPelvis.
int res = sscanf(line.c_str(), " CHANNELS %d", &joint->mNumChannels);
if ( res != 1 )
{
// Assume default if not otherwise specified.
if (mJoints.size()==1)
{
joint->mNumChannels = 6;
}
else
{
joint->mNumChannels = 3;
}
}
//----------------------------------------------------------------
// get rotation order
//----------------------------------------------------------------
const char *p = line.c_str();
for (S32 i=0; i<3; i++)
{
p = strstr(p, "rotation");
if (!p)
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_ROTATION;
}
const char axis = *(p - 1);
if ((axis != 'X') && (axis != 'Y') && (axis != 'Z'))
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_AXIS;
}
joint->mOrder[i] = axis;
p++;
}
}
//--------------------------------------------------------------------
// consume motion
//--------------------------------------------------------------------
if ( !strstr(line.c_str(), "MOTION") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_MOTION;
}
//--------------------------------------------------------------------
// get number of frames
//--------------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
if ( !strstr(line.c_str(), "Frames:") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_FRAMES;
}
if ( sscanf(line.c_str(), "Frames: %d", &mNumFrames) != 1 )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_FRAMES;
}
//--------------------------------------------------------------------
// get frame time
//--------------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
if ( !strstr(line.c_str(), "Frame Time:") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_FRAME_TIME;
}
if ( sscanf(line.c_str(), "Frame Time: %f", &mFrameTime) != 1 )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_FRAME_TIME;
}
// If the user only supplies one animation frame (after the ignored reference frame 0), hold for mFrameTime.
// If the user supples exactly one total frame, it isn't clear if that is a pose or reference frame, and the
// behavior is not defined. In this case, retain historical undefined behavior.
mDuration = llmax((F32)(mNumFrames - NUMBER_OF_UNPLAYED_FRAMES), 1.0f) * mFrameTime;
if (!mLoop)
{
mLoopOutPoint = mDuration;
}
//--------------------------------------------------------------------
// load frames
//--------------------------------------------------------------------
for (S32 i=0; i<mNumFrames; i++)
{
// get next line
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
// Split line into a collection of floats.
std::deque<F32> floats;
boost::char_separator<char> whitespace_sep("\t ");
tokenizer float_tokens(line, whitespace_sep);
tokenizer::iterator float_token_iter = float_tokens.begin();
while (float_token_iter != float_tokens.end())
{
int res = sscanf(line.c_str(), " CHANNELS %d", &joint->mNumChannels);
if ( res != 1 )
{
// Assume default if not otherwise specified.
if (mJoints.size()==1)
{
joint->mNumChannels = 6;
}
else
{
joint->mNumChannels = 3;
}
}
//----------------------------------------------------------------
// get rotation order
//----------------------------------------------------------------
const char *p = line.c_str();
for (S32 i=0; i<3; i++)
{
p = strstr(p, "rotation");
if (!p)
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_ROTATION;
}
const char axis = *(p - 1);
if ((axis != 'X') && (axis != 'Y') && (axis != 'Z'))
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_AXIS;
}
joint->mOrder[i] = axis;
p++;
}
}
//--------------------------------------------------------------------
// consume motion
//--------------------------------------------------------------------
if ( !strstr(line.c_str(), "MOTION") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_MOTION;
}
//--------------------------------------------------------------------
// get number of frames
//--------------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
if ( !strstr(line.c_str(), "Frames:") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_FRAMES;
}
if ( sscanf(line.c_str(), "Frames: %d", &mNumFrames) != 1 )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_FRAMES;
}
//--------------------------------------------------------------------
// get frame time
//--------------------------------------------------------------------
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
if ( !strstr(line.c_str(), "Frame Time:") )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_FRAME_TIME;
}
if ( sscanf(line.c_str(), "Frame Time: %f", &mFrameTime) != 1 )
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_FRAME_TIME;
}
// If the user only supplies one animation frame (after the ignored reference frame 0), hold for mFrameTime.
// If the user supples exactly one total frame, it isn't clear if that is a pose or reference frame, and the
// behavior is not defined. In this case, retain historical undefined behavior.
mDuration = llmax((F32)(mNumFrames - NUMBER_OF_UNPLAYED_FRAMES), 1.0f) * mFrameTime;
if (!mLoop)
{
mLoopOutPoint = mDuration;
}
//--------------------------------------------------------------------
// load frames
//--------------------------------------------------------------------
for (S32 i=0; i<mNumFrames; i++)
{
// get next line
if (iter == tokens.end())
{
return E_ST_EOF;
}
line = (*(iter++));
err_line++;
// Split line into a collection of floats.
std::deque<F32> floats;
boost::char_separator<char> whitespace_sep("\t ");
tokenizer float_tokens(line, whitespace_sep);
tokenizer::iterator float_token_iter = float_tokens.begin();
while (float_token_iter != float_tokens.end())
{
try
{
F32 val = boost::lexical_cast<float>(*float_token_iter);
......@@ -909,39 +906,39 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
}
catch (const boost::bad_lexical_cast&)
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_POS;
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_POS;
}
float_token_iter++;
}
LL_DEBUGS("BVH") << "Got " << floats.size() << " floats " << LL_ENDL;
for (U32 j=0; j<mJoints.size(); j++)
{
Joint *joint = mJoints[j];
joint->mKeys.push_back( Key() );
Key &key = joint->mKeys.back();
if (floats.size() < joint->mNumChannels)
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_POS;
}
// assume either numChannels == 6, in which case we have pos + rot,
// or numChannels == 3, in which case we have only rot.
if (joint->mNumChannels == 6)
{
key.mPos[0] = floats.front(); floats.pop_front();
key.mPos[1] = floats.front(); floats.pop_front();
key.mPos[2] = floats.front(); floats.pop_front();
}
key.mRot[ joint->mOrder[0]-'X' ] = floats.front(); floats.pop_front();
key.mRot[ joint->mOrder[1]-'X' ] = floats.front(); floats.pop_front();
key.mRot[ joint->mOrder[2]-'X' ] = floats.front(); floats.pop_front();
}
}
return E_ST_OK;
}
LL_DEBUGS("BVH") << "Got " << floats.size() << " floats " << LL_ENDL;
for (U32 j=0; j<mJoints.size(); j++)
{
Joint *joint = mJoints[j];
joint->mKeys.push_back( Key() );
Key &key = joint->mKeys.back();
if (floats.size() < joint->mNumChannels)
{
strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
return E_ST_NO_POS;
}
// assume either numChannels == 6, in which case we have pos + rot,
// or numChannels == 3, in which case we have only rot.
if (joint->mNumChannels == 6)
{
key.mPos[0] = floats.front(); floats.pop_front();
key.mPos[1] = floats.front(); floats.pop_front();
key.mPos[2] = floats.front(); floats.pop_front();
}
key.mRot[ joint->mOrder[0]-'X' ] = floats.front(); floats.pop_front();
key.mRot[ joint->mOrder[1]-'X' ] = floats.front(); floats.pop_front();
key.mRot[ joint->mOrder[2]-'X' ] = floats.front(); floats.pop_front();
}
}
return E_ST_OK;
}
......@@ -950,40 +947,38 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &
//------------------------------------------------------------------------
void LLBVHLoader::applyTranslations()
{
JointVector::iterator ji;
for (ji = mJoints.begin(); ji != mJoints.end(); ++ji )
{
Joint *joint = *ji;
//----------------------------------------------------------------
// Look for a translation for this joint.
// If none, skip to next joint
//----------------------------------------------------------------
TranslationMap::iterator ti = mTranslations.find( joint->mName );
if ( ti == mTranslations.end() )
{
continue;
}
Translation &trans = ti->second;
//----------------------------------------------------------------
// Set the ignore flag if necessary
//----------------------------------------------------------------
if ( trans.mIgnore )
{
for (Joint* joint : mJoints)
{
//----------------------------------------------------------------
// Look for a translation for this joint.
// If none, skip to next joint
//----------------------------------------------------------------
TranslationMap::iterator ti = mTranslations.find( joint->mName );
if ( ti == mTranslations.end() )
{
continue;
}
Translation &trans = ti->second;
//----------------------------------------------------------------
// Set the ignore flag if necessary
//----------------------------------------------------------------
if ( trans.mIgnore )
{
//LL_INFOS() << "NOTE: Ignoring " << joint->mName.c_str() << LL_ENDL;
joint->mIgnore = TRUE;
continue;
}
//----------------------------------------------------------------
// Set the output name
//----------------------------------------------------------------
if ( ! trans.mOutName.empty() )
{
//LL_INFOS() << "NOTE: Changing " << joint->mName.c_str() << " to " << trans.mOutName.c_str() << LL_ENDL;
joint->mOutName = trans.mOutName;
}
joint->mIgnore = TRUE;
continue;
}
//----------------------------------------------------------------
// Set the output name
//----------------------------------------------------------------
if ( ! trans.mOutName.empty() )
{
//LL_INFOS() << "NOTE: Changing " << joint->mName.c_str() << " to " << trans.mOutName.c_str() << LL_ENDL;
joint->mOutName = trans.mOutName;
}
//Allow joint position changes as of SL-318
joint->mIgnorePositions = FALSE;
......@@ -992,65 +987,65 @@ void LLBVHLoader::applyTranslations()
joint->mIgnorePositions = TRUE;
}
//----------------------------------------------------------------
// Set the relativepos flags if necessary
//----------------------------------------------------------------
if ( trans.mRelativePositionKey )
{
// LL_INFOS() << "NOTE: Removing 1st position offset from all keys for " << joint->mOutName.c_str() << LL_ENDL;
joint->mRelativePositionKey = TRUE;
}
if ( trans.mRelativeRotationKey )
{
// LL_INFOS() << "NOTE: Removing 1st rotation from all keys for " << joint->mOutName.c_str() << LL_ENDL;
joint->mRelativeRotationKey = TRUE;
}
if ( trans.mRelativePosition.magVec() > 0.0f )
{
joint->mRelativePosition = trans.mRelativePosition;
// LL_INFOS() << "NOTE: Removing " <<
// joint->mRelativePosition.mV[0] << " " <<
// joint->mRelativePosition.mV[1] << " " <<
// joint->mRelativePosition.mV[2] <<
// " from all position keys in " <<
// joint->mOutName.c_str() << LL_ENDL;
}
//----------------------------------------------------------------
// Set change of coordinate frame
//----------------------------------------------------------------
joint->mFrameMatrix = trans.mFrameMatrix;
joint->mOffsetMatrix = trans.mOffsetMatrix;
//----------------------------------------------------------------
// Set mergeparent name
//----------------------------------------------------------------
if ( ! trans.mMergeParentName.empty() )
{
// LL_INFOS() << "NOTE: Merging " << joint->mOutName.c_str() <<
// " with parent " <<
// trans.mMergeParentName.c_str() << LL_ENDL;
joint->mMergeParentName = trans.mMergeParentName;
}
//----------------------------------------------------------------
// Set mergechild name
//----------------------------------------------------------------
if ( ! trans.mMergeChildName.empty() )
{
// LL_INFOS() << "NOTE: Merging " << joint->mName.c_str() <<
// " with child " << trans.mMergeChildName.c_str() << LL_ENDL;
joint->mMergeChildName = trans.mMergeChildName;
}
//----------------------------------------------------------------
// Set joint priority
//----------------------------------------------------------------
joint->mPriority = mPriority + trans.mPriorityModifier;
}
//----------------------------------------------------------------
// Set the relativepos flags if necessary
//----------------------------------------------------------------
if ( trans.mRelativePositionKey )
{
// LL_INFOS() << "NOTE: Removing 1st position offset from all keys for " << joint->mOutName.c_str() << LL_ENDL;
joint->mRelativePositionKey = TRUE;
}
if ( trans.mRelativeRotationKey )
{
// LL_INFOS() << "NOTE: Removing 1st rotation from all keys for " << joint->mOutName.c_str() << LL_ENDL;
joint->mRelativeRotationKey = TRUE;
}
if ( trans.mRelativePosition.magVec() > 0.0f )
{
joint->mRelativePosition = trans.mRelativePosition;
// LL_INFOS() << "NOTE: Removing " <<
// joint->mRelativePosition.mV[0] << " " <<
// joint->mRelativePosition.mV[1] << " " <<
// joint->mRelativePosition.mV[2] <<
// " from all position keys in " <<
// joint->mOutName.c_str() << LL_ENDL;
}
//----------------------------------------------------------------
// Set change of coordinate frame
//----------------------------------------------------------------
joint->mFrameMatrix = trans.mFrameMatrix;
joint->mOffsetMatrix = trans.mOffsetMatrix;
//----------------------------------------------------------------
// Set mergeparent name
//----------------------------------------------------------------
if ( ! trans.mMergeParentName.empty() )
{
// LL_INFOS() << "NOTE: Merging " << joint->mOutName.c_str() <<
// " with parent " <<
// trans.mMergeParentName.c_str() << LL_ENDL;
joint->mMergeParentName = trans.mMergeParentName;
}
//----------------------------------------------------------------
// Set mergechild name
//----------------------------------------------------------------
if ( ! trans.mMergeChildName.empty() )
{
// LL_INFOS() << "NOTE: Merging " << joint->mName.c_str() <<
// " with child " << trans.mMergeChildName.c_str() << LL_ENDL;
joint->mMergeChildName = trans.mMergeChildName;
}
//----------------------------------------------------------------
// Set joint priority
//----------------------------------------------------------------
joint->mPriority = mPriority + trans.mPriorityModifier;
}
}
//-----------------------------------------------------------------------------
......@@ -1058,210 +1053,208 @@ void LLBVHLoader::applyTranslations()
//-----------------------------------------------------------------------------
void LLBVHLoader::optimize()
{
//RN: assumes motion blend, which is the default now
if (!mLoop && mEaseIn + mEaseOut > mDuration && mDuration != 0.f)
{
F32 factor = mDuration / (mEaseIn + mEaseOut);
mEaseIn *= factor;
mEaseOut *= factor;
}
JointVector::iterator ji;
for (ji = mJoints.begin(); ji != mJoints.end(); ++ji)
{
Joint *joint = *ji;
BOOL pos_changed = FALSE;
BOOL rot_changed = FALSE;
if ( ! joint->mIgnore )
{
joint->mNumPosKeys = 0;
joint->mNumRotKeys = 0;
LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
KeyVector::iterator first_key = joint->mKeys.begin();
// no keys?
if (first_key == joint->mKeys.end())
{
joint->mIgnore = TRUE;
continue;
}
LLVector3 first_frame_pos(first_key->mPos);
LLQuaternion first_frame_rot = mayaQ( first_key->mRot[0], first_key->mRot[1], first_key->mRot[2], order);
// skip first key
KeyVector::iterator ki = joint->mKeys.begin();
if (joint->mKeys.size() == 1)
{
// *FIX: use single frame to move pelvis
// if only one keyframe force output for this joint
rot_changed = TRUE;
}
else
{
// if more than one keyframe, use first frame as reference and skip to second
first_key->mIgnorePos = TRUE;
first_key->mIgnoreRot = TRUE;
++ki;
}
KeyVector::iterator ki_prev = ki;
KeyVector::iterator ki_last_good_pos = ki;
KeyVector::iterator ki_last_good_rot = ki;
S32 numPosFramesConsidered = 2;
S32 numRotFramesConsidered = 2;
F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)joint->mChildTreeMaxDepth * 0.33f, 1.f);
double diff_max = 0;
KeyVector::iterator ki_max = ki;
for (; ki != joint->mKeys.end(); ++ki)
{
if (ki_prev == ki_last_good_pos)
{
joint->mNumPosKeys++;
if (dist_vec_squared(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED)
{
pos_changed = TRUE;
}
}
else
{
//check position for noticeable effect
LLVector3 test_pos(ki_prev->mPos);
LLVector3 last_good_pos(ki_last_good_pos->mPos);
LLVector3 current_pos(ki->mPos);
LLVector3 interp_pos = lerp(current_pos, last_good_pos, 1.f / (F32)numPosFramesConsidered);
if (dist_vec_squared(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED)
{
pos_changed = TRUE;
}
if (dist_vec_squared(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD_SQUARED)
{
ki_prev->mIgnorePos = TRUE;
numPosFramesConsidered++;
}
else
{
numPosFramesConsidered = 2;
ki_last_good_pos = ki_prev;
joint->mNumPosKeys++;
}
}
if (ki_prev == ki_last_good_rot)
{
joint->mNumRotKeys++;
LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
F32 x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
F32 y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
F32 rot_test = x_delta + y_delta;
if (rot_test > ROTATION_MOTION_THRESHOLD)
{
rot_changed = TRUE;
}
}
else
{
//check rotation for noticeable effect
LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
LLQuaternion last_good_rot = mayaQ( ki_last_good_rot->mRot[0], ki_last_good_rot->mRot[1], ki_last_good_rot->mRot[2], order);
LLQuaternion current_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
LLQuaternion interp_rot = lerp(1.f / (F32)numRotFramesConsidered, current_rot, last_good_rot);
F32 x_delta;
F32 y_delta;
F32 rot_test;
// Test if the rotation has changed significantly since the very first frame. If false
// for all frames, then we'll just throw out this joint's rotation entirely.
x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
rot_test = x_delta + y_delta;
if (rot_test > ROTATION_MOTION_THRESHOLD)
{
rot_changed = TRUE;
}
x_delta = dist_vec(LLVector3::x_axis * interp_rot, LLVector3::x_axis * test_rot);
y_delta = dist_vec(LLVector3::y_axis * interp_rot, LLVector3::y_axis * test_rot);
rot_test = x_delta + y_delta;
// Draw a line between the last good keyframe and current. Test the distance between the last frame (current-1, i.e. ki_prev)
// and the line. If it's greater than some threshold, then it represents a significant frame and we want to include it.
if (rot_test >= rot_threshold ||
(ki+1 == joint->mKeys.end() && numRotFramesConsidered > 2))
{
// Add the current test keyframe (which is technically the previous key, i.e. ki_prev).
numRotFramesConsidered = 2;
ki_last_good_rot = ki_prev;
joint->mNumRotKeys++;
// Add another keyframe between the last good keyframe and current, at whatever point was the most "significant" (i.e.
// had the largest deviation from the earlier tests). Note that a more robust approach would be test all intermediate
// keyframes against the line between the last good keyframe and current, but we're settling for this other method
// because it's significantly faster.
if (diff_max > 0)
{
if (ki_max->mIgnoreRot == TRUE)
{
ki_max->mIgnoreRot = FALSE;
joint->mNumRotKeys++;
}
diff_max = 0;
}
}
else
{
// This keyframe isn't significant enough, throw it away.
ki_prev->mIgnoreRot = TRUE;
numRotFramesConsidered++;
// Store away the keyframe that has the largest deviation from the interpolated line, for insertion later.
if (rot_test > diff_max)
{
diff_max = rot_test;
ki_max = ki;
}
}
}
ki_prev = ki;
}
}
// don't output joints with no motion
if (!(pos_changed || rot_changed))
{
//LL_INFOS() << "Ignoring joint " << joint->mName << LL_ENDL;
joint->mIgnore = TRUE;
}
}
//RN: assumes motion blend, which is the default now
if (!mLoop && mEaseIn + mEaseOut > mDuration && mDuration != 0.f)
{
F32 factor = mDuration / (mEaseIn + mEaseOut);
mEaseIn *= factor;
mEaseOut *= factor;
}
for (Joint* joint : mJoints)
{
BOOL pos_changed = FALSE;
BOOL rot_changed = FALSE;
if ( ! joint->mIgnore )
{
joint->mNumPosKeys = 0;
joint->mNumRotKeys = 0;
LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
KeyVector::iterator first_key = joint->mKeys.begin();
// no keys?
if (first_key == joint->mKeys.end())
{
joint->mIgnore = TRUE;
continue;
}
LLVector3 first_frame_pos(first_key->mPos);
LLQuaternion first_frame_rot = mayaQ( first_key->mRot[0], first_key->mRot[1], first_key->mRot[2], order);
// skip first key
KeyVector::iterator ki = joint->mKeys.begin();
if (joint->mKeys.size() == 1)
{
// *FIX: use single frame to move pelvis
// if only one keyframe force output for this joint
rot_changed = TRUE;
}
else
{
// if more than one keyframe, use first frame as reference and skip to second
first_key->mIgnorePos = TRUE;
first_key->mIgnoreRot = TRUE;
++ki;
}
KeyVector::iterator ki_prev = ki;
KeyVector::iterator ki_last_good_pos = ki;
KeyVector::iterator ki_last_good_rot = ki;
S32 numPosFramesConsidered = 2;
S32 numRotFramesConsidered = 2;
F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)joint->mChildTreeMaxDepth * 0.33f, 1.f);
double diff_max = 0;
KeyVector::iterator ki_max = ki;
for (; ki != joint->mKeys.end(); ++ki)
{
if (ki_prev == ki_last_good_pos)
{
joint->mNumPosKeys++;
if (dist_vec_squared(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED)
{
pos_changed = TRUE;
}
}
else
{
//check position for noticeable effect
LLVector3 test_pos(ki_prev->mPos);
LLVector3 last_good_pos(ki_last_good_pos->mPos);
LLVector3 current_pos(ki->mPos);
LLVector3 interp_pos = lerp(current_pos, last_good_pos, 1.f / (F32)numPosFramesConsidered);
if (dist_vec_squared(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD_SQUARED)
{
pos_changed = TRUE;
}
if (dist_vec_squared(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD_SQUARED)
{
ki_prev->mIgnorePos = TRUE;
numPosFramesConsidered++;
}
else
{
numPosFramesConsidered = 2;
ki_last_good_pos = ki_prev;
joint->mNumPosKeys++;
}
}
if (ki_prev == ki_last_good_rot)
{
joint->mNumRotKeys++;
LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
F32 x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
F32 y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
F32 rot_test = x_delta + y_delta;
if (rot_test > ROTATION_MOTION_THRESHOLD)
{
rot_changed = TRUE;
}
}
else
{
//check rotation for noticeable effect
LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
LLQuaternion last_good_rot = mayaQ( ki_last_good_rot->mRot[0], ki_last_good_rot->mRot[1], ki_last_good_rot->mRot[2], order);
LLQuaternion current_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
LLQuaternion interp_rot = lerp(1.f / (F32)numRotFramesConsidered, current_rot, last_good_rot);
F32 x_delta;
F32 y_delta;
F32 rot_test;
// Test if the rotation has changed significantly since the very first frame. If false
// for all frames, then we'll just throw out this joint's rotation entirely.
x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
rot_test = x_delta + y_delta;
if (rot_test > ROTATION_MOTION_THRESHOLD)
{
rot_changed = TRUE;
}
x_delta = dist_vec(LLVector3::x_axis * interp_rot, LLVector3::x_axis * test_rot);
y_delta = dist_vec(LLVector3::y_axis * interp_rot, LLVector3::y_axis * test_rot);
rot_test = x_delta + y_delta;
// Draw a line between the last good keyframe and current. Test the distance between the last frame (current-1, i.e. ki_prev)
// and the line. If it's greater than some threshold, then it represents a significant frame and we want to include it.
if (rot_test >= rot_threshold ||
(ki+1 == joint->mKeys.end() && numRotFramesConsidered > 2))
{
// Add the current test keyframe (which is technically the previous key, i.e. ki_prev).
numRotFramesConsidered = 2;
ki_last_good_rot = ki_prev;
joint->mNumRotKeys++;
// Add another keyframe between the last good keyframe and current, at whatever point was the most "significant" (i.e.
// had the largest deviation from the earlier tests). Note that a more robust approach would be test all intermediate
// keyframes against the line between the last good keyframe and current, but we're settling for this other method
// because it's significantly faster.
if (diff_max > 0)
{
if (ki_max->mIgnoreRot == TRUE)
{
ki_max->mIgnoreRot = FALSE;
joint->mNumRotKeys++;
}
diff_max = 0;
}
}
else
{
// This keyframe isn't significant enough, throw it away.
ki_prev->mIgnoreRot = TRUE;
numRotFramesConsidered++;
// Store away the keyframe that has the largest deviation from the interpolated line, for insertion later.
if (rot_test > diff_max)
{
diff_max = rot_test;
ki_max = ki;
}
}
}
ki_prev = ki;
}
}
// don't output joints with no motion
if (!(pos_changed || rot_changed))
{
//LL_INFOS() << "Ignoring joint " << joint->mName << LL_ENDL;
joint->mIgnore = TRUE;
}
}
}
void LLBVHLoader::reset()
{
mLineNumber = 0;
mNumFrames = 0;
mFrameTime = 0.0f;
mDuration = 0.0f;
mPriority = 2;
mLoop = FALSE;
mLoopInPoint = 0.f;
mLoopOutPoint = 0.f;
mEaseIn = 0.3f;
mEaseOut = 0.3f;
mHand = 1;
mInitialized = FALSE;
mEmoteName = "";
mLineNumber = 0;
mTranslations.clear();
mConstraints.clear();
mLineNumber = 0;
mNumFrames = 0;
mFrameTime = 0.0f;
mDuration = 0.0f;
mPriority = 2;
mLoop = FALSE;
mLoopInPoint = 0.f;
mLoopOutPoint = 0.f;
mEaseIn = 0.3f;
mEaseOut = 0.3f;
mHand = 1;
mInitialized = FALSE;
mEmoteName = "";
mLineNumber = 0;
mTranslations.clear();
mConstraints.clear();
}
//------------------------------------------------------------------------
......@@ -1269,253 +1262,237 @@ void LLBVHLoader::reset()
//------------------------------------------------------------------------
BOOL LLBVHLoader::getLine(apr_file_t* fp)
{
if (apr_file_eof(fp) == APR_EOF)
{
return FALSE;
}
if ( apr_file_gets(mLine, BVH_PARSER_LINE_SIZE, fp) == APR_SUCCESS)
{
mLineNumber++;
return TRUE;
}
return FALSE;
if (apr_file_eof(fp) == APR_EOF)
{
return FALSE;
}
if ( apr_file_gets(mLine, BVH_PARSER_LINE_SIZE, fp) == APR_SUCCESS)
{
mLineNumber++;
return TRUE;
}
return FALSE;
}
// returns required size of output buffer
U32 LLBVHLoader::getOutputSize()
{
LLDataPackerBinaryBuffer dp;
serialize(dp);
LLDataPackerBinaryBuffer dp;
serialize(dp);
return dp.getCurrentSize();
return dp.getCurrentSize();
}
// writes contents to datapacker
BOOL LLBVHLoader::serialize(LLDataPacker& dp)
{
JointVector::iterator ji;
KeyVector::iterator ki;
F32 time;
// count number of non-ignored joints
S32 numJoints = 0;
for (ji=mJoints.begin(); ji!=mJoints.end(); ++ji)
{
Joint *joint = *ji;
if ( ! joint->mIgnore )
numJoints++;
}
// print header
dp.packU16(KEYFRAME_MOTION_VERSION, "version");
dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version");
dp.packS32(mPriority, "base_priority");
dp.packF32(mDuration, "duration");
dp.packString(mEmoteName, "emote_name");
dp.packF32(mLoopInPoint, "loop_in_point");
dp.packF32(mLoopOutPoint, "loop_out_point");
dp.packS32(mLoop, "loop");
dp.packF32(mEaseIn, "ease_in_duration");
dp.packF32(mEaseOut, "ease_out_duration");
dp.packU32(mHand, "hand_pose");
dp.packU32(numJoints, "num_joints");
for ( ji = mJoints.begin();
ji != mJoints.end();
++ji )
{
Joint *joint = *ji;
// if ignored, skip it
if ( joint->mIgnore )
continue;
LLQuaternion first_frame_rot;
LLQuaternion fixup_rot;
dp.packString(joint->mOutName, "joint_name");
dp.packS32(joint->mPriority, "joint_priority");
// compute coordinate frame rotation
LLQuaternion frameRot( joint->mFrameMatrix );
LLQuaternion frameRotInv = ~frameRot;
LLQuaternion offsetRot( joint->mOffsetMatrix );
// find mergechild and mergeparent joints, if specified
LLQuaternion mergeParentRot;
LLQuaternion mergeChildRot;
Joint *mergeParent = NULL;
Joint *mergeChild = NULL;
JointVector::iterator mji;
for (mji=mJoints.begin(); mji!=mJoints.end(); ++mji)
{
Joint *mjoint = *mji;
if ( !joint->mMergeParentName.empty() && (mjoint->mName == joint->mMergeParentName) )
{
mergeParent = *mji;
}
if ( !joint->mMergeChildName.empty() && (mjoint->mName == joint->mMergeChildName) )
{
mergeChild = *mji;
}
}
dp.packS32(joint->mNumRotKeys, "num_rot_keys");
LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
S32 outcount = 0;
S32 frame = 0;
for ( ki = joint->mKeys.begin();
ki != joint->mKeys.end();
++ki )
{
if ((frame == 0) && joint->mRelativeRotationKey)
{
first_frame_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
fixup_rot.shortestArc(LLVector3::z_axis * first_frame_rot * frameRot, LLVector3::z_axis);
}
if (ki->mIgnoreRot)
{
frame++;
continue;
}
time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts.
if (mergeParent)
{
mergeParentRot = mayaQ( mergeParent->mKeys[frame-1].mRot[0],
mergeParent->mKeys[frame-1].mRot[1],
mergeParent->mKeys[frame-1].mRot[2],
bvhStringToOrder(mergeParent->mOrder) );
LLQuaternion parentFrameRot( mergeParent->mFrameMatrix );
LLQuaternion parentOffsetRot( mergeParent->mOffsetMatrix );
mergeParentRot = ~parentFrameRot * mergeParentRot * parentFrameRot * parentOffsetRot;
}
else
{
mergeParentRot.loadIdentity();
}
if (mergeChild)
{
mergeChildRot = mayaQ( mergeChild->mKeys[frame-1].mRot[0],
mergeChild->mKeys[frame-1].mRot[1],
mergeChild->mKeys[frame-1].mRot[2],
bvhStringToOrder(mergeChild->mOrder) );
LLQuaternion childFrameRot( mergeChild->mFrameMatrix );
LLQuaternion childOffsetRot( mergeChild->mOffsetMatrix );
mergeChildRot = ~childFrameRot * mergeChildRot * childFrameRot * childOffsetRot;
}
else
{
mergeChildRot.loadIdentity();
}
LLQuaternion inRot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
LLQuaternion outRot = frameRotInv* mergeChildRot * inRot * mergeParentRot * ~first_frame_rot * frameRot * offsetRot;
U16 time_short = F32_to_U16(time, 0.f, mDuration);
dp.packU16(time_short, "time");
U16 x, y, z;
LLVector3 rot_vec = outRot.packToVector3();
rot_vec.quantize16(-1.f, 1.f, -1.f, 1.f);
x = F32_to_U16(rot_vec.mV[VX], -1.f, 1.f);
y = F32_to_U16(rot_vec.mV[VY], -1.f, 1.f);
z = F32_to_U16(rot_vec.mV[VZ], -1.f, 1.f);
dp.packU16(x, "rot_angle_x");
dp.packU16(y, "rot_angle_y");
dp.packU16(z, "rot_angle_z");
outcount++;
frame++;
}
// output position keys if joint has motion.
F32 time;
// count number of non-ignored joints
S32 numJoints = 0;
for (Joint* joint : mJoints)
{
if ( ! joint->mIgnore )
numJoints++;
}
// print header
dp.packU16(KEYFRAME_MOTION_VERSION, "version");
dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version");
dp.packS32(mPriority, "base_priority");
dp.packF32(mDuration, "duration");
dp.packString(mEmoteName, "emote_name");
dp.packF32(mLoopInPoint, "loop_in_point");
dp.packF32(mLoopOutPoint, "loop_out_point");
dp.packS32(mLoop, "loop");
dp.packF32(mEaseIn, "ease_in_duration");
dp.packF32(mEaseOut, "ease_out_duration");
dp.packU32(mHand, "hand_pose");
dp.packU32(numJoints, "num_joints");
for (Joint* joint : mJoints)
{
// if ignored, skip it
if ( joint->mIgnore )
continue;
LLQuaternion first_frame_rot;
LLQuaternion fixup_rot;
dp.packString(joint->mOutName, "joint_name");
dp.packS32(joint->mPriority, "joint_priority");
// compute coordinate frame rotation
LLQuaternion frameRot( joint->mFrameMatrix );
LLQuaternion frameRotInv = ~frameRot;
LLQuaternion offsetRot( joint->mOffsetMatrix );
// find mergechild and mergeparent joints, if specified
LLQuaternion mergeParentRot;
LLQuaternion mergeChildRot;
Joint *mergeParent = NULL;
Joint *mergeChild = NULL;
for (Joint* mjoint : mJoints)
{
if ( !joint->mMergeParentName.empty() && (mjoint->mName == joint->mMergeParentName) )
{
mergeParent = mjoint;
}
if ( !joint->mMergeChildName.empty() && (mjoint->mName == joint->mMergeChildName) )
{
mergeChild = mjoint;
}
}
dp.packS32(joint->mNumRotKeys, "num_rot_keys");
LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
S32 frame = 0;
for (Key& key : joint->mKeys)
{
if ((frame == 0) && joint->mRelativeRotationKey)
{
first_frame_rot = mayaQ( key.mRot[0], key.mRot[1], key.mRot[2], order);
fixup_rot.shortestArc(LLVector3::z_axis * first_frame_rot * frameRot, LLVector3::z_axis);
}
if (key.mIgnoreRot)
{
frame++;
continue;
}
time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts.
if (mergeParent)
{
mergeParentRot = mayaQ( mergeParent->mKeys[frame-1].mRot[0],
mergeParent->mKeys[frame-1].mRot[1],
mergeParent->mKeys[frame-1].mRot[2],
bvhStringToOrder(mergeParent->mOrder) );
LLQuaternion parentFrameRot( mergeParent->mFrameMatrix );
LLQuaternion parentOffsetRot( mergeParent->mOffsetMatrix );
mergeParentRot = ~parentFrameRot * mergeParentRot * parentFrameRot * parentOffsetRot;
}
else
{
mergeParentRot.loadIdentity();
}
if (mergeChild)
{
mergeChildRot = mayaQ( mergeChild->mKeys[frame-1].mRot[0],
mergeChild->mKeys[frame-1].mRot[1],
mergeChild->mKeys[frame-1].mRot[2],
bvhStringToOrder(mergeChild->mOrder) );
LLQuaternion childFrameRot( mergeChild->mFrameMatrix );
LLQuaternion childOffsetRot( mergeChild->mOffsetMatrix );
mergeChildRot = ~childFrameRot * mergeChildRot * childFrameRot * childOffsetRot;
}
else
{
mergeChildRot.loadIdentity();
}
LLQuaternion inRot = mayaQ( key.mRot[0], key.mRot[1], key.mRot[2], order);
LLQuaternion outRot = frameRotInv* mergeChildRot * inRot * mergeParentRot * ~first_frame_rot * frameRot * offsetRot;
U16 time_short = F32_to_U16(time, 0.f, mDuration);
dp.packU16(time_short, "time");
U16 x, y, z;
LLVector3 rot_vec = outRot.packToVector3();
rot_vec.quantize16(-1.f, 1.f, -1.f, 1.f);
x = F32_to_U16(rot_vec.mV[VX], -1.f, 1.f);
y = F32_to_U16(rot_vec.mV[VY], -1.f, 1.f);
z = F32_to_U16(rot_vec.mV[VZ], -1.f, 1.f);
dp.packU16(x, "rot_angle_x");
dp.packU16(y, "rot_angle_y");
dp.packU16(z, "rot_angle_z");
frame++;
}
// output position keys if joint has motion.
if ( !joint->mIgnorePositions )
{
dp.packS32(joint->mNumPosKeys, "num_pos_keys");
{
dp.packS32(joint->mNumPosKeys, "num_pos_keys");
LLVector3 relPos = joint->mRelativePosition;
LLVector3 relKey;
LLVector3 relPos = joint->mRelativePosition;
LLVector3 relKey;
frame = 0;
for ( ki = joint->mKeys.begin();
ki != joint->mKeys.end();
++ki )
{
if ((frame == 0) && joint->mRelativePositionKey)
{
relKey.setVec(ki->mPos);
}
frame = 0;
for (Key& key : joint->mKeys)
{
if ((frame == 0) && joint->mRelativePositionKey)
{
relKey.setVec(key.mPos);
}
if (ki->mIgnorePos)
{
frame++;
continue;
}
if (key.mIgnorePos)
{
frame++;
continue;
}
time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts.
time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts.
LLVector3 inPos = (LLVector3(ki->mPos) - relKey) * ~first_frame_rot;// * fixup_rot;
LLVector3 outPos = inPos * frameRot * offsetRot;
LLVector3 inPos = (LLVector3(key.mPos) - relKey) * ~first_frame_rot;// * fixup_rot;
LLVector3 outPos = inPos * frameRot * offsetRot;
outPos *= INCHES_TO_METERS;
outPos *= INCHES_TO_METERS;
//SL-318 Pelvis position can only move 5m. Limiting all joint position offsets to this dist.
outPos -= relPos;
outPos.clamp(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
U16 time_short = F32_to_U16(time, 0.f, mDuration);
dp.packU16(time_short, "time");
U16 x, y, z;
outPos.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
x = F32_to_U16(outPos.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
y = F32_to_U16(outPos.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
z = F32_to_U16(outPos.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
dp.packU16(x, "pos_x");
dp.packU16(y, "pos_y");
dp.packU16(z, "pos_z");
frame++;
}
}
else
{
dp.packS32(0, "num_pos_keys");
}
}
S32 num_constraints = (S32)mConstraints.size();
dp.packS32(num_constraints, "num_constraints");
for (ConstraintVector::iterator constraint_it = mConstraints.begin();
constraint_it != mConstraints.end();
constraint_it++)
{
U8 byte = constraint_it->mChainLength;
dp.packU8(byte, "chain_length");
byte = constraint_it->mConstraintType;
dp.packU8(byte, "constraint_type");
dp.packBinaryDataFixed((U8*)constraint_it->mSourceJointName, 16, "source_volume");
dp.packVector3(constraint_it->mSourceOffset, "source_offset");
dp.packBinaryDataFixed((U8*)constraint_it->mTargetJointName, 16, "target_volume");
dp.packVector3(constraint_it->mTargetOffset, "target_offset");
dp.packVector3(constraint_it->mTargetDir, "target_dir");
dp.packF32(constraint_it->mEaseInStart, "ease_in_start");
dp.packF32(constraint_it->mEaseInStop, "ease_in_stop");
dp.packF32(constraint_it->mEaseOutStart, "ease_out_start");
dp.packF32(constraint_it->mEaseOutStop, "ease_out_stop");
}
return TRUE;
outPos -= relPos;
outPos.clamp(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
U16 time_short = F32_to_U16(time, 0.f, mDuration);
dp.packU16(time_short, "time");
U16 x, y, z;
outPos.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
x = F32_to_U16(outPos.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
y = F32_to_U16(outPos.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
z = F32_to_U16(outPos.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
dp.packU16(x, "pos_x");
dp.packU16(y, "pos_y");
dp.packU16(z, "pos_z");
frame++;
}
}
else
{
dp.packS32(0, "num_pos_keys");
}
}
S32 num_constraints = (S32)mConstraints.size();
dp.packS32(num_constraints, "num_constraints");
for (Constraint& constraint : mConstraints)
{
U8 byte = constraint.mChainLength;
dp.packU8(byte, "chain_length");
byte = constraint.mConstraintType;
dp.packU8(byte, "constraint_type");
dp.packBinaryDataFixed((U8*)constraint.mSourceJointName, 16, "source_volume");
dp.packVector3(constraint.mSourceOffset, "source_offset");
dp.packBinaryDataFixed((U8*)constraint.mTargetJointName, 16, "target_volume");
dp.packVector3(constraint.mTargetOffset, "target_offset");
dp.packVector3(constraint.mTargetDir, "target_dir");
dp.packF32(constraint.mEaseInStart, "ease_in_start");
dp.packF32(constraint.mEaseInStop, "ease_in_stop");
dp.packF32(constraint.mEaseOutStart, "ease_out_start");
dp.packF32(constraint.mEaseOutStop, "ease_out_stop");
}
return TRUE;
}