/* =========================================================================== 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