diff --git a/source/common/audio/sound/s_sound.cpp b/source/common/audio/sound/s_sound.cpp index f73404f80..a9f12d8d2 100644 --- a/source/common/audio/sound/s_sound.cpp +++ b/source/common/audio/sound/s_sound.cpp @@ -89,7 +89,7 @@ void SoundEngine::Clear() { StopAllChannels(); UnloadAllSounds(); - GetSounds().Clear(); + S_sfx.Clear(); ClearRandoms(); } @@ -119,11 +119,11 @@ void SoundEngine::Shutdown () // //========================================================================== -void SoundEngine::MarkUsed(int id) +void SoundEngine::MarkUsed(FSoundID sid) { - if ((unsigned)id < S_sfx.Size()) + if (isValidSoundId(sid)) { - S_sfx[id].bUsed = true; + S_sfx[sid.index()].bUsed = true; } } @@ -167,9 +167,9 @@ void SoundEngine::CacheSound (sfxinfo_t *sfx) { if (GSnd && !sfx->bTentative) { - while (!sfx->bRandomHeader && sfx->link != sfxinfo_t::NO_LINK) + while (!sfx->bRandomHeader && isValidSoundId(sfx->link)) { - sfx = &S_sfx[sfx->link]; + sfx = &S_sfx[sfx->link.index()]; } if (sfx->bRandomHeader) { @@ -315,7 +315,7 @@ FString SoundEngine::ListSoundChannels() CalcPosVel(chan, &chanorigin, nullptr); - output.AppendFormat("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)S_sfx[chan->SoundID].name.GetChars(), chanorigin.X, chanorigin.Y, chanorigin.Z); + output.AppendFormat("%s at (%1.5f, %1.5f, %1.5f)\n", (const char*)S_sfx[chan->SoundID.index()].name.GetChars(), chanorigin.X, chanorigin.Y, chanorigin.Z); count++; } } @@ -354,7 +354,7 @@ bool SoundEngine::ValidatePosVel(const FSoundChan* const chan, const FVector3& p FSoundID SoundEngine::ResolveSound(const void *, int, FSoundID soundid, float &attenuation) { - const sfxinfo_t &sfx = S_sfx[soundid]; + const sfxinfo_t &sfx = S_sfx[soundid.index()]; if (sfx.bRandomHeader) { @@ -385,13 +385,13 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source, sfxinfo_t *sfx; EChanFlags chanflags = flags; int basepriority; - int org_id; + FSoundID org_id; int pitch; FSoundChan *chan; FVector3 pos, vel; FRolloffInfo *rolloff; - if (sound_id <= 0 || volume <= 0 || nosfx || !SoundEnabled() || blockNewSounds || (unsigned)sound_id >= S_sfx.Size()) + if (!isValidSoundId(sound_id) || volume <= 0 || nosfx || !SoundEnabled() || blockNewSounds) return NULL; // prevent crashes. @@ -406,7 +406,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source, return nullptr; } - sfx = &S_sfx[sound_id]; + sfx = &S_sfx[sound_id.index()]; // Scale volume according to SNDINFO data. volume = min(volume * sfx->Volume, 1.f); @@ -426,8 +426,8 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source, while (sfx->link != sfxinfo_t::NO_LINK) { sound_id = ResolveSound(source, type, sound_id, attenuation); - if (sound_id < 0) return nullptr; - auto newsfx = &S_sfx[sound_id]; + if (!isValidSoundId(sound_id)) return nullptr; + auto newsfx = &S_sfx[sound_id.index()]; if (newsfx != sfx) { if (near_limit < 0) @@ -595,7 +595,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source, if (chan != NULL) { chan->SoundID = sound_id; - chan->OrgID = FSoundID(org_id); + chan->OrgID = org_id; chan->EntChannel = channel; chan->Volume = float(volume); chan->ChanFlags |= chanflags; @@ -653,7 +653,7 @@ void SoundEngine::RestartChannel(FSoundChan *chan) assert(chan->ChanFlags & CHANF_EVICTED); FSoundChan *ochan; - sfxinfo_t *sfx = &S_sfx[chan->SoundID]; + sfxinfo_t *sfx = &S_sfx[chan->SoundID.index()]; // If this is a singular sound, don't play it if it's already playing. if (sfx->bSingular && CheckSingular(chan->SoundID)) @@ -688,7 +688,7 @@ void SoundEngine::RestartChannel(FSoundChan *chan) // If this sound doesn't like playing near itself, don't play it if // that's what would happen. - if (chan->NearLimit > 0 && CheckSoundLimit(&S_sfx[chan->SoundID], pos, chan->NearLimit, chan->LimitRange, 0, NULL, 0, chan->DistanceScale)) + if (chan->NearLimit > 0 && CheckSoundLimit(&S_sfx[chan->SoundID.index()], pos, chan->NearLimit, chan->LimitRange, 0, NULL, 0, chan->DistanceScale)) { return; } @@ -738,7 +738,7 @@ sfxinfo_t *SoundEngine::LoadSound(sfxinfo_t *sfx) (!sfx->bLoadRAW || (sfx->RawRate == S_sfx[i].RawRate))) // Raw sounds with different sample rates may not share buffers, even if they use the same source data. { DPrintf (DMSG_NOTIFY, "Linked %s to %s (%d)\n", sfx->name.GetChars(), S_sfx[i].name.GetChars(), i); - sfx->link = i; + sfx->link = FSoundID::fromInt(i); // This is necessary to avoid using the rolloff settings of the linked sound if its // settings are different. if (sfx->Rolloff.MinDistance == 0) sfx->Rolloff = S_Rolloff; @@ -799,7 +799,7 @@ sfxinfo_t *SoundEngine::LoadSound(sfxinfo_t *sfx) // //========================================================================== -bool SoundEngine::CheckSingular(int sound_id) +bool SoundEngine::CheckSingular(FSoundID sound_id) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -837,7 +837,7 @@ bool SoundEngine::CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_ for (chan = Channels, count = 0; chan != NULL && count < near_limit; chan = chan->NextChan) { if (chan->ChanFlags & CHANF_FORGETTABLE) continue; - if (!(chan->ChanFlags & CHANF_EVICTED) && &S_sfx[chan->SoundID] == sfx) + if (!(chan->ChanFlags & CHANF_EVICTED) && &S_sfx[chan->SoundID.index()] == sfx) { FVector3 chanorigin; @@ -867,7 +867,7 @@ bool SoundEngine::CheckSoundLimit(sfxinfo_t *sfx, const FVector3 &pos, int near_ // //========================================================================== -void SoundEngine::StopSoundID(int sound_id) +void SoundEngine::StopSoundID(FSoundID sound_id) { FSoundChan* chan = Channels; while (chan != NULL) @@ -889,13 +889,13 @@ void SoundEngine::StopSoundID(int sound_id) // //========================================================================== -void SoundEngine::StopSound (int channel, int sound_id) +void SoundEngine::StopSound (int channel, FSoundID sound_id) { FSoundChan *chan = Channels; while (chan != NULL) { FSoundChan *next = chan->NextChan; - if ((chan->SourceType == SOURCE_None && (sound_id == -1 || sound_id == chan->OrgID)) && (channel == CHAN_AUTO || channel == chan->EntChannel)) + if ((chan->SourceType == SOURCE_None && (sound_id == INVALID_SOUND || sound_id == chan->OrgID)) && (channel == CHAN_AUTO || channel == chan->EntChannel)) { StopChannel(chan); } @@ -911,7 +911,7 @@ void SoundEngine::StopSound (int channel, int sound_id) // //========================================================================== -void SoundEngine::StopSound(int sourcetype, const void* actor, int channel, int sound_id) +void SoundEngine::StopSound(int sourcetype, const void* actor, int channel, FSoundID sound_id) { FSoundChan* chan = Channels; while (chan != NULL) @@ -919,7 +919,7 @@ void SoundEngine::StopSound(int sourcetype, const void* actor, int channel, int FSoundChan* next = chan->NextChan; if (chan->SourceType == sourcetype && chan->Source == actor && - (sound_id == -1? (chan->EntChannel == channel || channel < 0) : (chan->OrgID == sound_id))) + (sound_id == INVALID_SOUND? (chan->EntChannel == channel || channel < 0) : (chan->OrgID == sound_id))) { StopChannel(chan); } @@ -1059,13 +1059,13 @@ void SoundEngine::SetVolume(FSoundChan* chan, float volume) // //========================================================================== -void SoundEngine::ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch, int sound_id) +void SoundEngine::ChangeSoundPitch(int sourcetype, const void *source, int channel, double pitch, FSoundID sound_id) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { if (chan->SourceType == sourcetype && chan->Source == source && - (sound_id == -1? (chan->EntChannel == channel) : (chan->OrgID == sound_id))) + (sound_id == INVALID_SOUND? (chan->EntChannel == channel) : (chan->OrgID == sound_id))) { SetPitch(chan, (float)pitch); } @@ -1087,10 +1087,10 @@ void SoundEngine::SetPitch(FSoundChan *chan, float pitch) // Is a sound being played by a specific emitter? //========================================================================== -int SoundEngine::GetSoundPlayingInfo (int sourcetype, const void *source, int sound_id, int chann) +int SoundEngine::GetSoundPlayingInfo (int sourcetype, const void *source, FSoundID sound_id, int chann) { int count = 0; - if (sound_id > 0) + if (sound_id.isvalid()) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { @@ -1153,13 +1153,13 @@ bool SoundEngine::IsChannelUsed(int sourcetype, const void *actor, int channel, // //========================================================================== -bool SoundEngine::IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, int sound_id) +bool SoundEngine::IsSourcePlayingSomething (int sourcetype, const void *actor, int channel, FSoundID sound_id) { for (FSoundChan *chan = Channels; chan != NULL; chan = chan->NextChan) { if (chan->SourceType == sourcetype && (sourcetype == SOURCE_None || sourcetype == SOURCE_Unattached || chan->Source == actor)) { - if ((channel == 0 || chan->EntChannel == channel) && (sound_id <= 0 || chan->OrgID == sound_id)) + if ((channel == 0 || chan->EntChannel == channel) && (sound_id == INVALID_SOUND || chan->OrgID == sound_id)) { return true; } @@ -1355,7 +1355,7 @@ void SoundEngine::ChannelEnded(FISoundChannel *ichan) else { unsigned int pos = GSnd->GetPosition(schan); - unsigned int len = GSnd->GetSampleLength(S_sfx[schan->SoundID].data); + unsigned int len = GSnd->GetSampleLength(S_sfx[schan->SoundID.index()].data); if (pos == 0) { evicted = !!(schan->ChanFlags & CHANF_JUSTSTARTED); @@ -1463,7 +1463,7 @@ void SoundEngine::Reset() // Given a logical name, find the sound's index in S_sfx. //========================================================================== -int SoundEngine::FindSound(const char* logicalname) +FSoundID SoundEngine::FindSound(const char* logicalname) { int i; @@ -1474,18 +1474,18 @@ int SoundEngine::FindSound(const char* logicalname) while ((i != 0) && stricmp(S_sfx[i].name, logicalname)) i = S_sfx[i].next; - return i; + return FSoundID::fromInt(i); } else { - return 0; + return NO_SOUND; } } -int SoundEngine::FindSoundByResID(int resid) +FSoundID SoundEngine::FindSoundByResID(int resid) { auto p = ResIdMap.CheckKey(resid); - return p ? *p : 0; + return p ? *p : NO_SOUND; } //========================================================================== @@ -1496,7 +1496,7 @@ int SoundEngine::FindSoundByResID(int resid) // using the hash table. //========================================================================== -int SoundEngine::FindSoundNoHash(const char* logicalname) +FSoundID SoundEngine::FindSoundNoHash(const char* logicalname) { unsigned int i; @@ -1504,10 +1504,10 @@ int SoundEngine::FindSoundNoHash(const char* logicalname) { if (stricmp(S_sfx[i].name, logicalname) == 0) { - return i; + return FSoundID::fromInt(i); } } - return 0; + return NO_SOUND; } //========================================================================== @@ -1517,7 +1517,7 @@ int SoundEngine::FindSoundNoHash(const char* logicalname) // Given a sound lump, find the sound's index in S_sfx. //========================================================================== -int SoundEngine::FindSoundByLump(int lump) +FSoundID SoundEngine::FindSoundByLump(int lump) { if (lump != -1) { @@ -1525,9 +1525,9 @@ int SoundEngine::FindSoundByLump(int lump) for (i = 1; i < S_sfx.Size(); i++) if (S_sfx[i].lumpnum == lump) - return i; + return FSoundID::fromInt(i); } - return 0; + return NO_SOUND; } //========================================================================== @@ -1537,7 +1537,7 @@ int SoundEngine::FindSoundByLump(int lump) // Adds a new sound mapping to S_sfx. //========================================================================== -int SoundEngine::AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid, int nearlimit) +FSoundID SoundEngine::AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid, int nearlimit) { S_sfx.Reserve(1); sfxinfo_t &newsfx = S_sfx.Last(); @@ -1549,9 +1549,10 @@ int SoundEngine::AddSoundLump(const char* logicalname, int lump, int CurrentPitc newsfx.NearLimit = nearlimit; newsfx.ResourceId = resid; newsfx.bTentative = false; + auto id = FSoundID::fromInt(S_sfx.Size() - 1); - if (resid >= 0) ResIdMap[resid] = S_sfx.Size() - 1; - return (int)S_sfx.Size()-1; + if (resid >= 0) ResIdMap[resid] = id; + return id; } @@ -1564,13 +1565,13 @@ int SoundEngine::AddSoundLump(const char* logicalname, int lump, int CurrentPitc // an associated lump is created. //========================================================================== -int SoundEngine::FindSoundTentative(const char* name) +FSoundID SoundEngine::FindSoundTentative(const char* name) { - int id = FindSoundNoHash(name); - if (id == 0) + auto id = FindSoundNoHash(name); + if (id == NO_SOUND) { id = AddSoundLump(name, -1, 0); - S_sfx[id].bTentative = true; + S_sfx[id.index()].bTentative = true; } return id; } @@ -1588,12 +1589,12 @@ void SoundEngine::CacheRandomSound(sfxinfo_t* sfx) { if (sfx->bRandomHeader) { - const FRandomSoundList* list = &S_rnd[sfx->link]; + const FRandomSoundList* list = &S_rnd[sfx->link.index()]; for (unsigned i = 0; i < list->Choices.Size(); ++i) { - sfx = &S_sfx[list->Choices[i]]; + sfx = &S_sfx[list->Choices[i].index()]; sfx->bUsed = true; - CacheSound(&S_sfx[list->Choices[i]]); + CacheSound(&S_sfx[list->Choices[i].index()]); } } } @@ -1609,12 +1610,12 @@ void SoundEngine::CacheRandomSound(sfxinfo_t* sfx) unsigned int SoundEngine::GetMSLength(FSoundID sound) { - if ((unsigned int)sound >= S_sfx.Size()) + if (!isValidSoundId(sound)) { return 0; } - sfxinfo_t* sfx = &S_sfx[sound]; + sfxinfo_t* sfx = &S_sfx[sound.index()]; // Resolve player sounds, random sounds, and aliases if (sfx->link != sfxinfo_t::NO_LINK) @@ -1626,7 +1627,7 @@ unsigned int SoundEngine::GetMSLength(FSoundID sound) // I think the longest one makes more sense. int length = 0; - const FRandomSoundList* list = &S_rnd[sfx->link]; + const FRandomSoundList* list = &S_rnd[sfx->link.index()]; for (auto& me : list->Choices) { @@ -1638,7 +1639,7 @@ unsigned int SoundEngine::GetMSLength(FSoundID sound) } else { - sfx = &S_sfx[sfx->link]; + sfx = &S_sfx[sfx->link.index()]; } } @@ -1655,11 +1656,11 @@ unsigned int SoundEngine::GetMSLength(FSoundID sound) // is not the head of a random list, then the sound passed is returned. //========================================================================== -int SoundEngine::PickReplacement(int refid) +FSoundID SoundEngine::PickReplacement(FSoundID refid) { - while (S_sfx[refid].bRandomHeader) + while (S_sfx[refid.index()].bRandomHeader) { - const FRandomSoundList* list = &S_rnd[S_sfx[refid].link]; + const FRandomSoundList* list = &S_rnd[S_sfx[refid.index()].link.index()]; refid = list->Choices[rand() % int(list->Choices.Size())]; } return refid; @@ -1695,15 +1696,15 @@ void SoundEngine::HashSounds() S_rnd.ShrinkToFit(); } -void SoundEngine::AddRandomSound(int Owner, TArray list) +void SoundEngine::AddRandomSound(FSoundID Owner, TArray list) { auto index = S_rnd.Reserve(1); auto& random = S_rnd.Last(); random.Choices = std::move(list); random.Owner = Owner; - S_sfx[Owner].link = index; - S_sfx[Owner].bRandomHeader = true; - S_sfx[Owner].NearLimit = -1; + S_sfx[Owner.index()].link = FSoundID::fromInt(index); + S_sfx[Owner.index()].bRandomHeader = true; + S_sfx[Owner.index()].NearLimit = -1; } void S_SoundReset() @@ -1736,8 +1737,8 @@ CCMD(cachesound) } for (int i = 1; i < argv.argc(); ++i) { - FSoundID sfxnum = argv[i]; - if (sfxnum != FSoundID(0)) + FSoundID sfxnum = S_FindSound(argv[i]); + if (sfxnum != NO_SOUND) { soundEngine->CacheSound(sfxnum); } @@ -1823,20 +1824,26 @@ void S_SetSoundPaused(int state) if ((state || i_soundinbackground) && !pauseext) { - S_ResumeSound(true); - if (GSnd != nullptr) + if (paused == 0) { - GSnd->SetInactive(SoundRenderer::INACTIVE_Active); + S_ResumeSound(true); + if (GSnd != nullptr) + { + GSnd->SetInactive(SoundRenderer::INACTIVE_Active); + } } } else { - S_PauseSound(false, true); - if (GSnd != nullptr) + if (paused == 0) { - GSnd->SetInactive(gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL ? - SoundRenderer::INACTIVE_Complete : - SoundRenderer::INACTIVE_Mute); + S_PauseSound(false, true); + if (GSnd != nullptr) + { + GSnd->SetInactive(gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL ? + SoundRenderer::INACTIVE_Complete : + SoundRenderer::INACTIVE_Mute); + } } } } diff --git a/source/common/audio/sound/s_soundinternal.h b/source/common/audio/sound/s_soundinternal.h index a30dd3fc0..57a8372b2 100644 --- a/source/common/audio/sound/s_soundinternal.h +++ b/source/common/audio/sound/s_soundinternal.h @@ -2,12 +2,6 @@ #include "i_sound.h" -struct FRandomSoundList -{ - TArray Choices; - uint32_t Owner = 0; -}; - enum { sfx_empty = -1 @@ -15,47 +9,6 @@ enum -// -// SoundFX struct. -// -struct sfxinfo_t -{ - // Next field is for use by the system sound interface. - // A non-null data means the sound has been loaded. - SoundHandle data{}; - - FString name; // [RH] Sound name defined in SNDINFO - int lumpnum = sfx_empty; // lump number of sfx - - unsigned int next = -1, index = 0; // [RH] For hashing - float Volume = 1.f; - - int ResourceId = -1; // Resource ID as implemented by Blood. Not used by Doom but added for completeness. - float LimitRange = 256*256; // Range for sound limiting (squared for faster computations) - float DefPitch = 0.f; // A defined pitch instead of a random one the sound plays at, similar to A_StartSound. - float DefPitchMax = 0.f; // Randomized range with stronger control over pitch itself. - - int16_t NearLimit = 4; // 0 means unlimited. - uint8_t PitchMask = 0; - bool bRandomHeader = false; - bool bLoadRAW = false; - bool b16bit = false; - bool bUsed = false; - bool bSingular = false; - bool bTentative = true; - - TArray UserData; - - int RawRate = 0; // Sample rate to use when bLoadRAW is true - int LoopStart = -1; // -1 means no specific loop defined - - unsigned int link = NO_LINK;; - enum { NO_LINK = 0xffffffff }; - - FRolloffInfo Rolloff{}; - float Attenuation = 1.f; // Multiplies the attenuation passed to S_Sound. -}; - // Rolloff types enum { @@ -65,8 +18,6 @@ enum ROLLOFF_Custom // Lookup volume from SNDCURVE }; -inline int S_FindSoundByResID(int ndx); -inline int S_FindSound(const char* name); // An index into the S_sfx[] array. class FSoundID @@ -74,60 +25,90 @@ class FSoundID public: FSoundID() = default; - static FSoundID byResId(int ndx) +private: + constexpr FSoundID(int id) : ID(id) { - return FSoundID(S_FindSoundByResID(ndx)); } - FSoundID(int id) +public: + static constexpr FSoundID fromInt(int i) { - ID = id; - } - FSoundID(const char *name) - { - ID = S_FindSound(name); - } - FSoundID(const FString &name) - { - ID = S_FindSound(name.GetChars()); + return FSoundID(i); } FSoundID(const FSoundID &other) = default; FSoundID &operator=(const FSoundID &other) = default; - FSoundID &operator=(const char *name) - { - ID = S_FindSound(name); - return *this; - } - FSoundID &operator=(const FString &name) - { - ID = S_FindSound(name.GetChars()); - return *this; - } bool operator !=(FSoundID other) const { return ID != other.ID; } - bool operator !=(int other) const + bool operator ==(FSoundID other) const { - return ID != other; + return ID == other.ID; } - operator int() const + bool operator ==(int other) const = delete; + bool operator !=(int other) const = delete; + constexpr int index() const { return ID; } + constexpr bool isvalid() const + { + return ID > 0; + } private: + int ID; -protected: - enum EDummy { NoInit }; - FSoundID(EDummy) {} }; - class FSoundIDNoInit : public FSoundID -{ -public: - FSoundIDNoInit() : FSoundID(NoInit) {} - using FSoundID::operator=; -}; +constexpr FSoundID NO_SOUND = FSoundID::fromInt(0); +constexpr FSoundID INVALID_SOUND = FSoundID::fromInt(-1); + struct FRandomSoundList + { + TArray Choices; + FSoundID Owner = NO_SOUND; + }; + + + // +// SoundFX struct. +// + struct sfxinfo_t + { + // Next field is for use by the system sound interface. + // A non-null data means the sound has been loaded. + SoundHandle data{}; + + FString name; // [RH] Sound name defined in SNDINFO + int lumpnum = sfx_empty; // lump number of sfx + + unsigned int next = -1, index = 0; // [RH] For hashing + float Volume = 1.f; + + int ResourceId = -1; // Resource ID as implemented by Blood. Not used by Doom but added for completeness. + float LimitRange = 256 * 256; // Range for sound limiting (squared for faster computations) + float DefPitch = 0.f; // A defined pitch instead of a random one the sound plays at, similar to A_StartSound. + float DefPitchMax = 0.f; // Randomized range with stronger control over pitch itself. + + int16_t NearLimit = 4; // 0 means unlimited. + uint8_t PitchMask = 0; + bool bRandomHeader = false; + bool bLoadRAW = false; + bool b16bit = false; + bool bUsed = false; + bool bSingular = false; + bool bTentative = true; + + TArray UserData; + + int RawRate = 0; // Sample rate to use when bLoadRAW is true + int LoopStart = -1; // -1 means no specific loop defined + + FSoundID link = NO_LINK; + constexpr static FSoundID NO_LINK = FSoundID::fromInt(-1); + + FRolloffInfo Rolloff{}; + float Attenuation = 1.f; // Multiplies the attenuation passed to S_Sound. + }; struct FSoundChan : public FISoundChannel @@ -211,7 +192,7 @@ protected: TArray S_sfx; FRolloffInfo S_Rolloff{}; TArray S_SoundCurve; - TMap ResIdMap; + TMap ResIdMap; TArray S_rnd; bool blockNewSounds = false; @@ -231,8 +212,9 @@ private: bool ValidatePosVel(const FSoundChan* const chan, const FVector3& pos, const FVector3& vel); // Checks if a copy of this sound is already playing. - bool CheckSingular(int sound_id); + bool CheckSingular(FSoundID sound_id); virtual TArray ReadSound(int lumpnum) = 0; + protected: virtual bool CheckSoundLimit(sfxinfo_t* sfx, const FVector3& pos, int near_limit, float limit_range, int sourcetype, const void* actor, int channel, float attenuation); virtual FSoundID ResolveSound(const void *ent, int srctype, FSoundID soundid, float &attenuation); @@ -251,10 +233,25 @@ public: virtual void StopChannel(FSoundChan* chan); sfxinfo_t* LoadSound(sfxinfo_t* sfx); - const sfxinfo_t* GetSfx(unsigned snd) + sfxinfo_t* GetWritableSfx(FSoundID snd) { - if (snd >= S_sfx.Size()) return nullptr; - return &S_sfx[snd]; + if ((unsigned)snd.index() >= S_sfx.Size()) return nullptr; + return &S_sfx[snd.index()]; + } + + const sfxinfo_t* GetSfx(FSoundID snd) + { + return GetWritableSfx(snd); + } + + unsigned GetNumSounds() const + { + return S_sfx.Size(); + } + + sfxinfo_t* AllocateSound() + { + return &S_sfx[S_sfx.Reserve(1)]; } // Initializes sound stuff, including volume @@ -276,7 +273,7 @@ public: // Loads a sound, including any random sounds it might reference. virtual void CacheSound(sfxinfo_t* sfx); - void CacheSound(int sfx) { CacheSound(&S_sfx[sfx]); } + void CacheSound(FSoundID sfx) { CacheSound(&S_sfx[sfx.index()]); } void UnloadSound(sfxinfo_t* sfx); void UnloadSound(int sfx) { @@ -289,21 +286,21 @@ public: const FVector3* pt, int channel, EChanFlags flags, FSoundID sound_id, float volume, float attenuation, FRolloffInfo* rolloff = nullptr, float spitch = 0.0f, float startTime = 0.0f); // Stops an origin-less sound from playing from this channel. - void StopSoundID(int sound_id); - void StopSound(int channel, int sound_id = -1); - void StopSound(int sourcetype, const void* actor, int channel, int sound_id = -1); + void StopSoundID(FSoundID sound_id); + void StopSound(int channel, FSoundID sound_id = INVALID_SOUND); + void StopSound(int sourcetype, const void* actor, int channel, FSoundID sound_id = INVALID_SOUND); void StopActorSounds(int sourcetype, const void* actor, int chanmin, int chanmax); void RelinkSound(int sourcetype, const void* from, const void* to, const FVector3* optpos); void ChangeSoundVolume(int sourcetype, const void* source, int channel, double dvolume); - void ChangeSoundPitch(int sourcetype, const void* source, int channel, double pitch, int sound_id = -1); - bool IsSourcePlayingSomething(int sourcetype, const void* actor, int channel, int sound_id = -1); + void ChangeSoundPitch(int sourcetype, const void* source, int channel, double pitch, FSoundID sound_id = INVALID_SOUND); + bool IsSourcePlayingSomething(int sourcetype, const void* actor, int channel, FSoundID sound_id = INVALID_SOUND); // Stop and resume music, during game PAUSE. - int GetSoundPlayingInfo(int sourcetype, const void* source, int sound_id, int chan = -1); + int GetSoundPlayingInfo(int sourcetype, const void* source, FSoundID sound_id, int chan = -1); void UnloadAllSounds(); void Reset(); - void MarkUsed(int num); + void MarkUsed(FSoundID num); void CacheMarkedSounds(); TArray AllActiveChannels(); virtual void SetSoundPaused(int state) {} @@ -339,30 +336,27 @@ public: } const char *GetSoundName(FSoundID id) { - return id == 0 ? "" : S_sfx[id].name.GetChars(); + return !id.isvalid() ? "" : S_sfx[id.index()].name.GetChars(); } - TArray &GetSounds() //This should only be used for constructing the sound list or for diagnostics code prinring information about the sound list. - { - return S_sfx; - } - FRolloffInfo& GlobalRolloff() // like GetSounds this is meant for sound list generators, not for gaining cheap access to the sound engine's innards. + FRolloffInfo& GlobalRolloff() // this is meant for sound list generators, not for gaining cheap access to the sound engine's innards. { return S_Rolloff; } FRandomSoundList *ResolveRandomSound(sfxinfo_t* sfx) { - return &S_rnd[sfx->link]; + return &S_rnd[sfx->link.index()]; } void ClearRandoms() { S_rnd.Clear(); } - int *GetUserData(int snd) + int *GetUserData(FSoundID snd) { - return S_sfx[snd].UserData.Data(); + return S_sfx[snd.index()].UserData.Data(); } - bool isValidSoundId(int id) + bool isValidSoundId(FSoundID sid) { + int id = sid.index(); return id > 0 && id < (int)S_sfx.Size() && !S_sfx[id].bTentative && S_sfx[id].lumpnum != sfx_empty; } @@ -393,17 +387,17 @@ public: virtual void SoundDone(FISoundChannel* ichan); // gets called when the sound has been completely taken down. // Lookup utilities. - int FindSound(const char* logicalname); - int FindSoundByResID(int rid); - int FindSoundNoHash(const char* logicalname); - int FindSoundByLump(int lump); - virtual int AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid = -1, int nearlimit = 2); - int FindSoundTentative(const char* name); + FSoundID FindSound(const char* logicalname); + FSoundID FindSoundByResID(int rid); + FSoundID FindSoundNoHash(const char* logicalname); + FSoundID FindSoundByLump(int lump); + virtual FSoundID AddSoundLump(const char* logicalname, int lump, int CurrentPitchMask, int resid = -1, int nearlimit = 2); + FSoundID FindSoundTentative(const char* name); void CacheRandomSound(sfxinfo_t* sfx); unsigned int GetMSLength(FSoundID sound); - int PickReplacement(int refid); + FSoundID PickReplacement(FSoundID refid); void HashSounds(); - void AddRandomSound(int Owner, TArray list); + void AddRandomSound(FSoundID Owner, TArray list); }; @@ -418,12 +412,12 @@ struct FReverbField }; -inline int S_FindSoundByResID(int ndx) +inline FSoundID S_FindSoundByResID(int ndx) { return soundEngine->FindSoundByResID(ndx); } -inline int S_FindSound(const char* name) +inline FSoundID S_FindSound(const char* name) { return soundEngine->FindSound(name); } diff --git a/source/common/cutscenes/movieplayer.cpp b/source/common/cutscenes/movieplayer.cpp index fbc1f9f64..94a7e4c60 100644 --- a/source/common/cutscenes/movieplayer.cpp +++ b/source/common/cutscenes/movieplayer.cpp @@ -210,14 +210,14 @@ public: { if (animSnd[i] == curframe) { - int sound = animSnd[i+1]; - if (sound == -1) + auto sound = FSoundID::fromInt(animSnd[i+1]); + if (sound == INVALID_SOUND) soundEngine->StopAllChannels(); else soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); } } - if (!nostopsound && curframe == numframes && soundEngine->GetSoundPlayingInfo(SOURCE_None, nullptr, -1)) return true; + if (!nostopsound && curframe == numframes && soundEngine->GetSoundPlayingInfo(SOURCE_None, nullptr, INVALID_SOUND)) return true; curframe++; return curframe < numframes; } @@ -586,8 +586,8 @@ public: { if (animSnd[i] == soundframe) { - int sound = animSnd[i + 1]; - if (sound == -1) + auto sound = FSoundID::fromInt(animSnd[i + 1]); + if (sound == INVALID_SOUND) soundEngine->StopAllChannels(); else soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); @@ -790,8 +790,8 @@ public: { if (animSnd[i] == nFrame) { - int sound = animSnd[i + 1]; - if (sound == -1) + auto sound = FSoundID::fromInt(animSnd[i + 1]); + if (sound == INVALID_SOUND) soundEngine->StopAllChannels(); else soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); diff --git a/source/common/cutscenes/screenjob.cpp b/source/common/cutscenes/screenjob.cpp index de929e9bf..97bc76862 100644 --- a/source/common/cutscenes/screenjob.cpp +++ b/source/common/cutscenes/screenjob.cpp @@ -153,10 +153,10 @@ void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps) int CutsceneDef::GetSound() { - int id = -1; + FSoundID id = INVALID_SOUND; if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName); - if (id <= 0) id = soundEngine->FindSoundByResID(soundID); - return id; + if (id == INVALID_SOUND) id = soundEngine->FindSoundByResID(soundID); + return id.index(); } void CutsceneDef::Create(DObject* runner) diff --git a/source/common/engine/d_event.cpp b/source/common/engine/d_event.cpp index 130c65622..b64aa6365 100644 --- a/source/common/engine/d_event.cpp +++ b/source/common/engine/d_event.cpp @@ -44,6 +44,8 @@ #include "gamestate.h" #include "i_interface.h" +bool G_Responder(event_t* ev); + int eventhead; int eventtail; event_t events[MAXEVENTS]; @@ -84,16 +86,12 @@ void D_ProcessEvents (void) if (ev->type == EV_DeviceChange) UpdateJoystickMenu(I_UpdateDeviceList()); - // allow the game to intercept Escape before dispatching it. - if (ev->type != EV_KeyDown || ev->data1 != KEY_ESCAPE || !sysCallbacks.WantEscape()) + if (gamestate != GS_INTRO) // GS_INTRO blocks the UI. { - if (gamestate != GS_INTRO) // GS_INTRO blocks the UI. - { - if (C_Responder(ev)) - continue; // console ate the event - if (M_Responder(ev)) - continue; // menu ate the event - } + if (C_Responder(ev)) + continue; // console ate the event + if (M_Responder(ev)) + continue; // menu ate the event } if (sysCallbacks.G_Responder(ev) && ev->type == EV_KeyDown) keywasdown.Set(ev->data1); diff --git a/source/common/engine/i_interface.h b/source/common/engine/i_interface.h index 48cd2b95d..c391e21dc 100644 --- a/source/common/engine/i_interface.h +++ b/source/common/engine/i_interface.h @@ -46,7 +46,6 @@ struct SystemCallbacks void (*LanguageChanged)(const char*); bool (*OkForLocalization)(FTextureID, const char*); FConfigFile* (*GetConfig)(); - bool (*WantEscape)(); }; extern SystemCallbacks sysCallbacks; diff --git a/source/common/engine/serializer.cpp b/source/common/engine/serializer.cpp index a024aeb4d..808b0fedc 100644 --- a/source/common/engine/serializer.cpp +++ b/source/common/engine/serializer.cpp @@ -1332,9 +1332,9 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI if (!arc.soundNamesAreUnique) { //If sound name here is not reliable, we need to save by index instead. - int id = sid; + int id = sid.index(); Serialize(arc, key, id, nullptr); - if (arc.isReading()) sid = FSoundID(id); + if (arc.isReading()) sid = FSoundID::fromInt(id); } else if (arc.isWriting()) { @@ -1354,16 +1354,16 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSoundID &sid, FSoundI assert(val->IsString() || val->IsNull()); if (val->IsString()) { - sid = UnicodeToString(val->GetString()); + sid = S_FindSound(UnicodeToString(val->GetString())); } else if (val->IsNull()) { - sid = 0; + sid = NO_SOUND; } else { Printf(TEXTCOLOR_RED "string type expected for '%s'\n", key); - sid = 0; + sid = NO_SOUND; arc.mErrors++; } } diff --git a/source/common/menu/menu.h b/source/common/menu/menu.h index bad3406e9..aaeecbcd4 100644 --- a/source/common/menu/menu.h +++ b/source/common/menu/menu.h @@ -94,6 +94,7 @@ public: int mVirtWidth; int mVirtHeight; bool mCustomSizeSet; + bool mForceList; void Reset(); }; diff --git a/source/common/menu/menudef.cpp b/source/common/menu/menudef.cpp index 0ef4e4fe1..1b25fcdad 100644 --- a/source/common/menu/menudef.cpp +++ b/source/common/menu/menudef.cpp @@ -419,6 +419,10 @@ static void DoParseListMenuBody(FScanner &sc, DListMenuDescriptor *desc, bool &s } } } + else if (sc.Compare("ForceList")) + { + desc->mForceList = true; + } else { // all item classes from which we know that they support sized scaling. @@ -759,6 +763,7 @@ static void ParseListMenu(FScanner &sc) desc->mFromEngine = fileSystem.GetFileContainer(sc.LumpNum) == 0; // flags menu if the definition is from the IWAD. desc->mVirtWidth = -2; desc->mCustomSizeSet = false; + desc->mForceList = false; if (DefaultListMenuSettings->mCustomSizeSet) { desc->mVirtHeight = DefaultListMenuSettings->mVirtHeight; @@ -1028,13 +1033,19 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc, int i { auto &args = func->Variants[0].Proto->ArgumentTypes; TArray params; + int start = 1; params.Push(0); + if (args.Size() > 1 && args[1] == NewPointer(PClass::FindClass("OptionMenuDescriptor"))) + { + params.Push(desc); + start = 2; + } auto TypeCVar = NewPointer(NewStruct("CVar", nullptr, true)); // Note that this array may not be reallocated so its initial size must be the maximum possible elements. TArray strings(args.Size()); - for (unsigned i = 1; i < args.Size(); i++) + for (unsigned i = start; i < args.Size(); i++) { sc.MustGetString(); if (args[i] == TypeString) @@ -1050,6 +1061,24 @@ static void ParseOptionMenuBody(FScanner &sc, DOptionMenuDescriptor *desc, int i { params.Push(V_GetColor(sc)); } + else if (args[i] == TypeFont) + { + auto f = V_GetFont(sc.String); + if (f == nullptr) + { + sc.ScriptError("Unknown font %s", sc.String); + } + params.Push(f); + } + else if (args[i] == TypeTextureID) + { + auto f = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch); + if (!f.Exists()) + { + sc.ScriptMessage("Unknown texture %s", sc.String); + } + params.Push(f.GetIndex()); + } else if (args[i]->isIntCompatible()) { char *endp; @@ -1228,6 +1257,16 @@ static void ParseImageScrollerBody(FScanner& sc, DImageScrollerDescriptor* desc) ParseImageScrollerBody(sc, desc); } } + else if (sc.Compare("Class")) + { + sc.MustGetString(); + PClass* cls = PClass::FindClass(sc.String); + if (cls == nullptr || !cls->IsDescendantOf("ImageScrollerMenu")) + { + sc.ScriptError("Unknown menu class '%s'", sc.String); + } + desc->mClass = cls; + } else if (sc.Compare("animatedtransition")) { desc->mAnimatedTransition = true; @@ -1300,6 +1339,24 @@ static void ParseImageScrollerBody(FScanner& sc, DImageScrollerDescriptor* desc) { params.Push(V_GetColor(sc)); } + else if (args[i] == TypeFont) + { + auto f = V_GetFont(sc.String); + if (f == nullptr) + { + sc.ScriptError("Unknown font %s", sc.String); + } + params.Push(f); + } + else if (args[i] == TypeTextureID) + { + auto f = TexMan.CheckForTexture(sc.String, ETextureType::MiscPatch); + if (!f.Exists()) + { + sc.ScriptMessage("Unknown texture %s", sc.String); + } + params.Push(f.GetIndex()); + } else if (args[i]->isIntCompatible()) { char* endp; diff --git a/source/common/scripting/backend/codegen.cpp b/source/common/scripting/backend/codegen.cpp index a450ceb59..c993ad093 100644 --- a/source/common/scripting/backend/codegen.cpp +++ b/source/common/scripting/backend/codegen.cpp @@ -1299,7 +1299,7 @@ FxExpression *FxStringCast::Resolve(FCompileContext &ctx) if (basex->isConstant()) { ExpVal constval = static_cast(basex)->GetValue(); - FxExpression *x = new FxConstant(soundEngine->GetSoundName(constval.GetInt()), ScriptPosition); + FxExpression *x = new FxConstant(soundEngine->GetSoundName(FSoundID::fromInt(constval.GetInt())), ScriptPosition); delete this; return x; } @@ -1475,7 +1475,7 @@ FxExpression *FxSoundCast::Resolve(FCompileContext &ctx) if (basex->isConstant()) { ExpVal constval = static_cast(basex)->GetValue(); - FxExpression *x = new FxConstant(FSoundID(constval.GetString()), ScriptPosition); + FxExpression *x = new FxConstant(S_FindSound(constval.GetString()), ScriptPosition); delete this; return x; } @@ -3093,7 +3093,14 @@ FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) [[fallthrough]]; case '*': - if ((left->IsVector() || left->IsQuaternion()) && right->IsNumeric()) + if (Operator == '*' && left->IsQuaternion() && (right->IsVector3() || right->IsQuaternion())) + { + // quat * vec3 + // quat * quat + ValueType = right->ValueType; + break; + } + else if ((left->IsVector() || left->IsQuaternion()) && right->IsNumeric()) { if (right->IsInteger()) { @@ -3208,10 +3215,26 @@ ExpEmit FxMulDiv::Emit(VMFunctionBuilder *build) ExpEmit op1 = left->Emit(build); ExpEmit op2 = right->Emit(build); - if (IsVector() || IsQuaternion()) + if (Operator == '*' && left->IsQuaternion() && right->IsQuaternion()) + { + op1.Free(build); + op2.Free(build); + ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount()); + build->Emit(OP_MULQQ_RR, to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + else if (Operator == '*' && left->IsQuaternion() && right->IsVector3()) + { + op1.Free(build); + op2.Free(build); + ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount()); + build->Emit(OP_MULQV3_RR, to.RegNum, op1.RegNum, op2.RegNum); + return to; + } + else if (IsVector() || IsQuaternion()) { assert(Operator != '%'); - if (right->IsVector()) + if (left->IsFloat()) { std::swap(op1, op2); } @@ -4378,7 +4401,7 @@ ExpEmit FxConcat::Emit(VMFunctionBuilder *build) build->Emit(op2.RegType == REGT_INT ? OP_LK : op2.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op2.RegNum); op2 = nonconst; } - if (op1.RegType == REGT_FLOAT) cast = op1.RegCount == 1 ? CAST_F2S : op1.RegCount == 2 ? CAST_V22S : op1.RegCount == 3 ? CAST_V32S : CAST_V42S; + if (op2.RegType == REGT_FLOAT) cast = op2.RegCount == 1 ? CAST_F2S : op2.RegCount == 2 ? CAST_V22S : op2.RegCount == 3 ? CAST_V32S : CAST_V42S; else if (right->ValueType == TypeUInt32) cast = CAST_U2S; else if (right->ValueType == TypeName) cast = CAST_N2S; else if (right->ValueType == TypeSound) cast = CAST_So2S; @@ -8206,6 +8229,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) // because the resulting value type would cause problems in nearly every other place where identifiers are being used. // [ZZ] substitute ccls for String internal type. if (id == NAME_String) ccls = TypeStringStruct; + else if (id == NAME_Quat || id == NAME_FQuat) ccls = TypeQuaternionStruct; else ccls = FindContainerType(id, ctx); if (ccls != nullptr) static_cast(Self)->noglobal = true; } @@ -8385,9 +8409,9 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) delete this; return x->Resolve(ctx); } + + Self->ValueType = TypeQuaternionStruct; } - - else if (Self->ValueType == TypeString) { if (MethodName == NAME_Length) // This is an intrinsic because a dedicated opcode exists for it. diff --git a/source/common/scripting/backend/codegen.h b/source/common/scripting/backend/codegen.h index 364333d85..f31b4ea1e 100644 --- a/source/common/scripting/backend/codegen.h +++ b/source/common/scripting/backend/codegen.h @@ -344,7 +344,7 @@ public: bool IsVector2() const { return ValueType == TypeVector2 || ValueType == TypeFVector2; }; bool IsVector3() const { return ValueType == TypeVector3 || ValueType == TypeFVector3; }; bool IsVector4() const { return ValueType == TypeVector4 || ValueType == TypeFVector4; }; - bool IsQuaternion() const { return ValueType == TypeQuaternion || ValueType == TypeFQuaternion; }; + bool IsQuaternion() const { return ValueType == TypeQuaternion || ValueType == TypeFQuaternion || ValueType == TypeQuaternionStruct; }; bool IsBoolCompat() const { return ValueType->isScalar(); } bool IsObject() const { return ValueType->isObjectPointer(); } bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); } @@ -450,7 +450,7 @@ public: FxConstant(FSoundID val, const FScriptPosition &pos) : FxExpression(EFX_Constant, pos) { ValueType = value.Type = TypeSound; - value.Int = val; + value.Int = val.index(); isresolved = true; } diff --git a/source/common/scripting/core/types.cpp b/source/common/scripting/core/types.cpp index 046c8358e..376587d15 100644 --- a/source/common/scripting/core/types.cpp +++ b/source/common/scripting/core/types.cpp @@ -69,6 +69,7 @@ PStruct* TypeFVector4; PStruct* TypeFQuaternion; PStruct *TypeColorStruct; PStruct *TypeStringStruct; +PStruct* TypeQuaternionStruct; PPointer *TypeNullPtr; PPointer *TypeVoidPtr; @@ -316,6 +317,7 @@ void PType::StaticInit() TypeVoidPtr = NewPointer(TypeVoid, false); TypeColorStruct = NewStruct("@ColorStruct", nullptr); //This name is intentionally obfuscated so that it cannot be used explicitly. The point of this type is to gain access to the single channels of a color value. TypeStringStruct = NewStruct("Stringstruct", nullptr, true); + TypeQuaternionStruct = NewStruct("QuatStruct", nullptr, true); TypeFont = NewPointer(NewStruct("Font", nullptr, true)); #ifdef __BIG_ENDIAN__ TypeColorStruct->AddField(NAME_a, TypeUInt8); @@ -1349,7 +1351,7 @@ bool PSound::ReadValue(FSerializer &ar, const char *key, void *addr) const } else { - *(FSoundID *)addr = FSoundID(cptr); + *(FSoundID *)addr = S_FindSound(cptr); return true; } } diff --git a/source/common/scripting/core/types.h b/source/common/scripting/core/types.h index 862138c1d..14597ddfd 100644 --- a/source/common/scripting/core/types.h +++ b/source/common/scripting/core/types.h @@ -623,6 +623,7 @@ extern PStruct* TypeQuaternion; extern PStruct* TypeFQuaternion; extern PStruct *TypeColorStruct; extern PStruct *TypeStringStruct; +extern PStruct* TypeQuaternionStruct; extern PStatePointer *TypeState; extern PPointer *TypeFont; extern PStateLabel *TypeStateLabel; diff --git a/source/common/scripting/interface/vmnatives.cpp b/source/common/scripting/interface/vmnatives.cpp index 4a0d3758b..a29a6b711 100644 --- a/source/common/scripting/interface/vmnatives.cpp +++ b/source/common/scripting/interface/vmnatives.cpp @@ -1129,3 +1129,136 @@ DEFINE_FIELD(DStatusBarCore, drawClip); DEFINE_FIELD(DStatusBarCore, fullscreenOffsets); DEFINE_FIELD(DStatusBarCore, defaultScale); DEFINE_FIELD(DHUDFont, mFont); + +// +// Quaternion +void QuatFromAngles(double yaw, double pitch, double roll, DQuaternion* pquat) +{ + *pquat = DQuaternion::FromAngles(DAngle::fromDeg(yaw), DAngle::fromDeg(pitch), DAngle::fromDeg(roll)); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_QuatStruct, FromAngles, QuatFromAngles) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(yaw); + PARAM_FLOAT(pitch); + PARAM_FLOAT(roll); + + DQuaternion quat; + QuatFromAngles(yaw, pitch, roll, &quat); + ACTION_RETURN_QUAT(quat); +} + +void QuatAxisAngle(double x, double y, double z, double angleDeg, DQuaternion* pquat) +{ + auto axis = DVector3(x, y, z); + auto angle = DAngle::fromDeg(angleDeg); + *pquat = DQuaternion::AxisAngle(axis, angle); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_QuatStruct, AxisAngle, QuatAxisAngle) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(x); + PARAM_FLOAT(y); + PARAM_FLOAT(z); + PARAM_FLOAT(angle); + + DQuaternion quat; + QuatAxisAngle(x, y, z, angle, &quat); + ACTION_RETURN_QUAT(quat); +} + +void QuatNLerp( + double ax, double ay, double az, double aw, + double bx, double by, double bz, double bw, + double t, + DQuaternion* pquat +) +{ + auto from = DQuaternion { ax, ay, az, aw }; + auto to = DQuaternion { bx, by, bz, bw }; + *pquat = DQuaternion::NLerp(from, to, t); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_QuatStruct, NLerp, QuatNLerp) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(ax); + PARAM_FLOAT(ay); + PARAM_FLOAT(az); + PARAM_FLOAT(aw); + PARAM_FLOAT(bx); + PARAM_FLOAT(by); + PARAM_FLOAT(bz); + PARAM_FLOAT(bw); + PARAM_FLOAT(t); + + DQuaternion quat; + QuatNLerp(ax, ay, az, aw, bx, by, bz, bw, t, &quat); + ACTION_RETURN_QUAT(quat); +} + +void QuatSLerp( + double ax, double ay, double az, double aw, + double bx, double by, double bz, double bw, + double t, + DQuaternion* pquat +) +{ + auto from = DQuaternion { ax, ay, az, aw }; + auto to = DQuaternion { bx, by, bz, bw }; + *pquat = DQuaternion::SLerp(from, to, t); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_QuatStruct, SLerp, QuatSLerp) +{ + PARAM_PROLOGUE; + PARAM_FLOAT(ax); + PARAM_FLOAT(ay); + PARAM_FLOAT(az); + PARAM_FLOAT(aw); + PARAM_FLOAT(bx); + PARAM_FLOAT(by); + PARAM_FLOAT(bz); + PARAM_FLOAT(bw); + PARAM_FLOAT(t); + + DQuaternion quat; + QuatSLerp(ax, ay, az, aw, bx, by, bz, bw, t, &quat); + ACTION_RETURN_QUAT(quat); +} + +void QuatConjugate( + double x, double y, double z, double w, + DQuaternion* pquat +) +{ + *pquat = DQuaternion(x, y, z, w).Conjugate(); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_QuatStruct, Conjugate, QuatConjugate) +{ + PARAM_SELF_STRUCT_PROLOGUE(DQuaternion); + + DQuaternion quat; + QuatConjugate(self->X, self->Y, self->Z, self->W, &quat); + ACTION_RETURN_QUAT(quat); +} + +void QuatInverse( + double x, double y, double z, double w, + DQuaternion* pquat +) +{ + *pquat = DQuaternion(x, y, z, w).Inverse(); +} + +DEFINE_ACTION_FUNCTION_NATIVE(_QuatStruct, Inverse, QuatInverse) +{ + PARAM_SELF_STRUCT_PROLOGUE(DQuaternion); + + DQuaternion quat; + QuatInverse(self->X, self->Y, self->Z, self->W, &quat); + ACTION_RETURN_QUAT(quat); +} diff --git a/source/common/scripting/jit/jit_math.cpp b/source/common/scripting/jit/jit_math.cpp index c75fa2041..24328b9d3 100644 --- a/source/common/scripting/jit/jit_math.cpp +++ b/source/common/scripting/jit/jit_math.cpp @@ -1606,6 +1606,62 @@ void JitCompiler::EmitEQV4_K() I_Error("EQV4_K is not used."); } +// Quaternion ops +void FuncMULQQ(void *result, double ax, double ay, double az, double aw, double bx, double by, double bz, double bw) +{ + *reinterpret_cast(result) = DQuaternion(ax, ay, az, aw) * DQuaternion(bx, by, bz, bw); +} + +void FuncMULQV3(void *result, double ax, double ay, double az, double aw, double bx, double by, double bz) +{ + *reinterpret_cast(result) = DQuaternion(ax, ay, az, aw) * DVector3(bx, by, bz); +} + +void JitCompiler::EmitMULQQ_RR() +{ + auto stack = GetTemporaryVectorStackStorage(); + auto tmp = newTempIntPtr(); + cc.lea(tmp, stack); + + auto call = CreateCall(FuncMULQQ); + call->setArg(0, tmp); + call->setArg(1, regF[B + 0]); + call->setArg(2, regF[B + 1]); + call->setArg(3, regF[B + 2]); + call->setArg(4, regF[B + 3]); + call->setArg(5, regF[C + 0]); + call->setArg(6, regF[C + 1]); + call->setArg(7, regF[C + 2]); + call->setArg(8, regF[C + 3]); + + cc.movsd(regF[A + 0], asmjit::x86::qword_ptr(tmp, 0)); + cc.movsd(regF[A + 1], asmjit::x86::qword_ptr(tmp, 8)); + cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16)); + cc.movsd(regF[A + 3], asmjit::x86::qword_ptr(tmp, 24)); +} + +void JitCompiler::EmitMULQV3_RR() +{ + auto stack = GetTemporaryVectorStackStorage(); + auto tmp = newTempIntPtr(); + cc.lea(tmp, stack); + + auto call = CreateCall(FuncMULQV3); + call->setArg(0, tmp); + call->setArg(1, regF[B + 0]); + call->setArg(2, regF[B + 1]); + call->setArg(3, regF[B + 2]); + call->setArg(4, regF[B + 3]); + call->setArg(5, regF[C + 0]); + call->setArg(6, regF[C + 1]); + call->setArg(7, regF[C + 2]); + + cc.movsd(regF[A + 0], asmjit::x86::qword_ptr(tmp, 0)); + cc.movsd(regF[A + 1], asmjit::x86::qword_ptr(tmp, 8)); + cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16)); +} + + ///////////////////////////////////////////////////////////////////////////// // Pointer math. diff --git a/source/common/scripting/jit/jit_move.cpp b/source/common/scripting/jit/jit_move.cpp index 1f0d4edc9..840a6487c 100644 --- a/source/common/scripting/jit/jit_move.cpp +++ b/source/common/scripting/jit/jit_move.cpp @@ -60,8 +60,8 @@ static int CastS2N(FString *b) { return b->Len() == 0 ? NAME_None : FName(*b).Ge static void CastN2S(FString *a, int b) { FName name = FName(ENamedName(b)); *a = name.IsValidName() ? name.GetChars() : ""; } static int CastS2Co(FString *b) { return V_GetColor(*b); } static void CastCo2S(FString *a, int b) { PalEntry c(b); a->Format("%02x %02x %02x", c.r, c.g, c.b); } -static int CastS2So(FString *b) { return FSoundID(*b); } -static void CastSo2S(FString* a, int b) { *a = soundEngine->GetSoundName(b); } +static int CastS2So(FString *b) { return S_FindSound(*b).index(); } +static void CastSo2S(FString* a, int b) { *a = soundEngine->GetSoundName(FSoundID::fromInt(b)); } static void CastSID2S(FString* a, unsigned int b) { VM_CastSpriteIDToString(a, b); } static void CastTID2S(FString *a, int b) { auto tex = TexMan.GetGameTexture(*(FTextureID*)&b); *a = (tex == nullptr) ? "(null)" : tex->GetName().GetChars(); } diff --git a/source/common/scripting/jit/jitintern.h b/source/common/scripting/jit/jitintern.h index 2a3dda426..aaf8d70a6 100644 --- a/source/common/scripting/jit/jitintern.h +++ b/source/common/scripting/jit/jitintern.h @@ -186,7 +186,13 @@ private: asmjit::CCFuncCall *CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6)) { return cc.call(asmjit::imm_ptr(reinterpret_cast(static_cast(func))), asmjit::FuncSignature6()); } template - asmjit::CCFuncCall *CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7)) { return cc.call(asmjit::imm_ptr(reinterpret_cast(static_cast(func))), asmjit::FuncSignature7()); } + asmjit::CCFuncCall* CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7)) { return cc.call(asmjit::imm_ptr(reinterpret_cast(static_cast(func))), asmjit::FuncSignature7()); } + + template + asmjit::CCFuncCall* CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8)) { return cc.call(asmjit::imm_ptr(reinterpret_cast(static_cast(func))), asmjit::FuncSignature8()); } + + template + asmjit::CCFuncCall* CreateCall(RetType(*func)(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, P9 p9)) { return cc.call(asmjit::imm_ptr(reinterpret_cast(static_cast(func))), asmjit::FuncSignature9()); } FString regname; size_t tmpPosInt32, tmpPosInt64, tmpPosIntPtr, tmpPosXmmSd, tmpPosXmmSs, tmpPosXmmPd, resultPosInt32, resultPosIntPtr, resultPosXmmSd; @@ -305,6 +311,19 @@ private: TArray labels; + // Get temporary storage enough for DVector4 which is required by operation such as MULQQ and MULQV3 + bool vectorStackAllocated = false; + asmjit::X86Mem vectorStack; + asmjit::X86Mem GetTemporaryVectorStackStorage() + { + if (!vectorStackAllocated) + { + vectorStack = cc.newStack(sizeof(DVector4), alignof(DVector4), "tmpDVector4"); + vectorStackAllocated = true; + } + return vectorStack; + } + const VMOP *pc; VM_UBYTE op; }; diff --git a/source/common/scripting/vm/vm.h b/source/common/scripting/vm/vm.h index 35db9a732..ef089eba5 100644 --- a/source/common/scripting/vm/vm.h +++ b/source/common/scripting/vm/vm.h @@ -39,6 +39,7 @@ #include "autosegs.h" #include "zstring.h" #include "vectors.h" +#include "quaternion.h" #include "cmdlib.h" #include "engineerrors.h" #include "memarena.h" @@ -147,6 +148,14 @@ struct VMReturn ((double *)Location)[2] = val[2]; ((double *)Location)[3] = val[3]; } + void SetQuaternion(const DQuaternion &val) + { + assert(RegType == (REGT_FLOAT | REGT_MULTIREG4)); + ((double *)Location)[0] = val[0]; + ((double *)Location)[1] = val[1]; + ((double *)Location)[2] = val[2]; + ((double *)Location)[3] = val[3]; + } void SetVector(const double val[3]) { assert(RegType == (REGT_FLOAT|REGT_MULTIREG3)); @@ -530,7 +539,7 @@ bool AssertObject(void * ob); #define PARAM_UINT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); unsigned x = param[p].i; #define PARAM_BOOL_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); bool x = !!param[p].i; #define PARAM_NAME_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FName x = ENamedName(param[p].i); -#define PARAM_SOUND_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FSoundID x = param[p].i; +#define PARAM_SOUND_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); FSoundID x = FSoundID::fromInt(param[p].i); #define PARAM_COLOR_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_INT); PalEntry x = param[p].i; #define PARAM_FLOAT_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_FLOAT); double x = param[p].f; #define PARAM_ANGLE_AT(p,x) assert((p) < numparam); assert(reginfo[p] == REGT_FLOAT); DAngle x = DAngle::fromDeg(param[p].f); @@ -748,6 +757,8 @@ class AActor; #define ACTION_RETURN_FLOAT(v) do { double u = v; if (numret > 0) { assert(ret != nullptr); ret->SetFloat(u); return 1; } return 0; } while(0) #define ACTION_RETURN_VEC2(v) do { DVector2 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector2(u); return 1; } return 0; } while(0) #define ACTION_RETURN_VEC3(v) do { DVector3 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector(u); return 1; } return 0; } while(0) +#define ACTION_RETURN_VEC4(v) do { DVector4 u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetVector4(u); return 1; } return 0; } while(0) +#define ACTION_RETURN_QUAT(v) do { DQuaternion u = v; if (numret > 0) { assert(ret != nullptr); ret[0].SetQuaternion(u); return 1; } return 0; } while(0) #define ACTION_RETURN_INT(v) do { int u = v; if (numret > 0) { assert(ret != NULL); ret->SetInt(u); return 1; } return 0; } while(0) #define ACTION_RETURN_BOOL(v) ACTION_RETURN_INT(v) #define ACTION_RETURN_STRING(v) do { FString u = v; if (numret > 0) { assert(ret != NULL); ret->SetString(u); return 1; } return 0; } while(0) diff --git a/source/common/scripting/vm/vmexec.h b/source/common/scripting/vm/vmexec.h index b7428eff2..671bfc081 100644 --- a/source/common/scripting/vm/vmexec.h +++ b/source/common/scripting/vm/vmexec.h @@ -1887,7 +1887,22 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret) ASSERTF(B+3); ASSERTKF(C+3); fcp = &konstf[C]; goto Do_EQV4; - + OP(MULQV3_RR): + ASSERTF(a + 2); ASSERTF(B + 3); ASSERTF(C + 2); + { + const DQuaternion& q = reinterpret_cast(reg.f[B]); + const DVector3& v = reinterpret_cast(reg.f[C]); + reinterpret_cast(reg.f[A]) = q * v; + } + NEXTOP; + OP(MULQQ_RR): + ASSERTF(a + 3); ASSERTF(B + 3); ASSERTF(C + 3); + { + const DQuaternion& q1 = reinterpret_cast(reg.f[B]); + const DQuaternion& q2 = reinterpret_cast(reg.f[C]); + reinterpret_cast(reg.f[A]) = q1 * q2; + } + NEXTOP; OP(ADDA_RR): ASSERTA(a); ASSERTA(B); ASSERTD(C); c = reg.d[C]; @@ -2107,12 +2122,12 @@ static void DoCast(const VMRegisters ®, const VMFrame *f, int a, int b, int c case CAST_S2So: ASSERTD(a); ASSERTS(b); - reg.d[a] = FSoundID(reg.s[b]); + reg.d[a] = S_FindSound(reg.s[b]).index(); break; case CAST_So2S: ASSERTS(a); ASSERTD(b); - reg.s[a] = soundEngine->GetSoundName(reg.d[b]); + reg.s[a] = soundEngine->GetSoundName(FSoundID::fromInt(reg.d[b])); break; case CAST_SID2S: diff --git a/source/common/scripting/vm/vmops.h b/source/common/scripting/vm/vmops.h index 9b9274d14..1782e78b7 100644 --- a/source/common/scripting/vm/vmops.h +++ b/source/common/scripting/vm/vmops.h @@ -278,6 +278,10 @@ xx(LENV4, lenv4, RFRV, NOP, 0, 0) // fA = vB.Length xx(EQV4_R, beqv4, CVRR, NOP, 0, 0) // if ((vB == vkC) != A) then pc++ (inexact if A & 33) xx(EQV4_K, beqv4, CVRK, NOP, 0, 0) // this will never be used. +// Quaternion math +xx(MULQQ_RR, mulqq, RVRVRV, NOP, 0, 0) // qA = qB * qC +xx(MULQV3_RR, mulqv3, RVRVRV, NOP, 0, 0) // qA = qB * vC + // Pointer math. xx(ADDA_RR, add, RPRPRI, NOP, 0, 0) // pA = pB + dkC xx(ADDA_RK, add, RPRPKI, ADDA_RR,4, REGT_INT) diff --git a/source/common/startscreen/endoom.cpp b/source/common/startscreen/endoom.cpp index cd2890403..0fff948c0 100644 --- a/source/common/startscreen/endoom.cpp +++ b/source/common/startscreen/endoom.cpp @@ -192,6 +192,7 @@ int RunEndoom() return 0; } +[[noreturn]] void ST_Endoom() { int code = RunEndoom(); diff --git a/source/common/startscreen/startscreen.h b/source/common/startscreen/startscreen.h index 75ccf97a0..afac6c759 100644 --- a/source/common/startscreen/startscreen.h +++ b/source/common/startscreen/startscreen.h @@ -102,4 +102,5 @@ protected: FStartScreen* GetGameStartScreen(int max_progress); -extern void ST_Endoom(); +[[noreturn]] +void ST_Endoom(); diff --git a/source/common/utility/quaternion.h b/source/common/utility/quaternion.h new file mode 100644 index 000000000..1437b56be --- /dev/null +++ b/source/common/utility/quaternion.h @@ -0,0 +1,352 @@ +#pragma once + +#include "vectors.h" + +template +class TQuaternion +{ +public: + typedef TVector2 Vector2; + typedef TVector3 Vector3; + + vec_t X, Y, Z, W; + + TQuaternion() = default; + + TQuaternion(vec_t x, vec_t y, vec_t z, vec_t w) + : X(x), Y(y), Z(z), W(w) + { + } + + TQuaternion(vec_t *o) + : X(o[0]), Y(o[1]), Z(o[2]), W(o[3]) + { + } + + TQuaternion(const TQuaternion &other) = default; + + TQuaternion(const Vector3 &v, vec_t s) + : X(v.X), Y(v.Y), Z(v.Z), W(s) + { + } + + TQuaternion(const vec_t v[4]) + : TQuaternion(v[0], v[1], v[2], v[3]) + { + } + + void Zero() + { + Z = Y = X = W = 0; + } + + bool isZero() const + { + return X == 0 && Y == 0 && Z == 0 && W == 0; + } + + TQuaternion &operator= (const TQuaternion &other) = default; + + // Access X and Y and Z as an array + vec_t &operator[] (int index) + { + return (&X)[index]; + } + + const vec_t &operator[] (int index) const + { + return (&X)[index]; + } + + // Test for equality + bool operator== (const TQuaternion &other) const + { + return X == other.X && Y == other.Y && Z == other.Z && W == other.W; + } + + // Test for inequality + bool operator!= (const TQuaternion &other) const + { + return X != other.X || Y != other.Y || Z != other.Z || W != other.W; + } + + // returns the XY fields as a 2D-vector. + const Vector2& XY() const + { + return *reinterpret_cast(this); + } + + Vector2& XY() + { + return *reinterpret_cast(this); + } + + // returns the XY fields as a 2D-vector. + const Vector3& XYZ() const + { + return *reinterpret_cast(this); + } + + Vector3& XYZ() + { + return *reinterpret_cast(this); + } + + + // Test for approximate equality + bool ApproximatelyEquals(const TQuaternion &other) const + { + return fabs(X - other.X) < EQUAL_EPSILON && fabs(Y - other.Y) < EQUAL_EPSILON && fabs(Z - other.Z) < EQUAL_EPSILON && fabs(W - other.W) < EQUAL_EPSILON; + } + + // Test for approximate inequality + bool DoesNotApproximatelyEqual(const TQuaternion &other) const + { + return fabs(X - other.X) >= EQUAL_EPSILON || fabs(Y - other.Y) >= EQUAL_EPSILON || fabs(Z - other.Z) >= EQUAL_EPSILON || fabs(W - other.W) >= EQUAL_EPSILON; + } + + // Unary negation + TQuaternion operator- () const + { + return TQuaternion(-X, -Y, -Z, -W); + } + + // Scalar addition + TQuaternion &operator+= (vec_t scalar) + { + X += scalar, Y += scalar, Z += scalar; W += scalar; + return *this; + } + + friend TQuaternion operator+ (const TQuaternion &v, vec_t scalar) + { + return TQuaternion(v.X + scalar, v.Y + scalar, v.Z + scalar, v.W + scalar); + } + + friend TQuaternion operator+ (vec_t scalar, const TQuaternion &v) + { + return TQuaternion(v.X + scalar, v.Y + scalar, v.Z + scalar, v.W + scalar); + } + + // Scalar subtraction + TQuaternion &operator-= (vec_t scalar) + { + X -= scalar, Y -= scalar, Z -= scalar, W -= scalar; + return *this; + } + + TQuaternion operator- (vec_t scalar) const + { + return TQuaternion(X - scalar, Y - scalar, Z - scalar, W - scalar); + } + + // Scalar multiplication + TQuaternion &operator*= (vec_t scalar) + { + X = vec_t(X *scalar), Y = vec_t(Y * scalar), Z = vec_t(Z * scalar), W = vec_t(W * scalar); + return *this; + } + + friend TQuaternion operator* (const TQuaternion &v, vec_t scalar) + { + return TQuaternion(v.X * scalar, v.Y * scalar, v.Z * scalar, v.W * scalar); + } + + friend TQuaternion operator* (vec_t scalar, const TQuaternion &v) + { + return TQuaternion(v.X * scalar, v.Y * scalar, v.Z * scalar, v.W * scalar); + } + + // Scalar division + TQuaternion &operator/= (vec_t scalar) + { + scalar = 1 / scalar, X = vec_t(X * scalar), Y = vec_t(Y * scalar), Z = vec_t(Z * scalar), W = vec_t(W * scalar); + return *this; + } + + TQuaternion operator/ (vec_t scalar) const + { + scalar = 1 / scalar; + return TQuaternion(X * scalar, Y * scalar, Z * scalar, W * scalar); + } + + // Vector addition + TQuaternion &operator+= (const TQuaternion &other) + { + X += other.X, Y += other.Y, Z += other.Z, W += other.W; + return *this; + } + + TQuaternion operator+ (const TQuaternion &other) const + { + return TQuaternion(X + other.X, Y + other.Y, Z + other.Z, W + other.W); + } + + // Vector subtraction + TQuaternion &operator-= (const TQuaternion &other) + { + X -= other.X, Y -= other.Y, Z -= other.Z, W -= other.W; + return *this; + } + + TQuaternion operator- (const TQuaternion &other) const + { + return TQuaternion(X - other.X, Y - other.Y, Z - other.Z, W - other.W); + } + + // Quaternion length + double Length() const + { + return g_sqrt(X*X + Y*Y + Z*Z + W*W); + } + + double LengthSquared() const + { + return X*X + Y*Y + Z*Z + W*W; + } + + double Sum() const + { + return abs(X) + abs(Y) + abs(Z) + abs(W); + } + + + // Return a unit vector facing the same direction as this one + TQuaternion Unit() const + { + double len = Length(); + if (len != 0) len = 1 / len; + return *this * (vec_t)len; + } + + // Scales this vector into a unit vector + void MakeUnit() + { + double len = Length(); + if (len != 0) len = 1 / len; + *this *= (vec_t)len; + } + + // Resizes this vector to be the specified length (if it is not 0) + TQuaternion &MakeResize(double len) + { + double vlen = Length(); + if (vlen != 0.) + { + double scale = len / vlen; + X = vec_t(X * scale); + Y = vec_t(Y * scale); + Z = vec_t(Z * scale); + W = vec_t(W * scale); + } + return *this; + } + + TQuaternion Resized(double len) const + { + double vlen = Length(); + if (vlen != 0.) + { + double scale = len / vlen; + return{ vec_t(X * scale), vec_t(Y * scale), vec_t(Z * scale), vec_t(W * scale) }; + } + else + { + return *this; + } + } + + // Dot product + vec_t operator | (const TQuaternion &other) const + { + return X*other.X + Y*other.Y + Z*other.Z + W*other.W; + } + + vec_t dot(const TQuaternion &other) const + { + return X*other.X + Y*other.Y + Z*other.Z + W*other.W; + } + + TQuaternion& operator*= (const TQuaternion& q) + { + *this = *this * q; + return *this; + } + + friend TQuaternion operator* (const TQuaternion& q1, const TQuaternion& q2) + { + return TQuaternion( + q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y, + q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X, + q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W, + q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z + ); + } + + // Rotate Vector3 by Quaternion q + friend TVector3 operator* (const TQuaternion& q, const TVector3& v) + { + auto r = TQuaternion({ v.X, v.Y, v.Z, 0 }) * TQuaternion({ -q.X, -q.Y, -q.Z, q.W }); + r = q * r; + return TVector3(r.X, r.Y, r.Z); + } + + TQuaternion Conjugate() + { + return TQuaternion(-X, -Y, -Z, +W); + } + TQuaternion Inverse() + { + return Conjugate() / LengthSquared(); + } + + static TQuaternion AxisAngle(TVector3 axis, TAngle angle) + { + auto lengthSquared = axis.LengthSquared(); + auto halfAngle = angle * 0.5; + auto sinTheta = halfAngle.Sin(); + auto cosTheta = halfAngle.Cos(); + auto factor = sinTheta / g_sqrt(lengthSquared); + TQuaternion ret; + ret.W = cosTheta; + ret.XYZ() = factor * axis; + return ret; + } + static TQuaternion FromAngles(TAngle yaw, TAngle pitch, TAngle roll) + { + auto zRotation = TQuaternion::AxisAngle(Vector3(vec_t{0.0}, vec_t{0.0}, vec_t{1.0}), yaw); + auto yRotation = TQuaternion::AxisAngle(Vector3(vec_t{0.0}, vec_t{1.0}, vec_t{0.0}), pitch); + auto xRotation = TQuaternion::AxisAngle(Vector3(vec_t{1.0}, vec_t{0.0}, vec_t{0.0}), roll); + return zRotation * yRotation * xRotation; + } + + static TQuaternion NLerp(TQuaternion from, TQuaternion to, vec_t t) + { + return (from * (vec_t{1.0} - t) + to * t).Unit(); + } + static TQuaternion SLerp(TQuaternion from, TQuaternion to, vec_t t) + { + auto dot = from.dot(to); + const auto dotThreshold = vec_t{0.9995}; + if (dot < vec_t{0.0}) + { + to = -to; + dot = -dot; + } + if (dot > dotThreshold) + { + return NLerp(from, to, t); + } + else + { + auto robustDot = clamp(dot, vec_t{-1.0}, vec_t{1.0}); + auto theta = TAngle::fromRad(g_acos(robustDot)); + auto scale0 = (theta * (vec_t{1.0} - t)).Sin(); + auto scale1 = (theta * t).Sin(); + return (from * scale0 + to * scale1).Unit(); + } + } +}; + +typedef TQuaternion FQuaternion; +typedef TQuaternion DQuaternion; diff --git a/source/common/utility/tarray.h b/source/common/utility/tarray.h index 1df7e5d39..dbd63002b 100644 --- a/source/common/utility/tarray.h +++ b/source/common/utility/tarray.h @@ -316,16 +316,9 @@ public: // Returns a reference to the last element T &Last() const { - assert(Count > 0); return Array[Count-1]; } - T SafeGet (size_t index, const T& defaultval) const - { - if (index <= Count) return Array[index]; - else return defaultval; - } - // returns address of first element T *Data() const { diff --git a/source/common/utility/vectors.h b/source/common/utility/vectors.h index 019eb9db1..439b7c7ec 100644 --- a/source/common/utility/vectors.h +++ b/source/common/utility/vectors.h @@ -724,6 +724,11 @@ struct TVector4 { } + TVector4(const vec_t v[4]) + : TVector4(v[0], v[1], v[2], v[3]) + { + } + void Zero() { Z = Y = X = W = 0; @@ -846,22 +851,6 @@ struct TVector4 return TVector4(v.X * scalar, v.Y * scalar, v.Z * scalar, v.W * scalar); } - // Multiply as Quaternion - TVector4& operator*= (const TVector4& v) - { - *this = *this * v; - return *this; - } - - friend TVector4 operator* (const TVector4& v1, const TVector4& v2) - { - return TVector4(v2.W * v1.X + v2.X * v1.W + v2.Y * v1.Z - v1.Z * v1.Y, - v2.W * v1.Y + v2.Y * v1.W + v2.Z * v1.X - v2.X * v1.Z, - v2.W * v1.Z + v2.Z * v1.W + v2.X * v1.Y - v2.Y * v1.X, - v2.W * v1.W - v2.X * v1.X - v2.Y * v1.Y - v2.Z * v1.Z - ); - } - // Scalar division TVector4 &operator/= (vec_t scalar) { @@ -1003,6 +992,11 @@ struct TVector4 { return X*other.X + Y*other.Y + Z*other.Z + W*other.W; } + + vec_t dot(const TVector4 &other) const + { + return X*other.X + Y*other.Y + Z*other.Z + W*other.W; + } }; template @@ -1726,7 +1720,6 @@ inline TMatrix3x3::TMatrix3x3(const TVector3 &axis, TAngle degrees) Cells[2][2] = T( (t-txx-tyy) + c ); } - typedef TVector2 FVector2; typedef TVector3 FVector3; typedef TVector4 FVector4; @@ -1803,5 +1796,4 @@ protected: float m_d; }; - #endif