- backend update from GZDoom.

Mainly quaternion math and sound system cleanup.
This commit is contained in:
Christoph Oelckers 2022-11-24 16:56:46 +01:00
parent f1bfaac301
commit 2ffdf3d0e1
25 changed files with 919 additions and 259 deletions

View file

@ -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<uint32_t> list)
void SoundEngine::AddRandomSound(FSoundID Owner, TArray<FSoundID> 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);
}
@ -1822,6 +1823,8 @@ void S_SetSoundPaused(int state)
}
if ((state || i_soundinbackground) && !pauseext)
{
if (paused == 0)
{
S_ResumeSound(true);
if (GSnd != nullptr)
@ -1829,7 +1832,10 @@ void S_SetSoundPaused(int state)
GSnd->SetInactive(SoundRenderer::INACTIVE_Active);
}
}
}
else
{
if (paused == 0)
{
S_PauseSound(false, true);
if (GSnd != nullptr)
@ -1840,6 +1846,7 @@ void S_SetSoundPaused(int state)
}
}
}
}

View file

@ -2,12 +2,6 @@
#include "i_sound.h"
struct FRandomSoundList
{
TArray<uint32_t> Choices;
uint32_t Owner = 0;
};
enum
{
sfx_empty = -1
@ -15,6 +9,66 @@ enum
// Rolloff types
enum
{
ROLLOFF_Doom, // Linear rolloff with a logarithmic volume scale
ROLLOFF_Linear, // Linear rolloff with a linear volume scale
ROLLOFF_Log, // Logarithmic rolloff (standard hardware type)
ROLLOFF_Custom // Lookup volume from SNDCURVE
};
// An index into the S_sfx[] array.
class FSoundID
{
public:
FSoundID() = default;
private:
constexpr FSoundID(int id) : ID(id)
{
}
public:
static constexpr FSoundID fromInt(int i)
{
return FSoundID(i);
}
FSoundID(const FSoundID &other) = default;
FSoundID &operator=(const FSoundID &other) = default;
bool operator !=(FSoundID other) const
{
return ID != other.ID;
}
bool operator ==(FSoundID other) const
{
return ID == other.ID;
}
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;
};
constexpr FSoundID NO_SOUND = FSoundID::fromInt(0);
constexpr FSoundID INVALID_SOUND = FSoundID::fromInt(-1);
struct FRandomSoundList
{
TArray<FSoundID> Choices;
FSoundID Owner = NO_SOUND;
};
//
// SoundFX struct.
//
@ -49,86 +103,13 @@ struct sfxinfo_t
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 };
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.
};
// Rolloff types
enum
{
ROLLOFF_Doom, // Linear rolloff with a logarithmic volume scale
ROLLOFF_Linear, // Linear rolloff with a linear volume scale
ROLLOFF_Log, // Logarithmic rolloff (standard hardware type)
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
{
public:
FSoundID() = default;
static FSoundID byResId(int ndx)
{
return FSoundID(S_FindSoundByResID(ndx));
}
FSoundID(int id)
{
ID = id;
}
FSoundID(const char *name)
{
ID = S_FindSound(name);
}
FSoundID(const FString &name)
{
ID = S_FindSound(name.GetChars());
}
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
{
return ID != other;
}
operator int() const
{
return ID;
}
private:
int ID;
protected:
enum EDummy { NoInit };
FSoundID(EDummy) {}
};
class FSoundIDNoInit : public FSoundID
{
public:
FSoundIDNoInit() : FSoundID(NoInit) {}
using FSoundID::operator=;
};
struct FSoundChan : public FISoundChannel
{
@ -211,7 +192,7 @@ protected:
TArray<sfxinfo_t> S_sfx;
FRolloffInfo S_Rolloff{};
TArray<uint8_t> S_SoundCurve;
TMap<int, int> ResIdMap;
TMap<int, FSoundID> ResIdMap;
TArray<FRandomSoundList> 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<uint8_t> 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<FSoundChan*> 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<sfxinfo_t> &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<uint32_t> list);
void AddRandomSound(FSoundID Owner, TArray<FSoundID> 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);
}

View file

@ -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);

View file

@ -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)

View file

@ -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,9 +86,6 @@ 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 (C_Responder(ev))
@ -94,7 +93,6 @@ void D_ProcessEvents (void)
if (M_Responder(ev))
continue; // menu ate the event
}
}
if (sysCallbacks.G_Responder(ev) && ev->type == EV_KeyDown) keywasdown.Set(ev->data1);
}

View file

@ -46,7 +46,6 @@ struct SystemCallbacks
void (*LanguageChanged)(const char*);
bool (*OkForLocalization)(FTextureID, const char*);
FConfigFile* (*GetConfig)();
bool (*WantEscape)();
};
extern SystemCallbacks sysCallbacks;

View file

@ -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++;
}
}

View file

@ -94,6 +94,7 @@ public:
int mVirtWidth;
int mVirtHeight;
bool mCustomSizeSet;
bool mForceList;
void Reset();
};

View file

@ -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<VMValue> 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<FString> 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;

View file

@ -1299,7 +1299,7 @@ FxExpression *FxStringCast::Resolve(FCompileContext &ctx)
if (basex->isConstant())
{
ExpVal constval = static_cast<FxConstant *>(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<FxConstant *>(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<FxIdentifier *>(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.

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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<DQuaternion*>(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<DVector3*>(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<void, void*, double, double, double, double, double, double, double, double>(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<void, void*, double, double, double, double, double, double, double>(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.

View file

@ -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(); }

View file

@ -188,6 +188,12 @@ private:
template<typename RetType, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7>
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<void*>(static_cast<RetType(*)(P1, P2, P3, P4, P5, P6, P7)>(func))), asmjit::FuncSignature7<RetType, P1, P2, P3, P4, P5, P6, P7>()); }
template<typename RetType, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
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<void*>(static_cast<RetType(*)(P1, P2, P3, P4, P5, P6, P7, P8)>(func))), asmjit::FuncSignature8<RetType, P1, P2, P3, P4, P5, P6, P7, P8>()); }
template<typename RetType, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
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<void*>(static_cast<RetType(*)(P1, P2, P3, P4, P5, P6, P7, P8, P9)>(func))), asmjit::FuncSignature9<RetType, P1, P2, P3, P4, P5, P6, P7, P8, P9>()); }
FString regname;
size_t tmpPosInt32, tmpPosInt64, tmpPosIntPtr, tmpPosXmmSd, tmpPosXmmSs, tmpPosXmmPd, resultPosInt32, resultPosIntPtr, resultPosXmmSd;
std::vector<asmjit::X86Gp> regTmpInt32, regTmpInt64, regTmpIntPtr, regResultInt32, regResultIntPtr;
@ -305,6 +311,19 @@ private:
TArray<OpcodeLabel> 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;
};

View file

@ -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)

View file

@ -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<DQuaternion&>(reg.f[B]);
const DVector3& v = reinterpret_cast<DVector3&>(reg.f[C]);
reinterpret_cast<DVector3&>(reg.f[A]) = q * v;
}
NEXTOP;
OP(MULQQ_RR):
ASSERTF(a + 3); ASSERTF(B + 3); ASSERTF(C + 3);
{
const DQuaternion& q1 = reinterpret_cast<DQuaternion&>(reg.f[B]);
const DQuaternion& q2 = reinterpret_cast<DQuaternion&>(reg.f[C]);
reinterpret_cast<DQuaternion&>(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 &reg, 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:

View file

@ -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)

View file

@ -192,6 +192,7 @@ int RunEndoom()
return 0;
}
[[noreturn]]
void ST_Endoom()
{
int code = RunEndoom();

View file

@ -102,4 +102,5 @@ protected:
FStartScreen* GetGameStartScreen(int max_progress);
extern void ST_Endoom();
[[noreturn]]
void ST_Endoom();

View file

@ -0,0 +1,352 @@
#pragma once
#include "vectors.h"
template<typename vec_t>
class TQuaternion
{
public:
typedef TVector2<vec_t> Vector2;
typedef TVector3<vec_t> 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<const Vector2*>(this);
}
Vector2& XY()
{
return *reinterpret_cast<Vector2*>(this);
}
// returns the XY fields as a 2D-vector.
const Vector3& XYZ() const
{
return *reinterpret_cast<const Vector3*>(this);
}
Vector3& XYZ()
{
return *reinterpret_cast<Vector3*>(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<vec_t> operator* (const TQuaternion<vec_t>& q1, const TQuaternion<vec_t>& 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<vec_t> operator* (const TQuaternion<vec_t>& q, const TVector3<vec_t>& 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<vec_t> Conjugate()
{
return TQuaternion(-X, -Y, -Z, +W);
}
TQuaternion<vec_t> Inverse()
{
return Conjugate() / LengthSquared();
}
static TQuaternion<vec_t> AxisAngle(TVector3<vec_t> axis, TAngle<vec_t> 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<vec_t> ret;
ret.W = cosTheta;
ret.XYZ() = factor * axis;
return ret;
}
static TQuaternion<vec_t> FromAngles(TAngle<vec_t> yaw, TAngle<vec_t> pitch, TAngle<vec_t> 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<vec_t> NLerp(TQuaternion<vec_t> from, TQuaternion<vec_t> to, vec_t t)
{
return (from * (vec_t{1.0} - t) + to * t).Unit();
}
static TQuaternion<vec_t> SLerp(TQuaternion<vec_t> from, TQuaternion<vec_t> 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<vec_t>::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<float> FQuaternion;
typedef TQuaternion<double> DQuaternion;

View file

@ -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
{

View file

@ -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<class vec_t>
@ -1726,7 +1720,6 @@ inline TMatrix3x3<T>::TMatrix3x3(const TVector3<T> &axis, TAngle<T> degrees)
Cells[2][2] = T( (t-txx-tyy) + c );
}
typedef TVector2<float> FVector2;
typedef TVector3<float> FVector3;
typedef TVector4<float> FVector4;
@ -1803,5 +1796,4 @@ protected:
float m_d;
};
#endif