mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-15 00:41:55 +00:00
- backend update from GZDoom.
Mainly quaternion math and sound system cleanup.
This commit is contained in:
parent
f1bfaac301
commit
2ffdf3d0e1
25 changed files with 919 additions and 259 deletions
|
@ -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)
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ struct SystemCallbacks
|
|||
void (*LanguageChanged)(const char*);
|
||||
bool (*OkForLocalization)(FTextureID, const char*);
|
||||
FConfigFile* (*GetConfig)();
|
||||
bool (*WantEscape)();
|
||||
};
|
||||
|
||||
extern SystemCallbacks sysCallbacks;
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ public:
|
|||
int mVirtWidth;
|
||||
int mVirtHeight;
|
||||
bool mCustomSizeSet;
|
||||
bool mForceList;
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 ®, 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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -192,6 +192,7 @@ int RunEndoom()
|
|||
return 0;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void ST_Endoom()
|
||||
{
|
||||
int code = RunEndoom();
|
||||
|
|
|
@ -102,4 +102,5 @@ protected:
|
|||
|
||||
FStartScreen* GetGameStartScreen(int max_progress);
|
||||
|
||||
extern void ST_Endoom();
|
||||
[[noreturn]]
|
||||
void ST_Endoom();
|
||||
|
|
352
source/common/utility/quaternion.h
Normal file
352
source/common/utility/quaternion.h
Normal 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;
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue