You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2705 lines
61 KiB
2705 lines
61 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: 00000000000000Sample Source Code for "demo smoothing" tool in the engine. This could be ported into the client .dll |
|
// pretty easily -- ywb |
|
// |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
#include "glquake.h" |
|
#include "cl_demosmootherpanel.h" |
|
//#include "vid.h" |
|
#include "../engine/demo.h" |
|
#include <vgui_controls/Button.h> |
|
#include <vgui_controls/CheckButton.h> |
|
#include <KeyValues.h> |
|
#include <vgui_controls/Label.h> |
|
|
|
#include <vgui_controls/Controls.h> |
|
#include <vgui/ISystem.h> |
|
#include <vgui/ISurface.h> |
|
#include <vgui_controls/PropertySheet.h> |
|
#include <vgui/IVgui.h> |
|
#include <vgui_controls/FileOpenDialog.h> |
|
#include <vgui_controls/ProgressBar.h> |
|
#include <vgui_controls/ListPanel.h> |
|
#include <vgui_controls/MenuButton.h> |
|
#include <vgui_controls/Menu.h> |
|
#include <vgui_controls/TextEntry.h> |
|
#include <vgui/IInput.h> |
|
|
|
#include "filesystem.h" |
|
#include "filesystem_engine.h" |
|
#include "cl_demouipanel.h" |
|
#include "demofile/demoformat.h" |
|
#include "cl_demoactionmanager.h" |
|
#include "proto_version.h" |
|
#include "iprediction.h" |
|
#include "debugoverlay.h" |
|
#include "draw.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
using namespace vgui; |
|
|
|
static float Ease_In( float t ) |
|
{ |
|
float out = sqrt( t ); |
|
return out; |
|
} |
|
|
|
static float Ease_Out( float t ) |
|
{ |
|
float out = t * t; |
|
return out; |
|
} |
|
|
|
static float Ease_Both( float t ) |
|
{ |
|
return SimpleSpline( t ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: A menu button that knows how to parse cvar/command menu data from gamedir\scripts\debugmenu.txt |
|
//----------------------------------------------------------------------------- |
|
class CSmoothingTypeButton : public vgui::MenuButton |
|
{ |
|
typedef vgui::MenuButton BaseClass; |
|
|
|
public: |
|
// Construction |
|
CSmoothingTypeButton( vgui::Panel *parent, const char *panelName, const char *text ); |
|
|
|
private: |
|
// Menu associated with this button |
|
Menu *m_pMenu; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CSmoothingTypeButton::CSmoothingTypeButton(Panel *parent, const char *panelName, const char *text) |
|
: BaseClass( parent, panelName, text ) |
|
{ |
|
// Assume no menu |
|
m_pMenu = new Menu( this, "DemoSmootherTypeMenu" ); |
|
|
|
m_pMenu->AddMenuItem( "Smooth Selection Angles", "smoothselectionangles", parent ); |
|
m_pMenu->AddMenuItem( "Smooth Selection Origin", "smoothselectionorigin", parent ); |
|
m_pMenu->AddMenuItem( "Linear Interp Angles", "smoothlinearinterpolateangles", parent ); |
|
m_pMenu->AddMenuItem( "Linear Interp Origin", "smoothlinearinterpolateorigin", parent ); |
|
m_pMenu->AddMenuItem( "Spline Angles", "splineangles", parent ); |
|
m_pMenu->AddMenuItem( "Spline Origin", "splineorigin", parent ); |
|
m_pMenu->AddMenuItem( "Look At Points", "lookatpoints", parent ); |
|
m_pMenu->AddMenuItem( "Look At Points Spline", "lookatpointsspline", parent ); |
|
m_pMenu->AddMenuItem( "Two Point Origin Ease Out", "origineaseout", parent ); |
|
m_pMenu->AddMenuItem( "Two Point Origin Ease In", "origineasein", parent ); |
|
m_pMenu->AddMenuItem( "Two Point Origin Ease In/Out", "origineaseboth", parent ); |
|
m_pMenu->AddMenuItem( "Auto-setup keys 1/2 second", "keyshalf", parent ); |
|
m_pMenu->AddMenuItem( "Auto-setup keys 1 second", "keys1", parent ); |
|
m_pMenu->AddMenuItem( "Auto-setup keys 2 second", "keys2", parent ); |
|
m_pMenu->AddMenuItem( "Auto-setup keys 4 second", "keys4", parent ); |
|
|
|
m_pMenu->MakePopup(); |
|
MenuButton::SetMenu(m_pMenu); |
|
SetOpenDirection(MenuButton::UP); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: A menu button that knows how to parse cvar/command menu data from gamedir\scripts\debugmenu.txt |
|
//----------------------------------------------------------------------------- |
|
class CFixEdgeButton : public vgui::MenuButton |
|
{ |
|
typedef vgui::MenuButton BaseClass; |
|
|
|
public: |
|
// Construction |
|
CFixEdgeButton( vgui::Panel *parent, const char *panelName, const char *text ); |
|
|
|
private: |
|
// Menu associated with this button |
|
Menu *m_pMenu; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CFixEdgeButton::CFixEdgeButton(Panel *parent, const char *panelName, const char *text) |
|
: BaseClass( parent, panelName, text ) |
|
{ |
|
// Assume no menu |
|
m_pMenu = new Menu( this, "DemoSmootherEdgeFixType" ); |
|
|
|
m_pMenu->AddMenuItem( "Smooth Left", "smoothleft", parent ); |
|
m_pMenu->AddMenuItem( "Smooth Right", "smoothright", parent ); |
|
m_pMenu->AddMenuItem( "Smooth Both", "smoothboth", parent ); |
|
|
|
m_pMenu->MakePopup(); |
|
MenuButton::SetMenu(m_pMenu); |
|
SetOpenDirection(MenuButton::UP); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Basic help dialog |
|
//----------------------------------------------------------------------------- |
|
CDemoSmootherPanel::CDemoSmootherPanel( vgui::Panel *parent ) : Frame( parent, "DemoSmootherPanel") |
|
{ |
|
int w = 440; |
|
int h = 300; |
|
|
|
SetSize( w, h ); |
|
|
|
SetTitle("Demo Smoother", true); |
|
|
|
m_pType = new CSmoothingTypeButton( this, "DemoSmootherType", "Process->" ); |
|
|
|
m_pRevert = new vgui::Button( this, "DemoSmoothRevert", "Revert" );; |
|
m_pOK = new vgui::Button( this, "DemoSmoothOk", "OK" ); |
|
m_pCancel = new vgui::Button( this, "DemoSmoothCancel", "Cancel" ); |
|
|
|
m_pSave = new vgui::Button( this, "DemoSmoothSave", "Save" ); |
|
m_pReloadFromDisk = new vgui::Button( this, "DemoSmoothReload", "Reload" ); |
|
|
|
m_pStartFrame = new vgui::TextEntry( this, "DemoSmoothStartFrame" ); |
|
m_pEndFrame = new vgui::TextEntry( this, "DemoSmoothEndFrame" ); |
|
|
|
m_pPreviewOriginal = new vgui::Button( this, "DemoSmoothPreviewOriginal", "Show Original" ); |
|
m_pPreviewProcessed = new vgui::Button( this, "DemoSmoothPreviewProcessed", "Show Processed" ); |
|
|
|
m_pBackOff = new vgui::CheckButton( this, "DemoSmoothBackoff", "Back off" ); |
|
m_pHideLegend = new vgui::CheckButton( this, "DemoSmoothHideLegend", "Hide legend" ); |
|
|
|
m_pHideOriginal = new vgui::CheckButton( this, "DemoSmoothHideOriginal", "Hide original" ); |
|
m_pHideProcessed = new vgui::CheckButton( this, "DemoSmoothHideProcessed", "Hide processed" ); |
|
|
|
m_pSelectionInfo = new vgui::Label( this, "DemoSmoothSelectionInfo", "" ); |
|
m_pShowAllSamples = new vgui::CheckButton( this, "DemoSmoothShowAll", "Show All" ); |
|
m_pSelectSamples = new vgui::Button( this, "DemoSmoothSelect", "Select" ); |
|
|
|
m_pPauseResume = new vgui::Button( this, "DemoSmoothPauseResume", "Pause" ); |
|
m_pStepForward = new vgui::Button( this, "DemoSmoothStepForward", ">>" ); |
|
m_pStepBackward = new vgui::Button( this, "DemoSmoothStepBackward", "<<" ); |
|
|
|
m_pRevertPoint = new vgui::Button( this, "DemoSmoothRevertPoint", "Revert Pt." ); |
|
m_pToggleKeyFrame = new vgui::Button( this, "DemoSmoothSetKeyFrame", "Mark Keyframe" ); |
|
m_pToggleLookTarget = new vgui::Button( this, "DemoSmoothSetLookTarget", "Mark Look Target" ); |
|
|
|
m_pUndo = new vgui::Button( this, "DemoSmoothUndo", "Undo" ); |
|
m_pRedo = new vgui::Button( this, "DemoSmoothRedo", "Redo" ); |
|
|
|
m_pNextKey = new vgui::Button( this, "DemoSmoothNextKey", "+Key" ); |
|
m_pPrevKey = new vgui::Button( this, "DemoSmoothPrevKey", "-Key" ); |
|
|
|
m_pNextTarget = new vgui::Button( this, "DemoSmoothNextTarget", "+Target" ); |
|
m_pPrevTarget = new vgui::Button( this, "DemoSmoothPrevTarget", "-Target" ); |
|
|
|
m_pMoveCameraToPoint = new vgui::Button( this, "DemoSmoothCameraAtPoint", "Set View" ); |
|
|
|
m_pFixEdges = new CFixEdgeButton( this, "DemoSmoothFixFrameButton", "Edge->" ); |
|
m_pFixEdgeFrames = new vgui::TextEntry( this, "DemoSmoothFixFrames" ); |
|
|
|
m_pProcessKey = new vgui::Button( this, "DemoSmoothSaveKey", "Save Key" ); |
|
|
|
m_pGotoFrame = new vgui::TextEntry( this, "DemoSmoothGotoFrame" ); |
|
m_pGoto = new vgui::Button( this, "DemoSmoothGoto", "Jump To" ); |
|
|
|
//m_pCurrentDemo = new vgui::Label( this, "DemoName", "" ); |
|
|
|
vgui::ivgui()->AddTickSignal( GetVPanel(), 0 ); |
|
|
|
LoadControlSettings("Resource\\DemoSmootherPanel.res"); |
|
|
|
/* |
|
int xpos, ypos; |
|
parent->GetPos( xpos, ypos ); |
|
ypos += parent->GetTall(); |
|
|
|
SetPos( xpos, ypos ); |
|
*/ |
|
|
|
OnRefresh(); |
|
|
|
SetVisible( true ); |
|
SetSizeable( false ); |
|
SetMoveable( true ); |
|
|
|
Reset(); |
|
|
|
m_vecEyeOffset = Vector( 0, 0, 64 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CDemoSmootherPanel::~CDemoSmootherPanel() |
|
{ |
|
} |
|
|
|
void CDemoSmootherPanel::Reset( void ) |
|
{ |
|
ClearSmoothingInfo( m_Smoothing ); |
|
|
|
m_bPreviewing = false; |
|
m_bPreviewPaused = false; |
|
m_bPreviewOriginal = false; |
|
m_iPreviewStartTick = 0; |
|
m_fPreviewCurrentTime = 0.0f; |
|
m_nPreviewLastFrame = 0; |
|
|
|
m_bHasSelection = false; |
|
memset( m_nSelection, 0, sizeof( m_nSelection ) ); |
|
m_iSelectionTicksSpan = 0; |
|
|
|
m_bInputActive = false; |
|
memset( m_nOldCursor, 0, sizeof( m_nOldCursor ) ); |
|
|
|
WipeUndo(); |
|
WipeRedo(); |
|
m_bRedoPending = false; |
|
m_nUndoLevel = 0; |
|
m_bDirty = false; |
|
} |
|
|
|
|
|
void CDemoSmootherPanel::OnTick() |
|
{ |
|
BaseClass::OnTick(); |
|
|
|
m_pUndo->SetEnabled( CanUndo() ); |
|
m_pRedo->SetEnabled( CanRedo() ); |
|
|
|
m_pPauseResume->SetEnabled( m_bPreviewing ); |
|
m_pStepForward->SetEnabled( m_bPreviewing ); |
|
m_pStepBackward->SetEnabled( m_bPreviewing ); |
|
|
|
m_pSave->SetEnabled( m_bDirty ); |
|
|
|
demosmoothing_t *p = GetCurrent(); |
|
if ( p ) |
|
{ |
|
m_pToggleKeyFrame->SetEnabled( true ); |
|
m_pToggleLookTarget->SetEnabled( true ); |
|
|
|
m_pToggleKeyFrame->SetText( p->samplepoint ? "Delete Key" : "Make Key" ); |
|
m_pToggleLookTarget->SetText( p->targetpoint ? "Delete Target" : "Make Target" ); |
|
|
|
m_pProcessKey->SetEnabled( p->samplepoint ); |
|
} |
|
else |
|
{ |
|
m_pToggleKeyFrame->SetEnabled( false ); |
|
m_pToggleLookTarget->SetEnabled( false ); |
|
|
|
m_pProcessKey->SetEnabled( false ); |
|
} |
|
|
|
if ( m_bPreviewing ) |
|
{ |
|
m_pPauseResume->SetText( m_bPreviewPaused ? "Resume" : "Pause" ); |
|
} |
|
|
|
if ( !m_Smoothing.active ) |
|
{ |
|
m_pSelectionInfo->SetText( "No smoothing info loaded" ); |
|
return; |
|
} |
|
|
|
if ( !demoplayer->IsPlayingBack() ) |
|
{ |
|
m_pSelectionInfo->SetText( "Not playing back .dem" ); |
|
return; |
|
} |
|
|
|
if ( !m_bHasSelection ) |
|
{ |
|
m_pSelectionInfo->SetText( "No selection." ); |
|
return; |
|
} |
|
|
|
char sz[ 512 ]; |
|
if ( m_bPreviewing ) |
|
{ |
|
Q_snprintf( sz, sizeof( sz ), "%.3f at tick %i (%.3f s)", |
|
m_fPreviewCurrentTime, |
|
GetTickForFrame( m_nPreviewLastFrame ), |
|
TICKS_TO_TIME( m_iSelectionTicksSpan ) ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( sz, sizeof( sz ), "%i to %i (%.3f s)", |
|
m_Smoothing.smooth[ m_nSelection[ 0 ] ].frametick, |
|
m_Smoothing.smooth[ m_nSelection[ 1 ] ].frametick, |
|
TICKS_TO_TIME( m_iSelectionTicksSpan ) ); |
|
} |
|
m_pSelectionInfo->SetText( sz ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CDemoSmootherPanel::CanEdit() |
|
{ |
|
if ( !m_Smoothing.active ) |
|
return false; |
|
|
|
if ( !demoplayer->IsPlayingBack() ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *command - |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnCommand(const char *command) |
|
{ |
|
if ( !Q_strcasecmp( command, "cancel" ) ) |
|
{ |
|
OnRevert(); |
|
MarkForDeletion(); |
|
Reset(); |
|
OnClose(); |
|
} |
|
else if ( !Q_strcasecmp( command, "close" ) ) |
|
{ |
|
OnSave(); |
|
MarkForDeletion(); |
|
Reset(); |
|
OnClose(); |
|
} |
|
else if ( !Q_strcasecmp( command, "gotoframe" ) ) |
|
{ |
|
OnGotoFrame(); |
|
} |
|
else if ( !Q_strcasecmp( command, "undo" ) ) |
|
{ |
|
Undo(); |
|
} |
|
else if ( !Q_strcasecmp( command, "redo" ) ) |
|
{ |
|
Redo(); |
|
} |
|
else if ( !Q_strcasecmp( command, "revert" ) ) |
|
{ |
|
OnRevert(); |
|
} |
|
else if ( !Q_strcasecmp( command, "original" ) ) |
|
{ |
|
OnPreview( true ); |
|
} |
|
else if ( !Q_strcasecmp( command, "processed" ) ) |
|
{ |
|
OnPreview( false ); |
|
} |
|
else if ( !Q_strcasecmp( command, "save" ) ) |
|
{ |
|
OnSave(); |
|
} |
|
else if ( !Q_strcasecmp( command, "reload" ) ) |
|
{ |
|
OnReload(); |
|
} |
|
else if ( !Q_strcasecmp( command, "select" ) ) |
|
{ |
|
OnSelect(); |
|
} |
|
else if ( !Q_strcasecmp( command, "togglepause" ) ) |
|
{ |
|
OnTogglePause(); |
|
} |
|
else if ( !Q_strcasecmp( command, "stepforward" ) ) |
|
{ |
|
OnStep( true ); |
|
} |
|
else if ( !Q_strcasecmp( command, "stepbackward" ) ) |
|
{ |
|
OnStep( false ); |
|
} |
|
else if ( !Q_strcasecmp( command, "revertpoint" ) ) |
|
{ |
|
OnRevertPoint(); |
|
} |
|
else if ( !Q_strcasecmp( command, "keyframe" ) ) |
|
{ |
|
OnToggleKeyFrame(); |
|
} |
|
else if ( !Q_strcasecmp( command, "looktarget" ) ) |
|
{ |
|
OnToggleLookTarget(); |
|
} |
|
else if ( !Q_strcasecmp( command, "nextkey" ) ) |
|
{ |
|
OnNextKey(); |
|
} |
|
else if ( !Q_strcasecmp( command, "prevkey" ) ) |
|
{ |
|
OnPrevKey(); |
|
} |
|
else if ( !Q_strcasecmp( command, "nexttarget" ) ) |
|
{ |
|
OnNextTarget(); |
|
} |
|
else if ( !Q_strcasecmp( command, "prevtarget" ) ) |
|
{ |
|
OnPrevTarget(); |
|
} |
|
else if ( !Q_strcasecmp( command, "smoothselectionangles" ) ) |
|
{ |
|
OnSmoothSelectionAngles(); |
|
} |
|
else if ( !Q_strcasecmp( command, "keyshalf" ) ) |
|
{ |
|
OnSetKeys( 0.5f ); |
|
} |
|
else if ( !Q_strcasecmp( command, "keys1" ) ) |
|
{ |
|
OnSetKeys( 1.0f ); |
|
} |
|
else if ( !Q_strcasecmp( command, "keys2" ) ) |
|
{ |
|
OnSetKeys( 2.0f ); |
|
} |
|
else if ( !Q_strcasecmp( command, "keys4" ) ) |
|
{ |
|
OnSetKeys( 4.0f ); |
|
} |
|
else if ( !Q_strcasecmp( command, "smoothselectionorigin" ) ) |
|
{ |
|
OnSmoothSelectionOrigin(); |
|
} |
|
else if ( !Q_strcasecmp( command, "smoothlinearinterpolateangles" ) ) |
|
{ |
|
OnLinearInterpolateAnglesBasedOnEndpoints(); |
|
} |
|
else if ( !Q_strcasecmp( command, "smoothlinearinterpolateorigin" ) ) |
|
{ |
|
OnLinearInterpolateOriginBasedOnEndpoints(); |
|
} |
|
else if ( !Q_strcasecmp( command, "splineorigin" ) ) |
|
{ |
|
OnSplineSampleOrigin(); |
|
} |
|
else if ( !Q_strcasecmp( command, "splineangles" ) ) |
|
{ |
|
OnSplineSampleAngles(); |
|
} |
|
else if ( !Q_strcasecmp( command, "lookatpoints" ) ) |
|
{ |
|
OnLookAtPoints( false ); |
|
} |
|
else if ( !Q_strcasecmp( command, "lookatpointsspline" ) ) |
|
{ |
|
OnLookAtPoints( true ); |
|
} |
|
else if ( !Q_strcasecmp( command, "smoothleft" ) ) |
|
{ |
|
OnSmoothEdges( true, false ); |
|
} |
|
else if ( !Q_strcasecmp( command, "smoothright" ) ) |
|
{ |
|
OnSmoothEdges( false, true ); |
|
} |
|
else if ( !Q_strcasecmp( command, "smoothboth" ) ) |
|
{ |
|
OnSmoothEdges( true, true ); |
|
} |
|
else if ( !Q_strcasecmp( command, "origineasein" ) ) |
|
{ |
|
OnOriginEaseCurve( Ease_In ); |
|
} |
|
else if ( !Q_strcasecmp( command, "origineaseout" ) ) |
|
{ |
|
OnOriginEaseCurve( Ease_Out ); |
|
} |
|
else if ( !Q_strcasecmp( command, "origineaseboth" ) ) |
|
{ |
|
OnOriginEaseCurve( Ease_Both ); |
|
} |
|
else if ( !Q_strcasecmp( command, "processkey" ) ) |
|
{ |
|
OnSaveKey(); |
|
} |
|
else if ( !Q_strcasecmp( command, "setview" ) ) |
|
{ |
|
OnSetView(); |
|
} |
|
else |
|
{ |
|
BaseClass::OnCommand( command ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnSave() |
|
{ |
|
if ( !m_Smoothing.active ) |
|
return; |
|
|
|
SaveSmoothingInfo( demoaction->GetCurrentDemoFile(), m_Smoothing ); |
|
WipeUndo(); |
|
m_bDirty = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnReload() |
|
{ |
|
WipeUndo(); |
|
WipeRedo(); |
|
LoadSmoothingInfo( demoaction->GetCurrentDemoFile(), m_Smoothing ); |
|
m_bDirty = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnRevert() |
|
{ |
|
OnRefresh(); |
|
if ( !m_Smoothing.active ) |
|
{ |
|
LoadSmoothingInfo( demoaction->GetCurrentDemoFile(), m_Smoothing ); |
|
WipeUndo(); |
|
WipeRedo(); |
|
} |
|
else |
|
{ |
|
ClearSmoothingInfo( m_Smoothing ); |
|
WipeUndo(); |
|
WipeRedo(); |
|
} |
|
|
|
m_bDirty = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnRefresh() |
|
{ |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pScheme - |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) |
|
{ |
|
BaseClass::ApplySchemeSettings( pScheme ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CDemoSmootherPanel::GetStartFrame() |
|
{ |
|
char text[ 32 ]; |
|
m_pStartFrame->GetText( text, sizeof( text ) ); |
|
int tick = atoi( text ); |
|
return GetFrameForTick( tick ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CDemoSmootherPanel::GetEndFrame() |
|
{ |
|
char text[ 32 ]; |
|
m_pEndFrame->GetText( text, sizeof( text ) ); |
|
int tick = atoi( text ); |
|
return GetFrameForTick( tick ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : original - |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnPreview( bool original ) |
|
{ |
|
if ( !CanEdit() ) |
|
return; |
|
|
|
if ( !m_bHasSelection ) |
|
{ |
|
Con_Printf( "Must have smoothing selection active\n" ); |
|
return; |
|
} |
|
|
|
m_bPreviewing = true; |
|
m_bPreviewPaused = false; |
|
m_bPreviewOriginal = original; |
|
SetLastFrame( false, max( 0, m_nSelection[0] - 10 ) ); |
|
m_iPreviewStartTick = GetTickForFrame( m_nPreviewLastFrame ); |
|
m_fPreviewCurrentTime = TICKS_TO_TIME( m_iPreviewStartTick ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : frame - |
|
// elapsed - |
|
// info - |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CDemoSmootherPanel::OverrideView( democmdinfo_t& info, int tick ) |
|
{ |
|
if ( !CanEdit() ) |
|
return false; |
|
|
|
if ( !demoplayer->IsPlaybackPaused() ) |
|
return false; |
|
|
|
if ( m_bPreviewing ) |
|
{ |
|
if ( m_bPreviewPaused && GetCurrent() && GetCurrent()->samplepoint ) |
|
{ |
|
info.viewOrigin = GetCurrent()->vecmoved; |
|
info.viewAngles = GetCurrent()->angmoved; |
|
info.localViewAngles = info.viewAngles; |
|
|
|
bool back_off = m_pBackOff->IsSelected(); |
|
if ( back_off ) |
|
{ |
|
Vector fwd; |
|
AngleVectors( info.viewAngles, &fwd, NULL, NULL ); |
|
|
|
info.viewOrigin -= fwd * 75.0f; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
// TODO: Hook up previewing view |
|
if ( !m_bPreviewPaused ) |
|
{ |
|
m_fPreviewCurrentTime += host_frametime; |
|
} |
|
|
|
if ( GetInterpolatedViewPoint( info.viewOrigin, info.viewAngles ) ) |
|
{ |
|
info.localViewAngles = info.viewAngles; |
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
bool back_off = m_pBackOff->IsSelected(); |
|
if ( back_off ) |
|
{ |
|
int useframe = GetFrameForTick( tick ); |
|
|
|
if ( useframe < m_Smoothing.smooth.Count() && useframe >= 0 ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ useframe ]; |
|
Vector fwd; |
|
AngleVectors( p->info.viewAngles, &fwd, NULL, NULL ); |
|
|
|
info.viewOrigin = p->info.viewOrigin - fwd * 75.0f; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void DrawVecForward( bool active, const Vector& origin, const QAngle& angles, int r, int g, int b ) |
|
{ |
|
Vector fwd; |
|
AngleVectors( angles, &fwd, NULL, NULL ); |
|
|
|
Vector end; |
|
end = origin + fwd * ( active ? 64 : 16 ); |
|
|
|
Draw_Line( origin, end, r, g, b, false ); |
|
} |
|
|
|
void GetColorForSample( bool original, bool samplepoint, bool targetpoint, demosmoothing_t *sample, int& r, int& g, int& b ) |
|
{ |
|
if ( samplepoint && sample->samplepoint ) |
|
{ |
|
r = 0; |
|
g = 255; |
|
b = 0; |
|
return; |
|
} |
|
|
|
if ( targetpoint && sample->targetpoint ) |
|
{ |
|
r = 255; |
|
g = 0; |
|
b = 0; |
|
return; |
|
} |
|
|
|
if ( sample->selected ) |
|
{ |
|
if( original ) |
|
{ |
|
r = 255; |
|
g = 200; |
|
b = 100; |
|
} |
|
else |
|
{ |
|
r = 200; |
|
g = 100; |
|
b = 255; |
|
} |
|
|
|
if ( sample->samplepoint || sample->targetpoint ) |
|
{ |
|
r = 255; |
|
g = 255; |
|
b = 0; |
|
} |
|
|
|
return; |
|
} |
|
|
|
if ( original ) |
|
{ |
|
r = g = b = 255; |
|
} |
|
else |
|
{ |
|
r = 150; |
|
g = 255; |
|
b = 100; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : origin - |
|
// mins - |
|
// maxs - |
|
// angles - |
|
// r - |
|
// g - |
|
// b - |
|
// a - |
|
//----------------------------------------------------------------------------- |
|
void Draw_Box( const Vector& origin, const Vector& mins, const Vector& maxs, const QAngle& angles, int r, int g, int b, int a ) |
|
{ |
|
Draw_AlphaBox( origin, mins, maxs, angles, r, g, b, a); |
|
Draw_WireframeBox( origin, mins, maxs, angles, r, g, b ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *sample - |
|
// *next - |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::DrawSmoothingSample( bool original, bool processed, int samplenumber, demosmoothing_t *sample, demosmoothing_t *next ) |
|
{ |
|
int r, g, b; |
|
|
|
if ( original ) |
|
{ |
|
Draw_Line( sample->info.viewOrigin + m_vecEyeOffset, next->info.viewOrigin + m_vecEyeOffset, |
|
180, 180, 180, false ); |
|
|
|
GetColorForSample( true, false, false, sample, r, g, b ); |
|
|
|
DrawVecForward( false, sample->info.viewOrigin + m_vecEyeOffset, sample->info.viewAngles, r, g, b ); |
|
} |
|
|
|
if ( processed && sample->info.flags != 0 ) |
|
{ |
|
Draw_Line( sample->info.GetViewOrigin() + m_vecEyeOffset, next->info.GetViewOrigin() + m_vecEyeOffset, |
|
255, 255, 180, false ); |
|
|
|
GetColorForSample( false, false, false, sample, r, g, b ); |
|
|
|
DrawVecForward( false, sample->info.GetViewOrigin() + m_vecEyeOffset, sample->info.GetViewAngles(), r, g, b ); |
|
} |
|
if ( sample->samplepoint ) |
|
{ |
|
GetColorForSample( false, true, false, sample, r, g, b ); |
|
Draw_Box( sample->vecmoved + m_vecEyeOffset, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), sample->angmoved, r, g, b, 127 ); |
|
DrawVecForward( false, sample->vecmoved + m_vecEyeOffset, sample->angmoved, r, g, b ); |
|
} |
|
|
|
if ( sample->targetpoint ) |
|
{ |
|
GetColorForSample( false, false, true, sample, r, g, b ); |
|
Draw_Box( sample->vectarget, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), vec3_angle, r, g, b, 127 ); |
|
} |
|
|
|
if ( samplenumber == m_nPreviewLastFrame + 1 ) |
|
{ |
|
r = 50; |
|
g = 100; |
|
b = 250; |
|
Draw_Box( sample->info.GetViewOrigin() + m_vecEyeOffset, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), sample->info.GetViewAngles(), r, g, b, 92 ); |
|
} |
|
|
|
if ( sample->targetpoint ) |
|
{ |
|
r = 200; |
|
g = 200; |
|
b = 220; |
|
|
|
Draw_Line( sample->info.GetViewOrigin() + m_vecEyeOffset, sample->vectarget, r, g, b, false ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::DrawDebuggingInfo( int frame, float elapsed ) |
|
{ |
|
if ( !CanEdit() ) |
|
return; |
|
|
|
if ( !IsVisible() ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
if ( c < 2 ) |
|
return; |
|
|
|
int start = 0; |
|
int end = c - 1; |
|
|
|
bool showall = m_pShowAllSamples->IsSelected(); |
|
if ( !showall ) |
|
{ |
|
start = max( frame - 200, 0 ); |
|
end = min( frame + 200, c - 1 ); |
|
} |
|
|
|
if ( m_bHasSelection && !showall ) |
|
{ |
|
start = max( m_nSelection[ 0 ] - 10, 0 ); |
|
end = min( m_nSelection[ 1 ] + 10, c - 1 ); |
|
} |
|
|
|
bool draworiginal = !m_pHideOriginal->IsSelected(); |
|
bool drawprocessed = !m_pHideProcessed->IsSelected(); |
|
int i; |
|
|
|
demosmoothing_t *p = NULL; |
|
demosmoothing_t *prev = NULL; |
|
for ( i = start; i < end; i++ ) |
|
{ |
|
p = &m_Smoothing.smooth[ i ]; |
|
if ( prev && p ) |
|
{ |
|
DrawSmoothingSample( draworiginal, drawprocessed, i, prev, p ); |
|
} |
|
prev = p; |
|
} |
|
|
|
Vector org; |
|
QAngle ang; |
|
|
|
if ( m_bPreviewing ) |
|
{ |
|
if ( GetInterpolatedOriginAndAngles( true, org, ang ) ) |
|
{ |
|
DrawVecForward( true, org + m_vecEyeOffset, ang, 200, 10, 50 ); |
|
} |
|
} |
|
|
|
int useframe = frame; |
|
|
|
useframe = clamp( useframe, 0, c - 1 ); |
|
if ( useframe < c ) |
|
{ |
|
p = &m_Smoothing.smooth[ useframe ]; |
|
org = p->info.GetViewOrigin(); |
|
ang = p->info.GetViewAngles(); |
|
|
|
DrawVecForward( true, org + m_vecEyeOffset, ang, 100, 220, 250 ); |
|
Draw_Box( org + m_vecEyeOffset, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), ang, 100, 220, 250, 127 ); |
|
} |
|
|
|
DrawKeySpline(); |
|
DrawTargetSpline(); |
|
|
|
|
|
if ( !m_pHideLegend->IsSelected() ) |
|
{ |
|
DrawLegend( start, end ); |
|
} |
|
} |
|
|
|
void CDemoSmootherPanel::OnSelect() |
|
{ |
|
if ( !CanEdit() ) |
|
return; |
|
|
|
m_bHasSelection = false; |
|
m_iSelectionTicksSpan = 0; |
|
memset( m_nSelection, 0, sizeof( m_nSelection ) ); |
|
|
|
int start, end; |
|
start = GetStartFrame(); |
|
end = GetEndFrame(); |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
if ( c < 2 ) |
|
return; |
|
|
|
start = clamp( start, 0, c - 1 ); |
|
end = clamp( end, 0, c - 1 ); |
|
|
|
if ( start >= end ) |
|
return; |
|
|
|
m_nSelection[ 0 ] = start; |
|
m_nSelection[ 1 ] = end; |
|
m_bHasSelection = true; |
|
|
|
demosmoothing_t *startsample = &m_Smoothing.smooth[ start ]; |
|
demosmoothing_t *endsample = &m_Smoothing.smooth[ end ]; |
|
|
|
m_bDirty = true; |
|
PushUndo( "select" ); |
|
|
|
int i = 0; |
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
if ( i >= start && i <= end ) |
|
{ |
|
m_Smoothing.smooth[ i ].selected = true; |
|
} |
|
else |
|
{ |
|
m_Smoothing.smooth[ i ].selected = false; |
|
} |
|
} |
|
|
|
PushRedo( "select" ); |
|
|
|
m_iSelectionTicksSpan = endsample->frametick - startsample->frametick; |
|
} |
|
|
|
int CDemoSmootherPanel::GetFrameForTick( int tick ) |
|
{ |
|
int count = m_Smoothing.smooth.Count(); |
|
int last = count - 1; |
|
int first = 0; |
|
|
|
if ( count <= 0 ) |
|
{ |
|
return -1; // no valid index |
|
} |
|
else if ( count == 1 ) |
|
{ |
|
return 0; // return the one and only frame we have |
|
} |
|
|
|
if ( tick <= m_Smoothing.smooth[ first ].frametick ) |
|
return 0; |
|
|
|
if ( tick >= m_Smoothing.smooth[ last ].frametick ) |
|
return last; |
|
|
|
// binary search |
|
int middle; |
|
|
|
while ( true ) |
|
{ |
|
middle = (first+last)/2; |
|
|
|
int middleTick = m_Smoothing.smooth[ middle ].frametick; |
|
|
|
if ( tick == middleTick ) |
|
return middle; |
|
|
|
if ( tick > middleTick ) |
|
{ |
|
if ( first == middle ) |
|
return first; |
|
|
|
first = middle; |
|
} |
|
else |
|
{ |
|
if ( last == middle ) |
|
return last; |
|
|
|
last = middle; |
|
} |
|
} |
|
|
|
|
|
} |
|
|
|
|
|
int CDemoSmootherPanel::GetTickForFrame( int frame ) |
|
{ |
|
if ( !CanEdit() ) |
|
return -1; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
if ( c < 1 ) |
|
return -1; |
|
|
|
if ( frame < 0 ) |
|
return m_Smoothing.smooth[ 0 ].frametick; |
|
|
|
if ( frame >= c ) |
|
return m_Smoothing.smooth[ c - 1 ].frametick; |
|
|
|
|
|
return m_Smoothing.smooth[ frame ].frametick; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Interpolate Euler angles using quaternions to avoid singularities |
|
// Input : start - |
|
// end - |
|
// output - |
|
// frac - |
|
//----------------------------------------------------------------------------- |
|
static void InterpolateAngles( const QAngle& start, const QAngle& end, QAngle& output, float frac ) |
|
{ |
|
Quaternion src, dest; |
|
|
|
// Convert to quaternions |
|
AngleQuaternion( start, src ); |
|
AngleQuaternion( end, dest ); |
|
|
|
Quaternion result; |
|
|
|
// Slerp |
|
QuaternionSlerp( src, dest, frac, result ); |
|
|
|
// Convert to euler |
|
QuaternionAngles( result, output ); |
|
} |
|
|
|
bool CDemoSmootherPanel::GetInterpolatedOriginAndAngles( bool readonly, Vector& origin, QAngle& angles ) |
|
{ |
|
origin.Init(); |
|
angles.Init(); |
|
|
|
Assert( m_bPreviewing ); |
|
|
|
// Figure out the best samples |
|
int startframe = m_nPreviewLastFrame; |
|
int nextframe = startframe + 1; |
|
|
|
float time = m_fPreviewCurrentTime; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
|
|
do |
|
{ |
|
if ( startframe >= c || nextframe >= c ) |
|
{ |
|
if ( !readonly ) |
|
{ |
|
//m_bPreviewing = false; |
|
} |
|
return false; |
|
} |
|
|
|
demosmoothing_t *startsample = &m_Smoothing.smooth[ startframe ]; |
|
demosmoothing_t *endsample = &m_Smoothing.smooth[ nextframe ]; |
|
|
|
if ( nextframe >= min( m_nSelection[1] + 10, c - 1 ) ) |
|
{ |
|
if ( !readonly ) |
|
{ |
|
OnPreview( m_bPreviewOriginal ); |
|
} |
|
return false; |
|
} |
|
|
|
// If large dt, then jump ahead quickly in time |
|
float dt = TICKS_TO_TIME( endsample->frametick - startsample->frametick ); |
|
if ( dt > 1.0f ) |
|
{ |
|
startframe++; |
|
nextframe++; |
|
continue; |
|
} |
|
|
|
if ( TICKS_TO_TIME( endsample->frametick ) >= time ) |
|
{ |
|
// Found a spot |
|
float dt = TICKS_TO_TIME( endsample->frametick - startsample->frametick ); |
|
// Should never occur!!! |
|
if ( dt <= 0.0f ) |
|
{ |
|
return false; |
|
} |
|
|
|
float frac = (float)( time - TICKS_TO_TIME(startsample->frametick) ) / dt; |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
// Compute render origin/angles |
|
Vector renderOrigin; |
|
QAngle renderAngles; |
|
|
|
if ( m_bPreviewOriginal ) |
|
{ |
|
VectorLerp( startsample->info.viewOrigin, endsample->info.viewOrigin, frac, renderOrigin ); |
|
InterpolateAngles( startsample->info.viewAngles, endsample->info.viewAngles, renderAngles, frac ); |
|
} |
|
else |
|
{ |
|
VectorLerp( startsample->info.GetViewOrigin(), endsample->info.GetViewOrigin(), frac, renderOrigin ); |
|
InterpolateAngles( startsample->info.GetViewAngles(), endsample->info.GetViewAngles(), renderAngles, frac ); |
|
} |
|
|
|
origin = renderOrigin; |
|
angles = renderAngles; |
|
|
|
if ( !readonly ) |
|
{ |
|
SetLastFrame( false, startframe ); |
|
} |
|
|
|
break; |
|
} |
|
|
|
startframe++; |
|
nextframe++; |
|
|
|
} while ( true ); |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : t - |
|
//----------------------------------------------------------------------------- |
|
bool CDemoSmootherPanel::GetInterpolatedViewPoint( Vector& origin, QAngle& angles ) |
|
{ |
|
Assert( m_bPreviewing ); |
|
|
|
if ( !GetInterpolatedOriginAndAngles( false, origin, angles ) ) |
|
return false; |
|
|
|
bool back_off = m_pBackOff->IsSelected(); |
|
if ( back_off ) |
|
{ |
|
Vector fwd; |
|
AngleVectors( angles, &fwd, NULL, NULL ); |
|
|
|
origin = origin - fwd * 75.0f; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CDemoSmootherPanel::OnTogglePause() |
|
{ |
|
if ( !m_bPreviewing ) |
|
return; |
|
|
|
m_bPreviewPaused = !m_bPreviewPaused; |
|
} |
|
|
|
void CDemoSmootherPanel::OnStep( bool forward ) |
|
{ |
|
if ( !m_bPreviewing ) |
|
return; |
|
|
|
if ( !m_bPreviewPaused ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
|
|
SetLastFrame( false, m_nPreviewLastFrame + ( forward ? 1 : -1 ) ); |
|
SetLastFrame( false, clamp( m_nPreviewLastFrame, max( m_nSelection[ 0 ] - 10, 0 ), min( m_nSelection[ 1 ] + 10, c - 1 ) ) ); |
|
m_fPreviewCurrentTime = TICKS_TO_TIME( GetTickForFrame( m_nPreviewLastFrame ) ); |
|
} |
|
|
|
void CDemoSmootherPanel::DrawLegend( int startframe, int endframe ) |
|
{ |
|
int i; |
|
int skip = 20; |
|
|
|
bool back_off = m_pBackOff->IsSelected(); |
|
|
|
for ( i = startframe; i <= endframe; i++ ) |
|
{ |
|
bool show = ( i % skip ) == 0; |
|
demosmoothing_t *sample = &m_Smoothing.smooth[ i ]; |
|
|
|
if ( sample->samplepoint || sample->targetpoint ) |
|
show = true; |
|
|
|
if ( !show ) |
|
continue; |
|
|
|
char sz[ 512 ]; |
|
Q_snprintf( sz, sizeof( sz ), "%.3f", TICKS_TO_TIME(sample->frametick) ); |
|
|
|
Vector fwd; |
|
AngleVectors( sample->info.GetViewAngles(), &fwd, NULL, NULL ); |
|
|
|
CDebugOverlay::AddTextOverlay( sample->info.GetViewOrigin() + m_vecEyeOffset + fwd * ( back_off ? 5.0f : 50.0f ), 0, -1.0f, sz ); |
|
} |
|
} |
|
|
|
#define EASE_TIME 0.2f |
|
|
|
Quaternion SmoothAngles( CUtlVector< Quaternion >& stack ) |
|
{ |
|
int c = stack.Count(); |
|
Assert( c >= 1 ); |
|
|
|
float weight = 1.0f / (float)c; |
|
|
|
Quaternion output; |
|
output.Init(); |
|
|
|
int i; |
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
Quaternion t = stack[ i ]; |
|
QuaternionBlend( output, t, weight, output ); |
|
} |
|
|
|
return output; |
|
} |
|
|
|
Vector SmoothOrigin( CUtlVector< Vector >& stack ) |
|
{ |
|
int c = stack.Count(); |
|
Assert( c >= 1 ); |
|
|
|
Vector output; |
|
output.Init(); |
|
|
|
int i; |
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
Vector t = stack[ i ]; |
|
VectorAdd( output, t, output ); |
|
} |
|
|
|
VectorScale( output, 1.0f / (float)c, output ); |
|
|
|
return output; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnSetKeys(float interval) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "OnSetKeys" ); |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
int i; |
|
|
|
demosmoothing_t *lastkey = NULL; |
|
|
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
if ( !p->selected ) |
|
continue; |
|
|
|
p->angmoved = p->info.GetViewAngles();; |
|
p->vecmoved = p->info.GetViewOrigin(); |
|
p->samplepoint = false; |
|
|
|
if ( !lastkey || |
|
TICKS_TO_TIME( p->frametick - lastkey->frametick ) >= interval ) |
|
{ |
|
lastkey = p; |
|
p->samplepoint = true; |
|
} |
|
} |
|
|
|
PushRedo( "OnSetKeys" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnSmoothSelectionAngles( void ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
int i; |
|
|
|
CUtlVector< Quaternion > stack; |
|
|
|
m_bDirty = true; |
|
PushUndo( "smooth angles" ); |
|
|
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
if ( !p->selected ) |
|
continue; |
|
|
|
while ( stack.Count() > 10 ) |
|
{ |
|
stack.Remove( 0 ); |
|
} |
|
|
|
Quaternion q; |
|
AngleQuaternion( p->info.GetViewAngles(), q ); |
|
stack.AddToTail( q ); |
|
|
|
p->info.flags |= FDEMO_USE_ANGLES2; |
|
|
|
Quaternion aveq = SmoothAngles( stack ); |
|
|
|
QAngle outangles; |
|
QuaternionAngles( aveq, outangles ); |
|
|
|
p->info.viewAngles2 = outangles; |
|
p->info.localViewAngles2 = outangles; |
|
} |
|
|
|
PushRedo( "smooth angles" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnSmoothSelectionOrigin( void ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
int i; |
|
|
|
CUtlVector< Vector > stack; |
|
|
|
m_bDirty = true; |
|
PushUndo( "smooth origin" ); |
|
|
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
if ( !p->selected ) |
|
continue; |
|
|
|
if ( i < 2 ) |
|
continue; |
|
|
|
if ( i >= c - 2 ) |
|
continue; |
|
|
|
stack.RemoveAll(); |
|
|
|
for ( int j = -2; j <= 2; j++ ) |
|
{ |
|
stack.AddToTail( m_Smoothing.smooth[ i + j ].info.GetViewOrigin() ); |
|
} |
|
|
|
p->info.flags |= FDEMO_USE_ORIGIN2; |
|
|
|
Vector org = SmoothOrigin( stack ); |
|
|
|
p->info.viewOrigin2 = org; |
|
} |
|
|
|
PushRedo( "smooth origin" ); |
|
} |
|
|
|
void CDemoSmootherPanel::PerformLinearInterpolatedAngleSmoothing( int startframe, int endframe ) |
|
{ |
|
demosmoothing_t *pstart = &m_Smoothing.smooth[ startframe ]; |
|
demosmoothing_t *pend = &m_Smoothing.smooth[ endframe ]; |
|
|
|
int dt = pend->frametick - pstart->frametick; |
|
if ( dt <= 0 ) |
|
{ |
|
dt = 1; |
|
} |
|
|
|
CUtlVector< Quaternion > stack; |
|
|
|
Quaternion qstart, qend; |
|
AngleQuaternion( pstart->info.GetViewAngles(), qstart ); |
|
AngleQuaternion( pend->info.GetViewAngles(), qend ); |
|
|
|
for ( int i = startframe; i <= endframe; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
int elapsed = p->frametick - pstart->frametick; |
|
float frac = (float)elapsed / (float)dt; |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
p->info.flags |= FDEMO_USE_ANGLES2; |
|
|
|
Quaternion interpolated; |
|
|
|
QuaternionSlerp( qstart, qend, frac, interpolated ); |
|
|
|
QAngle outangles; |
|
QuaternionAngles( interpolated, outangles ); |
|
|
|
p->info.viewAngles2 = outangles; |
|
p->info.localViewAngles2 = outangles; |
|
} |
|
} |
|
|
|
void CDemoSmootherPanel::OnLinearInterpolateAnglesBasedOnEndpoints( void ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
if ( c < 2 ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "linear interp angles" ); |
|
|
|
PerformLinearInterpolatedAngleSmoothing( m_nSelection[ 0 ], m_nSelection[ 1 ] ); |
|
|
|
PushRedo( "linear interp angles" ); |
|
} |
|
|
|
void CDemoSmootherPanel::OnLinearInterpolateOriginBasedOnEndpoints( void ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
|
|
if ( c < 2 ) |
|
return; |
|
|
|
demosmoothing_t *pstart = &m_Smoothing.smooth[ m_nSelection[ 0 ] ]; |
|
demosmoothing_t *pend = &m_Smoothing.smooth[ m_nSelection[ 1 ] ]; |
|
|
|
int dt = pend->frametick - pstart->frametick; |
|
if ( dt <= 0 ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "linear interp origin" ); |
|
|
|
Vector vstart, vend; |
|
vstart = pstart->info.GetViewOrigin(); |
|
vend = pend->info.GetViewOrigin(); |
|
|
|
for ( int i = m_nSelection[0]; i <= m_nSelection[1]; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
float elapsed = p->frametick - pstart->frametick; |
|
float frac = elapsed / (float)dt; |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
p->info.flags |= FDEMO_USE_ORIGIN2; |
|
|
|
Vector interpolated; |
|
|
|
VectorLerp( vstart, vend, frac, interpolated ); |
|
|
|
p->info.viewOrigin2 = interpolated; |
|
} |
|
|
|
PushRedo( "linear interp origin" ); |
|
|
|
} |
|
|
|
void CDemoSmootherPanel::OnRevertPoint( void ) |
|
{ |
|
demosmoothing_t *p = GetCurrent(); |
|
if ( !p ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "revert point" ); |
|
|
|
p->angmoved = p->info.GetViewAngles(); |
|
p->vecmoved = p->info.GetViewOrigin(); |
|
p->samplepoint = false; |
|
|
|
p->vectarget = p->info.GetViewOrigin(); |
|
p->targetpoint = false; |
|
|
|
// m_ViewOrigin = p->info.viewOrigin; |
|
// m_ViewAngles = p->info.viewAngles; |
|
|
|
PushRedo( "revert point" ); |
|
} |
|
|
|
demosmoothing_t *CDemoSmootherPanel::GetCurrent( void ) |
|
{ |
|
if ( !CanEdit() ) |
|
return NULL; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
if ( c < 1 ) |
|
return NULL; |
|
|
|
int frame = clamp( m_nPreviewLastFrame, 0, c - 1 ); |
|
|
|
return &m_Smoothing.smooth[ frame ]; |
|
} |
|
|
|
void CDemoSmootherPanel::AddSamplePoints( bool usetarget, bool includeboundaries, CUtlVector< demosmoothing_t * >& points, int start, int end ) |
|
{ |
|
points.RemoveAll(); |
|
|
|
int i; |
|
for ( i = start; i <= end; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
if ( includeboundaries ) |
|
{ |
|
if ( i == start ) |
|
{ |
|
// Add it twice |
|
points.AddToTail( p ); |
|
continue; |
|
} |
|
else if ( i == end ) |
|
{ |
|
// Add twice |
|
points.AddToTail( p ); |
|
continue; |
|
} |
|
} |
|
|
|
if ( usetarget && p->targetpoint ) |
|
{ |
|
points.AddToTail( p ); |
|
} |
|
if ( !usetarget && p->samplepoint ) |
|
{ |
|
points.AddToTail( p ); |
|
} |
|
} |
|
} |
|
|
|
demosmoothing_t *CDemoSmootherPanel::GetBoundedSample( CUtlVector< demosmoothing_t * >& points, int sample ) |
|
{ |
|
int c = points.Count(); |
|
if ( sample < 0 ) |
|
return points[ 0 ]; |
|
else if ( sample >= c ) |
|
return points[ c - 1 ]; |
|
return points[ sample ]; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : t - |
|
// points - |
|
// prev - |
|
// next - |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::FindSpanningPoints( int tick, CUtlVector< demosmoothing_t * >& points, int& prev, int& next ) |
|
{ |
|
prev = -1; |
|
next = 0; |
|
int c = points.Count(); |
|
int i; |
|
|
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
demosmoothing_t *p = points[ i ]; |
|
|
|
if ( tick < p->frametick ) |
|
break; |
|
} |
|
|
|
next = i; |
|
prev = i - 1; |
|
|
|
next = clamp( next, 0, c - 1 ); |
|
prev = clamp( prev, 0, c - 1 ); |
|
} |
|
|
|
void CDemoSmootherPanel::OnSplineSampleOrigin( void ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
|
|
if ( c < 2 ) |
|
return; |
|
|
|
demosmoothing_t *pstart = &m_Smoothing.smooth[ m_nSelection[ 0 ] ]; |
|
demosmoothing_t *pend = &m_Smoothing.smooth[ m_nSelection[ 1 ] ]; |
|
|
|
if ( pend->frametick - pstart->frametick <= 0 ) |
|
return; |
|
|
|
CUtlVector< demosmoothing_t * > points; |
|
AddSamplePoints( false, false, points, m_nSelection[ 0 ], m_nSelection[ 1 ] ); |
|
|
|
if ( points.Count() <= 0 ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "spline origin" ); |
|
|
|
for ( int i = m_nSelection[0]; i <= m_nSelection[1]; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
demosmoothing_t *earliest; |
|
demosmoothing_t *current; |
|
demosmoothing_t *next; |
|
demosmoothing_t *latest; |
|
|
|
int cur; |
|
int cur2; |
|
|
|
FindSpanningPoints( p->frametick, points, cur, cur2 ); |
|
|
|
earliest = GetBoundedSample( points, cur - 1 ); |
|
current = GetBoundedSample( points, cur ); |
|
next = GetBoundedSample( points, cur2 ); |
|
latest = GetBoundedSample( points, cur2 + 1 ); |
|
|
|
float frac = 0.0f; |
|
float dt = next->frametick - current->frametick; |
|
if ( dt > 0.0f ) |
|
{ |
|
frac = (float)( p->frametick - current->frametick ) / dt; |
|
} |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
Vector splined; |
|
|
|
Catmull_Rom_Spline_Normalize( earliest->vecmoved, current->vecmoved, next->vecmoved, latest->vecmoved, frac, splined ); |
|
|
|
p->info.flags |= FDEMO_USE_ORIGIN2; |
|
p->info.viewOrigin2 = splined; |
|
} |
|
|
|
PushRedo( "spline origin" ); |
|
|
|
} |
|
|
|
void CDemoSmootherPanel::OnSplineSampleAngles( void ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
|
|
if ( c < 2 ) |
|
return; |
|
|
|
demosmoothing_t *pstart = &m_Smoothing.smooth[ m_nSelection[ 0 ] ]; |
|
demosmoothing_t *pend = &m_Smoothing.smooth[ m_nSelection[ 1 ] ]; |
|
|
|
if ( pend->frametick - pstart->frametick <= 0 ) |
|
return; |
|
|
|
CUtlVector< demosmoothing_t * > points; |
|
AddSamplePoints( false, false, points, m_nSelection[ 0 ], m_nSelection[ 1 ] ); |
|
|
|
if ( points.Count() <= 0 ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "spline angles" ); |
|
|
|
for ( int i = m_nSelection[0]; i <= m_nSelection[1]; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
demosmoothing_t *current; |
|
demosmoothing_t *next; |
|
|
|
int cur; |
|
int cur2; |
|
|
|
FindSpanningPoints( p->frametick, points, cur, cur2 ); |
|
|
|
current = GetBoundedSample( points, cur ); |
|
next = GetBoundedSample( points, cur2 ); |
|
|
|
float frac = 0.0f; |
|
float dt = next->frametick - current->frametick; |
|
if ( dt > 0.0f ) |
|
{ |
|
frac = (float)( p->frametick - current->frametick ) / dt; |
|
} |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
frac = SimpleSpline( frac ); |
|
|
|
QAngle splined; |
|
|
|
InterpolateAngles( current->angmoved, next->angmoved, splined, frac ); |
|
|
|
p->info.flags |= FDEMO_USE_ANGLES2; |
|
p->info.viewAngles2 = splined; |
|
p->info.localViewAngles2 = splined; |
|
} |
|
|
|
PushRedo( "spline angles" ); |
|
} |
|
|
|
void CDemoSmootherPanel::OnLookAtPoints( bool spline ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
int i; |
|
|
|
if ( c < 2 ) |
|
return; |
|
|
|
demosmoothing_t *pstart = &m_Smoothing.smooth[ m_nSelection[ 0 ] ]; |
|
demosmoothing_t *pend = &m_Smoothing.smooth[ m_nSelection[ 1 ] ]; |
|
|
|
if ( pend->frametick - pstart->frametick <= 0 ) |
|
return; |
|
|
|
CUtlVector< demosmoothing_t * > points; |
|
AddSamplePoints( true, false, points, m_nSelection[ 0 ], m_nSelection[ 1 ] ); |
|
|
|
if ( points.Count() < 1 ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "lookat points" ); |
|
|
|
for ( i = m_nSelection[0]; i <= m_nSelection[1]; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
demosmoothing_t *earliest; |
|
demosmoothing_t *current; |
|
demosmoothing_t *next; |
|
demosmoothing_t *latest; |
|
|
|
int cur; |
|
int cur2; |
|
|
|
FindSpanningPoints( p->frametick, points, cur, cur2 ); |
|
|
|
earliest = GetBoundedSample( points, cur - 1 ); |
|
current = GetBoundedSample( points, cur ); |
|
next = GetBoundedSample( points, cur2 ); |
|
latest = GetBoundedSample( points, cur2 + 1 ); |
|
|
|
float frac = 0.0f; |
|
float dt = next->frametick - current->frametick; |
|
if ( dt > 0.0f ) |
|
{ |
|
frac = (float)( p->frametick - current->frametick ) / dt; |
|
} |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
Vector splined; |
|
|
|
if ( spline ) |
|
{ |
|
Catmull_Rom_Spline_Normalize( earliest->vectarget, current->vectarget, next->vectarget, latest->vectarget, frac, splined ); |
|
} |
|
else |
|
{ |
|
Vector d = next->vectarget - current->vectarget; |
|
VectorMA( current->vectarget, frac, d, splined ); |
|
} |
|
|
|
Vector vecToTarget = splined - ( p->info.GetViewOrigin() + m_vecEyeOffset ); |
|
VectorNormalize( vecToTarget ); |
|
|
|
QAngle angles; |
|
VectorAngles( vecToTarget, angles ); |
|
|
|
p->info.flags |= FDEMO_USE_ANGLES2; |
|
p->info.viewAngles2 = angles; |
|
p->info.localViewAngles2 = angles; |
|
} |
|
|
|
PushRedo( "lookat points" ); |
|
} |
|
|
|
void CDemoSmootherPanel::SetLastFrame( bool jumptotarget, int frame ) |
|
{ |
|
// bool changed = frame != m_nPreviewLastFrame; |
|
|
|
m_nPreviewLastFrame = frame; |
|
|
|
/* if ( changed && !m_pLockCamera->IsSelected() ) |
|
{ |
|
// Reset default view/angles |
|
demosmoothing_t *p = GetCurrent(); |
|
if ( p ) |
|
{ |
|
if ( p->samplepoint && !jumptotarget ) |
|
{ |
|
m_ViewOrigin = p->vecmoved; |
|
m_ViewAngles = p->angmoved; |
|
} |
|
else if ( p->targetpoint && jumptotarget ) |
|
{ |
|
m_ViewOrigin = p->vectarget - m_vecEyeOffset; |
|
} |
|
else |
|
{ |
|
if ( m_bPreviewing && m_bPreviewOriginal ) |
|
{ |
|
m_ViewOrigin = p->info.viewOrigin; |
|
m_ViewAngles = p->info.viewAngles; |
|
} |
|
else |
|
{ |
|
m_ViewOrigin = p->info.GetViewOrigin(); |
|
m_ViewAngles = p->info.GetViewAngles(); |
|
} |
|
} |
|
} |
|
} */ |
|
} |
|
|
|
// Undo/Redo |
|
void CDemoSmootherPanel::Undo( void ) |
|
{ |
|
if ( m_UndoStack.Size() > 0 && m_nUndoLevel > 0 ) |
|
{ |
|
m_nUndoLevel--; |
|
DemoSmoothUndo *u = m_UndoStack[ m_nUndoLevel ]; |
|
Assert( u->undo ); |
|
|
|
m_Smoothing = *(u->undo); |
|
} |
|
InvalidateLayout(); |
|
} |
|
|
|
void CDemoSmootherPanel::Redo( void ) |
|
{ |
|
if ( m_UndoStack.Size() > 0 && m_nUndoLevel <= m_UndoStack.Size() - 1 ) |
|
{ |
|
DemoSmoothUndo *u = m_UndoStack[ m_nUndoLevel ]; |
|
Assert( u->redo ); |
|
|
|
m_Smoothing = *(u->redo); |
|
m_nUndoLevel++; |
|
} |
|
|
|
InvalidateLayout(); |
|
} |
|
|
|
void CDemoSmootherPanel::PushUndo( char *description ) |
|
{ |
|
Assert( !m_bRedoPending ); |
|
m_bRedoPending = true; |
|
WipeRedo(); |
|
|
|
// Copy current data |
|
CSmoothingContext *u = new CSmoothingContext; |
|
*u = m_Smoothing; |
|
DemoSmoothUndo *undo = new DemoSmoothUndo; |
|
undo->undo = u; |
|
undo->redo = NULL; |
|
undo->udescription = COM_StringCopy( description ); |
|
undo->rdescription = NULL; |
|
m_UndoStack.AddToTail( undo ); |
|
m_nUndoLevel++; |
|
} |
|
|
|
void CDemoSmootherPanel::PushRedo( char *description ) |
|
{ |
|
Assert( m_bRedoPending ); |
|
m_bRedoPending = false; |
|
|
|
// Copy current data |
|
CSmoothingContext *r = new CSmoothingContext; |
|
*r = m_Smoothing; |
|
DemoSmoothUndo *undo = m_UndoStack[ m_nUndoLevel - 1 ]; |
|
undo->redo = r; |
|
undo->rdescription = COM_StringCopy( description ); |
|
} |
|
|
|
void CDemoSmootherPanel::WipeUndo( void ) |
|
{ |
|
while ( m_UndoStack.Size() > 0 ) |
|
{ |
|
DemoSmoothUndo *u = m_UndoStack[ 0 ]; |
|
delete u->undo; |
|
delete u->redo; |
|
delete[] u->udescription; |
|
delete[] u->rdescription; |
|
delete u; |
|
m_UndoStack.Remove( 0 ); |
|
} |
|
m_nUndoLevel = 0; |
|
} |
|
|
|
void CDemoSmootherPanel::WipeRedo( void ) |
|
{ |
|
// Wipe everything above level |
|
while ( m_UndoStack.Size() > m_nUndoLevel ) |
|
{ |
|
DemoSmoothUndo *u = m_UndoStack[ m_nUndoLevel ]; |
|
delete u->undo; |
|
delete u->redo; |
|
delete[] u->udescription; |
|
delete[] u->rdescription; |
|
delete u; |
|
m_UndoStack.Remove( m_nUndoLevel ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : const char |
|
//----------------------------------------------------------------------------- |
|
const char *CDemoSmootherPanel::GetUndoDescription( void ) |
|
{ |
|
if ( m_nUndoLevel != 0 ) |
|
{ |
|
DemoSmoothUndo *u = m_UndoStack[ m_nUndoLevel - 1 ]; |
|
return u->udescription; |
|
} |
|
return "???undo"; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : const char |
|
//----------------------------------------------------------------------------- |
|
const char *CDemoSmootherPanel::GetRedoDescription( void ) |
|
{ |
|
if ( m_nUndoLevel != m_UndoStack.Size() ) |
|
{ |
|
DemoSmoothUndo *u = m_UndoStack[ m_nUndoLevel ]; |
|
return u->rdescription; |
|
} |
|
return "???redo"; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CDemoSmootherPanel::CanRedo( void ) |
|
{ |
|
if ( !m_UndoStack.Count() ) |
|
return false; |
|
|
|
if ( m_nUndoLevel == m_UndoStack.Count() ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CDemoSmootherPanel::CanUndo( void ) |
|
{ |
|
if ( !m_UndoStack.Count() ) |
|
return false; |
|
|
|
if ( m_nUndoLevel == 0 ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnToggleKeyFrame( void ) |
|
{ |
|
demosmoothing_t *p = GetCurrent(); |
|
if ( !p ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "toggle keyframe" ); |
|
|
|
// use orginal data by default |
|
p->angmoved = p->info.GetViewAngles(); |
|
p->vecmoved = p->info.GetViewOrigin(); |
|
|
|
if ( !p->samplepoint ) |
|
{ |
|
if ( g_pDemoUI->IsInDriveMode() ) |
|
{ |
|
g_pDemoUI->GetDriveViewPoint( p->vecmoved, p->angmoved ); |
|
} |
|
|
|
if ( g_pDemoUI2->IsInDriveMode() ) |
|
{ |
|
g_pDemoUI2->GetDriveViewPoint( p->vecmoved, p->angmoved ); |
|
} |
|
|
|
p->samplepoint = true; |
|
} |
|
else |
|
{ |
|
p->samplepoint = false; |
|
} |
|
|
|
PushRedo( "toggle keyframe" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnToggleLookTarget( void ) |
|
{ |
|
demosmoothing_t *p = GetCurrent(); |
|
if ( !p ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "toggle look target" ); |
|
|
|
// use orginal data by default |
|
p->vectarget = p->info.GetViewOrigin(); |
|
|
|
if ( !p->targetpoint ) |
|
{ |
|
QAngle angles; |
|
g_pDemoUI->GetDriveViewPoint( p->vectarget, angles ); |
|
g_pDemoUI2->GetDriveViewPoint( p->vectarget, angles ); |
|
|
|
p->targetpoint = true; |
|
} |
|
else |
|
{ |
|
p->targetpoint = false; |
|
} |
|
|
|
PushRedo( "toggle look target" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnNextKey() |
|
{ |
|
if( !m_bHasSelection ) |
|
return; |
|
|
|
int start = m_nPreviewLastFrame + 1; |
|
int maxmove = m_nSelection[1] - m_nSelection[0] + 1; |
|
|
|
int moved = 0; |
|
|
|
while ( moved < maxmove ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ start ]; |
|
if ( p->samplepoint ) |
|
{ |
|
SetLastFrame( false, start ); |
|
break; |
|
} |
|
|
|
start++; |
|
|
|
if ( start > m_nSelection[1] ) |
|
start = m_nSelection[0]; |
|
|
|
moved++; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnPrevKey() |
|
{ |
|
if( !m_bHasSelection ) |
|
return; |
|
|
|
int start = m_nPreviewLastFrame - 1; |
|
int maxmove = m_nSelection[1] - m_nSelection[0] + 1; |
|
|
|
int moved = 0; |
|
|
|
while ( moved < maxmove && start >= 0 ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ start ]; |
|
if ( p->samplepoint ) |
|
{ |
|
SetLastFrame( false, start ); |
|
break; |
|
} |
|
|
|
start--; |
|
|
|
if ( start < m_nSelection[0] ) |
|
start = m_nSelection[1]; |
|
|
|
moved++; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnNextTarget() |
|
{ |
|
if( !m_bHasSelection ) |
|
return; |
|
|
|
int start = m_nPreviewLastFrame + 1; |
|
int maxmove = m_nSelection[1] - m_nSelection[0] + 1; |
|
|
|
int moved = 0; |
|
|
|
while ( moved < maxmove ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ start ]; |
|
if ( p->targetpoint ) |
|
{ |
|
SetLastFrame( true, start ); |
|
break; |
|
} |
|
|
|
start++; |
|
|
|
if ( start > m_nSelection[1] ) |
|
start = m_nSelection[0]; |
|
|
|
moved++; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnPrevTarget() |
|
{ |
|
if( !m_bHasSelection ) |
|
return; |
|
|
|
int start = m_nPreviewLastFrame - 1; |
|
int maxmove = m_nSelection[1] - m_nSelection[0] + 1; |
|
|
|
int moved = 0; |
|
|
|
while ( moved < maxmove ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ start ]; |
|
if ( p->targetpoint ) |
|
{ |
|
SetLastFrame( true, start ); |
|
break; |
|
} |
|
|
|
start--; |
|
|
|
if ( start < m_nSelection[0] ) |
|
start = m_nSelection[1]; |
|
|
|
moved++; |
|
} |
|
} |
|
|
|
void CDemoSmootherPanel::DrawTargetSpline() |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
int i; |
|
|
|
if ( c < 2 ) |
|
return; |
|
|
|
demosmoothing_t *pstart = &m_Smoothing.smooth[ m_nSelection[ 0 ] ]; |
|
demosmoothing_t *pend = &m_Smoothing.smooth[ m_nSelection[ 1 ] ]; |
|
|
|
if ( pend->frametick - pstart->frametick <= 0 ) |
|
return; |
|
|
|
CUtlVector< demosmoothing_t * > points; |
|
AddSamplePoints( true, false, points, m_nSelection[ 0 ], m_nSelection[ 1 ] ); |
|
|
|
if ( points.Count() < 1 ) |
|
return; |
|
|
|
Vector previous(0,0,0); |
|
|
|
for ( i = m_nSelection[0]; i <= m_nSelection[1]; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
demosmoothing_t *earliest; |
|
demosmoothing_t *current; |
|
demosmoothing_t *next; |
|
demosmoothing_t *latest; |
|
|
|
int cur; |
|
int cur2; |
|
|
|
FindSpanningPoints( p->frametick, points, cur, cur2 ); |
|
|
|
earliest = GetBoundedSample( points, cur - 1 ); |
|
current = GetBoundedSample( points, cur ); |
|
next = GetBoundedSample( points, cur2 ); |
|
latest = GetBoundedSample( points, cur2 + 1 ); |
|
|
|
float frac = 0.0f; |
|
float dt = next->frametick - current->frametick; |
|
if ( dt > 0.0f ) |
|
{ |
|
frac = (float)( p->frametick - current->frametick ) / dt; |
|
} |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
Vector splined; |
|
|
|
Catmull_Rom_Spline_Normalize( earliest->vectarget, current->vectarget, next->vectarget, latest->vectarget, frac, splined ); |
|
|
|
if ( i > m_nSelection[0] ) |
|
{ |
|
Draw_Line( previous, splined, 0, 255, 0, false ); |
|
} |
|
|
|
previous = splined; |
|
} |
|
} |
|
|
|
void CDemoSmootherPanel::DrawKeySpline() |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
int i; |
|
|
|
if ( c < 2 ) |
|
return; |
|
|
|
demosmoothing_t *pstart = &m_Smoothing.smooth[ m_nSelection[ 0 ] ]; |
|
demosmoothing_t *pend = &m_Smoothing.smooth[ m_nSelection[ 1 ] ]; |
|
|
|
if ( pend->frametick - pstart->frametick <= 0 ) |
|
return; |
|
|
|
CUtlVector< demosmoothing_t * > points; |
|
AddSamplePoints( false, false, points, m_nSelection[ 0 ], m_nSelection[ 1 ] ); |
|
|
|
if ( points.Count() < 1 ) |
|
return; |
|
|
|
Vector previous(0,0,0); |
|
|
|
for ( i = m_nSelection[0]; i <= m_nSelection[1]; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
demosmoothing_t *earliest; |
|
demosmoothing_t *current; |
|
demosmoothing_t *next; |
|
demosmoothing_t *latest; |
|
|
|
int cur; |
|
int cur2; |
|
|
|
FindSpanningPoints( p->frametick, points, cur, cur2 ); |
|
|
|
earliest = GetBoundedSample( points, cur - 1 ); |
|
current = GetBoundedSample( points, cur ); |
|
next = GetBoundedSample( points, cur2 ); |
|
latest = GetBoundedSample( points, cur2 + 1 ); |
|
|
|
float frac = 0.0f; |
|
float dt = next->frametick - current->frametick; |
|
if ( dt > 0.0f ) |
|
{ |
|
frac = (float)( p->frametick - current->frametick ) / dt; |
|
} |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
Vector splined; |
|
|
|
Catmull_Rom_Spline_Normalize( earliest->vecmoved, current->vecmoved, next->vecmoved, latest->vecmoved, frac, splined ); |
|
|
|
splined += m_vecEyeOffset; |
|
|
|
if ( i > m_nSelection[0] ) |
|
{ |
|
Draw_Line( previous, splined, 0, 255, 0, false ); |
|
} |
|
|
|
previous = splined; |
|
} |
|
} |
|
|
|
void CDemoSmootherPanel::OnSmoothEdges( bool left, bool right ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
if ( !left && !right ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
|
|
// Get number of frames |
|
char sz[ 512 ]; |
|
m_pFixEdgeFrames->GetText( sz, sizeof( sz ) ); |
|
|
|
int frames = atoi( sz ); |
|
if ( frames <= 2 ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "smooth edges" ); |
|
|
|
if ( left && m_nSelection[0] > 0 ) |
|
{ |
|
PerformLinearInterpolatedAngleSmoothing( m_nSelection[ 0 ] - 1, m_nSelection[ 0 ] + frames ); |
|
} |
|
if ( right && m_nSelection[1] < c - 1 ) |
|
{ |
|
PerformLinearInterpolatedAngleSmoothing( m_nSelection[ 1 ] - frames, m_nSelection[ 1 ] + 1 ); |
|
} |
|
|
|
PushRedo( "smooth edges" ); |
|
} |
|
|
|
void CDemoSmootherPanel::OnSaveKey() |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
demosmoothing_t *p = GetCurrent(); |
|
if ( !p ) |
|
return; |
|
|
|
if ( !p->samplepoint ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "save key" ); |
|
|
|
p->info.viewAngles2 = p->angmoved; |
|
p->info.localViewAngles2 = p->angmoved; |
|
p->info.viewOrigin2 = p->vecmoved; |
|
p->info.flags |= FDEMO_USE_ORIGIN2; |
|
p->info.flags |= FDEMO_USE_ANGLES2; |
|
|
|
PushRedo( "save key" ); |
|
} |
|
|
|
void CDemoSmootherPanel::OnSetView() |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
demosmoothing_t *p = GetCurrent(); |
|
if ( !p ) |
|
return; |
|
|
|
Vector origin = p->info.GetViewOrigin(); |
|
QAngle angle = p->info.GetViewAngles(); |
|
|
|
g_pDemoUI->SetDriveViewPoint( origin, angle ); |
|
g_pDemoUI2->SetDriveViewPoint( origin, angle ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CDemoSmootherPanel::OnGotoFrame() |
|
{ |
|
int c = m_Smoothing.smooth.Count(); |
|
if ( c < 2 ) |
|
return; |
|
|
|
char sz[ 256 ]; |
|
m_pGotoFrame->GetText( sz, sizeof( sz ) ); |
|
int frame = atoi( sz ); |
|
|
|
if ( !m_bPreviewing ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
{ |
|
m_pStartFrame->SetText( va( "%i", 0 ) ); |
|
m_pEndFrame->SetText( va( "%i", c - 1 ) ); |
|
OnSelect(); |
|
} |
|
OnPreview( false ); |
|
OnTogglePause(); |
|
} |
|
|
|
if ( !m_bPreviewing ) |
|
return; |
|
|
|
SetLastFrame( false, frame ); |
|
m_iPreviewStartTick = GetTickForFrame( m_nPreviewLastFrame ); |
|
m_fPreviewCurrentTime = TICKS_TO_TIME( m_iPreviewStartTick ); |
|
} |
|
|
|
void CDemoSmootherPanel::OnOriginEaseCurve( EASEFUNC easefunc ) |
|
{ |
|
if ( !m_bHasSelection ) |
|
return; |
|
|
|
int c = m_Smoothing.smooth.Count(); |
|
|
|
if ( c < 2 ) |
|
return; |
|
|
|
demosmoothing_t *pstart = &m_Smoothing.smooth[ m_nSelection[ 0 ] ]; |
|
demosmoothing_t *pend = &m_Smoothing.smooth[ m_nSelection[ 1 ] ]; |
|
|
|
float dt = pend->frametick - pstart->frametick; |
|
if ( dt <= 0.0f ) |
|
return; |
|
|
|
m_bDirty = true; |
|
PushUndo( "ease origin" ); |
|
|
|
Vector vstart, vend; |
|
vstart = pstart->info.GetViewOrigin(); |
|
vend = pend->info.GetViewOrigin(); |
|
|
|
for ( int i = m_nSelection[0]; i <= m_nSelection[1]; i++ ) |
|
{ |
|
demosmoothing_t *p = &m_Smoothing.smooth[ i ]; |
|
|
|
float elapsed = p->frametick - pstart->frametick; |
|
float frac = elapsed / dt; |
|
|
|
// Apply ease function |
|
frac = (*easefunc)( frac ); |
|
|
|
frac = clamp( frac, 0.0f, 1.0f ); |
|
|
|
p->info.flags |= FDEMO_USE_ORIGIN2; |
|
|
|
Vector interpolated; |
|
|
|
VectorLerp( vstart, vend, frac, interpolated ); |
|
|
|
p->info.viewOrigin2 = interpolated; |
|
} |
|
|
|
PushRedo( "ease origin" ); |
|
} |
|
|
|
void CDemoSmootherPanel::ParseSmoothingInfo( CDemoFile &demoFile, CUtlVector< demosmoothing_t >& smooth ) |
|
{ |
|
democmdinfo_t info; |
|
int dummy; |
|
|
|
bool demofinished = false; |
|
while ( !demofinished ) |
|
{ |
|
int tick = 0; |
|
byte cmd; |
|
|
|
bool swallowmessages = true; |
|
do |
|
{ |
|
demoFile.ReadCmdHeader( cmd, tick ); |
|
|
|
// COMMAND HANDLERS |
|
switch ( cmd ) |
|
{ |
|
case dem_synctick: |
|
break; |
|
case dem_stop: |
|
{ |
|
swallowmessages = false; |
|
demofinished = true; |
|
} |
|
break; |
|
case dem_consolecmd: |
|
{ |
|
demoFile.ReadConsoleCommand(); |
|
} |
|
break; |
|
case dem_datatables: |
|
{ |
|
demoFile.ReadNetworkDataTables( NULL ); |
|
} |
|
break; |
|
case dem_usercmd: |
|
{ |
|
demoFile.ReadUserCmd( NULL, dummy ); |
|
|
|
} |
|
break; |
|
default: |
|
{ |
|
swallowmessages = false; |
|
} |
|
break; |
|
} |
|
} |
|
while ( swallowmessages ); |
|
|
|
if ( demofinished ) |
|
{ |
|
// StopPlayback(); |
|
return; |
|
} |
|
|
|
int curpos = demoFile.GetCurPos(); |
|
|
|
demoFile.ReadCmdInfo( info ); |
|
demoFile.ReadSequenceInfo( dummy, dummy ); |
|
demoFile.ReadRawData( NULL, 0 ); |
|
|
|
// Add to end of list |
|
demosmoothing_t smoothing_entry; |
|
|
|
smoothing_entry.file_offset = curpos; |
|
smoothing_entry.frametick = tick; |
|
smoothing_entry.info = info; |
|
smoothing_entry.samplepoint = false; |
|
smoothing_entry.vecmoved = info.GetViewOrigin(); |
|
smoothing_entry.angmoved = info.GetViewAngles(); |
|
smoothing_entry.targetpoint = false; |
|
smoothing_entry.vectarget = info.GetViewOrigin(); |
|
|
|
smooth.AddToTail( smoothing_entry ); |
|
} |
|
} |
|
|
|
void CDemoSmootherPanel::LoadSmoothingInfo( const char *filename, CSmoothingContext& smoothing ) |
|
{ |
|
char name[ MAX_OSPATH ]; |
|
Q_strncpy (name, filename, sizeof(name) ); |
|
Q_DefaultExtension( name, ".dem", sizeof( name ) ); |
|
|
|
CDemoFile demoFile; |
|
|
|
if ( !demoFile.Open( filename, true ) ) |
|
{ |
|
Con_Printf( "ERROR: couldn't open %s.\n", name ); |
|
return; |
|
} |
|
|
|
demoheader_t * header = demoFile.ReadDemoHeader(); |
|
|
|
if ( !header ) |
|
{ |
|
demoFile.Close(); |
|
return; |
|
} |
|
|
|
Con_Printf ("Smoothing demo from %s ...", name ); |
|
|
|
smoothing.active = true; |
|
Q_strncpy( smoothing.filename, name, sizeof(smoothing.filename) ); |
|
|
|
smoothing.smooth.RemoveAll(); |
|
|
|
ClearSmoothingInfo( smoothing ); |
|
|
|
ParseSmoothingInfo( demoFile, smoothing.smooth ); |
|
|
|
demoFile.Close(); |
|
|
|
//Performsmoothing( smooth ); |
|
//SaveSmoothedDemo( name, smooth ); |
|
|
|
Con_Printf ( " done.\n" ); |
|
} |
|
|
|
void CDemoSmootherPanel::ClearSmoothingInfo( CSmoothingContext& smoothing ) |
|
{ |
|
int c = smoothing.smooth.Count(); |
|
int i; |
|
|
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
demosmoothing_t *p = &smoothing.smooth[ i ]; |
|
p->info.Reset(); |
|
p->vecmoved = p->info.GetViewOrigin(); |
|
p->angmoved = p->info.GetViewAngles(); |
|
p->samplepoint = false; |
|
p->vectarget = p->info.GetViewOrigin(); |
|
p->targetpoint = false; |
|
} |
|
} |
|
|
|
void CDemoSmootherPanel::SaveSmoothingInfo( char const *filename, CSmoothingContext& smoothing ) |
|
{ |
|
// Nothing to do |
|
int c = smoothing.smooth.Count(); |
|
if ( !c ) |
|
return; |
|
|
|
IFileSystem *fs = g_pFileSystem; |
|
|
|
FileHandle_t infile, outfile; |
|
|
|
COM_OpenFile( filename, &infile ); |
|
if ( infile == FILESYSTEM_INVALID_HANDLE ) |
|
return; |
|
|
|
int filesize = fs->Size( infile ); |
|
|
|
char outfilename[ 512 ]; |
|
Q_StripExtension( filename, outfilename, sizeof( outfilename ) ); |
|
Q_strncat( outfilename, "_smooth", sizeof(outfilename), COPY_ALL_CHARACTERS ); |
|
Q_DefaultExtension( outfilename, ".dem", sizeof( outfilename ) ); |
|
outfile = fs->Open( outfilename, "wb" ); |
|
if ( outfile == FILESYSTEM_INVALID_HANDLE ) |
|
{ |
|
fs->Close( infile ); |
|
return; |
|
} |
|
|
|
int i; |
|
|
|
int lastwritepos = 0; |
|
for ( i = 0; i < c; i++ ) |
|
{ |
|
demosmoothing_t *p = &smoothing.smooth[ i ]; |
|
|
|
int copyamount = p->file_offset - lastwritepos; |
|
|
|
COM_CopyFileChunk( outfile, infile, copyamount ); |
|
|
|
fs->Seek( infile, p->file_offset, FILESYSTEM_SEEK_HEAD ); |
|
|
|
// wacky hacky overwriting |
|
fs->Write( &p->info, sizeof( democmdinfo_t ), outfile ); |
|
|
|
lastwritepos = fs->Tell( outfile ); |
|
fs->Seek( infile, p->file_offset + sizeof( democmdinfo_t ), FILESYSTEM_SEEK_HEAD ); |
|
} |
|
|
|
int final = filesize - lastwritepos; |
|
|
|
COM_CopyFileChunk( outfile, infile, final ); |
|
|
|
fs->Close( outfile ); |
|
fs->Close( infile ); |
|
} |