Files
DoomRTX/neo/quake4/framework/DeclPlayback.cpp
T
2026-05-09 22:10:40 -07:00

976 lines
18 KiB
C++

/*
===========================================================================
Quake 4 / Raven playback decl support
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "DeclPlayback.h"
rvDeclPlaybackEdit* declPlaybackEdit = NULL;
/*
================
rvDeclPlayback::rvDeclPlayback
================
*/
rvDeclPlayback::rvDeclPlayback(void) {
flags = 0;
frameRate = 15.0f;
duration = 0.0f;
origin.Zero();
bounds.Clear();
//points.SetGranularity(16);
//angles.SetGranularity(16);
//buttons.SetGranularity(16);
}
/*
================
rvDeclPlayback::~rvDeclPlayback
================
*/
rvDeclPlayback::~rvDeclPlayback(void) {
FreeData();
}
/*
================
rvDeclPlayback::DefaultDefinition
================
*/
const char* rvDeclPlayback::DefaultDefinition(void) const {
return "{ sequence { } data { } }";
}
/*
================
rvDeclPlayback::FreeData
================
*/
void rvDeclPlayback::FreeData(void) {
points.Clear();
angles.Clear();
buttons.Clear();
flags = 0;
frameRate = 15.0f;
duration = 0.0f;
origin.Zero();
bounds.Clear();
}
/*
================
rvDeclPlayback::Size
================
*/
size_t rvDeclPlayback::Size(void) const {
return sizeof(rvDeclPlayback) +
points.GetNumValues() * sizeof(idVec3) +
angles.GetNumValues() * sizeof(idAngles) +
buttons.Num() * sizeof(rvButtonState);
}
/*
================
rvDeclPlayback::ParseSample
================
*/
void rvDeclPlayback::ParseSample(idLexer* src, idVec3& pos, idAngles& ang) {
idToken token;
if (!src->ReadToken(&token)) {
return;
}
while (token.Icmp("}") != 0) {
if (token.Icmp("pos") == 0) {
pos.x = src->ParseFloat();
src->ExpectTokenString(",");
pos.y = src->ParseFloat();
src->ExpectTokenString(",");
pos.z = src->ParseFloat();
SetHasPositions(true);
}
else if (token.Icmp("ang") == 0) {
ang.pitch = src->ParseFloat();
src->ExpectTokenString(",");
ang.yaw = src->ParseFloat();
ang.roll = 0.0f;
SetHasAngles(true);
}
else if (token.Icmp("rotate") == 0) {
ang.pitch = src->ParseFloat();
src->ExpectTokenString(",");
ang.yaw = src->ParseFloat();
src->ExpectTokenString(",");
ang.roll = src->ParseFloat();
SetHasAngles(true);
}
else if (token.Icmp("down") == 0 ||
token.Icmp("up") == 0 ||
token.Icmp("impulse") == 0) {
src->ParseInt();
}
else {
src->Warning("rvDeclPlayback::ParseSample: unknown token '%s'", token.c_str());
}
if (!src->ReadToken(&token)) {
return;
}
}
}
/*
================
rvDeclPlayback::ParseData
================
*/
bool rvDeclPlayback::ParseData(idLexer* src) {
idToken token;
idVec3 pos;
idAngles ang;
float time;
time = 0.0f;
pos.Zero();
ang.Zero();
src->ExpectTokenString("{");
if (!src->ReadToken(&token)) {
return false;
}
while (token.Icmp("}") != 0) {
if (token.Icmp("{") == 0) {
ParseSample(src, pos, ang);
points.AddValue(time, pos);
angles.AddValue(time, ang);
if (frameRate > 0.0f) {
time += 1.0f / frameRate;
}
}
if (!src->ReadToken(&token)) {
return false;
}
}
duration = time;
return true;
}
/*
================
rvDeclPlayback::ParseButton
================
*/
void rvDeclPlayback::ParseButton(idLexer* src, byte& button, rvButtonState& state) {
idToken token;
float time;
byte impulse;
impulse = 0;
time = src->ParseFloat();
if (!src->ReadToken(&token)) {
state.Init(time, button, impulse);
return;
}
while (token.Icmp("}") != 0) {
if (token.Icmp("down") == 0) {
button |= (byte)src->ParseInt();
}
else if (token.Icmp("up") == 0) {
button &= ~(byte)src->ParseInt();
}
else if (token.Icmp("impulse") == 0) {
impulse = (byte)src->ParseInt();
}
else {
src->Warning("rvDeclPlayback::ParseButton: unknown token '%s'", token.c_str());
}
if (!src->ReadToken(&token)) {
break;
}
}
state.Init(time, button, impulse);
}
/*
================
rvDeclPlayback::ParseButtons
================
*/
bool rvDeclPlayback::ParseButtons(idLexer* src) {
idToken token;
byte button;
rvButtonState state;
button = 0;
src->ExpectTokenString("{");
if (!src->ReadToken(&token)) {
return false;
}
while (token.Icmp("}") != 0) {
if (token.Icmp("{") == 0) {
ParseButton(src, button, state);
buttons.Append(state);
SetHasButtons(true);
}
if (!src->ReadToken(&token)) {
return false;
}
}
return true;
}
/*
================
rvDeclPlayback::ParseSequence
================
*/
bool rvDeclPlayback::ParseSequence(idLexer* src) {
idToken token;
src->ExpectTokenString("sequence");
src->ExpectTokenString("{");
if (!src->ReadToken(&token)) {
return false;
}
while (token.Icmp("}") != 0) {
if (token.Icmp("origin") == 0) {
origin.x = src->ParseFloat();
src->ExpectTokenString(",");
origin.y = src->ParseFloat();
src->ExpectTokenString(",");
origin.z = src->ParseFloat();
}
else if (token.Icmp("framerate") == 0 || token.Icmp("frameRate") == 0) {
frameRate = src->ParseFloat();
}
else if (token.Icmp("destination") == 0) {
idStr deprecated;
src->ParseRestOfLine(deprecated);
}
else {
src->Error("Invalid token %s in file %s.\n", token.c_str(), GetName());
return false;
}
if (!src->ReadToken(&token)) {
return false;
}
}
return true;
}
/*
================
rvDeclPlayback::Parse
================
*/
bool rvDeclPlayback::Parse(const char* text, const int textLength) {
idLexer src;
idToken token;
FreeData();
src.LoadMemory(text, textLength, GetFileName(), GetLineNum());
src.SetFlags(
LEXFL_NOSTRINGCONCAT |
LEXFL_NOSTRINGESCAPECHARS |
LEXFL_ALLOWPATHNAMES |
LEXFL_ALLOWMULTICHARLITERALS |
LEXFL_ALLOWBACKSLASHSTRINGCONCAT |
LEXFL_NOFATALERRORS |
LEXFL_ALLOWIPADDRESSES
);
src.SkipUntilString("{");
if (!ParseSequence(&src)) {
return false;
}
if (!src.ReadToken(&token)) {
return false;
}
while (token.Icmp("}") != 0) {
if (token.Icmp("data") == 0) {
if (!ParseData(&src)) {
return false;
}
}
else if (token.Icmp("buttons") == 0) {
if (!ParseButtons(&src)) {
return false;
}
}
else {
src.Warning("rvDeclPlayback::Parse: unknown token '%s'", token.c_str());
}
if (!src.ReadToken(&token)) {
return false;
}
}
SetOrigin();
return true;
}
/*
================
rvDeclPlayback::WriteSequence
================
*/
void rvDeclPlayback::WriteSequence(idFile_Memory& f) {
f.WriteFloatString("\tsequence\n\t{\n");
f.WriteFloatString("\t\torigin\t\t%.1f,%.1f,%.1f\n", origin.x, origin.y, origin.z);
f.WriteFloatString("\t\tframeRate\t%.1f\n", frameRate);
f.WriteFloatString("\t}\n");
}
/*
================
rvDeclPlayback::WriteData
================
*/
void rvDeclPlayback::WriteData(idFile_Memory& f) {
idVec3 oldPos;
idAngles oldAng;
oldPos.Zero();
oldAng.Zero();
if ((points.GetNumValues() == 0 && angles.GetNumValues() == 0) || !(flags & (PBFL_GET_POSITION | PBFL_GET_ANGLES))) {
return;
}
f.WriteFloatString("\tdata\n\t{\n");
for (int i = 0; i < points.GetNumValues(); i++) {
f.WriteFloatString("\t\t{ ");
if (flags & PBFL_GET_POSITION) {
const idVec3& pos = points.GetValue(i);
if (i == 0 || idMath::Fabs(pos.x - oldPos.x) > 0.0625f ||
idMath::Fabs(pos.y - oldPos.y) > 0.0625f ||
idMath::Fabs(pos.z - oldPos.z) > 0.0625f) {
f.WriteFloatString("pos %.1f,%.1f,%.1f ", pos.x, pos.y, pos.z);
oldPos = pos;
}
}
if (flags & PBFL_GET_ANGLES) {
const idAngles& ang = angles.GetValue(i);
if (i == 0 || idMath::Fabs(ang.pitch - oldAng.pitch) > 0.0625f ||
idMath::Fabs(ang.yaw - oldAng.yaw) > 0.0625f ||
idMath::Fabs(ang.roll - oldAng.roll) > 0.0625f) {
if (ang.roll == 0.0f) {
f.WriteFloatString("ang %.1f,%.1f ", ang.pitch, ang.yaw);
}
else {
f.WriteFloatString("rotate %.1f,%.1f,%.1f ", ang.pitch, ang.yaw, ang.roll);
}
oldAng = ang;
}
}
f.WriteFloatString("}\n");
}
f.WriteFloatString("\t}\n");
}
/*
================
rvDeclPlayback::WriteButtons
================
*/
void rvDeclPlayback::WriteButtons(idFile_Memory& f) {
byte oldState;
if (buttons.Num() == 0 || !(flags & PBFL_GET_BUTTONS)) {
return;
}
oldState = 0;
f.WriteFloatString("\tbuttons\n\t{\n");
for (int i = 0; i < buttons.Num(); i++) {
const rvButtonState& button = buttons[i];
const byte changed = oldState ^ button.state;
if (changed == 0 && button.impulse == 0) {
continue;
}
f.WriteFloatString("\t\t{ %.3g ", button.time);
if (changed != 0) {
if (button.state & changed) {
f.WriteFloatString("down %d ", button.state & changed);
}
if (oldState & changed) {
f.WriteFloatString("up %d ", oldState & changed);
}
oldState = button.state;
}
if (button.impulse != 0) {
f.WriteFloatString("impulse %d ", button.impulse);
}
f.WriteFloatString("}\n");
}
f.WriteFloatString("\t}\n");
}
/*
================
rvDeclPlayback::RebuildTextSource
================
*/
bool rvDeclPlayback::RebuildTextSource(void) {
#if 0
idFile_Memory f;
f.WriteFloatString("\nplayback %s\n{\n", GetName());
WriteSequence(f);
WriteData(f);
WriteButtons(f);
f.WriteFloatString("}\n\n");
SetText(f.GetDataPtr());
#endif
return true;
}
/*
================
rvDeclPlayback::Start
================
*/
void rvDeclPlayback::Start(void) {
origin.Zero();
bounds.Clear();
points.Clear();
angles.Clear();
buttons.Clear();
//points.SetGranularity(60);
//angles.SetGranularity(60);
//buttons.SetGranularity(60);
duration = 0.0f;
}
/*
================
rvDeclPlayback::SetOrigin
================
*/
void rvDeclPlayback::SetOrigin(void) {
if (points.GetNumValues() <= 0) {
return;
}
const idVec3 first = points.GetValue(0);
origin += first;
bounds.Clear();
for (int i = 0; i < points.GetNumValues(); i++) {
idVec3 p = points.GetValue(i);
p -= first;
points.SetValue(i, p);
bounds.AddPoint(p);
}
}
/*
================
rvDeclPlayback::Copy
================
*/
void rvDeclPlayback::Copy(rvDeclPlayback* pb) {
flags = pb->flags & ~PBFL_ED_MASK;
frameRate = pb->frameRate;
duration = pb->duration;
origin = pb->origin;
bounds = pb->bounds;
points = pb->points;
angles = pb->angles;
buttons = pb->buttons;
}
/*
================
rvDeclPlayback::SetCurrentData
================
*/
bool rvDeclPlayback::SetCurrentData(float localTime, int control, rvDeclPlaybackData* pbd) {
if (control & PBFL_GET_POSITION) {
points.AddValue(localTime, pbd->GetPosition());
SetHasPositions(true);
}
if (control & PBFL_GET_ANGLES) {
angles.AddValue(localTime, pbd->GetAngles());
SetHasAngles(true);
}
if (control & PBFL_GET_BUTTONS) {
rvButtonState state;
state.Init(localTime, pbd->GetButtons(), pbd->GetImpulse());
buttons.Append(state);
SetHasButtons(true);
}
if (localTime > duration) {
duration = localTime;
}
return true;
}
/*
================
rvDeclPlayback::GetCurrentOffset
================
*/
bool rvDeclPlayback::GetCurrentOffset(float localTime, idVec3& pos) const {
if (points.GetNumValues() <= 0) {
pos.Zero();
return false;
}
pos = points.GetCurrentValue(localTime);
return true;
}
/*
================
rvDeclPlayback::GetCurrentAngles
================
*/
bool rvDeclPlayback::GetCurrentAngles(float localTime, idAngles& ang) const {
if (angles.GetNumValues() <= 0) {
ang.Zero();
return false;
}
ang = angles.GetCurrentValue(localTime);
return true;
}
/*
================
rvDeclPlayback::GetCurrentData
================
*/
bool rvDeclPlayback::GetCurrentData(int control, float localTime, float lastTime, rvDeclPlaybackData* pbd) const {
byte changed;
if (pbd == NULL) {
return false;
}
changed = 0;
if (control & PBFL_GET_POSITION) {
idVec3 offset;
if (GetCurrentOffset(localTime, offset)) {
if (control & PBFL_RELATIVE_POSITION) {
pbd->SetPosition(offset);
}
else {
pbd->SetPosition(origin + offset);
}
changed |= PBFL_GET_POSITION;
}
}
if (control & PBFL_GET_ANGLES) {
idAngles currentAngles;
if (GetCurrentAngles(localTime, currentAngles)) {
pbd->SetAngles(currentAngles);
changed |= PBFL_GET_ANGLES;
}
}
if (control & PBFL_GET_VELOCITY) {
idVec3 now;
idVec3 prev;
float delta;
delta = localTime - lastTime;
if (delta <= 0.0f) {
delta = 1.0f / Max(frameRate, 1.0f);
}
if (GetCurrentOffset(localTime, now) && GetCurrentOffset(localTime - delta, prev)) {
pbd->SetVelocity((now - prev) / delta);
changed |= PBFL_GET_VELOCITY;
}
}
if (control & PBFL_GET_ACCELERATION) {
idVec3 now;
idVec3 prev;
idVec3 prev2;
float delta;
delta = localTime - lastTime;
if (delta <= 0.0f) {
delta = 1.0f / Max(frameRate, 1.0f);
}
if (GetCurrentOffset(localTime, now) &&
GetCurrentOffset(localTime - delta, prev) &&
GetCurrentOffset(localTime - delta * 2.0f, prev2)) {
const idVec3 vel0 = (now - prev) / delta;
const idVec3 vel1 = (prev - prev2) / delta;
pbd->SetAcceleration((vel0 - vel1) / delta);
changed |= PBFL_GET_ACCELERATION;
}
}
if (control & PBFL_GET_ANGLES_FROM_VEL) {
const idVec3& vel = pbd->GetVelocity();
if (vel.LengthSqr() > 0.0001f) {
pbd->SetAngles(vel.ToAngles());
changed |= PBFL_GET_ANGLES;
}
}
if (control & PBFL_GET_BUTTONS) {
byte currentButton;
byte impulse;
currentButton = 0;
impulse = 0;
for (int i = 0; i < buttons.Num(); i++) {
const rvButtonState& state = buttons[i];
if (state.time > localTime) {
break;
}
currentButton = state.state;
if (state.time > lastTime && state.time <= localTime) {
const byte previousButton = pbd->GetButtons();
const byte down = state.state & ~previousButton;
const byte up = previousButton & ~state.state;
if (down) {
pbd->SetButtons(state.state);
pbd->CallCallback(PBCB_BUTTON_DOWN, state.time);
}
if (up) {
pbd->SetButtons(state.state);
pbd->CallCallback(PBCB_BUTTON_UP, state.time);
}
if (state.impulse) {
impulse = state.impulse;
pbd->SetImpulse(impulse);
pbd->CallCallback(PBCB_IMPULSE, state.time);
}
}
}
pbd->SetButtons(currentButton);
pbd->SetImpulse(impulse);
changed |= PBFL_GET_BUTTONS;
}
pbd->SetChanged(changed);
return changed != 0;
}
/*
================
rvDeclPlayback::Finish
================
*/
bool rvDeclPlayback::Finish(float desiredDuration) {
rvDeclPlayback temp;
float speed;
SetOrigin();
temp.Copy(this);
FreeData();
flags = temp.flags;
frameRate = temp.frameRate;
origin = temp.origin;
if (desiredDuration >= 0.0f) {
duration = desiredDuration;
speed = temp.duration / Max(desiredDuration, 0.001f);
}
else {
duration = temp.duration;
speed = 1.0f;
}
for (int i = 0; i < temp.buttons.Num(); i++) {
const rvButtonState& src = temp.buttons[i];
if (i == 0 || src.state != temp.buttons[i - 1].state || src.impulse != 0) {
rvButtonState dst;
dst.Init(src.time / speed, src.state, src.impulse);
buttons.Append(dst);
}
}
float oldTime = 0.0f;
float newTime = 0.0f;
const float step = 1.0f / Max(frameRate, 1.0f);
while (oldTime <= temp.duration) {
rvDeclPlaybackData pbd;
pbd.Init();
temp.GetCurrentData(PBFL_GET_POSITION | PBFL_GET_ANGLES, oldTime, 0.0f, &pbd);
points.AddValue(newTime, pbd.GetPosition() - origin);
angles.AddValue(newTime, pbd.GetAngles());
oldTime += step * speed;
newTime += step;
}
//common->UpdateScreen();
return true;
}
/*
================
rvDeclPlayback::Validate
================
*/
bool rvDeclPlayback::Validate(const char* psText, int iTextLength, idStr& strReportTo) const {
idLexer src;
idToken token;
src.LoadMemory(psText, iTextLength, GetFileName(), GetLineNum());
src.SetFlags(
LEXFL_NOSTRINGCONCAT |
LEXFL_NOSTRINGESCAPECHARS |
LEXFL_ALLOWPATHNAMES |
LEXFL_ALLOWMULTICHARLITERALS |
LEXFL_ALLOWBACKSLASHSTRINGCONCAT |
LEXFL_NOFATALERRORS |
LEXFL_ALLOWIPADDRESSES
);
if (!src.SkipUntilString("{")) {
strReportTo += "Missing opening brace.\n";
return false;
}
if (!src.ReadToken(&token) || token.Icmp("sequence") != 0) {
strReportTo += "Missing sequence block.\n";
return false;
}
return true;
}
/*
================
rvDeclPlaybackEditLocal
================
*/
class rvDeclPlaybackEditLocal : public rvDeclPlaybackEdit {
public:
virtual bool Finish(rvDeclPlayback* edit, float desiredDuration) {
return edit->Finish(desiredDuration);
}
virtual void SetOrigin(rvDeclPlayback* edit) {
edit->SetOrigin();
}
virtual void SetOrigin(rvDeclPlayback* edit, idVec3& origin) {
edit->SetOrigin(origin);
}
virtual void Copy(rvDeclPlayback* edit, rvDeclPlayback* copy) {
edit->Copy(copy);
}
};
static rvDeclPlaybackEditLocal declPlaybackEditLocal;
/*
================
DeclPlayback_Init
================
*/
void DeclPlayback_Init(void) {
declPlaybackEdit = &declPlaybackEditLocal;
}
/*
================
DeclPlayback_Shutdown
================
*/
void DeclPlayback_Shutdown(void) {
if (declPlaybackEdit == &declPlaybackEditLocal) {
declPlaybackEdit = NULL;
}
}
#ifdef RV_BINARYDECLS
/*
================
rvDeclPlayback::rvDeclPlayback
================
*/
rvDeclPlayback::rvDeclPlayback(SerialInputStream& stream) {
flags = stream.ReadInt();
frameRate = stream.ReadFloat();
duration = stream.ReadFloat();
stream.ReadVec3(origin);
stream.ReadBounds(bounds);
int count = stream.ReadInt();
for (int i = 0; i < count; i++) {
float t;
idVec3 v;
t = stream.ReadFloat();
stream.ReadVec3(v);
points.AddValue(t, v);
}
count = stream.ReadInt();
for (int i = 0; i < count; i++) {
float t;
idAngles a;
t = stream.ReadFloat();
stream.ReadAngles(a);
angles.AddValue(t, a);
}
count = stream.ReadInt();
for (int i = 0; i < count; i++) {
rvButtonState state;
state.time = stream.ReadFloat();
state.state = stream.ReadByte();
state.impulse = stream.ReadByte();
buttons.Append(state);
}
}
/*
================
rvDeclPlayback::Write
================
*/
void rvDeclPlayback::Write(SerialOutputStream& stream) const {
stream.WriteInt(flags);
stream.WriteFloat(frameRate);
stream.WriteFloat(duration);
stream.WriteVec3(origin);
stream.WriteBounds(bounds);
stream.WriteInt(points.GetNumValues());
for (int i = 0; i < points.GetNumValues(); i++) {
stream.WriteFloat(points.GetTime(i));
stream.WriteVec3(points.GetValue(i));
}
stream.WriteInt(angles.GetNumValues());
for (int i = 0; i < angles.GetNumValues(); i++) {
stream.WriteFloat(angles.GetTime(i));
stream.WriteAngles(angles.GetValue(i));
}
stream.WriteInt(buttons.Num());
for (int i = 0; i < buttons.Num(); i++) {
stream.WriteFloat(buttons[i].time);
stream.WriteByte(buttons[i].state);
stream.WriteByte(buttons[i].impulse);
}
}
/*
================
rvDeclPlayback::AddReferences
================
*/
void rvDeclPlayback::AddReferences(void) const {
}
#endif