|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
#include "client_pch.h"
#include "cl_demosmootherpanel.h"
#include <vgui_controls/Button.h>
#include <vgui_controls/CheckButton.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 "cl_demouipanel.h"
#include "demofile/demoformat.h"
#include "cl_demoactionmanager.h"
#include "tier2/renderutils.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(Menu::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(Menu::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::OnVDMChanged( void ) { if ( IsVisible() ) { OnReload(); } else { Reset(); } }
//-----------------------------------------------------------------------------
// 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 ) { ConMsg( "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 );
RenderLine( origin, end, Color( r, g, b, 255 ), true ); }
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 ) { RenderBox( origin, angles, mins, maxs, Color( r, g, b, a ), false ); RenderWireframeBox( origin, angles, mins, maxs, Color( r, g, b, a ), true ); }
//-----------------------------------------------------------------------------
// 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 ) { RenderLine( sample->info.viewOrigin + m_vecEyeOffset, next->info.viewOrigin + m_vecEyeOffset, Color( 180, 180, 180, 255 ), true );
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 ) { RenderLine( sample->info.GetViewOrigin() + m_vecEyeOffset, next->info.GetViewOrigin() + m_vecEyeOffset, Color( 255, 255, 180, 255 ), true );
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 ); RenderBox( sample->vecmoved + m_vecEyeOffset, sample->angmoved, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), Color( r, g, b, 127 ), false ); DrawVecForward( false, sample->vecmoved + m_vecEyeOffset, sample->angmoved, r, g, b ); }
if ( sample->targetpoint ) { GetColorForSample( false, false, true, sample, r, g, b ); RenderBox( sample->vectarget, vec3_angle, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), Color( r, g, b, 127 ), false ); }
if ( samplenumber == m_nPreviewLastFrame + 1 ) { r = 50; g = 100; b = 250; RenderBox( sample->info.GetViewOrigin() + m_vecEyeOffset, sample->info.GetViewAngles(), Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), Color( r, g, b, 92 ), false ); }
if ( sample->targetpoint ) { r = 200; g = 200; b = 220;
RenderLine( sample->info.GetViewOrigin() + m_vecEyeOffset, sample->vectarget, Color( r, g, b, 255 ), true ); } }
//-----------------------------------------------------------------------------
// 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 = m_Smoothing.m_nFirstSelectableSample;
if ( first > last ) return -1;
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 first;
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
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;
int useFrame = max( m_Smoothing.m_nFirstSelectableSample, frame );
m_nPreviewLastFrame = useFrame;
/* 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( const 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( const 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] ) { RenderLine( previous, splined, Color( 0, 255, 0, 255 ), true ); }
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] ) { RenderLine( previous, splined, Color( 0, 255, 0, 255 ), true ); }
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, CSmoothingContext& smoothing ) { democmdinfo_t info; int dummy;
bool foundFirstSelectable = false;
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_stringtables: { demoFile.ReadStringTables( NULL ); } break; case dem_usercmd: { demoFile.ReadUserCmd( NULL, dummy ); } break; default: { swallowmessages = false; } break; } } while ( swallowmessages );
if ( demofinished ) { // StopPlayback();
return; }
int curpos = demoFile.GetCurPos( true );
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();
int sampleIndex = smoothing.smooth.AddToTail( smoothing_entry );
if ( !foundFirstSelectable && smoothing_entry.vecmoved.LengthSqr() > 0.0f ) { foundFirstSelectable = true; smoothing.m_nFirstSelectableSample = sampleIndex; } } }
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 ) ) { ConMsg( "ERROR: couldn't open %s.\n", name ); return; }
demoheader_t * header = demoFile.ReadDemoHeader();
if ( !header ) { demoFile.Close(); return; }
ConMsg ("Smoothing demo from %s ...", name );
smoothing.active = true; Q_strncpy( smoothing.filename, name, sizeof(smoothing.filename) );
smoothing.smooth.RemoveAll();
ClearSmoothingInfo( smoothing );
ParseSmoothingInfo( demoFile, smoothing ); demoFile.Close(); //Performsmoothing( smooth );
//SaveSmoothedDemo( name, smooth );
ConMsg ( " 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 ); }
|