summaryrefslogtreecommitdiff
path: root/movieobjects
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /movieobjects
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'movieobjects')
-rw-r--r--movieobjects/dmeanimationlist.cpp68
-rw-r--r--movieobjects/dmeanimationset.cpp1407
-rw-r--r--movieobjects/dmeattachment.cpp103
-rw-r--r--movieobjects/dmebalancetostereocalculatoroperator.cpp105
-rw-r--r--movieobjects/dmebookmark.cpp32
-rw-r--r--movieobjects/dmecamera.cpp288
-rw-r--r--movieobjects/dmechannel.cpp1007
-rw-r--r--movieobjects/dmeclip.cpp1725
-rw-r--r--movieobjects/dmecombinationoperator.cpp2184
-rw-r--r--movieobjects/dmedag.cpp438
-rw-r--r--movieobjects/dmedccmakefile.cpp323
-rw-r--r--movieobjects/dmedrawsettings.cpp265
-rw-r--r--movieobjects/dmeeditortypedictionary.cpp328
-rw-r--r--movieobjects/dmeexpressionoperator.cpp942
-rw-r--r--movieobjects/dmeeyeball.cpp48
-rw-r--r--movieobjects/dmeeyeposition.cpp46
-rw-r--r--movieobjects/dmefaceset.cpp187
-rw-r--r--movieobjects/dmegamemodel.cpp694
-rw-r--r--movieobjects/dmegamemodelinput.cpp253
-rw-r--r--movieobjects/dmeimage.cpp45
-rw-r--r--movieobjects/dmeinput.cpp36
-rw-r--r--movieobjects/dmejoint.cpp151
-rw-r--r--movieobjects/dmekeyboardinput.cpp141
-rw-r--r--movieobjects/dmelight.cpp331
-rw-r--r--movieobjects/dmelog.cpp6209
-rw-r--r--movieobjects/dmemakefile.cpp628
-rw-r--r--movieobjects/dmemakefileutils.cpp613
-rw-r--r--movieobjects/dmematerial.cpp82
-rw-r--r--movieobjects/dmematerialoverlayfxclip.cpp270
-rw-r--r--movieobjects/dmemdl.cpp207
-rw-r--r--movieobjects/dmemdlmakefile.cpp287
-rw-r--r--movieobjects/dmemesh.cpp5017
-rw-r--r--movieobjects/dmemodel.cpp335
-rw-r--r--movieobjects/dmemorphoperator.cpp157
-rw-r--r--movieobjects/dmemouseinput.cpp104
-rw-r--r--movieobjects/dmeoperator.cpp36
-rw-r--r--movieobjects/dmepackoperators.cpp321
-rw-r--r--movieobjects/dmeparticlesystemdefinition.cpp598
-rw-r--r--movieobjects/dmephonememapping.cpp22
-rw-r--r--movieobjects/dmeselection.cpp577
-rw-r--r--movieobjects/dmeshader.cpp260
-rw-r--r--movieobjects/dmeshape.cpp115
-rw-r--r--movieobjects/dmesound.cpp93
-rw-r--r--movieobjects/dmetestmesh.cpp1757
-rw-r--r--movieobjects/dmetexture.cpp290
-rw-r--r--movieobjects/dmetimeframe.cpp95
-rw-r--r--movieobjects/dmetimeselection.cpp336
-rw-r--r--movieobjects/dmetrack.cpp818
-rw-r--r--movieobjects/dmetrackgroup.cpp524
-rw-r--r--movieobjects/dmetransform.cpp82
-rw-r--r--movieobjects/dmetransforminput.cpp98
-rw-r--r--movieobjects/dmetransformlist.cpp38
-rw-r--r--movieobjects/dmetransformoperator.cpp107
-rw-r--r--movieobjects/dmeunpackoperators.cpp335
-rw-r--r--movieobjects/dmevertexdata.cpp1023
-rw-r--r--movieobjects/dmmeshcomp.cpp480
-rw-r--r--movieobjects/dmmeshutils.cpp3313
-rw-r--r--movieobjects/dmobjserializer.cpp1464
-rw-r--r--movieobjects/dmsmdserializer.cpp1706
-rw-r--r--movieobjects/dmx_to_vcd.cpp1052
-rw-r--r--movieobjects/importintovcd.cpp863
-rw-r--r--movieobjects/movieobjects.vpc162
-rw-r--r--movieobjects/movieobjects_interfaces.cpp8
-rw-r--r--movieobjects/movieobjects_interfaces.h36
64 files changed, 41665 insertions, 0 deletions
diff --git a/movieobjects/dmeanimationlist.cpp b/movieobjects/dmeanimationlist.cpp
new file mode 100644
index 0000000..0540884
--- /dev/null
+++ b/movieobjects/dmeanimationlist.cpp
@@ -0,0 +1,68 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// An element that contains a list of animations
+//
+//=============================================================================
+#include "movieobjects/dmeanimationlist.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmeclip.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeAnimationList, CDmeAnimationList );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeAnimationList::OnConstruction()
+{
+ m_Animations.Init( this, "animations" );
+}
+
+void CDmeAnimationList::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds, removes animations
+//-----------------------------------------------------------------------------
+int CDmeAnimationList::AddAnimation( CDmeChannelsClip *pAnimation )
+{
+ return m_Animations.AddToTail( pAnimation );
+}
+
+void CDmeAnimationList::RemoveAnimation( int nIndex )
+{
+ m_Animations.Remove( nIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the transform
+//-----------------------------------------------------------------------------
+void CDmeAnimationList::SetAnimation( int nIndex, CDmeChannelsClip *pAnimation )
+{
+ m_Animations.Set( nIndex, pAnimation );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds an animation by name
+//-----------------------------------------------------------------------------
+int CDmeAnimationList::FindAnimation( const char *pAnimName )
+{
+ int nCount = m_Animations.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( m_Animations[i]->GetName(), pAnimName ) )
+ return i;
+ }
+ return -1;
+}
diff --git a/movieobjects/dmeanimationset.cpp b/movieobjects/dmeanimationset.cpp
new file mode 100644
index 0000000..926de45
--- /dev/null
+++ b/movieobjects/dmeanimationset.cpp
@@ -0,0 +1,1407 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmeanimationset.h"
+#include "movieobjects/dmebookmark.h"
+#include "movieobjects/dmegamemodel.h"
+#include "movieobjects/dmecombinationoperator.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmehandle.h"
+#include "phonemeconverter.h"
+#include "tier1/utlstringmap.h"
+#include "tier2/tier2.h"
+#include "filesystem.h"
+#include "studio.h"
+#include "tier3/tier3.h"
+#include "tier1/utlbuffer.h"
+
+//-----------------------------------------------------------------------------
+// CDmePresetGroup - container for animation set info
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePreset, CDmePreset );
+
+void CDmePreset::OnConstruction()
+{
+ m_ControlValues.Init( this, "controlValues" );
+ m_nProceduralType.InitAndSet( this, "procedural", PROCEDURAL_PRESET_NOT );
+}
+
+void CDmePreset::OnDestruction()
+{
+}
+
+CDmaElementArray< CDmElement > &CDmePreset::GetControlValues()
+{
+ return m_ControlValues;
+}
+
+const CDmaElementArray< CDmElement > &CDmePreset::GetControlValues() const
+{
+ return m_ControlValues;
+}
+
+int CDmePreset::FindControlValueIndex( const char *pControlName )
+{
+ int c = m_ControlValues.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmElement *e = m_ControlValues.Get( i );
+ if ( !Q_stricmp( e->GetName(), pControlName ) )
+ return i;
+ }
+ return -1;
+}
+
+CDmElement *CDmePreset::FindControlValue( const char *pControlName )
+{
+ int i = FindControlValueIndex( pControlName );
+ if ( i >= 0 )
+ return m_ControlValues.Get(i);
+ return NULL;
+}
+
+CDmElement *CDmePreset::FindOrAddControlValue( const char *pControlName )
+{
+ CDmElement *pControlValues = FindControlValue( pControlName );
+ if ( !pControlValues )
+ {
+ // Create the default groups in order
+ pControlValues = CreateElement< CDmElement >( pControlName, GetFileId() );
+ m_ControlValues.AddToTail( pControlValues );
+ }
+ return pControlValues;
+}
+
+void CDmePreset::RemoveControlValue( const char *pControlName )
+{
+ int i = FindControlValueIndex( pControlName );
+ if ( i >= 0 )
+ {
+ m_ControlValues.Remove( i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Is the preset read-only?
+//-----------------------------------------------------------------------------
+bool CDmePreset::IsReadOnly()
+{
+ DmAttributeReferenceIterator_t h = g_pDataModel->FirstAttributeReferencingElement( GetHandle() );
+ while ( h != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
+ {
+ CDmAttribute *pAttribute = g_pDataModel->GetAttribute( h );
+ CDmePresetGroup *pOwner = CastElement<CDmePresetGroup>( pAttribute->GetOwner() );
+ if ( pOwner && pOwner->m_bIsReadOnly )
+ return true;
+ h = g_pDataModel->NextAttributeReferencingElement( h );
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Copies control values
+//-----------------------------------------------------------------------------
+void CDmePreset::CopyControlValuesFrom( CDmePreset *pSource )
+{
+ m_ControlValues.RemoveAll();
+
+ const CDmaElementArray< CDmElement > &sourceValues = pSource->GetControlValues();
+ int nCount = sourceValues.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pCopy = sourceValues[i]->Copy( );
+ m_ControlValues.AddToTail( pCopy );
+ }
+}
+
+void CDmePreset::SetProceduralPresetType( int nType )
+{
+ Assert( nType >= 0 && nType < NUM_PROCEDURAL_PRESET_TYPES );
+ m_nProceduralType = nType;
+}
+
+bool CDmePreset::IsProcedural() const
+{
+ return m_nProceduralType != PROCEDURAL_PRESET_NOT;
+}
+
+int CDmePreset::GetProceduralPresetType() const
+{
+ return m_nProceduralType;
+}
+
+IMPLEMENT_ELEMENT_FACTORY( DmeProceduralPresetSettings, CDmeProceduralPresetSettings );
+
+void CDmeProceduralPresetSettings::OnConstruction()
+{
+ m_flJitterScale.InitAndSet( this, "jitterscale", 1.0f );
+ m_flSmoothScale.InitAndSet( this, "smoothscale", 1.0f );
+ m_flSharpenScale.InitAndSet( this, "sharpenscale", 1.0f );
+ m_flSoftenScale.InitAndSet( this, "softenscale", 1.0f );
+
+ m_nJitterIterations.InitAndSet( this, "jitteriterations", 5 );
+ m_nSmoothIterations.InitAndSet( this, "smoothiterations", 5 );
+ m_nSharpenIterations.InitAndSet( this, "sharpeniterations", 1 );
+ m_nSoftenIterations.InitAndSet( this, "softeniterations", 1 );
+
+ // 1/12 second now ( 833 ten thousandths )
+ m_nStaggerInterval.InitAndSet( this, "staggerinterval", 10000 / 12 );
+}
+
+void CDmeProceduralPresetSettings::OnDestruction()
+{
+}
+
+//-----------------------------------------------------------------------------
+// CDmePresetRemap - copies presets from one group to another
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePresetRemap, CDmePresetRemap );
+
+void CDmePresetRemap::OnConstruction()
+{
+ m_SourcePresetGroup.Init( this, "sourcePresetGroup" );
+ m_SrcPresets.Init( this, "srcPresets" );
+ m_DestPresets.Init( this, "destPresets" );
+}
+
+void CDmePresetRemap::OnDestruction()
+{
+}
+
+
+const char *CDmePresetRemap::FindSourcePreset( const char *pDestPresetName )
+{
+ int nCount = m_DestPresets.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pDestPresetName, m_DestPresets[i] ) )
+ return m_SrcPresets[i];
+ }
+ return NULL;
+}
+
+void CDmePresetRemap::AddRemap( const char *pSourcePresetName, const char *pDestPresetName )
+{
+ m_SrcPresets.AddToTail( pSourcePresetName );
+ m_DestPresets.AddToTail( pDestPresetName );
+}
+
+void CDmePresetRemap::RemoveAll()
+{
+ m_SrcPresets.RemoveAll();
+ m_DestPresets.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Iteration
+//-----------------------------------------------------------------------------
+int CDmePresetRemap::GetRemapCount()
+{
+ return m_SrcPresets.Count();
+}
+
+const char *CDmePresetRemap::GetRemapSource( int i )
+{
+ return m_SrcPresets[i];
+}
+
+const char *CDmePresetRemap::GetRemapDest( int i )
+{
+ return m_DestPresets[i];
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmePresetGroup - container for animation set info
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePresetGroup, CDmePresetGroup );
+
+void CDmePresetGroup::OnConstruction()
+{
+ m_Presets.Init( this, "presets" );
+ m_bIsVisible.InitAndSet( this, "visible", true );
+ m_bIsReadOnly.Init( this, "readonly" );
+}
+
+void CDmePresetGroup::OnDestruction()
+{
+}
+
+CDmaElementArray< CDmePreset > &CDmePresetGroup::GetPresets()
+{
+ return m_Presets;
+}
+
+const CDmaElementArray< CDmePreset > &CDmePresetGroup::GetPresets() const
+{
+ return m_Presets;
+}
+
+//-----------------------------------------------------------------------------
+// Finds the index of a particular preset group
+//-----------------------------------------------------------------------------
+int CDmePresetGroup::FindPresetIndex( CDmePreset *pPreset )
+{
+ int c = m_Presets.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmePreset *e = m_Presets.Get( i );
+ if ( pPreset == e )
+ return i;
+ }
+ return -1;
+}
+
+
+CDmePreset *CDmePresetGroup::FindPreset( const char *pPresetName )
+{
+ int i;
+ int c = m_Presets.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ CDmePreset *e = m_Presets.Get( i );
+ if ( !Q_stricmp( e->GetName(), pPresetName ) )
+ return e;
+ }
+ return NULL;
+}
+
+CDmePreset *CDmePresetGroup::FindOrAddPreset( const char *pPresetName, int nType /*=PROCEDURAL_PRESET_NOT*/ )
+{
+ CDmePreset *pPreset = FindPreset( pPresetName );
+ if ( !pPreset )
+ {
+ // Create the default groups in order
+ pPreset = CreateElement< CDmePreset >( pPresetName, GetFileId() );
+ pPreset->SetProceduralPresetType( nType );
+ m_Presets.AddToTail( pPreset );
+ }
+ return pPreset;
+}
+
+bool CDmePresetGroup::RemovePreset( CDmePreset *pPreset )
+{
+ int i = FindPresetIndex( pPreset );
+ if ( i >= 0 )
+ {
+ m_Presets.Remove( i );
+ return true;
+ }
+ return false;
+}
+
+void CDmePresetGroup::MovePresetUp( CDmePreset *pPreset )
+{
+ int i = FindPresetIndex( pPreset );
+ if ( i >= 1 )
+ {
+ m_Presets.Swap( i, i-1 );
+ }
+}
+
+void CDmePresetGroup::MovePresetDown( CDmePreset *pPreset )
+{
+ int i = FindPresetIndex( pPreset );
+ if ( i >= 0 && i < m_Presets.Count() - 1 )
+ {
+ m_Presets.Swap( i, i+1 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reorder presets
+//-----------------------------------------------------------------------------
+void CDmePresetGroup::MovePresetInFrontOf( CDmePreset *pPreset, CDmePreset *pInFrontOf )
+{
+ if ( pPreset == pInFrontOf )
+ return;
+
+ int nEnd = pInFrontOf ? FindPresetIndex( pInFrontOf ) : m_Presets.Count();
+ Assert( nEnd >= 0 );
+
+ RemovePreset( pPreset );
+ if ( nEnd > m_Presets.Count() )
+ {
+ nEnd = m_Presets.Count();
+ }
+ m_Presets.InsertBefore( nEnd, pPreset );
+}
+
+
+//-----------------------------------------------------------------------------
+// The preset remap
+//-----------------------------------------------------------------------------
+CDmePresetRemap *CDmePresetGroup::GetPresetRemap()
+{
+ return GetValueElement< CDmePresetRemap >( "presetRemap" );
+}
+
+CDmePresetRemap *CDmePresetGroup::GetOrAddPresetRemap()
+{
+ CDmePresetRemap *pPresetRemap = GetPresetRemap();
+ if ( !pPresetRemap )
+ {
+ pPresetRemap = CreateElement< CDmePresetRemap >( "PresetRemap", GetFileId() );
+ SetValue( "presetRemap", pPresetRemap );
+ }
+ return pPresetRemap;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Finds a control index
+//-----------------------------------------------------------------------------
+struct ExportedControl_t
+{
+ CUtlString m_Name;
+ bool m_bIsStereo;
+ bool m_bIsMulti;
+ int m_nFirstIndex;
+};
+
+
+//-----------------------------------------------------------------------------
+// Builds a unique list of controls found in the presets
+//-----------------------------------------------------------------------------
+static int FindExportedControlIndex( const char *pControlName, CUtlVector< ExportedControl_t > &uniqueControls )
+{
+ int nCount = uniqueControls.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pControlName, uniqueControls[i].m_Name ) )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a unique list of controls found in the presets
+//-----------------------------------------------------------------------------
+static int BuildExportedControlList( CDmeAnimationSet *pAnimationSet, const CDmePresetGroup *pPresetGroup, CUtlVector< ExportedControl_t > &uniqueControls )
+{
+ int nGlobalIndex = 0;
+ const CDmrElementArrayConst< CDmePreset > &presets = pPresetGroup->GetPresets();
+ int nPresetCount = presets.Count();
+ for ( int iPreset = 0; iPreset < nPresetCount; ++iPreset )
+ {
+ CDmePreset *pPreset = presets[iPreset];
+ const CDmrElementArray< CDmElement > &controls = pPreset->GetControlValues();
+
+ int nControlCount = controls.Count();
+ for ( int i = 0; i < nControlCount; ++i )
+ {
+ const char *pControlName = controls[i]->GetName();
+ int nIndex = FindExportedControlIndex( pControlName, uniqueControls );
+ if ( nIndex >= 0 )
+ continue;
+ CDmAttribute *pValueAttribute = controls[i]->GetAttribute( "value" );
+ if ( !pValueAttribute || pValueAttribute->GetType() != AT_FLOAT )
+ continue;
+
+ if ( pAnimationSet )
+ {
+
+ CDmElement *pControl = pAnimationSet->FindControl( pControlName );
+ if ( !pControl )
+ continue;
+
+ int j = uniqueControls.AddToTail();
+ ExportedControl_t &control = uniqueControls[j];
+ control.m_Name = pControlName;
+ control.m_bIsStereo = pControl->GetValue<bool>( "combo" );
+ control.m_bIsMulti = pControl->GetValue<bool>( "multi" );
+ control.m_nFirstIndex = nGlobalIndex;
+ nGlobalIndex += 1 + control.m_bIsStereo + control.m_bIsMulti;
+ }
+ else
+ {
+ int j = uniqueControls.AddToTail();
+ ExportedControl_t &control = uniqueControls[j];
+ control.m_Name = pControlName;
+ // this isn't quite as reliable as querying the animation set but if we don't have one...
+ control.m_bIsStereo = controls[ i ]->GetAttribute( "balance" ) ? true : false;
+ control.m_bIsMulti = controls[ i ]->GetAttribute( "multilevel" ) ? true : false;
+ control.m_nFirstIndex = nGlobalIndex;
+ nGlobalIndex += 1 + control.m_bIsStereo + control.m_bIsMulti;
+ }
+ }
+ }
+ return nGlobalIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Exports this preset group to a faceposer .txt expression file
+// Either an animation set or a combination operator are required so that
+// the default value for unspecified
+//-----------------------------------------------------------------------------
+bool CDmePresetGroup::ExportToTXT( const char *pFileName, CDmeAnimationSet *pAnimationSet /* = NULL */, CDmeCombinationOperator *pComboOp /* = NULL */ ) const
+{
+ const CDmePresetGroup *pPresetGroup = this;
+
+ // find all used controls
+ CUtlVector< ExportedControl_t > exportedControls;
+ BuildExportedControlList( pAnimationSet, pPresetGroup, exportedControls );
+
+ CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ // Output the unique keys
+ buf.Printf( "$keys " );
+ int nExportedControlCount = exportedControls.Count();
+ for ( int i = 0; i < nExportedControlCount; ++i )
+ {
+ char pTempBuf[MAX_PATH];
+
+ ExportedControl_t &control = exportedControls[i];
+ if ( !control.m_bIsStereo )
+ {
+ buf.Printf("%s ", control.m_Name.Get() );
+ }
+ else
+ {
+ Q_snprintf( pTempBuf, sizeof(pTempBuf), "right_%s", control.m_Name.Get() );
+ buf.Printf("%s ", pTempBuf );
+ Q_snprintf( pTempBuf, sizeof(pTempBuf), "left_%s", control.m_Name.Get() );
+ buf.Printf("%s ", pTempBuf );
+ }
+
+ if ( control.m_bIsMulti )
+ {
+ Q_snprintf( pTempBuf, sizeof(pTempBuf), "multi_%s", control.m_Name.Get() );
+ buf.Printf("%s ", pTempBuf );
+ }
+ }
+ buf.Printf( "\n" );
+ buf.Printf( "$hasweighting\n" );
+ buf.Printf( "$normalized\n" );
+
+ // Output all presets
+ const CDmrElementArrayConst< CDmePreset > &presets = pPresetGroup->GetPresets();
+ int nPresetCount = presets.Count();
+ for ( int iPreset = 0; iPreset < nPresetCount; ++iPreset )
+ {
+ CDmePreset *pPreset = presets[iPreset];
+ const char *pPresetName = pPreset->GetName();
+
+ // Hack for 'silence' and for p_ naming scheme
+ if ( !Q_stricmp( pPresetName, "p_silence" ) )
+ {
+ pPresetName = "<sil>";
+ }
+ if ( pPresetName[0] == 'p' && pPresetName[1] == '_' )
+ {
+ pPresetName = &pPresetName[2];
+ }
+
+ buf.Printf( "\"%s\" \t", pPresetName );
+
+ int nPhonemeIndex = TextToPhonemeIndex( pPresetName );
+ int nCode = CodeForPhonemeByIndex( nPhonemeIndex );
+ if ( nCode < 128 )
+ {
+ buf.Printf( "\"%c\" \t", nCode );
+ }
+ else
+ {
+ buf.Printf( "\"0x%x\"\t", nCode );
+ }
+
+ for ( int i = 0; i < nExportedControlCount; ++i )
+ {
+ ExportedControl_t &control = exportedControls[i];
+ CDmElement *pControlValue = pPreset->FindControlValue( control.m_Name );
+ if ( !pControlValue )
+ {
+ CDmElement *pControl = pAnimationSet ? pAnimationSet->FindControl( control.m_Name ) : NULL;
+ if ( !pControl )
+ {
+ const ControlIndex_t nIndex = pComboOp ? pComboOp->FindControlIndex( control.m_Name ) : -1;
+ if ( nIndex >= 0 )
+ {
+ buf.Printf( "%.5f\t0.000\t", pComboOp->GetControlDefaultValue( nIndex ) );
+ if ( control.m_bIsStereo )
+ {
+ buf.Printf( "%.5f\t0.000\t", pComboOp->GetControlDefaultValue( nIndex ) );
+ }
+ if ( control.m_bIsMulti )
+ {
+ buf.Printf( "%.5f\t0.000\t", pComboOp->GetControlDefaultValue( nIndex ) );
+ }
+ }
+ else
+ {
+ buf.Printf( "0.000\t0.000\t" );
+ if ( control.m_bIsStereo )
+ {
+ buf.Printf( "0.000\t0.000\t" );
+ }
+ if ( control.m_bIsMulti )
+ {
+ buf.Printf( "0.000\t0.000\t" );
+ }
+ }
+
+ continue;
+ }
+
+ if ( !control.m_bIsStereo )
+ {
+ buf.Printf( "%.5f\t1.000\t", pControl->GetValue<float>( "defaultValue" ) );
+ }
+ else
+ {
+ float flValue, flBalance, flLeft, flRight;
+ flValue = pControl->GetValue<float>( "defaultValue", 0.0f );
+ flBalance = pControl->GetValue<float>( "defaultBalance", 0.5f );
+ ValueBalanceToLeftRight( &flLeft, &flRight, flValue, flBalance );
+ buf.Printf( "%.5f\t1.000\t", flRight );
+ buf.Printf( "%.5f\t1.000\t", flLeft );
+ }
+
+ if ( control.m_bIsMulti )
+ {
+ buf.Printf( "%.5f\t1.000\t", pControl->GetValue<float>( "defaultMultilevel" ) );
+ }
+ continue;
+ }
+
+ if ( !control.m_bIsStereo )
+ {
+ buf.Printf( "%.5f\t1.000\t", pControlValue->GetValue<float>( "value" ) );
+ }
+ else
+ {
+ float flValue, flBalance, flLeft, flRight;
+ flValue = pControlValue->GetValue<float>( "value" );
+ flBalance = pControlValue->GetValue<float>( "balance" );
+ ValueBalanceToLeftRight( &flLeft, &flRight, flValue, flBalance );
+ buf.Printf( "%.5f\t1.000\t", flRight );
+ buf.Printf( "%.5f\t1.000\t", flLeft );
+ }
+
+ if ( control.m_bIsMulti )
+ {
+ buf.Printf( "%.5f\t1.000\t", pControlValue->GetValue<float>( "multilevel" ) );
+ }
+ }
+ const char *pDesc = DescForPhonemeByIndex( nPhonemeIndex );
+ buf.Printf( "\"%s\"\n", pDesc ? pDesc : pPresetName );
+ }
+
+ return g_pFullFileSystem->WriteFile( pFileName, NULL, buf );
+}
+
+#ifdef ALIGN4
+#undef ALIGN4
+#endif // #ifdef ALIGN4
+#define ALIGN4( a ) a = (byte *)((int)((byte *)a + 3) & ~ 3)
+
+
+//-----------------------------------------------------------------------------
+// Exports this preset group to a faceposer .vfe expression file
+//-----------------------------------------------------------------------------
+bool CDmePresetGroup::ExportToVFE( const char *pFileName, CDmeAnimationSet *pAnimationSet /* = NULL */, CDmeCombinationOperator *pComboOp /* = NULL */ ) const
+{
+ const CDmePresetGroup *pPresetGroup = this;
+
+ int i;
+ const CDmrElementArrayConst< CDmePreset > &presets = pPresetGroup->GetPresets();
+
+ // find all used controls
+ CUtlVector< ExportedControl_t > exportedControls;
+ int nTotalControlCount = BuildExportedControlList( pAnimationSet, pPresetGroup, exportedControls );
+ const int nExportedControlCount = exportedControls.Count();
+
+ byte *pData = (byte *)calloc( 1024 * 1024, 1 );
+ byte *pDataStart = pData;
+
+ flexsettinghdr_t *fhdr = (flexsettinghdr_t *)pData;
+
+ fhdr->id = ('V' << 16) + ('F' << 8) + ('E');
+ fhdr->version = 0;
+ if ( !g_pFullFileSystem->FullPathToRelativePathEx( pFileName, "GAME", fhdr->name, sizeof(fhdr->name) ) )
+ {
+ Q_strncpy( fhdr->name, pFileName, sizeof(fhdr->name) );
+ }
+
+ // allocate room for header
+ pData += sizeof( flexsettinghdr_t );
+ ALIGN4( pData );
+
+ // store flex settings
+ flexsetting_t *pSetting = (flexsetting_t *)pData;
+ fhdr->numflexsettings = presets.Count();
+ fhdr->flexsettingindex = pData - pDataStart;
+ pData += sizeof( flexsetting_t ) * fhdr->numflexsettings;
+ ALIGN4( pData );
+ for ( i = 0; i < fhdr->numflexsettings; i++ )
+ {
+ CDmePreset *pPreset = presets[i];
+ Assert( pPreset );
+
+ pSetting[i].index = i;
+ pSetting[i].settingindex = pData - (byte *)(&pSetting[i]);
+
+ flexweight_t *pFlexWeights = (flexweight_t *)pData;
+
+ for ( int j = 0; j < nExportedControlCount; j++ )
+ {
+ ExportedControl_t &control = exportedControls[ j ];
+ CDmElement *pControlValue = pPreset->FindControlValue( control.m_Name );
+ if ( !pControlValue )
+ {
+ const ControlIndex_t nIndex = pComboOp ? pComboOp->FindControlIndex( control.m_Name ) : -1;
+ if ( nIndex >= 0 )
+ {
+ if ( !control.m_bIsStereo )
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = control.m_nFirstIndex;
+ pFlexWeights->weight = pComboOp->GetControlDefaultValue( nIndex );
+ pFlexWeights->influence = 1.0f;
+ pFlexWeights++;
+ }
+ else
+ {
+ float flValue, flBalance, flLeft, flRight;
+ flValue = pComboOp->GetControlDefaultValue( nIndex );
+ flBalance = 0.5;
+ ValueBalanceToLeftRight( &flLeft, &flRight, flValue, flBalance );
+
+ pSetting[i].numsettings += 2;
+ pFlexWeights->key = control.m_nFirstIndex;
+ pFlexWeights->weight = flRight;
+ pFlexWeights->influence = 1.0f;
+ pFlexWeights++;
+ pFlexWeights->key = control.m_nFirstIndex + 1;
+ pFlexWeights->weight = flLeft;
+ pFlexWeights->influence = 1.0f;
+ pFlexWeights++;
+ }
+
+ if ( control.m_bIsMulti )
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = control.m_nFirstIndex + 1 + control.m_bIsStereo;
+ pFlexWeights->weight = 0.5f;
+ pFlexWeights->influence = 1.0f;
+ pFlexWeights++;
+ }
+ }
+ else
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = control.m_nFirstIndex;
+ pFlexWeights->weight = 0.0f;
+ pFlexWeights->influence = 0.0f;
+ pFlexWeights++;
+
+ if ( control.m_bIsStereo )
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = control.m_nFirstIndex + 1;
+ pFlexWeights->weight = 0.0f;
+ pFlexWeights->influence = 0.0f;
+ pFlexWeights++;
+ }
+
+ if ( control.m_bIsMulti )
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = control.m_nFirstIndex + 1 + control.m_bIsStereo;
+ pFlexWeights->weight = 0.5f;
+ pFlexWeights->influence = 0.0f;
+ pFlexWeights++;
+ }
+ }
+
+ continue;
+ }
+
+ if ( !control.m_bIsStereo )
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = control.m_nFirstIndex;
+ pFlexWeights->weight = pControlValue->GetValue<float>( "value" );
+ pFlexWeights->influence = 1.0f;
+ pFlexWeights++;
+ }
+ else
+ {
+ float flValue, flBalance, flLeft, flRight;
+ flValue = pControlValue->GetValue<float>( "value" );
+ flBalance = pControlValue->GetValue<float>( "balance" );
+ ValueBalanceToLeftRight( &flLeft, &flRight, flValue, flBalance );
+
+ pSetting[i].numsettings += 2;
+ pFlexWeights->key = control.m_nFirstIndex;
+ pFlexWeights->weight = flRight;
+ pFlexWeights->influence = 1.0f;
+ pFlexWeights++;
+ pFlexWeights->key = control.m_nFirstIndex + 1;
+ pFlexWeights->weight = flLeft;
+ pFlexWeights->influence = 1.0f;
+ pFlexWeights++;
+ }
+
+ if ( control.m_bIsMulti )
+ {
+ pSetting[i].numsettings++;
+ pFlexWeights->key = control.m_nFirstIndex + 1 + control.m_bIsStereo;
+ pFlexWeights->weight = pControlValue->GetValue<float>( "multilevel" );
+ pFlexWeights->influence = 1.0f;
+ pFlexWeights++;
+ }
+ }
+
+ pData = (byte *)pFlexWeights;
+ ALIGN4( pData );
+ }
+
+ int numindexes = 1;
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ if ( pSetting[i].index >= numindexes )
+ {
+ numindexes = pSetting[i].index + 1;
+ }
+ }
+
+ // store indexed table
+ int *pIndex = (int *)pData;
+ fhdr->numindexes = numindexes;
+ fhdr->indexindex = pData - pDataStart;
+ pData += sizeof( int ) * numindexes;
+ ALIGN4( pData );
+ for (i = 0; i < numindexes; i++)
+ {
+ pIndex[i] = -1;
+ }
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ pIndex[pSetting[i].index] = i;
+ }
+
+ // store flex setting names
+ for (i = 0; i < fhdr->numflexsettings; i++)
+ {
+ CDmePreset *pPreset = presets[i];
+ const char *pPresetName = pPreset->GetName();
+
+ // Hack for 'silence' and for p_ naming scheme
+ if ( pPresetName[0] == 'p' && pPresetName[1] == '_' )
+ {
+ pPresetName = &pPresetName[2];
+ }
+ if ( !Q_stricmp( pPresetName, "silence" ) )
+ {
+ pPresetName = "<sil>";
+ }
+
+ pSetting[i].nameindex = pData - (byte *)(&pSetting[i]);
+ strcpy( (char *)pData, pPresetName );
+ pData += Q_strlen( pPresetName ) + 1;
+ }
+ ALIGN4( pData );
+
+ // store key names
+ char **pKeynames = (char **)pData;
+ fhdr->numkeys = nTotalControlCount;
+ fhdr->keynameindex = pData - pDataStart;
+ pData += sizeof(char *) * nTotalControlCount;
+ int j = 0;
+ for ( i = 0; i < nExportedControlCount; ++i )
+ {
+ char pTempBuf[MAX_PATH];
+
+ ExportedControl_t &control = exportedControls[i];
+ if ( !control.m_bIsStereo )
+ {
+ pKeynames[j++] = (char *)(pData - pDataStart);
+ strcpy( (char *)pData, control.m_Name );
+ pData += Q_strlen( control.m_Name ) + 1;
+ }
+ else
+ {
+ pKeynames[j++] = (char *)(pData - pDataStart);
+ Q_snprintf( pTempBuf, sizeof(pTempBuf), "right_%s", control.m_Name.Get() );
+ strcpy( (char *)pData, pTempBuf );
+ pData += Q_strlen( pTempBuf ) + 1;
+
+ pKeynames[j++] = (char *)(pData - pDataStart);
+ Q_snprintf( pTempBuf, sizeof(pTempBuf), "left_%s", control.m_Name.Get() );
+ strcpy( (char *)pData, pTempBuf );
+ pData += Q_strlen( pTempBuf ) + 1;
+ }
+
+ if ( control.m_bIsMulti )
+ {
+ pKeynames[j++] = (char *)(pData - pDataStart);
+ Q_snprintf( pTempBuf, sizeof(pTempBuf), "multi_%s", control.m_Name.Get() );
+ strcpy( (char *)pData, pTempBuf );
+ pData += Q_strlen( pTempBuf ) + 1;
+ }
+ }
+ Assert( j == nTotalControlCount );
+ ALIGN4( pData );
+
+ // allocate room for remapping
+ int *keymapping = (int *)pData;
+ fhdr->keymappingindex = pData - pDataStart;
+ pData += sizeof( int ) * nTotalControlCount;
+ for (i = 0; i < nTotalControlCount; i++)
+ {
+ keymapping[i] = -1;
+ }
+ ALIGN4( pData );
+
+ fhdr->length = pData - pDataStart;
+
+ FileHandle_t fh = g_pFullFileSystem->Open( pFileName, "wb" );
+ if ( !fh )
+ {
+ ConWarning( "Unable to write to %s (read-only?)\n", pFileName );
+ free( pDataStart );
+ return false;
+ }
+
+ g_pFullFileSystem->Write( pDataStart, fhdr->length, fh );
+ g_pFullFileSystem->Close( fh );
+ free( pDataStart );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// CDmeAnimationSet - container for animation set info
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeAnimationSet, CDmeAnimationSet );
+
+void CDmeAnimationSet::OnConstruction()
+{
+ m_Controls.Init( this, "controls" );
+ m_PresetGroups.Init( this, "presetGroups" );
+ m_SelectionGroups.Init( this, "selectionGroups" );
+ m_PhonemeMap.Init( this, "phonememap" );
+ m_Operators.Init( this, "operators" );
+ m_Bookmarks.Init( this, "bookmarks" );
+}
+
+void CDmeAnimationSet::OnDestruction()
+{
+}
+
+CDmaElementArray< CDmElement > &CDmeAnimationSet::GetControls()
+{
+ return m_Controls;
+}
+
+CDmaElementArray< CDmePresetGroup > &CDmeAnimationSet::GetPresetGroups()
+{
+ return m_PresetGroups;
+}
+
+CDmaElementArray< CDmeOperator > &CDmeAnimationSet::GetOperators()
+{
+ return m_Operators;
+}
+
+void CDmeAnimationSet::AddOperator( CDmeOperator *pOperator )
+{
+ m_Operators.AddToTail( pOperator );
+}
+
+void CDmeAnimationSet::RemoveOperator( CDmeOperator *pOperator )
+{
+ int nCount = m_Operators.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Operators[i] == pOperator )
+ {
+ m_Operators.Remove(i);
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Finds the index of a particular preset group
+//-----------------------------------------------------------------------------
+void CDmeAnimationSet::OnElementUnserialized()
+{
+ BaseClass::OnElementUnserialized();
+
+ CDmeGameModel *pGameModel = GetValueElement< CDmeGameModel >( "gameModel" );
+ if ( pGameModel )
+ {
+ // NOTE: The model preset manager can't possibly have the right
+ // file id at this point; it's up to the preset group manager to queue
+ // application requests until it gets one
+ g_pModelPresetGroupMgr->ApplyModelPresets( pGameModel->GetModelName(), this );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the index of a particular preset group
+//-----------------------------------------------------------------------------
+int CDmeAnimationSet::FindPresetGroupIndex( CDmePresetGroup *pPresetGroup )
+{
+ int c = m_PresetGroups.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmePresetGroup *e = m_PresetGroups.Get( i );
+ if ( pPresetGroup == e )
+ return i;
+ }
+ return -1;
+}
+
+int CDmeAnimationSet::FindPresetGroupIndex( const char *pGroupName )
+{
+ int c = m_PresetGroups.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmePresetGroup *e = m_PresetGroups.Get( i );
+ if ( e && !Q_stricmp( e->GetName(), pGroupName ) )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find by name
+//-----------------------------------------------------------------------------
+CDmePresetGroup *CDmeAnimationSet::FindPresetGroup( const char *pGroupName )
+{
+ int nIndex = FindPresetGroupIndex( pGroupName );
+ if ( nIndex >= 0 )
+ return m_PresetGroups[nIndex];
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find or add by name
+//-----------------------------------------------------------------------------
+CDmePresetGroup *CDmeAnimationSet::FindOrAddPresetGroup( const char *pGroupName )
+{
+ CDmePresetGroup *pPresetGroup = FindPresetGroup( pGroupName );
+ if ( !pPresetGroup )
+ {
+ // Create the default groups in order
+ pPresetGroup = CreateElement< CDmePresetGroup >( pGroupName, GetFileId() );
+ m_PresetGroups.AddToTail( pPresetGroup );
+ }
+ return pPresetGroup;
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove preset group
+//-----------------------------------------------------------------------------
+bool CDmeAnimationSet::RemovePresetGroup( CDmePresetGroup *pPresetGroup )
+{
+ int i = FindPresetGroupIndex( pPresetGroup );
+ if ( i >= 0 )
+ {
+ m_PresetGroups.Remove( i );
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Move preset group up/down in the list
+//-----------------------------------------------------------------------------
+void CDmeAnimationSet::MovePresetGroupUp( CDmePresetGroup *pPresetGroup )
+{
+ int i = FindPresetGroupIndex( pPresetGroup );
+ if ( i >= 1 )
+ {
+ m_PresetGroups.Swap( i, i-1 );
+ }
+}
+
+void CDmeAnimationSet::MovePresetGroupDown( CDmePresetGroup *pPresetGroup )
+{
+ int i = FindPresetGroupIndex( pPresetGroup );
+ if ( i >= 0 && i < m_PresetGroups.Count() - 1 )
+ {
+ m_PresetGroups.Swap( i, i+1 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reorder preset groups
+//-----------------------------------------------------------------------------
+void CDmeAnimationSet::MovePresetGroupInFrontOf( CDmePresetGroup *pPresetGroup, CDmePresetGroup *pInFrontOf )
+{
+ if ( pPresetGroup == pInFrontOf )
+ return;
+
+#ifdef DBGFLAG_ASSERT
+ int nStart = FindPresetGroupIndex( pPresetGroup );
+#endif
+
+ int nEnd = pInFrontOf ? FindPresetGroupIndex( pInFrontOf ) : m_PresetGroups.Count();
+ Assert( nStart >= 0 && nEnd >= 0 );
+
+ RemovePresetGroup( pPresetGroup );
+ if ( nEnd > m_PresetGroups.Count() )
+ {
+ nEnd = m_PresetGroups.Count();
+ }
+ m_PresetGroups.InsertBefore( nEnd, pPresetGroup );
+}
+
+
+CDmePreset *CDmeAnimationSet::FindOrAddPreset( const char *pGroupName, const char *pPresetName, int nType /*=PROCEDURAL_PRESET_NOT*/ )
+{
+ CDmePresetGroup *pPresetGroup = FindOrAddPresetGroup( pGroupName );
+ return pPresetGroup->FindOrAddPreset( pPresetName, nType );
+}
+
+bool CDmeAnimationSet::RemovePreset( CDmePreset *pPreset )
+{
+ int c = m_PresetGroups.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( m_PresetGroups[i]->RemovePreset( pPreset ) )
+ return true;
+ }
+ return false;
+}
+
+
+const CDmaElementArray< CDmeBookmark > &CDmeAnimationSet::GetBookmarks() const
+{
+ return m_Bookmarks;
+}
+
+CDmaElementArray< CDmeBookmark > &CDmeAnimationSet::GetBookmarks()
+{
+ return m_Bookmarks;
+}
+
+
+CDmaElementArray< CDmElement > &CDmeAnimationSet::GetSelectionGroups()
+{
+ return m_SelectionGroups;
+}
+
+CDmaElementArray< CDmePhonemeMapping > &CDmeAnimationSet::GetPhonemeMap()
+{
+ return m_PhonemeMap;
+}
+
+void CDmeAnimationSet::RestoreDefaultPhonemeMap()
+{
+ CUndoScopeGuard guard( "RestoreDefaultPhonemeMap" );
+
+ int i;
+ int c = m_PhonemeMap.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ g_pDataModel->DestroyElement( m_PhonemeMap[ i ]->GetHandle() );
+ }
+ m_PhonemeMap.Purge();
+
+ int phonemeCount = NumPhonemes();
+ for ( i = 0; i < phonemeCount; ++i )
+ {
+ const char *pName = NameForPhonemeByIndex( i );
+ CDmePhonemeMapping *mapping = CreateElement< CDmePhonemeMapping >( pName, GetFileId() );
+ char presetName[ 256 ];
+ Q_snprintf( presetName, sizeof( presetName ), "p_%s", pName );
+ mapping->m_Preset = presetName;
+ mapping->m_Weight = 1.0f;
+
+ m_PhonemeMap.AddToTail( mapping );
+ }
+}
+
+CDmePhonemeMapping *CDmeAnimationSet::FindMapping( const char *pRawPhoneme )
+{
+ int c = m_PhonemeMap.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmePhonemeMapping *e = m_PhonemeMap.Get( i );
+ Assert( e );
+ if ( !e )
+ continue;
+
+ if ( !Q_stricmp( e->GetName(), pRawPhoneme ) )
+ return e;
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a control
+//-----------------------------------------------------------------------------
+CDmElement *CDmeAnimationSet::FindControl( const char *pControlName )
+{
+ int c = m_Controls.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmElement *e = m_Controls.Get( i );
+ if ( !Q_stricmp( e->GetName(), pControlName ) )
+ return e;
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds or adds a control
+//-----------------------------------------------------------------------------
+CDmElement *CDmeAnimationSet::FindOrAddControl( const char *pControlName )
+{
+ CDmElement *pControl = FindControl( pControlName );
+ if ( !pControl )
+ {
+ // If not, then create one
+ pControl = CreateElement< CDmElement >( pControlName, GetFileId() );
+ m_Controls.AddToTail( pControl );
+ }
+ return pControl;
+}
+
+
+CDmElement *CDmeAnimationSet::FindSelectionGroup( const char *pSelectionGroupName )
+{
+ int c = m_SelectionGroups.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmElement *e = m_SelectionGroups.Get( i );
+ if ( !Q_stricmp( e->GetName(), pSelectionGroupName ) )
+ return e;
+ }
+ return NULL;
+}
+
+CDmElement *CDmeAnimationSet::FindOrAddSelectionGroup( const char *pSelectionGroupName )
+{
+ CDmElement *pSelectionGroup = FindSelectionGroup( pSelectionGroupName );
+ if ( !pSelectionGroup )
+ {
+ // Create the default groups in order
+ pSelectionGroup = CreateElement< CDmElement >( pSelectionGroupName, GetFileId() );
+ pSelectionGroup->AddAttribute( "selectedControls", AT_STRING_ARRAY );
+ m_SelectionGroups.AddToTail( pSelectionGroup );
+ }
+ return pSelectionGroup;
+}
+
+void CDmeAnimationSet::CollectOperators( CUtlVector< DmElementHandle_t > &operators )
+{
+ int numOperators = m_Operators.Count();
+ for ( int i = 0; i < numOperators; ++i )
+ {
+ DmElementHandle_t h = m_Operators.GetHandle( i );
+ if ( h != DMELEMENT_HANDLE_INVALID )
+ {
+ operators.AddToTail( h );
+ }
+ }
+}
+
+struct PPType_t
+{
+ int type;
+ char const *name;
+};
+
+static PPType_t g_PresetNames[ NUM_PROCEDURAL_PRESET_TYPES ] =
+{
+ { PROCEDURAL_PRESET_NOT, "NotProcedural!!!" },
+ { PROCEDURAL_PRESET_IN_CROSSFADE, "In" },
+ { PROCEDURAL_PRESET_OUT_CROSSFADE, "Out" },
+ { PROCEDURAL_PRESET_REVEAL, "Reveal" },
+ { PROCEDURAL_PRESET_PASTE, "Paste" },
+ { PROCEDURAL_PRESET_JITTER, "Jitter" },
+ { PROCEDURAL_PRESET_SMOOTH, "Smooth" },
+ { PROCEDURAL_PRESET_SHARPEN, "Sharpen" },
+ { PROCEDURAL_PRESET_SOFTEN, "Soften" },
+ { PROCEDURAL_PRESET_STAGGER, "Stagger" },
+};
+
+void CDmeAnimationSet::EnsureProceduralPresets()
+{
+ // Note: Starts at index 1 to skip the PROCEDURAL_PRESET_NOT case
+ for ( int i = 1; i < NUM_PROCEDURAL_PRESET_TYPES; ++i )
+ {
+ FindOrAddPreset( "Procedural", g_PresetNames[ i ].name, g_PresetNames[ i ].type );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// A cache of preset groups to be associated with specific models
+//-----------------------------------------------------------------------------
+class CModelPresetGroupManager : public IModelPresetGroupManager
+{
+public:
+ CModelPresetGroupManager();
+ virtual void AssociatePresetsWithFile( DmFileId_t fileId );
+ virtual void ApplyModelPresets( const char *pModelName, CDmeAnimationSet *pAnimationSet );
+
+private:
+ struct QueuedPresetRequest_t
+ {
+ CUtlString m_ModelName;
+ CDmeHandle< CDmeAnimationSet > m_hAnimationSet;
+ };
+
+ typedef CUtlVector< CDmeHandle< CDmePresetGroup, true > > PresetGroupList_t;
+
+ // Loads model presets from .pre files matching the model name
+ void LoadModelPresets( const char *pModelName, PresetGroupList_t &list );
+
+ CUtlStringMap< PresetGroupList_t > m_Lookup;
+ DmFileId_t m_FileId;
+ CUtlVector< QueuedPresetRequest_t > m_QueuedPresetRequest;
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton
+//-----------------------------------------------------------------------------
+static CModelPresetGroupManager s_ModelPresetGroupManager;
+IModelPresetGroupManager *g_pModelPresetGroupMgr = &s_ModelPresetGroupManager;
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CModelPresetGroupManager::CModelPresetGroupManager()
+{
+ m_FileId = DMFILEID_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Associates presets in the cache with a particular file
+//-----------------------------------------------------------------------------
+void CModelPresetGroupManager::AssociatePresetsWithFile( DmFileId_t fileId )
+{
+ m_FileId = fileId;
+ m_Lookup.Clear();
+ if ( m_FileId != DMFILEID_INVALID )
+ {
+ int nCount = m_QueuedPresetRequest.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ QueuedPresetRequest_t &request = m_QueuedPresetRequest[i];
+ if ( request.m_hAnimationSet.Get() )
+ {
+ ApplyModelPresets( request.m_ModelName, request.m_hAnimationSet.Get() );
+ }
+ }
+ m_QueuedPresetRequest.Purge();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads model presets from .pre files matching the model name
+//-----------------------------------------------------------------------------
+void CModelPresetGroupManager::LoadModelPresets( const char *pModelName, PresetGroupList_t &list )
+{
+ list.RemoveAll();
+
+ char pPresetPath[MAX_PATH];
+ Q_ExtractFilePath( pModelName, pPresetPath, sizeof(pPresetPath) );
+
+ char pPresetNameBuf[MAX_PATH];
+ Q_StripExtension( pModelName, pPresetNameBuf, sizeof(pPresetNameBuf) );
+ int nLen = Q_strlen( pPresetNameBuf );
+ Q_snprintf( &pPresetNameBuf[nLen], MAX_PATH - nLen, "*.pre" );
+
+ CDisableUndoScopeGuard sg;
+
+ FileFindHandle_t fh;
+ const char *pFileName = g_pFullFileSystem->FindFirstEx( pPresetNameBuf, "GAME", &fh );
+ for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( fh ) )
+ {
+ char pRelativePresetPath[MAX_PATH];
+ Q_ComposeFileName(pPresetPath, pFileName, pRelativePresetPath, sizeof(pRelativePresetPath) );
+
+ CDmElement* pRoot = NULL;
+ DmFileId_t fileid = g_pDataModel->RestoreFromFile( pRelativePresetPath, "GAME", NULL, &pRoot, CR_FORCE_COPY );
+ if ( fileid == DMFILEID_INVALID || !pRoot )
+ continue;
+
+ CDmePresetGroup *pPresetGroup = CastElement<CDmePresetGroup>( pRoot );
+ if ( !pPresetGroup )
+ {
+ if ( pRoot )
+ {
+ g_pDataModel->RemoveFileId( pRoot->GetFileId() );
+ }
+ continue;
+ }
+
+ pPresetGroup->SetFileId( m_FileId, TD_DEEP );
+
+ // Presets used through the model preset manager must be read only + shared
+ pPresetGroup->m_bIsReadOnly = true;
+ pPresetGroup->SetShared( true );
+
+ int i = list.AddToTail();
+ list[i] = pPresetGroup;
+ }
+ g_pFullFileSystem->FindClose( fh );
+}
+
+
+//-----------------------------------------------------------------------------
+// Applies model presets associated with a particular model to an animation set
+//-----------------------------------------------------------------------------
+void CModelPresetGroupManager::ApplyModelPresets( const char *pModelName, CDmeAnimationSet *pAnimationSet )
+{
+ if ( m_FileId == DMFILEID_INVALID )
+ {
+ int i = m_QueuedPresetRequest.AddToTail();
+ m_QueuedPresetRequest[i].m_ModelName = pModelName;
+ m_QueuedPresetRequest[i].m_hAnimationSet = pAnimationSet;
+ return;
+ }
+
+ if ( !m_Lookup.Defined( pModelName ) )
+ {
+ LoadModelPresets( pModelName, m_Lookup[pModelName] );
+ }
+
+ PresetGroupList_t &list = m_Lookup[pModelName];
+ int nCount = list.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmePresetGroup *pPresetGroup = list[i];
+ int nIndex = pAnimationSet->FindPresetGroupIndex( pPresetGroup->GetName() );
+ if ( nIndex >= 0 )
+ {
+ pAnimationSet->GetPresetGroups().Set( nIndex, pPresetGroup );
+ }
+ else
+ {
+ pAnimationSet->GetPresetGroups().AddToTail( pPresetGroup );
+ }
+ }
+}
diff --git a/movieobjects/dmeattachment.cpp b/movieobjects/dmeattachment.cpp
new file mode 100644
index 0000000..3601ba0
--- /dev/null
+++ b/movieobjects/dmeattachment.cpp
@@ -0,0 +1,103 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Dme version of a model attachment point
+//
+//=============================================================================
+#include "movieobjects/dmeattachment.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imesh.h"
+#include "tier1/KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeAttachment, CDmeAttachment );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeAttachment::OnConstruction()
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ m_bIsRigid.Init( this, "isRigid" );
+ m_bIsWorldAligned.Init( this, "isWorldAligned" );
+
+ KeyValues *pVMTKeyValues = new KeyValues( "wireframe" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$ignorez", 0 );
+ m_AttachmentMaterial.Init( "__DmeJointMaterial", pVMTKeyValues );
+}
+
+void CDmeAttachment::OnDestruction()
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ m_AttachmentMaterial.Shutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// For rendering joints
+//-----------------------------------------------------------------------------
+#define AXIS_SIZE 6.0f
+
+//-----------------------------------------------------------------------------
+// Rendering method for the dag
+//-----------------------------------------------------------------------------
+void CDmeAttachment::Draw( const matrix3x4_t &shapeToWorld, CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadMatrix( shapeToWorld );
+
+ pRenderContext->Bind( m_AttachmentMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 );
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( AXIS_SIZE, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, AXIS_SIZE, 0.0f );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, AXIS_SIZE );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+}
+
+
diff --git a/movieobjects/dmebalancetostereocalculatoroperator.cpp b/movieobjects/dmebalancetostereocalculatoroperator.cpp
new file mode 100644
index 0000000..a8fc482
--- /dev/null
+++ b/movieobjects/dmebalancetostereocalculatoroperator.cpp
@@ -0,0 +1,105 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The expression operator class - scalar math calculator
+// for a good list of operators and simple functions, see:
+// \\fileserver\user\MarcS\boxweb\aliveDistLite\v4.2.0\doc\alive\functions.txt
+// (although we'll want to implement elerp as the standard 3x^2 - 2x^3 with rescale)
+//
+//=============================================================================
+#include "movieobjects/dmebalancetostereocalculatoroperator.h"
+#include "movieobjects/dmechannel.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmattribute.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeBalanceToStereoCalculatorOperator, CDmeBalanceToStereoCalculatorOperator );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeBalanceToStereoCalculatorOperator::OnConstruction()
+{
+ m_result_left.Init( this, "result_left" );
+ m_result_right.Init( this, "result_right" );
+ m_result_multi.Init( this, "result_multi" );
+
+ m_value.Init( this, "value" );
+ m_balance.InitAndSet( this, "balance", 0.5f );
+ m_multilevel.InitAndSet( this, "multilevel", 0.5f );
+
+ m_bSpewResult.Init( this, "spewresult" );
+
+ m_flDefaultValue = FLT_MAX;
+}
+
+void CDmeBalanceToStereoCalculatorOperator::OnDestruction()
+{
+}
+
+void CDmeBalanceToStereoCalculatorOperator::GetInputAttributes ( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_value.GetAttribute() );
+ attrs.AddToTail( m_balance.GetAttribute() );
+ attrs.AddToTail( m_multilevel.GetAttribute() );
+}
+
+void CDmeBalanceToStereoCalculatorOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_result_left.GetAttribute() );
+ attrs.AddToTail( m_result_right.GetAttribute() );
+ attrs.AddToTail( m_result_multi.GetAttribute() );
+}
+
+float CDmeBalanceToStereoCalculatorOperator::ComputeDefaultValue()
+{
+ // NOTE: This is a total hack, which we expect to remove soon, when the balance work is done.
+ const static UtlSymId_t symToElement = g_pDataModel->GetSymbol( "toElement" );
+ CDmeChannel *pChannel = FindReferringElement<CDmeChannel>( this, symToElement );
+ if ( !pChannel )
+ return 0.0f;
+
+ CDmElement *pControl = pChannel->GetFromElement();
+ if ( !pControl )
+ return 0.0f;
+
+ return pControl->GetValue<float>( "defaultValue" );
+}
+
+void CDmeBalanceToStereoCalculatorOperator::Operate()
+{
+ if ( m_flDefaultValue == FLT_MAX )
+ {
+ m_flDefaultValue = ComputeDefaultValue();
+ }
+
+ float flValue = m_value - m_flDefaultValue;
+ if ( m_balance > 0.5f )
+ {
+ m_result_right = m_value;
+ m_result_left = ( ( 1.0f - m_balance ) / 0.5f ) * flValue + m_flDefaultValue;
+ }
+ else
+ {
+ m_result_right = ( m_balance / 0.5f ) * flValue + m_flDefaultValue;
+ m_result_left = m_value;
+ }
+
+ m_result_multi = m_multilevel;
+
+ if ( m_bSpewResult )
+ {
+ Msg( "%s = l('%f') r('%f') m('%f')\n", GetName(), (float)m_result_left, (float)m_result_right, (float)m_result_multi );
+ }
+}
+
+void CDmeBalanceToStereoCalculatorOperator::SetSpewResult( bool state )
+{
+ m_bSpewResult = state;
+}
diff --git a/movieobjects/dmebookmark.cpp b/movieobjects/dmebookmark.cpp
new file mode 100644
index 0000000..35063c4
--- /dev/null
+++ b/movieobjects/dmebookmark.cpp
@@ -0,0 +1,32 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmebookmark.h"
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Class factory
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeBookmark, CDmeBookmark );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeBookmark::OnConstruction()
+{
+ m_Time.InitAndSet( this, "time", 0 );
+ m_Duration.InitAndSet( this, "duration", 0 );
+ m_Note.Init( this, "note" );
+}
+
+void CDmeBookmark::OnDestruction()
+{
+}
diff --git a/movieobjects/dmecamera.cpp b/movieobjects/dmecamera.cpp
new file mode 100644
index 0000000..f593602
--- /dev/null
+++ b/movieobjects/dmecamera.cpp
@@ -0,0 +1,288 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmecamera.h"
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "mathlib/vector.h"
+#include "movieobjects/dmetransform.h"
+#include "materialsystem/imaterialsystem.h"
+#include "movieobjects_interfaces.h"
+#include "tier2/tier2.h"
+
+// FIXME: REMOVE
+#include "istudiorender.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeCamera, CDmeCamera );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeCamera::OnConstruction()
+{
+ m_fieldOfView.InitAndSet( this, "fieldOfView", 30.0f );
+
+ // FIXME: This currently matches the client DLL for HL2
+ // but we probably need a way of getting this state from the client DLL
+ m_zNear.InitAndSet( this, "znear", 3.0f );
+ m_zFar.InitAndSet( this, "zfar", 16384.0f * 1.73205080757f );
+
+ m_fFocalDistance.InitAndSet( this, "focalDistance", 72.0f);
+ m_fAperture.InitAndSet( this, "aperture", 0.2f);
+ m_fShutterSpeed.InitAndSet( this, "shutterSpeed", 1.0f / 48.0f );
+ m_fToneMapScale.InitAndSet( this, "toneMapScale", 1.0f );
+ m_fBloomScale.InitAndSet( this, "bloomScale", 0.28f );
+ m_nDoFQuality.InitAndSet( this, "depthOfFieldQuality", 0 );
+ m_nMotionBlurQuality.InitAndSet( this, "motionBlurQuality", 0 );
+}
+
+void CDmeCamera::OnDestruction()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Loads the material system view matrix based on the transform
+//-----------------------------------------------------------------------------
+void CDmeCamera::LoadViewMatrix( bool bUseEngineCoordinateSystem )
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ VMatrix view;
+ GetViewMatrix( view, bUseEngineCoordinateSystem );
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadMatrix( view );
+}
+
+//-----------------------------------------------------------------------------
+// Loads the material system projection matrix based on the fov, etc.
+//-----------------------------------------------------------------------------
+void CDmeCamera::LoadProjectionMatrix( int nDisplayWidth, int nDisplayHeight )
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+
+ VMatrix proj;
+ GetProjectionMatrix( proj, nDisplayWidth, nDisplayHeight );
+ pRenderContext->LoadMatrix( proj );
+}
+
+//-----------------------------------------------------------------------------
+// Sets up studiorender camera state
+//-----------------------------------------------------------------------------
+void CDmeCamera::LoadStudioRenderCameraState()
+{
+ // FIXME: Remove this! This should automatically happen in DrawModel
+ // in studiorender.
+ if ( !g_pStudioRender )
+ return;
+
+ matrix3x4_t transform;
+ GetTransform()->GetTransform( transform );
+
+ Vector vecOrigin, vecRight, vecUp, vecForward;
+ MatrixGetColumn( transform, 0, vecRight );
+ MatrixGetColumn( transform, 1, vecUp );
+ MatrixGetColumn( transform, 2, vecForward );
+ MatrixGetColumn( transform, 3, vecOrigin );
+ g_pStudioRender->SetViewState( vecOrigin, vecRight, vecUp, vecForward );
+}
+
+//-----------------------------------------------------------------------------
+// Returns the x FOV (the full angle)
+//-----------------------------------------------------------------------------
+float CDmeCamera::GetFOVx() const
+{
+ return m_fieldOfView;
+}
+
+void CDmeCamera::SetFOVx( float fov )
+{
+ m_fieldOfView = fov;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the focal distance in inches
+//-----------------------------------------------------------------------------
+float CDmeCamera::GetFocalDistance() const
+{
+ return m_fFocalDistance;
+}
+
+//-----------------------------------------------------------------------------
+// Sets the focal distance in inches
+//-----------------------------------------------------------------------------
+void CDmeCamera::SetFocalDistance( const float &fFocalDistance )
+{
+ m_fFocalDistance = fFocalDistance;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the camera aperture in inches
+//-----------------------------------------------------------------------------
+float CDmeCamera::GetAperture() const
+{
+ return m_fAperture;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the camera aperture in inches
+//-----------------------------------------------------------------------------
+float CDmeCamera::GetShutterSpeed() const
+{
+ return m_fShutterSpeed;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the tone map scale
+//-----------------------------------------------------------------------------
+float CDmeCamera::GetToneMapScale() const
+{
+ return m_fToneMapScale;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the bloom scale
+//-----------------------------------------------------------------------------
+float CDmeCamera::GetBloomScale() const
+{
+ return m_fBloomScale;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the number of Depth of Field samples
+//-----------------------------------------------------------------------------
+int CDmeCamera::GetDepthOfFieldQuality() const
+{
+ return m_nDoFQuality;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the number of Motion Blur samples
+//-----------------------------------------------------------------------------
+int CDmeCamera::GetMotionBlurQuality() const
+{
+ return m_nMotionBlurQuality;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the view direction
+//-----------------------------------------------------------------------------
+void CDmeCamera::GetViewDirection( Vector *pDirection )
+{
+ matrix3x4_t transform;
+ GetTransform()->GetTransform( transform );
+ MatrixGetColumn( transform, 2, *pDirection );
+
+ // We look down the -z axis
+ *pDirection *= -1.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Sets up render state in the material system for rendering
+//-----------------------------------------------------------------------------
+void CDmeCamera::SetupRenderState( int nDisplayWidth, int nDisplayHeight, bool bUseEngineCoordinateSystem /* = false */ )
+{
+ LoadViewMatrix( bUseEngineCoordinateSystem );
+ LoadProjectionMatrix( nDisplayWidth, nDisplayHeight );
+ LoadStudioRenderCameraState( );
+}
+
+//-----------------------------------------------------------------------------
+// accessors for generated matrices
+//-----------------------------------------------------------------------------
+void CDmeCamera::GetViewMatrix( VMatrix &view, bool bUseEngineCoordinateSystem /* = false */ )
+{
+ matrix3x4_t transform, invTransform;
+ CDmeTransform *pTransform = GetTransform();
+ pTransform->GetTransform( transform );
+
+ if ( bUseEngineCoordinateSystem )
+ {
+ VMatrix matRotate( transform );
+ VMatrix matRotateZ;
+ MatrixBuildRotationAboutAxis( matRotateZ, Vector(0,0,1), -90 );
+ MatrixMultiply( matRotate, matRotateZ, matRotate );
+
+ VMatrix matRotateX;
+ MatrixBuildRotationAboutAxis( matRotateX, Vector(1,0,0), 90 );
+ MatrixMultiply( matRotate, matRotateX, matRotate );
+ transform = matRotate.As3x4();
+ }
+
+ MatrixInvert( transform, invTransform );
+ view = invTransform;
+}
+
+void CDmeCamera::GetProjectionMatrix( VMatrix &proj, int width, int height )
+{
+ float flFOV = m_fieldOfView.Get();
+ float flZNear = m_zNear.Get();
+ float flZFar = m_zFar.Get();
+ float flApsectRatio = (float)width / (float)height;
+
+// MatrixBuildPerspective( proj, flFOV, flFOV * flApsectRatio, flZNear, flZFar );
+
+#if 1
+ float halfWidth = tan( flFOV * M_PI / 360.0 );
+ float halfHeight = halfWidth / flApsectRatio;
+#else
+ float halfHeight = tan( flFOV * M_PI / 360.0 );
+ float halfWidth = flApsectRatio * halfHeight;
+#endif
+ memset( proj.Base(), 0, sizeof( proj ) );
+ proj[0][0] = 1.0f / halfWidth;
+ proj[1][1] = 1.0f / halfHeight;
+ proj[2][2] = flZFar / ( flZNear - flZFar );
+ proj[3][2] = -1.0f;
+ proj[2][3] = flZNear * flZFar / ( flZNear - flZFar );
+}
+
+void CDmeCamera::GetViewProjectionInverse( VMatrix &viewprojinv, int width, int height )
+{
+ VMatrix view, proj;
+ GetViewMatrix( view );
+ GetProjectionMatrix( proj, width, height );
+
+ VMatrix viewproj;
+ MatrixMultiply( proj, view, viewproj );
+ bool success = MatrixInverseGeneral( viewproj, viewprojinv );
+ if ( !success )
+ {
+ Assert( 0 );
+ MatrixInverseTR( viewproj, viewprojinv );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Computes the screen space position given a screen size
+//-----------------------------------------------------------------------------
+void CDmeCamera::ComputeScreenSpacePosition( const Vector &vecWorldPosition, int width, int height, Vector2D *pScreenPosition )
+{
+ VMatrix view, proj, viewproj;
+ GetViewMatrix( view );
+ GetProjectionMatrix( proj, width, height );
+ MatrixMultiply( proj, view, viewproj );
+
+ Vector vecScreenPos;
+ Vector3DMultiplyPositionProjective( viewproj, vecWorldPosition, vecScreenPos );
+
+ pScreenPosition->x = ( vecScreenPos.x + 1.0f ) * width / 2.0f;
+ pScreenPosition->y = ( -vecScreenPos.y + 1.0f ) * height / 2.0f;
+}
+
+
diff --git a/movieobjects/dmechannel.cpp b/movieobjects/dmechannel.cpp
new file mode 100644
index 0000000..81e5e9d
--- /dev/null
+++ b/movieobjects/dmechannel.cpp
@@ -0,0 +1,1007 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmechannel.h"
+#include "movieobjects/dmelog.h"
+#include "movieobjects/dmeclip.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmehandle.h"
+#include "datamodel/dmattribute.h"
+#include "tier0/vprof.h"
+#include "tier1/KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+//
+// CDmeChannelRecordingMgr
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+static CDmeChannelRecordingMgr s_ChannelRecordingMgr;
+CDmeChannelRecordingMgr *g_pChannelRecordingMgr = &s_ChannelRecordingMgr;
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CDmeChannelRecordingMgr::CDmeChannelRecordingMgr()
+{
+ m_bActive = false;
+ m_bSavedUndoState = false;
+ m_bUseTimeSelection = false;
+ m_nRevealType = PROCEDURAL_PRESET_NOT;
+ m_pRevealTarget = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Activates, deactivates layer recording.
+//-----------------------------------------------------------------------------
+void CDmeChannelRecordingMgr::StartLayerRecording( const char *pUndoRedoDesc, const DmeLog_TimeSelection_t *pTimeSelection )
+{
+ g_pDataModel->StartUndo( pUndoRedoDesc, pUndoRedoDesc );
+ m_bSavedUndoState = g_pDataModel->IsUndoEnabled();
+ g_pDataModel->SetUndoEnabled( false );
+
+ Assert( !m_bActive );
+ Assert( m_LayerChannels.Count() == 0 );
+ m_LayerChannels.Purge();
+ m_bActive = true;
+ m_bUseTimeSelection = ( pTimeSelection != NULL );
+ if ( pTimeSelection )
+ {
+ m_TimeSelection = *pTimeSelection;
+ }
+ else
+ {
+ // Slam to default value
+ m_TimeSelection = DmeLog_TimeSelection_t();
+ }
+ m_TimeSelection.ResetTimeAdvancing();
+}
+
+void CDmeChannelRecordingMgr::FinishLayerRecording( float flThreshhold, bool bFlattenLayers /*=true*/ )
+{
+ Assert( m_bActive );
+
+ RemoveAllChannelsFromRecordingLayer();
+ m_bUseTimeSelection = false;
+ m_TimeSelection.ResetTimeAdvancing();
+
+ g_pDataModel->SetUndoEnabled( m_bSavedUndoState );
+ if ( bFlattenLayers )
+ {
+ FlattenLayers( flThreshhold );
+ }
+ g_pDataModel->FinishUndo();
+
+ m_bActive = false;
+ m_LayerChannels.Purge();
+ m_nRevealType = PROCEDURAL_PRESET_NOT;
+ m_pRevealTarget = NULL;
+ m_PasteTarget.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a channel to the recording layer
+//-----------------------------------------------------------------------------
+void CDmeChannelRecordingMgr::AddChannelToRecordingLayer( CDmeChannel *pChannel, CDmeClip *pRoot, CDmeClip *pShot )
+{
+ Assert( pChannel->m_nRecordLayerIndex == -1 );
+
+ CDmeLog *pLog = pChannel->GetLog();
+ if ( !pLog )
+ return;
+
+ int nRecordLayerIndex = m_LayerChannels.AddToTail();
+ LayerChannelInfo_t& info = m_LayerChannels[nRecordLayerIndex];
+ info.m_Channel = pChannel;
+ if ( pRoot )
+ {
+ if ( !pChannel->BuildClipStack( &info.m_ClipStack, pRoot, pShot ) )
+ {
+ m_LayerChannels.Remove( nRecordLayerIndex );
+ return;
+ }
+ }
+
+ pChannel->m_nRecordLayerIndex = nRecordLayerIndex;
+
+ // This operation is undoable
+ CEnableUndoScopeGuard guard;
+ pLog->AddNewLayer();
+ pChannel->SetMode( CM_RECORD );
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes all channels from the recording layer
+//-----------------------------------------------------------------------------
+void CDmeChannelRecordingMgr::RemoveAllChannelsFromRecordingLayer( )
+{
+ int c = m_LayerChannels.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CDmeChannel *pChannel = m_LayerChannels[ i ].m_Channel.Get();
+ if ( !pChannel )
+ continue;
+
+ CDmeLog *pLog = pChannel->GetLog();
+ if ( pLog && IsUsingTimeSelection() )
+ {
+ // Computes local times for the time selection
+ DmeLog_TimeSelection_t timeSelection;
+ GetLocalTimeSelection( timeSelection, pChannel->m_nRecordLayerIndex );
+ pLog->FinishTimeSelection( pChannel->GetCurrentTime(), timeSelection );
+ }
+ pChannel->m_nRecordLayerIndex = -1;
+ pChannel->SetMode( CM_PLAY );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Flattens recorded layers into the base layer
+//-----------------------------------------------------------------------------
+void CDmeChannelRecordingMgr::FlattenLayers( float flThreshhold )
+{
+ int nFlags = 0;
+ if ( IsUsingDetachedTimeSelection() && IsTimeAdvancing() )
+ {
+ nFlags |= CDmeLog::FLATTEN_NODISCONTINUITY_FIXUP;
+ }
+
+ int c = m_LayerChannels.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CDmeChannel *pChannel = m_LayerChannels[ i ].m_Channel.Get();
+ if ( !pChannel )
+ continue;
+
+ CDmeLog *pLog = pChannel->GetLog();
+ Assert( pLog );
+ if ( !pLog )
+ continue;
+
+ pLog->FlattenLayers( flThreshhold, nFlags );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to iterate over all channels currently being recorded
+//-----------------------------------------------------------------------------
+int CDmeChannelRecordingMgr::GetLayerRecordingChannelCount()
+{
+ return m_LayerChannels.Count();
+}
+
+CDmeChannel* CDmeChannelRecordingMgr::GetLayerRecordingChannel( int nIndex )
+{
+ return m_LayerChannels[nIndex].m_Channel.Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes time selection info in log time for a particular recorded channel
+//-----------------------------------------------------------------------------
+void CDmeChannelRecordingMgr::GetLocalTimeSelection( DmeLog_TimeSelection_t& selection, int nIndex )
+{
+ Assert( m_bUseTimeSelection );
+ LayerChannelInfo_t& info = m_LayerChannels[nIndex];
+ selection = m_TimeSelection;
+ for ( int i = 0; i < TS_TIME_COUNT; ++i )
+ {
+ selection.m_nTimes[i] = CDmeClip::ToChildMediaTime( info.m_ClipStack, selection.m_nTimes[i], false );
+ }
+ selection.m_pPresetValue = info.m_pPresetValue;
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods which control various aspects of recording
+//-----------------------------------------------------------------------------
+void CDmeChannelRecordingMgr::UpdateTimeAdvancing( bool bPaused, DmeTime_t tCurTime )
+{
+ Assert( m_bActive && m_bUseTimeSelection );
+ if ( !bPaused && !m_TimeSelection.IsTimeAdvancing() )
+ {
+ m_TimeSelection.StartTimeAdvancing();
+
+ // blow away logs after curtime
+ int nCount = m_LayerChannels.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ LayerChannelInfo_t& info = m_LayerChannels[i];
+ DmeTime_t t = CDmeClip::ToChildMediaTime( info.m_ClipStack, tCurTime, false );
+ info.m_Channel->GetLog()->RemoveKeys( t, DMETIME_MAXTIME );
+ }
+ }
+}
+
+void CDmeChannelRecordingMgr::UpdateRecordingTimeSelectionTimes( const DmeLog_TimeSelection_t& timeSelection )
+{
+ Assert( m_bActive );
+ for ( int i = 0; i < TS_TIME_COUNT; ++i )
+ {
+ m_TimeSelection.m_nTimes[i] = timeSelection.m_nTimes[i];
+ }
+ m_TimeSelection.m_nResampleInterval = timeSelection.m_nResampleInterval;
+}
+
+void CDmeChannelRecordingMgr::SetIntensityOnAllLayers( float flIntensity )
+{
+ m_TimeSelection.m_flIntensity = flIntensity;
+}
+
+void CDmeChannelRecordingMgr::SetRecordingMode( RecordingMode_t mode )
+{
+ m_TimeSelection.SetRecordingMode( mode );
+}
+
+void CDmeChannelRecordingMgr::SetPresetValue( CDmeChannel* pChannel, CDmAttribute *pPresetValue )
+{
+ Assert( pChannel->m_nRecordLayerIndex != -1 );
+ m_LayerChannels[ pChannel->m_nRecordLayerIndex ].m_pPresetValue = pPresetValue;
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to query aspects of recording
+//-----------------------------------------------------------------------------
+bool CDmeChannelRecordingMgr::IsUsingDetachedTimeSelection() const
+{
+ Assert( m_bActive );
+ return !m_TimeSelection.m_bAttachedMode;
+}
+
+bool CDmeChannelRecordingMgr::IsTimeAdvancing() const
+{
+ Assert( m_bActive );
+ return m_TimeSelection.IsTimeAdvancing();
+}
+
+bool CDmeChannelRecordingMgr::IsUsingTimeSelection() const
+{
+ return m_bUseTimeSelection;
+}
+
+bool CDmeChannelRecordingMgr::ShouldRecordUsingTimeSelection() const
+{
+ return m_bUseTimeSelection && m_bActive;
+}
+
+void CDmeChannelRecordingMgr::SetProceduralTarget( int nProceduralMode, const CDmAttribute *pTarget )
+{
+ m_nRevealType = nProceduralMode;
+ m_pRevealTarget = pTarget;
+ m_PasteTarget.RemoveAll();
+}
+
+void CDmeChannelRecordingMgr::SetProceduralTarget( int nProceduralMode, const CUtlVector< KeyValues * >& list )
+{
+ m_nRevealType = nProceduralMode;
+ m_pRevealTarget = NULL;
+ m_PasteTarget.RemoveAll();
+ for ( int i = 0; i < list.Count(); ++i )
+ {
+ m_PasteTarget.AddToTail( list[ i ] );
+ }
+}
+
+int CDmeChannelRecordingMgr::GetProceduralType() const
+{
+ return m_nRevealType;
+}
+
+const CDmAttribute *CDmeChannelRecordingMgr::GetProceduralTarget() const
+{
+ Assert( m_pRevealTarget );
+ return m_pRevealTarget;
+}
+
+const CUtlVector< KeyValues * > &CDmeChannelRecordingMgr::GetPasteTarget() const
+{
+ return m_PasteTarget;
+}
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeChannel, CDmeChannel );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeChannel::OnConstruction()
+{
+ m_nRecordLayerIndex = -1;
+ m_nNextCurveType = CURVE_DEFAULT;
+ m_tCurrentTime = DMETIME_INVALID;
+ m_tPreviousTime = DMETIME_INVALID;
+ m_timeOutsideTimeframe = DMETIME_INVALID;
+
+ m_fromElement .Init( this, "fromElement", FATTRIB_HAS_CALLBACK | FATTRIB_NEVERCOPY );
+ m_fromAttribute .Init( this, "fromAttribute", FATTRIB_TOPOLOGICAL | FATTRIB_HAS_CALLBACK );
+ m_fromIndex .Init( this, "fromIndex", FATTRIB_TOPOLOGICAL );
+ m_toElement .Init( this, "toElement", FATTRIB_HAS_CALLBACK | FATTRIB_NEVERCOPY );
+ m_toAttribute .Init( this, "toAttribute", FATTRIB_TOPOLOGICAL | FATTRIB_HAS_CALLBACK );
+ m_toIndex .Init( this, "toIndex", FATTRIB_TOPOLOGICAL );
+ m_mode .InitAndSet( this, "mode", (int)CM_PASS );
+ m_log .Init( this, "log" );
+ m_FromAttributeHandle = DMATTRIBUTE_HANDLE_INVALID;
+ m_ToAttributeHandle = DMATTRIBUTE_HANDLE_INVALID;
+}
+
+void CDmeChannel::OnDestruction()
+{
+}
+
+int CDmeChannel::GetFromArrayIndex() const
+{
+ return m_fromIndex;
+}
+
+int CDmeChannel::GetToArrayIndex() const
+{
+ return m_toIndex;
+}
+
+void CDmeChannel::Play()
+{
+ CDmAttribute *pToAttr = GetToAttribute();
+
+ if ( pToAttr == NULL )
+ return;
+
+ CDmeLog *pLog = GetLog();
+ if ( !pLog )
+ return;
+
+ DmeTime_t time = GetCurrentTime();
+
+ DmeTime_t t0 = pLog->GetBeginTime();
+ DmeTime_t tn = pLog->GetEndTime();
+
+ PlayMode_t pmode = PM_HOLD;
+ switch ( pmode )
+ {
+ case PM_HOLD:
+ time = clamp( time, t0, tn );
+ break;
+
+ case PM_LOOP:
+ if ( tn == t0 )
+ {
+ time = t0;
+ }
+ else
+ {
+ time -= t0;
+ time = time % ( tn - t0 );
+ time += t0;
+ }
+ break;
+ }
+
+ // We might not want to do it this way, but this makes empty logs not get in the way if there is a valid pFromAttr
+#if 1
+ if ( pLog->IsEmpty() && !pLog->HasDefaultValue() &&
+ GetFromAttribute() != NULL )
+ {
+ Pass();
+ return;
+ }
+#endif
+
+ pLog->GetValue( time, pToAttr, m_toIndex.Get() );
+}
+
+void CDmeChannel::Pass()
+{
+ CDmAttribute *pFromAttr = GetFromAttribute();
+ CDmAttribute *pToAttr = GetToAttribute();
+ if ( !pFromAttr || !pToAttr )
+ return;
+
+ if ( pFromAttr == pToAttr )
+ return;
+
+ DmAttributeType_t type = pFromAttr->GetType();
+ const void *pValue = NULL;
+ if ( IsArrayType( type ) )
+ {
+ CDmrGenericArray array( pFromAttr );
+ pValue = array.GetUntyped( m_fromIndex.Get() );
+ type = ArrayTypeToValueType( type );
+ }
+ else
+ {
+ pValue = pFromAttr->GetValueUntyped();
+ }
+
+ if ( IsArrayType( pToAttr->GetType() ) )
+ {
+ CDmrGenericArray array( pToAttr );
+ array.Set( m_toIndex.Get(), type, pValue );
+ }
+ else
+ {
+ pToAttr->SetValue( type, pValue );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// IsDirty - ie needs to operate
+//-----------------------------------------------------------------------------
+bool CDmeChannel::IsDirty()
+{
+ if ( BaseClass::IsDirty() )
+ return true;
+
+ switch( GetMode() )
+ {
+ case CM_PLAY:
+ return true;
+
+ case CM_RECORD:
+ if ( m_nRecordLayerIndex != -1 )
+ return true;
+
+ // NOTE: Fall through!
+ case CM_PASS:
+ {
+ CDmAttribute *pFromAttr = GetFromAttribute();
+ if ( pFromAttr && pFromAttr->IsFlagSet( FATTRIB_OPERATOR_DIRTY ) )
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+
+void CDmeChannel::Operate()
+{
+ VPROF( "CDmeChannel::Operate" );
+
+ switch ( GetMode() )
+ {
+ case CM_OFF:
+ return;
+
+ case CM_PLAY:
+ Play();
+ return;
+
+ case CM_RECORD:
+ Record();
+ return;
+
+ case CM_PASS:
+ Pass();
+ return;
+ }
+}
+
+void CDmeChannel::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ ChannelMode_t mode = GetMode();
+ if ( mode == CM_OFF || mode == CM_PLAY )
+ return; // off and play ignore inputs
+
+ CDmAttribute *pAttr = GetFromAttribute();
+ if ( pAttr != NULL )
+ {
+ attrs.AddToTail( pAttr );
+ }
+}
+
+void CDmeChannel::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ ChannelMode_t mode = GetMode();
+ if ( mode == CM_OFF )
+ return; // off ignores inputs
+
+ if ( mode == CM_RECORD || mode == CM_PASS )
+ {
+ if ( GetFromAttribute() == GetToAttribute() )
+ return; // record/pass from and to the same attribute doesn't write anything
+ }
+
+ CDmAttribute *pAttr = GetToAttribute();
+ if ( pAttr != NULL )
+ {
+ attrs.AddToTail( pAttr );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+CDmElement *CDmeChannel::GetFromElement() const
+{
+ return m_fromElement;
+}
+
+CDmElement *CDmeChannel::GetToElement() const
+{
+ return m_toElement;
+}
+
+void CDmeChannel::SetLog( CDmeLog *pLog )
+{
+ m_log = pLog;
+}
+
+CDmeLog *CDmeChannel::CreateLog( DmAttributeType_t type )
+{
+ CDmeLog *log = CDmeLog::CreateLog( type, GetFileId() );
+ m_log.Set( log );
+ return log;
+}
+
+// HACK: This is an evil hack since the element and attribute change sequentially, but they really need to change in lockstep or else you're looking
+// up an attribute from some other element or vice versa.
+
+
+void CDmeChannel::SetInput( CDmElement* pElement, const char* pAttribute, int index )
+{
+ m_fromElement.Set( pElement );
+ m_fromAttribute.Set( pAttribute );
+ m_fromIndex.Set( index );
+ SetupFromAttribute();
+}
+
+void CDmeChannel::SetOutput( CDmElement* pElement, const char* pAttribute, int index )
+{
+ m_toElement.Set( pElement );
+ m_toAttribute.Set( pAttribute );
+ m_toIndex.Set( index );
+ SetupToAttribute();
+}
+
+void CDmeChannel::SetInput( CDmAttribute *pAttribute, int index )
+{
+ if ( pAttribute )
+ {
+ SetInput( pAttribute->GetOwner(), pAttribute->GetName(), index );
+ }
+ else
+ {
+ SetInput( NULL, "", index );
+ }
+}
+
+void CDmeChannel::SetOutput( CDmAttribute *pAttribute, int index )
+{
+ if ( pAttribute )
+ {
+ SetOutput( pAttribute->GetOwner(), pAttribute->GetName(), index );
+ }
+ else
+ {
+ SetOutput( NULL, "", index );
+ }
+}
+
+
+ChannelMode_t CDmeChannel::GetMode()
+{
+ return static_cast< ChannelMode_t >( m_mode.Get() );
+}
+
+void CDmeChannel::SetMode( ChannelMode_t mode )
+{
+ if ( mode != m_mode )
+ {
+ m_mode.Set( static_cast< int >( mode ) );
+ m_tPreviousTime = DMETIME_INVALID;
+ }
+}
+
+void CDmeChannel::ClearLog()
+{
+ GetLog()->ClearKeys();
+}
+
+CDmeLog *CDmeChannel::GetLog()
+{
+ if ( !m_log.GetElement() && ( m_FromAttributeHandle == DMATTRIBUTE_HANDLE_INVALID ) )
+ {
+ // NOTE: This will generate a new log based on the from attribute
+ SetupFromAttribute();
+ }
+ return m_log.GetElement();
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to cache off handles to attributes
+//-----------------------------------------------------------------------------
+CDmAttribute *CDmeChannel::SetupFromAttribute()
+{
+ m_FromAttributeHandle = DMATTRIBUTE_HANDLE_INVALID;
+
+ CDmElement *pObject = m_fromElement.GetElement();
+ const char *pName = m_fromAttribute.Get();
+ if ( pObject == NULL || pName == NULL || !pName[0] )
+ return NULL;
+
+ CDmAttribute *pAttr = pObject->GetAttribute( pName );
+ if ( !pAttr )
+ return NULL;
+
+ m_FromAttributeHandle = pAttr->GetHandle();
+
+ DmAttributeType_t fromType = pAttr->GetType();
+ if ( IsArrayType( fromType ) )
+ {
+ fromType = ArrayTypeToValueType( fromType );
+ }
+
+ CDmeLog *pLog = m_log.GetElement();
+ if ( pLog == NULL )
+ {
+ CreateLog( fromType );
+ return pAttr;
+ }
+
+ DmAttributeType_t logType = pLog->GetDataType();
+ if ( IsArrayType( logType ) )
+ {
+ logType = ArrayTypeToValueType( logType );
+ }
+
+ if ( logType != fromType )
+ {
+ // NOTE: This will release the current log
+ CreateLog( fromType );
+ }
+
+ return pAttr;
+}
+
+CDmAttribute *CDmeChannel::SetupToAttribute()
+{
+ m_ToAttributeHandle = DMATTRIBUTE_HANDLE_INVALID;
+
+ CDmElement *pObject = m_toElement.GetElement();
+ const char *pName = m_toAttribute.Get();
+ if ( pObject == NULL || pName == NULL || !pName[0] )
+ return NULL;
+
+ CDmAttribute *pAttr = pObject->GetAttribute( pName );
+ if ( !pAttr )
+ return NULL;
+
+ m_ToAttributeHandle = pAttr->GetHandle();
+ return pAttr;
+}
+
+
+//-----------------------------------------------------------------------------
+// This function gets called whenever an attribute changes
+//-----------------------------------------------------------------------------
+void CDmeChannel::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ if ( ( pAttribute == m_fromElement .GetAttribute() ) ||
+ ( pAttribute == m_fromAttribute.GetAttribute() ) )
+ {
+ // NOTE: This will force a recache of the attribute handle
+ m_FromAttributeHandle = DMATTRIBUTE_HANDLE_INVALID;
+ return;
+ }
+
+ if ( ( pAttribute == m_toElement .GetAttribute() ) ||
+ ( pAttribute == m_toAttribute.GetAttribute() ) )
+ {
+ m_ToAttributeHandle = DMATTRIBUTE_HANDLE_INVALID;
+ return;
+ }
+
+ BaseClass::OnAttributeChanged( pAttribute );
+}
+
+
+DmeTime_t CDmeChannel::GetCurrentTime() const
+{
+ return m_tCurrentTime;
+}
+
+//-----------------------------------------------------------------------------
+// Simple version. Only works if multiple active channels clips
+// do not reference the same channels
+//-----------------------------------------------------------------------------
+void CDmeChannel::SetCurrentTime( DmeTime_t time )
+{
+ m_tPreviousTime = m_tCurrentTime;
+ m_tCurrentTime = time;
+ m_timeOutsideTimeframe = DMETIME_ZERO;
+}
+
+//-----------------------------------------------------------------------------
+// SetCurrentTime sets the current time on the clip,
+// choosing the time closest to (and after) a timeframe if multiple sets in a frame
+//-----------------------------------------------------------------------------
+void CDmeChannel::SetCurrentTime( DmeTime_t time, DmeTime_t start, DmeTime_t end )
+{
+ m_tPreviousTime = m_tCurrentTime;
+
+ DmeTime_t dt( 0 );
+ if ( time < start )
+ {
+ dt = time - start;
+ time = start;
+ }
+ else if ( time >= end )
+ {
+ dt = time - end;
+ time = end;
+ }
+ DmeTime_t totf = m_timeOutsideTimeframe;
+
+ const DmeTime_t t0( 0 );
+ if ( ( dt < t0 && totf < t0 && dt < totf ) || // both prior to clip, old totf closer
+ ( dt < t0 && totf >= t0 ) || // new dt prior to clip, old totf in or after
+ ( dt >= t0 && totf >= t0 && dt > totf ) ) // both after clip, old totf closer
+ return; // if old todt is a better match, don't update channel time
+
+ m_tCurrentTime = time;
+ m_timeOutsideTimeframe = dt;
+}
+
+//-----------------------------------------------------------------------------
+// ClearTimeMetric marks m_timeOutsideTimeframe invalid (-inf is the worst possible match)
+//-----------------------------------------------------------------------------
+void CDmeChannel::ClearTimeMetric()
+{
+ m_timeOutsideTimeframe = DmeTime_t::MinTime();
+}
+
+void CDmeChannel::SetChannelToPlayToSelf( const char *outputAttributeName, float defaultValue, bool force /*= false*/ )
+{
+ if ( !HasAttribute( outputAttributeName ) )
+ {
+ AddAttribute( outputAttributeName, AT_FLOAT );
+ }
+
+ CDmeTypedLog< bool > *log = static_cast< CDmeTypedLog< bool >* >( GetLog() );
+ // Usually we won't put it into playback if it's empty, we'll just read the default value continously
+ if ( force || ( log && !log->IsEmpty() && !log->HasDefaultValue() ) )
+ {
+ SetMode( CM_PLAY );
+ SetOutput( this, outputAttributeName );
+ }
+ SetValue( outputAttributeName, defaultValue );
+}
+
+void CDmeChannel::SetNextKeyCurveType( int nCurveType )
+{
+ m_nNextCurveType = nCurveType;
+}
+
+CDmeLogLayer *FindLayerInSnapshot( const CDmrElementArray<CDmElement>& snapshotArray, CDmeLog *origLog )
+{
+ if ( !snapshotArray.IsValid() )
+ return NULL;
+
+ int c = snapshotArray.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeLogLayer *layer = CastElement< CDmeLogLayer >( snapshotArray[ i ] );
+ if ( !layer )
+ continue;
+
+ CDmeLog *pLog = layer->GetValueElement< CDmeLog >( "origLog" );
+ if ( !pLog )
+ {
+ Assert( 0 );
+ continue;
+ }
+
+ if ( pLog == origLog )
+ return layer;
+ }
+
+ return NULL;
+}
+
+KeyValues *FindLayerInPasteData( const CUtlVector< KeyValues * > &list, CDmeLog *log )
+{
+ int c = list.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDisableUndoScopeGuard noundo;
+
+ KeyValues *kv = list[ i ];
+ Assert( kv );
+
+ if ( Q_stricmp( kv->GetName(), "ControlLayers" ) )
+ continue;
+
+ LayerSelectionData_t *data = reinterpret_cast< LayerSelectionData_t * >( kv->GetPtr( "LayerData" ) );
+ if ( !data )
+ continue;
+
+ CDmeChannel *ch = data->m_hChannel;
+ if ( !ch )
+ continue;
+
+ CDmeLog *chLog = ch->GetLog();
+ if ( chLog == log )
+ return kv;
+ }
+
+ return NULL;
+}
+
+static int FindSpanningLayerAndSetIntensity( DmeLog_TimeSelection_t &ts, LayerSelectionData_t *data )
+{
+ Assert( data->m_vecData.Count() >= 2 );
+
+ float frac = ts.m_flIntensity;
+ int i = 0;
+ for ( ; i < data->m_vecData.Count() - 1; ++i )
+ {
+ LayerSelectionData_t::DataLayer_t *current = &data->m_vecData[ i ];
+ LayerSelectionData_t::DataLayer_t *next = &data->m_vecData[ i + 1 ];
+
+ if ( frac >= current->m_flStartFraction && frac <= next->m_flStartFraction )
+ {
+ frac = RemapVal( frac, current->m_flStartFraction, next->m_flStartFraction, 0.0f, 1.0f );
+ ts.m_flIntensity = frac;
+ break;
+ }
+ }
+
+ return i;
+}
+
+void CDmeChannel::Record()
+{
+ VPROF( "CDmeChannel::Record" );
+
+ CDmAttribute *pFromAttr = GetFromAttribute();
+ if ( pFromAttr == NULL )
+ return; // or clear out the log?
+
+ CDmeLog *pLog = GetLog();
+ DmeTime_t time = GetCurrentTime();
+ if ( m_tPreviousTime == DMETIME_INVALID )
+ {
+ m_tPreviousTime = time;
+ }
+
+ if ( g_pChannelRecordingMgr->ShouldRecordUsingTimeSelection() )
+ {
+ Assert( m_nRecordLayerIndex != -1 );
+
+ // Computes local times for the time selection
+ DmeLog_TimeSelection_t timeSelection;
+ g_pChannelRecordingMgr->GetLocalTimeSelection( timeSelection, m_nRecordLayerIndex );
+
+ int nType = g_pChannelRecordingMgr->GetProceduralType();
+ switch ( nType )
+ {
+ default:
+ case PROCEDURAL_PRESET_NOT:
+ {
+ pLog->StampKeyAtHead( time, m_tPreviousTime, timeSelection, pFromAttr, m_fromIndex.Get() );
+ }
+ break;
+ case PROCEDURAL_PRESET_REVEAL:
+ {
+ // Find the matching layer in the "target" data array
+ const CDmrElementArray<CDmElement> snapshotArray = const_cast< CDmAttribute * >( g_pChannelRecordingMgr->GetProceduralTarget() );
+ CDmeLogLayer *snapshotLayer = FindLayerInSnapshot( snapshotArray, pLog );
+ if ( snapshotLayer )
+ {
+ Assert( pLog );
+ pLog->RevealUsingTimeSelection( timeSelection, snapshotLayer );
+ }
+ }
+ break;
+ case PROCEDURAL_PRESET_JITTER:
+ case PROCEDURAL_PRESET_SMOOTH:
+ case PROCEDURAL_PRESET_SHARPEN:
+ case PROCEDURAL_PRESET_SOFTEN:
+ case PROCEDURAL_PRESET_STAGGER:
+ case PROCEDURAL_PRESET_PASTE:
+ {
+ const CUtlVector< KeyValues * > &pasteTarget = g_pChannelRecordingMgr->GetPasteTarget();
+ KeyValues *layer = FindLayerInPasteData( pasteTarget, pLog );
+ if ( layer )
+ {
+ LayerSelectionData_t *data = reinterpret_cast< LayerSelectionData_t * >( layer->GetPtr( "LayerData" ) );
+ Assert( data );
+
+ int iSourceLayer = FindSpanningLayerAndSetIntensity( timeSelection, data );
+
+ CDmeLogLayer *sourceLayer = data->m_vecData[ iSourceLayer ].m_hData.Get();
+ CDmeLogLayer *targetLayer = data->m_vecData[ iSourceLayer + 1 ].m_hData.Get();
+ if ( sourceLayer && sourceLayer->GetKeyCount() > 0 &&
+ targetLayer && targetLayer->GetKeyCount() > 0 &&
+ sourceLayer->GetKeyCount() == targetLayer->GetKeyCount() )
+ {
+ Assert( pLog->GetNumLayers() >= 2 );
+ CDmeLogLayer *outputLayer = pLog->GetLayer( pLog->GetTopmostLayer() );
+ if ( nType == PROCEDURAL_PRESET_STAGGER )
+ {
+ pLog->BlendTimesUsingTimeSelection( sourceLayer, targetLayer, outputLayer, timeSelection, data->m_tStartOffset );
+ }
+ else
+ {
+ pLog->BlendLayersUsingTimeSelection( sourceLayer, targetLayer, outputLayer, timeSelection, false, data->m_tStartOffset );
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ else
+ {
+ if ( m_tPreviousTime != time )
+ {
+ pLog->SetDuplicateKeyAtTime( m_tPreviousTime );
+ }
+ pLog->SetKey( time, pFromAttr, m_fromIndex.Get(), m_nNextCurveType );
+ m_nNextCurveType = CURVE_DEFAULT;
+ }
+
+ // Output the data that's in the log
+ Play();
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a clip stack that passes through root + shot
+// Returns true if it succeeded
+//-----------------------------------------------------------------------------
+bool CDmeChannel::BuildClipStack( DmeClipStack_t *pClipStack, CDmeClip *pRoot, CDmeClip *pShot )
+{
+ DmAttributeReferenceIterator_t it;
+ for ( it = g_pDataModel->FirstAttributeReferencingElement( GetHandle() );
+ it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
+ it = g_pDataModel->NextAttributeReferencingElement( it ) )
+ {
+ CDmAttribute *pAttribute = g_pDataModel->GetAttribute( it );
+ CDmElement *pElement = pAttribute->GetOwner();
+ CDmeChannelsClip *pChannelsClip = CastElement< CDmeChannelsClip >( pElement );
+ if ( !pChannelsClip )
+ continue;
+
+ if ( pChannelsClip->BuildClipStack( pClipStack, pRoot, pShot ) )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the owner clip for a channel which passes through the root
+//-----------------------------------------------------------------------------
+CDmeClip* CDmeChannel::FindOwnerClipForChannel( CDmeClip *pRoot )
+{
+ DmeClipStack_t stack;
+ if ( BuildClipStack( &stack, pRoot, pRoot ) )
+ return stack[ stack.Count() - 1 ];
+ return NULL;
+}
+
diff --git a/movieobjects/dmeclip.cpp b/movieobjects/dmeclip.cpp
new file mode 100644
index 0000000..b97c5cb
--- /dev/null
+++ b/movieobjects/dmeclip.cpp
@@ -0,0 +1,1725 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmeclip.h"
+
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmehandle.h"
+
+#include "movieobjects/dmetimeframe.h"
+#include "movieobjects/dmebookmark.h"
+#include "movieobjects/dmesound.h"
+#include "movieobjects/dmechannel.h"
+#include "movieobjects/dmecamera.h"
+#include "movieobjects/dmelight.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmeinput.h"
+#include "movieobjects/dmeoperator.h"
+#include "movieobjects/dmematerial.h"
+#include "movieobjects/dmetrack.h"
+#include "movieobjects/dmetrackgroup.h"
+#include "movieobjects/dmematerialoverlayfxclip.h"
+#include "movieobjects/dmeanimationset.h"
+#include "movieobjects_interfaces.h"
+
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imesh.h"
+#include "tier3/tier3.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// String to clip type + back
+//-----------------------------------------------------------------------------
+static const char *s_pClipTypeNames[DMECLIP_TYPE_COUNT] =
+{
+ "Channel",
+ "Audio",
+ "Effects",
+ "Film",
+};
+
+DmeClipType_t ClipTypeFromString( const char *pName )
+{
+ for ( DmeClipType_t i = DMECLIP_FIRST; i <= DMECLIP_LAST; ++i )
+ {
+ if ( !Q_stricmp( pName, s_pClipTypeNames[i] ) )
+ return i;
+ }
+ return DMECLIP_UNKNOWN;
+}
+
+const char *ClipTypeToString( DmeClipType_t type )
+{
+ if ( type >= DMECLIP_FIRST && type <= DMECLIP_LAST )
+ return s_pClipTypeNames[ type ];
+ return "Unknown";
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeClip - common base class for filmclips, soundclips, and channelclips
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeClip, CDmeClip );
+
+void CDmeClip::OnConstruction()
+{
+ m_TimeFrame.InitAndCreate( this, "timeFrame" );
+ m_ClipColor.InitAndSet( this, "color", Color( 0, 0, 0, 0 ) );
+ m_ClipText.Init( this, "text" );
+ m_bMute.Init( this, "mute" );
+ m_TrackGroups.Init( this, "trackGroups", FATTRIB_MUSTCOPY | FATTRIB_HAS_ARRAY_CALLBACK );
+}
+
+void CDmeClip::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Inherited from IDmElement
+//-----------------------------------------------------------------------------
+void CDmeClip::OnAttributeArrayElementAdded( CDmAttribute *pAttribute, int nFirstElem, int nLastElem )
+{
+ BaseClass::OnAttributeArrayElementAdded( pAttribute, nFirstElem, nLastElem );
+ if ( pAttribute == m_TrackGroups.GetAttribute() )
+ {
+ for ( int i = nFirstElem; i <= nLastElem; ++i )
+ {
+ CDmeTrackGroup *pTrackGroup = m_TrackGroups[ i ];
+ if ( pTrackGroup )
+ {
+ pTrackGroup->SetOwnerClip( this );
+ }
+ }
+ return;
+ }
+}
+
+void CDmeClip::OnAttributeArrayElementRemoved( CDmAttribute *pAttribute, int nFirstElem, int nLastElem )
+{
+ BaseClass::OnAttributeArrayElementRemoved( pAttribute, nFirstElem, nLastElem );
+ if ( pAttribute == m_TrackGroups.GetAttribute() )
+ {
+ for ( int i = nFirstElem; i <= nLastElem; ++i )
+ {
+ CDmeTrackGroup *pTrackGroup = m_TrackGroups[ i ];
+ if ( pTrackGroup )
+ {
+ pTrackGroup->SetOwnerClip( NULL );
+ }
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Clip color
+//-----------------------------------------------------------------------------
+void CDmeClip::SetClipColor( const Color& clr )
+{
+ m_ClipColor.Set( clr );
+}
+
+Color CDmeClip::GetClipColor() const
+{
+ return m_ClipColor.Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Clip text
+//-----------------------------------------------------------------------------
+void CDmeClip::SetClipText( const char *pText )
+{
+ m_ClipText = pText;
+}
+
+const char* CDmeClip::GetClipText() const
+{
+ return m_ClipText;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the time frame
+//-----------------------------------------------------------------------------
+CDmeTimeFrame *CDmeClip::GetTimeFrame() const
+{
+ return m_TimeFrame.GetElement();
+}
+
+DmeTime_t CDmeClip::ToChildMediaTime( DmeTime_t t, bool bClamp ) const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->ToChildMediaTime( t, bClamp ) : DmeTime_t( 0 );
+}
+
+DmeTime_t CDmeClip::FromChildMediaTime( DmeTime_t t, bool bClamp ) const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->FromChildMediaTime( t, bClamp ) : DmeTime_t( 0 );
+}
+
+DmeTime_t CDmeClip::ToChildMediaDuration( DmeTime_t dt ) const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->ToChildMediaDuration( dt ) : DmeTime_t( 0 );
+}
+
+DmeTime_t CDmeClip::FromChildMediaDuration( DmeTime_t dt ) const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->FromChildMediaDuration( dt ) : DmeTime_t( 0 );
+}
+
+DmeTime_t CDmeClip::GetTimeOffset() const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->GetTimeOffset() : DmeTime_t( 0 );
+}
+
+float CDmeClip::GetTimeScale() const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->GetTimeScale() : 0.0f;
+}
+
+DmeTime_t CDmeClip::GetStartTime() const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->GetStartTime() : DmeTime_t( 0 );
+}
+
+DmeTime_t CDmeClip::GetEndTime() const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->GetStartTime() + tf->GetDuration() : DmeTime_t( 0 );
+}
+
+DmeTime_t CDmeClip::GetDuration() const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->GetDuration() : DmeTime_t( 0 );
+}
+
+DmeTime_t CDmeClip::GetStartInChildMediaTime() const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->GetStartInChildMediaTime() : DmeTime_t( 0 );
+}
+
+DmeTime_t CDmeClip::GetEndInChildMediaTime() const
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ return tf ? tf->GetEndInChildMediaTime() : DmeTime_t( 0 );
+}
+
+void CDmeClip::SetTimeOffset( DmeTime_t t )
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ if ( tf )
+ {
+ tf->SetTimeOffset( t );
+ }
+}
+
+void CDmeClip::SetTimeScale( float s )
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ if ( tf )
+ {
+ tf->SetTimeScale( s );
+ }
+}
+
+void CDmeClip::SetStartTime( DmeTime_t t )
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ if ( tf )
+ {
+ tf->SetStartTime( t );
+ }
+}
+
+void CDmeClip::SetDuration( DmeTime_t t )
+{
+ CDmeTimeFrame *tf = m_TimeFrame.GetElement();
+ if ( tf )
+ {
+ tf->SetDuration( t );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Track iteration
+//-----------------------------------------------------------------------------
+const CUtlVector< DmElementHandle_t > &CDmeClip::GetTrackGroups( ) const
+{
+ return m_TrackGroups.Get();
+}
+
+int CDmeClip::GetTrackGroupCount( ) const
+{
+ // Make sure no invalid clip types have snuck in
+ return m_TrackGroups.Count();
+}
+
+CDmeTrackGroup *CDmeClip::GetTrackGroup( int nIndex ) const
+{
+ if ( ( nIndex >= 0 ) && ( nIndex < m_TrackGroups.Count() ) )
+ return m_TrackGroups[ nIndex ];
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Is a track group valid to add?
+//-----------------------------------------------------------------------------
+bool CDmeClip::IsTrackGroupValid( CDmeTrackGroup *pTrackGroup )
+{
+ // FIXME: If track groups have allowed types, we can check for validity
+ for ( DmeClipType_t i = DMECLIP_FIRST; i <= DMECLIP_LAST; ++i )
+ {
+ if ( !IsSubClipTypeAllowed( i ) && pTrackGroup->IsSubClipTypeAllowed( i ) )
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Track group addition/removal
+//-----------------------------------------------------------------------------
+void CDmeClip::AddTrackGroup( CDmeTrackGroup *pTrackGroup )
+{
+ if ( !IsTrackGroupValid( pTrackGroup ) )
+ return;
+
+ // FIXME: Should check if track with same name already exists???
+ if ( GetTrackGroupIndex( pTrackGroup ) < 0 )
+ {
+ m_TrackGroups.AddToTail( pTrackGroup );
+ }
+}
+
+void CDmeClip::AddTrackGroupBefore( CDmeTrackGroup *pTrackGroup, CDmeTrackGroup *pBefore )
+{
+ if ( !IsTrackGroupValid( pTrackGroup ) )
+ return;
+
+ // FIXME: Should check if track with same name already exists???
+ if ( GetTrackGroupIndex( pTrackGroup ) < 0 )
+ {
+ int nBeforeIndex = pBefore ? GetTrackGroupIndex( pBefore ) : GetTrackGroupCount();
+ if ( nBeforeIndex >= 0 )
+ {
+ m_TrackGroups.InsertBefore( nBeforeIndex, pTrackGroup );
+ }
+ }
+}
+
+
+CDmeTrackGroup *CDmeClip::AddTrackGroup( const char *pTrackGroupName )
+{
+ CDmeTrackGroup *pTrackGroup = CreateElement< CDmeTrackGroup >( pTrackGroupName, GetFileId() );
+ pTrackGroup->SetMinimized( false );
+ m_TrackGroups.AddToTail( pTrackGroup );
+ return pTrackGroup;
+}
+
+void CDmeClip::RemoveTrackGroup( int nIndex )
+{
+ Assert( nIndex >= 0 && nIndex < m_TrackGroups.Count() );
+
+ m_TrackGroups.Remove( nIndex );
+}
+
+void CDmeClip::RemoveTrackGroup( CDmeTrackGroup *pTrackGroup )
+{
+ int i = GetTrackGroupIndex( pTrackGroup );
+ if ( i < 0 )
+ return;
+
+ m_TrackGroups.Remove( i );
+}
+
+void CDmeClip::RemoveTrackGroup( const char *pTrackGroupName )
+{
+ if ( !pTrackGroupName )
+ {
+ pTrackGroupName = DMETRACKGROUP_DEFAULT_NAME;
+ }
+
+ int c = m_TrackGroups.Count();
+ for ( int i = c; --i >= 0; )
+ {
+ if ( !Q_strcmp( m_TrackGroups[i]->GetName(), pTrackGroupName ) )
+ {
+ m_TrackGroups.Remove( i );
+ return;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Swap track groups
+//-----------------------------------------------------------------------------
+void CDmeClip::SwapOrder( CDmeTrackGroup *pTrackGroup1, CDmeTrackGroup *pTrackGroup2 )
+{
+ if ( pTrackGroup1 == pTrackGroup2 )
+ return;
+ if ( pTrackGroup1->IsFilmTrackGroup() || pTrackGroup2->IsFilmTrackGroup() )
+ return;
+
+ int nIndex1 = -1, nIndex2 = -1;
+ int c = m_TrackGroups.Count();
+ for ( int i = c; --i >= 0; )
+ {
+ if ( m_TrackGroups[i] == pTrackGroup1 )
+ {
+ nIndex1 = i;
+ }
+ if ( m_TrackGroups[i] == pTrackGroup2 )
+ {
+ nIndex2 = i;
+ }
+ }
+ if ( ( nIndex1 < 0 ) || ( nIndex2 < 0 ) )
+ return;
+
+ m_TrackGroups.Swap( nIndex1, nIndex2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Track group finding
+//-----------------------------------------------------------------------------
+CDmeTrackGroup *CDmeClip::FindTrackGroup( const char *pTrackGroupName ) const
+{
+ if ( !pTrackGroupName )
+ {
+ pTrackGroupName = DMETRACKGROUP_DEFAULT_NAME;
+ }
+
+ int c = m_TrackGroups.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CDmeTrackGroup *pTrackGroup = m_TrackGroups[i];
+ if ( !pTrackGroup )
+ continue;
+
+ if ( !Q_strcmp( pTrackGroup->GetName(), pTrackGroupName ) )
+ return pTrackGroup;
+ }
+ return NULL;
+}
+
+int CDmeClip::GetTrackGroupIndex( CDmeTrackGroup *pTrackGroup ) const
+{
+ int nTrackGroups = m_TrackGroups.Count();
+ for ( int i = 0 ; i < nTrackGroups; ++i )
+ {
+ if ( pTrackGroup == m_TrackGroups[i] )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find or create a track group
+//-----------------------------------------------------------------------------
+CDmeTrackGroup *CDmeClip::FindOrAddTrackGroup( const char *pTrackGroupName )
+{
+ CDmeTrackGroup *pTrackGroup = FindTrackGroup( pTrackGroupName );
+ if ( !pTrackGroup )
+ {
+ pTrackGroup = AddTrackGroup( pTrackGroupName );
+ }
+ return pTrackGroup;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finding clips in track groups
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeClip::FindTrackForClip( CDmeClip *pClip, CDmeTrackGroup **ppTrackGroup /*= NULL*/ ) const
+{
+// DmeClipType_t type = pClip->GetClipType();
+ int c = m_TrackGroups.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ // FIXME: If trackgroups have valid types, can early out here
+ CDmeTrack *pTrack = m_TrackGroups[i]->FindTrackForClip( pClip );
+ if ( pTrack )
+ {
+ if ( ppTrackGroup )
+ {
+ *ppTrackGroup = m_TrackGroups[i];
+ }
+ return pTrack;
+ }
+ }
+
+ return NULL;
+}
+
+bool CDmeClip::FindMultiTrackGroupForClip( CDmeClip *pClip, int *pTrackGroupIndex, int *pTrackIndex, int *pClipIndex ) const
+{
+ int nTrackGroups = m_TrackGroups.Count();
+ for ( int gi = 0 ; gi < nTrackGroups; ++gi )
+ {
+ CDmeTrackGroup *pTrackGroup = m_TrackGroups[ gi ];
+ if ( !pTrackGroup )
+ continue;
+
+ if ( !pTrackGroup->FindTrackForClip( pClip, pTrackIndex, pClipIndex ) )
+ continue;
+
+ if ( pTrackGroupIndex )
+ {
+ *pTrackGroupIndex = gi;
+ }
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Finding clips in tracks by time
+//-----------------------------------------------------------------------------
+void CDmeClip::FindClipsAtTime( DmeClipType_t clipType, DmeTime_t time, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( clipType == DMECLIP_FILM )
+ return;
+
+ int gc = GetTrackGroupCount();
+ for ( int i = 0; i < gc; ++i )
+ {
+ CDmeTrackGroup *pTrackGroup = GetTrackGroup( i );
+ if ( !pTrackGroup )
+ continue;
+
+ pTrackGroup->FindClipsAtTime( clipType, time, flags, clips );
+ }
+}
+
+void CDmeClip::FindClipsWithinTime( DmeClipType_t clipType, DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( clipType == DMECLIP_FILM )
+ return;
+
+ int gc = GetTrackGroupCount();
+ for ( int i = 0; i < gc; ++i )
+ {
+ CDmeTrackGroup *pTrackGroup = GetTrackGroup( i );
+ if ( !pTrackGroup )
+ continue;
+
+ pTrackGroup->FindClipsWithinTime( clipType, startTime, endTime, flags, clips );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Build a list of all referring clips
+//-----------------------------------------------------------------------------
+static int BuildReferringClipList( CDmeClip *pClip, CDmeClip** ppParents, int nMaxCount )
+{
+ int nCount = 0;
+
+ DmAttributeReferenceIterator_t it, it2, it3;
+ for ( it = g_pDataModel->FirstAttributeReferencingElement( pClip->GetHandle() );
+ it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
+ it = g_pDataModel->NextAttributeReferencingElement( it ) )
+ {
+ CDmAttribute *pAttribute = g_pDataModel->GetAttribute( it );
+ const char *pName = pAttribute->GetName();
+ CDmElement *pElement = pAttribute->GetOwner();
+ CDmeTrack *pTrack = CastElement< CDmeTrack >( pElement );
+ if ( !pTrack )
+ continue;
+
+ for ( it2 = g_pDataModel->FirstAttributeReferencingElement( pTrack->GetHandle() );
+ it2 != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
+ it2 = g_pDataModel->NextAttributeReferencingElement( it2 ) )
+ {
+ pAttribute = g_pDataModel->GetAttribute( it2 );
+ pName = pAttribute->GetName();
+ pElement = pAttribute->GetOwner();
+ CDmeTrackGroup *pTrackGroup = CastElement< CDmeTrackGroup >( pElement );
+ if ( !pTrackGroup )
+ continue;
+
+ for ( it3 = g_pDataModel->FirstAttributeReferencingElement( pTrackGroup->GetHandle() );
+ it3 != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
+ it3 = g_pDataModel->NextAttributeReferencingElement( it3 ) )
+ {
+ pAttribute = g_pDataModel->GetAttribute( it3 );
+ pName = pAttribute->GetName();
+ pElement = pAttribute->GetOwner();
+ CDmeClip *pParent = CastElement< CDmeClip >( pElement );
+ if ( !pParent )
+ continue;
+
+ Assert( nCount < nMaxCount );
+ if ( nCount >= nMaxCount )
+ return nCount;
+ ppParents[nCount++] = pParent;
+ }
+ }
+ }
+ return nCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Clip stack
+//-----------------------------------------------------------------------------
+static bool BuildClipStack_R( DmeClipStack_t* pStack, CDmeClip *pMovie, CDmeClip *pShot, CDmeClip *pCurrent )
+{
+ // Add this clip to the stack
+ int nIndex = pStack->AddToHead( CDmeHandle< CDmeClip >( pCurrent ) );
+
+ // Is this clip the shot? We don't need to look for it any more.
+ if ( pCurrent == pShot )
+ {
+ pShot = NULL;
+ }
+
+ // Is this clip the movie? We succeeded if we already found the shot!
+ if ( pCurrent == pMovie )
+ {
+ if ( !pShot )
+ return true;
+ }
+ else
+ {
+ // NOTE: This algorithm assumes a clip can never appear twice under another clip
+ // at a single level of hierarchy.
+ CDmeClip* ppParents[1024];
+ int nCount = BuildReferringClipList( pCurrent, ppParents, 1024 );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ // Can we find a path to the root through the shot? We succeeded!
+ if ( BuildClipStack_R( pStack, pMovie, pShot, ppParents[i] ) )
+ return true;
+ }
+ }
+
+ // This clip didn't work out for us. Remove it.
+ pStack->Remove( nIndex );
+
+ return false;
+}
+
+bool CDmeClip::BuildClipStack( DmeClipStack_t* pStack, CDmeClip *pMovie, CDmeClip *pShot )
+{
+ // Walk through each shot in the movie and look for the subClip, if don't find it recurse into each shot
+ return BuildClipStack_R( pStack, pMovie, pShot, this );
+}
+
+DmeTime_t CDmeClip::ToChildMediaTime( const DmeClipStack_t& stack, DmeTime_t globalTime, bool bClamp /* = true */ )
+{
+ DmeTime_t time = globalTime;
+
+ int nClips = stack.Count();
+ for ( int i = 0; i < nClips; ++i )
+ {
+ time = stack[ i ]->ToChildMediaTime( time, bClamp );
+ }
+
+ return time;
+}
+
+DmeTime_t CDmeClip::FromChildMediaTime( const DmeClipStack_t& stack, DmeTime_t localTime, bool bClamp /* = true */ )
+{
+ DmeTime_t time = localTime;
+
+ int nClips = stack.Count();
+ for ( int i = nClips-1; i >= 0; --i )
+ {
+ time = stack[ i ]->FromChildMediaTime( time, bClamp );
+ }
+
+ return time;
+}
+
+DmeTime_t CDmeClip::ToChildMediaDuration( const DmeClipStack_t& stack, DmeTime_t globalDuration )
+{
+ DmeTime_t duration = globalDuration;
+
+ int nClips = stack.Count();
+ for ( int i = 0; i < nClips; ++i )
+ {
+ duration = stack[ i ]->ToChildMediaDuration( duration );
+ }
+
+ return duration;
+}
+
+DmeTime_t CDmeClip::FromChildMediaDuration( const DmeClipStack_t& stack, DmeTime_t localDuration )
+{
+ DmeTime_t duration = localDuration;
+
+ int nClips = stack.Count();
+ for ( int i = nClips-1; i >= 0; --i )
+ {
+ duration = stack[ i ]->FromChildMediaDuration( duration );
+ }
+
+ return duration;
+}
+
+
+void CDmeClip::ToChildMediaTime( DmeLog_TimeSelection_t &params, const DmeClipStack_t& stack )
+{
+ for ( int i = 0; i < TS_TIME_COUNT; ++i )
+ {
+ params.m_nTimes[i] = ToChildMediaTime( stack, params.m_nTimes[i], false );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CDmeSoundClip - timeframe view into a dmesound
+//
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSoundClip, CDmeSoundClip );
+
+void CDmeSoundClip::OnConstruction()
+{
+ m_Sound.Init( this, "sound" );
+ m_bShowWave.InitAndSet( this, "showwave", false );
+}
+
+void CDmeSoundClip::OnDestruction()
+{
+}
+
+void CDmeSoundClip::SetShowWave( bool state )
+{
+ m_bShowWave = state;
+}
+
+bool CDmeSoundClip::ShouldShowWave( ) const
+{
+ return m_bShowWave;
+}
+
+//-----------------------------------------------------------------------------
+// CDmeChannelsClip - timeframe view into a set of channels
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeChannelsClip, CDmeChannelsClip );
+
+void CDmeChannelsClip::OnConstruction()
+{
+ m_Channels.Init( this, "channels" );
+}
+
+void CDmeChannelsClip::OnDestruction()
+{
+}
+
+CDmeChannel *CDmeChannelsClip::CreatePassThruConnection( char const *passThruName,
+ CDmElement *pFrom, char const *pFromAttribute,
+ CDmElement *pTo, char const *pToAttribute, int index /*= 0*/ )
+{
+ CDmeChannel *helper = CreateElement< CDmeChannel >( passThruName, GetFileId() );
+ Assert( helper );
+
+ helper->SetMode( CM_PASS );
+ helper->SetInput( pFrom, pFromAttribute );
+ helper->SetOutput( pTo, pToAttribute, index );
+
+ m_Channels.AddToTail( helper );
+
+ return helper;
+}
+
+void CDmeChannelsClip::RemoveChannel( CDmeChannel *pChannel )
+{
+ int nCount = m_Channels.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( pChannel == m_Channels[i] )
+ {
+ m_Channels.Remove( i );
+ break;
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// CDmeFXClip - timeframe view into an effect
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeFXClip, CDmeFXClip );
+
+void CDmeFXClip::OnConstruction()
+{
+}
+
+void CDmeFXClip::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Global list of FX clip types
+//-----------------------------------------------------------------------------
+const char *CDmeFXClip::s_pFXClipTypes[MAX_FXCLIP_TYPES];
+const char *CDmeFXClip::s_pFXClipDescriptions[MAX_FXCLIP_TYPES];
+int CDmeFXClip::s_nFXClipTypeCount = 0;
+
+void CDmeFXClip::InstallFXClipType( const char *pElementType, const char *pDescription )
+{
+ s_pFXClipTypes[s_nFXClipTypeCount] = pElementType;
+ s_pFXClipDescriptions[s_nFXClipTypeCount] = pDescription;
+ ++s_nFXClipTypeCount;
+}
+
+int CDmeFXClip::FXClipTypeCount()
+{
+ return s_nFXClipTypeCount;
+}
+
+const char *CDmeFXClip::FXClipType( int nIndex )
+{
+ Assert( s_nFXClipTypeCount > nIndex );
+ return s_pFXClipTypes[nIndex];
+}
+
+const char *CDmeFXClip::FXClipDescription( int nIndex )
+{
+ Assert( s_nFXClipTypeCount > nIndex );
+ return s_pFXClipDescriptions[nIndex];
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeFilmClip - hierarchical clip (movie, sequence or shot) w/ scene info
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeFilmClip, CDmeFilmClip );
+
+void CDmeFilmClip::OnConstruction()
+{
+ m_MaterialOverlayEffect.Init( this, "materialOverlay" );
+
+ m_MapName.Init( this, "mapname" );
+ m_Camera.Init( this, "camera" );
+ m_MonitorCameras.Init( this, "monitorCameras" );
+ m_nActiveMonitor.InitAndSet( this, "activeMonitor", -1 );
+ m_Lights.Init( this, "lights" );
+ m_Scene.Init( this, "scene" );
+ m_AVIFile.Init( this, "aviFile" );
+ m_fadeInDuration .InitAndSet( this, "fadeIn", 0 );
+ m_fadeOutDuration.InitAndSet( this, "fadeOut", 0 );
+
+ m_Inputs.Init( this, "inputs" );
+ m_Operators.Init( this, "operators" );
+ m_bIsUsingCachedVersion.Init( this, "useAviFile" );
+ m_AnimationSets.Init( this, "animationSets" );
+ m_Bookmarks.Init( this, "bookmarks" );
+ m_FilmTrackGroup.Init( this, "subClipTrackGroup", FATTRIB_HAS_CALLBACK | FATTRIB_HAS_PRE_CALLBACK );
+ m_Volume.InitAndSet( this, "volume", 1.0);
+
+ m_pCachedVersion = NULL;
+ m_bIsUsingCachedVersion = false;
+ m_bReloadCachedVersion = false;
+}
+
+void CDmeFilmClip::OnDestruction()
+{
+ if ( g_pVideo != NULL && m_pCachedVersion != NULL )
+ {
+ g_pVideo->DestroyVideoMaterial( m_pCachedVersion );
+ m_pCachedVersion = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the special film track group
+//-----------------------------------------------------------------------------
+CDmeTrackGroup *CDmeFilmClip::GetFilmTrackGroup() const
+{
+ return m_FilmTrackGroup;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the special film track
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeFilmClip::GetFilmTrack() const
+{
+ CDmeTrackGroup *pTrackGroup = m_FilmTrackGroup.GetElement();
+ if ( pTrackGroup )
+ return pTrackGroup->GetFilmTrack();
+ return NULL;
+}
+
+CDmeTrackGroup *CDmeFilmClip::FindOrCreateFilmTrackGroup()
+{
+ if ( !m_FilmTrackGroup )
+ {
+ m_FilmTrackGroup = CreateElement< CDmeTrackGroup >( "subClipTrackGroup", GetFileId() );
+ m_FilmTrackGroup->SetMinimized( false );
+ m_FilmTrackGroup->SetMaxTrackCount( 1 );
+ m_FilmTrackGroup->SetOwnerClip( this );
+ }
+ return m_FilmTrackGroup;
+}
+
+CDmeTrack *CDmeFilmClip::FindOrCreateFilmTrack()
+{
+ CDmeTrackGroup *pTrackGroup = FindOrCreateFilmTrackGroup();
+ CDmeTrack *pTrack = pTrackGroup->GetFilmTrack();
+ return pTrack ? pTrack : m_FilmTrackGroup->CreateFilmTrack();
+}
+
+
+//-----------------------------------------------------------------------------
+// Finding clips in track groups
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeFilmClip::FindTrackForClip( CDmeClip *pClip, CDmeTrackGroup **ppTrackGroup /*= NULL*/ ) const
+{
+ if ( m_FilmTrackGroup.GetElement() )
+ {
+ CDmeTrack *pTrack = m_FilmTrackGroup->FindTrackForClip( pClip );
+ if ( pTrack )
+ {
+ if ( ppTrackGroup )
+ {
+ *ppTrackGroup = m_FilmTrackGroup;
+ }
+ return pTrack;
+ }
+ }
+
+ return CDmeClip::FindTrackForClip( pClip, ppTrackGroup );
+}
+
+//-----------------------------------------------------------------------------
+// Finding clips in tracks by time
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::FindClipsAtTime( DmeClipType_t clipType, DmeTime_t time, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( clipType == DMECLIP_FILM ) || ( clipType == DMECLIP_UNKNOWN ) )
+ {
+ if ( m_FilmTrackGroup )
+ {
+ m_FilmTrackGroup->FindClipsAtTime( clipType, time, flags, clips );
+ }
+ }
+
+ CDmeClip::FindClipsAtTime( clipType, time, flags, clips );
+}
+
+void CDmeFilmClip::FindClipsWithinTime( DmeClipType_t clipType, DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( clipType == DMECLIP_FILM ) || ( clipType == DMECLIP_UNKNOWN ) )
+ {
+ if ( m_FilmTrackGroup )
+ {
+ m_FilmTrackGroup->FindClipsWithinTime( clipType, startTime, endTime, flags, clips );
+ }
+ }
+
+ CDmeClip::FindClipsWithinTime( clipType, startTime, endTime, flags, clips );
+}
+
+
+//-----------------------------------------------------------------------------
+// Volume
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::SetVolume( float state )
+{
+ m_Volume = state;
+}
+float CDmeFilmClip::GetVolume() const
+{
+ return m_Volume.Get();
+}
+
+//-----------------------------------------------------------------------------
+// mapname helper methods
+//-----------------------------------------------------------------------------
+const char *CDmeFilmClip::GetMapName()
+{
+ return m_MapName.Get();
+}
+
+void CDmeFilmClip::SetMapName( const char *pMapName )
+{
+ m_MapName.Set( pMapName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Attribute changed
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::PreAttributeChanged( CDmAttribute *pAttribute )
+{
+ BaseClass::PreAttributeChanged( pAttribute );
+ if ( pAttribute == m_FilmTrackGroup.GetAttribute() )
+ {
+ if ( m_FilmTrackGroup.GetElement() )
+ {
+ m_FilmTrackGroup->SetOwnerClip( NULL );
+ }
+ return;
+ }
+}
+
+void CDmeFilmClip::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ BaseClass::OnAttributeChanged( pAttribute );
+ if ( pAttribute == m_FilmTrackGroup.GetAttribute() )
+ {
+ if ( m_FilmTrackGroup.GetElement() )
+ {
+ m_FilmTrackGroup->SetMaxTrackCount( 1 );
+ m_FilmTrackGroup->SetOwnerClip( this );
+ }
+ }
+ else if ( pAttribute->GetOwner() == m_TimeFrame.GetElement() )
+ {
+ InvokeOnAttributeChangedOnReferrers( GetHandle(), pAttribute );
+ }
+}
+
+void CDmeFilmClip::OnElementUnserialized( )
+{
+ BaseClass::OnElementUnserialized();
+
+ CDmeTrackGroup *pFilmTrackGroup = m_FilmTrackGroup.GetElement();
+ if ( pFilmTrackGroup )
+ {
+ pFilmTrackGroup->SetMaxTrackCount( 1 );
+ if ( pFilmTrackGroup->GetTrackCount() == 0 )
+ {
+ pFilmTrackGroup->CreateFilmTrack();
+ }
+ }
+
+ // this conversion code went in on 10/31/2005
+ // I'm hoping we don't care about any files that old - if we ever hit this, we should move this code into an unserialization converter
+ Assert( !HasAttribute( "overlay" ) && !HasAttribute( "overlayalpha" ) );
+ if ( HasAttribute( "overlay" ) || HasAttribute( "overlayalpha" ) )
+ {
+ Warning( "CDmeFilmClip %s is an old version that has overlay and/or overlayalpha attributes!\n", GetName() );
+
+ // Backward compat conversion
+ // If this is an older file with an overlay attribute, strip it out into materialoverlay
+ CDmAttribute *pOverlayAttribute = GetAttribute( "overlay" );
+ if ( !pOverlayAttribute )
+ goto cleanUp;
+
+ const char *pName = pOverlayAttribute->GetValueString();
+ if ( !pName || !pName[0] )
+ goto cleanUp;
+
+ // If we don't yet have a material overlay, create one
+ if ( m_MaterialOverlayEffect.GetElement() == NULL )
+ {
+ m_MaterialOverlayEffect = CreateElement<CDmeMaterialOverlayFXClip>( "materialOverlay", GetFileId() );
+ }
+
+ m_MaterialOverlayEffect->SetOverlayEffect( pName );
+
+ // If this is an older file with an overlayalpha attribute, strip it out into materialoverlay
+ CDmAttribute *pOverlayAlphaAttribute = GetAttribute( "overlayalpha" );
+ if ( pOverlayAlphaAttribute )
+ {
+ float alpha = pOverlayAlphaAttribute->GetValue<float>();
+ m_MaterialOverlayEffect->SetAlpha( alpha );
+ }
+
+cleanUp:
+ // Always strip out the old overlay attribute
+ RemoveAttribute( "overlay" );
+ RemoveAttribute( "overlayalpha" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Resolve
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::Resolve()
+{
+ BaseClass::Resolve();
+ if ( m_AVIFile.IsDirty() )
+ {
+ m_bReloadCachedVersion = true;
+ m_AVIFile.GetAttribute()->RemoveFlag( FATTRIB_DIRTY );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Helper for overlays
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::SetOverlay( const char *pMaterialName )
+{
+ if ( pMaterialName && pMaterialName[0] )
+ {
+ if ( !m_MaterialOverlayEffect.GetElement() )
+ {
+ m_MaterialOverlayEffect = CreateElement<CDmeMaterialOverlayFXClip>( "materialOverlay", GetFileId() );
+ }
+
+ m_MaterialOverlayEffect->SetOverlayEffect( pMaterialName );
+ }
+ else
+ {
+ m_MaterialOverlayEffect.Set( NULL );
+ }
+}
+
+IMaterial *CDmeFilmClip::GetOverlayMaterial()
+{
+ return m_MaterialOverlayEffect.GetElement() ? m_MaterialOverlayEffect->GetMaterial() : NULL;
+}
+
+float CDmeFilmClip::GetOverlayAlpha()
+{
+ return m_MaterialOverlayEffect.GetElement() ? m_MaterialOverlayEffect->GetAlpha() : 0.0f;
+}
+
+void CDmeFilmClip::SetOverlayAlpha( float alpha )
+{
+ if ( m_MaterialOverlayEffect.GetElement() )
+ {
+ m_MaterialOverlayEffect->SetAlpha( alpha );
+ }
+}
+
+bool CDmeFilmClip::HasOpaqueOverlay( void )
+{
+ if ( m_MaterialOverlayEffect->GetMaterial()->IsTranslucent() ||
+ ( m_MaterialOverlayEffect->GetAlpha() < 1.0f ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void CDmeFilmClip::DrawOverlay( DmeTime_t time, Rect_t &currentRect, Rect_t &totalRect )
+{
+ if ( m_MaterialOverlayEffect.GetElement() )
+ {
+ m_MaterialOverlayEffect->ApplyEffect( ToChildMediaTime( time ), currentRect, totalRect, NULL );
+ }
+
+ DmeTime_t fadeIn( m_fadeInDuration );
+ DmeTime_t fadeOut( m_fadeOutDuration );
+
+ float fade = 1.0f;
+ if ( time < GetStartTime() + fadeIn )
+ {
+ fade = ( time - GetStartTime() ) / fadeIn;
+ }
+ if ( time > GetEndTime() - fadeOut )
+ {
+ fade = min( fade, ( GetEndTime() - time ) / fadeOut );
+ }
+ if ( fade < 1.0f )
+ {
+ if ( !m_FadeMaterial.IsValid() )
+ {
+ m_FadeMaterial.Init( "engine\\singlecolor.vmt", NULL, false );
+ }
+
+ float r, g, b;
+ m_FadeMaterial->GetColorModulation( &r, &g, &b );
+ float a = m_FadeMaterial->GetAlphaModulation();
+
+ m_FadeMaterial->ColorModulate( 0.0f, 0.0f, 0.0f );
+ m_FadeMaterial->AlphaModulate( 1.0f - fade );
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( m_FadeMaterial );
+
+ float w = currentRect.width;
+ float h = currentRect.height;
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, h, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( w, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( w, h, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ m_FadeMaterial->ColorModulate( r, g, b );
+ m_FadeMaterial->AlphaModulate( a );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// AVI tape out
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::UseCachedVersion( bool bUseCachedVersion )
+{
+ m_bIsUsingCachedVersion = bUseCachedVersion;
+}
+
+bool CDmeFilmClip::IsUsingCachedVersion() const
+{
+ return m_bIsUsingCachedVersion;
+}
+
+IVideoMaterial *CDmeFilmClip::GetCachedVideoMaterial()
+{
+ if ( m_bReloadCachedVersion )
+ {
+ if ( g_pVideo )
+ {
+ if ( m_pCachedVersion != NULL )
+ {
+ g_pVideo->DestroyVideoMaterial( m_pCachedVersion );
+ m_pCachedVersion = NULL;
+ }
+ if ( m_AVIFile[0] )
+ {
+ m_pCachedVersion = g_pVideo->CreateVideoMaterial( m_AVIFile, m_AVIFile, "MOD" );
+ }
+
+ }
+ m_bReloadCachedVersion = false;
+ }
+ return m_pCachedVersion;
+}
+
+void CDmeFilmClip::SetCachedAVI( const char *pAVIFile )
+{
+ m_AVIFile = pAVIFile;
+ m_bReloadCachedVersion = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Camera helper methods
+//-----------------------------------------------------------------------------
+CDmeCamera *CDmeFilmClip::GetCamera()
+{
+ return m_Camera;
+}
+
+void CDmeFilmClip::SetCamera( CDmeCamera *pCamera )
+{
+ m_Camera = pCamera;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the monitor camera associated with the clip (for now, only 1 supported)
+//-----------------------------------------------------------------------------
+CDmeCamera *CDmeFilmClip::GetMonitorCamera()
+{
+ if ( m_nActiveMonitor < 0 )
+ return NULL;
+ return m_MonitorCameras[ m_nActiveMonitor ];
+}
+
+void CDmeFilmClip::AddMonitorCamera( CDmeCamera *pCamera )
+{
+ m_MonitorCameras.AddToTail( pCamera );
+}
+
+int CDmeFilmClip::FindMonitorCamera( CDmeCamera *pCamera )
+{
+ return m_MonitorCameras.Find( pCamera->GetHandle() );
+}
+
+void CDmeFilmClip::RemoveMonitorCamera( CDmeCamera *pCamera )
+{
+ int i = m_MonitorCameras.Find( pCamera->GetHandle() );
+ if ( i >= 0 )
+ {
+ if ( m_nActiveMonitor == i )
+ {
+ m_nActiveMonitor = -1;
+ }
+ m_MonitorCameras.FastRemove( i );
+ }
+}
+
+void CDmeFilmClip::SelectMonitorCamera( CDmeCamera *pCamera )
+{
+ m_nActiveMonitor = pCamera ? m_MonitorCameras.Find( pCamera->GetHandle() ) : -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Light helper methods
+//-----------------------------------------------------------------------------
+int CDmeFilmClip::GetLightCount()
+{
+ return m_Lights.Count();
+}
+
+CDmeLight *CDmeFilmClip::GetLight( int nIndex )
+{
+ if ( ( nIndex < 0 ) || ( nIndex >= m_Lights.Count() ) )
+ return NULL;
+
+ return m_Lights[ nIndex ];
+}
+
+void CDmeFilmClip::AddLight( CDmeLight *pLight )
+{
+ m_Lights.AddToTail( pLight );
+}
+
+
+//-----------------------------------------------------------------------------
+// Scene / Dag helper methods
+//-----------------------------------------------------------------------------
+CDmeDag *CDmeFilmClip::GetScene()
+{
+ return m_Scene.GetElement();
+}
+
+void CDmeFilmClip::SetScene( CDmeDag *pDag )
+{
+ m_Scene.Set( pDag );
+}
+
+
+//-----------------------------------------------------------------------------
+// helper for inputs and operators
+//-----------------------------------------------------------------------------
+int CDmeFilmClip::GetInputCount()
+{
+ return m_Inputs.Count();
+}
+
+CDmeInput *CDmeFilmClip::GetInput( int nIndex )
+{
+ if ( nIndex < 0 || nIndex >= m_Inputs.Count() )
+ return NULL;
+
+ return m_Inputs[ nIndex ];
+}
+
+void CDmeFilmClip::AddInput( CDmeInput *pInput )
+{
+ m_Inputs.AddToTail( pInput );
+}
+
+void CDmeFilmClip::RemoveAllInputs()
+{
+ m_Inputs.RemoveAll();
+}
+
+void CDmeFilmClip::AddOperator( CDmeOperator *pOperator )
+{
+ m_Operators.AddToTail( pOperator );
+}
+
+void CDmeFilmClip::CollectOperators( CUtlVector< DmElementHandle_t > &operators )
+{
+ int numInputs = m_Inputs.Count();
+ for ( int i = 0; i < numInputs; ++i )
+ {
+ operators.AddToTail( m_Inputs[ i ]->GetHandle() );
+ }
+
+ int numOperators = m_Operators.Count();
+ for ( int i = 0; i < numOperators; ++i )
+ {
+ operators.AddToTail( m_Operators[ i ]->GetHandle() );
+ }
+}
+
+int CDmeFilmClip::GetAnimationSetCount()
+{
+ // yes, this is nasty perf-wise, but since we only have a dozen or so animation sets,
+ // and we're about to rewrite the entire structure of animation sets vs. clips vs. scene
+ // it's not worth polluting the leaf code just to save a couple cycles short term
+
+ int nCount = 0;
+
+ int nElements = m_AnimationSets.Count();
+ for ( int i = 0; i < nElements; ++i )
+ {
+ CDmElement *pElement = m_AnimationSets.Get( i );
+ if ( !pElement )
+ continue;
+
+ CDmeAnimationSet *pAnimSet = CastElement< CDmeAnimationSet >( pElement );
+ if ( pAnimSet )
+ {
+ ++nCount;
+ }
+ else
+ {
+ const CDmAttribute *pChildren = pElement->GetAttribute( "children", AT_ELEMENT_ARRAY );
+ if ( !pChildren )
+ continue;
+
+ CDmrElementArrayConst< CDmElement > array( pChildren );
+ nCount += array.Count();
+ }
+ }
+
+ return nCount;
+}
+
+CDmeAnimationSet *CDmeFilmClip::GetAnimationSet( int idx )
+{
+ // yes, this is nasty perf-wise, but since we only have a dozen or so animation sets,
+ // and we're about to rewrite the entire structure of animation sets vs. clips vs. scene
+ // it's not worth polluting the leaf code just to save a couple cycles short term
+
+ int nElements = m_AnimationSets.Count();
+ for ( int i = 0; i < nElements; ++i )
+ {
+ CDmElement *pElement = m_AnimationSets.Get( i );
+ if ( !pElement )
+ continue;
+
+ CDmeAnimationSet *pAnimSet = CastElement< CDmeAnimationSet >( pElement );
+ if ( pAnimSet )
+ {
+ if ( idx == 0 )
+ return pAnimSet;
+ --idx;
+ }
+ else
+ {
+ const CDmAttribute *pChildren = pElement->GetAttribute( "children", AT_ELEMENT_ARRAY );
+ if ( !pChildren )
+ continue;
+
+ CDmrElementArrayConst< CDmElement > array( pChildren );
+ int nChildren = array.Count();
+
+ if ( idx < nChildren )
+ return CastElement< CDmeAnimationSet >( array[ idx ] );
+ idx -= nChildren;
+ }
+ }
+
+ return NULL;
+}
+
+void CDmeFilmClip::AddAnimationSet( CDmeAnimationSet *element )
+{
+ m_AnimationSets.AddToTail( element );
+}
+
+void CDmeFilmClip::RemoveAllAnimationSets()
+{
+ m_AnimationSets.RemoveAll();
+}
+
+CDmaElementArray< CDmElement > &CDmeFilmClip::GetAnimationSets()
+{
+ return m_AnimationSets;
+}
+
+const CDmaElementArray< CDmElement > &CDmeFilmClip::GetAnimationSets() const
+{
+ return m_AnimationSets;
+}
+
+
+const CDmaElementArray< CDmeBookmark > &CDmeFilmClip::GetBookmarks() const
+{
+ return m_Bookmarks;
+}
+
+CDmaElementArray< CDmeBookmark > &CDmeFilmClip::GetBookmarks()
+{
+ return m_Bookmarks;
+}
+
+
+//-----------------------------------------------------------------------------
+// Used to move clips in non-film track groups with film clips
+// Call BuildClipAssociations before modifying the film track,
+// then UpdateAssociatedClips after modifying it.
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::BuildClipAssociations( CUtlVector< ClipAssociation_t > &association, bool bHandleGaps )
+{
+ association.RemoveAll();
+
+ CDmeTrack *pFilmTrack = GetFilmTrack();
+ if ( !pFilmTrack )
+ return;
+
+ int c = pFilmTrack->GetClipCount();
+ int gc = GetTrackGroupCount();
+ if ( c == 0 || gc == 0 )
+ return;
+
+ DmeTime_t clipStartTime = GetStartInChildMediaTime();
+ DmeTime_t clipEndTime = GetEndInChildMediaTime();
+
+ // These slugs will be removed in UpdateAssociatedClips
+ if ( bHandleGaps )
+ {
+ pFilmTrack->FillAllGapsWithSlugs( "__tempSlug__", clipStartTime, clipEndTime );
+ }
+
+ for ( int i = 0; i < gc; ++i )
+ {
+ CDmeTrackGroup *pTrackGroup = GetTrackGroup( i );
+ int tc = pTrackGroup->GetTrackCount();
+ for ( int j = 0; j < tc; ++j )
+ {
+ CDmeTrack *pTrack = pTrackGroup->GetTrack( j );
+ if ( !pTrack->IsSynched() )
+ continue;
+
+ // Only have visible tracks now
+ int cc = pTrack->GetClipCount();
+ association.EnsureCapacity( association.Count() + cc );
+ for ( int k = 0; k < cc; ++k )
+ {
+ CDmeClip *pClip = pTrack->GetClip( k );
+ CDmeClip *pFilmClip = pFilmTrack->FindFilmClipAtTime( pClip->GetStartTime() );
+
+ int nIndex = association.AddToTail();
+ association[nIndex].m_hClip = pClip;
+ association[nIndex].m_hAssociation = pFilmClip;
+ if ( pFilmClip )
+ {
+ association[nIndex].m_offset = pClip->GetStartTime() - pFilmClip->GetStartTime();
+ association[nIndex].m_nType = ClipAssociation_t::HAS_CLIP;
+ continue;
+ }
+
+ // Handle edge cases
+ if ( pClip->GetStartTime() <= clipStartTime )
+ {
+ association[nIndex].m_offset = pClip->GetStartTime() - clipStartTime;
+ association[nIndex].m_nType = ClipAssociation_t::BEFORE_START;
+ continue;
+ }
+
+ if ( pClip->GetStartTime() >= clipEndTime )
+ {
+ association[nIndex].m_offset = pClip->GetStartTime() - clipEndTime;
+ association[nIndex].m_nType = ClipAssociation_t::AFTER_END;
+ continue;
+ }
+
+ association[nIndex].m_offset = DmeTime_t( 0 );
+ association[nIndex].m_nType = ClipAssociation_t::NO_MOVEMENT;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Rolls associated clips so they remain in the same relative time
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::RollAssociatedClips( CDmeClip *pClip, CUtlVector< ClipAssociation_t > &association, DmeTime_t dt )
+{
+ int c = association.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( association[i].m_nType != ClipAssociation_t::HAS_CLIP )
+ continue;
+
+ if ( association[i].m_hAssociation.Get() != pClip )
+ continue;
+
+ DmeTime_t newStartTime = association[i].m_hClip->GetStartTime() - dt;
+ association[i].m_hClip->SetStartTime( newStartTime );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Rolls associated clips so they remain in the same relative time
+//-----------------------------------------------------------------------------
+void CDmeFilmClip::ScaleAssociatedClips( CDmeClip *pClip, CUtlVector< ClipAssociation_t > &association, float ratio, DmeTime_t oldOffset )
+{
+ int c = association.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ if ( association[i].m_nType != ClipAssociation_t::HAS_CLIP )
+ continue;
+
+ if ( association[i].m_hAssociation.Get() != pClip )
+ continue;
+
+ DmeTime_t clipStartTime = pClip->GetStartTime();
+ DmeTime_t oldStartTime = association[i].m_hClip->GetStartTime();
+ DmeTime_t newStartTime = ( oldStartTime - clipStartTime + oldOffset ) / ratio + clipStartTime - pClip->GetTimeOffset();
+ association[i].m_hClip->SetStartTime( newStartTime );
+ }
+}
+
+void CDmeFilmClip::UpdateAssociatedClips( CUtlVector< ClipAssociation_t > &association )
+{
+ int i;
+
+ CDmeTrack *pFilmTrack = GetFilmTrack();
+ if ( !pFilmTrack )
+ return;
+
+ int c = association.Count();
+ if ( c > 0 )
+ {
+ DmeTime_t clipStartTime = GetStartInChildMediaTime();
+ DmeTime_t clipEndTime = GetEndInChildMediaTime();
+
+ for ( i = 0; i < c; ++i )
+ {
+ ClipAssociation_t &curr = association[i];
+ if ( !curr.m_hClip.Get() )
+ continue;
+
+ switch( curr.m_nType )
+ {
+ case ClipAssociation_t::HAS_CLIP:
+ if ( curr.m_hAssociation.Get() )
+ {
+ curr.m_hClip->SetStartTime( curr.m_hAssociation->GetStartTime() + curr.m_offset );
+ }
+ break;
+
+ case ClipAssociation_t::BEFORE_START:
+ curr.m_hClip->SetStartTime( clipStartTime + curr.m_offset );
+ break;
+
+ case ClipAssociation_t::AFTER_END:
+ curr.m_hClip->SetStartTime( clipEndTime + curr.m_offset );
+ break;
+ }
+ }
+ }
+
+ c = pFilmTrack->GetClipCount();
+ for ( i = c; --i >= 0; )
+ {
+ CDmeClip *pClip = pFilmTrack->GetClip(i);
+ if ( !Q_strcmp( pClip->GetName(), "__tempSlug__" ) )
+ {
+ pFilmTrack->RemoveClip( i );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a slug clip
+//-----------------------------------------------------------------------------
+CDmeFilmClip *CreateSlugClip( const char *pClipName, DmeTime_t startTime, DmeTime_t endTime, DmFileId_t fileid )
+{
+ CDmeFilmClip *pSlugClip = CreateElement<CDmeFilmClip>( pClipName, fileid );
+ pSlugClip->GetTimeFrame()->SetName( "timeframe" );
+ pSlugClip->SetStartTime( startTime );
+ pSlugClip->SetDuration( endTime - startTime );
+ pSlugClip->SetTimeOffset( DmeTime_t( 0 ) );
+ pSlugClip->SetTimeScale( 1.0f );
+ pSlugClip->SetClipColor( Color( 0, 0, 0, 128 ) );
+ pSlugClip->SetOverlay( "vgui/black" );
+ return pSlugClip;
+}
+
+//-----------------------------------------------------------------------------
+// helper methods
+//-----------------------------------------------------------------------------
+CDmeTrack *GetParentTrack( CDmeClip *pClip )
+{
+ DmAttributeReferenceIterator_t hAttr = g_pDataModel->FirstAttributeReferencingElement( pClip->GetHandle() );
+ for ( ; hAttr != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; hAttr = g_pDataModel->NextAttributeReferencingElement( hAttr ) )
+ {
+ CDmAttribute *pAttr = g_pDataModel->GetAttribute( hAttr );
+ if ( !pAttr )
+ continue;
+
+ CDmeTrack *pTrack = CastElement< CDmeTrack >( pAttr->GetOwner() );
+ if ( pTrack )
+ return pTrack;
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a channel in a channel or film clip targetting a particular element
+//-----------------------------------------------------------------------------
+CDmeChannel *FindChannelTargetingElement( CDmeChannelsClip *pChannelsClip, CDmElement *pElement, const char *pAttributeName )
+{
+ int nChannels = pChannelsClip->m_Channels.Count();
+ for ( int i = 0; i < nChannels; ++i )
+ {
+ CDmeChannel *pChannel = pChannelsClip->m_Channels[ i ];
+ CDmElement *toElement = pChannel->GetToElement();
+ if ( toElement != pElement )
+ continue;
+
+ if ( pAttributeName && ( Q_stricmp( pChannel->GetToAttribute()->GetName(), pAttributeName ) != 0 ) )
+ continue;
+
+ return pChannel;
+ }
+
+ return NULL;
+}
+
+CDmeChannel *FindChannelTargetingElement( CDmeFilmClip *pClip, CDmElement *pElement, const char *pAttributeName, CDmeChannelsClip **ppChannelsClip, CDmeTrack **ppTrack, CDmeTrackGroup **ppTrackGroup )
+{
+ int gc = pClip->GetTrackGroupCount();
+ for ( int i = 0; i < gc; ++i )
+ {
+ CDmeTrackGroup *pTrackGroup = pClip->GetTrackGroup( i );
+ DMETRACKGROUP_FOREACH_CLIP_TYPE_START( CDmeChannelsClip, pTrackGroup, pTrack, pChannelsClip )
+
+ CDmeChannel *pChannel = FindChannelTargetingElement( pChannelsClip, pElement, pAttributeName );
+ if ( !pChannel )
+ continue;
+
+ if ( ppChannelsClip )
+ {
+ *ppChannelsClip = pChannelsClip;
+ }
+ if ( ppTrack )
+ {
+ *ppTrack = pTrack;
+ }
+ if ( ppTrackGroup )
+ {
+ *ppTrackGroup = pTrackGroup;
+ }
+ return pChannel;
+
+ DMETRACKGROUP_FOREACH_CLIP_TYPE_END()
+ }
+
+ return NULL;
+}
diff --git a/movieobjects/dmecombinationoperator.cpp b/movieobjects/dmecombinationoperator.cpp
new file mode 100644
index 0000000..abc4bdc
--- /dev/null
+++ b/movieobjects/dmecombinationoperator.cpp
@@ -0,0 +1,2184 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmecombinationoperator.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmattribute.h"
+#include "movieobjects/dmechannel.h"
+#include "movieobjects/dmemodel.h"
+#include "movieobjects/dmeshape.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmeclip.h"
+#include "movieobjects/dmelog.h"
+#include "movieobjects/dmevertexdata.h"
+#include "movieobjects/dmemesh.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeCombinationInputControl, CDmeCombinationInputControl );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeCombinationInputControl::OnConstruction()
+{
+ m_RawControlNames.Init( this, "rawControlNames" );
+ m_bIsStereo.Init( this, "stereo" );
+ m_bIsEyelid.InitAndSet( this, "eyelid", false );
+ m_WrinkleScales.Init( this, "wrinkleScales" );
+}
+
+void CDmeCombinationInputControl::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Backward compat
+//-----------------------------------------------------------------------------
+void CDmeCombinationInputControl::OnElementUnserialized()
+{
+ BaseClass::OnElementUnserialized();
+
+ int nWrinkleCount = m_WrinkleScales.Count();
+ int nControlCount = m_RawControlNames.Count();
+ if ( nWrinkleCount < nControlCount )
+ {
+ for ( int i = nWrinkleCount; i < nControlCount; ++i )
+ {
+ m_WrinkleScales.AddToTail( 0.0f );
+ }
+ }
+ else if ( nWrinkleCount > nControlCount )
+ {
+ m_WrinkleScales.RemoveMultiple( nControlCount, nWrinkleCount - nControlCount );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a control, returns the control index
+//-----------------------------------------------------------------------------
+bool CDmeCombinationInputControl::AddRawControl( const char *pRawControlName )
+{
+ Assert( !strchr( pRawControlName, '_' ) && !strchr( pRawControlName, ' ' ) );
+ int nCount = m_RawControlNames.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pRawControlName, m_RawControlNames[i] ) )
+ return false;
+ }
+
+ m_RawControlNames.AddToTail( pRawControlName );
+ m_WrinkleScales.AddToTail( 0.0f );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a raw control by name
+//-----------------------------------------------------------------------------
+int CDmeCombinationInputControl::FindRawControl( const char *pRawControlName )
+{
+ Assert( !strchr( pRawControlName, '_' ) && !strchr( pRawControlName, ' ' ) );
+ int nCount = m_RawControlNames.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pRawControlName, m_RawControlNames[i] ) )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes controls
+//-----------------------------------------------------------------------------
+bool CDmeCombinationInputControl::RemoveRawControl( const char *pRawControlName )
+{
+ int i = FindRawControl( pRawControlName );
+ if ( i >= 0 )
+ {
+ m_RawControlNames.FastRemove( i );
+ m_WrinkleScales.FastRemove( i );
+ return true;
+ }
+
+ return false;
+}
+
+void CDmeCombinationInputControl::RemoveAllRawControls()
+{
+ m_RawControlNames.RemoveAll();
+ m_WrinkleScales.RemoveAll( );
+}
+
+
+//-----------------------------------------------------------------------------
+// Iterates remapped controls
+//-----------------------------------------------------------------------------
+int CDmeCombinationInputControl::RawControlCount() const
+{
+ return m_RawControlNames.Count();
+}
+
+const char *CDmeCombinationInputControl::RawControlName( int nIndex ) const
+{
+ return m_RawControlNames[ nIndex ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Is this control a stereo control?
+//-----------------------------------------------------------------------------
+bool CDmeCombinationInputControl::IsStereo() const
+{
+ return m_bIsStereo;
+}
+
+void CDmeCombinationInputControl::SetStereo( bool bStereo )
+{
+ m_bIsStereo = bStereo;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is this control an eyelid control?
+//-----------------------------------------------------------------------------
+bool CDmeCombinationInputControl::IsEyelid() const
+{
+ return m_bIsEyelid;
+}
+
+void CDmeCombinationInputControl::SetEyelid( bool bEyelid )
+{
+ m_bIsEyelid = bEyelid;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reordering controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationInputControl::MoveRawControlUp( const char *pRawControlName )
+{
+ int nIndex = FindRawControl( pRawControlName );
+ if ( nIndex > 0 )
+ {
+ m_RawControlNames.Swap( nIndex, nIndex - 1 );
+ m_WrinkleScales.Swap( nIndex, nIndex - 1 );
+ }
+}
+
+void CDmeCombinationInputControl::MoveRawControlDown( const char *pRawControlName )
+{
+ int nIndex = FindRawControl( pRawControlName );
+ int nLastIndex = m_RawControlNames.Count() - 1;
+ if ( nIndex >= 0 && nIndex < nLastIndex )
+ {
+ m_RawControlNames.Swap( nIndex, nIndex + 1 );
+ m_WrinkleScales.Swap( nIndex, nIndex + 1 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the wrinkle scale for a particular control
+//-----------------------------------------------------------------------------
+float CDmeCombinationInputControl::WrinkleScale( const char *pRawControlName )
+{
+ int nIndex = FindRawControl( pRawControlName );
+ return WrinkleScale( nIndex );
+}
+
+float CDmeCombinationInputControl::WrinkleScale( int nIndex )
+{
+ if ( nIndex < 0 || ( nIndex >= m_WrinkleScales.Count() ) )
+ return 0.0f;
+ return m_WrinkleScales[ nIndex ];
+}
+
+void CDmeCombinationInputControl::SetWrinkleScale( const char *pRawControlName, float flWrinkleScale )
+{
+ int nIndex = FindRawControl( pRawControlName );
+ if ( nIndex < 0 || ( nIndex >= m_WrinkleScales.Count() ) )
+ return;
+ m_WrinkleScales.Set( nIndex, flWrinkleScale );
+}
+
+
+//-----------------------------------------------------------------------------
+// The default value of an input control is the value the UI has by default
+//-----------------------------------------------------------------------------
+float CDmeCombinationInputControl::GetDefaultValue() const
+{
+ return RawControlCount() == 2 ? 0.5f : 0.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// The base value of an input control will set the data to the base state
+// i.e. The state upon which the deltas are relative. Normally this is the
+// same as GetDefaultValue() except for EyeLid controls
+//-----------------------------------------------------------------------------
+float CDmeCombinationInputControl::GetBaseValue() const
+{
+ if ( IsEyelid() )
+ return 0.0f;
+
+ return GetDefaultValue();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const char *CDmeCombinationInputControl::GetEyesUpDownFlexName() const
+{
+ const CDmAttribute *pEyesUpDownFlexAttr = GetAttribute( "eyesUpDownFlex", AT_STRING );
+
+ if ( pEyesUpDownFlexAttr )
+ return pEyesUpDownFlexAttr->GetValueString();
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeCombinationDominationRule, CDmeCombinationDominationRule );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeCombinationDominationRule::OnConstruction()
+{
+ m_Dominators.Init( this, "dominators", FATTRIB_HAS_CALLBACK | FATTRIB_HAS_ARRAY_CALLBACK );
+ m_Suppressed.Init( this, "suppressed", FATTRIB_HAS_CALLBACK | FATTRIB_HAS_ARRAY_CALLBACK );
+}
+
+void CDmeCombinationDominationRule::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Notify parent that one of our attributes has changed
+//-----------------------------------------------------------------------------
+void CDmeCombinationDominationRule::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ BaseClass::OnAttributeChanged( pAttribute );
+
+ if ( pAttribute == m_Dominators.GetAttribute() || pAttribute == m_Suppressed.GetAttribute() )
+ {
+ InvokeOnAttributeChangedOnReferrers( GetHandle(), pAttribute );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we have this string already?
+//-----------------------------------------------------------------------------
+bool CDmeCombinationDominationRule::HasString( const char *pString, const CDmaStringArray& attr )
+{
+ int nCount = attr.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pString, attr[i] ) )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a dominating control
+//-----------------------------------------------------------------------------
+void CDmeCombinationDominationRule::AddDominator( const char *pDominatorControl )
+{
+ if ( HasString( pDominatorControl, m_Dominators ) )
+ {
+ Warning( "Domination rule already contains dominating control %s\n", pDominatorControl );
+ return;
+ }
+ if ( HasString( pDominatorControl, m_Suppressed ) )
+ {
+ Warning( "Attemped to add a control as both dominator + suppressed %s\n", pDominatorControl );
+ return;
+ }
+ m_Dominators.AddToTail( pDominatorControl );
+}
+
+
+//-----------------------------------------------------------------------------
+// Add a suppressed control
+//-----------------------------------------------------------------------------
+void CDmeCombinationDominationRule::AddSuppressed( const char *pSuppressedControl )
+{
+ if ( HasString( pSuppressedControl, m_Suppressed ) )
+ {
+ Warning( "Domination rule already contains suppressed control %s\n", pSuppressedControl );
+ return;
+ }
+ if ( HasString( pSuppressedControl, m_Dominators ) )
+ {
+ Warning( "Attemped to add a control as both dominator + suppressed %s\n", pSuppressedControl );
+ return;
+ }
+ m_Suppressed.AddToTail( pSuppressedControl );
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove all dominatior + suppressed controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationDominationRule::RemoveAllDominators()
+{
+ m_Dominators.RemoveAll();
+}
+
+void CDmeCombinationDominationRule::RemoveAllSuppressed()
+{
+ m_Suppressed.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Iteration
+//-----------------------------------------------------------------------------
+int CDmeCombinationDominationRule::DominatorCount() const
+{
+ return m_Dominators.Count();
+}
+
+const char *CDmeCombinationDominationRule::GetDominator( int i ) const
+{
+ return m_Dominators[i];
+}
+
+int CDmeCombinationDominationRule::SuppressedCount() const
+{
+ return m_Suppressed.Count();
+}
+
+const char *CDmeCombinationDominationRule::GetSuppressed( int i ) const
+{
+ return m_Suppressed[i];
+}
+
+
+//-----------------------------------------------------------------------------
+// Search
+//-----------------------------------------------------------------------------
+bool CDmeCombinationDominationRule::HasDominatorControl( const char *pDominatorControl ) const
+{
+ int nCount = DominatorCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( GetDominator(i), pDominatorControl ) )
+ return true;
+ }
+ return false;
+}
+
+bool CDmeCombinationDominationRule::HasSuppressedControl( const char *pSuppressedControl ) const
+{
+ int nCount = SuppressedCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( GetSuppressed(i), pSuppressedControl ) )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeCombinationOperator, CDmeCombinationOperator );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::OnConstruction()
+{
+ m_InputControls.Init( this, "controls" );
+ m_ControlValues[COMBO_CONTROL_NORMAL].Init( this, "controlValues" );
+ m_ControlValues[COMBO_CONTROL_LAGGED].Init( this, "controlValuesLagged" );
+ m_bSpecifyingLaggedData.Init( this, "usesLaggedValues" );
+
+ m_Dominators.Init( this, "dominators", FATTRIB_HAS_CALLBACK | FATTRIB_HAS_ARRAY_CALLBACK );
+
+ m_Targets.Init( this, "targets" );
+ m_flLastLaggedComputationTime = FLT_MIN;
+}
+
+void CDmeCombinationOperator::OnDestruction()
+{
+ m_RawControlInfo.RemoveAll();
+ m_CombinationInfo.RemoveAll();
+ m_DominatorInfo.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ m_Dominators.GetAttribute()->AddFlag( FATTRIB_DIRTY );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the index of the input control with the specified name (and creates one if necessary)
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::OnElementUnserialized()
+{
+ BaseClass::OnElementUnserialized();
+
+ // Mark all of the input as not being in their default state since we read it from a file
+ int nCount = m_InputControls.Count();
+ m_IsDefaultValue.SetCount( nCount );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_IsDefaultValue[i] = false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the index of the input control with the specified name (and creates one if necessary)
+//-----------------------------------------------------------------------------
+ControlIndex_t CDmeCombinationOperator::FindOrCreateControl( const char *pControlName, bool bStereo, bool bAutoAddRawControl )
+{
+ Assert( !strchr( pControlName, '_' ) && !strchr( pControlName, ' ' ) );
+ int nCount = m_InputControls.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !m_InputControls[i] )
+ continue;
+
+ if ( !Q_stricmp( pControlName, m_InputControls[i]->GetName() ) )
+ return i;
+ }
+
+ // NOTE: the y coordinate of the control value is -1 if it's not a stereo control
+ CDmeCombinationInputControl *pInputControl = CreateElement< CDmeCombinationInputControl >( pControlName, GetFileId() );
+ pInputControl->SetStereo( bStereo );
+ int nIndex = m_InputControls.AddToTail( pInputControl );
+ m_ControlValues[COMBO_CONTROL_NORMAL].AddToTail( Vector( 0.0f, 0.5f, 0.5f ) );
+ m_ControlValues[COMBO_CONTROL_LAGGED].AddToTail( Vector( 0.0f, 0.5f, 0.5f ) );
+ m_IsDefaultValue.AddToTail( true );
+ Assert( m_InputControls.Count() == m_ControlValues[COMBO_CONTROL_NORMAL].Count() );
+ Assert( m_InputControls.Count() == m_ControlValues[COMBO_CONTROL_LAGGED].Count() );
+ Assert( m_InputControls.Count() == m_IsDefaultValue.Count() );
+
+ if ( bAutoAddRawControl )
+ {
+ AddRawControl( nIndex, pControlName );
+ }
+
+ return nIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the index of the input control with the specified name
+//-----------------------------------------------------------------------------
+ControlIndex_t CDmeCombinationOperator::FindControlIndex( const char *pControlName )
+{
+ int nCount = m_InputControls.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !m_InputControls[i] )
+ continue;
+
+ if ( !Q_stricmp( pControlName, m_InputControls[i]->GetName() ) )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes a control
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::RemoveControl( const char *pControlName )
+{
+ ControlIndex_t nIndex = FindControlIndex( pControlName );
+ if ( nIndex >= 0 )
+ {
+ DestroyElement( m_InputControls[nIndex] );
+ m_InputControls.FastRemove( nIndex );
+ m_ControlValues[COMBO_CONTROL_NORMAL].FastRemove( nIndex );
+ m_ControlValues[COMBO_CONTROL_LAGGED].FastRemove( nIndex );
+ m_IsDefaultValue.FastRemove( nIndex );
+
+ Assert( m_InputControls.Count() == m_ControlValues[COMBO_CONTROL_NORMAL].Count() );
+ Assert( m_InputControls.Count() == m_ControlValues[COMBO_CONTROL_LAGGED].Count() );
+ Assert( m_InputControls.Count() == m_IsDefaultValue.Count() );
+
+ RebuildRawControlList();
+ }
+}
+
+void CDmeCombinationOperator::RemoveAllControls()
+{
+ int nCount = m_InputControls.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeCombinationInputControl *pInputControl = m_InputControls[i];
+ m_InputControls.Set( i, NULL );
+ DestroyElement( pInputControl );
+ }
+ m_InputControls.RemoveAll();
+ m_ControlValues[COMBO_CONTROL_NORMAL].RemoveAll( );
+ m_ControlValues[COMBO_CONTROL_LAGGED].RemoveAll( );
+ m_IsDefaultValue.RemoveAll( );
+
+ RebuildRawControlList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Changes a control's name
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::SetControlName( ControlIndex_t nControl, const char *pControlName )
+{
+ ControlIndex_t nFoundIndex = FindControlIndex( pControlName );
+ if ( nFoundIndex >= 0 && nFoundIndex != nControl )
+ {
+ Warning( "A control with name \"%s\" already exists!\n", pControlName );
+ return;
+ }
+ m_InputControls[nControl]->SetName( pControlName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the default value associated with a control
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::UpdateDefaultValue( ControlIndex_t nControlIndex )
+{
+ if ( m_IsDefaultValue[ nControlIndex ] )
+ {
+ float flDefaultValue = GetRawControlCount( nControlIndex ) == 2 ? 0.5f : 0.0f;
+ const Vector& vec = m_ControlValues[COMBO_CONTROL_NORMAL][nControlIndex];
+ m_ControlValues[COMBO_CONTROL_NORMAL].Set( nControlIndex, Vector( flDefaultValue, vec.y, vec.z ) );
+ const Vector& vec2 = m_ControlValues[COMBO_CONTROL_LAGGED][nControlIndex];
+ m_ControlValues[COMBO_CONTROL_LAGGED].Set( nControlIndex, Vector( flDefaultValue, vec2.y, vec2.z ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an output control to a input control
+// If force is true, removes the raw control from any existing controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::AddRawControl( ControlIndex_t nControl, const char *pRawControlName )
+{
+ if ( FindRawControlIndex( pRawControlName, true ) >= 0 )
+ {
+ Warning( "Attempted to add the same remapped control \"%s\" twice!\n", pRawControlName );
+ return;
+ }
+
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ if ( pInputControl->AddRawControl( pRawControlName ) )
+ {
+ UpdateDefaultValue( nControl );
+ RebuildRawControlList();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes an output control from an input control
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::RemoveRawControl( ControlIndex_t nControl, const char *pRawControlName )
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ if ( pInputControl->RemoveRawControl( pRawControlName ) )
+ {
+ UpdateDefaultValue( nControl );
+ RebuildRawControlList();
+ }
+}
+
+void CDmeCombinationOperator::RemoveAllRawControls( ControlIndex_t nControl )
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ pInputControl->RemoveAllRawControls( );
+
+ int nCount = m_InputControls.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ UpdateDefaultValue( i );
+ }
+
+ RebuildRawControlList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Iterates remapped controls
+//-----------------------------------------------------------------------------
+int CDmeCombinationOperator::GetRawControlCount( ControlIndex_t nControl ) const
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ return pInputControl->RawControlCount();
+}
+
+const char *CDmeCombinationOperator::GetRawControlName( ControlIndex_t nControl, int nIndex ) const
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ return pInputControl->RawControlName( nIndex );
+}
+
+float CDmeCombinationOperator::GetRawControlWrinkleScale( ControlIndex_t nControl, int nIndex ) const
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ return pInputControl->WrinkleScale( nIndex );
+}
+
+float CDmeCombinationOperator::GetRawControlWrinkleScale( ControlIndex_t nControl, const char *pRawControlName ) const
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ return pInputControl->WrinkleScale( pRawControlName );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+float CDmeCombinationOperator::GetControlDefaultValue( ControlIndex_t nControl ) const
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ return pInputControl->GetDefaultValue();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+float CDmeCombinationOperator::GetControlBaseValue( ControlIndex_t nControl ) const
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ return pInputControl->GetBaseValue();
+}
+
+
+//-----------------------------------------------------------------------------
+// Iterates a global list of output controls
+//-----------------------------------------------------------------------------
+int CDmeCombinationOperator::GetRawControlCount( ) const
+{
+ return m_RawControlInfo.Count();
+}
+
+const char *CDmeCombinationOperator::GetRawControlName( int nIndex ) const
+{
+ return m_RawControlInfo[nIndex].m_Name;
+}
+
+float CDmeCombinationOperator::GetRawControlWrinkleScale( int nIndex ) const
+{
+ return m_RawControlInfo[nIndex].m_flWrinkleScale;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the wrinkle scale for a particular raw control
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::SetWrinkleScale( ControlIndex_t nControl, const char *pRawControlName, float flWrinkleScale )
+{
+ CDmeCombinationInputControl *pInputControl = m_InputControls[nControl];
+ Assert( pInputControl );
+ pInputControl->SetWrinkleScale( pRawControlName, flWrinkleScale );
+ RebuildRawControlList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Reordering controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::MoveControlUp( const char *pControlName )
+{
+
+ int nIndex = FindControlIndex( pControlName );
+ if ( nIndex > 0 )
+ {
+ m_InputControls.Swap( nIndex, nIndex - 1 );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ m_ControlValues[ i ].Swap( nIndex, nIndex - 1 );
+ }
+ RebuildRawControlList();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reordering controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::MoveControlDown( const char *pControlName )
+{
+ int nIndex = FindControlIndex( pControlName );
+ int nLastIndex = m_InputControls.Count() - 1;
+ if ( nIndex >= 0 && nIndex < nLastIndex )
+ {
+ m_InputControls.Swap( nIndex, nIndex + 1 );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ m_ControlValues[ i ].Swap( nIndex, nIndex + 1 );
+ }
+ RebuildRawControlList();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reordering controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::MoveControlBefore( const char *pDragControlName, const char *pDropControlName )
+{
+ int pDragIndex( FindControlIndex( pDragControlName ) );
+ int pDropIndex( FindControlIndex( pDropControlName ) );
+
+ // Have to copy because InsertAfter may reallocate memory before doing the copy and therefore might be referencing garabage
+ CDmeCombinationInputControl *inputControlCopy( m_InputControls[ pDragIndex ] );
+ m_InputControls.InsertBefore( pDropIndex, inputControlCopy );
+ if ( pDragIndex <= pDropIndex )
+ {
+ m_InputControls.Remove( pDragIndex );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ const Vector controlValueCopy( m_ControlValues[ i ][ pDragIndex ] );
+ m_ControlValues[ i ].InsertBefore( pDropIndex, controlValueCopy );
+ m_ControlValues[ i ].Remove( pDragIndex );
+ }
+ }
+ else
+ {
+ m_InputControls.Remove( pDragIndex + 1 );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ const Vector controlValueCopy( m_ControlValues[ i ][ pDragIndex ] );
+ m_ControlValues[ i ].InsertBefore( pDropIndex, controlValueCopy );
+ m_ControlValues[ i ].Remove( pDragIndex + 1 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reordering controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::MoveControlAfter( const char *pDragControlName, const char *pDropControlName )
+{
+ int nDragIndex = FindControlIndex( pDragControlName );
+ int nDropIndex = FindControlIndex( pDropControlName );
+
+ // Have to copy because InsertAfter may reallocate memory before doing the copy and therefore might be referencing garabage
+ CDmeCombinationInputControl *inputControlCopy( m_InputControls[ nDragIndex ] );
+ m_InputControls.InsertBefore( nDropIndex + 1, inputControlCopy );
+ if ( nDragIndex < nDropIndex )
+ {
+ m_InputControls.Remove( nDragIndex );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ const Vector controlValueCopy( m_ControlValues[ i ][ nDragIndex ] );
+ m_ControlValues[ i ].InsertBefore( nDropIndex + 1, controlValueCopy );
+ m_ControlValues[ i ].Remove( nDragIndex );
+ }
+ }
+ else
+ {
+ m_InputControls.Remove( nDragIndex + 1 );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ const Vector controlValueCopy( m_ControlValues[ i ][ nDragIndex ] );
+ m_ControlValues[ i ].InsertBefore( nDropIndex + 1, controlValueCopy );
+ m_ControlValues[ i ].Remove( nDragIndex + 1 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Reordering controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::MoveRawControlUp( ControlIndex_t nControlIndex, const char *pRawControlName )
+{
+ m_InputControls[nControlIndex]->MoveRawControlUp( pRawControlName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Reordering controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::MoveRawControlDown( ControlIndex_t nControlIndex, const char *pRawControlName )
+{
+ m_InputControls[nControlIndex]->MoveRawControlDown( pRawControlName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if a control is a stereo control
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::SetStereoControl( ControlIndex_t nControlIndex, bool bIsStereo )
+{
+ m_InputControls[nControlIndex]->SetStereo( bIsStereo );
+}
+
+bool CDmeCombinationOperator::IsStereoControl( ControlIndex_t nControlIndex ) const
+{
+ return m_InputControls[nControlIndex]->IsStereo();
+}
+
+bool CDmeCombinationOperator::IsStereoRawControl( int nIndex ) const
+{
+ return IsStereoControl( m_RawControlInfo[nIndex].m_InputControl );
+}
+
+void CDmeCombinationOperator::SetEyelidControl( ControlIndex_t nControlIndex, bool bIsEyelid )
+{
+ m_InputControls[nControlIndex]->SetEyelid( bIsEyelid );
+}
+
+bool CDmeCombinationOperator::IsEyelidControl( ControlIndex_t nControlIndex ) const
+{
+ return m_InputControls[nControlIndex]->IsEyelid();
+}
+
+bool CDmeCombinationOperator::IsEyelidRawControl( int nIndex ) const
+{
+ return IsEyelidControl( m_RawControlInfo[nIndex].m_InputControl );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const char *CDmeCombinationOperator::GetEyesUpDownFlexName( ControlIndex_t nControlIndex ) const
+{
+ return m_InputControls[ nControlIndex ]->GetEyesUpDownFlexName();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the value of a control
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::SetControlValue( ControlIndex_t nControlIndex, float flValue, CombinationControlType_t type )
+{
+ m_IsDefaultValue[ nControlIndex ] = false;
+ float flMultiLevel = m_ControlValues[type][nControlIndex].z;
+ m_ControlValues[type].Set( nControlIndex, Vector( flValue, flValue, flMultiLevel ) );
+}
+
+void CDmeCombinationOperator::SetControlValue( ControlIndex_t nControlIndex, float flLevel, float flBalance, CombinationControlType_t type )
+{
+ Assert( IsStereoControl( nControlIndex ) );
+ m_IsDefaultValue[ nControlIndex ] = false;
+ float flMultiLevel = m_ControlValues[type][nControlIndex].z;
+ m_ControlValues[type].Set( nControlIndex, Vector( flLevel, flBalance, flMultiLevel ) );
+}
+
+void CDmeCombinationOperator::SetControlValue( ControlIndex_t nControlIndex, const Vector2D& vec, CombinationControlType_t type )
+{
+ Assert( IsStereoControl( nControlIndex ) );
+ m_IsDefaultValue[ nControlIndex ] = false;
+ float flMultiLevel = m_ControlValues[type][nControlIndex].z;
+ m_ControlValues[type].Set( nControlIndex, Vector( vec.x, vec.y, flMultiLevel ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the value of a control
+//-----------------------------------------------------------------------------
+float CDmeCombinationOperator::GetControlValue( ControlIndex_t nControlIndex, CombinationControlType_t type ) const
+{
+ Assert( !IsStereoControl( nControlIndex ) );
+ return m_ControlValues[type].Get( nControlIndex ).x;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the value of a control
+//-----------------------------------------------------------------------------
+const Vector2D& CDmeCombinationOperator::GetStereoControlValue( ControlIndex_t nControlIndex, CombinationControlType_t type ) const
+{
+ return m_ControlValues[type].Get( nControlIndex ).AsVector2D();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the level of a control (only used by controls w/ 3 or more remappings)
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::SetMultiControlLevel( ControlIndex_t nControlIndex, float flMultiLevel, CombinationControlType_t type )
+{
+ m_IsDefaultValue[ nControlIndex ] = false;
+ const Vector2D &value = m_ControlValues[type][nControlIndex].AsVector2D();
+ m_ControlValues[type].Set( nControlIndex, Vector( value.x, value.y, flMultiLevel ) );
+}
+
+float CDmeCombinationOperator::GetMultiControlLevel( ControlIndex_t nControlIndex, CombinationControlType_t type ) const
+{
+ Assert( IsMultiControl( nControlIndex ) );
+ return m_ControlValues[type][nControlIndex].z;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if a control is a multi control (a control w/ 3 or more remappings)
+//-----------------------------------------------------------------------------
+bool CDmeCombinationOperator::IsMultiControl( ControlIndex_t nControlIndex ) const
+{
+ return m_InputControls[nControlIndex]->RawControlCount() >= 3 || IsEyelidControl( nControlIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Iterates controls
+//-----------------------------------------------------------------------------
+int CDmeCombinationOperator::GetControlCount() const
+{
+ return m_InputControls.Count();
+}
+
+const char *CDmeCombinationOperator::GetControlName( ControlIndex_t i ) const
+{
+ return m_InputControls[i]->GetName();
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we have a raw control?
+//-----------------------------------------------------------------------------
+bool CDmeCombinationOperator::HasRawControl( const char *pRawControlName ) const
+{
+ return FindRawControlIndex( pRawControlName ) >= 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the index of the remapped control with the specified name
+//-----------------------------------------------------------------------------
+CDmeCombinationOperator::RawControlIndex_t CDmeCombinationOperator::FindRawControlIndex( const char *pControlName, bool bIgnoreDefaultControls ) const
+{
+ int nRawControlCount = m_RawControlInfo.Count();
+ for ( int i = 0; i < nRawControlCount; ++i )
+ {
+ if ( bIgnoreDefaultControls && m_RawControlInfo[i].m_bIsDefaultControl )
+ continue;
+
+ if ( !Q_stricmp( pControlName, m_RawControlInfo[i].m_Name ) )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is a particular remapped control stereo?
+//-----------------------------------------------------------------------------
+bool CDmeCombinationOperator::IsRawControlStereo( const char *pRawControlName )
+{
+ RawControlIndex_t nIndex = FindRawControlIndex( pRawControlName );
+ if ( nIndex < 0 )
+ return false;
+
+ CDmeCombinationInputControl *pInputControl = m_InputControls[ m_RawControlInfo[nIndex].m_InputControl ];
+ return pInputControl->IsStereo();
+}
+
+
+//-----------------------------------------------------------------------------
+// Would a particular delta state attached to this combination operator end up stereo?
+//-----------------------------------------------------------------------------
+bool CDmeCombinationOperator::IsDeltaStateStereo( const char *pDeltaStateName )
+{
+ int *pTemp = (int*)_alloca( m_RawControlInfo.Count() * sizeof(int) );
+ int nCount = ParseDeltaName( pDeltaStateName, pTemp );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeCombinationInputControl *pInputControl = m_InputControls[ m_RawControlInfo[ pTemp[i] ].m_InputControl ];
+ if ( pInputControl->IsStereo() )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Does one of the targets we refer to contain a particular delta state?
+//-----------------------------------------------------------------------------
+bool CDmeCombinationOperator::DoesTargetContainDeltaState( const char *pSearchName )
+{
+ int nTargetCount = m_Targets.Count();
+ for ( int i = 0; i < nTargetCount; ++i )
+ {
+ const CDmrElementArray<> deltaArray( m_Targets[i], "deltaStates" );
+ if ( !deltaArray.IsValid() )
+ continue;
+
+ int nDeltaCount = deltaArray.Count();
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ if ( !deltaArray[j] )
+ continue;
+
+ char pBuf[512];
+ Q_strncpy( pBuf, deltaArray[j]->GetName(), sizeof(pBuf) );
+
+ char *pEnd;
+ for ( char *pName = pBuf; *pName; pName = pEnd )
+ {
+ pEnd = strchr( pName, '_' );
+ if ( !pEnd )
+ {
+ pEnd = pName + Q_strlen( pName );
+ }
+ else
+ {
+ // Null-terminate
+ *pEnd = 0;
+ ++pEnd;
+ }
+
+ if ( !Q_stricmp( pSearchName, pName ) )
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes list of all remapped controls
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::RebuildRawControlList()
+{
+ m_RawControlInfo.RemoveAll();
+
+ int nControlCount = m_InputControls.Count();
+ for ( int i = 0; i < nControlCount; ++i )
+ {
+ CDmeCombinationInputControl *pInputControl = m_InputControls[i];
+ Assert( pInputControl );
+ int nRemapCount = pInputControl->RawControlCount();
+
+ const bool bIsEyelid = pInputControl->IsEyelid();
+
+ float flStep = ( nRemapCount > 2 ) ? 1.0f / ( nRemapCount - 1 ) : 0.0f;
+ for ( int j = 0; j < nRemapCount; ++j )
+ {
+ int k = m_RawControlInfo.AddToTail( );
+ RawControlInfo_t &info = m_RawControlInfo[k];
+
+ info.m_Name = pInputControl->RawControlName( j );
+ info.m_InputControl = i;
+ info.m_bIsDefaultControl = false;
+ info.m_flWrinkleScale = pInputControl->WrinkleScale( j );
+ info.m_bLowerEyelid = false;
+
+ if ( bIsEyelid )
+ {
+ info.m_FilterRamp.Init( 0.0f, 1.0f, 10.0f, 11.0f );
+ // TODO: Right now it's implicit that the lower eyelid is first...
+ if ( j == 0 )
+ {
+ // Close Lower Lid
+ info.m_bLowerEyelid = true;
+ }
+ continue;
+ }
+
+ switch( nRemapCount )
+ {
+ case 1:
+ info.m_FilterRamp.Init( 0.0f, 1.0f, 10.0f, 11.0f );
+ break;
+
+ case 2:
+ if ( j == 0 )
+ {
+ info.m_FilterRamp.Init( -11.0f, -10.0f, 0.0f, 0.5f );
+ }
+ else
+ {
+ info.m_FilterRamp.Init( 0.5f, 1.0f, 10.0f, 11.0f );
+ }
+ break;
+
+ default:
+ {
+ if ( j == 0 )
+ {
+ info.m_FilterRamp.Init( -11.0f, -10.0f, 0, flStep );
+ }
+ else if ( j == nRemapCount-1 )
+ {
+ info.m_FilterRamp.Init( 1.0f - flStep, 1.0f, 10.0f, 11.0f );
+ }
+ else
+ {
+ float flPeak = j * flStep;
+ info.m_FilterRamp.Init( flPeak - flStep, flPeak, flPeak, flPeak + flStep );
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ RebuildDominatorInfo();
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes lists of dominators and suppressors
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::RebuildDominatorInfo()
+{
+ m_DominatorInfo.RemoveAll();
+ int nCount = m_Dominators.Count();
+ int *pDominators = (int*)_alloca( m_RawControlInfo.Count() * sizeof(int) );
+ int *pSuppressed = (int*)_alloca( m_RawControlInfo.Count() * sizeof(int) );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeCombinationDominationRule *pRule = m_Dominators[i];
+ bool bRuleOk = true;
+
+ int nDominatorCount = pRule->DominatorCount();
+ int nSuppressedCount = pRule->SuppressedCount();
+ if ( ( nDominatorCount == 0 ) || ( nSuppressedCount == 0 ) )
+ continue;
+
+ for ( int j = 0; j < nDominatorCount; ++j )
+ {
+ int nControlIndex = FindRawControlIndex( pRule->GetDominator(j) );
+ if ( nControlIndex < 0 )
+ {
+ bRuleOk = false;
+ break;
+ }
+ pDominators[j] = nControlIndex;
+ }
+
+ for ( int j = 0; j < nSuppressedCount; ++j )
+ {
+ int nControlIndex = FindRawControlIndex( pRule->GetSuppressed(j) );
+ if ( nControlIndex < 0 )
+ {
+ bRuleOk = false;
+ break;
+ }
+ pSuppressed[j] = nControlIndex;
+ }
+
+ if ( !bRuleOk )
+ continue;
+
+ int k = m_DominatorInfo.AddToTail();
+ m_DominatorInfo[k].m_DominantIndices.AddMultipleToTail( nDominatorCount, pDominators );
+ m_DominatorInfo[k].m_SuppressedIndices.AddMultipleToTail( nSuppressedCount, pSuppressed );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a dominator. Dominators are specified using raw control names
+//-----------------------------------------------------------------------------
+CDmeCombinationDominationRule *CDmeCombinationOperator::AddDominationRule( )
+{
+ CDmeCombinationDominationRule *pDominationRule = CreateElement< CDmeCombinationDominationRule >( "rule", GetFileId() );
+ m_Dominators.AddToTail( pDominationRule );
+ return pDominationRule;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a dominator. Dominators are specified using raw control names
+//-----------------------------------------------------------------------------
+CDmeCombinationDominationRule *CDmeCombinationOperator::AddDominationRule( CDmeCombinationDominationRule *pSrcRule )
+{
+ CDmeCombinationDominationRule *pDestRule = pSrcRule->Copy( );
+ pDestRule->SetFileId( GetFileId(), TD_DEEP );
+ m_Dominators.AddToTail( pDestRule );
+ return pDestRule;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a dominator. Dominators are specified using raw control names
+//-----------------------------------------------------------------------------
+CDmeCombinationDominationRule *CDmeCombinationOperator::AddDominationRule( int nDominatorCount, const char **ppDominatorControlNames, int nSuppressedCount, const char **ppSuppressedControlNames )
+{
+ CDmeCombinationDominationRule *pDominationRule = AddDominationRule();
+ for ( int i = 0; i < nDominatorCount; ++i )
+ {
+ pDominationRule->AddDominator( ppDominatorControlNames[i] );
+ }
+ for ( int i = 0; i < nSuppressedCount; ++i )
+ {
+ pDominationRule->AddSuppressed( ppSuppressedControlNames[i] );
+ }
+ return pDominationRule;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a dominator. Dominators are specified using raw control names
+//-----------------------------------------------------------------------------
+CDmeCombinationDominationRule *CDmeCombinationOperator::AddDominationRule( const CUtlVector< const char * > dominators, const CUtlVector< const char * > suppressed )
+{
+ return AddDominationRule( dominators.Count(), (const char **)dominators.Base(), suppressed.Count(), (const char **)suppressed.Base() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes a domination rule
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::RemoveDominationRule( int nIndex )
+{
+ CDmeCombinationDominationRule *pRule = m_Dominators[nIndex];
+ m_Dominators.Remove( nIndex );
+ DestroyElement( pRule );
+}
+
+void CDmeCombinationOperator::RemoveDominationRule( CDmeCombinationDominationRule *pRule )
+{
+ int nCount = m_Dominators.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Dominators[i] == pRule )
+ {
+ RemoveDominationRule( i );
+ break;
+ }
+ }
+}
+
+void CDmeCombinationOperator::RemoveAllDominationRules()
+{
+ int nCount = m_Dominators.Count();
+ for ( int i = nCount; --i >= 0; )
+ {
+ RemoveDominationRule( i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Iteration
+//-----------------------------------------------------------------------------
+int CDmeCombinationOperator::DominationRuleCount() const
+{
+ return m_Dominators.Count();
+}
+
+CDmeCombinationDominationRule *CDmeCombinationOperator::GetDominationRule( int i )
+{
+ return m_Dominators[i];
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a domination rule
+//-----------------------------------------------------------------------------
+int CDmeCombinationOperator::FindDominationRule( CDmeCombinationDominationRule *pRule )
+{
+ int nCount = m_Dominators.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Dominators[i] == pRule )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Rule reordering
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::MoveDominationRuleUp( CDmeCombinationDominationRule* pRule )
+{
+ int nIndex = FindDominationRule( pRule );
+ if ( nIndex > 0 )
+ {
+ m_Dominators.Swap( nIndex, nIndex - 1 );
+ return;
+ }
+}
+
+void CDmeCombinationOperator::MoveDominationRuleDown( CDmeCombinationDominationRule* pRule )
+{
+ int nIndex = FindDominationRule( pRule );
+ if ( nIndex >= 0 && nIndex < m_Dominators.Count() - 1 )
+ {
+ m_Dominators.Swap( nIndex, nIndex + 1 );
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Attaches a channel to an input
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::AttachChannelToControlValue( ControlIndex_t nControlIndex, CombinationControlType_t type, CDmeChannel *pChannel )
+{
+ pChannel->SetOutput( m_ControlValues[type].GetAttribute(), nControlIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Determines the weighting of input controls based on the deltaState name
+//-----------------------------------------------------------------------------
+int CDmeCombinationOperator::FindDeltaStateIndex( CDmAttribute *pDeltaArray, const char *pDeltaStateName )
+{
+ const CDmrElementArray<> deltas( pDeltaArray );
+ int nDeltaArrayCount = deltas.Count();
+ for ( int i = 0; i < nDeltaArrayCount; ++i )
+ {
+ CDmElement *pDeltaElement = deltas[i];
+ if ( pDeltaElement && !Q_stricmp( pDeltaElement->GetName(), pDeltaStateName ) )
+ return i;
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Determines which combination to use based on the deltaState name
+//-----------------------------------------------------------------------------
+int CDmeCombinationOperator::ParseDeltaName(const char *pDeltaStateName, int *pControlIndices )
+{
+ char pBuf[512];
+ Q_strncpy( pBuf, pDeltaStateName, sizeof(pBuf) );
+
+ int nComboCount = 0;
+ char *pEnd;
+ for ( char *pName = pBuf; *pName; pName = pEnd )
+ {
+ pEnd = strchr( pName, '_' );
+ if ( !pEnd )
+ {
+ pEnd = pName + Q_strlen( pName );
+ }
+ else
+ {
+ // Null-terminate
+ *pEnd = 0;
+ ++pEnd;
+ }
+
+ int nControlIndex = FindRawControlIndex( pName );
+ if ( nControlIndex < 0 )
+ return 0;
+
+ pControlIndices[ nComboCount++ ] = nControlIndex;
+ }
+
+ return nComboCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds dominators
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::FindDominators( CombinationOperation_t& op )
+{
+ // Dominators are sets of inputs, which, when set to 1, will
+ // supress another set of inputs. Only combinations which contain *all*
+ // dominators will suppress any combinatation that contains *all* suppressors
+ int nDominatorCount = m_DominatorInfo.Count();
+ for ( int i = 0; i < nDominatorCount; ++i )
+ {
+ DominatorInfo_t &info = m_DominatorInfo[i];
+
+ // Look for suppressor indices in the control indices list
+ int nCount = info.m_SuppressedIndices.Count();
+ int j;
+ for ( j = 0; j < nCount; ++j )
+ {
+ if ( op.m_ControlIndices.Find( info.m_SuppressedIndices[j] ) < 0 )
+ break;
+ }
+ if ( j != nCount )
+ continue;
+
+ op.m_DominatorIndices.AddToTail( i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::ComputeCombinationInfo( int nIndex )
+{
+ CleanUpCombinationInfo( nIndex );
+
+ int nCurrentCount = m_CombinationInfo.Count();
+ while ( nIndex >= nCurrentCount )
+ {
+ int j = m_CombinationInfo.AddToTail();
+ CombinationInfo_t &info = m_CombinationInfo[j];
+ for ( int k = 0; k < COMBO_CONTROL_TYPE_COUNT; ++k )
+ {
+ info.m_hDestAttribute[k] = DMATTRIBUTE_HANDLE_INVALID;
+ }
+ ++nCurrentCount;
+ }
+
+ CombinationInfo_t &info = m_CombinationInfo[nIndex];
+
+ CDmElement *pSource = m_Targets[ nIndex ];
+ if ( !pSource )
+ return;
+
+ CDmrElementArray<> deltas( pSource, "deltaStates" );
+ if ( !deltas.IsValid() )
+ return;
+
+ CDmrArray<Vector2D> weights( pSource, "deltaStateWeights" );
+ if ( !weights.IsValid() )
+ return;
+
+ CDmrArray<Vector2D> weightsLagged( pSource, "deltaStateWeightsLagged" );
+
+ // This is an error state
+ if ( deltas.Count() > weights.Count() )
+ return;
+
+ // This is an error state
+ if ( weightsLagged.IsValid() && deltas.Count() > weightsLagged.Count() )
+ return;
+
+ info.m_hDestAttribute[COMBO_CONTROL_NORMAL] = weights.GetAttribute()->GetHandle();
+ info.m_hDestAttribute[COMBO_CONTROL_LAGGED] = weightsLagged.IsValid() ? weightsLagged.GetAttribute()->GetHandle() : DMATTRIBUTE_HANDLE_INVALID;
+
+ int *pTemp = (int*)_alloca( m_RawControlInfo.Count() * sizeof(int) );
+ int nDeltaCount = deltas.Count();
+ for ( int i = 0; i < nDeltaCount; ++i )
+ {
+ CDmElement *pDeltaElement = deltas[i];
+ if ( !pDeltaElement )
+ continue;
+
+ int nControlCount = ParseDeltaName( pDeltaElement->GetName(), pTemp );
+ if ( nControlCount == 0 )
+ continue;
+
+ int j = info.m_Outputs.AddToTail();
+ CombinationOperation_t &op = info.m_Outputs[j];
+ op.m_nDeltaStateIndex = i;
+ op.m_ControlIndices.AddMultipleToTail( nControlCount, pTemp );
+
+ // Find dominators
+ FindDominators( op );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::ComputeCombinationInfo()
+{
+ int nTargetCount = m_Targets.Count();
+ for ( int i = 0; i < nTargetCount; ++i )
+ {
+ ComputeCombinationInfo( i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::CleanUpCombinationInfo( int nIndex )
+{
+ if ( nIndex >= m_CombinationInfo.Count() )
+ return;
+
+ CombinationInfo_t &info = m_CombinationInfo[ nIndex ];
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ info.m_hDestAttribute[i] = DMATTRIBUTE_HANDLE_INVALID;
+ }
+ info.m_Outputs.RemoveAll();
+}
+
+
+void CDmeCombinationOperator::CleanUpCombinationInfo( )
+{
+ int nCount = m_CombinationInfo.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CleanUpCombinationInfo( i );
+ }
+ m_CombinationInfo.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// helpers for finding corrector names
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::Resolve()
+{
+ BaseClass::Resolve();
+
+ if ( m_InputControls.IsDirty() || m_Dominators.IsDirty() )
+ {
+ RebuildRawControlList();
+ ComputeCombinationInfo();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a target for the combination operator
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::AddTarget( CDmElement *pElement )
+{
+ if ( !pElement )
+ return;
+
+ int i = m_Targets.AddToTail( pElement );
+ ComputeCombinationInfo( i );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets targets
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::AddTarget( CDmeDag *pDag )
+{
+ if ( !pDag )
+ return;
+
+ CDmeShape *pShape = pDag->GetShape();
+ AddTarget( pShape );
+
+ int nChildCount = pDag->GetChildCount();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ CDmeDag *pChild = pDag->GetChild(i);
+
+ // Do not traverse into models
+ if ( CastElement< CDmeModel >( pChild ) )
+ continue;
+
+ AddTarget( pChild );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Remaps non-stereo -> stereo, stereo ->left/right, also adds multilevel + filter
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::ComputeInternalControlValue( RawControlIndex_t nRawControlIndex, CombinationControlType_t type, Vector2D &value )
+{
+ const RawControlInfo_t &info = m_RawControlInfo[nRawControlIndex];
+ const Vector &vecControlValue = m_ControlValues[ type ][ info.m_InputControl ];
+
+ const bool bMultiControl = IsMultiControl( info.m_InputControl );
+ float flValue = bMultiControl ? vecControlValue.z : vecControlValue.x;
+
+ // Apply multicontrol remapping
+ if ( flValue <= info.m_FilterRamp.x || flValue >= info.m_FilterRamp.w )
+ {
+ flValue = 0.0f;
+ }
+ else if ( flValue < info.m_FilterRamp.y )
+ {
+ flValue = RemapVal( flValue, info.m_FilterRamp.x, info.m_FilterRamp.y, 0.0f, 1.0f );
+ }
+ else if ( flValue > info.m_FilterRamp.z )
+ {
+ flValue = RemapVal( flValue, info.m_FilterRamp.z, info.m_FilterRamp.w, 1.0f, 0.0f );
+ }
+ else
+ {
+ flValue = 1.0f;
+ }
+
+ if ( IsEyelidControl( info.m_InputControl ) )
+ {
+ if ( info.m_bLowerEyelid )
+ {
+ flValue = ( 1.0f - flValue ) * vecControlValue.x;
+ }
+ else
+ {
+ flValue *= vecControlValue.x;
+ }
+ }
+ else if ( bMultiControl )
+ {
+ flValue *= vecControlValue.x;
+ }
+
+ value.x = value.y = flValue;
+ if ( IsStereoControl( info.m_InputControl ) )
+ {
+ if ( vecControlValue.y < 0.5f )
+ {
+ float flRightAmount = RemapVal( vecControlValue.y, 0.0f, 0.5f, 0.0f, 1.0f );
+ value.y *= flRightAmount;
+ }
+ else
+ {
+ float flLeftAmount = RemapVal( vecControlValue.y, 0.5f, 1.0f, 1.0f, 0.0f );
+ value.x *= flLeftAmount;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes lagged input values from non-lagged input
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::ComputeLaggedInputValues()
+{
+ if ( !m_bSpecifyingLaggedData )
+ return;
+
+ float t = Plat_FloatTime();
+ if ( m_flLastLaggedComputationTime == FLT_MIN )
+ {
+ m_flLastLaggedComputationTime = t;
+ }
+
+ float dt = t - m_flLastLaggedComputationTime;
+ m_flLastLaggedComputationTime = t;
+ float flFactor = ExponentialDecay( 0.8, 0.033, dt );
+
+ int nCount = m_ControlValues[COMBO_CONTROL_NORMAL].Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ Vector vecLerp;
+ VectorLerp( m_ControlValues[COMBO_CONTROL_NORMAL][i], m_ControlValues[COMBO_CONTROL_LAGGED][i], flFactor, vecLerp );
+ m_ControlValues[COMBO_CONTROL_LAGGED].Set( i, vecLerp );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::Operate()
+{
+ ComputeLaggedInputValues();
+
+ int nCount = m_CombinationInfo.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CombinationInfo_t &info = m_CombinationInfo[i];
+
+ for ( CombinationControlType_t type = COMBO_CONTROL_FIRST; type < COMBO_CONTROL_TYPE_COUNT; type = (CombinationControlType_t)(type+1) )
+ {
+ if ( ( info.m_hDestAttribute[type] == DMATTRIBUTE_HANDLE_INVALID ) || !g_pDataModel->IsAttributeHandleValid( info.m_hDestAttribute[type] ) )
+ continue;
+
+ CDmAttribute* pAttribute = g_pDataModel->GetAttribute( info.m_hDestAttribute[type] );
+ if ( !pAttribute || pAttribute->GetType() != AT_VECTOR2_ARRAY )
+ continue;
+
+ CDmrArray< Vector2D > vec2D( pAttribute );
+
+ CombinationControlType_t useType = m_bSpecifyingLaggedData ? type : COMBO_CONTROL_NORMAL;
+ int nOutputCount = info.m_Outputs.Count();
+ for ( int j = 0; j < nOutputCount; ++j )
+ {
+ CombinationOperation_t &op = info.m_Outputs[j];
+
+ // Compute the core combination
+ Vector2D vecValue( 1.0f, 1.0f );
+ int nCombinationCount = op.m_ControlIndices.Count();
+ for ( int k = 0; k < nCombinationCount; ++k )
+ {
+ Vector2D v;
+ ComputeInternalControlValue( op.m_ControlIndices[k], useType, v );
+ vecValue *= v;
+ }
+
+
+ // Compute the dominators
+ int nDominatorCount = op.m_DominatorIndices.Count();
+ for ( int k = 0; k < nDominatorCount; ++k )
+ {
+ const CUtlVector< int > &dominantIndices = m_DominatorInfo[ op.m_DominatorIndices[k] ].m_DominantIndices;
+ int nDominantCount = dominantIndices.Count();
+
+ Vector2D suppressor( -1.0f, -1.0f );
+ for ( int l = 0; l < nDominantCount; ++l )
+ {
+ Vector2D v;
+ ComputeInternalControlValue( dominantIndices[l], useType, v );
+ suppressor *= v;
+ }
+ suppressor.x += 1.0f; suppressor.y += 1.0f;
+
+ vecValue *= suppressor;
+ }
+
+ vec2D.Set( op.m_nDeltaStateIndex, vecValue );
+ }
+ }
+ }
+}
+
+void CDmeCombinationOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ attrs.AddToTail( m_ControlValues[i].GetAttribute() );
+ }
+}
+
+void CDmeCombinationOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ int nCount = m_CombinationInfo.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CombinationInfo_t &info = m_CombinationInfo[i];
+ for ( int j = 0; j < COMBO_CONTROL_TYPE_COUNT; ++j )
+ {
+ if ( ( info.m_hDestAttribute[j] == DMATTRIBUTE_HANDLE_INVALID ) || !g_pDataModel->IsAttributeHandleValid( info.m_hDestAttribute[j] ) )
+ continue;
+
+ CDmAttribute* pAttribute = g_pDataModel->GetAttribute( info.m_hDestAttribute[j] );
+ if ( !pAttribute || pAttribute->GetType() != AT_VECTOR2_ARRAY )
+ continue;
+
+ attrs.AddToTail( pAttribute );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used by studiomdl to discover the various combination rules
+//-----------------------------------------------------------------------------
+int CDmeCombinationOperator::GetOperationTargetCount() const
+{
+ return m_Targets.Count();
+}
+
+CDmElement *CDmeCombinationOperator::GetOperationTarget( int nTargetIndex )
+{
+ return m_Targets[ nTargetIndex ];
+}
+
+int CDmeCombinationOperator::GetOperationCount( int nTargetIndex ) const
+{
+ return m_CombinationInfo[ nTargetIndex ].m_Outputs.Count();
+}
+
+CDmElement *CDmeCombinationOperator::GetOperationDeltaState( int nTargetIndex, int nOpIndex )
+{
+ CDmElement *pElement = GetOperationTarget( nTargetIndex );
+ const CDmrElementArray<> deltaArray( pElement, "deltaStates" );
+ if ( !deltaArray.IsValid() )
+ return NULL;
+
+ int nDeltaStateIndex = m_CombinationInfo[ nTargetIndex ].m_Outputs[ nOpIndex ].m_nDeltaStateIndex;
+ return deltaArray[ nDeltaStateIndex ];
+}
+
+const CUtlVector< int > &CDmeCombinationOperator::GetOperationControls( int nTargetIndex, int nOpIndex ) const
+{
+ return m_CombinationInfo[ nTargetIndex ].m_Outputs[ nOpIndex ].m_ControlIndices;
+}
+
+int CDmeCombinationOperator::GetOperationDominatorCount( int nTargetIndex, int nOpIndex ) const
+{
+ return m_CombinationInfo[ nTargetIndex ].m_Outputs[ nOpIndex ].m_DominatorIndices.Count();
+}
+
+const CUtlVector< int > &CDmeCombinationOperator::GetOperationDominator( int nTargetIndex, int nOpIndex, int nDominatorIndex ) const
+{
+ int nIndex = m_CombinationInfo[ nTargetIndex ].m_Outputs[ nOpIndex ].m_DominatorIndices[ nDominatorIndex ];
+ return m_DominatorInfo[ nIndex ].m_DominantIndices;
+}
+
+void CDmeCombinationOperator::CopyControls( CDmeCombinationOperator *pSrc )
+{
+ // Clean Me Out!
+ RemoveAllControls();
+
+ // Copy The Source Controls
+ for ( int i = 0; i < pSrc->m_InputControls.Count(); ++i )
+ {
+ CDmeCombinationInputControl *pSrcInput = pSrc->m_InputControls[ i ];
+ if ( pSrcInput )
+ {
+ CDmeCombinationInputControl *pDstInput = pSrcInput->Copy( );
+ pDstInput->SetFileId( GetFileId(), TD_DEEP );
+ m_InputControls.AddToTail( pDstInput );
+ }
+ }
+
+ // Copy The Control Values
+ m_ControlValues[ COMBO_CONTROL_NORMAL ].CopyArray(
+ pSrc->m_ControlValues[ COMBO_CONTROL_NORMAL ].Base(),
+ pSrc->m_ControlValues[ COMBO_CONTROL_NORMAL ].Count() );
+
+ m_ControlValues[ COMBO_CONTROL_LAGGED ].CopyArray(
+ pSrc->m_ControlValues[ COMBO_CONTROL_LAGGED ].Base(),
+ pSrc->m_ControlValues[ COMBO_CONTROL_LAGGED ].Count() );
+
+ RebuildRawControlList();
+
+ m_Dominators.Purge();
+
+ // Copy The Source Controls
+ for ( int i = 0; i < pSrc->m_Dominators.Count(); ++i )
+ {
+ CDmeCombinationDominationRule *pSrcDom = pSrc->m_Dominators[ i ];
+ if ( pSrcDom )
+ {
+ CDmeCombinationDominationRule *pDstDom = pSrcDom->Copy( );
+ pDstDom->SetFileId( GetFileId(), TD_DEEP );
+ m_Dominators.AddToTail( pDstDom );
+ }
+ }
+
+ RebuildRawControlList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Generates wrinkle deltas for the uncorrected controls
+// NOTE: This is only being used because we have no authoring path for wrinkle data yet
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::GenerateWrinkleDeltas( CDmeShape *pShape, bool bOverwrite )
+{
+ CDmeMesh* pMesh = CastElement< CDmeMesh >( pShape );
+ CDmeVertexData *pBindState = pMesh ? pMesh->FindBaseState( "bind" ) : NULL;
+ if ( pBindState )
+ {
+ int nRawControlCount = m_RawControlInfo.Count();
+ for ( int i = 0; i < nRawControlCount; ++i )
+ {
+ CDmeVertexDeltaData* pDelta = pMesh->FindDeltaState( m_RawControlInfo[i].m_Name );
+ if ( pDelta )
+ {
+ pDelta->GenerateWrinkleDelta( pBindState, m_RawControlInfo[i].m_flWrinkleScale, bOverwrite );
+ }
+ }
+ }
+}
+
+void CDmeCombinationOperator::GenerateWrinkleDeltas( bool bOverwrite /* = true */ )
+{
+ int nTargetCount = m_Targets.Count();
+ for ( int i = 0; i < nTargetCount; ++i )
+ {
+ GenerateWrinkleDeltas( CastElement< CDmeShape >( m_Targets[i] ), bOverwrite );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::RemoveAllTargets()
+{
+ m_Targets.RemoveAll();
+
+ CleanUpCombinationInfo();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::SetToDefault()
+{
+ const int nControlsCount = m_InputControls.Count();
+ for ( int i = 0; i < nControlsCount; ++i )
+ {
+ m_IsDefaultValue[ i ] = true;
+ UpdateDefaultValue( i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::SetToBase()
+{
+ const int nControlsCount = m_InputControls.Count();
+ for ( int i = 0; i < nControlsCount; ++i )
+ {
+ const float flBaseValue = GetControlBaseValue( i );
+ for ( int j = 0; j < COMBO_CONTROL_TYPE_COUNT; ++j )
+ {
+ const Vector &v = m_ControlValues[ j ][ i ];
+ m_ControlValues[ j ].Set( i, Vector( flBaseValue, v.y, v.z ) );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// * Remove all controls which refer to raw controls which refer to delta
+// states which do not exist in any target
+// * Remove all dominators which refer to states which no longer exist
+//-----------------------------------------------------------------------------
+void CDmeCombinationOperator::Purge()
+{
+ bool bDelete = true;
+
+ const int nTargetCount = m_Targets.Count();
+ for ( int i = 0; i < nTargetCount; ++i )
+ {
+ CDmElement *pSource = m_Targets[ i ];
+ if ( !pSource )
+ continue;
+
+ CDmrElementArray<> deltas( pSource, "deltaStates" );
+ if ( !deltas.IsValid() )
+ continue;
+
+ const int nDeltaCount = deltas.Count();
+
+ do
+ {
+ bDelete = false;
+
+ const int nControlCount = GetControlCount();
+ for ( int j = 0; j < nControlCount; ++j )
+ {
+ bDelete = true;
+ const int nRawControlCount = GetRawControlCount( j );
+ for ( int k = 0; bDelete && k < nRawControlCount; ++k )
+ {
+ for ( int l = 0; l < nDeltaCount; ++l )
+ {
+ if ( !Q_strcmp( GetRawControlName( j, k ), deltas[ l ]->GetName() ) )
+ {
+ bDelete = false;
+ break;
+ }
+ }
+ }
+
+ if ( bDelete )
+ {
+ RemoveControl( GetControlName( j ) );
+ break;
+ }
+ }
+ } while ( bDelete );
+ }
+
+ do
+ {
+ bDelete = false;
+
+ const int nDomCount = DominationRuleCount();
+ for ( int i = 0; i < nDomCount; ++i )
+ {
+ bDelete = true;
+ CDmeCombinationDominationRule *pDom = GetDominationRule( i );
+
+ const int nDCount = pDom->DominatorCount();
+ for ( int j = 0; j < nDCount; ++j )
+ {
+ if ( HasRawControl( pDom->GetDominator( j ) ) )
+ {
+ bDelete = false;
+ break;
+ }
+ }
+
+ if ( !bDelete )
+ {
+ const int nSCount = pDom->SuppressedCount();
+ for ( int j = 0; j < nSCount; ++j )
+ {
+ if ( HasRawControl( pDom->GetSuppressed( j ) ) )
+ {
+ bDelete = false;
+ break;
+ }
+ }
+ }
+
+ if ( bDelete )
+ {
+ RemoveDominationRule( i );
+ break;
+ }
+ }
+ } while ( bDelete );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates lagged log data from an input log
+//-----------------------------------------------------------------------------
+static void CreateLaggedLog( CDmeVector2Log *pLog, CDmeVector2Log *pLaggedLog, int nSamplesPerSec )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Helper method to create a lagged version of channel data from source data
+//-----------------------------------------------------------------------------
+void CreateLaggedVertexAnimation( CDmeChannelsClip *pClip, int nSamplesPerSec )
+{
+ if ( !pClip )
+ return;
+
+ int nChannelCount = pClip->m_Channels.Count();
+ for ( int i = 0; i < nChannelCount; ++i )
+ {
+ CDmeChannel *pChannel = pClip->m_Channels[i];
+ CDmElement *pDest = pChannel->GetToElement();
+ CDmAttribute *pDestAttr = pChannel->GetToAttribute();
+ int nArrayIndex = pChannel->GetToArrayIndex();
+
+ if ( pChannel->GetLog()->GetDataType() != AT_VECTOR2 )
+ continue;
+
+ if ( Q_stricmp( pDestAttr->GetName(), "controlValues" ) )
+ continue;
+
+ if ( !pDest->GetValue<bool>( "usesLaggedValues" ) )
+ continue;
+
+ CDmAttribute *pLaggedAttr = pDest->GetAttribute( "controlValuesLagged" );
+ if ( !pLaggedAttr || pLaggedAttr->GetType() != AT_VECTOR2_ARRAY )
+ continue;
+
+ int nLen = Q_strlen( pChannel->GetName() );
+ char *pNewChannelName = (char*)_alloca( nLen + 10 );
+ memcpy( pNewChannelName, pChannel->GetName(), nLen+1 );
+ Q_strncpy( &pNewChannelName[nLen], "_lagged", 10 );
+ CDmeChannel *pNewChannel = CreateElement< CDmeChannel >( pNewChannelName, pClip->GetFileId() );
+ pNewChannel->SetOutput( pLaggedAttr, nArrayIndex );
+ CDmeVector2Log *pNewLog = pNewChannel->CreateLog< Vector2D >( );
+ pClip->m_Channels.AddToTail( pNewChannel );
+
+ CreateLaggedLog( static_cast<CDmeVector2Log*>( pChannel->GetLog() ), pNewLog, nSamplesPerSec );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// A class used to edit combination operators in Maya.. doesn't connect to targets
+//
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMayaCombinationOperator, CDmeMayaCombinationOperator );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMayaCombinationOperator::OnConstruction()
+{
+ m_DeltaStates.Init( this, "deltaStates" );
+ m_DeltaStateWeights[COMBO_CONTROL_NORMAL].Init( this, "deltaStateWeights" );
+ m_DeltaStateWeights[COMBO_CONTROL_LAGGED].Init( this, "deltaStateWeightsLagged" );
+ AddTarget( this );
+}
+
+void CDmeMayaCombinationOperator::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Add, remove delta states
+//-----------------------------------------------------------------------------
+void CDmeMayaCombinationOperator::AddDeltaState( const char *pDeltaStateName )
+{
+ m_DeltaStates.AddToTail( CreateElement<CDmElement>( pDeltaStateName ) );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ m_DeltaStateWeights[i].AddToTail( Vector2D( 1.0f, 1.0f ) );
+ }
+ ComputeCombinationInfo( 0 );
+}
+
+void CDmeMayaCombinationOperator::RemoveDeltaState( const char *pDeltaStateName )
+{
+ int nIndex = FindDeltaState( pDeltaStateName );
+ if ( nIndex >= 0 )
+ {
+ m_DeltaStates.Remove( nIndex );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ m_DeltaStateWeights[i].Remove( nIndex );
+ }
+ }
+ ComputeCombinationInfo( 0 );
+}
+
+void CDmeMayaCombinationOperator::RemoveAllDeltaStates()
+{
+ m_DeltaStates.RemoveAll( );
+ for ( int i = 0; i < COMBO_CONTROL_TYPE_COUNT; ++i )
+ {
+ m_DeltaStateWeights[i].RemoveAll();
+ }
+ ComputeCombinationInfo( 0 );
+}
+
+int CDmeMayaCombinationOperator::FindDeltaState( const char *pDeltaStateName )
+{
+ int nCount = m_DeltaStates.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pDeltaStateName, m_DeltaStates[i]->GetName() ) )
+ return i;
+ }
+ return -1;
+}
+
+int CDmeMayaCombinationOperator::DeltaStateCount() const
+{
+ return m_DeltaStates.Count();
+}
+
+const char *CDmeMayaCombinationOperator::GetDeltaState( int nIndex ) const
+{
+ return m_DeltaStates[nIndex]->GetName();
+}
+
+const Vector2D& CDmeMayaCombinationOperator::GetDeltaStateWeight( int nIndex, CombinationControlType_t type ) const
+{
+ return m_DeltaStateWeights[type][nIndex];
+}
diff --git a/movieobjects/dmedag.cpp b/movieobjects/dmedag.cpp
new file mode 100644
index 0000000..79eb21d
--- /dev/null
+++ b/movieobjects/dmedag.cpp
@@ -0,0 +1,438 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmeshape.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmetransform.h"
+#include "movieobjects_interfaces.h"
+#include "movieobjects/dmedrawsettings.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeDag, CDmeDag );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CUtlStack<CDmeDag::TransformInfo_t> CDmeDag::s_TransformStack;
+bool CDmeDag::s_bDrawUsingEngineCoordinates = false;
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeDag::OnConstruction()
+{
+ m_Transform.InitAndCreate( this, "transform", GetName() );
+ m_Shape.Init( this, "shape" );
+ m_Visible.InitAndSet( this, "visible", true, FATTRIB_HAS_CALLBACK );
+ m_Children.Init( this, "children" );
+}
+
+void CDmeDag::OnDestruction()
+{
+ g_pDataModel->DestroyElement( m_Transform.Get() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Accessors
+//-----------------------------------------------------------------------------
+CDmeTransform *CDmeDag::GetTransform()
+{
+ return m_Transform.GetElement();
+}
+
+CDmeShape *CDmeDag::GetShape()
+{
+ return m_Shape.GetElement();
+}
+
+void CDmeDag::SetShape( CDmeShape *pShape )
+{
+ m_Shape = pShape;
+}
+
+
+bool CDmeDag::IsVisible() const
+{
+ return m_Visible;
+}
+
+void CDmeDag::SetVisible( bool bVisible )
+{
+ m_Visible = bVisible;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the visibility attribute for DmeRenderable support
+//-----------------------------------------------------------------------------
+CDmAttribute *CDmeDag::GetVisibilityAttribute()
+{
+ return m_Visible.GetAttribute();
+}
+
+
+//-----------------------------------------------------------------------------
+// child helpers
+//-----------------------------------------------------------------------------
+const CUtlVector< DmElementHandle_t > &CDmeDag::GetChildren() const
+{
+ return m_Children.Get();
+}
+
+int CDmeDag::GetChildCount() const
+{
+ return m_Children.Count();
+}
+
+CDmeDag *CDmeDag::GetChild( int i ) const
+{
+ if ( i < 0 || i >= m_Children.Count() )
+ return NULL;
+
+ return m_Children.Get( i );
+}
+
+void CDmeDag::AddChild( CDmeDag* pDag )
+{
+ m_Children.AddToTail( pDag );
+}
+
+void CDmeDag::RemoveChild( int i )
+{
+ m_Children.FastRemove( i );
+}
+
+void CDmeDag::RemoveChild( const CDmeDag *pChild, bool bRecurse )
+{
+ int i = FindChild( pChild );
+ if ( i >= 0 )
+ {
+ RemoveChild( i );
+ }
+}
+
+int CDmeDag::FindChild( const CDmeDag *pChild ) const
+{
+ return m_Children.Find( pChild->GetHandle() );
+}
+
+// recursive
+int CDmeDag::FindChild( CDmeDag *&pParent, const CDmeDag *pChild )
+{
+ int index = FindChild( pChild );
+ if ( index >= 0 )
+ {
+ pParent = this;
+ return index;
+ }
+
+ int nChildren = m_Children.Count();
+ for ( int ci = 0; ci < nChildren; ++ci )
+ {
+ index = m_Children[ ci ]->FindChild( pParent, pChild );
+ if ( index >= 0 )
+ return index;
+ }
+
+ pParent = NULL;
+ return -1;
+}
+
+int CDmeDag::FindChild( const char *name ) const
+{
+ int nChildren = m_Children.Count();
+ for ( int ci = 0; ci < nChildren; ++ci )
+ {
+ if ( V_strcmp( m_Children[ ci ]->GetName(), name ) == 0 )
+ return ci;
+ }
+ return -1;
+}
+
+CDmeDag *CDmeDag::FindOrAddChild( const char *name )
+{
+ int i = FindChild( name );
+ if ( i >= 0 )
+ return GetChild( i );
+
+ CDmeDag *pChild = CreateElement< CDmeDag >( name, GetFileId() );
+ AddChild( pChild );
+ return pChild;
+}
+
+
+//-----------------------------------------------------------------------------
+// Recursively render the Dag hierarchy
+//-----------------------------------------------------------------------------
+void CDmeDag::PushDagTransform()
+{
+ int i = s_TransformStack.Push();
+ TransformInfo_t &info = s_TransformStack[i];
+ info.m_pTransform = GetTransform();
+ info.m_bComputedDagToWorld = false;
+}
+
+void CDmeDag::PopDagTransform()
+{
+ Assert( s_TransformStack.Top().m_pTransform == GetTransform() );
+ s_TransformStack.Pop();
+}
+
+
+//-----------------------------------------------------------------------------
+// Transform from DME to engine coordinates
+//-----------------------------------------------------------------------------
+void CDmeDag::DmeToEngineMatrix( matrix3x4_t& dmeToEngine )
+{
+ VMatrix rotation, rotationZ;
+ MatrixBuildRotationAboutAxis( rotation, Vector( 1, 0, 0 ), 90 );
+ MatrixBuildRotationAboutAxis( rotationZ, Vector( 0, 1, 0 ), 90 );
+ ConcatTransforms( rotation.As3x4(), rotationZ.As3x4(), dmeToEngine );
+}
+
+//-----------------------------------------------------------------------------
+// Transform from engine to DME coordinates
+//-----------------------------------------------------------------------------
+void CDmeDag::EngineToDmeMatrix( matrix3x4_t& engineToDme )
+{
+ VMatrix rotation, rotationZ;
+ MatrixBuildRotationAboutAxis( rotation, Vector( 1, 0, 0 ), -90 );
+ MatrixBuildRotationAboutAxis( rotationZ, Vector( 0, 1, 0 ), -90 );
+ ConcatTransforms( rotationZ.As3x4(), rotation.As3x4(), engineToDme );
+}
+
+
+void CDmeDag::GetShapeToWorldTransform( matrix3x4_t &mat )
+{
+ int nCount = s_TransformStack.Count();
+ if ( nCount == 0 )
+ {
+ if ( !s_bDrawUsingEngineCoordinates )
+ {
+ SetIdentityMatrix( mat );
+ }
+ else
+ {
+ DmeToEngineMatrix( mat );
+ }
+ return;
+ }
+
+ if ( s_TransformStack.Top().m_bComputedDagToWorld )
+ {
+ MatrixCopy( s_TransformStack.Top().m_DagToWorld, mat );
+ return;
+ }
+
+ // Compute all uncomputed dag to worls
+ int i;
+ for ( i = 0; i < nCount; ++i )
+ {
+ TransformInfo_t &info = s_TransformStack[i];
+ if ( !info.m_bComputedDagToWorld )
+ break;
+ }
+
+ // Set up the initial transform
+ if ( i == 0 )
+ {
+ if ( !s_bDrawUsingEngineCoordinates )
+ {
+ SetIdentityMatrix( mat );
+ }
+ else
+ {
+ DmeToEngineMatrix( mat );
+ }
+ }
+ else
+ {
+ MatrixCopy( s_TransformStack[i-1].m_DagToWorld, mat );
+ }
+
+ // Compute all transforms
+ for ( ; i < nCount; ++i )
+ {
+ matrix3x4_t localToParent;
+ TransformInfo_t &info = s_TransformStack[i];
+ info.m_pTransform->GetTransform( localToParent );
+ ConcatTransforms( mat, localToParent, info.m_DagToWorld );
+ info.m_bComputedDagToWorld = true;
+ MatrixCopy( info.m_DagToWorld, mat );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDag::GetLocalMatrix( matrix3x4_t &m )
+{
+ CDmeTransform *pTransform = GetTransform();
+ if ( pTransform )
+ {
+ pTransform->GetTransform( m );
+ }
+ else
+ {
+ SetIdentityMatrix( m );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDag::GetWorldMatrix( matrix3x4_t &m )
+{
+ GetLocalMatrix( m );
+ const static UtlSymId_t symChildren = g_pDataModel->GetSymbol( "children" );
+ CDmeDag *pParent = FindReferringElement< CDmeDag >( this, symChildren );
+ if ( pParent )
+ {
+ matrix3x4_t localMatrix;
+ GetLocalMatrix( localMatrix );
+
+ matrix3x4_t parentWorldMatrix;
+ pParent->GetWorldMatrix( parentWorldMatrix );
+ ConcatTransforms( parentWorldMatrix, localMatrix, m );
+ }
+ else
+ {
+ GetLocalMatrix( m );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDag::GetParentWorldMatrix( matrix3x4_t &m )
+{
+ const static UtlSymId_t symChildren = g_pDataModel->GetSymbol( "children" );
+ CDmeDag *pParent = FindReferringElement< CDmeDag >( this, symChildren );
+ if ( pParent )
+ {
+ pParent->GetWorldMatrix( m );
+ }
+ else
+ {
+ SetIdentityMatrix( m );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Recursively render the Dag hierarchy
+//-----------------------------------------------------------------------------
+void CDmeDag::DrawUsingEngineCoordinates( bool bEnable )
+{
+ s_bDrawUsingEngineCoordinates = bEnable;
+}
+
+
+//-----------------------------------------------------------------------------
+// Recursively render the Dag hierarchy
+//-----------------------------------------------------------------------------
+void CDmeDag::Draw( CDmeDrawSettings *pDrawSettings )
+{
+ if ( !m_Visible )
+ return;
+
+ PushDagTransform();
+
+ CDmeShape *pShape = GetShape();
+ if ( pShape )
+ {
+ matrix3x4_t shapeToWorld;
+ GetShapeToWorldTransform( shapeToWorld );
+ pShape->Draw( shapeToWorld, pDrawSettings );
+ }
+
+ uint cn = m_Children.Count();
+ for ( uint ci = 0; ci < cn; ++ci )
+ {
+ m_Children[ ci ]->Draw( pDrawSettings );
+ }
+
+ PopDagTransform();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDag::GetBoundingSphere( Vector &c0, float &r0, const matrix3x4_t &pMat ) const
+{
+ matrix3x4_t lMat;
+ m_Transform.GetElement()->GetTransform( lMat );
+ matrix3x4_t wMat;
+ ConcatTransforms( pMat, lMat, wMat );
+
+ c0.Zero();
+ r0 = 0.0f;
+
+ const CDmeShape *pShape = m_Shape.GetElement();
+ if ( pShape )
+ {
+ pShape->GetBoundingSphere( c0, r0 );
+ }
+
+ // No scale in Dme! :)
+ Vector vTemp;
+ VectorTransform( c0, pMat, vTemp );
+
+ const int nChildren = m_Children.Count();
+ if ( nChildren > 0 )
+ {
+ Vector c1; // Child center
+ float r1; // Child radius
+
+ Vector v01; // c1 - c0
+ float l01; // |v01|
+
+ for ( int i = 0; i < nChildren; ++i )
+ {
+ m_Children[ i ]->GetBoundingSphere( c1, r1, wMat );
+
+ if ( r0 == 0.0f )
+ {
+ c0 = c1;
+ r0 = r1;
+ continue;
+ }
+
+ v01 = c1 - c0;
+ l01 = v01.NormalizeInPlace();
+
+ if ( r0 < l01 + r1 )
+ {
+ // Current sphere doesn't contain both spheres
+ if ( r1 < l01 + r0 )
+ {
+ // Child sphere doesn't contain both spheres
+ c0 = c0 + 0.5f * ( r1 + l01 - r0 ) * v01;
+ r0 = 0.5f * ( r0 + l01 + r1 );
+ }
+ else
+ {
+ // Child sphere contains both spheres
+ c0 = c1;
+ r0 = r1;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/movieobjects/dmedccmakefile.cpp b/movieobjects/dmedccmakefile.cpp
new file mode 100644
index 0000000..458fd33
--- /dev/null
+++ b/movieobjects/dmedccmakefile.cpp
@@ -0,0 +1,323 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Describes an asset: something that is compiled from sources,
+// in potentially multiple steps, to a compiled resource
+//
+//=============================================================================
+
+
+#include "movieobjects/dmedccmakefile.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "tier2/fileutils.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSourceDCCFile, CDmeSourceDCCFile );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeSourceDCCFile::OnConstruction()
+{
+ m_RootDCCObjects.Init( this, "rootDCCObjects" );
+ m_ExportType.InitAndSet( this, "exportType", 0 );
+ m_FrameStart.InitAndSet( this, "frameStart", 0.0f );
+ m_FrameEnd.InitAndSet( this, "frameEnd", 0.0f );
+ m_FrameIncrement.InitAndSet( this, "frameIncrement", 1.0f );
+}
+
+void CDmeSourceDCCFile::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSourceMayaFile, CDmeSourceMayaFile );
+IMPLEMENT_ELEMENT_FACTORY( DmeSourceMayaModelFile, CDmeSourceMayaModelFile );
+IMPLEMENT_ELEMENT_FACTORY( DmeSourceMayaAnimationFile, CDmeSourceMayaAnimationFile );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeSourceMayaFile::OnConstruction()
+{
+}
+
+void CDmeSourceMayaFile::OnDestruction()
+{
+}
+
+void CDmeSourceMayaModelFile::OnConstruction()
+{
+ m_ExportType = 0;
+}
+
+void CDmeSourceMayaModelFile::OnDestruction()
+{
+}
+
+void CDmeSourceMayaAnimationFile::OnConstruction()
+{
+ m_ExportType = 1;
+}
+
+void CDmeSourceMayaAnimationFile::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSourceXSIFile, CDmeSourceXSIFile );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeSourceXSIFile::OnConstruction()
+{
+}
+
+void CDmeSourceXSIFile::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeDCCMakefile, CDmeDCCMakefile );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeDCCMakefile::OnConstruction()
+{
+ m_bFlushFile = false;
+}
+
+void CDmeDCCMakefile::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Compile assets
+//-----------------------------------------------------------------------------
+void CDmeDCCMakefile::GetOutputs( CUtlVector<CUtlString> &fullPaths )
+{
+ fullPaths.RemoveAll();
+
+ char pOutputName[MAX_PATH];
+ Q_FileBase( GetFileName(), pOutputName, sizeof(pOutputName) );
+ if ( !pOutputName[0] )
+ return;
+
+ // FIXME: We need to come up with an appropriate directory structure for export
+ char pOutputDir[MAX_PATH];
+ GetMakefilePath( pOutputDir, sizeof(pOutputDir) );
+ if ( !pOutputDir[0] )
+ return;
+
+ Q_StripTrailingSlash( pOutputDir );
+ char pFullPath[MAX_PATH];
+ Q_snprintf( pFullPath, sizeof(pFullPath), "%s\\%s.dmx", pOutputDir, pOutputName );
+ fullPaths.AddToTail( pFullPath );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates, destroys the output element associated with this makefile
+//-----------------------------------------------------------------------------
+CDmElement *CDmeDCCMakefile::CreateOutputElement( )
+{
+ if ( m_bFlushFile )
+ {
+ m_bFlushFile = false;
+ if ( GetFileId() != DMFILEID_INVALID )
+ {
+ // NOTE: CDmeHandles will correctly re-hook up to the new makefile after load
+ // If the file fails to load, we have the copy. If the file correctly has the make in it
+ // it will replace this copy I made
+ CDmeHandle< CDmeDCCMakefile > hMakefileOld;
+ hMakefileOld = this;
+
+ // NOTE NOTE NOTE
+ // UnloadFile essentially calls delete this!
+ // So don't refer to any state in this DmElement after that
+ DmFileId_t fileId = GetFileId();
+ g_pDataModel->UnloadFile( fileId );
+
+ CDmElement *pRoot = NULL;
+ if ( g_pDataModel->RestoreFromFile( g_pDataModel->GetFileName( fileId ), NULL, NULL, &pRoot, CR_DELETE_OLD ) != DMFILEID_INVALID )
+ {
+ // NOTE: Unload/restore kills the this pointer, we need to redo this
+ if ( hMakefileOld.Get() )
+ {
+ hMakefileOld->SetDirty( false );
+ return hMakefileOld->CreateOutputElement();
+ }
+ }
+
+ // NOTE: We expect file backup prior to compile to avoid really fatal errors
+ // This case happens if the file failed to load. In this case, we must use
+ // the copy of the makefile
+ Assert( 0 );
+ return NULL;
+ }
+ }
+
+ // The output element is the root element containing the makefile
+ return FindReferringElement< CDmElement >( this, "makefile" );
+}
+
+void CDmeDCCMakefile::DestroyOutputElement( CDmElement *pOutput )
+{
+ m_bFlushFile = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMayaMakefile, CDmeMayaMakefile );
+IMPLEMENT_ELEMENT_FACTORY( DmeMayaModelMakefile, CDmeMayaModelMakefile );
+IMPLEMENT_ELEMENT_FACTORY( DmeMayaAnimationMakefile, CDmeMayaAnimationMakefile );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMayaMakefile::OnConstruction()
+{
+}
+
+void CDmeMayaMakefile::OnDestruction()
+{
+}
+
+void CDmeMayaModelMakefile::OnConstruction()
+{
+}
+
+void CDmeMayaModelMakefile::OnDestruction()
+{
+}
+
+void CDmeMayaAnimationMakefile::OnConstruction()
+{
+}
+
+void CDmeMayaAnimationMakefile::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns source types
+//-----------------------------------------------------------------------------
+static DmeMakefileType_t s_pMayaModelSourceTypes[] =
+{
+ { "DmeSourceMayaModelFile", "Maya Model File", true, "makefiledir:../maya", "*.ma;*.mb", "Maya File (*.ma,*.mb)" },
+ { NULL, NULL, false, NULL, NULL, NULL },
+};
+
+DmeMakefileType_t* CDmeMayaModelMakefile::GetSourceTypes()
+{
+ return s_pMayaModelSourceTypes;
+}
+
+static DmeMakefileType_t s_pMayaAnimationSourceTypes[] =
+{
+ { "DmeSourceMayaAnimationFile", "Maya Animation File", true, "makefiledir:../maya", "*.ma;*.mb", "Maya File (*.ma,*.mb)" },
+ { NULL, NULL, false, NULL, NULL, NULL },
+};
+
+DmeMakefileType_t* CDmeMayaAnimationMakefile::GetSourceTypes()
+{
+ return s_pMayaAnimationSourceTypes;
+}
+
+
+//-----------------------------------------------------------------------------
+// Makefile type
+//-----------------------------------------------------------------------------
+static DmeMakefileType_t s_MayaModelMakefileType =
+{
+ "DmeMayaModelMakefile", "Maya Model Component", true, "contentdir:models", "*.dmx", "DMX File (*.dmx)"
+};
+
+DmeMakefileType_t *CDmeMayaModelMakefile::GetMakefileType()
+{
+ return &s_MayaModelMakefileType;
+}
+
+static DmeMakefileType_t s_MayaAnimationMakefileType =
+{
+ "DmeMayaAnimationMakefile", "Maya Animation Component", true, "contentdir:models", "*.dmx", "DMX File (*.dmx)"
+};
+
+DmeMakefileType_t *CDmeMayaAnimationMakefile::GetMakefileType()
+{
+ return &s_MayaAnimationMakefileType;
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeXSIMakefile, CDmeXSIMakefile );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeXSIMakefile::OnConstruction()
+{
+}
+
+void CDmeXSIMakefile::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns source types
+//-----------------------------------------------------------------------------
+static DmeMakefileType_t s_pXSISourceTypes[] =
+{
+ { "DmeSourceXSIFile", "XSI File", true, "makefiledir:../xsi", "*.xsi", "XSI File (*.xsi)" },
+ { NULL, NULL, false, NULL, NULL, NULL },
+};
+
+DmeMakefileType_t* CDmeXSIMakefile::GetSourceTypes()
+{
+ return s_pXSISourceTypes;
+}
+
+
+//-----------------------------------------------------------------------------
+// Makefile type
+//-----------------------------------------------------------------------------
+static DmeMakefileType_t s_XSIMakefileType =
+{
+ "DmeXSIMakefile", "XSI Model Component", true, "contentdir:models", "*.dmx", "DMX File (*.dmx)",
+};
+
+DmeMakefileType_t *CDmeXSIMakefile::GetMakefileType()
+{
+ return &s_XSIMakefileType;
+}
+
+
diff --git a/movieobjects/dmedrawsettings.cpp b/movieobjects/dmedrawsettings.cpp
new file mode 100644
index 0000000..59b25f4
--- /dev/null
+++ b/movieobjects/dmedrawsettings.cpp
@@ -0,0 +1,265 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+
+#include "datamodel/dmelementfactoryhelper.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlrbtree.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmedrawsettings.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeDrawSettings, CDmeDrawSettings );
+
+
+//-----------------------------------------------------------------------------
+// Statics
+//-----------------------------------------------------------------------------
+bool CDmeDrawSettings::s_bWireframeMaterialInitialized( false );
+CMaterialReference CDmeDrawSettings::s_WireframeMaterial;
+bool CDmeDrawSettings::s_bWireframeOnShadedMaterialInitialized( false );
+CMaterialReference CDmeDrawSettings::s_WireframeOnShadedMaterial;
+bool CDmeDrawSettings::s_bFlatGrayMaterial( false );
+CMaterialReference CDmeDrawSettings::s_FlatGrayMaterial;
+bool CDmeDrawSettings::s_bUnlitGrayMaterial( false );
+CMaterialReference CDmeDrawSettings::s_UnlitGrayMaterial;
+
+CUtlRBTree< CUtlSymbol > CDmeDrawSettings::s_KnownDrawableTypes;
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::OnConstruction()
+{
+ if ( s_KnownDrawableTypes.Count() == 0 )
+ {
+ BuildKnownDrawableTypes();
+ }
+
+ SetDefLessFunc< CUtlRBTree< CUtlSymbol > >( m_NotDrawable );
+ m_NotDrawable.RemoveAll();
+
+ m_DrawType.InitAndSet( this, "drawType", static_cast< int >( DRAW_SMOOTH ) );
+ m_bBackfaceCulling.InitAndSet( this, "backfaceCulling", true );
+ m_bWireframeOnShaded.InitAndSet( this, "wireframeOnShaded", false );
+ m_bXRay.InitAndSet( this, "xray", false );
+ m_bGrayShade.InitAndSet( this, "grayShade", false );
+ m_bNormals.InitAndSet( this, "normals", false );
+ m_NormalLength.InitAndSet( this, "normalLength", 1.0 );
+ m_Color.InitAndSet( this, "color", Color( 0, 0, 0, 1 ) );
+ m_bDeltaHighlight.InitAndSet( this, "highlightDeltas", false );
+ m_flHighlightSize.InitAndSet( this, "highlightSize", 1.5f );
+ m_cHighlightColor.InitAndSet( this, "highlightColor", Color( 0xff, 0x14, 0x93, 0xff ) ); // Deep Pink
+
+ m_IsAMaterialBound = false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::Resolve()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Wireframe
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::BindWireframe()
+{
+ if ( !s_bWireframeMaterialInitialized )
+ {
+ s_bWireframeMaterialInitialized = true;
+
+ KeyValues *pKeyValues = new KeyValues( "wireframe" );
+ pKeyValues->SetInt( "$decal", 0 );
+ pKeyValues->SetInt( "$vertexcolor", 1 );
+ pKeyValues->SetInt( "$ignorez", 1 );
+ s_WireframeMaterial.Init( "__DmeWireframe", pKeyValues );
+ }
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( s_WireframeMaterial );
+ m_IsAMaterialBound = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Wireframe as a decal
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::BindWireframeOnShaded()
+{
+ if ( !s_bWireframeOnShadedMaterialInitialized )
+ {
+ s_bWireframeOnShadedMaterialInitialized = true;
+
+ KeyValues *pKeyValues = new KeyValues( "wireframe" );
+ pKeyValues->SetInt( "$decal", 0 );
+ pKeyValues->SetInt( "$vertexcolor", 1 );
+ pKeyValues->SetInt( "$ignorez", 0 );
+ s_WireframeOnShadedMaterial.Init( "__DmeWireframeOnShaded", pKeyValues );
+ }
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( s_WireframeOnShadedMaterial );
+ m_IsAMaterialBound = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Flat Gray Shaded
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::BindGray()
+{
+ if ( !s_bFlatGrayMaterial )
+ {
+ s_bFlatGrayMaterial = true;
+
+ KeyValues *pKeyValues = new KeyValues( "VertexLitGeneric" );
+ pKeyValues->SetInt( "$model", 1 );
+ s_FlatGrayMaterial.Init( "__DmeFlatGray", pKeyValues );
+ }
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( s_FlatGrayMaterial );
+ m_IsAMaterialBound = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Flat Gray Shaded
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::BindUnlitGray()
+{
+ if ( !s_bUnlitGrayMaterial )
+ {
+ s_bUnlitGrayMaterial = true;
+
+ KeyValues *pKeyValues = new KeyValues( "UnlitGeneric" );
+ pKeyValues->SetInt( "$model", 1 );
+ pKeyValues->SetInt( "$vertexcolor", 1 );
+ s_UnlitGrayMaterial.Init( "__DmeUnlitGray", pKeyValues );
+ }
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( s_UnlitGrayMaterial );
+ m_IsAMaterialBound = true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeDrawSettings::Drawable( CDmElement *pElement )
+{
+ if ( m_NotDrawable.IsValidIndex( m_NotDrawable.Find( pElement->GetType() ) ) )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::BuildKnownDrawableTypes()
+{
+ SetDefLessFunc< CUtlRBTree< CUtlSymbol > >( s_KnownDrawableTypes );
+
+ s_KnownDrawableTypes.RemoveAll();
+
+ s_KnownDrawableTypes.InsertIfNotFound( g_pDataModel->GetSymbol( "DmeMesh" ) );
+ s_KnownDrawableTypes.InsertIfNotFound( g_pDataModel->GetSymbol( "DmeJoint" ) );
+ s_KnownDrawableTypes.InsertIfNotFound( g_pDataModel->GetSymbol( "DmeModel" ) );
+ s_KnownDrawableTypes.InsertIfNotFound( g_pDataModel->GetSymbol( "DmeAttachment" ) );
+
+ m_NotDrawable.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeDrawSettings::DrawDag( CDmeDag *pDag )
+{
+ if ( !pDag )
+ return;
+
+ m_vHighlightPoints.RemoveAll();
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ m_IsAMaterialBound = false;
+
+ if ( GetDeltaHighlight() )
+ {
+ BindUnlitGray();
+ }
+ else
+ {
+ if ( !Shaded() )
+ {
+ BindWireframe();
+ }
+ else if ( GetGrayShade() )
+ {
+ BindGray();
+ }
+ }
+
+ pDag->Draw( this );
+
+ m_IsAMaterialBound = false;
+
+ if ( GetDeltaHighlight() || ( GetWireframeOnShaded() && Shaded() ) )
+ {
+ VMatrix m;
+
+ pRenderContext->GetMatrix( MATERIAL_PROJECTION, &m );
+
+ /* Extract the near and far clipping plane values from projection matrix
+ float c = m[ 2 ][ 2 ];
+ float d = m[ 2 ][ 3 ];
+
+ const float near = d / ( c - 1.0f );
+ const float far = d / ( c + 1.0f );
+ */
+
+ const float zBias = 0.00025;
+ m[ 2 ][ 2 ] += zBias;
+ m[ 2 ][ 3 ] += zBias;
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadMatrix( m );
+
+ BindWireframeOnShaded();
+ PushDrawType();
+ SetDrawType( CDmeDrawSettings::DRAW_WIREFRAME );
+ pDag->Draw( this );
+ PopDrawType();
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PopMatrix();
+ }
+} \ No newline at end of file
diff --git a/movieobjects/dmeeditortypedictionary.cpp b/movieobjects/dmeeditortypedictionary.cpp
new file mode 100644
index 0000000..f4efc00
--- /dev/null
+++ b/movieobjects/dmeeditortypedictionary.cpp
@@ -0,0 +1,328 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Contains a bunch of information about editor types
+// Editor types are arbitrary
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "movieobjects/dmeeditortypedictionary.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose DmeEditorAttributeInfo to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeEditorAttributeInfo, CDmeEditorAttributeInfo );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeEditorAttributeInfo::OnConstruction()
+{
+ m_Widget.Init( this, "widget" );
+ m_bIsVisible.Init( this, "isVisible" );
+ m_bIsReadOnly.Init( this, "isReadOnly" );
+ m_ArrayEntries.Init( this, "arrayEntries" );
+ m_bHideType.Init( this, "hideType" );
+ m_bHideValue.Init( this, "hideValue" );
+ m_Help.Init( this, "help" );
+
+ m_bIsVisible = true;
+ m_bIsReadOnly = false;
+}
+
+void CDmeEditorAttributeInfo::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the attribute name this info is associated with
+//-----------------------------------------------------------------------------
+const char *CDmeEditorAttributeInfo::GetAttributeName() const
+{
+ return GetName();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the widget name
+//-----------------------------------------------------------------------------
+const char *CDmeEditorAttributeInfo::GetWidgetName() const
+{
+ return m_Widget.Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the info for a entry in an attribute array, if this attribute is an array type
+//-----------------------------------------------------------------------------
+CDmeEditorAttributeInfo *CDmeEditorAttributeInfo::GetArrayInfo()
+{
+ return m_ArrayEntries;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the info for an entry in an attribute array
+//-----------------------------------------------------------------------------
+void CDmeEditorAttributeInfo::SetArrayInfo( CDmeEditorAttributeInfo *pInfo )
+{
+ m_ArrayEntries = pInfo;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CDmeEditorChoicesInfo, Base class for configuration for choices
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Expose DmeEditorChoicesInfo to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeEditorChoicesInfo, CDmeEditorChoicesInfo );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeEditorChoicesInfo::OnConstruction()
+{
+ m_Choices.Init( this, "choices" );
+ m_ChoiceType.Init( this, "choicetype" );
+}
+
+void CDmeEditorChoicesInfo::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets/gets choice type
+//-----------------------------------------------------------------------------
+void CDmeEditorChoicesInfo::SetChoiceType( const char *pChoiceType )
+{
+ m_ChoiceType = pChoiceType;
+}
+
+const char *CDmeEditorChoicesInfo::GetChoiceType() const
+{
+ return m_ChoiceType;
+}
+
+bool CDmeEditorChoicesInfo::HasChoiceType() const
+{
+ return m_ChoiceType.Length() > 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the choices
+//-----------------------------------------------------------------------------
+int CDmeEditorChoicesInfo::GetChoiceCount() const
+{
+ return m_Choices.Count();
+}
+
+CDmElement *CDmeEditorChoicesInfo::CreateChoice( const char *pChoiceString )
+{
+ CDmElement *pChoice = CreateElement< CDmElement >( "", GetFileId() );
+ m_Choices.AddToTail( pChoice );
+ pChoice->SetValue<CUtlString>( "string", pChoiceString );
+ return pChoice;
+}
+
+const char *CDmeEditorChoicesInfo::GetChoiceString( int nIndex ) const
+{
+ Assert( ( nIndex < GetChoiceCount() ) && ( nIndex >= 0 ) );
+ CDmElement *pChoice = m_Choices[nIndex];
+ if ( !pChoice )
+ return "";
+
+ return pChoice->GetValueString( "string" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose DmeEditorType class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeEditorType, CDmeEditorType );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeEditorType::OnConstruction()
+{
+}
+
+void CDmeEditorType::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the actual attribute name stored in the type
+//-----------------------------------------------------------------------------
+const char *CDmeEditorType::GetActualAttributeName( const char *pAttributeName )
+{
+ // Fixup the names of the attribute info for the 3 standard fields (name, type, id)
+ if ( !V_stricmp( "name", pAttributeName ) )
+ return "__name";
+
+ if ( !V_stricmp( "id", pAttributeName ) )
+ return "__id";
+
+ if ( !V_stricmp( "type", pAttributeName ) )
+ return "__type";
+
+ return pAttributeName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the editor info associated with an editor type
+//-----------------------------------------------------------------------------
+void CDmeEditorType::AddAttributeInfo( const char *pAttributeName, CDmeEditorAttributeInfo *pInfo )
+{
+ pAttributeName = GetActualAttributeName( pAttributeName );
+ SetValue( pAttributeName, pInfo );
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes a editor type associated with a particular attribute
+//-----------------------------------------------------------------------------
+void CDmeEditorType::RemoveAttributeInfo( const char *pAttributeName )
+{
+ pAttributeName = GetActualAttributeName( pAttributeName );
+ if ( HasAttribute( pAttributeName ) )
+ {
+ RemoveAttribute( pAttributeName );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the editor info associated with an editor type
+//-----------------------------------------------------------------------------
+CDmeEditorAttributeInfo *CDmeEditorType::GetAttributeInfo( const char *pAttributeName )
+{
+ pAttributeName = GetActualAttributeName( pAttributeName );
+ if ( !HasAttribute( pAttributeName ) )
+ return NULL;
+
+ return GetValueElement< CDmeEditorAttributeInfo >( pAttributeName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the editor info associated with a single entry in an attribute array
+//-----------------------------------------------------------------------------
+CDmeEditorAttributeInfo *CDmeEditorType::GetAttributeArrayInfo( const char *pAttributeName )
+{
+ CDmeEditorAttributeInfo *pAttributeInfo = GetAttributeInfo( pAttributeName );
+ if ( !pAttributeInfo )
+ return NULL;
+
+ return pAttributeInfo->GetArrayInfo();
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose DmeEditorTypeDictionary class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeEditorTypeDictionary, CDmeEditorTypeDictionary );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeEditorTypeDictionary::OnConstruction()
+{
+}
+
+void CDmeEditorTypeDictionary::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeEditorTypeDictionary::AddEditorType( CDmeEditorType *pEditorType )
+{
+ const char *pEditorTypeName = pEditorType->GetName();
+ if ( HasAttribute( pEditorTypeName ) )
+ {
+ Warning( "Editor type %s is already defined! Ignoring...\n", pEditorTypeName );
+ return;
+ }
+
+ SetValue( pEditorTypeName, pEditorType->GetHandle() );
+}
+
+void CDmeEditorTypeDictionary::AddEditorTypesFromFile( const char *pFileName, const char *pPathID )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the editor type to use with an element
+//-----------------------------------------------------------------------------
+CDmeEditorType *CDmeEditorTypeDictionary::GetEditorType( CDmElement *pElement )
+{
+ if ( !pElement )
+ return NULL;
+
+ const char *pEditorTypeName = NULL;
+ if ( pElement->HasAttribute( "editorType" ) )
+ {
+ pEditorTypeName = pElement->GetValueString( "editorType" );
+ }
+
+ if ( !pEditorTypeName || !pEditorTypeName[0] )
+ {
+ // Try to use the type name as an editor
+ pEditorTypeName = pElement->GetTypeString();
+ }
+
+ if ( !pEditorTypeName || !pEditorTypeName[0] )
+ return NULL;
+
+ if ( !HasAttribute( pEditorTypeName ) )
+ return NULL;
+
+ return GetValueElement< CDmeEditorType >( pEditorTypeName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the editor info associated with an editor type
+//-----------------------------------------------------------------------------
+CDmeEditorAttributeInfo *CDmeEditorTypeDictionary::GetAttributeInfo( CDmElement *pElement, const char *pAttributeName )
+{
+ CDmeEditorType *pEditorType = GetEditorType( pElement );
+ if ( !pEditorType )
+ return NULL;
+ return pEditorType->GetAttributeInfo( pAttributeName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the editor info associated with an editor type
+//-----------------------------------------------------------------------------
+CDmeEditorAttributeInfo *CDmeEditorTypeDictionary::GetAttributeArrayInfo( CDmElement *pElement, const char *pAttributeName )
+{
+ CDmeEditorType *pEditorType = GetEditorType( pElement );
+ if ( !pEditorType )
+ return NULL;
+ return pEditorType->GetAttributeArrayInfo( pAttributeName );
+}
diff --git a/movieobjects/dmeexpressionoperator.cpp b/movieobjects/dmeexpressionoperator.cpp
new file mode 100644
index 0000000..424a344
--- /dev/null
+++ b/movieobjects/dmeexpressionoperator.cpp
@@ -0,0 +1,942 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The expression operator class - scalar math calculator
+// for a good list of operators and simple functions, see:
+// \\fileserver\user\MarcS\boxweb\aliveDistLite\v4.2.0\doc\alive\functions.txt
+// (although we'll want to implement elerp as the standard 3x^2 - 2x^3 with rescale)
+//
+//=============================================================================
+#include "movieobjects/dmeexpressionoperator.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmattribute.h"
+#include "mathlib/noise.h"
+#include "mathlib/vector.h"
+#include <ctype.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Parsing helper methods
+//-----------------------------------------------------------------------------
+bool ParseLiteral( const char *&expr, float &value )
+{
+ const char *startExpr = expr;
+ value = ( float )strtod( startExpr, const_cast< char** >( &expr ) );
+ return ( startExpr != expr );
+}
+
+bool ParseString( const char *&expr, const char *str )
+{
+ const char *startExpr = expr;
+ while ( ( *expr == ' ' ) || ( *expr == '\t' ) )
+ expr++; // skip whitespace
+
+ expr = StringAfterPrefix( expr, str );
+ if ( expr )
+ return true;
+
+ expr = startExpr;
+ return false;
+}
+
+bool ParseStringList( const char *&expr, const char **pOps, int &nOp )
+{
+ while ( nOp-- )
+ {
+ if ( ParseString( expr, pOps[ nOp ] ) )
+ return true;
+ }
+ return false;
+}
+
+bool ParseStringList( const char *&expr, const CUtlVector< CUtlString > &strings, int &nOp )
+{
+ while ( nOp-- )
+ {
+ if ( ParseString( expr, strings[ nOp ] ) )
+ return true;
+ }
+ return false;
+}
+
+int FindString( const CUtlVector< CUtlString > &strings, const char *str )
+{
+ uint sn = strings.Count();
+ for ( uint si = 0; si < sn; ++si )
+ {
+ if ( !Q_strcmp( str, strings[ si ] ) )
+ return si;
+ }
+ return -1;
+}
+
+class ParseState_t
+{
+public:
+ ParseState_t( const CUtlStack<float> &stack, const char *expr )
+ : m_stacksize( stack.Count() ), m_startingExpr( expr ) {}
+ void Reset( CUtlStack<float> &stack, const char *&expr )
+ {
+ Assert( m_stacksize <= stack.Count() );
+ stack.PopMultiple( stack.Count() - m_stacksize );
+ expr = m_startingExpr;
+ }
+
+private:
+ int m_stacksize;
+ const char* m_startingExpr;
+};
+
+
+
+
+void CExpressionCalculator::SetVariable( int nVariableIndex, float value )
+{
+ m_varValues[ nVariableIndex ] = value;
+}
+
+void CExpressionCalculator::SetVariable( const char *var, float value )
+{
+ int vi = FindString( m_varNames, var );
+ if ( vi >= 0 )
+ {
+ m_varValues[ vi ] = value;
+ }
+ else
+ {
+ m_varNames.AddToTail( var );
+ m_varValues.AddToTail( value );
+ }
+}
+
+int CExpressionCalculator::FindVariableIndex( const char *var )
+{
+ return FindString( m_varNames, var );
+}
+
+bool CExpressionCalculator::Evaluate( float &value )
+{
+ m_bIsBuildingArgumentList = false;
+ m_stack.PopMultiple( m_stack.Count() );
+ const char *pExpr = m_expr.Get();
+ bool success = ParseExpr( pExpr );
+ if ( success && m_stack.Count() == 1 )
+ {
+ value = m_stack.Top();
+ return true;
+ }
+
+ value = 0.0f;
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a list of variable names from the expression
+//-----------------------------------------------------------------------------
+bool CExpressionCalculator::BuildVariableListFromExpression( )
+{
+ m_bIsBuildingArgumentList = true;
+ m_stack.PopMultiple( m_stack.Count() );
+ const char *pExpr = m_expr.Get();
+ bool bSuccess = ParseExpr( pExpr );
+ m_bIsBuildingArgumentList = false;
+ if ( !bSuccess || m_stack.Count() != 1 )
+ {
+ m_varNames.RemoveAll();
+ return false;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Iterate over variables
+//-----------------------------------------------------------------------------
+int CExpressionCalculator::VariableCount()
+{
+ return m_varNames.Count();
+}
+
+const char *CExpressionCalculator::VariableName( int nIndex )
+{
+ return m_varNames[nIndex];
+}
+
+
+
+bool CExpressionCalculator::ParseExpr( const char *&expr )
+{
+ return ( expr != NULL ) && ParseConditional( expr );
+}
+
+bool CExpressionCalculator::ParseConditional( const char *&expr )
+{
+ ParseState_t ps0( m_stack, expr );
+ if ( !ParseOr( expr ) )
+ {
+ ps0.Reset( m_stack, expr );
+ return false; // nothing matched
+ }
+
+ ParseState_t ps1( m_stack, expr );
+ if ( ParseString( expr, "?" ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, ":" ) &&
+ ParseExpr( expr ) )
+ {
+ float f3 = m_stack.Top();
+ m_stack.Pop();
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ m_stack.Push( f1 != 0.0f ? f2 : f3 );
+ return true; // and matched
+ }
+ ps1.Reset( m_stack, expr );
+ return true; // equality (or lower) matched
+}
+
+bool CExpressionCalculator::ParseOr( const char *&expr )
+{
+ ParseState_t ps0( m_stack, expr );
+ if ( !ParseAnd( expr ) )
+ {
+ ps0.Reset( m_stack, expr );
+ return false; // nothing matched
+ }
+
+ ParseState_t ps1( m_stack, expr );
+ if ( ParseString( expr, "||" ) &&
+ ParseOr( expr ) )
+ {
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ m_stack.Push( ( f1 != 0.0f ) || ( f2 != 0.0f ) ? 1 : 0 );
+ return true; // and matched
+ }
+ ps1.Reset( m_stack, expr );
+ return true; // equality (or lower) matched
+}
+
+bool CExpressionCalculator::ParseAnd( const char *&expr )
+{
+ ParseState_t ps0( m_stack, expr );
+ if ( !ParseEquality( expr ) )
+ {
+ ps0.Reset( m_stack, expr );
+ return false; // nothing matched
+ }
+
+ ParseState_t ps1( m_stack, expr );
+ if ( ParseString( expr, "&&" ) &&
+ ParseAnd( expr ) )
+ {
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ m_stack.Push( ( f1 != 0.0f ) && ( f2 != 0.0f ) ? 1 : 0 );
+ return true; // and matched
+ }
+ ps1.Reset( m_stack, expr );
+ return true; // equality (or lower) matched
+}
+
+bool CExpressionCalculator::ParseEquality( const char *&expr )
+{
+ ParseState_t ps0( m_stack, expr );
+ if ( !ParseLessGreater( expr ) )
+ {
+ ps0.Reset( m_stack, expr );
+ return false; // nothing matched
+ }
+
+ const char *pOps[] = { "==", "!=" };
+ int nOp = 2;
+
+ ParseState_t ps1( m_stack, expr );
+ if ( ParseStringList( expr, pOps, nOp ) &&
+ ParseEquality( expr ) )
+ {
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nOp )
+ {
+ case 0: // ==
+ m_stack.Push( f1 == f2 ? 1 : 0 );
+ break;
+ case 1: // !=
+ m_stack.Push( f1 != f2 ? 1 : 0 );
+ break;
+ }
+ return true; // equality matched
+ }
+ ps1.Reset( m_stack, expr );
+ return true; // lessgreater (or lower) matched
+}
+
+bool CExpressionCalculator::ParseLessGreater( const char *&expr )
+{
+ ParseState_t ps0( m_stack, expr );
+ if ( !ParseAddSub( expr ) )
+ {
+ ps0.Reset( m_stack, expr );
+ return false; // nothing matched
+ }
+
+ const char *pOps[] = { "<", ">", "<=", ">=" };
+ int nOp = 4;
+
+ ParseState_t ps1( m_stack, expr );
+ if ( ParseStringList( expr, pOps, nOp ) &&
+ ParseLessGreater( expr ) )
+ {
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nOp )
+ {
+ case 0: // <
+ m_stack.Push( f1 < f2 ? 1 : 0 );
+ break;
+ case 1: // >
+ m_stack.Push( f1 > f2 ? 1 : 0 );
+ break;
+ case 2: // <=
+ m_stack.Push( f1 <= f2 ? 1 : 0 );
+ break;
+ case 3: // >=
+ m_stack.Push( f1 >= f2 ? 1 : 0 );
+ break;
+ }
+ return true; // inequality matched
+ }
+ ps1.Reset( m_stack, expr );
+ return true; // addsub (or lower) matched
+}
+
+bool CExpressionCalculator::ParseAddSub( const char *&expr )
+{
+ ParseState_t ps0( m_stack, expr );
+ if ( !ParseDivMul( expr ) )
+ {
+ ps0.Reset( m_stack, expr );
+ return false; // nothing matched
+ }
+
+ const char *pOps[] = { "+", "-" };
+ int nOp = 2;
+
+ ParseState_t ps1( m_stack, expr );
+ if ( ParseStringList( expr, pOps, nOp ) &&
+ ParseAddSub( expr ) )
+ {
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nOp )
+ {
+ case 0: // +
+ m_stack.Push( f1 + f2 );
+ break;
+ case 1: // -
+ m_stack.Push( f1 - f2 );
+ break;
+ }
+ return true; // addsub matched
+ }
+ ps1.Reset( m_stack, expr );
+ return true; // divmul (or lower) matched
+}
+
+bool CExpressionCalculator::ParseDivMul( const char *&expr )
+{
+ ParseState_t ps0( m_stack, expr );
+ if ( !ParseUnary( expr ) )
+ {
+ ps0.Reset( m_stack, expr );
+ return false; // nothing matched
+ }
+
+ const char *pOps[] = { "*", "/", "%" };
+ int nOp = 3;
+
+ ParseState_t ps1( m_stack, expr );
+ if ( ParseStringList( expr, pOps, nOp ) &&
+ ParseDivMul( expr ) )
+ {
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nOp )
+ {
+ case 0: // *
+ m_stack.Push( f1 * f2 );
+ break;
+ case 1: // /
+ m_stack.Push( f1 / f2 );
+ break;
+ case 2: // %
+ m_stack.Push( fmod( f1, f2 ) );
+ break;
+ }
+ return true; // divmul matched
+ }
+ ps1.Reset( m_stack, expr );
+ return true; // unary (or lower) matched
+}
+
+bool CExpressionCalculator::ParseUnary( const char *&expr )
+{
+ ParseState_t ps( m_stack, expr );
+
+ const char *pOps[] = { "+", "-", "!" };
+ int nOp = 3;
+
+ if ( ParseStringList( expr, pOps, nOp ) &&
+ ParseUnary( expr ) )
+ {
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nOp )
+ {
+ case 0: // +
+ m_stack.Push( f1 );
+ break;
+ case 1: // -
+ m_stack.Push( -f1 );
+ break;
+ case 2: // !
+ m_stack.Push( f1 == 0 ? 1 : 0 );
+ break;
+ }
+ return true;
+ }
+
+ ps.Reset( m_stack, expr );
+ if ( ParsePrimary( expr ) )
+ return true;
+
+ ps.Reset( m_stack, expr );
+ return false;
+}
+
+bool CExpressionCalculator::ParsePrimary( const char *&expr )
+{
+ ParseState_t ps( m_stack, expr );
+
+ float value = 0.0f;
+ if ( ParseLiteral( expr, value ) )
+ {
+ m_stack.Push( value );
+ return true;
+ }
+
+ ps.Reset( m_stack, expr );
+ int nVar = m_varNames.Count();
+ if ( ParseStringList( expr, m_varNames, nVar) )
+ {
+ m_stack.Push( m_varValues[ nVar ] );
+ return true;
+ }
+
+ ps.Reset( m_stack, expr );
+ if ( ParseString( expr, "(" ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, ")" ) )
+ {
+ return true;
+ }
+
+ ps.Reset( m_stack, expr );
+ if ( Parse1ArgFunc( expr ) ||
+ Parse2ArgFunc( expr ) ||
+ Parse3ArgFunc( expr ) ||
+// Parse4ArgFunc( expr ) ||
+ Parse5ArgFunc( expr ) )
+ {
+ return true;
+ }
+
+ // If we're parsing it to discover names of variable names, add them here
+ if ( !m_bIsBuildingArgumentList )
+ return false;
+
+ // Variables can't start with a number
+ if ( isdigit( *expr ) )
+ return false;
+
+ const char *pStart = expr;
+ while ( isalnum( *expr ) || *expr == '_' )
+ {
+ ++expr;
+ }
+
+ int nLen = (size_t)expr - (size_t)pStart;
+ char *pVariableName = (char*)_alloca( nLen+1 );
+ memcpy( pVariableName, pStart, nLen );
+ pVariableName[nLen] = 0;
+
+ SetVariable( pVariableName, 0.0f );
+ m_stack.Push( 0.0f );
+ return true;
+}
+
+/*
+dtor(d) : converts degrees to radians
+rtod(r) : converts radians to degrees
+
+abs(a) : absolute value
+floor(a) : rounds down to the nearest integer
+ceiling(a) : rounds up to the nearest integer
+round(a) : rounds to the nearest integer
+sgn(a) : if a < 0 returns -1 else 1
+sqr(a) : returns a * a
+sqrt(a) : returns sqrt(a)
+
+sin(a) : sin(a), a is in degrees
+asin(a) : asin(a) returns degrees
+cos(a) : cos(a), a is in degrees
+acos(a) : acos(a) returns degrees
+tan(a) : tan(a), a is in degrees
+
+exp(a) : returns the exponential function of a
+log(a) : returns the natural logaritm of a
+*/
+bool CExpressionCalculator::Parse1ArgFunc( const char *&expr )
+{
+ ParseState_t ps( m_stack, expr );
+
+ const char *pFuncs[] =
+ {
+ "abs", "sqr", "sqrt", "sin", "asin", "cos", "acos", "tan",
+ "exp", "log", "dtor", "rtod", "floor", "ceiling", "round", "sign"
+ };
+ int nFunc = 16;
+
+ if ( ParseStringList( expr, pFuncs, nFunc ) &&
+ ParseString( expr, "(" ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, ")" ) )
+ {
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nFunc )
+ {
+ case 0: // abs
+ m_stack.Push( fabs( f1 ) );
+ break;
+ case 1: // sqr
+ m_stack.Push( f1 * f1 );
+ break;
+ case 2: // sqrt
+ m_stack.Push( sqrt( f1 ) );
+ break;
+ case 3: // sin
+ m_stack.Push( sin( f1 ) );
+ break;
+ case 4: // asin
+ m_stack.Push( asin( f1 ) );
+ break;
+ case 5: // cos
+ m_stack.Push( cos( f1 ) );
+ break;
+ case 6: // acos
+ m_stack.Push( acos( f1 ) );
+ break;
+ case 7: // tan
+ m_stack.Push( tan( f1 ) );
+ break;
+ case 8: // exp
+ m_stack.Push( exp( f1 ) );
+ break;
+ case 9: // log
+ m_stack.Push( log( f1 ) );
+ break;
+ case 10: // dtor
+ m_stack.Push( DEG2RAD( f1 ) );
+ break;
+ case 11: // rtod
+ m_stack.Push( RAD2DEG( f1 ) );
+ break;
+ case 12: // floor
+ m_stack.Push( floor( f1 ) );
+ break;
+ case 13: // ceiling
+ m_stack.Push( ceil( f1 ) );
+ break;
+ case 14: // round
+ m_stack.Push( floor( f1 + 0.5f ) );
+ break;
+ case 15: // sign
+ m_stack.Push( f1 >= 0.0f ? 1.0f : -1.0f );
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+/*
+min(a,b) : if a<b returns a else b
+max(a,b) : if a>b returns a else b
+atan2(a,b) : atan2(a/b) returns degrees
+pow(a,b) : function returns a raised to the power of b
+*/
+bool CExpressionCalculator::Parse2ArgFunc( const char *&expr )
+{
+ ParseState_t ps( m_stack, expr );
+
+ const char *pFuncs[] = { "min", "max", "atan2", "pow" };
+ int nFunc = 4;
+
+ if ( ParseStringList( expr, pFuncs, nFunc ) &&
+ ParseString( expr, "(" ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, "," ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, ")" ) )
+ {
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nFunc )
+ {
+ case 0: // min
+ m_stack.Push( min( f1, f2 ) );
+ break;
+ case 1: // max
+ m_stack.Push( max( f1, f2 ) );
+ break;
+ case 2: // atan2
+ m_stack.Push( atan2( f1, f2 ) );
+ break;
+ case 3: // pow
+ m_stack.Push( pow( f1, f2 ) );
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+/*
+inrange(x,a,b) : if x is between a and b, returns 1 else returns 0
+clamp(x,a,b) : see bound() above
+
+ramp(value,a,b) : returns 0 -> 1 as value goes from a to b
+lerp(factor,a,b) : returns a -> b as value goes from 0 to 1
+
+cramp(value,a,b) : clamp(ramp(value,a,b),0,1)
+clerp(factor,a,b) : clamp(lerp(factor,a,b),a,b)
+
+elerp(x,a,b) : ramp( 3*x*x - 2*x*x*x, a, b)
+//elerp(factor,a,b) : lerp(lerp(sind(clerp(factor,-90,90)),0.5,1.0),a,b)
+
+noise(a,b,c) : { solid noise pattern (improved perlin noise) indexed with three numbers }
+*/
+
+float ramp( float x, float a, float b )
+{
+ return ( x - a ) / ( b - a );
+}
+
+float lerp( float x, float a, float b )
+{
+ return a + x * ( b - a );
+}
+
+float smoothstep( float x )
+{
+ return 3*x*x - 2*x*x*x;
+}
+
+bool CExpressionCalculator::Parse3ArgFunc( const char *&expr )
+{
+ ParseState_t ps( m_stack, expr );
+
+ const char *pFuncs[] = { "inrange", "clamp", "ramp", "lerp", "cramp", "clerp", "elerp", "noise" };
+ int nFunc = 8;
+
+ if ( ParseStringList( expr, pFuncs, nFunc ) &&
+ ParseString( expr, "(" ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, "," ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, "," ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, ")" ) )
+ {
+ float f3 = m_stack.Top();
+ m_stack.Pop();
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nFunc )
+ {
+ case 0: // inrange
+ m_stack.Push( ( f1 >= f2 ) && ( f1 <= f3 ) ? 1.0f : 0.0f );
+ break;
+ case 1: // clamp
+ m_stack.Push( clamp( f1, f2, f3 ) );
+ break;
+ case 2: // ramp
+ m_stack.Push( ramp( f1, f2, f3 ) );
+ break;
+ case 3: // lerp
+ m_stack.Push( lerp( f1, f2, f3 ) );
+ break;
+ case 4: // cramp
+ m_stack.Push( clamp( ramp( f1, f2, f3 ), 0, 1 ) );
+ break;
+ case 5: // clerp
+ m_stack.Push( clamp( lerp( f1, f2, f3 ), f2, f3 ) );
+ break;
+ case 6: // elerp
+ m_stack.Push( lerp( smoothstep( f1 ), f2, f3 ) );
+ break;
+ case 7: // noise
+ m_stack.Push( ImprovedPerlinNoise( Vector( f1, f2, f3 ) ) );
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+//bool CExpressionCalculator::Parse4ArgFunc( const char *&expr );
+
+/*
+rescale (X,Xa,Xb,Ya,Yb) : lerp(ramp(X,Xa,Xb),Ya,Yb)
+crescale(X,Xa,Xb,Ya,Yb) : clamp(rescale(X,Xa,Xb,Ya,Yb),Ya,Yb)
+*/
+float rescale( float x, float a, float b, float c, float d )
+{
+ return lerp( ramp( x, a, b ), c, d );
+}
+
+bool CExpressionCalculator::Parse5ArgFunc( const char *&expr )
+{
+ ParseState_t ps( m_stack, expr );
+
+ const char *pFuncs[] = { "rescale", "crescale" };
+ int nFunc = 2;
+
+ if ( ParseStringList( expr, pFuncs, nFunc ) &&
+ ParseString( expr, "(" ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, "," ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, "," ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, "," ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, "," ) &&
+ ParseExpr( expr ) &&
+ ParseString( expr, ")" ) )
+ {
+ float f5 = m_stack.Top();
+ m_stack.Pop();
+ float f4 = m_stack.Top();
+ m_stack.Pop();
+ float f3 = m_stack.Top();
+ m_stack.Pop();
+ float f2 = m_stack.Top();
+ m_stack.Pop();
+ float f1 = m_stack.Top();
+ m_stack.Pop();
+ switch ( nFunc )
+ {
+ case 0: // rescale
+ m_stack.Push( rescale( f1, f2, f3, f4, f5 ) );
+ break;
+ case 1: // crescale
+ m_stack.Push( clamp( rescale( f1, f2, f3, f4, f5 ), f4, f5 ) );
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+void TestCalculator( const char *expr, float answer )
+{
+ CExpressionCalculator calc( expr );
+ float result = 0.0f;
+
+#ifdef DBGFLAG_ASSERT
+ bool success =
+#endif
+ calc.Evaluate( result );
+ Assert( success && ( result == answer ) );
+}
+
+void TestCalculator( const char *expr, float answer, const char *var, float value )
+{
+ CExpressionCalculator calc( expr );
+ calc.SetVariable( var, value );
+ float result = 0.0f;
+
+#ifdef DBGFLAG_ASSERT
+ bool success =
+#endif
+ calc.Evaluate( result );
+ Assert( success && ( result == answer ) );
+}
+
+void TestCalculator()
+{
+// TestCalculator( "-1", 1 );
+ TestCalculator( "2 * 3 + 4", 10 );
+ TestCalculator( "2 + 3 * 4", 14 );
+ TestCalculator( "2 * 3 * 4", 24 );
+ TestCalculator( "2 * -3 + 4", -2 );
+ TestCalculator( "12.0 / 2.0", 6 );
+ TestCalculator( "(2*3)+4", 10 );
+ TestCalculator( "( 1 + 2 ) / (1+2)", 1 );
+ TestCalculator( "(((5)))", 5 );
+ TestCalculator( "--5", 5 );
+ TestCalculator( "3.5 % 2", 1.5 );
+ TestCalculator( "1e-2", 0.01 );
+ TestCalculator( "9 == ( 3 * ( 1 + 2 ) )", 1 );
+ TestCalculator( "9 != ( 3 * ( 1 + 2 ) )", 0 );
+ TestCalculator( "9 <= ( 3 * ( 1 + 2 ) )", 1 );
+ TestCalculator( "9 < ( 3 * ( 1 + 2 ) )", 0 );
+ TestCalculator( "9 < 3", 0 );
+ TestCalculator( "10 >= 5", 1 );
+// TestCalculator( "9 < ( 3 * ( 2 + 2 ) )", 0 );
+ TestCalculator( "x + 1", 5, "x", 4 );
+ TestCalculator( "pi - 3.14159", 0, "pi", 3.14159 );
+// TestCalculator( "pi / 2", 0, "pi", 3.14159 );
+ TestCalculator( "abs(-10)", 10 );
+ TestCalculator( "sqr(-5)", 25 );
+ TestCalculator( "sqrt(9)", 3 );
+// TestCalculator( "sqrt(-9)", -3 );
+ TestCalculator( "pow(2,3)", 8 );
+ TestCalculator( "min(abs(-4),2+3/2)", 3.5 );
+ TestCalculator( "round(0.5)", 1 );
+ TestCalculator( "round(0.49)", 0 );
+ TestCalculator( "round(-0.5)", 0 );
+ TestCalculator( "round(-0.51)", -1 );
+ TestCalculator( "inrange( 5, -8, 10 )", 1 );
+ TestCalculator( "inrange( 5, 5, 10 )", 1 );
+ TestCalculator( "inrange( 5, 6, 10 )", 0 );
+ TestCalculator( "elerp( 1/4, 0, 1 )", 3/16.0f - 1/32.0f );
+ TestCalculator( "rescale( 0.5, -1, 1, 0, 100 )", 75 );
+ TestCalculator( "1 > 2 ? 6 : 9", 9 );
+ TestCalculator( "1 ? 1 ? 2 : 4 : 1 ? 6 : 8", 2 );
+ TestCalculator( "0 ? 1 ? 2 : 4 : 1 ? 6 : 8", 6 );
+ TestCalculator( "noise( 0.123, 4.56, 78.9 )", ImprovedPerlinNoise( Vector( 0.123, 4.56, 78.9 ) ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeExpressionOperator, CDmeExpressionOperator );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeExpressionOperator::OnConstruction()
+{
+ m_result.Init( this, "result" );
+ m_expr.Init( this, "expr" );
+ m_bSpewResult.Init( this, "spewresult" );
+
+#ifdef _DEBUG
+ TestCalculator();
+#endif _DEBUG
+}
+
+void CDmeExpressionOperator::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CDmeExpressionOperator::IsInputAttribute( CDmAttribute *pAttribute )
+{
+ const char *pName = pAttribute->GetName( );
+#if 0 // skip this test, since none of these are float attributes, but leave the code as a reminder
+ if ( Q_strcmp( pName, "name" ) == 0 )
+ return false;
+
+ if ( Q_strcmp( pName, "expr" ) == 0 )
+ return false;
+#endif
+
+ if ( Q_strcmp( pName, "result" ) == 0 )
+ return false;
+
+ if ( pAttribute->GetType() != AT_FLOAT )
+ return false;
+
+ return true;
+}
+
+void CDmeExpressionOperator::Operate()
+{
+ CExpressionCalculator calc( m_expr.Get() );
+
+ for ( CDmAttribute *pAttribute = FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( IsInputAttribute( pAttribute ) )
+ {
+ const char *pName = pAttribute->GetName( );
+ calc.SetVariable( pName, pAttribute->GetValue< float >() );
+ }
+ }
+
+ float oldValue = m_result;
+
+ calc.Evaluate( oldValue );
+
+ m_result = oldValue;
+
+ if ( m_bSpewResult )
+ {
+ Msg( "%s = '%f'\n", GetName(), (float)m_result );
+ }
+}
+
+void CDmeExpressionOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ for ( CDmAttribute *pAttribute = FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ if ( IsInputAttribute( pAttribute ) )
+ {
+ attrs.AddToTail( pAttribute );
+ }
+ }
+
+ attrs.AddToTail( m_expr.GetAttribute() );
+}
+
+void CDmeExpressionOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_result.GetAttribute() );
+}
+
+
+void CDmeExpressionOperator::SetSpewResult( bool state )
+{
+ m_bSpewResult = state;
+}
diff --git a/movieobjects/dmeeyeball.cpp b/movieobjects/dmeeyeball.cpp
new file mode 100644
index 0000000..8d38c1d
--- /dev/null
+++ b/movieobjects/dmeeyeball.cpp
@@ -0,0 +1,48 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=============================================================================
+
+
+// Valve includes
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmeeyeball.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeEyeball, CDmeEyeball );
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeEyeball::OnConstruction()
+{
+ m_flDiameter.InitAndSet( this, "diameter", 1.0 );
+ m_flYawAngle.InitAndSet( this, "angle", 2.0 );
+ m_flPupilScale.InitAndSet( this, "pupilScale", 1.0 );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeEyeball::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the model relative position of the eyeball Position
+//-----------------------------------------------------------------------------
+void CDmeEyeball::GetWorldPosition( Vector &worldPosition )
+{
+ matrix3x4_t mWorld;
+ GetShapeToWorldTransform( mWorld );
+
+ MatrixPosition( mWorld, worldPosition );
+}
diff --git a/movieobjects/dmeeyeposition.cpp b/movieobjects/dmeeyeposition.cpp
new file mode 100644
index 0000000..1cd86dd
--- /dev/null
+++ b/movieobjects/dmeeyeposition.cpp
@@ -0,0 +1,46 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=============================================================================
+
+
+// Valve includes
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmeeyeposition.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeEyePosition, CDmeEyePosition );
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeEyePosition::OnConstruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeEyePosition::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the model relative position of the eyePosition
+//-----------------------------------------------------------------------------
+void CDmeEyePosition::GetWorldPosition( Vector &worldPosition )
+{
+ matrix3x4_t mWorld;
+ GetShapeToWorldTransform( mWorld );
+
+ MatrixPosition( mWorld, worldPosition );
+}
diff --git a/movieobjects/dmefaceset.cpp b/movieobjects/dmefaceset.cpp
new file mode 100644
index 0000000..5fecdfd
--- /dev/null
+++ b/movieobjects/dmefaceset.cpp
@@ -0,0 +1,187 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmefaceset.h"
+#include "movieobjects/dmematerial.h"
+#include "tier0/dbg.h"
+#include "UtlBuffer.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeFaceSet, CDmeFaceSet );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeFaceSet::OnConstruction()
+{
+ m_indices.Init( this, "faces" );
+ m_material.Init( this, "material" );
+}
+
+void CDmeFaceSet::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+CDmeMaterial *CDmeFaceSet::GetMaterial()
+{
+ return m_material.GetElement();
+}
+
+void CDmeFaceSet::SetMaterial( CDmeMaterial *pMaterial )
+{
+ m_material = pMaterial;
+}
+
+int CDmeFaceSet::AddIndices( int nCount )
+{
+ int nCurrentCount = m_indices.Count();
+ m_indices.EnsureCount( nCount + nCurrentCount );
+ return nCurrentCount;
+}
+
+void CDmeFaceSet::SetIndices( int nFirstIndex, int nCount, int *pIndices )
+{
+ m_indices.SetMultiple( nFirstIndex, nCount, pIndices );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeFaceSet::SetIndex( int i, int nValue )
+{
+ m_indices.Set( i, nValue );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of triangulated indices
+//-----------------------------------------------------------------------------
+int CDmeFaceSet::GetNextPolygonVertexCount( int nFirstIndex ) const
+{
+ int nCurrIndex = nFirstIndex;
+ int nTotalCount = m_indices.Count();
+ while( nCurrIndex < nTotalCount )
+ {
+ if ( m_indices[nCurrIndex] == -1 )
+ break;
+ ++nCurrIndex;
+ }
+
+ return nCurrIndex - nFirstIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of triangulated indices total
+//-----------------------------------------------------------------------------
+int CDmeFaceSet::GetTriangulatedIndexCount() const
+{
+ int nIndexCount = 0;
+ int nVertexCount = 0;
+ int nTotalCount = m_indices.Count();
+ for ( int nCurrIndex = 0; nCurrIndex < nTotalCount; ++nCurrIndex )
+ {
+ if ( m_indices[nCurrIndex] == -1 )
+ {
+ if ( nVertexCount >= 3 )
+ {
+ nIndexCount += ( nVertexCount - 2 ) * 3;
+ }
+ nVertexCount = 0;
+ continue;
+ }
+
+ ++nVertexCount;
+ }
+
+ if ( nVertexCount >= 3 )
+ {
+ nIndexCount += ( nVertexCount - 2 ) * 3;
+ }
+
+ return nIndexCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of indices total
+//-----------------------------------------------------------------------------
+int CDmeFaceSet::GetIndexCount() const
+{
+ int nIndexCount = 0;
+ int nVertexCount = 0;
+ int nTotalCount = m_indices.Count();
+
+ for ( int nCurrIndex = 0; nCurrIndex < nTotalCount; ++nCurrIndex )
+ {
+ if ( m_indices[nCurrIndex] == -1 )
+ {
+ nIndexCount += nVertexCount;
+ nVertexCount = 0;
+ continue;
+ }
+
+ ++nVertexCount;
+ }
+
+ nIndexCount += nVertexCount;
+
+ return nIndexCount;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeFaceSet::RemoveMultiple( int elem, int num )
+{
+ m_indices.RemoveMultiple( elem, num );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of faces in the face set
+//-----------------------------------------------------------------------------
+int CDmeFaceSet::GetFaceCount() const
+{
+ int nFaceCount = 0;
+ int nVertexCount = 0;
+
+ const int nIndexCount = NumIndices();
+ for ( int i = 0; i < nIndexCount; ++i )
+ {
+ if ( GetIndex( i ) < 0 )
+ {
+ if ( nVertexCount > 0 )
+ {
+ ++nFaceCount;
+ }
+ nVertexCount = 0;
+ continue;
+ }
+
+ ++nVertexCount;
+ }
+
+ if ( nVertexCount > 0 )
+ {
+ ++nFaceCount;
+ }
+
+ return nFaceCount;
+} \ No newline at end of file
diff --git a/movieobjects/dmegamemodel.cpp b/movieobjects/dmegamemodel.cpp
new file mode 100644
index 0000000..324f744
--- /dev/null
+++ b/movieobjects/dmegamemodel.cpp
@@ -0,0 +1,694 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Dme version of a game model (MDL)
+//
+//=============================================================================
+#include "movieobjects/dmegamemodel.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datacache/imdlcache.h"
+#include "studio.h"
+#include "tier3/tier3.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeGlobalFlexControllerOperator, CDmeGlobalFlexControllerOperator );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeGlobalFlexControllerOperator::OnConstruction()
+{
+ m_flexWeight.Init( this, "flexWeight" );
+ m_gameModel.Init( this, "gameModel", FATTRIB_HAS_CALLBACK );
+
+ m_ToAttributeHandle = DMATTRIBUTE_HANDLE_INVALID;
+
+ m_nFlexControllerIndex = -1;
+}
+
+void CDmeGlobalFlexControllerOperator::OnDestruction()
+{
+}
+
+void CDmeGlobalFlexControllerOperator::Resolve()
+{
+ if ( m_nFlexControllerIndex < 0 )
+ {
+ m_nFlexControllerIndex = FindGlobalFlexControllerIndex();
+ }
+}
+
+void CDmeGlobalFlexControllerOperator::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ // Don't have the required interface...
+ if ( !g_pGlobalFlexController )
+ return;
+
+ if ( pAttribute == m_gameModel.GetAttribute() && m_gameModel.GetElement() )
+ {
+ m_nFlexControllerIndex = FindGlobalFlexControllerIndex();
+
+ SetupToAttribute();
+ }
+}
+
+void CDmeGlobalFlexControllerOperator::Operate()
+{
+ CDmAttribute *pToAttr = g_pDataModel->GetAttribute( m_ToAttributeHandle );
+ if ( !pToAttr )
+ return;
+
+ DmAttributeType_t type = m_flexWeight.GetAttribute()->GetType();
+
+ const void *pValue = m_flexWeight.GetAttribute()->GetValueUntyped();
+ if ( IsArrayType( pToAttr->GetType() ) )
+ {
+ if ( m_nFlexControllerIndex == -1 )
+ return;
+
+ CDmrGenericArray array( pToAttr );
+ array.Set( m_nFlexControllerIndex, type, pValue );
+ }
+ else
+ {
+ pToAttr->SetValue( type, pValue );
+ }
+}
+
+void CDmeGlobalFlexControllerOperator::SetGameModel( CDmeGameModel *gameModel )
+{
+ m_gameModel = gameModel;
+}
+
+void CDmeGlobalFlexControllerOperator::SetWeight( float flWeight )
+{
+ m_flexWeight = flWeight;
+}
+
+void CDmeGlobalFlexControllerOperator::SetMapping( int globalIndex )
+{
+ m_nFlexControllerIndex = globalIndex;
+ if ( m_gameModel.GetElement() )
+ {
+ if ( (uint)globalIndex >= m_gameModel->NumFlexWeights() )
+ {
+ m_gameModel->SetNumFlexWeights( (uint)( globalIndex + 1 ) );
+ }
+ }
+}
+
+int CDmeGlobalFlexControllerOperator::GetGlobalIndex() const
+{
+ return m_nFlexControllerIndex;
+}
+
+void CDmeGlobalFlexControllerOperator::GetInputAttributes ( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_flexWeight.GetAttribute() );
+}
+
+void CDmeGlobalFlexControllerOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ CDmAttribute *toAttribute = g_pDataModel->GetAttribute( m_ToAttributeHandle );
+ if ( toAttribute )
+ {
+ attrs.AddToTail( toAttribute );
+ }
+}
+
+void CDmeGlobalFlexControllerOperator::SetupToAttribute()
+{
+ CDmElement *pObject = m_gameModel.GetElement();
+ if ( pObject == NULL)
+ return;
+
+ CDmAttribute *pAttr = pObject->GetAttribute( "flexWeights" );
+ Assert( pAttr );
+ if ( !pAttr )
+ return;
+
+ m_ToAttributeHandle = pAttr->GetHandle();
+ return;
+}
+
+
+//-----------------------------------------------------------------------------
+// Connect up stuff by index
+//-----------------------------------------------------------------------------
+int CDmeGlobalFlexControllerOperator::FindGlobalFlexControllerIndex() const
+{
+ int nGlobalFlexControllerIndex = -1;
+
+ const char *pModelName = m_gameModel->GetModelName();
+ MDLHandle_t h = pModelName && pModelName[0] ? g_pMDLCache->FindMDL( pModelName ) : MDLHANDLE_INVALID;
+ if ( h != MDLHANDLE_INVALID )
+ {
+ studiohdr_t *hdr = g_pMDLCache->GetStudioHdr( h );
+ Assert( hdr );
+ if ( hdr )
+ {
+ int fc = hdr->numflexcontrollers;
+ for ( LocalFlexController_t i = LocalFlexController_t(0) ; i < fc; ++i )
+ {
+ mstudioflexcontroller_t *flex = hdr->pFlexcontroller( i );
+ if ( flex->localToGlobal == -1 )
+ {
+ flex->localToGlobal = g_pGlobalFlexController->FindGlobalFlexController( flex->pszName() );
+ }
+
+ if ( !Q_stricmp( flex->pszName(), GetName() ) )
+ {
+ nGlobalFlexControllerIndex = flex->localToGlobal;
+ // Grow the array
+ if ( (uint)flex->localToGlobal >= m_gameModel->NumFlexWeights() )
+ {
+ m_gameModel->SetNumFlexWeights( (uint)( flex->localToGlobal + 1 ) );
+ }
+ break;
+ }
+ }
+ }
+
+ g_pMDLCache->Release( h );
+ }
+
+ return nGlobalFlexControllerIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeGameModel, CDmeGameModel );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeGameModel::OnConstruction()
+{
+ m_flexWeights.Init( this, "flexWeights" );
+ m_viewTarget.Init( this, "viewTarget" );
+ m_modelName.Init( this, "modelName", FATTRIB_HAS_CALLBACK );
+ m_skin.Init( this, "skin" );
+ m_body.Init( this, "body" );
+ m_sequence.Init( this, "sequence" );
+ m_flags.Init( this, "flags" );
+ m_bones.Init( this, "bones" );
+ m_globalFlexControllers.Init( this, "globalFlexControllers" );
+ m_bComputeBounds.Init( this, "computeBounds" );
+}
+
+void CDmeGameModel::OnDestruction()
+{
+}
+
+CDmeGlobalFlexControllerOperator *CDmeGameModel::AddGlobalFlexController( const char *controllerName, int globalIndex )
+{
+ int i, c;
+ c = m_globalFlexControllers.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeGlobalFlexControllerOperator *op = m_globalFlexControllers.Get( i );
+ Assert( op );
+ if ( op && !Q_stricmp( op->GetName(), controllerName ) )
+ break;
+ }
+
+ if ( i >= c )
+ {
+ CDmeGlobalFlexControllerOperator *newOperator = CreateElement< CDmeGlobalFlexControllerOperator >( controllerName, GetFileId() );
+ Assert( newOperator );
+ if ( !newOperator )
+ return NULL;
+
+ i = m_globalFlexControllers.AddToTail( newOperator );
+ }
+
+ Assert( m_globalFlexControllers.IsValidIndex( i ) );
+ CDmeGlobalFlexControllerOperator *op = m_globalFlexControllers.Get( i );
+ Assert( op );
+ if ( op )
+ {
+ op->SetMapping( globalIndex );
+ op->SetGameModel( this );
+ }
+
+ if ( (uint)globalIndex >= NumFlexWeights() )
+ {
+ SetNumFlexWeights( globalIndex + 1 );
+ }
+
+ return op;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find a flex controller by its global index
+//-----------------------------------------------------------------------------
+CDmeGlobalFlexControllerOperator *CDmeGameModel::FindGlobalFlexController( int nGlobalIndex )
+{
+ int i, c;
+ c = m_globalFlexControllers.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ CDmeGlobalFlexControllerOperator *op = m_globalFlexControllers.Get( i );
+ Assert( op );
+ if ( op && op->GetGlobalIndex() == nGlobalIndex )
+ return op;
+ }
+
+ return NULL;
+}
+
+studiohdr_t* CDmeGameModel::GetStudioHdr() const
+{
+ const char *pModelName = GetModelName();
+ MDLHandle_t h = pModelName && pModelName[0] ? g_pMDLCache->FindMDL( pModelName ) : MDLHANDLE_INVALID;
+ return ( h != MDLHANDLE_INVALID ) ? g_pMDLCache->GetStudioHdr( h ) : NULL;
+}
+
+
+// A src bone transform transforms pre-compiled data (.dmx or .smd files, for example)
+// into post-compiled data (.mdl or .ani files)
+bool CDmeGameModel::GetSrcBoneTransforms( matrix3x4_t *pPreTransform, matrix3x4_t *pPostTransform, int nBoneIndex ) const
+{
+ studiohdr_t *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ if ( pStudioHdr->numbones <= nBoneIndex )
+ return false;
+
+ const char *pBoneName = pStudioHdr->pBone( nBoneIndex )->pszName();
+ int nCount = pStudioHdr->NumSrcBoneTransforms();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const mstudiosrcbonetransform_t *pSrcTransform = pStudioHdr->SrcBoneTransform( i );
+ if ( Q_stricmp( pSrcTransform->pszName(), pBoneName ) )
+ continue;
+
+ MatrixCopy( pSrcTransform->pretransform, *pPreTransform );
+ MatrixCopy( pSrcTransform->posttransform, *pPostTransform );
+ return true;
+ }
+ return false;
+}
+
+bool CDmeGameModel::IsRootTransform( int nBoneIndex ) const
+{
+ studiohdr_t *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return true;
+
+ if ( pStudioHdr->numbones <= nBoneIndex )
+ return true;
+
+ mstudiobone_t *pBone = pStudioHdr->pBone( nBoneIndex );
+ return pBone->parent == -1;
+}
+
+int CDmeGameModel::NumGlobalFlexControllers() const
+{
+ return m_globalFlexControllers.Count();
+}
+
+CDmeGlobalFlexControllerOperator *CDmeGameModel::GetGlobalFlexController( int localIndex )
+{
+ return m_globalFlexControllers.Get( localIndex );
+}
+
+void CDmeGameModel::RemoveGlobalFlexController( CDmeGlobalFlexControllerOperator *controller )
+{
+ int c = m_globalFlexControllers.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeGlobalFlexControllerOperator *check = m_globalFlexControllers.Get( i );
+ if ( check == controller )
+ {
+ m_globalFlexControllers.Remove( i );
+ break;
+ }
+ }
+}
+
+void CDmeGameModel::AppendGlobalFlexControllerOperators( CUtlVector< IDmeOperator * >& list )
+{
+ int c = m_globalFlexControllers.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CDmeOperator *op = m_globalFlexControllers.Get( i );
+ if ( !op )
+ continue;
+ list.AddToTail( op );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+void CDmeGameModel::AddBone( CDmeTransform* pTransform )
+{
+ m_bones.AddToTail( pTransform );
+}
+
+
+//-----------------------------------------------------------------------------
+// Is this dag under the game model?
+//-----------------------------------------------------------------------------
+static bool IsDagUnderGameModel( CDmeDag *pDag, CDmeGameModel *pGameModel )
+{
+ if ( pDag == pGameModel )
+ return true;
+
+ DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pDag->GetHandle() );
+ while ( i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
+ {
+ CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
+ CDmElement *pDmeParent = pAttribute->GetOwner();
+ const static UtlSymId_t symChildren = g_pDataModel->GetSymbol( "children" );
+ if ( pDmeParent && pAttribute->GetNameSymbol() == symChildren )
+ {
+ CDmeDag *pParent = CastElement< CDmeDag >( pDmeParent );
+ if ( pParent && ( pParent->GetFileId() == pDag->GetFileId() ) )
+ {
+ if ( IsDagUnderGameModel( pParent, pGameModel ) )
+ return true;
+ }
+ }
+ i = g_pDataModel->NextAttributeReferencingElement( i );
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is this dag under the game model?
+//-----------------------------------------------------------------------------
+static CDmeDag* GetDagForTransform( CDmeTransform *pTransform, CDmeGameModel *pGameModel )
+{
+ DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pTransform->GetHandle() );
+ while ( i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
+ {
+ CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
+ CDmElement *pDmeParent = pAttribute->GetOwner();
+ const static UtlSymId_t symTransform = g_pDataModel->GetSymbol( "transform" );
+ if ( pDmeParent && pAttribute->GetNameSymbol() == symTransform )
+ {
+ CDmeDag *pParent = CastElement< CDmeDag >( pDmeParent );
+ if ( pParent && ( pParent->GetFileId() == pTransform->GetFileId() ) )
+ {
+ if ( IsDagUnderGameModel( pParent, pGameModel ) )
+ return pParent;
+ }
+ }
+ i = g_pDataModel->NextAttributeReferencingElement( i );
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds existing dags
+//-----------------------------------------------------------------------------
+void CDmeGameModel::PopulateExistingDagList( CDmeDag** pDags, int nCount )
+{
+ int nCurrentBoneCount = m_bones.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( i >= nCurrentBoneCount )
+ {
+ pDags[ i ] = NULL;
+ continue;
+ }
+
+ CDmeTransform *pTransform = GetBone( i );
+ Assert( pTransform );
+ pDags[ i ] = pTransform ? GetDagForTransform( pTransform, this ) : NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds bones to the game model
+//-----------------------------------------------------------------------------
+void CDmeGameModel::AddBones( studiohdr_t *pStudioHdr, const char *pBaseName, int nFirstBone, int nCount )
+{
+ if ( nFirstBone + nCount > pStudioHdr->numbones )
+ {
+ nCount = pStudioHdr->numbones - nFirstBone;
+ if ( nCount <= 0 )
+ return;
+ }
+
+ // make room for bones
+ CDmeDag** pDags = ( CDmeDag** )_alloca( pStudioHdr->numbones * sizeof(CDmeDag*) );
+ int nDagCount = nFirstBone;
+ PopulateExistingDagList( pDags, nFirstBone );
+
+ char name[ 256 ];
+ for ( int i = 0; i < nCount; ++i )
+ {
+ int bi = i + nFirstBone;
+
+ // get parent
+ mstudiobone_t *pBone = pStudioHdr->pBone( bi );
+ int parentIndex = pBone->parent;
+ Assert( parentIndex < nDagCount );
+
+ // build dag hierarchy to match bone hierarchy
+ CDmeDag *pParent = ( parentIndex < 0 ) ? this : pDags[ parentIndex ];
+ Q_snprintf( name, sizeof( name ), "%s_bone %d (%s)", pBaseName, bi, pBone->pszName() );
+ CDmeDag *pDag = CreateElement< CDmeDag >( name, GetFileId() );
+ pDags[nDagCount++] = pDag;
+ pParent->AddChild( pDag );
+
+ CDmeTransform *pTransform = pDag->GetTransform();
+ pTransform->SetName( name );
+
+ // add different bone representations to dme model and input
+ AddBone( pTransform );
+ }
+}
+
+
+void CDmeGameModel::SetBone( uint index, const Vector& pos, const Quaternion& rot )
+{
+ m_bones[ index ]->SetPosition( pos );
+ m_bones[ index ]->SetOrientation( rot );
+}
+
+void CDmeGameModel::RemoveAllBones()
+{
+ m_bones.RemoveAll();
+}
+
+uint CDmeGameModel::NumBones() const
+{
+ return m_bones.Count();
+}
+
+CDmeTransform *CDmeGameModel::GetBone( uint index ) const
+{
+ return m_bones[ index ];
+}
+
+int CDmeGameModel::FindBone( CDmeTransform *pTransform ) const
+{
+ return m_bones.Find( pTransform );
+}
+
+uint CDmeGameModel::NumFlexWeights() const
+{
+ return m_flexWeights.Count();
+}
+
+const CUtlVector< float >& CDmeGameModel::GetFlexWeights() const
+{
+ return m_flexWeights.Get();
+}
+
+void CDmeGameModel::SetNumFlexWeights( uint nFlexWeights )
+{
+ if ( nFlexWeights > (uint)m_flexWeights.Count() )
+ {
+ while ( (uint)m_flexWeights.Count() < nFlexWeights )
+ {
+ m_flexWeights.AddToTail( 0.0f );
+ }
+ }
+ else
+ {
+ while ( (uint)m_flexWeights.Count() > nFlexWeights )
+ {
+ m_flexWeights.Remove( (uint)m_flexWeights.Count() - 1 );
+ }
+ }
+}
+
+void CDmeGameModel::SetFlexWeights( uint nFlexWeights, const float* flexWeights )
+{
+ m_flexWeights.CopyArray( flexWeights, nFlexWeights );
+}
+
+const Vector& CDmeGameModel::GetViewTarget() const
+{
+ return m_viewTarget.Get();
+}
+
+void CDmeGameModel::SetViewTarget( const Vector &viewTarget )
+{
+ m_viewTarget = viewTarget;
+}
+
+void CDmeGameModel::SetFlags( int nFlags )
+{
+ m_flags = nFlags;
+}
+
+void CDmeGameModel::SetSkin( int nSkin )
+{
+ m_skin = nSkin;
+}
+
+void CDmeGameModel::SetBody( int nBody )
+{
+ m_body = nBody;
+}
+
+void CDmeGameModel::SetSequence( int nSequence )
+{
+ m_sequence = nSequence;
+}
+
+int CDmeGameModel::GetSkin() const
+{
+ return m_skin;
+}
+
+int CDmeGameModel::GetBody() const
+{
+ return m_body;
+}
+
+int CDmeGameModel::GetSequence() const
+{
+ return m_sequence;
+}
+
+const char *CDmeGameModel::GetModelName() const
+{
+ return m_modelName.Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeGameSprite, CDmeGameSprite );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeGameSprite::OnConstruction()
+{
+ m_modelName .Init( this, "modelName" );
+ m_frame .Init( this, "frame" );
+ m_rendermode.Init( this, "rendermode" );
+ m_renderfx .Init( this, "renderfx" );
+ m_renderscale.Init( this, "renderscale" );
+ m_color .Init( this, "color" );
+ m_proxyRadius.Init( this, "proxyRadius" );
+}
+
+void CDmeGameSprite::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+const char *CDmeGameSprite::GetModelName() const
+{
+ return m_modelName.Get();
+}
+
+float CDmeGameSprite::GetScale() const
+{
+ return m_renderscale;
+}
+
+float CDmeGameSprite::GetFrame() const
+{
+ return m_frame;
+}
+
+int CDmeGameSprite::GetRenderMode() const
+{
+ return m_rendermode;
+}
+
+int CDmeGameSprite::GetRenderFX() const
+{
+ return m_renderfx;
+}
+
+const Color &CDmeGameSprite::GetColor() const
+{
+ return m_color;
+}
+
+float CDmeGameSprite::GetProxyRadius() const
+{
+ return m_proxyRadius;
+}
+
+void CDmeGameSprite::SetState( bool bVisible, float nFrame, int nRenderMode, int nRenderFX, float flRenderScale, float flProxyRadius,
+ const Vector &pos, const Quaternion &rot, const Color &color )
+{
+ m_Visible = bVisible;
+ m_frame = nFrame;
+ m_rendermode = nRenderMode;
+ m_renderfx = nRenderFX;
+ m_renderscale = flRenderScale;
+ m_proxyRadius = flProxyRadius;
+ m_color = color;
+
+ CDmeTransform *pTransform = GetTransform();
+ pTransform->SetPosition( pos );
+ pTransform->SetOrientation( rot );
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeGamePortal, CDmeGamePortal );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeGamePortal::OnConstruction()
+{
+ m_flStaticAmount .Init( this, "staticAmount" );
+ m_flSecondaryStaticAmount .Init( this, "secondaryStaticAmount" );
+ m_flOpenAmount .Init( this, "openAmount" );
+ m_nPortalId .Init( this, "portalId" );
+ m_nLinkedPortalId .Init( this, "linkedPortalId" );
+ m_bIsPortal2 .Init( this, "isPortal2" );
+}
+
+void CDmeGamePortal::OnDestruction()
+{
+}
diff --git a/movieobjects/dmegamemodelinput.cpp b/movieobjects/dmegamemodelinput.cpp
new file mode 100644
index 0000000..903db2d
--- /dev/null
+++ b/movieobjects/dmegamemodelinput.cpp
@@ -0,0 +1,253 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// game model input - gets its values from an MDL within the game
+//
+//=============================================================================
+
+#include "movieobjects/dmegamemodelinput.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeGameModelInput, CDmeGameModelInput );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeGameModelInput::OnConstruction()
+{
+ m_skin.Init( this, "skin" );
+ m_body.Init( this, "body" );
+ m_sequence.Init( this, "sequence" );
+ m_visible.Init( this, "visible" );
+ m_flags.Init( this, "flags" );
+ m_flexWeights.Init( this, "flexWeights" );
+ m_viewTarget.Init( this, "viewTarget" );
+ m_bonePositions.Init( this, "bonePositions" );
+ m_boneRotations.Init( this, "boneRotations" );
+ m_position.Init( this, "position" );
+ m_rotation.Init( this, "rotation" );
+}
+
+void CDmeGameModelInput::OnDestruction()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Operator methods
+//-----------------------------------------------------------------------------
+bool CDmeGameModelInput::IsDirty()
+{
+ return true; // TODO - keep some bit of state that remembers when its changed
+}
+
+void CDmeGameModelInput::Operate()
+{
+}
+
+void CDmeGameModelInput::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_skin.GetAttribute() );
+ attrs.AddToTail( m_body.GetAttribute() );
+ attrs.AddToTail( m_sequence.GetAttribute() );
+ attrs.AddToTail( m_visible.GetAttribute() );
+ attrs.AddToTail( m_flags.GetAttribute() );
+ attrs.AddToTail( m_flexWeights.GetAttribute() );
+ attrs.AddToTail( m_viewTarget.GetAttribute() );
+ attrs.AddToTail( m_bonePositions.GetAttribute() );
+ attrs.AddToTail( m_boneRotations.GetAttribute() );
+ attrs.AddToTail( m_position.GetAttribute() );
+ attrs.AddToTail( m_rotation.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+void CDmeGameModelInput::SetFlags( int nFlags )
+{
+ m_flags = nFlags;
+}
+
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+void CDmeGameModelInput::AddBone( const Vector& pos, const Quaternion& rot )
+{
+ m_bonePositions.AddToTail( pos );
+ m_boneRotations.AddToTail( rot );
+}
+
+void CDmeGameModelInput::SetBone( uint index, const Vector& pos, const Quaternion& rot )
+{
+ m_bonePositions.Set( index, pos );
+ m_boneRotations.Set( index, rot );
+}
+
+void CDmeGameModelInput::SetRootBone( const Vector& pos, const Quaternion& rot )
+{
+ m_position.Set( pos );
+ m_rotation.Set( rot );
+}
+
+uint CDmeGameModelInput::NumBones() const
+{
+ Assert( m_bonePositions.Count() == m_boneRotations.Count() );
+ return m_bonePositions.Count();
+}
+
+void CDmeGameModelInput::SetFlexWeights( uint nFlexWeights, const float* flexWeights )
+{
+ m_flexWeights.CopyArray( flexWeights, nFlexWeights );
+}
+
+uint CDmeGameModelInput::NumFlexWeights() const
+{
+ return m_flexWeights.Count();
+}
+
+const Vector& CDmeGameModelInput::GetViewTarget() const
+{
+ return m_viewTarget;
+}
+
+void CDmeGameModelInput::SetViewTarget( const Vector &viewTarget )
+{
+ m_viewTarget = viewTarget;
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeGameSpriteInput, CDmeGameSpriteInput );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeGameSpriteInput::OnConstruction()
+{
+ m_visible .Init( this, "visible" );
+ m_frame .Init( this, "frame" );
+ m_rendermode .Init( this, "rendermode" );
+ m_renderfx .Init( this, "renderfx" );
+ m_renderscale.Init( this, "renderscale" );
+ m_proxyRadius.Init( this, "proxyRadius" );
+ m_position .Init( this, "position" );
+ m_rotation .Init( this, "rotation" );
+ m_color .Init( this, "color" );
+}
+
+void CDmeGameSpriteInput::OnDestruction()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Operator methods
+//-----------------------------------------------------------------------------
+bool CDmeGameSpriteInput::IsDirty()
+{
+ return true; // TODO - keep some bit of state that remembers when its changed
+}
+
+void CDmeGameSpriteInput::Operate()
+{
+}
+
+void CDmeGameSpriteInput::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_visible.GetAttribute() );
+ attrs.AddToTail( m_frame.GetAttribute() );
+ attrs.AddToTail( m_rendermode.GetAttribute() );
+ attrs.AddToTail( m_renderfx.GetAttribute() );
+ attrs.AddToTail( m_renderscale.GetAttribute() );
+ attrs.AddToTail( m_proxyRadius.GetAttribute() );
+ attrs.AddToTail( m_position.GetAttribute() );
+ attrs.AddToTail( m_rotation.GetAttribute() );
+ attrs.AddToTail( m_color.GetAttribute() );
+}
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+
+void CDmeGameSpriteInput::SetState( bool bVisible, float nFrame, int nRenderMode, int nRenderFX, float flRenderScale, float flProxyRadius,
+ const Vector &pos, const Quaternion &rot, const Color &color )
+{
+ m_visible = bVisible;
+ m_frame = nFrame;
+ m_rendermode = nRenderMode;
+ m_renderfx = nRenderFX;
+ m_renderscale = flRenderScale;
+ m_proxyRadius = flProxyRadius;
+ m_position = pos;
+ m_rotation = rot;
+ m_color = color;
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeGameCameraInput, CDmeGameCameraInput );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeGameCameraInput::OnConstruction()
+{
+ m_position.Init( this, "position" );
+ m_rotation.Init( this, "rotation" );
+ m_fov.Init( this, "fov" );
+}
+
+void CDmeGameCameraInput::OnDestruction()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Operator methods
+//-----------------------------------------------------------------------------
+bool CDmeGameCameraInput::IsDirty()
+{
+ return true; // TODO - keep some bit of state that remembers when its changed
+}
+
+void CDmeGameCameraInput::Operate()
+{
+}
+
+void CDmeGameCameraInput::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_position.GetAttribute() );
+ attrs.AddToTail( m_rotation.GetAttribute() );
+ attrs.AddToTail( m_fov.GetAttribute() );
+}
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+void CDmeGameCameraInput::SetPosition( const Vector& pos )
+{
+ m_position.Set( pos );
+}
+
+void CDmeGameCameraInput::SetOrientation( const Quaternion& rot )
+{
+ m_rotation.Set( rot );
+}
+
+void CDmeGameCameraInput::SetFOV( float fov )
+{
+ m_fov = fov;
+}
diff --git a/movieobjects/dmeimage.cpp b/movieobjects/dmeimage.cpp
new file mode 100644
index 0000000..2b972dc
--- /dev/null
+++ b/movieobjects/dmeimage.cpp
@@ -0,0 +1,45 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmeimage.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "bitmap/imageformat.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeImage, CDmeImage );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeImage::OnConstruction()
+{
+ m_Width.Init( this, "width" );
+ m_Height.Init( this, "height" );
+ m_Format.Init( this, "format" );
+ m_Bits.Init( this, "bits" );
+}
+
+void CDmeImage::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Image format
+//-----------------------------------------------------------------------------
+ImageFormat CDmeImage::Format() const
+{
+ return (ImageFormat)( m_Format.Get() );
+}
+
+const char *CDmeImage::FormatName() const
+{
+ return ImageLoader::GetName( Format() );
+}
+
diff --git a/movieobjects/dmeinput.cpp b/movieobjects/dmeinput.cpp
new file mode 100644
index 0000000..838082b
--- /dev/null
+++ b/movieobjects/dmeinput.cpp
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmeinput.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ABSTRACT_ELEMENT( DmeInput, CDmeInput );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeInput::OnConstruction()
+{
+}
+
+void CDmeInput::OnDestruction()
+{
+}
+
+//-----------------------------------------------------------------------------
+// IsDirty - ie needs to operate
+//-----------------------------------------------------------------------------
+bool CDmeInput::IsDirty()
+{
+ return true;
+}
diff --git a/movieobjects/dmejoint.cpp b/movieobjects/dmejoint.cpp
new file mode 100644
index 0000000..feb3f16
--- /dev/null
+++ b/movieobjects/dmejoint.cpp
@@ -0,0 +1,151 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Dme version of a joint of a skeletal model (gets compiled into a MDL)
+//
+//=============================================================================
+#include "movieobjects/dmejoint.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imesh.h"
+#include "tier1/KeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeJoint, CDmeJoint );
+
+
+//-----------------------------------------------------------------------------
+// Should I draw joints?
+//-----------------------------------------------------------------------------
+bool CDmeJoint::s_bDrawJoints = false;
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeJoint::OnConstruction()
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ KeyValues *pVMTKeyValues = new KeyValues( "wireframe" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ m_JointMaterial.Init( "__DmeJointMaterial", pVMTKeyValues );
+}
+
+void CDmeJoint::OnDestruction()
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ m_JointMaterial.Shutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Activate, deactivate joint drawing
+//-----------------------------------------------------------------------------
+void CDmeJoint::DrawJointHierarchy( bool bDrawJoints )
+{
+ s_bDrawJoints = bDrawJoints;
+}
+
+
+//-----------------------------------------------------------------------------
+// For rendering joints
+//-----------------------------------------------------------------------------
+#define AXIS_SIZE 3.0f
+
+void CDmeJoint::DrawJoints( )
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ int cn = GetChildCount();
+
+ // Draw the joint hierarchy
+ PushDagTransform();
+ matrix3x4_t shapeToWorld;
+ GetShapeToWorldTransform( shapeToWorld );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadMatrix( shapeToWorld );
+
+ pRenderContext->Bind( m_JointMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 + cn );
+
+ for ( int ci = 0; ci < cn; ++ci )
+ {
+ CDmeJoint *pJoint = CastElement<CDmeJoint>( GetChild( ci ) );
+ if ( !pJoint )
+ continue;
+
+ Vector vecChildPosition = pJoint->GetTransform()->GetPosition();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 128, 128, 128, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( vecChildPosition.Base() );
+ meshBuilder.Color4ub( 128, 128, 128, 255 );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( AXIS_SIZE, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, AXIS_SIZE, 0.0f );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, AXIS_SIZE );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity();
+
+ PopDagTransform();
+}
+
+
+//-----------------------------------------------------------------------------
+// Rendering method for the dag
+//-----------------------------------------------------------------------------
+void CDmeJoint::Draw( CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ if ( s_bDrawJoints && IsVisible() )
+ {
+ DrawJoints();
+ }
+
+ BaseClass::Draw( pDrawSettings );
+}
+
+
diff --git a/movieobjects/dmekeyboardinput.cpp b/movieobjects/dmekeyboardinput.cpp
new file mode 100644
index 0000000..0fcea71
--- /dev/null
+++ b/movieobjects/dmekeyboardinput.cpp
@@ -0,0 +1,141 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmekeyboardinput.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+#include "vgui/iinput.h"
+#include "vgui/keycode.h"
+#include "tier3/tier3.h"
+
+#include "tier0/dbg.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// global list of all keys supported
+//-----------------------------------------------------------------------------
+struct KeyInfo
+{
+ vgui::KeyCode code;
+ const char *str;
+};
+
+const uint g_nKeys = 48;
+const KeyInfo g_keyInfo[ g_nKeys ] =
+{
+ { KEY_0, "0" },
+ { KEY_1, "1" },
+ { KEY_2, "2" },
+ { KEY_3, "3" },
+ { KEY_4, "4" },
+ { KEY_5, "5" },
+ { KEY_6, "6" },
+ { KEY_7, "7" },
+ { KEY_8, "8" },
+ { KEY_9, "9" },
+ { KEY_A, "A" },
+ { KEY_B, "B" },
+ { KEY_C, "C" },
+ { KEY_D, "D" },
+ { KEY_E, "E" },
+ { KEY_F, "F" },
+ { KEY_G, "G" },
+ { KEY_H, "H" },
+ { KEY_I, "I" },
+ { KEY_J, "J" },
+ { KEY_K, "K" },
+ { KEY_L, "L" },
+ { KEY_M, "M" },
+ { KEY_N, "N" },
+ { KEY_O, "O" },
+ { KEY_P, "P" },
+ { KEY_Q, "Q" },
+ { KEY_R, "R" },
+ { KEY_S, "S" },
+ { KEY_T, "T" },
+ { KEY_U, "U" },
+ { KEY_V, "V" },
+ { KEY_W, "W" },
+ { KEY_X, "X" },
+ { KEY_Y, "Y" },
+ { KEY_Z, "Z" },
+ { KEY_F1, "F1" },
+ { KEY_F2, "F2" },
+ { KEY_F3, "F3" },
+ { KEY_F4, "F4" },
+ { KEY_F5, "F5" },
+ { KEY_F6, "F6" },
+ { KEY_F7, "F7" },
+ { KEY_F8, "F8" },
+ { KEY_F9, "F9" },
+ { KEY_F10, "F10" },
+ { KEY_F11, "F11" },
+ { KEY_F12, "F12" },
+};
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeKeyboardInput, CDmeKeyboardInput );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeKeyboardInput::OnConstruction()
+{
+ m_keys = new CDmaVar< bool >[ g_nKeys ];
+
+ for ( uint ki = 0; ki < g_nKeys; ++ki )
+ {
+ m_keys[ ki ].Init( this, g_keyInfo[ ki ].str );
+ }
+}
+
+void CDmeKeyboardInput::OnDestruction()
+{
+ delete[] m_keys;
+}
+
+bool CDmeKeyboardInput::IsDirty()
+{
+ for ( uint ki = 0; ki < g_nKeys; ++ki )
+ {
+ if ( m_keys[ ki ].Get() != GetKeyStatus( ki ) )
+ return true;
+ }
+ return false;
+}
+
+void CDmeKeyboardInput::Operate()
+{
+ for ( uint ki = 0; ki < g_nKeys; ++ki )
+ {
+ m_keys[ ki ].Set( GetKeyStatus( ki ) );
+ }
+}
+
+void CDmeKeyboardInput::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+}
+
+void CDmeKeyboardInput::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ for ( uint ki = 0; ki < g_nKeys; ++ki )
+ {
+ attrs.AddToTail( m_keys[ ki ].GetAttribute() );
+ }
+}
+
+bool CDmeKeyboardInput::GetKeyStatus( uint ki )
+{
+ return g_pVGuiInput->IsKeyDown( g_keyInfo[ ki ].code );
+}
diff --git a/movieobjects/dmelight.cpp b/movieobjects/dmelight.cpp
new file mode 100644
index 0000000..61de774
--- /dev/null
+++ b/movieobjects/dmelight.cpp
@@ -0,0 +1,331 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmelight.h"
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "mathlib/vector.h"
+#include "movieobjects/dmetransform.h"
+#include "materialsystem/imaterialsystem.h"
+#include "movieobjects_interfaces.h"
+#include "tier2/tier2.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeLight, CDmeLight );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeLight::OnConstruction()
+{
+ m_Color.InitAndSet( this, "color", Color( 255, 255, 255, 255 ), FATTRIB_HAS_CALLBACK );
+ m_flIntensity.InitAndSet( this, "intensity", 1.0f, FATTRIB_HAS_CALLBACK );
+}
+
+void CDmeLight::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the color and intensity
+// NOTE: Color is specified 0-255 floating point.
+//-----------------------------------------------------------------------------
+void CDmeLight::SetColor( const Color &color )
+{
+ m_Color.Set( color );
+}
+
+void CDmeLight::SetIntensity( float flIntensity )
+{
+ m_flIntensity = flIntensity;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up render state in the material system for rendering
+//-----------------------------------------------------------------------------
+void CDmeLight::SetupRenderStateInternal( LightDesc_t &desc, float flAtten0, float flAtten1, float flAtten2 )
+{
+ desc.m_Color[0] = m_Color.Get().r();
+ desc.m_Color[1] = m_Color.Get().g();
+ desc.m_Color[2] = m_Color.Get().b();
+ desc.m_Color *= m_flIntensity / 255.0f;
+
+ desc.m_Attenuation0 = flAtten0;
+ desc.m_Attenuation1 = flAtten1;
+ desc.m_Attenuation2 = flAtten2;
+
+ desc.m_Flags = 0;
+ if ( desc.m_Attenuation0 != 0.0f )
+ {
+ desc.m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0;
+ }
+ if ( desc.m_Attenuation1 != 0.0f )
+ {
+ desc.m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1;
+ }
+ if ( desc.m_Attenuation2 != 0.0f )
+ {
+ desc.m_Flags |= LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets lighting state
+//-----------------------------------------------------------------------------
+void CDmeLight::SetupRenderState( int nLightIndex )
+{
+ LightDesc_t desc;
+ if ( GetLightDesc( &desc ) )
+ {
+ // FIXME: Should we pass the light color in?
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->SetLight( nLightIndex, desc );
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// A directional light
+//
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeDirectionalLight, CDmeDirectionalLight );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeDirectionalLight::OnConstruction()
+{
+ m_Direction.InitAndSet( this, "direction", Vector( 0.0f, 0.0f, -1.0f ), FATTRIB_HAS_CALLBACK );
+}
+
+void CDmeDirectionalLight::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the light direction
+//-----------------------------------------------------------------------------
+void CDmeDirectionalLight::SetDirection( const Vector &direction )
+{
+ m_Direction.Set( direction );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets a light desc for the light
+//-----------------------------------------------------------------------------
+bool CDmeDirectionalLight::GetLightDesc( LightDesc_t *pDesc )
+{
+ memset( pDesc, 0, sizeof(LightDesc_t) );
+
+ pDesc->m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+ SetupRenderStateInternal( *pDesc, 1.0f, 0.0f, 0.0f );
+
+ matrix3x4_t m;
+ GetTransform()->GetTransform( m );
+ VectorRotate( m_Direction.Get(), m, pDesc->m_Direction );
+ VectorNormalize( pDesc->m_Direction );
+
+ pDesc->m_Theta = 0.0f;
+ pDesc->m_Phi = 0.0f;
+ pDesc->m_Falloff = 1.0f;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// A point light
+//
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePointLight, CDmePointLight );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmePointLight::OnConstruction()
+{
+ m_Position.InitAndSet( this, "position", Vector( 0, 0, 0 ), FATTRIB_HAS_CALLBACK );
+ m_flAttenuation0.InitAndSet( this, "constantAttenuation", 1.0f, FATTRIB_HAS_CALLBACK );
+ m_flAttenuation1.InitAndSet( this, "linearAttenuation", 0.0f, FATTRIB_HAS_CALLBACK );
+ m_flAttenuation2.InitAndSet( this, "quadraticAttenuation", 0.0f, FATTRIB_HAS_CALLBACK );
+ m_flMaxDistance.InitAndSet( this, "maxDistance", 0.0f, FATTRIB_HAS_CALLBACK );
+}
+
+void CDmePointLight::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the attenuation factors
+//-----------------------------------------------------------------------------
+void CDmePointLight::SetAttenuation( float flConstant, float flLinear, float flQuadratic )
+{
+ m_flAttenuation0 = flConstant;
+ m_flAttenuation1 = flLinear;
+ m_flAttenuation2 = flQuadratic;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the maximum range
+//-----------------------------------------------------------------------------
+void CDmePointLight::SetMaxDistance( float flMaxDistance )
+{
+ m_flMaxDistance = flMaxDistance;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up render state in the material system for rendering
+//-----------------------------------------------------------------------------
+bool CDmePointLight::GetLightDesc( LightDesc_t *pDesc )
+{
+ memset( pDesc, 0, sizeof(LightDesc_t) );
+
+ pDesc->m_Type = MATERIAL_LIGHT_POINT;
+ SetupRenderStateInternal( *pDesc, m_flAttenuation0, m_flAttenuation1, m_flAttenuation2 );
+
+ matrix3x4_t m;
+ GetTransform()->GetTransform( m );
+ VectorTransform( m_Position, m, pDesc->m_Position );
+ pDesc->m_Direction.Init( 0, 0, 1 );
+ pDesc->m_Range = m_flMaxDistance;
+
+ pDesc->m_Theta = 0.0f;
+ pDesc->m_Phi = 0.0f;
+ pDesc->m_Falloff = 1.0f;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// A spot light
+//
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSpotLight, CDmeSpotLight );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeSpotLight::OnConstruction()
+{
+ m_Direction.InitAndSet( this, "direction", Vector( 0.0f, 0.0f, -1.0f ) );
+ m_flSpotInnerAngle.InitAndSet( this, "spotInnerAngle", 60.0f );
+ m_flSpotOuterAngle.InitAndSet( this, "spotOuterAngle", 90.0f );
+ m_flSpotAngularFalloff.InitAndSet( this, "spotAngularFalloff", 1.0f );
+}
+
+void CDmeSpotLight::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the light direction
+//-----------------------------------------------------------------------------
+void CDmeSpotLight::SetDirection( const Vector &direction )
+{
+ m_Direction = direction;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the spotlight angle factors
+// Angles are specified in degrees, as full angles (as opposed to half-angles)
+//-----------------------------------------------------------------------------
+void CDmeSpotLight::SetAngles( float flInnerAngle, float flOuterAngle, float flAngularFalloff )
+{
+ m_flSpotInnerAngle = flInnerAngle;
+ m_flSpotOuterAngle = flOuterAngle;
+ m_flSpotAngularFalloff = flAngularFalloff;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up render state in the material system for rendering
+//-----------------------------------------------------------------------------
+bool CDmeSpotLight::GetLightDesc( LightDesc_t *pDesc )
+{
+ memset( pDesc, 0, sizeof(LightDesc_t) );
+
+ pDesc->m_Type = MATERIAL_LIGHT_SPOT;
+ SetupRenderStateInternal( *pDesc, m_flAttenuation0, m_flAttenuation1, m_flAttenuation2 );
+
+ matrix3x4_t m;
+ GetTransform()->GetTransform( m );
+ VectorTransform( m_Position, m, pDesc->m_Position );
+ VectorRotate( m_Direction.Get(), m, pDesc->m_Direction );
+ VectorNormalize( pDesc->m_Direction );
+ pDesc->m_Range = m_flMaxDistance;
+
+ // Convert to radians
+ pDesc->m_Theta = m_flSpotInnerAngle * M_PI / 180.0f;
+ pDesc->m_Phi = m_flSpotOuterAngle * M_PI / 180.0f;
+ pDesc->m_Falloff = m_flSpotAngularFalloff;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// An ambient light
+//
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeAmbientLight, CDmeAmbientLight );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeAmbientLight::OnConstruction()
+{
+}
+
+void CDmeAmbientLight::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up render state in the material system for rendering
+//-----------------------------------------------------------------------------
+void CDmeAmbientLight::SetupRenderState( int nLightIndex )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ Vector4D cube[6];
+
+ Vector4D vec4color( m_Color.Get().r(), m_Color.Get().g(), m_Color.Get().b(), m_Color.Get().a() );
+ Vector4DMultiply( vec4color, m_flIntensity / 255.0f, cube[0] );
+ cube[1] = cube[0];
+ cube[2] = cube[0];
+ cube[3] = cube[0];
+ cube[4] = cube[0];
+ cube[5] = cube[0];
+
+ pRenderContext->SetAmbientLightCube( cube );
+}
+
diff --git a/movieobjects/dmelog.cpp b/movieobjects/dmelog.cpp
new file mode 100644
index 0000000..6eb5e51
--- /dev/null
+++ b/movieobjects/dmelog.cpp
@@ -0,0 +1,6209 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmelog.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmehandle.h"
+#include "vstdlib/random.h"
+
+#include "tier0/dbg.h"
+
+#include <limits.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+LayerSelectionData_t::DataLayer_t::DataLayer_t( float frac, CDmeLogLayer *layer ) :
+ m_flStartFraction( frac )
+{
+ m_hData = layer;
+}
+
+LayerSelectionData_t::LayerSelectionData_t() :
+ m_DataType( AT_UNKNOWN ),
+ m_nDuration( 0 ),
+ m_tStartOffset( DMETIME_ZERO )
+{
+ m_nHoldTimes[ 0 ] = m_nHoldTimes[ 1 ] = 0;
+}
+
+void LayerSelectionData_t::Release()
+{
+ for ( int i = 0; i < m_vecData.Count(); ++i )
+ {
+ DataLayer_t *dl = &m_vecData[ i ];
+ if ( dl->m_hData.Get() )
+ {
+ g_pDataModel->DestroyElement( dl->m_hData->GetHandle() );
+ }
+ }
+ m_vecData.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Interpolatable types
+//-----------------------------------------------------------------------------
+inline bool IsInterpolableType( DmAttributeType_t type )
+{
+ return ( type == AT_FLOAT ) ||
+ ( type == AT_COLOR ) ||
+ ( type == AT_VECTOR2 ) ||
+ ( type == AT_VECTOR3 ) ||
+ ( type == AT_QANGLE ) ||
+ ( type == AT_QUATERNION );
+}
+
+static Vector s_pInterolationPoints[ 4 ] =
+{
+ Vector( 0.0f, 0.0f, 0.0f ),
+ Vector( 0.0f, 0.0f, 0.0f ),
+ Vector( 1.0f, 1.0f, 0.0f ),
+ Vector( 1.0f, 1.0f, 0.0f )
+};
+
+static inline float ComputeInterpolationFactor( float flFactor, int nInterpolatorType )
+{
+ Vector out;
+ Interpolator_CurveInterpolate
+ (
+ nInterpolatorType,
+ s_pInterolationPoints[ 0 ], // unused
+ s_pInterolationPoints[ 1 ],
+ s_pInterolationPoints[ 2 ],
+ s_pInterolationPoints[ 3 ], // unused
+ flFactor,
+ out
+ );
+ return out.y; // clamp( out.y, 0.0f, 1.0f );
+}
+
+float DmeLog_TimeSelection_t::AdjustFactorForInterpolatorType( float flFactor, int nSide ) const
+{
+ return ComputeInterpolationFactor( flFactor, m_nFalloffInterpolatorTypes[ nSide ] );
+}
+
+//-----------------------------------------------------------------------------
+// NOTE: See DmeTimeSelectionTimes_t for return values, -1 means before, TS_TIME_COUNT means after
+//-----------------------------------------------------------------------------
+static inline int ComputeRegionForTime( DmeTime_t t, const DmeTime_t *pRegionTimes )
+{
+ if ( t >= pRegionTimes[TS_LEFT_HOLD] )
+ {
+ if ( t <= pRegionTimes[TS_RIGHT_HOLD] )
+ return 2;
+ return ( t <= pRegionTimes[TS_RIGHT_FALLOFF] ) ? 3 : 4;
+ }
+ return ( t >= pRegionTimes[TS_LEFT_FALLOFF] ) ? 1 : 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// NOTE: See DmeTimeSelectionTimes_t for return values, -1 means before, TS_TIME_COUNT means after
+//-----------------------------------------------------------------------------
+int DmeLog_TimeSelection_t::ComputeRegionForTime( DmeTime_t curtime ) const
+{
+ return ::ComputeRegionForTime( curtime, m_nTimes );
+}
+
+
+//-----------------------------------------------------------------------------
+// per-type averaging methods
+//-----------------------------------------------------------------------------
+float DmeLog_TimeSelection_t::GetAmountForTime( DmeTime_t dmetime ) const
+{
+ float minfrac = 0.0f;
+
+ float t = dmetime.GetSeconds();
+
+ // FIXME, this is slow, we should cache this maybe?
+ COMPILE_TIME_ASSERT( TS_TIME_COUNT == 4 );
+ float times[ TS_TIME_COUNT ];
+ times[ 0 ] = m_nTimes[ 0 ].GetSeconds();
+ times[ 1 ] = m_nTimes[ 1 ].GetSeconds();
+ times[ 2 ] = m_nTimes[ 2 ].GetSeconds();
+ times[ 3 ] = m_nTimes[ 3 ].GetSeconds();
+
+ float dt1, dt2;
+ dt1 = times[ 1 ] - times[ 0 ];
+ dt2 = times[ 3 ] - times[ 2 ];
+
+ if ( dt1 > 0.0f && t >= times[ 0 ] && t < times[ 1 ] )
+ {
+ float f = ( t - times[ 0 ] ) / dt1;
+
+ Vector out;
+
+ Interpolator_CurveInterpolate
+ (
+ m_nFalloffInterpolatorTypes[ 0 ],
+ s_pInterolationPoints[ 0 ], // unused
+ s_pInterolationPoints[ 1 ],
+ s_pInterolationPoints[ 2 ],
+ s_pInterolationPoints[ 3 ], // unused
+ f,
+ out
+ );
+ return clamp( out.y, minfrac, 1.0f );
+ }
+
+ if ( t >= times[ 1 ] && t <= times[ 2 ] )
+ return 1.0f;
+
+ if ( dt2 > 0.0f && t > times[ 2 ] && t <= times[ 3 ] )
+ {
+ float f = ( times[ 3 ] - t ) / dt2;
+
+ Vector out;
+
+ Interpolator_CurveInterpolate
+ (
+ m_nFalloffInterpolatorTypes[ 1 ],
+ s_pInterolationPoints[ 0 ], // unused
+ s_pInterolationPoints[ 1 ],
+ s_pInterolationPoints[ 2 ],
+ s_pInterolationPoints[ 3 ], // unused
+ f,
+ out
+ );
+ return clamp( out.y, minfrac, 1.0f );
+ }
+
+ return minfrac;
+}
+
+// catch-all for non-interpolable types - just holds first value
+template < class T >
+T Average( const T *pValues, int nValues)
+{
+ if ( IsInterpolableType( CDmAttributeInfo< T >::AttributeType() ) )
+ {
+ static bool first = true;
+ if ( first )
+ {
+ first = false;
+ Warning( "CDmeLog: interpolable type %s doesn't have an averaging function!", CDmAttributeInfo< T >::AttributeTypeName() );
+ }
+ }
+
+ Assert( nValues > 0 );
+ if ( nValues <= 0 )
+ return T(); // uninitialized for most value classes!!!
+
+ return pValues[ 0 ];
+}
+
+// float version
+template <>
+float Average( const float *pValues, int nValues )
+{
+ Assert( nValues > 0 );
+ if ( nValues <= 0 )
+ return 0.0f;
+
+ float sum = 0.0f;
+ for ( int i = 0; i < nValues; ++i )
+ {
+ sum += pValues[ i ];
+ }
+ return sum / nValues;
+}
+
+// Color version
+template <>
+Color Average( const Color *pValues, int nValues )
+{
+ Assert( nValues > 0 );
+ if ( nValues <= 0 )
+ return Color( 0, 0, 0, 0 );
+
+ float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
+ for ( int i = 0; i < nValues; ++i )
+ {
+ r += pValues[ i ].r();
+ g += pValues[ i ].g();
+ b += pValues[ i ].b();
+ a += pValues[ i ].a();
+ }
+ float inv = nValues;
+ return Color( r * inv, g * inv, b * inv, a * inv );
+}
+
+// Vector2 version
+template <>
+Vector2D Average( const Vector2D *pValues, int nValues )
+{
+ Assert( nValues > 0 );
+ if ( nValues <= 0 )
+ return Vector2D( 0.0f, 0.0f );
+
+ Vector2D sum( 0.0f, 0.0f );
+ for ( int i = 0; i < nValues; ++i )
+ {
+ sum += pValues[ i ];
+ }
+ return sum / nValues;
+}
+
+// Vector3 version
+template <>
+Vector Average( const Vector *pValues, int nValues )
+{
+ Assert( nValues > 0 );
+ if ( nValues <= 0 )
+ return Vector( 0.0f, 0.0f, 0.0f );
+
+ Vector sum( 0.0f, 0.0f, 0.0f );
+ for ( int i = 0; i < nValues; ++i )
+ {
+ sum += pValues[ i ];
+ }
+ return sum / nValues;
+}
+
+// QAngle version
+template <>
+QAngle Average( const QAngle *pValues, int nValues )
+{
+ Assert( nValues > 0 );
+ if ( nValues <= 0 )
+ return QAngle( 0.0f, 0.0f, 0.0f );
+
+ Quaternion ave;
+ AngleQuaternion( pValues[ 0 ], ave );
+
+ // this is calculating the average by slerping with decreasing weights
+ // for example: ave = 1/3 * q2 + 2/3 ( 1/2 * q1 + 1/2 * q0 )
+ for ( int i = 1; i < nValues; ++i )
+ {
+ Quaternion quat;
+ AngleQuaternion( pValues[ i ], quat );
+ QuaternionSlerp( ave, quat, 1 / float( i + 1 ), ave );
+ }
+
+ QAngle qangle;
+ QuaternionAngles( ave, qangle );
+ return qangle;
+}
+
+// Quaternion version
+template <>
+Quaternion Average( const Quaternion *pValues, int nValues )
+{
+ Assert( nValues > 0 );
+ if ( nValues <= 0 )
+ return Quaternion( 0.0f, 0.0f, 0.0f, 1.0f );
+
+ Quaternion ave = pValues[ 0 ];
+
+ // this is calculating the average by slerping with decreasing weights
+ // for example: ave = 1/3 * q2 + 2/3 ( 1/2 * q1 + 1/2 * q0 )
+ for ( int i = 1; i < nValues; ++i )
+ {
+ QuaternionSlerp( ave, pValues[ i ], 1 / float( i + 1 ), ave );
+ }
+
+ return ave;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// per-type interpolation methods
+//-----------------------------------------------------------------------------
+
+// catch-all for non-interpolable types - just holds first value
+template < class T >
+T Interpolate( float t, const T& ti, const T& tj )
+{
+ if ( IsInterpolableType( CDmAttributeInfo< T >::AttributeType() ) )
+ {
+ static bool first = true;
+ if ( first )
+ {
+ first = false;
+ Warning( "CDmeLog: interpolable type %s doesn't have an interpolation function!", CDmAttributeInfo< T >::AttributeTypeName() );
+ }
+ }
+
+ return ti;
+}
+
+// float version
+template <>
+float Interpolate( float t, const float& ti, const float& tj )
+{
+ return t * tj + (1.0f - t) * ti;
+}
+
+// Color version
+template <>
+Color Interpolate( float t, const Color& ti, const Color& tj )
+{
+ int ri, gi, bi, ai;
+ int rj, gj, bj, aj;
+
+ ti.GetColor( ri, gi, bi, ai );
+ tj.GetColor( rj, gj, bj, aj );
+
+ return Color( t * rj + (1.0f - t) * ri,
+ t * gj + (1.0f - t) * gi,
+ t * bj + (1.0f - t) * bi,
+ t * aj + (1.0f - t) * ai);
+}
+
+// Vector2 version
+template <>
+Vector2D Interpolate( float t, const Vector2D& ti, const Vector2D& tj )
+{
+ return t * tj + (1.0f - t) * ti;
+}
+
+// Vector3 version
+template <>
+Vector Interpolate( float t, const Vector& ti, const Vector& tj )
+{
+ return t * tj + (1.0f - t) * ti;
+}
+
+// QAngle version
+template <>
+QAngle Interpolate( float t, const QAngle& ti, const QAngle& tj )
+{
+ QAngle qaResult;
+ Quaternion q, qi, qj; // Some Quaternion temps for doing the slerp
+
+ AngleQuaternion( ti, qi ); // Convert QAngles to Quaternions
+ AngleQuaternion( tj, qj );
+ QuaternionSlerp( qi, qj, t, q ); // Do a slerp as Quaternions
+ QuaternionAngles( q, qaResult ); // Convert back to QAngles
+ return qaResult;
+}
+
+// Quaternion version
+template <>
+Quaternion Interpolate( float t, const Quaternion& ti, const Quaternion& tj )
+{
+ static Quaternion s_value;
+ QuaternionSlerp( ti, tj, t, s_value );
+ return s_value;
+}
+
+// catch-all for non-interpolable types - just holds first value
+template < class T >
+T Curve_Interpolate( float t, DmeTime_t times[ 4 ], const T values[ 4 ], int curveTypes[ 4 ], float fmin, float fmax )
+{
+ if ( IsInterpolableType( CDmAttributeInfo< T >::AttributeType() ) )
+ {
+ static bool first = true;
+ if ( first )
+ {
+ first = false;
+ Warning( "CDmeLog: interpolable type %s doesn't have an interpolation function!", CDmAttributeInfo< T >::AttributeTypeName() );
+ }
+ }
+
+ return t;
+}
+
+// float version
+template <>
+float Curve_Interpolate( float t, DmeTime_t times[ 4 ], const float values[ 4 ], int curveTypes[ 4 ], float fmin, float fmax )
+{
+ Vector args[ 4 ];
+ for ( int i = 0; i < 4; ++i )
+ {
+ args[ i ].Init( times[ i ].GetSeconds(), values[ i ], 0.0f );
+ }
+
+ Vector vOut;
+ int dummy;
+ int earlypart, laterpart;
+
+ // Not holding out value of previous curve...
+ Interpolator_CurveInterpolatorsForType( curveTypes[ 1 ], dummy, earlypart );
+ Interpolator_CurveInterpolatorsForType( curveTypes[ 2 ], laterpart, dummy );
+
+ if ( earlypart == INTERPOLATE_HOLD )
+ {
+ // Hold "out" of previous sample (can cause a discontinuity)
+ VectorLerp( args[ 1 ], args[ 2 ], t, vOut );
+ vOut.y = args[ 1 ].y;
+ }
+ else if ( laterpart == INTERPOLATE_HOLD )
+ {
+ // Hold "out" of previous sample (can cause a discontinuity)
+ VectorLerp( args[ 1 ], args[ 2 ], t, vOut );
+ vOut.y = args[ 2 ].y;
+ }
+ else
+ {
+ bool sameCurveType = earlypart == laterpart ? true : false;
+ if ( sameCurveType )
+ {
+ Interpolator_CurveInterpolate( laterpart, args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], t, vOut );
+ }
+ else // curves differ, sigh
+ {
+ Vector vOut1, vOut2;
+
+ Interpolator_CurveInterpolate( earlypart, args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], t, vOut1 );
+ Interpolator_CurveInterpolate( laterpart, args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], t, vOut2 );
+
+ VectorLerp( vOut1, vOut2, t, vOut );
+ }
+ }
+
+ // FIXME: This means we can only work with curves that range from 0.0 to 1.0f!!!
+ float retval = clamp( vOut.y, fmin, fmax );
+ return retval;
+}
+
+// Vector version
+template <>
+Vector Curve_Interpolate( float t, DmeTime_t times[ 4 ], const Vector values[ 4 ], int curveTypes[ 4 ], float fmin, float fmax )
+{
+ Vector vOut;
+ int dummy;
+ int earlypart, laterpart;
+
+ // Not holding out value of previous curve...
+ Interpolator_CurveInterpolatorsForType( curveTypes[ 1 ], dummy, earlypart );
+ Interpolator_CurveInterpolatorsForType( curveTypes[ 2 ], laterpart, dummy );
+
+ if ( earlypart == INTERPOLATE_HOLD )
+ {
+ // Hold "out" of previous sample (can cause a discontinuity)
+ vOut = values[ 1 ];
+ }
+ else if ( laterpart == INTERPOLATE_HOLD )
+ {
+ // Hold "out" of previous sample (can cause a discontinuity)
+ vOut = values[ 2 ];
+ }
+ else
+ {
+ bool sameCurveType = earlypart == laterpart;
+ if ( sameCurveType )
+ {
+ Interpolator_CurveInterpolate_NonNormalized( laterpart, values[ 0 ], values[ 1 ], values[ 2 ], values[ 3 ], t, vOut );
+ }
+ else // curves differ, sigh
+ {
+ Vector vOut1, vOut2;
+
+ Interpolator_CurveInterpolate_NonNormalized( earlypart, values[ 0 ], values[ 1 ], values[ 2 ], values[ 3 ], t, vOut1 );
+ Interpolator_CurveInterpolate_NonNormalized( laterpart, values[ 0 ], values[ 1 ], values[ 2 ], values[ 3 ], t, vOut2 );
+
+ VectorLerp( vOut1, vOut2, t, vOut );
+ }
+ }
+
+ return vOut;
+}
+
+// Quaternion version
+template <>
+Quaternion Curve_Interpolate( float t, DmeTime_t times[ 4 ], const Quaternion values[ 4 ], int curveTypes[ 4 ], float fmin, float fmax )
+{
+ Quaternion vOut;
+ int dummy;
+ int earlypart, laterpart;
+
+ // Not holding out value of previous curve...
+ Interpolator_CurveInterpolatorsForType( curveTypes[ 1 ], dummy, earlypart );
+ Interpolator_CurveInterpolatorsForType( curveTypes[ 2 ], laterpart, dummy );
+
+ if ( earlypart == INTERPOLATE_HOLD )
+ {
+ // Hold "out" of previous sample (can cause a discontinuity)
+ vOut = values[ 1 ];
+ }
+ else if ( laterpart == INTERPOLATE_HOLD )
+ {
+ // Hold "out" of previous sample (can cause a discontinuity)
+ vOut = values[ 2 ];
+ }
+ else
+ {
+ bool sameCurveType = ( earlypart == laterpart ) ? true : false;
+ if ( sameCurveType )
+ {
+ Interpolator_CurveInterpolate_NonNormalized( laterpart, values[ 0 ], values[ 1 ], values[ 2 ], values[ 3 ], t, vOut );
+ }
+ else // curves differ, sigh
+ {
+ Quaternion vOut1, vOut2;
+
+ Interpolator_CurveInterpolate_NonNormalized( earlypart, values[ 0 ], values[ 1 ], values[ 2 ], values[ 3 ], t, vOut1 );
+ Interpolator_CurveInterpolate_NonNormalized( laterpart, values[ 0 ], values[ 1 ], values[ 2 ], values[ 3 ], t, vOut2 );
+
+ QuaternionSlerp( vOut1, vOut2, t, vOut );
+ }
+ }
+
+ return vOut;
+}
+
+
+template< class T >
+T ScaleValue( const T& value, float scale )
+{
+ return value * scale;
+}
+template<>
+bool ScaleValue( const bool& value, float scale )
+{
+ Assert( 0 );
+ return value;
+}
+template<>
+Color ScaleValue( const Color& value, float scale )
+{
+ Assert( 0 );
+ return value;
+}
+template<>
+Vector4D ScaleValue( const Vector4D& value, float scale )
+{
+ return Vector4D( value.x * scale, value.y * scale, value.z * scale, value.w * scale );
+}
+
+template<>
+Quaternion ScaleValue( const Quaternion& value, float scale )
+{
+ return Quaternion( value.x * scale, value.y * scale, value.z * scale, value.w * scale );
+}
+
+template<>
+VMatrix ScaleValue( const VMatrix& value, float scale )
+{
+ Assert( 0 );
+ return value;
+}
+
+template<>
+CUtlString ScaleValue( const CUtlString& value, float scale )
+{
+ Assert( 0 );
+ return value;
+}
+
+template< class T >
+float LengthOf( const T& value )
+{
+ return value;
+}
+
+template<>
+float LengthOf( const bool& value )
+{
+ if ( value )
+ return 1.0f;
+ return 0.0f;
+}
+template<>
+float LengthOf( const Color& value )
+{
+ return (float)sqrt( (float)( value.r() * value.r() +
+ value.g() * value.g() +
+ value.b() * value.b() +
+ value.a() * value.a()) );
+}
+template<>
+float LengthOf( const Vector4D& value )
+{
+ return sqrt( value.x * value.x +
+ value.y * value.y +
+ value.z * value.z +
+ value.w * value.w );
+}
+
+template<>
+float LengthOf( const Quaternion& value )
+{
+ return sqrt( value.x * value.x +
+ value.y * value.y +
+ value.z * value.z +
+ value.w * value.w );
+}
+
+template<>
+float LengthOf( const VMatrix& value )
+{
+ return 0.0f;
+}
+
+template<>
+float LengthOf( const CUtlString& value )
+{
+ return 0.0f;
+}
+
+template<>
+float LengthOf( const Vector2D& value )
+{
+ return value.Length();
+}
+
+template<>
+float LengthOf( const Vector& value )
+{
+ return value.Length();
+}
+
+template<>
+float LengthOf( const QAngle& value )
+{
+ return value.Length();
+}
+
+template< class T >
+T Subtract( const T& v1, const T& v2 )
+{
+ return v1 - v2;
+}
+
+template<>
+bool Subtract( const bool& v1, const bool& v2 )
+{
+ return v1;
+}
+
+template<>
+CUtlString Subtract( const CUtlString& v1, const CUtlString& v2 )
+{
+ return v1;
+}
+
+template<>
+Color Subtract( const Color& v1, const Color& v2 )
+{
+ Color ret;
+ for ( int i = 0; i < 4; ++i )
+ {
+ ret[ i ] = clamp( v1[ i ] - v2[ i ], 0, 255 );
+ }
+ return ret;
+}
+
+template<>
+Vector4D Subtract( const Vector4D& v1, const Vector4D& v2 )
+{
+ Vector4D ret;
+ for ( int i = 0; i < 4; ++i )
+ {
+ ret[ i ] = v1[ i ] - v2[ i ];
+ }
+ return ret;
+}
+
+template<>
+Quaternion Subtract( const Quaternion& v1, const Quaternion& v2 )
+{
+ Quaternion ret;
+ for ( int i = 0; i < 4; ++i )
+ {
+ ret[ i ] = v1[ i ];
+ }
+ return ret;
+}
+
+template< class T >
+T Add( const T& v1, const T& v2 )
+{
+ return v1 + v2;
+}
+
+template<>
+bool Add( const bool& v1, const bool& v2 )
+{
+ return v1;
+}
+
+template<>
+CUtlString Add( const CUtlString& v1, const CUtlString& v2 )
+{
+ return v1;
+}
+
+
+template<>
+Color Add( const Color& v1, const Color& v2 )
+{
+ Color ret;
+ for ( int i = 0; i < 4; ++i )
+ {
+ ret[ i ] = clamp( v1[ i ] + v2[ i ], 0, 255 );
+ }
+ return ret;
+}
+
+template<>
+Vector4D Add( const Vector4D& v1, const Vector4D& v2 )
+{
+ Vector4D ret;
+ for ( int i = 0; i < 4; ++i )
+ {
+ ret[ i ] = v1[ i ] + v2[ i ];
+ }
+ return ret;
+}
+
+template<>
+Quaternion Add( const Quaternion& v1, const Quaternion& v2 )
+{
+ return v1;
+}
+
+IMPLEMENT_ABSTRACT_ELEMENT( DmeLogLayer, CDmeLogLayer );
+
+IMPLEMENT_ELEMENT_FACTORY( DmeIntLogLayer, CDmeIntLogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeFloatLogLayer, CDmeFloatLogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeBoolLogLayer, CDmeBoolLogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeColorLogLayer, CDmeColorLogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector2LogLayer, CDmeVector2LogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector3LogLayer, CDmeVector3LogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector4LogLayer, CDmeVector4LogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeQAngleLogLayer, CDmeQAngleLogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeQuaternionLogLayer, CDmeQuaternionLogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeVMatrixLogLayer, CDmeVMatrixLogLayer );
+IMPLEMENT_ELEMENT_FACTORY( DmeStringLogLayer, CDmeStringLogLayer );
+
+//-----------------------------------------------------------------------------
+// explicit template instantiation
+//-----------------------------------------------------------------------------
+template class CDmeTypedLogLayer<int>;
+template class CDmeTypedLogLayer<float>;
+template class CDmeTypedLogLayer<bool>;
+template class CDmeTypedLogLayer<Color>;
+template class CDmeTypedLogLayer<Vector2D>;
+template class CDmeTypedLogLayer<Vector>;
+template class CDmeTypedLogLayer<Vector4D>;
+template class CDmeTypedLogLayer<QAngle>;
+template class CDmeTypedLogLayer<Quaternion>;
+template class CDmeTypedLogLayer<VMatrix>;
+template class CDmeTypedLogLayer<CUtlString>;
+
+
+IMPLEMENT_ABSTRACT_ELEMENT( DmeCurveInfo, CDmeCurveInfo );
+
+IMPLEMENT_ELEMENT_FACTORY( DmeIntCurveInfo, CDmeIntCurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeFloatCurveInfo, CDmeFloatCurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeBoolCurveInfo, CDmeBoolCurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeColorCurveInfo, CDmeColorCurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector2CurveInfo, CDmeVector2CurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector3CurveInfo, CDmeVector3CurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector4CurveInfo, CDmeVector4CurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeQAngleCurveInfo, CDmeQAngleCurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeQuaternionCurveInfo, CDmeQuaternionCurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeVMatrixCurveInfo, CDmeVMatrixCurveInfo );
+IMPLEMENT_ELEMENT_FACTORY( DmeStringCurveInfo, CDmeStringCurveInfo );
+
+//-----------------------------------------------------------------------------
+// explicit template instantiation
+//-----------------------------------------------------------------------------
+template class CDmeTypedCurveInfo<int>;
+template class CDmeTypedCurveInfo<float>;
+template class CDmeTypedCurveInfo<bool>;
+template class CDmeTypedCurveInfo<Color>;
+template class CDmeTypedCurveInfo<Vector2D>;
+template class CDmeTypedCurveInfo<Vector>;
+template class CDmeTypedCurveInfo<Vector4D>;
+template class CDmeTypedCurveInfo<QAngle>;
+template class CDmeTypedCurveInfo<Quaternion>;
+template class CDmeTypedCurveInfo<VMatrix>;
+template class CDmeTypedCurveInfo<CUtlString>;
+
+
+//-----------------------------------------------------------------------------
+// Class factory
+//-----------------------------------------------------------------------------
+IMPLEMENT_ABSTRACT_ELEMENT( DmeLog, CDmeLog );
+
+IMPLEMENT_ELEMENT_FACTORY( DmeIntLog, CDmeIntLog );
+IMPLEMENT_ELEMENT_FACTORY( DmeFloatLog, CDmeFloatLog );
+IMPLEMENT_ELEMENT_FACTORY( DmeBoolLog, CDmeBoolLog );
+IMPLEMENT_ELEMENT_FACTORY( DmeColorLog, CDmeColorLog );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector2Log, CDmeVector2Log );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector3Log, CDmeVector3Log );
+IMPLEMENT_ELEMENT_FACTORY( DmeVector4Log, CDmeVector4Log );
+IMPLEMENT_ELEMENT_FACTORY( DmeQAngleLog, CDmeQAngleLog );
+IMPLEMENT_ELEMENT_FACTORY( DmeQuaternionLog, CDmeQuaternionLog );
+IMPLEMENT_ELEMENT_FACTORY( DmeVMatrixLog, CDmeVMatrixLog );
+IMPLEMENT_ELEMENT_FACTORY( DmeStringLog, CDmeStringLog );
+
+
+//-----------------------------------------------------------------------------
+// explicit template instantiation
+//-----------------------------------------------------------------------------
+template class CDmeTypedLog<int>;
+template class CDmeTypedLog<float>;
+template class CDmeTypedLog<bool>;
+template class CDmeTypedLog<Color>;
+template class CDmeTypedLog<Vector2D>;
+template class CDmeTypedLog<Vector>;
+template class CDmeTypedLog<Vector4D>;
+template class CDmeTypedLog<QAngle>;
+template class CDmeTypedLog<Quaternion>;
+template class CDmeTypedLog<VMatrix>;
+template class CDmeTypedLog<CUtlString>;
+
+
+//-----------------------------------------------------------------------------
+// instantiate and initialize static vars
+//-----------------------------------------------------------------------------
+float CDmeIntLog::s_defaultThreshold = 0.0f;
+float CDmeFloatLog::s_defaultThreshold = 0.0f;
+float CDmeBoolLog::s_defaultThreshold = 0.0f;
+float CDmeColorLog::s_defaultThreshold = 0.0f;
+float CDmeVector2Log::s_defaultThreshold = 0.0f;
+float CDmeVector3Log::s_defaultThreshold = 0.0f;
+float CDmeVector4Log::s_defaultThreshold = 0.0f;
+float CDmeQAngleLog::s_defaultThreshold = 0.0f;
+float CDmeQuaternionLog::s_defaultThreshold = 0.0f;
+float CDmeVMatrixLog::s_defaultThreshold = 0.0f;
+float CDmeStringLog::s_defaultThreshold = 0.0f;
+
+
+void CDmeLogLayer::OnConstruction()
+{
+ m_pOwnerLog = NULL;
+ m_lastKey = 0;
+ m_times.Init( this, "times" );
+ m_CurveTypes.Init( this, "curvetypes" );
+}
+
+void CDmeLogLayer::OnDestruction()
+{
+}
+
+CDmeLog *CDmeLogLayer::GetOwnerLog()
+{
+ return m_pOwnerLog;
+}
+
+const CDmeLog *CDmeLogLayer::GetOwnerLog() const
+{
+ return m_pOwnerLog;
+}
+
+DmeTime_t CDmeLogLayer::GetBeginTime() const
+{
+ if ( m_times.Count() == 0 )
+ return DmeTime_t::MinTime();
+
+ return DmeTime_t( m_times[ 0 ] );
+}
+
+DmeTime_t CDmeLogLayer::GetEndTime() const
+{
+ uint tn = m_times.Count();
+ if ( tn == 0 )
+ return DmeTime_t::MaxTime();
+
+ return DmeTime_t( m_times[ tn - 1 ] );
+}
+
+
+// Validates that all keys are correctly sorted in time
+bool CDmeLogLayer::ValidateKeys() const
+{
+ int nCount = m_times.Count();
+ for ( int i = 1; i < nCount; ++i )
+ {
+ if ( m_times[i] <= m_times[i-1] )
+ {
+ Warning( "Error in log %s! Key times are out of order [keys %d->%d: %d->%d]!\n",
+ GetName(), i-1, i, m_times[i-1], m_times[i] );
+ return false;
+ }
+ }
+ return true;
+}
+
+int CDmeLogLayer::FindKey( DmeTime_t time ) const
+{
+ int tn = m_times.Count();
+ if ( m_lastKey >= 0 && m_lastKey < tn )
+ {
+ if ( time >= DmeTime_t( m_times[ m_lastKey ] ) )
+ {
+ // common case - playing forward
+ for ( ; m_lastKey < tn - 1; ++m_lastKey )
+ {
+ if ( time < DmeTime_t( m_times[ m_lastKey + 1 ] ) )
+ return m_lastKey;
+ }
+
+ // if time past the end, return the last key
+ return m_lastKey;
+ }
+ else
+ {
+ tn = m_lastKey;
+ }
+ }
+
+ for ( int ti = tn - 1; ti >= 0; --ti )
+ {
+ if ( time >= DmeTime_t( m_times[ ti ] ) )
+ {
+ m_lastKey = ti;
+ return ti;
+ }
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of keys
+//-----------------------------------------------------------------------------
+int CDmeLogLayer::GetKeyCount() const
+{
+ return m_times.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : nKeyIndex -
+// keyTime -
+//-----------------------------------------------------------------------------
+void CDmeLogLayer::SetKeyTime( int nKeyIndex, DmeTime_t keyTime )
+{
+ m_times.Set( nKeyIndex, keyTime.GetTenthsOfMS() );
+}
+
+//-----------------------------------------------------------------------------
+// Returns a specific key's value
+//-----------------------------------------------------------------------------
+DmeTime_t CDmeLogLayer::GetKeyTime( int nKeyIndex ) const
+{
+ return DmeTime_t( m_times[ nKeyIndex ] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Scale + bias key times
+//-----------------------------------------------------------------------------
+void CDmeLogLayer::ScaleBiasKeyTimes( double flScale, DmeTime_t nBias )
+{
+ // Don't waste time on the identity transform
+ if ( ( nBias == DMETIME_ZERO ) && ( fabs( flScale - 1.0 ) < 1e-5 ) )
+ return;
+
+ int nCount = GetKeyCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ DmeTime_t t = GetKeyTime( i );
+ t.SetSeconds( t.GetSeconds() * flScale );
+ t += nBias;
+ SetKeyTime( i, t );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the index of a particular key
+//-----------------------------------------------------------------------------
+int CDmeLogLayer::FindKeyWithinTolerance( DmeTime_t nTime, DmeTime_t nTolerance )
+{
+ int nClosest = -1;
+ DmeTime_t nClosestTolerance = DmeTime_t::MaxTime();
+ DmeTime_t nCurrTolerance;
+ int start = 0, end = GetKeyCount() - 1;
+ while ( start <= end )
+ {
+ int mid = (start + end) >> 1;
+ DmeTime_t nDelta = nTime - DmeTime_t( m_times[mid] );
+ if ( nDelta > DmeTime_t( 0 ) )
+ {
+ nCurrTolerance = nDelta;
+ start = mid + 1;
+ }
+ else if ( nDelta < DmeTime_t( 0 ) )
+ {
+ nCurrTolerance = -nDelta;
+ end = mid - 1;
+ }
+ else
+ {
+ return mid;
+ }
+
+ if ( nCurrTolerance < nClosestTolerance )
+ {
+ nClosest = mid;
+ nClosestTolerance = nCurrTolerance;
+ }
+ }
+ if ( nClosestTolerance > nTolerance )
+ return -1;
+ return nClosest;
+}
+
+void CDmeLogLayer::OnUsingCurveTypesChanged()
+{
+ if ( g_pDataModel->IsUnserializing() )
+ return;
+
+ if ( !IsUsingCurveTypes() )
+ {
+ m_CurveTypes.RemoveAll();
+ }
+ else
+ {
+ m_CurveTypes.RemoveAll();
+ // Fill in an array with the default curve type for
+ int c = m_times.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ m_CurveTypes.AddToTail( GetDefaultCurveType() );
+ }
+ }
+}
+
+bool CDmeLogLayer::IsUsingCurveTypes() const
+{
+ return GetOwnerLog() ? GetOwnerLog()->IsUsingCurveTypes() : false;
+}
+
+int CDmeLogLayer::GetDefaultCurveType() const
+{
+ return GetOwnerLog()->GetDefaultCurveType();
+}
+
+void CDmeLogLayer::SetKeyCurveType( int nKeyIndex, int curveType )
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ return;
+
+ Assert( GetOwnerLog()->IsUsingCurveTypes() );
+ Assert( m_CurveTypes.IsValidIndex( nKeyIndex ) );
+ if ( !m_CurveTypes.IsValidIndex( nKeyIndex ) )
+ return;
+
+ m_CurveTypes.Set( nKeyIndex, curveType );
+}
+
+int CDmeLogLayer::GetKeyCurveType( int nKeyIndex ) const
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ return CURVE_DEFAULT;
+
+ Assert( GetOwnerLog()->IsUsingCurveTypes() );
+ Assert( m_CurveTypes.IsValidIndex( nKeyIndex ) );
+ if ( !m_CurveTypes.IsValidIndex( nKeyIndex ) )
+ return GetOwnerLog()->GetDefaultCurveType();
+
+ return m_CurveTypes[ nKeyIndex ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes all keys outside the specified time range
+//-----------------------------------------------------------------------------
+void CDmeLogLayer::RemoveKeysOutsideRange( DmeTime_t tStart, DmeTime_t tEnd )
+{
+ int i;
+ int nKeysToRemove = 0;
+ int nKeyCount = m_times.Count();
+ for ( i = 0; i < nKeyCount; ++i, ++nKeysToRemove )
+ {
+ if ( m_times[i] >= tStart.GetTenthsOfMS() )
+ break;
+ }
+
+ if ( nKeysToRemove )
+ {
+ RemoveKey( 0, nKeysToRemove );
+ }
+
+ nKeyCount = m_times.Count();
+ for ( i = 0; i < nKeyCount; ++i )
+ {
+ if ( m_times[i] > tEnd.GetTenthsOfMS() )
+ break;
+ }
+ nKeysToRemove = nKeyCount - i;
+ if ( nKeysToRemove )
+ {
+ RemoveKey( i, nKeysToRemove );
+ }
+}
+
+
+template < class T >
+class CUndoLayerAdded : public CUndoElement
+{
+ typedef CUndoElement BaseClass;
+
+public:
+ CUndoLayerAdded( const char *desc, CDmeLog *pLog ) :
+ BaseClass( desc ),
+ m_bNeedsCleanup( false ),
+ m_hLog( pLog )
+ {
+ Assert( pLog && pLog->GetFileId() != DMFILEID_INVALID );
+ }
+
+ virtual ~CUndoLayerAdded()
+ {
+ if ( m_bNeedsCleanup )
+ {
+ g_pDataModel->DestroyElement( m_hLayer );
+ }
+ }
+
+ virtual void Undo()
+ {
+ m_bNeedsCleanup = true;
+ m_hLayer = m_hLog->RemoveLayerFromTail()->GetHandle();
+ g_pDataModel->MarkHandleInvalid( m_hLayer );
+ }
+
+ virtual void Redo()
+ {
+ m_bNeedsCleanup = false;
+ g_pDataModel->MarkHandleValid( m_hLayer );
+ m_hLog->AddLayerToTail( GetElement< CDmeTypedLogLayer< T > >( m_hLayer ) );
+ }
+
+ virtual const char *GetDesc()
+ {
+ static char sz[ 512 ];
+ int iLayer = m_hLog->GetTopmostLayer();
+ if ( iLayer >= 0 )
+ {
+ CDmeLogLayer *layer = m_hLog->GetLayer( iLayer );
+ Q_snprintf( sz, sizeof( sz ), "addlayer: log %p lc[%d], layer %p",
+ m_hLog.Get(), m_hLog->GetNumLayers(), layer );
+ }
+ else
+ {
+ Q_snprintf( sz, sizeof( sz ), "addlayer: log %p lc[%d], layer NULL",
+ m_hLog.Get(), m_hLog->GetNumLayers() );
+ }
+ return sz;
+ }
+
+private:
+ CDmeHandle< CDmeLog > m_hLog;
+ bool m_bNeedsCleanup;
+ CDmeCountedHandle m_hLayer;
+};
+
+template < class T >
+class CUndoFlattenLayers : public CUndoElement
+{
+ typedef CUndoElement BaseClass;
+
+public:
+
+ CUndoFlattenLayers( const char *desc, CDmeTypedLog< T > *pLog, float threshold, int flags ) :
+ BaseClass( desc ),
+ m_bNeedsCleanup( true ),
+ m_hLog( pLog ),
+ m_nFlags( flags ),
+ m_flThreshold( threshold )
+ {
+ Assert( pLog && pLog->GetFileId() != DMFILEID_INVALID );
+ LatchCurrentLayers();
+ }
+
+ virtual ~CUndoFlattenLayers()
+ {
+ if ( m_bNeedsCleanup )
+ {
+ for ( int i = 0; i < m_hLayers.Count(); ++i )
+ {
+ m_hLayers[ i ] = DMELEMENT_HANDLE_INVALID;
+#ifdef _DEBUG
+ CDmElement *pElement = g_pDataModel->GetElement( m_hLayers[ i ] );
+ Assert( !pElement || pElement->IsStronglyReferenced() );
+#endif
+ }
+ }
+ }
+
+ virtual void Undo()
+ {
+ m_bNeedsCleanup = false;
+
+ for ( int i = 0; i < m_hLayers.Count(); ++i )
+ {
+ if ( i == 0 )
+ {
+ // Copy base layer in place so handles to the base layer remain valid
+ CDmeTypedLogLayer< T > *base = m_hLog->GetLayer( i );
+ base->CopyLayer( GetElement< CDmeTypedLogLayer< T > >( m_hLayers[ i ] ) );
+ // Release it since we didn't txfer it over
+ g_pDataModel->DestroyElement( m_hLayers[ i ] );
+ }
+ else
+ {
+ // This transfers ownership, so no Release needed
+ m_hLog->AddLayerToTail( GetElement< CDmeTypedLogLayer< T > >( m_hLayers[ i ] ) );
+ }
+ }
+
+ m_hLayers.RemoveAll();
+ }
+
+ virtual void Redo()
+ {
+ m_bNeedsCleanup = true;
+ Assert( m_hLayers.Count() == 0 );
+
+ LatchCurrentLayers();
+ // Flatten them again (won't create undo records since we're in undo already)
+
+ m_hLog->FlattenLayers( m_flThreshold, m_nFlags );
+ }
+
+ virtual const char *GetDesc()
+ {
+ static char sz[ 512 ];
+ Q_snprintf( sz, sizeof( sz ), "flatten log %p lc[%d]",
+ m_hLog.Get(), m_hLayers.Count() );
+ return sz;
+ }
+
+private:
+
+ void LatchCurrentLayers()
+ {
+ CDisableUndoScopeGuard guard;
+ Assert( m_hLayers.Count() == 0 );
+ Assert( m_hLog->GetNumLayers() >= 1 );
+
+ // Entry 0 is the original "base" layer
+ for ( int i = 0; i < m_hLog->GetNumLayers(); ++i )
+ {
+ CDmeTypedLogLayer< T > *pLayer = CastElement< CDmeTypedLogLayer< T > >( CreateLayer< T >( m_hLog ) );
+ pLayer->CopyLayer( m_hLog->GetLayer( i ) );
+ m_hLayers.AddToTail( pLayer->GetHandle() );
+ }
+ }
+
+ CDmeHandle< CDmeTypedLog< T > > m_hLog;
+ bool m_bNeedsCleanup;
+ CUtlVector< CDmeCountedHandle > m_hLayers;
+ int m_nFlags;
+ float m_flThreshold;
+};
+
+//-----------------------------------------------------------------------------
+// CDmeTypedLogLayer - a generic typed layer used by a log
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLogLayer< T >::OnConstruction()
+{
+// m_times.Init( this, "times" );
+// m_CurveTypes.Init( this, "curvetypes" );
+ m_values.Init( this, "values" );
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::SetOwnerLog( CDmeLog *owner )
+{
+ Assert( owner );
+ Assert( assert_cast< CDmeTypedLog< T > * >( owner ) );
+ m_pOwnerLog = owner;
+}
+
+template< class T >
+CDmeTypedLog< T > *CDmeTypedLogLayer< T >::GetTypedOwnerLog()
+{
+ return assert_cast< CDmeTypedLog< T > * >( m_pOwnerLog );
+}
+
+template< class T >
+const CDmeTypedLog< T > *CDmeTypedLogLayer< T >::GetTypedOwnerLog() const
+{
+ return assert_cast< CDmeTypedLog< T > * >( m_pOwnerLog );
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::OnDestruction()
+{
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::RemoveKeys( DmeTime_t starttime )
+{
+ int ti = FindKey( starttime );
+ if ( ti < 0 )
+ return;
+
+ if ( starttime > DmeTime_t( m_times[ ti ] ) )
+ ++ti;
+
+ int nKeys = m_times.Count() - ti;
+ if ( nKeys == 0 )
+ return;
+
+ m_times.RemoveMultiple( ti, nKeys );
+ m_values.RemoveMultiple( ti, nKeys );
+ if ( IsUsingCurveTypes() )
+ {
+ m_CurveTypes.RemoveMultiple( ti, nKeys );
+ }
+
+ if ( m_lastKey >= ti && m_lastKey < ti + nKeys )
+ {
+ m_lastKey = ( ti > 0 ) ? ti - 1 : 0;
+ }
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::ClearKeys()
+{
+ m_times.RemoveAll();
+ m_values.RemoveAll();
+ m_CurveTypes.RemoveAll();
+ m_lastKey = 0;
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::RemoveKey( int nKeyIndex, int nNumKeysToRemove /*= 1*/ )
+{
+ m_times.RemoveMultiple( nKeyIndex, nNumKeysToRemove );
+ m_values.RemoveMultiple( nKeyIndex, nNumKeysToRemove );
+ if ( IsUsingCurveTypes() )
+ {
+ m_CurveTypes.RemoveMultiple( nKeyIndex, nNumKeysToRemove );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Sets a key, removes all keys after this time
+// FIXME: This needs to account for interpolation!!!
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLogLayer< T >::SetKey( DmeTime_t time, const T& value, int curveType /*=CURVE_DEFAULT*/)
+{
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+
+ // Remove all keys after this time
+ RemoveKeys( time );
+
+ // Add the key and then check to see if the penultimate key is still necessary
+ m_times.AddToTail( time.GetTenthsOfMS() );
+ m_values.AddToTail( value );
+ if ( IsUsingCurveTypes() )
+ {
+ m_CurveTypes.AddToTail( curveType );
+ }
+
+ int nKeys = m_values.Count();
+ if ( ( nKeys < 3 ) ||
+ ( IsUsingCurveTypes() && ( curveType != m_CurveTypes[ nKeys -1 ] || ( curveType != m_CurveTypes[ nKeys - 2 ] ) ) )
+ )
+ {
+ return;
+ }
+
+ // If adding the new means that the penultimate key's value was unneeded, then we will remove the penultimate key value
+ T check = GetValueSkippingKey( nKeys - 2 );
+ T oldPenultimateValue = m_values[ nKeys - 2 ];
+ if ( GetTypedOwnerLog()->ValuesDiffer( oldPenultimateValue, check ) )
+ {
+ return;
+ }
+
+ // Remove penultimate, it's not needed
+ m_times.Remove( nKeys - 2 );
+ m_values.Remove( nKeys - 2 );
+ if ( IsUsingCurveTypes() )
+ {
+ m_CurveTypes.Remove( nKeys - 2 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a key within tolerance, or adds one
+//-----------------------------------------------------------------------------
+template< class T >
+int CDmeTypedLogLayer< T >::FindOrAddKey( DmeTime_t nTime, DmeTime_t nTolerance, const T& value, int curveType /*=CURVE_DEFAULT*/ )
+{
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+
+ // NOTE: This math must occur in 64bits because the max delta nDelta
+ // can be 33 bits large. Bleah.
+ int nClosest = -1;
+ int64 nClosestTolerance = DmeTime_t::MinTime().GetTenthsOfMS();
+ int64 nCurrTolerance;
+ int start = 0, end = GetKeyCount() - 1;
+ while ( start <= end )
+ {
+ int mid = (start + end) >> 1;
+ int64 nDelta = (int64)nTime.GetTenthsOfMS() - (int64)m_times[mid];
+ if ( nDelta > 0 )
+ {
+ nCurrTolerance = nDelta;
+ start = mid + 1;
+ }
+ else if ( nDelta < 0 )
+ {
+ nCurrTolerance = -nDelta;
+ end = mid - 1;
+ }
+ else
+ {
+ nClosest = end = mid;
+ nClosestTolerance = 0;
+ break;
+ }
+
+ if ( nCurrTolerance < nClosestTolerance )
+ {
+ nClosest = mid;
+ nClosestTolerance = nCurrTolerance;
+ }
+ }
+
+ // At this point, end is the entry less than or equal to the entry
+ if ( nClosest == -1 || nTolerance.GetTenthsOfMS() < nClosestTolerance )
+ {
+ ++end;
+ nClosest = m_times.InsertBefore( end, nTime.GetTenthsOfMS() );
+ m_values.InsertBefore( end, value );
+ if ( IsUsingCurveTypes() )
+ {
+ m_CurveTypes.InsertBefore( end, curveType );
+ }
+ }
+ return nClosest;
+}
+
+
+//-----------------------------------------------------------------------------
+// This inserts a key. Unlike SetKey, this will *not* delete keys after the specified time
+//-----------------------------------------------------------------------------
+template < class T >
+int CDmeTypedLogLayer< T >::InsertKey( DmeTime_t nTime, const T& value, int curveType /*=CURVE_DEFAULT*/ )
+{
+ int idx = FindOrAddKey( nTime, DmeTime_t( 0 ), value );
+ m_times .Set( idx, nTime.GetTenthsOfMS() );
+ m_values.Set( idx, value );
+ if ( IsUsingCurveTypes() )
+ {
+ m_CurveTypes.Set( idx, curveType );
+ }
+ return idx;
+}
+
+template< class T >
+int CDmeTypedLogLayer< T >::InsertKeyAtTime( DmeTime_t nTime, int curveType /*=CURVE_DEFAULT*/ )
+{
+ T curVal = GetValue( nTime );
+ return InsertKey( nTime, curVal, curveType );
+}
+
+static bool CanInterpolateType( DmAttributeType_t attType )
+{
+ switch ( attType )
+ {
+ default:
+ return false;
+ case AT_FLOAT:
+ case AT_VECTOR3:
+ case AT_QUATERNION:
+ break;
+ }
+ return true;
+}
+
+template< class T >
+const T& CDmeTypedLogLayer< T >::GetValue( DmeTime_t time ) const
+{
+ // Curve Interpolation only for 1-D float data right now!!!
+ if ( IsUsingCurveTypes() &&
+ CanInterpolateType( GetDataType() ) )
+ {
+ static T out;
+ GetValueUsingCurveInfo( time, out );
+ return out;
+ }
+
+ int tc = m_times.Count();
+
+ Assert( m_values.Count() == tc );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == tc ) );
+
+ int ti = FindKey( time );
+ if ( ti < 0 )
+ {
+ if ( tc > 0 )
+ return m_values[ 0 ];
+
+ const CDmeTypedLog< T > *pOwner = GetTypedOwnerLog();
+ if ( pOwner->HasDefaultValue() )
+ return pOwner->GetDefaultValue();
+
+ static T s_value;
+ CDmAttributeInfo< T >::SetDefaultValue( s_value ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return s_value;
+ }
+
+ // Early out if we're at the end
+ if ( ti >= tc - 1 )
+ return m_values[ ti ];
+
+ if ( !IsInterpolableType( GetDataType() ) )
+ return m_values[ ti ];
+
+ // Figure out the lerp factor
+ float t = GetFractionOfTimeBetween( time, DmeTime_t( m_times[ti] ), DmeTime_t( m_times[ti+1] ) );
+ static T s_value;
+ s_value = Interpolate( t, m_values[ti], m_values[ti+1] ); // Compute the lerp between ti and ti+1
+ return s_value;
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::SetKey( DmeTime_t time, const CDmAttribute *pAttr, uint index, int curveType /*= CURVE_DEFAULT*/ )
+{
+ DmAttributeType_t type = pAttr->GetType();
+ if ( IsValueType( type ) )
+ {
+ Assert( pAttr->GetType() == GetDataType() );
+ SetKey( time, pAttr->GetValue< T >(), curveType );
+ }
+ else if ( IsArrayType( type ) )
+ {
+ Assert( ArrayTypeToValueType( type ) == GetDataType() );
+ CDmrArrayConst<T> array( pAttr );
+ SetKey( time, array[ index ], curveType );
+ }
+ else
+ {
+ Assert( 0 );
+ }
+}
+
+template< class T >
+bool CDmeTypedLogLayer< T >::SetDuplicateKeyAtTime( DmeTime_t time )
+{
+ int nKeys = m_times.Count();
+ if ( nKeys == 0 || DmeTime_t( m_times[ nKeys - 1 ] ) == time )
+ return false;
+
+ T value = GetValue( time );
+ // these two calls need to be separated (and we need to make an extra copy here) because
+ // CUtlVector has an assert to try to safeguard against inserting an existing value
+ // therefore, m_values.AddToTail( m_values[ i ] ) is illegal (or at least, triggers the assert)
+ SetKey( time, value );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a specific key's value
+//-----------------------------------------------------------------------------
+template< class T >
+const T& CDmeTypedLogLayer< T >::GetKeyValue( int nKeyIndex ) const
+{
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+
+ return m_values[ nKeyIndex ];
+}
+
+
+template< class T >
+void CDmeTypedLogLayer< T >::GetValue( DmeTime_t time, CDmAttribute *pAttr, uint index ) const
+{
+ DmAttributeType_t attrtype = pAttr->GetType();
+ if ( IsValueType( attrtype ) )
+ {
+ Assert( attrtype == GetDataType() );
+ pAttr->SetValue( GetValue( time ) );
+ }
+ else if ( IsArrayType( attrtype ) )
+ {
+ Assert( ArrayTypeToValueType( attrtype ) == GetDataType() );
+ CDmrArray<T> array( pAttr );
+ array.Set( index, GetValue( time ) );
+ }
+ else
+ {
+ Assert( 0 );
+ }
+}
+
+template< class T >
+float CDmeTypedLogLayer< T >::GetComponent( DmeTime_t time, int componentIndex ) const
+{
+ return ::GetComponent( GetValue( time ), componentIndex );
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::SetKeyValue( int nKey, const T& value )
+{
+ Assert( nKey >= 0 );
+ Assert( nKey < m_values.Count() );
+
+ m_values.Set( nKey, value );
+}
+
+
+//-----------------------------------------------------------------------------
+// resampling and filtering
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLogLayer< T >::Resample( DmeFramerate_t samplerate )
+{
+ // FIXME: Might have to revisit how to determine "curve types" for "resampled points...
+ Assert( !IsUsingCurveTypes() );
+
+ // make sure we resample to include _at_least_ the existing time range
+ DmeTime_t begin = GetBeginTime();
+ DmeTime_t end = GetEndTime();
+ int nSamples = 2 + FrameForTime( end - begin, samplerate );
+
+ CUtlVector< int > resampledTimes;
+ CUtlVector< T > resampledValues;
+ CUtlVector< int > resampledCurveTypes;
+
+ resampledValues.EnsureCapacity( nSamples );
+ resampledTimes.EnsureCapacity( nSamples );
+
+ DmeTime_t time( begin );
+ for ( int i = 0; i < nSamples; ++i )
+ {
+ resampledTimes.AddToTail( time.GetTenthsOfMS() );
+ resampledValues.AddToTail( GetValue( time ) );
+ if ( IsUsingCurveTypes() )
+ {
+ resampledCurveTypes.AddToTail( CURVE_DEFAULT );
+ }
+
+ time = time.TimeAtNextFrame( samplerate );
+ }
+
+ m_times.SwapArray( resampledTimes );
+ m_values.SwapArray( resampledValues );
+ if ( IsUsingCurveTypes() )
+ {
+ m_CurveTypes.SwapArray( resampledCurveTypes );
+ }
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::Filter( int nSampleRadius )
+{
+ // Doesn't mess with curvetypes!!!
+
+ const CUtlVector< T > &values = m_values.Get();
+ CUtlVector< T > filteredValues;
+
+ int nValues = values.Count();
+ filteredValues.EnsureCapacity( nValues );
+
+ for ( int i = 0; i < nValues; ++i )
+ {
+ int nSamples = min( nSampleRadius, min( i, nValues - i - 1 ) );
+ filteredValues.AddToTail( Average( values.Base() + i - nSamples, 2 * nSamples + 1 ) );
+ }
+
+ m_values.SwapArray( filteredValues );
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::Filter2( DmeTime_t sampleRadius )
+{
+ // Doesn't mess with curvetypes!!!
+
+ const CUtlVector< T > &values = m_values.Get();
+ CUtlVector< T > filteredValues;
+
+ int nValues = values.Count();
+ filteredValues.EnsureCapacity( nValues );
+
+ DmeTime_t earliest = DMETIME_ZERO;
+ if ( nValues > 0 )
+ {
+ earliest = DmeTime_t( m_times[ 0 ] );
+ }
+ for ( int i = 0; i < nValues; ++i )
+ {
+ T vals[ 3 ];
+ DmeTime_t t = GetKeyTime( i );
+ DmeTime_t t0 = t - sampleRadius;
+ DmeTime_t t1 = t + sampleRadius;
+
+ if ( t0 >= earliest )
+ {
+ vals[ 0 ] = GetValue( t0 );
+ }
+ else
+ {
+ vals[ 0 ] = m_values[ 0 ];
+ }
+ vals[ 1 ] = GetValue( t );
+ vals[ 2 ] = GetValue( t1 );
+
+ if ( i == 0 || i == nValues - 1 )
+ {
+ filteredValues.AddToTail( values[ i ] );
+ }
+ else
+ {
+ filteredValues.AddToTail( Average( vals, 3 ) );
+ }
+ }
+
+ m_values.SwapArray( filteredValues );
+}
+
+template< class T >
+const T& CDmeTypedLogLayer< T >::GetValueSkippingKey( int nKeyToSkip ) const
+{
+ // Curve Interpolation only for 1-D float data right now!!!
+ if ( IsUsingCurveTypes() && CanInterpolateType( GetDataType() ) )
+ {
+ static T out;
+ GetValueUsingCurveInfoSkippingKey( nKeyToSkip, out );
+ return out;
+ }
+
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+
+ DmeTime_t time = GetKeyTime( nKeyToSkip );
+
+ int prevKey = nKeyToSkip - 1;
+ int nextKey = nKeyToSkip + 1;
+
+ DmeTime_t prevTime;
+ T prevValue;
+ int prevCurveType;
+ DmeTime_t nextTime;
+ T nextValue;
+ int nextCurveType;
+
+ GetBoundedSample( prevKey, prevTime, prevValue, prevCurveType );
+ GetBoundedSample( nextKey, nextTime, nextValue, nextCurveType );
+
+ // Figure out the lerp factor
+ float t = GetFractionOfTimeBetween( time, prevTime, nextTime );
+
+ static T s_value;
+ s_value = Interpolate( t, prevValue, nextValue );
+ return s_value;
+}
+
+template< class T >
+void CDmeTypedLog<T>::RemoveRedundantKeys( float threshold )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return;
+
+ GetLayer( bestLayer )->RemoveRedundantKeys( threshold );
+}
+
+template< class T >
+void CDmeTypedLogLayer<T>::RemoveRedundantKeys( float threshold )
+{
+ Assert( GetTypedOwnerLog() );
+ if ( !GetTypedOwnerLog() )
+ return;
+
+ float saveThreshold;
+ {
+ CDisableUndoScopeGuard sg;
+ saveThreshold = GetTypedOwnerLog()->GetValueThreshold();
+ GetTypedOwnerLog()->SetValueThreshold( threshold );
+ }
+
+ RemoveRedundantKeys();
+
+ {
+ CDisableUndoScopeGuard sg;
+ GetTypedOwnerLog()->SetValueThreshold( saveThreshold );
+ }
+
+}
+
+// Implementation of Douglas-Peucker curve simplification routine (hacked to only care about error against original curve (sort of 1D)
+
+template< class T >
+void CDmeTypedLogLayer< T >::CurveSimplify_R( float thresholdSqr, int startPoint, int endPoint, CDmeTypedLogLayer< T > *output )
+{
+ if ( endPoint <= startPoint + 1 )
+ {
+ return;
+ }
+
+ int maxPoint = startPoint;
+ float maxDistanceSqr = 0.0f;
+
+ for ( int i = startPoint + 1 ; i < endPoint; ++i )
+ {
+ DmeTime_t keyTime = GetKeyTime( i );
+ T check = GetKeyValue( i );
+ T check2 = output->GetValue( keyTime );
+ T dist = Subtract( check, check2 );
+ float distSqr = LengthOf( dist ) * LengthOf( dist );
+
+ if ( distSqr < maxDistanceSqr )
+ continue;
+
+ maxPoint = i;
+ maxDistanceSqr = distSqr;
+ }
+
+ if ( maxDistanceSqr > thresholdSqr )
+ {
+ output->InsertKey( GetKeyTime( maxPoint ), GetKeyValue( maxPoint ) );
+ CurveSimplify_R( thresholdSqr, startPoint, maxPoint, output );
+ CurveSimplify_R( thresholdSqr, maxPoint, endPoint, output );
+ }
+}
+
+template<> void CDmeTypedLogLayer< bool >::CurveSimplify_R( float thresholdSqr, int startPoint, int endPoint, CDmeTypedLogLayer< bool > *output ) {};
+template<> void CDmeTypedLogLayer< int >::CurveSimplify_R( float thresholdSqr, int startPoint, int endPoint, CDmeTypedLogLayer< int > *output ) {};
+template<> void CDmeTypedLogLayer< Color >::CurveSimplify_R( float thresholdSqr, int startPoint, int endPoint, CDmeTypedLogLayer< Color > *output ) {};
+template<> void CDmeTypedLogLayer< Quaternion >::CurveSimplify_R( float thresholdSqr, int startPoint, int endPoint, CDmeTypedLogLayer< Quaternion > *output ) {};
+template<> void CDmeTypedLogLayer< VMatrix >::CurveSimplify_R( float thresholdSqr, int startPoint, int endPoint, CDmeTypedLogLayer< VMatrix > *output ) {};
+
+// We can't just walk the keys linearly since it'll accumulate too much error and give us a bad curve after simplification. We do a recursive subdivide which has a worst case of O(n^2) but
+// probably is better than that in most cases.
+template< class T >
+void CDmeTypedLogLayer<T>::RemoveRedundantKeys()
+{
+ CDmeTypedLog< T > *pOwner = GetTypedOwnerLog();
+ if ( !pOwner )
+ return;
+
+ int nKeys = GetKeyCount();
+ if ( nKeys <= 2 )
+ return;
+
+ float thresh = pOwner->GetValueThreshold();
+ if ( thresh < 0.0f )
+ return;
+
+ CDmeTypedLogLayer< T > *save = 0;
+ {
+ CDisableUndoScopeGuard guard;
+ save = CastElement< CDmeTypedLogLayer< T > >( CreateLayer< T >( pOwner ) );
+ Assert( save );
+
+ save->m_times.EnsureCapacity( nKeys );
+ save->m_values.EnsureCapacity( nKeys );
+
+ // Insert start and end points as first "guess" at simplified curve
+ // Skip preceeding and ending keys that have the same value
+ int nFirstKey, nLastKey;
+ for ( nFirstKey = 1; nFirstKey < nKeys; ++nFirstKey )
+ {
+ // FIXME: Should we use a tolerance check here?
+ if ( GetKeyValue( nFirstKey ) != GetKeyValue( nFirstKey - 1 ) )
+ break;
+ }
+ --nFirstKey;
+
+ for ( nLastKey = nKeys; --nLastKey >= 1; )
+ {
+ // FIXME: Should we use a tolerance check here?
+ if ( GetKeyValue( nLastKey ) != GetKeyValue( nLastKey - 1 ) )
+ break;
+ }
+
+ if ( nLastKey <= nFirstKey )
+ {
+ save->InsertKey( GetKeyTime( 0 ), GetKeyValue( 0 ) );
+ }
+ else
+ {
+ if ( GetDataType() == AT_FLOAT )
+ {
+ save->InsertKey( GetKeyTime( nFirstKey ), GetKeyValue( nFirstKey ) );
+ save->InsertKey( GetKeyTime( nLastKey ), GetKeyValue( nLastKey ) );
+
+ // Recursively finds the point with the largest error from the "simplified curve" and subdivides the problem on both sides until the largest delta from the simplified
+ // curve is less than the tolerance (squared)
+ CurveSimplify_R( thresh * thresh, nFirstKey, nLastKey, save );
+ }
+ else
+ {
+ save->InsertKey( GetKeyTime( nFirstKey ), GetKeyValue( nFirstKey ) );
+
+ // copy over keys that differ from their prior or next keys - this keeps the first and last key of a run of same-valued keys
+ for ( int i = nFirstKey + 1; i < nLastKey; ++i )
+ {
+ // prev is from the saved log to allow deleting runs of same-valued keys
+ const T &prev = save->GetKeyValue( save->GetKeyCount() - 1 );
+ const T &curr = GetKeyValue( i );
+ const T &next = GetKeyValue( i + 1 );
+ if ( pOwner->ValuesDiffer( prev, curr ) || pOwner->ValuesDiffer( curr, next ) )
+ {
+ save->InsertKey( GetKeyTime( i ), curr );
+ }
+ }
+
+ save->InsertKey( GetKeyTime( nLastKey ), GetKeyValue( nLastKey ) );
+ }
+ }
+ }
+
+ // This operation is undoable
+ CopyLayer( save );
+
+ {
+ CDisableUndoScopeGuard guard;
+ g_pDataModel->DestroyElement( save->GetHandle() );
+ }
+}
+
+// curve info helpers
+template< class T >
+const CDmeTypedCurveInfo< T > *CDmeTypedLogLayer<T>::GetTypedCurveInfo() const
+{
+ Assert( GetTypedOwnerLog() );
+ return GetTypedOwnerLog()->GetTypedCurveInfo();
+}
+
+template< class T >
+CDmeTypedCurveInfo< T > *CDmeTypedLogLayer<T>::GetTypedCurveInfo()
+{
+ Assert( GetTypedOwnerLog() );
+ return GetTypedOwnerLog()->GetTypedCurveInfo();
+}
+
+template< class T >
+bool CDmeTypedLogLayer< T >::IsUsingEdgeInfo() const
+{
+ return GetTypedOwnerLog()->IsUsingEdgeInfo();
+}
+
+template< class T >
+const T& CDmeTypedLogLayer< T >::GetDefaultEdgeZeroValue() const
+{
+ return GetTypedOwnerLog()->GetDefaultEdgeZeroValue();
+}
+
+template< class T >
+DmeTime_t CDmeTypedLogLayer< T >::GetRightEdgeTime() const
+{
+ return GetTypedOwnerLog()->GetRightEdgeTime();
+}
+
+
+template< class T >
+void CDmeTypedLogLayer< T >::GetEdgeInfo( int edge, bool& active, T& val, int& curveType ) const
+{
+ GetTypedOwnerLog()->GetEdgeInfo( edge, active, val, curveType );
+}
+
+template< class T >
+int CDmeTypedLogLayer< T >::GetEdgeCurveType( int edge ) const
+{
+ return GetTypedOwnerLog()->GetEdgeCurveType( edge );
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::GetZeroValue( int side, T& val ) const
+{
+ return GetTypedOwnerLog()->GetZeroValue( side, val );
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::GetBoundedSample( int keyindex, DmeTime_t& time, T& val, int& curveType ) const
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ {
+ time = DmeTime_t( 0 );
+ CDmAttributeInfo< T >::SetDefaultValue( val );
+ curveType = CURVE_DEFAULT;
+ return;
+ }
+
+ if ( keyindex < 0 )
+ {
+ time = DmeTime_t( 0 );
+ GetZeroValue( 0, val );
+ curveType = GetEdgeCurveType( 0 );
+ return;
+ }
+ else if ( keyindex >= m_times.Count() )
+ {
+ time = GetTypedOwnerLog()->GetRightEdgeTime();
+ if ( time == DmeTime_t( 0 ) && m_times.Count() > 0 )
+ {
+ // Push it one msec past the final end time
+ time = DmeTime_t( m_times[ m_times.Count() - 1 ] ) + DmeTime_t( 1 );
+ }
+ GetTypedOwnerLog()->GetZeroValue( 1, val );
+ curveType = GetTypedOwnerLog()->GetEdgeCurveType( 1 );
+ return;
+ }
+
+ time = DmeTime_t( m_times[ keyindex ] );
+ val = m_values[ keyindex ];
+ if ( IsUsingCurveTypes() )
+ {
+ curveType = m_CurveTypes[ keyindex ];
+ if ( curveType == CURVE_DEFAULT )
+ {
+ curveType = GetTypedOwnerLog()->GetDefaultCurveType();
+ }
+ }
+}
+
+template<>
+void CDmeTypedLogLayer< float >::GetValueUsingCurveInfoSkippingKey( int nKeyToSkip, float& out ) const
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ {
+ out = 0.0f;
+ return;
+ }
+
+ Assert( CanInterpolateType( GetDataType() ) );
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+ Assert( IsInterpolableType( GetDataType() ) );
+
+ float v[ 4 ];
+ DmeTime_t t[ 4 ];
+ int curvetypes[ 4 ];
+ int ti = nKeyToSkip;
+ DmeTime_t time = GetKeyTime( nKeyToSkip );
+
+ if ( !IsUsingCurveTypes() )
+ {
+ if ( ti < 0 )
+ {
+ CDmAttributeInfo< float >::SetDefaultValue( out ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return;
+ }
+ else if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti + 1 ];
+ return;
+ }
+ }
+
+ DmeTime_t finalTime = GetTypedOwnerLog()->GetRightEdgeTime();
+ if ( finalTime != DmeTime_t( 0 ) )
+ {
+ if ( time > finalTime )
+ {
+ GetZeroValue( 1, out );
+ return;
+ }
+ }
+ else
+ {
+ if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti + 1 ];
+ return;
+ }
+ }
+
+ GetBoundedSample( ti - 2, t[ 0 ], v[ 0 ], curvetypes[ 0 ] );
+ GetBoundedSample( ti - 1, t[ 1 ], v[ 1 ], curvetypes[ 1 ] );
+ GetBoundedSample( ti + 1, t[ 2 ], v[ 2 ], curvetypes[ 2 ] );
+ GetBoundedSample( ti + 2, t[ 3 ], v[ 3 ], curvetypes[ 3 ] );
+
+ float frac = 0.0f;
+ if ( t[2] > t[ 1 ] )
+ {
+ frac = (time.GetSeconds() - t[1].GetSeconds()) / (float) ( t[2].GetSeconds() - t[ 1 ].GetSeconds() );
+ }
+
+ // Compute the lerp between ti and ti+1
+ out = Curve_Interpolate( frac, t, v, curvetypes, GetOwnerLog()->GetMinValue(), GetOwnerLog()->GetMaxValue() );
+}
+
+template<>
+void CDmeTypedLogLayer< Vector >::GetValueUsingCurveInfoSkippingKey( int nKeyToSkip, Vector& out ) const
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ {
+ CDmAttributeInfo< Vector >::SetDefaultValue( out );
+ return;
+ }
+
+ Assert( CanInterpolateType( GetDataType() ) );
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+ Assert( IsInterpolableType( GetDataType() ) );
+
+ Vector v[ 4 ];
+ DmeTime_t t[ 4 ];
+ int curvetypes[ 4 ];
+ int ti = nKeyToSkip;
+ DmeTime_t time = GetKeyTime( nKeyToSkip );
+
+ if ( !IsUsingCurveTypes() )
+ {
+ if ( ti < 0 )
+ {
+ CDmAttributeInfo< Vector >::SetDefaultValue( out ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return;
+ }
+ else if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti + 1 ];
+ return;
+ }
+ }
+
+ DmeTime_t finalTime = GetTypedOwnerLog()->GetRightEdgeTime();
+ if ( finalTime != DmeTime_t( 0 ) )
+ {
+ if ( time > finalTime )
+ {
+ CDmAttributeInfo< Vector >::SetDefaultValue( out );
+ return;
+ }
+ }
+ else
+ {
+ if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti + 1 ];
+ return;
+ }
+ }
+
+ GetBoundedSample( ti - 2, t[ 0 ], v[ 0 ], curvetypes[ 0 ] );
+ GetBoundedSample( ti - 1, t[ 1 ], v[ 1 ], curvetypes[ 1 ] );
+ GetBoundedSample( ti + 1, t[ 2 ], v[ 2 ], curvetypes[ 2 ] );
+ GetBoundedSample( ti + 2, t[ 3 ], v[ 3 ], curvetypes[ 3 ] );
+
+ float frac = 0.0f;
+ if ( t[2] > t[ 1 ] )
+ {
+ frac = (time.GetSeconds() - t[1].GetSeconds()) / (float) ( t[2].GetSeconds() - t[ 1 ].GetSeconds() );
+ }
+
+ // Compute the lerp between ti and ti+1
+ out = Curve_Interpolate( frac, t, v, curvetypes, GetOwnerLog()->GetMinValue(), GetOwnerLog()->GetMaxValue() );
+}
+
+template<>
+void CDmeTypedLogLayer< Quaternion >::GetValueUsingCurveInfoSkippingKey( int nKeyToSkip, Quaternion& out ) const
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ {
+ CDmAttributeInfo< Quaternion >::SetDefaultValue( out );
+ return;
+ }
+
+ Assert( CanInterpolateType( GetDataType() ) );
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+ Assert( IsInterpolableType( GetDataType() ) );
+
+ Quaternion v[ 4 ];
+ DmeTime_t t[ 4 ];
+ int curvetypes[ 4 ];
+ int ti = nKeyToSkip;
+ DmeTime_t time = GetKeyTime( nKeyToSkip );
+
+ if ( !IsUsingCurveTypes() )
+ {
+ if ( ti < 0 )
+ {
+ CDmAttributeInfo< Quaternion >::SetDefaultValue( out ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return;
+ }
+ else if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti + 1 ];
+ return;
+ }
+ }
+
+ DmeTime_t finalTime = GetTypedOwnerLog()->GetRightEdgeTime();
+ if ( finalTime != DmeTime_t( 0 ) )
+ {
+ if ( time > finalTime )
+ {
+ CDmAttributeInfo< Quaternion >::SetDefaultValue( out );
+ return;
+ }
+ }
+ else
+ {
+ if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti + 1 ];
+ return;
+ }
+ }
+
+ GetBoundedSample( ti - 2, t[ 0 ], v[ 0 ], curvetypes[ 0 ] );
+ GetBoundedSample( ti - 1, t[ 1 ], v[ 1 ], curvetypes[ 1 ] );
+ GetBoundedSample( ti + 1, t[ 2 ], v[ 2 ], curvetypes[ 2 ] );
+ GetBoundedSample( ti + 2, t[ 3 ], v[ 3 ], curvetypes[ 3 ] );
+
+ float frac = 0.0f;
+ if ( t[2] > t[ 1 ] )
+ {
+ frac = (time.GetSeconds() - t[1].GetSeconds()) / (float) ( t[2].GetSeconds() - t[ 1 ].GetSeconds() );
+ }
+
+ // Compute the lerp between ti and ti+1
+ out = Curve_Interpolate( frac, t, v, curvetypes, GetOwnerLog()->GetMinValue(), GetOwnerLog()->GetMaxValue() );
+}
+
+
+template<>
+void CDmeTypedLogLayer< float >::GetValueUsingCurveInfo( DmeTime_t time, float& out ) const
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ {
+ out = 0.0f;
+ return;
+ }
+
+ Assert( CanInterpolateType( GetDataType() ) );
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+ Assert( IsInterpolableType( GetDataType() ) );
+
+ float v[ 4 ];
+ DmeTime_t t[ 4 ];
+ int curvetypes[ 4 ];
+ int ti = FindKey( time );
+ if ( !IsUsingCurveTypes() )
+ {
+ if ( ti < 0 )
+ {
+ CDmAttributeInfo< float >::SetDefaultValue( out ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return;
+ }
+ else if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti ];
+ return;
+ }
+ }
+
+ DmeTime_t finalTime = GetTypedOwnerLog()->GetRightEdgeTime();
+ if ( finalTime != DmeTime_t( 0 ) )
+ {
+ if ( time > finalTime )
+ {
+ GetZeroValue( 1, out );
+ return;
+ }
+ }
+ else
+ {
+ if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti ];
+ return;
+ }
+ }
+
+ GetBoundedSample( ti - 1, t[ 0 ], v[ 0 ], curvetypes[ 0 ] );
+ GetBoundedSample( ti + 0, t[ 1 ], v[ 1 ], curvetypes[ 1 ] );
+ GetBoundedSample( ti + 1, t[ 2 ], v[ 2 ], curvetypes[ 2 ] );
+ GetBoundedSample( ti + 2, t[ 3 ], v[ 3 ], curvetypes[ 3 ] );
+
+ float frac = 0.0f;
+ if ( t[2] > t[ 1 ] )
+ {
+ frac = (time.GetSeconds() - t[1].GetSeconds()) / (float) ( t[2].GetSeconds() - t[ 1 ].GetSeconds() );
+ }
+
+ // Compute the lerp between ti and ti+1
+ out = Curve_Interpolate( frac, t, v, curvetypes, GetOwnerLog()->GetMinValue(), GetOwnerLog()->GetMaxValue() );
+}
+
+template<>
+void CDmeTypedLogLayer< Vector >::GetValueUsingCurveInfo( DmeTime_t time, Vector& out ) const
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ {
+ CDmAttributeInfo< Vector >::SetDefaultValue( out );
+ return;
+ }
+
+ Assert( CanInterpolateType( GetDataType() ) );
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+ Assert( IsInterpolableType( GetDataType() ) );
+
+ Vector v[ 4 ];
+ DmeTime_t t[ 4 ];
+ int curvetypes[ 4 ];
+ int ti = FindKey( time );
+ if ( !IsUsingCurveTypes() )
+ {
+ if ( ti < 0 )
+ {
+ CDmAttributeInfo< Vector >::SetDefaultValue( out ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return;
+ }
+ else if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti ];
+ return;
+ }
+ }
+
+ DmeTime_t finalTime = GetTypedOwnerLog()->GetRightEdgeTime();
+ if ( finalTime != DmeTime_t( 0 ) )
+ {
+ if ( time > finalTime )
+ {
+ CDmAttributeInfo< Vector >::SetDefaultValue( out );
+ return;
+ }
+ }
+ else
+ {
+ if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti ];
+ return;
+ }
+ }
+
+ GetBoundedSample( ti - 1, t[ 0 ], v[ 0 ], curvetypes[ 0 ] );
+ GetBoundedSample( ti + 0, t[ 1 ], v[ 1 ], curvetypes[ 1 ] );
+ GetBoundedSample( ti + 1, t[ 2 ], v[ 2 ], curvetypes[ 2 ] );
+ GetBoundedSample( ti + 2, t[ 3 ], v[ 3 ], curvetypes[ 3 ] );
+
+ float frac = 0.0f;
+ if ( t[2] > t[ 1 ] )
+ {
+ frac = (time.GetSeconds() - t[1].GetSeconds()) / (float) ( t[2].GetSeconds() - t[ 1 ].GetSeconds() );
+ }
+
+ // Compute the lerp between ti and ti+1
+ out = Curve_Interpolate( frac, t, v, curvetypes, GetOwnerLog()->GetMinValue(), GetOwnerLog()->GetMaxValue() );
+}
+
+template<>
+void CDmeTypedLogLayer< Quaternion >::GetValueUsingCurveInfo( DmeTime_t time, Quaternion& out ) const
+{
+ Assert( GetOwnerLog() );
+ if ( !GetOwnerLog() )
+ {
+ CDmAttributeInfo< Quaternion >::SetDefaultValue( out );
+ return;
+ }
+
+ Assert( CanInterpolateType( GetDataType() ) );
+ Assert( m_values.Count() == m_times.Count() );
+ Assert( !IsUsingCurveTypes() || ( m_CurveTypes.Count() == m_times.Count() ) );
+ Assert( IsInterpolableType( GetDataType() ) );
+
+ Quaternion v[ 4 ];
+ DmeTime_t t[ 4 ];
+ int curvetypes[ 4 ];
+ int ti = FindKey( time );
+ if ( !IsUsingCurveTypes() )
+ {
+ if ( ti < 0 )
+ {
+ CDmAttributeInfo< Quaternion >::SetDefaultValue( out ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return;
+ }
+ else if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti ];
+ return;
+ }
+ }
+
+ DmeTime_t finalTime = GetTypedOwnerLog()->GetRightEdgeTime();
+ if ( finalTime != DmeTime_t( 0 ) )
+ {
+ if ( time > finalTime )
+ {
+ CDmAttributeInfo< Quaternion >::SetDefaultValue( out );
+ return;
+ }
+ }
+ else
+ {
+ if ( ti >= m_times.Count() - 1 )
+ {
+ out = m_values[ ti ];
+ return;
+ }
+ }
+
+ GetBoundedSample( ti - 1, t[ 0 ], v[ 0 ], curvetypes[ 0 ] );
+ GetBoundedSample( ti + 0, t[ 1 ], v[ 1 ], curvetypes[ 1 ] );
+ GetBoundedSample( ti + 1, t[ 2 ], v[ 2 ], curvetypes[ 2 ] );
+ GetBoundedSample( ti + 2, t[ 3 ], v[ 3 ], curvetypes[ 3 ] );
+
+ float frac = 0.0f;
+ if ( t[2] > t[ 1 ] )
+ {
+ frac = (time.GetSeconds() - t[1].GetSeconds()) / (float) ( t[2].GetSeconds() - t[ 1 ].GetSeconds() );
+ }
+
+ // Compute the lerp between ti and ti+1
+ out = Curve_Interpolate( frac, t, v, curvetypes, GetOwnerLog()->GetMinValue(), GetOwnerLog()->GetMaxValue() );
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::CopyLayer( const CDmeLogLayer *src )
+{
+ const CDmeTypedLogLayer< T > *pSrc = static_cast< const CDmeTypedLogLayer< T > * >( src );
+ m_times = pSrc->m_times;
+ m_lastKey = pSrc->m_lastKey;
+ m_values = pSrc->m_values;
+ m_CurveTypes = pSrc->m_CurveTypes;
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::InsertKeyFromLayer( DmeTime_t keyTime, const CDmeLogLayer *src, DmeTime_t srcKeyTime )
+{
+ const CDmeTypedLogLayer< T > *pSrc = static_cast< const CDmeTypedLogLayer< T > * >( src );
+ Assert( pSrc );
+
+ // NOTE: This copy is necessary if src == this
+ T value = pSrc->GetValue( srcKeyTime );
+ InsertKey( keyTime, value );
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::ExplodeLayer( const CDmeLogLayer *src, DmeTime_t startTime, DmeTime_t endTime, bool bRebaseTimestamps, DmeTime_t tResampleInterval )
+{
+ const CDmeTypedLogLayer< T > *pSrc = static_cast< const CDmeTypedLogLayer< T > * >( src );
+ Assert( pSrc );
+
+ DmeTime_t tTimeOffset = DMETIME_ZERO;
+ if ( bRebaseTimestamps )
+ {
+ tTimeOffset = -startTime;
+ }
+
+ m_times.RemoveAll();
+ m_values.RemoveAll();
+ m_CurveTypes.RemoveAll();
+
+ bool usecurvetypes = pSrc->IsUsingCurveTypes();
+
+ // Now copy the data for the later
+ for ( DmeTime_t t = startTime ; t + tResampleInterval < endTime; t += tResampleInterval )
+ {
+ DmeTime_t keyTime = DmeTime_t( t );
+ if ( keyTime > endTime )
+ {
+ keyTime = endTime;
+ }
+
+ T val = pSrc->GetValue( keyTime );
+
+ keyTime += tTimeOffset;
+
+ InsertKey( keyTime, val, usecurvetypes ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+
+ m_lastKey = m_times.Count() - 1;
+}
+
+template< class T >
+void CDmeTypedLogLayer< T >::CopyPartialLayer( const CDmeLogLayer *src, DmeTime_t startTime, DmeTime_t endTime, bool bRebaseTimestamps )
+{
+ const CDmeTypedLogLayer< T > *pSrc = static_cast< const CDmeTypedLogLayer< T > * >( src );
+ Assert( pSrc );
+
+ int nTimeOffset = 0;
+ if ( bRebaseTimestamps )
+ {
+ nTimeOffset = -startTime.GetTenthsOfMS();
+ }
+
+ m_times.RemoveAll();
+ m_values.RemoveAll();
+ m_CurveTypes.RemoveAll();
+
+ bool usecurvetypes = pSrc->IsUsingCurveTypes();
+
+ // Now copy the data for the later
+ int c = pSrc->m_times.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ DmeTime_t keyTime = DmeTime_t( pSrc->m_times[ i ] );
+ if ( keyTime < startTime || keyTime > endTime )
+ continue;
+
+ m_times.AddToTail( pSrc->m_times[ i ] + nTimeOffset );
+ m_values.AddToTail( pSrc->m_values[ i ] );
+ if ( usecurvetypes )
+ {
+ m_CurveTypes.AddToTail( pSrc->m_CurveTypes[ i ] );
+ }
+ }
+
+ m_lastKey = m_times.Count() - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Creates a log of a specific type
+//-----------------------------------------------------------------------------
+template< class T >
+CDmeLogLayer *CreateLayer< T >( CDmeTypedLog< T > *pOwnerLog )
+{
+ DmFileId_t fileid = pOwnerLog ? pOwnerLog->GetFileId() : DMFILEID_INVALID;
+ CDmeLogLayer *layer = NULL;
+
+ switch ( CDmAttributeInfo<T>::AttributeType() )
+ {
+ case AT_INT:
+ case AT_INT_ARRAY:
+ layer = CreateElement< CDmeIntLogLayer >( "int log", fileid );
+ break;
+ case AT_FLOAT:
+ case AT_FLOAT_ARRAY:
+ layer = CreateElement< CDmeFloatLogLayer >( "float log", fileid );
+ break;
+ case AT_BOOL:
+ case AT_BOOL_ARRAY:
+ layer = CreateElement< CDmeBoolLogLayer >( "bool log", fileid );
+ break;
+ case AT_COLOR:
+ case AT_COLOR_ARRAY:
+ layer = CreateElement< CDmeColorLogLayer >( "color log", fileid );
+ break;
+ case AT_VECTOR2:
+ case AT_VECTOR2_ARRAY:
+ layer = CreateElement< CDmeVector2LogLayer >( "vector2 log", fileid );
+ break;
+ case AT_VECTOR3:
+ case AT_VECTOR3_ARRAY:
+ layer = CreateElement< CDmeVector3LogLayer >( "vector3 log", fileid );
+ break;
+ case AT_VECTOR4:
+ case AT_VECTOR4_ARRAY:
+ layer = CreateElement< CDmeVector4LogLayer >( "vector4 log", fileid );
+ break;
+ case AT_QANGLE:
+ case AT_QANGLE_ARRAY:
+ layer = CreateElement< CDmeQAngleLogLayer >( "qangle log", fileid );
+ break;
+ case AT_QUATERNION:
+ case AT_QUATERNION_ARRAY:
+ layer = CreateElement< CDmeQuaternionLogLayer >( "quaternion log", fileid );
+ break;
+ case AT_VMATRIX:
+ case AT_VMATRIX_ARRAY:
+ layer = CreateElement< CDmeVMatrixLogLayer >( "vmatrix log", fileid );
+ break;
+ case AT_STRING:
+ case AT_STRING_ARRAY:
+ layer = CreateElement< CDmeStringLogLayer >( "string log", fileid );
+ break;
+ }
+
+ if ( layer )
+ {
+ layer->SetOwnerLog( pOwnerLog );
+ }
+ return layer;
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// CDmeCurveInfo - abstract base class
+//
+//-----------------------------------------------------------------------------
+void CDmeCurveInfo::OnConstruction()
+{
+ m_DefaultCurveType.Init( this, "defaultCurveType" );
+
+ m_MinValue.InitAndSet( this, "minvalue", 0.0f );
+ m_MaxValue.InitAndSet( this, "maxvalue", 1.0f );
+}
+
+void CDmeCurveInfo::OnDestruction()
+{
+}
+
+// Global override for all keys unless overriden by specific key
+void CDmeCurveInfo::SetDefaultCurveType( int curveType )
+{
+ m_DefaultCurveType = curveType;
+}
+
+int CDmeCurveInfo::GetDefaultCurveType() const
+{
+ return m_DefaultCurveType.Get();
+}
+
+void CDmeCurveInfo::SetMinValue( float val )
+{
+ m_MinValue = val;
+}
+
+float CDmeCurveInfo::GetMinValue() const
+{
+ return m_MinValue;
+}
+
+void CDmeCurveInfo::SetMaxValue( float val )
+{
+ m_MaxValue = val;
+}
+
+float CDmeCurveInfo::GetMaxValue() const
+{
+ return m_MaxValue;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CDmeTypedCurveInfo - implementation class for all logs
+//
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedCurveInfo< T >::OnConstruction()
+{
+ m_bUseEdgeInfo.Init( this, "useEdgeInfo" );
+ m_DefaultEdgeValue.Init( this, "defaultEdgeZeroValue" );
+ m_RightEdgeTime.Init( this, "rightEdgeTime" );
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ char edgename[ 32 ];
+ Q_snprintf( edgename, sizeof( edgename ), "%s", i == 0 ? "left" : "right" );
+ char name[ 32 ];
+ Q_snprintf( name, sizeof( name ), "%sEdgeActive", edgename );
+ m_bEdgeActive[ i ].Init( this, name );
+ Q_snprintf( name, sizeof( name ), "%sEdgeValue", edgename );
+ m_EdgeValue[ i ].Init( this, name );
+ Q_snprintf( name, sizeof( name ), "%sEdgeCurveType", edgename );
+ m_EdgeCurveType[ i ].Init( this, name );
+ }
+}
+
+template< class T >
+void CDmeTypedCurveInfo< T >::OnDestruction()
+{
+}
+
+template< class T >
+void CDmeTypedCurveInfo< T >::SetUseEdgeInfo( bool state )
+{
+ m_bUseEdgeInfo = state;
+}
+
+template< class T >
+bool CDmeTypedCurveInfo< T >::IsUsingEdgeInfo() const
+{
+ return m_bUseEdgeInfo;
+}
+
+template< class T >
+void CDmeTypedCurveInfo< T >::SetEdgeInfo( int edge, bool active, const T& val, int curveType )
+{
+ SetUseEdgeInfo( true );
+
+ Assert( edge == 0 || edge == 1 );
+
+ m_bEdgeActive[ edge ] = active;
+ m_EdgeValue[ edge ] = val;
+ m_EdgeCurveType[ edge ] = curveType;
+}
+
+template< class T >
+void CDmeTypedCurveInfo< T >::SetDefaultEdgeZeroValue( const T& val )
+{
+ m_DefaultEdgeValue = val;
+}
+
+template< class T >
+const T& CDmeTypedCurveInfo< T >::GetDefaultEdgeZeroValue() const
+{
+ return m_DefaultEdgeValue;
+}
+
+template< class T >
+void CDmeTypedCurveInfo< T >::SetRightEdgeTime( DmeTime_t time )
+{
+ m_RightEdgeTime = time.GetTenthsOfMS();
+}
+
+template< class T >
+DmeTime_t CDmeTypedCurveInfo< T >::GetRightEdgeTime() const
+{
+ return DmeTime_t( m_RightEdgeTime );
+}
+
+template< class T >
+void CDmeTypedCurveInfo< T >::GetEdgeInfo( int edge, bool& active, T& val, int& curveType ) const
+{
+ Assert( IsUsingEdgeInfo() );
+
+ Assert( edge == 0 || edge == 1 );
+
+ active = m_bEdgeActive[ edge ];
+ val = m_EdgeValue[ edge ];
+ curveType = m_EdgeCurveType[ edge ];
+}
+
+template< class T >
+int CDmeTypedCurveInfo< T >::GetEdgeCurveType( int edge ) const
+{
+ Assert( edge == 0 || edge == 1 );
+
+ if ( !m_bEdgeActive[ edge ] )
+ {
+ return m_DefaultCurveType;
+ }
+
+ if ( m_EdgeCurveType[ edge ] == CURVE_DEFAULT )
+ {
+ return m_DefaultCurveType;
+ }
+
+ return m_EdgeCurveType[ edge ];
+}
+
+template<>
+void CDmeTypedCurveInfo<float>::GetZeroValue( int side, float& val ) const
+{
+ if ( !m_bUseEdgeInfo )
+ {
+ val = 0.0f;
+ return;
+ }
+
+ if ( m_bEdgeActive[ side ] )
+ {
+ val = m_EdgeValue[ side ];
+ return;
+ }
+
+ val = m_DefaultEdgeValue;
+}
+
+template<>
+bool CDmeTypedCurveInfo<float>::IsEdgeActive( int edge ) const
+{
+ return m_bEdgeActive[ edge ];
+}
+
+template<>
+void CDmeTypedCurveInfo<float>::GetEdgeValue( int edge, float& value ) const
+{
+ value = m_EdgeValue[ edge ];
+}
+
+template<>
+void CDmeTypedCurveInfo<Vector>::GetZeroValue( int side, Vector& val ) const
+{
+ if ( !m_bUseEdgeInfo )
+ {
+ val = vec3_origin;
+ return;
+ }
+
+ if ( m_bEdgeActive[ side ] )
+ {
+ val = m_EdgeValue[ side ];
+ return;
+ }
+
+ val = m_DefaultEdgeValue;
+}
+
+template<>
+void CDmeTypedCurveInfo<Quaternion>::GetZeroValue( int side, Quaternion& val ) const
+{
+ if ( !m_bUseEdgeInfo )
+ {
+ val.Init();
+ return;
+ }
+
+ if ( m_bEdgeActive[ side ] )
+ {
+ val = m_EdgeValue[ side ];
+ return;
+ }
+
+ val = m_DefaultEdgeValue;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CDmeLog - abstract base class
+//
+//-----------------------------------------------------------------------------
+void CDmeLog::OnConstruction()
+{
+ m_Layers.Init( this, "layers", FATTRIB_MUSTCOPY | FATTRIB_HAS_ARRAY_CALLBACK );
+ m_CurveInfo.Init( this, "curveinfo", FATTRIB_MUSTCOPY | FATTRIB_HAS_CALLBACK );
+}
+
+void CDmeLog::OnDestruction()
+{
+}
+
+int CDmeLog::GetTopmostLayer() const
+{
+ return m_Layers.Count() - 1;
+}
+
+int CDmeLog::GetNumLayers() const
+{
+ return m_Layers.Count();
+}
+
+CDmeLogLayer *CDmeLog::GetLayer( int index )
+{
+ return m_Layers[ index ];
+}
+
+const CDmeLogLayer *CDmeLog::GetLayer( int index ) const
+{
+ return m_Layers[ index ];
+}
+
+bool CDmeLog::IsEmpty() const
+{
+ int c = m_Layers.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeLogLayer* layer = m_Layers[ i ];
+ if ( layer->GetKeyCount() > 0 )
+ return false;
+ }
+ return true;
+}
+
+void CDmeLog::FindLayersForTime( DmeTime_t time, CUtlVector< int >& list ) const
+{
+ list.RemoveAll();
+ int c = m_Layers.Count();
+ // The base layer is always available!!!
+ if ( c > 0 )
+ {
+ list.AddToTail( 0 );
+ }
+ for ( int i = 1; i < c; ++i )
+ {
+ CDmeLogLayer* layer = m_Layers[ i ];
+ DmeTime_t layerStart = layer->GetBeginTime();
+ if ( layerStart == DmeTime_t::MinTime() )
+ continue;
+ DmeTime_t layerEnd = layer->GetEndTime();
+ if ( layerEnd == DmeTime_t::MaxTime() )
+ continue;
+
+ if ( time >= layerStart && time <= layerEnd )
+ {
+ list.AddToTail( i );
+ }
+ }
+}
+
+int CDmeLog::FindLayerForTimeSkippingTopmost( DmeTime_t time ) const
+{
+ int c = m_Layers.Count() - 1; // This makes it never consider the topmost layer!!!
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CDmeLogLayer* layer = m_Layers[ i ];
+ DmeTime_t layerStart = layer->GetBeginTime();
+ if ( layerStart == DmeTime_t::MinTime() )
+ continue;
+ DmeTime_t layerEnd = layer->GetEndTime();
+ if ( layerEnd == DmeTime_t::MaxTime() )
+ continue;
+
+ if ( time >= layerStart && time <= layerEnd )
+ return i;
+ }
+ return ( c > 0 ) ? 0 : -1;
+}
+
+int CDmeLog::FindLayerForTime( DmeTime_t time ) const
+{
+ int c = m_Layers.Count();
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CDmeLogLayer* layer = m_Layers[ i ];
+ DmeTime_t layerStart = layer->GetBeginTime();
+ if ( layerStart == DmeTime_t::MinTime() )
+ continue;
+ DmeTime_t layerEnd = layer->GetEndTime();
+ if ( layerEnd == DmeTime_t::MaxTime() )
+ continue;
+
+ if ( time >= layerStart && time <= layerEnd )
+ return i;
+ }
+ return ( c > 0 ) ? 0 : -1;
+}
+
+DmeTime_t CDmeLog::GetBeginTime() const
+{
+ int c = m_Layers.Count();
+ if ( c == 0 )
+ return DmeTime_t::MinTime();
+
+ DmeTime_t bestMin = DmeTime_t::MinTime();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeLogLayer* layer = m_Layers[ i ];
+ DmeTime_t layerStart = layer->GetBeginTime();
+ if ( layerStart == DmeTime_t::MinTime() )
+ continue;
+
+ if ( bestMin == DmeTime_t::MinTime() )
+ {
+ bestMin = layerStart;
+ }
+ else if ( layerStart < bestMin )
+ {
+ bestMin = layerStart;
+ }
+ }
+
+ return bestMin;
+}
+
+DmeTime_t CDmeLog::GetEndTime() const
+{
+ int c = m_Layers.Count();
+ if ( c == 0 )
+ return DmeTime_t::MaxTime();
+
+ DmeTime_t bestMax = DmeTime_t::MaxTime();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeLogLayer *layer = m_Layers[ i ];
+ DmeTime_t layerEnd = layer->GetEndTime();
+ if ( layerEnd == DmeTime_t::MaxTime() )
+ continue;
+ if ( bestMax == DmeTime_t::MaxTime() )
+ {
+ bestMax = layerEnd;
+ }
+ else if ( layerEnd > bestMax )
+ {
+ bestMax = layerEnd;
+ }
+ }
+
+ return bestMax;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the number of keys
+//-----------------------------------------------------------------------------
+int CDmeLog::GetKeyCount() const
+{
+ int count = 0;
+ int c = m_Layers.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeLogLayer* layer = m_Layers[ i ];
+ int timecount = layer->GetKeyCount();
+ count += timecount;
+ }
+ return count;
+}
+
+
+//-----------------------------------------------------------------------------
+// Scale + bias key times
+//-----------------------------------------------------------------------------
+void CDmeLog::ScaleBiasKeyTimes( double flScale, DmeTime_t nBias )
+{
+ // Don't waste time on the identity transform
+ if ( ( nBias == DMETIME_ZERO ) && ( fabs( flScale - 1.0 ) < 1e-5 ) )
+ return;
+
+ int nCount = GetNumLayers();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeLogLayer *pLayer = GetLayer( i );
+ pLayer->ScaleBiasKeyTimes( flScale, nBias );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Resolve - keeps non-attribute data in sync with attribute data
+//-----------------------------------------------------------------------------
+void CDmeLog::Resolve()
+{
+ int c = m_Layers.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeLogLayer* layer = m_Layers[ i ];
+ layer->SetOwnerLog( this );
+ }
+}
+
+void CDmeLog::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ if ( pAttribute == m_CurveInfo.GetAttribute() )
+ {
+ OnUsingCurveTypesChanged();
+ }
+}
+
+void CDmeLog::OnUsingCurveTypesChanged()
+{
+ int c = m_Layers.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ GetLayer( i )->OnUsingCurveTypesChanged();
+ }
+}
+
+// curve info helpers
+bool CDmeLog::IsUsingCurveTypes() const
+{
+ return m_CurveInfo.GetElement() != NULL;
+}
+
+const CDmeCurveInfo *CDmeLog::GetCurveInfo() const
+{
+ return m_CurveInfo.GetElement();
+}
+
+CDmeCurveInfo *CDmeLog::GetCurveInfo()
+{
+ return m_CurveInfo.GetElement();
+}
+
+// accessors for CurveInfo data
+int CDmeLog::GetDefaultCurveType() const
+{
+ Assert( IsUsingCurveTypes() );
+ return m_CurveInfo->GetDefaultCurveType();
+}
+
+// min/max accessors
+float CDmeLog::GetMinValue() const
+{
+ Assert( IsUsingCurveTypes() );
+ return m_CurveInfo->GetMinValue();
+}
+
+void CDmeLog::SetMinValue( float val )
+{
+ Assert( IsUsingCurveTypes() );
+ m_CurveInfo->SetMinValue( val );
+}
+
+float CDmeLog::GetMaxValue() const
+{
+ Assert( IsUsingCurveTypes() );
+ return m_CurveInfo->GetMaxValue();
+}
+
+void CDmeLog::SetMaxValue( float val )
+{
+ Assert( IsUsingCurveTypes() );
+ m_CurveInfo->SetMaxValue( val );
+}
+
+void CDmeLog::SetKeyCurveType( int nKeyIndex, int curveType )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return;
+
+ GetLayer( bestLayer )->SetKeyCurveType( nKeyIndex, curveType );
+}
+
+int CDmeLog::GetKeyCurveType( int nKeyIndex ) const
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return CURVE_DEFAULT;
+
+ return GetLayer( bestLayer )->GetKeyCurveType( nKeyIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes all keys in a certain time interval
+//-----------------------------------------------------------------------------
+bool CDmeLog::RemoveKeys( DmeTime_t tStartTime, DmeTime_t tEndTime )
+{
+ CDmeLogLayer *pLayer = GetLayer( GetTopmostLayer() );
+
+ int nKeyCount = pLayer->GetKeyCount();
+ int nFirstRemove = -1;
+ int nLastRemove = -1;
+ for ( int nKey = 0; nKey < nKeyCount; ++nKey )
+ {
+ DmeTime_t tKeyTime = pLayer->GetKeyTime( nKey );
+ if ( tKeyTime < tStartTime )
+ continue;
+ if ( tKeyTime > tEndTime )
+ break;
+ if ( nFirstRemove == -1 )
+ {
+ nFirstRemove = nKey;
+ }
+ nLastRemove = nKey;
+ }
+
+ if ( nFirstRemove != -1 )
+ {
+ int nRemoveCount = nLastRemove - nFirstRemove + 1;
+ pLayer->RemoveKey( nFirstRemove, nRemoveCount );
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeTypedLog - implementation class for all logs
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLog< T >::OnConstruction()
+{
+ if ( !g_pDataModel->IsUnserializing() )
+ {
+ // Add the default layer!!!
+ AddNewLayer();
+ Assert( m_Layers.Count() == 1 );
+ }
+
+ m_threshold = s_defaultThreshold;
+
+ m_UseDefaultValue.InitAndSet( this, "usedefaultvalue", false );
+ m_DefaultValue.Init( this, "defaultvalue" );
+}
+
+template< class T >
+void CDmeTypedLog< T >::OnDestruction()
+{
+}
+
+template< class T >
+void CDmeTypedLog< T >::SetDefaultValue( const T& value )
+{
+ m_UseDefaultValue = true;
+ m_DefaultValue.Set( value );
+}
+
+template< class T >
+const T& CDmeTypedLog< T >::GetDefaultValue() const
+{
+ Assert( (bool)m_UseDefaultValue );
+ return m_DefaultValue;
+}
+
+template< class T >
+bool CDmeTypedLog< T >::HasDefaultValue() const
+{
+ return m_UseDefaultValue;
+}
+
+template< class T >
+void CDmeTypedLog< T >::ClearDefaultValue()
+{
+ m_UseDefaultValue = false;
+ T out;
+ CDmAttributeInfo< T >::SetDefaultValue( out );
+ m_DefaultValue.Set( out );
+}
+
+// Only used by undo system!!!
+template< class T >
+void CDmeTypedLog< T >::AddLayerToTail( CDmeLogLayer *layer )
+{
+ Assert( layer );
+ Assert( (static_cast< CDmeTypedLogLayer< T > * >( layer ))->GetTypedOwnerLog() == this );
+ m_Layers.AddToTail( layer );
+}
+
+template< class T >
+CDmeLogLayer *CDmeTypedLog< T >::RemoveLayerFromTail()
+{
+ Assert( m_Layers.Count() >= 1 );
+ CDmeLogLayer *layer = m_Layers[ m_Layers.Count() -1 ];
+ m_Layers.Remove( m_Layers.Count() - 1 );
+ return layer;
+}
+
+template< class T >
+CDmeLogLayer *CDmeTypedLog< T >::RemoveLayer( int iLayer )
+{
+ Assert( m_Layers.IsValidIndex( iLayer ) );
+ CDmeLogLayer *layer = m_Layers[ iLayer ];
+ m_Layers.Remove( iLayer );
+ return layer;
+}
+
+
+template< class T >
+CDmeLogLayer *CDmeTypedLog< T >::AddNewLayer()
+{
+ if ( g_pDataModel->UndoEnabledForElement( this ) )
+ {
+ CUndoLayerAdded<T> *pUndo = new CUndoLayerAdded<T>( "AddNewLayer", this );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ CDisableUndoScopeGuard guard;
+
+ // Now add the layer to the stack!!!
+ CDmeTypedLogLayer< T > *layer = static_cast< CDmeTypedLogLayer< T > * >( CreateLayer<T>( this ) );
+ if ( layer )
+ {
+ layer->SetOwnerLog( this );
+ m_Layers.AddToTail( layer );
+ }
+
+ return layer;
+}
+
+// curve info helpers
+template< class T >
+const CDmeTypedCurveInfo< T > *CDmeTypedLog<T>::GetTypedCurveInfo() const
+{
+ Assert( !m_CurveInfo.GetElement() || dynamic_cast< const CDmeTypedCurveInfo< T > * >( m_CurveInfo.GetElement() ) );
+ return static_cast< const CDmeTypedCurveInfo< T > * >( m_CurveInfo.GetElement() );
+}
+
+template< class T >
+CDmeTypedCurveInfo< T > *CDmeTypedLog<T>::GetTypedCurveInfo()
+{
+ Assert( !m_CurveInfo.GetElement() || dynamic_cast< CDmeTypedCurveInfo< T > * >( m_CurveInfo.GetElement() ) );
+ return static_cast< CDmeTypedCurveInfo< T > * >( m_CurveInfo.GetElement() );
+}
+
+template< class T >
+void CDmeTypedLog<T>::SetCurveInfo( CDmeCurveInfo *pCurveInfo )
+{
+ Assert( !pCurveInfo || dynamic_cast< CDmeTypedCurveInfo< T > * >( pCurveInfo ) );
+ m_CurveInfo = pCurveInfo;
+ OnUsingCurveTypesChanged(); // FIXME: Is this really necessary? OnAttributeChanged should have already called this!
+}
+
+template< class T >
+CDmeCurveInfo *CDmeTypedLog<T>::GetOrCreateCurveInfo()
+{
+ CDmeCurveInfo *pCurveInfo = m_CurveInfo.GetElement();
+ if ( pCurveInfo )
+ return pCurveInfo;
+
+ SetCurveInfo( CreateElement< CDmeTypedCurveInfo< T > >( "curveinfo", GetFileId() ) );
+ return m_CurveInfo.GetElement();
+}
+
+
+
+template < class T >
+struct ActiveLayer_t
+{
+ ActiveLayer_t() :
+ bValid( false ),
+ priority( 0 ),
+ firstTime( 0 ),
+ lastTime( 0 ),
+ layer( NULL )
+ {
+ }
+
+ static bool PriorityLessFunc( ActiveLayer_t< T > * const & lhs, ActiveLayer_t< T > * const & rhs )
+ {
+ return lhs->priority < rhs->priority;
+ }
+
+ int priority; // higher wins
+
+ bool bValid;
+ DmeTime_t firstTime;
+ DmeTime_t lastTime;
+
+ CDmeTypedLogLayer< T > *layer;
+};
+
+template < class T >
+struct LayerEvent_t
+{
+ enum EventType_t
+ {
+ LE_START = 0,
+ LE_END
+ };
+
+ LayerEvent_t() : m_pList( NULL ), m_Type( LE_START ), m_nLayer( 0 ), m_Time( 0 )
+ {
+ }
+
+ static bool LessFunc( const LayerEvent_t& lhs, const LayerEvent_t& rhs )
+ {
+ return lhs.m_Time < rhs.m_Time;
+ }
+
+ CUtlVector< ActiveLayer_t< T > > *m_pList;
+ EventType_t m_Type;
+ int m_nLayer;
+ DmeTime_t m_Time;
+ T m_NeighborValue;
+};
+
+template< class T >
+static const T& GetActiveLayerValue( CUtlVector< ActiveLayer_t< T > > &layerlist, DmeTime_t t, int nTopmostLayer )
+{
+ int nCount = layerlist.Count();
+#ifdef _DEBUG
+ Assert( nCount >= nTopmostLayer );
+#endif
+
+ for ( int i = nTopmostLayer; i >= 0; --i )
+ {
+ ActiveLayer_t< T > &layer = layerlist[i];
+ if ( layer.firstTime > t || layer.lastTime < t )
+ continue;
+
+ return layer.layer->GetValue( t );
+ }
+
+ if ( nCount != 0 )
+ {
+ const CDmeTypedLog< T > *pOwner = layerlist[0].layer->GetTypedOwnerLog();
+ if ( pOwner->HasDefaultValue() )
+ return pOwner->GetDefaultValue();
+ }
+
+ static T defaultVal;
+ CDmAttributeInfo<T>::SetDefaultValue( defaultVal );
+ return defaultVal;
+}
+
+
+template< class T >
+static void SpewEvents( CUtlRBTree< LayerEvent_t< T > > &events )
+{
+ for ( unsigned short idx = events.FirstInorder(); idx != events.InvalidIndex(); idx = events.NextInorder( idx ) )
+ {
+ LayerEvent_t< T > *pEvent = &events[ idx ];
+ Msg( "Event %u layer %i at time %i type %s\n",
+ (unsigned)idx, pEvent->m_nLayer, pEvent->m_Time.GetTenthsOfMS(), pEvent->m_Type == LayerEvent_t< T >::LE_START ? "start" : "end" );
+ }
+}
+
+template< class T >
+inline void SpewKey( const T& )
+{
+ Msg( "GenericType" );
+}
+
+template<>
+inline void SpewKey<float>( const float& val )
+{
+ Msg( "%f", val );
+}
+
+template<>
+inline void SpewKey<int>( const int& val )
+{
+ Msg( "%d", val );
+}
+
+template<>
+inline void SpewKey<Vector2D>( const Vector2D& val )
+{
+ Msg( "%f,%f", val.x, val.y );
+}
+
+template<>
+inline void SpewKey<Vector4D>( const Vector4D& val )
+{
+ Msg( "%f,%f,%f,%f", val.x, val.y, val.z, val.w );
+}
+
+template<>
+inline void SpewKey<DmeTime_t>( const DmeTime_t& val )
+{
+ Msg( "%d", val.GetTenthsOfMS() );
+}
+
+template<>
+inline void SpewKey<bool>( const bool& val )
+{
+ Msg( "%s", val ? "true" : "false" );
+}
+
+template<>
+inline void SpewKey<Color>( const Color& val )
+{
+ Msg( "%08x", val.GetRawColor() );
+}
+
+template< >
+inline void SpewKey( const Vector& val )
+{
+ Msg( "[%f %f %f]", val.x, val.y, val.z );
+}
+
+template< >
+inline void SpewKey( const Quaternion& val )
+{
+ Msg( "[%f %f %f %f]", val.x, val.y, val.z, val.w );
+}
+
+template< class T >
+static void SpewFlattenedKey( CDmeTypedLogLayer< T > *pLogLayer, ActiveLayer_t< T > *pActiveLayer, DmeTime_t t, const T& val )
+{
+ Msg( "Layer %d: adding key at time %f [%d -> %d], value ",
+ pActiveLayer->priority, t.GetSeconds(), pActiveLayer->firstTime.GetTenthsOfMS(), pActiveLayer->lastTime.GetTenthsOfMS() );
+ SpewKey( val );
+ Msg( "\n" );
+}
+
+template< class T >
+static void ComputeLayerEvents( CDmeTypedLog< T >* pLog,
+ CUtlVector< ActiveLayer_t< T > > &layerlist,
+ CUtlRBTree< LayerEvent_t< T > > &events )
+{
+ // Build a list of all known layers and a sorted list of layer "transitions"
+ for ( int i = 0; i < pLog->GetNumLayers(); ++i )
+ {
+ ActiveLayer_t< T > layer;
+ layer.priority = i;
+ layer.layer = static_cast< CDmeTypedLogLayer< T > * >( pLog->GetLayer( i ) );
+ layer.firstTime = layer.layer->GetBeginTime();
+ layer.lastTime = layer.layer->GetEndTime();
+ layer.bValid = true;
+
+ if ( ( layer.firstTime == DMETIME_MINTIME || layer.lastTime == DMETIME_MAXTIME ) && ( i > 0 ) ) // Base layer is always valid
+ {
+ layer.bValid = false;
+ }
+
+ // Skip invalid layers
+ if ( !layer.bValid )
+ continue;
+
+ // Layer zero can capture everything from above...
+ if ( i == 0 )
+ {
+ layer.firstTime = DmeTime_t::MinTime();
+ layer.lastTime = DmeTime_t::MaxTime();
+ }
+
+ // Add layer to global list
+ int nIndex = layerlist.AddToTail( layer );
+
+ // Add layer start/end events
+ DmeTime_t tNeighbor = ( layer.firstTime != DMETIME_MINTIME ) ? ( layer.firstTime - DMETIME_MINDELTA ) : DMETIME_MINTIME;
+ LayerEvent_t< T > start;
+ start.m_pList = &layerlist;
+ start.m_nLayer = nIndex;
+ start.m_Type = LayerEvent_t< T >::LE_START;
+ start.m_Time = layer.firstTime;
+ start.m_NeighborValue = GetActiveLayerValue( layerlist, tNeighbor, nIndex - 1 );
+ events.Insert( start );
+
+ tNeighbor = ( layer.lastTime != DMETIME_MAXTIME ) ? ( layer.lastTime + DMETIME_MINDELTA ) : DMETIME_MAXTIME;
+ LayerEvent_t< T > end;
+ end.m_pList = &layerlist;
+ end.m_nLayer = nIndex;
+ end.m_Type = LayerEvent_t< T >::LE_END;
+ end.m_Time = layer.lastTime;
+ end.m_NeighborValue = GetActiveLayerValue( layerlist, tNeighbor, nIndex - 1 );
+ events.Insert( end );
+ }
+}
+
+
+template< class T >
+static void AddDiscontinitySample( CDmeTypedLogLayer< T > *pTargetLayer, CDmeTypedLog< T > *pLog, DmeTime_t tKeyTime, const T& val, const char *pSpewLabel )
+{
+ // Finally, add a helper key
+ if ( pLog->IsUsingCurveTypes() )
+ {
+ if ( pSpewLabel )
+ {
+ Msg( "Adding %s helper key at %d value ", pSpewLabel, tKeyTime.GetTenthsOfMS() );
+ SpewKey( val );
+ Msg( " [curvetype %s]\n", Interpolator_NameForCurveType( pLog->GetDefaultCurveType(), false ) );
+ }
+ pTargetLayer->SetKey( tKeyTime, val, pLog->GetDefaultCurveType() );
+ }
+ else
+ {
+ if ( pSpewLabel )
+ {
+ Msg( "Adding %s helper key at %d value ", pSpewLabel, tKeyTime.GetTenthsOfMS() );
+ SpewKey( val );
+ Msg( "\n" );
+ }
+ pTargetLayer->SetKey( tKeyTime, val );
+ }
+}
+
+
+template< class T >
+static DmeTime_t ProcessStartLayerStartEvent(
+ bool bSpew,
+ bool bFixupDiscontinuities,
+ CDmeTypedLog< T > *pLog,
+ LayerEvent_t< T > *pEvent,
+ CUtlVector< ActiveLayer_t< T > > &layerlist,
+ CUtlRBTree< ActiveLayer_t< T > * > &active,
+ CDmeTypedLogLayer< T > *flattenedlayer )
+{
+ Assert( pEvent->m_Type == LayerEvent_t< T >::LE_START );
+
+ // Push it onto the active stack if it's not already on the stack
+ if ( active.Find( &layerlist[ pEvent->m_nLayer ] ) != active.InvalidIndex() )
+ return pEvent->m_Time;
+
+ if ( bSpew )
+ {
+ Msg( "adding layer %d to stack\n", layerlist[ pEvent->m_nLayer ].priority );
+ }
+ active.Insert( &layerlist[ pEvent->m_nLayer ] );
+
+ if ( !bFixupDiscontinuities || ( pEvent->m_Time == DMETIME_MINTIME ) )
+ return pEvent->m_Time;
+
+ // We'll need to add 2 new "discontinuity" fixup samples.
+ // 1) A sample from the base layer @ start time - .1 msec
+ // 2) A sample from the new layer @ start time
+ int nActiveCount = active.Count();
+ if ( nActiveCount >= 2 )
+ {
+ DmeTime_t tKeyTime = pEvent->m_Time - DmeTime_t( 1 );
+ AddDiscontinitySample( flattenedlayer, pLog, tKeyTime, pEvent->m_NeighborValue, bSpew ? "start" : NULL );
+ }
+ AddDiscontinitySample( flattenedlayer, pLog, pEvent->m_Time, GetActiveLayerValue( layerlist, pEvent->m_Time, pEvent->m_nLayer ), bSpew ? "start" : NULL );
+ return pEvent->m_Time;
+}
+
+template< class T >
+static DmeTime_t ProcessStartLayerEndEvent(
+ bool bSpew,
+ bool bFixupDiscontinuities,
+ CDmeTypedLog< T > *pLog,
+ LayerEvent_t< T > *pEvent,
+ CUtlVector< ActiveLayer_t< T > > &layerlist,
+ CUtlRBTree< ActiveLayer_t< T > * > &active,
+ CDmeTypedLogLayer< T > *pBaseLayer )
+{
+ Assert( pEvent->m_Type == LayerEvent_t< T >::LE_END );
+
+ // Push it onto the active stack if it's not already on the stack
+ if ( bSpew )
+ {
+ Msg( "removing layer %d from stack\n", layerlist[ pEvent->m_nLayer ].priority );
+ }
+
+ // We'll need to add a "discontinuity" fixup sample from the
+ // 1) A sample from the ending layer @ start time
+ // 2) A sample from the new layer @ start time + .1 msec
+ // NOTE: This will cause problems if there are non-default value keys at max time
+ Assert( active.Count() >= 1 );
+ if ( bFixupDiscontinuities && ( pEvent->m_Time != DMETIME_MAXTIME ) )
+ {
+ AddDiscontinitySample( pBaseLayer, pLog, pEvent->m_Time, GetActiveLayerValue( layerlist, pEvent->m_Time, pEvent->m_nLayer ), bSpew ? "end" : NULL );
+ if ( active.Count() >= 2 )
+ {
+ DmeTime_t keyTime = pEvent->m_Time + DmeTime_t( 1 );
+ AddDiscontinitySample( pBaseLayer, pLog, keyTime, pEvent->m_NeighborValue, bSpew ? "end" : NULL );
+ }
+ }
+
+ active.Remove( &layerlist[ pEvent->m_nLayer ] );
+ return ( active.Count() >= 2 ) ? pEvent->m_Time + DmeTime_t( 1 ) : pEvent->m_Time;
+}
+
+template< class T >
+void CDmeTypedLog< T >::FlattenLayers( float threshold, int flags )
+{
+ // Already flattened
+ if ( m_Layers.Count() <= 1 )
+ return;
+
+ if ( g_pDataModel->UndoEnabledForElement( this ) )
+ {
+ CUndoFlattenLayers<T> *pUndo = new CUndoFlattenLayers<T>( "FlattenLayers", this, threshold, flags );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ bool bSpew = ( flags & FLATTEN_SPEW ) != 0;
+ bool bFixupDiscontinuities = true; //( flags & FLATTEN_NODISCONTINUITY_FIXUP ) == 0;
+
+ // NOTE: UNDO IS DISABLED FOR THE REST OF THIS OPERATION (the above function does what we need to preserve the layers)
+ CDisableUndoScopeGuard guard;
+
+ CDmeTypedLogLayer< T > *flattenedlayer = static_cast< CDmeTypedLogLayer< T > * >( CreateLayer< T >( this ) );
+ flattenedlayer->SetOwnerLog( this );
+
+ // Global list of layers
+ CUtlVector< ActiveLayer_t< T > > layerlist;
+ // List of all start/end layer events, sorted by the time at which the event occurs ( we walk this list in order )
+ CUtlRBTree< LayerEvent_t< T > > events( 0, 0, LayerEvent_t< T >::LessFunc );
+ // Stack of active events, sorted by event "priority", which means last item is the one writing data into the new base layer
+ CUtlRBTree< ActiveLayer_t< T > * > active( 0, 0, ActiveLayer_t< T >::PriorityLessFunc );
+
+ // Build layer list and list of start/end events and times
+ ComputeLayerEvents( this, layerlist, events );
+
+ // Debuggins
+ if ( bSpew )
+ {
+ SpewEvents( events );
+ }
+
+ // Now walk from the earliest time in any layer until the latest time, going key by key and checking if the active layer should change as we go
+
+ DmeTime_t iCurrentKeyTime = DmeTime_t::MinTime();
+ unsigned short idx = events.FirstInorder();
+ while ( 1 )
+ {
+ if ( idx == events.InvalidIndex() )
+ break;
+
+ LayerEvent_t< T > *pEvent = &events[ idx ];
+
+ switch ( pEvent->m_Type )
+ {
+ default:
+ iCurrentKeyTime = pEvent->m_Time;
+ Assert( 0 );
+ break;
+ case LayerEvent_t< T >::LE_START:
+ iCurrentKeyTime = ProcessStartLayerStartEvent( bSpew, bFixupDiscontinuities, this, pEvent, layerlist, active, flattenedlayer );
+ break;
+
+ case LayerEvent_t< T >::LE_END:
+ iCurrentKeyTime = ProcessStartLayerEndEvent( bSpew, bFixupDiscontinuities, this, pEvent, layerlist, active, flattenedlayer );
+ break;
+ }
+
+ int nNextIndex = events.NextInorder( idx );
+
+ // We popped the last item off the stack
+ if ( nNextIndex == events.InvalidIndex() )
+ {
+ Assert( active.Count() == 0 );
+ break;
+ }
+
+ // Walk from current time up to the time of the next relevant event
+ LayerEvent_t< T > *nextevent = &events[ nNextIndex ];
+ DmeTime_t layerFinishTime = nextevent->m_Time;
+
+ // The topmost layer is the active layer
+ int layernum = active.LastInorder();
+ if ( layernum == active.InvalidIndex() )
+ break;
+
+ ActiveLayer_t< T > *activeLayer = active[ layernum ];
+ CDmeTypedLogLayer< T > *loglayer = activeLayer->layer;
+
+ // Splat all keys betweeen the current head position and the next event time (layerFinishTime) into the flattened layer
+ int keyCount = loglayer->GetKeyCount();
+ for ( int j = 0; j < keyCount; ++j )
+ {
+ DmeTime_t keyTime = loglayer->GetKeyTime( j );
+ // Key is too early, skip
+ if ( keyTime < iCurrentKeyTime )
+ continue;
+
+ // Done with this layer, set time exactly equal to end time so next layer can take over
+ // at the correct spot
+ if ( keyTime >= layerFinishTime )
+ {
+ iCurrentKeyTime = layerFinishTime;
+ break;
+ }
+
+ // Advance the head position
+ iCurrentKeyTime = keyTime;
+
+ // Because it's a key, the interpolated value should == the actual value (not true for certain 4 point curve types, but we shouldn't support them
+ // for this type of operation anyway)
+ const T& val = loglayer->GetKeyValue( j );
+
+ // Debugging spew
+ if ( bSpew )
+ {
+ SpewFlattenedKey( loglayer, activeLayer, iCurrentKeyTime, val );
+ }
+
+ // Now set the key into the flattened layer
+ flattenedlayer->SetKey( iCurrentKeyTime, val, loglayer->IsUsingCurveTypes() ? loglayer->GetKeyCurveType( j ) : CURVE_DEFAULT );
+ }
+ idx = nNextIndex;
+ }
+
+ // Blow away all of the existing layers except the original base layer
+ while ( GetNumLayers() > 1 )
+ {
+ CDmeTypedLogLayer< T > *layer = static_cast< CDmeTypedLogLayer< T > * >( RemoveLayerFromTail() );
+ g_pDataModel->DestroyElement( layer->GetHandle() );
+ }
+
+ // Compress the flattened layer
+ flattenedlayer->RemoveRedundantKeys( threshold );
+
+ // Copy the flattened layer over the existing base layer
+ GetLayer( 0 )->CopyLayer( flattenedlayer );
+
+ g_pDataModel->DestroyElement( flattenedlayer->GetHandle() );
+}
+
+template< class T >
+void CDmeTypedLog< T >::StampKeyAtHead( DmeTime_t tHeadPosition, DmeTime_t tPreviousHeadPosition, const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ )
+{
+ DmAttributeType_t type = pAttr->GetType();
+ if ( IsValueType( type ) )
+ {
+ Assert( pAttr->GetType() == GetDataType() );
+ StampKeyAtHead( tHeadPosition, tPreviousHeadPosition, params, pAttr->GetValue< T >() );
+ }
+ else if ( IsArrayType( type ) )
+ {
+ Assert( ArrayTypeToValueType( type ) == GetDataType() );
+ CDmrArrayConst<T> array( pAttr );
+ StampKeyAtHead( tHeadPosition, tPreviousHeadPosition, params, array[ index ] );
+ }
+ else
+ {
+ Assert( 0 );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::FinishTimeSelection( DmeTime_t tHeadPosition, DmeLog_TimeSelection_t& params )
+{
+ bool bWasAdvancing = params.IsTimeAdvancing();
+ params.ResetTimeAdvancing();
+
+ if ( !params.m_bAttachedMode )
+ return;
+
+ if ( !bWasAdvancing )
+ return;
+
+ // Should be in "layer recording" mode!!!
+ Assert( GetNumLayers() >= 2 );
+ int nBestLayer = GetTopmostLayer(); // Topmost should be at least layer # 1 (0 is the base layer)
+ if ( nBestLayer < 1 )
+ return;
+
+ CDmeTypedLogLayer< T > *pWriteLayer = GetLayer( nBestLayer );
+ Assert( pWriteLayer );
+ if ( !pWriteLayer )
+ return;
+
+ int nKeyCount = pWriteLayer->GetKeyCount();
+ if ( nKeyCount <= 0 )
+ return;
+
+ // The head is considered to be at the "last" value
+ T headValue = pWriteLayer->GetKeyValue( nKeyCount - 1 );
+ _StampKeyAtHeadResample( tHeadPosition, params, headValue, true, false );
+}
+
+template< >
+float CDmeTypedLog< float >::ClampValue( const float& value )
+{
+ float retval;
+ if ( !IsUsingCurveTypes() )
+ {
+ retval = clamp( value, 0.0f, 1.0f );
+ }
+ else
+ {
+ retval = clamp( value, GetMinValue(), GetMaxValue() );
+ }
+ return retval;
+}
+
+template< class T >
+void CDmeTypedLog< T >::StampKeyAtHead( DmeTime_t tHeadPosition, DmeTime_t tPreviousHeadPosition, const DmeLog_TimeSelection_t& params, const T& value )
+{
+ //T useValue = ClampValue( value );
+
+ // This gets set if time ever starts moving (even if the user pauses time while still holding a slider)
+ if ( params.IsTimeAdvancing() )
+ {
+ // This uses the time selection as a "filter" to decide whether to stamp a new key at the current position
+ _StampKeyAtHeadFilteredByTimeSelection( tHeadPosition, tPreviousHeadPosition, params, value );
+ }
+ else
+ {
+ Assert( params.m_bResampleMode );
+ _StampKeyAtHeadResample( tHeadPosition, params, value, false, true );
+ }
+}
+
+/*
+template<>
+void CDmeTypedLog< float >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const float& value );
+template<>
+void CDmeTypedLog< bool >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const bool& value );
+template<>
+void CDmeTypedLog< Color >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Color& value );
+template<>
+void CDmeTypedLog< Vector4D >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Vector4D& value );
+template<>
+void CDmeTypedLog< Vector2D >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Vector2D& value );
+template<>
+void CDmeTypedLog< VMatrix >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const VMatrix& value );
+template<>
+void CDmeTypedLog< Quaternion >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Quaternion& value );
+template<>
+void CDmeTypedLog< QAngle >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const QAngle& value );
+*/
+
+
+//-----------------------------------------------------------------------------
+// Helper class used to compute falloff blend factors
+//-----------------------------------------------------------------------------
+template< class T >
+struct LogClampHelper_t
+{
+public:
+ LogClampHelper_t() : m_tLastTime( DMETIME_MINTIME ) {}
+
+ DmeTime_t m_tLastTime;
+ T m_LastUnclampedValue;
+};
+
+
+template< class T >
+class CLogFalloffBlend
+{
+public:
+ void Init( CDmeTypedLog<T> *pLog, DmeTime_t tFalloff, DmeTime_t tHold, bool bLeftFalloff, int nInterpolatorType, const T& delta );
+ void Init( CDmeTypedLog<T> *pLog, const T& delta );
+ float ComputeBlendFactor( DmeTime_t tTime, const T& oldVal, bool bUsingInterpolation ) const;
+ const T& GetDelta() const;
+ void StampKey( CDmeTypedLogLayer<T>* pWriteLayer, DmeTime_t t, const CDmeTypedLogLayer<T>* pReadLayer, float flIntensity, LogClampHelper_t<T> &helper, bool bSpew, const T* pInterpTarget );
+ void UpdateClampHelper( DmeTime_t t, const CDmeTypedLogLayer<T>* pReadLayer, float flIntensity, LogClampHelper_t<T> &helper, const T* pInterpTarget );
+
+private:
+ void ComputeDelta( CDmeTypedLog<T> *pLog, const T& delta, const T& holdValue );
+ void InsertClampTransitionPoints( CDmeTypedLogLayer<T>* pWriteLayer, DmeTime_t t, LogClampHelper_t<T> &clampHelper, const T& val, bool bSpew );
+ void ComputeBounds( CDmeTypedLog<T> *pLog );
+
+ T m_BaseValue;
+ T m_Delta;
+ DmeTime_t m_tBaseTime;
+ DmeTime_t m_tHoldTime;
+ float m_flOOTime;
+ int m_nTestSign;
+ int m_nInterpolatorType;
+ int m_nCurveType;
+ T m_MinValue;
+ T m_MaxValue;
+ bool m_bHold;
+};
+
+template< class T >
+void CLogFalloffBlend< T >::Init( CDmeTypedLog<T> *pLog, DmeTime_t tFalloffTime, DmeTime_t tHoldTime, bool bLeftFalloff, int nInterpolatorType, const T& delta )
+{
+ m_tBaseTime = tFalloffTime;
+ m_tHoldTime = tHoldTime;
+ m_BaseValue = pLog->GetValueSkippingTopmostLayer( tFalloffTime );
+ T holdValue = pLog->GetValueSkippingTopmostLayer( tHoldTime );
+ m_nTestSign = bLeftFalloff ? 1 : -1;
+ m_nInterpolatorType = nInterpolatorType;
+ m_bHold = false;
+ m_nCurveType = pLog->IsUsingCurveTypes() ? pLog->GetDefaultCurveType() : CURVE_DEFAULT;
+
+ float flDuration = tHoldTime.GetSeconds() - tFalloffTime.GetSeconds();
+ m_flOOTime = ( flDuration != 0.0f ) ? 1.0f / flDuration : 0.0f;
+ ComputeBounds( pLog );
+ ComputeDelta( pLog, delta, holdValue );
+}
+
+template< class T >
+void CLogFalloffBlend< T >::Init( CDmeTypedLog<T> *pLog, const T& delta )
+{
+ m_nTestSign = 0;
+ m_nInterpolatorType = INTERPOLATE_DEFAULT;
+ m_bHold = true;
+ m_nCurveType = pLog->IsUsingCurveTypes() ? pLog->GetDefaultCurveType() : CURVE_DEFAULT;
+ m_Delta = delta;
+ ComputeBounds( pLog );
+}
+
+template< class T >
+void CLogFalloffBlend< T >::ComputeBounds( CDmeTypedLog<T> *pLog )
+{
+}
+
+template<>
+void CLogFalloffBlend< float >::ComputeBounds( CDmeTypedLog<float> *pLog )
+{
+ m_MinValue = pLog->IsUsingCurveTypes() ? pLog->GetMinValue() : 0.0f;
+ m_MaxValue = pLog->IsUsingCurveTypes() ? pLog->GetMaxValue() : 1.0f;
+}
+
+template< class T >
+void CLogFalloffBlend< T >::ComputeDelta( CDmeTypedLog<T> *pLog, const T& delta, const T& holdValue )
+{
+ // By default, no clamping
+ m_Delta = delta;
+}
+
+template<>
+void CLogFalloffBlend< float >::ComputeDelta( CDmeTypedLog<float> *pLog, const float& delta, const float& holdValue )
+{
+ if ( LengthOf( delta ) > 0.0f )
+ {
+ m_Delta = min( delta, m_MaxValue - holdValue ); // Max amount we can move up...
+ }
+ else
+ {
+ m_Delta = max( delta, m_MinValue - holdValue ); // Amount we can move down...
+ }
+}
+
+
+template< class T >
+float CLogFalloffBlend< T >::ComputeBlendFactor( DmeTime_t tTime, const T& oldVal, bool bUsingInterpolation ) const
+{
+ if ( m_bHold )
+ return 1.0f;
+
+ // Clamp inside region; hold time beats base time (for zero width regions)
+ if ( ( tTime - m_tHoldTime ) * m_nTestSign >= DMETIME_ZERO )
+ return 1.0f;
+
+ if ( ( tTime - m_tBaseTime ) * m_nTestSign <= DMETIME_ZERO )
+ return 0.0f;
+
+ float flFactor = ( tTime.GetSeconds() - m_tBaseTime.GetSeconds() ) * m_flOOTime;
+ return ComputeInterpolationFactor( flFactor, m_nInterpolatorType );
+}
+
+template< class T >
+const T& CLogFalloffBlend< T >::GetDelta( ) const
+{
+ return m_Delta;
+}
+
+
+//-----------------------------------------------------------------------------
+// Insert points where clamping begins or ends
+//-----------------------------------------------------------------------------
+template< class T >
+void CLogFalloffBlend< T >::InsertClampTransitionPoints( CDmeTypedLogLayer<T>* pWriteLayer,
+ DmeTime_t t, LogClampHelper_t<T> &clampHelper, const T& val, bool bSpew )
+{
+ // NOTE: By default, nothing clamps, so no transition points are needed
+}
+
+template<>
+void CLogFalloffBlend< float >::InsertClampTransitionPoints( CDmeTypedLogLayer<float>* pWriteLayer,
+ DmeTime_t t, LogClampHelper_t<float> &clampHelper, const float& val, bool bSpew )
+{
+ bool bLastLess, bLastGreater, bCurrLess, bCurrGreater;
+ DmeTime_t tCrossing, tDuration;
+ double flOODv;
+
+ // First time through? cache last values.
+ if ( clampHelper.m_tLastTime == DMETIME_MINTIME )
+ goto cacheLastValues;
+
+ bLastLess = clampHelper.m_LastUnclampedValue < m_MinValue;
+ bLastGreater = clampHelper.m_LastUnclampedValue > m_MaxValue;
+ bCurrLess = val < m_MinValue;
+ bCurrGreater = val > m_MaxValue;
+ if ( bLastLess == bCurrLess && bLastGreater == bCurrGreater )
+ goto cacheLastValues;
+
+ // NOTE: The check above means val != m_LastUnclampedValue
+ flOODv = 1.0 / ( val - clampHelper.m_LastUnclampedValue );
+ tDuration = t - clampHelper.m_tLastTime;
+
+ // NOTE: Clamp semantics here favor keeping the non-clamped value
+ // That's why when we start outside + end inside, we never overwrite the dest
+ // and why when we start inside + end outside, we never overwrite the start
+ // These two checks deal with starting outside + heading inside
+ if ( bLastLess && !bCurrLess )
+ {
+ // Insert at min crossing
+ double flFactor = ( m_MinValue - clampHelper.m_LastUnclampedValue ) * flOODv;
+ tCrossing = clampHelper.m_tLastTime + tDuration * flFactor;
+ tCrossing.Clamp( clampHelper.m_tLastTime, t - DMETIME_MINDELTA );
+ pWriteLayer->InsertKey( tCrossing, m_MinValue, m_nCurveType );
+ if ( bSpew )
+ {
+ Msg(" Clamp Crossing Key: %d %f\n", tCrossing.GetTenthsOfMS(), m_MinValue );
+ }
+ }
+ else if ( bLastGreater && !bCurrGreater )
+ {
+ // Insert at max crossing
+ double flFactor = ( m_MaxValue - clampHelper.m_LastUnclampedValue ) * flOODv;
+ tCrossing = clampHelper.m_tLastTime + tDuration * flFactor;
+ tCrossing.Clamp( clampHelper.m_tLastTime, t - DMETIME_MINDELTA );
+ pWriteLayer->InsertKey( tCrossing, m_MaxValue, m_nCurveType );
+ if ( bSpew )
+ {
+ Msg(" Clamp Crossing Key: %d %f\n", tCrossing.GetTenthsOfMS(), m_MaxValue );
+ }
+ }
+
+ // These two checks deal with starting inside + heading outside
+ if ( !bLastLess && bCurrLess )
+ {
+ // Insert at min crossing
+ // NOTE: Clamp semantics here favor keeping the non-clamped value
+ double flFactor = ( m_MinValue - clampHelper.m_LastUnclampedValue ) * flOODv;
+ tCrossing = clampHelper.m_tLastTime + tDuration * flFactor;
+ tCrossing.Clamp( clampHelper.m_tLastTime + DMETIME_MINDELTA, t );
+ pWriteLayer->InsertKey( tCrossing, m_MinValue, m_nCurveType );
+ if ( bSpew )
+ {
+ Msg(" Clamp Crossing Key: %d %f\n", tCrossing.GetTenthsOfMS(), m_MinValue );
+ }
+ }
+ else if ( !bLastGreater && bCurrGreater )
+ {
+ // Insert at max crossing
+ double flFactor = ( m_MaxValue - clampHelper.m_LastUnclampedValue ) * flOODv;
+ tCrossing = clampHelper.m_tLastTime + tDuration * flFactor;
+ tCrossing.Clamp( clampHelper.m_tLastTime + DMETIME_MINDELTA, t );
+ pWriteLayer->InsertKey( tCrossing, m_MaxValue, m_nCurveType );
+ if ( bSpew )
+ {
+ Msg(" Clamp Crossing Key: %d %f\n", tCrossing.GetTenthsOfMS(), m_MaxValue );
+ }
+ }
+
+ // Cache off the last values
+cacheLastValues:
+ clampHelper.m_tLastTime = t;
+ clampHelper.m_LastUnclampedValue = val;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Stamp the key at the specified time
+//-----------------------------------------------------------------------------
+template< class T >
+void CLogFalloffBlend< T >::StampKey( CDmeTypedLogLayer<T>* pWriteLayer, DmeTime_t t,
+ const CDmeTypedLogLayer<T>* pReadLayer, float flIntensity, LogClampHelper_t<T> &clampHelper, bool bSpew, const T* pInterpTarget )
+{
+ // Stamp the key at the current time
+ T oldVal = pReadLayer->GetValue( t );
+
+ // In the falloff area
+ float flFactor = ComputeBlendFactor( t, oldVal, ( pInterpTarget != NULL ) );
+ flFactor *= flIntensity;
+
+ T newVal;
+ if ( !pInterpTarget )
+ {
+ newVal = ScaleValue( m_Delta, flFactor );
+ newVal = Add( oldVal, newVal );
+ }
+ else
+ {
+ newVal = Interpolate( flFactor, oldVal, *pInterpTarget );
+ }
+
+ InsertClampTransitionPoints( pWriteLayer, t, clampHelper, newVal, bSpew );
+
+ T clampedVal = pWriteLayer->GetTypedOwnerLog()->ClampValue( newVal );
+
+ // Add a key to the new "layer" at this time with this value
+ pWriteLayer->InsertKey( t, clampedVal, m_nCurveType );
+
+ if ( bSpew )
+ {
+ Msg(" Key: %d ", t.GetTenthsOfMS() );
+ SpewKey( clampedVal );
+ Msg(" [" );
+ SpewKey( newVal );
+ Msg( "]\n" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Stamp the key at the specified time
+//-----------------------------------------------------------------------------
+template< class T >
+void CLogFalloffBlend< T >::UpdateClampHelper( DmeTime_t t, const CDmeTypedLogLayer<T>* pReadLayer,
+ float flIntensity, LogClampHelper_t<T> &clampHelper, const T* pInterpTarget )
+{
+ // Stamp the key at the current time
+ T oldVal = pReadLayer->GetValue( t );
+
+ // In the falloff area
+ float flFactor = ComputeBlendFactor( t, oldVal, ( pInterpTarget != NULL ) );
+ flFactor *= flIntensity;
+
+ T val;
+ if ( !pInterpTarget )
+ {
+ val = ScaleValue( m_Delta, flFactor );
+ val = Add( oldVal, val );
+ }
+ else
+ {
+ val = Interpolate( flFactor, oldVal, *pInterpTarget );
+ }
+
+ clampHelper.m_tLastTime = t;
+ clampHelper.m_LastUnclampedValue = val;
+}
+
+
+//-----------------------------------------------------------------------------
+// This is used to modify the entire portion of the curve under the time selection
+//-----------------------------------------------------------------------------
+static inline DmeTime_t ComputeResampleStartTime( const DmeLog_TimeSelection_t &params, int nSide )
+{
+ // NOTE: This logic will place the resampled points centered in the falloff regions
+ DmeTimeSelectionTimes_t start = ( nSide == 0 ) ? TS_LEFT_FALLOFF : TS_RIGHT_HOLD;
+ DmeTimeSelectionTimes_t end = ( nSide == 0 ) ? TS_LEFT_HOLD : TS_RIGHT_FALLOFF;
+
+ if ( params.m_nFalloffInterpolatorTypes[nSide] != INTERPOLATE_LINEAR_INTERP )
+ {
+ DmeTime_t tDuration = params.m_nTimes[end] - params.m_nTimes[start];
+ if ( tDuration > params.m_nResampleInterval )
+ {
+ int nFactor = tDuration.GetTenthsOfMS() / params.m_nResampleInterval.GetTenthsOfMS();
+ tDuration -= params.m_nResampleInterval * nFactor;
+ tDuration /= 2;
+ return params.m_nTimes[start] + tDuration;
+ }
+ }
+ return DMETIME_MAXTIME;
+}
+
+
+//-----------------------------------------------------------------------------
+// This is used to modify the entire portion of the curve under the time selection
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLog< T >::_StampKeyAtHeadResample( DmeTime_t tHeadPosition, const DmeLog_TimeSelection_t& params, const T& value, bool bSkipToHead, bool bClearPreviousKeys )
+{
+ Assert( params.m_nResampleInterval > DmeTime_t( 0 ) );
+ if ( params.m_nResampleInterval < DmeTime_t( 0 ) )
+ return;
+
+ // Should be in "layer recording" mode!!!
+ Assert( GetNumLayers() >= 2 );
+ int nBestLayer = GetTopmostLayer(); // Topmost should be at least layer # 1 (0 is the base layer)
+ if ( nBestLayer < 1 )
+ return;
+ CDmeTypedLogLayer< T > *pWriteLayer = GetLayer( nBestLayer );
+ Assert( pWriteLayer );
+ if ( !pWriteLayer )
+ return;
+
+ if ( bClearPreviousKeys )
+ {
+ pWriteLayer->ClearKeys();
+ }
+
+ bool bSpew = false;
+
+ // NOTE: The headDelta is only used when not blending toward a preset
+ // When not blending toward a preset, just add the head delta onto everything.
+ // When blending toward a preset, lerp towards the preset.
+ T oldHeadValue = GetValueSkippingTopmostLayer( tHeadPosition );
+ T headDelta = Subtract( value, oldHeadValue );
+
+ // When dragging preset fader, eveything get's blended in by the amount of the preset being applied
+ bool bUsePresetRules = ( RECORD_PRESET == params.GetRecordingMode() );
+ bool bIsStampingQuaternions = ( CDmAttributeInfo<T>::ATTRIBUTE_TYPE == AT_QUATERNION );
+ bool bPerformInterpolation = bUsePresetRules || bIsStampingQuaternions;
+
+ // FIXME: Preset value should never be NULL. We need to grab it from the attribute
+ bool bUsePresetValue = bUsePresetRules && params.m_pPresetValue && params.m_pPresetValue->GetType() == CDmAttributeInfo<T>::ATTRIBUTE_TYPE;
+ const T& interpTarget = bUsePresetValue ? params.m_pPresetValue->GetValue<T>() : value;
+
+ // Compute falloff region blend factors
+ CLogFalloffBlend< T > blend[ 3 ];
+ blend[0].Init( this, params.m_nTimes[ TS_FALLOFF(0) ], params.m_nTimes[ TS_HOLD(0) ], true, params.m_nFalloffInterpolatorTypes[0], headDelta );
+ blend[1].Init( this, headDelta );
+ blend[2].Init( this, params.m_nTimes[ TS_FALLOFF(1) ], params.m_nTimes[ TS_HOLD(1) ], false, params.m_nFalloffInterpolatorTypes[1], headDelta );
+
+ // The algorithm we're going to use is to add samples in the following places:
+ // 1) At each time selection transition point (start, end of falloff regions)
+ // NOTE: If a falloff region has 0 size, we'll add points right outside the transition
+ // 2) At the resample point (we're going to base this so the resamples always occur at the same spots)
+ // 3) At any existing sample position
+ // 4) Any time we switch from clamped to not clamped
+ // By doing this, we will guarantee no bogus slope changes
+
+ // First, compute times for transition regions
+ DmeTime_t tTransitionTimes[TS_TIME_COUNT];
+ memcpy( &tTransitionTimes, &params.m_nTimes, sizeof(params.m_nTimes) );
+ if ( tTransitionTimes[TS_LEFT_FALLOFF] == tTransitionTimes[TS_LEFT_HOLD] )
+ {
+ tTransitionTimes[TS_LEFT_FALLOFF] -= DMETIME_MINDELTA;
+ }
+ if ( tTransitionTimes[TS_RIGHT_FALLOFF] == tTransitionTimes[TS_RIGHT_HOLD] )
+ {
+ tTransitionTimes[TS_RIGHT_FALLOFF] += DMETIME_MINDELTA;
+ }
+
+ DmeTime_t tStartTime = params.m_nTimes[ TS_LEFT_FALLOFF ];
+
+ // Next, compute the first resample time for each region
+ DmeTime_t tResampleStartTime[TS_TIME_COUNT];
+ tResampleStartTime[TS_LEFT_FALLOFF] = DMETIME_MAXTIME;
+ tResampleStartTime[TS_LEFT_HOLD] = ComputeResampleStartTime( params, 0 );
+ tResampleStartTime[TS_RIGHT_HOLD] = DMETIME_MAXTIME;
+ tResampleStartTime[TS_RIGHT_FALLOFF] = ComputeResampleStartTime( params, 1 );
+
+ // Finally, figure out which layer we're reading from,
+ // where the next key is, and when we must stop reading from it
+ int nReadLayer = FindLayerForTimeSkippingTopmost( tStartTime );
+ CDmeTypedLogLayer< T > *pReadLayer = GetLayer( nReadLayer );
+ int nLayerSampleIndex = pReadLayer->FindKey( tStartTime ) + 1;
+ DmeTime_t tLayerEndTime = pReadLayer->GetEndTime();
+ // NOTE: This can happen after reading off the end of layer 0
+ if ( tLayerEndTime <= tStartTime )
+ {
+ tLayerEndTime = DMETIME_MAXTIME;
+ }
+ DmeTime_t tNextSampleTime = nLayerSampleIndex >= pReadLayer->GetKeyCount() ? tLayerEndTime : pReadLayer->GetKeyTime( nLayerSampleIndex );
+ if ( tNextSampleTime > tLayerEndTime )
+ {
+ tNextSampleTime = tLayerEndTime;
+ }
+
+ // Now keep going until we've hit the end point
+ // NOTE: We use tTransitionTimes, *not* params.m_nTimes, so that we can get a single
+ // sample before zero-width left falloff regions
+ DmeTime_t tCurrent = tTransitionTimes[TS_LEFT_FALLOFF];
+ int nNextTransition = TS_LEFT_HOLD;
+ DmeTime_t tResampleTime = tResampleStartTime[nNextTransition];
+
+ const T* pInterpTarget = bPerformInterpolation ? &interpTarget : NULL;
+
+ if ( bSpew )
+ {
+ Msg( "Stamp key at head resample: %s\n", GetName() );
+ }
+
+ LogClampHelper_t<T> clampHelper;
+ while( nNextTransition < TS_TIME_COUNT )
+ {
+ // Stamp the key at the current time
+ if ( !bSkipToHead || ( tCurrent >= tHeadPosition ) )
+ {
+ blend[nNextTransition-1].StampKey( pWriteLayer, tCurrent, pReadLayer, params.m_flIntensity, clampHelper, bSpew, pInterpTarget );
+ }
+
+ // Update the read layer sample
+ if ( tCurrent == tNextSampleTime )
+ {
+ ++nLayerSampleIndex;
+ tNextSampleTime = nLayerSampleIndex >= pReadLayer->GetKeyCount() ? tLayerEndTime : pReadLayer->GetKeyTime( nLayerSampleIndex );
+ }
+
+ // Update the read layer
+ if ( tCurrent == tLayerEndTime )
+ {
+ nReadLayer = FindLayerForTimeSkippingTopmost( tCurrent + DMETIME_MINDELTA );
+ pReadLayer = GetLayer( nReadLayer );
+ nLayerSampleIndex = pReadLayer->FindKey( tCurrent ) + 1;
+ tLayerEndTime = pReadLayer->GetEndTime();
+
+ // NOTE: This can happen after reading off the end of layer 0
+ if ( tLayerEndTime <= tCurrent )
+ {
+ tLayerEndTime = DMETIME_MAXTIME;
+ }
+
+ tNextSampleTime = nLayerSampleIndex >= pReadLayer->GetKeyCount() ? tLayerEndTime : pReadLayer->GetKeyTime( nLayerSampleIndex );
+ if ( tNextSampleTime > tLayerEndTime )
+ {
+ tNextSampleTime = tLayerEndTime;
+ }
+ }
+
+ // Update the transition time
+ if ( tCurrent == tTransitionTimes[nNextTransition] )
+ {
+ // NOTE: This is necessary because each blend region has different 'deltas'
+ // to avoid overdriving in the falloff regions. Therefore, the 'previous value'
+ // used in the clamping operation will be different
+ if ( nNextTransition < ARRAYSIZE(blend) )
+ {
+ blend[nNextTransition].UpdateClampHelper( tCurrent, pReadLayer, params.m_flIntensity, clampHelper, pInterpTarget );
+ }
+
+ // Also need to update the 'previous' value stored in the
+ ++nNextTransition;
+ if ( nNextTransition >= ARRAYSIZE(tResampleStartTime) )
+ break;
+
+ // Update the first resample time
+ tResampleTime = tResampleStartTime[nNextTransition];
+
+ if ( bSpew )
+ {
+ Msg( " Entering region %d\n", nNextTransition-1 );
+ }
+ }
+
+ // Update the resample time
+ if ( tCurrent == tResampleTime )
+ {
+ tResampleTime += params.m_nResampleInterval;
+ }
+
+ // Now that the key is stamped, update current time.
+ tCurrent = tTransitionTimes[nNextTransition];
+ if ( tResampleTime < tCurrent )
+ {
+ tCurrent = tResampleTime;
+ }
+ if ( tNextSampleTime < tCurrent )
+ {
+ tCurrent = tNextSampleTime;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// In this case, we actually stamp a key right at the head position unlike the above method
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLog< T >::_StampKeyFilteredByTimeSelection( CDmeTypedLogLayer< T > *pWriteLayer, DmeTime_t t, const DmeLog_TimeSelection_t &params, const T& value, bool bForce )
+{
+ // Found a key which needs to be modulated upward
+ float flFraction = params.GetAmountForTime( t ) * params.m_flIntensity;
+ if ( flFraction <= 0.0f && !bForce )
+ return;
+
+ // When dragging preset fader, eveything get's blended in by the amount of the preset being applied
+ bool bUsePresetRules = ( RECORD_PRESET == params.GetRecordingMode() );
+
+ // FIXME: Preset value should never be NULL. We need to grab it from the attribute
+ const T& interpTarget = ( bUsePresetRules && params.m_pPresetValue ) ? params.m_pPresetValue->GetValue<T>() : value;
+ T oldVal = GetValueSkippingTopmostLayer( t );
+ T newVal = Interpolate( flFraction, oldVal, interpTarget );
+ T writeVal = ClampValue( newVal );
+ pWriteLayer->InsertKey( t, writeVal, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+}
+
+
+//-----------------------------------------------------------------------------
+// In this case, we actually stamp a key right at the head position unlike the above method
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLog< T >::_StampKeyAtHeadFilteredByTimeSelection( DmeTime_t tHeadPosition, DmeTime_t tPreviousHeadPosition, const DmeLog_TimeSelection_t &params, const T& value )
+{
+ // Should be in "layer recording" mode!!!
+ Assert( GetNumLayers() >= 2 );
+ int nBestLayer = GetTopmostLayer(); // Topmost should be at least layer # 1 (0 is the base layer)
+ if ( nBestLayer < 1 )
+ return;
+
+ CDmeTypedLogLayer< T > *pWriteLayer = GetLayer( nBestLayer );
+ Assert( pWriteLayer );
+ if ( !pWriteLayer )
+ return;
+
+ // NOTE: This little trickery is necessary to generate samples right outside the
+ // transition region in the case of zero length falloff regions
+ DmeLog_TimeSelection_t tempParams = params;
+ if ( tempParams.m_nTimes[TS_LEFT_FALLOFF] == tempParams.m_nTimes[TS_LEFT_HOLD] )
+ {
+ tempParams.m_nTimes[TS_LEFT_FALLOFF] -= DMETIME_MINDELTA;
+ }
+ if ( tempParams.m_nTimes[TS_RIGHT_FALLOFF] == tempParams.m_nTimes[TS_RIGHT_HOLD] )
+ {
+ tempParams.m_nTimes[TS_RIGHT_FALLOFF] += DMETIME_MINDELTA;
+ }
+
+ int nPrevRegion = tempParams.ComputeRegionForTime( tPreviousHeadPosition );
+ int nCurrRegion = tempParams.ComputeRegionForTime( tHeadPosition );
+
+ // Test for backward performance!
+ if ( nCurrRegion < nPrevRegion )
+ {
+ V_swap( nCurrRegion, nPrevRegion );
+ }
+
+ // Insert samples at each transition point we skipped over
+ for ( int i = nPrevRegion; i < nCurrRegion; ++i )
+ {
+ _StampKeyFilteredByTimeSelection( pWriteLayer, tempParams.m_nTimes[i], params, value, true );
+ }
+
+ _StampKeyFilteredByTimeSelection( pWriteLayer, tHeadPosition, params, value );
+}
+
+template< class T >
+void CDmeTypedLog< T >::RemoveKeys( DmeTime_t starttime )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return;
+
+ GetLayer( bestLayer )->RemoveKeys( starttime );
+}
+
+template< class T >
+void CDmeTypedLog< T >::RemoveKey( int nKeyIndex, int nNumKeysToRemove /*= 1*/ )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return;
+
+ GetLayer( bestLayer )->RemoveKey( nKeyIndex, nNumKeysToRemove );
+}
+
+template< class T >
+void CDmeTypedLog< T >::ClearKeys()
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return;
+
+ GetLayer( bestLayer )->ClearKeys();
+}
+
+//-----------------------------------------------------------------------------
+// Returns a specific key's value
+//-----------------------------------------------------------------------------
+template< class T >
+DmeTime_t CDmeTypedLog< T >::GetKeyTime( int nKeyIndex ) const
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return DmeTime_t::MinTime();
+ return GetLayer( bestLayer )->GetKeyTime( nKeyIndex );
+}
+
+template< class T >
+void CDmeTypedLog< T >::SetKeyTime( int nKeyIndex, DmeTime_t keyTime )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return;
+ return GetLayer( bestLayer )->SetKeyTime( nKeyIndex, keyTime );
+}
+
+//-----------------------------------------------------------------------------
+// Returns the index of a particular key
+//-----------------------------------------------------------------------------
+template< class T >
+int CDmeTypedLog< T >::FindKeyWithinTolerance( DmeTime_t nTime, DmeTime_t nTolerance )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return -1;
+
+ return GetLayer( bestLayer )->FindKeyWithinTolerance( nTime, nTolerance );
+}
+
+//-----------------------------------------------------------------------------
+// tests whether two values differ by more than the threshold
+//-----------------------------------------------------------------------------
+template<>
+bool CDmeTypedLog< Vector >::ValuesDiffer( const Vector& a, const Vector& b ) const
+{
+ return a.DistToSqr( b ) > m_threshold * m_threshold;
+}
+
+template<>
+bool CDmeTypedLog< QAngle >::ValuesDiffer( const QAngle& a, const QAngle& b ) const
+{
+ return ( a - b ).LengthSqr() > m_threshold * m_threshold;
+}
+
+template<>
+bool CDmeTypedLog< Quaternion >::ValuesDiffer( const Quaternion& a, const Quaternion& b ) const
+{
+ return QuaternionAngleDiff( a, b ) > m_threshold;
+}
+
+template<>
+bool CDmeTypedLog< float >::ValuesDiffer( const float& a, const float& b ) const
+{
+ return fabs( a - b ) > m_threshold;
+}
+
+template< class T >
+bool CDmeTypedLog< T >::ValuesDiffer( const T& a, const T& b ) const
+{
+ return a != b;
+}
+
+//-----------------------------------------------------------------------------
+// Sets a key, removes all keys after this time
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLog< T >::SetKey( DmeTime_t time, const T& value, int curveType /*=CURVE_DEFAULT*/)
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer < 0 )
+ return;
+
+ GetLayer( bestLayer )->SetKey( time, value, curveType );
+}
+
+template< class T >
+CDmeTypedLogLayer< T > *CDmeTypedLog< T >::GetLayer( int index )
+{
+ if ( index < 0 )
+ return NULL;
+
+ return static_cast< CDmeTypedLogLayer< T > * >( m_Layers[ index ] );
+}
+
+template< class T >
+const CDmeTypedLogLayer< T > *CDmeTypedLog< T >::GetLayer( int index ) const
+{
+ if ( index < 0 )
+ return NULL;
+
+ return static_cast< CDmeTypedLogLayer< T > * >( m_Layers[ index ] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a key within tolerance, or adds one
+//-----------------------------------------------------------------------------
+template< class T >
+int CDmeTypedLog< T >::FindOrAddKey( DmeTime_t nTime, DmeTime_t nTolerance, const T& value, int curveType /*=CURVE_DEFAULT*/ )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer == -1 )
+ return -1;
+
+ return GetLayer( bestLayer )->FindOrAddKey( nTime, nTolerance, value, curveType );
+}
+
+
+//-----------------------------------------------------------------------------
+// This inserts a key. Unlike SetKey, this will *not* delete keys after the specified time
+//-----------------------------------------------------------------------------
+template < class T >
+int CDmeTypedLog< T >::InsertKey( DmeTime_t nTime, const T& value, int curveType /*=CURVE_DEFAULT*/ )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer == -1 )
+ return -1;
+
+ return GetLayer( bestLayer )->InsertKey( nTime, value, curveType );
+}
+
+template < class T >
+int CDmeTypedLog< T >::InsertKeyAtTime( DmeTime_t nTime, int curveType /*=CURVE_DEFAULT*/ )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer == -1 )
+ return -1;
+
+ return GetLayer( bestLayer )->InsertKeyAtTime( nTime, curveType );
+}
+
+template< class T >
+const T& CDmeTypedLog< T >::GetValue( DmeTime_t time ) const
+{
+ int bestLayer = FindLayerForTime( time );
+ if ( bestLayer < 0 )
+ {
+ static T s_value;
+ CDmAttributeInfo< T >::SetDefaultValue( s_value ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return s_value;
+ }
+
+ return GetLayer( bestLayer )->GetValue( time );
+}
+
+template< class T >
+const T& CDmeTypedLog< T >::GetValueSkippingTopmostLayer( DmeTime_t time ) const
+{
+ int nLayer = FindLayerForTimeSkippingTopmost( time );
+ if ( nLayer < 0 )
+ return GetValue( time );
+ return GetLayer( nLayer )->GetValue( time );
+}
+
+template< class T >
+void CDmeTypedLog< T >::SetKey( DmeTime_t time, const CDmAttribute *pAttr, uint index, int curveType /*= CURVE_DEFAULT*/ )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer == -1 )
+ return;
+
+ GetLayer( bestLayer )->SetKey( time, pAttr, index, curveType );
+}
+
+template< class T >
+bool CDmeTypedLog< T >::SetDuplicateKeyAtTime( DmeTime_t time )
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer == -1 )
+ return false;
+
+ return GetLayer( bestLayer )->SetDuplicateKeyAtTime( time );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a specific key's value
+//-----------------------------------------------------------------------------
+template< class T >
+const T& CDmeTypedLog< T >::GetKeyValue( int nKeyIndex ) const
+{
+ int bestLayer = GetTopmostLayer();
+ if ( bestLayer == -1 )
+ {
+ static T s_value;
+ CDmAttributeInfo< T >::SetDefaultValue( s_value ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ return s_value;
+ }
+
+ return GetLayer( bestLayer )->GetKeyValue( nKeyIndex );
+}
+
+template< class T >
+void CDmeTypedLog< T >::GetValue( DmeTime_t time, CDmAttribute *pAttr, uint index ) const
+{
+ int bestLayer = FindLayerForTime( time );
+ if ( bestLayer < 0 )
+ {
+ T value;
+ CDmAttributeInfo< T >::SetDefaultValue( value ); // TODO - create GetDefaultValue that returns a default T, to avoid rebuilding every time
+ pAttr->SetValue( CDmAttributeInfo< T >::AttributeType(), &value );
+ }
+
+ return GetLayer( bestLayer )->GetValue( time, pAttr, index );
+}
+
+template< class T >
+void CDmeTypedLog< T >::GetValueSkippingTopmostLayer( DmeTime_t time, CDmAttribute *pAttr, uint index = 0 ) const
+{
+ CUtlVector< int > layers;
+ FindLayersForTime( time, layers );
+ int layerCount = layers.Count();
+ if ( layerCount <= 1 )
+ {
+ return GetValue( time, pAttr, index );
+ }
+
+ int topMostLayer = GetTopmostLayer();
+ int useLayer = layers[ layerCount - 1 ];
+ if ( topMostLayer == useLayer )
+ {
+ useLayer = layers[ layerCount - 2 ];
+ }
+ Assert( useLayer >= 0 );
+ return GetLayer( useLayer )->GetValue( time, pAttr, index );
+}
+
+template< class T >
+float CDmeTypedLog< T >::GetComponent( DmeTime_t time, int componentIndex ) const
+{
+ return ::GetComponent( GetValue( time ), componentIndex );
+}
+
+//-----------------------------------------------------------------------------
+// resampling and filtering
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmeTypedLog< T >::Resample( DmeFramerate_t samplerate )
+{
+ int c = m_Layers.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ GetLayer( i )->Resample( samplerate );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::Filter( int nSampleRadius )
+{
+ int c = m_Layers.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ GetLayer( i )->Filter( nSampleRadius );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::Filter2( DmeTime_t sampleRadius )
+{
+ int c = m_Layers.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ GetLayer( i )->Filter2( sampleRadius );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::OnAttributeArrayElementAdded( CDmAttribute *pAttribute, int nFirstElem, int nLastElem )
+{
+ BaseClass::OnAttributeArrayElementAdded( pAttribute, nFirstElem, nLastElem );
+ if ( pAttribute == m_Layers.GetAttribute() )
+ {
+ for ( int i = nFirstElem; i <= nLastElem; ++i )
+ {
+ m_Layers[i]->SetOwnerLog( this );
+ }
+ return;
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::SetUseEdgeInfo( bool state )
+{
+ Assert( IsUsingCurveTypes() );
+ GetTypedCurveInfo()->SetUseEdgeInfo( state );
+}
+
+template< class T >
+bool CDmeTypedLog< T >::IsUsingEdgeInfo() const
+{
+ Assert( IsUsingCurveTypes() );
+ return GetTypedCurveInfo()->IsUsingEdgeInfo();
+}
+
+template< class T >
+void CDmeTypedLog< T >::SetEdgeInfo( int edge, bool active, const T& val, int curveType )
+{
+ Assert( IsUsingCurveTypes() );
+ GetTypedCurveInfo()->SetEdgeInfo( edge, active, val, curveType );
+}
+
+template< class T >
+void CDmeTypedLog< T >::SetDefaultEdgeZeroValue( const T& val )
+{
+ Assert( IsUsingCurveTypes() );
+ GetTypedCurveInfo()->SetDefaultEdgeZeroValue( val );
+}
+
+template< class T >
+const T& CDmeTypedLog< T >::GetDefaultEdgeZeroValue() const
+{
+ Assert( IsUsingCurveTypes() );
+ return GetTypedCurveInfo()->GetDefaultEdgeZeroValue();
+}
+
+template< class T >
+void CDmeTypedLog< T >::SetRightEdgeTime( DmeTime_t time )
+{
+ Assert( IsUsingCurveTypes() );
+ GetTypedCurveInfo()->SetRightEdgeTime( time );
+}
+
+template< class T >
+DmeTime_t CDmeTypedLog< T >::GetRightEdgeTime() const
+{
+ Assert( IsUsingCurveTypes() );
+ return GetTypedCurveInfo()->GetRightEdgeTime();
+}
+
+template< class T >
+void CDmeTypedLog< T >::GetEdgeInfo( int edge, bool& active, T& val, int& curveType ) const
+{
+ Assert( IsUsingCurveTypes() );
+ GetTypedCurveInfo()->GetEdgeInfo( edge, active, val, curveType );
+}
+
+template< class T >
+int CDmeTypedLog< T >::GetEdgeCurveType( int edge ) const
+{
+ Assert( IsUsingCurveTypes() );
+ return GetTypedCurveInfo()->GetEdgeCurveType( edge );
+}
+
+template< class T >
+void CDmeTypedLog< T >::GetZeroValue( int side, T& val ) const
+{
+ Assert( IsUsingCurveTypes() );
+ GetTypedCurveInfo()->GetZeroValue( side, val );
+}
+
+template< class T >
+bool CDmeTypedLog< T >::IsEdgeActive( int edge ) const
+{
+ Assert( IsUsingCurveTypes() );
+ return GetTypedCurveInfo()->IsEdgeActive( edge );
+}
+
+template< class T >
+void CDmeTypedLog< T >::GetEdgeValue( int edge, T& val ) const
+{
+ Assert( IsUsingCurveTypes() );
+ GetTypedCurveInfo()->GetEdgeValue( edge, val );
+}
+
+template< class T >
+void CDmeTypedLog< T >::BlendTimesUsingTimeSelection( const CDmeLogLayer *firstLayer, const CDmeLogLayer *secondLayer, CDmeLogLayer *outputLayer, const DmeLog_TimeSelection_t &params, DmeTime_t tStartOffset )
+{
+ const CDmeTypedLogLayer< T > *topLayer = static_cast< const CDmeTypedLogLayer< T > * >( secondLayer );
+ if ( !topLayer )
+ return;
+
+ const CDmeTypedLogLayer< T > *baseLayer = static_cast< const CDmeTypedLogLayer< T > * >( firstLayer );
+ if ( !baseLayer )
+ return;
+
+ CDmeTypedLogLayer< T > *newLayer = static_cast< CDmeTypedLogLayer< T > * >( outputLayer );
+ if ( !newLayer )
+ return;
+
+ Assert( topLayer->GetKeyCount() == baseLayer->GetKeyCount() );
+
+ int i;
+ // Resample everything in the base layer first
+ int kc = baseLayer->GetKeyCount();
+
+ newLayer->ClearKeys();
+
+ for ( i = 0; i < kc; ++i )
+ {
+ DmeTime_t baseKeyTime = baseLayer->GetKeyTime( i );
+ DmeTime_t checkTime = baseKeyTime + tStartOffset;
+ if ( checkTime < params.m_nTimes[ TS_LEFT_FALLOFF ] )
+ continue;
+ if ( checkTime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ break;
+ float frac = params.GetAmountForTime( checkTime );
+ float frac2 = params.m_flIntensity;
+ float flInterp = frac2 * frac;
+
+ DmeTime_t targetKeyTime = topLayer->GetKeyTime( i );
+
+ DmeTime_t blendedKeyTime = Lerp( flInterp, baseKeyTime, targetKeyTime ) + tStartOffset;
+
+ T baseVal = baseLayer->GetKeyValue( i );
+
+ newLayer->InsertKey( blendedKeyTime, baseVal );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::BlendLayersUsingTimeSelection( const CDmeLogLayer *firstLayer, const CDmeLogLayer *secondLayer, CDmeLogLayer *outputLayer, const DmeLog_TimeSelection_t &params, bool bUseBaseLayerSamples, DmeTime_t tStartOffset )
+{
+ const CDmeTypedLogLayer< T > *topLayer = static_cast< const CDmeTypedLogLayer< T > * >( secondLayer );
+ if ( !topLayer )
+ return;
+
+ const CDmeTypedLogLayer< T > *baseLayer = static_cast< const CDmeTypedLogLayer< T > * >( firstLayer );
+ if ( !baseLayer )
+ return;
+
+ CDmeTypedLogLayer< T > *newLayer = static_cast< CDmeTypedLogLayer< T > * >( outputLayer );
+ if ( !newLayer )
+ return;
+
+ int i;
+ // Resample everything in the base layer first
+ int kc = baseLayer->GetKeyCount();
+ if ( bUseBaseLayerSamples )
+ {
+ for ( i = 0; i < kc; ++i )
+ {
+ DmeTime_t keyTime = baseLayer->GetKeyTime( i );
+ if ( keyTime < params.m_nTimes[ TS_LEFT_FALLOFF ] )
+ continue;
+ if ( keyTime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ break;
+
+ float frac = params.GetAmountForTime( keyTime );
+ float frac2 = params.m_flIntensity;
+
+ T baseVal = baseLayer->GetKeyValue( i );
+ T newVal = topLayer->GetValue( keyTime );
+ T blended = Interpolate( frac2 * frac, baseVal, newVal );
+
+ newLayer->SetKey( keyTime + tStartOffset, blended );
+ }
+ }
+
+ kc = topLayer->GetKeyCount();
+ for ( i = 0; i < kc; ++i )
+ {
+ DmeTime_t keyTime = topLayer->GetKeyTime( i );
+ DmeTime_t finalKeyTime = keyTime + tStartOffset;
+ if ( finalKeyTime < params.m_nTimes[ TS_LEFT_FALLOFF ] )
+ continue;
+ if ( finalKeyTime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ break;
+ float frac = params.GetAmountForTime( finalKeyTime );
+ float frac2 = params.m_flIntensity;
+
+ T baseVal = baseLayer->GetValue( keyTime );
+ T newVal = topLayer->GetKeyValue( i );
+ T blended = Interpolate( frac2 *frac, baseVal, newVal );
+
+ newLayer->InsertKey( finalKeyTime, blended );
+ }
+
+ if ( g_pDmElementFramework->GetPhase() == PH_EDIT )
+ {
+ newLayer->RemoveRedundantKeys( params.m_flThreshold );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::BlendLayersUsingTimeSelection( const DmeLog_TimeSelection_t &params )
+{
+ Assert( GetNumLayers() >= 2 );
+ int bestLayer = GetTopmostLayer(); // Topmost should be at least layer # 1 (0 is the base layer)
+ if ( bestLayer <= 0 )
+ return;
+
+ Assert( params.m_nResampleInterval > DmeTime_t( 0 ) );
+ if ( params.m_nResampleInterval < DmeTime_t( 0 ) )
+ return;
+
+ CDmeTypedLogLayer< T > *topLayer = GetLayer( bestLayer );
+ Assert( topLayer );
+ if ( !topLayer )
+ return;
+
+ CDmeTypedLogLayer< T > *baseLayer = GetLayer( 0 );
+ if ( !baseLayer )
+ return;
+
+ CDmeTypedLogLayer< T > *newLayer = static_cast< CDmeTypedLogLayer< T > * >( CreateLayer< T >( this ) );
+ if ( !newLayer )
+ return;
+
+ BlendLayersUsingTimeSelection( baseLayer, topLayer, newLayer, params, true, DMETIME_ZERO );
+
+ // Store it back into the new topmost layer
+ topLayer->CopyLayer( newLayer );
+
+ g_pDataModel->DestroyElement( newLayer->GetHandle() );
+}
+
+template< class T >
+void CDmeTypedLog< T >::RevealUsingTimeSelection( const DmeLog_TimeSelection_t &params, CDmeLogLayer *savedLayer )
+{
+ CDmeTypedLogLayer< T > *saved = static_cast< CDmeTypedLogLayer< T > * >( savedLayer );
+ if ( !saved )
+ return;
+
+ Assert( GetNumLayers() >= 2 );
+ int bestLayer = GetTopmostLayer(); // Topmost should be at least layer # 1 (0 is the base layer)
+ if ( bestLayer <= 0 )
+ return;
+
+ Assert( params.m_nResampleInterval > DmeTime_t( 0 ) );
+ if ( params.m_nResampleInterval < DmeTime_t( 0 ) )
+ return;
+
+ CDmeTypedLogLayer< T > *writeLayer = static_cast< CDmeTypedLogLayer< T > * >( GetLayer( bestLayer ) );
+ Assert( writeLayer );
+ if ( !writeLayer )
+ return;
+
+ CDmeLogLayer *baseLayer = GetLayer( 0 );
+ if ( !baseLayer )
+ return;
+
+ DmeTime_t resample = 0.5f * params.m_nResampleInterval;
+
+ // Do a second pass where we bis the keys in the falloff area back toward the original value
+ for ( int t = params.m_nTimes[ TS_LEFT_FALLOFF ].GetTenthsOfMS(); t < params.m_nTimes[ TS_RIGHT_FALLOFF ].GetTenthsOfMS() + resample.GetTenthsOfMS(); t += resample.GetTenthsOfMS() )
+ {
+ DmeTime_t curtime = DmeTime_t( t );
+ if ( curtime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ curtime = params.m_nTimes[ TS_RIGHT_FALLOFF ];
+
+ float frac = params.GetAmountForTime( curtime );
+ frac *= params.m_flIntensity;
+
+ if ( frac <= 0.0f )
+ continue;
+
+ // Get current value in layer
+ T curValue = GetValueSkippingTopmostLayer( curtime );
+ T revealValue = saved->GetValue( curtime );
+
+ T newValue = Interpolate( frac, curValue, revealValue );
+
+ // Overwrite key
+ writeLayer->InsertKey( curtime, newValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+
+ if ( g_pDmElementFramework->GetPhase() == PH_EDIT )
+ {
+ writeLayer->RemoveRedundantKeys( params.m_flThreshold );
+ }
+}
+
+template< class T >
+void RandomValue( const T& average, const T& oldValue, T& newValue )
+{
+ newValue = oldValue;
+}
+
+template<> void RandomValue( const Vector& average, const Vector& oldValue, Vector& newValue )
+{
+ newValue = oldValue;
+
+ for ( int i = 0; i < 3; ++i )
+ {
+ newValue[ i ] += RandomFloat( -fabs( average[ i ] ), fabs( average[ i ] ) );
+ }
+}
+
+template<> void RandomValue( const Quaternion& average, const Quaternion& oldValue, Quaternion& newValue )
+{
+ newValue = oldValue;
+
+ for ( int i = 0; i < 4; ++i )
+ {
+ newValue[ i ] += RandomFloat( -fabs( average[ i ] ), fabs( average[ i ] ) );
+ }
+}
+
+template<> void RandomValue( const Vector4D& average, const Vector4D& oldValue, Vector4D& newValue )
+{
+ newValue = oldValue;
+
+ for ( int i = 0; i < 4; ++i )
+ {
+ newValue[ i ] += RandomFloat( -fabs( average[ i ] ), fabs( average[ i ] ) );
+ }
+}
+
+template<> void RandomValue( const Vector2D& average, const Vector2D& oldValue, Vector2D& newValue )
+{
+ newValue = oldValue;
+
+ for ( int i = 0; i < 2; ++i )
+ {
+ newValue[ i ] += RandomFloat( -fabs( average[ i ] ), fabs( average[ i ] ) );
+ }
+}
+
+template<> void RandomValue( const float& average, const float& oldValue, float& newValue )
+{
+ newValue = oldValue + RandomFloat( -average, average );
+}
+
+template<> void RandomValue( const int& average, const int& oldValue, int& newValue )
+{
+ newValue = oldValue + RandomInt( -average, average );
+}
+
+// Builds a layer with samples matching the times in reference layer, from the data in pDataLayer, putting the resulting keys into pOutputLayer
+template< class T >
+void CDmeTypedLog< T >::BuildCorrespondingLayer( const CDmeLogLayer *pReferenceLayer, const CDmeLogLayer *pDataLayer, CDmeLogLayer *pOutputLayer )
+{
+ const CDmeTypedLogLayer< T > *ref = static_cast< const CDmeTypedLogLayer< T > * >( pReferenceLayer );
+ const CDmeTypedLogLayer< T > *data = static_cast< const CDmeTypedLogLayer< T > * >( pDataLayer );
+ CDmeTypedLogLayer< T > *out = static_cast< CDmeTypedLogLayer< T > * >( pOutputLayer );
+
+ if ( !ref || !data || !out )
+ {
+ Assert( 0 );
+ return;
+ }
+
+ bool usecurvetypes = ref->IsUsingCurveTypes();
+
+ out->ClearKeys();
+ int kc = ref->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t keyTime = ref->GetKeyTime( i );
+ T value = data->GetValue( keyTime );
+
+ out->InsertKey( keyTime, value, usecurvetypes ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::StaggerUsingTimeSelection( const DmeLog_TimeSelection_t& params, DmeTime_t tStaggerAmount, const CDmeLogLayer *pBaseLayer, CDmeLogLayer *pWriteLayer )
+{
+ CDmeTypedLogLayer< T > *writeLayer = static_cast< CDmeTypedLogLayer< T > * >( pWriteLayer );
+ Assert( writeLayer );
+ if ( !writeLayer )
+ return;
+
+ const CDmeTypedLogLayer< T > *baseLayer = static_cast< const CDmeTypedLogLayer< T > * >( pBaseLayer );
+ if ( !baseLayer )
+ return;
+
+ writeLayer->ClearKeys();
+
+ DmeLog_TimeSelection_t newParams;
+ newParams = params;
+
+ // Move the hold area by the stagger amount
+ float flScaleFactor[ 2 ] = { 1.0f, 1.0f };
+
+ newParams.m_nTimes[ TS_LEFT_HOLD ] += tStaggerAmount;
+ newParams.m_nTimes[ TS_RIGHT_HOLD ] += tStaggerAmount;
+
+ for ( int i = 0; i < 2 ; ++i )
+ {
+ DmeTime_t dt = params.m_nTimes[ 2 * i + 1 ] - params.m_nTimes[ 2 * i ];
+ if ( dt > DMETIME_ZERO )
+ {
+ DmeTime_t newDt = newParams.m_nTimes[ 2 * i + 1 ] - newParams.m_nTimes[ 2 * i ];
+ flScaleFactor[ i ] = newDt / dt;
+ }
+ }
+
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t curtime = baseLayer->GetKeyTime( i );
+ T oldValue = baseLayer->GetKeyValue( i );
+
+ // Classify time
+ if ( curtime <= params.m_nTimes[ TS_LEFT_HOLD ] )
+ {
+ curtime = curtime * flScaleFactor[ 0 ];
+ }
+ else if ( curtime >= params.m_nTimes[ TS_RIGHT_HOLD ] )
+ {
+ curtime = params.m_nTimes[ TS_RIGHT_FALLOFF ] - ( params.m_nTimes[ TS_RIGHT_FALLOFF ] - curtime ) * flScaleFactor[ 1 ];
+ }
+ else
+ {
+ curtime += tStaggerAmount;
+ }
+
+ writeLayer->InsertKey( curtime, oldValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::FilterUsingTimeSelection( IUniformRandomStream *random, const DmeLog_TimeSelection_t& params, int filterType, bool bResample, bool bApplyFalloff )
+{
+ Assert( GetNumLayers() >= 2 );
+ int bestLayer = GetTopmostLayer(); // Topmost should be at least layer # 1 (0 is the base layer)
+ if ( bestLayer <= 0 )
+ return;
+
+ CDmeTypedLogLayer< T > *writeLayer = GetLayer( bestLayer );
+ Assert( writeLayer );
+ if ( !writeLayer )
+ return;
+
+ CDmeTypedLogLayer< T > *baseLayer = GetLayer( 0 );
+ if ( !baseLayer )
+ return;
+
+ FilterUsingTimeSelection( random, 1.0f, params, filterType, bResample, bApplyFalloff, baseLayer, writeLayer );
+}
+
+template< class T >
+void CDmeTypedLog< T >::FilterUsingTimeSelection( IUniformRandomStream *random, float flScale, const DmeLog_TimeSelection_t& params, int filterType, bool bResample, bool bApplyFalloff, const CDmeLogLayer *pBaseLayer, CDmeLogLayer *pWriteLayer )
+{
+ Assert( params.m_nResampleInterval > DmeTime_t( 0 ) );
+ if ( params.m_nResampleInterval < DmeTime_t( 0 ) )
+ return;
+
+ CDmeTypedLogLayer< T > *writeLayer = static_cast< CDmeTypedLogLayer< T > * >( pWriteLayer );
+ Assert( writeLayer );
+ if ( !writeLayer )
+ return;
+
+ const CDmeTypedLogLayer< T > *baseLayer = static_cast< const CDmeTypedLogLayer< T > * >( pBaseLayer );
+ if ( !baseLayer )
+ return;
+
+ writeLayer->ClearKeys();
+
+ DmeTime_t resample = 0.5f * params.m_nResampleInterval;
+
+ switch ( filterType )
+ {
+ default:
+ case FILTER_SMOOTH:
+ {
+ int t;
+ if ( bResample )
+ {
+ for ( t = params.m_nTimes[ TS_LEFT_FALLOFF ].GetTenthsOfMS(); t < params.m_nTimes[ TS_RIGHT_FALLOFF ].GetTenthsOfMS() + resample.GetTenthsOfMS(); t += resample.GetTenthsOfMS() )
+ {
+ DmeTime_t curtime = DmeTime_t( t );
+ if ( curtime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ curtime = params.m_nTimes[ TS_RIGHT_FALLOFF ];
+
+ T curValue = baseLayer->GetValue( curtime );
+ writeLayer->SetKey( curtime, curValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+ }
+ else
+ {
+ // Do a second pass where we bias the keys in the falloff area back toward the original value
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t curtime = baseLayer->GetKeyTime( i );
+ if ( curtime < params.m_nTimes[ TS_LEFT_FALLOFF ] )
+ continue;
+
+ if ( curtime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ continue;
+
+ T oldValue = baseLayer->GetKeyValue( i );
+ writeLayer->InsertKey( curtime, oldValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+ }
+
+ writeLayer->Filter2( params.m_nResampleInterval * 0.95f * flScale );
+
+ if ( bApplyFalloff )
+ {
+ if ( bResample )
+ {
+ // Do a second pass where we bias the keys in the falloff area back toward the original value
+ for ( t = params.m_nTimes[ TS_LEFT_FALLOFF ].GetTenthsOfMS(); t < params.m_nTimes[ TS_RIGHT_FALLOFF ].GetTenthsOfMS() + resample.GetTenthsOfMS(); t += resample.GetTenthsOfMS() )
+ {
+ DmeTime_t curtime = DmeTime_t( t );
+ if ( curtime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ curtime = params.m_nTimes[ TS_RIGHT_FALLOFF ];
+
+ T oldValue = baseLayer->GetValue( curtime );
+
+ if ( curtime >= params.m_nTimes[ TS_LEFT_HOLD ] && curtime <= params.m_nTimes[ TS_RIGHT_HOLD ] )
+ continue;
+
+ // Modulate these keys back down toward the original value
+ T newValue = writeLayer->GetValue( curtime );
+
+ float frac = bApplyFalloff ? params.GetAmountForTime( curtime ) : 1.0f;
+
+ newValue = Interpolate( frac, oldValue, newValue );
+
+ // Overwrite key
+ writeLayer->InsertKey( curtime, newValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+ }
+ else
+ {
+ // Do a second pass where we bias the keys in the falloff area back toward the original value
+ int kc = writeLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t curtime = writeLayer->GetKeyTime( i );
+ if ( curtime < params.m_nTimes[ TS_LEFT_FALLOFF ] )
+ continue;
+
+ if ( curtime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ continue;
+
+ if ( curtime >= params.m_nTimes[ TS_LEFT_HOLD ] && curtime <= params.m_nTimes[ TS_RIGHT_HOLD ] )
+ continue;
+
+ T oldValue = baseLayer->GetValue( curtime );
+
+ // Modulate these keys back down toward the original value
+ T newValue = writeLayer->GetValue( curtime );
+
+ float frac = bApplyFalloff ? params.GetAmountForTime( curtime ) : 1.0f;
+
+ newValue = Interpolate( frac, oldValue, newValue );
+
+ // Overwrite key
+ writeLayer->InsertKey( curtime, newValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+ }
+ }
+
+ if ( bResample )
+ {
+ writeLayer->RemoveRedundantKeys( params.m_flThreshold );
+ }
+ }
+ break;
+ case FILTER_JITTER:
+ {
+ // Compute average value in entire log
+
+ T average = Average( baseLayer->m_values.Base(), baseLayer->m_values.Count() );
+ average = ScaleValue( average, 0.05f * flScale );
+
+ if ( bResample )
+ {
+ int t;
+ for ( t = params.m_nTimes[ TS_LEFT_FALLOFF ].GetTenthsOfMS(); t < params.m_nTimes[ TS_RIGHT_FALLOFF ].GetTenthsOfMS() + resample.GetTenthsOfMS(); t += resample.GetTenthsOfMS() )
+ {
+ DmeTime_t curtime = DmeTime_t( t );
+ if ( curtime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ curtime = params.m_nTimes[ TS_RIGHT_FALLOFF ];
+
+ float frac = bApplyFalloff ? params.GetAmountForTime( curtime ) : 1.0f;
+
+ T oldValue = baseLayer->GetValue( curtime );
+
+ T newValue;
+ RandomValue( average, oldValue, newValue );
+
+ newValue = Interpolate( frac, oldValue, newValue );
+
+ writeLayer->SetKey( curtime, newValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+
+ }
+ else
+ {
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t curtime = baseLayer->GetKeyTime( i );
+ if ( curtime < params.m_nTimes[ TS_LEFT_FALLOFF ] )
+ continue;
+
+ if ( curtime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ continue;
+
+ float frac = bApplyFalloff ? params.GetAmountForTime( curtime ) : 1.0f;
+
+ T oldValue = baseLayer->GetValue( curtime );
+
+ T newValue;
+ RandomValue( average, oldValue, newValue );
+
+ newValue = Interpolate( frac, oldValue, newValue );
+
+ writeLayer->InsertKey( curtime, newValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+ }
+ }
+ break;
+ case FILTER_SHARPEN:
+ case FILTER_SOFTEN:
+ {
+ writeLayer->ClearKeys();
+
+ bool bSharpen = filterType == FILTER_SHARPEN;
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t curtime = baseLayer->GetKeyTime( i );
+ if ( curtime < params.m_nTimes[ TS_LEFT_FALLOFF ] )
+ continue;
+
+ if ( curtime > params.m_nTimes[ TS_RIGHT_FALLOFF ] )
+ continue;
+
+ float frac = bApplyFalloff ? params.GetAmountForTime( curtime ) : 1.0f;
+
+ T oldValue = baseLayer->GetValue( curtime );
+
+ T newValue = oldValue;
+ if ( frac != 1.0f )
+ {
+ T crossingValue[ 2 ] = { oldValue, oldValue };
+ if ( curtime <= params.m_nTimes[ TS_LEFT_HOLD ] )
+ {
+ // Get the value at the crossing point (either green edge for sharpen, or left edge for soften...)
+ crossingValue[ 0 ] = baseLayer->GetValue( params.m_nTimes[ TS_LEFT_FALLOFF ] );
+ crossingValue[ 1 ] = baseLayer->GetValue( params.m_nTimes[ TS_LEFT_HOLD ] );
+ }
+ else if ( curtime >= params.m_nTimes[ TS_RIGHT_HOLD ] )
+ {
+ crossingValue[ 0 ] = baseLayer->GetValue( params.m_nTimes[ TS_RIGHT_FALLOFF ] );
+ crossingValue[ 1 ] = baseLayer->GetValue( params.m_nTimes[ TS_RIGHT_HOLD ] );
+ }
+ else
+ {
+ Assert( 0 );
+ }
+
+ T dynamicRange = Subtract( crossingValue[ 1 ], crossingValue[ 0 ] );
+
+ int iType = bSharpen ? INTERPOLATE_EASE_IN : INTERPOLATE_EASE_OUT;
+
+ Vector points[ 4 ];
+ points[ 0 ].Init();
+ points[ 1 ].Init( 0.0, 0.0, 0.0f );
+ points[ 2 ].Init( 1.0f, 1.0f, 0.0f );
+ points[ 3 ].Init();
+
+ Vector out;
+
+ Interpolator_CurveInterpolate
+ (
+ iType,
+ points[ 0 ], // unused
+ points[ 1 ],
+ points[ 2 ],
+ points[ 3 ], // unused
+ frac,
+ out
+ );
+
+ float flBias = clamp( out.y, 0.0f, 1.0f );
+ float dFrac = flScale * ( frac - flBias );
+
+ newValue = Add( oldValue, ScaleValue( dynamicRange, dFrac ) );
+ }
+
+ writeLayer->InsertKey( curtime, newValue, IsUsingCurveTypes() ? GetDefaultCurveType() : CURVE_DEFAULT );
+ }
+ }
+ break;
+ }
+}
+
+enum PasteState_t
+{
+ PASTE_STATE_BEFORE = -1,
+
+ PASTE_STATE_RAMP_IN = 0,
+ PASTE_STATE_HOLD,
+ PASTE_STATE_RAMP_OUT,
+
+ PASTE_STATE_COUNT,
+ PASTE_STATE_AFTER = PASTE_STATE_COUNT,
+};
+
+template<class T >
+static void CountClipboardSamples( int *pCount, CDmeTypedLogLayer< T > *pClipboard, const DmeLog_TimeSelection_t &params )
+{
+ pCount[0] = pCount[1] = pCount[2] = 0;
+
+ int nKeyCount = pClipboard->GetKeyCount();
+ for ( int i = 0; i < nKeyCount; ++i )
+ {
+ DmeTime_t tKeyTime = pClipboard->GetKeyTime( i );
+ int nIndex = params.ComputeRegionForTime( tKeyTime ) - 1;
+ if ( nIndex < 0 || nIndex > 2 )
+ continue;
+
+ // Only count interstitial samples.. don't count ones that land exactly on boundaries
+ if ( tKeyTime != params.m_nTimes[nIndex] && tKeyTime != params.m_nTimes[nIndex+1] )
+ {
+ pCount[nIndex]++;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used by PasteAndRescaleSamples to determine if it should skip a transition or not
+//-----------------------------------------------------------------------------
+static inline bool ShouldSkipTransition( int nTransition, int nZeroField )
+{
+ // NOTE: This is pretty tricky. The bits of the 'zero field' are set to true
+ // for each region whose source + dest region size is exactly 0 seconds.
+ // Here's the table this logic is reproducing:
+ // 0,1,2,3 are the time selection m_nTimes, and A,B,C are the regions
+ // 0 1 2 3
+ // | A | B | C |
+ //
+ // nZeroField bits
+ // C B A Skip transitions
+ // 0 0 0 none
+ // 0 0 1 2
+ // 0 1 0 2
+ // 0 1 1 1, 2
+ // 1 0 0 1
+ // 1 0 1 1, 2
+ // 1 1 0 1, 2
+ // 1 1 1 1, 2, 3
+ switch( nTransition )
+ {
+ default: case 0: return false;
+ case 1: return ( (( nZeroField & 0x1 ) != 0 ) || nZeroField >= 5 );
+ case 2: return ( nZeroField >= 2 );
+ case 3: return ( nZeroField == 7 );
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::PasteAndRescaleSamples(
+ const CDmeLogLayer *pBase,
+ const CDmeLogLayer *pDataLayer,
+ CDmeLogLayer *pOutputLayer,
+ const DmeLog_TimeSelection_t& srcParams,
+ const DmeLog_TimeSelection_t& destParams,
+ bool bBlendAreaInFalloffRegion )
+{
+ Assert( GetNumLayers() >= 2 );
+ if ( GetNumLayers() < 2 )
+ return;
+
+ CDmeTypedLogLayer< T > *pClipboard = CastElement< CDmeTypedLogLayer< T > >( const_cast< CDmeLogLayer * >( pDataLayer ) );
+
+ // Could have passed in layer with wrong attribute type?!
+ Assert( pClipboard );
+ if ( !pClipboard )
+ return;
+
+ CDmeTypedLogLayer< T > *pBaseLayer = CastElement< CDmeTypedLogLayer< T > >( const_cast< CDmeLogLayer * >( pBase ) );
+ CDmeTypedLogLayer< T > *pWriteLayer = CastElement< CDmeTypedLogLayer< T > >( pOutputLayer );
+ Assert( pBaseLayer );
+ Assert( pWriteLayer );
+
+ // NOTE: Array index 0 is src (pClipboard), index 1 is dest (pWriteLayer)
+ DmeTime_t tStartTime[ PASTE_STATE_COUNT+1 ][ 2 ] =
+ {
+ { DmeTime_t( srcParams.m_nTimes[ 0 ] ), DmeTime_t( destParams.m_nTimes[ 0 ] ) },
+ { DmeTime_t( srcParams.m_nTimes[ 1 ] ), DmeTime_t( destParams.m_nTimes[ 1 ] ) },
+ { DmeTime_t( srcParams.m_nTimes[ 2 ] ), DmeTime_t( destParams.m_nTimes[ 2 ] ) },
+ { DmeTime_t( srcParams.m_nTimes[ 3 ] ), DmeTime_t( destParams.m_nTimes[ 3 ] ) },
+ };
+
+ // compute rescaling factors
+ int pDuration[ PASTE_STATE_COUNT ][ 2 ];
+ double pScaleFactor[ PASTE_STATE_COUNT ];
+ int nZeroField = 0;
+ for ( int i = 0; i < PASTE_STATE_COUNT; ++i )
+ {
+ for ( int s = 0; s < 2; ++s )
+ {
+ pDuration[ i ][ s ] = tStartTime[ i+1 ][ s ].GetTenthsOfMS() - tStartTime[ i ][ s ].GetTenthsOfMS();
+ }
+
+ // We're building up a bitfield to find which regions have src + dest durations of 0
+ // for use in determining which regions to completely skip processing
+ if ( pDuration[i][0] == 0 && pDuration[i][1] == 0 )
+ {
+ nZeroField |= ( 1 << i );
+ }
+
+ pScaleFactor[i] = 1.0;
+ if ( pDuration[ i ][ 0 ] > 0 )
+ {
+ pScaleFactor[i] = 1.0 / ( double )pDuration[ i ][ 0 ];
+ }
+ }
+
+ // Compute values used to paste into selection state transitions
+ T pStartValue[ PASTE_STATE_COUNT + 1 ] =
+ {
+ bBlendAreaInFalloffRegion ? pBaseLayer->GetValue( tStartTime[ PASTE_STATE_RAMP_IN ][ 1 ] ) : pClipboard->GetValue( tStartTime[ PASTE_STATE_RAMP_IN ][ 0 ] ),
+ pClipboard->GetValue( tStartTime[ PASTE_STATE_HOLD ][ 0 ] ),
+ pClipboard->GetValue( tStartTime[ PASTE_STATE_RAMP_OUT ][ 0 ] ),
+ bBlendAreaInFalloffRegion ? pBaseLayer->GetValue( tStartTime[ PASTE_STATE_AFTER ][ 1 ] ) : pClipboard->GetValue( tStartTime[ PASTE_STATE_AFTER ][ 0 ] )
+ };
+
+ // Compute state necessary to blend in the ramp in + ramp out regions
+ // NOTE: These computations are only used if bBlendAreaInFalloffRegion is true
+ T pBlendBase[ 2 ];
+ float pOOBlendLength[ 2 ];
+ DmeTime_t pBlendTime[ 2 ];
+ for ( int s = 0; s < 2; ++s )
+ {
+ pBlendTime[ s ] = destParams.m_nTimes[ TS_FALLOFF(s) ];
+ pBlendBase[ s ] = pBaseLayer->GetValue( pBlendTime[ s ] );
+ T holdValue = pBaseLayer->GetValue( destParams.m_nTimes[ TS_HOLD(s) ] );
+
+ Vector2D vec;
+ vec.x = destParams.m_nTimes[ TS_HOLD(s) ].GetSeconds() - pBlendTime[ s ].GetSeconds();
+ vec.y = LengthOf( Subtract( holdValue, pBlendBase[ s ] ) );
+ pOOBlendLength[ s ] = vec.Length();
+ if ( pOOBlendLength[ s ] != 0.0f )
+ {
+ pOOBlendLength[ s ] = 1.0f / pOOBlendLength[ s ];
+ }
+ }
+
+ // Count the number of samples on the clipboard in the various regions
+ int pKeyCount[PASTE_STATE_COUNT];
+ CountClipboardSamples( pKeyCount, pClipboard, srcParams );
+
+ // Walk the samples in the clipboard
+ int nKeyCount = pClipboard->GetKeyCount();
+ int nPrevState = PASTE_STATE_BEFORE;
+ DmeTime_t tLastWrittenTime = DMETIME_MINTIME;
+ DmeTime_t tMaxKeyTime = DMETIME_MAXTIME;
+ bool bCollapseSamples = false;
+ for ( int j = 0 ; j < nKeyCount; ++j )
+ {
+ DmeTime_t tKeyTime = pClipboard->GetKeyTime( j );
+ T val = pClipboard->GetKeyValue( j );
+
+ // Determine which state we're in
+ // NOTE: Don't use ComputeRegionForTime here because it includes
+ // the endpoint of the hold region into the hold region.
+ int nState;
+ for ( nState = nPrevState; nState < PASTE_STATE_COUNT; ++nState )
+ {
+ if ( tKeyTime < tStartTime[ nState + 1 ][ 0 ] )
+ break;
+ }
+
+ // This logic inserts a key if there is no sample in the clipboard at the transition time
+ bool bForceKey = false;
+ if ( nPrevState < nState )
+ {
+ nState = ++nPrevState;
+
+ // This logic will prevent samples at the hold start + end if
+ // the source + dest regions are 0 width and will only do the first and last
+ // if we're squeezing the entire time selection down to a single point.
+ bForceKey = true;
+
+ if ( nState != PASTE_STATE_AFTER )
+ {
+ bCollapseSamples = ( pKeyCount[nState] >= pDuration[nState][1] );
+ tMaxKeyTime = bCollapseSamples ? tStartTime[ nState ][ 1 ] : ( tStartTime[ nState+1 ][ 1 ] - DmeTime_t( pKeyCount[nState] + 1 ) );
+ }
+ else
+ {
+ bCollapseSamples = false;
+ tMaxKeyTime = DMETIME_MAXTIME;
+ }
+
+ // NOTE: This has to occur after collapse samples + max key time has been set
+ if ( ShouldSkipTransition( nState, nZeroField ) )
+ {
+ --j;
+ continue;
+ }
+
+ // Don't insert an extra key if the current one we're looking at is right at that point
+ if ( tKeyTime != tStartTime[ nPrevState ][ 0 ] )
+ {
+ tKeyTime = tStartTime[ nPrevState ][ 0 ];
+ val = pStartValue[nPrevState];
+
+ // We want to re-do this key, since we inserted a key beforehand
+ --j;
+ }
+ }
+
+ if ( nState == PASTE_STATE_BEFORE )
+ continue;
+
+ if ( nState == PASTE_STATE_AFTER && !bForceKey )
+ return;
+
+ // Compute destination time based on scale + offset
+ double flFactor = ( tKeyTime - tStartTime[ nState ][ 0 ] ).GetTenthsOfMS() * pScaleFactor[ nState ];
+
+ // FIXME: Fix the algorithm, then uncomment to get time-scaled falloff regions
+ // if ( nState == PASTE_STATE_RAMP_IN || nState == PASTE_STATE_RAMP_OUT )
+ // {
+ // int s = ( nState == PASTE_STATE_RAMP_IN ) ? 0 : 1;
+ // flFactor = ComputeInterpolationFactor( flFactor, destParams.m_nFalloffInterpolatorTypes[s] );
+ // }
+ double flTempTime = flFactor * pDuration[ nState ][ 1 ];
+ DmeTime_t tDestTime( (int)( flTempTime + 0.5 ) );
+ tDestTime += tStartTime[ nState ][ 1 ];
+
+ // Clamp necessary to not lose samples
+ // NOTE: The !bForceKey check here makes it so we don't clamp points
+ // in time corresponding to transitions of the time selection
+ if ( !bForceKey && ( tDestTime > tMaxKeyTime ) )
+ {
+ tDestTime = tMaxKeyTime;
+ }
+ if ( tMaxKeyTime != DMETIME_MAXTIME )
+ {
+ tMaxKeyTime += DMETIME_MINDELTA;
+ }
+
+ // This logic will cause *all* samples to appear if we have enough room for them
+ if ( !bCollapseSamples )
+ {
+ bForceKey = true;
+ }
+
+ // If we'd go outside our region and we're not forcing the key, then skip
+ if ( !bForceKey && tDestTime >= tStartTime[ nState+1 ][ 1 ] )
+ continue;
+
+ // Perform blending on ramp in + ramp out regions
+ if ( bBlendAreaInFalloffRegion && ( nState != PASTE_STATE_HOLD ) )
+ {
+ int nBlendIndex = ( nState < PASTE_STATE_HOLD ) ? 0 : 1;
+ T baseValue = pBaseLayer->GetValue( tDestTime );
+
+ Vector2D oldDist;
+ oldDist.x = tDestTime.GetSeconds() - pBlendTime[ nBlendIndex ].GetSeconds();
+ oldDist.y = LengthOf( Subtract( baseValue, pBlendBase[ nBlendIndex ] ) );
+
+ float flDistance = oldDist.Length();
+ float flFactorBlend = flDistance * pOOBlendLength[ nBlendIndex ];
+ flFactorBlend = destParams.AdjustFactorForInterpolatorType( flFactorBlend, nBlendIndex );
+ val = Interpolate( flFactorBlend, baseValue, val );
+ }
+
+ // Force key insertion when we transition between states
+ if ( bForceKey && ( tLastWrittenTime >= tDestTime ) )
+ {
+ tDestTime = tLastWrittenTime + DMETIME_MINDELTA;
+ }
+
+ // Insert the key into the log
+ if ( tLastWrittenTime < tDestTime )
+ {
+ pWriteLayer->InsertKey( tDestTime, val );
+ tLastWrittenTime = tDestTime;
+ }
+ }
+}
+
+template< class T >
+void CDmeTypedLog< T >::PasteAndRescaleSamples(
+ const CDmeLogLayer *src, // clipboard data
+ const DmeLog_TimeSelection_t& srcParams, // clipboard time selection
+ const DmeLog_TimeSelection_t& destParams, // current time selection
+ bool bBlendAreaInFalloffRegion ) // blending behavior in falloff area of current time selection
+{
+ CDmeLogLayer *pBaseLayer = GetLayer( 0 );
+ CDmeLogLayer *pWriteLayer = GetLayer( GetTopmostLayer() );
+ PasteAndRescaleSamples( pBaseLayer, src, pWriteLayer, srcParams, destParams, bBlendAreaInFalloffRegion );
+}
+
+template<>
+void CDmeTypedLog< Vector >::BuildNormalizedLayer( CDmeTypedLogLayer< float > *target )
+{
+ Assert( target );
+ Assert( GetDataType() != AT_FLOAT );
+
+ CDmeTypedLogLayer< Vector > *baseLayer = static_cast< CDmeTypedLogLayer< Vector > * >( GetLayer( 0 ) );
+ if ( !baseLayer )
+ return;
+
+ float flMin = FLT_MAX;
+ float flMax = FLT_MIN;
+
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t keyTime = baseLayer->GetKeyTime( i );
+ Vector keyValue = baseLayer->GetKeyValue( i );
+
+ float len = keyValue.Length();
+ if ( len < flMin )
+ {
+ flMin = len;
+ }
+ if ( len > flMax )
+ {
+ flMax = len;
+ }
+
+ target->InsertKey( keyTime, len );
+ }
+
+ for ( int i = 0; i < kc; ++i )
+ {
+ float keyValue = target->GetKeyValue( i );
+ float normalized = RemapVal( keyValue, flMin, flMax, 0.0f, 1.0f );
+ target->SetKeyValue( i, normalized );
+ }
+
+ if ( HasDefaultValue() )
+ {
+ target->GetTypedOwnerLog()->SetDefaultValue( RemapVal( GetDefaultValue().Length(), flMin, flMax, 0.0f, 1.0f ) );
+ }
+}
+
+template<>
+void CDmeTypedLog< Vector2D >::BuildNormalizedLayer( CDmeTypedLogLayer< float > *target )
+{
+ Assert( target );
+ Assert( GetDataType() != AT_FLOAT );
+
+ CDmeTypedLogLayer< Vector2D > *baseLayer = static_cast< CDmeTypedLogLayer< Vector2D > * >( GetLayer( 0 ) );
+ if ( !baseLayer )
+ return;
+
+ float flMin = FLT_MAX;
+ float flMax = FLT_MIN;
+
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t keyTime = baseLayer->GetKeyTime( i );
+ Vector2D keyValue = baseLayer->GetKeyValue( i );
+
+ float len = keyValue.Length();
+
+ if ( len < flMin )
+ {
+ flMin = len;
+ }
+ if ( len > flMax )
+ {
+ flMax = len;
+ }
+
+ target->InsertKey( keyTime, len );
+ }
+
+ for ( int i = 0; i < kc; ++i )
+ {
+ float keyValue = target->GetKeyValue( i );
+ float normalized = RemapVal( keyValue, flMin, flMax, 0.0f, 1.0f );
+ target->SetKeyValue( i, normalized );
+ }
+
+ if ( HasDefaultValue() )
+ {
+ target->GetTypedOwnerLog()->SetDefaultValue( RemapVal( GetDefaultValue().Length(), flMin, flMax, 0.0f, 1.0f ) );
+ }
+}
+
+template<>
+void CDmeTypedLog< Vector4D >::BuildNormalizedLayer( CDmeTypedLogLayer< float > *target )
+{
+ Assert( target );
+ Assert( GetDataType() != AT_FLOAT );
+
+ CDmeTypedLogLayer< Vector4D > *baseLayer = static_cast< CDmeTypedLogLayer< Vector4D > * >( GetLayer( 0 ) );
+ if ( !baseLayer )
+ return;
+
+ float flMin = FLT_MAX;
+ float flMax = FLT_MIN;
+
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t keyTime = baseLayer->GetKeyTime( i );
+ Vector4D keyValue = baseLayer->GetKeyValue( i );
+
+ float len = keyValue.Length();
+
+ if ( len < flMin )
+ {
+ flMin = len;
+ }
+ if ( len > flMax )
+ {
+ flMax = len;
+ }
+
+ target->InsertKey( keyTime, len );
+ }
+
+ for ( int i = 0; i < kc; ++i )
+ {
+ float keyValue = target->GetKeyValue( i );
+ float normalized = RemapVal( keyValue, flMin, flMax, 0.0f, 1.0f );
+ target->SetKeyValue( i, normalized );
+ }
+
+ if ( HasDefaultValue() )
+ {
+ target->GetTypedOwnerLog()->SetDefaultValue( RemapVal( GetDefaultValue().Length(), flMin, flMax, 0.0f, 1.0f ) );
+ }
+}
+
+template<>
+void CDmeTypedLog< int >::BuildNormalizedLayer( CDmeTypedLogLayer< float > *target )
+{
+ Assert( target );
+ Assert( GetDataType() != AT_FLOAT );
+
+ CDmeTypedLogLayer< int > *baseLayer = static_cast< CDmeTypedLogLayer< int > * >( GetLayer( 0 ) );
+ if ( !baseLayer )
+ return;
+
+ float flMin = FLT_MAX;
+ float flMax = FLT_MIN;
+
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t keyTime = baseLayer->GetKeyTime( i );
+ int keyValue = baseLayer->GetKeyValue( i );
+
+ float len = (float)keyValue;
+
+ if ( len < flMin )
+ {
+ flMin = len;
+ }
+ if ( len > flMax )
+ {
+ flMax = len;
+ }
+
+ target->InsertKey( keyTime, len );
+ }
+
+ for ( int i = 0; i < kc; ++i )
+ {
+ float keyValue = target->GetKeyValue( i );
+ float normalized = RemapVal( keyValue, flMin, flMax, 0.0f, 1.0f );
+ target->SetKeyValue( i, normalized );
+ }
+
+ if ( HasDefaultValue() )
+ {
+ target->GetTypedOwnerLog()->SetDefaultValue( RemapVal( GetDefaultValue(), flMin, flMax, 0.0f, 1.0f ) );
+ }
+}
+
+template<>
+void CDmeTypedLog< float >::BuildNormalizedLayer( CDmeTypedLogLayer< float > *target )
+{
+ Assert( target );
+ Assert( GetDataType() != AT_FLOAT );
+
+ CDmeTypedLogLayer< float > *baseLayer = static_cast< CDmeTypedLogLayer< float > * >( GetLayer( 0 ) );
+ if ( !baseLayer )
+ return;
+
+ float flMin = FLT_MAX;
+ float flMax = FLT_MIN;
+
+ int kc = baseLayer->GetKeyCount();
+ for ( int i = 0; i < kc; ++i )
+ {
+ DmeTime_t keyTime = baseLayer->GetKeyTime( i );
+ int keyValue = baseLayer->GetKeyValue( i );
+
+ float len = (float)keyValue;
+
+ if ( len < flMin )
+ {
+ flMin = len;
+ }
+ if ( len > flMax )
+ {
+ flMax = len;
+ }
+
+ target->InsertKey( keyTime, len );
+ }
+
+ for ( int i = 0; i < kc; ++i )
+ {
+ float keyValue = target->GetKeyValue( i );
+ float normalized = RemapVal( keyValue, flMin, flMax, 0.0f, 1.0f );
+ target->SetKeyValue( i, normalized );
+ }
+
+ if ( HasDefaultValue() )
+ {
+ target->GetTypedOwnerLog()->SetDefaultValue( RemapVal( GetDefaultValue(), flMin, flMax, 0.0f, 1.0f ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Creates a log of a specific type
+//-----------------------------------------------------------------------------
+CDmeLog *CDmeLog::CreateLog( DmAttributeType_t type, DmFileId_t fileid )
+{
+ switch ( type )
+ {
+ case AT_INT:
+ case AT_INT_ARRAY:
+ return CreateElement< CDmeIntLog >( "int log", fileid );
+ case AT_FLOAT:
+ case AT_FLOAT_ARRAY:
+ return CreateElement< CDmeFloatLog >( "float log", fileid );
+ case AT_BOOL:
+ case AT_BOOL_ARRAY:
+ return CreateElement< CDmeBoolLog >( "bool log", fileid );
+ case AT_COLOR:
+ case AT_COLOR_ARRAY:
+ return CreateElement< CDmeColorLog >( "color log", fileid );
+ case AT_VECTOR2:
+ case AT_VECTOR2_ARRAY:
+ return CreateElement< CDmeVector2Log >( "vector2 log", fileid );
+ case AT_VECTOR3:
+ case AT_VECTOR3_ARRAY:
+ return CreateElement< CDmeVector3Log >( "vector3 log", fileid );
+ case AT_VECTOR4:
+ case AT_VECTOR4_ARRAY:
+ return CreateElement< CDmeVector4Log >( "vector4 log", fileid );
+ case AT_QANGLE:
+ case AT_QANGLE_ARRAY:
+ return CreateElement< CDmeQAngleLog >( "qangle log", fileid );
+ case AT_QUATERNION:
+ case AT_QUATERNION_ARRAY:
+ return CreateElement< CDmeQuaternionLog >( "quaternion log", fileid );
+ case AT_VMATRIX:
+ case AT_VMATRIX_ARRAY:
+ return CreateElement< CDmeVMatrixLog >( "vmatrix log", fileid );
+ case AT_STRING:
+ case AT_STRING_ARRAY:
+ return CreateElement< CDmeStringLog >( "string log", fileid );
+ }
+
+ return NULL;
+}
+
+// Disallowed methods for types
+//template<> void CDmeTypedLog< bool >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const bool& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< bool >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const bool& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< bool >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< bool >::FinishTimeSelection( DmeLog_TimeSelection_t& params ) { Assert( 0 ); }
+//
+//template<> void CDmeTypedLog< Color >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const Color& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Color >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Color& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Color >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Color >::FinishTimeSelection( DmeLog_TimeSelection_t& params ) { Assert( 0 ); }
+//
+//template<> void CDmeTypedLog< Vector4D >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const Vector4D& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector4D >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Vector4D& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector4D >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector4D >::FinishTimeSelection( DmeLog_TimeSelection_t& params ) { Assert( 0 ); }
+//
+//template<> void CDmeTypedLog< Vector2D >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const Vector2D& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector2D >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Vector2D& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector2D >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector2D >::FinishTimeSelection( DmeLog_TimeSelection_t& params ) { Assert( 0 ); }
+
+//template<> void CDmeTypedLog< Vector >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const Vector& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Vector& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Vector >::FinishTimeSelection( DmeLog_TimeSelection_t& params ) { Assert( 0 ); }
+
+//template<> void CDmeTypedLog< VMatrix >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const VMatrix& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< VMatrix >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const VMatrix& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< VMatrix >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< VMatrix >::FinishTimeSelection( DmeLog_TimeSelection_t& params ) { Assert( 0 ); }
+//
+//template<> void CDmeTypedLog< Quaternion >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const Quaternion& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Quaternion >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const Quaternion& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Quaternion >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< Quaternion >::FinishTimeSelection( DmeLog_TimeSelection_t& params ) { Assert( 0 ); }
+//
+//template<> void CDmeTypedLog< QAngle >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const QAngle& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< QAngle >::_StampKeyAtHeadResample( const DmeLog_TimeSelection_t& params, const QAngle& value ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< QAngle >::StampKeyAtHead( const DmeLog_TimeSelection_t& params, const CDmAttribute *pAttr, uint index /*= 0*/ ) { Assert( 0 ); }
+//template<> void CDmeTypedLog< QAngle >::FinishTimeSelection( DmeLog_TimeSelection_t& params ) { Assert( 0 ); }
+
+
+//-----------------------------------------------------------------------------
+// Helpers for particular types of log layers
+//-----------------------------------------------------------------------------
+void GenerateRotationLog( CDmeQuaternionLogLayer *pLayer, const Vector &vecAxis, DmeTime_t pTime[4], float pRevolutionsPerSec[4] )
+{
+ for ( int i = 1; i < 4; ++i )
+ {
+ if ( pTime[i] < pTime[i-1] )
+ {
+ Warning( "Bogus times passed into GenerateRotationLog\n" );
+ return;
+ }
+ }
+
+ // Gets the initial value
+ matrix3x4_t initial;
+ Quaternion q = pLayer->GetValue( pTime[0] );
+ QuaternionMatrix( q, initial );
+
+ // Find the max rps, and compute the total rotation in degrees
+ // by the time we reach the transition points. The total rotation =
+ // integral from 0 to t of 360 * ( rate[i] - rate[i-1] ) t / tl + rate[i-1] )
+ // == 360 * ( ( rate[i] - rate[i-1] ) t^2 / 2 + rate[i-1] t )
+ float pTotalRotation[4];
+ float flMaxRPS = pRevolutionsPerSec[0];
+ pTotalRotation[0] = 0.0f;
+ for ( int i = 1; i < 4; ++i )
+ {
+ if ( pRevolutionsPerSec[i] > flMaxRPS )
+ {
+ flMaxRPS = pRevolutionsPerSec[i];
+ }
+ float dt = pTime[i].GetSeconds() - pTime[i-1].GetSeconds();
+ float dRot = pRevolutionsPerSec[i] - pRevolutionsPerSec[i-1];
+ pTotalRotation[i] = 360.0f * ( dRot * dt * 0.5 + pRevolutionsPerSec[i-1] * dt ) + pTotalRotation[i-1];
+ }
+
+ // We need to compute how long a single rotation takes, then create samples
+ // at 1/4 the frequency of that amount of time
+ VMatrix rot;
+ matrix3x4_t total;
+ QAngle angles;
+ float flMaxRotationTime = (flMaxRPS != 0.0f) ? ( 0.125f / flMaxRPS ) : ( pTime[3].GetSeconds() - pTime[0].GetSeconds() );
+ DmeTime_t dt( flMaxRotationTime );
+ for ( DmeTime_t t = pTime[0]; t <= pTime[3]; t += dt )
+ {
+ int i = ( t < pTime[1] ) ? 1 : ( ( t < pTime[2] ) ? 2 : 3 );
+ float flInterval = t.GetSeconds() - pTime[i-1].GetSeconds();
+ float flOOSegmentDur = pTime[i].GetSeconds() - pTime[i-1].GetSeconds();
+ if ( flOOSegmentDur == 0.0f )
+ {
+ Assert( flInterval == 0.0f );
+ flOOSegmentDur = 1.0f;
+ }
+ else
+ {
+ flOOSegmentDur = 1.0f / flOOSegmentDur;
+ }
+ float dRot = pRevolutionsPerSec[i] - pRevolutionsPerSec[i-1];
+ float flRotation = 360.0f * ( dRot * flInterval * flInterval * 0.5f * flOOSegmentDur + pRevolutionsPerSec[i-1] * flInterval ) + pTotalRotation[i-1];
+
+ MatrixBuildRotationAboutAxis( rot, vecAxis, flRotation );
+ ConcatTransforms( initial, rot.As3x4(), total );
+ MatrixToAngles( total, angles );
+ AngleQuaternion( angles, q );
+ pLayer->SetKey( t, q );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Transforms a position log
+//-----------------------------------------------------------------------------
+void RotatePositionLog( CDmeVector3LogLayer *pPositionLog, const matrix3x4_t& matrix )
+{
+ Assert( fabs( matrix[0][3] ) < 1e-3 && fabs( matrix[1][3] ) < 1e-3 && fabs( matrix[2][3] ) < 1e-3 );
+ Vector position;
+ int nCount = pPositionLog->GetKeyCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const Vector &srcPosition = pPositionLog->GetKeyValue( i );
+ VectorTransform( srcPosition, matrix, position );
+ pPositionLog->SetKeyValue( i, position );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Transforms a orientation log
+//-----------------------------------------------------------------------------
+void RotateOrientationLog( CDmeQuaternionLogLayer *pOrientationLog, const matrix3x4_t& matrix, bool bPreMultiply = false )
+{
+ Assert( fabs( matrix[0][3] ) < 1e-3 && fabs( matrix[1][3] ) < 1e-3 && fabs( matrix[2][3] ) < 1e-3 );
+ matrix3x4_t orientation, newOrientation;
+ Quaternion q;
+ int nCount = pOrientationLog->GetKeyCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const Quaternion &srcQuat = pOrientationLog->GetKeyValue( i );
+ QuaternionMatrix( srcQuat, orientation );
+ if ( bPreMultiply )
+ {
+ ConcatTransforms( matrix, orientation, newOrientation );
+ }
+ else
+ {
+ ConcatTransforms( orientation, matrix, newOrientation );
+ }
+ MatrixQuaternion( newOrientation, q );
+ pOrientationLog->SetKeyValue( i, q );
+ }
+}
diff --git a/movieobjects/dmemakefile.cpp b/movieobjects/dmemakefile.cpp
new file mode 100644
index 0000000..3fc41ba
--- /dev/null
+++ b/movieobjects/dmemakefile.cpp
@@ -0,0 +1,628 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Describes an asset: something that is compiled from sources,
+// in potentially multiple steps, to a compiled resource
+//
+//=============================================================================
+
+
+#include "movieobjects/dmemdlmakefile.h"
+#include "movieobjects/idmemakefileutils.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "tier2/fileutils.h"
+#include "tier3/tier3.h"
+#include "filesystem.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Hook into element factories
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSource, CDmeSource );
+
+
+//-----------------------------------------------------------------------------
+// Construction/destruction
+//-----------------------------------------------------------------------------
+void CDmeSource::OnConstruction()
+{
+ m_DependentMakefile = NULL;
+}
+
+void CDmeSource::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets/gets the makefile that was used to build this source
+//-----------------------------------------------------------------------------
+void CDmeSource::SetDependentMakefile( CDmeMakefile *pMakeFile )
+{
+ m_DependentMakefile = pMakeFile;
+}
+
+CDmeMakefile *CDmeSource::GetDependentMakefile()
+{
+ return m_DependentMakefile.Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Call this to open the source file in an editor
+//-----------------------------------------------------------------------------
+void CDmeSource::OpenEditor()
+{
+ if ( g_pDmeMakefileUtils )
+ {
+ g_pDmeMakefileUtils->PerformOpenEditor( this );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook into element factories
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMakefile, CDmeMakefile );
+
+
+//-----------------------------------------------------------------------------
+// Construction/destruction
+//-----------------------------------------------------------------------------
+void CDmeMakefile::OnConstruction()
+{
+ m_Sources.Init( this, "sources" );
+ m_hOutput = NULL;
+ m_hCompileProcess = PROCESS_HANDLE_INVALID;
+ m_bIsDirty = false;
+}
+
+void CDmeMakefile::OnDestruction()
+{
+ DestroyOutputElement( m_hOutput.Get() );
+ m_hOutput = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Performs pre-compilation step
+//-----------------------------------------------------------------------------
+void CDmeMakefile::PreCompile( )
+{
+ // Make all outputs writeable
+ MakeOutputsWriteable();
+
+ // Destroy the current output object; we'll need to reload it
+ // NOTE: Don't check for m_hOutput == 0; we always need to call DestroyOutputElement
+ // Sometimes makefiles have to do stuff even if m_hOutput == NULL
+ DestroyOutputElement( m_hOutput );
+ m_hOutput = NULL;
+}
+
+void CDmeMakefile::PostCompile( )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the output element created by compilation of this makefile
+//-----------------------------------------------------------------------------
+CDmElement *CDmeMakefile::GetOutputElement( bool bCreateIfNecessary )
+{
+ if ( m_hOutput.Get() )
+ return m_hOutput.Get();
+
+ if ( !bCreateIfNecessary )
+ return NULL;
+
+ if ( !g_pDmeMakefileUtils || !g_pDmeMakefileUtils->IsCurrentlyCompiling() )
+ {
+ m_hOutput = CreateOutputElement();
+ }
+
+ return m_hOutput.Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the path of the makefile
+//-----------------------------------------------------------------------------
+void CDmeMakefile::GetMakefilePath( char *pFullPath, int nBufLen )
+{
+ DmFileId_t fileId = GetFileId();
+ const char *pFileName = ( fileId != DMFILEID_INVALID ) ? g_pDataModel->GetFileName( fileId ) : "";
+ Assert( !pFileName[0] || Q_IsAbsolutePath( pFileName ) );
+
+ Q_ExtractFilePath( pFileName, pFullPath, nBufLen );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the output directory we expect to compile files into
+//-----------------------------------------------------------------------------
+bool CDmeMakefile::GetOutputDirectory( char *pFullPath, int nBufLen )
+{
+ return GetDefaultDirectory( GetOutputDirectoryID(), pFullPath, nBufLen );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the output name (output directory + filename, no extension)
+//-----------------------------------------------------------------------------
+bool CDmeMakefile::GetOutputName( char *pFullPath, int nBufLen )
+{
+ pFullPath[0] = 0;
+
+ char pOutputPath[MAX_PATH];
+ if ( !GetDefaultDirectory( GetOutputDirectoryID(), pOutputPath, sizeof(pOutputPath) ) )
+ return false;
+
+ DmFileId_t fileId = GetFileId();
+ const char *pFileName = ( fileId != DMFILEID_INVALID ) ? g_pDataModel->GetFileName( fileId ) : "";
+ if ( !pFileName || !pFileName[0] )
+ return false;
+
+ Q_ComposeFileName( pOutputPath, Q_UnqualifiedFileName(pFileName), pFullPath, nBufLen );
+ Q_RemoveDotSlashes( pFullPath );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Converts the m_pDefaultDirectoryID field of the DmeMakefileType_t to a full path
+//-----------------------------------------------------------------------------
+bool CDmeMakefile::GetDefaultDirectory( const char *pDefaultDirectoryID, char *pFullPath, int nBufLen )
+{
+ if ( StringHasPrefix( pDefaultDirectoryID, "contentdir:" ) )
+ {
+ pDefaultDirectoryID += 11;
+ GetModContentSubdirectory( pDefaultDirectoryID, pFullPath, nBufLen );
+ Q_RemoveDotSlashes( pFullPath );
+ return true;
+ }
+
+ if ( StringHasPrefix( pDefaultDirectoryID, "gamedir:" ) )
+ {
+ pDefaultDirectoryID += 8;
+ GetModSubdirectory( pDefaultDirectoryID, pFullPath, nBufLen );
+ Q_RemoveDotSlashes( pFullPath );
+ return true;
+ }
+
+ if ( StringHasPrefix( pDefaultDirectoryID, "makefiledir:" ) )
+ {
+ char pMakefilePath[MAX_PATH];
+ GetMakefilePath( pMakefilePath, sizeof(pMakefilePath) );
+ pDefaultDirectoryID += 12;
+ Q_ComposeFileName( pMakefilePath, pDefaultDirectoryID, pFullPath, nBufLen );
+ Q_RemoveDotSlashes( pFullPath );
+ return true;
+ }
+
+ if ( StringHasPrefix( pDefaultDirectoryID, "makefilegamedir:" ) )
+ {
+ char pMakefilePath[MAX_PATH];
+ GetMakefilePath( pMakefilePath, sizeof(pMakefilePath) );
+
+ char pModContentDirectory[MAX_PATH];
+ GetModContentSubdirectory( NULL, pModContentDirectory, sizeof(pModContentDirectory) );
+
+ char pRelativePath[MAX_PATH];
+ if ( !Q_MakeRelativePath( pMakefilePath, pModContentDirectory, pRelativePath, sizeof(pRelativePath) ) )
+ {
+ pFullPath[0] = 0;
+ return false;
+ }
+
+ char pModDirectory[MAX_PATH];
+ GetModSubdirectory( NULL, pModDirectory, sizeof(pModDirectory) );
+
+ char pMakefileGamePath[MAX_PATH];
+ Q_ComposeFileName( pModDirectory, pRelativePath, pMakefileGamePath, sizeof(pMakefileGamePath) );
+
+ pDefaultDirectoryID += 16;
+ Q_ComposeFileName( pMakefileGamePath, pDefaultDirectoryID, pFullPath, nBufLen );
+ Q_RemoveDotSlashes( pFullPath );
+ return true;
+ }
+
+ // Assume it's a content subdir
+ GetModContentSubdirectory( pDefaultDirectoryID, pFullPath, nBufLen );
+ Q_RemoveDotSlashes( pFullPath );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Relative path to full path
+//-----------------------------------------------------------------------------
+void CDmeMakefile::RelativePathToFullPath( const char *pRelativePath, char *pFullPath, int nBufLen )
+{
+ if ( !pRelativePath[0] )
+ {
+ pFullPath[0] = 0;
+ return;
+ }
+ char pRootDir[ MAX_PATH ];
+ GetMakefilePath( pRootDir, sizeof(pRootDir) );
+ Q_ComposeFileName( pRootDir, pRelativePath, pFullPath, nBufLen );
+ Q_RemoveDotSlashes( pFullPath );
+}
+
+
+//-----------------------------------------------------------------------------
+// Fullpath to relative path
+//-----------------------------------------------------------------------------
+void CDmeMakefile::FullPathToRelativePath( const char *pFullPath, char *pRelativePath, int nBufLen )
+{
+ if ( !pFullPath[0] )
+ {
+ pRelativePath[0] = 0;
+ return;
+ }
+ char pRootDir[ MAX_PATH ];
+ GetMakefilePath( pRootDir, sizeof(pRootDir) );
+ if ( pRootDir[0] )
+ {
+ Q_MakeRelativePath( pFullPath, pRootDir, pRelativePath, nBufLen );
+ }
+ else
+ {
+ Q_strncpy( pRelativePath, pFullPath, nBufLen );
+ Q_FixSlashes( pRelativePath );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a single source
+//-----------------------------------------------------------------------------
+CDmeSource *CDmeMakefile::AddSource( const char *pSourceType, const char *pFullPath )
+{
+ if ( pFullPath[0] && FindSource( pSourceType, pFullPath ) )
+ {
+ Warning( "Attempted to add the same source twice %s!\n", pFullPath );
+ return NULL;
+ }
+
+ CDmElement *pElement = GetElement< CDmElement >( g_pDataModel->CreateElement( pSourceType, "", GetFileId() ) );
+ CDmeSource *pSource = CastElement< CDmeSource >( pElement );
+ Assert( pSource );
+ if ( !pSource )
+ {
+ Warning( "Invalid source type name %s!\n", pSourceType );
+ if ( pElement )
+ {
+ DestroyElement( pElement );
+ }
+ return NULL;
+ }
+
+ char pRelativePath[MAX_PATH];
+ FullPathToRelativePath( pFullPath, pRelativePath, sizeof( pRelativePath ) );
+ pSource->SetRelativeFileName( pRelativePath );
+ m_Sources.AddToTail( pSource );
+ return pSource;
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes a single source
+//-----------------------------------------------------------------------------
+CDmeSource *CDmeMakefile::FindSource( const char *pSourceType, const char *pFullPath )
+{
+ char pRelativePath[MAX_PATH];
+ FullPathToRelativePath( pFullPath, pRelativePath, sizeof( pRelativePath ) );
+ int nCount = m_Sources.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( Q_stricmp( pSourceType, m_Sources[i]->GetTypeString() ) )
+ continue;
+
+ if ( !Q_stricmp( pRelativePath, m_Sources[i]->GetRelativeFileName() ) )
+ return m_Sources[i];
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets a source to be a single source
+//-----------------------------------------------------------------------------
+CDmeSource *CDmeMakefile::SetSingleSource( const char *pSourceType, const char *pFullPath )
+{
+ // FIXME: we maybe shouldn't remove everything if the source can't be created for some reason?
+ RemoveAllSources( pSourceType );
+ return AddSource( pSourceType, pFullPath );
+}
+
+
+//-----------------------------------------------------------------------------
+// Changes a source
+//-----------------------------------------------------------------------------
+void CDmeMakefile::SetSourceFullPath( CDmeSource *pSource, const char *pFullPath )
+{
+ char pRelativePath[MAX_PATH];
+ FullPathToRelativePath( pFullPath, pRelativePath, sizeof( pRelativePath ) );
+
+ if ( Q_stricmp( pRelativePath, pSource->GetRelativeFileName() ) )
+ {
+ pSource->SetRelativeFileName( pRelativePath );
+
+ // FIXME: Should we delete the dependent makefile?
+ pSource->SetDependentMakefile( NULL );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the full path of a source
+//-----------------------------------------------------------------------------
+void CDmeMakefile::GetSourceFullPath( CDmeSource *pSource, char *pFullPath, int nBufLen )
+{
+ const char *pRelativePath = pSource->GetRelativeFileName( );
+ RelativePathToFullPath( pRelativePath, pFullPath, nBufLen );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a list of sources
+//-----------------------------------------------------------------------------
+void CDmeMakefile::GetSources( const char *pSourceType, CUtlVector< CDmeHandle< CDmeSource > > &sources )
+{
+ int nCount = m_Sources.Count();
+ sources.EnsureCapacity( nCount );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Sources[i]->IsA( pSourceType ) )
+ {
+ int j = sources.AddToTail();
+ sources[j] = m_Sources[i];
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets a list of all sources, regardless of type
+//-----------------------------------------------------------------------------
+int CDmeMakefile::GetSourceCount()
+{
+ return m_Sources.Count();
+}
+
+CDmeSource *CDmeMakefile::GetSource( int nIndex )
+{
+ return m_Sources[nIndex];
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes a single source
+//-----------------------------------------------------------------------------
+void CDmeMakefile::RemoveSource( CDmeSource *pSource )
+{
+ int nCount = m_Sources.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Sources[i] == pSource )
+ {
+ m_Sources.Remove( i );
+ break;
+ }
+ }
+}
+
+void CDmeMakefile::RemoveSource( const char *pSourceType, const char *pFullPath )
+{
+ char pRelativePath[MAX_PATH];
+ FullPathToRelativePath( pFullPath, pRelativePath, sizeof( pRelativePath ) );
+ int nCount = m_Sources.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( Q_stricmp( pSourceType, m_Sources[i]->GetTypeString() ) )
+ continue;
+
+ if ( !Q_stricmp( pRelativePath, m_Sources[i]->GetRelativeFileName() ) )
+ {
+ m_Sources.Remove( i );
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes all sources of a particular type
+//-----------------------------------------------------------------------------
+void CDmeMakefile::RemoveAllSources( const char *pSourceType )
+{
+ int nCount = m_Sources.Count();
+ for ( int i = nCount; --i >= 0; )
+ {
+ if ( !Q_stricmp( pSourceType, m_Sources[i]->GetTypeString() ) )
+ {
+ // NOTE: This works because we're iterating backward
+ m_Sources.Remove( i );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Source iteration
+//-----------------------------------------------------------------------------
+bool CDmeMakefile::HasSourceOfType( const char *pSourceType )
+{
+ int nCount = m_Sources.Count();
+ for ( int i = nCount; --i >= 0; )
+ {
+ if ( !Q_stricmp( pSourceType, m_Sources[i]->GetTypeString() ) )
+ return true;
+ }
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the source names to be relative to a particular path
+//-----------------------------------------------------------------------------
+bool CDmeMakefile::UpdateSourceNames( const char *pOldRootDir, const char *pNewRootDir, bool bApplyChanges )
+{
+ char pOldSourcePath[ MAX_PATH ];
+ char pNewSourcePath[ MAX_PATH ];
+
+ int nCount = m_Sources.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ const char *pOldRelativePath = m_Sources[i]->GetRelativeFileName();
+ if ( pOldRelativePath[0] )
+ {
+ Q_ComposeFileName( pOldRootDir, pOldRelativePath, pOldSourcePath, sizeof(pOldSourcePath) );
+ Q_RemoveDotSlashes( pOldSourcePath );
+ if ( !Q_MakeRelativePath( pOldSourcePath, pNewRootDir, pNewSourcePath, sizeof(pNewSourcePath) ) )
+ {
+ Assert( !bApplyChanges );
+ return false;
+ }
+ }
+ else
+ {
+ pNewSourcePath[0] = 0;
+ }
+
+ if ( !bApplyChanges )
+ continue;
+
+ m_Sources[i]->SetRelativeFileName( pNewSourcePath );
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the filename
+//-----------------------------------------------------------------------------
+const char *CDmeMakefile::GetFileName() const
+{
+ DmFileId_t fileId = GetFileId();
+ return g_pDataModel->GetFileName( fileId );
+}
+
+
+//-----------------------------------------------------------------------------
+// Call this to change the file the makefile is stored in
+// Will make all sources be relative to this path
+//-----------------------------------------------------------------------------
+bool CDmeMakefile::SetFileName( const char *pFileName )
+{
+ if ( !Q_IsAbsolutePath( pFileName ) )
+ return false;
+
+ char pOldRootDir[ MAX_PATH ];
+ char pNewRootDir[ MAX_PATH ];
+ GetMakefilePath( pOldRootDir, sizeof(pOldRootDir) );
+ Q_ExtractFilePath( pFileName, pNewRootDir, sizeof(pNewRootDir) );
+
+ // Gotta do this twice; once to check for validity, once to actually do it
+ if ( !UpdateSourceNames( pOldRootDir, pNewRootDir, false ) )
+ return false;
+
+ UpdateSourceNames( pOldRootDir, pNewRootDir, true );
+
+ DmFileId_t fileId = GetFileId();
+ if ( fileId == DMFILEID_INVALID )
+ {
+ fileId = g_pDataModel->FindOrCreateFileId( pFileName );
+ SetFileId( fileId, TD_DEEP );
+ }
+ else
+ {
+ g_pDataModel->SetFileName( fileId, pFileName );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Make all outputs writeable
+//-----------------------------------------------------------------------------
+void CDmeMakefile::MakeOutputsWriteable( )
+{
+ // When we publish, we'll check them out.
+ CUtlVector<CUtlString> outputs;
+ GetOutputs( outputs );
+ int nCount = outputs.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ g_pFullFileSystem->SetFileWritable( outputs[i], true );
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Sets a makefile/source association
+//-----------------------------------------------------------------------------
+void CDmeMakefile::SetAssociation( CDmeSource *pSource, CDmeMakefile *pSourceMakefile )
+{
+ if ( !pSource )
+ return;
+
+ int nCount = m_Sources.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Sources[i] != pSource )
+ continue;
+
+ CDmeMakefile *pDependentMakeFile = m_Sources[i]->GetDependentMakefile();
+ if ( pSourceMakefile != pDependentMakeFile )
+ {
+ // FIXME: Should I recursively delete pDependentMakeFile ?
+ m_Sources[i]->SetDependentMakefile( pSourceMakefile );
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a dependent makefile
+//-----------------------------------------------------------------------------
+CDmeMakefile *CDmeMakefile::FindDependentMakefile( CDmeSource *pSource )
+{
+ if ( !pSource )
+ return NULL;
+
+ int nCount = m_Sources.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Sources[i] == pSource )
+ return m_Sources[i]->GetDependentMakefile();
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the associated source
+//-----------------------------------------------------------------------------
+CDmeSource *CDmeMakefile::FindAssociatedSource( CDmeMakefile *pChildMakefile )
+{
+ if ( !pChildMakefile )
+ return NULL;
+
+ int nCount = m_Sources.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_Sources[i]->GetDependentMakefile() == pChildMakefile )
+ return m_Sources[i];
+ }
+
+ return NULL;
+}
+
diff --git a/movieobjects/dmemakefileutils.cpp b/movieobjects/dmemakefileutils.cpp
new file mode 100644
index 0000000..5f20661
--- /dev/null
+++ b/movieobjects/dmemakefileutils.cpp
@@ -0,0 +1,613 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Interface for makefiles to build differently depending on where they are run from
+//
+//===========================================================================//
+
+#include "movieobjects/dmemakefileutils.h"
+#include "movieobjects/dmemdlmakefile.h"
+#include "movieobjects/dmedccmakefile.h"
+#include "tier2/fileutils.h"
+#include "filesystem.h"
+
+
+//-----------------------------------------------------------------------------
+// Statics
+//-----------------------------------------------------------------------------
+IMPLEMENT_DMEMAKEFILE_UTIL_CLASS( CDmeMakefileUtils );
+
+
+//-----------------------------------------------------------------------------
+// Default implementation
+//-----------------------------------------------------------------------------
+static CDmeMakefileUtils s_MakefileUtils;
+IDmeMakefileUtils *GetDefaultDmeMakefileUtils()
+{
+ return &s_MakefileUtils;
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CDmeMakefileUtils::CDmeMakefileUtils() : BaseClass( false )
+{
+ m_CompilationStep = NOT_COMPILING;
+ m_hCompileProcess = PROCESS_HANDLE_INVALID;
+ m_nCurrentCompileTask = -1;
+ m_nExitCode = 0;
+}
+
+CDmeMakefileUtils::~CDmeMakefileUtils()
+{
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Here's where systems can access other interfaces implemented by this object
+//-----------------------------------------------------------------------------
+void *CDmeMakefileUtils::QueryInterface( const char *pInterfaceName )
+{
+ if ( !V_strcmp( pInterfaceName, DMEMAKEFILE_UTILS_INTERFACE_VERSION ) )
+ return (IDmeMakefileUtils*)this;
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Initialization.. set up messagemaps
+//-----------------------------------------------------------------------------
+InitReturnVal_t CDmeMakefileUtils::Init()
+{
+ InitializeFuncMaps();
+ return INIT_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Looks for an appropriate method to compile this element with
+//-----------------------------------------------------------------------------
+CCompileFuncAdapterBase *CDmeMakefileUtils::DetermineCompileAdapter( CDmElement *pElement )
+{
+ int nBestInheritanceDepth = -1;
+ CCompileFuncAdapterBase *pBestAdapter = NULL;
+
+ CompileFuncTree_t *pTree = GetCompileTree();
+ while ( pTree )
+ {
+ CCompileFuncAdapterBase *pCurr = pTree->m_pFirstAdapter;
+ for ( ; pCurr; pCurr = pCurr->m_pNext )
+ {
+ // Choose this factory if it's more derived than the previous best
+ int nInheritanceDepth = pElement->GetInheritanceDepth( pCurr->m_ElementType );
+ if ( nInheritanceDepth < 0 )
+ continue;
+
+ if ( nInheritanceDepth == 0 )
+ {
+ // Found exact match.. do it!
+ return pCurr;
+ }
+
+ // Don't look for the best thingy if we're not the root
+ if ( nBestInheritanceDepth >= 0 && ( nInheritanceDepth >= nBestInheritanceDepth ) )
+ continue;
+
+ nBestInheritanceDepth = nInheritanceDepth;
+ pBestAdapter = pCurr;
+ }
+
+ pTree = pTree->m_pBaseAdapterTree;
+ }
+
+ // Return the closest match we could find
+ return pBestAdapter;
+}
+
+
+//-----------------------------------------------------------------------------
+// Looks for an appropriate method to open this element with
+//-----------------------------------------------------------------------------
+COpenEditorFuncAdapterBase *CDmeMakefileUtils::DetermineOpenEditorAdapter( CDmElement *pElement )
+{
+ int nBestInheritanceDepth = -1;
+ COpenEditorFuncAdapterBase *pBestAdapter = NULL;
+ OpenEditorFuncTree_t *pTree = GetOpenEditorTree();
+ while ( pTree )
+ {
+ COpenEditorFuncAdapterBase *pCurr = pTree->m_pFirstAdapter;
+ for ( ; pCurr; pCurr = pCurr->m_pNext )
+ {
+ // Choose this factory if it's more derived than the previous best
+ int nInheritanceDepth = pElement->GetInheritanceDepth( pCurr->m_ElementType );
+ if ( nInheritanceDepth < 0 )
+ continue;
+
+ // Found exact match.. do it!
+ if ( nInheritanceDepth == 0 )
+ return pCurr;
+
+ if ( nBestInheritanceDepth >= 0 && ( nInheritanceDepth >= nBestInheritanceDepth ) )
+ continue;
+
+ nBestInheritanceDepth = nInheritanceDepth;
+ pBestAdapter = pCurr;
+ }
+
+ pTree = pTree->m_pBaseAdapterTree;
+ }
+ return pBestAdapter;
+}
+
+
+//-----------------------------------------------------------------------------
+// Opens a element in an external editor
+//-----------------------------------------------------------------------------
+void CDmeMakefileUtils::PerformOpenEditor( CDmElement *pElement )
+{
+ COpenEditorFuncAdapterBase *pAdapter = DetermineOpenEditorAdapter( pElement );
+ if ( pAdapter )
+ {
+ pAdapter->OpenEditor( pElement );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Queues up a compilation task
+//-----------------------------------------------------------------------------
+void CDmeMakefileUtils::AddCompilationTask( CDmElement* pElement, CCompileFuncAdapterBase *pAdapter )
+{
+ Assert( m_CompilationStep == BUILDING_STANDARD_DEPENDENCIES || m_CompilationStep == BUILDING_ALL_DEPENDENCIES );
+
+ // Queue up the compilation task
+ int j = m_CompileTasks.AddToTail();
+ m_CompileTasks[j].m_hElement = pElement;
+ m_CompileTasks[j].m_pAdapter = pAdapter;
+}
+
+void CDmeMakefileUtils::AddCompilationTask( CDmElement* pElement )
+{
+ CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pElement );
+ if ( pAdapter )
+ {
+ // Queue up the compilation task
+ AddCompilationTask( pElement, pAdapter );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the compile process
+//-----------------------------------------------------------------------------
+void CDmeMakefileUtils::SetCompileProcess( ProcessHandle_t hProcess )
+{
+ Assert( m_CompilationStep == PERFORMING_COMPILATION );
+ m_hCompileProcess = hProcess;
+ if ( m_hCompileProcess == PROCESS_HANDLE_INVALID )
+ {
+ m_CompilationStep = AFTER_COMPILATION_FAILED;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Default implementatations for compile dependencies
+//-----------------------------------------------------------------------------
+bool CDmeMakefileUtils::AddCompileDependencies( CDmeMakefile *pMakefile, bool bBuildAllDependencies )
+{
+ if ( !pMakefile )
+ return true;
+
+ CUtlVector< CUtlString > outputs;
+ int nCount = pMakefile->GetSourceCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeSource *pSource = pMakefile->GetSource( i );
+ if ( !pSource )
+ continue;
+
+ CDmeMakefile *pDependentMakefile = pSource->GetDependentMakefile();
+ if ( !pDependentMakefile )
+ continue;
+
+ bool bShouldBuildFile = bBuildAllDependencies;
+
+ // Does the output files exist?
+ int j = 0;
+ if ( !bBuildAllDependencies )
+ {
+ pDependentMakefile->GetOutputs( outputs );
+ int nOutputCount = outputs.Count();
+ for ( j = 0; j < nOutputCount; ++j )
+ {
+ // If the file doesn't exist, we have to build it
+ if ( !g_pFullFileSystem->FileExists( outputs[j] ) )
+ break;
+
+ bShouldBuildFile = true;
+ break;
+ }
+ }
+
+ if ( !bShouldBuildFile )
+ continue;
+
+ CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pDependentMakefile );
+ if ( pAdapter )
+ {
+ // Add dependent makefiles first
+ if ( !pAdapter->PerformCompilationStep( pDependentMakefile, bBuildAllDependencies ? BUILDING_ALL_DEPENDENCIES : BUILDING_STANDARD_DEPENDENCIES ) )
+ return false;
+ }
+
+ // Queue up the compilation task
+ AddCompilationTask( pDependentMakefile, pAdapter );
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Default implementatations for precompilation step
+//-----------------------------------------------------------------------------
+bool CDmeMakefileUtils::PerformCompilationStep( CDmElement *pElement, CompilationStep_t step )
+{
+ // Do nothing
+ return true;
+}
+
+bool CDmeMakefileUtils::PerformCompilationStep( CDmeMakefile *pMakefile, CompilationStep_t step )
+{
+ switch( step )
+ {
+ case BUILDING_ALL_DEPENDENCIES:
+ return AddCompileDependencies( pMakefile, true );
+
+ case BUILDING_STANDARD_DEPENDENCIES:
+ return AddCompileDependencies( pMakefile, false );
+
+ case BEFORE_COMPILATION:
+ pMakefile->PreCompile();
+ break;
+
+ case AFTER_COMPILATION_SUCCEEDED:
+ pMakefile->PostCompile();
+ break;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Starts the next compile task
+//-----------------------------------------------------------------------------
+void CDmeMakefileUtils::StartNextCompileTask( )
+{
+ Assert( m_hCompileProcess == PROCESS_HANDLE_INVALID );
+ ++m_nCurrentCompileTask;
+ if ( m_nCurrentCompileTask == m_CompileTasks.Count() )
+ {
+ PerformCompilationStep( AFTER_COMPILATION_SUCCEEDED );
+ m_nCurrentCompileTask = -1;
+ m_CompileTasks.RemoveAll();
+ return;
+ }
+
+ m_hCompileProcess = PROCESS_HANDLE_INVALID;
+
+ // NOTE: PerformCompilationStep is expected to call SetCompileProcess to set m_hCompileProcess
+ CompileInfo_t &info = m_CompileTasks[m_nCurrentCompileTask];
+ bool bOk = info.m_pAdapter->PerformCompilationStep( info.m_hElement, PERFORMING_COMPILATION );
+
+ if ( !bOk || ( m_hCompileProcess == PROCESS_HANDLE_INVALID ) )
+ {
+ AbortCurrentCompilation();
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Performs the compilation step on all elements
+//-----------------------------------------------------------------------------
+bool CDmeMakefileUtils::PerformCompilationStep( CompilationStep_t step )
+{
+ // Iterate through all elements and run a compilation step
+ m_CompilationStep = step;
+ int nCount = m_CompileTasks.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CompileInfo_t &info = m_CompileTasks[i];
+ if ( info.m_hElement.Get() )
+ {
+ if ( !info.m_pAdapter->PerformCompilationStep( info.m_hElement, step ) )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for compilation
+//-----------------------------------------------------------------------------
+void CDmeMakefileUtils::PerformCompile( CDmElement *pElement, bool bBuildAllDependencies )
+{
+ if ( IsCurrentlyCompiling() )
+ {
+ AbortCurrentCompilation();
+ }
+
+ CCompileFuncAdapterBase *pAdapter = DetermineCompileAdapter( pElement );
+ if ( !pAdapter )
+ {
+ m_CompilationStep = AFTER_COMPILATION_FAILED;
+ return;
+ }
+
+ // Add dependent makefiles first
+ m_CompilationStep = bBuildAllDependencies ? BUILDING_ALL_DEPENDENCIES : BUILDING_STANDARD_DEPENDENCIES;
+ if ( !pAdapter->PerformCompilationStep( pElement, m_CompilationStep ) )
+ {
+ AbortCurrentCompilation();
+ return;
+ }
+
+ // Queue up the compilation task
+ AddCompilationTask( pElement, pAdapter );
+
+ // Iterate through all elements and run a precompilation step
+ // NOTE: This is where perforce integration should go
+ if ( !PerformCompilationStep( BEFORE_COMPILATION ) )
+ {
+ AbortCurrentCompilation();
+ return;
+ }
+
+ // Dequeue the first compile task and start it up
+ m_CompilationStep = PERFORMING_COMPILATION;
+ StartNextCompileTask();
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we in the middle of compiling this makefile?
+//-----------------------------------------------------------------------------
+bool CDmeMakefileUtils::IsCurrentlyCompiling()
+{
+ return ( m_CompilationStep != NOT_COMPILING );
+}
+
+
+//-----------------------------------------------------------------------------
+// Aborts any current compilation
+//-----------------------------------------------------------------------------
+void CDmeMakefileUtils::AbortCurrentCompilation()
+{
+ if ( m_hCompileProcess != PROCESS_HANDLE_INVALID )
+ {
+ g_pProcessUtils->AbortProcess( m_hCompileProcess );
+ m_hCompileProcess = PROCESS_HANDLE_INVALID;
+ }
+
+ if ( IsCurrentlyCompiling() )
+ {
+ PerformCompilationStep( AFTER_COMPILATION_FAILED );
+ m_nCurrentCompileTask = -1;
+ m_CompileTasks.RemoveAll();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the exit code of the failed compilation (if COMPILATION_FAILED occurred)
+//-----------------------------------------------------------------------------
+int CDmeMakefileUtils::GetExitCode()
+{
+ return m_nExitCode;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns output from the compilation
+//-----------------------------------------------------------------------------
+int CDmeMakefileUtils::GetCompileOutputSize()
+{
+ if ( m_hCompileProcess == PROCESS_HANDLE_INVALID )
+ return 0;
+ return g_pProcessUtils->GetProcessOutputSize( m_hCompileProcess );
+}
+
+CompilationState_t CDmeMakefileUtils::UpdateCompilation( char *pOutputBuf, int nBufLen )
+{
+ switch( m_CompilationStep )
+ {
+ case BUILDING_STANDARD_DEPENDENCIES:
+ case BUILDING_ALL_DEPENDENCIES:
+ case BEFORE_COMPILATION:
+ return COMPILATION_NOT_COMPLETE;
+
+ case AFTER_COMPILATION_FAILED:
+ m_CompilationStep = NOT_COMPILING;
+ return COMPILATION_FAILED;
+
+ case AFTER_COMPILATION_SUCCEEDED:
+ m_CompilationStep = NOT_COMPILING;
+ return COMPILATION_SUCCESSFUL;
+ }
+
+ // This is the PERFORMING_COMPILATION case:
+
+ // FIXME: Check return codes from compile process..
+ // fail if compilation process had a problem
+ if ( m_hCompileProcess == PROCESS_HANDLE_INVALID )
+ {
+ if ( nBufLen > 0 )
+ {
+ pOutputBuf[0] = 0;
+ }
+ return COMPILATION_SUCCESSFUL;
+ }
+
+ if ( nBufLen > 0 )
+ {
+ g_pProcessUtils->GetProcessOutput( m_hCompileProcess, pOutputBuf, nBufLen );
+ }
+
+ if ( !g_pProcessUtils->IsProcessComplete( m_hCompileProcess ) )
+ return COMPILATION_NOT_COMPLETE;
+
+ m_nExitCode = g_pProcessUtils->GetProcessExitCode( m_hCompileProcess );
+ bool bCompileSucceeded = ( m_nExitCode == 0 );
+ g_pProcessUtils->CloseProcess( m_hCompileProcess );
+ m_hCompileProcess = PROCESS_HANDLE_INVALID;
+
+ if ( !bCompileSucceeded )
+ {
+ AbortCurrentCompilation();
+ return COMPILATION_NOT_COMPLETE;
+ }
+
+ StartNextCompileTask();
+ if ( m_CompilationStep == PERFORMING_COMPILATION )
+ return COMPILATION_NOT_COMPLETE;
+
+ CompilationState_t retVal = ( m_CompilationStep == AFTER_COMPILATION_SUCCEEDED ) ? COMPILATION_SUCCESSFUL : COMPILATION_FAILED;
+ m_CompilationStep = NOT_COMPILING;
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Type-specific compilation functions
+//-----------------------------------------------------------------------------
+bool CDmeMakefileUtils::PerformCompilationStep( CDmeMDLMakefile *pMakeFile, CompilationStep_t step )
+{
+ if ( step != PERFORMING_COMPILATION )
+ return PerformCompilationStep( static_cast<CDmeMakefile*>( pMakeFile ), step );
+
+ char pBinDirectory[MAX_PATH];
+ GetModSubdirectory( "..\\bin", pBinDirectory, sizeof(pBinDirectory) );
+ Q_RemoveDotSlashes( pBinDirectory );
+
+ char pStudioMDLCmd[MAX_PATH];
+#ifdef _DEBUG
+ Q_snprintf( pStudioMDLCmd, sizeof(pStudioMDLCmd), "%s\\studiomdl.exe -allowdebug %s", pBinDirectory, pMakeFile->GetFileName() );
+#else
+ Q_snprintf( pStudioMDLCmd, sizeof(pStudioMDLCmd), "%s\\studiomdl.exe %s", pBinDirectory, pMakeFile->GetFileName() );
+#endif
+
+ ProcessHandle_t hProcess = g_pProcessUtils->StartProcess( pStudioMDLCmd, true );
+ SetCompileProcess( hProcess );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Exports a Maya file to a DMX file
+//-----------------------------------------------------------------------------
+bool CDmeMakefileUtils::PerformCompilationStep( CDmeMayaMakefile *pMakeFile, CompilationStep_t step )
+{
+ if ( step != PERFORMING_COMPILATION )
+ return PerformCompilationStep( static_cast<CDmeMakefile*>( pMakeFile ), step );
+
+ // FIXME: Create batch export command here
+ CUtlString mayaCommand;
+ mayaCommand = "vsDmxIO -export";
+
+ CUtlVector< CDmeHandle< CDmeSourceMayaFile > > sources;
+ pMakeFile->GetSources( sources );
+
+ if ( !sources.Count() )
+ return false;
+
+ CDmeSourceMayaFile *pDmeSourceDCCFile( sources[ 0 ].Get() );
+
+ mayaCommand += " -selection";
+
+ char pObjectId[128];
+ UniqueIdToString( pMakeFile->GetId(), pObjectId, sizeof(pObjectId) );
+ mayaCommand += " -makefileObjectId \\\"";
+ mayaCommand += pObjectId;
+ mayaCommand += "\\\"";
+
+ mayaCommand += " -";
+ mayaCommand += pDmeSourceDCCFile->m_ExportType.GetAttribute()->GetName();
+
+ switch ( pDmeSourceDCCFile->m_ExportType.Get() )
+ {
+ case 1: // skeletal animation
+ mayaCommand += " skeletalAnimation";
+
+ mayaCommand += " -";
+ mayaCommand += pDmeSourceDCCFile->m_FrameStart.GetAttribute()->GetName();
+ mayaCommand += " ";
+ mayaCommand += pDmeSourceDCCFile->m_FrameStart.Get();
+
+ mayaCommand += " -";
+ mayaCommand += pDmeSourceDCCFile->m_FrameEnd.GetAttribute()->GetName();
+ mayaCommand += " ";
+ mayaCommand += pDmeSourceDCCFile->m_FrameEnd.Get();
+
+ mayaCommand += " -";
+ mayaCommand += pDmeSourceDCCFile->m_FrameIncrement.GetAttribute()->GetName();
+ mayaCommand += " ";
+ mayaCommand += pDmeSourceDCCFile->m_FrameIncrement.Get();
+ break;
+ default: // Model
+ mayaCommand += " model";
+ break;
+ }
+
+ char pFileName[MAX_PATH];
+ Q_strncpy( pFileName, pMakeFile->GetFileName(), sizeof( pFileName ) );
+ Q_FixSlashes( pFileName, '/' );
+ mayaCommand += " -filename \\\"";
+ mayaCommand += pFileName;
+ mayaCommand += "\\\"";
+
+ const int rootObjectCount( pDmeSourceDCCFile->m_RootDCCObjects.Count() );
+ for ( int rootObjectIndex( 0 ); rootObjectIndex < rootObjectCount; ++rootObjectIndex )
+ {
+ mayaCommand += " ";
+ mayaCommand += pDmeSourceDCCFile->m_RootDCCObjects[ rootObjectIndex ];
+ }
+
+ char pSourcePath[MAX_PATH];
+ pMakeFile->GetSourceFullPath( pDmeSourceDCCFile, pSourcePath, sizeof(pSourcePath) );
+
+ // Maya wants forward slashes
+ Q_FixSlashes( pSourcePath, '/' );
+
+ char pMayaCommand[1024];
+ Q_snprintf( pMayaCommand, sizeof(pMayaCommand), "mayabatch.exe -batch -file \"%s\" -command \"%s\"", pSourcePath, mayaCommand.Get() );
+ ProcessHandle_t hProcess = g_pProcessUtils->StartProcess( pMayaCommand, true );
+ SetCompileProcess( hProcess );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Opens Maya with a particular file
+//-----------------------------------------------------------------------------
+void CDmeMakefileUtils::OpenEditor( CDmeSourceMayaFile *pDmeSourceDCCFile )
+{
+ CDmeMayaMakefile *pMakefile = FindReferringElement< CDmeMayaMakefile >( pDmeSourceDCCFile, "sources" );
+ if ( !pMakefile )
+ return;
+
+ char pSourcePath[MAX_PATH];
+ pMakefile->GetSourceFullPath( pDmeSourceDCCFile, pSourcePath, sizeof(pSourcePath) );
+
+ // Maya wants forward slashes
+ Q_FixSlashes( pSourcePath, '/' );
+
+ char pMayaCommand[1024];
+ Q_snprintf( pMayaCommand, sizeof(pMayaCommand), "maya.exe -file \"%s\"", pSourcePath );
+ g_pProcessUtils->StartProcess( pMayaCommand, true );
+}
diff --git a/movieobjects/dmematerial.cpp b/movieobjects/dmematerial.cpp
new file mode 100644
index 0000000..3bc984a
--- /dev/null
+++ b/movieobjects/dmematerial.cpp
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmematerial.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects_interfaces.h"
+
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialsystem.h"
+#include "tier2/tier2.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMaterial, CDmeMaterial );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeMaterial::OnConstruction()
+{
+ m_pMTL = NULL;
+ m_mtlName.Init( this, "mtlName" );
+}
+
+void CDmeMaterial::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// resolve
+//-----------------------------------------------------------------------------
+void CDmeMaterial::Resolve()
+{
+ BaseClass::Resolve();
+ if ( m_mtlName.IsDirty() )
+ {
+ m_pMTL = NULL; // no cleanup necessary
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the material
+//-----------------------------------------------------------------------------
+void CDmeMaterial::SetMaterial( const char *pMaterialName )
+{
+ m_mtlName = pMaterialName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the material name
+//-----------------------------------------------------------------------------
+const char *CDmeMaterial::GetMaterialName() const
+{
+ return m_mtlName;
+}
+
+
+//-----------------------------------------------------------------------------
+// accessor for cached IMaterial
+//-----------------------------------------------------------------------------
+IMaterial *CDmeMaterial::GetCachedMTL()
+{
+ if ( m_pMTL == NULL )
+ {
+ const char *mtlName = m_mtlName.Get();
+ if ( mtlName == NULL )
+ return NULL;
+ m_pMTL = g_pMaterialSystem->FindMaterial( mtlName, NULL, false );
+ }
+ return m_pMTL;
+}
diff --git a/movieobjects/dmematerialoverlayfxclip.cpp b/movieobjects/dmematerialoverlayfxclip.cpp
new file mode 100644
index 0000000..3fb62b1
--- /dev/null
+++ b/movieobjects/dmematerialoverlayfxclip.cpp
@@ -0,0 +1,270 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmematerialoverlayfxclip.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/imaterial.h"
+#include "tier1/KeyValues.h"
+#include "tier1/convar.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// CDmeClip - common base class for filmclips, soundclips, and channelclips
+//-----------------------------------------------------------------------------
+IMPLEMENT_FX_CLIP_ELEMENT_FACTORY( DmeMaterialOverlayFXClip, CDmeMaterialOverlayFXClip, "Material Overlay Effect" );
+
+void CDmeMaterialOverlayFXClip::OnConstruction()
+{
+ m_Material.Init( this, "material" );
+ m_Color.Init( this, "overlaycolor" );
+ m_nLeft.Init( this, "left" );
+ m_nTop.Init( this, "top" );
+ m_nWidth.Init( this, "width" );
+ m_nHeight.Init( this, "height" );
+ m_bFullScreen.Init( this, "fullscreen" );
+ m_bUseSubRect.Init( this, "useSubRect" );
+ m_flMovementAngle.Init( this, "movementAngle" );
+ m_flMovementSpeed.Init( this, "movementSpeed" );
+ m_nSubRectLeft.Init( this, "subRectLeft" );
+ m_nSubRectTop.Init( this, "subRectTop" );
+ m_nSubRectWidth.Init( this, "subRectWidth" );
+ m_nSubRectHeight.Init( this, "subRectHeight" );
+
+ m_Color.SetColor( 255, 255, 255, 255 );
+ m_bFullScreen = true;
+ m_nLeft = m_nTop = 0;
+ m_nWidth = m_nHeight = 1;
+}
+
+void CDmeMaterialOverlayFXClip::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Resolve
+//-----------------------------------------------------------------------------
+void CDmeMaterialOverlayFXClip::Resolve()
+{
+ if ( m_Material.IsDirty() )
+ {
+ m_OverlayMaterial.Shutdown();
+ const char *pName = m_Material.Get();
+ if ( pName && pName[0] )
+ {
+ m_OverlayMaterial.Init( pName, NULL, false );
+ }
+ m_Material.GetAttribute()->RemoveFlag( FATTRIB_DIRTY );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Helper for overlays
+//-----------------------------------------------------------------------------
+void CDmeMaterialOverlayFXClip::SetOverlayEffect( const char *pMaterialName )
+{
+ m_Material = pMaterialName;
+}
+
+void CDmeMaterialOverlayFXClip::SetAlpha( float flAlpha )
+{
+ m_Color.SetAlpha( flAlpha * 255 );
+}
+
+float CDmeMaterialOverlayFXClip::GetAlpha( void )
+{
+ return ( (float)m_Color.a() ) / 255.0f;
+}
+
+bool CDmeMaterialOverlayFXClip::HasOpaqueOverlay( void )
+{
+ if ( m_OverlayMaterial )
+ return ( !m_OverlayMaterial->IsTranslucent() && ( m_Color.a() == 255 ) && m_bFullScreen );
+
+ // no material overlay set
+ return false;
+}
+
+IMaterial *CDmeMaterialOverlayFXClip::GetMaterial()
+{
+ return m_OverlayMaterial;
+}
+
+
+//-----------------------------------------------------------------------------
+// All effects must be able to apply their effect
+//-----------------------------------------------------------------------------
+void CDmeMaterialOverlayFXClip::ApplyEffect( DmeTime_t time, Rect_t &currentRect, Rect_t &totalRect, ITexture *pTextures[MAX_FX_INPUT_TEXTURES] )
+{
+ if ( !m_OverlayMaterial || m_Color.a() == 0 )
+ return;
+
+ time = ToChildMediaTime( time, false );
+
+ // Clip the overlay rectangle to the currently drawn one
+ int x, y, w, h;
+ int tx, ty, tw, th;
+ if ( m_bFullScreen )
+ {
+ x = currentRect.x;
+ y = currentRect.y;
+ w = currentRect.width;
+ h = currentRect.height;
+ tx = ty = 0;
+ tw = totalRect.width;
+ th = totalRect.height;
+ }
+ else
+ {
+ x = clamp( m_nLeft, currentRect.x, currentRect.x + currentRect.width );
+ y = clamp( m_nTop, currentRect.y, currentRect.y + currentRect.height );
+ int x1 = clamp( m_nLeft + m_nWidth, currentRect.x, currentRect.x + currentRect.width );
+ int y1 = clamp( m_nTop + m_nHeight, currentRect.y, currentRect.y + currentRect.height );
+ w = x1 - x;
+ h = y1 - y;
+
+ tx = m_nLeft;
+ ty = m_nTop;
+ tw = m_nWidth;
+ th = m_nHeight;
+
+ // Clipped...
+ if ( w <= 0 || h <= 0 )
+ return;
+ }
+
+ if ( tw == 0 || th == 0 )
+ return;
+
+ // Compute texture coordinate range of the entire texture
+ int mw = m_OverlayMaterial->GetMappingWidth();
+ int mh = m_OverlayMaterial->GetMappingHeight();
+
+ // Compute the texture coords in texels we want over the entire image
+ float uMin = 0;
+ float uMax = mw;
+ float vMin = 0;
+ float vMax = mh;
+
+ if ( m_bUseSubRect )
+ {
+ uMin = m_nSubRectLeft;
+ vMin = m_nSubRectTop;
+ uMax = uMin + m_nSubRectWidth;
+ vMax = vMin + m_nSubRectHeight;
+ }
+
+ if ( m_flMovementSpeed )
+ {
+ float flRadians = M_PI * m_flMovementAngle / 180.0f;
+ float dUdT = -m_flMovementSpeed * cos( flRadians );
+ float dVdT = m_flMovementSpeed * sin( flRadians );
+ float dU = time.GetSeconds() * dUdT;
+ float dV = time.GetSeconds() * dVdT;
+ uMin += dU; uMax += dU;
+ vMin += dV; vMax += dV;
+ }
+
+ // This is the range of normalizes (u,v) coordinates over the *total* image
+ uMin = ( uMin + 0.5f ) / mw;
+ vMin = ( vMin + 0.5f ) / mh;
+ uMax = ( uMax - 0.5f ) / mw;
+ vMax = ( vMax - 0.5f ) / mh;
+
+ // Now determine the subrange we should use given we're rendering a portion of the image
+ float u0, v0, u1, v1, f;
+
+ f = ( x - tx ) / tw;
+ u0 = Lerp( f, uMin, uMax );
+
+ f = ( x + w - tx ) / tw;
+ u1 = Lerp( f, uMin, uMax );
+
+ f = ( y - ty ) / th;
+ v0 = Lerp( f, vMin, vMax );
+
+ f = ( y + h - ty ) / th;
+ v1 = Lerp( f, vMin, vMax );
+
+ x -= currentRect.x;
+ y -= currentRect.y;
+
+ if ( m_OverlayMaterial->NeedsPowerOfTwoFrameBufferTexture() )
+ {
+ CMatRenderContextPtr pRenderContext( materials );
+ ITexture *pTexture = materials->FindTexture( "_rt_PowerOfTwoFB", TEXTURE_GROUP_RENDER_TARGET );
+
+ // forced or only once per frame
+ Rect_t rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.width = currentRect.width;
+ rect.height = currentRect.height;
+ pRenderContext->CopyRenderTargetToTextureEx( pTexture, 0, &rect, NULL );
+ pRenderContext->SetFrameBufferCopyTexture( pTexture );
+ }
+
+ float r, g, b, a;
+ m_OverlayMaterial->GetColorModulation( &r, &g, &b );
+ a = m_OverlayMaterial->GetAlphaModulation();
+
+ m_OverlayMaterial->ColorModulate( m_Color.r() / 255.0f, m_Color.g() / 255.0f, m_Color.b() / 255.0f );
+ m_OverlayMaterial->AlphaModulate( m_Color.a() / 255.0f );
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( m_OverlayMaterial );
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ meshBuilder.Position3f( x, y, 0.0f );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.TexCoord2f( 0, u0, v0 );
+ meshBuilder.TexCoord2f( 1, 0.0f, 0.0f );
+ meshBuilder.TexCoord2f( 2, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x, y+h, 0.0f );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.TexCoord2f( 0, u0, v1 );
+ meshBuilder.TexCoord2f( 1, 0.0f, 1.0f );
+ meshBuilder.TexCoord2f( 2, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x+w, y, 0.0f );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.TexCoord2f( 0, u1, v0 );
+ meshBuilder.TexCoord2f( 1, 1.0f, 0.0f );
+ meshBuilder.TexCoord2f( 2, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( x+w, y+h, 0.0f );
+ meshBuilder.BoneWeight( 0, 1.0f );
+ meshBuilder.BoneMatrix( 0, 0 );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.TexCoord2f( 0, u1, v1 );
+ meshBuilder.TexCoord2f( 1, 1.0f, 1.0f );
+ meshBuilder.TexCoord2f( 2, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ m_OverlayMaterial->ColorModulate( r, g, b );
+ m_OverlayMaterial->AlphaModulate( a );
+}
+
diff --git a/movieobjects/dmemdl.cpp b/movieobjects/dmemdl.cpp
new file mode 100644
index 0000000..3b720f0
--- /dev/null
+++ b/movieobjects/dmemdl.cpp
@@ -0,0 +1,207 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmemdl.h"
+#include "movieobjects/dmetransform.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datacache/imdlcache.h"
+#include "istudiorender.h"
+#include "bone_setup.h"
+#include "tier3/tier3.h"
+#include "tier3/mdlutils.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMDL, CDmeMDL );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMDL::OnConstruction()
+{
+ m_bDrawInEngine = false;
+// SetAttributeValueElement( "transform", CreateElement< CDmeTransform >() );
+// SetAttributeValue( "mdlfilename", "models/alyx.mdl" );
+ m_Color.InitAndSet( this, "color", Color( 255, 255, 255, 255 ) );
+ m_nSkin.InitAndSet( this, "skin", 0 );
+ m_nBody.InitAndSet( this, "body", 0 );
+ m_nSequence.InitAndSet( this, "sequence", 0 );
+ m_nLOD.InitAndSet( this, "lod", 0 );
+ m_flPlaybackRate.InitAndSet( this, "playbackrate", 30.0f );
+ m_flTime.InitAndSet( this, "time", 0.0f );
+ m_vecViewTarget.Init( this, "viewTarget" );
+ m_bWorldSpaceViewTarget.Init( this, "worldSpaceViewTarget" );
+}
+
+void CDmeMDL::OnDestruction()
+{
+ m_MDL.SetMDL( MDLHANDLE_INVALID );
+}
+
+
+void CDmeMDL::SetMDL( MDLHandle_t handle )
+{
+ m_MDL.SetMDL( handle );
+
+ Vector vecMins, vecMaxs;
+ GetMDLBoundingBox( &vecMins, &vecMaxs, m_MDL.GetMDL(), m_nSequence );
+ Vector vecLookAt( 100.0f, 0.0f, vecMaxs.z );
+
+ m_vecViewTarget.Set( vecLookAt );
+ m_bWorldSpaceViewTarget = false;
+}
+
+MDLHandle_t CDmeMDL::GetMDL( ) const
+{
+ return m_MDL.GetMDL();
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads the model matrix based on the transform
+//-----------------------------------------------------------------------------
+void CDmeMDL::DrawInEngine( bool bDrawInEngine )
+{
+ m_bDrawInEngine = bDrawInEngine;
+}
+
+bool CDmeMDL::IsDrawingInEngine() const
+{
+ return m_bDrawInEngine;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the bounding box for the model
+//-----------------------------------------------------------------------------
+void CDmeMDL::GetBoundingBox( Vector *pMins, Vector *pMaxs ) const
+{
+ GetMDLBoundingBox( pMins, pMaxs, m_MDL.GetMDL(), m_nSequence );
+
+ // Rotate the root transform to make it align with DMEs
+ // DMEs up vector is the y axis
+ if ( !m_bDrawInEngine )
+ {
+ Vector vecMins, vecMaxs;
+ matrix3x4_t engineToDme;
+ CDmeDag::EngineToDmeMatrix( engineToDme );
+ TransformAABB( engineToDme, *pMins, *pMaxs, vecMins, vecMaxs );
+ *pMins = vecMins;
+ *pMaxs = vecMaxs;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the radius of the model as measured from the origin
+//-----------------------------------------------------------------------------
+float CDmeMDL::GetRadius() const
+{
+ return GetMDLRadius( m_MDL.GetMDL(), m_nSequence );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a more accurate bounding sphere
+//-----------------------------------------------------------------------------
+void CDmeMDL::GetBoundingSphere( Vector &vecCenter, float &flRadius )
+{
+ Vector vecEngineCenter;
+ GetMDLBoundingSphere( &vecEngineCenter, &flRadius, m_MDL.GetMDL(), m_nSequence );
+
+ // Rotate the root transform to make it align with DMEs
+ // DMEs up vector is the y axis
+ if ( !m_bDrawInEngine )
+ {
+ matrix3x4_t engineToDme;
+ CDmeDag::EngineToDmeMatrix( engineToDme );
+ VectorTransform( vecEngineCenter, engineToDme, vecCenter );
+ }
+ else
+ {
+ vecCenter = vecEngineCenter;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the MDL rendering helper
+//-----------------------------------------------------------------------------
+void CDmeMDL::UpdateMDL()
+{
+ m_MDL.m_Color = m_Color;
+ m_MDL.m_nSkin = m_nSkin;
+ m_MDL.m_nBody = m_nBody;
+ m_MDL.m_nSequence = m_nSequence;
+ m_MDL.m_nLOD = m_nLOD;
+ m_MDL.m_flPlaybackRate = m_flPlaybackRate;
+ m_MDL.m_flTime = m_flTime;
+ m_MDL.m_vecViewTarget = m_vecViewTarget;
+ m_MDL.m_Color = m_Color;
+ m_MDL.m_bWorldSpaceViewTarget = m_bWorldSpaceViewTarget;
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh
+//-----------------------------------------------------------------------------
+void CDmeMDL::Draw( const matrix3x4_t &shapeToWorld, CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ UpdateMDL();
+ studiohdr_t *pStudioHdr = m_MDL.GetStudioHdr();
+ if ( !pStudioHdr )
+ return;
+
+ // FIXME: Why is this necessary!?!?!?
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ if ( !m_bDrawInEngine )
+ {
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
+ }
+
+ matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( pStudioHdr->numbones );
+ SetUpBones( shapeToWorld, pStudioHdr->numbones, pBoneToWorld );
+ g_pStudioRender->UnlockBoneMatrices();
+
+ m_MDL.Draw( shapeToWorld, pBoneToWorld );
+
+ // FIXME: Why is this necessary!?!?!?
+ if ( !m_bDrawInEngine )
+ {
+ pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
+ }
+}
+
+
+void CDmeMDL::SetUpBones( const matrix3x4_t& shapeToWorld, int nMaxBoneCount, matrix3x4_t *pOutputMatrices )
+{
+ UpdateMDL();
+
+ // Root transform
+ matrix3x4_t rootToWorld;
+
+ // Rotate the root transform to make it align with DMEs
+ // DMEs up vector is the y axis
+ if ( !m_bDrawInEngine )
+ {
+ matrix3x4_t engineToDme;
+ CDmeDag::EngineToDmeMatrix( engineToDme );
+ ConcatTransforms( engineToDme, shapeToWorld, rootToWorld );
+ }
+ else
+ {
+ MatrixCopy( shapeToWorld, rootToWorld );
+ }
+
+ m_MDL.SetUpBones( rootToWorld, nMaxBoneCount, pOutputMatrices );
+}
diff --git a/movieobjects/dmemdlmakefile.cpp b/movieobjects/dmemdlmakefile.cpp
new file mode 100644
index 0000000..f79e2fb
--- /dev/null
+++ b/movieobjects/dmemdlmakefile.cpp
@@ -0,0 +1,287 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Describes an asset: something that is compiled from sources,
+// in potentially multiple steps, to a compiled resource
+//
+//=============================================================================
+
+
+#include "movieobjects/dmemdlmakefile.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmemdl.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datacache/imdlcache.h"
+#include "filesystem.h"
+#include "tier3/tier3.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSourceSkin, CDmeSourceSkin );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeSourceSkin::OnConstruction()
+{
+ m_SkinName.Init( this, "skinName" );
+ m_bFlipTriangles.Init( this, "flipTriangles" );
+ m_flScale.InitAndSet( this, "scale", 1.0f );
+}
+
+void CDmeSourceSkin::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// These can be built from DCC makefiles
+//-----------------------------------------------------------------------------
+static const char *s_pSkinMakeFiles[] =
+{
+ "DmeMayaModelMakefile",
+ "DmeXSIModelMakefile",
+ NULL
+};
+
+const char **CDmeSourceSkin::GetSourceMakefileTypes()
+{
+ return s_pSkinMakeFiles;
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSourceCollisionModel, CDmeSourceCollisionModel );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeSourceCollisionModel::OnConstruction()
+{
+}
+
+void CDmeSourceCollisionModel::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// These can be built from DCC makefiles
+//-----------------------------------------------------------------------------
+const char **CDmeSourceCollisionModel::GetSourceMakefileTypes()
+{
+ return s_pSkinMakeFiles;
+}
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSourceAnimation, CDmeSourceAnimation );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeSourceAnimation::OnConstruction()
+{
+ m_AnimationName.Init( this, "animationName" );
+ m_SourceAnimationName.Init( this, "sourceAnimationName" );
+}
+
+void CDmeSourceAnimation::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// These can be built from DCC makefiles
+//-----------------------------------------------------------------------------
+static const char *s_pAnimationMakeFiles[] =
+{
+ "DmeMayaAnimationMakefile",
+ "DmeXSIAnimationMakefile",
+ NULL
+};
+
+const char **CDmeSourceAnimation::GetSourceMakefileTypes()
+{
+ return s_pAnimationMakeFiles;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Hook into datamodel
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMDLMakefile, CDmeMDLMakefile );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMDLMakefile::OnConstruction()
+{
+ m_hMDL = CreateElement< CDmeMDL >( "MDLMakefile Preview" );
+ m_bFlushMDL = false;
+}
+
+void CDmeMDLMakefile::OnDestruction()
+{
+ DestroyElement( m_hMDL.Get() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns source types
+//-----------------------------------------------------------------------------
+static DmeMakefileType_t s_pSourceTypes[] =
+{
+ { "DmeSourceSkin", "Skin", true, "makefiledir:models\\dmx", "*.dmx", "Valve DMX File (*.dmx)" },
+ { "DmeSourceAnimation", "Animation", false, "makefiledir:animations\\dmx", "*.dmx", "Valve DMX File (*.dmx)" },
+ { "DmeSourceCollisionModel", "Collision Model", true, "makefiledir:models\\dmx", "*.dmx", "Valve DMX File (*.dmx)" },
+ { NULL, NULL, false, NULL, NULL, NULL },
+};
+
+DmeMakefileType_t* CDmeMDLMakefile::GetSourceTypes()
+{
+ return s_pSourceTypes;
+}
+
+
+//-----------------------------------------------------------------------------
+// Makefile type
+//-----------------------------------------------------------------------------
+static DmeMakefileType_t s_MakefileType =
+{
+ "DmeMDLMakefile", "Model", true, "contentdir:models", "*.dmx", "Valve Model MakeFile (*.dmx)"
+};
+
+
+DmeMakefileType_t *CDmeMDLMakefile::GetMakefileType()
+{
+ return &s_MakefileType;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add, remove sources
+//-----------------------------------------------------------------------------
+void CDmeMDLMakefile::SetSkin( const char *pFullPath )
+{
+ RemoveAllSources( "DmeSourceSkin" );
+ AddSource( "DmeSourceSkin", pFullPath );
+}
+
+void CDmeMDLMakefile::AddAnimation( const char *pFullPath )
+{
+ AddSource( "animation", pFullPath );
+}
+
+void CDmeMDLMakefile::RemoveAnimation( const char *pFullPath )
+{
+ RemoveSource( "animation", pFullPath );
+}
+
+void CDmeMDLMakefile::RemoveAllAnimations( )
+{
+ RemoveAllSources( "animation" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Inherited classes should re-implement these methods
+//-----------------------------------------------------------------------------
+CDmElement *CDmeMDLMakefile::CreateOutputElement( )
+{
+ if ( m_bFlushMDL )
+ {
+ // Flush the model out of the cache; detach it from the MDL
+ MDLHandle_t h = m_hMDL->GetMDL();
+ if ( h != MDLHANDLE_INVALID )
+ {
+ g_pMDLCache->Flush( h );
+ }
+ m_bFlushMDL = false;
+ }
+ m_hMDL->SetMDL( MDLHANDLE_INVALID );
+
+ // FIXME: Should we ask the tool (studiomdl) for this?
+ // Should we have output type names? Not sure yet..
+ // Doing the simplest thing first.
+ char pOutputName[MAX_PATH];
+ Q_FileBase( GetFileName(), pOutputName, sizeof(pOutputName) );
+ if ( !pOutputName[0] )
+ return m_hMDL.Get();
+
+ char pOutputDir[MAX_PATH];
+ GetOutputDirectory( pOutputDir, sizeof(pOutputDir) );
+ if ( !pOutputDir[0] )
+ return m_hMDL.Get();
+
+ Q_StripTrailingSlash( pOutputDir );
+ char pFullPath[MAX_PATH];
+ Q_snprintf( pFullPath, sizeof(pFullPath), "%s\\%s.mdl", pOutputDir, pOutputName );
+
+ char pRelativePath[MAX_PATH];
+ g_pFullFileSystem->FullPathToRelativePathEx( pFullPath, "GAME", pRelativePath, sizeof( pRelativePath ) );
+
+ MDLHandle_t h = g_pMDLCache->FindMDL( pRelativePath );
+ m_hMDL->SetMDL( h );
+ return m_hMDL.Get();
+}
+
+void CDmeMDLMakefile::DestroyOutputElement( CDmElement *pOutput )
+{
+ m_bFlushMDL = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compile assets
+//-----------------------------------------------------------------------------
+static const char *s_pOutputExtensions[] =
+{
+ "dx80.vtx",
+ "dx90.vtx",
+ "sw.vtx",
+ "mdl",
+ "vvd",
+ "phy",
+ NULL
+};
+
+void CDmeMDLMakefile::GetOutputs( CUtlVector<CUtlString> &fullPaths )
+{
+ fullPaths.RemoveAll();
+
+ // FIXME: Should we ask the tool (studiomdl) for this?
+ // Should we have output type names? Not sure yet..
+ // Doing the simplest thing first.
+ char pOutputName[MAX_PATH];
+ Q_FileBase( GetFileName(), pOutputName, sizeof(pOutputName) );
+ if ( !pOutputName[0] )
+ return;
+
+ char pOutputDir[MAX_PATH];
+ GetOutputDirectory( pOutputDir, sizeof(pOutputDir) );
+ if ( !pOutputDir[0] )
+ return;
+
+ Q_StripTrailingSlash( pOutputDir );
+ char pFullPath[MAX_PATH];
+ for ( int i = 0; s_pOutputExtensions[i]; ++i )
+ {
+ Q_snprintf( pFullPath, sizeof(pFullPath), "%s\\%s.%s", pOutputDir, pOutputName, s_pOutputExtensions[i] );
+ fullPaths.AddToTail( pFullPath );
+ }
+}
+
diff --git a/movieobjects/dmemesh.cpp b/movieobjects/dmemesh.cpp
new file mode 100644
index 0000000..aac63a9
--- /dev/null
+++ b/movieobjects/dmemesh.cpp
@@ -0,0 +1,5017 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+// Standard includes
+#include <limits.h>
+
+// Valve includes
+#include "movieobjects/dmemesh.h"
+#include "movieobjects/dmevertexdata.h"
+#include "movieobjects/dmefaceset.h"
+#include "movieobjects/dmematerial.h"
+#include "movieobjects/dmetransform.h"
+#include "movieobjects/dmemodel.h"
+#include "movieobjects_interfaces.h"
+#include "movieobjects/dmecombinationoperator.h"
+#include "movieobjects/dmeselection.h"
+#include "movieobjects/dmedrawsettings.h"
+#include "movieobjects/dmmeshcomp.h"
+#include "tier3/tier3.h"
+#include "tier1/KeyValues.h"
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imorph.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/imaterialvar.h"
+#include "istudiorender.h"
+#include "studio.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Normal rendering materials
+//-----------------------------------------------------------------------------
+bool CDmeMesh::s_bNormalMaterialInitialized;
+CMaterialReference CDmeMesh::s_NormalMaterial;
+CMaterialReference CDmeMesh::s_NormalErrorMaterial;
+
+
+
+//-----------------------------------------------------------------------------
+// Computes a skin matrix
+//-----------------------------------------------------------------------------
+static const matrix3x4_t *ComputeSkinMatrix( int nBoneCount, const float *pJointWeight, const int *pJointIndices, const matrix3x4_t *pPoseToWorld, matrix3x4_t &result )
+{
+ float flWeight0, flWeight1, flWeight2, flWeight3;
+
+ switch( nBoneCount )
+ {
+ default:
+ case 1:
+ return &pPoseToWorld[pJointIndices[0]];
+
+ case 2:
+ {
+ const matrix3x4_t &boneMat0 = pPoseToWorld[pJointIndices[0]];
+ const matrix3x4_t &boneMat1 = pPoseToWorld[pJointIndices[1]];
+ flWeight0 = pJointWeight[0];
+ flWeight1 = pJointWeight[1];
+
+ // NOTE: Inlining here seems to make a fair amount of difference
+ result[0][0] = boneMat0[0][0] * flWeight0 + boneMat1[0][0] * flWeight1;
+ result[0][1] = boneMat0[0][1] * flWeight0 + boneMat1[0][1] * flWeight1;
+ result[0][2] = boneMat0[0][2] * flWeight0 + boneMat1[0][2] * flWeight1;
+ result[0][3] = boneMat0[0][3] * flWeight0 + boneMat1[0][3] * flWeight1;
+ result[1][0] = boneMat0[1][0] * flWeight0 + boneMat1[1][0] * flWeight1;
+ result[1][1] = boneMat0[1][1] * flWeight0 + boneMat1[1][1] * flWeight1;
+ result[1][2] = boneMat0[1][2] * flWeight0 + boneMat1[1][2] * flWeight1;
+ result[1][3] = boneMat0[1][3] * flWeight0 + boneMat1[1][3] * flWeight1;
+ result[2][0] = boneMat0[2][0] * flWeight0 + boneMat1[2][0] * flWeight1;
+ result[2][1] = boneMat0[2][1] * flWeight0 + boneMat1[2][1] * flWeight1;
+ result[2][2] = boneMat0[2][2] * flWeight0 + boneMat1[2][2] * flWeight1;
+ result[2][3] = boneMat0[2][3] * flWeight0 + boneMat1[2][3] * flWeight1;
+ }
+ return &result;
+
+ case 3:
+ {
+ const matrix3x4_t &boneMat0 = pPoseToWorld[pJointIndices[0]];
+ const matrix3x4_t &boneMat1 = pPoseToWorld[pJointIndices[1]];
+ const matrix3x4_t &boneMat2 = pPoseToWorld[pJointIndices[2]];
+ flWeight0 = pJointWeight[0];
+ flWeight1 = pJointWeight[1];
+ flWeight2 = pJointWeight[2];
+
+ result[0][0] = boneMat0[0][0] * flWeight0 + boneMat1[0][0] * flWeight1 + boneMat2[0][0] * flWeight2;
+ result[0][1] = boneMat0[0][1] * flWeight0 + boneMat1[0][1] * flWeight1 + boneMat2[0][1] * flWeight2;
+ result[0][2] = boneMat0[0][2] * flWeight0 + boneMat1[0][2] * flWeight1 + boneMat2[0][2] * flWeight2;
+ result[0][3] = boneMat0[0][3] * flWeight0 + boneMat1[0][3] * flWeight1 + boneMat2[0][3] * flWeight2;
+ result[1][0] = boneMat0[1][0] * flWeight0 + boneMat1[1][0] * flWeight1 + boneMat2[1][0] * flWeight2;
+ result[1][1] = boneMat0[1][1] * flWeight0 + boneMat1[1][1] * flWeight1 + boneMat2[1][1] * flWeight2;
+ result[1][2] = boneMat0[1][2] * flWeight0 + boneMat1[1][2] * flWeight1 + boneMat2[1][2] * flWeight2;
+ result[1][3] = boneMat0[1][3] * flWeight0 + boneMat1[1][3] * flWeight1 + boneMat2[1][3] * flWeight2;
+ result[2][0] = boneMat0[2][0] * flWeight0 + boneMat1[2][0] * flWeight1 + boneMat2[2][0] * flWeight2;
+ result[2][1] = boneMat0[2][1] * flWeight0 + boneMat1[2][1] * flWeight1 + boneMat2[2][1] * flWeight2;
+ result[2][2] = boneMat0[2][2] * flWeight0 + boneMat1[2][2] * flWeight1 + boneMat2[2][2] * flWeight2;
+ result[2][3] = boneMat0[2][3] * flWeight0 + boneMat1[2][3] * flWeight1 + boneMat2[2][3] * flWeight2;
+ }
+ return &result;
+
+ case 4:
+ {
+ const matrix3x4_t &boneMat0 = pPoseToWorld[pJointIndices[0]];
+ const matrix3x4_t &boneMat1 = pPoseToWorld[pJointIndices[1]];
+ const matrix3x4_t &boneMat2 = pPoseToWorld[pJointIndices[2]];
+ const matrix3x4_t &boneMat3 = pPoseToWorld[pJointIndices[3]];
+ flWeight0 = pJointWeight[0];
+ flWeight1 = pJointWeight[1];
+ flWeight2 = pJointWeight[2];
+ flWeight3 = pJointWeight[3];
+
+ result[0][0] = boneMat0[0][0] * flWeight0 + boneMat1[0][0] * flWeight1 + boneMat2[0][0] * flWeight2 + boneMat3[0][0] * flWeight3;
+ result[0][1] = boneMat0[0][1] * flWeight0 + boneMat1[0][1] * flWeight1 + boneMat2[0][1] * flWeight2 + boneMat3[0][1] * flWeight3;
+ result[0][2] = boneMat0[0][2] * flWeight0 + boneMat1[0][2] * flWeight1 + boneMat2[0][2] * flWeight2 + boneMat3[0][2] * flWeight3;
+ result[0][3] = boneMat0[0][3] * flWeight0 + boneMat1[0][3] * flWeight1 + boneMat2[0][3] * flWeight2 + boneMat3[0][3] * flWeight3;
+ result[1][0] = boneMat0[1][0] * flWeight0 + boneMat1[1][0] * flWeight1 + boneMat2[1][0] * flWeight2 + boneMat3[1][0] * flWeight3;
+ result[1][1] = boneMat0[1][1] * flWeight0 + boneMat1[1][1] * flWeight1 + boneMat2[1][1] * flWeight2 + boneMat3[1][1] * flWeight3;
+ result[1][2] = boneMat0[1][2] * flWeight0 + boneMat1[1][2] * flWeight1 + boneMat2[1][2] * flWeight2 + boneMat3[1][2] * flWeight3;
+ result[1][3] = boneMat0[1][3] * flWeight0 + boneMat1[1][3] * flWeight1 + boneMat2[1][3] * flWeight2 + boneMat3[1][3] * flWeight3;
+ result[2][0] = boneMat0[2][0] * flWeight0 + boneMat1[2][0] * flWeight1 + boneMat2[2][0] * flWeight2 + boneMat3[2][0] * flWeight3;
+ result[2][1] = boneMat0[2][1] * flWeight0 + boneMat1[2][1] * flWeight1 + boneMat2[2][1] * flWeight2 + boneMat3[2][1] * flWeight3;
+ result[2][2] = boneMat0[2][2] * flWeight0 + boneMat1[2][2] * flWeight1 + boneMat2[2][2] * flWeight2 + boneMat3[2][2] * flWeight3;
+ result[2][3] = boneMat0[2][3] * flWeight0 + boneMat1[2][3] * flWeight1 + boneMat2[2][3] * flWeight2 + boneMat3[2][3] * flWeight3;
+ }
+ return &result;
+ }
+
+ Assert(0);
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Helper class to deal with software skinning
+//-----------------------------------------------------------------------------
+class CRenderInfo
+{
+public:
+ CRenderInfo( const CDmeVertexData *pBaseState );
+
+ void ComputeVertex( int vi, const matrix3x4_t *pPoseToWorld, CDmeMesh::RenderVertexDelta_t *pDelta, Vector *pPosition, Vector *pNormal, Vector4D *pTangent );
+ void ComputeVertex( int vi, const matrix3x4_t *pPoseToWorld, Vector *pDeltaPosition, int nStride, Vector *pPosition );
+ void ComputePosition( int posIndex, const matrix3x4_t *pPoseToWorld, Vector *pDeltaPosition, Vector *pPosition );
+
+ inline bool HasPositionData() const { return m_bHasPositionData; }
+ inline bool HasNormalData() const { return m_bHasNormalData; }
+ inline bool HasTangentData() const { return m_bHasTangentData; }
+private:
+ const CUtlVector<int>& m_PositionIndices;
+ const CUtlVector<Vector>& m_PositionData;
+ const CUtlVector<int>& m_NormalIndices;
+ const CUtlVector<Vector>& m_NormalData;
+ const CUtlVector<int>& m_TangentIndices;
+ const CUtlVector<Vector4D>& m_TangentData;
+ const CDmeVertexData *m_pBaseState;
+ int m_nJointCount;
+ bool m_bHasPositionData;
+ bool m_bHasNormalData;
+ bool m_bHasTangentData;
+ bool m_bHasSkinningData;
+};
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CRenderInfo::CRenderInfo( const CDmeVertexData *pBaseState ) :
+ m_PositionIndices( pBaseState->GetVertexIndexData( CDmeVertexData::FIELD_POSITION ) ),
+ m_PositionData( pBaseState->GetPositionData() ),
+ m_NormalIndices( pBaseState->GetVertexIndexData( CDmeVertexData::FIELD_NORMAL ) ),
+ m_NormalData( pBaseState->GetNormalData() ),
+ m_TangentIndices( pBaseState->GetVertexIndexData( CDmeVertexData::FIELD_TANGENT ) ),
+ m_TangentData( pBaseState->GetTangentData() )
+{
+ m_pBaseState = pBaseState;
+ m_bHasPositionData = m_PositionIndices.Count() > 0;
+ m_bHasNormalData = m_NormalIndices.Count() > 0;
+ m_bHasTangentData = m_TangentIndices.Count() > 0;
+ m_nJointCount = pBaseState->JointCount();
+ m_bHasSkinningData = pBaseState->HasSkinningData() && m_nJointCount > 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes where a vertex is
+//-----------------------------------------------------------------------------
+void CRenderInfo::ComputeVertex( int vi, const matrix3x4_t *pPoseToWorld, Vector *pDeltaPosition, int nDeltaStride, Vector *pPosition )
+{
+ matrix3x4_t result;
+ Vector vecMorphPosition, vecMorphNormal;
+ const matrix3x4_t *pSkinMatrix = pPoseToWorld;
+ if ( m_bHasSkinningData )
+ {
+ const float *pJointWeight = m_pBaseState->GetJointWeights( vi );
+ const int *pJointIndices = m_pBaseState->GetJointIndices( vi );
+ pSkinMatrix = ComputeSkinMatrix( m_nJointCount, pJointWeight, pJointIndices, pPoseToWorld, result );
+ }
+
+ int pi = m_PositionIndices[ vi ];
+ const Vector *pPositionData = &m_PositionData[ pi ];
+ if ( pDeltaPosition )
+ {
+ Vector *pDelta = (Vector*)( (unsigned char *)pDeltaPosition + nDeltaStride * pi );
+ VectorAdd( *pPositionData, *pDelta, vecMorphPosition );
+ pPositionData = &vecMorphPosition;
+ }
+ VectorTransform( *pPositionData, *pSkinMatrix, *pPosition );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes where a vertex is
+//-----------------------------------------------------------------------------
+void CRenderInfo::ComputePosition( int posIndex, const matrix3x4_t *pPoseToWorld, Vector *pDeltaPosition, Vector *pPosition )
+{
+ matrix3x4_t result;
+ Vector vecMorphPosition;
+ const matrix3x4_t *pSkinMatrix = pPoseToWorld;
+
+ if ( m_bHasSkinningData )
+ {
+ const float *pJointWeight = m_pBaseState->GetJointPositionWeights( posIndex );
+ const int *pJointIndices = m_pBaseState->GetJointPositionIndices( posIndex );
+ pSkinMatrix = ComputeSkinMatrix( m_nJointCount, pJointWeight, pJointIndices, pPoseToWorld, result );
+ }
+
+ const Vector *pPositionData = &m_PositionData[ posIndex ];
+
+ if ( pDeltaPosition )
+ {
+ VectorAdd( *pPositionData, *( pDeltaPosition + posIndex ), vecMorphPosition );
+ pPositionData = &vecMorphPosition;
+ }
+
+ VectorTransform( *pPositionData, *pSkinMatrix, *( pPosition + posIndex ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes where a vertex is
+//-----------------------------------------------------------------------------
+void CRenderInfo::ComputeVertex( int vi, const matrix3x4_t *pPoseToWorld, CDmeMesh::RenderVertexDelta_t *pDelta, Vector *pPosition, Vector *pNormal, Vector4D *pTangent )
+{
+ matrix3x4_t result;
+ Vector vecMorphPosition, vecMorphNormal;
+ const matrix3x4_t *pSkinMatrix = pPoseToWorld;
+
+ if ( m_bHasSkinningData )
+ {
+ const float *pJointWeight = m_pBaseState->GetJointWeights( vi );
+ const int *pJointIndices = m_pBaseState->GetJointIndices( vi );
+ pSkinMatrix = ComputeSkinMatrix( m_nJointCount, pJointWeight, pJointIndices, pPoseToWorld, result );
+ }
+
+ int pi = m_PositionIndices[ vi ];
+ const Vector *pPositionData = &m_PositionData[ pi ];
+ if ( pDelta )
+ {
+ VectorAdd( *pPositionData, pDelta[ pi ].m_vecDeltaPosition, vecMorphPosition );
+ pPositionData = &vecMorphPosition;
+ }
+ VectorTransform( *pPositionData, *pSkinMatrix, *pPosition );
+
+ if ( m_bHasNormalData )
+ {
+ int ni = m_NormalIndices[ vi ];
+ const Vector *pNormalData = &m_NormalData[ ni ];
+ if ( pDelta )
+ {
+ VectorAdd( *pNormalData, pDelta[ni].m_vecDeltaNormal, vecMorphNormal );
+ pNormalData = &vecMorphNormal;
+ }
+ VectorRotate( *pNormalData, *pSkinMatrix, *pNormal );
+ VectorNormalize( *pNormal );
+ }
+ else
+ {
+ pNormal->Init( 0.0f, 0.0f, 1.0f );
+ }
+
+ if ( m_bHasTangentData )
+ {
+ const Vector4D &tangentData = m_TangentData[ m_TangentIndices[ vi ] ];
+ VectorRotate( tangentData.AsVector3D(), *pSkinMatrix, pTangent->AsVector3D() );
+ VectorNormalize( pTangent->AsVector3D() );
+ pTangent->w = tangentData.w;
+ }
+ else
+ {
+ pTangent->Init( 1.0f, 0.0f, 0.0f, 1.0f );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMesh, CDmeMesh );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMesh::OnConstruction()
+{
+ m_BindBaseState.Init( this, "bindState" );
+ m_CurrentBaseState.Init( this, "currentState" );
+ m_BaseStates.Init( this, "baseStates", FATTRIB_MUSTCOPY );
+ m_DeltaStates.Init( this, "deltaStates", FATTRIB_MUSTCOPY | FATTRIB_HAS_CALLBACK );
+ m_FaceSets.Init( this, "faceSets", FATTRIB_MUSTCOPY );
+ m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Init( this, "deltaStateWeights" );
+ m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED].Init( this, "deltaStateWeightsLagged" );
+}
+
+void CDmeMesh::OnDestruction()
+{
+ if ( g_pMaterialSystem )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ int nCount = m_hwFaceSets.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !m_hwFaceSets[i].m_bBuilt )
+ continue;
+
+ if ( m_hwFaceSets[i].m_pMesh )
+ {
+ pRenderContext->DestroyStaticMesh( m_hwFaceSets[i].m_pMesh );
+ }
+ }
+ m_hwFaceSets.RemoveAll();
+ }
+
+ DeleteAttributeVarElementArray( m_BaseStates );
+ DeleteAttributeVarElementArray( m_DeltaStates );
+ DeleteAttributeVarElementArray( m_FaceSets );
+}
+
+
+//-----------------------------------------------------------------------------
+// Initializes the normal material
+//-----------------------------------------------------------------------------
+void CDmeMesh::InitializeNormalMaterial()
+{
+ if ( !s_bNormalMaterialInitialized )
+ {
+ s_bNormalMaterialInitialized = true;
+
+ KeyValues *pVMTKeyValues = new KeyValues( "wireframe" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$decal", 1 );
+// pVMTKeyValues->SetInt( "$ignorez", 0 );
+ s_NormalMaterial.Init( "__DmeMeshNormalMaterial", pVMTKeyValues );
+
+ pVMTKeyValues = new KeyValues( "unlitgeneric" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ s_NormalErrorMaterial.Init( "__DmeMeshNormalErrorMaterial", pVMTKeyValues );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resolve internal data from changed attributes
+//-----------------------------------------------------------------------------
+void CDmeMesh::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ BaseClass::OnAttributeChanged( pAttribute );
+
+ if ( pAttribute == m_DeltaStates.GetAttribute() )
+ {
+ int nDeltaStateCount = m_DeltaStates.Count();
+ for ( int i = 0; i < MESH_DELTA_WEIGHT_TYPE_COUNT; ++i )
+ {
+ // Make sure we have the correct number of weights
+ int nWeightCount = m_DeltaStateWeights[i].Count();
+ if ( nWeightCount < nDeltaStateCount )
+ {
+ for ( int j = nWeightCount; j < nDeltaStateCount; ++j )
+ {
+ m_DeltaStateWeights[i].AddToTail( Vector2D( 0.0f, 0.0f ) );
+ }
+ }
+ else if ( nDeltaStateCount > nWeightCount )
+ {
+ m_DeltaStateWeights[i].RemoveMultiple( nWeightCount, nWeightCount - nDeltaStateCount );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds deltas into a delta mesh
+//-----------------------------------------------------------------------------
+template< class T > bool CDmeMesh::AddVertexDelta(
+ CDmeVertexData *pBaseState,
+ void *pVertexData, int nStride, CDmeVertexDataBase::StandardFields_t fieldId, int nIndex, bool bDoLag )
+{
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nIndex );
+
+ if ( !pBaseState || !pDeltaState )
+ return false;
+
+ const FieldIndex_t nBaseFieldIndex = pBaseState->FindFieldIndex( fieldId == CDmeVertexData::FIELD_WRINKLE ? CDmeVertexData::FIELD_TEXCOORD : fieldId );
+ const FieldIndex_t nDeltaFieldIndex = pDeltaState->FindFieldIndex( fieldId );
+ if ( nBaseFieldIndex < 0 || nDeltaFieldIndex < 0 )
+ return false;
+
+ const CDmrArray<int> indices = pDeltaState->GetIndexData( nDeltaFieldIndex );
+ const CDmrArray<T> delta = pDeltaState->GetVertexData( nDeltaFieldIndex );
+ const int nDeltaCount = indices.Count();
+
+ const float flWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][nIndex].x;
+
+ const FieldIndex_t nSpeedFieldIndex = pBaseState->FindFieldIndex( CDmeVertexData::FIELD_MORPH_SPEED );
+
+ if ( !bDoLag || nSpeedFieldIndex < 0 )
+ {
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ int nDataIndex = indices.Get( j );
+ T* pDeltaData = (T*)( (char*)pVertexData + nStride * nDataIndex );
+ *pDeltaData += delta.Get( j ) * flWeight;
+ }
+
+ return true;
+ }
+
+ const float flLaggedWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][nIndex].x;
+
+ const CDmrArrayConst<int> speedIndices = pBaseState->GetIndexData( nSpeedFieldIndex );
+ const CDmrArrayConst<float> speedDelta = pBaseState->GetVertexData( nSpeedFieldIndex );
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ int nDataIndex = indices.Get( j );
+ const CUtlVector<int> &list = pBaseState->FindVertexIndicesFromDataIndex( nBaseFieldIndex, nDataIndex );
+ Assert( list.Count() > 0 );
+ // FIXME: Average everything in the list.. shouldn't be necessary though
+ float flSpeed = speedDelta.Get( speedIndices.Get( list[0] ) );
+ float flActualWeight = Lerp( flSpeed, flLaggedWeight, flWeight );
+
+ T* pDeltaData = (T*)( (char*)pVertexData + nStride * nDataIndex );
+ *pDeltaData += delta.Get( j ) * flActualWeight;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::AddTexCoordDelta( RenderVertexDelta_t *pRenderDelta, float flWeight, CDmeVertexDeltaData *pDeltaState )
+{
+ FieldIndex_t nFieldIndex = pDeltaState->FindFieldIndex( CDmeVertexDeltaData::FIELD_TEXCOORD );
+ if ( nFieldIndex < 0 )
+ return;
+
+ bool bIsVCoordinateFlipped = pDeltaState->IsVCoordinateFlipped();
+ const CDmrArray<int> indices = pDeltaState->GetIndexData( nFieldIndex );
+ const CDmrArray<Vector2D> delta = pDeltaState->GetVertexData( nFieldIndex );
+ int nDeltaCount = indices.Count();
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ Vector2D uvDelta = delta.Get( j );
+ if ( bIsVCoordinateFlipped )
+ {
+ uvDelta.y = -uvDelta.y;
+ }
+ Vector2D &vec2D = pRenderDelta[ indices.Get( j ) ].m_vecDeltaUV;
+ Vector2DMA( vec2D, flWeight, uvDelta, vec2D );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::AddColorDelta( RenderVertexDelta_t *pRenderDelta, float flWeight, CDmeVertexDeltaData *pDeltaState )
+{
+ FieldIndex_t nFieldIndex = pDeltaState->FindFieldIndex( CDmeVertexDeltaData::FIELD_COLOR );
+ if ( nFieldIndex < 0 )
+ return;
+
+ const CDmrArray<int> indices = pDeltaState->GetIndexData( nFieldIndex );
+ const CDmrArray<Color> delta = pDeltaState->GetVertexData( nFieldIndex );
+ int nDeltaCount = indices.Count();
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ const Color &srcDeltaColor = delta[ j ];
+ Vector4D &vecDelta = pRenderDelta[ indices[ j ] ].m_vecDeltaColor;
+ vecDelta[0] += flWeight * srcDeltaColor.r();
+ vecDelta[1] += flWeight * srcDeltaColor.g();
+ vecDelta[2] += flWeight * srcDeltaColor.b();
+ vecDelta[3] += flWeight * srcDeltaColor.a();
+ }
+}
+
+template< class T > bool CDmeMesh::AddStereoVertexDelta(
+ CDmeVertexData *pBaseState,
+ void *pVertexData, int nStride, CDmeVertexDataBase::StandardFields_t fieldId, int nIndex, bool bDoLag )
+{
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nIndex );
+ if ( !pBaseState || !pDeltaState )
+ return false;
+
+ const FieldIndex_t nBaseFieldIndex = pBaseState->FindFieldIndex( fieldId == CDmeVertexData::FIELD_WRINKLE ? CDmeVertexData::FIELD_TEXCOORD : fieldId );
+ const FieldIndex_t nDeltaFieldIndex = pDeltaState->FindFieldIndex( fieldId );
+ if ( nBaseFieldIndex < 0 || nDeltaFieldIndex < 0 )
+ return false;
+
+ float flLeftWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][nIndex].x;
+ float flRightWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][nIndex].y;
+
+ const CDmrArray<int> indices = pDeltaState->GetIndexData( nDeltaFieldIndex );
+ const CDmrArray<T> delta = pDeltaState->GetVertexData( nDeltaFieldIndex );
+ const CUtlVector<int>& balanceIndices = pBaseState->GetVertexIndexData( CDmeVertexData::FIELD_BALANCE );
+ const CUtlVector<float> &balanceDelta = pBaseState->GetBalanceData();
+ const int nDeltaCount = indices.Count();
+
+ const FieldIndex_t nSpeedFieldIndex = pBaseState->FindFieldIndex( CDmeVertexData::FIELD_MORPH_SPEED );
+
+ if ( !bDoLag || nSpeedFieldIndex < 0 )
+ {
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ int nDataIndex = indices.Get( j );
+ const CUtlVector<int> &list = pBaseState->FindVertexIndicesFromDataIndex( nBaseFieldIndex, nDataIndex );
+ Assert( list.Count() > 0 );
+ // FIXME: Average everything in the list.. shouldn't be necessary though
+ float flRightAmount = balanceDelta[ balanceIndices[ list[0] ] ];
+ float flWeight = Lerp( flRightAmount, flLeftWeight, flRightWeight );
+
+ T* pDeltaData = (T*)( (char*)pVertexData + nStride * nDataIndex );
+ *pDeltaData += delta.Get( j ) * flWeight;
+ }
+
+ return true;
+ }
+
+ float flLeftWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][nIndex].x;
+ float flRightWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][nIndex].y;
+
+ const CDmrArray<int> pSpeedIndices = pBaseState->GetIndexData( nSpeedFieldIndex );
+ const CDmrArray<float> pSpeedDelta = pBaseState->GetVertexData( nSpeedFieldIndex );
+ for ( int j = 0; j < nDeltaCount; ++j )
+ {
+ int nDataIndex = indices.Get( j );
+ const CUtlVector<int> &list = pBaseState->FindVertexIndicesFromDataIndex( nBaseFieldIndex, nDataIndex );
+ Assert( list.Count() > 0 );
+ // FIXME: Average everything in the list.. shouldn't be necessary though
+ float flRightAmount = balanceDelta[ balanceIndices[ list[0] ] ];
+ float flWeight = Lerp( flRightAmount, flLeftWeight, flRightWeight );
+ float flLaggedWeight = Lerp( flRightAmount, flLeftWeightLagged, flRightWeightLagged );
+ float flSpeed = pSpeedDelta.Get( pSpeedIndices.Get( list[0] ) );
+ float flActualWeight = Lerp( flSpeed, flLaggedWeight, flWeight );
+
+ T* pDeltaData = (T*)( (char*)pVertexData + nStride * nDataIndex );
+ *pDeltaData += delta.Get( j ) * flActualWeight;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh when it uses too many bones
+//-----------------------------------------------------------------------------
+bool CDmeMesh::BuildDeltaMesh( int nVertices, RenderVertexDelta_t *pRenderDelta )
+{
+ bool bHasWrinkleDelta = false;
+
+ memset( pRenderDelta, 0, nVertices * sizeof( RenderVertexDelta_t ) );
+ int nCount = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Count();
+ Assert( m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Count() == m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED].Count() );
+
+ CDmeVertexData *pBindState = GetBindBaseState();
+
+ const FieldIndex_t nBalanceFieldIndex = pBindState->FindFieldIndex( CDmeVertexDeltaData::FIELD_BALANCE );
+ const FieldIndex_t nSpeedFieldIndex = pBindState->FindFieldIndex( CDmeVertexDeltaData::FIELD_MORPH_SPEED );
+ const bool bDoLag = nSpeedFieldIndex >= 0;
+ if ( nBalanceFieldIndex < 0 )
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flLaggedWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ if ( flWeight <= 0.0f && flLaggedWeight <= 0.0f )
+ continue;
+
+ // prepare vertices
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState(i);
+ AddVertexDelta<Vector>( pBindState, &pRenderDelta->m_vecDeltaPosition, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_POSITION, i, bDoLag );
+ AddVertexDelta<Vector>( pBindState, &pRenderDelta->m_vecDeltaNormal, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_NORMAL, i, bDoLag );
+ AddTexCoordDelta( pRenderDelta, flWeight, pDeltaState );
+ AddColorDelta( pRenderDelta, flWeight, pDeltaState );
+ bool bWrinkle = AddVertexDelta<float>( pBindState, &pRenderDelta->m_flDeltaWrinkle, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_WRINKLE, i, bDoLag );
+ bHasWrinkleDelta = bHasWrinkleDelta || bWrinkle;
+ }
+
+ return bHasWrinkleDelta;
+ }
+
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flLeftWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flRightWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].y;
+ float flLeftWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ float flRightWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].y;
+ if ( flLeftWeight <= 0.0f && flRightWeight <= 0.0f && flLeftWeightLagged <= 0.0f && flRightWeightLagged <= 0.0f )
+ continue;
+
+ // FIXME: Need to make balanced versions of texcoord + color
+ bool bWrinkle;
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState(i);
+ AddStereoVertexDelta<Vector>( pBindState, &pRenderDelta->m_vecDeltaPosition, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_POSITION, i, bDoLag );
+ AddStereoVertexDelta<Vector>( pBindState, &pRenderDelta->m_vecDeltaNormal, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_NORMAL, i, bDoLag );
+ bWrinkle = AddStereoVertexDelta<float>( pBindState, &pRenderDelta->m_flDeltaWrinkle, sizeof(RenderVertexDelta_t), CDmeVertexDeltaData::FIELD_WRINKLE, i, bDoLag);
+ bHasWrinkleDelta = bHasWrinkleDelta || bWrinkle;
+ AddTexCoordDelta( pRenderDelta, flLeftWeight, pDeltaState );
+ AddColorDelta( pRenderDelta, flLeftWeight, pDeltaState );
+ }
+
+ return bHasWrinkleDelta;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes triangulated indices for a face set into a meshbuilder
+//-----------------------------------------------------------------------------
+void CDmeMesh::WriteTriangluatedIndices( const CDmeVertexData *pBaseState, CDmeFaceSet *pFaceSet, CMeshBuilder &meshBuilder )
+{
+ // prepare indices
+ int nFirstIndex = 0;
+ int nIndexCount = pFaceSet->NumIndices();
+ while ( nFirstIndex < nIndexCount )
+ {
+ int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
+ if ( nVertexCount >= 3 )
+ {
+ int nOutCount = ( nVertexCount-2 ) * 3;
+ int *pIndices = (int*)_alloca( nOutCount * sizeof(int) );
+ ComputeTriangulatedIndices( pBaseState, pFaceSet, nFirstIndex, pIndices, nOutCount );
+ for ( int ii = 0; ii < nOutCount; ++ii )
+ {
+ meshBuilder.FastIndex( pIndices[ii] );
+ }
+ }
+ nFirstIndex += nVertexCount + 1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh when it uses too many bones
+//-----------------------------------------------------------------------------
+void CDmeMesh::DrawDynamicMesh( CDmeFaceSet *pFaceSet, matrix3x4_t *pPoseToWorld, bool bHasActiveDeltaStates, CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ CDmeVertexData *pBindBase = GetCurrentBaseState();
+ if ( !pBindBase )
+ return;
+
+ // NOTE: This is inherently inefficient; we re-skin the *entire* mesh,
+ // even if it's not being used by the entire model. This is because we can't
+ // guarantee the various materials from the various face sets use the
+ // same vertex format (even though they should), and we don't want to
+ // spend the work to detemine the sub-part of the mesh used by this face set.
+
+ // Compute vertex deltas for rendering
+ const int nVertices = pBindBase->VertexCount();
+
+ // NOTE: The Delta Data is actually indexed by the pPositionIndices, pNormalIndices, etc.
+ // The fact that we're storing one delta per final vertex nVertices
+ // is a waste of memory and simply implementational convenience.
+ bool bHasActiveWrinkle = false;
+ RenderVertexDelta_t *pVertexDelta = (RenderVertexDelta_t*)_alloca( nVertices * sizeof(RenderVertexDelta_t) );
+ if ( bHasActiveDeltaStates )
+ {
+ bHasActiveWrinkle = BuildDeltaMesh( nVertices, pVertexDelta );
+ }
+ else
+ {
+ pVertexDelta = NULL;
+ }
+
+ CRenderInfo renderInfo( pBindBase );
+ Assert( renderInfo.HasPositionData() );
+
+ // prepare vertices
+ FieldIndex_t uvField = pBindBase->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+ FieldIndex_t colorField = pBindBase->FindFieldIndex( CDmeVertexData::FIELD_COLOR );
+
+ bool bHasTexCoords = ( uvField >= 0 );
+ bool bHasColors = ( colorField >= 0 );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ const CDmrArrayConst<int> pUVIndices = bHasTexCoords ? pBindBase->GetIndexData( uvField ) : NULL;
+
+ if ( bHasActiveWrinkle && bHasTexCoords )
+ {
+ // Create the wrinkle flex mesh
+ IMesh *pFlexDelta = pRenderContext->GetFlexMesh();
+ int nFlexVertexOffset = 0;
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pFlexDelta, MATERIAL_HETEROGENOUS, nVertices, 0, &nFlexVertexOffset );
+ for ( int j=0; j < nVertices; j++ )
+ {
+ // NOTE: The UV indices are also used to index into wrinkle data
+ int nUVIndex = pUVIndices.Get( j );
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.NormalDelta3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Wrinkle1f( pVertexDelta[nUVIndex].m_flDeltaWrinkle );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End( false, false );
+ pMesh->SetFlexMesh( pFlexDelta, nFlexVertexOffset );
+ }
+
+ // build the mesh
+ int nIndices = pFaceSet->GetTriangulatedIndexCount();
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertices, nIndices );
+
+ const CDmrArrayConst<Vector2D> pUVData = bHasTexCoords ? pBindBase->GetVertexData( uvField ) : NULL;
+ const CDmrArrayConst<int> pColorIndices = bHasColors ? pBindBase->GetIndexData( colorField ) : NULL;
+ const CDmrArrayConst<Color> pColorData = bHasColors ? pBindBase->GetVertexData( colorField ) : NULL;
+
+ Vector vecPosition, vecNormal;
+ Vector4D vecTangent;
+ for ( int vi = 0; vi < nVertices; ++vi )
+ {
+ renderInfo.ComputeVertex( vi, pPoseToWorld, pVertexDelta, &vecPosition, &vecNormal, &vecTangent );
+ meshBuilder.Position3fv( vecPosition.Base() );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.UserData( vecTangent.Base() );
+
+ if ( pUVData.IsValid() )
+ {
+ int uvi = pUVIndices.Get( vi );
+ Vector2D uv = pUVData.Get( uvi );
+ if ( pBindBase->IsVCoordinateFlipped() )
+ {
+ uv.y = 1.0f - uv.y;
+ }
+
+ if ( bHasActiveDeltaStates )
+ {
+ uv += pVertexDelta[uvi].m_vecDeltaUV;
+ }
+ meshBuilder.TexCoord2fv( 0, uv.Base() );
+ }
+ else
+ {
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ }
+
+ if ( pColorIndices.IsValid() )
+ {
+ int ci = pColorIndices.Get( vi );
+ int color = pColorData.Get( ci ).GetRawColor();
+ meshBuilder.Color4ubv( (unsigned char*)&color );
+ }
+ else
+ {
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ }
+
+ meshBuilder.AdvanceVertex();
+ }
+
+ WriteTriangluatedIndices( pBindBase, pFaceSet, meshBuilder );
+
+ meshBuilder.End();
+
+ pMesh->Draw();
+
+ if ( pDrawSettings && pDrawSettings->GetNormals() )
+ {
+ RenderNormals( pPoseToWorld, bHasActiveDeltaStates ? pVertexDelta : NULL );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders normals
+//-----------------------------------------------------------------------------
+#define NORMAL_LINE_SIZE 0.25f
+
+void CDmeMesh::RenderNormals( matrix3x4_t *pPoseToWorld, RenderVertexDelta_t *pDelta )
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return;
+
+ CRenderInfo renderInfo( pBind );
+
+ Assert( renderInfo.HasPositionData() );
+ if ( !renderInfo.HasNormalData() )
+ return;
+ bool bHasTangents = renderInfo.HasTangentData();
+
+ // build the mesh
+ InitializeNormalMaterial();
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( s_NormalMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ int nMaxIndices, nMaxVertices;
+ pRenderContext->GetMaxToRender( pMesh, false, &nMaxVertices, &nMaxIndices );
+ int nFirstVertex = 0;
+ int nVerticesRemaining = pBind->VertexCount();;
+ int nFactor = bHasTangents ? 6 : 2;
+
+ while ( nVerticesRemaining > 0 )
+ {
+ int nVertices = nVerticesRemaining;
+ if ( nVertices > nMaxVertices / nFactor )
+ {
+ nVertices = nMaxVertices / nFactor;
+ }
+ if ( nVertices > nMaxIndices / nFactor )
+ {
+ nVertices = nMaxIndices / nFactor;
+ }
+ nVerticesRemaining -= nVertices;
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, bHasTangents ? nVertices * 3 : nVertices );
+
+ Vector vecPosition, vecNormal, vecEndPoint, vecTangentS, vecTangentT;
+ Vector4D vecTangent;
+ for ( int vi = nFirstVertex; vi < nVertices; ++vi )
+ {
+ renderInfo.ComputeVertex( vi, pPoseToWorld, pDelta, &vecPosition, &vecNormal, &vecTangent );
+
+ meshBuilder.Position3fv( vecPosition.Base() );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vecPosition, NORMAL_LINE_SIZE, vecNormal, vecEndPoint );
+ meshBuilder.Position3fv( vecEndPoint.Base() );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ continue;
+
+ if ( !bHasTangents )
+ continue;
+
+ CrossProduct( vecNormal, vecTangent.AsVector3D(), vecTangentT );
+ VectorNormalize( vecTangentT );
+ // NOTE: This is the new, desired tangentS morphing behavior
+ // CrossProduct( vecTangentT, vecNormal, vecTangentS );
+ VectorCopy( vecTangent.AsVector3D(), vecTangentS );
+ vecTangentT *= vecTangent.w;
+
+ meshBuilder.Position3fv( vecPosition.Base() );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vecPosition, NORMAL_LINE_SIZE, vecTangentS, vecEndPoint );
+ meshBuilder.Position3fv( vecEndPoint.Base() );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( vecPosition.Base() );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vecPosition, NORMAL_LINE_SIZE, vecTangentT, vecEndPoint );
+ meshBuilder.Position3fv( vecEndPoint.Base() );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ nFirstVertex += nVertices;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the passed DmeFaceSet in wireframe mode
+//-----------------------------------------------------------------------------
+void CDmeMesh::DrawWireframeFaceSet( CDmeFaceSet *pFaceSet, matrix3x4_t *pPoseToWorld, bool bHasActiveDeltaStates, CDmeDrawSettings *pDrawSettings )
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return;
+
+ const FieldIndex_t posField = pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ if ( posField < 0 )
+ return;
+
+ const CUtlVector< Vector > &posData( CDmrArrayConst< Vector >( pBind->GetVertexData( posField ) ).Get() );
+ const int nVertices = posData.Count();
+
+ const CUtlVector< int > &posIndices( CDmrArrayConst< int >( pBind->GetIndexData( posField ) ).Get() );
+
+ Vector *pDeltaVertices = bHasActiveDeltaStates ? pDeltaVertices = reinterpret_cast< Vector * >( alloca( nVertices * sizeof( Vector ) ) ) : NULL;
+
+ if ( bHasActiveDeltaStates )
+ {
+ memset( pDeltaVertices, 0, sizeof( Vector ) * nVertices );
+ const int nCount = m_DeltaStateWeights[ MESH_DELTA_WEIGHT_NORMAL ].Count();
+
+ const FieldIndex_t nBalanceFieldIndex = pBind->FindFieldIndex( CDmeVertexDeltaData::FIELD_BALANCE );
+ const FieldIndex_t nSpeedFieldIndex = pBind->FindFieldIndex( CDmeVertexDeltaData::FIELD_MORPH_SPEED );
+ const bool bDoLag = ( nSpeedFieldIndex >= 0 );
+
+ if ( nBalanceFieldIndex < 0 )
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flWeight = m_DeltaStateWeights[ MESH_DELTA_WEIGHT_NORMAL ][ i ].x;
+ float flLaggedWeight = m_DeltaStateWeights[ MESH_DELTA_WEIGHT_LAGGED ][ i ].x;
+ if ( flWeight <= 0.0f && ( !bDoLag || flLaggedWeight <= 0.0f ) )
+ continue;
+
+ AddVertexDelta< Vector >( pBind, pDeltaVertices, sizeof( Vector ), CDmeVertexDeltaData::FIELD_POSITION, i, bDoLag );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flLeftWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flRightWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].y;
+ float flLeftWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ float flRightWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].y;
+ if ( flLeftWeight <= 0.0f && flRightWeight <= 0.0f && ( !bDoLag || ( flLeftWeightLagged <= 0.0f && flRightWeightLagged <= 0.0f ) ) )
+ continue;
+
+ AddStereoVertexDelta< Vector >( pBind, pDeltaVertices, sizeof( Vector ), CDmeVertexDeltaData::FIELD_POSITION, i, bDoLag );
+ }
+ }
+ }
+
+ Vector *pVertices = reinterpret_cast< Vector * >( alloca( nVertices * sizeof( Vector ) ) );
+
+ CRenderInfo renderInfo( pBind );
+ Assert( renderInfo.HasPositionData() );
+
+ for ( int pi = 0; pi < nVertices; ++pi )
+ {
+ renderInfo.ComputePosition( pi, pPoseToWorld, pDeltaVertices, pVertices );
+ }
+
+ InitializeNormalMaterial();
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( s_NormalMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+
+ // build the mesh
+ CMeshBuilder meshBuilder;
+
+ // Draw the polygons in the face set
+ const int nFaceSetIndices = pFaceSet->NumIndices();
+ const int *pFaceSetIndices = pFaceSet->GetIndices();
+
+ int vR = 0;
+ int vG = 0;
+ int vB = 0;
+
+ if ( pDrawSettings )
+ {
+ const Color &vColor = pDrawSettings->GetColor();
+ vR = vColor.r();
+ vG = vColor.g();
+ vB = vColor.b();
+ }
+
+ int nFaceIndices;
+ for ( int i = 0; i < nFaceSetIndices; )
+ {
+ nFaceIndices = pFaceSet->GetNextPolygonVertexCount( i );
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, nFaceIndices );
+
+ for ( int j = 0; j < nFaceIndices; ++j )
+ {
+ Assert( i < nFaceSetIndices );
+
+ int vIndex0 = posIndices[ pFaceSetIndices[ i + j ] ];
+ Assert( vIndex0 < nVertices );
+ meshBuilder.Position3fv( reinterpret_cast< float * >( pVertices + vIndex0 ) );
+ meshBuilder.Color3ub( vR, vG, vB );
+ meshBuilder.AdvanceVertex();
+
+ int vIndex1 = posIndices[ pFaceSetIndices[ i + ( ( j + 1 ) % nFaceIndices ) ] ];
+ Assert( vIndex1 < nVertices );
+ meshBuilder.Position3fv( reinterpret_cast< float * >( pVertices + vIndex1) );
+ meshBuilder.Color3ub( vR, vG, vB );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+
+ i += nFaceIndices + 1;
+ }
+
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we have active delta state data?
+//-----------------------------------------------------------------------------
+bool CDmeMesh::HasActiveDeltaStates() const
+{
+ for ( int t = 0; t < MESH_DELTA_WEIGHT_TYPE_COUNT; ++t )
+ {
+ int nCount = m_DeltaStateWeights[t].Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_DeltaStateWeights[t][i].x != 0.0f || m_DeltaStateWeights[t][i].y != 0.0f )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh
+//-----------------------------------------------------------------------------
+void CDmeMesh::Draw( const matrix3x4_t &shapeToWorld, CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ const CDmeVertexData *pBind = GetBindBaseState();
+
+ if ( !pBind || !g_pMaterialSystem || !g_pMDLCache || !g_pStudioRender )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ const bool bHasActiveDeltaStates = HasActiveDeltaStates();
+ const bool bDrawNormals = pDrawSettings ? pDrawSettings->GetNormals() : false;
+ const CDmeDrawSettings::DrawType_t drawType = pDrawSettings ? pDrawSettings->GetDrawType() : CDmeDrawSettings::DRAW_SMOOTH;
+
+ const bool bShaded = ( drawType == CDmeDrawSettings::DRAW_SMOOTH || drawType == CDmeDrawSettings::DRAW_FLAT );
+ const bool bWireframe = ( drawType == CDmeDrawSettings::DRAW_WIREFRAME );
+// const bool bBoundingBox = ( drawType == CDmeDrawSettings::DRAW_BOUNDINGBOX );
+
+ const bool bSoftwareSkinning = bHasActiveDeltaStates | bDrawNormals | bWireframe;
+
+ matrix3x4_t *pPoseToWorld = CDmeModel::SetupModelRenderState( shapeToWorld, pBind->HasSkinningData(), bSoftwareSkinning );
+
+ pRenderContext->SetNumBoneWeights( pPoseToWorld ? 0 : pBind->JointCount() );
+
+ int nFaceSets = FaceSetCount();
+ m_hwFaceSets.EnsureCount( nFaceSets );
+
+ const bool bindMaterial = pDrawSettings ? !pDrawSettings->IsAMaterialBound() : true;
+
+ for ( int fi = 0; fi < nFaceSets; ++fi )
+ {
+ CDmeFaceSet *pFaceSet = GetFaceSet( fi );
+
+ if ( bWireframe )
+ {
+ DrawWireframeFaceSet( pFaceSet, pPoseToWorld, bHasActiveDeltaStates, pDrawSettings );
+ continue;
+ }
+
+ if ( bindMaterial )
+ {
+ pRenderContext->Bind( pFaceSet->GetMaterial()->GetCachedMTL() );
+ }
+
+ if ( pPoseToWorld || ( bShaded && bSoftwareSkinning ) )
+ {
+ DrawDynamicMesh( pFaceSet, pPoseToWorld, bHasActiveDeltaStates, pDrawSettings );
+ continue;
+ }
+
+ // TODO: figure out how to tell the mesh when the faceset's indices change
+ if ( !m_hwFaceSets[fi].m_bBuilt )
+ {
+ m_hwFaceSets[fi].m_pMesh = CreateHwMesh( pFaceSet );
+ m_hwFaceSets[fi].m_bBuilt = true;
+ }
+
+ if ( m_hwFaceSets[fi].m_pMesh )
+ {
+ m_hwFaceSets[fi].m_pMesh->Draw();
+ }
+ }
+
+ pRenderContext->SetNumBoneWeights( 0 );
+ CDmeModel::CleanupModelRenderState();
+}
+
+
+//-----------------------------------------------------------------------------
+// Face sets
+//-----------------------------------------------------------------------------
+int CDmeMesh::FaceSetCount() const
+{
+ return m_FaceSets.Count();
+}
+
+CDmeFaceSet *CDmeMesh::GetFaceSet( int faceSetIndex )
+{
+ return m_FaceSets[ faceSetIndex ];
+}
+
+const CDmeFaceSet *CDmeMesh::GetFaceSet( int faceSetIndex ) const
+{
+ return m_FaceSets[ faceSetIndex ];
+}
+
+void CDmeMesh::AddFaceSet( CDmeFaceSet *faceSet )
+{
+ m_FaceSets.AddToTail( faceSet );
+}
+
+void CDmeMesh::RemoveFaceSet( int faceSetIndex )
+{
+ m_FaceSets.Remove( faceSetIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Find a base state by name
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::FindBaseState( const char *pStateName ) const
+{
+ const int nBaseStateCount = BaseStateCount();
+ for ( int i = 0; i < nBaseStateCount; ++i )
+ {
+ CDmeVertexData *pBaseState = GetBaseState( i );
+ if ( !Q_stricmp( pStateName, pBaseState->GetName() ) )
+ return pBaseState;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find a base state by name, add a new one if not found
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::FindOrCreateBaseState( const char *pStateName )
+{
+ CDmeVertexData *pBaseState = FindBaseState( pStateName );
+ if ( pBaseState )
+ return pBaseState;
+
+ pBaseState = CreateElement< CDmeVertexData >( pStateName, GetFileId() );
+ m_BaseStates.AddToTail( pBaseState );
+
+ return pBaseState;
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove a base state by name
+//-----------------------------------------------------------------------------
+bool CDmeMesh::DeleteBaseState( const char *pStateName )
+{
+ const int nBaseStateCount = BaseStateCount();
+ for ( int i = 0; i < nBaseStateCount; ++i )
+ {
+ const CDmeVertexData *pBaseState = GetBaseState( i );
+ if ( !Q_stricmp( pStateName, pBaseState->GetName() ) )
+ {
+ m_BaseStates.Remove( i );
+ g_pDataModel->DestroyElement( pBaseState->GetHandle() );
+
+ // TODO: Fix up all dependent states
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a particular base state to be current state
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetCurrentBaseState( const char *pStateName )
+{
+ m_CurrentBaseState = FindBaseState( pStateName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a particular base state to be current state
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::GetCurrentBaseState()
+{
+ return m_CurrentBaseState;
+}
+
+
+//-----------------------------------------------------------------------------
+// Selects a particular base state to be current state
+//-----------------------------------------------------------------------------
+const CDmeVertexData *CDmeMesh::GetCurrentBaseState() const
+{
+ return m_CurrentBaseState;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeMesh::SetBindBaseState( CDmeVertexData *pBaseState )
+{
+ if ( !pBaseState )
+ return false;
+
+ CDmeVertexData *pCheckState = FindBaseState( pBaseState->GetName() );
+ if ( pCheckState != pBaseState )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::GetBindBaseState()
+{
+ if ( m_BindBaseState.GetElement() )
+ return m_BindBaseState;
+
+ // Backwards compatibility
+ return FindBaseState( "bind" );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const CDmeVertexData *CDmeMesh::GetBindBaseState() const
+{
+ if ( m_BindBaseState.GetElement() )
+ return m_BindBaseState;
+
+ // Backwards compatibility
+ return FindBaseState( "bind" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Delta states
+//-----------------------------------------------------------------------------
+int CDmeMesh::DeltaStateCount() const
+{
+ return m_DeltaStates.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the delta
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmeMesh::GetDeltaState( int nDeltaIndex ) const
+{
+ if ( nDeltaIndex < 0 || nDeltaIndex >= m_DeltaStates.Count() )
+ return NULL;
+
+ return m_DeltaStates[ nDeltaIndex ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a delta state by name. If it isn't found, return NULL
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmeMesh::FindDeltaState( const char *pDeltaName ) const
+{
+ return GetDeltaState( FindDeltaStateIndex( pDeltaName ) );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int SortDeltaNameFunc( const void *a, const void *b )
+{
+ return Q_strcmp( *( const char ** )( a ), *( const char ** )( b ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// If the name doe
+//-----------------------------------------------------------------------------
+const char *SortDeltaName( const char *pInDeltaName, char *pOutDeltaName, int nOutDeltaNameBufLen )
+{
+ if ( !pInDeltaName || !strchr( pInDeltaName, '_' ) )
+ return pInDeltaName;
+
+ char **ppDeltaNames = reinterpret_cast< char ** >( stackalloc( nOutDeltaNameBufLen * sizeof( char * ) ) );
+ memset( ppDeltaNames, 0, nOutDeltaNameBufLen * sizeof( char * ) );
+
+ const char *pStart = pInDeltaName;
+ int nDimensionCount = 0;
+ while ( pStart )
+ {
+ const char *pUnderBar = strchr( pStart, '_' );
+ const int nControlNameBufLen = ( pUnderBar ? pUnderBar - pStart : Q_strlen( pStart ) ) + 1;
+
+ if ( nControlNameBufLen )
+ {
+ ppDeltaNames[ nDimensionCount ] = reinterpret_cast< char * >( stackalloc( nControlNameBufLen * sizeof( char ) ) );
+ Q_strncpy( ppDeltaNames[ nDimensionCount ], pStart, nControlNameBufLen );
+ ++nDimensionCount;
+ }
+
+ pStart = pUnderBar;
+ if ( pStart )
+ {
+ ++pStart;
+ }
+ }
+
+ // This should only happen if the input name is all _'s
+ if ( nDimensionCount <= 0 )
+ return pInDeltaName;
+
+ qsort( ppDeltaNames, nDimensionCount, sizeof( char * ), SortDeltaNameFunc );
+
+ char *pDst = pOutDeltaName;
+ for ( int i = 0; i < nDimensionCount; ++i )
+ {
+ if ( i != 0 )
+ {
+ Q_strncpy( pDst, "_", nOutDeltaNameBufLen );
+ ++pDst;
+ --nOutDeltaNameBufLen;
+ }
+
+ const int nControlNameLen = Q_strlen( ppDeltaNames[ i ] );
+ Q_strncpy( pDst, ppDeltaNames[ i ], nOutDeltaNameBufLen );
+ pDst += nControlNameLen;
+ nOutDeltaNameBufLen -= nControlNameLen;
+ }
+
+ return pOutDeltaName;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmeMesh::FindOrCreateDeltaState( const char *pInDeltaName )
+{
+ CDmeVertexDeltaData *pDeltaState = FindDeltaState( pInDeltaName );
+ if ( pDeltaState )
+ return pDeltaState;
+
+ const int nDeltaNameBufLen = Q_strlen( pInDeltaName ) + 1;
+ char *pDeltaNameBuf = reinterpret_cast< char * >( stackalloc( nDeltaNameBufLen * sizeof( char ) ) );
+ const char *pDeltaName = SortDeltaName( pInDeltaName, pDeltaNameBuf, nDeltaNameBufLen );
+
+ pDeltaState = CreateElement< CDmeVertexDeltaData >( pDeltaName, GetFileId() );
+ if ( pDeltaState )
+ {
+ m_DeltaStates.AddToTail( pDeltaState );
+ }
+
+ return pDeltaState;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a delta state index by comparing names, if it can't be found
+// searches for all permutations of the delta name
+//-----------------------------------------------------------------------------
+int CDmeMesh::FindDeltaStateIndex( const char *pInDeltaName ) const
+{
+ const char *pDeltaName = pInDeltaName;
+
+ if ( strchr( pInDeltaName, '_' ) )
+ {
+ const int nDeltaNameBufLen = Q_strlen( pInDeltaName ) + 1;
+ char *pDeltaNameBuf = reinterpret_cast< char * >( stackalloc( nDeltaNameBufLen * sizeof( char ) ) );
+ pDeltaName = SortDeltaName( pInDeltaName, pDeltaNameBuf, nDeltaNameBufLen );
+ }
+
+ int dn = DeltaStateCount();
+ for ( int di = 0; di < dn; ++di )
+ {
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( di );
+ if ( !Q_stricmp( pDeltaName, pDeltaState->GetName() ) )
+ return di;
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetDeltaStateWeight( int nDeltaIndex, MeshDeltaWeightType_t type, float flMorphWeight )
+{
+ if ( nDeltaIndex < m_DeltaStateWeights[type].Count() )
+ {
+ m_DeltaStateWeights[type].Set( nDeltaIndex, Vector2D( flMorphWeight, flMorphWeight ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetDeltaStateWeight( int nDeltaIndex, MeshDeltaWeightType_t type, float flLeftWeight, float flRightWeight )
+{
+ if ( nDeltaIndex < m_DeltaStateWeights[type].Count() )
+ {
+ m_DeltaStateWeights[type].Set( nDeltaIndex, Vector2D( flLeftWeight, flRightWeight ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Determines the appropriate vertex format for hardware meshes
+//-----------------------------------------------------------------------------
+VertexFormat_t CDmeMesh::ComputeHwMeshVertexFormat( void )
+{
+ bool bIsDX7 = !g_pMaterialSystemHardwareConfig->SupportsVertexAndPixelShaders();
+ VertexFormat_t vertexFormat = VERTEX_POSITION | VERTEX_COLOR | VERTEX_NORMAL | VERTEX_TEXCOORD_SIZE(0,2) | VERTEX_BONEWEIGHT(2) | VERTEX_BONE_INDEX
+ | ( bIsDX7 ? 0 : VERTEX_USERDATA_SIZE(4) );
+
+ // FIXME: set VERTEX_FORMAT_COMPRESSED if there are no artifacts and if it saves enough memory (use 'mem_dumpvballocs')
+ // vertexFormat |= VERTEX_FORMAT_COMPRESSED;
+ // FIXME: check for and strip unused vertex elements (see 'bHasNormals', etc, in CreateHwMesh below)
+
+ return vertexFormat;
+}
+
+//-----------------------------------------------------------------------------
+// Builds a hardware mesh
+//-----------------------------------------------------------------------------
+IMesh *CDmeMesh::CreateHwMesh( CDmeFaceSet *pFaceSet )
+{
+ const CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return NULL;
+
+ // NOTE: This is memory inefficient. We create a copy of all vertices
+ // for each face set, even if those vertices aren't used by the face set
+ // Mostly chose to do this for code simplicity, although it also is faster to generate meshes
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ VertexFormat_t vertexFormat = ComputeHwMeshVertexFormat( );
+ IMesh *pMesh = pRenderContext->CreateStaticMesh( vertexFormat, "dmemesh" );
+
+ CMeshBuilder meshBuilder;
+
+ // prepare vertices
+ FieldIndex_t posField = pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ FieldIndex_t normalField = pBind->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ FieldIndex_t tangentField = pBind->FindFieldIndex( CDmeVertexData::FIELD_TANGENT );
+ FieldIndex_t uvField = pBind->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+ FieldIndex_t colorField = pBind->FindFieldIndex( CDmeVertexData::FIELD_COLOR );
+
+ Assert( posField >= 0 );
+ bool bHasNormals = ( normalField >= 0 );
+ bool bHasTangent = ( tangentField >= 0 );
+ bool bHasTexCoords = ( uvField >= 0 );
+ bool bHasColors = ( colorField >= 0 );
+
+ // build the mesh
+ int nIndices = pFaceSet->GetTriangulatedIndexCount();
+ int nVertices = pBind->VertexCount();
+ int nJointCount = pBind->JointCount();
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertices, nIndices );
+
+ const CDmrArrayConst<int> pPositionIndices = pBind->GetIndexData( posField );
+ const CDmrArrayConst<Vector> pPositionData = pBind->GetVertexData( posField );
+ const CDmrArrayConst<int> pNormalIndices = bHasNormals ? pBind->GetIndexData( normalField ) : NULL;
+ const CDmrArrayConst<Vector> pNormalData = bHasNormals ? pBind->GetVertexData( normalField ) : NULL;
+ const CDmrArrayConst<int> pTangentIndices = bHasTangent ? pBind->GetIndexData( tangentField ) : NULL;
+ const CDmrArrayConst<Vector4D> pTangentData = bHasTangent ? pBind->GetVertexData( tangentField ) : NULL;
+ const CDmrArrayConst<int> pUVIndices = bHasTexCoords ? pBind->GetIndexData( uvField ) : NULL;
+ const CDmrArrayConst<Vector2D> pUVData = bHasTexCoords ? pBind->GetVertexData( uvField ) : NULL;
+ const CDmrArrayConst<int> pColorIndices = bHasColors ? pBind->GetIndexData( colorField ) : NULL;
+ const CDmrArrayConst<Color> pColorData = bHasColors ? pBind->GetVertexData( colorField ) : NULL;
+
+ Vector4D defaultTangentS( 1.0f, 0.0f, 0.0f, 1.0f );
+ for ( int vi = 0; vi < nVertices; ++vi )
+ {
+ meshBuilder.Position3fv( pPositionData.Get( pPositionIndices.Get( vi ) ).Base() );
+ if ( pNormalData.IsValid() )
+ {
+ meshBuilder.Normal3fv( pNormalData.Get( pNormalIndices.Get( vi ) ).Base() );
+ }
+ else
+ {
+ meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
+ }
+ if ( pTangentData.IsValid() )
+ {
+ meshBuilder.UserData( pTangentData.Get( pTangentIndices.Get( vi ) ).Base() );
+ }
+ else
+ {
+ meshBuilder.UserData( defaultTangentS.Base() );
+ }
+ if ( pUVData.IsValid() )
+ {
+ const Vector2D &uv = pUVData.Get( pUVIndices.Get( vi ) );
+ if ( !pBind->IsVCoordinateFlipped() )
+ {
+ meshBuilder.TexCoord2fv( 0, uv.Base() );
+ }
+ else
+ {
+ meshBuilder.TexCoord2f( 0, uv.x, 1.0f - uv.y );
+ }
+ }
+ else
+ {
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ }
+ if ( pColorIndices.IsValid() )
+ {
+ int color = pColorData.Get( pColorIndices.Get( vi ) ).GetRawColor();
+ meshBuilder.Color4ubv( (unsigned char*)&color );
+ }
+ else
+ {
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ }
+
+ // FIXME: Note that this will break once we exceeed the max joint count
+ // that the hardware can handle
+ const float *pJointWeight = pBind->GetJointWeights( vi );
+ const int *pJointIndices = pBind->GetJointIndices( vi );
+ for ( int i = 0; i < nJointCount; ++i )
+ {
+ meshBuilder.BoneWeight( i, pJointWeight[i] );
+ meshBuilder.BoneMatrix( i, pJointIndices[i] );
+ }
+
+ for ( int i = nJointCount; i < 4; ++i )
+ {
+ meshBuilder.BoneWeight( i, ( i == 0 ) ? 1.0f : 0.0f );
+ meshBuilder.BoneMatrix( i, 0 );
+ }
+
+ meshBuilder.AdvanceVertex();
+ }
+
+ // prepare indices
+ int nFirstIndex = 0;
+ int nIndexCount = pFaceSet->NumIndices();
+ while ( nFirstIndex < nIndexCount )
+ {
+ int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
+ if ( nVertexCount >= 3 )
+ {
+ int nOutCount = ( nVertexCount-2 ) * 3;
+ int *pIndices = (int*)_alloca( nOutCount * sizeof(int) );
+ ComputeTriangulatedIndices( pBind, pFaceSet, nFirstIndex, pIndices, nOutCount );
+ for ( int ii = 0; ii < nOutCount; ++ii )
+ {
+ meshBuilder.FastIndex( pIndices[ii] );
+ }
+ }
+ nFirstIndex += nVertexCount + 1;
+ }
+
+ meshBuilder.End();
+
+ return pMesh;
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute triangulated indices
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeTriangulatedIndices( const CDmeVertexData *pBaseState, CDmeFaceSet *pFaceSet, int nFirstIndex, int *pIndices, int nOutCount )
+{
+ // FIXME: Come up with a more efficient way of computing this
+ // This involves a bunch of recomputation of distances
+ float flMinDistance = FLT_MAX;
+ int nMinIndex = 0;
+ int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
+
+ // Optimization for quads + triangles.. it's totally symmetric
+ int nLoopCount = nVertexCount;
+ if ( nVertexCount <= 3 )
+ {
+ nLoopCount = 0;
+ }
+ else if ( nVertexCount == 4 )
+ {
+ nLoopCount = 2;
+ }
+
+ for ( int i = 0; i < nLoopCount; ++i )
+ {
+ float flDistance = 0.0f;
+ const Vector &vecCenter = pBaseState->GetPosition( pFaceSet->GetIndex( nFirstIndex+i ) );
+ for ( int j = 2; j < nVertexCount-1; ++j )
+ {
+ int vi = ( i + j ) % nVertexCount;
+ const Vector &vecEdge = pBaseState->GetPosition( pFaceSet->GetIndex( nFirstIndex+vi ) );
+ flDistance += vecEdge.DistTo( vecCenter );
+ }
+
+ if ( flDistance < flMinDistance )
+ {
+ nMinIndex = i;
+ flMinDistance = flDistance;
+ }
+ }
+
+ // Compute the triangulation indices
+ Assert( nOutCount == ( nVertexCount - 2 ) * 3 );
+ int nOutIndex = 0;
+ for ( int i = 1; i < nVertexCount - 1; ++i )
+ {
+ pIndices[nOutIndex++] = pFaceSet->GetIndex( nFirstIndex + nMinIndex );
+ pIndices[nOutIndex++] = pFaceSet->GetIndex( nFirstIndex + ((nMinIndex + i) % nVertexCount) );
+ pIndices[nOutIndex++] = pFaceSet->GetIndex( nFirstIndex + ((nMinIndex + i + 1) % nVertexCount) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Build a map from vertex index to a list of triangles that share the vert.
+//-----------------------------------------------------------------------------
+void CDmeMesh::BuildTriangleMap( const CDmeVertexData *pBaseState, CDmeFaceSet* pFaceSet, CUtlVector<Triangle_t>& triangles, CUtlVector< CUtlVector<int> >* pVertToTriMap )
+{
+ // prepare indices
+ int nFirstIndex = 0;
+ int nIndexCount = pFaceSet->NumIndices();
+ while ( nFirstIndex < nIndexCount )
+ {
+ int nVertexCount = pFaceSet->GetNextPolygonVertexCount( nFirstIndex );
+ if ( nVertexCount >= 3 )
+ {
+ int nOutCount = ( nVertexCount-2 ) * 3;
+ int *pIndices = (int*)_alloca( nOutCount * sizeof(int) );
+ ComputeTriangulatedIndices( pBaseState, pFaceSet, nFirstIndex, pIndices, nOutCount );
+ for ( int ii = 0; ii < nOutCount; ii += 3 )
+ {
+ int t = triangles.AddToTail();
+ Triangle_t& triangle = triangles[t];
+
+ triangle.m_nIndex[0] = pIndices[ii];
+ triangle.m_nIndex[1] = pIndices[ii+1];
+ triangle.m_nIndex[2] = pIndices[ii+2];
+
+ if ( pVertToTriMap )
+ {
+ (*pVertToTriMap)[ pIndices[ii] ].AddToTail( t );
+ (*pVertToTriMap)[ pIndices[ii+1] ].AddToTail( t );
+ (*pVertToTriMap)[ pIndices[ii+2] ].AddToTail( t );
+ }
+ }
+ }
+ nFirstIndex += nVertexCount + 1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes tangent space data for triangles
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeTriangleTangets( const CDmeVertexData *pVertexData, CUtlVector<Triangle_t>& triangles )
+{
+ // Calculate the tangent space for each triangle.
+ int nTriangleCount = triangles.Count();
+ for ( int triID = 0; triID < nTriangleCount; triID++ )
+ {
+ Triangle_t &triangle = triangles[triID];
+
+ const Vector &p0 = pVertexData->GetPosition( triangle.m_nIndex[0] );
+ const Vector &p1 = pVertexData->GetPosition( triangle.m_nIndex[1] );
+ const Vector &p2 = pVertexData->GetPosition( triangle.m_nIndex[2] );
+ const Vector2D &t0 = pVertexData->GetTexCoord( triangle.m_nIndex[0] );
+ const Vector2D &t1 = pVertexData->GetTexCoord( triangle.m_nIndex[1] );
+ const Vector2D &t2 = pVertexData->GetTexCoord( triangle.m_nIndex[2] );
+ CalcTriangleTangentSpace( p0, p1, p2, t0, t1, t2, triangle.m_vecTangentS, triangle.m_vecTangentT );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Build a map from vertex index to a list of triangles that share the vert.
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeAverageTangent( CDmeVertexData *pVertexData, bool bSmoothTangents, CUtlVector< CUtlVector<int> >& vertToTriMap, CUtlVector<Triangle_t>& triangles )
+{
+ FieldIndex_t posField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ FieldIndex_t normalField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ FieldIndex_t tangentField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_TANGENT );
+
+ const CDmrArray<int> pPositionIndices = pVertexData->GetIndexData( posField );
+ const CDmrArray<Vector> pPositionData = pVertexData->GetVertexData( posField );
+ const CDmrArray<int> pNormalIndices = pVertexData->GetIndexData( normalField );
+ const CDmrArray<Vector> pNormalData = pVertexData->GetVertexData( normalField );
+
+ // calculate an average tangent space for each vertex.
+ int nVertexCount = pVertexData->VertexCount();
+ Vector4D finalSVect;
+ for( int vertID = 0; vertID < nVertexCount; vertID++ )
+ {
+ CUtlVector<int> &triangleList = vertToTriMap[vertID];
+
+ Vector sVect, tVect;
+ sVect.Init( 0.0f, 0.0f, 0.0f );
+ tVect.Init( 0.0f, 0.0f, 0.0f );
+ int nTriangleCount = triangleList.Count();
+ for ( int triID = 0; triID < nTriangleCount; triID++ )
+ {
+ Triangle_t &tri = triangles[ triangleList[triID] ];
+ sVect += tri.m_vecTangentS;
+ tVect += tri.m_vecTangentT;
+ }
+
+ // In the case of zbrush, everything needs to be treated as smooth.
+ if ( bSmoothTangents )
+ {
+ const Vector &vertPos1 = pPositionData.Get( pPositionIndices.Get( vertID ) );
+ for( int vertID2 = 0; vertID2 < nVertexCount; vertID2++ )
+ {
+ if ( vertID2 == vertID )
+ continue;
+
+ const Vector &vertPos2 = pPositionData.Get( pPositionIndices.Get( vertID2 ) );
+ if ( vertPos1 != vertPos2 )
+ continue;
+
+ CUtlVector<int> &triangleList2 = vertToTriMap[vertID2];
+ int nTriangleCount2 = triangleList2.Count();
+ for ( int triID2 = 0; triID2 < nTriangleCount2; triID2++ )
+ {
+ Triangle_t &tri2 = triangles[ triangleList2[triID2] ];
+ sVect += tri2.m_vecTangentS;
+ tVect += tri2.m_vecTangentT;
+ }
+ }
+ }
+
+ // make an orthonormal system.
+ // need to check if we are left or right handed.
+ Vector tmpVect;
+ CrossProduct( sVect, tVect, tmpVect );
+ const Vector &normal = pNormalData.Get( pNormalIndices.Get( vertID ) );
+ bool bLeftHanded = DotProduct( tmpVect, normal ) < 0.0f;
+ if ( !bLeftHanded )
+ {
+ CrossProduct( normal, sVect, tVect );
+ CrossProduct( tVect, normal, sVect );
+ VectorNormalize( sVect );
+ VectorNormalize( tVect );
+ finalSVect[0] = sVect[0];
+ finalSVect[1] = sVect[1];
+ finalSVect[2] = sVect[2];
+ finalSVect[3] = 1.0f;
+ }
+ else
+ {
+ CrossProduct( sVect, normal, tVect );
+ CrossProduct( normal, tVect, sVect );
+ VectorNormalize( sVect );
+ VectorNormalize( tVect );
+ finalSVect[0] = sVect[0];
+ finalSVect[1] = sVect[1];
+ finalSVect[2] = sVect[2];
+ finalSVect[3] = -1.0f;
+ }
+
+ pVertexData->SetVertexData( tangentField, vertID, 1, AT_VECTOR4, &finalSVect );
+ pVertexData->SetVertexIndices( tangentField, vertID, 1, &vertID );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a map from vertex index to all triangles that use it
+//-----------------------------------------------------------------------------
+void CDmeMesh::BuildVertToTriMap( const CDmeVertexData *pVertexData, CUtlVector<Triangle_t> &triangles, CUtlVector< CUtlVector<int> > &vertToTriMap )
+{
+ vertToTriMap.AddMultipleToTail( pVertexData->VertexCount() );
+
+ int nCount = FaceSetCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = GetFaceSet( i );
+ BuildTriangleMap( pVertexData, pFaceSet, triangles, &vertToTriMap );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute a default per-vertex tangent given normal data + uv data
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDefaultTangentData( CDmeVertexData *pVertexData, bool bSmoothTangents )
+{
+ if ( !pVertexData )
+ return;
+
+ // Need to have valid pos, uv, and normal to perform this operation
+ FieldIndex_t posField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ FieldIndex_t normalField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ FieldIndex_t uvField = pVertexData->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+ if ( posField < 0 || uvField < 0 || normalField < 0 )
+ return;
+
+ // FIXME: Need to do a pass to make sure no vertex is referenced by
+ // multiple facesets that have different materials in them.
+ // In that case, we need to add extra copies of that vertex and modify
+ // the face set data to refer to the new vertices
+
+ // Build a map from vertex to a list of triangles that share the vert.
+ CUtlVector<Triangle_t> triangles( 0, 1024 );
+ CUtlVector< CUtlVector<int> > vertToTriMap;
+ vertToTriMap.AddMultipleToTail( pVertexData->VertexCount() );
+
+ int nCount = FaceSetCount();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = GetFaceSet( i );
+ BuildTriangleMap( pVertexData, pFaceSet, triangles, &vertToTriMap );
+ }
+
+ ComputeTriangleTangets( pVertexData, triangles );
+
+ // FIXME: We could do a pass to determine the unique combinations of
+ // position + tangent indices in the vertex data. We only need to have
+ // a unique tangent for each of these unique vertices. For simplicity
+ // (and speed), I'll assume all tangents are unique per vertex.
+ FieldIndex_t tangent = pVertexData->CreateField<Vector4D>( "tangents" );
+ pVertexData->RemoveAllVertexData( tangent );
+ pVertexData->AddVertexData( tangent, pVertexData->VertexCount() );
+
+ ComputeAverageTangent( pVertexData, bSmoothTangents, vertToTriMap, triangles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute a default per-vertex tangent given normal data + uv data for all vertex data referenced by this mesh
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDefaultTangentData( bool bSmoothTangents )
+{
+ const int nBaseStateCount = m_BaseStates.Count();
+ for ( int i = 0; i < nBaseStateCount; ++i )
+ {
+ if ( m_BaseStates[i] && m_BaseStates[i]->NeedsTangentData() )
+ {
+ ComputeDefaultTangentData( m_BaseStates[i], bSmoothTangents );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Utility method to compute default tangent data on all meshes in the sub-dag hierarchy
+//-----------------------------------------------------------------------------
+void ComputeDefaultTangentData( CDmeDag *pDag, bool bSmoothTangents )
+{
+ if ( !pDag )
+ return;
+
+ CDmeMesh *pMesh = CastElement< CDmeMesh >( pDag->GetShape() );
+ if ( pMesh )
+ {
+ pMesh->ComputeDefaultTangentData( bSmoothTangents );
+ }
+
+ int nChildCount = pDag->GetChildCount();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ ComputeDefaultTangentData( pDag->GetChild( i ), bSmoothTangents );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the dimensionality of the delta state (how many inputs affect it)
+//-----------------------------------------------------------------------------
+int CDmeMesh::ComputeDeltaStateDimensionality( int nDeltaIndex )
+{
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nDeltaIndex );
+ const char *pDeltaStateName = pDeltaState->GetName();
+
+ const char *pUnderBar = pDeltaStateName;
+ int nDimensions = 0;
+ while ( pUnderBar )
+ {
+ ++nDimensions;
+ pUnderBar = strchr( pUnderBar, '_' );
+ if ( pUnderBar )
+ {
+ ++pUnderBar;
+ }
+ }
+
+ return nDimensions;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the aggregate position for all vertices after applying a set of delta states
+//-----------------------------------------------------------------------------
+void CDmeMesh::AddDelta( CDmeVertexData *pBaseState, Vector *pDeltaPosition, int nDeltaStateIndex, CDmeVertexData::StandardFields_t fieldId )
+{
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nDeltaStateIndex );
+ FieldIndex_t nFieldIndex = pDeltaState->FindFieldIndex( fieldId );
+ if ( nFieldIndex < 0 )
+ return;
+
+ if ( pBaseState->FindFieldIndex( CDmeVertexData::FIELD_BALANCE ) != -1 )
+ {
+ AddStereoVertexDelta<Vector>( pBaseState, pDeltaPosition, sizeof(Vector), fieldId, nDeltaStateIndex, true );
+ }
+ else
+ {
+ AddVertexDelta<Vector>( pBaseState, pDeltaPosition, sizeof(Vector), fieldId, nDeltaStateIndex, true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes correctly averaged vertex normals from position data
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeNormalsFromPositions(
+ CDmeVertexData *pBase,
+ const Vector *pPosition,
+ const CUtlVector<Triangle_t> &triangles,
+ int nNormalCount,
+ Vector *pNormals )
+{
+ Assert( nNormalCount == pBase->GetNormalData().Count() );
+ int *pNormalsAdded = (int*)_alloca( nNormalCount * sizeof(int) );
+ memset( pNormalsAdded, 0, nNormalCount * sizeof(int) );
+ memset( pNormals, 0, nNormalCount * sizeof(Vector) );
+
+ const CUtlVector<int> &positionIndices = pBase->GetVertexIndexData( CDmeVertexData::FIELD_POSITION );
+ const CUtlVector<int> &normalIndices = pBase->GetVertexIndexData( CDmeVertexData::FIELD_NORMAL );
+
+ int nTriangleCount = triangles.Count();
+ for ( int i = 0; i < nTriangleCount; ++i )
+ {
+ const Triangle_t &tri = triangles[i];
+ int p1 = positionIndices[ tri.m_nIndex[0] ];
+ int p2 = positionIndices[ tri.m_nIndex[1] ];
+ int p3 = positionIndices[ tri.m_nIndex[2] ];
+
+ int n1 = normalIndices[ tri.m_nIndex[0] ];
+ int n2 = normalIndices[ tri.m_nIndex[1] ];
+ int n3 = normalIndices[ tri.m_nIndex[2] ];
+
+ Vector vecDelta, vecDelta2, vecNormal;
+ VectorSubtract( pPosition[p2], pPosition[p1], vecDelta );
+ VectorSubtract( pPosition[p3], pPosition[p1], vecDelta2 );
+ CrossProduct( vecDelta, vecDelta2, vecNormal );
+ VectorNormalize( vecNormal );
+
+ pNormals[n1] += vecNormal;
+ pNormals[n2] += vecNormal;
+ pNormals[n3] += vecNormal;
+
+ ++pNormalsAdded[n1]; ++pNormalsAdded[n2]; ++pNormalsAdded[n3];
+ }
+
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ if ( pNormalsAdded[i] > 0 )
+ {
+ pNormals[i] /= pNormalsAdded[i];
+ VectorNormalize( pNormals[i] );
+ }
+ else
+ {
+ pNormals[i].Init( 0, 1, 0 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Converts pose-space normals into deltas appropriate for correction delta states
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeCorrectedNormalsFromActualNormals( const CUtlVector<int> &deltaStateList, int nNormalCount, Vector *pNormals )
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return;
+
+ Assert( nNormalCount == pBind->GetNormalData().Count() );
+
+ // Subtract out all other normal contributions
+ Vector *pUncorrectedNormals = (Vector*)_alloca( nNormalCount * sizeof(Vector) );
+ memcpy( pUncorrectedNormals, pBind->GetNormalData().Base(), nNormalCount * sizeof( Vector ) );
+ int nDeltaStateCount = deltaStateList.Count();
+ for ( int i = 0; i < nDeltaStateCount; ++i )
+ {
+ AddDelta( pBind, pUncorrectedNormals, deltaStateList[i], CDmeVertexData::FIELD_NORMAL );
+ }
+
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ pNormals[i] -= pUncorrectedNormals[i];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Copies the corrected normal data into a delta state
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetDeltaNormalData( int nDeltaIndex, int nNormalCount, Vector *pNormals )
+{
+ // pNormals represents the correct normal delta state for this combination
+ // Copy it into the delta state for this combination.
+ // Use tolerance to deal with precision errors introduced by the various computations
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( nDeltaIndex );
+ FieldIndex_t nNormalField = pDeltaState->FindFieldIndex( CDmeVertexDeltaData::FIELD_NORMAL );
+ if ( nNormalField >= 0 )
+ {
+ pDeltaState->RemoveAllVertexData( nNormalField );
+ }
+ else
+ {
+ nNormalField = pDeltaState->CreateField( CDmeVertexDeltaData::FIELD_NORMAL );
+ }
+
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ if ( pNormals[i].LengthSqr() < 1e-4 )
+ continue;
+
+ int nNormalIndex = pDeltaState->AddVertexData( nNormalField, 1 );
+ pDeltaState->SetVertexData( nNormalField, nNormalIndex, 1, AT_VECTOR3, &pNormals[i] );
+ pDeltaState->SetVertexIndices( nNormalField, nNormalIndex, 1, &i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Discovers the atomic controls used by the various delta states
+//-----------------------------------------------------------------------------
+static int DeltaStateUsageLessFunc( const int * lhs, const int * rhs )
+{
+ return *lhs - *rhs;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::BuildAtomicControlLists( int nCount, DeltaComputation_t *pInfo, CUtlVector< CUtlVector< int > > &deltaStateUsage )
+{
+ CUtlVector< CUtlString > atomicControls;
+ deltaStateUsage.SetCount( nCount );
+
+ // Build a list of atomic controls
+ int nCurrentDelta;
+ for ( nCurrentDelta = 0; nCurrentDelta < nCount; ++nCurrentDelta )
+ {
+ if ( pInfo[nCurrentDelta].m_nDimensionality != 1 )
+ break;
+ int j = atomicControls.AddToTail( GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex )->GetName() );
+ deltaStateUsage[ nCurrentDelta ].AddToTail( j );
+ }
+
+ for ( ; nCurrentDelta < nCount; ++nCurrentDelta )
+ {
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex );
+ int nLen = Q_strlen( pDeltaState->GetName() );
+ char *pTempBuf = (char*)_alloca( nLen + 1 );
+ memcpy( pTempBuf, pDeltaState->GetName(), nLen+1 );
+ char *pNext;
+ for ( char *pUnderBar = pTempBuf; pUnderBar; pUnderBar = pNext )
+ {
+ pNext = strchr( pUnderBar, '_' );
+ if ( pNext )
+ {
+ *pNext = 0;
+ ++pNext;
+ }
+
+ // Find this name in the list of strings
+ int j;
+ int nControlCount = atomicControls.Count();
+ for ( j = 0; j < nControlCount; ++j )
+ {
+ if ( !Q_stricmp( pUnderBar, atomicControls[j] ) )
+ break;
+ }
+ if ( j == nControlCount )
+ {
+ j = atomicControls.AddToTail( pUnderBar );
+ }
+ deltaStateUsage[ nCurrentDelta ].AddToTail( j );
+ }
+ deltaStateUsage[ nCurrentDelta ].Sort( DeltaStateUsageLessFunc );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Construct list of all n-1 -> 1 dimensional delta states
+// that will be active when this delta state is active
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDependentDeltaStateList( CUtlVector< DeltaComputation_t > &compList )
+{
+ if ( compList.Count() == 0 )
+ {
+ ComputeDeltaStateComputationList( compList );
+ }
+
+ CUtlVector< CUtlVector< int > > deltaStateUsage;
+ const int nCount( compList.Count() );
+ BuildAtomicControlLists( nCount, compList.Base(), deltaStateUsage );
+
+ // Now build up a list of dependent delta states based on usage
+ // NOTE: Usage is sorted in ascending order.
+ for ( int i = 1; i < nCount; ++i )
+ {
+ int nUsageCount1 = deltaStateUsage[i].Count();
+ for ( int j = 0; j < i; ++j )
+ {
+ // At the point they have the same dimensionality, no more need to check
+ if ( compList[j].m_nDimensionality == compList[i].m_nDimensionality )
+ break;
+
+ int ii = 0;
+ bool bSubsetFound = true;
+ int nUsageCount2 = deltaStateUsage[j].Count();
+ for ( int ji = 0; ji < nUsageCount2; ++ji )
+ {
+ for ( bSubsetFound = false; ii < nUsageCount1; ++ii )
+ {
+ if ( deltaStateUsage[j][ji] == deltaStateUsage[i][ii] )
+ {
+ ++ii;
+ bSubsetFound = true;
+ break;
+ }
+
+ if ( deltaStateUsage[j][ji] < deltaStateUsage[i][ii] )
+ break;
+ }
+
+ if ( !bSubsetFound )
+ break;
+ }
+
+ if ( bSubsetFound )
+ {
+ compList[i].m_DependentDeltas.AddToTail( compList[j].m_nDeltaIndex );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sorts DeltaComputation_t's by dimensionality
+//-----------------------------------------------------------------------------
+int CDmeMesh::DeltaStateLessFunc( const void * lhs, const void * rhs )
+{
+ DeltaComputation_t &info1 = *(DeltaComputation_t*)lhs;
+ DeltaComputation_t &info2 = *(DeltaComputation_t*)rhs;
+ return info1.m_nDimensionality - info2.m_nDimensionality;
+}
+
+
+//-----------------------------------------------------------------------------
+// Generates a sorted list in order of dimensionality of the delta states
+// NOTE: This assumes a naming scheme where delta state names have _ that separate control names
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDeltaStateComputationList( CUtlVector< DeltaComputation_t > &compList )
+{
+ // Do all combinations in order of dimensionality, lowest dimension first
+ const int nCount = DeltaStateCount();
+ compList.EnsureCount( nCount ); // Resets the CUtlVector
+ for ( int i = 0; i < nCount; ++i )
+ {
+ compList[i].m_nDeltaIndex = i;
+ compList[i].m_nDimensionality = ComputeDeltaStateDimensionality( i );
+ }
+ qsort( compList.Base(), nCount, sizeof(DeltaComputation_t), DeltaStateLessFunc );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes normal deltas for all delta states based on position deltas
+// NOTE: This assumes a naming scheme where delta state names have _ that separate control names
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeDeltaStateNormals()
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return;
+
+ const FieldIndex_t nBindNormalIndex = pBind->CreateField( CDmeVertexData::FIELD_NORMAL );
+
+ const CUtlVector< Vector > &basePosData = pBind->GetPositionData();
+ const int nPosCount = basePosData.Count();
+
+ // Build a map from vertex to a list of triangles that share the vert.
+ CUtlVector< Triangle_t > triangles( 0, 1024 );
+ CUtlVector< CUtlVector<int> > vertToTriMap;
+ vertToTriMap.AddMultipleToTail( pBind->VertexCount() );
+
+ const int nFaceSetCount = FaceSetCount();
+ for ( int i = 0; i < nFaceSetCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = GetFaceSet( i );
+ BuildTriangleMap( pBind, pFaceSet, triangles, &vertToTriMap );
+ }
+
+ // Temporary storage for normals
+ Vector *pNormals = reinterpret_cast< Vector * >( alloca( nPosCount * sizeof( Vector ) ) );
+
+ // Make all of the normals in the bind pose smooth
+ {
+ const CUtlVector< int > &basePosIndices = pBind->GetVertexIndexData( CDmeVertexData::FIELD_POSITION );
+
+ pBind->SetVertexIndices( nBindNormalIndex, 0, basePosIndices.Count(), basePosIndices.Base() );
+ pBind->RemoveAllVertexData( nBindNormalIndex );
+ pBind->AddVertexData( nBindNormalIndex, nPosCount );
+
+ ComputeNormalsFromPositions( pBind, basePosData.Base(), triangles, nPosCount, pNormals );
+ pBind->SetVertexData( nBindNormalIndex, 0, nPosCount, AT_VECTOR3, pNormals );
+
+ // Fix up the current state to have smooth normals if current is not bind
+ CDmeVertexData *pCurrent = GetCurrentBaseState();
+ if ( pCurrent != pBind )
+ {
+ const FieldIndex_t nCurrentNormalIndex = pCurrent->CreateField( CDmeVertexData::FIELD_NORMAL );
+ pCurrent->SetVertexIndices( nCurrentNormalIndex, 0, basePosIndices.Count(), basePosIndices.Base() );
+ pCurrent->RemoveAllVertexData( nCurrentNormalIndex );
+ pCurrent->AddVertexData( nCurrentNormalIndex, nPosCount );
+
+ const CUtlVector< Vector > &currPosData = pCurrent->GetPositionData();
+ ComputeNormalsFromPositions( pCurrent, currPosData.Base(), triangles, nPosCount, pNormals );
+ pCurrent->SetVertexData( nCurrentNormalIndex, 0, nPosCount, AT_VECTOR3, pNormals );
+ }
+ }
+
+ // Temporary storage for the positions
+ Vector *pPosData = reinterpret_cast< Vector * >( alloca( nPosCount * sizeof( Vector ) ) );
+
+ // Compute the dependent delta state list like thing
+ CUtlVector< DeltaComputation_t > computationOrder;
+ ComputeDependentDeltaStateList( computationOrder );
+
+ const int nDeltaStateCount = computationOrder.Count();
+ for ( int i = 0; i < nDeltaStateCount; ++i )
+ {
+ const DeltaComputation_t &deltaComputation = computationOrder[ i ];
+
+ memcpy( pPosData, basePosData.Base(), nPosCount * sizeof( Vector ) );
+
+ const CUtlVector< int > &depDeltas = deltaComputation.m_DependentDeltas;
+ const int nDepStateCount = depDeltas.Count();
+ for ( int j = 0; j < nDepStateCount; ++j )
+ {
+ AddDelta( GetDeltaState( depDeltas[ j ] ), pPosData, nPosCount, CDmeVertexData::FIELD_POSITION );
+ }
+
+ AddDelta( GetDeltaState( deltaComputation.m_nDeltaIndex ), pPosData, nPosCount, CDmeVertexData::FIELD_POSITION );
+
+ ComputeNormalsFromPositions( pBind, pPosData, triangles, nPosCount, pNormals );
+
+ SetDeltaNormalDataFromActualNormals( computationOrder[ i ].m_nDeltaIndex, depDeltas, nPosCount, pNormals );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes normal deltas for all delta states based on position deltas
+// NOTE: This assumes a naming scheme where delta state names have _ that separate control names
+//-----------------------------------------------------------------------------
+void CDmeMesh::SetDeltaNormalDataFromActualNormals( int nDeltaIndex, const CUtlVector<int> &deltaStateList, int nNormalCount, Vector *pNormals )
+{
+ // Store off the current state values
+ CUtlVector< Vector2D > deltaStateWeights[MESH_DELTA_WEIGHT_TYPE_COUNT];
+ for ( int i = 0; i < MESH_DELTA_WEIGHT_TYPE_COUNT; ++i )
+ {
+ deltaStateWeights[i] = m_DeltaStateWeights[i].Get();
+
+ // Turn on the current weights to all be 1 to get max effect of morphs
+ int nCount = m_DeltaStateWeights[i].Count();
+ for ( int j = 0; j < nCount; ++j )
+ {
+ m_DeltaStateWeights[i].Set( j, Vector2D( 1.0f, 1.0f ) );
+ }
+ }
+
+ ComputeCorrectedNormalsFromActualNormals( deltaStateList, nNormalCount, pNormals );
+
+ // Finally, store the corrected normals into the delta state
+ SetDeltaNormalData( nDeltaIndex, nNormalCount, pNormals );
+
+ // Restore weights to their current value
+ for ( int i = 0; i < MESH_DELTA_WEIGHT_TYPE_COUNT; ++i )
+ {
+ m_DeltaStateWeights[i] = deltaStateWeights[i];
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// A recursive algorithm to compute nCk, i.e. the number of order independent
+// Combinations without any repeats of k items taking n at a time
+// The size of the returned array is:
+//
+// n!
+// -------------
+// k! ( n - r )!
+//
+// e.g. 4C4 = { 0 1 2 3 }
+// e.g. 3C4 = { 0 1 2 }, { 0 1 3 }, { 0 2 3 }, { 1 2 3 }
+// e.g. 2C4 = { 0 1 }, { 0 2 }, { 0 3 }, { 1 2 }, { 1 3 }, { 2 3 }
+// e.g. 1C4 = { 0 }, { 1 }, { 2 }, { 3 }
+//
+// It's recursive and meant to be called by the user with just n, k and combos
+// the other default arguments are for the recursive steps
+//-----------------------------------------------------------------------------
+void CDmeMesh::Combinations(
+ int n,
+ int k,
+ CUtlVector< CUtlVector< int > > &combos,
+ int *pTmpArray,
+ int start,
+ int currentK )
+{
+ if ( !pTmpArray )
+ {
+ pTmpArray = reinterpret_cast< int * >( alloca( k * sizeof( int ) ) );
+ memset( pTmpArray, 0, k * sizeof( int ) );
+ }
+
+ if ( currentK >= k )
+ {
+ combos[ combos.AddToTail() ].CopyArray( pTmpArray, k );
+ return;
+ }
+
+ for ( int i( start ); i < n; ++i )
+ {
+ pTmpArray[ currentK ] = i;
+
+ Combinations( n, k, combos, pTmpArray, i + 1, currentK + 1 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Takes an incoming Delta state, splits it's name '_' and then finds the
+// control delta (a state without a '_' in its name) and adds the index
+// of that control delta to the referenced array
+//
+// Returns true if all of the control states exist, false otherwise
+//-----------------------------------------------------------------------------
+bool CDmeMesh::GetControlDeltaIndices(
+ CDmeVertexDeltaData *pDeltaState,
+ CUtlVector< int > &controlDeltaIndices ) const
+{
+ Assert( pDeltaState );
+ return GetControlDeltaIndices( pDeltaState->GetName(), controlDeltaIndices );
+}
+
+
+//-----------------------------------------------------------------------------
+// Same as above but just uses the name of a delta
+//-----------------------------------------------------------------------------
+bool CDmeMesh::GetControlDeltaIndices(
+ const char *pDeltaStateName,
+ CUtlVector< int > &controlDeltaIndices ) const
+{
+ Assert( pDeltaStateName );
+ controlDeltaIndices.RemoveAll();
+
+ const int nDeltaStateName( Q_strlen( pDeltaStateName ) );
+ char *pTmpBuf( reinterpret_cast< char * >( alloca( nDeltaStateName + 1 ) ) );
+ Q_strncpy( pTmpBuf, pDeltaStateName, nDeltaStateName + 1 );
+ char *pNext;
+ for ( char *pCurr = pTmpBuf; pCurr; pCurr = pNext )
+ {
+ pNext = strchr( pCurr, '_' );
+ if ( pNext )
+ {
+ *pNext = '\0';
+ ++pNext;
+ }
+
+ if ( Q_strlen( pCurr ) )
+ {
+ const int controlDeltaIndex( FindDeltaStateIndex( pCurr ) );
+ if ( controlDeltaIndex >= 0 )
+ {
+ controlDeltaIndices.AddToTail( controlDeltaIndex );
+ }
+ else
+ {
+ controlDeltaIndices.RemoveAll();
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a list of all of the underlying control delta indices for each
+// delta state in the mesh
+//
+// e.g. Say the delta states are (in this order): A, B, C, A_C, A_B_C
+//
+// Will build: {
+// { 0 },
+// { 1 },
+// { 2 },
+// { 0, 2 },
+// { 0, 1, 2 }
+// }
+//
+// Returns true if all of the control states exist, false otherwise
+//-----------------------------------------------------------------------------
+bool CDmeMesh::BuildCompleteDeltaStateControlList(
+ CUtlVector< CUtlVector< int > > &deltaStateControlList ) const
+{
+ deltaStateControlList.RemoveAll();
+
+ CUtlVector< int > tmpControlDeltaIndices;
+
+ const int nDeltas( m_DeltaStates.Count() );
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ if ( !GetControlDeltaIndices( m_DeltaStates[ i ], tmpControlDeltaIndices ) )
+ return false;
+
+ deltaStateControlList[ deltaStateControlList.AddToTail() ].CopyArray( tmpControlDeltaIndices.Base(), tmpControlDeltaIndices.Count() );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Searches controlList for a sub array that has exactly the same indices as
+// controlIndices. The order of the indices do not have to match but all of
+// them must be present and no extras can be present.
+// It assumes that the controlList is in the same order as m_deltaStates
+//-----------------------------------------------------------------------------
+int CDmeMesh::FindDeltaIndexFromControlIndices(
+ const CUtlVector< int > &controlIndices,
+ const CUtlVector< CUtlVector< int > > &controlList ) const
+{
+ const int nControlIndices( controlIndices.Count() );
+ const int nControlList( controlList.Count() );
+
+ int nControlListIndices;
+ int foundCount;
+
+ for ( int i = 0; i < nControlList; ++i )
+ {
+ const CUtlVector< int > &controlListIndices( controlList[ i ] );
+ nControlListIndices = controlListIndices.Count();
+ if ( nControlListIndices == nControlIndices )
+ {
+ foundCount = 0;
+
+ for ( int j( 0 ); j < nControlListIndices; ++j )
+ {
+ for ( int k( 0 ); k < nControlIndices; ++k )
+ {
+ if ( controlListIndices[ j ] == controlIndices[ k ] )
+ {
+ ++foundCount;
+ break;
+ }
+ }
+ }
+
+ if ( foundCount == nControlIndices )
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a list of all of the required underlying deltas that make up this
+// state whether that do not exist. All of the control deltas must exist
+// though (Deltas without '_' in their name).
+//
+// e.g. Say only Delta states A, B, C, D, A_B_C_D exist and A_B_C_D is
+// passed in. This function will return:
+//
+// A_B_C, A_B_D, A_C_D, B_C_D, A_B, A_C, A_D, B_C, B_D, C_D
+//
+// Returns true if all of the control states exist, false otherwise
+//-----------------------------------------------------------------------------
+bool CDmeMesh::BuildMissingDependentDeltaList(
+ CDmeVertexDeltaData *pDeltaState,
+ CUtlVector< int > &controlIndices,
+ CUtlVector< CUtlVector< int > > &dependentStates ) const
+{
+ dependentStates.RemoveAll();
+
+ CUtlVector< CUtlVector< int > > deltaStateControlList;
+ BuildCompleteDeltaStateControlList( deltaStateControlList );
+
+ if ( !GetControlDeltaIndices( pDeltaState, controlIndices ) )
+ return false;
+
+ const int nControlIndices( controlIndices.Count() );
+
+ CUtlVector< int > comboControls;
+
+ for ( int i( nControlIndices - 1 ); i > 0; --i )
+ {
+ CUtlVector< CUtlVector< int > > combos;
+ Combinations( nControlIndices, i, combos );
+ const int nCombos( combos.Count() );
+ for ( int j( 0 ); j < nCombos; ++j )
+ {
+ const CUtlVector< int > &comboIndices( combos[ j ] );
+ const int nComboIndices( comboIndices.Count() );
+ if ( comboIndices.Count() )
+ {
+ comboControls.RemoveAll();
+ comboControls.EnsureCapacity( nComboIndices );
+
+ for ( int k( 0 ); k < nComboIndices; ++k )
+ {
+ comboControls.AddToTail( controlIndices[ comboIndices[ k ] ] );
+ }
+
+ if ( FindDeltaIndexFromControlIndices( comboControls, deltaStateControlList) < 0 )
+ {
+ dependentStates[ dependentStates.AddToTail() ].CopyArray( comboControls.Base(), comboControls.Count() );
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+int CDmeMesh::GenerateCompleteDataForDelta(
+ const CDmeVertexDeltaData *pDelta,
+ T_t *pFullData,
+ int nFullData,
+ CDmeVertexData::StandardFields_t standardField )
+{
+ memset( pFullData, 0, nFullData * sizeof( T_t ) );
+
+ const FieldIndex_t fIndex( pDelta->FindFieldIndex( standardField ) );
+ if ( fIndex >= 0 )
+ {
+ CDmrArrayConst< T_t > fDataArray( pDelta->GetVertexData( fIndex ) );
+ const CUtlVector< T_t > &fData( fDataArray.Get() );
+ const CUtlVector< int > &fIndexData( pDelta->GetVertexIndexData( fIndex ) );
+ const int nIndexData( fIndexData.Count() );
+
+ Assert( nIndexData <= nFullData );
+
+ int index;
+
+ int i( 0 );
+
+ for ( int j( 0 ); j < nIndexData; ++j )
+ {
+ index = fIndexData[ j ];
+ while ( index > i )
+ {
+ ++i;
+ }
+
+ Assert( i < nFullData );
+ pFullData[ i ] = fData[ j ];
+ }
+
+ return nIndexData;
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddDelta(
+ const CDmeVertexDeltaData *pDelta,
+ T_t *pFullData,
+ int nFullData,
+ FieldIndex_t fieldIndex,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ if ( fieldIndex >= 0 )
+ {
+ CDmrArrayConst< T_t > fDataArray( pDelta->GetVertexData( fieldIndex ) );
+ const CUtlVector< T_t > &fData( fDataArray.Get() );
+ const CUtlVector< int > &fIndexData( pDelta->GetVertexIndexData( fieldIndex ) );
+ const int nIndexData( fIndexData.Count() );
+
+ T_t t;
+
+ Assert( nIndexData <= nFullData );
+
+ int index;
+
+ int i( 0 );
+
+ if ( pMask )
+ {
+ float cWeight;
+
+ for ( int j( 0 ); j < nIndexData; ++j )
+ {
+ index = fIndexData[ j ];
+
+ if ( !pMask->GetWeight( index, cWeight ) )
+ continue;
+
+ while ( index > i )
+ {
+ ++i;
+ }
+
+ Assert( i < nFullData );
+
+ t = fData[ j ];
+ t *= ( weight * cWeight );
+ pFullData[ i ] += t;
+ }
+ }
+ else
+ {
+ for ( int j( 0 ); j < nIndexData; ++j )
+ {
+ index = fIndexData[ j ];
+ while ( index > i )
+ {
+ ++i;
+ }
+
+ Assert( i < nFullData );
+ t = fData[ j ];
+ t *= weight;
+ pFullData[ i ] += t;
+ }
+ }
+ }
+}
+
+template void CDmeMesh::AddDelta< float >( const CDmeVertexDeltaData *, float *, int, FieldIndex_t, float, const CDmeSingleIndexedComponent * );
+template void CDmeMesh::AddDelta< Vector2D >( const CDmeVertexDeltaData *, Vector2D *, int, FieldIndex_t, float, const CDmeSingleIndexedComponent * );
+template void CDmeMesh::AddDelta< Vector >( const CDmeVertexDeltaData *, Vector *, int, FieldIndex_t, float, const CDmeSingleIndexedComponent * );
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddDelta(
+ const CDmeVertexDeltaData *pDelta,
+ T_t *pFullData,
+ int nFullData,
+ CDmeVertexData::StandardFields_t standardField,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ const FieldIndex_t fIndex( pDelta->FindFieldIndex( standardField ) );
+ AddDelta( pDelta, pFullData, nFullData, fIndex, weight, pMask );
+}
+
+template void CDmeMesh::AddDelta< float >( const CDmeVertexDeltaData *, float *, int, CDmeVertexData::StandardFields_t, float, const CDmeSingleIndexedComponent * );
+template void CDmeMesh::AddDelta< Vector2D >( const CDmeVertexDeltaData *, Vector2D *, int, CDmeVertexData::StandardFields_t, float, const CDmeSingleIndexedComponent * );
+template void CDmeMesh::AddDelta< Vector >( const CDmeVertexDeltaData *, Vector *, int, CDmeVertexData::StandardFields_t, float, const CDmeSingleIndexedComponent * );
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::ComputeAllCorrectedPositionsFromActualPositions()
+{
+ const CDmeVertexData *pBase = GetBindBaseState();
+ if ( !pBase )
+ return;
+
+ CUtlVector< DeltaComputation_t > deltaList;
+ ComputeDependentDeltaStateList( deltaList );
+
+ const int nDeltas( deltaList.Count() );
+
+ const int nPositions( pBase->GetPositionData().Count() );
+
+ Vector *pPositions( reinterpret_cast< Vector * >( alloca( nPositions * sizeof( Vector ) ) ) );
+ int *pIndices( reinterpret_cast< int * >( alloca( nPositions * sizeof( int ) ) ) );
+
+ int pCount;
+
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ const DeltaComputation_t &deltaComputation( deltaList[ i ] );
+ CDmeVertexDeltaData *pDelta( m_DeltaStates[ deltaComputation.m_nDeltaIndex ] );
+ if ( !pDelta->GetValue< bool >( "corrected" ) )
+ {
+ const FieldIndex_t pIndex( pDelta->FindFieldIndex( CDmeVertexDeltaData::FIELD_POSITION ) );
+ if ( pIndex < 0 )
+ continue;
+
+ GenerateCompleteDataForDelta( pDelta, pPositions, nPositions, CDmeVertexData::FIELD_POSITION );
+
+ const CUtlVector< int > &dependentDeltas( deltaComputation.m_DependentDeltas );
+ const int nDependentDeltas( dependentDeltas.Count() );
+ for ( int j( 0 ); j < nDependentDeltas; ++j )
+ {
+ const CDmeVertexDeltaData *pDependentDelta( m_DeltaStates[ dependentDeltas[ j ] ] );
+ const CUtlVector< Vector > &dPositions( pDependentDelta->GetPositionData() );
+ const CUtlVector<int> &dIndices( pDependentDelta->GetVertexIndexData( CDmeVertexData::FIELD_POSITION ) );
+ Assert( dPositions.Count() == dIndices.Count() );
+ const int nIndices( dIndices.Count() );
+
+ int index;
+
+ int k( 0 );
+ for ( int l( 0 ); l < nIndices; ++l )
+ {
+ index = dIndices[ l ];
+ while ( index > k )
+ {
+ ++k;
+ }
+
+ Assert( k < nPositions );
+ pPositions[ k ] -= dPositions[ l ];
+ }
+ }
+
+ pCount = 0;
+ for ( int j( 0 ); j < nPositions; ++j )
+ {
+ const Vector &v( pPositions[ j ] );
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
+ {
+ pPositions[ pCount ] = v;
+ pIndices[ pCount ] = j;
+ ++pCount;
+ }
+ }
+
+ pDelta->RemoveAllVertexData( pIndex );
+
+ if ( pCount )
+ {
+ pDelta->AddVertexData( pIndex, pCount );
+ pDelta->SetVertexData( pIndex, 0, pCount, AT_VECTOR3, pPositions );
+ pDelta->SetVertexIndices( pIndex, 0, pCount, pIndices );
+ }
+ pDelta->SetValue( "corrected", true );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// There's no guarantee that fields are added in any order, nor that only
+// standard fields exist...
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddCorrectedDelta(
+ CDmrArray< T_t > &baseDataArray,
+ const CUtlVector< int > &baseIndices,
+ const DeltaComputation_t &deltaComputation,
+ const char *pFieldName,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ const CUtlVector< T_t > &baseData( baseDataArray.Get() );
+ const int nData( baseData.Count() );
+ T_t *pData( reinterpret_cast< T_t * >( alloca( nData * sizeof( T_t ) ) ) );
+ Q_memcpy( pData, baseData.Base(), nData * sizeof( T_t ) );
+
+ CDmeVertexDeltaData *pDelta( GetDeltaState( deltaComputation.m_nDeltaIndex ) );
+
+ const int deltaFieldIndex( pDelta->FindFieldIndex( pFieldName ) );
+ if ( deltaFieldIndex < 0 )
+ return;
+
+ AddDelta( pDelta, pData, nData, deltaFieldIndex, weight, pMask );
+
+ const CUtlVector< int > &depDeltas( deltaComputation.m_DependentDeltas );
+ const int nDepDeltas( depDeltas.Count() );
+ for ( int j( 0 ); j < nDepDeltas; ++j )
+ {
+ pDelta = GetDeltaState( depDeltas[ j ] );
+
+ int depFieldIndex = pDelta->FindFieldIndex( pFieldName );
+ if ( depFieldIndex < 0 )
+ continue;
+
+ AddDelta( pDelta, pData, nData, depFieldIndex, weight, pMask );
+ }
+
+ baseDataArray.CopyArray( pData, nData );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddCorrectedDelta(
+ CUtlVector< T_t > &baseData,
+ const CUtlVector< int > &baseIndices,
+ const DeltaComputation_t &deltaComputation,
+ const char *pFieldName,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ const int nData( baseData.Count() );
+
+ CDmeVertexDeltaData *pDelta( GetDeltaState( deltaComputation.m_nDeltaIndex ) );
+
+ const int deltaFieldIndex( pDelta->FindFieldIndex( pFieldName ) );
+ if ( deltaFieldIndex < 0 )
+ return;
+
+ AddDelta( pDelta, baseData.Base(), nData, deltaFieldIndex, weight, pMask );
+
+ const CUtlVector< int > &depDeltas( deltaComputation.m_DependentDeltas );
+ const int nDepDeltas( depDeltas.Count() );
+ for ( int j( 0 ); j < nDepDeltas; ++j )
+ {
+ pDelta = GetDeltaState( depDeltas[ j ] );
+
+ int depFieldIndex = pDelta->FindFieldIndex( pFieldName );
+ if ( depFieldIndex < 0 )
+ continue;
+
+ AddDelta( pDelta, baseData.Base(), nData, depFieldIndex, weight, pMask );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// There's no guarantee that fields are added in any order, nor that only
+// standard fields exist...
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddRawDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmrArray< T_t > &baseDataArray,
+ FieldIndex_t nDeltaFieldIndex,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ if ( !pDelta || nDeltaFieldIndex < 0 )
+ return;
+
+ const CUtlVector< T_t > &baseData( baseDataArray.Get() );
+ const int nData( baseData.Count() );
+ T_t *pData( reinterpret_cast< T_t * >( alloca( nData * sizeof( T_t ) ) ) );
+ Q_memcpy( pData, baseData.Base(), nData * sizeof( T_t ) );
+
+ AddDelta( pDelta, pData, nData, nDeltaFieldIndex, weight, pMask );
+
+ baseDataArray.CopyArray( pData, nData );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CDmeMesh::AddRawDelta(
+ CDmeVertexDeltaData *pDelta,
+ CUtlVector< T_t > &baseData,
+ FieldIndex_t nDeltaFieldIndex,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask )
+{
+ if ( !pDelta || nDeltaFieldIndex < 0 )
+ return;
+
+ const int nData( baseData.Count() );
+
+ AddDelta( pDelta, baseData.Base(), nData, nDeltaFieldIndex, weight, pMask );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the specified base state to the specified delta
+// If no delta is specified then the current state is copied from the bind state
+// If no base state is specified then the current base state is used
+// The specified base state or the current base state cannot be the bind state
+//-----------------------------------------------------------------------------
+bool CDmeMesh::SetBaseStateToDelta( const CDmeVertexDeltaData *pDelta, CDmeVertexData *pPassedBase /* = NULL */ )
+{
+ CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ const CDmeVertexData *pBind = GetBindBaseState();
+
+ if ( !pBase || !pBind || pBase == pBind )
+ return false;
+
+ pBind->CopyTo( pBase );
+
+ if ( !pDelta )
+ return true;
+
+ // This should be cached and recomputed only when states are added
+ CUtlVector< DeltaComputation_t > compList;
+ ComputeDependentDeltaStateList( compList );
+
+ const int nDeltas( compList.Count() );
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ if ( pDelta != GetDeltaState( compList[ i ].m_nDeltaIndex ) )
+ continue;
+
+ const int nBaseField( pBase->FieldCount() );
+ const int nDeltaField( pDelta->FieldCount() );
+
+ for ( int j( 0 ); j < nBaseField; ++j )
+ {
+ const CUtlString &baseFieldName( pBase->FieldName( j ) );
+
+ for ( int k( 0 ); k < nDeltaField; ++k )
+ {
+ const CUtlString &deltaFieldName( pDelta->FieldName( k ) );
+
+ if ( baseFieldName != deltaFieldName )
+ continue;
+
+ const FieldIndex_t baseFieldIndex( pBase->FindFieldIndex( baseFieldName ) );
+ const FieldIndex_t deltaFieldIndex( pDelta->FindFieldIndex( deltaFieldName ) );
+ if ( baseFieldIndex < 0 || deltaFieldIndex < 0 )
+ break;
+
+ CDmAttribute *pBaseData( pBase->GetVertexData( baseFieldIndex ) );
+ const CDmAttribute *pDeltaData( pDelta->GetVertexData( deltaFieldIndex ) );
+
+ if ( pBaseData->GetType() != pDeltaData->GetType() )
+ break;
+
+ const CUtlVector< int > &baseIndices( pBase->GetVertexIndexData( baseFieldIndex ) );
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ AddCorrectedDelta( CDmrArray< float >( pBaseData ), baseIndices, compList[ i ], baseFieldName );
+ break;
+ case AT_COLOR_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector >( pBaseData ), baseIndices, compList[ i ], baseFieldName );
+ break;
+ case AT_VECTOR2_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector2D >( pBaseData ), baseIndices, compList[ i ], baseFieldName );
+ break;
+ case AT_VECTOR3_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector >( pBaseData ), baseIndices, compList[ i ], baseFieldName );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SelectVerticesFromDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmeSingleIndexedComponent *pSelection )
+{
+ if ( !pSelection )
+ return;
+
+ pSelection->Clear();
+
+ if ( !pDelta )
+ return;
+
+ const FieldIndex_t pField( pDelta->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( pField < 0 )
+ return;
+
+ const CUtlVector< int > &pIndicies( pDelta->GetVertexIndexData( CDmeVertexData::FIELD_POSITION ) );
+
+ pSelection->AddComponents( pIndicies );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SelectAllVertices( CDmeSingleIndexedComponent *pSelection, CDmeVertexData *pPassedBase /* = NULL */ )
+{
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+
+ if ( !pBase )
+ {
+ pBase = GetBindBaseState();
+ }
+
+ if ( !pBase )
+ return;
+
+ if ( !pSelection )
+ return;
+
+ pSelection->Clear();
+
+ const FieldIndex_t pField( pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( pField < 0 )
+ return;
+
+ CUtlVector< int > indices;
+ indices.EnsureCount( CDmrArrayConst< Vector >( pBase->GetVertexData( pField ) ).Count() );
+ const int nIndices = indices.Count();
+ for ( int i = 0; i < nIndices; ++i )
+ {
+ indices[ i ] = i;
+ }
+
+ pSelection->AddComponents( indices );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::SelectHalfVertices( SelectHalfType_t selectHalfType, CDmeSingleIndexedComponent *pSelection, CDmeVertexData *pPassedBase /* = NULL */ )
+{
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+
+ if ( !pBase )
+ {
+ pBase = GetBindBaseState();
+ }
+
+ if ( !pBase )
+ return;
+
+ if ( !pSelection )
+ return;
+
+ pSelection->Clear();
+
+ const FieldIndex_t pField( pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( pField < 0 )
+ return;
+
+ const CDmrArrayConst< Vector > pos( pBase->GetVertexData( pField ) );
+ const int nPosCount = pos.Count();
+
+ CUtlVector< int > indices;
+ indices.EnsureCapacity( nPosCount );
+
+ if ( selectHalfType == kRight )
+ {
+ for ( int i = 0; i < nPosCount; ++i )
+ {
+ if ( pos[ i ].x <= 0.0f )
+ {
+ indices.AddToTail( i );
+ }
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nPosCount; ++i )
+ {
+ if ( pos[ i ].x >= 0.0f )
+ {
+ indices.AddToTail( i );
+ }
+ }
+ }
+
+ pSelection->AddComponents( indices );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeMesh::CreateDeltaFieldFromBaseField(
+ CDmeVertexData::StandardFields_t nStandardFieldIndex,
+ const CDmrArrayConst< float > &baseArray,
+ const CDmrArrayConst< float > &bindArray,
+ CDmeVertexDeltaData *pDelta )
+{
+ const int nData( baseArray.Count() );
+ if ( nData != bindArray.Count() )
+ return false;
+
+ const float *pBaseData( baseArray.Get().Base() );
+ const float *pBindData( bindArray.Get().Base() );
+
+ float *pData( reinterpret_cast< float * >( nData * sizeof( float ) ) );
+ Q_memcpy( pData, pBaseData, nData * sizeof( float ) );
+ int *pIndices( reinterpret_cast< int * >( nData * sizeof( int ) ) );
+
+ float v;
+
+ int nDeltaCount( 0 );
+ for ( int i = 0; i < nData; ++i )
+ {
+ v = pBaseData[ i ] - pBindData[ i ];
+
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v ) >= ( 1 / 4096.0f ) )
+ {
+ pData[ nDeltaCount ] = v;
+ pIndices[ nDeltaCount ] = i;
+ ++nDeltaCount;
+ }
+ }
+
+ if ( nDeltaCount <= 0 )
+ return true;
+
+ FieldIndex_t fieldIndex( pDelta->CreateField( nStandardFieldIndex ) );
+ if ( fieldIndex < 0 )
+ return false;
+
+ pDelta->AddVertexData( fieldIndex, nDeltaCount );
+ pDelta->SetVertexData( fieldIndex, 0, nDeltaCount, AT_FLOAT, pData );
+ pDelta->SetVertexIndices( fieldIndex, 0, nDeltaCount, pIndices );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeMesh::CreateDeltaFieldFromBaseField(
+ CDmeVertexData::StandardFields_t nStandardFieldIndex,
+ const CDmrArrayConst< Vector2D > &baseArray,
+ const CDmrArrayConst< Vector2D > &bindArray,
+ CDmeVertexDeltaData *pDelta )
+{
+ const int nData( baseArray.Count() );
+ if ( nData != bindArray.Count() )
+ return false;
+
+ const Vector2D *pBaseData( baseArray.Get().Base() );
+ const Vector2D *pBindData( bindArray.Get().Base() );
+
+ Vector2D *pData( reinterpret_cast< Vector2D * >( nData * sizeof( Vector2D ) ) );
+ Q_memcpy( pData, pBaseData, nData * sizeof( Vector2D ) );
+ int *pIndices( reinterpret_cast< int * >( nData * sizeof( int ) ) );
+
+ Vector2D v;
+
+ int nDeltaCount( 0 );
+ for ( int i = 0; i < nData; ++i )
+ {
+ v = pBaseData[ i ] - pBindData[ i ];
+
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) )
+ {
+ pData[ nDeltaCount ] = v;
+ pIndices[ nDeltaCount ] = i;
+ ++nDeltaCount;
+ }
+ }
+
+ if ( nDeltaCount <= 0 )
+ return true;
+
+ FieldIndex_t fieldIndex( pDelta->CreateField( nStandardFieldIndex ) );
+ if ( fieldIndex < 0 )
+ return false;
+
+ pDelta->AddVertexData( fieldIndex, nDeltaCount );
+ pDelta->SetVertexData( fieldIndex, 0, nDeltaCount, AT_VECTOR2, pData );
+ pDelta->SetVertexIndices( fieldIndex, 0, nDeltaCount, pIndices );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeMesh::CreateDeltaFieldFromBaseField(
+ CDmeVertexData::StandardFields_t nStandardFieldIndex,
+ const CDmrArrayConst< Vector > &baseArray,
+ const CDmrArrayConst< Vector > &bindArray,
+ CDmeVertexDeltaData *pDelta )
+{
+ const int nData( baseArray.Count() );
+ if ( nData != bindArray.Count() )
+ return false;
+
+ const Vector *pBaseData( baseArray.Get().Base() );
+ const Vector *pBindData( bindArray.Get().Base() );
+
+ Vector *pData( reinterpret_cast< Vector * >( alloca( nData * sizeof( Vector ) ) ) );
+ Q_memcpy( pData, pBaseData, nData * sizeof( Vector ) );
+ int *pIndices( reinterpret_cast< int * >( alloca( nData * sizeof( int ) ) ) );
+
+ Vector v;
+
+ int nDeltaCount( 0 );
+ for ( int i = 0; i < nData; ++i )
+ {
+ v = pBaseData[ i ] - pBindData[ i ];
+
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
+ {
+ pData[ nDeltaCount ] = v;
+ pIndices[ nDeltaCount ] = i;
+ ++nDeltaCount;
+ }
+ }
+
+ if ( nDeltaCount <= 0 )
+ return true;
+
+ FieldIndex_t fieldIndex( pDelta->CreateField( nStandardFieldIndex ) );
+ if ( fieldIndex < 0 )
+ return false;
+
+ pDelta->AddVertexData( fieldIndex, nDeltaCount );
+ pDelta->SetVertexData( fieldIndex, 0, nDeltaCount, AT_VECTOR3, pData );
+ pDelta->SetVertexIndices( fieldIndex, 0, nDeltaCount, pIndices );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Creates a delta from the difference between the bind base state and the
+// specified base state. If pBaseName is NULL the current base state is used
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmeMesh::ModifyOrCreateDeltaStateFromBaseState( const char *pDeltaName, CDmeVertexData *pPassedBase /* = NULL */, bool absolute /* = false */ )
+{
+ // Find All States Which Have This Guy
+ CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return NULL;
+
+ CDmeVertexData *pBind = GetBindBaseState();
+ if ( !pBind )
+ return NULL;
+
+ // It's ok if pBase == pBind
+
+ CUtlVector< int > superiorDeltaStates;
+ ComputeSuperiorDeltaStateList( pDeltaName, superiorDeltaStates );
+ const int nSuperior = superiorDeltaStates.Count();
+
+ if ( nSuperior > 0 )
+ {
+ UniqueId_t id;
+ char idBuf[ MAX_PATH ];
+
+ CDmeVertexData *pTmpBaseState = NULL;
+ do
+ {
+ CreateUniqueId( &id );
+ UniqueIdToString( id, idBuf, sizeof( idBuf ) );
+ pTmpBaseState = FindBaseState( idBuf );
+ } while( pTmpBaseState != NULL );
+
+ pTmpBaseState = FindOrCreateBaseState( idBuf );
+ if ( !pTmpBaseState )
+ return NULL;
+
+ for ( int i = 0; i < nSuperior; ++i )
+ {
+ Assert( superiorDeltaStates[ i ] < DeltaStateCount() );
+ CDmeVertexDeltaData *pSuperiorDelta = GetDeltaState( superiorDeltaStates[ i ] );
+ if ( pSuperiorDelta->GetValue< bool >( "corrected" ) )
+ {
+ // Only fiddle with states that are "corrected"
+ if ( !SetBaseStateToDelta( pSuperiorDelta, pTmpBaseState ) )
+ return NULL;
+
+ if ( !ModifyOrCreateDeltaStateFromBaseState( CUtlString( pSuperiorDelta->GetName() ), pTmpBaseState, true ) )
+ return NULL;
+ }
+ }
+
+ DeleteBaseState( idBuf );
+ }
+
+ ResetDeltaState( pDeltaName );
+ CDmeVertexDeltaData *pDelta = FindOrCreateDeltaState( pDeltaName );
+ if ( !pDelta )
+ return NULL;
+
+ CDmeVertexData::StandardFields_t deltaFields[] =
+ {
+ CDmeVertexData::FIELD_POSITION,
+ CDmeVertexData::FIELD_NORMAL,
+ CDmeVertexData::FIELD_WRINKLE
+ };
+
+ for ( int i = 0; i < sizeof( deltaFields ) / sizeof( deltaFields[ 0 ] ); ++i )
+ {
+ CDmeVertexData::StandardFields_t standardFieldIndex( deltaFields[ i ] );
+ const FieldIndex_t baseFieldIndex( pBase->FindFieldIndex( standardFieldIndex ) );
+ const FieldIndex_t bindFieldIndex( pBind->FindFieldIndex( standardFieldIndex ) );
+
+ if ( baseFieldIndex < 0 || bindFieldIndex < 0 )
+ continue;
+
+ CDmAttribute *pBaseData( pBase->GetVertexData( baseFieldIndex ) );
+ CDmAttribute *pBindData( pBind->GetVertexData( bindFieldIndex ) );
+
+ if ( pBaseData->GetType() != pBindData->GetType() )
+ continue;
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ CreateDeltaFieldFromBaseField( standardFieldIndex, CDmrArrayConst< float >( pBaseData ), CDmrArrayConst< float >( pBindData ), pDelta );
+ break;
+ case AT_COLOR_ARRAY:
+ CreateDeltaFieldFromBaseField( standardFieldIndex, CDmrArrayConst< Vector >( pBaseData ), CDmrArrayConst< Vector >( pBindData ), pDelta );
+ break;
+ case AT_VECTOR2_ARRAY:
+ CreateDeltaFieldFromBaseField( standardFieldIndex, CDmrArrayConst< Vector2D >( pBaseData ), CDmrArrayConst< Vector2D >( pBindData ), pDelta );
+ break;
+ case AT_VECTOR3_ARRAY:
+ CreateDeltaFieldFromBaseField( standardFieldIndex, CDmrArrayConst< Vector >( pBaseData ), CDmrArrayConst< Vector >( pBindData ), pDelta );
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ( !strchr( pDelta->GetName(), '_' ) )
+ {
+ const static UtlSymId_t symTargets = g_pDataModel->GetSymbol( "targets" );
+ CDmeCombinationOperator *pCombo( FindReferringElement< CDmeCombinationOperator >( this, symTargets ) );
+ if ( pCombo )
+ {
+ pCombo->FindOrCreateControl( pDelta->GetName(), false, true );
+ }
+ }
+
+ if ( !absolute )
+ {
+ ComputeAllCorrectedPositionsFromActualPositions();
+ }
+
+ return pDelta;
+}
+
+
+//-----------------------------------------------------------------------------
+// TODO: Uncorrect all superior states and then correct them afterwards
+//-----------------------------------------------------------------------------
+bool CDmeMesh::DeleteDeltaState( const char *pDeltaName )
+{
+ const int nDeltaIndex = FindDeltaStateIndex( pDeltaName );
+ if ( nDeltaIndex < 0 )
+ return false;
+
+ Assert( m_DeltaStates.Count() == m_DeltaStateWeights[ MESH_DELTA_WEIGHT_NORMAL ].Count() );
+ Assert( m_DeltaStates.Count() == m_DeltaStateWeights[ MESH_DELTA_WEIGHT_LAGGED ].Count() );
+ CDmeVertexDeltaData *pDelta( m_DeltaStates[ nDeltaIndex ] );
+ if ( !pDelta )
+ return false;
+
+ m_DeltaStates.Remove( nDeltaIndex );
+ m_DeltaStateWeights[ MESH_DELTA_WEIGHT_NORMAL ].Remove( nDeltaIndex );
+ m_DeltaStateWeights[ MESH_DELTA_WEIGHT_LAGGED ].Remove( nDeltaIndex );
+ g_pDataModel->DestroyElement( pDelta->GetHandle() );
+
+ const static UtlSymId_t symTargets = g_pDataModel->GetSymbol( "targets" );
+ CDmeCombinationOperator *pCombo( FindReferringElement< CDmeCombinationOperator >( this, symTargets ) );
+ if ( pCombo )
+ {
+ pCombo->Purge();
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// TODO: Uncorrect all superior states and then correct them afterwards
+//-----------------------------------------------------------------------------
+bool CDmeMesh::ResetDeltaState( const char *pDeltaName )
+{
+ const int nDeltaIndex = FindDeltaStateIndex( pDeltaName );
+ if ( nDeltaIndex < 0 )
+ return false;
+
+ CDmeVertexDeltaData *pOldDelta = m_DeltaStates[ nDeltaIndex ];
+ CDmeVertexDeltaData *pNewDelta = CreateElement< CDmeVertexDeltaData >( pOldDelta->GetName(), GetFileId() );
+ if ( !pNewDelta )
+ return false;
+
+ m_DeltaStates.Set( nDeltaIndex, pNewDelta );
+ g_pDataModel->DestroyElement( pOldDelta->GetHandle() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+class CSelectionHelper
+{
+public:
+ class CVert
+ {
+ public:
+ int m_index;
+ int m_count;
+ float m_weight;
+ };
+
+ void AddVert( int vIndex, float weight = 1.0f );
+
+ int AddToSelection( CDmeSingleIndexedComponent *pSelection ) const;
+
+ int RemoveFromSelection( CDmeSingleIndexedComponent *pSelection, bool bAllowEmpty ) const;
+
+protected:
+ CUtlVector< CVert > m_verts;
+
+ int BinarySearch( int component ) const;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CSelectionHelper::AddVert( int vIndex, float weight /* = 1.0f */ )
+{
+ // Find the vertex, add it if necessary
+ const int index = BinarySearch( vIndex );
+
+ if ( index == m_verts.Count() )
+ {
+ // New Add to end
+ CVert &v( m_verts[ m_verts.AddToTail() ] );
+ v.m_index = vIndex;
+ v.m_count = 1;
+ v.m_weight = weight;
+ }
+ else if ( vIndex == m_verts[ index ].m_index )
+ {
+ // Existing, increment
+ CVert &v( m_verts[ index ] );
+ Assert( v.m_index == vIndex );
+ v.m_count += 1;
+ v.m_weight += weight;
+ }
+ else
+ {
+ // New insert before index
+ CVert &v( m_verts[ m_verts.InsertBefore( index ) ] );
+ v.m_index = vIndex;
+ v.m_count = 1;
+ v.m_weight = weight;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CSelectionHelper::AddToSelection( CDmeSingleIndexedComponent *pSelection ) const
+{
+ const int nVerts = m_verts.Count();
+
+ for ( int i = 0; i < nVerts; ++i )
+ {
+ const CVert &v( m_verts[ i ] );
+ Assert( !pSelection->HasComponent( v.m_index ) );
+ pSelection->AddComponent( v.m_index, v.m_weight / static_cast< float >( v.m_count ) );
+ }
+
+ return nVerts;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CSelectionHelper::RemoveFromSelection( CDmeSingleIndexedComponent *pSelection, bool bAllowEmpty ) const
+{
+ const int nVerts = m_verts.Count();
+ int nVertsRemovedCount = 0;
+
+ for ( int i = 0; i < nVerts; ++i )
+ {
+ const CVert &v( m_verts[ i ] );
+ if ( bAllowEmpty || pSelection->Count() > 1 )
+ {
+ pSelection->RemoveComponent( v.m_index );
+ ++nVertsRemovedCount;
+ }
+ }
+
+ return nVertsRemovedCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Searches for the component in the sorted component list and returns the
+// index if it's found or if it's not found, returns the index at which it
+// should be inserted to maintain the sorted order of the component list
+//-----------------------------------------------------------------------------
+int CSelectionHelper::BinarySearch( int vIndex ) const
+{
+ const int nVerts( m_verts.Count() );
+
+ int left( 0 );
+ int right( nVerts - 1 );
+ int mid;
+
+ while ( left <= right )
+ {
+ mid = ( left + right ) >> 1; // floor( ( left + right ) / 2.0 )
+ if ( vIndex > m_verts[ mid ].m_index )
+ {
+ left = mid + 1;
+ }
+ else if ( vIndex < m_verts[ mid ].m_index )
+ {
+ right = mid - 1;
+ }
+ else
+ {
+ return mid;
+ }
+ }
+
+ return left;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::GrowSelection( int nSize, CDmeSingleIndexedComponent *pSelection, CDmMeshComp *pPassedMeshComp )
+{
+ if ( nSize <= 0 || !pSelection )
+ return;
+
+ CUtlVector< int > sIndices;
+ CUtlVector< float > sWeights;
+ pSelection->GetComponents( sIndices, sWeights );
+ const int nVertices = sIndices.Count();
+
+ CDmMeshComp *pMeshComp = pPassedMeshComp ? pPassedMeshComp : new CDmMeshComp( this );
+
+ CUtlVector< CDmMeshComp::CVert * > neighbours;
+
+ CSelectionHelper sHelper;
+
+ for ( int i = 0; i < nVertices; ++i )
+ {
+ const int nNeighbours = pMeshComp->FindNeighbouringVerts( sIndices[ i ], neighbours );
+ for ( int j = 0; j < nNeighbours; ++j )
+ {
+ CDmMeshComp::CVert *pNeighbour = neighbours[ j ];
+ Assert( pNeighbour );
+ if ( pNeighbour )
+ {
+ const int vIndex = pNeighbour->PositionIndex();
+ if ( !pSelection->HasComponent( vIndex ) )
+ {
+ sHelper.AddVert( vIndex, sWeights[ i ] );
+ }
+ }
+ }
+ }
+
+ if ( sHelper.AddToSelection( pSelection ) > 0 )
+ {
+ GrowSelection( nSize - 1, pSelection, pMeshComp );
+ }
+
+ if ( pMeshComp != pPassedMeshComp )
+ {
+ delete pMeshComp;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::ShrinkSelection( int nSize, CDmeSingleIndexedComponent *pSelection, CDmMeshComp *pPassedMeshComp )
+{
+ if ( nSize <= 0 || !pSelection )
+ return;
+
+ CUtlVector< int > sIndices;
+ CUtlVector< float > sWeights;
+ pSelection->GetComponents( sIndices, sWeights );
+ const int nVertices = sIndices.Count();
+
+ CDmMeshComp *pMeshComp = pPassedMeshComp ? pPassedMeshComp : new CDmMeshComp( this );
+
+ CUtlVector< CDmMeshComp::CVert * > neighbours;
+
+ CSelectionHelper sHelper;
+
+ for ( int i = 0; i < nVertices; ++i )
+ {
+ bool hasSelectedNeighbour = false;
+ bool hasUnselectedNeighbour = false;
+
+ const int vIndex = sIndices[ i ];
+ const int nNeighbours = pMeshComp->FindNeighbouringVerts( vIndex, neighbours );
+ for ( int j = 0; j < nNeighbours; ++j )
+ {
+ const int nvIndex = neighbours[ j ]->PositionIndex();
+ if ( pSelection->HasComponent( nvIndex ) )
+ {
+ hasSelectedNeighbour = true;
+ if ( hasUnselectedNeighbour )
+ {
+ sHelper.AddVert( vIndex );
+ break;
+ }
+ }
+ else
+ {
+ hasUnselectedNeighbour = true;
+ if ( hasSelectedNeighbour )
+ {
+ sHelper.AddVert( vIndex );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( sHelper.RemoveFromSelection( pSelection, false ) > 0 )
+ {
+ ShrinkSelection( nSize - 1, pSelection, pMeshComp );
+ }
+
+ if ( pMeshComp != pPassedMeshComp )
+ {
+ delete pMeshComp;
+ }
+}
+
+
+CDmeSingleIndexedComponent *CDmeMesh::FeatherSelection(
+ float falloffDistance,
+ Falloff_t falloffType,
+ Distance_t distanceType,
+ CDmeSingleIndexedComponent *pSelection,
+ CDmMeshComp *pPassedMeshComp )
+{
+ switch ( falloffType )
+ {
+ case SMOOTH:
+ return FeatherSelection< SMOOTH >( falloffDistance, distanceType, pSelection, pPassedMeshComp );
+ case SPIKE:
+ return FeatherSelection< SPIKE >( falloffDistance, distanceType, pSelection, pPassedMeshComp );
+ case DOME:
+ return FeatherSelection< DOME >( falloffDistance, distanceType, pSelection, pPassedMeshComp );
+ default:
+ return FeatherSelection< LINEAR >( falloffDistance, distanceType, pSelection, pPassedMeshComp );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < int T >
+CDmeSingleIndexedComponent *CDmeMesh::FeatherSelection(
+ float fDistance, Distance_t distanceType,
+ CDmeSingleIndexedComponent *pSelection, CDmMeshComp *pPassedMeshComp )
+{
+ // TODO: Support feathering inward instead of just outward
+ if ( fDistance <= 0.0f || !pSelection )
+ return NULL;
+
+ // Make a new CDmeSingleIndexedComponent to do all of the dirty work
+ CDmeSingleIndexedComponent *pNewSelection = CreateElement< CDmeSingleIndexedComponent >( "feather", pSelection->GetFileId() );
+ pSelection->CopyAttributesTo( pNewSelection );
+
+ CDmMeshComp *pMeshComp = pPassedMeshComp ? pPassedMeshComp : new CDmMeshComp( this );
+ CDmeVertexData *pBase = pMeshComp->BaseState();
+
+ if ( distanceType == DIST_RELATIVE )
+ {
+ Vector vCenter;
+ float flRadius;
+ GetBoundingSphere( vCenter, flRadius, pBase, pSelection );
+ fDistance *= flRadius;
+ }
+
+ const CUtlVector< Vector > &positions( pBase->GetPositionData() );
+ const int nPositions = positions.Count();
+
+ if ( !pBase )
+ return NULL;
+
+ CUtlVector< int > sIndices;
+
+ int insideCount = 0;
+
+ CFalloff< T > falloff;
+
+ do
+ {
+ insideCount = 0;
+ CUtlVector< CDmMeshComp::CVert * > neighbours;
+ CSelectionHelper sHelper;
+
+ pNewSelection->GetComponents( sIndices );
+ int nVertices = sIndices.Count();
+
+ for ( int i = 0; i < nVertices; ++i )
+ {
+ const int nNeighbours = pMeshComp->FindNeighbouringVerts( sIndices[ i ], neighbours );
+
+ for ( int j = 0; j < nNeighbours; ++j )
+ {
+ const int vIndex = neighbours[ j ]->PositionIndex();
+
+ if ( pNewSelection->HasComponent( vIndex ) )
+ continue;
+
+ const int closestVert = ClosestSelectedVertex( vIndex, pSelection, pBase );
+ if ( closestVert < 0 || closestVert >= nPositions )
+ continue;
+
+ const float vDistance = positions[ vIndex ].DistTo( positions[ closestVert ] );
+ if ( vDistance <= fDistance )
+ {
+ sHelper.AddVert( vIndex, falloff( vDistance / fDistance ) );
+ ++insideCount;
+ }
+ }
+ }
+
+ sHelper.AddToSelection( pNewSelection );
+
+ } while ( insideCount > 0 );
+
+ return pNewSelection;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add the specified delta, scaled by the weight value to the DmeVertexData
+// base state specified. Optionally the add can be masked by a specified
+// weight map.
+//
+// If a DmeVertexData is not explicitly specified, the current state of the
+// mesh is modified unless it's the bind state. The bind state will never
+// be modified even if it is explicitly specified.
+//
+// Only the delta specified is added. No dependent states are added.
+//-----------------------------------------------------------------------------
+bool CDmeMesh::AddMaskedDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmeVertexData *pDst /* = NULL */,
+ float weight /* = 1.0f */,
+ const CDmeSingleIndexedComponent *pMask /* = NULL */ )
+{
+ CDmeVertexData *pBase = pDst ? pDst : GetCurrentBaseState();
+
+ if ( !pBase || pBase == GetBindBaseState() )
+ return false;
+
+ bool retVal = true;
+
+ const int nBaseField( pBase->FieldCount() );
+ const int nDeltaField( pDelta->FieldCount() );
+
+ // Try to add every field of the base state
+ for ( int j( 0 ); j < nBaseField; ++j )
+ {
+ const CUtlString &baseFieldName( pBase->FieldName( j ) );
+
+ // Find the corresponding field in the delta
+ for ( int k( 0 ); k < nDeltaField; ++k )
+ {
+ const CUtlString &deltaFieldName( pDelta->FieldName( k ) );
+
+ if ( baseFieldName != deltaFieldName )
+ continue;
+
+ const FieldIndex_t baseFieldIndex( pBase->FindFieldIndex( baseFieldName ) );
+ const FieldIndex_t deltaFieldIndex( pDelta->FindFieldIndex( deltaFieldName ) );
+ if ( baseFieldIndex < 0 || deltaFieldIndex < 0 )
+ break;
+
+ CDmAttribute *pBaseData( pBase->GetVertexData( baseFieldIndex ) );
+ CDmAttribute *pDeltaData( pDelta->GetVertexData( deltaFieldIndex ) );
+
+ if ( pBaseData->GetType() != pDeltaData->GetType() )
+ break;
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ AddRawDelta( pDelta, CDmrArray< float >( pBaseData ), baseFieldIndex, weight, pMask );
+ break;
+ case AT_COLOR_ARRAY:
+ // TODO: Color is missing some algebraic operators
+// AddRawDelta( pDelta, CDmrArray< Color >( pBaseData ), baseFieldIndex, weight, pMask );
+ break;
+ case AT_VECTOR2_ARRAY:
+ AddRawDelta( pDelta, CDmrArray< Vector2D >( pBaseData ), baseFieldIndex, weight, pMask );
+ break;
+ case AT_VECTOR3_ARRAY:
+ AddRawDelta( pDelta, CDmrArray< Vector >( pBaseData ), baseFieldIndex, weight, pMask );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add the specified delta, scaled by the weight value to the DmeVertexData
+// base state specified. Optionally the add can be masked by a specified
+// weight map.
+//
+// If a DmeVertexData is not explicitly specified, the current state of the
+// mesh is modified unless it's the bind state. The bind state will never
+// be modified even if it is explicitly specified.
+//
+// Only the delta specified is added. No dependent states are added.
+//-----------------------------------------------------------------------------
+bool CDmeMesh::AddCorrectedMaskedDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmeVertexData *pDst /* = NULL */,
+ float weight /* = 1.0f */,
+ const CDmeSingleIndexedComponent *pMask /* = NULL */ )
+{
+ CDmeVertexData *pBase = pDst ? pDst : GetCurrentBaseState();
+
+ if ( !pBase || pBase == GetBindBaseState() )
+ return false;
+
+ bool retVal = true;
+
+ const int nBaseField( pBase->FieldCount() );
+ const int nDeltaField( pDelta->FieldCount() );
+
+ // This should be cached and recomputed only when states are added
+ CUtlVector< DeltaComputation_t > compList;
+ ComputeDependentDeltaStateList( compList );
+
+ const int nDeltas( compList.Count() );
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ if ( pDelta != GetDeltaState( compList[ i ].m_nDeltaIndex ) )
+ continue;
+
+ // Try to add every field of the base state
+ for ( int j( 0 ); j < nBaseField; ++j )
+ {
+ const CUtlString &baseFieldName( pBase->FieldName( j ) );
+
+ // Find the corresponding field in the delta
+ for ( int k( 0 ); k < nDeltaField; ++k )
+ {
+ const CUtlString &deltaFieldName( pDelta->FieldName( k ) );
+
+ if ( baseFieldName != deltaFieldName )
+ continue;
+
+ const FieldIndex_t baseFieldIndex( pBase->FindFieldIndex( baseFieldName ) );
+ const FieldIndex_t deltaFieldIndex( pDelta->FindFieldIndex( deltaFieldName ) );
+ if ( baseFieldIndex < 0 || deltaFieldIndex < 0 )
+ break;
+
+ CDmAttribute *pBaseData( pBase->GetVertexData( baseFieldIndex ) );
+ CDmAttribute *pDeltaData( pDelta->GetVertexData( deltaFieldIndex ) );
+
+ if ( pBaseData->GetType() != pDeltaData->GetType() )
+ break;
+
+ const CUtlVector< int > &baseIndices( pBase->GetVertexIndexData( baseFieldIndex ) );
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ AddCorrectedDelta( CDmrArray< float >( pBaseData ), baseIndices, compList[ i ], baseFieldName, weight, pMask );
+ break;
+ case AT_COLOR_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector >( pBaseData ), baseIndices, compList[ i ], baseFieldName, weight, pMask );
+ break;
+ case AT_VECTOR2_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector2D >( pBaseData ), baseIndices, compList[ i ], baseFieldName, weight, pMask );
+ break;
+ case AT_VECTOR3_ARRAY:
+ AddCorrectedDelta( CDmrArray< Vector >( pBaseData ), baseIndices, compList[ i ], baseFieldName, weight, pMask );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Interpolates between two arrays of values and stores the result in a
+// CDmrArray.
+//
+// result = ( ( 1 - weight ) * a ) + ( weight * b )
+//
+//-----------------------------------------------------------------------------
+template< class T_t >
+bool CDmeMesh::InterpMaskedData(
+ CDmrArray< T_t > &aData,
+ const CUtlVector< T_t > &bData,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask ) const
+{
+ const int nDst = aData.Count();
+
+ if ( bData.Count() != nDst )
+ return false;
+
+ // The wacky way of writing these expression is because Vector4D is missing operators
+ // And this probably works better because of fewer temporaries
+
+ T_t a;
+ T_t b;
+
+ if ( pMask )
+ {
+ // With a weight mask
+ float vWeight;
+ for ( int i = 0; i < nDst; ++i )
+ {
+ if ( pMask->GetWeight( i, vWeight ) )
+ {
+ vWeight *= weight; // Specifically not clamping
+ a = aData.Get( i );
+ a *= ( 1.0f - vWeight );
+ b = bData[ i ];
+ b *= vWeight;
+ b += a;
+ aData.Set( i, b );
+ }
+ }
+ }
+ else
+ {
+ // Without a weight mask
+ const float oneMinusWeight( 1.0f - weight );
+ for ( int i = 0; i < nDst; ++i )
+ {
+ a = aData.Get( i );
+ a *= oneMinusWeight;
+ b = bData[ i ];
+ b *= weight;
+ b += a;
+ aData.Set( i, b );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Interpolates between two CDmeVertexData's
+//
+// paData = ( ( 1 - weight ) * a ) + ( weight * b )
+//-----------------------------------------------------------------------------
+bool CDmeMesh::InterpMaskedData(
+ CDmeVertexData *paData,
+ const CDmeVertexData *pbData,
+ float weight,
+ const CDmeSingleIndexedComponent *pMask ) const
+{
+ if ( !paData || !pbData || paData == pbData )
+ return false;
+
+ const int naField = paData->FieldCount();
+ const int nbField = pbData->FieldCount();
+
+ for ( int i = 0; i < naField; ++i )
+ {
+ const CUtlString &aFieldName( paData->FieldName( i ) );
+
+ for ( int j = 0; j < nbField; ++j )
+ {
+ const CUtlString &bFieldName( pbData->FieldName( j ) );
+ if ( aFieldName != bFieldName )
+ continue;
+
+ const FieldIndex_t aFieldIndex( paData->FindFieldIndex( aFieldName ) );
+ const FieldIndex_t bFieldIndex( pbData->FindFieldIndex( bFieldName ) );
+
+ if ( aFieldIndex < 0 || bFieldIndex < 0 )
+ break;
+
+ CDmAttribute *paAttr( paData->GetVertexData( aFieldIndex ) );
+ const CDmAttribute *pbAttr( pbData->GetVertexData( bFieldIndex ) );
+
+ if ( paAttr->GetType() != pbAttr->GetType() )
+ break;
+
+ if ( paData->GetVertexIndexData( aFieldIndex ).Count() != pbData->GetVertexIndexData( bFieldIndex ).Count() )
+ break;
+
+ switch ( paAttr->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ InterpMaskedData( CDmrArray< float >( paAttr ), CDmrArrayConst< float >( pbAttr ).Get(), weight, pMask );
+ break;
+ case AT_COLOR_ARRAY:
+ InterpMaskedData( CDmrArray< Vector4D >( paAttr ), CDmrArrayConst< Vector4D >( pbAttr ).Get(), weight, pMask );
+ break;
+ case AT_VECTOR2_ARRAY:
+ InterpMaskedData( CDmrArray< Vector2D >( paAttr ), CDmrArrayConst< Vector2D >( pbAttr ).Get(), weight, pMask );
+ break;
+ case AT_VECTOR3_ARRAY:
+ InterpMaskedData( CDmrArray< Vector >( paAttr ), CDmrArrayConst< Vector >( pbAttr ).Get(), weight, pMask );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Interpolates between the specified VertexData and the specified Delta
+// If pBase is NULL it will become the current state
+// If pDelta is NULL then the state to interpolate to will be the bind state
+//-----------------------------------------------------------------------------
+bool CDmeMesh::InterpMaskedDelta(
+ CDmeVertexDeltaData *pDelta,
+ CDmeVertexData *pDst /* = NULL */,
+ float weight /*= 1.0f */,
+ const CDmeSingleIndexedComponent *pMask /*= NULL */ )
+{
+ CDmeVertexData *pDstBase = pDst ? pDst : GetCurrentBaseState();
+ CDmeVertexData *pBind = GetBindBaseState();
+
+ if ( !pDstBase || !pBind || pDstBase == pBind )
+ return false;
+
+ if ( pDelta == NULL )
+ {
+ // Interpolate between specified state and bind state
+ return InterpMaskedData( pDstBase, pBind, weight, pMask );
+ }
+
+ // This should be cached and recomputed only when states are added
+ CUtlVector< DeltaComputation_t > compList;
+ ComputeDependentDeltaStateList( compList );
+
+ bool retVal = false;
+
+ const int nDeltas( compList.Count() );
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ if ( pDelta != GetDeltaState( compList[ i ].m_nDeltaIndex ) )
+ continue;
+
+ retVal = true;
+
+ const int nBaseField( pDstBase->FieldCount() );
+ const int nBindField( pBind->FieldCount() );
+ const int nDeltaField( pDelta->FieldCount() );
+
+ CUtlVector< float > floatData;
+ CUtlVector< Vector2D > vector2DData;
+ CUtlVector< Vector > vectorData;
+ CUtlVector< Vector4D > vector4DData;
+
+ for ( int j( 0 ); j < nBaseField; ++j )
+ {
+ const CUtlString &baseFieldName( pDstBase->FieldName( j ) );
+
+ for ( int k = 0; k < nBindField; ++k )
+ {
+ const CUtlString &bindFieldName( pBind->FieldName( k ) );
+ if ( baseFieldName != bindFieldName )
+ continue;
+
+ for ( int l = 0; l < nDeltaField; ++l )
+ {
+ const CUtlString &deltaFieldName( pDelta->FieldName( l ) );
+ if ( bindFieldName != deltaFieldName )
+ continue;
+
+ const FieldIndex_t baseFieldIndex( pDstBase->FindFieldIndex( baseFieldName ) );
+ const FieldIndex_t bindFieldIndex( pBind->FindFieldIndex( bindFieldName ) );
+ const FieldIndex_t deltaFieldIndex( pDelta->FindFieldIndex( deltaFieldName ) );
+
+ if ( baseFieldIndex < 0 || bindFieldIndex < 0 || deltaFieldIndex < 0 )
+ break;
+
+ CDmAttribute *pDstBaseData( pDstBase->GetVertexData( baseFieldIndex ) );
+ CDmAttribute *pBindData( pBind->GetVertexData( bindFieldIndex ) );
+ CDmAttribute *pDeltaData( pDelta->GetVertexData( deltaFieldIndex ) );
+
+ if ( pDstBaseData->GetType() != pBindData->GetType() || pBindData->GetType() != pDeltaData->GetType() )
+ break;
+
+ const CUtlVector< int > &bindIndices( pBind->GetVertexIndexData( bindFieldIndex ) );
+
+ switch ( pDstBaseData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ floatData = CDmrArrayConst< float >( pBindData ).Get();
+ AddCorrectedDelta( floatData, bindIndices, compList[ i ], baseFieldName );
+ InterpMaskedData( CDmrArray< float >( pDstBaseData ), floatData, weight, pMask );
+ break;
+ case AT_COLOR_ARRAY:
+ vector4DData = CDmrArrayConst< Vector4D >( pBindData ).Get();
+ AddCorrectedDelta( vector4DData, bindIndices, compList[ i ], baseFieldName );
+ InterpMaskedData( CDmrArray< Vector4D >( pDstBaseData ), vector4DData, weight, pMask );
+ break;
+ case AT_VECTOR2_ARRAY:
+ vector2DData = CDmrArrayConst< Vector2D >( pBindData ).Get();
+ AddCorrectedDelta( vector2DData, bindIndices, compList[ i ], baseFieldName );
+ InterpMaskedData( CDmrArray< Vector2D >( pDstBaseData ), vector2DData, weight, pMask );
+ break;
+ case AT_VECTOR3_ARRAY:
+ vectorData = CDmrArrayConst< Vector >( pBindData ).Get();
+ AddCorrectedDelta( vectorData, bindIndices, compList[ i ], baseFieldName );
+ InterpMaskedData( CDmrArray< Vector >( pDstBaseData ), vectorData, weight, pMask );
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the index of the closest selected vertex in the mesh to vIndex
+// -1 on failure
+//-----------------------------------------------------------------------------
+int CDmeMesh::ClosestSelectedVertex( int vIndex, CDmeSingleIndexedComponent *pSelection, const CDmeVertexData *pPassedBase /* = NULL */ ) const
+{
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return -1;
+
+ const CUtlVector< Vector > &positions( pBase->GetPositionData() );
+
+ if ( vIndex >= positions.Count() )
+ return -1;
+
+ const Vector &p( positions[ vIndex ] );
+
+ CUtlVector< int > verts;
+ pSelection->GetComponents( verts );
+ const int nVerts = verts.Count();
+
+ if ( nVerts <= 0 )
+ return -1;
+
+ float minSqDist = p.DistToSqr( positions[ verts[ 0 ] ] );
+ float tmpSqDist;
+
+ int retVal = verts[ 0 ];
+ for ( int i = 1; i < nVerts; ++i )
+ {
+ tmpSqDist = p.DistToSqr( positions[ verts[ i ] ] );
+ if ( tmpSqDist < minSqDist )
+ {
+ minSqDist = tmpSqDist;
+ retVal = verts[ i ];
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+float CDmeMesh::DistanceBetween( int vIndex0, int vIndex1, const CDmeVertexData *pPassedBase /*= NULL */ ) const
+{
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return 0.0f;
+
+ const CUtlVector< Vector > &positions( pBase->GetPositionData() );
+ const int nPositions = positions.Count();
+
+ if ( vIndex0 >= nPositions || vIndex1 >= nPositions )
+ return 0.0f;
+
+ return positions[ vIndex0 ].DistTo( positions[ vIndex1 ] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sorts DeltaComputation_t's by dimensionality
+//-----------------------------------------------------------------------------
+int ControlIndexLessFunc( const void *lhs, const void *rhs )
+{
+ const int &lVal = *reinterpret_cast< const int * >( lhs );
+ const int &rVal = *reinterpret_cast< const int * >( rhs );
+ return lVal - rVal;
+}
+
+//-----------------------------------------------------------------------------
+// This will compute a list of delta states that are superior to the passed
+// delta state name (which has the form of <NAME>[_<NAME>]..., i.e. controls
+// separated by underscores. The states will be returned in order from
+// most superior to least superior. Since the deltas need to be broken down
+// by the control deltas, if any control delta doesn't exist it will return false.
+//
+// A superior delta state is defined as a delta which has this delta as
+// a dependent (or inferior) delta.
+//
+// Given the network of:
+//
+// A, B, C
+// A_B, A_C, B_C
+// A_B_C
+//
+// A_B_C is superior to A, B, A_B, A_C & B_C
+// A_B is superior to A, B & C
+// A_C is superior to A, B & C
+// B_C is superior to A, B & C
+//
+// Input Output
+// ------- --------------------
+// A A_B_C, A_B, A_C, B_C
+// B A_B_C, A_B, A_C, B_C
+// C A_B_C, A_B, A_C, B_C
+// A_B A_B_C
+// A_C A_B_C
+// B_C A_B_C
+// A_B_C
+//-----------------------------------------------------------------------------
+bool CDmeMesh::ComputeSuperiorDeltaStateList( const char *pInferiorDeltaName, CUtlVector< int > &superiorDeltaStates )
+{
+ // TODO: Compute this data only when the deltas are added, removed or renamed
+ CUtlVector< DeltaComputation_t > compList;
+ ComputeDeltaStateComputationList( compList );
+
+ // Typically the passed delta won't be in the list yet, but it could be, that's ok
+ // Treat it like it isn't to be sure.
+ CUtlVector< int > inferiorIndices;
+ if ( !GetControlDeltaIndices( pInferiorDeltaName, inferiorIndices ) )
+ return false;
+
+ const int nInferiorIndices = inferiorIndices.Count();
+ qsort( inferiorIndices.Base(), nInferiorIndices, sizeof( int ), ControlIndexLessFunc );
+
+ CUtlVector< int > superiorIndices;
+ int nSuperiorIndices;
+ CDmeVertexDeltaData *pSuperiorDelta;
+
+ for ( int i = compList.Count() - 1; i >= 0; --i )
+ {
+ const DeltaComputation_t &deltaComp = compList[ i ];
+
+ // For a delta to be superior, it has to have more control inputs than the specified delta
+ // compList is sorted in order of dimensionality, so safe to abort
+ if ( nInferiorIndices >= deltaComp.m_nDimensionality )
+ break;
+
+ pSuperiorDelta = GetDeltaState( deltaComp.m_nDeltaIndex );
+ if ( !pSuperiorDelta )
+ continue;
+
+ if ( !GetControlDeltaIndices( pSuperiorDelta, superiorIndices ) )
+ continue;
+
+ nSuperiorIndices = superiorIndices.Count();
+
+ qsort( superiorIndices.Base(), nSuperiorIndices, sizeof( int ), ControlIndexLessFunc );
+
+ int nFound = 0;
+ int si = 0;
+ for ( int ii = 0; ii < nInferiorIndices; ++ii )
+ {
+ const int &iIndex = inferiorIndices[ ii ];
+ while ( si < nSuperiorIndices && iIndex != superiorIndices[ si ] )
+ {
+ ++si;
+ }
+
+ if ( si < nSuperiorIndices )
+ {
+ ++nFound;
+ }
+ }
+
+ if ( nFound == nInferiorIndices )
+ {
+ superiorDeltaStates.AddToTail( deltaComp.m_nDeltaIndex );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes the passed base state from the list of base states in the mesh
+// if it exists in the list of base states in the mesh, but doesn't delete
+// the element itself
+//-----------------------------------------------------------------------------
+bool CDmeMesh::RemoveBaseState( CDmeVertexData *pBase )
+{
+ const int nBaseStates = m_BaseStates.Count();
+ for ( int i = 0; i < nBaseStates; ++i )
+ {
+ if ( m_BaseStates[ i ] == pBase )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds an existing element to the list of base states of the mesh if it
+// isn't already one of the base states
+//-----------------------------------------------------------------------------
+CDmeVertexData *CDmeMesh::FindOrAddBaseState( CDmeVertexData *pBase )
+{
+ const int nBaseStates = m_BaseStates.Count();
+ for ( int i = 0; i < nBaseStates; ++i )
+ {
+ if ( m_BaseStates[ i ] == pBase )
+ {
+ return pBase;
+ }
+ }
+
+ return m_BaseStates[ m_BaseStates.AddToTail( pBase ) ];
+}
+
+
+//-----------------------------------------------------------------------------
+// TODO: Current state is insufficient as long as the current state isn't
+// created from the current delta weights
+//-----------------------------------------------------------------------------
+void CDmeMesh::GetBoundingSphere(
+ Vector &c, float &r,
+ CDmeVertexData *pPassedBase /* = NULL */, CDmeSingleIndexedComponent *pPassedSelection /* = NULL */ ) const
+{
+ c.Zero();
+ r = 0.0f;
+
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return;
+
+ const FieldIndex_t pIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ if ( pIndex < 0 )
+ return;
+
+ const CUtlVector< Vector > &pData( pBase->GetPositionData() );
+ const int nPositions = pData.Count();
+
+ if ( pPassedSelection )
+ {
+ const int nSelectionCount = pPassedSelection->Count();
+ int nIndex;
+ float fWeight;
+ for ( int i = 0; i < nSelectionCount; ++i )
+ {
+ pPassedSelection->GetComponent( i, nIndex, fWeight );
+ c += pData[ nIndex ];
+ }
+
+ c /= static_cast< float >( nSelectionCount );
+
+ float sqDist;
+ for ( int i = 0; i < nSelectionCount; ++i )
+ {
+ for ( int iPos = 0; iPos < nPositions; ++iPos )
+ {
+ sqDist = c.DistToSqr( pData[ iPos ] );
+ if ( sqDist > r )
+ {
+ r = sqDist;
+ }
+ }
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nPositions; ++i )
+ {
+ c += pData[ i ];
+ }
+
+ c /= static_cast< float >( nPositions );
+
+ float sqDist;
+ for ( int i = 0; i < nPositions; ++i )
+ {
+ for ( int iPos = 0; iPos < nPositions; ++iPos )
+ {
+ sqDist = c.DistToSqr( pData[iPos] );
+ if ( sqDist > r )
+ {
+ r = sqDist;
+ }
+ }
+ }
+ }
+
+ r = sqrt( r );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::GetBoundingBox( Vector &min, Vector &max, CDmeVertexData *pPassedBase /* = NULL */, CDmeSingleIndexedComponent *pPassedSelection /* = NULL */ ) const
+{
+ min.Zero();
+ max.Zero();
+
+ const CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+ if ( !pBase )
+ return;
+
+ const FieldIndex_t pIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ if ( pIndex < 0 )
+ return;
+
+ const CUtlVector< Vector > &pData( pBase->GetPositionData() );
+ const int nPositions = pData.Count();
+
+ if ( pPassedSelection )
+ {
+ const int nSelectionCount = pPassedSelection->Count();
+
+ if ( nSelectionCount > 0 )
+ {
+ int nIndex;
+ float fWeight;
+
+ pPassedSelection->GetComponent( 0, nIndex, fWeight );
+ min = pData[ nIndex ];
+ max = min;
+
+ for ( int i = 1; i < nSelectionCount; ++i )
+ {
+ pPassedSelection->GetComponent( i, nIndex, fWeight );
+
+ const Vector &p = pData[ nIndex ];
+ if ( p.x < min.x )
+ {
+ min.x = p.x;
+ }
+ else if ( p.x > max.x )
+ {
+ max.x = p.x;
+ }
+
+ if ( p.y < min.y )
+ {
+ min.y = p.y;
+ }
+ else if ( p.y > max.y )
+ {
+ max.y = p.y;
+ }
+
+ if ( p.z < min.z )
+ {
+ min.z = p.z;
+ }
+ else if ( p.z > max.z )
+ {
+ max.z = p.z;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( nPositions > 0 )
+ {
+ min = pData[ 0 ];
+ max = min;
+
+ for ( int i = 1; i < nPositions; ++i )
+ {
+ const Vector &p = pData[ i ];
+ if ( p.x < min.x )
+ {
+ min.x = p.x;
+ }
+ else if ( p.x > max.x )
+ {
+ max.x = p.x;
+ }
+
+ if ( p.y < min.y )
+ {
+ min.y = p.y;
+ }
+ else if ( p.y > max.y )
+ {
+ max.y = p.y;
+ }
+
+ if ( p.z < min.z )
+ {
+ min.z = p.z;
+ }
+ else if ( p.z > max.z )
+ {
+ max.z = p.z;
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+bool CDmeMesh::SetBaseDataToDeltas(
+ CDmeVertexData *pBase,
+ CDmeVertexData::StandardFields_t nStandardField, CDmrArrayConst< T_t > &srcData, CDmrArray< T_t > &dstData, bool bDoStereo, bool bDoLag )
+{
+ const int nDataCount = dstData.Count();
+ if ( srcData.Count() != nDataCount )
+ return false;
+
+ // Create the temp buffer for the data
+ T_t *pData = reinterpret_cast< T_t * >( alloca( nDataCount * sizeof( T_t ) ) );
+
+ // Copy the data from the src base state
+ memcpy( pData, srcData.Base(), nDataCount * sizeof( T_t ) );
+
+ const int nCount = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Count();
+ Assert( m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL].Count() == m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED].Count() );
+
+ if ( bDoStereo )
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flLeftWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flRightWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].y;
+ float flLeftWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ float flRightWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].y;
+ if ( flLeftWeight <= 0.0f && flRightWeight <= 0.0f && ( !bDoLag || ( flLeftWeightLagged <= 0.0f && flRightWeightLagged <= 0.0f ) ) )
+ continue;
+
+ AddStereoVertexDelta< T_t >( pBase, pData, sizeof( T_t ), nStandardField, i, bDoLag );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flWeight = m_DeltaStateWeights[MESH_DELTA_WEIGHT_NORMAL][i].x;
+ float flWeightLagged = m_DeltaStateWeights[MESH_DELTA_WEIGHT_LAGGED][i].x;
+ if ( flWeight < 0.0f && ( !bDoLag || flWeightLagged <= 0.0f ) )
+ continue;
+
+ AddVertexDelta< T_t >( pBase, pData, sizeof( T_t ), nStandardField, i, bDoLag );
+ }
+ }
+
+ dstData.SetMultiple( 0, nDataCount, pData );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the specified based state to the version of the mesh specified by the
+// current weighted deltas
+// It's ok to modify the bind state... if you know what you're doing
+//-----------------------------------------------------------------------------
+bool CDmeMesh::SetBaseStateToDeltas( CDmeVertexData *pPassedBase /*= NULL */ )
+{
+ CDmeVertexData *pBind = GetBindBaseState();
+ CDmeVertexData *pBase = pPassedBase ? pPassedBase : GetCurrentBaseState();
+
+ if ( !pBind || !pBase )
+ return false;
+
+ CDmeVertexData::StandardFields_t deltaFields[] =
+ {
+ CDmeVertexData::FIELD_POSITION,
+ CDmeVertexData::FIELD_NORMAL,
+ CDmeVertexData::FIELD_WRINKLE
+ };
+
+ const bool bDoStereo = ( pBind->FindFieldIndex( CDmeVertexDeltaData::FIELD_BALANCE ) >= 0 );
+
+ for ( int i = 0; i < sizeof( deltaFields ) / sizeof( deltaFields[ 0 ] ); ++i )
+ {
+ const CDmeVertexDeltaData::StandardFields_t nStandardField = deltaFields[ i ];
+ const int nSrcField = pBind->FindFieldIndex( nStandardField );
+ const int nDstField = pBase->FindFieldIndex( nStandardField );
+ if ( nSrcField < 0 || nDstField < 0 )
+ continue;
+
+ const CDmAttribute *pSrcAttr = pBind->GetVertexData( nSrcField );
+ CDmAttribute *pDstAttr = pBase->GetVertexData( nDstField );
+ if ( !pSrcAttr || !pDstAttr || pSrcAttr->GetType() != pDstAttr->GetType() )
+ continue;
+
+ switch ( pDstAttr->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ SetBaseDataToDeltas( pBind, nStandardField, CDmrArrayConst< float >( pSrcAttr ), CDmrArray< float >( pDstAttr ), bDoStereo, false );
+ break;
+ case AT_VECTOR3_ARRAY:
+ SetBaseDataToDeltas( pBind, nStandardField, CDmrArrayConst< Vector >( pSrcAttr ), CDmrArray< Vector >( pDstAttr ), bDoStereo, false );
+ break;
+ default:
+ Assert( 0 );
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Replace all instances of a material with a different material
+//-----------------------------------------------------------------------------
+void CDmeMesh::ReplaceMaterial( const char *pOldMaterialName, const char *pNewMaterialName )
+{
+ char pOldFixedName[MAX_PATH];
+ char pNewFixedName[MAX_PATH];
+ char pFixedName[MAX_PATH];
+ if ( pOldMaterialName )
+ {
+ V_FixupPathName( pOldFixedName, sizeof(pOldFixedName), pOldMaterialName );
+ }
+ V_FixupPathName( pNewFixedName, sizeof(pNewFixedName), pNewMaterialName );
+ V_FixSlashes( pNewFixedName, '/' );
+
+ CDmeMaterial *pReplacementMaterial = NULL;
+
+ int nCount = m_FaceSets.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = m_FaceSets[i];
+ CDmeMaterial *pMaterial = pFaceSet->GetMaterial();
+ if ( pOldMaterialName )
+ {
+ const char *pMaterialName = pMaterial->GetMaterialName();
+ V_FixupPathName( pFixedName, sizeof(pFixedName), pMaterialName );
+ if ( Q_stricmp( pFixedName, pOldFixedName ) )
+ continue;
+ }
+
+ if ( !pReplacementMaterial )
+ {
+ pReplacementMaterial = CreateElement< CDmeMaterial >( pMaterial->GetName(), pMaterial->GetFileId() );
+ pReplacementMaterial->SetMaterial( pNewFixedName );
+ }
+ pFaceSet->SetMaterial( pReplacementMaterial );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleans up delta data that is referring to normals which have been merged out
+//-----------------------------------------------------------------------------
+static void CollapseRedundantDeltaNormals( CDmeVertexDeltaData *pDmeDelta, const CUtlVector< int > &normalMap )
+{
+ if ( !pDmeDelta )
+ return;
+
+ FieldIndex_t nNormalFieldIndex = pDmeDelta->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nNormalFieldIndex < 0 )
+ return; // No normal deltas
+
+ const CUtlVector< Vector > &oldNormalData = pDmeDelta->GetNormalData();
+ const CUtlVector< int > &oldNormalIndices = pDmeDelta->GetVertexIndexData( nNormalFieldIndex );
+
+ Assert( oldNormalData.Count() == oldNormalIndices.Count() );
+
+ CUtlVector< bool > done;
+ done.SetCount( normalMap.Count() );
+ Q_memset( done.Base(), 0, done.Count() * sizeof( bool ) );
+
+ CUtlVector< Vector > newNormalData;
+ CUtlVector< int > newNormalIndices;
+
+ for ( int i = 0; i < oldNormalIndices.Count(); ++i )
+ {
+ const int nNewIndex = normalMap[ oldNormalIndices[i] ];
+ if ( nNewIndex < 0 || done[ nNewIndex ] )
+ continue;
+
+ done[ nNewIndex ] = true;
+ newNormalData.AddToTail( oldNormalData[i] );
+ newNormalIndices.AddToTail( nNewIndex );
+ }
+
+ pDmeDelta->RemoveAllVertexData( nNormalFieldIndex );
+ nNormalFieldIndex = pDmeDelta->CreateField( CDmeVertexDeltaData::FIELD_NORMAL );
+ pDmeDelta->AddVertexData( nNormalFieldIndex, newNormalData.Count() );
+ pDmeDelta->SetVertexData( nNormalFieldIndex, 0, newNormalData.Count(), AT_VECTOR3, newNormalData.Base() );
+ pDmeDelta->SetVertexIndices( nNormalFieldIndex, 0, newNormalIndices.Count(), newNormalIndices.Base() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove redundant normals from a DMX Mesh
+// Looks at all of the normals around each position vertex and merges normals
+// which are numerically similar (within flNormalBlend which by default in
+// studiomdl is within 2 degrees) around that vertex
+//
+// If this would result in more normals being created, then don't do anything
+// return false.
+//-----------------------------------------------------------------------------
+static bool CollapseRedundantBaseNormals( CDmeVertexData *pDmeVertexData, CUtlVector< int > &normalMap, float flNormalBlend )
+{
+ if ( !pDmeVertexData )
+ return false;
+
+ FieldIndex_t nPositionFieldIndex = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ FieldIndex_t nNormalFieldIndex = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nPositionFieldIndex < 0 || nNormalFieldIndex < 0 )
+ return false;
+
+ const CUtlVector< Vector > &oldNormalData = pDmeVertexData->GetNormalData();
+ const CUtlVector< int > &oldNormalIndices = pDmeVertexData->GetVertexIndexData( nNormalFieldIndex );
+
+ CUtlVector< Vector > newNormalData;
+ CUtlVector< int > newNormalIndices;
+
+ newNormalIndices.SetCount( oldNormalIndices.Count() );
+ for ( int i = 0; i < newNormalIndices.Count(); ++i )
+ {
+ newNormalIndices[i] = -1;
+ }
+
+ const int nPositionDataCount = pDmeVertexData->GetPositionData().Count();
+ for ( int i = 0; i < nPositionDataCount; ++i )
+ {
+ int nNewNormalDataIndex = newNormalData.Count();
+
+ const CUtlVector< int > &vertexIndices = pDmeVertexData->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, i );
+ for ( int j = 0; j < vertexIndices.Count(); ++j )
+ {
+ bool bUnique = true;
+ const int nVertexIndex = vertexIndices[j];
+ const Vector &vNormal = oldNormalData[ oldNormalIndices[ vertexIndices[j] ] ];
+
+ for ( int k = nNewNormalDataIndex; k < newNormalData.Count(); ++k )
+ {
+ if ( DotProduct( vNormal, newNormalData[k] ) > flNormalBlend )
+ {
+ newNormalIndices[ nVertexIndex ] = k;
+ bUnique = false;
+ break;
+ }
+ }
+
+ if ( !bUnique )
+ continue;
+
+ newNormalIndices[ nVertexIndex ] = newNormalData.AddToTail( vNormal );
+ }
+ }
+
+ for ( int i = 0; i < newNormalIndices.Count(); ++i )
+ {
+ if ( newNormalIndices[i] == -1 )
+ {
+ newNormalIndices[i] = newNormalData.AddToTail( oldNormalData[ oldNormalIndices[i] ] );
+ }
+ }
+
+ // If it's the same or more don't do anything
+ if ( newNormalData.Count() >= oldNormalData.Count() )
+ return false;
+
+ normalMap.SetCount( oldNormalData.Count() );
+ for ( int i = 0; i < normalMap.Count(); ++i )
+ {
+ normalMap[i] = -1;
+ }
+
+ Assert( newNormalIndices.Count() == oldNormalIndices.Count() );
+ for ( int i = 0; i < oldNormalIndices.Count(); ++i )
+ {
+ if ( normalMap[ oldNormalIndices[i] ] == -1 )
+ {
+ normalMap[ oldNormalIndices[i] ] = newNormalIndices[i];
+ }
+ else
+ {
+ Assert( normalMap[ oldNormalIndices[i] ] == newNormalIndices[i] );
+ }
+ }
+
+ pDmeVertexData->RemoveAllVertexData( nNormalFieldIndex );
+ nNormalFieldIndex = pDmeVertexData->CreateField( CDmeVertexDeltaData::FIELD_NORMAL );
+ pDmeVertexData->AddVertexData( nNormalFieldIndex, newNormalData.Count() );
+ pDmeVertexData->SetVertexData( nNormalFieldIndex, 0, newNormalData.Count(), AT_VECTOR3, newNormalData.Base() );
+ pDmeVertexData->SetVertexIndices( nNormalFieldIndex, 0, newNormalIndices.Count(), newNormalIndices.Base() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Collapse all normals with the same numerical value into the same normal
+//-----------------------------------------------------------------------------
+static bool CollapseRedundantBaseNormalsAggressive( CDmeVertexData *pDmeVertexData, float flNormalBlend )
+{
+ if ( !pDmeVertexData )
+ return false;
+
+ FieldIndex_t nNormalFieldIndex = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nNormalFieldIndex < 0 )
+ return false;
+
+ const CUtlVector< Vector > &oldNormalData = pDmeVertexData->GetNormalData();
+ const CUtlVector< int > &oldNormalIndices = pDmeVertexData->GetVertexIndexData( nNormalFieldIndex );
+
+ CUtlVector< int > normalMap;
+ normalMap.SetCount( oldNormalData.Count() );
+
+ CUtlVector< Vector > newNormalData;
+
+ for ( int i = 0; i < oldNormalData.Count(); ++i )
+ {
+ bool bUnique = true;
+ const Vector &vNormal = oldNormalData[ i ];
+
+ for ( int j = 0; j < newNormalData.Count(); ++j )
+ {
+ if ( DotProduct( vNormal, newNormalData[j] ) > flNormalBlend )
+ {
+ normalMap[ i ] = j;
+ bUnique = false;
+ break;
+ }
+ }
+
+ if ( !bUnique )
+ continue;
+
+ normalMap[ i ] = newNormalData.AddToTail( vNormal );
+ }
+
+ // If it's the same then don't do anything.
+ if ( newNormalData.Count() >= oldNormalData.Count() )
+ return false;
+
+ CUtlVector< int > newNormalIndices;
+ newNormalIndices.SetCount( oldNormalIndices.Count() );
+
+ for ( int i = 0; i < oldNormalIndices.Count(); ++i )
+ {
+ newNormalIndices[i] = normalMap[ oldNormalIndices[i] ];
+ }
+
+ pDmeVertexData->RemoveAllVertexData( nNormalFieldIndex );
+ nNormalFieldIndex = pDmeVertexData->CreateField( CDmeVertexDeltaData::FIELD_NORMAL );
+ pDmeVertexData->AddVertexData( nNormalFieldIndex, newNormalData.Count() );
+ pDmeVertexData->SetVertexData( nNormalFieldIndex, 0, newNormalData.Count(), AT_VECTOR3, newNormalData.Base() );
+ pDmeVertexData->SetVertexIndices( nNormalFieldIndex, 0, newNormalIndices.Count(), newNormalIndices.Base() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::NormalizeNormals()
+{
+ Vector vNormal;
+
+ for ( int i = 0; i < this->BaseStateCount(); ++i )
+ {
+ CDmeVertexData *pDmeVertexData = GetBaseState( i );
+ if ( !pDmeVertexData )
+ continue;
+
+ FieldIndex_t nNormalIndex = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nNormalIndex < 0 )
+ continue;
+
+ CDmAttribute *pDmNormalAttr = pDmeVertexData->GetVertexData( nNormalIndex );
+ if ( !pDmNormalAttr )
+ continue;
+
+ CDmrArray< Vector > normalData( pDmNormalAttr );
+ for ( int j = 0; j < normalData.Count(); ++j )
+ {
+ vNormal = normalData.Get( j );
+ VectorNormalize( vNormal );
+ normalData.Set( j, vNormal );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeMesh::CollapseRedundantNormals( float flNormalBlend )
+{
+ NormalizeNormals();
+
+ CDmeVertexData *pDmeBind = GetBindBaseState();
+ if ( !pDmeBind )
+ return;
+
+ CUtlVector< int > normalMap;
+
+ const int nDeltaStateCount = DeltaStateCount();
+ if ( nDeltaStateCount <= 0 )
+ {
+ // No deltas
+ if ( CollapseRedundantBaseNormalsAggressive( pDmeBind, flNormalBlend ) )
+ {
+ // Collapse any other states
+ for ( int i = 0; i < BaseStateCount(); ++i )
+ {
+ CDmeVertexData *pDmeVertexData = GetBaseState( i );
+ if ( !pDmeVertexData || pDmeVertexData == pDmeBind )
+ continue;
+
+ CollapseRedundantBaseNormalsAggressive( pDmeVertexData, flNormalBlend );
+ }
+ }
+ }
+ else
+ {
+ // Collapse the base state
+ if ( CollapseRedundantBaseNormals( pDmeBind, normalMap, flNormalBlend ) )
+ {
+ // Collapse any delta states using the baseState normal map
+ for ( int i = 0; i < DeltaStateCount(); ++i )
+ {
+ CDmeVertexDeltaData *pDmeDeltaData = GetDeltaState( i );
+ if ( !pDmeDeltaData )
+ continue;
+
+ CollapseRedundantDeltaNormals( pDmeDeltaData, normalMap );
+ }
+
+ // Collapse any other states
+ for ( int i = 0; i < BaseStateCount(); ++i )
+ {
+ CDmeVertexData *pDmeVertexData = GetBaseState( i );
+ if ( !pDmeVertexData || pDmeVertexData == pDmeBind )
+ continue;
+
+ CollapseRedundantBaseNormals( pDmeVertexData, normalMap, flNormalBlend );
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/movieobjects/dmemodel.cpp b/movieobjects/dmemodel.cpp
new file mode 100644
index 0000000..a4145e3
--- /dev/null
+++ b/movieobjects/dmemodel.cpp
@@ -0,0 +1,335 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Dme version of a skeletal model (gets compiled into a MDL)
+//
+//=============================================================================
+#include "movieobjects/dmemodel.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datacache/imdlcache.h"
+#include "materialsystem/imaterialsystem.h"
+#include "tier2/tier2.h"
+#include "studio.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeModel, CDmeModel );
+
+
+//-----------------------------------------------------------------------------
+// Stack of DmeModels currently being rendered. Used to set up render state
+//-----------------------------------------------------------------------------
+CUtlStack< CDmeModel * > CDmeModel::s_ModelStack;
+static CUtlVector< matrix3x4_t > s_PoseToWorld;
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeModel::OnConstruction()
+{
+ m_JointTransforms.Init( this, "jointTransforms" );
+ m_BaseStates.Init( this, "baseStates" );
+}
+
+void CDmeModel::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Add joint
+//-----------------------------------------------------------------------------
+int CDmeModel::AddJoint( CDmeDag *pJoint )
+{
+ int nIndex = GetJointTransformIndex( pJoint->GetTransform() );
+ if ( nIndex >= 0 )
+ return nIndex;
+
+ return m_JointTransforms.AddToTail( pJoint->GetTransform() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Add joint
+//-----------------------------------------------------------------------------
+CDmeJoint *CDmeModel::AddJoint( const char *pJointName, CDmeDag *pParent )
+{
+ CDmeJoint *pJoint = CreateElement<CDmeJoint>( pJointName, GetFileId() );
+ CDmeTransform *pTransform = pJoint->GetTransform();
+ pTransform->SetName( pJointName );
+
+ if ( !pParent )
+ {
+ pParent = this;
+ }
+ pParent->AddChild( pJoint );
+ m_JointTransforms.AddToTail( pTransform );
+ return pJoint;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the number of joint transforms we know about
+//-----------------------------------------------------------------------------
+int CDmeModel::GetJointTransformCount() const
+{
+ return m_JointTransforms.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Determines joint transform index given a joint transform
+//-----------------------------------------------------------------------------
+int CDmeModel::GetJointTransformIndex( CDmeTransform *pTransform ) const
+{
+ int nCount = m_JointTransforms.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( pTransform == m_JointTransforms[i] )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Determines joint transform index given a joint
+//-----------------------------------------------------------------------------
+int CDmeModel::GetJointTransformIndex( CDmeDag *pJoint ) const
+{
+ return GetJointTransformIndex( pJoint->GetTransform() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Determines joint transform index given a joint name
+//-----------------------------------------------------------------------------
+CDmeTransform *CDmeModel::GetJointTransform( int nIndex )
+{
+ return m_JointTransforms[ nIndex ];
+}
+
+const CDmeTransform *CDmeModel::GetJointTransform( int nIndex ) const
+{
+ return m_JointTransforms[ nIndex ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a base state by name, returns NULL if not found
+//-----------------------------------------------------------------------------
+CDmeTransformList *CDmeModel::FindBaseState( const char *pBaseStateName )
+{
+ int nCount = m_BaseStates.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( m_BaseStates[i]->GetName(), pBaseStateName ) )
+ return m_BaseStates[i];
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Captures the current joint transforms into a base state
+//-----------------------------------------------------------------------------
+void CDmeModel::CaptureJointsToBaseState( const char *pBaseStateName )
+{
+ CDmeTransformList *pTransformList = FindBaseState( pBaseStateName );
+ if ( !pTransformList )
+ {
+ pTransformList = CreateElement<CDmeTransformList>( pBaseStateName, GetFileId() );
+ m_BaseStates.AddToTail( pTransformList );
+ }
+
+ // Make the transform list have the correct number of elements
+ int nJointCount = m_JointTransforms.Count();
+ int nCurrentCount = pTransformList->GetTransformCount();
+ if ( nJointCount > nCurrentCount )
+ {
+ for ( int i = nCurrentCount; i < nJointCount; ++i )
+ {
+ CDmeTransform *pTransform = CreateElement<CDmeTransform>( m_JointTransforms[i]->GetName(), pTransformList->GetFileId() );
+ pTransformList->m_Transforms.AddToTail( pTransform );
+ }
+ }
+ else if ( nJointCount < nCurrentCount )
+ {
+ pTransformList->m_Transforms.RemoveMultiple( nJointCount, nCurrentCount - nJointCount );
+ }
+
+ // Copy the state over
+ for ( int i = 0; i < nJointCount; ++i )
+ {
+ matrix3x4_t mat;
+ m_JointTransforms[i]->GetTransform( mat );
+ pTransformList->SetTransform( i, mat );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads up joint transforms for this model
+//-----------------------------------------------------------------------------
+void CDmeModel::LoadJointTransform( CDmeDag *pJoint, CDmeTransformList *pBindPose, const matrix3x4_t &parentToWorld, const matrix3x4_t &parentToBindPose, bool bSetHardwareState )
+{
+ CDmeTransform *pTransform = pJoint->GetTransform();
+
+ // Determines joint transform index; no index, no traversing lower in the hierarchy
+ int nJointIndex = GetJointTransformIndex( pTransform );
+ if ( nJointIndex < 0 )
+ return;
+
+ // FIXME: Sucky search here necessary to find bone matrix index
+ matrix3x4_t jointToWorld, jointToParent;
+ pTransform->GetTransform( jointToParent );
+ ConcatTransforms( parentToWorld, jointToParent, jointToWorld );
+
+ matrix3x4_t bindJointToParent, bindPoseToJoint, bindPoseToWorld, jointToBindPose;
+ if ( pBindPose )
+ {
+ if ( nJointIndex >= pBindPose->GetTransformCount() )
+ {
+ Warning( "Model is in an invalid state! There are different numbers of bones in the bind pose and joint transform list!\n" );
+ return;
+ }
+ pBindPose->GetTransform( nJointIndex )->GetTransform( bindJointToParent );
+ }
+ else
+ {
+ MatrixCopy( jointToParent, bindJointToParent );
+ }
+ ConcatTransforms( parentToBindPose, bindJointToParent, jointToBindPose );
+
+ MatrixInvert( jointToBindPose, bindPoseToJoint );
+ ConcatTransforms( jointToWorld, bindPoseToJoint, bindPoseToWorld );
+
+ if ( bSetHardwareState )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->LoadBoneMatrix( nJointIndex, bindPoseToWorld );
+ }
+ MatrixCopy( bindPoseToWorld, s_PoseToWorld[ nJointIndex ] );
+
+ int nChildCount = pJoint->GetChildCount();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ CDmeDag *pChildJoint = pJoint->GetChild(i);
+ if ( !pChildJoint )
+ continue;
+
+ LoadJointTransform( pChildJoint, pBindPose, jointToWorld, jointToBindPose, bSetHardwareState );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the render state for the model
+//-----------------------------------------------------------------------------
+CDmeModel::SetupBoneRetval_t CDmeModel::SetupBoneMatrixState( const matrix3x4_t& shapeToWorld, bool bForceSoftwareSkin )
+{
+ int nJointCount = m_JointTransforms.Count();
+ if ( nJointCount <= 0 )
+ return NO_SKIN_DATA;
+
+ int nBoneBatchCount = g_pMaterialSystemHardwareConfig->MaxVertexShaderBlendMatrices();
+ bool bSetHardwareState = ( nJointCount <= nBoneBatchCount ) && !bForceSoftwareSkin;
+
+ s_PoseToWorld.EnsureCount( nJointCount );
+
+ // Finds a base state by name, returns NULL if not found
+ CDmeTransformList *pBindPose = FindBaseState( "bind" );
+
+ matrix3x4_t parentToBindPose;
+ SetIdentityMatrix( parentToBindPose );
+
+ int nChildCount = GetChildCount();
+ for ( int i = 0; i < nChildCount; ++i )
+ {
+ CDmeDag *pChildJoint = GetChild(i);
+ if ( !pChildJoint )
+ continue;
+
+ LoadJointTransform( pChildJoint, pBindPose, shapeToWorld, parentToBindPose, bSetHardwareState );
+ }
+
+ return bSetHardwareState ? BONES_SET_UP : TOO_MANY_BONES;
+}
+
+matrix3x4_t *CDmeModel::SetupModelRenderState( const matrix3x4_t& shapeToWorld, bool bHasSkinningData, bool bForceSoftwareSkin )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ if ( bHasSkinningData && ( s_ModelStack.Count() > 0 ) )
+ {
+ SetupBoneRetval_t retVal = s_ModelStack.Top()->SetupBoneMatrixState( shapeToWorld, bForceSoftwareSkin );
+ if ( retVal == TOO_MANY_BONES )
+ {
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity( );
+ return s_PoseToWorld.Base();
+ }
+ if ( retVal != NO_SKIN_DATA )
+ return NULL;
+ }
+
+ if ( bForceSoftwareSkin )
+ {
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity( );
+ s_PoseToWorld.EnsureCount( 1 );
+ MatrixCopy( shapeToWorld, s_PoseToWorld[0] );
+ return s_PoseToWorld.Base();
+ }
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadMatrix( shapeToWorld );
+ return NULL;
+}
+
+void CDmeModel::CleanupModelRenderState()
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadIdentity();
+}
+
+
+//-----------------------------------------------------------------------------
+// Recursively render the Dag hierarchy
+//-----------------------------------------------------------------------------
+void CDmeModel::Draw( CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ s_ModelStack.Push( this );
+ BaseClass::Draw( pDrawSettings );
+ s_ModelStack.Pop( );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set if Z is the up axis of the model
+//-----------------------------------------------------------------------------
+void CDmeModel::ZUp( bool bZUp )
+{
+ SetValue( "upAxis", bZUp ? "Z" : "Y" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the DmeModel is Z Up.
+// NOTE: Since Y & Z are the only supported modes and Y is the default
+// because that's how DmeModel data was originally defined,
+// assume Y is up if the m_UpAxis attribute is not "Z"
+//-----------------------------------------------------------------------------
+bool CDmeModel::IsZUp() const
+{
+ const char *pszZUp = this->GetValueString( "upAxis" );
+
+ return ( pszZUp && *pszZUp ) ? StringHasPrefix( pszZUp, "Z" ) : false;
+} \ No newline at end of file
diff --git a/movieobjects/dmemorphoperator.cpp b/movieobjects/dmemorphoperator.cpp
new file mode 100644
index 0000000..95b0f98
--- /dev/null
+++ b/movieobjects/dmemorphoperator.cpp
@@ -0,0 +1,157 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmemorphoperator.h"
+#include "movieobjects/dmevertexdata.h"
+#include "movieobjects/dmemesh.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMorphOperator, CDmeMorphOperator );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMorphOperator::OnConstruction()
+{
+ m_mesh.Init( this, "mesh", FATTRIB_HAS_CALLBACK );
+ m_deltaStateWeights.Init( this, "deltaStateWeights", FATTRIB_MUSTCOPY );
+ m_baseStateName.Init( this, "baseStateName", FATTRIB_TOPOLOGICAL );
+}
+
+void CDmeMorphOperator::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// accessors
+//-----------------------------------------------------------------------------
+uint CDmeMorphOperator::NumDeltaStateWeights()
+{
+ return m_deltaStateWeights.Count();
+}
+
+CDmElement *CDmeMorphOperator::GetDeltaStateWeight( uint i )
+{
+ return m_deltaStateWeights[ i ];
+}
+
+CDmeMesh *CDmeMorphOperator::GetMesh()
+{
+ return m_mesh.GetElement();
+}
+
+
+//-----------------------------------------------------------------------------
+// This function gets called whenever an attribute changes
+//-----------------------------------------------------------------------------
+void CDmeMorphOperator::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ if ( pAttribute == m_mesh.GetAttribute() )
+ {
+ CDmeMesh *pMesh = GetMesh();
+ if ( pMesh )
+ {
+#if 0 // right now, the file already contains these weights, and re-creating them breaks the channel connections
+ m_deltaStateWeights.RemoveAll();
+
+ uint dn = pMesh->NumDeltaStates();
+ for ( uint di = 0; di < dn; ++di )
+ {
+ CDmElement *pDeltaState = pMesh->GetDeltaState( di );
+ const char *name = pDeltaState->GetName();
+
+ CDmElement *pDeltaWeight = CreateElement< CDmElement >( name, GetFileId() );
+ pDeltaWeight->SetAttributeValue( "weight", 0.0f );
+
+ m_deltaStateWeights.AddToTail( pDeltaWeight->GetHandle() );
+ }
+#endif
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMorphOperator::Operate()
+{
+ CDmeMesh *mesh = GetMesh();
+
+ uint mn = NumDeltaStateWeights();
+ for ( uint mi = 0; mi < mn; ++mi )
+ {
+ CDmElement *pDeltaState = GetDeltaStateWeight( mi );
+ const char *deltaName = pDeltaState->GetName();
+ float deltaWeight = pDeltaState->GetValue< float >( "weight" );
+
+ int di = mesh->FindDeltaStateIndex( deltaName );
+ if ( di != -1 )
+ {
+ mesh->SetDeltaStateWeight( di, MESH_DELTA_WEIGHT_NORMAL, deltaWeight );
+ }
+ else
+ {
+ Msg( "MorphOperator::Operate: invalid delta state name: %s\n", deltaName );
+ }
+ }
+}
+
+// hack to avoid MSVC complaining about multiply defined symbols
+namespace MorphOp
+{
+void AddAttr( CUtlVector< CDmAttribute * > &attrs, CDmAttribute *pAttr )
+{
+ if ( pAttr == NULL )
+ return;
+ attrs.AddToTail( pAttr );
+}
+
+void AddVertexAttributes( CUtlVector< CDmAttribute * > &attrs, CDmElement *pObject )
+{
+ AddAttr( attrs, pObject->GetAttribute( "coordinates" ) );
+ AddAttr( attrs, pObject->GetAttribute( "normals" ) );
+ AddAttr( attrs, pObject->GetAttribute( "textureCoordinates" ) );
+ // TODO - add colors, occlusionFactors, boneIndices*, boneWeights*, tangents
+}
+};
+using namespace MorphOp;
+
+void CDmeMorphOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ uint nWeights = NumDeltaStateWeights();
+ for ( uint wi = 0; wi < nWeights; ++wi )
+ {
+ CDmElement *pDelta = GetDeltaStateWeight( wi );
+ AddAttr( attrs, pDelta->GetAttribute( "weight" ) );
+ }
+
+ CDmeMesh *pMesh = GetMesh();
+ CDmeVertexData *pBaseState = pMesh->FindBaseState( m_baseStateName.Get() );
+ AddVertexAttributes( attrs, pBaseState );
+
+ uint nDeltas = pMesh->DeltaStateCount();
+ for ( uint di = 0; di < nDeltas; ++di )
+ {
+ CDmElement *pDeltaState = pMesh->GetDeltaState( di );
+ AddAttr( attrs, pDeltaState->GetAttribute( "indices" ) );
+ AddVertexAttributes( attrs, pDeltaState );
+ }
+}
+
+void CDmeMorphOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ AddVertexAttributes( attrs, GetMesh() );
+}
diff --git a/movieobjects/dmemouseinput.cpp b/movieobjects/dmemouseinput.cpp
new file mode 100644
index 0000000..6215d2a
--- /dev/null
+++ b/movieobjects/dmemouseinput.cpp
@@ -0,0 +1,104 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmemouseinput.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+#include "vgui/iinput.h"
+#include "vgui/ipanel.h"
+#include "tier3/tier3.h"
+
+#include "tier0/dbg.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeMouseInput, CDmeMouseInput );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeMouseInput::OnConstruction()
+{
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+
+ m_xOrigin = 0.0f;
+ m_yOrigin = 0.0f;
+}
+
+void CDmeMouseInput::OnDestruction()
+{
+}
+
+//-----------------------------------------------------------------------------
+// IsDirty - ie needs to operate
+//-----------------------------------------------------------------------------
+bool CDmeMouseInput::IsDirty()
+{
+ float flX, flY;
+ GetNormalizedCursorPos( flX, flY );
+ flX -= m_xOrigin;
+ flY -= m_yOrigin;
+
+ return ( flX != GetValue< float >( "x" ) ) || ( flY != GetValue< float >( "y" ) );
+}
+
+void CDmeMouseInput::Operate()
+{
+ float flX, flY;
+ GetNormalizedCursorPos( flX, flY );
+
+ SetValue( "x", flX - m_xOrigin );
+ SetValue( "y", flY - m_yOrigin );
+
+// Msg( "CDmeMouseInput::Operate() at <%f, %f>\n", flX, flY );
+}
+
+void CDmeMouseInput::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+}
+
+void CDmeMouseInput::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( GetAttribute( "x" ) );
+ attrs.AddToTail( GetAttribute( "y" ) );
+}
+
+void CDmeMouseInput::ResetOrigin( float dx, float dy )
+{
+ GetNormalizedCursorPos( m_xOrigin, m_yOrigin );
+ m_xOrigin += dx;
+ m_yOrigin += dy;
+}
+
+void CDmeMouseInput::GetNormalizedCursorPos( float &flX, float &flY )
+{
+ int x, y;
+ g_pVGuiInput->GetCursorPos( x, y );
+
+ vgui::VPANEL vpanel = g_pVGuiInput->GetFocus();
+ if ( !vpanel )
+ {
+ flX = flY = 0.0f;
+ return;
+ }
+
+ int x0, y0;
+ g_pVGuiPanel->GetPos( vpanel, x0, y0 );
+
+ int w, h;
+ g_pVGuiPanel->GetSize( vpanel, w, h );
+
+ flX = ( x - x0 ) / float(w);
+ flY = ( y - y0 ) / float(h);
+}
diff --git a/movieobjects/dmeoperator.cpp b/movieobjects/dmeoperator.cpp
new file mode 100644
index 0000000..e0f0707
--- /dev/null
+++ b/movieobjects/dmeoperator.cpp
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmeoperator.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ABSTRACT_ELEMENT( DmeOperator, CDmeOperator );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeOperator::OnConstruction()
+{
+}
+
+void CDmeOperator::OnDestruction()
+{
+}
+
+//-----------------------------------------------------------------------------
+// IsDirty - ie needs to operate
+//-----------------------------------------------------------------------------
+bool CDmeOperator::IsDirty()
+{
+ return BaseClass::IsDirty();
+}
diff --git a/movieobjects/dmepackoperators.cpp b/movieobjects/dmepackoperators.cpp
new file mode 100644
index 0000000..96abf22
--- /dev/null
+++ b/movieobjects/dmepackoperators.cpp
@@ -0,0 +1,321 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmepackoperators.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmattribute.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// CDmePackColorOperator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePackColorOperator, CDmePackColorOperator );
+
+void CDmePackColorOperator::OnConstruction()
+{
+ m_color.Init( this, "color" );
+ m_red .Init( this, "red" );
+ m_green.Init( this, "green" );
+ m_blue .Init( this, "blue" );
+ m_alpha.Init( this, "alpha" );
+}
+
+void CDmePackColorOperator::OnDestruction()
+{
+}
+
+bool CDmePackColorOperator::IsDirty()
+{
+ const Color &c = m_color.Get();
+// float s = 255.999f;
+// return c.r() != s*m_red.Get() || c.g() != s*m_green.Get() || c.b() != s*m_blue.Get() || c.a() != s*m_alpha.Get();
+ return c.r() != m_red.Get() || c.g() != m_green.Get() || c.b() != m_blue.Get() || c.a() != m_alpha.Get();
+}
+
+void CDmePackColorOperator::Operate()
+{
+// float s = 255.999f;
+// m_color.Set( Color( s*m_red.Get(), s*m_green.Get(), s*m_blue.Get(), s*m_alpha.Get() ) );
+ m_color.Set( Color( m_red.Get(), m_green.Get(), m_blue.Get(), m_alpha.Get() ) );
+}
+
+void CDmePackColorOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_red.GetAttribute() );
+ attrs.AddToTail( m_green.GetAttribute() );
+ attrs.AddToTail( m_blue.GetAttribute() );
+ attrs.AddToTail( m_alpha.GetAttribute() );
+}
+
+void CDmePackColorOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_color.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmePackVector2Operator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePackVector2Operator, CDmePackVector2Operator );
+
+void CDmePackVector2Operator::OnConstruction()
+{
+ m_vector.Init( this, "vector" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+}
+
+void CDmePackVector2Operator::OnDestruction()
+{
+}
+
+bool CDmePackVector2Operator::IsDirty()
+{
+ const Vector2D &v = m_vector.Get();
+ return v.x != m_x.Get() || v.y != m_y.Get();
+}
+
+void CDmePackVector2Operator::Operate()
+{
+ m_vector.Set( Vector2D( m_x.Get(), m_y.Get() ) );
+}
+
+void CDmePackVector2Operator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+}
+
+void CDmePackVector2Operator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_vector.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmePackVector3Operator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePackVector3Operator, CDmePackVector3Operator );
+
+void CDmePackVector3Operator::OnConstruction()
+{
+ m_vector.Init( this, "vector" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+ m_z.Init( this, "z" );
+}
+
+void CDmePackVector3Operator::OnDestruction()
+{
+}
+
+bool CDmePackVector3Operator::IsDirty()
+{
+ const Vector &v = m_vector.Get();
+ return v.x != m_x.Get() || v.y != m_y.Get() || v.z != m_z.Get();
+}
+
+void CDmePackVector3Operator::Operate()
+{
+ m_vector.Set( Vector( m_x.Get(), m_y.Get(), m_z.Get() ) );
+}
+
+void CDmePackVector3Operator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+ attrs.AddToTail( m_z.GetAttribute() );
+}
+
+void CDmePackVector3Operator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_vector.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmePackVector4Operator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePackVector4Operator, CDmePackVector4Operator );
+
+void CDmePackVector4Operator::OnConstruction()
+{
+ m_vector.Init( this, "vector" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+ m_z.Init( this, "z" );
+ m_w.Init( this, "w" );
+}
+
+void CDmePackVector4Operator::OnDestruction()
+{
+}
+
+bool CDmePackVector4Operator::IsDirty()
+{
+ const Vector4D &v = m_vector.Get();
+ return v.x != m_x.Get() || v.y != m_y.Get() || v.z != m_z.Get() || v.w != m_w.Get();
+}
+
+void CDmePackVector4Operator::Operate()
+{
+ m_vector.Set( Vector4D( m_x.Get(), m_y.Get(), m_z.Get(), m_w.Get() ) );
+}
+
+void CDmePackVector4Operator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+ attrs.AddToTail( m_z.GetAttribute() );
+ attrs.AddToTail( m_w.GetAttribute() );
+}
+
+void CDmePackVector4Operator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_vector.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmePackQAngleOperator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePackQAngleOperator, CDmePackQAngleOperator );
+
+void CDmePackQAngleOperator::OnConstruction()
+{
+ m_qangle.Init( this, "qangle" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+ m_z.Init( this, "z" );
+}
+
+void CDmePackQAngleOperator::OnDestruction()
+{
+}
+
+bool CDmePackQAngleOperator::IsDirty()
+{
+ const QAngle &q = m_qangle.Get();
+ return q.x != m_x.Get() || q.y != m_y.Get() || q.z != m_z.Get();
+}
+
+void CDmePackQAngleOperator::Operate()
+{
+ m_qangle.Set( QAngle( m_x.Get(), m_y.Get(), m_z.Get() ) );
+}
+
+void CDmePackQAngleOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+ attrs.AddToTail( m_z.GetAttribute() );
+}
+
+void CDmePackQAngleOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_qangle.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmePackQuaternionOperator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePackQuaternionOperator, CDmePackQuaternionOperator );
+
+void CDmePackQuaternionOperator::OnConstruction()
+{
+ m_quaternion.Init( this, "quaternion" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+ m_z.Init( this, "z" );
+ m_w.Init( this, "w" );
+}
+
+void CDmePackQuaternionOperator::OnDestruction()
+{
+}
+
+bool CDmePackQuaternionOperator::IsDirty()
+{
+ const Quaternion &q = m_quaternion.Get();
+ return q.x != m_x.Get() || q.y != m_y.Get() || q.z != m_z.Get() || q.w != m_w.Get();
+}
+
+void CDmePackQuaternionOperator::Operate()
+{
+ m_quaternion.Set( Quaternion( m_x.Get(), m_y.Get(), m_z.Get(), m_w.Get() ) );
+}
+
+void CDmePackQuaternionOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+ attrs.AddToTail( m_z.GetAttribute() );
+ attrs.AddToTail( m_w.GetAttribute() );
+}
+
+void CDmePackQuaternionOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_quaternion.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmePackVMatrixOperator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePackVMatrixOperator, CDmePackVMatrixOperator );
+
+void CDmePackVMatrixOperator::OnConstruction()
+{
+ m_vmatrix.Init( this, "vmatrix" );
+ char name[ 4 ];
+ for ( uint i = 0; i < 16; ++i )
+ {
+ Q_snprintf( name, sizeof(name), "m%d%d", i >> 2, i & 0x3 );
+ m_cells[ i ].Init( this, name );
+ }
+}
+
+void CDmePackVMatrixOperator::OnDestruction()
+{
+}
+
+bool CDmePackVMatrixOperator::IsDirty()
+{
+ const VMatrix &v = m_vmatrix.Get();
+ for ( uint i = 0; i < 16; ++i )
+ {
+ if ( *( v[ i ] ) != m_cells[ i ].Get() )
+ return true;
+ }
+ return false;
+}
+
+void CDmePackVMatrixOperator::Operate()
+{
+ VMatrix v;
+ for ( uint i = 0; i < 16; ++i )
+ {
+ *( v[ i ] ) = m_cells[ i ].Get();
+ }
+ m_vmatrix.Set( v );
+}
+
+void CDmePackVMatrixOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ for ( uint i = 0; i < 16; ++i )
+ {
+ attrs.AddToTail( m_cells[i].GetAttribute() );
+ }
+}
+
+void CDmePackVMatrixOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_vmatrix.GetAttribute() );
+}
diff --git a/movieobjects/dmeparticlesystemdefinition.cpp b/movieobjects/dmeparticlesystemdefinition.cpp
new file mode 100644
index 0000000..d279d44
--- /dev/null
+++ b/movieobjects/dmeparticlesystemdefinition.cpp
@@ -0,0 +1,598 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmeparticlesystemdefinition.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmeeditortypedictionary.h"
+#include "toolutils/enginetools_int.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/convar.h"
+#include "particles/particles.h"
+#include "dme_controls/attributeintchoicepanel.h"
+#include "dme_controls/attributeboolchoicepanel.h"
+#include "dme_controls/attributestringchoicepanel.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+
+//-----------------------------------------------------------------------------
+// Human readable string for the particle functions
+//-----------------------------------------------------------------------------
+static const char *s_pParticleFuncTypeName[PARTICLE_FUNCTION_COUNT] =
+{
+ "Renderer", // FUNCTION_RENDERER = 0,
+ "Operator", // FUNCTION_OPERATOR,
+ "Initializer", // FUNCTION_INITIALIZER,
+ "Emitter", // FUNCTION_EMITTER,
+ "Children", // FUNCTION_CHILDREN,
+ "ForceGenerator", // FUNCTION_FORCEGENERATOR
+ "Constraint", // FUNCTION_CONSTRAINT
+};
+
+const char *GetParticleFunctionTypeName( ParticleFunctionType_t type )
+{
+ return s_pParticleFuncTypeName[type];
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY_INSTALL_EXPLICITLY( DmeParticleFunction, CDmeParticleFunction );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeParticleFunction::OnConstruction()
+{
+ m_bSkipNextResolve = false;
+}
+
+void CDmeParticleFunction::OnDestruction()
+{
+ DestroyElement( m_hTypeDictionary, TD_DEEP );
+}
+
+
+//-----------------------------------------------------------------------------
+// Construct an appropriate editor attribute info
+//-----------------------------------------------------------------------------
+static void CreateEditorAttributeInfo( CDmeEditorType *pEditorType, const char *pAttributeName, const char *pWidgetInfo )
+{
+ if ( !pWidgetInfo )
+ return;
+
+ CCommand parse;
+ parse.Tokenize( pWidgetInfo );
+ if ( parse.ArgC() == 1 )
+ {
+ CDmeEditorAttributeInfo *pInfo = CreateElement< CDmeEditorAttributeInfo >( "field info" );
+ pEditorType->AddAttributeInfo( pAttributeName, pInfo );
+ pInfo->m_Widget = parse[0];
+ return;
+ }
+
+ if ( parse.ArgC() == 2 )
+ {
+ CDmeEditorChoicesInfo *pInfo = NULL;
+ if ( !Q_stricmp( parse[0], "intchoice" ) )
+ {
+ pInfo = CreateElement< CDmeEditorIntChoicesInfo >( "field info" );
+ }
+
+ if ( !Q_stricmp( parse[0], "boolchoice" ) )
+ {
+ pInfo = CreateElement< CDmeEditorBoolChoicesInfo >( "field info" );
+ }
+
+ if ( !Q_stricmp( parse[0], "stringchoice" ) )
+ {
+ pInfo = CreateElement< CDmeEditorStringChoicesInfo >( "field info" );
+ }
+
+ if ( !Q_stricmp( parse[0], "elementchoice" ) )
+ {
+ pInfo = CreateElement< CDmeEditorChoicesInfo >( "field info" );
+ }
+
+ if ( pInfo )
+ {
+ pInfo->SetChoiceType( parse[1] );
+ pEditorType->AddAttributeInfo( pAttributeName, pInfo );
+ pInfo->m_Widget = parse[0];
+ return;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used for backward compat
+//-----------------------------------------------------------------------------
+void CDmeParticleFunction::AddMissingFields( const DmxElementUnpackStructure_t *pUnpack )
+{
+ DestroyElement( m_hTypeDictionary, TD_DEEP );
+ m_hTypeDictionary = CreateElement< CDmeEditorTypeDictionary >( "particleFunctionDict" );
+ CDmeEditorType *pEditorType = CreateElement< CDmeEditorType >( GetTypeString() );
+
+ for ( ; pUnpack->m_pAttributeName; ++pUnpack )
+ {
+ CreateEditorAttributeInfo( pEditorType, pUnpack->m_pAttributeName, (const char *)pUnpack->m_pUserData );
+
+ // Can happen if 'name' or 'functionName' is used
+ if ( HasAttribute( pUnpack->m_pAttributeName ) )
+ continue;
+
+ CDmAttribute *pAttribute = AddAttribute( pUnpack->m_pAttributeName, pUnpack->m_AttributeType );
+ if ( pUnpack->m_pDefaultString )
+ {
+ int nLen = Q_strlen( pUnpack->m_pDefaultString );
+ CUtlBuffer bufParse( pUnpack->m_pDefaultString, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+ pAttribute->Unserialize( bufParse );
+ }
+ }
+
+ m_hTypeDictionary->AddEditorType( pEditorType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the particle operator
+//-----------------------------------------------------------------------------
+void CDmeParticleFunction::UpdateAttributes( const DmxElementUnpackStructure_t *pUnpack )
+{
+ // Delete all old attributes
+ CDmAttribute *pNext;
+ for( CDmAttribute *pAttr = FirstAttribute(); pAttr; pAttr = pNext )
+ {
+ pNext = pAttr->NextAttribute();
+ if ( pAttr->IsFlagSet( FATTRIB_EXTERNAL | FATTRIB_STANDARD ) )
+ continue;
+
+ RemoveAttributeByPtr( pAttr );
+ }
+
+ AddMissingFields( pUnpack );
+}
+
+
+//-----------------------------------------------------------------------------
+// Marks a particle system as a new instance
+// This is basically a workaround to prevent newly-copied particle functions
+// from recompiling themselves a zillion times
+//-----------------------------------------------------------------------------
+void CDmeParticleFunction::MarkNewInstance()
+{
+ m_bSkipNextResolve = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Don't bother resolving during unserialization, the owning def will handle it
+//-----------------------------------------------------------------------------
+void CDmeParticleFunction::OnElementUnserialized()
+{
+ BaseClass::OnElementUnserialized();
+ MarkNewInstance();
+}
+
+
+//-----------------------------------------------------------------------------
+// Recompiles the particle system when a change occurs
+//-----------------------------------------------------------------------------
+void CDmeParticleFunction::Resolve()
+{
+ BaseClass::Resolve();
+
+ if ( m_bSkipNextResolve )
+ {
+ m_bSkipNextResolve = false;
+ return;
+ }
+
+ for( CDmAttribute* pAttr = FirstAttribute(); pAttr; pAttr = pAttr->NextAttribute() )
+ {
+ if ( !pAttr->IsFlagSet( FATTRIB_DIRTY ) )
+ continue;
+
+ // Find all CDmeParticleSystemDefinitions referring to this function
+ DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( GetHandle() );
+ while ( i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
+ {
+ CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
+
+ // NOTE: This could cause the same particle system definition to recompile
+ // multiple times if it refers to the same function multiple times,
+ // but we don't expect that to happen, so we won't bother checking for it
+ CDmeParticleSystemDefinition *pDef = CastElement<CDmeParticleSystemDefinition>( pAttribute->GetOwner() );
+ if ( pDef && pDef->GetFileId() == GetFileId() )
+ {
+ pDef->RecompileParticleSystem();
+ }
+ i = g_pDataModel->NextAttributeReferencingElement( i );
+ }
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the editor type dictionary
+//-----------------------------------------------------------------------------
+CDmeEditorTypeDictionary* CDmeParticleFunction::GetEditorTypeDictionary()
+{
+ return m_hTypeDictionary;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY_INSTALL_EXPLICITLY( DmeParticleOperator, CDmeParticleOperator );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeParticleOperator::OnConstruction()
+{
+ m_FunctionName.Init( this, "functionName" );
+}
+
+void CDmeParticleOperator::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the particle operator
+//-----------------------------------------------------------------------------
+void CDmeParticleOperator::SetFunction( IParticleOperatorDefinition *pDefinition )
+{
+ m_FunctionName = pDefinition->GetName();
+ const DmxElementUnpackStructure_t *pUnpack = pDefinition->GetUnpackStructure();
+ UpdateAttributes( pUnpack );
+}
+
+const char *CDmeParticleOperator::GetFunctionType() const
+{
+ return m_FunctionName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY_INSTALL_EXPLICITLY( DmeParticleChild, CDmeParticleChild );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeParticleChild::OnConstruction()
+{
+ m_Child.Init( this, "child", FATTRIB_NEVERCOPY );
+}
+
+void CDmeParticleChild::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the particle system child
+//-----------------------------------------------------------------------------
+void CDmeParticleChild::SetChildParticleSystem( CDmeParticleSystemDefinition *pDef, IParticleOperatorDefinition *pDefinition )
+{
+ // FIXME: Convert system name into a
+ m_Child = pDef;
+ const DmxElementUnpackStructure_t *pUnpack = pDefinition->GetUnpackStructure();
+ UpdateAttributes( pUnpack );
+}
+
+const char *CDmeParticleChild::GetFunctionType() const
+{
+ const CDmeParticleSystemDefinition *pChild = m_Child;
+ return pChild ? pChild->GetName() : "";
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY_INSTALL_EXPLICITLY( DmeParticleSystemDefinition, CDmeParticleSystemDefinition );
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeParticleSystemDefinition::OnConstruction()
+{
+ m_ParticleFunction[FUNCTION_RENDERER].Init( this, "renderers" );
+ m_ParticleFunction[FUNCTION_OPERATOR].Init( this, "operators" );
+ m_ParticleFunction[FUNCTION_INITIALIZER].Init( this, "initializers" );
+ m_ParticleFunction[FUNCTION_EMITTER].Init( this, "emitters" );
+ m_ParticleFunction[FUNCTION_CHILDREN].Init( this, "children" );
+ m_ParticleFunction[FUNCTION_FORCEGENERATOR].Init( this, "forces" );
+ m_ParticleFunction[FUNCTION_CONSTRAINT].Init( this, "constraints" );
+ m_bPreventNameBasedLookup.Init( this, "preventNameBasedLookup" );
+
+ m_hTypeDictionary = CreateElement< CDmeEditorTypeDictionary >( "particleSystemDefinitionDict" );
+ CDmeEditorType *pEditorType = CreateElement< CDmeEditorType >( "DmeParticleSystemDefinition" );
+
+ const DmxElementUnpackStructure_t *pUnpack = g_pParticleSystemMgr->GetParticleSystemDefinitionUnpackStructure();
+ for ( ; pUnpack->m_pAttributeName; ++pUnpack )
+ {
+ CreateEditorAttributeInfo( pEditorType, pUnpack->m_pAttributeName, (const char *)pUnpack->m_pUserData );
+
+ CDmAttribute *pAttribute = AddAttribute( pUnpack->m_pAttributeName, pUnpack->m_AttributeType );
+ if ( pUnpack->m_pDefaultString )
+ {
+ int nLen = Q_strlen( pUnpack->m_pDefaultString );
+ CUtlBuffer bufParse( pUnpack->m_pDefaultString, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+ pAttribute->Unserialize( bufParse );
+ }
+ }
+
+ m_hTypeDictionary->AddEditorType( pEditorType );
+}
+
+void CDmeParticleSystemDefinition::OnDestruction()
+{
+ DestroyElement( m_hTypeDictionary, TD_DEEP );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the editor type dictionary
+//-----------------------------------------------------------------------------
+CDmeEditorTypeDictionary* CDmeParticleSystemDefinition::GetEditorTypeDictionary()
+{
+ return m_hTypeDictionary;
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove obsolete attributes
+//-----------------------------------------------------------------------------
+static void RemoveObsoleteAttributes( CDmElement *pElement, const DmxElementUnpackStructure_t *pUnpack )
+{
+ // Delete all obsolete attributes
+ CDmAttribute *pNext;
+ for( CDmAttribute *pAttr = pElement->FirstAttribute(); pAttr; pAttr = pNext )
+ {
+ pNext = pAttr->NextAttribute();
+ if ( pAttr->IsFlagSet( FATTRIB_EXTERNAL | FATTRIB_STANDARD ) )
+ continue;
+
+ bool bFound = false;
+ for ( const DmxElementUnpackStructure_t *pTrav = pUnpack; pTrav->m_pAttributeName; ++pTrav )
+ {
+ if ( !Q_stricmp( pTrav->m_pAttributeName, pAttr->GetName() ) )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if ( !bFound )
+ {
+ pElement->RemoveAttributeByPtr( pAttr );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Used for automatic handling of backward compatability
+//-----------------------------------------------------------------------------
+void CDmeParticleSystemDefinition::OnElementUnserialized()
+{
+ BaseClass::OnElementUnserialized();
+
+ RemoveObsoleteAttributes( this, g_pParticleSystemMgr->GetParticleSystemDefinitionUnpackStructure() );
+
+ // Add missing fields that are new
+ for ( int i = 0; i < PARTICLE_FUNCTION_COUNT; ++i )
+ {
+ ParticleFunctionType_t type = (ParticleFunctionType_t)i;
+ CUtlVector< IParticleOperatorDefinition *> &list = g_pParticleSystemMgr->GetAvailableParticleOperatorList( type );
+ int nAvailType = list.Count();
+ int nCount = GetParticleFunctionCount( type );
+ for ( int j = 0; j < nCount; ++j )
+ {
+ CDmeParticleFunction *pFunction = GetParticleFunction( type, j );
+
+ if ( i == FUNCTION_CHILDREN )
+ {
+ RemoveObsoleteAttributes( pFunction, list[0]->GetUnpackStructure() );
+ pFunction->AddMissingFields( list[0]->GetUnpackStructure() );
+ continue;
+ }
+
+ for ( int k = 0; k < nAvailType; ++k )
+ {
+ if ( Q_stricmp( pFunction->GetName(), list[k]->GetName() ) )
+ continue;
+
+ RemoveObsoleteAttributes( pFunction, list[k]->GetUnpackStructure() );
+ pFunction->AddMissingFields( list[k]->GetUnpackStructure() );
+ break;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Check to see if any attributes changed
+//-----------------------------------------------------------------------------
+void CDmeParticleSystemDefinition::Resolve()
+{
+ BaseClass::Resolve();
+ for( CDmAttribute* pAttr = FirstAttribute(); pAttr; pAttr = pAttr->NextAttribute() )
+ {
+ if ( pAttr->IsFlagSet( FATTRIB_DIRTY ) )
+ {
+ RecompileParticleSystem();
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Add, remove
+//-----------------------------------------------------------------------------
+CDmeParticleFunction* CDmeParticleSystemDefinition::AddOperator( ParticleFunctionType_t type, const char *pFunctionName )
+{
+ CUtlVector< IParticleOperatorDefinition *> &list = g_pParticleSystemMgr->GetAvailableParticleOperatorList( type );
+
+ int nCount = list.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( Q_stricmp( pFunctionName, list[i]->GetName() ) )
+ continue;
+
+ CDmeParticleOperator *pFunction = CreateElement< CDmeParticleOperator >( pFunctionName, GetFileId() );
+ m_ParticleFunction[type].AddToTail( pFunction );
+ pFunction->SetFunction( list[i] );
+ return pFunction;
+ }
+ return NULL;
+}
+
+CDmeParticleFunction* CDmeParticleSystemDefinition::AddChild( CDmeParticleSystemDefinition *pChild )
+{
+ Assert( pChild );
+
+ CUtlVector< IParticleOperatorDefinition *> &list = g_pParticleSystemMgr->GetAvailableParticleOperatorList( FUNCTION_CHILDREN );
+ Assert( list.Count() == 1 );
+ CDmeParticleChild *pFunction = CreateElement< CDmeParticleChild >( pChild->GetName(), GetFileId() );
+ m_ParticleFunction[FUNCTION_CHILDREN].AddToTail( pFunction );
+ pFunction->SetChildParticleSystem( pChild, list[0] );
+ return pFunction;
+}
+
+//-----------------------------------------------------------------------------
+// Remove
+void CDmeParticleSystemDefinition::RemoveFunction( ParticleFunctionType_t type, CDmeParticleFunction *pFunction )
+{
+ int nIndex = FindFunction( type, pFunction );
+ RemoveFunction( type, nIndex );
+}
+
+void CDmeParticleSystemDefinition::RemoveFunction( ParticleFunctionType_t type, int nIndex )
+{
+ if ( nIndex >= 0 )
+ {
+ m_ParticleFunction[type].Remove(nIndex);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Find
+//-----------------------------------------------------------------------------
+int CDmeParticleSystemDefinition::FindFunction( ParticleFunctionType_t type, CDmeParticleFunction *pParticleFunction )
+{
+ int nCount = m_ParticleFunction[type].Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( pParticleFunction == m_ParticleFunction[type][i] )
+ return i;
+ }
+ return -1;
+}
+
+int CDmeParticleSystemDefinition::FindFunction( ParticleFunctionType_t type, const char *pFunctionName )
+{
+ int nCount = m_ParticleFunction[type].Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pFunctionName, m_ParticleFunction[type][i]->GetFunctionType() ) )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Iteration
+//-----------------------------------------------------------------------------
+int CDmeParticleSystemDefinition::GetParticleFunctionCount( ParticleFunctionType_t type ) const
+{
+ return m_ParticleFunction[type].Count();
+}
+
+CDmeParticleFunction *CDmeParticleSystemDefinition::GetParticleFunction( ParticleFunctionType_t type, int nIndex )
+{
+ return m_ParticleFunction[type][nIndex];
+}
+
+
+//-----------------------------------------------------------------------------
+// Reordering
+//-----------------------------------------------------------------------------
+void CDmeParticleSystemDefinition::MoveFunctionUp( ParticleFunctionType_t type, CDmeParticleFunction *pElement )
+{
+ int nIndex = FindFunction( type, pElement );
+ if ( nIndex > 0 )
+ {
+ m_ParticleFunction[type].Swap( nIndex, nIndex - 1 );
+ }
+}
+
+void CDmeParticleSystemDefinition::MoveFunctionDown( ParticleFunctionType_t type, CDmeParticleFunction *pElement )
+{
+ int nIndex = FindFunction( type, pElement );
+ int nLastIndex = m_ParticleFunction[type].Count() - 1;
+ if ( nIndex >= 0 && nIndex < nLastIndex )
+ {
+ m_ParticleFunction[type].Swap( nIndex, nIndex + 1 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Marks a particle system as a new instance
+// This is basically a workaround to prevent newly-copied particle functions
+// from recompiling themselves a zillion times
+//-----------------------------------------------------------------------------
+void CDmeParticleSystemDefinition::MarkNewInstance()
+{
+ for ( int i = 0; i < PARTICLE_FUNCTION_COUNT; ++i )
+ {
+ int nCount = m_ParticleFunction[i].Count();
+ for ( int j = 0; j < nCount; ++j )
+ {
+ m_ParticleFunction[i][j]->MarkNewInstance();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Recompiles the particle system when a change occurs
+//-----------------------------------------------------------------------------
+void CDmeParticleSystemDefinition::RecompileParticleSystem()
+{
+ const char *pFileFormat = "pcf";
+ const char *pEncoding = g_pDataModel->GetDefaultEncoding( pFileFormat );
+ int nFlags = g_pDataModel->IsEncodingBinary( pEncoding ) ? 0 : CUtlBuffer::TEXT_BUFFER;
+ CUtlBuffer buf( 0, 0, nFlags );
+ if ( g_pDataModel->Serialize( buf, pEncoding, pFileFormat, GetHandle() ) )
+ {
+ g_pParticleSystemMgr->ReadParticleConfigFile( buf, true, NULL );
+ }
+}
+
+
diff --git a/movieobjects/dmephonememapping.cpp b/movieobjects/dmephonememapping.cpp
new file mode 100644
index 0000000..03a1c5d
--- /dev/null
+++ b/movieobjects/dmephonememapping.cpp
@@ -0,0 +1,22 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmephonememapping.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+//-----------------------------------------------------------------------------
+// CDmePhonemeMapping
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmePhonemeMapping, CDmePhonemeMapping );
+
+void CDmePhonemeMapping::OnConstruction()
+{
+ m_Preset.Init( this, "preset" );
+ m_Weight.InitAndSet( this, "weight", 1.0f );
+}
+
+void CDmePhonemeMapping::OnDestruction()
+{
+} \ No newline at end of file
diff --git a/movieobjects/dmeselection.cpp b/movieobjects/dmeselection.cpp
new file mode 100644
index 0000000..7528f88
--- /dev/null
+++ b/movieobjects/dmeselection.cpp
@@ -0,0 +1,577 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmeselection.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeComponent, CDmeComponent );
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeComponent::OnConstruction()
+{
+ m_Type.InitAndSet( this, "componentType", COMP_INVALID );
+ m_bComplete.InitAndSet( this, "complete", false );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeComponent::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeComponent::Resolve()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSingleIndexedComponent, CDmeSingleIndexedComponent );
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::OnConstruction()
+{
+ m_CompleteCount.InitAndSet( this, "completeCount", 0 );
+ m_Components.Init( this, "components" );
+ m_Weights.Init( this, "weights" );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::Resolve()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmeSingleIndexedComponent::Count() const
+{
+ return IsComplete() ? m_CompleteCount.Get() : m_Components.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeSingleIndexedComponent::SetType( Component_t type )
+{
+ switch ( type )
+ {
+ case COMP_VTX:
+ case COMP_FACE:
+ m_Type.Set( type );
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::AddComponent( int component, float weight /*= 1.0f */ )
+{
+ if ( IsComplete() )
+ return;
+
+ const int index( BinarySearch( component ) );
+ Assert( index >= 0 );
+ Assert( index <= m_Components.Count() );
+ if ( index == m_Components.Count() )
+ {
+ m_Components.AddToTail( component );
+ m_Weights.AddToTail( weight );
+ }
+ else if ( component == m_Components.Get( index ) )
+ {
+ Assert( index < m_Weights.Count() );
+ m_Weights.Set( index, weight );
+ }
+ else
+ {
+ m_Components.InsertBefore( index, component );
+ m_Weights.InsertBefore( index, weight );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::AddComponents( const CUtlVector< int > &components )
+{
+ const int nComponents( components.Count() );
+ for ( int i( 0 ); i < nComponents; ++i )
+ {
+ AddComponent( components[ i ] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::AddComponents(
+ const CUtlVector< int > &components, const CUtlVector< float > &weights )
+{
+ const int nComponents( min( components.Count(), weights.Count() ) );
+ for ( int i( 0 ); i < nComponents; ++i )
+ {
+ AddComponent( components[ i ], weights[ i ] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::RemoveComponent( int component )
+{
+ Assert( !IsComplete() ); // TODO: Convert from complete to complete - component
+
+ const int cIndex = BinarySearch( component );
+ if ( cIndex >= m_Components.Count() || m_Components[ cIndex ] != component ) // Component not in selection
+ return;
+
+ m_Components.Remove( cIndex );
+ m_Weights.Remove( cIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeSingleIndexedComponent::GetComponent( int index, int &component, float &weight ) const
+{
+ if ( index < Count() )
+ {
+ if ( IsComplete() )
+ {
+ component = index;
+ weight = 1.0f;
+ }
+ else
+ {
+ component = m_Components[ index ];
+ weight = m_Weights[ index ];
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::GetComponents( CUtlVector< int > &components, CUtlVector< float > &weights ) const
+{
+ if ( IsComplete() )
+ {
+ const int nComponents = Count();
+
+ int *pComponents = reinterpret_cast< int * >( alloca( nComponents * sizeof( int ) ) );
+ float *pWeights = reinterpret_cast< float * >( alloca( nComponents * sizeof( float ) ) );
+
+ for ( int i = 0; i < nComponents; ++i )
+ {
+ pComponents[ i ] = i;
+ pWeights[ i ] = 1.0f;
+ }
+
+ components.CopyArray( pComponents, nComponents );
+ weights.CopyArray( pWeights, nComponents );
+ }
+ else
+ {
+ components.RemoveAll();
+ components.CopyArray( m_Components.Base(), m_Components.Count() );
+
+ weights.RemoveAll();
+ weights.CopyArray( m_Weights.Base(), m_Weights.Count() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::GetComponents( CUtlVector< int > &components ) const
+{
+ if ( IsComplete() )
+ {
+ const int nComponents = Count();
+
+ int *pComponents = reinterpret_cast< int * >( alloca( nComponents * sizeof( int ) ) );
+
+ for ( int i = 0; i < nComponents; ++i )
+ {
+ pComponents[ i ] = i;
+ }
+
+ components.CopyArray( pComponents, nComponents );
+ }
+ else
+ {
+ components.RemoveAll();
+ components.CopyArray( m_Components.Base(), m_Components.Count() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::SetComplete( int nComplete )
+{
+ m_bComplete.Set( true );
+ m_CompleteCount.Set( max( 0, nComplete ) );
+ m_Components.Purge();
+ m_Weights.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmeSingleIndexedComponent::GetComplete() const
+{
+ return IsComplete() ? m_CompleteCount.Get() : 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Reset to an empty selection
+//-----------------------------------------------------------------------------
+inline void CDmeSingleIndexedComponent::Clear()
+{
+ CDmeComponent::Clear();
+ m_CompleteCount.Set( 0 );
+ m_Components.RemoveAll();
+ m_Weights.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Searches for the component in the sorted component list and returns the
+// index if it's found or if it's not found, returns the index at which it
+// should be inserted to maintain the sorted order of the component list
+//-----------------------------------------------------------------------------
+int CDmeSingleIndexedComponent::BinarySearch( int component ) const
+{
+ const CUtlVector< int > &components( m_Components.Get() );
+ const int nComponents( components.Count() );
+
+ int left( 0 );
+ int right( nComponents - 1 );
+ int mid;
+
+ while ( left <= right )
+ {
+ mid = ( left + right ) >> 1; // floor( ( left + right ) / 2.0 )
+ if ( component > m_Components[ mid ] )
+ {
+ left = mid + 1;
+ }
+ else if ( component < m_Components[ mid ] )
+ {
+ right = mid - 1;
+ }
+ else
+ {
+ return mid;
+ }
+ }
+
+ return left;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeSingleIndexedComponent::HasComponent( int component ) const
+{
+ if ( IsComplete() )
+ return true;
+
+ const int cIndex = BinarySearch( component );
+ if ( cIndex >= m_Components.Count() )
+ return false;
+
+ if ( m_Components[ cIndex ] == component )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmeSingleIndexedComponent::GetWeight( int component, float &weight ) const
+{
+ Assert( !IsComplete() );
+
+ const int cIndex = BinarySearch( component );
+ if ( cIndex >= m_Components.Count() )
+ return false;
+
+ if ( m_Components[ cIndex ] == component )
+ {
+ weight = m_Weights[ cIndex ];
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::Subtract( const CDmeSingleIndexedComponent &rhs )
+{
+ const int nLhs = Count();
+ const int nRhs = rhs.Count();
+
+ int l = 0;
+ int r = 0;
+
+ if ( IsComplete() )
+ {
+ // TODO
+ Assert( 0 );
+ }
+ else
+ {
+ CUtlVector< int > newComponents;
+ newComponents.EnsureCapacity( nLhs );
+ CUtlVector< float > newWeights;
+ newWeights.EnsureCapacity( nLhs );
+
+ while ( l < nLhs || r < nRhs )
+ {
+ // In LHS but not RHS
+ while ( l < nLhs && ( r >= nRhs || m_Components[ l ] < rhs.m_Components[ r ] ) )
+ {
+ newComponents.AddToTail( m_Components[ l ] );
+ newWeights.AddToTail( m_Weights[ l ] );
+ ++l;
+ }
+
+ // In RHS but not LHS
+ while ( r < nRhs && ( l >= nLhs || m_Components[ l ] > rhs.m_Components[ r ] ) )
+ {
+ ++r;
+ }
+
+ // In Both LHS & RHS
+ while ( l < nLhs && r < nRhs && m_Components[ l ] == rhs.m_Components[ r ] )
+ {
+ ++l;
+ ++r;
+ }
+ }
+
+ m_Components.CopyArray( newComponents.Base(), newComponents.Count() );
+ m_Weights.CopyArray( newWeights.Base(), newWeights.Count() );
+ }
+
+ m_CompleteCount.Set( 0 );
+ m_bComplete.Set( false );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::Add( const CDmeSingleIndexedComponent &rhs )
+{
+ int nLhs = Count();
+ const int nRhs = rhs.Count();
+
+ int l = 0;
+ int r = 0;
+
+ if ( IsComplete() )
+ {
+ if ( rhs.IsComplete() && nRhs > nLhs )
+ {
+ m_CompleteCount.Set( nRhs );
+ }
+ else
+ {
+ while ( r < nRhs )
+ {
+ if ( rhs.m_Components[ r ] >= nLhs )
+ {
+ // Got one that's greater than the complete count of this one
+
+ CUtlVector< int > newComponents;
+ newComponents.EnsureCapacity( nLhs + nRhs - r );
+
+ CUtlVector< float > newWeights;
+ newWeights.EnsureCapacity( nLhs + nRhs - r );
+
+ GetComponents( newComponents, newWeights );
+
+ while ( r < nRhs )
+ {
+ newComponents.AddToTail( rhs.m_Components[ r ] );
+ newWeights.AddToTail( rhs.m_Weights[ r ] );
+ ++r;
+ }
+
+ m_Components.CopyArray( newComponents.Base(), newComponents.Count() );
+ m_Weights.CopyArray( newWeights.Base(), newWeights.Count() );
+
+ m_CompleteCount.Set( 0 );
+ m_bComplete.Set( false );
+
+ break;
+ }
+
+ ++r;
+ }
+ }
+ }
+ else
+ {
+ CUtlVector< int > newComponents;
+ newComponents.EnsureCapacity( nLhs + nRhs * 0.5 ); // Just an estimate assuming 50% of the components in rhs aren't in lhs
+ CUtlVector< float > newWeights;
+ newWeights.EnsureCapacity( nLhs + nRhs * 0.5 ); // Just an estimate
+
+ while ( l < nLhs || r < nRhs )
+ {
+ while ( l < nLhs && ( r >= nRhs || m_Components[ l ] < rhs.m_Components[ r ] ) )
+ {
+ newComponents.AddToTail( m_Components[ l ] );
+ newWeights.AddToTail( m_Weights[ l ] );
+ ++l;
+ }
+
+ // In RHS but not LHS
+ while ( r < nRhs && ( l >= nLhs || m_Components[ l ] > rhs.m_Components[ r ] ) )
+ {
+ newComponents.AddToTail( rhs.m_Components[ r ] );
+ newWeights.AddToTail( rhs.m_Weights[ r ] );
+ ++r;
+ }
+
+ // In Both LHS & RHS
+ while ( l < nLhs && r < nRhs && m_Components[ l ] == rhs.m_Components[ r ] )
+ {
+ newComponents.AddToTail( m_Components[ l ] );
+ newWeights.AddToTail( m_Weights[ l ] );
+ ++l;
+ ++r;
+ }
+ }
+
+ m_Components.CopyArray( newComponents.Base(), newComponents.Count() );
+ m_Weights.CopyArray( newWeights.Base(), newWeights.Count() );
+ }
+
+ m_CompleteCount.Set( 0 );
+ m_bComplete.Set( false );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeSingleIndexedComponent::Intersection( const CDmeSingleIndexedComponent &rhs )
+{
+ const int nLhs = Count();
+ const int nRhs = rhs.Count();
+
+ int l = 0;
+ int r = 0;
+
+ if ( IsComplete() )
+ {
+ // TODO
+ Assert( 0 );
+ }
+ else
+ {
+ CUtlVector< int > newComponents;
+ newComponents.EnsureCapacity( nLhs );
+ CUtlVector< float > newWeights;
+ newWeights.EnsureCapacity( nLhs );
+
+ while ( l < nLhs || r < nRhs )
+ {
+ // In LHS but not RHS
+ while ( l < nLhs && ( r >= nRhs || m_Components[ l ] < rhs.m_Components[ r ] ) )
+ {
+ ++l;
+ }
+
+ // In RHS but not LHS
+ while ( r < nRhs && ( l >= nLhs || m_Components[ l ] > rhs.m_Components[ r ] ) )
+ {
+ ++r;
+ }
+
+ // In Both LHS & RHS
+ while ( l < nLhs && r < nRhs && m_Components[ l ] == rhs.m_Components[ r ] )
+ {
+ newComponents.AddToTail( m_Components[ l ] );
+ newWeights.AddToTail( m_Weights[ l ] );
+ ++l;
+ ++r;
+ }
+ }
+
+ m_Components.CopyArray( newComponents.Base(), newComponents.Count() );
+ m_Weights.CopyArray( newWeights.Base(), newWeights.Count() );
+ }
+
+ m_CompleteCount.Set( 0 );
+ m_bComplete.Set( false );
+} \ No newline at end of file
diff --git a/movieobjects/dmeshader.cpp b/movieobjects/dmeshader.cpp
new file mode 100644
index 0000000..8408124
--- /dev/null
+++ b/movieobjects/dmeshader.cpp
@@ -0,0 +1,260 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmeshader.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects_interfaces.h"
+
+#include "materialsystem/IShader.h"
+#include "materialsystem/imaterialsystem.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeShader, CDmeShader );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeShader::OnConstruction()
+{
+ m_ShaderName.Init( this, "shaderName" );
+
+ m_ShaderName = "wireframe";
+ m_pShader = NULL;
+}
+
+void CDmeShader::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Shader name access
+//-----------------------------------------------------------------------------
+void CDmeShader::SetShaderName( const char *pShaderName )
+{
+ m_ShaderName = pShaderName;
+}
+
+const char *CDmeShader::GetShaderName() const
+{
+ return m_ShaderName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a shader
+//-----------------------------------------------------------------------------
+IShader *CDmeShader::FindShader()
+{
+ int nCount = MaterialSystem()->ShaderCount();
+ IShader **ppShaderList = (IShader**)_alloca( nCount * sizeof(IShader*) );
+ MaterialSystem()->GetShaders( 0, nCount, ppShaderList );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( m_ShaderName, ppShaderList[i]->GetName() ) )
+ return ppShaderList[i];
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Remove all shader parameters that don't exist in the new shader
+//-----------------------------------------------------------------------------
+void CDmeShader::RemoveUnusedShaderParams( IShader *pShader )
+{
+ IDmAttribute* pAttribute = FirstAttribute();
+ IDmAttribute* pNextAttribute = NULL;
+ for ( ; pAttribute; pAttribute = pNextAttribute )
+ {
+ pNextAttribute = pAttribute->NextAttribute();
+
+ // Don't remove name, type, or id
+ if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
+ continue;
+
+ const char *pShaderParam = pAttribute->GetName();
+ int nCount = pShader->GetNumParams();
+ int i;
+ for ( i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pShaderParam, pShader->GetParamName( i ) ) )
+ break;
+ }
+
+ // No match? Remove it!
+ if ( i == nCount )
+ {
+ RemoveAttributeByPtr( pAttribute );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Add attribute for shader parameter
+//-----------------------------------------------------------------------------
+IDmAttribute* CDmeShader::AddAttributeForShaderParameter( IShader *pShader, int nIndex )
+{
+ ShaderParamType_t paramType = pShader->GetParamType( nIndex );
+ const char *pParamName = pShader->GetParamName( nIndex );
+
+ IDmAttribute *pAttribute = NULL;
+ switch ( paramType )
+ {
+ case SHADER_PARAM_TYPE_INTEGER:
+ pAttribute = AddAttributeTyped<int>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_BOOL:
+ pAttribute = AddAttributeTyped<bool>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_FLOAT:
+ pAttribute = AddAttributeTyped<float>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_STRING:
+ pAttribute = AddAttributeTyped<CUtlString>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_COLOR:
+ pAttribute = AddAttributeTyped<Color>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_VEC2:
+ pAttribute = AddAttributeTyped<Vector2D>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_VEC3:
+ pAttribute = AddAttributeTyped<Vector>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_VEC4:
+ pAttribute = AddAttributeTyped<Vector4D>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_FOURCC:
+ Assert( 0 );
+ break;
+
+ case SHADER_PARAM_TYPE_MATRIX:
+ pAttribute = AddAttributeTyped<VMatrix>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_TEXTURE:
+ pAttribute = AddAttributeTyped<CDmElementRef>( pParamName );
+ break;
+
+ case SHADER_PARAM_TYPE_MATERIAL:
+ pAttribute = AddAttributeTyped<CDmElementRef>( pParamName );
+ break;
+
+ default:
+ break;
+ }
+ return pAttribute;
+}
+
+
+//-----------------------------------------------------------------------------
+// Add all shader parameters that don't currently exist
+//-----------------------------------------------------------------------------
+void CDmeShader::AddNewShaderParams( IShader *pShader )
+{
+ int nCount = pShader->GetNumParams();
+ int i;
+ for ( i = 0; i < nCount; ++i )
+ {
+ const char *pParamName = pShader->GetParamName( i );
+
+ IDmAttribute* pAttribute = NULL;
+ for ( pAttribute = FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
+ {
+ // Don't remove name, type, or id
+ if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
+ continue;
+
+ const char *pAttributeName = pAttribute->GetName();
+ if ( !Q_stricmp( pAttributeName, pParamName ) )
+ break;
+ }
+
+ // No match? Add it!
+ if ( pAttribute != NULL )
+ continue;
+
+ pAttribute = AddAttributeForShaderParameter( pShader, i );
+ if ( pAttribute )
+ {
+ const char *pDefault = pShader->GetParamDefault( i );
+
+ SetAttributeValueFromString( pParamName, pDefault );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// resolve
+//-----------------------------------------------------------------------------
+void CDmeShader::Resolve()
+{
+ if ( !m_ShaderName.IsDirty() || !MaterialSystem() )
+ return;
+
+ // First, find the shader
+ IShader *pShader = FindShader();
+
+ // Remove all shader parameters that don't exist in the new shader
+ RemoveUnusedShaderParams( pShader );
+
+ // Add all shader parameters that don't currently exist
+ AddNewShaderParams( pShader );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a procedural material to be associated with this shader
+//-----------------------------------------------------------------------------
+void CDmeShader::CreateMaterial( const char *pMaterialName )
+{
+ KeyValues *pVMTKeyValues = new KeyValues( GetShaderName() );
+
+ IDmAttribute* pAttribute = FirstAttribute();
+ IDmAttribute* pNextAttribute = NULL;
+ for ( ; pAttribute; pAttribute = pNextAttribute )
+ {
+ pNextAttribute = pAttribute->NextAttribute();
+
+ // Don't remove name, type, or id
+ if ( pAttribute->IsFlagSet( FATTRIB_STANDARD ) )
+ continue;
+
+ const char *pShaderParam = pAttribute->GetName();
+ int nCount = pShader->GetNumParams();
+ int i;
+ for ( i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( pShaderParam, pShader->GetParamName( i ) ) )
+ break;
+ }
+
+ // No match? Remove it!
+ if ( i == nCount )
+ {
+ RemoveAttributeByPtr( pAttribute );
+ }
+ }
+
+ pVMTKeyValues->SetInt( "$model", 1 );
+ pVMTKeyValues->SetFloat( "$decalscale", 0.05f );
+ pVMTKeyValues->SetString( "$basetexture", "error" );
+ return MaterialSystem()->CreateMaterial( pMaterialName, pVMTKeyValues );
+}
diff --git a/movieobjects/dmeshape.cpp b/movieobjects/dmeshape.cpp
new file mode 100644
index 0000000..33a1f20
--- /dev/null
+++ b/movieobjects/dmeshape.cpp
@@ -0,0 +1,115 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmeshape.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects_interfaces.h"
+#include "movieobjects/dmedag.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeShape, CDmeShape );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeShape::OnConstruction()
+{
+ m_visible.InitAndSet( this, "visible", true );
+}
+
+void CDmeShape::OnDestruction()
+{
+}
+
+void CDmeShape::Draw( const matrix3x4_t &shapeToWorld, CDmeDrawSettings *pDrawSettings /* = NULL */ )
+{
+ Assert( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// The default bounding sphere is empty at the origin
+//-----------------------------------------------------------------------------
+void CDmeShape::GetBoundingSphere( Vector &c, float &r ) const
+{
+ c.Zero();
+ r = 0.0f;
+}
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmeShape::GetParentCount() const
+{
+ int nReferringDags = 0;
+
+ DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( GetHandle() );
+ while ( i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
+ {
+ CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
+ CDmeDag *pDag = CastElement< CDmeDag >( pAttribute->GetOwner() );
+ const static UtlSymId_t symShape = g_pDataModel->GetSymbol( "shape" );
+ if ( pDag && pAttribute->GetNameSymbol() == symShape && pDag->GetFileId() == GetFileId() )
+ {
+ ++nReferringDags;
+ }
+
+ i = g_pDataModel->NextAttributeReferencingElement( i );
+ }
+
+ return nReferringDags;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeDag *CDmeShape::GetParent( int nParentIndex /*= 0 */ ) const
+{
+ int nReferringDags = 0;
+
+ DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( GetHandle() );
+ while ( i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID )
+ {
+ CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
+ CDmeDag *pDag = CastElement< CDmeDag >( pAttribute->GetOwner() );
+ const static UtlSymId_t symShape = g_pDataModel->GetSymbol( "shape" );
+ if ( pDag && pAttribute->GetNameSymbol() == symShape && pDag->GetFileId() == GetFileId() )
+ {
+ if ( nReferringDags == nParentIndex )
+ return pDag;
+
+ ++nReferringDags;
+ }
+
+ i = g_pDataModel->NextAttributeReferencingElement( i );
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeShape::GetShapeToWorldTransform( matrix3x4_t &mat, int nParentIndex /*= 0 */ ) const
+{
+ CDmeDag *pDag = GetParent( nParentIndex );
+
+ if ( pDag )
+ {
+ pDag->GetShapeToWorldTransform( mat );
+ }
+ else
+ {
+ SetIdentityMatrix( mat );
+ }
+}
diff --git a/movieobjects/dmesound.cpp b/movieobjects/dmesound.cpp
new file mode 100644
index 0000000..d94c732
--- /dev/null
+++ b/movieobjects/dmesound.cpp
@@ -0,0 +1,93 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmesound.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects_interfaces.h"
+#include "tier2/tier2.h"
+#include "filesystem.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeSound, CDmeSound );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeSound::OnConstruction()
+{
+ m_SoundName.Init( this, "soundname" );
+ m_GameSoundName.Init( this, "gameSoundName" );
+}
+
+void CDmeSound::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// For sounds that are relative paths (instead of GameSound names), get full path
+//-----------------------------------------------------------------------------
+bool CDmeSound::ComputeSoundFullPath( char *pBuf, int nBufLen )
+{
+ if ( !m_SoundName[0] )
+ {
+ pBuf[0] = 0;
+ return false;
+ }
+
+ // Compute the full path of the sound
+ char pRelativePath[MAX_PATH];
+ Q_snprintf( pRelativePath, sizeof(pRelativePath), "sound\\%s", m_SoundName.Get() );
+ return g_pFullFileSystem->RelativePathToFullPath( pRelativePath, "GAME", pBuf, nBufLen ) != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeGameSound, CDmeGameSound );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeGameSound::OnConstruction()
+{
+ m_Volume .Init( this, "volume" );
+ m_Level .Init( this, "level" );
+ m_Pitch .Init( this, "pitch" );
+
+ m_IsStatic .Init( this, "static" );
+ m_Channel .Init( this, "channel" );
+ m_Flags .Init( this, "flags" );
+
+// m_Source .Init( this, "source" );
+// m_FollowSource.Init( this, "followsource" );
+ m_Origin .Init( this, "origin" );
+ m_Direction .Init( this, "direction" );
+}
+
+void CDmeGameSound::OnDestruction()
+{
+}
+
+CDmElement *CDmeGameSound::FindOrAddPhonemeExtractionSettings()
+{
+ if ( HasAttribute( "PhonemeExtractionSettings" ) )
+ return GetValueElement< CDmElement >( "PhonemeExtractionSettings" );
+
+ CDmElement *settings = CreateElement< CDmElement >( "PhonemeExtractionSettings", GetFileId() );
+ if ( !settings )
+ return NULL;
+
+ SetValue( "PhonemeExtractionSettings", settings );
+ return settings;
+}
diff --git a/movieobjects/dmetestmesh.cpp b/movieobjects/dmetestmesh.cpp
new file mode 100644
index 0000000..f3e57af
--- /dev/null
+++ b/movieobjects/dmetestmesh.cpp
@@ -0,0 +1,1757 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmetestmesh.h"
+#include "movieobjects/dmetransform.h"
+#include "movieobjects_interfaces.h"
+
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "mathlib/vector.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imesh.h"
+#include "datacache/imdlcache.h"
+#include "istudiorender.h"
+#include "studio.h"
+#include "bone_setup.h"
+#include "materialsystem/ivertextexture.h"
+#include "morphdata.h"
+#include "tier3/tier3.h"
+
+#include <strstream>
+#include <fstream>
+#include <algorithm>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTestMesh, CDmeTestMesh );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::OnConstruction()
+{
+ m_MDLHandle = MDLHANDLE_INVALID;
+ m_pMaterial = NULL;
+ m_pMesh = NULL;
+ m_pMorph = NULL;
+ m_pControlCage = NULL;
+ SetValue( "transform", g_pDataModel->IsUnserializing() ? NULL : CreateElement< CDmeTransform >( "transform", GetFileId() ) );
+ SetValue( "mdlfilename", "models/alyx.mdl" );
+ SetValue( "morphfilename", "models/alyx.morph" );
+ SetValue( "skin", 0 );
+ SetValue( "body", 0 );
+ SetValue( "sequence", 0 );
+ SetValue( "lod", 0 );
+ SetValue( "playbackrate", 1.0f );
+ SetValue( "time", 0.0f );
+ SetValue( "subdivlevel", 1 );
+}
+
+void CDmeTestMesh::OnDestruction()
+{
+ UnloadMorphData();
+ UnreferenceMDL();
+ DestroyControlCage();
+ DestroyMesh();
+}
+
+
+//-----------------------------------------------------------------------------
+// Addref/Release the MDL handle
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::ReferenceMDL( const char *pMDLName )
+{
+ if ( !g_pMDLCache )
+ return;
+
+ if ( pMDLName && pMDLName[0] )
+ {
+ Assert( m_MDLHandle == MDLHANDLE_INVALID );
+ m_MDLHandle = g_pMDLCache->FindMDL( pMDLName );
+ }
+}
+
+void CDmeTestMesh::UnreferenceMDL()
+{
+ if ( !g_pMDLCache )
+ return;
+
+ if ( m_MDLHandle != MDLHANDLE_INVALID )
+ {
+ g_pMDLCache->Release( m_MDLHandle );
+ m_MDLHandle = MDLHANDLE_INVALID;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates the mesh to draw
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::CreateMesh()
+{
+ DestroyMesh();
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ m_pMaterial = g_pMaterialSystem->FindMaterial( "shadertest/vertextexturetest", NULL, false );
+ m_pMesh = pRenderContext->CreateStaticMesh( m_pMaterial, 0, "dmemesh" );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( m_pMesh, MATERIAL_TRIANGLES, 8, 36 );
+
+ // Draw a simple cube
+ static Vector s_pPositions[8] =
+ {
+ Vector( -10, -10, -10 ),
+ Vector( 10, -10, -10 ),
+ Vector( -10, 10, -10 ),
+ Vector( 10, 10, -10 ),
+ Vector( -10, -10, 10 ),
+ Vector( 10, -10, 10 ),
+ Vector( -10, 10, 10 ),
+ Vector( 10, 10, 10 ),
+ };
+
+ static Vector2D s_pTexCoords[8] =
+ {
+ Vector2D( 0, 0 ),
+ Vector2D( 0.5, 0 ),
+ Vector2D( 0, 0.5 ),
+ Vector2D( 0.5, 0.5 ),
+ Vector2D( 0.5, 0.5 ),
+ Vector2D( 1, 0.5 ),
+ Vector2D( 0.5, 1 ),
+ Vector2D( 1, 1 ),
+ };
+
+ static unsigned char s_pColor[8][3] =
+ {
+ { 255, 255, 255 },
+ { 0, 255, 255 },
+ { 255, 0, 255 },
+ { 255, 255, 0 },
+ { 255, 0, 0 },
+ { 0, 255, 0 },
+ { 0, 0, 255 },
+ { 0, 0, 0 },
+ };
+
+ static int s_pIndices[12][3] =
+ {
+ { 0, 1, 5 }, { 0, 5, 4 },
+ { 4, 5, 7 }, { 4, 7, 6 },
+ { 0, 4, 6 }, { 0, 6, 2 },
+ { 0, 2, 3 }, { 0, 3, 1 },
+ { 1, 3, 7 }, { 1, 7, 5 },
+ { 2, 6, 7 }, { 2, 7, 3 },
+ };
+
+ for ( int i = 0; i < 8; ++i )
+ {
+ meshBuilder.Position3fv( s_pPositions[ i ].Base() );
+ meshBuilder.TexCoord2fv( 0, s_pTexCoords[ i ].Base() );
+// meshBuilder.TexCoord2f( 1, i, 0.0f );
+ meshBuilder.Color3ubv( s_pColor[ i ] );
+ meshBuilder.AdvanceVertex();
+ }
+
+ for ( int i = 0; i < 12; ++i )
+ {
+ meshBuilder.FastIndex( s_pIndices[i][0] );
+ meshBuilder.FastIndex( s_pIndices[i][1] );
+ meshBuilder.FastIndex( s_pIndices[i][2] );
+ }
+
+ meshBuilder.End();
+}
+
+void CDmeTestMesh::DestroyMesh()
+{
+ if ( m_pMesh )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->DestroyStaticMesh( m_pMesh );
+ m_pMesh = NULL;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Morph data
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::LoadMorphData( const char *pMorphFile, int nVertexCount )
+{
+ UnloadMorphData();
+
+ IMorphData *pMorphData = CreateMorphData();
+ m_pMorph = pMorphData->Compile( pMorphFile, m_pMaterial, nVertexCount );
+ DestroyMorphData( pMorphData );
+}
+
+void CDmeTestMesh::UnloadMorphData()
+{
+ if ( m_pMorph )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->DestroyMorph( m_pMorph );
+ m_pMorph = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// This function gets called whenever an attribute changes
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::Resolve()
+{
+ CDmAttribute *pMDLFilename = GetAttribute( "mdlfilename" );
+ if ( pMDLFilename && pMDLFilename->IsFlagSet( FATTRIB_DIRTY ) )
+ {
+ UnreferenceMDL();
+ ReferenceMDL( GetValueString( "mdlfilename" ) );
+ return;
+ }
+
+ CDmAttribute *pMorphFilename = GetAttribute( "morphfilename" );
+ if ( pMorphFilename && pMorphFilename->IsFlagSet( FATTRIB_DIRTY ) )
+ {
+ CreateMesh();
+
+ UnloadMorphData();
+ LoadMorphData( GetValueString( "morphfilename" ), 8 );
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads the model matrix based on the transform
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::LoadModelMatrix( CDmeTransform *pTransform )
+{
+ // FIXME: Should this go into the DmeTransform node?
+ matrix3x4_t transform;
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pTransform->GetTransform( transform );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->LoadMatrix( transform );
+}
+
+
+//-----------------------------------------------------------------------------
+// A subvision mesh
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// NOTES:
+// The subdivision mesh is fast because it assumes a very particular ordering
+// and definition of the data so that it can determine all subdivided data by
+// inspection without any searching. Here's the layout:
+//
+// First, a face stores a list of edge indices which reference the edges
+// that make up the face. A face is assumed to traverse its vertices in CCW order.
+// We define the "relative edge index" for an edge within a face as the
+// order in which that edge is visited while traversing the edges in CCW order,
+// so 0 is the first visited edge, and 1 is the next, etc.
+//
+// First, edges are defined in a specific way. The edge is assumed to be
+// *directed*, starting at vertex 0 and leading toward vertex 1. Now imagine the
+// two faces that shared this edge and that they both traverse their edges in
+// a right-handed, or CCW direction. Face 0 associated with the edge, to maintain
+// a CCW ordering, must traverse the edge in a *reverse* direction, heading from
+// vertex 1 to vertex 0. Face 1 associated with the edge traverses the edge
+// in a forward direction, from vertex 0 to vertex 1.
+//
+// When subdivision happens, it occurs in a very specific way also. First, when
+// creating the new vertices, for uniform subdivision, we create a new vertex
+// per face, a new vertex per edge, and adjust all existing vertices. When creating
+// these vertices in the subdivided mesh, we first add the face midpoint vertices,
+// then the edge midpoint vertices, then the vertices from the un-subdivided mesh, to
+// the m_Vertices array of the subdivided mesh.
+//
+// Edge subdivision always works in a uniform way: For each edge in the unsubdivided
+// mesh, 4 edges are created from the edge midpoint, connecting to the two
+// face midpoint vertices and the two edge endpoints. In order to maintain the
+// specific ordering of the edges described above, we define the edges in the
+// following manner:
+// * Subdivided edge 0 : Starts at face 0 midpoint, ends at edge midpoint
+// * Subdivided edge 1 : Starts at edge midpoint, ends at face 1 midpoint
+// * Subdivided edge 2 : Starts at original edge's vertex 0, ends at edge midpoint
+// * Subdivided edge 3 : Starts at edge midpoint, ends at original edge's vertex 1
+//
+// Face subdivision *also* always works in a uniform way: For each face in the
+// unsubdivided mesh, N new faces are created, one for each edge in the unsubdivided
+// face. The faces are ordered in a very specific way:
+// * Subdivided face 0 : Starts at the face midpoint, goes to unsubdivided edge 0's midpoint,
+// winds around the edge until it hits unsubdivided edge 1's midpoint,
+// then heads back to the face midpoint.
+// * Subdivided face 1 : Starts at the face midpoint, goes to unsubdivided edge 1's midpoint,
+// winds around the edge until it hits unsubdivided edge 2's midpoint,
+// then heads back to the face midpoint.
+// etc.
+//-----------------------------------------------------------------------------
+struct SubdivVertex_t
+{
+ Vector m_vecPosition;
+ Vector m_vecNormal;
+ Vector m_vecTexCoord;
+ int m_nValence;
+};
+
+// NOTE: The edge is always defined such that the edge going from vertex[0] to vertex[1]
+// is counter-clockwise when seen from face[1] and and clockwise when seen from face[0].
+struct Edge_t
+{
+ int m_pFace[2];
+ int m_pRelativeEdgeIndex[2]; // Goes from 0-N always, specifies the Nth edge of the polygon it's part of for each of the two faces
+ int m_pVertex[2];
+};
+
+struct Face_t
+{
+ int m_nFirstEdgeIndex;
+ int m_nEdgeCount;
+
+ // Stores the index of the first face in the subdivided mesh
+ // isn't actually a part of the mesh data, but I'm storing it here to reduce number of allocations to make
+ mutable int m_nFirstSubdividedFace;
+};
+
+struct SubdivMesh_t
+{
+ CUtlVector<SubdivVertex_t> m_Vertices;
+ CUtlVector<Edge_t> m_Edges;
+
+ // Positive values mean read from m_Edges[x], use m_pVertex[0] for leading vertex
+ // Negative values mean read from m_Edges[-1-x], use m_pVertex[1] for leading vertex
+ CUtlVector<int> m_EdgeIndices;
+ CUtlVector<Face_t> m_Faces;
+
+ int m_nTotalIndexCount;
+ int m_nTotalLineCount;
+};
+
+
+//-----------------------------------------------------------------------------
+// Clears a mesh
+//-----------------------------------------------------------------------------
+static void ClearMesh( SubdivMesh_t &dest )
+{
+ dest.m_Vertices.RemoveAll();
+ dest.m_Edges.RemoveAll();
+ dest.m_EdgeIndices.RemoveAll();
+ dest.m_Faces.RemoveAll();
+ dest.m_nTotalIndexCount = 0;
+ dest.m_nTotalLineCount = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the leading vertex of an edge
+//-----------------------------------------------------------------------------
+static inline int GetLeadingEdgeVertexIndex( const SubdivMesh_t &src, int nEdge )
+{
+ if ( nEdge >= 0 )
+ {
+ const Edge_t &edge = src.m_Edges[nEdge];
+ return edge.m_pVertex[0];
+ }
+
+ const Edge_t &edge = src.m_Edges[ -1 - nEdge ];
+ return edge.m_pVertex[1];
+}
+
+static inline const SubdivVertex_t &GetLeadingEdgeVertex( const SubdivMesh_t &src, int nEdge )
+{
+ return src.m_Vertices[ GetLeadingEdgeVertexIndex( src, nEdge ) ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds face midpoints to a mesh
+//-----------------------------------------------------------------------------
+static void AddFaceMidpointsToMesh( const SubdivMesh_t &src, SubdivMesh_t &dest )
+{
+ int nCurrSubdividedFace = 0;
+
+ int nSrcFaceCount = src.m_Faces.Count();
+ for ( int i = 0; i < nSrcFaceCount; ++i )
+ {
+ int nEdgeCount = src.m_Faces[i].m_nEdgeCount;
+ int nEdgeIndex = src.m_Faces[i].m_nFirstEdgeIndex;
+
+ Assert( nEdgeCount != 0 );
+
+ int v = dest.m_Vertices.AddToTail( );
+ SubdivVertex_t &vert = dest.m_Vertices[v];
+ vert.m_vecPosition.Init();
+ vert.m_vecTexCoord.Init();
+ vert.m_nValence = nEdgeCount;
+
+ for ( int j = 0; j < nEdgeCount; ++j, ++nEdgeIndex )
+ {
+ // NOTE: Instead of calling GetLeadingEdgeVertex,
+ // I could add both vertices for each edge + multiply by 0.5
+ int nEdge = src.m_EdgeIndices[nEdgeIndex];
+
+ const SubdivVertex_t &srcVert = GetLeadingEdgeVertex( src, nEdge );
+ vert.m_vecPosition += srcVert.m_vecPosition;
+ vert.m_vecTexCoord += srcVert.m_vecTexCoord;
+ }
+
+ vert.m_vecPosition /= nEdgeCount;
+ vert.m_vecTexCoord /= nEdgeCount;
+
+ // Store off the face index in the dest mesh of the first subdivided face for this guy.
+ src.m_Faces[i].m_nFirstSubdividedFace = nCurrSubdividedFace;
+ nCurrSubdividedFace += nEdgeCount;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds edge midpoints to a mesh
+//-----------------------------------------------------------------------------
+static void AddEdgeMidpointsToMesh( const SubdivMesh_t &src, SubdivMesh_t &dest )
+{
+ int nSrcEdgeCount = src.m_Edges.Count();
+ for ( int i = 0; i < nSrcEdgeCount; ++i )
+ {
+ const Edge_t &edge = src.m_Edges[i];
+
+ int v = dest.m_Vertices.AddToTail( );
+ SubdivVertex_t &vert = dest.m_Vertices[v];
+ vert.m_nValence = 4;
+
+ const SubdivVertex_t *pSrcVert = &src.m_Vertices[ edge.m_pVertex[0] ];
+ vert.m_vecPosition = pSrcVert->m_vecPosition;
+ vert.m_vecTexCoord = pSrcVert->m_vecTexCoord;
+
+ pSrcVert = &src.m_Vertices[ edge.m_pVertex[1] ];
+ vert.m_vecPosition += pSrcVert->m_vecPosition;
+ vert.m_vecTexCoord += pSrcVert->m_vecTexCoord;
+
+ // NOTE: We know that the first n vertices added to dest correspond to the src face midpoints
+ pSrcVert = &dest.m_Vertices[ edge.m_pFace[0] ];
+ vert.m_vecPosition += pSrcVert->m_vecPosition;
+ vert.m_vecTexCoord += pSrcVert->m_vecTexCoord;
+
+ pSrcVert = &dest.m_Vertices[ edge.m_pFace[1] ];
+ vert.m_vecPosition += pSrcVert->m_vecPosition;
+ vert.m_vecTexCoord += pSrcVert->m_vecTexCoord;
+
+ vert.m_vecPosition /= 4.0f;
+ vert.m_vecTexCoord /= 4.0f;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds edge midpoints to a mesh
+//-----------------------------------------------------------------------------
+static void AddModifiedVerticesToMesh( const SubdivMesh_t &src, SubdivMesh_t &dest )
+{
+ int nSrcVertexCount = src.m_Vertices.Count();
+
+ // This computes the equation v(i+1) = ((N-2)/N) * v(i) + (1/N^2) * sum( ei + fi )
+ int nFirstDestVertex = dest.m_Vertices.Count();
+ for ( int i = 0; i < nSrcVertexCount; ++i )
+ {
+ int v = dest.m_Vertices.AddToTail( );
+ SubdivVertex_t &vert = dest.m_Vertices[v];
+
+ int nValence = src.m_Vertices[i].m_nValence;
+ vert.m_nValence = nValence;
+ float flScale = (float)(nValence - 2) / nValence;
+ VectorScale( src.m_Vertices[i].m_vecPosition, flScale, vert.m_vecPosition );
+ VectorScale( src.m_Vertices[i].m_vecTexCoord, flScale, vert.m_vecTexCoord );
+ }
+
+ int nSrcEdgeCount = src.m_Edges.Count();
+ for ( int i = 0; i < nSrcEdgeCount; ++i )
+ {
+ const Edge_t &edge = src.m_Edges[i];
+ for ( int j = 0; j < 2; ++j )
+ {
+ int nDestVertIndex = nFirstDestVertex + edge.m_pVertex[j];
+ SubdivVertex_t &destVertex = dest.m_Vertices[nDestVertIndex];
+
+ float ooValenceSq = 1.0f / destVertex.m_nValence;
+ ooValenceSq *= ooValenceSq;
+
+ // This adds in the contribution from the source vertex at the opposite edge
+ const SubdivVertex_t &srcOtherVert = src.m_Vertices[ edge.m_pVertex[ 1 - j ] ];
+ VectorMA( destVertex.m_vecPosition, ooValenceSq, srcOtherVert.m_vecPosition, destVertex.m_vecPosition );
+ VectorMA( destVertex.m_vecTexCoord, ooValenceSq, srcOtherVert.m_vecTexCoord, destVertex.m_vecTexCoord );
+
+ // This adds in the contribution from the two faces it's part of
+ // NOTE: Usage of dest here is correct; this grabs the vertex that
+ // was created that was in the middle of the source mesh's face
+ const SubdivVertex_t *pSrcFace = &dest.m_Vertices[ edge.m_pFace[ 0 ] ];
+ VectorMA( destVertex.m_vecPosition, 0.5f * ooValenceSq, pSrcFace->m_vecPosition, destVertex.m_vecPosition );
+ VectorMA( destVertex.m_vecTexCoord, 0.5f * ooValenceSq, pSrcFace->m_vecTexCoord, destVertex.m_vecTexCoord );
+ pSrcFace = &dest.m_Vertices[ edge.m_pFace[ 1 ] ];
+ VectorMA( destVertex.m_vecPosition, 0.5f * ooValenceSq, pSrcFace->m_vecPosition, destVertex.m_vecPosition );
+ VectorMA( destVertex.m_vecTexCoord, 0.5f * ooValenceSq, pSrcFace->m_vecTexCoord, destVertex.m_vecTexCoord );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds unique subdivided edges so they aren't repeated.
+//-----------------------------------------------------------------------------
+static void AddSubdividedEdges( const SubdivMesh_t &src, SubdivMesh_t &dest )
+{
+ // NOTE: We iterate over each edge in sequence and add edges
+ // between face 0, then face 1, then vertex 0, then vertex 1.
+ // The vertex index for the vert at the center of original face N is N.
+ // The vertex index for the vert at the center of original edge N is nSrcFaceCount + N;
+ // The vertex index for the vert at original vertex N is nSrcFaceCount + nSrcEdgeCount + N;
+ int nSrcFaceCount = src.m_Faces.Count();
+ int nSrcEdgeCount = src.m_Edges.Count();
+
+ for ( int i = 0; i < nSrcEdgeCount; ++i )
+ {
+ const Edge_t &srcEdge = src.m_Edges[i];
+
+ int e = dest.m_Edges.AddMultipleToTail( 4 );
+ Edge_t *pDstEdge = &dest.m_Edges[e];
+
+ // Grab the two source faces
+ const Face_t *pFaces[2];
+ pFaces[0] = &src.m_Faces[ srcEdge.m_pFace[0] ];
+ pFaces[1] = &src.m_Faces[ srcEdge.m_pFace[1] ];
+
+ // Get the first subdivided face index + relative edge index
+ int pSubdividedFaceIndex[2];
+ pSubdividedFaceIndex[0] = pFaces[0]->m_nFirstSubdividedFace;
+ pSubdividedFaceIndex[1] = pFaces[1]->m_nFirstSubdividedFace;
+
+ // Get the relative edge index
+ int pRelativeEdgeIndex[2];
+ pRelativeEdgeIndex[0] = srcEdge.m_pRelativeEdgeIndex[0];
+ pRelativeEdgeIndex[1] = srcEdge.m_pRelativeEdgeIndex[1];
+
+ int pPrevRelativeEdgeIndex[2];
+ pPrevRelativeEdgeIndex[0] = (srcEdge.m_pRelativeEdgeIndex[0] - 1);
+ if ( pPrevRelativeEdgeIndex[0] < 0 )
+ {
+ pPrevRelativeEdgeIndex[0] = pFaces[0]->m_nEdgeCount - 1;
+ }
+ pPrevRelativeEdgeIndex[1] = (srcEdge.m_pRelativeEdgeIndex[1] - 1);
+ if ( pPrevRelativeEdgeIndex[1] < 0 )
+ {
+ pPrevRelativeEdgeIndex[1] = pFaces[1]->m_nEdgeCount - 1;
+ }
+
+ // This ordering maintains clockwise order
+ pDstEdge[0].m_pVertex[0] = srcEdge.m_pFace[0];
+ pDstEdge[0].m_pVertex[1] = nSrcFaceCount + i;
+ pDstEdge[0].m_pFace[0] = pSubdividedFaceIndex[0] + pPrevRelativeEdgeIndex[0];
+ pDstEdge[0].m_pFace[1] = pSubdividedFaceIndex[0] + pRelativeEdgeIndex[0];
+ pDstEdge[0].m_pRelativeEdgeIndex[0] = 3;
+ pDstEdge[0].m_pRelativeEdgeIndex[1] = 0;
+
+ pDstEdge[1].m_pVertex[0] = nSrcFaceCount + i;
+ pDstEdge[1].m_pVertex[1] = srcEdge.m_pFace[1];
+ pDstEdge[1].m_pFace[0] = pSubdividedFaceIndex[1] + pRelativeEdgeIndex[1];
+ pDstEdge[1].m_pFace[1] = pSubdividedFaceIndex[1] + pPrevRelativeEdgeIndex[1];
+ pDstEdge[1].m_pRelativeEdgeIndex[0] = 0;
+ pDstEdge[1].m_pRelativeEdgeIndex[1] = 3;
+
+ pDstEdge[2].m_pVertex[0] = nSrcFaceCount + nSrcEdgeCount + srcEdge.m_pVertex[0];
+ pDstEdge[2].m_pVertex[1] = nSrcFaceCount + i;
+ pDstEdge[2].m_pFace[0] = pSubdividedFaceIndex[0] + pRelativeEdgeIndex[0];
+ pDstEdge[2].m_pFace[1] = pSubdividedFaceIndex[1] + pPrevRelativeEdgeIndex[1];
+ pDstEdge[2].m_pRelativeEdgeIndex[0] = 1;
+ pDstEdge[2].m_pRelativeEdgeIndex[1] = 2;
+
+ pDstEdge[3].m_pVertex[0] = nSrcFaceCount + i;
+ pDstEdge[3].m_pVertex[1] = nSrcFaceCount + nSrcEdgeCount + srcEdge.m_pVertex[1];
+ pDstEdge[3].m_pFace[0] = pSubdividedFaceIndex[0] + pPrevRelativeEdgeIndex[0];
+ pDstEdge[3].m_pFace[1] = pSubdividedFaceIndex[1] + pRelativeEdgeIndex[1];
+ pDstEdge[3].m_pRelativeEdgeIndex[0] = 2;
+ pDstEdge[3].m_pRelativeEdgeIndex[1] = 1;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds unique subdivided faces
+//-----------------------------------------------------------------------------
+static void AddSubdividedFaces( const SubdivMesh_t &src, SubdivMesh_t &dest )
+{
+ dest.m_nTotalIndexCount = 0;
+ dest.m_nTotalLineCount = 0;
+ int nSrcFaceCount = src.m_Faces.Count();
+ for ( int i = 0; i < nSrcFaceCount; ++i )
+ {
+ int nEdgeCount = src.m_Faces[i].m_nEdgeCount;
+ const int *pSrcEdgeIndex = &src.m_EdgeIndices[ src.m_Faces[i].m_nFirstEdgeIndex ];
+
+ int ei = dest.m_EdgeIndices.AddMultipleToTail( nEdgeCount * 4 );
+ int *pDestEdgeIndex = &dest.m_EdgeIndices[ ei ];
+ int *pPrevDestEdgeIndex = &pDestEdgeIndex[(nEdgeCount - 1) * 4];
+ for ( int j = 0; j < nEdgeCount; ++j )
+ {
+ // Add another quad.
+ dest.m_nTotalIndexCount += 6;
+ dest.m_nTotalLineCount += 4;
+
+ // Add a face for every edge. Note that subdivided face N
+ // is the face whose goes through edge N.
+ int f = dest.m_Faces.AddToTail();
+ Face_t *pDestFace = &dest.m_Faces[f];
+ pDestFace->m_nEdgeCount = 4;
+ pDestFace->m_nFirstEdgeIndex = ei + (j * 4);
+
+ // Fill it with bogus data
+ pDestFace->m_nFirstSubdividedFace = -1;
+
+ // Now add in the edge indices to refer to the edges created in AddSubdividedEdges.
+ // Note that the new edge index == the old edge index * 4, since we always
+ // create 4 edges for every edge in the source list.
+ int *pCurrDestEdgeIndex = &pDestEdgeIndex[j*4];
+ int nSrcEdgeIndex = pSrcEdgeIndex[j];
+ if ( nSrcEdgeIndex >= 0 )
+ {
+ // This means this polygon is the '1' index in the edge; it's following this edge CCW.
+ int nDestEdgeIndex = nSrcEdgeIndex * 4;
+ pCurrDestEdgeIndex[0] = -1 - (nDestEdgeIndex + 1); // We're following this edge backwards
+ pCurrDestEdgeIndex[1] = nDestEdgeIndex + 3;
+ pPrevDestEdgeIndex[2] = nDestEdgeIndex + 2;
+ pPrevDestEdgeIndex[3] = nDestEdgeIndex + 1;
+ }
+ else
+ {
+ // This means this polygon is the '0' index in the edge; it's following this edge CW.
+ int nDestEdgeIndex = (-1 - nSrcEdgeIndex) * 4;
+ pCurrDestEdgeIndex[0] = nDestEdgeIndex;
+ pCurrDestEdgeIndex[1] = -1 - (nDestEdgeIndex + 2); // We're following this edge backwards
+ pPrevDestEdgeIndex[2] = -1 - (nDestEdgeIndex + 3); // We're following this edge backwards
+ pPrevDestEdgeIndex[3] = -1 - (nDestEdgeIndex); // We're following this edge backwards
+ }
+
+ pPrevDestEdgeIndex = pCurrDestEdgeIndex;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Subdivides a mesh
+//-----------------------------------------------------------------------------
+static void SubdivideMesh( const SubdivMesh_t &src, SubdivMesh_t &dest )
+{
+ // Preallocate space for dest data
+ int nSrcFaceCount = src.m_Faces.Count();
+ int nSrcEdgeCount = src.m_Edges.Count();
+ dest.m_Vertices.EnsureCapacity( nSrcFaceCount + nSrcEdgeCount + src.m_Vertices.Count() );
+ dest.m_Edges.EnsureCapacity( nSrcEdgeCount * 4 );
+ dest.m_EdgeIndices.EnsureCapacity( nSrcFaceCount * 16 );
+ dest.m_Faces.EnsureCapacity( nSrcFaceCount * 4 ); // This is only true if we have valence 4 everywhere.
+
+ // First, compute midpoints of each face, add them to the mesh
+ AddFaceMidpointsToMesh( src, dest );
+
+ // Next, for each edge, compute a new point which is the average of the edge points and the face midpoints
+ AddEdgeMidpointsToMesh( src, dest );
+
+ // Add modified versions of the vertices in the src mesh based on the new computed points and add them to the dest mesh
+ AddModifiedVerticesToMesh( src, dest );
+
+ // Add subdivided edges based on the previous edges
+ AddSubdividedEdges( src, dest );
+
+ // Add subdivided faces referencing the subdivided edges
+ AddSubdividedFaces( src, dest );
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates/destroys the subdiv control cage
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::CreateControlCage( )
+{
+ DestroyControlCage();
+ m_pControlCage = new SubdivMesh_t;
+
+ // Draw a simple cube
+ static Vector s_pPositions[8] =
+ {
+ Vector( -30, -30, -30 ),
+ Vector( 30, -30, -30 ),
+ Vector( -30, 30, -30 ),
+ Vector( 30, 30, -30 ),
+ Vector( -30, -30, 30 ),
+ Vector( 30, -30, 30 ),
+ Vector( -30, 30, 30 ),
+ Vector( 30, 30, 30 ),
+ };
+
+ static Vector2D s_pTexCoords[8] =
+ {
+ Vector2D( 0, 0 ),
+ Vector2D( 0.5, 0 ),
+ Vector2D( 0, 0.5 ),
+ Vector2D( 0.5, 0.5 ),
+ Vector2D( 0.5, 0.5 ),
+ Vector2D( 1, 0.5 ),
+ Vector2D( 0.5, 1 ),
+ Vector2D( 1, 1 ),
+ };
+
+ // Indices into the vertex array
+ static int s_pEdges[12][2] =
+ {
+ { 0, 4 }, { 4, 6 }, { 6, 2 }, { 2, 0 }, // 0 -> -x
+ { 1, 3 }, { 3, 7 }, { 7, 5 }, { 5, 1 }, // 1 -> +x
+ { 0, 1 }, { 5, 4 }, // 2 -> -y
+ { 6, 7 }, { 3, 2 }, // 3 -> +y
+ // 4 -> -z
+ // 5 -> +z
+ };
+
+ // Indices into the face array associated w/ the edges above
+ static int s_pEdgeFaces[12][2] =
+ {
+ { 2, 0 }, { 5, 0 }, { 3, 0 }, { 4, 0 }, // 0 -> -x
+ { 4, 1 }, { 3, 1 }, { 5, 1 }, { 2, 1 }, // 1 -> +x
+ { 4, 2 }, { 5, 2 }, // 2 -> -y
+ { 5, 3 }, { 4, 3 }, // 3 -> +y
+ // 4 -> -z
+ // 5 -> +z
+ };
+
+ // In what order does edge s_pEdges[i] appear on faces s_pEdgeFaces[i][0] and s_pEdgeFaces[i][1]
+ // in the list s_pIndices[s_pEdgeFaces[i][j]] below? Note the #s 0, 1, 2, and 3 should appear 6 times each in this array
+ // representing the fact that each face has a 0th,1st,2nd, and 3rd edge.
+ static int s_pRelativeEdgeIndex[12][2] =
+ {
+ { 3, 0 }, { 3, 1 }, { 0, 2 }, { 0, 3 }, // 0 -> -x
+ { 2, 0 }, { 2, 1 }, { 1, 2 }, { 1, 3 }, // 1 -> +x
+ { 3, 0 }, { 0, 2 }, // 2 -> -y
+ { 2, 1 }, { 1, 3 }, // 3 -> +y
+ // 4 -> -z
+ // 5 -> +z
+ };
+
+ static int s_pIndices[6][5] =
+ {
+ { 0, 4, 6, 2, 0 }, // 0 -> -x
+ { 1, 3, 7, 5, 1 }, // 1 -> +x
+ { 0, 1, 5, 4, 0 }, // 2 -> -y
+ { 2, 6, 7, 3, 2 }, // 3 -> +y
+ { 0, 2, 3, 1, 0 }, // 4 -> -z
+ { 4, 5, 7, 6, 4 }, // 5 -> +z
+ };
+
+ // Add vertices
+ int i;
+ for ( i = 0; i < 8; ++i )
+ {
+ int v = m_pControlCage->m_Vertices.AddToTail();
+ SubdivVertex_t &vert = m_pControlCage->m_Vertices[v];
+ vert.m_vecPosition = s_pPositions[i];
+ vert.m_vecNormal = vec3_origin;
+ vert.m_vecTexCoord.AsVector2D() = s_pTexCoords[i];
+ vert.m_nValence = 3;
+ }
+
+ // Add unique edges
+ for ( i = 0; i < 12; ++i )
+ {
+ int e = m_pControlCage->m_Edges.AddToTail();
+ Edge_t &edge = m_pControlCage->m_Edges[e];
+ edge.m_pVertex[0] = s_pEdges[i][0];
+ edge.m_pVertex[1] = s_pEdges[i][1];
+ edge.m_pFace[0] = s_pEdgeFaces[i][0];
+ edge.m_pFace[1] = s_pEdgeFaces[i][1];
+ edge.m_pRelativeEdgeIndex[0] = s_pRelativeEdgeIndex[i][0];
+ edge.m_pRelativeEdgeIndex[1] = s_pRelativeEdgeIndex[i][1];
+ }
+
+ m_pControlCage->m_nTotalIndexCount = 0;
+ m_pControlCage->m_nTotalLineCount = 0;
+ for ( i = 0; i < 6; ++i )
+ {
+ int f = m_pControlCage->m_Faces.AddToTail();
+ Face_t &face = m_pControlCage->m_Faces[f];
+ face.m_nFirstEdgeIndex = m_pControlCage->m_EdgeIndices.Count();
+ face.m_nEdgeCount = 4;
+
+ // Place an invalid value here
+ face.m_nFirstSubdividedFace = -1;
+
+ // Two triangles per quad
+ m_pControlCage->m_nTotalIndexCount += 6;
+ m_pControlCage->m_nTotalLineCount += 4;
+
+ for ( int j = 0; j < 4; ++j )
+ {
+ int k;
+ for ( k = 0; k < 12; ++k )
+ {
+ if ( (s_pIndices[i][j] == s_pEdges[k][0]) && (s_pIndices[i][j+1] == s_pEdges[k][1]) )
+ {
+ m_pControlCage->m_EdgeIndices.AddToTail( k );
+ break;
+ }
+ if ( (s_pIndices[i][j] == s_pEdges[k][1]) && (s_pIndices[i][j+1] == s_pEdges[k][0]) )
+ {
+ m_pControlCage->m_EdgeIndices.AddToTail( -1-k );
+ break;
+ }
+ }
+ Assert( k != 12 );
+ }
+ }
+}
+
+void CDmeTestMesh::DestroyControlCage( )
+{
+ if ( m_pControlCage )
+ {
+ delete m_pControlCage;
+ m_pControlCage = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a subdiv mesh
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::DrawSubdivMesh( const SubdivMesh_t &mesh )
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ IMaterial *pMaterial = g_pMaterialSystem->FindMaterial( "debug/debugwireframe", NULL, false );
+ pRenderContext->Bind( pMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+
+ int nVertexCount = mesh.m_Vertices.Count();
+
+// meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertexCount, mesh.m_nTotalIndexCount );
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, nVertexCount, mesh.m_nTotalLineCount * 2 );
+
+ for ( int i = 0; i < nVertexCount; ++i )
+ {
+ meshBuilder.Position3fv( mesh.m_Vertices[ i ].m_vecPosition.Base() );
+ meshBuilder.TexCoord2fv( 0, mesh.m_Vertices[ i ].m_vecTexCoord.Base() );
+ meshBuilder.TexCoord2f( 1, i, 0.0f );
+ meshBuilder.Color3ub( 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+ }
+
+ int nFaceCount = mesh.m_Faces.Count();
+ for ( int i = 0; i < nFaceCount; ++i )
+ {
+ int nEdgeCount = mesh.m_Faces[i].m_nEdgeCount;
+ const int *pEdgeIndex = &mesh.m_EdgeIndices[ mesh.m_Faces[i].m_nFirstEdgeIndex ];
+ int nPrevIndex = GetLeadingEdgeVertexIndex( mesh, pEdgeIndex[nEdgeCount-1] );
+ for ( int j = 0; j < nEdgeCount; ++j )
+ {
+ int nCurrIndex = GetLeadingEdgeVertexIndex( mesh, pEdgeIndex[j] );
+ meshBuilder.FastIndex( nPrevIndex );
+ meshBuilder.FastIndex( nCurrIndex );
+ nPrevIndex = nCurrIndex;
+ }
+ }
+
+ /*
+ int nFaceCount = mesh.m_Faces.Count();
+ for ( int i = 0; i < nFaceCount; ++i )
+ {
+ int nEdgeCount = mesh.m_Faces[i].m_nEdgeCount;
+ const int *pEdgeIndex = &mesh.m_EdgeIndices[ mesh.m_Faces[i].m_nFirstEdgeIndex ];
+ int nRootIndex = GetLeadingEdgeVertexIndex( mesh, pEdgeIndex[0] );
+ int nPrevIndex = GetLeadingEdgeVertexIndex( mesh, pEdgeIndex[1] );
+ for ( int j = 0; j < nEdgeCount - 2; ++j )
+ {
+ int nCurrIndex = GetLeadingEdgeVertexIndex( mesh, pEdgeIndex[j+2] );
+ meshBuilder.FastIndex( nRootIndex );
+ meshBuilder.FastIndex( nPrevIndex );
+ meshBuilder.FastIndex( nCurrIndex );
+ nPrevIndex = nCurrIndex;
+ }
+ }
+ */
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a subdivided box
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::DrawSubdividedBox()
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ if ( !m_pControlCage )
+ {
+ CreateControlCage( );
+ }
+
+ int nSubdivLevel = GetValue<int>( "subdivlevel" );
+ if ( nSubdivLevel == 0 )
+ {
+ DrawSubdivMesh( *m_pControlCage );
+ return;
+ }
+
+ // Construct the initial mesh
+ SubdivMesh_t subdivMesh[2];
+ SubdivideMesh( *m_pControlCage, subdivMesh[0] );
+
+ // Compute the subdivided vertices
+ int nCurrMesh = 0;
+ while ( --nSubdivLevel > 0 )
+ {
+ ClearMesh( subdivMesh[1 - nCurrMesh] );
+ SubdivideMesh( subdivMesh[nCurrMesh], subdivMesh[1 - nCurrMesh] );
+ if (( subdivMesh[1 - nCurrMesh].m_nTotalLineCount * 2 >= 32768 ) || ( subdivMesh[1 - nCurrMesh].m_Vertices.Count() >= 32768 ))
+ break;
+ nCurrMesh = 1 - nCurrMesh;
+ }
+
+ // Draw the subdivided mesh
+ DrawSubdivMesh( subdivMesh[nCurrMesh] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::DrawBox( CDmeTransform *pTransform )
+{
+ if ( !g_pMaterialSystem )
+ return;
+
+ // FIXME: Hack!
+ if ( !m_pMorph || !m_pMesh )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ // Set up morph factors
+ float pMorphFactors[32];
+ for ( int i = 0; i < 32; ++i )
+ {
+ pMorphFactors[i] = 0.5f + 0.5f * sin( 2 * 3.14 * ( Plat_FloatTime() / 5.0f + (float)i / 32.0f ) );
+ }
+ pMorphFactors[1] = 1.0f - pMorphFactors[0];
+ pRenderContext->SetMorphTargetFactors( 0, pMorphFactors, 32 );
+
+ // FIXME: Should this call be made from the application rendering the mesh?
+ LoadModelMatrix( pTransform );
+
+ pRenderContext->BindMorph( m_pMorph );
+
+ pRenderContext->Bind( m_pMaterial );
+ m_pMesh->Draw();
+
+ pRenderContext->BindMorph( NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the mesh
+//-----------------------------------------------------------------------------
+void CDmeTestMesh::Draw( const matrix3x4_t& shapeToWorld, CDmeDrawSettings *pDrawSettings )
+{
+ if ( !g_pMaterialSystem || !g_pMDLCache || !g_pStudioRender )
+ return;
+
+#if 0
+// DrawSubdividedBox( pTransform );
+ DrawBox( pTransform );
+ return;
+
+#elif 0
+ if ( m_MDLHandle == MDLHANDLE_INVALID )
+ return;
+
+ // Color + alpha modulation
+ Vector white(1.0f, 1.0f, 1.0f);
+ g_pStudioRender->SetColorModulation( white.Base() );
+ g_pStudioRender->SetAlphaModulation( 1.0f );
+
+ DrawModelInfo_t info;
+ info.m_pStudioHdr = g_pMDLCache->GetStudioHdr( m_MDLHandle );
+ info.m_pHardwareData = g_pMDLCache->GetHardwareData( m_MDLHandle );
+ info.m_Decals = STUDIORENDER_DECAL_INVALID;
+ info.m_Skin = GetAttributeValueInt( "skin" );
+ info.m_Body = GetAttributeValueInt( "body" );
+ info.m_HitboxSet = 0;
+ info.m_pClientEntity = NULL;
+ info.m_ppColorMeshes = NULL;
+ info.m_bStaticLighting = false;
+ info.m_Lod = GetAttributeValueInt( "lod" );
+
+ // FIXME: Deal with lighting
+ for ( int i = 0; i < 6; ++ i )
+ {
+ info.m_vecAmbientCube[i].Init( 1, 1, 1 );
+ }
+
+ info.m_nLocalLightCount = 0;
+// info.m_LocalLightDescs;
+
+ matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( info.m_pStudioHdr->numbones );
+ SetUpBones( pTransform, info.m_pStudioHdr->numbones, pBoneToWorld );
+ g_pStudioRender->UnlockBoneMatrices();
+
+ // Root transform
+ matrix3x4_t rootToWorld;
+ pTransform->GetTransform( rootToWorld );
+
+ Vector vecModelOrigin;
+ MatrixGetColumn( rootToWorld, 3, vecModelOrigin );
+ g_pStudioRender->DrawModel( NULL, info, pBoneToWorld, vecModelOrigin, STUDIORENDER_DRAW_ENTIRE_MODEL );
+#else
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+#if 1
+ matrix3x4_t mat;
+ if ( m_bones.size() == 1 )
+ {
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ m_bones[0]->GetTransform( mat );
+ pRenderContext->LoadMatrix( mat );
+// pRenderContext->LoadMatrix( m_bones[0] ); // m_PoseToWorld[0]
+ }
+
+ pRenderContext->SetNumBoneWeights( 2 ); // pStrip->numBones
+
+ uint bn = m_bones.size();
+ for ( uint bi = 0; bi < bn; ++bi )
+ {
+ m_bones[bi]->GetTransform( mat );
+
+#if 0 // hack to see whether bones are actually affecting the model
+ float f = 100.0f;
+ Vector translation;
+ MatrixGetColumn( mat, 3, &translation );
+ translation.x += (bi&1) ? f : -f;
+ translation.y += (bi&2) ? f : -f;
+ translation.z += (bi&4) ? f : -f;
+ MatrixSetColumn( translation, 3, mat );
+#endif
+ pRenderContext->LoadBoneMatrix( bi, mat );
+ }
+#else
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ matrix3x4_t mat;
+ Assert( !m_bones.empty() );
+ m_bones[0]->GetTransform( mat );
+ pRenderContext->LoadMatrix( mat );
+#endif
+
+ IMaterial *pMaterial = g_pMaterialSystem->FindMaterial( "Models/shadertest/unlitgenericmodel", NULL, false );
+// IMaterial *pMaterial = g_pMaterialSystem->FindMaterial( "debug/debugwireframevertexcolor", NULL, false );
+// IMaterial *pMaterial = g_pMaterialSystem->FindMaterial( "debug/debugwireframe", NULL, false );
+ pRenderContext->Bind( pMaterial );
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh();
+
+ int mn = m_submeshes.size();
+ for ( int mi = 0; mi < mn; ++mi )
+ {
+ CMeshBuilder meshBuilder;
+ std::vector< int > &indices = m_submeshes[mi]->indices;
+ std::vector< vertex_t > &vertices = m_submeshes[mi]->vertices;
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, vertices.size(), indices.size() );
+
+ int vn = vertices.size();
+ for ( int vi = 0; vi < vn; ++vi )
+ {
+ vertex_t &vertex = vertices[vi];
+ meshBuilder.Position3fv( vertex.coord.Base() );
+ meshBuilder.Normal3fv ( vertex.normal.Base() );
+ meshBuilder.TexCoord2fv( 0, vertex.texcoord.Base() );
+ switch ( vertex.skinning[0].index )
+ {
+ case 0: meshBuilder.Color3f(1,0,0); break;
+ case 1: meshBuilder.Color3f(0,1,0); break;
+ case 2: meshBuilder.Color3f(0,0,1); break;
+ case 3: meshBuilder.Color3f(1,1,0); break;
+ case 4: meshBuilder.Color3f(0,1,1); break;
+ case 5: meshBuilder.Color3f(1,0,1); break;
+ case 6: meshBuilder.Color3f(0,0,0); break;
+ case 7: meshBuilder.Color3f(1,1,1); break;
+ default: meshBuilder.Color3f(0.5f,0.5f,0.5f); break;
+ }
+
+ int bn = vertex.skinning.size();
+ for ( int bi = 0; bi < bn; ++bi )
+ {
+ meshBuilder.BoneMatrix( bi, vertex.skinning[bi].index );
+ meshBuilder.BoneWeight( bi, vertex.skinning[bi].weight );
+ }
+
+ meshBuilder.AdvanceVertex();
+ }
+
+ int in = indices.size();
+ for ( int ii = 0; ii < in; ++ii )
+ {
+ meshBuilder.FastIndex( indices[ii] );
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a mask indicating which bones to set up
+//-----------------------------------------------------------------------------
+int CDmeTestMesh::BoneMask( void )
+{
+ int nLod = GetValue<int>( "lod" );
+ return BONE_USED_BY_VERTEX_AT_LOD( nLod );
+}
+
+void CDmeTestMesh::SetUpBones( CDmeTransform *pTransform, int nMaxBoneCount, matrix3x4_t *pBoneToWorld )
+{
+ // Default to middle of the pose parameter range
+ float pPoseParameter[MAXSTUDIOPOSEPARAM];
+ for ( int i = 0; i < MAXSTUDIOPOSEPARAM; ++i )
+ {
+ pPoseParameter[i] = 0.5f;
+ }
+
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( m_MDLHandle ), g_pMDLCache );
+
+ int nSequence = GetValue<int>( "sequence" );
+ float flPlaybackRate = GetValue<float>( "playbackrate" );
+ float flTime = GetValue<float>( "time" );
+
+ int nFrameCount = Studio_MaxFrame( &studioHdr, nSequence, pPoseParameter );
+ if ( nFrameCount == 0 )
+ {
+ nFrameCount = 1;
+ }
+ float flCycle = ( flTime * flPlaybackRate ) / nFrameCount;
+
+ // FIXME: We're always wrapping; may want to determing if we should clamp
+ flCycle -= (int)(flCycle);
+
+ Vector pos[MAXSTUDIOBONES];
+ Quaternion q[MAXSTUDIOBONES];
+
+ IBoneSetup boneSetup( &studioHdr, BoneMask(), pPoseParameter );
+ boneSetup.InitPose( pos, q );
+ boneSetup.AccumulatePose( pos, q, nSequence, flCycle, 1.0f, flTime, NULL );
+
+ // FIXME: Try enabling this?
+// CalcAutoplaySequences( pStudioHdr, NULL, pos, q, pPoseParameter, BoneMask( ), flTime );
+
+ // Root transform
+ matrix3x4_t rootToWorld;
+ pTransform->GetTransform( rootToWorld );
+
+ if ( studioHdr.numBones() < nMaxBoneCount )
+ {
+ nMaxBoneCount = studioHdr.numBones();
+ }
+
+ for ( int i = 0; i < nMaxBoneCount; i++ )
+ {
+ // If it's not being used, fill with NAN for errors
+#ifdef _DEBUG
+ if ( !(studioHdr.pBone( i )->flags & BoneMask()))
+ {
+ int j, k;
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ pBoneToWorld[i][j][k] = VEC_T_NAN;
+ }
+ }
+ continue;
+ }
+#endif
+
+ matrix3x4_t boneMatrix;
+ QuaternionMatrix( q[i], boneMatrix );
+ MatrixSetColumn( pos[i], 3, boneMatrix );
+
+ if (studioHdr.pBone(i)->parent == -1)
+ {
+ ConcatTransforms (rootToWorld, boneMatrix, pBoneToWorld[ i ]);
+ }
+ else
+ {
+ ConcatTransforms ( pBoneToWorld[ studioHdr.pBone(i)->parent ], boneMatrix, pBoneToWorld[ i ] );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// FIXME: This trashy glue code is really not acceptable. Figure out a way of making it unnecessary.
+//-----------------------------------------------------------------------------
+const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *pModelName ) const
+{
+ MDLHandle_t handle = g_pMDLCache->FindMDL( pModelName );
+ *cache = (void*)handle;
+ return g_pMDLCache->GetStudioHdr( handle );
+}
+
+virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const
+{
+ return g_pMDLCache->GetVirtualModel( (MDLHandle_t)virtualModel );
+}
+
+byte *studiohdr_t::GetAnimBlock( int i ) const
+{
+ return g_pMDLCache->GetAnimBlock( (MDLHandle_t)virtualModel, i );
+}
+
+int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const
+{
+ return g_pMDLCache->GetAutoplayList( (MDLHandle_t)virtualModel, pOut );
+}
+
+const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const
+{
+ return g_pMDLCache->GetStudioHdr( (MDLHandle_t)cache );
+}
+
+//-----------------------------------------------------------------------------
+// First attempt at making a hacky SMD loader - clean this up later
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// SMD format:
+//
+// format key:
+// #n = integer
+// .x = float
+// 'a' = literal string
+// $s = string
+// " = the literal quote character
+// // = comment - not in file!!!
+//
+// 'version' #version // right now, #version = 1
+//
+// 'nodes' // bone naming and hierarchy
+// #bone "$bonename" #parent // one of these per bone - can be in any order, but generally sequential
+// 'end'
+//
+// 'skeleton' // joint animation (and begin pose)
+// 'time' #time // repeat time + joints block once per frame
+// #bone .x .y .z .rx .ry .rz // bone/translation/rotation - can traverse bones in any order, and even skip them
+// 'end'
+//
+// 'triangles' // actual vertex data - as non-indexed triangle lists
+// $texturefilename // repeat texture + 3 vertex lines for each triangle
+// #bone .x .y .z .nx .ny .nz .tu .tv #count #bone0 .weight0 // boneN & weightN may or may not exist for N={0..511}
+// #bone .x .y .z .nx .ny .nz .tu .tv #count #bone0 .weight0 // boneN & weightN may or may not exist for N={0..511}
+// #bone .x .y .z .nx .ny .nz .tu .tv #count #bone0 .weight0 // boneN & weightN may or may not exist for N={0..511}
+// 'end'
+//
+// 'vertexanimation' // morph targets
+// 'time' #time // repeat time + vertices block once per vertex
+// #vertex .x .y .z .nx .ny .nz // vertex/position/normal
+// 'end'
+//
+//-----------------------------------------------------------------------------
+
+// TODO - check out lookup_index for whether it's looking for exact vertex matches, or within a float tolerance
+// DONE - lookup_index checks materiaks, coords and texcoords for exact match, and normals for within 2 degrees
+
+const int MAXNAME = 128;
+const int MAXLINE = 4096;
+const int MAXCMD = 1024;
+const int MAXBONEWEIGHTS = 3;
+const int MAXTEXNAME = 64;
+
+void ReadBonesFromSMD( std::vector< CDmeTransform* > &bones, std::istream &is, DmFileId_t fileid )
+{
+ uint index;
+ int parent;
+ char name[ MAXNAME ];
+
+ char line[ MAXLINE ];
+
+ while ( is.getline( line, MAXLINE ) )
+ {
+ if ( sscanf( line, "%d \"%[^\"]\" %d", &index, name, &parent ) == 3 )
+ {
+ if ( index != bones.size() )
+ {
+ Warning( "ReadBonesFromSMD: reading node %d out of order\n", index );
+ }
+ if ( index >= bones.size() )
+ {
+ bones.resize( index + 1 );
+ }
+
+ bones[index] = CreateElement< CDmeTransform >( name, fileid );
+ if ( parent > 0 )
+ {
+ if ( ( uint( parent ) >= bones.size() ) || ( bones[ parent ] == NULL ) )
+ {
+ Warning( "ReadBonesFromSMD: reading node %d before parent\n", index, parent );
+ }
+ else
+ {
+ Assert( 0 ); // this code is so badly bit-rotten...
+// bones[parent]->AddChild( bones[index]->GetHandle() );
+ }
+ }
+ }
+ else
+ {
+ if ( strncmp( line, "end", 3 ) != 0 )
+ {
+ Warning( "ReadBonesFromSMD: expected 'end' or bone, found %s\n", line );
+ }
+ return;
+ }
+ }
+}
+
+void clip_rotations( RadianEuler& rot )
+{
+ // remap rotations to [ -M_PI .. M_PI )
+ for ( int j = 0; j < 3; j++ ) {
+ if ( rot[j] != -M_PI ) // keep -M_PI as is
+ {
+ rot[j] = fmod( (double)rot[j], M_PI );
+ }
+ }
+}
+
+void ReadSkeletalAnimationFromSMD( std::vector< CDmeTransform* > &bones, std::istream &is )
+{
+ char line[ MAXLINE ];
+
+ char cmd[ MAXCMD ];
+
+ int time = INT_MIN;
+ int startframe = -1;
+ int endframe = -1;
+
+#if 1
+ // Root transform
+ matrix3x4_t rootToWorld;
+ SetIdentityMatrix( rootToWorld );
+// GetTransform()->GetTransform( rootToWorld );
+#endif
+
+ while ( is.getline( line, MAXLINE ) )
+ {
+ int index;
+ Vector pos;
+ RadianEuler rot;
+
+ if ( sscanf( line, "%d %f %f %f %f %f %f", &index, &pos[0], &pos[1], &pos[2], &rot[0], &rot[1], &rot[2] ) == 7 )
+ {
+ if ( startframe < 0 )
+ {
+ Warning( "ReadSkeletalAnimationFromSMD: missing frame start\n" );
+ }
+
+// clip_rotations( rot );
+ Quaternion quat;
+ AngleQuaternion( rot, quat );
+#if 0
+ matrix3x4_t boneMatrix;
+ QuaternionMatrix( quat, boneMatrix );
+ MatrixSetColumn( pos, 3, boneMatrix );
+
+ if ( bones[index]->NumParents() > 0 )
+ {
+ DmElementHandle_t hParent = bones[index]->GetParent( 0 );
+ CDmeTransform *parentXform = GetElement< CDmeTransform >( hParent );
+ matrix3x4_t parentMatrix, newMatrix;
+ parentXform->GetTransform( parentMatrix );
+// ConcatTransforms( parentMatrix, boneMatrix, newMatrix );
+ SetIdentityMatrix( newMatrix );
+ MatrixAngles( newMatrix, quat, pos );
+ }
+ else
+ {
+ matrix3x4_t parentMatrix, newMatrix;
+// ConcatTransforms( rootToWorld, boneMatrix, newMatrix );
+ SetIdentityMatrix( newMatrix );
+ MatrixAngles( newMatrix, quat, pos );
+ }
+#endif
+ bones[index]->SetValue( "orientation", quat );
+ bones[index]->SetValue( "position", pos );
+
+ // TODO - save animation data - currently just overwriting w/ last frame
+ }
+ else if ( sscanf( line, "%1023s %d", cmd, &index ) )
+ {
+ if ( strcmp( cmd, "time" ) == 0 )
+ {
+ time = index;
+ if ( startframe == -1 )
+ {
+ startframe = index;
+ }
+ if ( time < startframe )
+ {
+ Error( "ReadSkeletalAnimationFromSMD: time %d found after time %d\n", time, startframe );
+ }
+ if ( time > endframe )
+ {
+ endframe = time;
+ }
+ time -= startframe;
+ /*
+ if ( time != anim.size() )
+ {
+ Warning( "ReadSkeletalAnimationFromSMD: reading keyframe %d out of order\n", time );
+ }
+ if ( time >= anim.size() )
+ {
+ anim.resize( time + 1 );
+ anim[time] = new bone_t[nodes.size()];
+ }
+
+ if ( time > 0 )
+ {
+ if ( anim[time-1] )
+ {
+ std::copy( anim[time-1], anim[time-1] + nodes.size(), anim[time] );
+ }
+ else
+ {
+ Warning( "ReadSkeletalAnimationFromSMD: missing skeletal keyframe %d\n", time-1 );
+ }
+ }
+ */
+ }
+ else if ( strcmp( cmd, "end" ) == 0 )
+ {
+// Build_Reference( nodes, anim, matrices ); // skip - leave this for dmemesh generation
+ return;
+ }
+ else
+ {
+ Warning( "ReadSkeletalAnimationFromSMD: expected bone, time or end, found %s\n", line );
+ }
+ }
+ else
+ {
+ Warning( "ReadSkeletalAnimationFromSMD: expected bone, time or end, found %s\n", line );
+ }
+ }
+ Error( "ReadSkeletalAnimationFromSMD: unexpected EOF\n" );
+}
+
+float vertex_t::normal_tolerance = cos( DEG2RAD( 2.0f ));
+
+void SortAndBalanceBones( std::vector< skinning_info_t > &skinning )
+{
+ // TODO - studiomdl collapses (sums) duplicate bone weights - is this necessary?!?!
+
+ std::sort( skinning.begin(), skinning.end() );
+
+ // throw away bone weights < 0.05f
+ while ( skinning.size() > 1 && skinning.back().weight >= 0.05f )
+ {
+ skinning.pop_back();
+ }
+ Assert( !skinning.empty() );
+
+ if ( skinning.size() > MAXBONEWEIGHTS )
+ {
+ skinning.resize( MAXBONEWEIGHTS );
+ }
+
+ float weightSum = 0.0f;
+ for ( uint i = 0; i < skinning.size(); ++i )
+ {
+ weightSum += skinning[i].weight;
+ }
+
+ if ( weightSum <= 0.0f )
+ {
+ for ( uint i = 0; i < skinning.size(); ++i )
+ {
+ skinning[i].weight = weightSum;
+ }
+ }
+ else
+ {
+ float weightScale = 1.0f / weightSum;
+ for ( uint i = 0; i < skinning.size(); ++i )
+ {
+ skinning[i].weight *= weightScale;
+ }
+ }
+}
+
+int ReadVertexFromSMD( std::vector< vertex_t > &vertices, int numbones, std::istream &is )
+{
+ int boneIndex;
+ is >> boneIndex;
+
+ if ( boneIndex < 0 || boneIndex >= numbones )
+ {
+ Error( "ReadVertexFromSMD: invalid bone index: %d\n", boneIndex );
+ }
+
+ vertex_t vert;
+ is >> vert.coord.x >> vert.coord.y >> vert.coord.z;
+ is >> vert.normal.x >> vert.normal.y >> vert.normal.z;
+ is >> vert.texcoord.x >> vert.texcoord.y;
+
+ // invert v
+ vert.texcoord.y = 1.0f - vert.texcoord.y;
+
+ char line[MAXLINE];
+ is.getline( line, MAXLINE );
+ std::istrstream istr( line );
+
+ int nBones = 0;
+ istr >> nBones;
+ Assert( istr.good() || nBones == 0 );
+
+ if ( nBones == 0 )
+ {
+ vert.skinning.push_back( skinning_info_t( boneIndex, 1.0f ) );
+ }
+ else
+ {
+ vert.skinning.reserve( nBones );
+ for ( int i = 0; i < nBones; ++i )
+ {
+ skinning_info_t info;
+ istr >> info.index >> info.weight;
+ vert.skinning.push_back( info );
+
+ if ( info.index < 0 || info.index >= numbones )
+ {
+ Error( "ReadVertexFromSMD: invalid bone index: %d\n", info.index );
+ }
+ }
+ }
+
+ std::vector< vertex_t >::iterator vi = std::find( vertices.begin(), vertices.end(), vert );
+ if ( vi != vertices.end() )
+ return vi - vertices.begin();
+
+ SortAndBalanceBones( vert.skinning );
+
+ vertices.push_back( vert );
+ return vertices.size() - 1;
+}
+
+bool IsEnd( char const* pLine )
+{
+ if ( strncmp( "end", pLine, 3 ) != 0 )
+ return false;
+ return ( pLine[3] == '\0' ) || ( pLine[3] == '\n' );
+}
+
+void ReadTrianglesFromSMD( std::vector< submesh_t* > &meshes, int numbones, std::istream &is )
+{
+ Vector vmin( FLT_MAX, FLT_MAX, FLT_MAX );
+ Vector vmax( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+
+ char line[ MAXLINE ];
+
+ char texname[ MAXTEXNAME ];
+
+ while ( is.getline( line, MAXLINE ) )
+ {
+ if ( IsEnd( line ) )
+ break;
+
+ int lineLen = is.gcount();
+ if ( lineLen >= MAXTEXNAME )
+ {
+ Warning( "ReadTrianglesFromSMD: expected a texture name, found %s\n", line );
+ continue;
+ }
+
+ // the studiomdl comment here is "strip off trailing smag" whatever smag is...
+ strncpy( texname, line, MAXTEXNAME );
+ int i;
+ for ( i = strlen( texname ) - 1; i >= 0 && ! isgraph( texname[i] ); i-- )
+ {
+ }
+ texname[i + 1] = '\0';
+
+ // Skip empty names (studiomdl comment: "weird source problem, skip them")
+ // Skip null texture references
+ if ( texname[0] == '\0' ||
+ stricmp( texname, "null.bmp" ) == 0 ||
+ stricmp( texname, "null.tga" ) == 0 )
+ {
+ is.getline( line, MAXLINE );
+ is.getline( line, MAXLINE );
+ is.getline( line, MAXLINE );
+ continue;
+ }
+
+ // find mesh with matching texture - starting with last one created
+ int mi;
+ for ( mi = meshes.size() - 1; mi >= 0; --mi )
+ {
+ if ( stricmp( meshes[mi]->texname.c_str(), texname ) == 0 )
+ break;
+ }
+
+ // if no mesh with texname found, create a new one
+ if ( mi < 0 )
+ {
+ mi = meshes.size();
+ meshes.push_back( new submesh_t( texname ) );
+ }
+ submesh_t *mesh = meshes[mi];
+
+ mesh->indices.push_back( ReadVertexFromSMD( mesh->vertices, numbones, is ) );
+ mesh->indices.push_back( ReadVertexFromSMD( mesh->vertices, numbones, is ) );
+ mesh->indices.push_back( ReadVertexFromSMD( mesh->vertices, numbones, is ) );
+
+#if 0
+ // flip triangle - the default in studiomdl
+ int numIndices = mesh->indices.size();
+ std::swap( mesh->indices[numIndices-1], mesh->indices[numIndices-2] );
+#endif
+ }
+}
+
+void RemapBonesOnSubmesh( submesh_t *pMesh, std::vector< CDmeTransform* > &bones )
+{
+ std::vector<int> vertsPerBone( bones.size() ); // initializes all counts to 0
+
+ // find vertex-per-bone counts
+ int vn = pMesh->vertices.size();
+ for ( int vi = 0; vi < vn; ++vi )
+ {
+ vertex_t &vert = pMesh->vertices[vi];
+ int bn = vert.skinning.size();
+ for ( int bi = 0; bi < bn; ++bi )
+ {
+ ++vertsPerBone[vert.skinning[bi].index];
+ }
+ }
+
+ std::vector<int> boneMap( bones.size() );
+
+ // copy only used bones into mesh's internal bone list and write mapping
+ int bn = vertsPerBone.size();
+ for ( int bi = 0; bi < bn; ++bi )
+ {
+ if ( vertsPerBone[bi] == 0 )
+ {
+ boneMap[bi] = -1;
+ }
+ else
+ {
+ boneMap[bi] = pMesh->bones.size();
+ pMesh->bones.push_back( bones[bi] );
+ }
+ }
+
+ // remap mesh's verts to use the interal bone indexing
+ for ( int vi = 0; vi < vn; ++vi )
+ {
+ vertex_t &vert = pMesh->vertices[vi];
+ int bn = vert.skinning.size();
+ for ( int bi = 0; bi < bn; ++bi )
+ {
+ vert.skinning[bi].index = boneMap[vert.skinning[bi].index];
+ }
+ }
+}
+
+CDmeTestMesh *CDmeTestMesh::ReadMeshFromSMD( char *pFilename, DmFileId_t fileid )
+{
+ std::ifstream is( pFilename );
+ if ( !is )
+ {
+ Warning( "Unable to open file %s\n", pFilename );
+ return NULL;
+ }
+
+ CDmeTestMesh *pMesh = CreateElement< CDmeTestMesh >( "New Mesh", fileid );
+
+ char line[ MAXLINE ];
+
+ char cmd[ MAXCMD ];
+ int option;
+
+ while ( is.getline( line, MAXLINE ) )
+ {
+ int numRead = sscanf( line, "%1023s %d", cmd, &option );
+
+ if ( ( numRead == EOF ) || ( numRead == 0 ) )
+ continue; // blank line
+
+ if ( strcmp( cmd, "version" ) == 0 )
+ {
+ if ( option != 1 )
+ {
+ Error( "ReadMeshFromSMD: bad version\n" );
+ }
+ }
+ else if ( strcmp( cmd, "nodes" ) == 0 )
+ {
+ pMesh->m_bones.clear();
+ ReadBonesFromSMD( pMesh->m_bones, is, fileid );
+ }
+ else if ( strcmp( cmd, "skeleton" ) == 0 )
+ {
+ ReadSkeletalAnimationFromSMD( pMesh->m_bones, is );
+ }
+ else if ( strcmp( cmd, "triangles" ) == 0 )
+ {
+ ReadTrianglesFromSMD( pMesh->m_submeshes, pMesh->m_bones.size(), is );
+ }
+ else if ( strcmp( cmd, "vertexanimation" ) == 0 )
+ {
+// Grab_Vertexanimation( psource );
+ return pMesh; // TODO - implement Grab_Vertexanimation!!!
+ }
+ else
+ {
+ Warning( "unknown studio command\n" );
+ }
+ }
+
+#if 0
+ // remap only the needed bones to hopefully fit within maxbone contraints
+ int mn = pMesh->m_submeshes.size();
+ for ( int mi = 0; mi < mn; ++mi)
+ {
+ RemapBonesOnSubmesh( pMesh->m_submeshes[mi], pMesh->m_bones );
+ Msg( "remapping %d bones on mesh to %d bones on submesh %d\n",
+ pMesh->m_bones.size(),
+ pMesh->m_submeshes[mi]->bones.size(),
+ mi );
+ }
+#endif
+
+ return pMesh;
+}
diff --git a/movieobjects/dmetexture.cpp b/movieobjects/dmetexture.cpp
new file mode 100644
index 0000000..39214d1
--- /dev/null
+++ b/movieobjects/dmetexture.cpp
@@ -0,0 +1,290 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmetexture.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects_interfaces.h"
+
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialsystem.h"
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeBaseTexture, CDmeBaseTexture );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeBaseTexture::OnConstruction()
+{
+ m_pVTFTexture = CreateVTFTexture();
+
+ m_bClampS.Init( this, "clampS" );
+ m_bClampT.Init( this, "clampT" );
+ m_bClampU.Init( this, "clampU" );
+ m_bNoLod.Init( this, "noMipmapLOD" );
+ m_bNiceFiltered.Init( this, "niceFiltered" );
+ m_bNormalMap.Init( this, "normalMap" );
+ m_flBumpScale.Init( this, "bumpScale" );
+ m_nCompressType.Init( this, "compressType" );
+ m_nFilterType.Init( this, "filterType" );
+ m_nMipmapType.Init( this, "mipmapType" );
+}
+
+void CDmeBaseTexture::OnDestruction()
+{
+ if ( m_pVTFTexture )
+ {
+ DestroyVTFTexture( m_pVTFTexture );
+ m_pVTFTexture = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// accessor for cached ITexture
+//-----------------------------------------------------------------------------
+ITexture *CDmeBaseTexture::GetCachedTexture()
+{
+ return m_Texture;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes texture flags
+//-----------------------------------------------------------------------------
+int CDmeBaseTexture::CalcTextureFlags( int nDepth ) const
+{
+ int nFlags = 0;
+ if ( m_bClampS )
+ {
+ nFlags |= TEXTUREFLAGS_CLAMPS;
+ }
+ if ( m_bClampT )
+ {
+ nFlags |= TEXTUREFLAGS_CLAMPT;
+ }
+ if ( m_bClampU )
+ {
+ nFlags |= TEXTUREFLAGS_CLAMPU;
+ }
+ if ( m_bNoLod )
+ {
+ nFlags |= TEXTUREFLAGS_NOLOD;
+ }
+ if ( m_bNiceFiltered )
+ {
+ nFlags |= TEXTUREFLAGS_NICEFILTERED;
+ }
+ if ( m_bNormalMap )
+ {
+ nFlags |= TEXTUREFLAGS_NORMAL;
+ }
+ if ( m_bNormalMap )
+ {
+ nFlags |= TEXTUREFLAGS_NORMAL;
+ }
+ if ( m_bNoDebugOverride )
+ {
+ nFlags |= TEXTUREFLAGS_NODEBUGOVERRIDE;
+ }
+
+ switch ( m_nCompressType )
+ {
+ case DMETEXTURE_COMPRESS_DEFAULT:
+ case DMETEXTURE_COMPRESS_DXT1:
+ break;
+ case DMETEXTURE_COMPRESS_NONE:
+ nFlags |= TEXTUREFLAGS_NOCOMPRESS;
+ break;
+ case DMETEXTURE_COMPRESS_DXT5:
+ nFlags |= TEXTUREFLAGS_HINT_DXT5;
+ break;
+ }
+
+ switch ( m_nFilterType )
+ {
+ case DMETEXTURE_FILTER_DEFAULT:
+ case DMETEXTURE_FILTER_BILINEAR:
+ break;
+ case DMETEXTURE_FILTER_ANISOTROPIC:
+ nFlags |= TEXTUREFLAGS_ANISOTROPIC;
+ break;
+ case DMETEXTURE_FILTER_TRILINEAR:
+ nFlags |= TEXTUREFLAGS_TRILINEAR;
+ break;
+ case DMETEXTURE_FILTER_POINT:
+ nFlags |= TEXTUREFLAGS_POINTSAMPLE;
+ break;
+ }
+
+ switch ( m_nMipmapType )
+ {
+ case DMETEXTURE_MIPMAP_DEFAULT:
+ case DMETEXTURE_MIPMAP_ALL_LEVELS:
+ break;
+ case DMETEXTURE_MIPMAP_NONE:
+ nFlags |= TEXTUREFLAGS_NOMIP;
+ break;
+ }
+
+ if ( nDepth > 1 )
+ {
+ // FIXME: Volume textures don't currently support DXT compression
+ nFlags &= ~TEXTUREFLAGS_HINT_DXT5;
+ nFlags |= TEXTUREFLAGS_NOCOMPRESS;
+
+ // FIXME: Volume textures don't currently support NICE filtering
+ nFlags &= ~TEXTUREFLAGS_NICEFILTERED;
+ }
+
+ return nFlags;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the desired texture format based on flags
+//-----------------------------------------------------------------------------
+ImageFormat CDmeBaseTexture::ComputeDesiredImageFormat( ImageFormat srcFormat, int nWidth, int nHeight, int nDepth, int nFlags )
+{
+ // HDRFIXME: Need to figure out what format to use here.
+ if ( srcFormat == IMAGE_FORMAT_RGB323232F )
+ return IMAGE_FORMAT_RGBA16161616F;
+
+ /*
+ if( bDUDVTarget)
+ {
+ if ( bCopyAlphaToLuminance && ( nFlags & ( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA ) ) )
+ return IMAGE_FORMAT_UVLX8888;
+ return IMAGE_FORMAT_UV88;
+ }
+ */
+
+ // can't compress textures that are smaller than 4x4
+ if ( (nFlags & TEXTUREFLAGS_NOCOMPRESS) || (nFlags & TEXTUREFLAGS_PROCEDURAL) ||
+ ( nWidth < 4 ) || ( nHeight < 4 ) || ( nDepth > 1 ) )
+ {
+ if ( nFlags & ( TEXTUREFLAGS_ONEBITALPHA | TEXTUREFLAGS_EIGHTBITALPHA ) )
+ return IMAGE_FORMAT_BGRA8888;
+ return IMAGE_FORMAT_BGR888;
+ }
+
+ if( nFlags & TEXTUREFLAGS_HINT_DXT5 )
+ return IMAGE_FORMAT_DXT5;
+
+ // compressed with alpha blending
+ if ( nFlags & TEXTUREFLAGS_EIGHTBITALPHA )
+ return IMAGE_FORMAT_DXT5;
+
+ if ( nFlags & TEXTUREFLAGS_ONEBITALPHA )
+ return IMAGE_FORMAT_DXT5; // IMAGE_FORMAT_DXT1_ONEBITALPHA
+
+ return IMAGE_FORMAT_DXT1;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Normal texture
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTexture, CDmeTexture );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeTexture::OnConstruction()
+{
+ m_Images.Init( this, "images" );
+}
+
+void CDmeTexture::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Build a VTF representing the
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// resolve
+//-----------------------------------------------------------------------------
+void CDmeTexture::Resolve()
+{
+ // FIXME: Could change this to not shutdown if only the bits have changed
+ m_Texture.Shutdown();
+
+ int nFrameCount = m_Images.Count();
+ if ( nFrameCount == 0 )
+ return;
+
+ // Use the size of the 0th image
+ CDmeImage *pImage = m_Images[0];
+ int nWidth = pImage->m_Width;
+ int nHeight = pImage->m_Height;
+ int nDepth = pImage->m_Depth;
+ ImageFormat srcFormat = pImage->Format();
+
+ // FIXME: How should this work exactly?
+ int nFlags = CalcTextureFlags( nDepth );
+
+ m_pVTFTexture->Init( nWidth, nHeight, nDepth, srcFormat, nFlags, nFrameCount );
+
+
+ ImageFormat format = ComputeDesiredImageFormat( pImage->Format(), nWidth, nHeight, nDepth, nFlags );
+
+// m_Texture.InitProceduralTexture( GetName(), "DmeTexture", nWidth, nHeight, nDepth, nFrameCount, format, nFlags );
+// m_Texture->SetTextureRegenerator( this );
+
+ // Fill in the texture bits
+}
+
+/*
+//-----------------------------------------------------------------------------
+// Fill in the texture bits
+//-----------------------------------------------------------------------------
+void CDmeTexture::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
+{
+}
+*/
+
+//-----------------------------------------------------------------------------
+//
+// Cube texture
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeCubeTexture, CDmeCubeTexture );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeCubeTexture::OnConstruction()
+{
+ m_ImagesPosX.Init( this, "imagesPosX" );
+ m_ImagesNegX.Init( this, "imagesNegX" );
+ m_ImagesPosY.Init( this, "imagesPosY" );
+ m_ImagesNegY.Init( this, "imagesNegY" );
+ m_ImagesPosZ.Init( this, "imagesPosZ" );
+ m_ImagesNegZ.Init( this, "imagesNegZ" );
+}
+
+void CDmeCubeTexture::OnDestruction()
+{
+}
diff --git a/movieobjects/dmetimeframe.cpp b/movieobjects/dmetimeframe.cpp
new file mode 100644
index 0000000..4dff9f1
--- /dev/null
+++ b/movieobjects/dmetimeframe.cpp
@@ -0,0 +1,95 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmetimeframe.h"
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Class factory
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTimeFrame, CDmeTimeFrame );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeTimeFrame::OnConstruction()
+{
+ m_Start .InitAndSet( this, "startTime", 0, FATTRIB_HAS_CALLBACK );
+ m_Duration.InitAndSet( this, "durationTime", 0, FATTRIB_HAS_CALLBACK );
+ m_Offset .InitAndSet( this, "offsetTime", 0 );
+ m_Scale .InitAndSet( this, "scale", 1.0f );
+}
+
+void CDmeTimeFrame::OnDestruction()
+{
+}
+
+
+void CDmeTimeFrame::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ BaseClass::OnAttributeChanged( pAttribute );
+
+ // notify parent clip that the time has changed
+ if ( pAttribute == m_Start.GetAttribute() || pAttribute == m_Duration.GetAttribute() )
+ {
+ InvokeOnAttributeChangedOnReferrers( GetHandle(), pAttribute );
+ }
+}
+
+void CDmeTimeFrame::SetEndTime( DmeTime_t endTime, bool bChangeDuration )
+{
+ if ( bChangeDuration )
+ {
+ m_Duration = endTime.GetTenthsOfMS() - m_Start;
+ }
+ else
+ {
+ m_Start = endTime.GetTenthsOfMS() - m_Duration;
+ }
+}
+
+void CDmeTimeFrame::SetTimeScale( float flScale, DmeTime_t scaleCenter, bool bChangeDuration )
+{
+#ifdef _DEBUG
+ DmeTime_t preCenterTime = ToChildMediaTime( scaleCenter, false );
+#endif
+
+ float ratio = m_Scale / flScale;
+ int t = scaleCenter.GetTenthsOfMS() - m_Start;
+
+ if ( bChangeDuration )
+ {
+ int newDuration = int( m_Duration * ratio );
+
+ if ( scaleCenter.GetTenthsOfMS() != m_Start )
+ {
+ int newStart = int( ( m_Start - scaleCenter.GetTenthsOfMS() ) * ratio + scaleCenter.GetTenthsOfMS() );
+ SetStartTime( DmeTime_t( newStart ) );
+ }
+
+ int newStart = m_Start;
+ int newOffset = int( ( t + m_Offset ) * ratio + newStart - scaleCenter.GetTenthsOfMS() );
+ SetTimeOffset( DmeTime_t( newOffset ) );
+ SetDuration( DmeTime_t( newDuration ) );
+ }
+ else
+ {
+ int newOffset = int( ( t + m_Offset ) * ratio - t );
+ SetTimeOffset( DmeTime_t( newOffset ) );
+ }
+
+ SetTimeScale( flScale );
+
+#ifdef _DEBUG
+ DmeTime_t postCenterTime = ToChildMediaTime( scaleCenter, false );
+ Assert( abs( preCenterTime - postCenterTime ) <= DMETIME_MINDELTA );
+#endif
+}
diff --git a/movieobjects/dmetimeselection.cpp b/movieobjects/dmetimeselection.cpp
new file mode 100644
index 0000000..c1e4667
--- /dev/null
+++ b/movieobjects/dmetimeselection.cpp
@@ -0,0 +1,336 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#include "movieobjects/dmetimeselection.h"
+#include "interpolatortypes.h"
+#include "datamodel/dmelementfactoryhelper.h"
+// #include "dme_controls/RecordingState.h"
+
+IMPLEMENT_ELEMENT_FACTORY( DmeTimeSelection, CDmeTimeSelection );
+
+void CDmeTimeSelection::OnConstruction()
+{
+ m_bEnabled.InitAndSet( this, "enabled", false );
+ m_bRelative.InitAndSet( this, "relative", false );
+
+ DmeTime_t one( 1.0f );
+
+ m_falloff[ 0 ].InitAndSet( this, "falloff_left", -one.GetTenthsOfMS() );
+ m_falloff[ 1 ].InitAndSet( this, "falloff_right", one.GetTenthsOfMS() );
+
+ m_hold[ 0 ].Init( this, "hold_left" );
+ m_hold[ 1 ].Init( this, "hold_right" );
+
+ m_nFalloffInterpolatorType[ 0 ].InitAndSet( this, "interpolator_left", INTERPOLATE_LINEAR_INTERP );
+ m_nFalloffInterpolatorType[ 1 ].InitAndSet( this, "interpolator_right", INTERPOLATE_LINEAR_INTERP );
+
+ m_threshold.InitAndSet( this, "threshold", 0.0005f );
+
+ m_nRecordingState.InitAndSet( this, "recordingstate", 3 /*AS_PLAYBACK : HACK THIS SHOULD MOVE TO A PUBLIC HEADER*/ );
+}
+
+
+void CDmeTimeSelection::OnDestruction()
+{
+}
+
+static int g_InterpolatorTypes[] =
+{
+ INTERPOLATE_LINEAR_INTERP,
+ INTERPOLATE_EASE_IN,
+ INTERPOLATE_EASE_OUT,
+ INTERPOLATE_EASE_INOUT,
+};
+
+float CDmeTimeSelection::AdjustFactorForInterpolatorType( float factor, int side )
+{
+ Vector points[ 4 ];
+ points[ 0 ].Init();
+ points[ 1 ].Init( 0.0, 0.0, 0.0f );
+ points[ 2 ].Init( 1.0f, 1.0f, 0.0f );
+ points[ 3 ].Init();
+
+ Vector out;
+ Interpolator_CurveInterpolate
+ (
+ GetFalloffInterpolatorType( side ),
+ points[ 0 ], // unused
+ points[ 1 ],
+ points[ 2 ],
+ points[ 3 ], // unused
+ factor,
+ out
+ );
+ return out.y; // clamp( out.y, 0.0f, 1.0f );
+}
+
+
+//-----------------------------------------------------------------------------
+// per-type averaging methods
+//-----------------------------------------------------------------------------
+float CDmeTimeSelection::GetAmountForTime( DmeTime_t t, DmeTime_t curtime )
+{
+ Assert( IsEnabled() );
+
+ float minfrac = 0.0f;
+
+ // FIXME, this is slow, we should cache this maybe?
+ DmeTime_t times[ 4 ];
+ times[ 0 ] = GetAbsFalloff( curtime, 0 );
+ times[ 1 ] = GetAbsHold( curtime, 0 );
+ times[ 2 ] = GetAbsHold( curtime, 1 );
+ times[ 3 ] = GetAbsFalloff( curtime, 1 );
+
+ Vector points[ 4 ];
+ points[ 0 ].Init();
+ points[ 1 ].Init( 0.0, 0.0, 0.0f );
+ points[ 2 ].Init( 1.0f, 1.0f, 0.0f );
+ points[ 3 ].Init();
+
+ if ( t >= times[ 0 ] && t < times[ 1 ] )
+ {
+ float f = GetFractionOfTimeBetween( t, times[ 0 ], times[ 1 ], true );
+
+ Vector out;
+
+ Interpolator_CurveInterpolate
+ (
+ GetFalloffInterpolatorType( 0 ),
+ points[ 0 ], // unused
+ points[ 1 ],
+ points[ 2 ],
+ points[ 3 ], // unused
+ f,
+ out
+ );
+ return clamp( out.y, minfrac, 1.0f );
+ }
+
+ if ( t >= times[ 1 ] && t <= times[ 2 ] )
+ {
+ return 1.0f;
+ }
+
+ if ( t > times[ 2 ] && t <= times[ 3 ] )
+ {
+ float f = 1.0f - GetFractionOfTimeBetween( t, times[ 2 ], times[ 3 ], true );
+
+ Vector out;
+
+ Interpolator_CurveInterpolate
+ (
+ GetFalloffInterpolatorType( 1 ),
+ points[ 0 ], // unused
+ points[ 1 ],
+ points[ 2 ],
+ points[ 3 ], // unused
+ f,
+ out
+ );
+ return clamp( out.y, minfrac, 1.0f );
+ }
+ return minfrac;
+}
+
+void CDmeTimeSelection::GetAlphaForTime( DmeTime_t t, DmeTime_t curtime, byte& alpha )
+{
+ Assert( IsEnabled() );
+
+ byte minAlpha = 31;
+
+ // FIXME, this is slow, we should cache this maybe?
+ DmeTime_t times[ 4 ];
+ times[ 0 ] = GetAbsFalloff( curtime, 0 );
+ times[ 1 ] = GetAbsHold( curtime, 0 );
+ times[ 2 ] = GetAbsHold( curtime, 1 );
+ times[ 3 ] = GetAbsFalloff( curtime, 1 );
+
+ DmeTime_t dt1, dt2;
+ dt1 = times[ 1 ] - times[ 0 ];
+ dt2 = times[ 3 ] - times[ 2 ];
+
+ if ( dt1 > DmeTime_t( 0 ) &&
+ t >= times[ 0 ] && t < times[ 1 ] )
+ {
+ float frac = GetFractionOfTime( t - times[ 0 ], dt1, false );
+ alpha = clamp( alpha * frac, minAlpha, 255 );
+ return;
+ }
+ if ( dt2 > DmeTime_t( 0 ) &&
+ t > times[ 2 ] && t <= times[ 3 ] )
+ {
+ float frac = GetFractionOfTime( times[ 3 ] - t, dt2, false );
+ alpha = clamp( alpha * frac, minAlpha, 255 );
+ return;
+ }
+ if ( t < times[ 0 ] )
+ alpha = minAlpha;
+ else if ( t > times[ 3 ] )
+ alpha = minAlpha;
+}
+
+int CDmeTimeSelection::GetFalloffInterpolatorType( int side )
+{
+ return m_nFalloffInterpolatorType[ side ];
+}
+
+void CDmeTimeSelection::SetFalloffInterpolatorType( int side, int interpolatorType )
+{
+ m_nFalloffInterpolatorType[ side ] = interpolatorType;
+}
+
+bool CDmeTimeSelection::IsEnabled() const
+{
+ return m_bEnabled;
+}
+
+void CDmeTimeSelection::SetEnabled( bool state )
+{
+ m_bEnabled = state;
+}
+
+bool CDmeTimeSelection::IsRelative() const
+{
+ return m_bRelative;
+}
+
+void CDmeTimeSelection::SetRelative( DmeTime_t time, bool state )
+{
+ bool changed = m_bRelative != state;
+ m_bRelative = state;
+ if ( changed )
+ {
+ if ( state )
+ ConvertToRelative( time );
+ else
+ ConvertToAbsolute( time );
+ }
+}
+
+DmeTime_t CDmeTimeSelection::GetAbsFalloff( DmeTime_t time, int side )
+{
+ if ( m_bRelative )
+ {
+ return DmeTime_t( m_falloff[ side ] ) + time;
+ }
+ return DmeTime_t( m_falloff[ side ] );
+}
+
+DmeTime_t CDmeTimeSelection::GetAbsHold( DmeTime_t time, int side )
+{
+ if ( m_bRelative )
+ {
+ return DmeTime_t( m_hold[ side ] ) + time;
+ }
+ return DmeTime_t( m_hold[ side ] );
+}
+
+DmeTime_t CDmeTimeSelection::GetRelativeFalloff( DmeTime_t time, int side )
+{
+ if ( m_bRelative )
+ {
+ return DmeTime_t( m_falloff[ side ] );
+ }
+ return DmeTime_t( m_falloff[ side ] ) - time;
+}
+
+DmeTime_t CDmeTimeSelection::GetRelativeHold( DmeTime_t time, int side )
+{
+ if ( m_bRelative )
+ {
+ return DmeTime_t( m_hold[ side ] );
+ }
+ return DmeTime_t( m_hold[ side ] ) - time;
+}
+
+void CDmeTimeSelection::ConvertToRelative( DmeTime_t time )
+{
+ m_falloff[ 0 ] -= time.GetTenthsOfMS();
+ m_falloff[ 1 ] -= time.GetTenthsOfMS();
+ m_hold[ 0 ] -= time.GetTenthsOfMS();
+ m_hold[ 1 ] -= time.GetTenthsOfMS();
+}
+
+void CDmeTimeSelection::ConvertToAbsolute( DmeTime_t time )
+{
+ m_falloff[ 0 ] += time.GetTenthsOfMS();
+ m_falloff[ 1 ] += time.GetTenthsOfMS();
+ m_hold[ 0 ] += time.GetTenthsOfMS();
+ m_hold[ 1 ] += time.GetTenthsOfMS();
+}
+
+void CDmeTimeSelection::SetAbsFalloff( DmeTime_t time, int side, DmeTime_t absfallofftime )
+{
+ DmeTime_t newTime;
+ if ( m_bRelative )
+ {
+ newTime = absfallofftime - time;
+ }
+ else
+ {
+ newTime = absfallofftime;
+ }
+
+ m_falloff[ side ] = newTime.GetTenthsOfMS();
+}
+
+void CDmeTimeSelection::SetAbsHold( DmeTime_t time, int side, DmeTime_t absholdtime )
+{
+ DmeTime_t newTime;
+ if ( m_bRelative )
+ {
+ newTime = absholdtime - time;
+ }
+ else
+ {
+ newTime = absholdtime;
+ }
+
+ m_hold[ side ] = newTime.GetTenthsOfMS();
+}
+
+void CDmeTimeSelection::CopyFrom( const CDmeTimeSelection& src )
+{
+ m_bEnabled = src.m_bEnabled;
+ m_bRelative = src.m_bRelative;
+ m_threshold = src.m_threshold;
+
+ for ( int i = 0 ; i < 2; ++i )
+ {
+ m_falloff[ i ] = src.m_falloff[ i ];
+ m_hold[ i ] = src.m_hold[ i ];
+ m_nFalloffInterpolatorType[ i ] = src.m_nFalloffInterpolatorType[ i ];
+ }
+
+ m_nRecordingState = src.m_nRecordingState;
+}
+
+void CDmeTimeSelection::GetCurrent( DmeTime_t pTimes[TS_TIME_COUNT] )
+{
+ pTimes[TS_LEFT_FALLOFF].SetTenthsOfMS( m_falloff[ 0 ] );
+ pTimes[TS_LEFT_HOLD].SetTenthsOfMS( m_hold[ 0 ] );
+ pTimes[TS_RIGHT_HOLD].SetTenthsOfMS( m_hold[ 1 ] );
+ pTimes[TS_RIGHT_FALLOFF].SetTenthsOfMS( m_falloff[ 1 ] );
+}
+
+void CDmeTimeSelection::SetCurrent( DmeTime_t* pTimes )
+{
+ m_falloff[ 0 ] = pTimes[ TS_LEFT_FALLOFF ].GetTenthsOfMS();
+ m_hold[ 0 ] = pTimes[ TS_LEFT_HOLD ].GetTenthsOfMS();
+ m_hold[ 1 ] = pTimes[ TS_RIGHT_HOLD ].GetTenthsOfMS();
+ m_falloff[ 1 ] = pTimes[ TS_RIGHT_FALLOFF ].GetTenthsOfMS();
+}
+
+float CDmeTimeSelection::GetThreshold()
+{
+ return m_threshold;
+}
+
+
+void CDmeTimeSelection::SetRecordingState( RecordingState_t state )
+{
+ m_nRecordingState = ( int )state;
+}
+
+RecordingState_t CDmeTimeSelection::GetRecordingState() const
+{
+ return ( RecordingState_t )m_nRecordingState.Get();
+} \ No newline at end of file
diff --git a/movieobjects/dmetrack.cpp b/movieobjects/dmetrack.cpp
new file mode 100644
index 0000000..431c87b
--- /dev/null
+++ b/movieobjects/dmetrack.cpp
@@ -0,0 +1,818 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmetrack.h"
+
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmeclip.h"
+#include "movieobjects/dmetrackgroup.h"
+
+#include "movieobjects_interfaces.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// The solo track
+//-----------------------------------------------------------------------------
+DmElementHandle_t CDmeTrack::m_hSoloTrack[ DMECLIP_TYPE_COUNT ] =
+{
+ DMELEMENT_HANDLE_INVALID,
+ DMELEMENT_HANDLE_INVALID,
+ DMELEMENT_HANDLE_INVALID,
+ DMELEMENT_HANDLE_INVALID,
+};
+
+
+//-----------------------------------------------------------------------------
+// CDmeTrack - common container class for clip objects
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTrack, CDmeTrack );
+
+void CDmeTrack::OnConstruction()
+{
+ m_hOwner = DMELEMENT_HANDLE_INVALID;
+
+ m_Flags.ClearAllFlags();
+ m_Clips.Init( this, "children" );
+ m_Collapsed.InitAndSet( this, "collapsed", true );
+ m_Mute.InitAndSet( this, "mute", false );
+ m_Synched.InitAndSet( this, "synched", true );
+ m_ClipType.InitAndSet( this, "clipType", DMECLIP_UNKNOWN, FATTRIB_HAS_CALLBACK | FATTRIB_HAS_PRE_CALLBACK );
+
+ m_Volume.InitAndSet( this, "volume", 1.0 );
+
+}
+
+void CDmeTrack::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods of IDmElement
+//-----------------------------------------------------------------------------
+void CDmeTrack::OnAttributeChanged( CDmAttribute *pAttribute )
+{
+ BaseClass::OnAttributeChanged( pAttribute );
+
+ // Attach callbacks to detected sorted conditions if we're a film clip
+ if ( pAttribute == m_ClipType.GetAttribute() )
+ {
+ if ( m_ClipType == DMECLIP_FILM )
+ {
+ m_Flags.ClearFlag( IS_SORTED );
+ }
+ return;
+ }
+
+ // This gets called when start/end time of children change, or if the array changes
+ // This is a hack, since any OnAttributeChanged call that gets chained here from another element will trigger this
+ // At some point, we'll probably have to start sending more data through OnAttributeChanged, (like an event string or chain path)
+ // or perhaps add a new callback OnElementChanged() with this data
+ if ( pAttribute == m_Clips.GetAttribute() || ( pAttribute->GetOwner() != this ) )
+ {
+ if ( !m_Flags.IsFlagSet( SUPPRESS_DIRTY_ORDERING ) )
+ {
+ m_Flags.ClearFlag( IS_SORTED );
+ }
+ return;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Clip type
+//-----------------------------------------------------------------------------
+DmeClipType_t CDmeTrack::GetClipType() const
+{
+ return (DmeClipType_t)m_ClipType.Get();
+}
+
+void CDmeTrack::SetClipType( DmeClipType_t type )
+{
+ m_ClipType = type;
+}
+
+void CDmeTrack::SetCollapsed( bool state )
+{
+ m_Collapsed = state;
+
+}
+
+bool CDmeTrack::IsCollapsed() const
+{
+ return m_Collapsed.Get();
+}
+
+void CDmeTrack::SetMute( bool state )
+{
+ m_Mute = state;
+}
+
+//-----------------------------------------------------------------------------
+// Volume
+//-----------------------------------------------------------------------------
+void CDmeTrack::SetVolume( float state )
+{
+ m_Volume = state;
+}
+float CDmeTrack::GetVolume() const
+{
+ return m_Volume.Get();
+}
+
+// Is this track synched to the film track?
+void CDmeTrack::SetSynched( bool bState )
+{
+ m_Synched = bState;
+}
+
+bool CDmeTrack::IsSynched() const
+{
+ return m_Synched;
+}
+
+bool CDmeTrack::IsMute( bool bCheckSoloing ) const
+{
+ // if we're muted, don't play regardless of whether we're solo
+ CDmeTrack *pSoloTrack = bCheckSoloing ? GetSoloTrack() : NULL;
+ return m_Mute.Get() || ( pSoloTrack != this && pSoloTrack != NULL );
+}
+
+int CDmeTrack::GetClipCount() const
+{
+ return m_Clips.Count();
+}
+
+CDmeClip *CDmeTrack::GetClip( int i ) const
+{
+ return m_Clips[ i ];
+}
+
+const CUtlVector< DmElementHandle_t > &CDmeTrack::GetClips( ) const
+{
+ return m_Clips.Get();
+}
+
+void CDmeTrack::AddClip( CDmeClip *clip )
+{
+ if ( clip->GetClipType() == GetClipType() )
+ {
+ // FIXME: In the case of a non-overlapped track,
+ // we could optimize this to insert the clip in sorted order,
+ // then fix overlaps (fixing overlaps requires a sorted list)
+ Assert( FindClip( clip ) < 0 );
+ m_Clips.AddToTail( clip );
+ }
+}
+
+void CDmeTrack::RemoveClip( int i )
+{
+ // NOTE: Removal shouldn't cause sort order or fixup to become invalid
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+ m_Clips.Remove( i );
+}
+
+bool CDmeTrack::RemoveClip( CDmeClip *clip )
+{
+ Assert( clip->GetClipType() == GetClipType() );
+ int i = FindClip( clip );
+ if ( i != -1 )
+ {
+ RemoveClip( i );
+ return true;
+ }
+ return false;
+}
+
+void CDmeTrack::RemoveAllClips()
+{
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+ m_Clips.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the solo track, if any
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeTrack::GetSoloTrack( DmeClipType_t clipType )
+{
+ return GetElement< CDmeTrack >( m_hSoloTrack[ clipType ] );
+}
+
+void CDmeTrack::SetSoloTrack( DmeClipType_t clipType, CDmeTrack *pTrack )
+{
+ m_hSoloTrack[ clipType ] = pTrack->GetHandle();
+}
+
+bool CDmeTrack::IsSoloTrack() const
+{
+ return m_hSoloTrack[ GetClipType() ] == GetHandle();
+}
+
+CDmeTrack *CDmeTrack::GetSoloTrack() const
+{
+ return GetSoloTrack( GetClipType() );
+}
+
+void CDmeTrack::SetSoloTrack( )
+{
+ m_hSoloTrack[ GetClipType() ] = GetHandle();
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods related to finding clips
+//-----------------------------------------------------------------------------
+int CDmeTrack::FindClip( CDmeClip *clip )
+{
+ Assert( clip->GetClipType() == GetClipType() );
+ int c = m_Clips.Count();
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ if ( m_Clips[ i ] == clip )
+ return i;
+ }
+ return -1;
+}
+
+CDmeClip *CDmeTrack::FindNamedClip( const char *name )
+{
+ int c = m_Clips.Count();
+ for ( int i = c - 1; i >= 0; --i )
+ {
+ CDmeClip *child = m_Clips[ i ];
+ if ( child && !Q_stricmp( child->GetName(), name ) )
+ return child;
+ }
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find clips at, intersecting or within a particular time interval
+//-----------------------------------------------------------------------------
+void CDmeTrack::FindClipsAtTime( DmeTime_t time, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int nClipCount = GetClipCount();
+ for ( int j = 0; j < nClipCount; ++j )
+ {
+ CDmeClip *pSubClip = GetClip( j );
+ if ( !pSubClip )
+ continue;
+ if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() )
+ continue;
+
+ if ( time.IsInRange( pSubClip->GetStartTime(), pSubClip->GetEndTime() ) )
+ {
+ clips.AddToTail( pSubClip );
+ }
+ }
+}
+
+void CDmeTrack::FindClipsIntersectingTime( DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int nClipCount = GetClipCount();
+ for ( int j = 0; j < nClipCount; ++j )
+ {
+ CDmeClip *pSubClip = GetClip( j );
+ if ( !pSubClip )
+ continue;
+ if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() )
+ continue;
+
+ DmeTime_t clipStart = pSubClip->GetStartTime();
+ DmeTime_t clipEnd = pSubClip->GetEndTime();
+ if ( clipEnd >= startTime && clipStart < endTime )
+ {
+ clips.AddToTail( pSubClip );
+ }
+ }
+}
+
+void CDmeTrack::FindClipsWithinTime( DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && IsCollapsed() )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int nClipCount = GetClipCount();
+ for ( int j = 0; j < nClipCount; ++j )
+ {
+ CDmeClip *pSubClip = GetClip( j );
+ if ( !pSubClip )
+ continue;
+ if ( ( flags & DMESKIP_MUTED ) && pSubClip->IsMute() )
+ continue;
+
+ DmeTime_t clipStart = pSubClip->GetStartTime();
+ DmeTime_t clipEnd = pSubClip->GetEndTime();
+ if ( clipStart >= startTime && clipEnd <= endTime )
+ {
+ clips.AddToTail( pSubClip );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods related to shifting clips
+//-----------------------------------------------------------------------------
+void CDmeTrack::ShiftAllClips( DmeTime_t dt )
+{
+ if ( dt == DmeTime_t( 0 ) )
+ return;
+
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = m_Clips[ i ];
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+}
+
+void CDmeTrack::ShiftAllClipsAfter( DmeTime_t startTime, DmeTime_t dt, bool bTestStartingTime )
+{
+ if ( dt == DmeTime_t( 0 ) )
+ return;
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ DmeTime_t testTime = bTestStartingTime ? pSubClip->GetStartTime() : pSubClip->GetEndTime();
+ if ( startTime < testTime )
+ {
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+ }
+}
+
+void CDmeTrack::ShiftAllClipsBefore( DmeTime_t endTime, DmeTime_t dt, bool bTestEndingTime )
+{
+ if ( dt == DmeTime_t( 0 ) )
+ return;
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ DmeTime_t testTime = bTestEndingTime ? pSubClip->GetEndTime() : pSubClip->GetStartTime();
+ if ( endTime > testTime )
+ {
+ DmeTime_t startTime = pSubClip->GetStartTime();
+ pSubClip->SetStartTime( startTime + dt );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// A version that works only on film clips
+//-----------------------------------------------------------------------------
+void CDmeTrack::ShiftAllFilmClipsAfter( CDmeClip *pClip, DmeTime_t dt, bool bShiftClip )
+{
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) || ( dt == DmeTime_t( 0 ) ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = c; --i >= 0; )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip == pClip )
+ {
+ if ( bShiftClip )
+ {
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+ return;
+ }
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+
+ // Clip wasn't found!
+ Assert( 0 );
+}
+
+void CDmeTrack::ShiftAllFilmClipsBefore( CDmeClip *pClip, DmeTime_t dt, bool bShiftClip )
+{
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) || ( dt == DmeTime_t( 0 ) ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+
+ if ( pSubClip == pClip )
+ {
+ if ( bShiftClip )
+ {
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+ return;
+ }
+ pSubClip->SetStartTime( pSubClip->GetStartTime() + dt );
+ }
+
+ // Clip wasn't found!
+ Assert( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to sort clips by start time
+//-----------------------------------------------------------------------------
+struct SortInfo_t
+{
+ DmeTime_t m_startTime;
+ CDmeClip *m_pClip;
+};
+
+static int ClipStartLessFunc( const void * lhs, const void * rhs )
+{
+ SortInfo_t *pInfo1 = (SortInfo_t*)lhs;
+ SortInfo_t *pInfo2 = (SortInfo_t*)rhs;
+ if ( pInfo1->m_startTime == pInfo2->m_startTime )
+ return 0;
+
+ return pInfo1->m_startTime < pInfo2->m_startTime ? -1 : 1;
+}
+
+void CDmeTrack::SortClipsByStartTime( )
+{
+ // If we're not a film clip, then we haven't installed callbacks to make sorting fast.
+ // The IS_SORTED flag is some random state
+ if ( (m_ClipType == DMECLIP_FILM) && m_Flags.IsFlagSet( IS_SORTED ) )
+ return;
+ m_Flags.SetFlag( IS_SORTED );
+
+ int c = GetClipCount();
+ if ( c <= 1 )
+ return;
+
+ DmeTime_t lastTime;
+ SortInfo_t *pSortInfo = (SortInfo_t*)_alloca( c * sizeof(SortInfo_t) );
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip(i);
+ pSortInfo[i].m_startTime = pSubClip ? pSubClip->GetStartTime() : DmeTime_t::InvalidTime();
+ pSortInfo[i].m_pClip = pSubClip;
+ if ( lastTime > pSortInfo[i].m_startTime )
+ {
+ m_Flags.ClearFlag( IS_SORTED );
+ }
+ lastTime = pSortInfo[i].m_startTime;
+ }
+ if ( m_Flags.IsFlagSet( IS_SORTED ) )
+ return;
+
+ m_Flags.SetFlag( IS_SORTED );
+ qsort( pSortInfo, c, sizeof(SortInfo_t), ClipStartLessFunc );
+
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+
+ m_Clips.RemoveAll();
+
+ for ( int i = 0; i < c; ++i )
+ {
+ m_Clips.AddToTail( pSortInfo[i].m_pClip );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Shifts all clips to be non-overlapping
+//-----------------------------------------------------------------------------
+void CDmeTrack::FixOverlaps()
+{
+ int c = GetClipCount();
+ if ( c <= 1 )
+ return;
+
+ SortClipsByStartTime();
+
+ CSuppressAutoFixup suppress( this, SUPPRESS_OVERLAP_FIXUP | SUPPRESS_DIRTY_ORDERING );
+
+ // Cull NULL clips
+ int nActualCount = 0;
+ CDmeClip **pClips = (CDmeClip**)_alloca( c * sizeof(CDmeClip*) );
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pCurr = GetClip( i );
+ if ( pCurr && ((i == 0) || (pClips[i-1] != pCurr)) )
+ {
+ pClips[nActualCount++] = pCurr;
+ }
+ }
+
+ if ( nActualCount <= 1 )
+ return;
+
+ CDmeClip *pPrev = pClips[0];
+ for ( int i = 1; i < nActualCount; ++i )
+ {
+ CDmeClip *pCurr = pClips[i];
+
+ DmeTime_t prevEndTime = pPrev->GetEndTime();
+ DmeTime_t startTime = pCurr->GetStartTime();
+
+ if ( startTime < prevEndTime )
+ {
+ pCurr->SetStartTime( prevEndTime );
+ }
+
+ pPrev = pCurr;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a clip at a particular time
+//-----------------------------------------------------------------------------
+CDmeClip* CDmeTrack::FindFilmClipAtTime( DmeTime_t localTime )
+{
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return NULL;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip && pSubClip->GetStartTime() <= localTime && pSubClip->GetEndTime() > localTime )
+ return pSubClip;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Find first clip in a specific time range
+//-----------------------------------------------------------------------------
+CDmeClip* CDmeTrack::FindFirstFilmClipIntesectingTime( DmeTime_t localStartTime, DmeTime_t localEndTime )
+{
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return NULL;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( !pSubClip )
+ continue;
+ if ( ( localStartTime < pSubClip->GetEndTime() ) && ( localEndTime >= pSubClip->GetStartTime() ) )
+ return static_cast<CDmeFilmClip*>( pSubClip );
+ if ( localEndTime <= pSubClip->GetStartTime() )
+ break;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Inserts space in a film track for a film clip
+//-----------------------------------------------------------------------------
+void CDmeTrack::InsertSpaceInFilmTrack( DmeTime_t localStartTime, DmeTime_t localEndTime )
+{
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ CDmeClip *pClip = FindFirstFilmClipIntesectingTime( localStartTime, localEndTime );
+ if ( pClip )
+ {
+ DmeTime_t filmStart = pClip->GetStartTime();
+ DmeTime_t dt = localEndTime - filmStart;
+ ShiftAllFilmClipsAfter( pClip, dt, true );
+ }
+
+ return;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the next/previous clip in a film track
+//-----------------------------------------------------------------------------
+CDmeClip* CDmeTrack::FindPrevFilmClip( CDmeClip *pClip )
+{
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return NULL;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ if ( !pClip )
+ return m_Clips[ m_Clips.Count() - 1 ];
+
+ // FIXME: Could use a binary search here based on time.
+ // Probably doesn't matter though, since there will usually not be a ton of tracks
+ CDmeClip *pPrevClip = NULL;
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip == pClip )
+ return pPrevClip;
+
+ pPrevClip = pSubClip;
+ }
+
+ return NULL;
+}
+
+CDmeClip* CDmeTrack::FindNextFilmClip( CDmeClip *pClip )
+{
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return NULL;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ if ( !pClip )
+ return m_Clips[ 0 ];
+
+ CDmeClip *pNextClip = NULL;
+
+ int c = GetClipCount();
+ for ( int i = c; --i >= 0; )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip == pClip )
+ return pNextClip;
+
+ pNextClip = pSubClip;
+ }
+
+ return NULL;
+}
+
+
+void CDmeTrack::FindAdjacentFilmClips( CDmeClip *pClip, CDmeClip *&pPrevClip, CDmeClip *&pNextClip )
+{
+ pPrevClip = pNextClip = NULL;
+
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || !pClip || ( m_Clips.Count() == 0 ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( pSubClip == pClip )
+ {
+ pNextClip = ( i != c-1 ) ? GetClip( i+1 ) : NULL;
+ return;
+ }
+
+ pPrevClip = pSubClip;
+ }
+
+ pPrevClip = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the start/end time of the owning clip in local time
+//-----------------------------------------------------------------------------
+void CDmeTrack::FindAdjacentFilmClips( DmeTime_t localTime, CDmeClip *&pPrevClip, CDmeClip *&pNextClip )
+{
+ pPrevClip = pNextClip = NULL;
+
+ Assert( IsFilmTrack() );
+ if ( !IsFilmTrack() || ( m_Clips.Count() == 0 ) )
+ return;
+
+ // This algorithm requires sorted clips
+ SortClipsByStartTime();
+
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pSubClip = GetClip( i );
+ if ( localTime >= pSubClip->GetEndTime() )
+ {
+ pPrevClip = pSubClip;
+ }
+ if ( localTime < pSubClip->GetStartTime() )
+ {
+ pNextClip = pSubClip;
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Fills all gaps in a film track with slugs
+//-----------------------------------------------------------------------------
+void CDmeTrack::FillAllGapsWithSlugs( const char *pSlugName, DmeTime_t startTime, DmeTime_t endTime )
+{
+ if ( !IsFilmTrack() )
+ return;
+
+ FixOverlaps();
+
+ // Create temporary slugs to fill in the gaps
+ bool bSlugAdded = false;
+ int c = GetClipCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeClip *pFilmClip = GetClip(i);
+ DmeTime_t clipStartTime = pFilmClip->GetStartTime();
+ if ( clipStartTime > startTime )
+ {
+ // There's a gap, create a slug
+ CDmeFilmClip *pSlug = CreateSlugClip( pSlugName, startTime, clipStartTime, GetFileId() );
+
+ // This will add the slug to the end; so we don't have to
+ // worry about iterating over it (we've cached off the initial count)
+ AddClip( pSlug );
+
+ bSlugAdded = true;
+ }
+ startTime = pFilmClip->GetEndTime();
+ }
+
+ if ( endTime > startTime )
+ {
+ // There's a gap, create a temporary slug
+ CDmeFilmClip *pSlug = CreateSlugClip( pSlugName, startTime, endTime, GetFileId() );
+
+ // This will add the slug to the end; so we don't have to
+ // worry about iterating over it (we've cached off the initial count)
+ AddClip( pSlug );
+
+ bSlugAdded = true;
+ }
+
+ if ( bSlugAdded )
+ {
+ FixOverlaps();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// helper methods
+//-----------------------------------------------------------------------------
+CDmeTrackGroup *GetParentTrackGroup( CDmeTrack *pTrack )
+{
+ DmAttributeReferenceIterator_t hAttr = g_pDataModel->FirstAttributeReferencingElement( pTrack->GetHandle() );
+ for ( ; hAttr != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; hAttr = g_pDataModel->NextAttributeReferencingElement( hAttr ) )
+ {
+ CDmAttribute *pAttr = g_pDataModel->GetAttribute( hAttr );
+ if ( !pAttr )
+ continue;
+
+ CDmeTrackGroup *pTrackGroup = CastElement< CDmeTrackGroup >( pAttr->GetOwner() );
+ if ( pTrackGroup )
+ return pTrackGroup;
+ }
+ return NULL;
+}
diff --git a/movieobjects/dmetrackgroup.cpp b/movieobjects/dmetrackgroup.cpp
new file mode 100644
index 0000000..0070329
--- /dev/null
+++ b/movieobjects/dmetrackgroup.cpp
@@ -0,0 +1,524 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmetrackgroup.h"
+
+#include <limits.h>
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "movieobjects/dmetrack.h"
+#include "movieobjects/dmeclip.h"
+
+#include "movieobjects_interfaces.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// CDmeTrackGroup - contains a list of tracks
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTrackGroup, CDmeTrackGroup );
+
+void CDmeTrackGroup::OnConstruction()
+{
+ m_hOwner = DMELEMENT_HANDLE_INVALID;
+
+ m_Tracks.Init( this, "tracks", FATTRIB_MUSTCOPY | FATTRIB_HAS_ARRAY_CALLBACK );
+ m_bIsVisible.InitAndSet( this, "visible", true );
+ m_bMute.Init( this, "mute" );
+ m_nDisplaySize.InitAndSet( this, "displaySize", 110 );
+ m_bMinimized.InitAndSet( this, "minimized", true );
+ m_nMaxTrackCount = INT_MAX;
+ m_Volume.InitAndSet( this, "volume", 1.0 );
+
+}
+
+void CDmeTrackGroup::OnDestruction()
+{
+ // NOTE: The track owner handles may still be pointing to us when we get destructed,
+ // but their handles will be invalid, so GetTrackGroup on a track
+ // will correctly return NULL.
+}
+
+
+//-----------------------------------------------------------------------------
+// Max track count
+//-----------------------------------------------------------------------------
+void CDmeTrackGroup::SetMaxTrackCount( int nCount )
+{
+ m_nMaxTrackCount = nCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Mute
+//-----------------------------------------------------------------------------
+void CDmeTrackGroup::SetMute( bool state )
+{
+ m_bMute = state;
+}
+
+bool CDmeTrackGroup::IsMute( ) const
+{
+ return m_bMute;
+}
+
+//-----------------------------------------------------------------------------
+// Volume
+//-----------------------------------------------------------------------------
+void CDmeTrackGroup::SetVolume( float state )
+{
+ m_Volume = state;
+}
+float CDmeTrackGroup::GetVolume() const
+{
+ return m_Volume.Get();
+}
+
+//-----------------------------------------------------------------------------
+// Owning clip
+//-----------------------------------------------------------------------------
+CDmeClip *CDmeTrackGroup::GetOwnerClip()
+{
+ return GetElement< CDmeClip >( m_hOwner );
+}
+
+void CDmeTrackGroup::SetOwnerClip( CDmeClip *pClip )
+{
+ m_hOwner = pClip ? pClip->GetHandle() : DMELEMENT_HANDLE_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Are we a film track group?
+//-----------------------------------------------------------------------------
+bool CDmeTrackGroup::IsFilmTrackGroup()
+{
+ CDmeClip *pOwnerClip = GetOwnerClip();
+ if ( pOwnerClip )
+ return pOwnerClip->GetFilmTrackGroup() == this;
+ return m_nMaxTrackCount == 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is a particular clip typed able to be added?
+//-----------------------------------------------------------------------------
+bool CDmeTrackGroup::IsSubClipTypeAllowed( DmeClipType_t type )
+{
+ if ( IsFilmTrackGroup() )
+ {
+ if ( type != DMECLIP_FILM )
+ return false;
+ }
+ else
+ {
+ if ( type == DMECLIP_FILM )
+ return false;
+ }
+
+ CDmeClip *pOwnerClip = GetOwnerClip();
+ Assert( pOwnerClip );
+ if ( !pOwnerClip )
+ return true;
+
+ return pOwnerClip->IsSubClipTypeAllowed( type );
+}
+
+
+//-----------------------------------------------------------------------------
+// Track addition/removal
+//-----------------------------------------------------------------------------
+void CDmeTrackGroup::AddTrack( CDmeTrack *pTrack )
+{
+ // FIXME: Should check if track with same name already exists???
+ if ( GetTrackIndex( pTrack ) < 0 )
+ {
+ // Tracks can only exist in one track group
+ Assert( GetTrackIndex( pTrack ) >= 0 );
+ m_Tracks.AddToTail( pTrack );
+ Assert( m_nMaxTrackCount >= m_Tracks.Count() );
+ }
+}
+
+CDmeTrack* CDmeTrackGroup::AddTrack( const char *pTrackName, DmeClipType_t trackType )
+{
+ CDmeTrack *pTrack = CreateElement< CDmeTrack >( pTrackName, GetFileId() );
+ pTrack->SetClipType( trackType );
+ pTrack->SetCollapsed( false );
+ m_Tracks.AddToTail( pTrack );
+ Assert( m_nMaxTrackCount >= m_Tracks.Count() );
+ return pTrack;
+}
+
+CDmeTrack* CDmeTrackGroup::FindOrAddTrack( const char *pTrackName, DmeClipType_t trackType )
+{
+ CDmeTrack *pTrack = FindTrack( pTrackName );
+ if ( pTrack )
+ {
+ // If we found it, but it's the wrong type, no dice
+ if ( pTrack->GetClipType() != trackType )
+ return NULL;
+ }
+ else
+ {
+ pTrack = AddTrack( pTrackName, trackType );
+ }
+ return pTrack;
+}
+
+void CDmeTrackGroup::RemoveTrack( int nIndex )
+{
+ m_Tracks.Remove( nIndex );
+}
+
+void CDmeTrackGroup::RemoveTrack( CDmeTrack *pTrack )
+{
+ int i = GetTrackIndex( pTrack );
+ if ( i >= 0 )
+ {
+ m_Tracks.Remove( i );
+ }
+}
+
+void CDmeTrackGroup::RemoveTrack( const char *pTrackName )
+{
+ if ( !pTrackName )
+ {
+ pTrackName = DMETRACK_DEFAULT_NAME;
+ }
+
+ int c = m_Tracks.Count();
+ for ( int i = c; --i >= 0; )
+ {
+ if ( !Q_strcmp( m_Tracks[i]->GetName(), pTrackName ) )
+ {
+ m_Tracks.Remove( i );
+ return;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Track finding
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeTrackGroup::FindTrack( const char *pTrackName ) const
+{
+ if ( !pTrackName )
+ {
+ pTrackName = DMETRACK_DEFAULT_NAME;
+ }
+
+ int c = m_Tracks.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CDmeTrack *pTrack = m_Tracks[i];
+ if ( !pTrack )
+ continue;
+
+ if ( !Q_strcmp( pTrack->GetName(), pTrackName ) )
+ return pTrack;
+ }
+ return NULL;
+}
+
+int CDmeTrackGroup::GetTrackIndex( CDmeTrack *pTrack ) const
+{
+ int nTracks = m_Tracks.Count();
+ for ( int i = 0 ; i < nTracks; ++i )
+ {
+ if ( pTrack == m_Tracks[i] )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates the film track group [for internal use only]
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeTrackGroup::CreateFilmTrack()
+{
+ Assert( GetTrackCount() == 0 );
+ return AddTrack( "Film", DMECLIP_FILM );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the film track, if any
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeTrackGroup::GetFilmTrack()
+{
+ if ( !IsFilmTrackGroup() )
+ return NULL;
+
+ if ( GetTrackCount() > 0 )
+ {
+ Assert( GetTrackCount() == 1 );
+ return m_Tracks[0];
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adding/removing clips from tracks
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeTrackGroup::AddClip( CDmeClip *pClip, const char *pTrackName )
+{
+ DmeClipType_t type = pClip->GetClipType();
+ if ( !pTrackName )
+ {
+ pTrackName = DMETRACK_DEFAULT_NAME;
+ }
+
+ CDmeTrack *pTrack = FindOrAddTrack( pTrackName, type );
+ if ( pTrack )
+ {
+ pTrack->AddClip( pClip );
+ }
+ return pTrack;
+}
+
+bool CDmeTrackGroup::RemoveClip( CDmeClip *pClip )
+{
+ CDmeTrack *pTrack = FindTrackForClip( pClip );
+ if ( pTrack )
+ return pTrack->RemoveClip( pClip );
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Changing clip track
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeTrackGroup::ChangeTrack( CDmeClip *pClip, const char *pNewTrack )
+{
+ // Add, then remove, to avoid refcount problems
+ // Don't remove if it wasn't added for some reason.
+ CDmeTrack *pOldTrack = FindTrackForClip( pClip );
+ CDmeTrack *pTrack = AddClip( pClip, pNewTrack );
+ if ( pTrack && pOldTrack )
+ {
+ pOldTrack->RemoveClip( pClip );
+ }
+ return pTrack;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finding clips in tracks
+//-----------------------------------------------------------------------------
+CDmeTrack *CDmeTrackGroup::FindTrackForClip( CDmeClip *pClip ) const
+{
+ int nTrackIndex = -1;
+ if ( !FindTrackForClip( pClip, &nTrackIndex, NULL ) )
+ return NULL;
+
+ return GetTrack( nTrackIndex );
+}
+
+
+bool CDmeTrackGroup::FindTrackForClip( CDmeClip *pClip, int *pTrackIndex, int *pClipIndex ) const
+{
+ DmeClipType_t type = pClip->GetClipType();
+ int c = GetTrackCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeTrack *pTrack = GetTrack( i );
+ if ( !pTrack )
+ continue;
+
+ if ( pTrack->GetClipType() != type )
+ continue;
+
+ int nClipCount = pTrack->GetClipCount();
+ for ( int j = 0; j < nClipCount; ++j )
+ {
+ if ( pTrack->GetClip( j ) == pClip )
+ {
+ if ( pTrackIndex )
+ {
+ *pTrackIndex = i;
+ }
+ if ( pClipIndex )
+ {
+ *pClipIndex = j;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Finding clips in tracks by time
+//-----------------------------------------------------------------------------
+void CDmeTrackGroup::FindClipsAtTime( DmeClipType_t clipType, DmeTime_t time, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && ( !IsVisible() || IsMinimized() ) )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int c = GetTrackCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeTrack *pTrack = GetTrack( i );
+ if ( !pTrack )
+ continue;
+
+ if ( ( clipType != DMECLIP_UNKNOWN ) && ( pTrack->GetClipType() != clipType ) )
+ continue;
+
+ pTrack->FindClipsAtTime( time, flags, clips );
+ }
+}
+
+
+void CDmeTrackGroup::FindClipsIntersectingTime( DmeClipType_t clipType, DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && ( !IsVisible() || IsMinimized() ) )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int c = GetTrackCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeTrack *pTrack = GetTrack( i );
+ if ( !pTrack )
+ continue;
+
+ if ( ( clipType != DMECLIP_UNKNOWN ) && ( pTrack->GetClipType() != clipType ) )
+ continue;
+
+ pTrack->FindClipsIntersectingTime( startTime, endTime, flags, clips );
+ }
+}
+
+
+void CDmeTrackGroup::FindClipsWithinTime( DmeClipType_t clipType, DmeTime_t startTime, DmeTime_t endTime, DmeClipSkipFlag_t flags, CUtlVector< CDmeClip * >& clips ) const
+{
+ if ( ( flags & DMESKIP_INVISIBLE ) && ( !IsVisible() || IsMinimized() ) )
+ return;
+
+ if ( ( flags & DMESKIP_MUTED ) && IsMute() )
+ return;
+
+ int c = GetTrackCount();
+ for ( int i = 0; i < c; ++i )
+ {
+ CDmeTrack *pTrack = GetTrack( i );
+ if ( !pTrack )
+ continue;
+
+ if ( ( clipType != DMECLIP_UNKNOWN ) && ( pTrack->GetClipType() != clipType ) )
+ continue;
+
+ pTrack->FindClipsWithinTime( startTime, endTime, flags, clips );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes empty tracks
+//-----------------------------------------------------------------------------
+void CDmeTrackGroup::RemoveEmptyTracks()
+{
+ int tc = GetTrackCount();
+ for ( int i = tc; --i >= 0; )
+ {
+ CDmeTrack *pTrack = GetTrack( i );
+ if ( pTrack->GetClipCount() == 0 )
+ {
+ RemoveTrack( i );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sort tracks by track type, then alphabetically
+//-----------------------------------------------------------------------------
+static int TrackLessFunc( const void * lhs, const void * rhs )
+{
+ CDmeTrack *pInfo1 = *(CDmeTrack**)lhs;
+ CDmeTrack *pInfo2 = *(CDmeTrack**)rhs;
+ if ( pInfo1->GetClipType() < pInfo2->GetClipType() )
+ return -1;
+ if ( pInfo1->GetClipType() > pInfo2->GetClipType() )
+ return 1;
+ return Q_strcmp( pInfo1->GetName(), pInfo2->GetName() );
+}
+
+void CDmeTrackGroup::SortTracksByType()
+{
+ int tc = GetTrackCount();
+ if ( tc == 0 )
+ return;
+
+ CDmeTrack **ppTrack = (CDmeTrack**)_alloca( tc * sizeof(CDmeTrack*) );
+ for ( int i = 0; i < tc; ++i )
+ {
+ ppTrack[i] = GetTrack(i);
+ }
+
+ qsort( ppTrack, tc, sizeof(CDmeTrack*), TrackLessFunc );
+
+ m_Tracks.RemoveAll();
+
+ for ( int i = 0; i < tc; ++i )
+ {
+ m_Tracks.AddToTail( ppTrack[i] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the flattened clip count
+//-----------------------------------------------------------------------------
+int CDmeTrackGroup::GetSubClipCount() const
+{
+ int nCount = 0;
+ DMETRACKGROUP_FOREACH_CLIP_START( this, pTrack, pClip )
+ ++nCount;
+ DMETRACKGROUP_FOREACH_CLIP_END()
+ return nCount;
+}
+
+void CDmeTrackGroup::GetSubClips( CDmeClip **ppClips )
+{
+ int nCount = 0;
+ DMETRACKGROUP_FOREACH_CLIP_START( this, pTrack, pClip )
+ ppClips[nCount++] = pClip;
+ DMETRACKGROUP_FOREACH_CLIP_END()
+}
+
+//-----------------------------------------------------------------------------
+// helper methods
+//-----------------------------------------------------------------------------
+CDmeFilmClip *GetParentClip( CDmeTrackGroup *pTrackGroup )
+{
+ DmAttributeReferenceIterator_t hAttr = g_pDataModel->FirstAttributeReferencingElement( pTrackGroup->GetHandle() );
+ for ( ; hAttr != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; hAttr = g_pDataModel->NextAttributeReferencingElement( hAttr ) )
+ {
+ CDmAttribute *pAttr = g_pDataModel->GetAttribute( hAttr );
+ if ( !pAttr )
+ continue;
+
+ CDmeFilmClip *pFilmClip = CastElement< CDmeFilmClip >( pAttr->GetOwner() );
+ if ( pFilmClip )
+ return pFilmClip;
+ }
+ return NULL;
+}
diff --git a/movieobjects/dmetransform.cpp b/movieobjects/dmetransform.cpp
new file mode 100644
index 0000000..ab7c456
--- /dev/null
+++ b/movieobjects/dmetransform.cpp
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmetransform.h"
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "mathlib/vector.h"
+#include "mathlib/mathlib.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTransform, CDmeTransform );
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+void CDmeTransform::OnConstruction()
+{
+ m_Position.Init( this, "position" );
+ m_Orientation.Init( this, "orientation" );
+}
+
+void CDmeTransform::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// FIXME: Replace this with actual methods to do editing
+//-----------------------------------------------------------------------------
+void CDmeTransform::SetTransform( const matrix3x4_t &transform )
+{
+ Vector origin;
+ Quaternion angles;
+ MatrixAngles( transform, angles, origin );
+ m_Orientation.Set( angles );
+ m_Position.Set( origin );
+}
+
+void CDmeTransform::GetTransform( matrix3x4_t &transform )
+{
+ QuaternionMatrix( m_Orientation.Get(), m_Position.Get(), transform );
+}
+
+const Vector &CDmeTransform::GetPosition() const
+{
+ return m_Position.Get();
+}
+
+void CDmeTransform::SetPosition( const Vector &vecPosition )
+{
+ m_Position = vecPosition;
+}
+
+const Quaternion &CDmeTransform::GetOrientation() const
+{
+ return m_Orientation.Get();
+}
+
+void CDmeTransform::SetOrientation( const Quaternion &orientation )
+{
+ m_Orientation = orientation;
+}
+
+
+CDmAttribute *CDmeTransform::GetPositionAttribute()
+{
+ return m_Position.GetAttribute();
+}
+
+CDmAttribute *CDmeTransform::GetOrientationAttribute()
+{
+ return m_Orientation.GetAttribute();
+}
diff --git a/movieobjects/dmetransforminput.cpp b/movieobjects/dmetransforminput.cpp
new file mode 100644
index 0000000..081381d
--- /dev/null
+++ b/movieobjects/dmetransforminput.cpp
@@ -0,0 +1,98 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmetransforminput.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTranslationInput, CDmeTranslationInput );
+
+
+void CDmeTranslationInput::OnConstruction()
+{
+ m_translation.Init( this, "translation" );
+}
+
+void CDmeTranslationInput::OnDestruction()
+{
+}
+
+bool CDmeTranslationInput::IsDirty()
+{
+ return true;
+}
+
+void CDmeTranslationInput::Operate()
+{
+}
+
+void CDmeTranslationInput::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+}
+
+void CDmeTranslationInput::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_translation.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeRotationInput, CDmeRotationInput );
+
+
+void CDmeRotationInput::OnConstruction()
+{
+ m_orientation.Init( this, "orientation" );
+ m_angles.Init( this, "angles" );
+}
+
+void CDmeRotationInput::OnDestruction()
+{
+}
+
+bool CDmeRotationInput::IsDirty()
+{
+ return true;
+}
+
+void CDmeRotationInput::Operate()
+{
+}
+
+void CDmeRotationInput::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+}
+
+void CDmeRotationInput::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_orientation.GetAttribute() );
+ attrs.AddToTail( m_angles.GetAttribute() );
+}
+
+void CDmeRotationInput::SetRotation( const Quaternion& quat )
+{
+ QAngle qangle;
+ QuaternionAngles( quat, qangle );
+ m_angles = qangle;
+ m_orientation = quat;
+}
+
+void CDmeRotationInput::SetRotation( const QAngle& qangle )
+{
+ Quaternion quat;
+ AngleQuaternion( qangle, quat );
+ m_orientation = quat;
+ m_angles = qangle;
+}
diff --git a/movieobjects/dmetransformlist.cpp b/movieobjects/dmetransformlist.cpp
new file mode 100644
index 0000000..e6b9a30
--- /dev/null
+++ b/movieobjects/dmetransformlist.cpp
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// An element that contains a transformlist
+//
+//=============================================================================
+#include "movieobjects/dmetransformlist.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTransformList, CDmeTransformList );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeTransformList::OnConstruction()
+{
+ m_Transforms.Init( this, "transforms" );
+}
+
+void CDmeTransformList::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the transform
+//-----------------------------------------------------------------------------
+void CDmeTransformList::SetTransform( int nIndex, const matrix3x4_t& mat )
+{
+ m_Transforms[nIndex]->SetTransform( mat );
+}
diff --git a/movieobjects/dmetransformoperator.cpp b/movieobjects/dmetransformoperator.cpp
new file mode 100644
index 0000000..e331347
--- /dev/null
+++ b/movieobjects/dmetransformoperator.cpp
@@ -0,0 +1,107 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The transform operator class - shortcut to setting transform values from floats
+//
+//=============================================================================
+#include "movieobjects/dmetransformoperator.h"
+#include "movieobjects/dmetransform.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeTransformOperator, CDmeTransformOperator );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeTransformOperator::OnConstruction()
+{
+ m_transform.Init( this, "transform" );
+ m_positionX.Init( this, "positionX" );
+ m_positionY.Init( this, "positionY" );
+ m_positionZ.Init( this, "positionZ" );
+ m_orientationX.Init( this, "orientationX" );
+ m_orientationY.Init( this, "orientationY" );
+ m_orientationZ.Init( this, "orientationZ" );
+ m_orientationW.Init( this, "orientationW" );
+}
+
+void CDmeTransformOperator::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeTransformOperator::Operate()
+{
+ CDmeTransform *pTransform = m_transform.GetElement();
+ if ( pTransform == NULL )
+ return;
+
+ Vector position = pTransform->GetValue< Vector >( "position" );
+ Quaternion orientation = pTransform->GetValue< Quaternion >( "orientation" );
+
+ position.x = m_positionX.Get();
+ position.y = m_positionY.Get();
+ position.z = m_positionZ.Get();
+
+ orientation.x = m_orientationX.Get();
+ orientation.y = m_orientationY.Get();
+ orientation.z = m_orientationZ.Get();
+ orientation.w = m_orientationW.Get();
+
+ pTransform->SetValue( "position", position );
+ pTransform->SetValue( "orientation", orientation );
+}
+
+// hack to avoid MSVC complaining about multiply defined symbols
+namespace TransformOp
+{
+void AddAttr( CUtlVector< CDmAttribute * > &attrs, CDmAttribute *pAttr )
+{
+ if ( pAttr == NULL )
+ return;
+ attrs.AddToTail( pAttr );
+}
+};
+using namespace TransformOp;
+
+void CDmeTransformOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ AddAttr( attrs, m_positionX.GetAttribute() );
+ AddAttr( attrs, m_positionY.GetAttribute() );
+ AddAttr( attrs, m_positionZ.GetAttribute() );
+ AddAttr( attrs, m_orientationX.GetAttribute() );
+ AddAttr( attrs, m_orientationY.GetAttribute() );
+ AddAttr( attrs, m_orientationZ.GetAttribute() );
+ AddAttr( attrs, m_orientationW.GetAttribute() );
+}
+
+void CDmeTransformOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ CDmeTransform *pTransform = m_transform.GetElement();
+ if ( pTransform == NULL )
+ return;
+
+ AddAttr( attrs, pTransform->GetAttribute( "position" ) );
+ AddAttr( attrs, pTransform->GetAttribute( "orientation" ) );
+}
+
+void CDmeTransformOperator::SetTransform( CDmeTransform *pTransform )
+{
+ m_transform.Set( pTransform );
+}
+
+const CDmeTransform *CDmeTransformOperator::GetTransform() const
+{
+ return m_transform.GetElement();
+}
diff --git a/movieobjects/dmeunpackoperators.cpp b/movieobjects/dmeunpackoperators.cpp
new file mode 100644
index 0000000..d9e8c8b
--- /dev/null
+++ b/movieobjects/dmeunpackoperators.cpp
@@ -0,0 +1,335 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects/dmeunpackoperators.h"
+#include "movieobjects_interfaces.h"
+#include "datamodel/dmelementfactoryhelper.h"
+#include "datamodel/dmattribute.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// CDmeUnpackColorOperator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeUnpackColorOperator, CDmeUnpackColorOperator );
+
+void CDmeUnpackColorOperator::OnConstruction()
+{
+ m_color.Init( this, "color" );
+ m_red .Init( this, "red" );
+ m_green.Init( this, "green" );
+ m_blue .Init( this, "blue" );
+ m_alpha.Init( this, "alpha" );
+}
+
+void CDmeUnpackColorOperator::OnDestruction()
+{
+}
+
+bool CDmeUnpackColorOperator::IsDirty()
+{
+ const Color &c = m_color.Get();
+// float s = 255.999f;
+// return c.r() != s*m_red.Get() || c.g() != s*m_green.Get() || c.b() != s*m_blue.Get() || c.a() != s*m_alpha.Get();
+ return c.r() != m_red.Get() || c.g() != m_green.Get() || c.b() != m_blue.Get() || c.a() != m_alpha.Get();
+}
+
+void CDmeUnpackColorOperator::Operate()
+{
+// float s = 255.999f;
+// m_color.Set( Color( s*m_red.Get(), s*m_green.Get(), s*m_blue.Get(), s*m_alpha.Get() ) );
+ m_red .Set( m_color.Get().r() );
+ m_green.Set( m_color.Get().g() );
+ m_blue .Set( m_color.Get().b() );
+ m_alpha.Set( m_color.Get().a() );
+}
+
+void CDmeUnpackColorOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_color.GetAttribute() );
+}
+
+void CDmeUnpackColorOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_red.GetAttribute() );
+ attrs.AddToTail( m_green.GetAttribute() );
+ attrs.AddToTail( m_blue.GetAttribute() );
+ attrs.AddToTail( m_alpha.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeUnpackVector2Operator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeUnpackVector2Operator, CDmeUnpackVector2Operator );
+
+void CDmeUnpackVector2Operator::OnConstruction()
+{
+ m_vector.Init( this, "vector" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+}
+
+void CDmeUnpackVector2Operator::OnDestruction()
+{
+}
+
+bool CDmeUnpackVector2Operator::IsDirty()
+{
+ const Vector2D &v = m_vector.Get();
+ return v.x != m_x.Get() || v.y != m_y.Get();
+}
+
+void CDmeUnpackVector2Operator::Operate()
+{
+ m_x.Set( m_vector.Get().x );
+ m_y.Set( m_vector.Get().y );
+}
+
+void CDmeUnpackVector2Operator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_vector.GetAttribute() );
+}
+
+void CDmeUnpackVector2Operator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeUnpackVector3Operator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeUnpackVector3Operator, CDmeUnpackVector3Operator );
+
+void CDmeUnpackVector3Operator::OnConstruction()
+{
+ m_vector.Init( this, "vector" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+ m_z.Init( this, "z" );
+}
+
+void CDmeUnpackVector3Operator::OnDestruction()
+{
+}
+
+bool CDmeUnpackVector3Operator::IsDirty()
+{
+ const Vector &v = m_vector.Get();
+ return v.x != m_x.Get() || v.y != m_y.Get() || v.z != m_z.Get();
+}
+
+void CDmeUnpackVector3Operator::Operate()
+{
+ m_x.Set( m_vector.Get().x );
+ m_y.Set( m_vector.Get().y );
+ m_z.Set( m_vector.Get().z );
+}
+
+void CDmeUnpackVector3Operator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_vector.GetAttribute() );
+}
+
+void CDmeUnpackVector3Operator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+ attrs.AddToTail( m_z.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeUnpackVector4Operator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeUnpackVector4Operator, CDmeUnpackVector4Operator );
+
+void CDmeUnpackVector4Operator::OnConstruction()
+{
+ m_vector.Init( this, "vector" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+ m_z.Init( this, "z" );
+ m_w.Init( this, "w" );
+}
+
+void CDmeUnpackVector4Operator::OnDestruction()
+{
+}
+
+bool CDmeUnpackVector4Operator::IsDirty()
+{
+ const Vector4D &v = m_vector.Get();
+ return v.x != m_x.Get() || v.y != m_y.Get() || v.z != m_z.Get() || v.w != m_w.Get();
+}
+
+void CDmeUnpackVector4Operator::Operate()
+{
+ m_x.Set( m_vector.Get().x );
+ m_y.Set( m_vector.Get().y );
+ m_z.Set( m_vector.Get().z );
+ m_w.Set( m_vector.Get().w );
+}
+
+void CDmeUnpackVector4Operator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_vector.GetAttribute() );
+}
+
+void CDmeUnpackVector4Operator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+ attrs.AddToTail( m_z.GetAttribute() );
+ attrs.AddToTail( m_w.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeUnpackQAngleOperator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeUnpackQAngleOperator, CDmeUnpackQAngleOperator );
+
+void CDmeUnpackQAngleOperator::OnConstruction()
+{
+ m_qangle.Init( this, "qangle" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+ m_z.Init( this, "z" );
+}
+
+void CDmeUnpackQAngleOperator::OnDestruction()
+{
+}
+
+bool CDmeUnpackQAngleOperator::IsDirty()
+{
+ const QAngle &q = m_qangle.Get();
+ return q.x != m_x.Get() || q.y != m_y.Get() || q.z != m_z.Get();
+}
+
+void CDmeUnpackQAngleOperator::Operate()
+{
+ m_x.Set( m_qangle.Get().x );
+ m_y.Set( m_qangle.Get().y );
+ m_z.Set( m_qangle.Get().z );
+}
+
+void CDmeUnpackQAngleOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_qangle.GetAttribute() );
+}
+
+void CDmeUnpackQAngleOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+ attrs.AddToTail( m_z.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeUnpackQuaternionOperator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeUnpackQuaternionOperator, CDmeUnpackQuaternionOperator );
+
+void CDmeUnpackQuaternionOperator::OnConstruction()
+{
+ m_quaternion.Init( this, "quaternion" );
+ m_x.Init( this, "x" );
+ m_y.Init( this, "y" );
+ m_z.Init( this, "z" );
+ m_w.Init( this, "w" );
+}
+
+void CDmeUnpackQuaternionOperator::OnDestruction()
+{
+}
+
+bool CDmeUnpackQuaternionOperator::IsDirty()
+{
+ const Quaternion &q = m_quaternion.Get();
+ return q.x != m_x.Get() || q.y != m_y.Get() || q.z != m_z.Get() || q.w != m_w.Get();
+}
+
+void CDmeUnpackQuaternionOperator::Operate()
+{
+ m_x.Set( m_quaternion.Get().x );
+ m_y.Set( m_quaternion.Get().y );
+ m_z.Set( m_quaternion.Get().z );
+ m_w.Set( m_quaternion.Get().w );
+}
+
+void CDmeUnpackQuaternionOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_quaternion.GetAttribute() );
+}
+
+void CDmeUnpackQuaternionOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_x.GetAttribute() );
+ attrs.AddToTail( m_y.GetAttribute() );
+ attrs.AddToTail( m_z.GetAttribute() );
+ attrs.AddToTail( m_w.GetAttribute() );
+}
+
+
+//-----------------------------------------------------------------------------
+// CDmeUnpackVMatrixOperator
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeUnpackVMatrixOperator, CDmeUnpackVMatrixOperator );
+
+void CDmeUnpackVMatrixOperator::OnConstruction()
+{
+ m_vmatrix.Init( this, "vmatrix" );
+ char name[ 4 ];
+ for ( uint i = 0; i < 16; ++i )
+ {
+ Q_snprintf( name, sizeof(name), "m%d%d", i >> 2, i & 0x3 );
+ m_cells[ i ].Init( this, name );
+ }
+}
+
+void CDmeUnpackVMatrixOperator::OnDestruction()
+{
+}
+
+bool CDmeUnpackVMatrixOperator::IsDirty()
+{
+ const VMatrix &v = m_vmatrix.Get();
+ for ( uint i = 0; i < 16; ++i )
+ {
+ if ( *( v[ i ] ) != m_cells[ i ].Get() )
+ return true;
+ }
+ return false;
+}
+
+void CDmeUnpackVMatrixOperator::Operate()
+{
+ VMatrix v;
+ for ( uint i = 0; i < 16; ++i )
+ {
+ m_cells[ i ].Set( *( v[ i ] ) );
+ }
+ m_vmatrix.Set( v );
+}
+
+void CDmeUnpackVMatrixOperator::GetInputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ attrs.AddToTail( m_vmatrix.GetAttribute() );
+}
+
+void CDmeUnpackVMatrixOperator::GetOutputAttributes( CUtlVector< CDmAttribute * > &attrs )
+{
+ for ( uint i = 0; i < 16; ++i )
+ {
+ attrs.AddToTail( m_cells[i].GetAttribute() );
+ }
+}
diff --git a/movieobjects/dmevertexdata.cpp b/movieobjects/dmevertexdata.cpp
new file mode 100644
index 0000000..6de6f7a
--- /dev/null
+++ b/movieobjects/dmevertexdata.cpp
@@ -0,0 +1,1023 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmevertexdata.h"
+#include "movieobjects_interfaces.h"
+#include <limits.h>
+#include "tier3/tier3.h"
+#include "tier0/dbg.h"
+#include "datamodel/dmelementfactoryhelper.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Standard vertex fields
+//-----------------------------------------------------------------------------
+static char *g_pStandardFieldNames[] =
+{
+ "positions",
+ "normals",
+ "tangents",
+ "textureCoordinates",
+ "colors",
+ "jointWeights",
+ "jointIndices",
+ "balance",
+ "speed",
+ "wrinkle",
+ "weight"
+};
+
+static DmAttributeType_t g_pStandardFieldTypes[] =
+{
+ AT_VECTOR3_ARRAY,
+ AT_VECTOR3_ARRAY,
+ AT_VECTOR4_ARRAY,
+ AT_VECTOR2_ARRAY,
+ AT_COLOR_ARRAY,
+ AT_FLOAT_ARRAY,
+ AT_INT_ARRAY,
+ AT_FLOAT_ARRAY,
+ AT_FLOAT_ARRAY,
+ AT_FLOAT_ARRAY,
+ AT_FLOAT_ARRAY
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeVertexDataBase, CDmeVertexDataBase );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::OnConstruction()
+{
+ m_nVertexCount = 0;
+ memset( m_pStandardFieldIndex, 0xFF, sizeof(m_pStandardFieldIndex) );
+ m_VertexFormat.Init( this, "vertexFormat" );
+
+ m_nJointCount.Init( this, "jointCount" );
+ m_bFlipVCoordinates.Init( this, "flipVCoordinates" );
+}
+
+void CDmeVertexDataBase::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates info for fast lookups for well-known fields
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::UpdateStandardFieldInfo( int nFieldIndex, const char *pFieldName, DmAttributeType_t attrType )
+{
+ COMPILE_TIME_ASSERT( ARRAYSIZE(g_pStandardFieldNames) == STANDARD_FIELD_COUNT );
+ COMPILE_TIME_ASSERT( ARRAYSIZE(g_pStandardFieldTypes) == STANDARD_FIELD_COUNT );
+
+ for ( int i = 0; i < STANDARD_FIELD_COUNT; ++i )
+ {
+ if ( !Q_stricmp( pFieldName, g_pStandardFieldNames[i] ) )
+ {
+ if ( attrType != g_pStandardFieldTypes[i] )
+ {
+ Warning( "Standard field %s has incorrect attribute type!\n", pFieldName );
+ return;
+ }
+ m_pStandardFieldIndex[i] = nFieldIndex;
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes information about how to find particular fields
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::ComputeFieldInfo()
+{
+ // Clear existing field info,
+ // but keep the old names around so field indices remain constant
+ int nCurrentCount = m_FieldInfo.Count();
+ for ( int i = 0; i < nCurrentCount; ++i )
+ {
+ m_FieldInfo[i].m_pIndexData = NULL;
+ m_FieldInfo[i].m_pVertexData = NULL;
+ }
+
+ // FIXME: Want to maintain field indices as constants for all time
+ int nFieldCount = m_VertexFormat.Count();
+ for ( int i = 0; i < nFieldCount; ++i )
+ {
+ const char *pFieldName = m_VertexFormat[i];
+ int nLen = Q_strlen( pFieldName ) + 21;
+ char *pIndicesName = (char*)_alloca( nLen );
+ Q_snprintf( pIndicesName, nLen, "%sIndices", pFieldName );
+
+ CDmAttribute *pVerticesArray = GetAttribute( pFieldName );
+ if ( !pVerticesArray || !IsArrayType( pVerticesArray->GetType() ) )
+ continue;
+
+ CDmAttribute *pIndicesArray = NULL;
+ if ( Q_stricmp( pFieldName, g_pStandardFieldNames[FIELD_JOINT_WEIGHTS] ) &&
+ Q_stricmp( pFieldName, g_pStandardFieldNames[FIELD_JOINT_INDICES] ) )
+ {
+ pIndicesArray = GetAttribute( pIndicesName );
+ if ( !pIndicesArray || pIndicesArray->GetType() != AT_INT_ARRAY )
+ continue;
+ }
+
+ FieldIndex_t nFieldIndex = FindFieldIndex( pFieldName );
+ if ( nFieldIndex < 0 )
+ {
+ nFieldIndex = m_FieldInfo.AddToTail();
+ m_FieldInfo[nFieldIndex].m_Name = pFieldName;
+ m_FieldInfo[nFieldIndex].m_bInverseMapDirty = true;
+ UpdateStandardFieldInfo( nFieldIndex, pFieldName, pVerticesArray->GetType() );
+ }
+ m_FieldInfo[nFieldIndex].m_pVertexData = pVerticesArray;
+ m_FieldInfo[nFieldIndex].m_pIndexData = pIndicesArray;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the vertex count ( min of the index buffers )
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::ComputeVertexCount()
+{
+ int nCount = m_FieldInfo.Count();
+ if ( nCount == 0 )
+ {
+ m_nVertexCount = 0;
+ return;
+ }
+
+ m_nVertexCount = INT_MAX;
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !m_FieldInfo[i].m_pIndexData )
+ continue;
+
+ CDmrGenericArray array( m_FieldInfo[i].m_pIndexData );
+ int nFieldCount = array.Count();
+ if ( nFieldCount < m_nVertexCount )
+ {
+ m_nVertexCount = nFieldCount;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// resolve internal data from changed attributes
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::Resolve()
+{
+ BaseClass::Resolve();
+
+ if ( m_VertexFormat.IsDirty() )
+ {
+ ComputeFieldInfo();
+ }
+
+ if ( !IsVertexDeltaData() )
+ {
+ ComputeVertexCount();
+ }
+
+ // Mark inverse map dirty if necessary
+ int nCount = m_FieldInfo.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_FieldInfo[i].m_pIndexData && m_FieldInfo[i].m_pIndexData->IsFlagSet( FATTRIB_DIRTY ) )
+ {
+ m_FieldInfo[i].m_bInverseMapDirty = true;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns indices into the various fields
+//-----------------------------------------------------------------------------
+int CDmeVertexDataBase::GetPositionIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_POSITION );
+}
+
+int CDmeVertexDataBase::GetNormalIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_NORMAL );
+}
+
+int CDmeVertexDataBase::GetTangentIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_TANGENT );
+}
+
+int CDmeVertexDataBase::GetTexCoordIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_TEXCOORD );
+}
+
+int CDmeVertexDataBase::GetColorIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_COLOR );
+}
+
+int CDmeVertexDataBase::GetBalanceIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_BALANCE );
+}
+
+int CDmeVertexDataBase::GetMorphSpeedIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_MORPH_SPEED );
+}
+
+int CDmeVertexDataBase::GetWrinkleIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_WRINKLE );
+}
+
+int CDmeVertexDataBase::GetWeightIndex( int nVertexIndex ) const
+{
+ return GetFieldIndex( nVertexIndex, FIELD_WEIGHT );
+}
+
+
+//-----------------------------------------------------------------------------
+// Vertex accessors
+//-----------------------------------------------------------------------------
+const Vector& CDmeVertexDataBase::GetPosition( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_POSITION];
+ if ( nFieldIndex < 0 )
+ return vec3_origin;
+
+ CDmrArrayConst<int> indices( GetIndexData( nFieldIndex ) );
+ CDmrArrayConst<Vector> vertexData( GetVertexData( nFieldIndex ) );
+ return vertexData[ indices[nIndex] ];
+}
+
+const float *CDmeVertexDataBase::GetJointWeights( int nVertexIndex ) const
+{
+ Assert( IsVertexDeltaData() || nVertexIndex < m_nVertexCount );
+ FieldIndex_t nPosFieldIndex = m_pStandardFieldIndex[FIELD_POSITION];
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_JOINT_WEIGHTS];
+ if ( nPosFieldIndex < 0 || nFieldIndex < 0 )
+ return NULL;
+
+ CDmrArrayConst<int> indices = GetIndexData( nPosFieldIndex );
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return &vertexData[ indices[ nVertexIndex ] * m_nJointCount ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Same as GetJointWeights except it uses a direct position index instead of
+// the vertex index to access the data
+//-----------------------------------------------------------------------------
+const float *CDmeVertexDataBase::GetJointPositionWeights( int nPositionIndex ) const
+{
+ Assert( !IsVertexDeltaData() );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_JOINT_WEIGHTS];
+ if ( nFieldIndex < 0 )
+ return NULL;
+
+ CDmrArrayConst< float > jointWeights = GetVertexData( nFieldIndex );
+ Assert( nPositionIndex * m_nJointCount < jointWeights.Count() );
+ return &jointWeights[ nPositionIndex * m_nJointCount ];
+}
+
+const int *CDmeVertexDataBase::GetJointIndices( int nVertexIndex ) const
+{
+ Assert( IsVertexDeltaData() || nVertexIndex < m_nVertexCount );
+ FieldIndex_t nPosFieldIndex = m_pStandardFieldIndex[FIELD_POSITION];
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_JOINT_INDICES];
+ if ( nPosFieldIndex < 0 || nFieldIndex < 0 )
+ return NULL;
+
+ CDmrArrayConst<int> indices = GetIndexData( nPosFieldIndex );
+ CDmrArrayConst<int> vertexData = GetVertexData( nFieldIndex );
+ return &vertexData[ indices[ nVertexIndex ] * m_nJointCount ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Same as GetJointIndices except it uses a direct position index instead of
+// the vertex index to access the data
+//-----------------------------------------------------------------------------
+const int *CDmeVertexDataBase::GetJointPositionIndices( int nPositionIndex ) const
+{
+ Assert( !IsVertexDeltaData() );
+ FieldIndex_t nJointIndicesField = m_pStandardFieldIndex[ FIELD_JOINT_INDICES ];
+ if ( nJointIndicesField < 0 )
+ return NULL;
+
+ CDmrArrayConst<int> jointIndices = GetVertexData( nJointIndicesField );
+ Assert( nPositionIndex * m_nJointCount < jointIndices.Count() );
+ return &jointIndices[ nPositionIndex * m_nJointCount ];
+}
+
+const Vector& CDmeVertexDataBase::GetNormal( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_NORMAL];
+ if ( nFieldIndex < 0 )
+ return vec3_origin;
+
+ CDmrArrayConst<int> indices = GetIndexData( nFieldIndex );
+ CDmrArrayConst<Vector> vertexData = GetVertexData( nFieldIndex );
+ return vertexData[ indices[ nIndex ] ];
+}
+
+const Vector4D& CDmeVertexDataBase::GetTangent( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_TANGENT];
+ if ( nFieldIndex < 0 )
+ return vec4_origin;
+
+ CDmrArrayConst<int> indices = GetIndexData( nFieldIndex );
+ CDmrArrayConst<Vector4D> vertexData = GetVertexData( nFieldIndex );
+ return vertexData[ indices[ nIndex ] ];
+}
+
+const Vector2D& CDmeVertexDataBase::GetTexCoord( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_TEXCOORD];
+ if ( nFieldIndex < 0 )
+ return vec2_origin;
+
+ CDmrArrayConst<int> indices = GetIndexData( nFieldIndex );
+ CDmrArrayConst<Vector2D> vertexData = GetVertexData( nFieldIndex );
+ return vertexData[ indices[ nIndex ] ];
+}
+
+static Color s_Black( 0, 0, 0, 255 );
+const Color& CDmeVertexDataBase::GetColor( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_COLOR];
+ if ( nFieldIndex < 0 )
+ return s_Black;
+
+ CDmrArrayConst<int> indices = GetIndexData( nFieldIndex );
+ CDmrArrayConst<Color> vertexData = GetVertexData( nFieldIndex );
+ return vertexData[ indices[ nIndex ] ];
+}
+
+float CDmeVertexDataBase::GetBalance( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_BALANCE];
+ if ( nFieldIndex < 0 )
+ return 0.5f;
+
+ CDmrArrayConst<int> indices = GetIndexData( nFieldIndex );
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return vertexData[ indices[ nIndex ] ];
+}
+
+float CDmeVertexDataBase::GetMorphSpeed( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_MORPH_SPEED];
+ if ( nFieldIndex < 0 )
+ return 1.0f;
+
+ CDmrArrayConst<int> indices = GetIndexData( nFieldIndex );
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return vertexData[ indices[ nIndex ] ];
+}
+
+float CDmeVertexDataBase::GetWrinkle( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_WRINKLE];
+ if ( nFieldIndex < 0 )
+ return 1.0f;
+
+ CDmrArrayConst<int> indices = GetIndexData( nFieldIndex );
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return vertexData[ indices[ nIndex ] ];
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+float CDmeVertexDataBase::GetWeight( int nIndex ) const
+{
+ Assert( IsVertexDeltaData() || nIndex < m_nVertexCount );
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_WEIGHT];
+ if ( nFieldIndex < 0 )
+ return 1.0f;
+
+ CDmrArrayConst<int> indices = GetIndexData( nFieldIndex );
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return vertexData[ indices[ nIndex ] ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a field to the vertex format
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::FindOrAddVertexField( const char *pFieldName )
+{
+ int i;
+ int nFormatCount = m_VertexFormat.Count();
+ for ( i = 0; i < nFormatCount; ++i )
+ {
+ if ( !Q_stricmp( pFieldName, m_VertexFormat[i] ) )
+ return;
+ }
+ m_VertexFormat.AddToTail( pFieldName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the field index of a particular field
+//-----------------------------------------------------------------------------
+FieldIndex_t CDmeVertexDataBase::CreateField( const char *pFieldName, DmAttributeType_t type )
+{
+ Assert( Q_stricmp( pFieldName, g_pStandardFieldNames[FIELD_JOINT_WEIGHTS] ) );
+ Assert( Q_stricmp( pFieldName, g_pStandardFieldNames[FIELD_JOINT_INDICES] ) );
+ if ( !Q_stricmp( pFieldName, g_pStandardFieldNames[FIELD_JOINT_WEIGHTS] ) ||
+ !Q_stricmp( pFieldName, g_pStandardFieldNames[FIELD_JOINT_INDICES] ) )
+ {
+ return -1;
+ }
+
+ AddAttribute( pFieldName, type );
+
+ int nLen = Q_strlen( pFieldName ) + 21;
+ char *pIndicesName = (char*)_alloca( nLen );
+ Q_snprintf( pIndicesName, nLen, "%sIndices", pFieldName );
+ AddAttribute( pIndicesName, AT_INT_ARRAY );
+
+ FindOrAddVertexField( pFieldName );
+
+ // FIXME: Not hugely efficient, is there a better way of doing this?
+ // Necessary to return a field index for the name
+ ComputeFieldInfo();
+ FieldIndex_t nFieldIndex = FindFieldIndex( pFieldName );
+ if ( !IsVertexDeltaData() && m_nVertexCount > 0 )
+ {
+ CDmrArray<int> indices( GetIndexData( nFieldIndex ) );
+ indices.EnsureCount( m_nVertexCount );
+ }
+ return nFieldIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates a field given a file ID
+//-----------------------------------------------------------------------------
+FieldIndex_t CDmeVertexDataBase::CreateField( StandardFields_t fieldId )
+{
+ return CreateField( g_pStandardFieldNames[fieldId], g_pStandardFieldTypes[fieldId] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Use this to create vertex fields for joint weights + indices
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::CreateJointWeightsAndIndices( int nJointCount, FieldIndex_t *pJointWeightsField, FieldIndex_t *pJointIndicesField )
+{
+ m_nJointCount = nJointCount;
+
+ AddAttribute( g_pStandardFieldNames[FIELD_JOINT_WEIGHTS], AT_FLOAT_ARRAY );
+ AddAttribute( g_pStandardFieldNames[FIELD_JOINT_INDICES], AT_INT_ARRAY );
+
+ FindOrAddVertexField( g_pStandardFieldNames[FIELD_JOINT_WEIGHTS] );
+ FindOrAddVertexField( g_pStandardFieldNames[FIELD_JOINT_INDICES] );
+
+
+ // FIXME: Not hugely efficient, is there a better way of doing this?
+ // Necessary to return a field index for the name
+ ComputeFieldInfo();
+ *pJointWeightsField = FindFieldIndex( g_pStandardFieldNames[FIELD_JOINT_WEIGHTS] );
+ *pJointIndicesField = FindFieldIndex( g_pStandardFieldNames[FIELD_JOINT_INDICES] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds a new vertex; creates a new entry in all vertex data fields
+// Returns the vertex index
+//-----------------------------------------------------------------------------
+int CDmeVertexDataBase::AddVertexData( FieldIndex_t nFieldIndex, int nCount )
+{
+ CDmrGenericArray array( m_FieldInfo[nFieldIndex].m_pVertexData );
+ int nDataCount = array.Count();
+ array.EnsureCount( nDataCount + nCount );
+
+ // DmeMeshDeltaData must have the same number of vertices + indices
+ if ( IsVertexDeltaData() )
+ {
+ CDmrArray<int> indices( GetIndexData( nFieldIndex ) );
+ Assert( nDataCount == indices.Count() );
+ indices.EnsureCount( nDataCount + nCount );
+ }
+
+ return nDataCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets vertex data
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::SetVertexData( FieldIndex_t nFieldIndex, int nFirstVertex, int nCount, DmAttributeType_t valueType, const void *pData )
+{
+ CDmrGenericArray array( m_FieldInfo[nFieldIndex].m_pVertexData );
+ Assert( nFirstVertex + nCount <= array.Count() );
+ array.SetMultiple( nFirstVertex, nCount, valueType, pData );
+}
+
+void CDmeVertexDataBase::SetVertexIndices( FieldIndex_t nFieldIndex, int nFirstIndex, int nCount, const int *pIndices )
+{
+ CDmrArray<int> array( GetIndexData( nFieldIndex ) );
+ Assert( nFirstIndex + nCount <= array.Count() );
+ array.SetMultiple( nFirstIndex, nCount, pIndices );
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes all vertex data associated with a particular field
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::RemoveAllVertexData( FieldIndex_t nFieldIndex )
+{
+ CDmrGenericArray array( m_FieldInfo[nFieldIndex].m_pVertexData );
+ array.RemoveAll();
+ if ( IsVertexDeltaData() )
+ {
+ CDmrArray<int> arrayDelta( m_FieldInfo[nFieldIndex].m_pIndexData );
+ arrayDelta.RemoveAll();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the field index of a particular field
+//-----------------------------------------------------------------------------
+FieldIndex_t CDmeVertexDataBase::FindFieldIndex( const char *pFieldName ) const
+{
+ int nCount = m_FieldInfo.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( m_FieldInfo[i].m_Name, pFieldName ) )
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns well-known vertex data
+//-----------------------------------------------------------------------------
+static CUtlVector<Vector4D> s_EmptyVector4D;
+static CUtlVector<Vector> s_EmptyVector;
+static CUtlVector<Vector2D> s_EmptyVector2D;
+static CUtlVector<Color> s_EmptyColor;
+static CUtlVector<float> s_EmptyFloat;
+
+const CUtlVector<Vector> &CDmeVertexDataBase::GetPositionData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_POSITION ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyVector;
+
+ CDmrArrayConst<Vector> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+const CUtlVector<Vector> &CDmeVertexDataBase::GetNormalData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_NORMAL ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyVector;
+
+ CDmrArrayConst<Vector> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+const CUtlVector<Vector4D> &CDmeVertexDataBase::GetTangentData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_TANGENT ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyVector4D;
+
+ CDmrArrayConst<Vector4D> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+const CUtlVector<Vector2D> &CDmeVertexDataBase::GetTextureCoordData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_TEXCOORD ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyVector2D;
+
+ CDmrArrayConst<Vector2D> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+const CUtlVector<Color> &CDmeVertexDataBase::GetColorData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_COLOR ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyColor;
+
+ CDmrArrayConst<Color> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+const float *CDmeVertexDataBase::GetJointWeightData( int nDataIndex ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_JOINT_WEIGHTS];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return NULL;
+
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return &vertexData[ nDataIndex * m_nJointCount ];
+}
+
+const int *CDmeVertexDataBase::GetJointIndexData( int nDataIndex ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[FIELD_JOINT_INDICES];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return NULL;
+
+ CDmrArrayConst<int> vertexData = GetVertexData( nFieldIndex );
+ return &vertexData.Element( nDataIndex * m_nJointCount );
+}
+
+const CUtlVector<float> &CDmeVertexDataBase::GetBalanceData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_BALANCE ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyFloat;
+
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+const CUtlVector<float> &CDmeVertexDataBase::GetMorphSpeedData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_MORPH_SPEED ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyFloat;
+
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+const CUtlVector<float> &CDmeVertexDataBase::GetWrinkleData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_WRINKLE ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyFloat;
+
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+const CUtlVector<float> &CDmeVertexDataBase::GetWeightData( ) const
+{
+ FieldIndex_t nFieldIndex = m_pStandardFieldIndex[ FIELD_WEIGHT ];
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyFloat;
+
+ CDmrArrayConst<float> vertexData = GetVertexData( nFieldIndex );
+ return vertexData.Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns well-known index data
+//-----------------------------------------------------------------------------
+static CUtlVector<int> s_EmptyInt;
+const CUtlVector<int> &CDmeVertexDataBase::GetVertexIndexData( FieldIndex_t nFieldIndex ) const
+{
+ if ( nFieldIndex < 0 || nFieldIndex >= m_FieldInfo.Count() )
+ return s_EmptyInt;
+
+ CDmrArrayConst<int> indexData = GetIndexData( nFieldIndex );
+ return indexData.Get();
+}
+
+const CUtlVector<int> &CDmeVertexDataBase::GetVertexIndexData( StandardFields_t fieldId ) const
+{
+ return GetVertexIndexData( m_pStandardFieldIndex[fieldId] );
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns an inverse map from vertex data index to vertex index
+//-----------------------------------------------------------------------------
+const CUtlVector< int > &CDmeVertexDataBase::FindVertexIndicesFromDataIndex( FieldIndex_t nFieldIndex, int nDataIndex )
+{
+ if ( nFieldIndex < 0 )
+ return s_EmptyInt;
+
+ FieldInfo_t &info = m_FieldInfo[nFieldIndex];
+ if ( info.m_bInverseMapDirty )
+ {
+ CDmrArrayConst<int> array( info.m_pIndexData );
+ CDmrGenericArray vertexArray( info.m_pVertexData );
+
+ int nDataCount = vertexArray.Count();
+ int nCount = array.Count();
+
+ // Clear out the utlvectors
+ info.m_InverseMap.RemoveAll();
+ info.m_InverseMap.SetCount( nDataCount );
+
+ for ( int i = 0; i < nCount; ++i )
+ {
+ int nIndex = array[ i ];
+ info.m_InverseMap[nIndex].AddToTail( i );
+ }
+ info.m_bInverseMapDirty = false;
+ }
+
+ return info.m_InverseMap[ nDataIndex ];
+}
+
+const CUtlVector< int > &CDmeVertexDataBase::FindVertexIndicesFromDataIndex( StandardFields_t fieldId, int nDataIndex )
+{
+ // NOTE! Wrinkles don't exist in the base state, therefore we use the index to index
+ // into the TEXCOORD base state fields instead of the wrinkle fields
+ if ( fieldId == FIELD_WRINKLE )
+ {
+ fieldId = FIELD_TEXCOORD;
+ }
+
+ return FindVertexIndicesFromDataIndex( m_pStandardFieldIndex[fieldId], nDataIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we have skinning data?
+//-----------------------------------------------------------------------------
+bool CDmeVertexDataBase::HasSkinningData() const
+{
+ if ( m_nJointCount == 0 )
+ return false;
+ FieldIndex_t nWeightFieldIndex = m_pStandardFieldIndex[FIELD_JOINT_WEIGHTS];
+ if ( nWeightFieldIndex < 0 )
+ return false;
+ FieldIndex_t nIndexFieldIndex = m_pStandardFieldIndex[FIELD_JOINT_INDICES];
+ if ( nIndexFieldIndex < 0 )
+ return false;
+
+ CDmrArrayConst<float> weightData = GetVertexData( nWeightFieldIndex );
+ CDmrArrayConst<int> indexData = GetVertexData( nIndexFieldIndex );
+ return ( weightData.Count() > 0 && indexData.Count() > 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Do we need tangent data? (Utility method for applications to know if they should call ComputeDefaultTangentData)
+//-----------------------------------------------------------------------------
+bool CDmeVertexDataBase::NeedsTangentData() const
+{
+ FieldIndex_t posField = m_pStandardFieldIndex[CDmeVertexDataBase::FIELD_POSITION];
+ FieldIndex_t normalField = m_pStandardFieldIndex[CDmeVertexDataBase::FIELD_NORMAL];
+ FieldIndex_t uvField = m_pStandardFieldIndex[CDmeVertexDataBase::FIELD_TEXCOORD];
+ FieldIndex_t tangentField = m_pStandardFieldIndex[CDmeVertexDataBase::FIELD_TANGENT];
+ return ( posField >= 0 && uvField >= 0 && normalField >= 0 && tangentField < 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmeVertexDataBase::FieldCount() const
+{
+ return m_VertexFormat.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const char *CDmeVertexDataBase::FieldName( int i ) const
+{
+ if ( i < 0 || i >= m_VertexFormat.Count() )
+ return NULL;
+
+ return m_VertexFormat[ i ];
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::CopyFrom( CDmeVertexDataBase *pSrc )
+{
+ pSrc->CopyTo( this );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmeVertexDataBase::CopyTo( CDmeVertexDataBase *pDst ) const
+{
+ // Preserve the name of the destination
+ const CUtlString dstName = pDst->GetName();
+
+ CopyAttributesTo( pDst );
+
+ pDst->SetName( dstName ); // The Copy really copies everything!
+ pDst->Resolve();
+}
+
+
+//-----------------------------------------------------------------------------
+// Expose this class to the scene database
+//-----------------------------------------------------------------------------
+IMPLEMENT_ELEMENT_FACTORY( DmeVertexData, CDmeVertexData );
+IMPLEMENT_ELEMENT_FACTORY( DmeVertexDeltaData, CDmeVertexDeltaData );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDmeVertexData::OnConstruction()
+{
+}
+
+void CDmeVertexData::OnDestruction()
+{
+}
+
+void CDmeVertexDeltaData::OnConstruction()
+{
+ m_bCorrected.InitAndSet( this, "corrected", false );
+}
+
+void CDmeVertexDeltaData::OnDestruction()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Method to add vertex indices for normal vertex data
+//-----------------------------------------------------------------------------
+int CDmeVertexData::AddVertexIndices( int nIndexCount )
+{
+ int nFirstVertex = m_nVertexCount;
+ m_nVertexCount += nIndexCount;
+ int nCount = m_FieldInfo.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( m_FieldInfo[i].m_pIndexData )
+ {
+ CDmrArray<int> indices( m_FieldInfo[i].m_pIndexData );
+ indices.EnsureCount( m_nVertexCount );
+ }
+ }
+ return nFirstVertex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes max positional delta length
+//-----------------------------------------------------------------------------
+float CDmeVertexDeltaData::ComputeMaxDeflection( )
+{
+ float flMaxDeflection = 0.0f;
+
+ const CUtlVector<Vector> &pos = GetPositionData();
+ int nCount = pos.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flDeflection = pos[i].Length();
+ if ( flMaxDeflection < flDeflection )
+ {
+ flMaxDeflection = flDeflection;
+ }
+ }
+ return flMaxDeflection;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes wrinkle data from position deltas
+//-----------------------------------------------------------------------------
+void CDmeVertexDeltaData::GenerateWrinkleDelta( CDmeVertexData *pBindState, float flScale, bool bOverwrite )
+{
+ FieldIndex_t nPosIndex = FindFieldIndex( FIELD_POSITION );
+ if ( nPosIndex < 0 )
+ return;
+
+ FieldIndex_t nBaseTexCoordIndex = pBindState->FindFieldIndex( FIELD_TEXCOORD );
+ if ( nBaseTexCoordIndex < 0 )
+ return;
+
+ FieldIndex_t nWrinkleIndex = FindFieldIndex( FIELD_WRINKLE );
+ if ( nWrinkleIndex < 0 )
+ {
+ nWrinkleIndex = CreateField( FIELD_WRINKLE );
+ }
+ else if ( !bOverwrite )
+ return;
+
+ RemoveAllVertexData( nWrinkleIndex );
+ if ( flScale == 0.0f )
+ return;
+
+ const float flMaxDeflection( ComputeMaxDeflection() );
+ if ( flMaxDeflection == 0.0f )
+ return;
+
+ const double scaledInverseMaxDeflection = static_cast< double >( flScale ) / static_cast< double >( flMaxDeflection );
+
+ const CUtlVector<int> &positionIndices = GetVertexIndexData( nPosIndex );
+ const CUtlVector<Vector> &pos = GetPositionData();
+ const CUtlVector<int> &baseTexCoordIndices = pBindState->GetVertexIndexData( nBaseTexCoordIndex );
+
+ CDmrArrayConst<Vector2D> texData( pBindState->GetVertexData( nBaseTexCoordIndex ) );
+ int nBaseTexCoordCount = texData.Count();
+ int nBufSize = ( ( nBaseTexCoordCount + 7 ) >> 3 );
+ unsigned char *pUsedBits = (unsigned char*)_alloca( nBufSize );
+ memset( pUsedBits, 0, nBufSize );
+
+ int nCount = pos.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ float flWrinkleDelta = static_cast< float >( static_cast< double >( pos[i].Length() ) * scaledInverseMaxDeflection );
+ Assert( fabs( flWrinkleDelta ) <= fabs( flScale ) );
+
+ // NOTE: This will produce bad behavior in cases where two positions share the
+ // same texcoord, which shouldn't theoretically happen.
+ const CUtlVector< int > &baseVerts = pBindState->FindVertexIndicesFromDataIndex( FIELD_POSITION, positionIndices[i] );
+ int nBaseVertCount = baseVerts.Count();
+ for ( int j = 0; j < nBaseVertCount; ++j )
+ {
+ // See if we have a delta for this texcoord...
+ int nTexCoordIndex = baseTexCoordIndices[ baseVerts[j] ];
+ if ( pUsedBits[ nTexCoordIndex >> 3 ] & ( 1 << ( nTexCoordIndex & 0x7 ) ) )
+ continue;
+
+ pUsedBits[ nTexCoordIndex >> 3 ] |= 1 << ( nTexCoordIndex & 0x7 );
+
+ int nDeltaIndex = AddVertexData( nWrinkleIndex, 1 );
+ SetVertexIndices( nWrinkleIndex, nDeltaIndex, 1, &nTexCoordIndex );
+ SetVertexData( nWrinkleIndex, nDeltaIndex, 1, AT_FLOAT, &flWrinkleDelta );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Computes weight data from position deltas
+//-----------------------------------------------------------------------------
+float CDmeVertexDeltaData::GenerateWeightDelta( CDmeVertexData *pBindState )
+{
+ FieldIndex_t nPosIndex = FindFieldIndex( FIELD_POSITION );
+ if ( nPosIndex < 0 )
+ return 0.0;
+
+ FieldIndex_t nFieldIndex = FindFieldIndex( FIELD_WEIGHT );
+ if ( nFieldIndex < 0 )
+ {
+ nFieldIndex = CreateField( FIELD_WEIGHT );
+ }
+
+ RemoveAllVertexData( nFieldIndex );
+
+ const float maxDeflection( static_cast< double >( ComputeMaxDeflection() ) );
+
+ if ( maxDeflection == 0.0 )
+ return maxDeflection;
+
+ const CUtlVector<Vector> &pos( GetPositionData() );
+ const CUtlVector< int > &posIndices( GetVertexIndexData( nPosIndex ) );
+
+ float flDeltaDistance;
+ int nDeltaIndex;
+ const int nCount = pos.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ flDeltaDistance = pos[ i ].Length();
+
+ nDeltaIndex = AddVertexData( nFieldIndex, 1 );
+ SetVertexData( nFieldIndex, nDeltaIndex, 1, AT_FLOAT, &flDeltaDistance );
+ }
+
+ SetVertexIndices( nFieldIndex, 0, posIndices.Count(), posIndices.Base() );
+
+ return maxDeflection;
+} \ No newline at end of file
diff --git a/movieobjects/dmmeshcomp.cpp b/movieobjects/dmmeshcomp.cpp
new file mode 100644
index 0000000..d2aab1c
--- /dev/null
+++ b/movieobjects/dmmeshcomp.cpp
@@ -0,0 +1,480 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Implementation of CDmMeshComp - CDmeMesh computation class
+//
+//=============================================================================
+
+
+// Valve includes
+#include "movieobjects/dmmeshcomp.h"
+#include "movieobjects/dmefaceset.h"
+#include "movieobjects/dmemesh.h"
+#include "movieobjects/dmevertexdata.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//=============================================================================
+//
+//=============================================================================
+CDmMeshComp::CDmMeshComp( CDmeMesh *pMesh, CDmeVertexData *pPassedBase )
+: m_pMesh( pMesh )
+, m_pBase( NULL )
+{
+ m_pBase = pPassedBase ? pPassedBase : pMesh->GetCurrentBaseState();
+ if ( !m_pBase )
+ return;
+
+ const FieldIndex_t pIndex( m_pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( pIndex < 0 )
+ return;
+
+ const CUtlVector< Vector > &pPositionData( m_pBase->GetPositionData() );
+ const CUtlVector<int> &pPositionIndices( m_pBase->GetVertexIndexData( CDmeVertexData::FIELD_POSITION ) );
+
+ const int nVertices( pPositionData.Count() );
+ if ( nVertices <= 0 )
+ return;
+
+ // Create vertices
+ // TODO: check for duplicates in pPositionData - that would break this algorithm
+ m_verts.EnsureCapacity( nVertices );
+ for ( int i = 0; i < nVertices; ++i )
+ {
+ const CUtlVector< int > &vertexIndices = m_pBase->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, i );
+ m_verts.AddToTail( new CVert( i, &vertexIndices, &pPositionData[ i ] ) );
+ }
+
+ // Create edges and faces
+ const int nFaceSets( pMesh->FaceSetCount() );
+ for ( int i = 0; i < nFaceSets; ++i )
+ {
+ CDmeFaceSet *pFaceSet( pMesh->GetFaceSet( i ) );
+ const int nIndices( pFaceSet->NumIndices() );
+ if ( nIndices < 4 ) // At least a triangle and a -1
+ continue;
+
+ m_faces.EnsureCapacity( m_faces.Count() + nIndices / 4 ); // # new faces <= nIndices/4 (tri + -1)
+ m_edges.EnsureCapacity( m_edges.Count() + nIndices / 2 ); // # new edges <= 2*new faces
+
+ int facePosIndex( -1 );
+ int edgePosIndex0( -1 );
+ int edgePosIndex1( -1 );
+
+ CUtlVector< CVert * > verts;
+ CUtlVector< CEdge * > edges;
+ CUtlVector< bool > edgeReverseMap;
+ bool bReverse = false;
+
+ for ( int j( 0 ); j < nIndices; ++j )
+ {
+ const int faceVertexIndex( pFaceSet->GetIndex( j ) );
+
+ if ( faceVertexIndex < 0 )
+ {
+ // End of face
+ edgePosIndex0 = edgePosIndex1;
+ edgePosIndex1 = facePosIndex;
+
+ Assert( edgePosIndex0 >= 0 );
+ Assert( edgePosIndex1 >= 0 );
+
+ edges.AddToTail( FindOrCreateEdge( edgePosIndex0, edgePosIndex1, &bReverse ) );
+ edgeReverseMap.AddToTail( bReverse );
+
+ CreateFace( verts, edges, edgeReverseMap );
+
+ facePosIndex = -1;
+ verts.RemoveAll();
+ edges.RemoveAll();
+ edgeReverseMap.RemoveAll();
+ continue;
+ }
+
+ if ( facePosIndex < 0 )
+ {
+ // First vertex
+ facePosIndex = pPositionIndices[ faceVertexIndex ];
+ edgePosIndex1 = facePosIndex;
+ verts.AddToTail( m_verts[ edgePosIndex1 ] );
+ continue;
+ }
+
+ // 2nd through last vertex
+ edgePosIndex0 = edgePosIndex1;
+ edgePosIndex1 = pPositionIndices[ faceVertexIndex ];
+ verts.AddToTail( m_verts[ edgePosIndex1 ] );
+
+ Assert( edgePosIndex0 >= 0 );
+ Assert( edgePosIndex1 >= 0 );
+
+ edges.AddToTail( FindOrCreateEdge( edgePosIndex0, edgePosIndex1, &bReverse ) );
+ edgeReverseMap.AddToTail( bReverse );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmMeshComp::~CDmMeshComp()
+{
+ m_verts.PurgeAndDeleteElements();
+ m_edges.PurgeAndDeleteElements();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmMeshComp::CVert::CVert( int nPositionIndex, const CUtlVector< int > *pVertexIndices, const Vector *pPosition )
+: m_positionIndex( nPositionIndex )
+, m_pVertexIndices( pVertexIndices )
+, m_pPosition( pPosition )
+, m_edges( 8, 8 )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmMeshComp::CVert::CVert( const CVert &src )
+: m_positionIndex( src.m_positionIndex )
+, m_pVertexIndices( src.m_pVertexIndices )
+, m_pPosition( src.m_pPosition )
+, m_edges( 8, 8 )
+{
+ m_edges.AddMultipleToTail( src.m_edges.Count(), src.m_edges.Base() );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmMeshComp::CVert::PositionIndex() const
+{
+ return m_positionIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const Vector *CDmMeshComp::CVert::Position() const
+{
+ return m_pPosition;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const CUtlVector< int > *CDmMeshComp::CVert::VertexIndices() const
+{
+ return m_pVertexIndices;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshComp::CVert::operator==( const CVert &rhs ) const
+{
+ return ( m_pPosition->DistToSqr( *rhs.m_pPosition ) < FLT_EPSILON );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmMeshComp::CEdge::CEdge()
+: m_pVert0( NULL )
+, m_pVert1( NULL )
+, m_faceCount( 0 )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the vertex position index given the edge relative vertex index
+//-----------------------------------------------------------------------------
+int CDmMeshComp::CEdge::GetVertPositionIndex( int edgeRelativeVertexIndex ) const
+{
+ if ( edgeRelativeVertexIndex == 0 && m_pVert0 )
+ return m_pVert0->PositionIndex();
+
+ if ( edgeRelativeVertexIndex == 1 && m_pVert1 )
+ return m_pVert1->PositionIndex();
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmMeshComp::CVert *CDmMeshComp::CEdge::GetVert( int edgeRelativeVertexIndex ) const
+{
+ if ( edgeRelativeVertexIndex == 0 )
+ return m_pVert0;
+
+ if ( edgeRelativeVertexIndex == 1 )
+ return m_pVert1;
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true if the edge starts and stops at the same position in space
+// The order of the vertices is not checked
+//-----------------------------------------------------------------------------
+bool CDmMeshComp::CEdge::operator==( const CEdge &rhs ) const
+{
+ return (
+ ( *m_pVert0 == *rhs.m_pVert0 && *m_pVert1 == *rhs.m_pVert1 ) ||
+ ( *m_pVert0 == *rhs.m_pVert1 && *m_pVert1 == *rhs.m_pVert0 ) );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+Vector CDmMeshComp::CEdge::EdgeVector() const
+{
+ if ( m_pVert0 && m_pVert1 )
+ return *m_pVert1->Position() - *m_pVert0->Position();
+
+ return vec3_origin;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds or Creates an edge... Can still return NULL if vertices do not exist
+//-----------------------------------------------------------------------------
+CDmMeshComp::CEdge *CDmMeshComp::FindOrCreateEdge( int vIndex0, int vIndex1, bool *pReverse /* = NULL */ )
+{
+ CEdge *pEdge = FindEdge( vIndex0, vIndex1, pReverse );
+ if ( pEdge )
+ return pEdge;
+
+ CVert *pVert0 = m_verts[ vIndex0 ];
+ if ( pVert0 == NULL )
+ return NULL;
+
+ CVert *pVert1 = m_verts[ vIndex1 ];
+ if ( pVert1 == NULL )
+ return NULL;
+
+ pEdge = m_edges[ m_edges.AddToTail( new CEdge() ) ];
+ pEdge->m_pVert0 = pVert0;
+ pEdge->m_pVert1 = pVert1;
+ pVert0->m_edges.AddToTail( pEdge );
+ if ( vIndex0 != vIndex1 )
+ pVert1->m_edges.AddToTail( pEdge );
+
+ if ( pReverse )
+ {
+ *pReverse = false;
+ }
+
+ return pEdge;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the edge between vIndex0 & vIndex1 (or vice versa), NULL if not found
+//-----------------------------------------------------------------------------
+CDmMeshComp::CEdge *CDmMeshComp::FindEdge( int vIndex0, int vIndex1, bool *pReverse /* = NULL */ )
+{
+ CUtlVector< CEdge * > &edges = m_verts[ vIndex0 ]->m_edges;
+ for ( int i = 0; i < edges.Count(); i++ )
+ {
+ CEdge *e = edges[ i ];
+
+ if ( e->GetVertPositionIndex( 0 ) == vIndex0 && e->GetVertPositionIndex( 1 ) == vIndex1 )
+ {
+ if ( pReverse )
+ {
+ *pReverse = false;
+ }
+ return e;
+ }
+
+ if ( e->GetVertPositionIndex( 1 ) == vIndex0 && e->GetVertPositionIndex( 0 ) == vIndex1 )
+ {
+ if ( pReverse )
+ {
+ *pReverse = true;
+ }
+ return e;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmMeshComp::CFace *CDmMeshComp::CreateFace( const CUtlVector< CVert * > &verts, const CUtlVector< CEdge * > &edges, const CUtlVector< bool > &edgeReverseMap )
+{
+ CFace *pFace = &m_faces[ m_faces.AddToTail() ];
+
+ pFace->m_verts.RemoveAll();
+ pFace->m_verts.AddVectorToTail( verts );
+
+ pFace->m_edges.RemoveAll();
+ pFace->m_edges.AddVectorToTail( edges );
+
+ pFace->m_edgeReverseMap.RemoveAll();
+ pFace->m_edgeReverseMap.AddVectorToTail( edgeReverseMap );
+
+ for ( int nEdgeIndex = edges.Count() - 1; nEdgeIndex >= 0; --nEdgeIndex )
+ {
+ edges[ nEdgeIndex ]->m_faceCount += 1;
+ }
+
+ return pFace;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmMeshComp::FindFacesWithVert( int vIndex, CUtlVector< CFace * > &faces )
+{
+ // TODO: optimize this by adding a vector of face pointers to each vertex
+ faces.RemoveAll();
+
+ for ( int fi( m_faces.Head() ); fi != m_faces.InvalidIndex(); fi = m_faces.Next( fi ) )
+ {
+ CFace &face( m_faces[ fi ] );
+ for ( int i = 0; i < face.m_verts.Count(); ++i )
+ {
+ if ( face.m_verts[ i ]->PositionIndex() == vIndex )
+ {
+ faces.AddToTail( &face );
+ break;
+ }
+ }
+ }
+
+ return faces.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmMeshComp::FindNeighbouringVerts( int vIndex, CUtlVector< CVert * > &verts )
+{
+ verts.RemoveAll();
+
+ const CUtlVector< CEdge * > & edges = m_verts[ vIndex ]->m_edges;
+
+ for ( int i = 0; i < edges.Count(); ++i )
+ {
+ CEdge *e = edges[ i ];
+ if ( e->GetVertPositionIndex( 0 ) == vIndex )
+ {
+ verts.AddToTail( e->GetVert( 1 ) );
+ }
+ else
+ {
+ verts.AddToTail( e->GetVert( 0 ) );
+ }
+ }
+
+ return verts.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Find all edges that are only used by 1 face
+//-----------------------------------------------------------------------------
+int CDmMeshComp::GetBorderEdges( CUtlVector< CUtlVector< CEdge * > > &borderEdgesList )
+{
+ // TODO: optimize this by stepping from edge to edge to build chains, using CVert::m_edges
+ int retVal = 0;
+
+ borderEdgesList.RemoveAll();
+
+ bool connected;
+
+ for ( int ei = 0; ei < m_edges.Count(); ei++ )
+ {
+ CEdge *pEdge = m_edges[ ei ];
+ if ( pEdge->IsBorderEdge() )
+ {
+ ++retVal;
+ connected = false;
+
+ for ( int i = borderEdgesList.Count() - 1; !connected && i >= 0; --i )
+ {
+ CUtlVector< CEdge * > &borderEdges = borderEdgesList[ i ];
+ for ( int j = borderEdges.Count() - 1; j >= 0; --j )
+ {
+ if ( borderEdges[ j ]->ConnectedTo( pEdge ) )
+ {
+ borderEdges.AddToTail( pEdge );
+ connected = true;
+ break;
+ }
+ }
+ }
+
+ if ( !connected )
+ {
+ CUtlVector< CEdge * > &borderEdges = borderEdgesList[ borderEdgesList.AddToTail() ];
+ borderEdges.AddToTail( pEdge );
+ }
+ }
+ }
+
+ // Shrink the borderEdgesList to minimum number required
+
+ bool anyConnected = false;
+ do
+ {
+ anyConnected = false;
+
+ for ( int i = borderEdgesList.Count() - 1; i >= 0; --i )
+ {
+ CUtlVector< CEdge * > &srcBorderEdges = borderEdgesList[ i ];
+ for ( int j = srcBorderEdges.Count() - 1; j >= 0; --j )
+ {
+ CEdge *pSrcEdge = srcBorderEdges[ j ];
+ connected = false;
+
+ for ( int k = 0; !connected && k < i; ++k )
+ {
+ CUtlVector< CEdge * > &dstBorderEdges = borderEdgesList[ k ];
+ for ( int l = dstBorderEdges.Count() - 1; l >= 0; --l )
+ {
+ if ( dstBorderEdges[ l ]->ConnectedTo( pSrcEdge ) )
+ {
+ connected = true;
+ anyConnected = true;
+ dstBorderEdges.AddToTail( pSrcEdge );
+ srcBorderEdges.Remove( j );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( srcBorderEdges.Count() == 0 )
+ {
+ borderEdgesList.Remove( i );
+ }
+ }
+ } while( anyConnected );
+
+ return retVal;
+}
diff --git a/movieobjects/dmmeshutils.cpp b/movieobjects/dmmeshutils.cpp
new file mode 100644
index 0000000..9492085
--- /dev/null
+++ b/movieobjects/dmmeshutils.cpp
@@ -0,0 +1,3313 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Functions which do things to a DmeMesh
+//
+//=============================================================================
+
+
+// Valve includes
+#include "movieobjects/dmeanimationset.h"
+#include "movieobjects/dmecombinationoperator.h"
+#include "movieobjects/dmemodel.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmemesh.h"
+#include "movieobjects/dmefaceset.h"
+#include "movieobjects/dmematerial.h"
+#include "movieobjects/dmevertexdata.h"
+#include "movieobjects/dmmeshcomp.h" // TODO: This has to be included before dmmeshutils.h
+#include "movieobjects/dmmeshutils.h"
+#include "tier1/utlstack.h"
+#include "tier2/p4helpers.h"
+#include "tier1/utlstring.h"
+#include "tier1/utlstringmap.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/fmtstr.h"
+#include "filesystem.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::RemoveLargeAxisAlignedPlanarFaces( CDmeMesh *pMesh )
+{
+ CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) );
+ if ( !pBase )
+ return false;
+
+ const int posIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ if ( posIndex < 0 )
+ return false;
+
+ const CUtlVector< Vector > &posData( CDmrArrayConst< Vector >( pBase->GetVertexData( posIndex ) ).Get() );
+ if ( posData.Count() <= 0 )
+ return false;
+
+ const CUtlVector< int > &posIndices( CDmrArrayConst< int >( pBase->GetIndexData( posIndex ) ).Get() );
+ if ( posIndices.Count() <= 0 )
+ return false;
+
+ bool bMeshChanged = false;
+
+ CUtlVector< int > emptyFaceSets;
+
+ int faceStartIndex = 0;
+ int faceCurrentIndex = 0;
+
+ int faceVertexCount = 0;
+
+ bool bPlanarX = true;
+ bool bPlanarY = true;
+ bool bPlanarZ = true;
+
+ Vector p;
+
+ CUtlVector< int > removeStart;
+ CUtlVector< int > removeCount;
+
+ const int nFaceSets = pMesh->FaceSetCount();
+ for ( int i = 0; i < nFaceSets; ++i )
+ {
+ CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i );
+ const int nFaceIndices = pFaceSet->NumIndices();
+ if ( nFaceIndices <= 0 )
+ continue;
+
+ faceStartIndex = 0;
+
+ faceCurrentIndex = pFaceSet->GetIndex( 0 );
+ if ( faceCurrentIndex < 0 )
+ continue;
+
+ faceVertexCount = 0;
+
+ bPlanarX = true;
+ bPlanarY = true;
+ bPlanarZ = true;
+
+ removeStart.RemoveAll();
+ removeCount.RemoveAll();
+
+ p = posData[ posIndices[ faceCurrentIndex ] ];
+
+ for ( int j = 1; j < nFaceIndices; ++j )
+ {
+ faceCurrentIndex = pFaceSet->GetIndex( j );
+
+ if ( faceCurrentIndex < 0 )
+ {
+ // End of a face
+
+ if ( faceVertexCount > 4 && ( bPlanarX || bPlanarY || bPlanarZ ) )
+ {
+ removeStart.AddToTail( faceStartIndex );
+ removeCount.AddToTail( j - faceStartIndex + 1 );
+ }
+
+ faceStartIndex = j + 1;
+
+ if ( faceStartIndex < nFaceIndices )
+ {
+ p = posData[ posIndices[ pFaceSet->GetIndex( faceStartIndex ) ] ];
+ }
+
+ faceVertexCount = 0;
+
+ bPlanarX = true;
+ bPlanarY = true;
+ bPlanarZ = true;
+
+ continue;
+ }
+
+ Assert( faceCurrentIndex < posIndices.Count() );
+ Assert( posIndices[ faceCurrentIndex ] < posData.Count() );
+ const Vector &vPos = posData[ posIndices[ faceCurrentIndex ] ];
+
+ if ( vPos.x != p.x )
+ bPlanarX = false;
+
+ if ( vPos.y != p.y )
+ bPlanarY = false;
+
+ if ( vPos.z != p.z )
+ bPlanarZ = false;
+
+ ++faceVertexCount;
+ }
+
+ Assert( removeStart.Count() == removeCount.Count() );
+ for ( int j = removeStart.Count() - 1; j >= 0; --j )
+ {
+ pFaceSet->RemoveMultiple( removeStart[ j ], removeCount[ j ] );
+ bMeshChanged = true;
+ }
+
+ if ( pFaceSet->GetIndexCount() == 0 )
+ {
+ emptyFaceSets.AddToTail( i );
+ }
+ }
+
+ for ( int i = emptyFaceSets.Count() - 1; i >= 0; --i )
+ {
+ pMesh->RemoveFaceSet( emptyFaceSets[ i ] );
+ bMeshChanged = true;
+ }
+
+ if ( bMeshChanged )
+ {
+ PurgeUnusedData( pMesh );
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::RemoveFacesWithMaterial( CDmeMesh *pMesh, const char *pMaterialName )
+{
+ bool bMeshChanged = false;
+
+ CUtlVector< int > emptyFaceSets;
+
+ const int nFaceSets = pMesh->FaceSetCount();
+ for ( int i = 0; i < nFaceSets; ++i )
+ {
+ CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i );
+ if ( !Q_strcmp( pFaceSet->GetMaterial()->GetMaterialName(), pMaterialName ) )
+ {
+ emptyFaceSets.AddToTail( i );
+ bMeshChanged = true;
+ }
+ }
+
+ for ( int i = emptyFaceSets.Count() - 1; i >= 0; --i )
+ {
+ pMesh->RemoveFaceSet( emptyFaceSets[ i ] );
+ bMeshChanged = true;
+ }
+
+ if ( bMeshChanged )
+ {
+ PurgeUnusedData( pMesh );
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::RemoveFacesWithMoreThanNVerts( CDmeMesh *pMesh, const int nVertexCount )
+{
+ CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) );
+ if ( !pBase )
+ return false;
+
+ bool bMeshChanged = false;
+
+ CUtlVector< int > emptyFaceSets;
+
+ int faceStartIndex = 0;
+ int faceCurrentIndex = 0;
+
+ int faceVertexCount = 0;
+
+ CUtlVector< int > removeStart;
+ CUtlVector< int > removeCount;
+
+ const int nFaceSets = pMesh->FaceSetCount();
+ for ( int i = 0; i < nFaceSets; ++i )
+ {
+ CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i );
+ const int nFaceIndices = pFaceSet->NumIndices();
+ if ( nFaceIndices <= 0 )
+ continue;
+
+ faceStartIndex = 0;
+
+ faceCurrentIndex = pFaceSet->GetIndex( 0 );
+ if ( faceCurrentIndex < 0 )
+ continue;
+
+ faceVertexCount = 0;
+
+ removeStart.RemoveAll();
+ removeCount.RemoveAll();
+
+ for ( int j = 1; j < nFaceIndices; ++j )
+ {
+ faceCurrentIndex = pFaceSet->GetIndex( j );
+
+ if ( faceCurrentIndex < 0 )
+ {
+ // End of a face
+
+ if ( faceVertexCount > nVertexCount )
+ {
+ removeStart.AddToTail( faceStartIndex );
+ removeCount.AddToTail( j - faceStartIndex + 1 );
+ }
+
+ faceStartIndex = j + 1;
+
+ faceVertexCount = 0;
+
+ continue;
+ }
+
+ ++faceVertexCount;
+ }
+
+ Assert( removeStart.Count() == removeCount.Count() );
+ for ( int j = removeStart.Count() - 1; j >= 0; --j )
+ {
+ pFaceSet->RemoveMultiple( removeStart[ j ], removeCount[ j ] );
+ bMeshChanged = true;
+ }
+
+ if ( pFaceSet->GetIndexCount() == 0 )
+ {
+ emptyFaceSets.AddToTail( i );
+ }
+ }
+
+ for ( int i = emptyFaceSets.Count() - 1; i >= 0; --i )
+ {
+ pMesh->RemoveFaceSet( emptyFaceSets[ i ] );
+ bMeshChanged = true;
+ }
+
+ if ( bMeshChanged )
+ {
+ PurgeUnusedData( pMesh );
+ return true;
+ }
+
+ // Nothing remove
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Figures out which vertexIndices are missing
+// Returned list will be in sorted order
+//-----------------------------------------------------------------------------
+void ComputeVertexIndexMap( CDmeMesh *pMesh, int nMaxVertexCount, CUtlVector< int > &vertexIndexMap )
+{
+ bool *pVertexFound = reinterpret_cast< bool * >( alloca( nMaxVertexCount * sizeof( bool ) ) );
+ memset( pVertexFound, 0, nMaxVertexCount * sizeof( bool ) );
+
+ // Loop through all the face sets to find out the highest vertex index
+ const int nFaceSetCount = pMesh->FaceSetCount();
+ for ( int i = 0; i < nFaceSetCount; ++i )
+ {
+ const CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i );
+ const int nFaceSetIndices = pFaceSet->NumIndices();
+ for ( int j = 0; j < nFaceSetIndices; ++j )
+ {
+ const int &nIndex = pFaceSet->GetIndex( j );
+ if ( nIndex >= 0 )
+ {
+ Assert( nIndex < nMaxVertexCount );
+ pVertexFound[ nIndex ] = true;
+ }
+ }
+ }
+
+ int nMissingCount = 0;
+ for ( int i = 0; i < nMaxVertexCount; ++i )
+ {
+ if ( !pVertexFound[ i ] )
+ {
+ ++nMissingCount;
+ }
+ }
+
+ vertexIndexMap.SetSize( nMaxVertexCount );
+ for ( int i = 0; i < nMaxVertexCount; ++i )
+ {
+ vertexIndexMap[ i ] = i;
+ }
+
+ for ( int i = nMaxVertexCount - 1; i >= 0; --i )
+ {
+ if ( !pVertexFound[ i ] )
+ {
+ vertexIndexMap.Remove( i );
+ }
+ }
+
+ // Build up the reverse map
+ int *pReverseVertexIndexMap = reinterpret_cast< int * >( alloca( nMaxVertexCount * sizeof( int ) ) );
+ for ( int i = 0; i < nFaceSetCount; ++i )
+ {
+ pReverseVertexIndexMap[ i ] = -1;
+ }
+
+ for ( int i = vertexIndexMap.Count() - 1; i >= 0; --i )
+ {
+ pReverseVertexIndexMap[ vertexIndexMap[ i ] ] = i;
+ }
+
+ // Fix up the face set indices to compensate for the ones which are going to be removed
+ for ( int i = 0; i < nFaceSetCount; ++i )
+ {
+ CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i );
+ const int nFaceSetIndices = pFaceSet->NumIndices();
+ for ( int j = 0; j < nFaceSetIndices; ++j )
+ {
+ const int &nIndex = pFaceSet->GetIndex( j );
+ if ( nIndex >= 0 )
+ {
+ Assert( pReverseVertexIndexMap[ nIndex ] >= 0 );
+ pFaceSet->SetIndex( j, pReverseVertexIndexMap[ nIndex ] );
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the highest vertex index used by the face sets of the mesh + 1
+//-----------------------------------------------------------------------------
+int GetMaxVertexCount( const CDmeMesh *pMesh )
+{
+ int nMaxVertexIndex = 0;
+
+ // Loop through all the face sets to find out the highest vertex index
+ const int nFaceSetCount = pMesh->FaceSetCount();
+ for ( int i = 0; i < nFaceSetCount; ++i )
+ {
+ const CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i );
+ const int nFaceSetIndices = pFaceSet->NumIndices();
+ for ( int j = 0; j < nFaceSetIndices; ++j )
+ {
+ const int &nIndex = pFaceSet->GetIndex( j );
+
+ if ( nIndex > nMaxVertexIndex )
+ {
+ nMaxVertexIndex = nIndex;
+ }
+ }
+ }
+
+ return nMaxVertexIndex + 1;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void RemapData(
+ CDmrArray< T_t > data,
+ const CUtlVector< int > &newToOldMap )
+{
+ const int nNewToOldMapCount = newToOldMap.Count();
+
+ T_t *pNewData = reinterpret_cast< T_t * >( alloca( nNewToOldMapCount * sizeof( T_t ) ) );
+ for ( int i = 0; i < nNewToOldMapCount; ++i )
+ {
+ pNewData[ i ] = data.Get( newToOldMap[ i ] );
+ }
+
+ data.RemoveMultiple( nNewToOldMapCount, data.Count() - nNewToOldMapCount );
+ data.SetMultiple( 0, nNewToOldMapCount, pNewData );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the map of new data indices to old data indices
+//-----------------------------------------------------------------------------
+void RemoveUnusedData(
+ CDmeMesh *pMesh,
+ CDmeVertexData *pVertexData,
+ bool bBind,
+ const char *pFieldName,
+ int *pIndices,
+ int nIndicesCount,
+ CDmrGenericArray &data )
+{
+ const int nDataCount = data.Count();
+
+ bool *pDataIndexFound = reinterpret_cast< bool * >( alloca( nDataCount * sizeof( bool ) ) );
+ memset( pDataIndexFound, 0, nDataCount * sizeof( bool ) );
+
+ // Figure out which data is used
+ for ( int i = 0; i < nIndicesCount; ++i )
+ {
+ Assert( pIndices[ i ] >= 0 && pIndices[ i ] < nDataCount );
+ pDataIndexFound[ pIndices[ i ] ] = true;
+ }
+
+ int nMissingCount = 0;
+ for ( int i = 0; i < nDataCount; ++i )
+ {
+ if ( !pDataIndexFound[ i ] )
+ {
+ ++nMissingCount;
+ }
+ }
+
+ // Compute the New to Old data map
+ CUtlVector< int > newToOldDataMap;
+ newToOldDataMap.SetSize( nDataCount );
+ for ( int i = 0; i < nDataCount; ++i )
+ {
+ newToOldDataMap[ i ] = i;
+ }
+
+ for ( int i = nDataCount - 1; i >= 0; --i )
+ {
+ if ( !pDataIndexFound[ i ] )
+ {
+ newToOldDataMap.Remove( i );
+ }
+ }
+
+ // Fix up the data
+ CDmAttribute *pDataAttr = data.GetAttribute();
+ const DmAttributeType_t dataAttrType = pDataAttr->GetType();
+ switch ( dataAttrType )
+ {
+ case AT_FLOAT_ARRAY:
+ RemapData( CDmrArray< float >( pDataAttr ), newToOldDataMap );
+ break;
+ case AT_VECTOR2_ARRAY:
+ RemapData( CDmrArray< Vector2D >( pDataAttr ), newToOldDataMap );
+ break;
+ case AT_VECTOR3_ARRAY:
+ RemapData( CDmrArray< Vector >( pDataAttr ), newToOldDataMap );
+ break;
+ case AT_VECTOR4_ARRAY:
+ RemapData( CDmrArray< Vector4D >( pDataAttr ), newToOldDataMap );
+ break;
+ case AT_QUATERNION_ARRAY:
+ RemapData( CDmrArray< Quaternion >( pDataAttr ), newToOldDataMap );
+ break;
+ case AT_COLOR_ARRAY:
+ RemapData( CDmrArray< Color >( pDataAttr ), newToOldDataMap );
+ break;
+ default:
+ Assert( 0 );
+ break;
+ }
+
+ // Compute Old To New Data Map
+ int *pOldToNewDataMap = reinterpret_cast< int * >( alloca( nDataCount * sizeof( int ) ) );
+ for ( int i = 0; i < nDataCount; ++i )
+ {
+ pOldToNewDataMap[ i ] = -1;
+ }
+
+ for ( int i = newToOldDataMap.Count() - 1; i >= 0; --i )
+ {
+ pOldToNewDataMap[ newToOldDataMap[ i ] ] = i;
+ }
+
+ // Fix up the indices
+ for ( int i = 0; i < nIndicesCount; ++i )
+ {
+ pIndices[ i ] = pOldToNewDataMap[ pIndices[ i ] ];
+ }
+
+ // TODO: Fix up "jointWeight & "jointIndices" if this is "position"
+ if ( !Q_strcmp( pFieldName, "position" ) )
+ {
+ const int nFields = pVertexData->FieldCount();
+ for ( int i = 0; i < nFields; ++i )
+ {
+
+ }
+ }
+
+ // If this is the bind state then fix up any delta states
+ if ( !bBind )
+ return;
+
+ // Fix up any Delta states
+ const int nDeltaStateCount = pMesh->DeltaStateCount();
+ for ( int i = 0; i < nDeltaStateCount; ++i )
+ {
+ CDmeVertexDeltaData *pDelta = pMesh->GetDeltaState( i );
+ const int nDeltaFieldCount = pDelta->FieldCount();
+ for ( int j = 0; j < nDeltaFieldCount; ++j )
+ {
+ if ( !Q_strcmp( pFieldName, pDelta->FieldName( j ) ) )
+ {
+ CDmrArray< int > deltaIndices = pDelta->GetIndexData( j );
+ CDmrGenericArray deltaData = pDelta->GetVertexData( j );
+ Assert( deltaIndices.Count() == deltaData.Count() );
+
+ for ( int k = deltaIndices.Count() - 1; k >= 0; --k )
+ {
+ const int oldIndex = deltaIndices.Get( k );
+ const int &newIndex = pOldToNewDataMap[ oldIndex ];
+ if ( newIndex < 0 )
+ {
+ deltaIndices.Remove( k );
+ deltaData.Remove( k );
+ }
+ else if ( newIndex != oldIndex )
+ {
+ deltaIndices.Set( k, newIndex );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void RemoveUnusedVerticesFromBaseState(
+ CDmeMesh *pMesh,
+ CDmeVertexData *pVertexData,
+ const CUtlVector< int > &newToOldIndexMap )
+{
+ const int nNewToOldIndexMapCount = newToOldIndexMap.Count();
+ int *pNewVertexIndices = reinterpret_cast< int * >( alloca( nNewToOldIndexMapCount * sizeof( int ) ) );
+
+ // See if this is the bind state for the mesh
+ const bool bBind = !Q_strcmp( pVertexData->GetName(), "bind" );
+
+ const int nFieldCount = pVertexData->FieldCount();
+ for ( int i = 0; i < nFieldCount; ++i )
+ {
+ const char *pFieldName = pVertexData->FieldName( i );
+ // TODO: Checking by name is lame... should be a lookup to map fieldIndex to a standard field index
+ if ( !Q_strcmp( pFieldName, "jointWeights" ) || !Q_strcmp( pFieldName, "jointIndices" ) )
+ {
+ // TODO: Handle when positions are Remapped
+ continue;
+ }
+
+ CDmrArray< int > indices = pVertexData->GetIndexData( i );
+
+ // Create the new index array accounting for missing indices
+ for ( int j = 0; j < nNewToOldIndexMapCount; ++j )
+ {
+ Assert( newToOldIndexMap[ j ] < indices.Count() );
+ pNewVertexIndices[ j ] = indices.Get( newToOldIndexMap[ j ] );
+ }
+
+ CDmrGenericArray data = pVertexData->GetVertexData( i );
+
+ // This will also update pNewVertexIndices
+ RemoveUnusedData( pMesh, pVertexData, bBind, pFieldName, pNewVertexIndices, nNewToOldIndexMapCount, CDmrGenericArray( pVertexData->GetVertexData( i ) ) );
+
+ // Shrink the indices array
+ indices.RemoveMultiple( nNewToOldIndexMapCount, indices.Count() - nNewToOldIndexMapCount );
+
+ // Set the new index values
+ indices.SetMultiple( 0, nNewToOldIndexMapCount, pNewVertexIndices );
+ }
+
+ // Update the vertex count
+ pVertexData->Resolve();
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes unused data from the mesh
+// Unused means a 'vertex' that isn't referred to by any face
+// Once all unused vertices are removed, unused data is removed from each
+// bit of data
+// TODO: Also loop through each field of data, see which ones are no longer
+// being referred to and then purge the data as well
+// Would also have to purge delta data at the same time
+// Would also have to purge joints at the same time (for position)
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::PurgeUnusedData( CDmeMesh *pMesh )
+{
+ // Get the maximum vertex index of the mesh
+ const int nMaxVertexCount = GetMaxVertexCount( pMesh );
+
+ // Now find any missing indices
+ CUtlVector< int > vertexIndexMap;
+ ComputeVertexIndexMap( pMesh, nMaxVertexCount, vertexIndexMap );
+
+ // Remove the redundant vertices from all base states
+ for ( int i = pMesh->BaseStateCount() - 1; i >= 0; --i )
+ {
+ RemoveUnusedVerticesFromBaseState( pMesh, pMesh->GetBaseState( i ), vertexIndexMap );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::Mirror( CDmeMesh *pMesh, int axis /*= kXAxis */ )
+{
+ CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) );
+ if ( !pBase )
+ return false;
+
+ CUtlVector< int > mirrorMap;
+ if ( !MirrorVertices( pMesh, pBase, axis, mirrorMap ) )
+ return false;
+
+ int vertexIndex;
+ int faceStart = 0;
+
+ CUtlVector< int > newFaceIndices;
+
+ const int nFaceSets = pMesh->FaceSetCount();
+ for ( int i = 0; i < nFaceSets; ++i )
+ {
+ CDmeFaceSet *pSrcFaceSet = pMesh->GetFaceSet( i );
+ const int nFaceSetIndices = pSrcFaceSet->NumIndices();
+ if ( nFaceSetIndices <= 0 )
+ continue;
+
+ CDmeFaceSet *pDstFaceSet = pSrcFaceSet;
+
+ // See if a new face set needs to be created
+
+ CDmeMaterial *pSrcMaterial = pSrcFaceSet->GetMaterial();
+ const char *pSrcMaterialName = pSrcMaterial->GetMaterialName();
+ const int nNameLen = Q_strlen( pSrcMaterialName );
+ if ( nNameLen >= 2 )
+ {
+ CUtlString materialName;
+
+ if ( !Q_stricmp( pSrcMaterialName + nNameLen - 2, "_l" ) )
+ {
+ materialName = pSrcMaterialName;
+ materialName.SetLength( nNameLen - 2 );
+ materialName += "_r";
+ }
+ else if ( !Q_stricmp( pSrcMaterialName + nNameLen - 2, "_r" ) )
+ {
+ materialName = pSrcMaterialName;
+ materialName.SetLength( nNameLen - 2 );
+ materialName += "_l";
+ }
+ else if ( nNameLen >= 5 && !Q_stricmp( pSrcMaterialName + nNameLen - 5, "_left" ) )
+ {
+ materialName = pSrcMaterialName;
+ materialName.SetLength( nNameLen - 5 );
+ materialName += "_right";
+ }
+ else if ( nNameLen >= 6 && !Q_stricmp( pSrcMaterialName + nNameLen - 6, "_right" ) )
+ {
+ materialName = pSrcMaterialName;
+ materialName.SetLength( nNameLen - 6 );
+ materialName += "_left";
+ }
+
+ if ( materialName.Length() )
+ {
+ pDstFaceSet = CreateElement< CDmeFaceSet >( materialName, pMesh->GetFileId() );
+ CDmeMaterial *pDstMaterial = CreateElement< CDmeMaterial >( materialName, pDstFaceSet->GetFileId() );
+ pDstMaterial->SetMaterial( materialName );
+ pDstFaceSet->SetMaterial( pDstMaterial );
+ pMesh->AddFaceSet( pDstFaceSet );
+ }
+ }
+
+ faceStart = 0;
+
+ for ( int j = 0; j < nFaceSetIndices; ++j )
+ {
+ vertexIndex = pSrcFaceSet->GetIndex( j );
+
+ if ( vertexIndex < 0 )
+ {
+ newFaceIndices.RemoveAll();
+
+ for ( int k = j - 1; k >= faceStart; --k )
+ {
+ newFaceIndices.AddToTail( mirrorMap[ pSrcFaceSet->GetIndex( k ) ] );
+ }
+ newFaceIndices.AddToTail( -1 );
+
+ const int oldNumIndices = pDstFaceSet->NumIndices();
+
+ pDstFaceSet->AddIndices( newFaceIndices.Count() );
+ pDstFaceSet->SetIndices( oldNumIndices, newFaceIndices.Count(), newFaceIndices.Base() );
+
+ // End of face
+ faceStart = j + 1;
+ continue;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Initializes the CUtlVector to a linear ramp where utlVector[ i ] == i
+//-----------------------------------------------------------------------------
+template < typename T_t >
+void RampInit( CUtlVector< T_t > &utlVector, const int nCount )
+{
+ utlVector.SetCount( nCount );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ utlVector[ i ] = i;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Build Data Mirror Map
+// Returns a pointer to the memory holding the indices for the map or NULL
+//-----------------------------------------------------------------------------
+const int *CDmMeshUtils::BuildDataMirrorMap( CDmeVertexData *pBase, int axis, CDmeVertexData::StandardFields_t standardField, CUtlVector< int > &dataMirrorMap )
+{
+ const FieldIndex_t fieldIndex = pBase->FindFieldIndex( standardField );
+ if ( fieldIndex < 0 )
+ return NULL;
+
+ const CUtlVector< int > &indices( CDmrArrayConst< int >( pBase->GetIndexData( fieldIndex ) ).Get() );
+
+ CDmAttribute *pData = pBase->GetVertexData( fieldIndex );
+ if ( standardField == CDmeVertexData::FIELD_POSITION || standardField == CDmeVertexData::FIELD_NORMAL )
+ {
+ const Vector mirrorOrigin( 0.0f, 0.0f, 0.0f );
+ const float mirrorAxisVal = mirrorOrigin[ axis ];
+
+ CDmrArray< Vector > data( pBase->GetVertexData( fieldIndex ) );
+ Vector v;
+
+ const int nDataCount = data.Count();
+ dataMirrorMap.SetCount( nDataCount );
+
+ int nMirrorDataCount = nDataCount;
+ for ( int i = 0; i < nDataCount; ++i )
+ {
+ if ( fabs( data[ i ][ axis ] - mirrorAxisVal ) > FLT_EPSILON * 1000.0f )
+ {
+ dataMirrorMap[ i ] = nMirrorDataCount++;
+ }
+ else
+ {
+ dataMirrorMap[ i ] = i;
+ v = data[ i ];
+ v[ axis ] = mirrorOrigin[ axis ];
+ data.Set( i, v );
+ }
+ }
+ }
+ else if ( standardField == CDmeVertexData::FIELD_TEXCOORD )
+ {
+ const Vector2D mirrorOrigin( 0.5f, 0.5f );
+ const float mirrorAxisVal = mirrorOrigin[ axis % 2 ];
+
+ const CUtlVector< Vector2D > &data( CDmrArrayConst< Vector2D >( pBase->GetVertexData( fieldIndex ) ).Get() );
+ const int nDataCount = data.Count();
+ dataMirrorMap.SetCount( nDataCount );
+
+ int nMirrorDataCount = nDataCount;
+ for ( int i = 0; i < nDataCount; ++i )
+ {
+ if ( fabs( data[ i ][ axis ] - mirrorAxisVal ) > FLT_EPSILON * 1000.0f )
+ {
+ dataMirrorMap[ i ] = nMirrorDataCount++;
+ }
+ else
+ {
+ dataMirrorMap[ i ] = i;
+ }
+ }
+ }
+ else
+ {
+ RampInit( dataMirrorMap, CDmrGenericArrayConst( pData ).Count() );
+ }
+
+ return indices.Base();
+}
+
+
+//-----------------------------------------------------------------------------
+// y = mirrorMap[ x ] means that if y < 0 then original position x is not
+// mirrored. Otherwise y is the index into the vertex indices of the mirrored
+// version of vertex
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::MirrorVertices( CDmeMesh *pMesh, CDmeVertexData *pBase, int axis, CUtlVector< int > &mirrorMap )
+{
+ mirrorMap.RemoveAll();
+
+ if ( !pMesh || !pBase || axis < kXAxis || axis > kZAxis )
+ return false;
+
+ const int posIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ if ( posIndex < 0 )
+ return false;
+
+ const CUtlVector< int > &posIndices( CDmrArrayConst< int >( pBase->GetIndexData( posIndex ) ).Get() );
+ const int nIndices = posIndices.Count();
+ Assert( nIndices == pBase->VertexCount() );
+ CUtlVector< int > posMirrorMap;
+
+ if ( !BuildDataMirrorMap( pBase, axis, CDmeVertexData::FIELD_POSITION, posMirrorMap ) )
+ return false;
+
+ CUtlVector< int > normalMirrorMap;
+ const int *pNormalIndices = BuildDataMirrorMap( pBase, axis, CDmeVertexData::FIELD_NORMAL, normalMirrorMap );
+
+ CUtlVector< int > uvMirrorMap;
+ const int *pUVIndices = BuildDataMirrorMap( pBase, axis, CDmeVertexData::FIELD_TEXCOORD, uvMirrorMap );
+
+ RampInit( mirrorMap, nIndices );
+ int mirrorCount = 0;
+ {
+ bool mirror;
+
+ Vector tmpVec;
+ Vector2D tmpVec2D;
+
+ for ( int i = 0; i < nIndices; ++i )
+ {
+ mirror = false;
+
+ if ( posMirrorMap[ posIndices[ i ] ] != posIndices[ i ] )
+ {
+ mirror = true;
+ }
+
+ if ( pNormalIndices && normalMirrorMap[ pNormalIndices[ i ] ] != pNormalIndices[ i ] )
+ {
+ mirror = true;
+ }
+
+ if ( pUVIndices && uvMirrorMap[ pUVIndices[ i ] ] != pUVIndices[ i ] )
+ {
+ mirror = true;
+ }
+
+ if ( mirror )
+ {
+ mirrorMap[ i ] = nIndices + mirrorCount;
+ ++mirrorCount;
+ }
+ }
+ }
+
+ const int nBaseState = pMesh->BaseStateCount();
+ for ( int i = 0; i < nBaseState; ++i )
+ {
+ pBase = pMesh->GetBaseState( i );
+ const int nVertexCount = pBase->VertexCount();
+ MirrorVertices( pBase, axis, nVertexCount, mirrorCount, mirrorMap, posMirrorMap, normalMirrorMap, uvMirrorMap );
+ }
+
+ const int nDeltaState = pMesh->DeltaStateCount();
+ for ( int i = 0; i < nDeltaState; ++i )
+ {
+ CDmeVertexDeltaData *pDelta = pMesh->GetDeltaState( i );
+ MirrorDelta( pDelta, axis, posMirrorMap, normalMirrorMap, uvMirrorMap );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+inline void MirrorData( Vector &d, const int &axis )
+{
+ d[ axis ] *= -1.0f;
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+inline void MirrorData( Vector2D &d, const int &axis )
+{
+ d[ axis ] = ( d[ axis ] - 0.5f ) * -1.0f + 0.5f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Mirror 3D things like positions & normals
+//-----------------------------------------------------------------------------
+template < class T_t >
+void MirrorVertexData(
+ CDmeVertexData *pBase,
+ FieldIndex_t fieldIndex,
+ int axis,
+ int nOrigVertexCount,
+ int nMirrorCount,
+ const CDmrArrayConst< T_t > &origData,
+ const CUtlVector< int > &origIndices,
+ const CUtlVector< int > &mirrorMap,
+ const CUtlVector< int > &dataMirrorMap )
+{
+ if ( nMirrorCount <= 0 )
+ return;
+
+ Assert( origIndices.Count() == nOrigVertexCount + nMirrorCount );
+ Assert( mirrorMap.Count() == nOrigVertexCount );
+ Assert( dataMirrorMap.Count() == origData.Count() );
+
+ const int nData = origData.Count();
+ T_t *pMirrorData = reinterpret_cast< T_t * >( alloca( nMirrorCount * sizeof( T_t ) ) );
+ int *pMirrorIndices = reinterpret_cast< int * >( alloca( nMirrorCount * sizeof( int ) ) );
+
+ T_t mirrorData;
+ int nMirrorIndex = 0;
+ int nMirrorDataCount = -1;
+ for ( int i = 0; i < nOrigVertexCount; ++i )
+ {
+ if ( mirrorMap[ i ] != i )
+ {
+ // Vertex must be mirrored
+
+ if ( dataMirrorMap[ origIndices[ i ] ] != origIndices[ i ] )
+ {
+ // Data referred to by vertex i must be mirror (this may be done a redundant number of times)
+ const T_t &origDataRef( origData[ origIndices[ i ] ] );
+ mirrorData = origDataRef;
+ MirrorData( mirrorData, axis );
+ pMirrorData[ dataMirrorMap[ origIndices[ i ] ] - nData ] = mirrorData;
+ if ( ( dataMirrorMap[ origIndices[ i ] ] - nData ) > nMirrorDataCount )
+ {
+ nMirrorDataCount = dataMirrorMap[ origIndices[ i ] ] - nData;
+ }
+ pMirrorIndices[ nMirrorIndex ] = dataMirrorMap[ origIndices[ i ] ];
+ }
+ else
+ {
+ // The data does not need to be mirrored
+ pMirrorIndices[ nMirrorIndex ] = origIndices[ i ];
+ }
+
+ ++nMirrorIndex;
+ }
+ else
+ {
+ Assert( dataMirrorMap[ origIndices[ i ] ] == origIndices[ i ] );
+ }
+ }
+ ++nMirrorDataCount;
+
+ Assert( nMirrorCount == nMirrorIndex );
+ Assert( nMirrorDataCount <= nMirrorCount );
+
+ const DmAttributeType_t dmAttributeType = ArrayTypeToValueType( origData.GetAttribute()->GetType() );
+
+ pBase->AddVertexData( fieldIndex, nMirrorDataCount );
+ pBase->SetVertexData( fieldIndex, nData, nMirrorDataCount, dmAttributeType, pMirrorData );
+
+ pBase->SetVertexIndices( fieldIndex, nOrigVertexCount, nMirrorCount, pMirrorIndices );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::MirrorVertices(
+ CDmeVertexData *pBase,
+ int axis,
+ int nOldVertexCount,
+ int nMirrorCount,
+ const CUtlVector< int > &mirrorMap,
+ const CUtlVector< int > &posMirrorMap,
+ const CUtlVector< int > &normalMirrorMap,
+ const CUtlVector< int > &uvMirrorMap )
+{
+ if ( !pBase || axis < kXAxis || axis > kZAxis )
+ return false;
+
+ pBase->AddVertexIndices( nMirrorCount );
+
+ const int posFieldIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ const int normalFieldIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ const int uvFieldIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+
+ const int nFields = pBase->FieldCount();
+ for ( int i = 0; i < nFields; ++i )
+ {
+ CDmAttribute *pBaseData( pBase->GetVertexData( i ) );
+ const CUtlVector< int > &baseIndices( pBase->GetVertexIndexData( i ) );
+ Assert( baseIndices.Count() == nOldVertexCount + nMirrorCount );
+ Assert( mirrorMap.Count() == nOldVertexCount );
+
+ switch ( pBaseData->GetType() )
+ {
+ case AT_VECTOR2_ARRAY:
+ if ( i == uvFieldIndex )
+ {
+ MirrorVertexData( pBase, i, axis % 2, nOldVertexCount, nMirrorCount, CDmrArrayConst< Vector2D >( pBaseData ), baseIndices, mirrorMap, uvMirrorMap );
+ continue;
+ }
+ break;
+ case AT_VECTOR3_ARRAY:
+ if ( i == posFieldIndex )
+ {
+ MirrorVertexData( pBase, i, axis, nOldVertexCount, nMirrorCount, CDmrArrayConst< Vector >( pBaseData ), baseIndices, mirrorMap, posMirrorMap );
+ continue;
+ }
+ else if ( i == normalFieldIndex )
+ {
+ MirrorVertexData( pBase, i, axis, nOldVertexCount, nMirrorCount, CDmrArrayConst< Vector >( pBaseData ), baseIndices, mirrorMap, normalMirrorMap );
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+
+ MirrorVertices( pBase, i, nOldVertexCount, nMirrorCount, baseIndices, mirrorMap );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// This does the default case of mirroring which is no mirroring at all!
+// No data is changed, the extra indices are added to the index
+//-----------------------------------------------------------------------------
+void CDmMeshUtils::MirrorVertices(
+ CDmeVertexData *pBase,
+ FieldIndex_t fieldIndex,
+ int nOldVertexCount,
+ int nMirrorCount,
+ const CUtlVector< int > &baseIndices,
+ const CUtlVector< int > &mirrorMap )
+{
+ if ( nMirrorCount <= 0 )
+ return;
+
+ Assert( baseIndices.Count() == nOldVertexCount + nMirrorCount );
+ Assert( mirrorMap.Count() == nOldVertexCount );
+ int *pIndices = reinterpret_cast< int * >( alloca( nMirrorCount * sizeof( int ) ) );
+
+ {
+ int pIndex = 0;
+ for ( int i = 0; i < nOldVertexCount; ++i )
+ {
+ if ( mirrorMap[ i ] != i )
+ {
+ pIndices[ pIndex ] = baseIndices[ mirrorMap[ i ] - nOldVertexCount ];
+ ++pIndex;
+ }
+ }
+ Assert( pIndex == nMirrorCount );
+ }
+
+ pBase->SetVertexIndices( fieldIndex, nOldVertexCount, nMirrorCount, pIndices );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void MirrorDeltaData(
+ CDmeVertexDeltaData *pDelta,
+ FieldIndex_t fieldIndex,
+ int axis,
+ const CDmrArrayConst< T_t > &origData,
+ const CUtlVector< int > &origIndices,
+ const CUtlVector< int > &dataMap )
+{
+ Assert( origData.Count() == origIndices.Count() );
+
+ const int nOrigDataCount = origData.Count();
+
+ T_t *pMirrorData = reinterpret_cast< T_t * >( alloca( nOrigDataCount * sizeof( T_t ) ) );
+ int *pMirrorIndices = reinterpret_cast< int * >( alloca( nOrigDataCount * sizeof( int ) ) );
+
+ int nMirrorDataCount = 0;
+ for ( int i = 0; i < nOrigDataCount; ++i )
+ {
+ if ( dataMap[ origIndices[ i ] ] != origIndices[ i ] )
+ {
+ pMirrorData[ nMirrorDataCount ] = origData[ i ];
+ MirrorData( pMirrorData[ nMirrorDataCount ], axis );
+ pMirrorIndices[ nMirrorDataCount ] = dataMap[ origIndices[ i ] ];
+ ++nMirrorDataCount;
+ }
+ }
+
+ const DmAttributeType_t dmAttributeType = ArrayTypeToValueType( origData.GetAttribute()->GetType() );
+
+ pDelta->AddVertexData( fieldIndex, nMirrorDataCount );
+ pDelta->SetVertexData( fieldIndex, nOrigDataCount, nMirrorDataCount, dmAttributeType, pMirrorData );
+ pDelta->SetVertexIndices( fieldIndex, nOrigDataCount, nMirrorDataCount, pMirrorIndices );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::MirrorDelta(
+ CDmeVertexDeltaData *pDelta,
+ int axis,
+ const CUtlVector< int > &posMirrorMap,
+ const CUtlVector< int > &normalMirrorMap,
+ const CUtlVector< int > &uvMirrorMap )
+{
+ if ( !pDelta || axis < kXAxis || axis > kZAxis )
+ return false;
+
+ const int posFieldIndex = pDelta->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ const int normalFieldIndex = pDelta->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ const int uvFieldIndex = pDelta->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+
+ const int nFields = pDelta->FieldCount();
+ for ( int i = 0; i < nFields; ++i )
+ {
+ CDmAttribute *pDeltaData( pDelta->GetVertexData( i ) );
+ const CUtlVector< int > &deltaIndices( pDelta->GetVertexIndexData( i ) );
+
+ switch ( pDeltaData->GetType() )
+ {
+ case AT_VECTOR2_ARRAY:
+ if ( i == uvFieldIndex )
+ {
+ MirrorDeltaData( pDelta, i, axis % 2, CDmrArrayConst< Vector2D >( pDeltaData ), deltaIndices, uvMirrorMap );
+ continue;
+ }
+ break;
+ case AT_VECTOR3_ARRAY:
+ if ( i == posFieldIndex )
+ {
+ MirrorDeltaData( pDelta, i, axis, CDmrArrayConst< Vector >( pDeltaData ), deltaIndices, posMirrorMap );
+ continue;
+ }
+ else if ( i == normalFieldIndex )
+ {
+ MirrorDeltaData( pDelta, i, axis, CDmrArrayConst< Vector >( pDeltaData ), deltaIndices, normalMirrorMap );
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds all materials bound to the mesh and replaces ones which match the
+// source name with the destination name
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::RemapMaterial( CDmeMesh *pMesh, const CUtlString &src, const CUtlString &dst )
+{
+ bool retVal = false;
+
+ char srcName[ MAX_PATH ];
+ char matName[ MAX_PATH ];
+ char dstName[ MAX_PATH ];
+
+ Q_StripExtension( src.Get(), srcName, sizeof( srcName ) );
+ Q_FixSlashes( srcName, '/' );
+
+ Q_strncpy( dstName, dst.Get(), sizeof( dstName ) );
+ Q_FixSlashes( dstName, '/' );
+
+ const int nFaceSets = pMesh->FaceSetCount();
+ for ( int i = 0; i < nFaceSets; ++i )
+ {
+ CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( i );
+ if ( !pFaceSet )
+ continue;
+
+ CDmeMaterial *pMaterial = pFaceSet->GetMaterial();
+ if ( !pMaterial )
+ continue;
+
+ const char *pMaterialName = pMaterial->GetMaterialName();
+ Q_StripExtension( pMaterialName, matName, sizeof( matName ) );
+ Q_FixSlashes( matName, '/' );
+
+ // TODO: Regular expressions or at least glob style matching would be cool
+ if ( !Q_stricmp( srcName, matName ) )
+ {
+ pMaterial->SetMaterial( dstName );
+ pMaterial->SetName( dstName );
+ retVal = true;
+ }
+ }
+
+ return retVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Replaces the nth material found with the specified material name
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::RemapMaterial( CDmeMesh *pMesh, const int nMaterialIndex, const CUtlString &dst )
+{
+ const int nFaceSets = pMesh->FaceSetCount();
+ if ( nMaterialIndex >= nFaceSets )
+ return false;
+
+ CDmeFaceSet *pFaceSet = pMesh->GetFaceSet( nMaterialIndex );
+ if ( !pFaceSet )
+ return false;
+
+ CDmeMaterial *pMaterial = pFaceSet->GetMaterial();
+ if ( !pMaterial )
+ return false;
+
+ pMaterial->SetMaterial( dst );
+ pMaterial->SetName( dst );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds the "socket" on which to base the mesh merge
+// This is defined as the vertices along the two meshes
+// Returns the index into srcBorderEdgesList of the edge list that is found
+// -1 if not found
+//-----------------------------------------------------------------------------
+int CDmMeshUtils::FindMergeSocket(
+ const CUtlVector< CUtlVector< CDmMeshComp::CEdge * > > &srcBorderEdgesList,
+ CDmeMesh *pDstMesh )
+{
+ CDmMeshComp dstComp( pDstMesh );
+
+ const CUtlVector< CDmMeshComp::CEdge * > &edgeList = dstComp.m_edges;
+
+ for ( int i = srcBorderEdgesList.Count() - 1; i >= 0; --i )
+ {
+ const CUtlVector< CDmMeshComp::CEdge * > &srcBorderEdges = srcBorderEdgesList[ i ];
+
+ int nEdgeMatch = 0;
+
+ for ( int j = 0; j != edgeList.Count(); j++ )
+ {
+ const CDmMeshComp::CEdge &e = *edgeList[ j ];
+
+ for ( int k = srcBorderEdges.Count() - 1; k >= 0; --k )
+ {
+ if ( e == *srcBorderEdges[ k ] )
+ {
+ ++nEdgeMatch;
+ break;
+ }
+ }
+ }
+
+ if ( nEdgeMatch == srcBorderEdges.Count() )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Merge by finding the two meshes in the scene which are joined at a socket
+// A socket being defined as a group of border edges that match exactly
+// between two meshes
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::Merge( CDmeMesh *pSrcMesh, CDmElement *pRoot )
+{
+ CDmMeshComp srcComp( pSrcMesh );
+
+ CUtlVector< CUtlVector< CDmMeshComp::CEdge * > > srcBorderEdgesList;
+ if ( srcComp.GetBorderEdges( srcBorderEdgesList ) == 0 )
+ return false;
+
+ CDmeMesh *pDstMesh = NULL;
+
+ // Find each mesh under pRoot
+ CDmeDag *pModel = pRoot->GetValueElement< CDmeDag >( "model" );
+ if ( !pModel )
+ return false;
+
+ CUtlStack< CDmeDag * > traverseStack;
+ traverseStack.Push( pModel );
+
+ CDmeDag *pDag;
+ CDmeMesh *pMesh;
+
+ Vector srcCenter;
+ float srcRadius;
+
+ Vector dstCenter;
+ float dstRadius;
+
+ float sqDist = FLT_MAX;
+ pSrcMesh->GetBoundingSphere( srcCenter, srcRadius );
+
+ int nEdgeListIndex = -1;
+
+ while ( traverseStack.Count() )
+ {
+ traverseStack.Pop( pDag );
+ if ( !pDag )
+ continue;
+
+ // Push all children onto stack in reverse order
+ for ( int nChildIndex = pDag->GetChildCount() - 1; nChildIndex >= 0; --nChildIndex )
+ {
+ traverseStack.Push( pDag->GetChild( nChildIndex ) );
+ }
+
+ // See if there's a mesh associated with this dag
+ pMesh = CastElement< CDmeMesh >( pDag->GetShape() );
+ if ( !pMesh )
+ continue;
+
+ int eli = FindMergeSocket( srcBorderEdgesList, pMesh );
+ if ( eli < 0 )
+ continue;
+
+ pMesh->GetBoundingSphere( dstCenter, dstRadius );
+ dstRadius = dstCenter.DistToSqr( srcCenter );
+
+ if ( dstRadius < sqDist )
+ {
+ sqDist = dstRadius;
+ pDstMesh = pMesh;
+ nEdgeListIndex = eli;
+ }
+ }
+
+ if ( pDstMesh )
+ {
+ return Merge( srcComp, srcBorderEdgesList[ nEdgeListIndex ], pDstMesh );
+ }
+
+ Msg( "Error: Merge() - No Merge Socket Found - i.e. A Set Of Border Edges On The Source Model That Are Found On The Merge Model" );
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void AppendData(
+ const CDmrArrayConst< T_t > &srcData,
+ CDmrArray< T_t > &dstData,
+ const matrix3x4_t *pMat = NULL )
+{
+ const int nSrcCount = srcData.Count();
+ const int nDstCount = dstData.Count();
+
+ dstData.AddMultipleToTail( nSrcCount );
+ dstData.SetMultiple( nDstCount, nSrcCount, srcData.Base() );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template <>
+void AppendData(
+ const CDmrArrayConst< Vector > &srcData,
+ CDmrArray< Vector > &dstData,
+ const matrix3x4_t *pMat )
+{
+ const int nSrcCount = srcData.Count();
+ const int nDstCount = dstData.Count();
+
+ dstData.AddMultipleToTail( nSrcCount );
+
+ if ( pMat )
+ {
+ Vector v;
+ for ( int i = 0; i < nSrcCount; ++i )
+ {
+ v = srcData.Get( i );
+ VectorTransform( srcData.Get( i ), *pMat, v );
+ dstData.Set( nDstCount + i, v );
+ }
+ }
+ else
+ {
+ dstData.SetMultiple( nDstCount, nSrcCount, srcData.Base() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Merge data from a base state on one DmeMesh into another DmeMesh
+// Preserve positions and normals by transforming them with the
+// positionMatrix & normalMatrix
+//
+// Return the number of new vertices in the mesh
+//-----------------------------------------------------------------------------
+int MergeBaseState(
+ CDmeVertexData *pSrcBase,
+ CDmeVertexData *pDstBase,
+ const matrix3x4_t &pMat,
+ const matrix3x4_t &nMat,
+ int nSkinningJointIndex,
+ int &nPositionOffset,
+ int &nNormalOffset,
+ int &nWrinkleOffset )
+{
+ int nRetVal = -1;
+
+ const int nSrcPositionIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ const int nSrcNormalIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ const int nSrcWrinkleIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_WRINKLE );
+ const int nSrcJointWeightsIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS );
+ const int nSrcJointIndicesIndex = pSrcBase->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES );
+
+ const int nDstJointWeightsIndex = pDstBase->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS );
+ const int nDstJointIndicesIndex = pDstBase->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES );
+
+ // Handle skinning the new mesh data to a single joint if the destination mesh
+ // is already skinned. If the destination mesh is skinned but there is no
+ // specific joint specified to skin to, the first joint is used and a warning issued
+
+ if ( nDstJointWeightsIndex >= 0 && nDstJointIndicesIndex >= 0 )
+ {
+ if ( nSkinningJointIndex < 0 )
+ {
+ Msg( "Warning: Destination mesh is skinned but no valid joint specified to skin to, using first joint\n" );
+ nSkinningJointIndex = 0;
+ }
+
+ const int nJointCount = pDstBase->JointCount();
+
+ CDmrGenericArray srcPos( pSrcBase->GetVertexData( nSrcPositionIndex ) );
+ const int nSrcPosCount = srcPos.Count();
+
+ CDmrArray< float > dstWeights( pDstBase->GetVertexData( nDstJointWeightsIndex ) );
+ CDmrArray< int > dstIndices( pDstBase->GetVertexData( nDstJointIndicesIndex ) );
+
+ const int nDstCount = dstWeights.Count();
+ Assert( nDstCount == dstIndices.Count() );
+
+ dstWeights.AddMultipleToTail( nSrcPosCount * nJointCount );
+ dstIndices.AddMultipleToTail( nSrcPosCount * nJointCount );
+
+ // Since there can be more than 1 joint per vertex, specify 1
+ // for the first joint and 0 for the rest but use the same joint
+ const int nEnd = nDstCount + nSrcPosCount * nJointCount;
+ for ( int i = nDstCount; i < nEnd; i += nJointCount )
+ {
+ dstWeights.Set( i, 1.0f );
+ dstIndices.Set( i, nSkinningJointIndex );
+ }
+
+ for ( int i = 1; i < nJointCount; ++i )
+ {
+ for ( int j = nDstCount + i; j < nEnd; j += nJointCount )
+ {
+ dstWeights.Set( j, 0.0f );
+ dstIndices.Set( j, nSkinningJointIndex );
+ }
+ }
+ }
+
+ // Handling merging all fields that match
+ int nIndexPadCount = -1;
+
+ for ( int i = 0; i < pSrcBase->FieldCount(); ++i )
+ {
+ bool bMerged = false;
+
+ for ( int j = 0; j < pDstBase->FieldCount(); ++j )
+ {
+ if ( i == nSrcJointWeightsIndex || i == nSrcJointIndicesIndex || Q_strcmp( pSrcBase->FieldName( i ), pDstBase->FieldName( j ) ) )
+ continue;
+
+ bMerged = true;
+
+ CDmAttribute *pSrcData = pSrcBase->GetVertexData( i );
+ CDmAttribute *pDstData = pDstBase->GetVertexData( j );
+
+ const int nOffset = CDmrGenericArray( pDstData ).Count();
+
+ switch ( pSrcData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ AppendData( CDmrArrayConst< float >( pSrcData ), CDmrArray< float >( pDstData ) );
+ break;
+ case AT_VECTOR2_ARRAY:
+ AppendData( CDmrArrayConst< Vector2D >( pSrcData ), CDmrArray< Vector2D >( pDstData ) );
+ break;
+ case AT_VECTOR3_ARRAY:
+ if ( i == nSrcPositionIndex )
+ {
+ AppendData( CDmrArrayConst< Vector >( pSrcData ), CDmrArray< Vector >( pDstData ), &pMat );
+ }
+ else if ( i == nSrcNormalIndex )
+ {
+ AppendData( CDmrArrayConst< Vector >( pSrcData ), CDmrArray< Vector >( pDstData ), &nMat );
+ }
+ else
+ {
+ AppendData( CDmrArrayConst< Vector >( pSrcData ), CDmrArray< Vector >( pDstData ) );
+ }
+ break;
+ case AT_VECTOR4_ARRAY:
+ AppendData( CDmrArrayConst< Vector4D >( pSrcData ), CDmrArray< Vector4D >( pDstData ) );
+ break;
+ case AT_QUATERNION_ARRAY:
+ AppendData( CDmrArrayConst< Quaternion >( pSrcData ), CDmrArray< Quaternion >( pDstData ) );
+ break;
+ case AT_COLOR_ARRAY:
+ AppendData( CDmrArrayConst< Color >( pSrcData ), CDmrArray< Color >( pDstData ) );
+ break;
+ default:
+ Assert( 0 );
+ break;
+ }
+
+ CDmrArray< int > srcIndices( pSrcBase->GetIndexData( i ) );
+ CDmrArray< int > dstIndices( pDstBase->GetIndexData( j ) );
+
+ const int nSrcIndexCount = srcIndices.Count();
+ const int nDstIndexCount = dstIndices.Count();
+
+ if ( nRetVal < 0 )
+ {
+ nRetVal = nDstIndexCount;
+ }
+ Assert( nRetVal == nDstIndexCount );
+
+ dstIndices.AddMultipleToTail( nSrcIndexCount );
+ if ( nIndexPadCount < 0 )
+ {
+ nIndexPadCount = nSrcIndexCount;
+ }
+ Assert( nIndexPadCount == nSrcIndexCount );
+
+ for ( int k = 0; k < nSrcIndexCount; ++k )
+ {
+ dstIndices.Set( nDstIndexCount + k, srcIndices.Get( k ) + nOffset );
+ }
+
+ if ( i == nSrcPositionIndex )
+ {
+ nPositionOffset = nOffset;
+ }
+ else if ( i == nSrcNormalIndex )
+ {
+ nNormalOffset = nOffset;
+ }
+ else if ( i == nSrcWrinkleIndex )
+ {
+ nWrinkleOffset = nOffset;
+ }
+ }
+
+ if ( !bMerged )
+ {
+ Msg( "Warning: Not merging base data %s\n", pSrcBase->FieldName( i ) );
+ }
+ }
+
+ const int nDstSpeedIndex = pDstBase->FindFieldIndex( CDmeVertexData::FIELD_MORPH_SPEED );
+
+ // Handle all fields on the destination mesh that weren't on the source mesh
+ for ( int i = 0; i < pDstBase->FieldCount(); ++i )
+ {
+ bool bFound = false;
+
+ if ( i == nDstJointWeightsIndex || i == nDstJointIndicesIndex )
+ continue;
+
+ for ( int j = 0; j < pSrcBase->FieldCount(); ++j )
+ {
+ if ( Q_strcmp( pDstBase->FieldName( i ), pSrcBase->FieldName( j ) ) )
+ continue;
+
+ bFound = true;
+ break;
+ }
+
+ if ( !bFound )
+ {
+ int nDstIndex = -1;
+
+ if ( i == nDstSpeedIndex )
+ {
+ // Pad data with a 1
+ nDstIndex = CDmrArray< float >( pDstBase->GetVertexData( i ) ).AddToTail( 1.0f );
+ }
+ else
+ {
+ // Pad data with a 0
+ nDstIndex = CDmrGenericArray( pDstBase->GetVertexData( i ) ).AddToTail();
+ }
+
+ // Pad data indices with index to that extra data value
+ CDmrArray< int > dstIndices( pDstBase->GetIndexData( i ) );
+ const int nStart = dstIndices.Count();
+ const int nEnd = dstIndices.Count() + nIndexPadCount;
+ dstIndices.AddMultipleToTail( nIndexPadCount );
+ for ( int k = nStart; k < nEnd; ++k )
+ {
+ dstIndices.Set( k, nDstIndex );
+ }
+ }
+ }
+
+ pDstBase->Resolve();
+
+ return nRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void MergeDeltaState( CDmeMesh *pDmeMesh, CDmeVertexDeltaData *pSrcDelta, CDmeVertexDeltaData *pDstDelta, int &nPositionOffset, int &nNormalOffset, int &nWrinkleOffset )
+{
+ if ( !pDstDelta )
+ {
+ // No destination delta... copy it
+ pDstDelta = pDmeMesh->FindOrCreateDeltaState( pSrcDelta->GetName() );
+ if ( !pDstDelta )
+ return;
+ }
+
+ for ( int i = 0; i < pSrcDelta->FieldCount(); ++i )
+ {
+ bool bFound = false;
+
+ for ( int j = 0; j < pDstDelta->FieldCount(); ++j )
+ {
+ if ( Q_strcmp( pSrcDelta->FieldName( i ), pDstDelta->FieldName( j ) ) )
+ continue;
+
+ bFound = true;
+ break;
+ }
+
+ if ( !bFound )
+ {
+ // Make an empty one, data will be added below
+ CDmAttribute *pSrcData = pSrcDelta->GetVertexData( i );
+ pDstDelta->CreateField( pSrcDelta->FieldName( i ), pSrcData->GetType() );
+ }
+ }
+
+ const int nSrcPositionIndex = pSrcDelta->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ const int nSrcNormalIndex = pSrcDelta->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ const int nSrcWrinkleIndex = pSrcDelta->FindFieldIndex( CDmeVertexData::FIELD_WRINKLE );
+
+ for ( int i = 0; i < pSrcDelta->FieldCount(); ++i )
+ {
+ int nOffset = 0;
+
+ if ( i == nSrcPositionIndex )
+ {
+ nOffset = nPositionOffset;
+ }
+ else if ( i == nSrcNormalIndex )
+ {
+ nOffset = nNormalOffset;
+ }
+ else if ( i == nSrcWrinkleIndex )
+ {
+ nOffset = nWrinkleOffset;
+ }
+
+ if ( nOffset < 0 )
+ {
+ nOffset = 0;
+ }
+
+ for ( int j = 0; j < pDstDelta->FieldCount(); ++j )
+ {
+ if ( Q_strcmp( pSrcDelta->FieldName( i ), pDstDelta->FieldName( j ) ) )
+ continue;
+
+ CDmAttribute *pSrcData = pSrcDelta->GetVertexData( i );
+ CDmAttribute *pDstData = pDstDelta->GetVertexData( j );
+
+ switch ( pSrcData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ AppendData( CDmrArrayConst< float >( pSrcData ), CDmrArray< float >( pDstData ) );
+ break;
+ case AT_VECTOR2_ARRAY:
+ AppendData( CDmrArrayConst< Vector2D >( pSrcData ), CDmrArray< Vector2D >( pDstData ) );
+ break;
+ case AT_VECTOR3_ARRAY:
+ AppendData( CDmrArrayConst< Vector >( pSrcData ), CDmrArray< Vector >( pDstData ) );
+ break;
+ case AT_VECTOR4_ARRAY:
+ AppendData( CDmrArrayConst< Vector4D >( pSrcData ), CDmrArray< Vector4D >( pDstData ) );
+ break;
+ case AT_QUATERNION_ARRAY:
+ AppendData( CDmrArrayConst< Quaternion >( pSrcData ), CDmrArray< Quaternion >( pDstData ) );
+ break;
+ case AT_COLOR_ARRAY:
+ AppendData( CDmrArrayConst< Color >( pSrcData ), CDmrArray< Color >( pDstData ) );
+ break;
+ default:
+ Assert( 0 );
+ break;
+ }
+
+ CDmrArray< int > srcIndices( pSrcDelta->GetIndexData( i ) );
+ CDmrArray< int > dstIndices( pDstDelta->GetIndexData( j ) );
+
+ const int nSrcIndexCount = srcIndices.Count();
+ const int nDstIndexCount = dstIndices.Count();
+
+ dstIndices.AddMultipleToTail( nSrcIndexCount );
+
+ for ( int k = 0; k < nSrcIndexCount; ++k )
+ {
+ dstIndices.Set( nDstIndexCount + k, srcIndices.Get( k ) + nOffset );
+ }
+
+ break;
+ }
+ }
+
+ // TODO: Centralize all of the '_' for corrector business...
+ const char *pszDeltaName = pDstDelta->GetName();
+ if ( strchr( pszDeltaName, '_' ) )
+ return; // No controls for deltas with '_''s
+
+ if ( !pDmeMesh )
+ return;
+
+ CDmeCombinationOperator *pDmeCombo = FindReferringElement< CDmeCombinationOperator >( pDmeMesh, "targets" );
+ if ( !pDmeCombo )
+ return;
+
+ if ( pDmeCombo->HasRawControl( pszDeltaName ) )
+ return;
+
+ pDmeCombo->FindOrCreateControl( pDstDelta->GetName(), false, true );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void GetAbsTransform( CDmeDag *pDmeDag, matrix3x4_t &m )
+{
+ matrix3x4_t mParentAbsTransform;
+ pDmeDag->GetParentWorldMatrix( mParentAbsTransform );
+
+ matrix3x4_t mLocal;
+ CDmeTransform *pDmeTransform = pDmeDag->GetTransform();
+ if ( pDmeTransform )
+ {
+ pDmeTransform->GetTransform( mLocal );
+ }
+ else
+ {
+ SetIdentityMatrix( mLocal );
+ }
+
+ ConcatTransforms( mParentAbsTransform, mLocal, m );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::Merge( CDmeMesh *pSrcMesh, CDmeMesh *pDstMesh, int nSkinningJointIndex )
+{
+ if ( !pSrcMesh || !pDstMesh )
+ return false;
+
+ CDmeDag *pSrcDag = FindReferringElement< CDmeDag >( pSrcMesh, "shape", true );
+ CDmeDag *pDstDag = FindReferringElement< CDmeDag >( pDstMesh, "shape", true );
+
+ if ( !pSrcDag || !pDstDag )
+ return false;
+
+ matrix3x4_t nMat;
+ GetAbsTransform( pSrcDag, nMat );
+ matrix3x4_t pMat;
+ GetAbsTransform( pDstDag, pMat );
+ matrix3x4_t dMatInv;
+
+ MatrixInvert( pMat, dMatInv );
+ MatrixMultiply( dMatInv, nMat, pMat );
+ MatrixInverseTranspose( pMat, nMat );
+
+ int nPositionOffset = -1;
+ int nNormalOffset = -1;
+ int nWrinkleOffset = -1;
+
+ int nVertexOffset = -1;
+
+ for ( int i = 0; i < pSrcMesh->BaseStateCount(); ++i )
+ {
+ CDmeVertexData *pSrcBase = pSrcMesh->GetBaseState( i );
+ bool bMerged = false;
+
+ for ( int j = 0; j < pDstMesh->BaseStateCount(); ++j )
+ {
+ CDmeVertexData *pDstBase = pDstMesh->GetBaseState( j );
+
+ if ( Q_strcmp( pSrcBase->GetName(), pDstBase->GetName() ) )
+ continue;
+
+ bMerged = true;
+ const int nTmpVertexOffset = MergeBaseState( pSrcBase, pDstBase, pMat, nMat, nSkinningJointIndex, nPositionOffset, nNormalOffset, nWrinkleOffset );
+ if ( nVertexOffset < 0 )
+ {
+ nVertexOffset = nTmpVertexOffset;
+ }
+
+ Assert( nVertexOffset == nTmpVertexOffset );
+ }
+
+ if ( !bMerged )
+ {
+ Msg( "Error: Merge( %s, %s ) - Can't Find Base State %s On %s\n", pSrcMesh->GetName(), pDstMesh->GetName(), pSrcBase->GetName(), pDstMesh->GetName() );
+ }
+ }
+
+ // Merge Face Sets
+
+ int nFaceSetIndex;
+
+ for ( int i = 0; i < pSrcMesh->FaceSetCount(); ++i )
+ {
+ CDmeFaceSet *pFaceSet = pSrcMesh->GetFaceSet( i )->Copy();
+ pFaceSet->SetFileId( pDstMesh->GetFileId(), TD_DEEP );
+ const int nFaceSetIndexCount = pFaceSet->NumIndices();
+ for ( int j = 0; j < nFaceSetIndexCount; ++j )
+ {
+ nFaceSetIndex = pFaceSet->GetIndex( j );
+ if ( nFaceSetIndex >= 0 )
+ {
+ pFaceSet->SetIndex( j, nFaceSetIndex + nVertexOffset );
+ }
+ }
+ pDstMesh->AddFaceSet( pFaceSet );
+ }
+
+ // Merge Deltas
+
+ for ( int i = 0; i < pSrcMesh->DeltaStateCount(); ++i )
+ {
+ CDmeVertexDeltaData *pSrcDelta = pSrcMesh->GetDeltaState( i );
+ CDmeVertexDeltaData *pDstDelta = pDstMesh->FindDeltaState( pSrcDelta->GetName() );
+ MergeDeltaState( pDstMesh, pSrcDelta, pDstDelta, nPositionOffset, nNormalOffset, nWrinkleOffset );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+struct VertexWeightMap_s
+{
+ struct VertexWeight_s
+ {
+ int m_vertexDataIndex; // Index into the CDmeVertexData data (only used for joint weights & indices)
+ const CUtlVector< int > *m_pVertexIndices; // Index into the CDmeVertexData vertex indices
+ float m_vertexWeight;
+ };
+
+ int m_nVertexWeights;
+ VertexWeight_s m_vertexWeights[ 5 ];
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CopyJointWeights(
+ CDmeVertexData *pSrcData,
+ CDmeVertexData *pDstData,
+ const CUtlVector< VertexWeightMap_s > &vertexWeightMap )
+{
+ const int nJointCount = pSrcData->GetValue< int >( "jointCount" );
+
+ const FieldIndex_t nSrcJointWeightsField = pSrcData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS );
+ const FieldIndex_t nSrcJointIndicesField = pSrcData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES );
+
+ if ( nJointCount <= 0 || nSrcJointWeightsField < 0 || nSrcJointIndicesField < 0 )
+ return false;
+
+ const CUtlVector< float > &srcJointWeights = CDmrArrayConst< float >( pSrcData->GetVertexData( nSrcJointWeightsField ) ).Get();
+ const float *const pSrcJointWeights = srcJointWeights.Base();
+
+ const CUtlVector< int > &srcJointIndices = CDmrArrayConst< int >( pSrcData->GetVertexData( nSrcJointIndicesField ) ).Get();
+ const int *const pSrcJointIndices = srcJointIndices.Base();
+
+ FieldIndex_t nDstJointWeightsField;
+ FieldIndex_t nDstJointIndicesField;
+
+ pDstData->CreateJointWeightsAndIndices( nJointCount, &nDstJointWeightsField, &nDstJointIndicesField );
+
+ const int nDstCount = vertexWeightMap.Count();
+
+ float *pDstJointWeights = reinterpret_cast< float * >( alloca( nDstCount * nJointCount * sizeof( float ) ) );
+ memset( pDstJointWeights, 0, nDstCount * nJointCount );
+
+ int *pDstJointIndices = reinterpret_cast< int * >( alloca( nDstCount * nJointCount * sizeof( int ) ) );
+ memset( pDstJointIndices, 0, nDstCount * nJointCount );
+
+ for ( int i = 0; i < nDstCount; ++i )
+ {
+ const VertexWeightMap_s &vertexWeight = vertexWeightMap[ i ];
+ const int nVertexWeights = vertexWeight.m_nVertexWeights;
+
+ if ( nVertexWeights > 0 )
+ {
+ // TODO: Find the best weights to use! For now, use the first one
+ int nMatchIndex = vertexWeight.m_vertexWeights[ 0 ].m_vertexDataIndex;
+
+ memcpy( pDstJointWeights + i * nJointCount, pSrcJointWeights + nMatchIndex * nJointCount, nJointCount * sizeof( float ) );
+ memcpy( pDstJointIndices + i * nJointCount, pSrcJointIndices + nMatchIndex * nJointCount, nJointCount * sizeof( int ) );
+ }
+ }
+
+ pDstData->AddVertexData( nDstJointIndicesField, nDstCount * nJointCount );
+ pDstData->SetVertexData( nDstJointIndicesField, 0, nDstCount * nJointCount, AT_INT, pDstJointIndices );
+
+ pDstData->AddVertexData( nDstJointWeightsField, nDstCount * nJointCount );
+ pDstData->SetVertexData( nDstJointWeightsField, 0, nDstCount * nJointCount, AT_FLOAT, pDstJointWeights );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Replaces the DstMesh with the SrcMesh
+//-----------------------------------------------------------------------------
+CDmeMesh *ReplaceMesh(
+ CDmeMesh *pSrcMesh,
+ CDmeMesh *pDstMesh )
+{
+ if ( !pSrcMesh || !pDstMesh )
+ return NULL;
+
+ CDmeDag *pSrcDag = pSrcMesh->GetParent();
+ CDmeDag *pDstDag = pDstMesh->GetParent();
+
+ if ( !pSrcDag || !pDstDag )
+ return NULL;
+
+ // Fix up the transform
+ matrix3x4_t inclusiveMat;
+ matrix3x4_t localMat;
+
+ pDstDag->GetShapeToWorldTransform( inclusiveMat );
+ pDstDag->GetTransform()->GetTransform( localMat );
+
+ matrix3x4_t inverseMat;
+ MatrixInvert( localMat, inverseMat );
+
+ matrix3x4_t exclusiveMat;
+ MatrixMultiply( inclusiveMat, inverseMat, exclusiveMat );
+ MatrixInvert( exclusiveMat, inverseMat );
+
+ pSrcDag->GetShapeToWorldTransform( inclusiveMat );
+ MatrixMultiply( inverseMat, inclusiveMat, localMat );
+
+ pDstDag->GetTransform()->SetTransform( localMat );
+
+ // Duplicate the mesh
+ CDmeMesh *pNewMesh = pSrcMesh->Copy();
+ pNewMesh->SetFileId( pDstMesh->GetFileId(), TD_DEEP );
+
+ // A bit of cleanup
+ pNewMesh->RemoveAttribute( "selection" );
+ pNewMesh->SetCurrentBaseState( "bind" );
+ pNewMesh->DeleteBaseState( "__dmxEdit_work" );
+
+ // Replace the DstMesh with the SrcMesh
+ pDstDag->SetShape( pNewMesh );
+
+ // Replace the combination operators, if applicable
+ CDmeCombinationOperator *pSrcComboOp = FindReferringElement< CDmeCombinationOperator >( pSrcMesh, "targets" );
+ if ( pSrcComboOp )
+ {
+ CDmeCombinationOperator *pDstComboOp = FindReferringElement< CDmeCombinationOperator >( pDstMesh, "targets" );
+ CDmElement *pDstRoot = NULL;
+ if ( pDstComboOp )
+ {
+ // Find the root the easy way
+ pDstRoot = FindReferringElement< CDmElement >( pDstComboOp, "combinationOperator" );
+
+ // Delete the old busted combination operator
+ g_pDataModel->DestroyElement( pDstComboOp->GetHandle() );
+ }
+ else
+ {
+ // Find the root the hard way
+ CDmeDag *pDmeDag = pDstDag;
+ for ( ;; )
+ {
+ // Walk backwards via "children" attribute
+ CDmeDag *pNextDag = FindReferringElement< CDmeDag >( pDmeDag, "children" );
+ if ( pNextDag )
+ {
+ pDmeDag = pNextDag;
+ }
+ else
+ {
+ // Can't find anyone referring to this via "children" so, hopefully it's the DmeModel referred to by "model"
+ pDstRoot = FindReferringElement< CDmElement >( pDmeDag, "model" );
+ break;
+ }
+ }
+ }
+
+ if ( pDstRoot )
+ {
+ // Install the shiny new combination operator
+ CDmeCombinationOperator *pNewComboOp = pSrcComboOp->Copy();
+ pNewComboOp->SetFileId( pDstRoot->GetFileId(), TD_DEEP );
+ pDstRoot->SetValue( "combinationOperator", pNewComboOp );
+ pNewComboOp->RemoveAllTargets();
+ pNewComboOp->AddTarget( pNewMesh );
+ pNewComboOp->GenerateWrinkleDeltas( false );
+
+ }
+ }
+
+ return pNewMesh;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+template < class T_t >
+void CopyFieldData(
+ const CDmrArrayConst< T_t > &srcData,
+ const CUtlVector< int > &srcIndices,
+ CDmeVertexData *pDstVertexData,
+ FieldIndex_t dstFieldIndex,
+ const CUtlVector< VertexWeightMap_s > &vertexWeightMap )
+{
+ const int nDstData = vertexWeightMap.Count();
+
+ T_t sum;
+
+ T_t *pDstData = reinterpret_cast< T_t * >( alloca( nDstData * sizeof( T_t ) ) );
+
+ for ( int i = 0; i < nDstData; ++i )
+ {
+ CDmAttributeInfo< T_t >::SetDefaultValue( pDstData[ i ] );
+
+ const VertexWeightMap_s &vertexWeight = vertexWeightMap[ i ];
+ for ( int j = 0; j < vertexWeight.m_nVertexWeights; ++j )
+ {
+ const VertexWeightMap_s::VertexWeight_s &vWeight = vertexWeight.m_vertexWeights[ j ];
+
+ CDmAttributeInfo< T_t >::SetDefaultValue( sum );
+
+ const CUtlVector< int > &vertexList = *vWeight.m_pVertexIndices;
+ for ( int k = 0; k < vertexList.Count(); ++k )
+ {
+ sum += srcData[ srcIndices[ vertexList[ k ] ] ];
+ }
+ sum /= static_cast< float >( vertexList.Count() );
+
+ pDstData[ i ] += sum * vWeight.m_vertexWeight;
+ }
+ }
+
+ const DmAttributeType_t dmAttributeType = ArrayTypeToValueType( srcData.GetAttribute()->GetType() );
+ CDmrArray< T_t > dstData( pDstVertexData->GetVertexData( dstFieldIndex ) );
+ dstData.EnsureCount( nDstData );
+ pDstVertexData->SetVertexData( dstFieldIndex, 0, nDstData, dmAttributeType, pDstData );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CopyField(
+ CDmeVertexData::StandardFields_t field,
+ CDmeVertexData *pSrcData,
+ CDmeVertexData *pDstData,
+ const CUtlVector< VertexWeightMap_s > &vertexWeightMap )
+{
+ FieldIndex_t srcFieldIndex = pSrcData->FindFieldIndex( field );
+ if ( srcFieldIndex < 0 )
+ return false;
+
+ FieldIndex_t dstFieldIndex = pDstData->CreateField( field );
+ if ( dstFieldIndex < 0 )
+ return false;
+
+ CDmAttribute *pSrcVertexData = pSrcData->GetVertexData( srcFieldIndex );
+ const CUtlVector< int > &srcIndices = pSrcData->GetVertexIndexData( srcFieldIndex );
+
+ // Everything on dst has to be indexed the same as position
+ const CUtlVector< int > &dstPosIndices = pDstData->GetVertexIndexData( CDmeVertexData::FIELD_POSITION );
+ CDmrArray< int > dstIndices( pDstData->GetIndexData( dstFieldIndex ) );
+ dstIndices.EnsureCount( dstPosIndices.Count() );
+ pDstData->SetVertexIndices( dstFieldIndex, 0, dstPosIndices.Count(), dstPosIndices.Base() );
+
+
+ switch ( pSrcVertexData->GetType() )
+ {
+ case AT_FLOAT_ARRAY:
+ CopyFieldData( CDmrArrayConst< float >( pSrcVertexData ), srcIndices, pDstData, dstFieldIndex, vertexWeightMap );
+ break;
+ case AT_VECTOR2_ARRAY:
+ CopyFieldData( CDmrArrayConst< Vector2D >( pSrcVertexData ), srcIndices, pDstData, dstFieldIndex, vertexWeightMap );
+ break;
+ case AT_VECTOR3_ARRAY:
+ CopyFieldData( CDmrArrayConst< Vector >( pSrcVertexData ), srcIndices, pDstData, dstFieldIndex, vertexWeightMap );
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::Merge(
+ CDmMeshComp &srcComp,
+ const CUtlVector< CDmMeshComp::CEdge * > &edgeList,
+ CDmeMesh *pDstMesh )
+{
+ CDmeMesh *pSrcMesh = srcComp.m_pMesh;
+ if ( !pSrcMesh || !pDstMesh )
+ return false;
+
+ CDmeVertexData *pSrcData = pSrcMesh->FindBaseState( "bind" );
+ CDmeVertexData *pDstData = pDstMesh->FindBaseState( "bind" );
+
+ if ( !pSrcData || !pDstData )
+ return false;
+
+ const CUtlVector< Vector > &srcPosData = pSrcData->GetPositionData();
+ const int nSrcCount = srcPosData.Count();
+
+ const CUtlVector< Vector > &dstPosData = pDstData->GetPositionData();
+ const int nDstCount = dstPosData.Count();
+
+ if ( nSrcCount <= 0 || nDstCount <= 0 )
+ return false;
+
+ CUtlVector< VertexWeightMap_s > vertexWeightMap;
+ vertexWeightMap.SetSize( nSrcCount );
+
+ for ( int i = 0; i < nSrcCount; ++i )
+ {
+ int nClosestIndex = -1;
+ float closest = FLT_MAX;
+
+ VertexWeightMap_s &vertexWeight = vertexWeightMap[ i ];
+ vertexWeight.m_nVertexWeights = 0;
+
+ const Vector &vSrc = srcPosData[ i ];
+
+ for ( int j = 0; j < nDstCount; ++j )
+ {
+ const Vector &vDst = dstPosData[ j ];
+ if ( vSrc.DistToSqr( vDst ) < FLT_EPSILON * 10.0f )
+ {
+ vertexWeight.m_nVertexWeights = 1;
+ vertexWeight.m_vertexWeights[ 0 ].m_vertexDataIndex = j;
+ vertexWeight.m_vertexWeights[ 0 ].m_vertexWeight = 1.0f;
+ vertexWeight.m_vertexWeights[ 0 ].m_pVertexIndices = &pDstData->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, j );
+ break;
+ }
+
+ float distance = vSrc.DistToSqr( vDst );
+ if ( distance < closest )
+ {
+ closest = distance;
+ nClosestIndex = j;
+ }
+ }
+
+ if ( vertexWeight.m_nVertexWeights == 0 )
+ {
+ Warning( "Warning: Merge() - No Match For Src Vertex: %f %f %f, Using Closest: %f %f %f\n",
+ vSrc.x, vSrc.y, vSrc.z,
+ dstPosData[ nClosestIndex ].x, dstPosData[ nClosestIndex ].y, dstPosData[ nClosestIndex ].z );
+
+ // TODO: Loop through and find up to n closest vertices by position
+
+ vertexWeight.m_nVertexWeights = 1;
+ vertexWeight.m_vertexWeights[ 0 ].m_vertexDataIndex = nClosestIndex;
+ vertexWeight.m_vertexWeights[ 0 ].m_vertexWeight = 1.0f;
+ vertexWeight.m_vertexWeights[ 0 ].m_pVertexIndices = &pDstData->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, nClosestIndex );
+
+ // Assert( vertexWeight.m_nVertexWeights );
+ // return false;
+ }
+ }
+
+ CDmeMesh *pNewMesh = ReplaceMesh( pSrcMesh, pDstMesh );
+ if ( !pNewMesh )
+ {
+ Error( "Error: Merge() - Couldn't Replace Mesh %s With %s\n", pDstMesh->GetName(), pSrcMesh->GetName() );
+ return false;
+ }
+
+ CDmeVertexData *pNewData = pNewMesh->FindBaseState( "bind" );
+ if ( pNewData )
+ {
+ CopyJointWeights( pDstData, pNewData, vertexWeightMap );
+
+ CopyField( CDmeVertexData::FIELD_BALANCE, pDstData, pNewData, vertexWeightMap );
+
+ CopyField( CDmeVertexData::FIELD_MORPH_SPEED, pDstData, pNewData, vertexWeightMap );
+
+ if ( pNewData->FindFieldIndex( CDmeVertexData::FIELD_MORPH_SPEED ) >= 0 )
+ {
+ CDmeCombinationOperator *pComboOp( FindReferringElement< CDmeCombinationOperator >( pNewMesh, "targets" ) );
+ if ( pComboOp )
+ {
+ pComboOp->UsingLaggedData( true );
+ }
+ }
+ }
+
+ // Destroy the old busted mesh
+ g_pDataModel->DestroyElement( pDstMesh->GetHandle() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a guaranteed unique DmFileId_t
+//-----------------------------------------------------------------------------
+DmFileId_t CreateUniqueFileId()
+{
+ DmFileId_t fileId = DMFILEID_INVALID;
+
+ UniqueId_t uniqueId;
+ char fileIdBuf[ MAX_PATH ];
+
+ do
+ {
+ CreateUniqueId( &uniqueId );
+ UniqueIdToString( uniqueId, fileIdBuf, sizeof( fileIdBuf ) );
+
+ fileId = g_pDataModel->GetFileId( fileIdBuf );
+ } while( fileId != DMFILEID_INVALID );
+
+ return g_pDataModel->FindOrCreateFileId( fileIdBuf );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CreateExpressionFile( const char *pExpressionFile, const CUtlVector< CUtlString > *pPurgeAllButThese, CDmeCombinationOperator *pComboOp, CDmePresetGroup *pPresetGroup )
+{
+ if ( !pPresetGroup )
+ return false;
+
+ Assert( pExpressionFile && pComboOp );
+
+ const int nControlCount = pComboOp->GetControlCount();
+
+ const CDmaElementArray< CDmePreset > &presets = pPresetGroup->GetPresets();
+ const int nPresetsCount = presets.Count();
+
+ if ( nControlCount <= 0 || nPresetsCount <= 0 )
+ return false;
+
+ char expName[ MAX_PATH ];
+ Q_FileBase( pExpressionFile, expName, sizeof( expName ) );
+
+ CDmePresetGroup *pDstPresetGroup = CreateElement< CDmePresetGroup >( expName, CreateUniqueFileId() );
+ if ( !pDstPresetGroup )
+ return false;
+
+ for ( int i = 0; i < nPresetsCount; ++i )
+ {
+ CDmePreset *pPreset = presets[ i ];
+ const char *pPresetName = pPreset->GetName();
+ CDmePreset *pDstPreset = pDstPresetGroup->FindOrAddPreset( pPresetName );
+
+ const CDmaElementArray< CDmElement > &controlValues = pPreset->GetControlValues();
+ const int nControlValueCount = controlValues.Count();
+
+ for ( int j = 0; j < nControlCount; ++j )
+ {
+ // Figure out if this preset is used
+ bool bFound = false; // Used for two things
+ const char *pControlName = pComboOp->GetControlName( j );
+
+ if ( pPurgeAllButThese )
+ {
+ for ( int k = 0; k < pPurgeAllButThese->Count(); ++k )
+ {
+ if ( !Q_strcmp( pControlName, pPurgeAllButThese->Element( k ).Get() ) )
+ {
+ bFound = true;
+ break;
+ }
+ }
+ }
+
+ if ( !bFound && pPresetGroup->FindPreset( pControlName ) )
+ bFound = true;
+
+ if ( !bFound )
+ continue;
+
+ CDmElement *pDstControlValue = NULL;
+
+ const bool bStereo = pComboOp->IsStereoControl( j );
+ const bool bMulti = pComboOp->IsMultiControl( j );
+
+ if ( !Q_strcmp( pControlName, pPresetName ) )
+ {
+ pDstControlValue = pDstPreset->FindOrAddControlValue( pControlName );
+ pDstControlValue->SetValue( "value", 1.0f );
+
+ // These shouldn't really happen because these are presets which were made
+ // into deltas so they are never stereo nor multi-controls
+ if ( bStereo )
+ {
+ pDstControlValue->SetValue( "balance", 0.5f );
+ }
+ if ( bStereo )
+ {
+ pDstControlValue->SetValue( "multilevel", 0.5f );
+ }
+ continue;
+ }
+
+ for ( int k = 0; k < nControlValueCount; ++k )
+ {
+ CDmElement *pControlPreset = controlValues[ k ];
+
+ if ( !Q_strcmp( pControlName, pControlPreset->GetName() ) )
+ {
+ pDstControlValue = pDstPreset->FindOrAddControlValue( pControlName );
+ pDstControlValue->SetValue( "value", pControlPreset->GetValue( "value", 0.0f ) );
+ if ( bStereo )
+ {
+ pDstControlValue->SetValue( "balance", pControlPreset->GetValue( "balance", 0.5f ) );
+ }
+ if ( bMulti )
+ {
+ pDstControlValue->SetValue( "multilevel", pControlPreset->GetValue( "multilevel", 0.5f ) );
+ }
+ break;
+ }
+ }
+
+ if ( !pDstControlValue )
+ {
+ pDstControlValue = pDstPreset->FindOrAddControlValue( pControlName );
+ pDstControlValue->SetValue( "value", pComboOp->GetControlDefaultValue( j ) );
+
+ if ( bStereo )
+ {
+ pDstControlValue->SetValue( "balance", 0.5f );
+ }
+ if ( bMulti )
+ {
+ pDstControlValue->SetValue( "multilevel", 0.5f );
+ }
+ }
+ }
+ }
+
+ char buf[ MAX_PATH ];
+ char buf1[ MAX_PATH ];
+ Q_strncpy( buf, pExpressionFile, sizeof( buf ) );
+ Q_SetExtension( buf, ".txt", sizeof( buf ) );
+ Q_ExtractFilePath( buf, buf1, sizeof( buf1 ) );
+ Q_FixSlashes( buf1 );
+ g_pFullFileSystem->CreateDirHierarchy( buf1 );
+
+ if ( !g_p4factory->AccessFile( buf )->Edit() )
+ {
+ g_p4factory->AccessFile( buf )->Add();
+ }
+
+ pDstPresetGroup->ExportToTXT( buf, NULL, pComboOp );
+
+ Q_SetExtension( buf, ".vfe", sizeof( buf ) );
+ Q_ExtractFilePath( buf, buf1, sizeof( buf1 ) );
+ Q_FixSlashes( buf1 );
+ g_pFullFileSystem->CreateDirHierarchy( buf1 );
+
+ if ( !g_p4factory->AccessFile( buf )->Edit() )
+ {
+ g_p4factory->AccessFile( buf )->Add();
+ }
+
+ pDstPresetGroup->ExportToVFE( buf, NULL, pComboOp );
+
+ g_pDataModel->UnloadFile( pDstPresetGroup->GetFileId() );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::CreateDeltasFromPresets(
+ CDmeMesh *pMesh,
+ CDmeVertexData *pPassedDst,
+ const CUtlStringMap< CUtlString > &presetExpressionMap,
+ bool bPurge,
+ const CUtlVector< CUtlString > *pPurgeAllButThese /*= NULL */ )
+{
+ if ( !pMesh )
+ return false;
+
+ CDisableUndoScopeGuard sgDisableUndo;
+
+ CUtlStringMap< CDmePreset * > presetMap;
+ CUtlStringMap< CUtlString > conflictingNames;
+
+ CDmeVertexData *pDst = pPassedDst ? pPassedDst : pMesh->GetCurrentBaseState();
+ CDmeVertexData *pBind = pMesh->FindBaseState( "bind" );
+ if ( !pDst || !pBind || pDst == pBind )
+ return false;
+
+ CDmeCombinationOperator *pComboOp = FindReferringElement< CDmeCombinationOperator >( pMesh, "targets" );
+ if ( !pComboOp )
+ return false;
+
+ const bool bSavedUsingLagged = pComboOp->IsUsingLaggedData();
+
+ CUtlVector< CDmePresetGroup * > presetGroups;
+
+ for ( int i = 0; i < presetExpressionMap.GetNumStrings(); ++i )
+ {
+ const char *pPresetFilename = presetExpressionMap.String( i );
+
+ // Load the preset file
+ CDmElement *pRoot = NULL;
+ g_p4factory->AccessFile( pPresetFilename )->Add();
+ g_pDataModel->RestoreFromFile( pPresetFilename, NULL, NULL, &pRoot );
+ CDmePresetGroup *pPresetGroup = CastElement< CDmePresetGroup >( pRoot );
+
+ presetGroups.AddToTail( pPresetGroup );
+
+ if ( !pPresetGroup )
+ continue;
+
+ CreateDeltasFromPresetGroup( pPresetGroup, pComboOp, pPurgeAllButThese, pMesh, pDst, conflictingNames, presetMap );
+ }
+
+ if ( bPurge )
+ {
+ PurgeUnreferencedDeltas( pMesh, presetMap, pPurgeAllButThese, pComboOp );
+ }
+
+ for ( int i = 0; i < presetMap.GetNumStrings(); ++i )
+ {
+ const char *pPresetName = presetMap[ i ]->GetName();
+ const int nControlIndex = pComboOp->FindControlIndex( pPresetName );
+ if ( nControlIndex < 0 )
+ {
+ pComboOp->FindOrCreateControl( pPresetName, false, true );
+ }
+ else
+ {
+ bool bFound = false;
+
+ if ( bPurge )
+ {
+ pComboOp->RemoveAllRawControls( nControlIndex );
+ }
+ else
+ {
+ const int nRawControls = pComboOp->GetRawControlCount( nControlIndex );
+ for ( int j = 0; j < nRawControls; ++j )
+ {
+ if ( !Q_strcmp( pComboOp->GetRawControlName( nControlIndex, j ), pPresetName ) )
+ {
+ bFound = true;
+ break;
+ }
+ }
+ }
+
+ if ( !bFound )
+ {
+ pComboOp->AddRawControl( nControlIndex, pPresetName );
+ }
+ }
+ }
+
+ pComboOp->UsingLaggedData( bSavedUsingLagged );
+ pComboOp->SetToDefault();
+
+ for ( int i = 0; i < presetExpressionMap.GetNumStrings(); ++i )
+ {
+ const CUtlString &expressionFile = presetExpressionMap[ i ];
+ if ( expressionFile.IsEmpty() )
+ continue;
+
+ CreateExpressionFile( expressionFile.Get(), pPurgeAllButThese, pComboOp, presetGroups[ i ] );
+ }
+
+ for ( int i = 0; i < presetGroups.Count(); ++i )
+ {
+ CDmePresetGroup *pPresetGroup = presetGroups[ i ];
+ if ( !pPresetGroup )
+ continue;
+
+ g_pDataModel->UnloadFile( pPresetGroup->GetFileId() );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Removes any deltas from the specified mesh which are not referred to by
+// any rule of the combination operator driving the mesh
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::PurgeUnusedDeltas( CDmeMesh *pMesh )
+{
+ // Disable for now
+ // This code will delete all corrective delta states, i.e. deltas named A_B
+ return true;
+
+ if ( !pMesh )
+ return false;
+
+ CDmeCombinationOperator *pCombo = FindReferringElement< CDmeCombinationOperator >( pMesh, "targets" );
+ if ( !pCombo )
+ return false;
+
+ const int nControlCount = pCombo->GetControlCount();
+
+ CUtlVector< CDmeMesh::DeltaComputation_t > compList;
+ pMesh->ComputeDependentDeltaStateList( compList );
+ const int nDeltaCount = compList.Count();
+
+ Assert( nDeltaCount == pMesh->DeltaStateCount() );
+
+ CUtlVector< bool > deltasToKeep;
+ deltasToKeep.EnsureCount( nDeltaCount );
+ memset( deltasToKeep.Base(), 0, sizeof( bool ) * nDeltaCount );
+
+ for ( int i = 0; i < nControlCount; ++i )
+ {
+ const int nRawControlCount = pCombo->GetRawControlCount( i );
+ for ( int j = 0; j < nRawControlCount; ++j )
+ {
+ const int nDeltaIndex = pMesh->FindDeltaStateIndex( pCombo->GetRawControlName( i, j ) );
+ const CDmeMesh::DeltaComputation_t &deltaComp = compList[ nDeltaIndex ];
+ deltasToKeep[ deltaComp.m_nDeltaIndex ] = true;
+ for ( int k = 0; k < deltaComp.m_DependentDeltas.Count(); ++k )
+ {
+ deltasToKeep[ deltaComp.m_DependentDeltas[ k ] ] = true;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshUtils::CreateWrinkleDeltaFromBaseState(
+ CDmeVertexDeltaData *pDelta,
+ float flScale /* = 1.0f */,
+ WrinkleOp wrinkleOp /* = kReplace */,
+ CDmeMesh *pPassedMesh /* = NULL */,
+ CDmeVertexData *pPassedBind /* = NULL */,
+ CDmeVertexData *pPassedCurrent /* = NULL */ )
+{
+ CDmeVertexData *pBind = pPassedBind ? pPassedBind : pPassedMesh ? pPassedMesh->GetBindBaseState() : NULL;
+ CDmeVertexData *pCurr = pPassedCurrent ? pPassedCurrent : pPassedMesh ? pPassedMesh->GetCurrentBaseState() : NULL;
+
+ const CDmeMesh *pMesh = pPassedMesh ? pPassedMesh : pBind ? FindReferringElement< CDmeMesh >( pBind, "baseStates" ) : NULL;
+ const CDmeMesh *pBindMesh = pBind ? FindReferringElement< CDmeMesh >( pBind, "baseStates" ) : NULL;
+ const CDmeMesh *pCurrMesh = pCurr ? FindReferringElement< CDmeMesh >( pCurr, "baseStates", false ) : NULL;
+ const CDmeMesh *pDeltaMesh = pDelta ? FindReferringElement< CDmeMesh >( pDelta, "deltaStates" ) : NULL;
+
+ if ( !pDelta || !pBind || !pCurr || pBind == pCurr || !pMesh || pMesh != pBindMesh || pMesh != pCurrMesh || pMesh != pDeltaMesh )
+ {
+ return false;
+ }
+
+ const FieldIndex_t nBindPosIndex = pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ const FieldIndex_t nBindTexIndex = pBind->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+ const FieldIndex_t nCurrPosIndex = pCurr->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+
+ if ( nBindPosIndex < 0 || nBindTexIndex < 0 || nCurrPosIndex < 0 )
+ return false;
+
+ const CUtlVector< Vector > &bindPos = CDmrArrayConst< Vector >( pBind->GetVertexData( nBindPosIndex ) ).Get();
+ const CUtlVector< Vector > &currPos = CDmrArrayConst< Vector >( pCurr->GetVertexData( nCurrPosIndex ) ).Get();
+ const CUtlVector< int > &baseTexCoordIndices = pBind->GetVertexIndexData( nBindTexIndex );
+
+ const int nPosCount = bindPos.Count();
+ if ( nPosCount != currPos.Count() )
+ return false;
+
+ const CDmrArrayConst< Vector2D > texData( pBind->GetVertexData( nBindTexIndex ) );
+ const int nBaseTexCoordCount = texData.Count();
+
+ FieldIndex_t nWrinkleIndex = pDelta->FindFieldIndex( CDmeVertexDeltaData::FIELD_WRINKLE );
+ if ( nWrinkleIndex < 0 )
+ {
+ nWrinkleIndex = pDelta->CreateField( CDmeVertexDeltaData::FIELD_WRINKLE );
+ }
+
+ float *pOldWrinkleData = NULL;
+
+ if ( wrinkleOp == kAdd )
+ {
+ // Copy the old wrinkle data
+ CDmAttribute *pWrinkleDeltaAttr = pDelta->GetVertexData( nWrinkleIndex );
+ if ( pWrinkleDeltaAttr )
+ {
+ CDmrArrayConst< float > wrinkleDeltaArray( pWrinkleDeltaAttr );
+ if ( wrinkleDeltaArray.Count() )
+ {
+ const CUtlVector< int > &wrinkleDeltaIndices = pDelta->GetVertexIndexData( nWrinkleIndex );
+ Assert( wrinkleDeltaIndices.Count() == wrinkleDeltaArray.Count() );
+
+ pOldWrinkleData = reinterpret_cast< float * >( alloca( nBaseTexCoordCount * sizeof( float ) ) );
+ memset( pOldWrinkleData, 0, nBaseTexCoordCount * sizeof( float ) );
+
+ for ( int i = 0; i < wrinkleDeltaIndices.Count(); ++i )
+ {
+ if ( i < nPosCount )
+ {
+ *( pOldWrinkleData + wrinkleDeltaIndices[i]) = wrinkleDeltaArray[ i ];
+ }
+ }
+ }
+ }
+ }
+
+ pDelta->RemoveAllVertexData( nWrinkleIndex );
+ if ( flScale == 0.0f && wrinkleOp != kAdd )
+ return true;
+
+ float flMaxDeflection = 0.0f;
+ int *pWrinkleIndices = reinterpret_cast< int * >( alloca( nPosCount * sizeof( int ) ) );
+ float *pWrinkleDelta = reinterpret_cast< float * >( alloca( nPosCount * sizeof( float ) ) );
+ int nWrinkleCount = 0;
+
+ float flDelta;
+ Vector v;
+
+ if ( pOldWrinkleData )
+ {
+ for ( int i = 0; i < nPosCount; ++i )
+ {
+ v = bindPos[ i ] - currPos[ i ];
+
+ // Figure out the texture indices for this position index
+ const CUtlVector< int > &baseVerts = pBind->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, i );
+
+ for ( int j = 0; j < baseVerts.Count(); ++j )
+ {
+ // See if we have a delta for this texcoord...
+ const int nTexCoordIndex = baseTexCoordIndices[ baseVerts[ j ] ];
+
+ if ( fabs( pOldWrinkleData[ nTexCoordIndex ] ) > 0.0001 || fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
+ {
+ flDelta = v.Length();
+ if ( flDelta > flMaxDeflection )
+ {
+ flMaxDeflection = flDelta;
+ }
+ pWrinkleDelta[ nWrinkleCount ] = flDelta;
+ pWrinkleIndices[ nWrinkleCount ] = i;
+ ++nWrinkleCount;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < nPosCount; ++i )
+ {
+ v = bindPos[ i ] - currPos[ i ];
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
+ {
+ flDelta = v.Length();
+ if ( flDelta > flMaxDeflection )
+ {
+ flMaxDeflection = flDelta;
+ }
+ pWrinkleDelta[ nWrinkleCount ] = flDelta;
+ pWrinkleIndices[ nWrinkleCount ] = i;
+ ++nWrinkleCount;
+ }
+ }
+ }
+
+ if ( flMaxDeflection == 0.0f )
+ return true;
+
+ const double scaledInverseMaxDeflection = static_cast< double >( flScale ) / static_cast< double >( flMaxDeflection );
+
+ const int nBufSize = ( ( nBaseTexCoordCount + 7 ) >> 3 );
+ unsigned char * const pUsedBits = reinterpret_cast< unsigned char* >( alloca( nBufSize * sizeof( unsigned char ) ) );
+ memset( pUsedBits, 0, nBufSize );
+
+ for ( int i = 0; i < nWrinkleCount; ++i )
+ {
+ float flWrinkleDelta = static_cast< float >( static_cast< double >( pWrinkleDelta[ i ] ) * scaledInverseMaxDeflection );
+
+ Assert( fabs( flWrinkleDelta ) <= fabs( flScale ) );
+
+ // NOTE: This will produce bad behavior in cases where two positions share the
+ // same texcoord, which shouldn't theoretically happen.
+ const CUtlVector< int > &baseVerts = pBind->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, pWrinkleIndices[ i ] );
+ const int nBaseVertCount = baseVerts.Count();
+ for ( int j = 0; j < nBaseVertCount; ++j )
+ {
+ // See if we have a delta for this texcoord...
+ int nTexCoordIndex = baseTexCoordIndices[ baseVerts[j] ];
+ if ( pUsedBits[ nTexCoordIndex >> 3 ] & ( 1 << ( nTexCoordIndex & 0x7 ) ) )
+ continue;
+
+ pUsedBits[ nTexCoordIndex >> 3 ] |= 1 << ( nTexCoordIndex & 0x7 );
+
+ if ( pOldWrinkleData )
+ {
+ flWrinkleDelta += pOldWrinkleData[ nTexCoordIndex ];
+ }
+
+ int nDeltaIndex = pDelta->AddVertexData( nWrinkleIndex, 1 );
+ pDelta->SetVertexIndices( nWrinkleIndex, nDeltaIndex, 1, &nTexCoordIndex );
+ pDelta->SetVertexData( nWrinkleIndex, nDeltaIndex, 1, AT_FLOAT, &flWrinkleDelta );
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmMeshFaceIt::CDmMeshFaceIt( const CDmeMesh *pMesh, const CDmeVertexData *pVertexData /* = NULL */ )
+{
+ Reset( pMesh, pVertexData );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshFaceIt::Reset( const CDmeMesh *pMesh, const CDmeVertexData *pVertexData /* = NULL */ )
+{
+ m_nFaceIndex = 0;
+
+ if ( pMesh )
+ {
+ m_pMesh = pMesh;
+ m_pVertexData = pVertexData ? pVertexData : m_pVertexData ? m_pVertexData : m_pMesh->GetCurrentBaseState();
+
+ m_nFaceSetCount = 0;
+ m_nFaceSetIndex = 0;
+
+ m_pFaceSet = NULL;
+
+ m_nFaceSetIndexCount = 0;
+ m_nFaceSetIndexIndex = 0;
+
+ m_nFaceCount = 0;
+
+ // Get number of face sets in current mesh
+ m_nFaceSetCount = m_pMesh->FaceSetCount();
+ if ( m_nFaceSetCount <= 0 )
+ return false;
+
+ // Get number of faces in current mesh
+ for ( m_nFaceSetIndex = 0; m_nFaceSetIndex < m_nFaceSetCount; ++m_nFaceSetIndex )
+ {
+ const CDmeFaceSet *pFaceSet = m_pMesh->GetFaceSet( m_nFaceSetIndex );
+ m_nFaceCount += pFaceSet->GetFaceCount();
+ }
+ }
+ else if ( !m_pMesh )
+ {
+ return false;
+ }
+
+ // Set indices to point to first index of first face of first face set, accounting for
+ // NULL face sets and NULL faces
+ for ( m_nFaceSetIndex = 0; m_nFaceSetIndex < m_nFaceSetCount; ++m_nFaceSetIndex )
+ {
+ if ( SetFaceSet() )
+ return true;
+ }
+
+ // All face sets were empty or full of nothing but -1's
+ Assert( m_nFaceSetIndex == m_nFaceSetCount );
+ Assert( m_nFaceCount == 0 );
+
+ m_pFaceSet = NULL;
+
+ m_nFaceSetIndexCount = 0;
+ m_nFaceSetIndexIndex = 0;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmMeshFaceIt::Count() const
+{
+ return m_nFaceCount;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmMeshFaceIt::VertexCount() const
+{
+ if ( IsDone() )
+ return 0;
+
+ return m_pFaceSet->GetNextPolygonVertexCount( m_nFaceSetIndexIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshFaceIt::IsDone() const
+{
+ if ( m_nFaceIndex < m_nFaceCount )
+ {
+ Assert( m_nFaceSetIndex < m_nFaceSetCount );
+ Assert( m_nFaceSetIndexIndex < m_nFaceSetIndexCount );
+ }
+ else
+ {
+ Assert( m_nFaceSetIndex >= m_nFaceSetCount );
+ Assert( m_nFaceSetIndexIndex >= m_nFaceSetIndexCount );
+ }
+
+ return m_nFaceIndex >= m_nFaceCount;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshFaceIt::Next()
+{
+ // Set indices to point to first index of first face of first face set, accounting for
+ // NULL face sets and NULL faces
+
+ while ( m_nFaceSetIndex < m_nFaceSetCount )
+ {
+ // Skip to next -1 face delimiter
+ while ( m_nFaceSetIndexIndex < m_nFaceSetIndexCount )
+ {
+ if ( m_pFaceSet->GetIndex( m_nFaceSetIndexIndex ) >= 0 )
+ break;
+
+ ++m_nFaceSetIndexIndex;
+ }
+
+ // Skip to next face index
+ while ( m_nFaceSetIndexIndex < m_nFaceSetIndexCount )
+ {
+ if ( m_pFaceSet->GetIndex( m_nFaceSetIndexIndex ) < 0 )
+ break;
+
+ ++m_nFaceSetIndexIndex;
+ }
+
+ if ( m_nFaceSetIndexIndex < m_nFaceSetIndexCount )
+ {
+ ++m_nFaceIndex;
+ Assert( m_nFaceIndex < m_nFaceCount );
+ return true;
+ }
+
+ // Must increment the face set
+ ++m_nFaceSetIndex;
+ SetFaceSet();
+ }
+
+ // At the end of the iteration
+ Assert( IsDone() );
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshFaceIt::SetFaceSet()
+{
+ if ( !m_pMesh )
+ {
+ m_pFaceSet = NULL;
+ m_nFaceSetIndexCount = 0;
+ m_nFaceSetIndexIndex = 0;
+
+ return false;
+ }
+
+ if ( m_nFaceSetIndex >= m_nFaceSetCount )
+ {
+ m_pFaceSet = NULL;
+ m_nFaceSetIndexCount = 0;
+ m_nFaceSetIndexIndex = 0;
+
+ return false;
+ }
+
+ m_pFaceSet = m_pMesh->GetFaceSet( m_nFaceSetIndex );
+ m_nFaceSetIndexCount = m_pFaceSet->NumIndices();
+ m_nFaceSetIndexIndex = 0;
+
+ // Skip to the first valid face index
+ for ( m_nFaceSetIndexIndex = 0; m_nFaceSetIndexIndex < m_nFaceSetIndexCount; ++m_nFaceSetIndexIndex )
+ {
+ if ( m_pFaceSet->GetIndex( m_nFaceSetIndex ) >= 0 )
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshFaceIt::GetVertexIndices( int *pIndices, int nIndices ) const
+{
+ if ( IsDone() || nIndices != VertexCount() )
+ {
+ memset( pIndices, 0, nIndices * sizeof( int ) );
+ return false;
+ }
+
+ int vertexIndex;
+
+ for ( int i = m_nFaceSetIndexIndex; i < m_nFaceSetIndexCount; ++i )
+ {
+ vertexIndex = m_pFaceSet->GetIndex( i );
+ if ( vertexIndex < 0 )
+ {
+ Assert( i == m_nFaceSetIndexIndex + VertexCount() );
+ return true;
+ }
+
+ Assert( i < m_nFaceSetIndexIndex + VertexCount() );
+
+ *pIndices = vertexIndex;
+ ++pIndices;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmMeshFaceIt::GetVertexIndices( CUtlVector< int > &vertexIndices ) const
+{
+ vertexIndices.SetCount( VertexCount() );
+
+ if ( IsDone() )
+ {
+ memset( vertexIndices.Base(), 0, vertexIndices.Count() * sizeof( int ) );
+ return false;
+ }
+
+ return GetVertexIndices( vertexIndices.Base(), vertexIndices.Count() );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmMeshFaceIt::GetVertexIndex( int nFaceRelativeVertexIndex ) const
+{
+ if ( IsDone() )
+ return -1;
+
+ const int nVertexCount = VertexCount();
+ if ( nVertexCount <= 0 || nFaceRelativeVertexIndex < 0 || nFaceRelativeVertexIndex >= nVertexCount )
+ return -1;
+
+ int *pVertexIndices = reinterpret_cast< int * >( alloca( nVertexCount * sizeof( int ) ) );
+ if ( !GetVertexIndices( pVertexIndices, nVertexCount ) )
+ return -1;
+
+ return pVertexIndices[ nFaceRelativeVertexIndex ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Copied from dmeanimationset.cpp, remove this function
+// after further integrations
+//-----------------------------------------------------------------------------
+ControlIndex_t FindComboOpControlIndexForAnimSetControl( CDmeCombinationOperator *pComboOp, const char *pControlName, bool *pIsMulti /*= NULL*/ )
+{
+ const char *pMultiControlBaseName = pControlName ? StringAfterPrefix( pControlName, "multi_" ) : NULL;
+ if ( pIsMulti )
+ {
+ *pIsMulti = pMultiControlBaseName != NULL;
+ }
+
+ if ( !pComboOp || !pControlName )
+ return -1;
+
+ ControlIndex_t index = pComboOp->FindControlIndex( pControlName );
+ if ( index >= 0 )
+ return index;
+
+ if ( !pMultiControlBaseName )
+ return -1;
+
+ index = pComboOp->FindControlIndex( pMultiControlBaseName );
+ if ( index < 0 )
+ return -1;
+
+ Assert( pComboOp->IsMultiControl( index ) );
+
+ return index;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmMeshUtils::CreateDeltasFromPresetGroup(
+ CDmePresetGroup *pPresetGroup,
+ CDmeCombinationOperator * pComboOp,
+ const CUtlVector< CUtlString > *pPurgeAllButThese,
+ CDmeMesh *pMesh,
+ CDmeVertexData *pDst,
+ CUtlStringMap< CUtlString > &conflictingNames,
+ CUtlStringMap< CDmePreset * > &presetMap )
+{
+ const CDmaElementArray< CDmePreset > &presets = pPresetGroup->GetPresets();
+ const int nPresetsCount = presets.Count();
+
+ if ( nPresetsCount <= 0 )
+ return;
+
+ for ( int i = 0; i < nPresetsCount; ++i )
+ {
+ pComboOp->SetToBase();
+ CDmePreset *pPreset = presets[ i ];
+
+ CDmaElementArray< CDmElement > &controlValues = pPreset->GetControlValues();
+ const int nControlValues = controlValues.Count();
+ for ( int j = 0; j < nControlValues; ++j )
+ {
+ CDmElement *pControlPreset = controlValues[ j ];
+
+ const ControlIndex_t nControlIndex = pComboOp->FindControlIndex( pControlPreset->GetName() );
+ if ( nControlIndex < 0 )
+ continue;
+
+ bool bSkip = false;
+
+ if ( pPurgeAllButThese )
+ {
+ for ( int k = 0; k < pPurgeAllButThese->Count(); ++k )
+ {
+ if ( !Q_strcmp( pControlPreset->GetName(), pPurgeAllButThese->Element( k ).Get() ) )
+ {
+ bSkip = true;
+ }
+ }
+ }
+
+ if ( bSkip )
+ continue;
+
+ if ( pComboOp->IsStereoControl( nControlIndex ) )
+ {
+ pComboOp->SetControlValue(
+ nControlIndex,
+ pControlPreset->GetValue< float >( "value", 0.0 ),
+ pControlPreset->GetValue< float >( "balance", 0.5 ) );
+ }
+ else
+ {
+ pComboOp->SetControlValue(
+ nControlIndex,
+ pControlPreset->GetValue< float >( "value", 0.0 ) );
+ }
+
+ if ( pComboOp->IsMultiControl( nControlIndex ) )
+ {
+ pComboOp->SetMultiControlLevel(
+ nControlIndex,
+ pControlPreset->GetValue< float >( "multilevel", 0.5 ) );
+ }
+ }
+
+ // Pass the control data from the DmeCombinationOperator into the mesh
+ pComboOp->Resolve();
+ pComboOp->Operate();
+
+ pMesh->Resolve();
+ pMesh->SetBaseStateToDeltas( pDst );
+
+ CUtlString presetName = pPreset->GetName();
+
+ // Look for any conflicting pre-existing names
+ for ( int presetSuffix = 1; pComboOp->FindControlIndex( presetName ) >= 0 || pMesh->FindDeltaState( presetName ) != NULL || conflictingNames.Defined( presetName ) || presetMap.Defined( presetName ); ++presetSuffix )
+ {
+ presetName = pPreset->GetName();
+ presetName += presetSuffix;
+ }
+
+ if ( Q_strcmp( pPreset->GetName(), presetName ) )
+ {
+ // Had to rename preset... save name for later renaming back
+ conflictingNames[ presetName ] = pPreset->GetName();
+ }
+
+ presetMap[ presetName ] = pPreset;
+
+ pMesh->ModifyOrCreateDeltaStateFromBaseState( presetName, pDst, true );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmMeshUtils::PurgeUnreferencedDeltas( CDmeMesh *pMesh, CUtlStringMap< CDmePreset * > &presetMap, const CUtlVector< CUtlString > *pPurgeAllButThese, CDmeCombinationOperator *pComboOp )
+{
+ // Loop because deleting changes indexing
+ bool bDeleted = false;
+ do
+ {
+ bDeleted = false;
+ for ( int i = 0; i < pMesh->DeltaStateCount(); ++i )
+ {
+ const char *pDeltaStateName = pMesh->GetDeltaState( i )->GetName();
+
+ if ( presetMap.Defined( pDeltaStateName ) )
+ continue;
+
+ bool bDelete = true;
+
+ if ( pPurgeAllButThese )
+ {
+ for ( int j = 0; j < pPurgeAllButThese->Count(); ++j )
+ {
+ if ( !Q_strcmp( pDeltaStateName, pPurgeAllButThese->Element( j ).Get() ) )
+ {
+ bDelete = false;
+ break;
+ }
+
+ const ControlIndex_t nControlIndex = pComboOp->FindControlIndex( pPurgeAllButThese->Element( j ) );
+ if ( nControlIndex < 0 )
+ continue;
+
+ for ( int k = 0; k < pComboOp->GetRawControlCount( nControlIndex ); ++k )
+ {
+ if ( !Q_strcmp( pDeltaStateName, pComboOp->GetRawControlName( nControlIndex, k ) ) )
+ {
+ bDelete = false;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( bDelete )
+ {
+ pMesh->DeleteDeltaState( pDeltaStateName );
+ bDeleted = true;
+ break;
+ }
+ }
+ } while( bDeleted );
+
+ // Loop because deleting changes indexing
+ do
+ {
+ bDeleted = false;
+ for ( int i = 0; i < pComboOp->GetControlCount(); ++i )
+ {
+ const char *pControlName = pComboOp->GetControlName( i );
+
+ if ( presetMap.Defined( pControlName ) )
+ continue;
+
+ bool bDelete = true;
+
+ if ( pPurgeAllButThese )
+ {
+ for ( int j = 0; j < pPurgeAllButThese->Count(); ++j )
+ {
+ if ( !Q_strcmp( pControlName, pPurgeAllButThese->Element( j ) ) )
+ {
+ bDelete = false;
+ break;
+ }
+ }
+ }
+
+ if ( bDelete )
+ {
+ pComboOp->RemoveControl( pControlName );
+ bDeleted = true;
+ break;
+ }
+ }
+ } while( bDeleted );
+
+ // Rename any that can be renamed... which should be all of them
+ for ( int i = 0; i < presetMap.GetNumStrings(); ++i )
+ {
+ const char *pPresetName = presetMap.String( i );
+ CDmePreset *pPreset = presetMap[ i ];
+
+ if ( Q_strcmp( pPreset->GetName(), pPresetName ) )
+ {
+ const ControlIndex_t nOrigIndex = pComboOp->FindControlIndex( pPreset->GetName() );
+ const ControlIndex_t nRenamedIndex = pComboOp->FindControlIndex( pPresetName );
+ CDmeVertexDeltaData *pOrigDelta = pMesh->FindDeltaState( pPreset->GetName() );
+ CDmeVertexDeltaData *pRenamedDelta = pMesh->FindDeltaState( pPresetName );
+
+ if ( nOrigIndex < 0 && nRenamedIndex >= 0 && pOrigDelta == NULL && pRenamedDelta != NULL )
+ {
+ pComboOp->RemoveControl( pPresetName );
+ pRenamedDelta->SetName( pPreset->GetName() );
+ }
+ }
+ }
+}
diff --git a/movieobjects/dmobjserializer.cpp b/movieobjects/dmobjserializer.cpp
new file mode 100644
index 0000000..e8c3a83
--- /dev/null
+++ b/movieobjects/dmobjserializer.cpp
@@ -0,0 +1,1464 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Serialize and Unserialize Wavefront OBJ <-> DME Data
+//
+//=============================================================================
+
+
+// Valve includes
+#include "tier1/characterset.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmemesh.h"
+#include "movieobjects/dmefaceset.h"
+#include "movieobjects/dmematerial.h"
+#include "movieobjects/dmobjserializer.h"
+#include "movieobjects/dmecombinationoperator.h"
+#include "movieobjects/dmemodel.h"
+#include "filesystem.h"
+#include "tier2/tier2.h"
+#include "tier1/UtlStringMap.h"
+#include "mathlib/mathlib.h"
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+class CFaceSetData
+{
+public:
+ void Clear();
+
+ inline CUtlVector< int > *GetFaceSetIndices( const char *pFaceSetName )
+ {
+ return &m_faceSetIndices[ pFaceSetName ];
+ }
+
+ void AddToMesh( CDmeMesh *pMesh );
+protected:
+
+ CUtlStringMap< CUtlVector< int > > m_faceSetIndices;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CFaceSetData::Clear()
+{
+ m_faceSetIndices.Clear();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CFaceSetData::AddToMesh( CDmeMesh *pMesh )
+{
+ const int nFaceSets( m_faceSetIndices.GetNumStrings() );
+ for ( int i( 0 ); i < nFaceSets; ++i )
+ {
+ const char *pName( m_faceSetIndices.String( i ) );
+ CUtlVector< int > &faceSetIndices( m_faceSetIndices[ pName ] );
+
+ if ( faceSetIndices.Count() )
+ {
+ CDmeFaceSet *pFaceSet = CreateElement< CDmeFaceSet >( pName, pMesh->GetFileId() );
+
+ CDmeMaterial *pMaterial = CreateElement< CDmeMaterial >( pName, pMesh->GetFileId() );
+ pMaterial->SetMaterial( pName );
+
+ pFaceSet->AddIndices( faceSetIndices.Count() );
+ pFaceSet->SetIndices( 0, faceSetIndices.Count(), faceSetIndices.Base() );
+
+ pFaceSet->SetMaterial( pMaterial );
+
+ pMesh->AddFaceSet( pFaceSet );
+ }
+ }
+
+ Clear();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+class CVertexData
+{
+public:
+ void Clear();
+
+ inline void AddPosition( const Vector &p ) { m_positions.AddToTail( p ); }
+
+ inline void AddPositionIndex( int i ) { m_pIndices.AddToTail( i ); }
+
+ inline void AddNormal( const Vector &n ) { m_normals.AddToTail( n ); }
+
+ inline void AddNormalIndex( int i ) { m_nIndices.AddToTail( i ); }
+
+ inline void AddUV( const Vector2D &uv ) { AddUniqueValue( uv, m_uvs, m_uvIndexMap, FLT_EPSILON * 0.1f ); }
+
+ inline void AddUVIndex( int i ) { Assert( i < m_uvIndexMap.Count() ); m_uvIndices.AddToTail( m_uvIndexMap[ i ] ); }
+
+ inline int VertexCount() const { return m_pIndices.Count(); }
+
+ CDmeVertexDataBase *AddToMesh( CDmeMesh *pMesh, bool bAbsolute, const char *pName, bool bDelta );
+
+protected:
+ template < class T_t > void AddUniqueValue(
+ const T_t &v,
+ CUtlVector< T_t > &vs,
+ CUtlVector< int > &map,
+ float flThresh = FLT_EPSILON )
+ {
+ const int nVs( vs.Count() );
+ for ( int i( 0 ); i < nVs; ++i )
+ {
+ if ( v.DistToSqr( vs[ i ] ) < flThresh )
+ {
+ map.AddToTail( i );
+ return;
+ }
+ }
+
+ map.AddToTail( vs.Count() );
+ vs.AddToTail( v );
+ }
+
+ CDmeVertexDataBase *Add( CDmeMesh *pMesh, const char *pName = "bind" );
+
+ CDmeVertexDeltaData *AddDelta( CDmeMesh *pMesh, bool bAbsolute, const char *pName = "bind" );
+
+ CUtlVector< Vector > m_positions;
+ CUtlVector< int > m_pIndices;
+
+ CUtlVector< Vector > m_normals;
+ CUtlVector< int > m_nIndices;
+
+ CUtlVector< Vector2D > m_uvs;
+ CUtlVector< int > m_uvIndexMap;
+ CUtlVector< int > m_uvIndices;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CVertexData::Clear()
+{
+ m_positions.RemoveAll();
+ m_pIndices.RemoveAll();
+
+ m_normals.RemoveAll();
+ m_nIndices.RemoveAll();
+
+ m_uvs.RemoveAll();
+ m_uvIndexMap.RemoveAll();
+ m_uvIndices.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDataBase *CVertexData::AddToMesh( CDmeMesh *pMesh, bool bAbsolute, const char *pName, bool delta )
+{
+ if ( delta )
+ return AddDelta( pMesh, bAbsolute, pName );
+
+ return Add( pMesh, pName );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDataBase *CVertexData::Add( CDmeMesh *pMesh, const char *pName )
+{
+ CDmeVertexDataBase *pVertexData( NULL );
+
+ if ( m_positions.Count() && m_pIndices.Count() )
+ {
+ {
+ CDmeVertexData *pBaseVertexData = pMesh->FindOrCreateBaseState( pName );
+ pMesh->SetCurrentBaseState( pName );
+ pBaseVertexData->AddVertexIndices( m_pIndices.Count() );
+ pVertexData = pBaseVertexData;
+ }
+
+ pVertexData->FlipVCoordinate( true );
+
+ const FieldIndex_t pIndex( pVertexData->CreateField( CDmeVertexData::FIELD_POSITION ) );
+ pVertexData->AddVertexData( pIndex, m_positions.Count() );
+ pVertexData->SetVertexData( pIndex, 0, m_positions.Count(), AT_VECTOR3, m_positions.Base() );
+ pVertexData->SetVertexIndices( pIndex, 0, m_pIndices.Count(), m_pIndices.Base() );
+
+ if ( pVertexData && m_normals.Count() && m_nIndices.Count() )
+ {
+ Assert( m_pIndices.Count() == m_nIndices.Count() );
+
+ const FieldIndex_t nIndex( pVertexData->CreateField( CDmeVertexData::FIELD_NORMAL ) );
+ pVertexData->AddVertexData( nIndex, m_normals.Count() );
+ pVertexData->SetVertexData( nIndex, 0, m_normals.Count(), AT_VECTOR3, m_normals.Base() );
+ pVertexData->SetVertexIndices( nIndex, 0, m_nIndices.Count(), m_nIndices.Base() );
+ }
+
+ if ( pVertexData && m_uvs.Count() && m_uvIndices.Count() )
+ {
+ Assert( m_pIndices.Count() == m_uvIndices.Count() );
+
+ const FieldIndex_t uvIndex( pVertexData->CreateField( CDmeVertexData::FIELD_TEXCOORD ) );
+ pVertexData->AddVertexData( uvIndex, m_uvs.Count() );
+ pVertexData->SetVertexData( uvIndex, 0, m_uvs.Count(), AT_VECTOR2, m_uvs.Base() );
+ pVertexData->SetVertexIndices( uvIndex, 0, m_uvIndices.Count(), m_uvIndices.Base() );
+ }
+ }
+
+ return pVertexData;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CVertexData::AddDelta( CDmeMesh *pMesh, bool bAbsolute, const char *pName )
+{
+ CDmeVertexDeltaData *pDelta( NULL );
+
+ if ( m_positions.Count() )
+ {
+ CDmeVertexData *pBind = pMesh->FindBaseState( "bind" );
+ if ( pBind == NULL )
+ return NULL;
+
+ const FieldIndex_t pBindIndex( pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+
+ if ( pBindIndex < 0 )
+ return NULL;
+
+ CDmrArrayConst< Vector > pBindData( pBind->GetVertexData( pBindIndex ) );
+
+ const int pCount( m_positions.Count() );
+ if ( pBindData.Count() != pCount )
+ return NULL;
+
+ for ( int i( 0 ); i < pCount; ++i )
+ {
+ m_positions[ i ] -= pBindData[ i ];
+ }
+
+ int *pIndices = reinterpret_cast< int * >( alloca( pCount * sizeof( int ) ) );
+ int nNonZero( 0 );
+ for ( int i( 0 ); i < pCount; ++i )
+ {
+ const Vector &v( m_positions[ i ] );
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( v.x ) >= ( 1 / 4096.0f ) || fabs( v.y ) >= ( 1 / 4096.0f ) || fabs( v.z ) >= ( 1 / 4096.0f ) )
+ {
+ m_positions[ nNonZero ] = v;
+ pIndices[ nNonZero ] = i;
+ ++nNonZero;
+ }
+ }
+
+ pDelta = pMesh->FindOrCreateDeltaState( pName );
+ pDelta->FlipVCoordinate( true );
+ pDelta->SetValue( "corrected", !bAbsolute );
+
+ const FieldIndex_t pIndex( pDelta->CreateField( CDmeVertexData::FIELD_POSITION ) );
+ pDelta->AddVertexData( pIndex, nNonZero );
+ pDelta->SetVertexData( pIndex, 0, nNonZero, AT_VECTOR3, m_positions.Base() );
+ pDelta->SetVertexIndices( pIndex, 0, nNonZero, pIndices );
+
+ const FieldIndex_t nBindNormalIndex = pBind->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ if ( nBindNormalIndex >= 0 )
+ {
+ CDmrArrayConst< Vector > bindNormalData( pBind->GetVertexData( nBindNormalIndex ) );
+ const int nNormalCount = m_normals.Count();
+ if ( bindNormalData.Count() == nNormalCount )
+ {
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ m_normals[ i ] -= bindNormalData[ i ];
+ }
+
+ int *pNormalIndices = reinterpret_cast< int * >( stackalloc( nNormalCount * sizeof( int ) ) );
+ int nNormalDeltaCount = 0;
+ for ( int i = 0; i < nNormalCount; ++i )
+ {
+ const Vector &n = m_normals[ i ];
+ // Kind of a magic number but it's because of 16 bit compression of the delta values
+ if ( fabs( n.x ) >= ( 1 / 4096.0f ) || fabs( n.y ) >= ( 1 / 4096.0f ) || fabs( n.z ) >= ( 1 / 4096.0f ) )
+ {
+ m_normals[ nNormalDeltaCount ] = n;
+ pNormalIndices[ nNormalDeltaCount ] = i;
+ ++nNormalDeltaCount;
+ }
+ }
+ }
+ }
+ }
+
+ return pDelta;
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert from DME -> OBJ
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::Serialize( CUtlBuffer &buf, CDmElement *pRoot )
+{
+ return false; // For now
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert from OBJ -> DME
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::Unserialize( CUtlBuffer &buf, const char *pEncodingName, int nEncodingVersion,
+ const char *pSourceFormatName, int nSourceFormatVersion,
+ DmFileId_t fileid, DmConflictResolution_t idConflictResolution, CDmElement **ppRoot )
+{
+ *ppRoot = ReadOBJ( buf, fileid, "bind" );
+ return *ppRoot != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert from OBJ -> DME
+// If mesh is not NULL, the OBJ is added as a delta state to the mesh
+//-----------------------------------------------------------------------------
+CDmElement *CDmObjSerializer::ReadOBJ(
+ const char *pFilename,
+ CDmeMesh **ppCreatedMesh,
+ bool bLoadAllDeltas /* = true */,
+ bool bAbsolute /* = true */ )
+{
+ char filename[ MAX_PATH ];
+ Q_strncpy( filename, pFilename, sizeof( filename ) );
+ Q_FixSlashes( filename );
+
+ CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ if ( !g_pFullFileSystem->ReadFile( filename, NULL, utlBuf ) )
+ return NULL;
+
+ char baseFile[ MAX_PATH ];
+ Q_FileBase( filename, baseFile, sizeof( baseFile ) );
+
+ CDmeMesh *pMesh( NULL );
+
+ DmFileId_t nFileId = g_pDataModel->FindOrCreateFileId( pFilename );
+
+ CDmElement *pRoot = ReadOBJ( utlBuf, nFileId, baseFile, filename, NULL, &pMesh, bAbsolute );
+ if ( pRoot && pMesh )
+ {
+ if ( ppCreatedMesh )
+ {
+ *ppCreatedMesh = pMesh;
+ }
+
+ CDmeCombinationOperator *pCombo( NULL );
+
+ // Check if there are deltas in the directory with the same prefix
+ // But only if the rest of the file is <prefix>=<suffix>.obj or is <prefix>_zero.obj
+
+ char *pSuffix = Q_strrchr( baseFile, '=' );
+ if ( !pSuffix || !*pSuffix )
+ {
+ pSuffix = Q_strrchr( baseFile, '_' );
+ if ( !pSuffix || !*pSuffix )
+ return pRoot;
+
+ if ( Q_stricmp( pSuffix, "_zero" ) )
+ return pRoot;
+ }
+
+ char findGlob[ MAX_PATH ];
+ Q_strncpy( findGlob, baseFile, sizeof( findGlob ) );
+ pSuffix = findGlob + ( pSuffix - baseFile );
+ *( pSuffix + 0 ) = '_'; // Just in case it was <prefix>=<suffix>.obj
+ *( pSuffix + 1 ) = '*';
+ *( pSuffix + 2 ) = '.';
+ Q_strncpy( pSuffix + 3, Q_GetFileExtension( filename ), sizeof( findGlob ) - ( pSuffix - findGlob + 3 ) );
+
+ char path[ MAX_PATH ];
+ Q_ExtractFilePath( filename, path, sizeof( path ) );
+
+ m_objDirectory = path;
+
+ char findPath[ MAX_PATH ];
+ Q_ComposeFileName( path, findGlob, findPath, sizeof( findPath ) );
+
+ FileFindHandle_t hFind;
+
+ char deltaFile[ MAX_PATH ];
+ char deltaPath[ MAX_PATH ];
+
+ for ( const char *pFindFile( g_pFullFileSystem->FindFirst( findPath, &hFind ) ); pFindFile && *pFindFile; pFindFile = g_pFullFileSystem->FindNext( hFind ) )
+ {
+ Q_FileBase( pFindFile, deltaFile, sizeof( deltaFile ) );
+
+ if ( Q_stricmp( baseFile, deltaFile ) )
+ {
+ Q_ComposeFileName( path, pFindFile, deltaPath, sizeof( deltaPath ) );
+
+ if ( !g_pFullFileSystem->FileExists( deltaPath ) )
+ continue;
+
+ char *pControlName = strchr( deltaFile, '_' );
+ if ( pControlName && *( pControlName + 1 ) )
+ {
+ ++pControlName;
+ char *pDeltaName( pControlName );
+ for ( char *pPlus( strchr( pDeltaName, '+' ) ); pPlus; pPlus = strchr( pPlus, '+' ) )
+ {
+ *pPlus = '_';
+ }
+ }
+
+ if ( !strchr( pControlName, '_' ) )
+ {
+ if ( pCombo == NULL )
+ {
+ pCombo = CreateElement< CDmeCombinationOperator >( "combinationOperator", pRoot->GetFileId() );
+ pRoot->SetValue( "combinationOperator", pCombo );
+ }
+ }
+
+ DeltaInfo_t &deltaInfo = m_deltas[ pControlName ];
+ deltaInfo.m_filename = pFindFile;
+ deltaInfo.m_pMesh = pMesh;
+ deltaInfo.m_pComboOp = pCombo;
+
+ if ( bLoadAllDeltas )
+ {
+ GetDelta( pControlName, bAbsolute );
+ }
+ }
+ }
+
+ g_pFullFileSystem->FindClose( hFind );
+
+ if ( pCombo )
+ {
+ pCombo->AddTarget( pMesh );
+ pMesh->ComputeAllCorrectedPositionsFromActualPositions();
+ }
+ }
+
+ return pRoot;
+}
+
+
+//-----------------------------------------------------------------------------
+// Common function both ReadOBJ & Unserialize can call
+//-----------------------------------------------------------------------------
+CDmElement *CDmObjSerializer::ReadOBJ( CUtlBuffer &buf,
+ DmFileId_t dmFileId,
+ const char *pName,
+ const char *pFilename /* = NULL */,
+ CDmeMesh *pBaseMesh /* = NULL */,
+ CDmeMesh **ppCreatedMesh /* = NULL */,
+ bool bAbsolute /* = true */ )
+{
+ CDmElement *pRoot( NULL );
+ CDmeModel *pModel( NULL );
+
+ if ( !pBaseMesh )
+ {
+ pRoot = CreateElement< CDmElement >( "root", dmFileId );
+ pModel = CreateElement< CDmeModel >( "model", dmFileId );
+
+ pRoot->SetValue( "skeleton", pModel );
+ pRoot->SetValue( "model", pModel );
+ }
+
+ m_mtlLib.RemoveAll();
+
+ char tmpBuf0[ 4096 ];
+ char tmpBuf1[ 4096 ];
+
+ characterset_t breakSet;
+ CharacterSetBuild( &breakSet, "/\\" );
+
+ const char *pBuf;
+
+ Vector p;
+ Vector2D uv;
+
+ CVertexData vertexData;
+ CFaceSetData faceSetData;
+
+ CUtlString groupName;
+
+ CDmeDag *pDmeDag( NULL );
+ CDmeMesh *pDmeMesh( NULL );
+ CUtlVector< int > *pFaceIndices( NULL );
+
+ while ( buf.IsValid() )
+ {
+ buf.GetLine( tmpBuf0, sizeof( tmpBuf0 ) );
+
+ pBuf = SkipSpace( tmpBuf0 );
+
+ if ( sscanf( tmpBuf0, "v %f %f %f", &p.x, &p.y, &p.z ) == 3 )
+ {
+ if ( pDmeDag )
+ {
+ vertexData.AddToMesh( pDmeMesh, bAbsolute, "bind", false );
+ faceSetData.AddToMesh( pDmeMesh );
+
+ pDmeDag = NULL;
+ pDmeMesh = NULL;
+ pFaceIndices = NULL;
+ }
+
+ vertexData.AddPosition( p );
+
+ continue;
+ }
+
+ if ( sscanf( pBuf, "vn %f %f %f", &p.x, &p.y, &p.z ) == 3 )
+ {
+ vertexData.AddNormal( p );
+ continue;
+ }
+
+ if ( !pBaseMesh )
+ {
+ if ( sscanf( pBuf, "vt %f %f", &uv.x, &uv.y ) == 2 )
+ {
+ vertexData.AddUV( uv );
+ continue;
+ }
+
+ if ( pFilename && sscanf( pBuf, "mtllib %4096s", tmpBuf1 ) == 1 )
+ {
+ CUtlString mtlLib( tmpBuf1 );
+
+ Q_strncpy( tmpBuf0, pFilename, sizeof( tmpBuf0 ) );
+ Q_FixSlashes( tmpBuf0 );
+ Q_StripFilename( tmpBuf0 );
+
+ char mtlLibPath[ MAX_PATH ];
+
+ Q_ComposeFileName( tmpBuf0, tmpBuf1, mtlLibPath, sizeof( mtlLibPath ) );
+ CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ if ( g_pFullFileSystem->ReadFile( mtlLibPath, NULL, utlBuf ) )
+ {
+ ParseMtlLib( utlBuf );
+ }
+
+ continue;
+ }
+
+ if ( sscanf( pBuf, "usemtl %4096s", tmpBuf1 ) == 1 )
+ {
+ // Remove any 'SG' suffix from the material
+ const uint sLen = Q_strlen( tmpBuf1 );
+ if ( sLen && !Q_strcmp( tmpBuf1 + sLen - 2, "SG" ) )
+ {
+ tmpBuf1[ sLen - 2 ] = '\0';
+ }
+
+ const char *pTexture( FindMtlEntry( tmpBuf1 ) );
+ if ( pTexture )
+ {
+ pFaceIndices = faceSetData.GetFaceSetIndices( pTexture );
+ }
+ else
+ {
+ pFaceIndices = faceSetData.GetFaceSetIndices( tmpBuf1 );
+ }
+
+ continue;
+ }
+
+ if ( sscanf( pBuf, "g %4096s", tmpBuf1 ) == 1 )
+ {
+ groupName = tmpBuf1;
+ if ( pFaceIndices == NULL )
+ {
+ pFaceIndices = faceSetData.GetFaceSetIndices( tmpBuf1 );
+ }
+ continue;
+ }
+ if ( *pBuf == 'f' && ( *( pBuf + 1 ) == ' ' || *( pBuf + 1 ) == '\t' ) )
+ {
+ if ( pDmeDag == NULL )
+ {
+ pDmeDag = CreateElement< CDmeDag >( pName ? pName : ( groupName.IsEmpty() ? "obj" : groupName.Get() ), pRoot->GetFileId() );
+ Assert( pDmeDag );
+ pDmeMesh = CreateElement< CDmeMesh >( pName ? pName : ( groupName.IsEmpty() ? "obj" : groupName.Get() ), pRoot->GetFileId() );
+ if ( ppCreatedMesh && *ppCreatedMesh == NULL )
+ {
+ // Only the first mesh created...
+ *ppCreatedMesh = pDmeMesh;
+ }
+ pDmeDag->SetShape( pDmeMesh );
+ if ( pModel )
+ {
+ pModel->AddJoint( pDmeDag );
+ pModel->AddChild( pDmeDag );
+ }
+ }
+
+ if ( pFaceIndices == NULL )
+ {
+ pFaceIndices = faceSetData.GetFaceSetIndices( "facetSet" );
+ }
+
+ int v;
+ int t;
+ int n;
+
+ pBuf = SkipSpace( pBuf + 1 );
+ int nLen = Q_strlen( pBuf );
+
+ CUtlBuffer bufParse( pBuf, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+
+ while ( bufParse.IsValid() )
+ {
+ if ( !ParseVertex( bufParse, breakSet, v, t, n ) )
+ break;
+
+ pFaceIndices->AddToTail( vertexData.VertexCount() );
+ if ( v > 0 )
+ {
+ vertexData.AddPositionIndex( v - 1 );
+ }
+
+ if ( n > 0 )
+ {
+ vertexData.AddNormalIndex( n - 1 );
+ }
+
+ if ( t > 0 )
+ {
+ vertexData.AddUVIndex( t - 1 );
+ }
+ }
+
+ pFaceIndices->AddToTail( -1 );
+ continue;
+ }
+ }
+ }
+
+ CDmeVertexDataBase *pVertexData( NULL );
+
+ if ( pBaseMesh )
+ {
+ pVertexData = vertexData.AddToMesh( pBaseMesh, bAbsolute, pName, true );
+ }
+ else
+ {
+ pVertexData = vertexData.AddToMesh( pDmeMesh, bAbsolute, "bind", false );
+ faceSetData.AddToMesh( pDmeMesh );
+ }
+
+ if ( pModel )
+ {
+ pModel->CaptureJointsToBaseState( "bind" );
+ }
+
+ if ( pBaseMesh )
+ return pVertexData;
+
+ return pRoot;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmObjSerializer::OutputVectors(
+ CUtlBuffer &b,
+ const char *pPrefix,
+ const CUtlVector< Vector > &vData,
+ const matrix3x4_t &matrix )
+{
+ Vector v;
+
+ const int nv( vData.Count() );
+
+ for ( int i( 0 ); i < nv; ++i )
+ {
+ VectorTransform( vData[ i ], matrix, v );
+ b << pPrefix << v << "\n";
+ }
+
+ return nv;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CDmObjSerializer::OutputVectors(
+ CUtlBuffer &b,
+ const char *pPrefix,
+ const CUtlVector< Vector2D > &vData )
+{
+ Vector v;
+
+ const int nv( vData.Count() );
+
+ for ( int i( 0 ); i < nv; ++i )
+ {
+ b << pPrefix << vData[ i ] << "\n";
+ }
+
+ return nv;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::MeshToObj(
+ CUtlBuffer &b,
+ const matrix3x4_t &parentWorldMatrix,
+ CDmeMesh *pMesh,
+ const char *pDeltaName,
+ bool absolute )
+{
+ CUtlVector< CDmeMesh::DeltaComputation_t > compList;
+
+ if ( pDeltaName )
+ {
+ pMesh->ComputeDependentDeltaStateList( compList );
+ }
+
+ const int nCompList( compList.Count() );
+
+ CDmeVertexData *pBase( pMesh->FindBaseState( "bind" ) );
+ if ( !pBase )
+ return;
+
+ const FieldIndex_t nPosIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) );
+ if ( nPosIndex < 0 )
+ return;
+
+ int nPositionCount = 0;
+ int nTextureCount = 0;
+ int nNormalCount = 0;
+
+ b << "g " << pMesh->GetName() << "\n";
+
+ CDmrArrayConst< Vector > pArray( pBase->GetVertexData( nPosIndex ) );
+ const CUtlVector< int > *ppIndices( &pBase->GetVertexIndexData( nPosIndex ) );
+ const CUtlVector< Vector > &pConstData( pArray.Get() );
+
+ if ( nCompList )
+ {
+ CUtlVector< Vector > pData;
+ pData.CopyArray( pConstData.Base(), pConstData.Count() );
+
+ if ( absolute )
+ {
+ for ( int i ( 0 ); i < nCompList; ++i )
+ {
+ CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
+ if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
+ continue;
+
+ b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
+
+ const CUtlVector< int > &depDeltas( compList[ i ].m_DependentDeltas );
+ const int nDepDeltas( depDeltas.Count() );
+ for ( int j( 0 ); j < nDepDeltas; ++j )
+ {
+ pTmpDeltaState = pMesh->GetDeltaState( depDeltas[ j ] );
+ b << "# Dependent Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
+ }
+ }
+ }
+ else
+ {
+ for ( int i ( 0 ); i < nCompList; ++i )
+ {
+ CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
+ if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
+ continue;
+
+ b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, pData.Base(), pData.Count(), CDmeVertexData::FIELD_POSITION, 1.0f );
+ }
+ }
+
+ nPositionCount = OutputVectors( b, "v ", pData, parentWorldMatrix );
+ }
+ else
+ {
+ nPositionCount = OutputVectors( b, "v ", pConstData, parentWorldMatrix );
+ }
+
+ const CUtlVector< int > *puvIndices( NULL );
+ const FieldIndex_t uvIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD ) );
+ if ( uvIndex >= 0 )
+ {
+ CDmrArrayConst< Vector2D > uvArray( pBase->GetVertexData( uvIndex ) );
+ const CUtlVector< Vector2D > &uvData( uvArray.Get() );
+ puvIndices = &pBase->GetVertexIndexData( uvIndex );
+
+ nTextureCount = OutputVectors( b, "vt ", uvData );
+ }
+
+ const CUtlVector< int > *pnIndices( NULL );
+ const FieldIndex_t nIndex( pBase->FindFieldIndex( CDmeVertexData::FIELD_NORMAL ) );
+ if ( nIndex >= 0 )
+ {
+ matrix3x4_t normalMatrix;
+ MatrixInverseTranspose( parentWorldMatrix, normalMatrix );
+
+ CDmrArrayConst< Vector > nArray( pBase->GetVertexData( nIndex ) );
+ const CUtlVector< Vector > &nConstData( nArray.Get() );
+ pnIndices = &pBase->GetVertexIndexData( nIndex );
+
+ if ( nCompList )
+ {
+ CUtlVector< Vector > nData;
+ nData.CopyArray( nConstData.Base(), nConstData.Count() );
+
+ if ( absolute )
+ {
+ for ( int i ( 0 ); i < nCompList; ++i )
+ {
+ CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
+ if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
+ continue;
+
+ b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
+
+ const CUtlVector< int > &depDeltas( compList[ i ].m_DependentDeltas );
+ const int nDepDeltas( depDeltas.Count() );
+ for ( int j( 0 ); j < nDepDeltas; ++j )
+ {
+ pTmpDeltaState = pMesh->GetDeltaState( depDeltas[ j ] );
+ b << "# Dependent Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
+ }
+ }
+ }
+ else
+ {
+ for ( int i ( 0 ); i < nCompList; ++i )
+ {
+ CDmeVertexDeltaData *pTmpDeltaState( pMesh->GetDeltaState( compList[ i ].m_nDeltaIndex ) );
+ if ( Q_strcmp( pTmpDeltaState->GetName(), pDeltaName ) )
+ continue;
+
+ b << "# Delta: " << pTmpDeltaState->GetName() << "\n";
+ pMesh->AddDelta( pTmpDeltaState, nData.Base(), nData.Count(), CDmeVertexData::FIELD_NORMAL, 1.0f );
+ }
+ }
+
+ nNormalCount = OutputVectors( b, "vn ", nData, normalMatrix );
+ }
+ else
+ {
+ nNormalCount = OutputVectors( b, "vn ", nConstData, normalMatrix );
+ }
+ }
+
+ const int pCount( ppIndices->Count() );
+ const int uvCount( puvIndices ? puvIndices->Count() : 0 );
+ const int nCount( pnIndices ? pnIndices->Count() : 0 );
+
+ const int nFaceSets( pMesh->FaceSetCount() );
+ for ( int i= 0 ; i < nFaceSets; ++i )
+ {
+ CDmeFaceSet *pFaceSet( pMesh->GetFaceSet( i ) );
+ CDmeMaterial *pMaterial( pFaceSet->GetMaterial() );
+ if ( pMaterial )
+ {
+ b << "usemtl " << pMaterial->GetMaterialName() << "\n";
+ }
+
+ const int nIndices( pFaceSet->NumIndices() );
+ const int *pnFaceSetIndex( pFaceSet->GetIndices() );
+ const int *const pEnd( pnFaceSetIndex + nIndices );
+ int fIndex;
+
+ const char *const pFaceStart( "f " );
+ const char *const pFaceNext( " " );
+ const char *pFaceSep( pFaceStart );
+
+ if ( pCount == uvCount && pCount == nCount )
+ {
+ const CUtlVector< int > &pIndices( *ppIndices );
+ const CUtlVector< int > &uvIndices( *puvIndices );
+ const CUtlVector< int > &nvIndices( *pnIndices );
+
+ while ( pnFaceSetIndex < pEnd )
+ {
+ fIndex = *pnFaceSetIndex++;
+ if ( fIndex < 0 )
+ {
+ b << "\n";
+ pFaceSep = pFaceStart;
+ continue;
+ }
+
+ b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << '/' << ( uvIndices[ fIndex ] + m_nTextureOffset ) << '/' << ( nvIndices[ fIndex ] + m_nNormalOffset );
+ pFaceSep = pFaceNext;
+ }
+ }
+ else if ( pCount == uvCount )
+ {
+ const CUtlVector< int > &pIndices( *ppIndices );
+ const CUtlVector< int > &uvIndices( *puvIndices );
+
+ while ( pnFaceSetIndex < pEnd )
+ {
+ fIndex = *pnFaceSetIndex++;
+ if ( fIndex < 0 )
+ {
+ b << "\n";
+ pFaceSep = pFaceStart;
+ continue;
+ }
+
+ b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << '/' << ( uvIndices[ fIndex ] + m_nTextureOffset ) << '/';
+ pFaceSep = pFaceNext;
+ }
+ }
+ else if ( pCount == nCount )
+ {
+ const CUtlVector< int > &pIndices( *ppIndices );
+ const CUtlVector< int > &nvIndices( *pnIndices );
+
+ while ( pnFaceSetIndex < pEnd )
+ {
+ fIndex = *pnFaceSetIndex++;
+ if ( fIndex < 0 )
+ {
+ b << "\n";
+ pFaceSep = pFaceStart;
+ continue;
+ }
+
+ b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << "//" << ( nvIndices[ fIndex ] + m_nNormalOffset );
+ pFaceSep = pFaceNext;
+ }
+ }
+ else
+ {
+ const CUtlVector< int > &pIndices( *ppIndices );
+
+ while ( pnFaceSetIndex < pEnd )
+ {
+ fIndex = *pnFaceSetIndex++;
+ if ( fIndex < 0 )
+ {
+ b << "\n";
+ pFaceSep = pFaceStart;
+ continue;
+ }
+
+ b << pFaceSep << ( pIndices[ fIndex ] + m_nPositionOffset ) << "//";
+ pFaceSep = pFaceNext;
+ }
+ }
+ }
+
+ m_nPositionOffset += nPositionCount;
+ m_nTextureOffset += nTextureCount;
+ m_nNormalOffset += nNormalCount;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::DagToObj(
+ CUtlBuffer &b,
+ const matrix3x4_t &parentWorldMatrix,
+ CDmeDag *pDag,
+ const char *pDeltaName,
+ bool absolute )
+{
+ matrix3x4_t inclusiveMatrix;
+ pDag->GetTransform()->GetTransform( inclusiveMatrix );
+
+ ConcatTransforms( parentWorldMatrix, inclusiveMatrix, inclusiveMatrix );
+
+ CDmeMesh *pMesh( CastElement< CDmeMesh >( pDag->GetShape() ) );
+ if ( pMesh )
+ {
+ MeshToObj( b, inclusiveMatrix, pMesh, pDeltaName, absolute );
+ }
+
+ const int nChildren( pDag->GetChildCount() );
+ for ( int i( 0 ); i < nChildren; ++i )
+ {
+ DagToObj( b, inclusiveMatrix, pDag->GetChild( i ), pDeltaName, absolute );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::FindDeltaMeshes( CDmeDag *pDag, CUtlVector< CDmeMesh * > &meshes )
+{
+ CDmeMesh *pMesh( CastElement< CDmeMesh >( pDag->GetShape() ) );
+ if ( pMesh && pMesh->DeltaStateCount() )
+ {
+ meshes.AddToTail( pMesh );
+ }
+
+ const int nChildren( pDag->GetChildCount() );
+ for ( int i( 0 ); i < nChildren; ++i )
+ {
+ FindDeltaMeshes( pDag->GetChild( i ), meshes );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert from OBJ -> DME
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::WriteOBJ( const char *pFilename, CDmElement *pRoot, bool bWriteOBJs, const char *pDeltaName, bool absolute )
+{
+ CDmeDag *pModel = pRoot->GetValueElement< CDmeDag >( "model" );
+
+ if ( !pModel )
+ return false;
+
+ matrix3x4_t identityMatrix;
+ SetIdentityMatrix( identityMatrix );
+
+ if ( !pDeltaName )
+ {
+ CUtlBuffer b( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ b << "# OBJ\n";
+ b << "#\n";
+
+ m_nPositionOffset = 1; // OBJs start indexing at 1
+ m_nTextureOffset = 1;
+ m_nNormalOffset = 1;
+
+ DagToObj( b, identityMatrix, pModel, pDeltaName, absolute );
+
+ g_pFullFileSystem->WriteFile( pFilename, NULL, b );
+
+ // Filesystem is silly
+ // On WIN32 filesystem changes all of the characters to lowercase grrrr.....
+ rename( pFilename, pFilename );
+ }
+
+ if ( !bWriteOBJs )
+ return true;
+
+ CUtlVector< CDmeMesh * > deltaMeshes;
+ FindDeltaMeshes( pModel, deltaMeshes );
+
+ if ( deltaMeshes.Count() )
+ {
+ char base[ MAX_PATH ];
+ Q_FileBase( pFilename, base, sizeof( base ) );
+
+ char path[ MAX_PATH ];
+ Q_ExtractFilePath( pFilename, path, sizeof( path ) );
+
+ char *pSuffix = strchr( base, '=' );
+ if ( !pSuffix )
+ {
+ pSuffix = strchr( base, '_' );
+ }
+
+ if ( pSuffix )
+ {
+ *( pSuffix + 0 ) = '_';
+ *( pSuffix + 1 ) = '\0';
+ }
+
+ char filename[ MAX_PATH ];
+
+ const int nDeltaMeshes( deltaMeshes.Count() );
+ for ( int i( 0 ); i < nDeltaMeshes; ++i )
+ {
+ CDmeMesh *pDeltaMesh( deltaMeshes[ i ] );
+ const int nDeltas( pDeltaMesh->DeltaStateCount() );
+ for ( int j( 0 ); j < nDeltas; ++j )
+ {
+ CDmeVertexDeltaData *pDelta( pDeltaMesh->GetDeltaState( j ) );
+
+ if ( !pDeltaName || !Q_strcmp( pDeltaName, pDelta->GetName() ) )
+ {
+ CUtlBuffer b( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ b << "# Delta OBJ: " << pDelta->GetName() << "\n";
+ b << "#\n";
+
+ Q_strncpy( filename, pDelta->GetName(), sizeof( filename ) );
+ // Change _ to +
+ const char *const pEnd( filename + sizeof( filename ) );
+ for ( char *pChar = filename; *pChar && pChar < pEnd; ++pChar )
+ {
+ if ( *pChar == '_' )
+ {
+ *pChar = '+';
+ }
+ }
+
+ CUtlString deltaFile( base );
+ deltaFile += filename;
+ deltaFile += ".obj";
+
+ m_nPositionOffset = 1; // OBJs use 1 based indexes
+ m_nTextureOffset = 1;
+ m_nNormalOffset = 1;
+
+ DagToObj( b, identityMatrix, pModel, pDelta->GetName(), absolute );
+
+ Q_ComposeFileName( path, deltaFile.Get(), filename, sizeof( filename ) );
+ g_pFullFileSystem->WriteFile( filename, NULL, b );
+ // On WIN32 filesystem changes all of the characters to lowercase grrrr.....
+ rename( filename, filename );
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::ParseMtlLib( CUtlBuffer &buf )
+{
+ char tmpBuf0[ 4096 ];
+
+ int nCurrentMtl = -1;
+ while ( buf.IsValid() )
+ {
+ buf.GetLine( tmpBuf0, sizeof(tmpBuf0) );
+
+ if ( StringHasPrefix( tmpBuf0, "newmtl " ) )
+ {
+ char mtlName[1024];
+ if ( sscanf( tmpBuf0, "newmtl %s", mtlName ) == 1 )
+ {
+ // Remove any 'SG' suffix from the material
+ const uint sLen = Q_strlen( mtlName );
+ if ( sLen > 2 && !Q_strcmp( mtlName + sLen - 2, "SG" ) )
+ {
+ mtlName[ sLen - 2 ] = '\0';
+ }
+
+ nCurrentMtl = m_mtlLib.AddToTail( );
+ m_mtlLib[nCurrentMtl].m_MtlName = mtlName;
+ m_mtlLib[nCurrentMtl].m_TgaName = mtlName;
+ }
+ continue;
+ }
+
+ if ( StringHasPrefix( tmpBuf0, "map_Kd " ) )
+ {
+ if ( nCurrentMtl < 0 )
+ continue;
+
+ char tgaPath[MAX_PATH];
+ char tgaName[1024];
+ if ( sscanf( tmpBuf0, "map_Kd %s", tgaPath ) == 1 )
+ {
+ // Try a cheesy hack - look for /materialsrc/ and set the material name off the entire path minus extension
+ Q_strncpy( tmpBuf0, tgaPath, sizeof( tmpBuf0 ) );
+ Q_FixSlashes( tmpBuf0, '/' );
+ const char *pMaterialSrc = Q_strstr( tmpBuf0, "/materialsrc/" );
+ if ( pMaterialSrc )
+ {
+ pMaterialSrc += Q_strlen( "/materialsrc/" );
+ Q_StripExtension( pMaterialSrc, tgaName, sizeof( tgaName) );
+ m_mtlLib[ nCurrentMtl ].m_TgaName = tgaName;
+ }
+ else
+ {
+ Q_FileBase( tgaPath, tgaName, sizeof(tgaName) );
+ m_mtlLib[nCurrentMtl].m_TgaName = tgaName;
+ }
+ }
+ continue;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const char *CDmObjSerializer::FindMtlEntry( const char *pTgaName )
+{
+ int nCount = m_mtlLib.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( !Q_stricmp( m_mtlLib[i].m_MtlName, pTgaName ) )
+ return m_mtlLib[i].m_TgaName;
+ }
+ return pTgaName;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::ParseVertex( CUtlBuffer& bufParse, characterset_t &breakSet, int &v, int &t, int &n )
+{
+ char cmd[1024];
+ int nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ if ( nLen <= 0 )
+ return false;
+
+ v = atoi( cmd );
+ n = 0;
+ t = 0;
+
+ char c = *(char*)bufParse.PeekGet();
+ bool bHasTexCoord = IN_CHARACTERSET( breakSet, c ) != 0;
+ bool bHasNormal = false;
+ if ( bHasTexCoord )
+ {
+ // Snag the '/'
+ nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ Assert( nLen == 1 );
+
+ c = *(char*)bufParse.PeekGet();
+ if ( !IN_CHARACTERSET( breakSet, c ) )
+ {
+ nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ Assert( nLen > 0 );
+ t = atoi( cmd );
+
+ c = *(char*)bufParse.PeekGet();
+ bHasNormal = IN_CHARACTERSET( breakSet, c ) != 0;
+ }
+ else
+ {
+ bHasNormal = true;
+ bHasTexCoord = false;
+ }
+
+ if ( bHasNormal )
+ {
+ // Snag the '/'
+ nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ Assert( nLen == 1 );
+
+ nLen = bufParse.ParseToken( &breakSet, cmd, sizeof(cmd), false );
+ Assert( nLen > 0 );
+ n = atoi( cmd );
+ }
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+const char *CDmObjSerializer::SkipSpace(
+ const char *pBuf )
+{
+ while ( *pBuf == ' ' || *pBuf == '\t' )
+ ++pBuf;
+
+ return pBuf;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeVertexDeltaData *CDmObjSerializer::GetDelta( const char *pDeltaName, bool bAbsolute )
+{
+ if ( !m_deltas.Defined( pDeltaName ) )
+ return NULL;
+
+ DeltaInfo_t &deltaInfo( m_deltas[ pDeltaName ] );
+
+ if ( deltaInfo.m_pDeltaData )
+ return deltaInfo.m_pDeltaData;
+
+ if ( !LoadDependentDeltas( pDeltaName ) )
+ return NULL;
+
+ CUtlBuffer utlBuf;
+
+ char deltaPath[ MAX_PATH ];
+ Q_ComposeFileName( m_objDirectory, deltaInfo.m_filename, deltaPath, sizeof( deltaPath ) );
+ Q_FixSlashes( deltaPath );
+
+ if ( !g_pFullFileSystem->ReadFile( deltaPath, NULL, utlBuf ) )
+ return NULL;
+
+ if ( deltaInfo.m_pComboOp && !strchr( pDeltaName, '_' ) )
+ {
+ deltaInfo.m_pComboOp->FindOrCreateControl( pDeltaName, false, true );
+ }
+
+ deltaInfo.m_pDeltaData = CastElement< CDmeVertexDeltaData >( ReadOBJ( utlBuf, deltaInfo.m_pMesh->GetFileId(), pDeltaName, deltaPath, deltaInfo.m_pMesh, NULL, bAbsolute ) );
+
+ return deltaInfo.m_pDeltaData;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmObjSerializer::LoadDependentDeltas( const char *pDeltaName )
+{
+ // TODO: Load Dependent Deltas
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Counts the number of _'s in a string
+//-----------------------------------------------------------------------------
+int ComputeDimensionality( const char *pDeltaName )
+{
+ const char *pUnderBar = pDeltaName;
+ int nDimensions = 0;
+
+ while ( pUnderBar )
+ {
+ ++nDimensions;
+ pUnderBar = strchr( pUnderBar, '_' );
+ if ( pUnderBar )
+ {
+ ++pUnderBar;
+ }
+ }
+
+ return nDimensions;
+}
+
+/*
+//-----------------------------------------------------------------------------
+// Generates a sorted list in order of dimensionality of the delta states
+// NOTE: This assumes a naming scheme where delta state names have _ that separate control names
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::ComputeDeltaStateComputationList( CUtlVector< CUtlVector< int > > &dependentDeltaList )
+{
+ // Do all combinations in order of dimensionality, lowest dimension first
+
+ for ( int i = 0; i < nDeltas; ++i )
+ {
+ compList[i].m_nDeltaIndex = i;
+ compList[i].m_nDimensionality = ComputeDeltaStateDimensionality( i );
+ }
+ qsort( compList.Base(), nCount, sizeof(DeltaComputation_t), DeltaStateLessFunc );
+}
+
+
+
+{
+ CUtlVector< CUtlString > atomicControls;
+ deltaStateUsage.SetCount( nCount );
+
+ // Build a list of atomic controls
+ int nCurrentDelta;
+ for ( nCurrentDelta = 0; nCurrentDelta < nCount; ++nCurrentDelta )
+ {
+ if ( pInfo[nCurrentDelta].m_nDimensionality != 1 )
+ break;
+ int j = atomicControls.AddToTail( GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex )->GetName() );
+ deltaStateUsage[ nCurrentDelta ].AddToTail( j );
+ }
+
+ for ( ; nCurrentDelta < nCount; ++nCurrentDelta )
+ {
+ CDmeVertexDeltaData *pDeltaState = GetDeltaState( pInfo[nCurrentDelta].m_nDeltaIndex );
+ int nLen = Q_strlen( pDeltaState->GetName() );
+ char *pTempBuf = (char*)_alloca( nLen + 1 );
+ memcpy( pTempBuf, pDeltaState->GetName(), nLen+1 );
+ char *pNext;
+ for ( char *pUnderBar = pTempBuf; pUnderBar; pUnderBar = pNext )
+ {
+ pNext = strchr( pUnderBar, '_' );
+ if ( pNext )
+ {
+ *pNext = 0;
+ ++pNext;
+ }
+
+ // Find this name in the list of strings
+ int j;
+ int nControlCount = atomicControls.Count();
+ for ( j = 0; j < nControlCount; ++j )
+ {
+ if ( !Q_stricmp( pUnderBar, atomicControls[j] ) )
+ break;
+ }
+ if ( j == nControlCount )
+ {
+ j = atomicControls.AddToTail( pUnderBar );
+ }
+ deltaStateUsage[ nCurrentDelta ].AddToTail( j );
+ }
+ deltaStateUsage[ nCurrentDelta ].Sort( DeltaStateUsageLessFunc );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmObjSerializer::ComputeDependentUsage( CUtlVector< CUtlVector< int > > &deltaUsage )
+{
+ const int nDeltas = m_deltas.GetNumStrings();
+ compList.EnsureCount( nDeltas );
+
+ CUtlVector< CUtlVector< int > > deltaStateUsage;
+ const int nCount( compList.Count() );
+ BuildAtomicControlLists( nCount, compList.Base(), deltaStateUsage );
+
+ // Now build up a list of dependent delta states based on usage
+ // NOTE: Usage is sorted in ascending order.
+ for ( int i = 1; i < nCount; ++i )
+ {
+ int nUsageCount1 = deltaStateUsage[i].Count();
+ for ( int j = 0; j < i; ++j )
+ {
+ // At the point they have the same dimensionality, no more need to check
+ if ( compList[j].m_nDimensionality == compList[i].m_nDimensionality )
+ break;
+
+ int ii = 0;
+ bool bSubsetFound = true;
+ int nUsageCount2 = deltaStateUsage[j].Count();
+ for ( int ji = 0; ji < nUsageCount2; ++ji )
+ {
+ for ( bSubsetFound = false; ii < nUsageCount1; ++ii )
+ {
+ if ( deltaStateUsage[j][ji] == deltaStateUsage[i][ii] )
+ {
+ ++ii;
+ bSubsetFound = true;
+ break;
+ }
+
+ if ( deltaStateUsage[j][ji] < deltaStateUsage[i][ii] )
+ break;
+ }
+
+ if ( !bSubsetFound )
+ break;
+ }
+
+ if ( bSubsetFound )
+ {
+ compList[i].m_DependentDeltas.AddToTail( compList[j].m_nDeltaIndex );
+ }
+ }
+ }
+}
+*/ \ No newline at end of file
diff --git a/movieobjects/dmsmdserializer.cpp b/movieobjects/dmsmdserializer.cpp
new file mode 100644
index 0000000..f1299cc
--- /dev/null
+++ b/movieobjects/dmsmdserializer.cpp
@@ -0,0 +1,1706 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Read SMD and create DMX data
+//
+//=============================================================================
+
+
+// Because we use STL
+#pragma warning( disable: 4530 )
+
+
+// Standard includes
+#include <io.h>
+#include <algorithm>
+#include <deque>
+#include <fstream>
+#include <list>
+#include <map>
+#include <string>
+
+
+// Valve includes
+#include "movieobjects/dmeanimationlist.h"
+#include "movieobjects/dmechannel.h"
+#include "movieobjects/dmedag.h"
+#include "movieobjects/dmemesh.h"
+#include "movieobjects/dmefaceset.h"
+#include "movieobjects/dmematerial.h"
+#include "movieobjects/dmemodel.h"
+#include "movieobjects/dmsmdserializer.h"
+#include "filesystem.h"
+#include "tier1/characterset.h"
+#include "tier1/fmtstr.h"
+#include "tier2/tier2.h"
+#include "mathlib/mathlib.h"
+
+
+// Last include
+#include "tier0/memdbgon.h"
+
+
+//=============================================================================
+//
+// CDmSmdSerializer
+//
+//=============================================================================
+//-----------------------------------------------------------------------------
+// Convert from SMD -> DME
+//-----------------------------------------------------------------------------
+bool CDmSmdSerializer::Unserialize(
+ CUtlBuffer &utlBuf,
+ const char * /* pszEncodingName */,
+ int /* nEncodingVersion */,
+ const char * /* pszSourceFormatName */,
+ int /* nSourceFormatVersion */,
+ DmFileId_t nDmFileId,
+ DmConflictResolution_t /* nDmConflictResolution */,
+ CDmElement **ppDmRoot )
+{
+ if ( !ppDmRoot )
+ return false;
+
+ const char *pszFilename = g_pDataModel->GetFileName( nDmFileId );
+
+ if ( pszFilename )
+ {
+ char szFilename[ MAX_PATH ];
+ V_strncpy( szFilename, pszFilename, ARRAYSIZE( szFilename ) );
+ V_FixSlashes( szFilename );
+
+ *ppDmRoot = ReadSMD( utlBuf, nDmFileId, szFilename, NULL );
+ }
+ else
+ {
+ *ppDmRoot = ReadSMD( utlBuf, nDmFileId, "utlBuffer", NULL );
+ }
+
+ return *ppDmRoot != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmElement *CDmSmdSerializer::ReadSMD(
+ const char *pszFilename,
+ CDmeMesh **ppDmeMeshCreated /* = NULL */ )
+{
+ char szFilename[ MAX_PATH ];
+ V_strncpy( szFilename, pszFilename, ARRAYSIZE( szFilename ) );
+ V_FixSlashes( szFilename );
+
+ CUtlBuffer utlBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+
+ if ( !g_pFullFileSystem->ReadFile( szFilename, NULL, utlBuf ) )
+ return NULL;
+
+ DmFileId_t nDmFileId = g_pDataModel->FindOrCreateFileId( pszFilename );
+
+ return ReadSMD( utlBuf, nDmFileId, szFilename, ppDmeMeshCreated );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmSmdSerializer::SetUpAxis( CDmSmdSerializer::Axis_t nUpAxis )
+{
+ m_nUpAxis = clamp( nUpAxis, X_AXIS, Z_AXIS );
+
+ switch ( m_nUpAxis )
+ {
+ case X_AXIS: // X Up
+ AngleMatrix( RadianEuler( -M_PI / 2.0, M_PI / 2.0, 0.0 ), m_mAdj );
+ MatrixInverseTranspose( m_mAdj, m_mAdjNormal );
+ break;
+ case Y_AXIS: // Y Up
+ SetIdentityMatrix( m_mAdj );
+ SetIdentityMatrix( m_mAdjNormal );
+ break;
+ case Z_AXIS:
+ default:
+ AngleMatrix( RadianEuler( -M_PI / 2.0, 0.0, 0.0 ), m_mAdj );
+ MatrixInverseTranspose( m_mAdj, m_mAdjNormal );
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Tests whether the passed buffer is an all whitespace line or not
+//-----------------------------------------------------------------------------
+static bool ParserIsBlankLine( const char *pszBuf )
+{
+ for ( const char *pChar = pszBuf; *pChar; ++pChar )
+ {
+ if ( !V_isspace( static_cast< unsigned char >( *pChar ) ) )
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Skips over whitespace
+//-----------------------------------------------------------------------------
+static const char *ParserSkipSpace( const char *pszBuf )
+{
+ // Skip to first non-whitespace character
+ for ( ;; )
+ {
+ if ( !V_isspace( static_cast< unsigned char >( *pszBuf ) ) )
+ break;
+
+ ++pszBuf;
+ }
+
+ return pszBuf;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns true the specified buffer is a comment line
+// meaning that it starts with // (with optional white space preceding the //)
+//-----------------------------------------------------------------------------
+static bool IsCommentLine( const char *pszBuf )
+{
+ pszBuf = ParserSkipSpace( pszBuf );
+
+ if ( *pszBuf && *pszBuf == '/' && *( pszBuf + 1 ) == '/' )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static bool ParserHandleVersion( const char *pszBuf, int *pnVersion = NULL )
+{
+ pszBuf = ParserSkipSpace( pszBuf );
+
+ if ( !( *pszBuf && V_strnicmp( pszBuf, "version", 7 ) == 0 ) )
+ return false;
+
+ if ( pnVersion )
+ {
+ *pnVersion = strtol( pszBuf + 7, NULL, 0 ); // Skip past "version"
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static bool ParserHandleSectionStart( const char *pszBuf, const char *pszSectionId )
+{
+ pszBuf = ParserSkipSpace( pszBuf );
+
+ if ( ! *pszBuf )
+ return false;
+
+ const int nCmd = V_stricmp( pszBuf, pszSectionId );
+ if ( !nCmd )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static bool ParserHandleTime( const char *pszBuf, int &nTime )
+{
+ if ( sscanf( pszBuf, "time %d", &nTime ) == 1 )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Strip trailing CR/NL
+//-----------------------------------------------------------------------------
+static void Chomp( char *pszBuf )
+{
+ char *pChar = pszBuf + V_strlen( pszBuf ) - 1;
+
+ while ( pChar >= pszBuf && ( *pChar == '\n' || *pChar == '\r' ) )
+ {
+ *pChar = '\0';
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static void GetLine( CUtlBuffer &utlBuf, char *pszBuf, int nMaxLen )
+{
+ utlBuf.GetLine( pszBuf, nMaxLen );
+ Chomp( pszBuf );
+}
+
+
+//=============================================================================
+//
+//=============================================================================
+class CQcData
+{
+public:
+ CQcData()
+ : m_nUpAxis( CDmSmdSerializer::Z_AXIS )
+ , m_scale( 1.0f )
+ {}
+
+ bool ParseQc( const CUtlString &smdPath, const CUtlString &qcPath );
+
+ bool GetQcData( const CUtlString &smdPath );
+
+ CDmSmdSerializer::Axis_t m_nUpAxis;
+ float m_scale;
+ std::list< std::string > m_cdmaterials;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static bool HandleQcHints(
+ const char *pBuf,
+ CQcData &qcData )
+{
+ if ( !IsCommentLine( pBuf ) )
+ return false;
+
+ char key[ 512 ];
+ key[0] = '\0';
+
+ char val[ 512 ];
+ val[0] = '\0';
+
+ if ( sscanf( pBuf, "// %511s=%511s", key, val ) == 2 )
+ {
+ if ( Q_stricmp( key, "UPAXIS" ) == 0 )
+ {
+ if ( strpbrk( val, "xX" ) )
+ {
+ qcData.m_nUpAxis = CDmSmdSerializer::X_AXIS;
+ }
+ else if ( strpbrk( val, "yY" ) )
+ {
+ qcData.m_nUpAxis = CDmSmdSerializer::Y_AXIS;
+ }
+ else if ( strpbrk( val, "zZ" ) )
+ {
+ qcData.m_nUpAxis = CDmSmdSerializer::Z_AXIS;
+ }
+ }
+ }
+
+ key[ ARRAYSIZE(key) - 1 ] = '\0';
+ val[ ARRAYSIZE(val) - 1 ] = '\0';
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static void Tokenize( CUtlVector< CUtlString > &tokens, const char *pszBuf )
+{
+ tokens.RemoveAll();
+ CUtlString strBuf( pszBuf );
+
+ for ( char *pszToken = strtok( (char *)strBuf.Get(), " \t\n" ); pszToken; pszToken = strtok( NULL, " \t\n" ) )
+ {
+ tokens.AddToTail( pszToken );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmSmdSerializer::ParserGetNodeName( const char *pszBuf, CUtlString &sName ) const
+{
+ sName = ParserSkipSpace( pszBuf );
+ FixNodeName( sName );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CDmSmdSerializer::ParserHandleSkeletonLine(
+ const char *pszBuf,
+ CUtlString &sName,
+ int &nId,
+ int &nParentId ) const
+{
+ const char *pszOrigBuf = pszBuf;
+
+ pszBuf = ParserSkipSpace( pszBuf );
+
+ char szTmpBuf[ 512 ];
+ if ( sscanf( pszBuf, "%d \"%[^\"]\" %d", &nId, szTmpBuf, &nParentId ) == 3 )
+ {
+ ParserGetNodeName( szTmpBuf, sName );
+ return true;
+ }
+
+ Warning( "Warning! Ignoring malformed skeleton line: %s\n", pszOrigBuf );
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static CUtlString PathJoin( const char *pszStr1, const char *pszStr2 )
+{
+ char szPath[MAX_PATH];
+ V_ComposeFileName( pszStr1, pszStr2, szPath, sizeof( szPath ) );
+ return CUtlString( szPath );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CQcData::ParseQc(
+ const CUtlString &smdPath,
+ const CUtlString &qcPath )
+{
+ bool bRetVal = false;
+
+ if ( _access( qcPath.Get(), 04 ) == 0 )
+ {
+ try
+ {
+ std::string buf;
+ std::ifstream ifs( qcPath.Get() );
+
+ CUtlVector< CUtlString > tokens;
+
+ while ( std::getline( ifs, buf ) )
+ {
+ Tokenize( tokens, buf.c_str() );
+
+ if ( tokens.Count() < 1 )
+ continue;
+
+ if ( !V_stricmp( tokens[0], "$upaxis" ) )
+ {
+ if ( strchr( tokens[1].Get(), 'y' ) || strchr( tokens[1].Get(), 'Y' ) )
+ {
+ m_nUpAxis = CDmSmdSerializer::Y_AXIS;
+ }
+ else if ( strchr( tokens[1].Get(), 'x' ) || strchr( tokens[1].Get(), 'X' ) )
+ {
+ m_nUpAxis = CDmSmdSerializer::X_AXIS;
+ }
+ else
+ {
+ m_nUpAxis = CDmSmdSerializer::Z_AXIS;
+ }
+ }
+ else if ( !V_stricmp( tokens[0], "$scale" ) )
+ {
+ m_scale = strtod( tokens[1].Get(), NULL );
+ }
+ else if ( !V_stricmp( tokens[0], "$cdmaterials" ) )
+ {
+ m_cdmaterials.push_back( tokens[1].Get() );
+ }
+ }
+
+ bRetVal = true;
+ }
+ catch ( ... )
+ {
+ }
+ }
+
+ if ( m_cdmaterials.empty() )
+ {
+ // If m_cdmaterials is empty, then put the relative smd path onto the cdmaterials path
+
+ char szBuf0[MAX_PATH];
+ V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) );
+ V_StripFilename( szBuf0 );
+ V_FixSlashes( szBuf0, '/' );
+
+ CUtlVector< char *, CUtlMemory< char *, int > > sPathArray;
+ V_SplitString( szBuf0, "/", sPathArray );
+
+ CUtlString sRelSmdPath;
+
+ bool bJoin = false;
+ for ( int i = 0; i < sPathArray.Count(); ++i )
+ {
+ if ( !bJoin && !V_stricmp( sPathArray[i], "models" ) )
+ {
+ bJoin = true;
+ }
+
+ if ( bJoin )
+ {
+ sRelSmdPath = PathJoin( sRelSmdPath.Get(), sPathArray[i] );
+ }
+ }
+ sRelSmdPath.FixSlashes( '/' );
+
+ m_cdmaterials.push_back( sRelSmdPath.Get() );
+ }
+
+ return bRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CQcData::GetQcData(
+ const CUtlString &smdPath )
+{
+ try
+ {
+ // Look for same thing named with a .qc extension
+ char szBuf0[MAX_PATH];
+ char szBuf1[MAX_PATH];
+
+ V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) );
+ V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) );
+ if ( _access( szBuf0, 04 ) == 0 )
+ return ParseQc( smdPath, szBuf0 );
+
+ // Remove "_reference" if found
+ char *pszRef = V_stristr( szBuf0, "_reference.qc" );
+ if ( pszRef )
+ {
+ *pszRef = '\0';
+
+ V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) );
+ if ( _access( szBuf0, 04 ) == 0 )
+ return ParseQc( smdPath, szBuf0 );
+ }
+ else
+ {
+ // Add _reference
+
+ V_SetExtension( szBuf0, "", ARRAYSIZE( szBuf0 ) );
+ V_strcat( szBuf0, "_reference", ARRAYSIZE( szBuf0 ) );
+ V_SetExtension( szBuf0, ".qc", ARRAYSIZE( szBuf0 ) );
+ if ( _access( szBuf0, 04 ) == 0 )
+ return ParseQc( smdPath, szBuf0 );
+ }
+
+ // Look for any *.qc file in the same directory as the smd that contains the smd pathname
+ V_strncpy( szBuf0, smdPath.Get(), ARRAYSIZE( szBuf0 ) );
+ V_FixSlashes( szBuf0 );
+
+ V_FileBase( szBuf0, szBuf1, ARRAYSIZE( szBuf1 ) );
+ CUtlString sFileBase0( "\"" );
+ sFileBase0 += szBuf1;
+ sFileBase0 += "\"";
+
+ V_SetExtension( szBuf1, ".smd", ARRAYSIZE( szBuf1 ) );
+ CUtlString sFileBase1( "\"" );
+ sFileBase1 += szBuf1;
+ sFileBase1 += "\"";
+
+ V_ExtractFilePath( szBuf0, szBuf1, ARRAYSIZE( szBuf1 ) );
+ CUtlString sFilePath = szBuf1;
+
+ if ( sFileBase0.Length() > 0 && sFilePath.Length() > 0 )
+ {
+ struct _finddata_t qcFile;
+ long hFile;
+
+ CUtlVector< CUtlString > tokens;
+
+ /* Find first .qc file in current directory */
+
+ CUtlString sQcGlob = sFilePath;
+ sQcGlob += "*.qc";
+
+ if ( ( hFile = _findfirst( sQcGlob.Get(), &qcFile ) ) != -1L )
+ {
+ /* Find the rest of the .qc files */
+ do {
+ CUtlString sQcFile = sFilePath;
+ sQcFile += qcFile.name;
+
+ std::ifstream ifs( sQcFile.Get() );
+ std::string buf;
+
+ while ( std::getline( ifs, buf ) )
+ {
+ if ( V_stristr( buf.c_str(), sFileBase0.Get() ) || V_stristr( buf.c_str(), sFileBase1.Get() ) )
+ {
+ _findclose( hFile );
+ return ParseQc( smdPath, sQcFile );
+ }
+ }
+ } while( _findnext( hFile, &qcFile ) == 0 );
+
+ _findclose( hFile );
+ }
+ }
+ }
+ catch ( const std::exception &e )
+ {
+ Error( "Exception: %s\n", e.what() );
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+#define MAX_WEIGHTS_PER_VERTEX 3
+
+
+//=============================================================================
+//
+//=============================================================================
+class CVertexWeight
+{
+public:
+ int m_nBoneIndex;
+ float m_flWeight;
+
+ CVertexWeight()
+ : m_nBoneIndex( -1 )
+ , m_flWeight( 0.0f )
+ {}
+
+ inline void Reset()
+ {
+ m_nBoneIndex = -1;
+ m_flWeight = 0.0f;
+ }
+
+ inline bool operator==( const CVertexWeight &rhs ) const
+ {
+ return m_nBoneIndex == rhs.m_nBoneIndex && m_flWeight == rhs.m_flWeight;
+ }
+
+ inline bool operator!=( const CVertexWeight &rhs ) const
+ {
+ return m_nBoneIndex != rhs.m_nBoneIndex || m_flWeight != rhs.m_flWeight;
+ }
+};
+
+
+//=============================================================================
+//
+//=============================================================================
+class CVertex
+{
+public:
+ Vector m_vPosition;
+ Vector m_vNormal;
+ Vector2D m_vUV;
+ uint m_nWeights;
+ CVertexWeight m_vertexWeights[ MAX_WEIGHTS_PER_VERTEX ];
+
+ CVertex()
+ {
+ Reset();
+ }
+
+ inline bool operator==( const CVertex &rhs ) const
+ {
+ if ( m_vPosition != rhs.m_vPosition ||
+ m_vNormal != rhs.m_vNormal ||
+ m_vUV != rhs.m_vUV ||
+ m_nWeights != rhs.m_nWeights )
+ return false;
+
+ for ( uint i = 0; i != m_nWeights; ++i )
+ {
+ if ( m_vertexWeights[ i ] != rhs.m_vertexWeights[ i ] )
+ return false;
+ }
+
+ return true;
+ }
+
+ inline void Reset()
+ {
+ m_nWeights = 0;
+ for ( int i = 0; i < ARRAYSIZE( m_vertexWeights ); ++i )
+ {
+ m_vertexWeights[i].Reset();
+ }
+ }
+};
+
+
+//=============================================================================
+//
+//=============================================================================
+class CTriangle
+{
+public:
+ uint m_nVertices;
+ CVertex m_vertices[ 3 ];
+
+ CTriangle()
+ {
+ Reset();
+ }
+
+ bool Valid() const;
+
+ inline void Reset()
+ {
+ m_nVertices = 0;
+ m_vertices[ 0U ].Reset();
+ m_vertices[ 1U ].Reset();
+ m_vertices[ 2U ].Reset();
+ }
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CTriangle::Valid() const
+{
+ // A triangle is valid it it has three vertices and all three vertices are unique
+
+ if ( m_nVertices != 3U )
+ return false;
+
+ if ( m_vertices[0] == m_vertices[1] )
+ return false;
+
+ if ( m_vertices[1] == m_vertices[2] )
+ return false;
+
+ if ( m_vertices[2] == m_vertices[0] )
+ return false;
+
+ return true;
+}
+
+
+//=============================================================================
+//
+//=============================================================================
+class CShadingGroup
+{
+public:
+ std::string m_materialPath;
+ std::map< int, std::deque< int > > m_componentListMap;
+};
+
+
+//=============================================================================
+//
+//=============================================================================
+enum ParserState_t
+{
+ kUnknown,
+ kPreamble,
+ kGeneral,
+ kNodes,
+ kSkeleton,
+ kTriangles
+};
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static bool ParserAddJoint(
+ CDmSmdSerializer::SmdJointMap_t &smdJointMap,
+ int nId,
+ const char *pszName,
+ int nParentId,
+ const char *pszFilename,
+ int nLineNumber )
+{
+ if ( nId < 0 )
+ {
+ Error( "Error! %s(%d) : node error : invalid node id %d, must be >= 0: Line '%d \"%s\" %d'\n",
+ pszFilename, nLineNumber, nId, nId, pszName, nParentId );
+ return false;
+ }
+
+ if ( smdJointMap.IsValidIndex( smdJointMap.Find( nId ) ) )
+ {
+ Error( "Error! %s(%d) : node error : node id %d already defined\n",
+ pszFilename, nLineNumber, nId );
+ return false;
+ }
+
+ CDmSmdSerializer::SmdJoint_t &smdJoint = smdJointMap.Element( smdJointMap.Insert( nId ) );
+
+ smdJoint.m_nId = nId;
+ smdJoint.m_sName = pszName;
+ smdJoint.m_nParentId = MAX( -1, nParentId );
+ smdJoint.m_nLineNumber = nLineNumber;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static bool ParserCreateJoint(
+ const CDmSmdSerializer::SmdJointMap_t &smdJointMap,
+ CDmeModel *pDmeModel,
+ CDmeChannelsClip *pDmeChannelsClip,
+ CDmSmdSerializer::SmdJoint_t *pSmdJoint,
+ const char *pszFilename )
+{
+ const CUtlString &sName = pSmdJoint->m_sName;
+ const int nId = pSmdJoint->m_nId;
+ const int nParentId = pSmdJoint->m_nParentId;
+ const int nLineNumber = pSmdJoint->m_nLineNumber;
+
+ CDmeJoint *pDmeJoint = CreateElement< CDmeJoint >( sName.Get(), pDmeModel->GetFileId() );
+
+ if ( !pDmeJoint )
+ {
+ Error( "%s(%d) : node error : can't create joint %d(%s)\n",
+ pszFilename, nLineNumber, nId, sName.Get() );
+ return false;
+ }
+
+ if ( nParentId < 0 )
+ {
+ if ( !pDmeModel )
+ {
+ Error( "%s(%d) : node error : No DmeModel passed for root joint %d(%s)\n",
+ pszFilename, nLineNumber, nId, sName.Get() );
+ DestroyElement( pDmeJoint );
+
+ return false;
+ }
+
+ pDmeModel->AddChild( pDmeJoint );
+ CDmAttribute *pRootJointAttr = pDmeJoint->AddAttribute( "__rootJoint", AT_BOOL );
+ pRootJointAttr->AddFlag( FATTRIB_DONTSAVE );
+ pRootJointAttr->SetValue( true );
+ }
+ else
+ {
+ if ( nParentId == nId )
+ {
+ Error( "%s(%d) : node error : joint %d(%s) is its own parent\n",
+ pszFilename, nLineNumber, nId, sName.Get() );
+ DestroyElement( pDmeJoint );
+
+ return false;
+ }
+
+ const int nParentIdIndex = smdJointMap.Find( nParentId );
+
+ if ( !smdJointMap.IsValidIndex( nParentIdIndex ) )
+ {
+ Error( "%s(%d) : node error : joint %d(%s) has invalid parentId (%d)\n",
+ pszFilename, nLineNumber, nId, sName.Get(), nParentId );
+ DestroyElement( pDmeJoint );
+
+ return false;
+ }
+
+ CDmeDag *pDmeDagParent = smdJointMap.Element( nParentIdIndex ).m_pDmeDag;
+ if ( pDmeDagParent )
+ {
+ pDmeDagParent->AddChild( pDmeJoint );
+ }
+ else
+ {
+ Error( "%s(%d) : node error : joint %d(%s) has invalid parentId (%d)\n",
+ pszFilename, nLineNumber, nId, sName.Get(), nParentId );
+ }
+ }
+
+ if ( pDmeChannelsClip )
+ {
+ CDmeTransform *pDmeTransform = pDmeJoint->GetTransform();
+
+ CDmeChannel *pDmePosChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_p", pDmeTransform->GetName() ).Access(), pDmeChannelsClip->GetFileId() );
+ pDmePosChannel->SetMode( CM_PLAY );
+ pDmePosChannel->SetOutput( pDmeTransform, "position" );
+ CDmeVector3Log *pDmePosLog = pDmePosChannel->CreateLog< Vector >();
+ pDmePosLog->SetValueThreshold( 1.0e-6 );
+ pDmeChannelsClip->m_Channels.AddToTail( pDmePosChannel );
+
+ CDmAttribute *pPosLogAttr = pDmeJoint->AddAttribute( "__posLog", AT_ELEMENT );
+ pPosLogAttr->AddFlag( FATTRIB_DONTSAVE );
+ pPosLogAttr->SetValue( pDmePosLog );
+
+ CDmeChannel *pDmeRotChannel = CreateElement< CDmeChannel >( CFmtStr( "%s_o", pDmeTransform->GetName() ).Access(), pDmeChannelsClip->GetFileId() );
+ pDmeRotChannel->SetMode( CM_PLAY );
+ pDmeRotChannel->SetOutput( pDmeTransform, "orientation" );
+ CDmeQuaternionLog *pDmeRotLog = pDmeRotChannel->CreateLog< Quaternion >();
+ pDmeRotLog->SetValueThreshold( 1.0e-6 );
+ pDmeChannelsClip->m_Channels.AddToTail( pDmeRotChannel );
+
+ CDmAttribute *pRotLogAttr = pDmeJoint->AddAttribute( "__rotLog", AT_ELEMENT );
+ pRotLogAttr->AddFlag( FATTRIB_DONTSAVE );
+ pRotLogAttr->SetValue( pDmeRotLog );
+ }
+
+ pSmdJoint->m_pDmeDag = pDmeJoint;
+ pDmeModel->AddJoint( pDmeJoint );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Used by ParserAddJoints, sorts joints by parent index
+//-----------------------------------------------------------------------------
+static bool SmdJointLessFunc( const CDmSmdSerializer::SmdJoint_t *pLhs, const CDmSmdSerializer::SmdJoint_t *pRhs )
+{
+ // try to preserve joints without parents in their original order as much as possible
+ if ( pLhs->m_nParentId < 0 )
+ return pLhs->m_nId < pRhs->m_nId;
+
+ return pLhs->m_nParentId < pRhs->m_nParentId;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static void ParserCreateJoints(
+ CDmSmdSerializer::SmdJointMap_t &smdJointMap,
+ CDmeModel *pDmeModel,
+ CDmeChannelsClip *pDmeChannelsClip,
+ const char *pszFilename )
+{
+ // Sort the joints in order of parent, pointers only temporary and no elements
+ // added/removed from map while pointers exist, so pointers are stable
+ CUtlVector< CDmSmdSerializer::SmdJoint_t * > smdJointList;
+ FOR_EACH_MAP_FAST( smdJointMap, nSmdJointMapIndex )
+ {
+ smdJointList.AddToTail( &smdJointMap.Element( nSmdJointMapIndex ) );
+ }
+
+ std::stable_sort( smdJointList.Base(), smdJointList.Base() + smdJointList.Count(), SmdJointLessFunc );
+
+ for ( int i = 0; i < smdJointList.Count(); ++i )
+ {
+ CDmSmdSerializer::SmdJoint_t *pSmdJoint = smdJointList[i];
+ pSmdJoint->m_nActualId = i;
+
+ ParserCreateJoint( smdJointMap, pDmeModel, pDmeChannelsClip, pSmdJoint, pszFilename );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmSmdSerializer::ParserSetJoint(
+ const SmdJointMap_t &smdJointMap,
+ int nFrame,
+ int nId,
+ const Vector &vPosition,
+ const RadianEuler &eRadianEulerXYZ,
+ const char *pszFilename,
+ int nLineNumber )
+{
+ const SmdJointMap_t::IndexType_t nIdIndex = smdJointMap.Find( nId );
+ if ( !smdJointMap.IsValidIndex( nIdIndex ) )
+ {
+ Error( "%s(%d) : skeleton error : can't find joint %d\n",
+ pszFilename, nLineNumber, nId );
+ return;
+ }
+
+ CDmeDag *pDmeDag = smdJointMap.Element( nIdIndex ).m_pDmeDag;
+ if ( !pDmeDag )
+ {
+ Error( "%s(%d) : skeleton error : no dmedag created for can't find joint %d (%s)\n",
+ pszFilename, nLineNumber, nId, smdJointMap.Element( nIdIndex ).m_sName.Get() );
+ return;
+ }
+
+ const Quaternion qOrientation = eRadianEulerXYZ;
+
+ if ( pDmeDag->GetValue( "__rootJoint", false ) && GetUpAxis() != CDmSmdSerializer::Y_AXIS )
+ {
+ matrix3x4_t mPre;
+ matrix3x4_t mPost;
+ AngleMatrix( qOrientation, vPosition, mPre );
+ ConcatTransforms( m_mAdj, mPre, mPost );
+ pDmeDag->GetTransform()->SetTransform( mPost );
+ }
+ else
+ {
+ pDmeDag->GetTransform()->SetPosition( vPosition );
+ pDmeDag->GetTransform()->SetOrientation( qOrientation );
+ }
+
+ if ( m_bOptAnimation )
+ {
+ const DmeTime_t tCurrent( static_cast< float >( nFrame ), DmeFramerate_t( m_flFrameRate ) );
+
+ CDmeTransform *pDmeTransform = pDmeDag->GetTransform();
+
+ CDmeVector3Log *pDmePosLog = pDmeDag->GetValueElement< CDmeVector3Log >( "__posLog" );
+ pDmePosLog->SetKey( tCurrent, pDmeTransform->GetPosition() );
+
+ CDmeQuaternionLog *pDmeRotLog = pDmeDag->GetValueElement< CDmeQuaternionLog >( "__rotLog" );
+ pDmeRotLog->SetKey( tCurrent, pDmeTransform->GetOrientation() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Returns -1 if invalid
+//-----------------------------------------------------------------------------
+static int GetActualId( const CDmSmdSerializer::SmdJointMap_t &smdJointMap, int nId )
+{
+ const CDmSmdSerializer::SmdJointMap_t::IndexType_t nIdIndex = smdJointMap.Find( nId );
+
+ if ( !smdJointMap.IsValidIndex( nIdIndex ) )
+ {
+ Error( "Error! Invalid node id %d looked up\n", nId );
+ return -1;
+ }
+
+ return smdJointMap.Element( nIdIndex ).m_nActualId;
+}
+
+
+//-----------------------------------------------------------------------------
+// Used by HandleVertexWeights, sorts vertex weights by weight
+//-----------------------------------------------------------------------------
+static int VertexWeightLessFunc( const void *pLhs, const void *pRhs )
+{
+ const CVertexWeight *pVertexWeightL = reinterpret_cast< const CVertexWeight * >( pLhs );
+ const CVertexWeight *pVertexWeightR = reinterpret_cast< const CVertexWeight * >( pRhs );
+
+ if ( pVertexWeightL->m_nBoneIndex < 0 )
+ {
+ if ( pVertexWeightR->m_nBoneIndex < 0 )
+ {
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ else if ( pVertexWeightR->m_nBoneIndex < 0 )
+ {
+ return -1;
+ }
+
+ if ( pVertexWeightL->m_flWeight > pVertexWeightR->m_flWeight )
+ {
+ return -1;
+ }
+ else if ( pVertexWeightL->m_flWeight < pVertexWeightR->m_flWeight )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static void HandleVertexWeights(
+ const CDmSmdSerializer::SmdJointMap_t &smdJointMap,
+ int nFallbackBoneIndexId,
+ CVertex &vertex,
+ const char *pszLine,
+ const char *pszFilename,
+ int nLineNumber )
+{
+ for ( int i = 0; i < ARRAYSIZE( vertex.m_vertexWeights ); ++i )
+ {
+ vertex.m_vertexWeights[i].Reset();
+ }
+
+ CUtlVector< CVertexWeight > tmpVertexWeights;
+
+ CUtlVector< CUtlString > tokens;
+ Tokenize( tokens, pszLine );
+
+ const float flEps = 1.0e-6;
+
+ uint nTokenEnd = tokens.Count();
+ if ( nTokenEnd > 10 )
+ {
+ int nId = -1;
+ float flWeight = 0.0f;
+ for ( uint i = 10; i < nTokenEnd; ++i )
+ {
+ nId = strtol( tokens[ i ].Get(), NULL, 0 );
+ ++i;
+ flWeight = strtod( tokens[ i ].Get(), NULL );
+
+ if ( nId < 0 || flWeight < flEps )
+ continue;
+
+ const int nActualId = GetActualId( smdJointMap, nId );
+
+ if ( nActualId < 0 )
+ {
+ Error( "%s(%d) : triangle error : ignoring unknown joint id(%d) with actual id(%d) for vertex weight\n",
+ pszFilename, nLineNumber, nId, nActualId );
+ continue;
+ }
+
+ CVertexWeight &tmpVertexWeight = tmpVertexWeights[ tmpVertexWeights.AddToTail() ];
+ tmpVertexWeight.m_nBoneIndex = nActualId;
+ tmpVertexWeight.m_flWeight = flWeight;
+ }
+
+ // Sort vertex weights inversely by weight
+ qsort( tmpVertexWeights.Base(), tmpVertexWeights.Count(), sizeof( CVertexWeight ), VertexWeightLessFunc );
+
+ // Take at most the top ARRAYSIZE( vertex.m_vertexWeights ) from tmpVertexWeights
+ // Figure out actual weight count and total weight
+
+ float flTotalWeight = 0.0f;
+ vertex.m_nWeights = 0;
+
+ for ( int i = 0; i < tmpVertexWeights.Count() && i < ARRAYSIZE( vertex.m_vertexWeights ); ++i )
+ {
+ CVertexWeight &vertexWeight( vertex.m_vertexWeights[ i ] );
+ vertexWeight = tmpVertexWeights[i];
+ flTotalWeight += vertexWeight.m_flWeight;
+ vertex.m_nWeights += 1;
+ }
+
+ // If there are valid user specified weights then renormalize
+ if ( vertex.m_nWeights > 0 && flTotalWeight > flEps )
+ {
+ for ( uint i = 0; i != ARRAYSIZE( vertex.m_vertexWeights ); ++i )
+ {
+ CVertexWeight &vertexWeight( vertex.m_vertexWeights[ i ] );
+ if ( vertexWeight.m_nBoneIndex >= 0 )
+ {
+ vertexWeight.m_flWeight /= flTotalWeight;
+ }
+ }
+ }
+ else
+ {
+ // No valid user weights, assign to fallback bone with weight of 1
+ vertex.m_vertexWeights[ 0 ].m_nBoneIndex = GetActualId( smdJointMap, nFallbackBoneIndexId );
+ if ( vertex.m_vertexWeights[0].m_nBoneIndex < 0 )
+ {
+ // Vertex is not weighted, can't find fallback bone, this is invalid
+ vertex.m_nWeights = 0;
+ vertex.m_vertexWeights[0].m_nBoneIndex = -1;
+ vertex.m_vertexWeights[0].m_flWeight = 0.0f;
+ }
+ else
+ {
+ vertex.m_nWeights = 1;
+ vertex.m_vertexWeights[0].m_flWeight = 1.0f;
+ }
+
+ // Assign rest of weights to -1, 0.0f
+ for ( uint i = 1; i < ARRAYSIZE( vertex.m_vertexWeights ); ++i )
+ {
+ vertex.m_vertexWeights[i].m_nBoneIndex = -1;
+ vertex.m_vertexWeights[i].m_flWeight = 0.0f;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static CDmeMesh *CreateDmeMesh(
+ CDmeModel *pDmeModel,
+ const char *pszFilename,
+ int nLineNumber )
+{
+ char szFileBase[MAX_PATH];
+ szFileBase[0] = '\0';
+
+ const char *pszMeshName = "mesh";
+
+ if ( pszFilename )
+ {
+ V_FileBase( pszFilename, szFileBase, ARRAYSIZE( szFileBase ) );
+
+ if ( V_strlen( szFileBase ) > 0 )
+ {
+ pszMeshName = szFileBase;
+ }
+ }
+
+ CDmeDag *pDmeDag = CreateElement< CDmeDag >( pszMeshName, pDmeModel->GetFileId() );
+ if ( !pDmeDag )
+ return NULL;
+
+ pDmeModel->AddChild( pDmeDag );
+
+ CUtlString sMeshName = pDmeDag->GetName();
+ sMeshName += "Shape";
+
+ CDmeMesh *pDmeMesh = CreateElement< CDmeMesh >( sMeshName.Get(), pDmeDag->GetFileId() );
+ if ( !pDmeMesh )
+ return NULL;
+
+ CDmeVertexData *pDmeVertexData = pDmeMesh->FindOrCreateBaseState( "bind" );
+ pDmeMesh->SetCurrentBaseState( "bind" );
+
+ pDmeVertexData->FlipVCoordinate( true );
+ pDmeVertexData->CreateField( CDmeVertexData::FIELD_POSITION );
+ pDmeVertexData->CreateField( CDmeVertexData::FIELD_NORMAL );
+ pDmeVertexData->CreateField( CDmeVertexData::FIELD_TEXCOORD );
+ FieldIndex_t nJointWeightField;
+ FieldIndex_t nJointIndexField;
+ pDmeVertexData->CreateJointWeightsAndIndices( MAX_WEIGHTS_PER_VERTEX, &nJointWeightField, &nJointIndexField );
+
+ pDmeDag->SetShape( pDmeMesh );
+
+ pDmeVertexData->Resolve();
+ pDmeMesh->Resolve();
+ pDmeDag->Resolve();
+
+ return pDmeMesh;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static CDmeFaceSet *FindOrCreateFaceSet(
+ CDmeMesh *pDmeMesh,
+ const CUtlString &sMaterial,
+ const char *pszFilename,
+ int nLineNumber )
+{
+ // Could cache in a hashed map or something...
+ for ( int i = 0; i < pDmeMesh->FaceSetCount(); ++i )
+ {
+ CDmeFaceSet *pDmeFaceSet = pDmeMesh->GetFaceSet( i );
+ if ( !pDmeFaceSet )
+ continue;
+
+ CDmeMaterial *pDmeMaterial = pDmeFaceSet->GetMaterial();
+ if ( !pDmeMaterial )
+ continue;
+
+ if ( !V_strcmp( pDmeMaterial->GetMaterialName(), sMaterial.Get() ) )
+ return pDmeFaceSet;
+ }
+
+ char szFaceSetName[ MAX_PATH ];
+ V_FileBase( sMaterial.Get(), szFaceSetName, ARRAYSIZE( szFaceSetName ) );
+
+ CDmeFaceSet *pDmeFaceSet = CreateElement< CDmeFaceSet >( szFaceSetName, pDmeMesh->GetFileId() );
+ Assert( pDmeFaceSet );
+
+ CDmeMaterial *pDmeMaterial = CreateElement< CDmeMaterial >( szFaceSetName, pDmeMesh->GetFileId() );
+ Assert( pDmeMaterial );
+
+ pDmeMaterial->SetMaterial( sMaterial.Get() );
+ pDmeFaceSet->SetMaterial( pDmeMaterial );
+
+ pDmeMesh->AddFaceSet( pDmeFaceSet );
+
+ return pDmeFaceSet;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static void CreatePolygon(
+ CDmeMesh *pDmeMesh,
+ const CUtlString &sMaterial,
+ CTriangle &triangle,
+ const char *pszFilename,
+ int nLineNumber )
+{
+ if ( !triangle.Valid() || !pDmeMesh )
+ return;
+
+ CDmeVertexData *pDmeVertexData = pDmeMesh->GetBindBaseState();
+ Assert( pDmeVertexData );
+
+ FieldIndex_t nPositionField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
+ CDmrArray< Vector > positionData = pDmeVertexData->GetVertexData( nPositionField );
+
+ FieldIndex_t nNormalField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
+ CDmrArray< Vector > normalData = pDmeVertexData->GetVertexData( nNormalField );
+
+ FieldIndex_t nUvField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
+ CDmrArray< Vector2D > uvData = pDmeVertexData->GetVertexData( nUvField );
+
+ FieldIndex_t nJointWeightField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_WEIGHTS );
+ CDmrArray< float > jointWeightData = pDmeVertexData->GetVertexData( nJointWeightField );
+
+ FieldIndex_t nJointIndexField = pDmeVertexData->FindFieldIndex( CDmeVertexData::FIELD_JOINT_INDICES );
+ CDmrArray< int > jointIndexData = pDmeVertexData->GetVertexData( nJointIndexField );
+
+ CDmeFaceSet *pDmeFaceSet = FindOrCreateFaceSet( pDmeMesh, sMaterial, pszFilename, nLineNumber );
+ if ( !pDmeFaceSet )
+ {
+ Error( "%s(%d) : mesh error : couldn't find or create DmeFace set for material \"%s\" on mesh \"%s\"\n",
+ pszFilename, nLineNumber, sMaterial.Get(), pDmeMesh->GetName() );
+ return;
+ }
+
+ for ( uint i = 0; i < 3; ++i )
+ {
+ const int nNewVertexIndex = pDmeVertexData->AddVertexIndices( 1 );
+
+ CVertex &cVertex = triangle.m_vertices[i];
+
+ {
+ // TODO: Make two positions if they are skinned differently
+ int nPositionIndex = positionData.Find( cVertex.m_vPosition );
+ if ( !positionData.IsValidIndex( nPositionIndex ) )
+ {
+ nPositionIndex = positionData.AddToTail( cVertex.m_vPosition );
+
+ for ( int j = 0; j < ARRAYSIZE( cVertex.m_vertexWeights ); ++j )
+ {
+ jointWeightData.AddToTail( cVertex.m_vertexWeights[j].m_flWeight );
+ jointIndexData.AddToTail( cVertex.m_vertexWeights[j].m_nBoneIndex );
+ }
+ }
+
+ pDmeVertexData->SetVertexIndices( nPositionField, nNewVertexIndex, 1, &nPositionIndex );
+
+ const int nFaceSetIndex = pDmeFaceSet->AddIndices( 1 );
+ pDmeFaceSet->SetIndices( nFaceSetIndex, 1, const_cast< int * >( &nNewVertexIndex ) );
+
+ }
+
+ {
+ int nNormalIndex = normalData.Find( cVertex.m_vNormal );
+ if ( !normalData.IsValidIndex( nNormalIndex ) )
+ {
+ nNormalIndex = normalData.AddToTail( cVertex.m_vNormal );
+ }
+
+ pDmeVertexData->SetVertexIndices( nNormalField, nNewVertexIndex, 1, &nNormalIndex );
+ }
+
+
+ {
+ int nUvIndex = uvData.Find( cVertex.m_vUV );
+ if ( !uvData.IsValidIndex( nUvIndex ) )
+ {
+ nUvIndex = uvData.AddToTail( cVertex.m_vUV );
+ }
+
+ pDmeVertexData->SetVertexIndices( nUvField, nNewVertexIndex, 1, &nUvIndex );
+ }
+
+ }
+
+ static const int nFaceDelimiter = -1;
+ const int nFaceSetIndex = pDmeFaceSet->AddIndices( 1 );
+ pDmeFaceSet->SetIndices( nFaceSetIndex, 1, const_cast< int * >( &nFaceDelimiter ) );
+
+ triangle.Reset();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+static CDmeFaceSet *FindOrAddFaceSet(
+ CDmeMesh *pDmeMesh,
+ CUtlMap< CUtlString, CDmeFaceSet * > &faceListMap,
+ const char *pszMaterialPath )
+{
+ CUtlMap< CUtlString, CDmeFaceSet * >::IndexType_t nIndex = faceListMap.Find( CUtlString( pszMaterialPath ) );
+
+ if ( faceListMap.IsValidIndex( nIndex ) )
+ return faceListMap.Element( nIndex );
+
+ CDmeFaceSet *pDmeFaceSet = CreateElement< CDmeFaceSet >( pszMaterialPath, pDmeMesh->GetFileId() );
+ CDmeMaterial *pDmeMaterial = CreateElement< CDmeMaterial >( pszMaterialPath, pDmeMesh->GetFileId() );
+ Assert( pDmeFaceSet && pDmeMaterial );
+ pDmeMaterial->SetMaterial( pszMaterialPath );
+ pDmeMesh->AddFaceSet( pDmeFaceSet );
+
+ return faceListMap.Element( faceListMap.Insert( CUtlString( pszMaterialPath ), pDmeFaceSet ) );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CDmeChannelsClip *FindOrCreateChannelsClip( CDmElement *pDmeRoot, const char *pszAnimationName )
+{
+ CDmeAnimationList *pDmeAnimationList = pDmeRoot->GetValueElement< CDmeAnimationList >( "animationList" );
+ CDmeChannelsClip *pDmeChannelsClip = NULL;
+
+ if ( pDmeAnimationList )
+ {
+ pDmeChannelsClip = pDmeAnimationList->GetAnimation( 0 );
+ }
+ else
+ {
+ CDmeModel *pDmeModel = pDmeRoot->GetValueElement< CDmeModel >( "skeleton" );
+
+ pDmeAnimationList = CreateElement< CDmeAnimationList >( pszAnimationName, pDmeRoot->GetFileId() );
+ pDmeChannelsClip = CreateElement< CDmeChannelsClip >( pszAnimationName, pDmeRoot->GetFileId() );
+ pDmeAnimationList->AddAnimation( pDmeChannelsClip );
+ pDmeRoot->SetValue( "animationList", pDmeAnimationList );
+ pDmeModel->SetValue( "animationList", pDmeAnimationList );
+ }
+
+ return pDmeChannelsClip;
+}
+
+//-----------------------------------------------------------------------------
+// Common function both ReadSMD & Unserialize can call
+//-----------------------------------------------------------------------------
+CDmElement *CDmSmdSerializer::ReadSMD(
+ CUtlBuffer &utlBuf,
+ DmFileId_t nDmFileId,
+ const char *pszFilename,
+ CDmeMesh **ppDmeMeshCreated )
+{
+ CDmElement *pDmeRoot = CreateElement< CDmElement >( "root", nDmFileId );
+ CDmeModel *pDmeModel = CreateElement< CDmeModel >( "model", nDmFileId );
+
+ char szAnimationName[ MAX_PATH ] = "";
+
+ if ( pszFilename )
+ {
+ V_FileBase( pszFilename, szAnimationName, ARRAYSIZE( szAnimationName ) );
+ }
+ else
+ {
+ pszFilename = "unknown";
+ }
+
+ if ( V_strlen( szAnimationName ) > 0 )
+ {
+ pDmeModel->SetName( szAnimationName );
+ }
+ else
+ {
+ V_strcpy_safe( szAnimationName, "anim" );
+ }
+
+ pDmeRoot->SetValue( "skeleton", pDmeModel );
+
+ if ( !m_bOptAnimation )
+ {
+ // Don't set root.model if animation only, studiomdl can't handle it
+ pDmeRoot->SetValue( "model", pDmeModel );
+ }
+
+ SmdJointMap_t smdJointMap( CDefOps< int >::LessFunc );
+
+ CUtlString sMaterial;
+
+ CTriangle triangle;
+
+ uint nLineNumber = 0;
+ uint nBadPreambleCount = 0;
+ ParserState_t nParserState = kPreamble;
+ int nFrame = 0;
+
+ CDmeChannelsClip *pDmeChannelsClip = NULL;
+
+ if ( m_bOptAnimation )
+ {
+ pDmeChannelsClip = FindOrCreateChannelsClip( pDmeRoot, szAnimationName );
+ }
+
+ bool bStartTimeSet = false;
+
+ CQcData qcData;
+
+ CDmeMesh *pDmeMesh = NULL;
+
+ char szLine[ 4096 ];
+
+ while ( utlBuf.IsValid() )
+ {
+ GetLine( utlBuf, szLine, ARRAYSIZE( szLine ) );
+ ++nLineNumber;
+
+ const char *pszLine = ParserSkipSpace( szLine );
+
+ if ( ParserIsBlankLine( pszLine ) )
+ continue;
+
+ if ( nParserState == kPreamble )
+ {
+ if ( HandleQcHints( pszLine, qcData ) )
+ continue;
+
+ if ( ParserHandleVersion( pszLine ) )
+ {
+ if ( qcData.m_nUpAxis != m_nUpAxis )
+ {
+ Warning( "Importer UpAxis (%d) is different from UpAxis in SMD data (%d), using value found in SMD/QC\n", m_nUpAxis, qcData.m_nUpAxis );
+ }
+
+ SetUpAxis( qcData.m_nUpAxis );
+ nParserState = kGeneral;
+ continue;
+ }
+
+ Error( "%s(%d) : preamble error : expecting comment: \"%s\"\n",
+ pszFilename, nLineNumber, szLine );
+
+ ++nBadPreambleCount;
+ if ( nBadPreambleCount > 10 )
+ {
+ Error( "%s(%d) : preamble error : too many errors, not an SMD file, aborting\n",
+ pszFilename, nLineNumber );
+ break;
+ }
+ }
+ else if ( nParserState == kGeneral )
+ {
+ if ( ParserHandleSectionStart( pszLine, "nodes" ) )
+ {
+ nParserState = kNodes;
+ }
+ else if ( ParserHandleSectionStart( pszLine, "skeleton" ) )
+ {
+ nParserState = kSkeleton;
+ }
+ else if ( ParserHandleSectionStart( pszLine, "triangles" ) )
+ {
+ nParserState = kTriangles;
+ }
+ }
+ else if ( nParserState == kNodes )
+ {
+ if ( ParserHandleSectionStart( pszLine, "end" ) )
+ {
+ ParserCreateJoints( smdJointMap, pDmeModel, pDmeChannelsClip, pszFilename );
+
+ nParserState = kGeneral;
+ continue;
+ }
+
+ CUtlString sName;
+ int nId;
+ int nParentId;
+
+ if ( !m_bOptImportSkeleton || !ParserHandleSkeletonLine( pszLine, sName, nId, nParentId ) )
+ continue;
+
+ if ( !ParserAddJoint( smdJointMap, nId, sName, nParentId, pszFilename, nLineNumber ) )
+ continue;
+ }
+ else if ( nParserState == kSkeleton )
+ {
+ if ( ParserHandleSectionStart( pszLine, "end" ) )
+ {
+ nParserState = kGeneral;
+ continue;
+ }
+
+ if ( m_bOptAnimation && ParserHandleTime( pszLine, nFrame ) )
+ {
+ if ( !bStartTimeSet )
+ {
+ pDmeChannelsClip->SetStartTime( DmeTime_t( static_cast< float >( nFrame ), DmeFramerate_t( m_flFrameRate ) ) );
+ bStartTimeSet = true;
+ }
+ continue;
+ }
+
+ int nId;
+ Vector vPosition;
+ RadianEuler eRadianEulerXYZ;
+
+ if ( sscanf( pszLine, "%d %f %f %f %f %f %f", &nId, &vPosition.x, &vPosition.y, &vPosition.z, &eRadianEulerXYZ.x, &eRadianEulerXYZ.y, &eRadianEulerXYZ.z ) == 7 )
+ {
+ if ( !m_bOptImportSkeleton )
+ continue;
+
+ ParserSetJoint( smdJointMap, nFrame, nId, vPosition, eRadianEulerXYZ, pszFilename, nLineNumber );
+ }
+ }
+ else if ( nParserState == kTriangles )
+ {
+ if ( ParserHandleSectionStart( pszLine, "end" ) )
+ {
+ triangle.Reset();
+ nParserState = kGeneral;
+ continue;
+ }
+
+ int nFallbackSkinJoint;
+ Vector vPosition;
+ Vector vNormal;
+ Vector2D vUV;
+ char szShadingGroup[ ARRAYSIZE( szLine ) ];
+
+ if ( sscanf( pszLine, "%d %f %f %f %f %f %f %f %f",
+ &nFallbackSkinJoint,
+ &vPosition.x, &vPosition.y, &vPosition.z,
+ &vNormal.x, &vNormal.y, &vNormal.z, &vUV.x, &vUV.y ) == 9 )
+ {
+ if ( triangle.m_nVertices >= 3 )
+ {
+ Error( "%s(%d) : triangle error : Too many vertices in triangle\n",
+ pszFilename, nLineNumber );
+ continue;
+ }
+
+ CVertex &vertex( triangle.m_vertices[ triangle.m_nVertices++ ] );
+ vertex.m_vUV = vUV;
+
+ if ( GetUpAxis() != CDmSmdSerializer::Y_AXIS )
+ {
+ VectorTransform( vPosition, m_mAdj, vertex.m_vPosition );
+ VectorTransform( vNormal, m_mAdjNormal, vertex.m_vNormal );
+ }
+ else
+ {
+ vertex.m_vPosition = vPosition;
+ vertex.m_vNormal = vNormal;
+ }
+
+ if ( m_bOptImportSkeleton )
+ {
+ HandleVertexWeights( smdJointMap, nFallbackSkinJoint, vertex, pszLine, pszFilename, nLineNumber );
+ }
+
+ if ( !pDmeMesh )
+ {
+ pDmeMesh = CreateDmeMesh( pDmeModel, pszFilename, nLineNumber );
+ if ( !pDmeMesh )
+ {
+ Error( "%s(%d) : Couldn't create DmeMesh\n",
+ pszFilename, nLineNumber );
+ break;
+ }
+ }
+
+ CreatePolygon( pDmeMesh, sMaterial, triangle, pszFilename, nLineNumber );
+ }
+ else if ( sscanf( pszLine, "%1024s", szShadingGroup ) == 1 )
+ {
+ char pszMaterialPath[ ARRAYSIZE( szShadingGroup ) ];
+ V_StripExtension( szShadingGroup, pszMaterialPath, ARRAYSIZE( pszMaterialPath ) );
+
+ sMaterial = pszMaterialPath;
+
+ triangle.Reset();
+ }
+ else
+ {
+ Error( "%s(%d) : triangle error : unexpected data in triangles\n",
+ pszFilename, nLineNumber );
+ triangle.Reset();
+ }
+ }
+ else
+ {
+ Error( "%s(%d) : parser error : unknown parser state\n",
+ pszFilename, nLineNumber );
+ nParserState = kGeneral;
+ }
+ }
+
+ pDmeModel->CaptureJointsToBaseState( "bind" );
+
+ if ( pDmeChannelsClip )
+ {
+ // Reset skeleton values to the first sample
+ FOR_EACH_MAP_FAST( smdJointMap, nJointMapIndex )
+ {
+ CDmeDag *pDmeDag = smdJointMap.Element( nJointMapIndex ).m_pDmeDag;
+ if ( pDmeDag )
+ {
+ CDmeTransform *pDmeTransform = pDmeDag->GetTransform();
+
+ CDmeVector3Log *pDmePosLog = pDmeDag->GetValueElement< CDmeVector3Log >( "__posLog" );
+ pDmeTransform->SetPosition( pDmePosLog->GetKeyValue( 0 ) );
+
+ CDmeQuaternionLog *pDmeRotLog = pDmeDag->GetValueElement< CDmeQuaternionLog >( "__rotLog" );
+ pDmeTransform->SetOrientation( pDmeRotLog->GetKeyValue( 0 ) );
+ }
+ }
+
+ pDmeChannelsClip->SetDuration( DmeTime_t( nFrame, DmeFramerate_t( m_flFrameRate ) ) - pDmeChannelsClip->GetStartTime() );
+ }
+
+ return pDmeRoot;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void CDmSmdSerializer::FixNodeName( CUtlString &sName ) const
+{
+ char szTmpBuf0[ MAX_PATH ];
+ char szTmpBuf1[ MAX_PATH ];
+
+ V_strncpy( szTmpBuf0, sName.Get(), ARRAYSIZE( szTmpBuf0 ) );
+
+ // Strip trailing quotes
+ char *pszName = szTmpBuf0 + sName.Length() - 1;
+ while ( pszName >= szTmpBuf0 && *pszName && *pszName == '"' )
+ {
+ *pszName = '\0';
+ --pszName;
+ }
+
+ // Strip leading quotes
+ pszName = szTmpBuf0;
+ while ( *pszName && *pszName == '"')
+ {
+ ++pszName;
+ }
+
+ if ( m_bOptAutoStripPrefix )
+ {
+ // Get the string before the '.'
+ V_FileBase( pszName, szTmpBuf1, ARRAYSIZE( szTmpBuf1 ) );
+ V_strncpy( szTmpBuf0, szTmpBuf1, ARRAYSIZE( szTmpBuf0 ) );
+ pszName = szTmpBuf0;
+ }
+
+ if ( !m_sNodeDelPrefix.IsEmpty() && StringHasPrefix( pszName, m_sNodeDelPrefix.Get() ) )
+ {
+ pszName = const_cast< char * >( StringAfterPrefix( pszName, m_sNodeDelPrefix.Get() ) );
+ }
+
+ if ( m_sNodeAddPrefix.IsEmpty() )
+ {
+ sName = pszName;
+ }
+ else
+ {
+ sName = m_sNodeAddPrefix;
+ sName += pszName;
+ }
+} \ No newline at end of file
diff --git a/movieobjects/dmx_to_vcd.cpp b/movieobjects/dmx_to_vcd.cpp
new file mode 100644
index 0000000..a0037bc
--- /dev/null
+++ b/movieobjects/dmx_to_vcd.cpp
@@ -0,0 +1,1052 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/dmx_to_vcd.h"
+#include "movieobjects/movieobjects.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "choreoevent.h"
+#include "iscenetokenprocessor.h"
+#include "characterset.h"
+
+
+bool ConvertEventToDmx( CChoreoEvent *event, CDmeChannelsClip *clip )
+{
+ clip->SetName( event->GetName() );
+ clip->SetValue( "eventtype", CChoreoEvent::NameForType( event->GetType() ) );
+
+ CDmeTimeFrame *tf = clip->GetTimeFrame();
+ Assert( tf );
+ if ( tf )
+ {
+ tf->SetStartTime( DmeTime_t( event->GetStartTime() ) );
+ tf->SetDuration( DmeTime_t( event->GetDuration() ) );
+ }
+
+ clip->SetValue( "parameters", event->GetParameters() );
+ clip->SetValue( "parameters2", event->GetParameters2() );
+
+ // event_ramp is a channel under the event's channels clip
+ CDmrElementArray<> array( clip, "channels" );
+ Assert( array.IsValid() );
+ if ( array.IsValid() )
+ {
+ CDmeChannel *channel = CreateElement< CDmeChannel >( "event_ramp", clip->GetFileId() );
+ array.AddToTail( channel );
+
+ // Fill in values..., just log for now
+ channel->CreateLog( AT_FLOAT );
+ CDmeTypedLog<float> *ramp = static_cast< CDmeTypedLog<float> * >( channel->GetLog() );
+ if ( ramp )
+ {
+ CDmeFloatCurveInfo *pCurveInfo = CreateElement< CDmeFloatCurveInfo >( "floatcurveinfo", clip->GetFileId() );
+
+ pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZE, INTERPOLATE_CATMULL_ROM_NORMALIZE ) );
+ pCurveInfo->SetRightEdgeTime( DmeTime_t( event->GetDuration() ) );
+
+ pCurveInfo->SetUseEdgeInfo( true );
+ pCurveInfo->SetDefaultEdgeZeroValue( 0.0f );
+ // Left edge
+ pCurveInfo->SetEdgeInfo( 0,
+ event->GetRamp()->IsEdgeActive( true ),
+ event->GetRamp()->GetEdgeZeroValue( true ),
+ event->GetRamp()->GetEdgeCurveType( true ) );
+ // Right edge
+ pCurveInfo->SetEdgeInfo( 1,
+ event->GetRamp()->IsEdgeActive( false ),
+ event->GetRamp()->GetEdgeZeroValue( false ),
+ event->GetRamp()->GetEdgeCurveType( false ) );
+
+ ramp->SetCurveInfo( pCurveInfo );
+
+ int rampCount = event->GetRampCount();
+ for ( int j = 0; j < rampCount; ++j )
+ {
+ CExpressionSample *sample = event->GetRamp( j );
+ ramp->SetKey( DmeTime_t( sample->time ), sample->value, sample->GetCurveType() );
+ }
+ }
+ }
+
+ clip->SetValue< float >( "pitch", event->GetPitch() );
+ clip->SetValue< float >( "yaw", event->GetYaw() );
+
+ clip->SetValue< bool >( "resumecondition", event->IsResumeCondition() );
+ clip->SetValue< bool >( "lockbodyfacing", event->IsLockBodyFacing() );
+ clip->SetValue< float >( "distancetotarget", event->GetDistanceToTarget() );
+ clip->SetValue< bool >( "fixedlength", event->IsFixedLength() );
+ clip->SetValue< bool >( "forceshortmovement", event->GetForceShortMovement() );
+ clip->SetValue< bool >( "active", event->GetActive() );
+
+ // Relative tags
+ if ( event->GetNumRelativeTags() > 0 )
+ {
+ CDmElement *tags = CreateElement< CDmElement >( "relative_tags", clip->GetFileId() );
+ if ( tags )
+ {
+ clip->SetValue< CDmElement >( "tags", tags );
+
+ // Now create arrays for the name and percentages
+ CDmrStringArray names( tags, "tagname", true );
+ CDmrArray<float> percentages( tags, "tagpercentage", true );
+ Assert( names.IsValid() && percentages.IsValid() );
+
+ for ( int t = 0; t < event->GetNumRelativeTags(); t++ )
+ {
+ CEventRelativeTag *rt = event->GetRelativeTag( t );
+ Assert( rt );
+
+ names.AddToTail( rt->GetName() );
+ percentages.AddToTail( rt->GetPercentage() );
+ }
+ }
+ }
+
+ // Timing tags
+ if ( event->GetNumTimingTags() > 0 )
+ {
+ CDmElement *tags = CreateElement< CDmElement >( "timing_tags", clip->GetFileId() );
+ if ( tags )
+ {
+ clip->SetValue< CDmElement >( "flextimingtags", tags );
+
+ // Now create arrays for the name and percentages
+ CDmrStringArray names( tags, "tagname", true );
+ CDmrArray<float> percentages( tags, "tagpercentage", true );
+ CDmrArray<bool> lockstates( tags, "lockedstate", true );
+ Assert( names.IsValid() && percentages.IsValid() && lockstates.IsValid() );
+
+ for ( int t = 0; t < event->GetNumTimingTags(); t++ )
+ {
+ CFlexTimingTag *tt = event->GetTimingTag( t );
+ Assert( tt );
+
+ names.AddToTail( tt->GetName() );
+ percentages.AddToTail( tt->GetPercentage() );
+ lockstates.AddToTail( tt->GetLocked() );
+ }
+ }
+ }
+
+ // Abs tags
+ int tagtype;
+ for ( tagtype = 0; tagtype < CChoreoEvent::NUM_ABS_TAG_TYPES; tagtype++ )
+ {
+ if ( event->GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)tagtype ) > 0 )
+ {
+ char sz[ 512 ];
+
+ Q_snprintf( sz, sizeof( sz ), "absolutetags %s", CChoreoEvent::NameForAbsoluteTagType( (CChoreoEvent::AbsTagType)tagtype ) );
+
+ CDmElement *tags = CreateElement< CDmElement >( sz, clip->GetFileId() );
+ if ( tags )
+ {
+ clip->SetValue< CDmElement >( sz, tags );
+
+ // Now create arrays for the name and percentages
+ CDmrStringArray names( tags, "tagname", true );
+ CDmrArray<float> percentages( tags, "tagpercentage", true );
+ Assert( names.IsValid() && percentages.IsValid() );
+ for ( int t = 0; t < event->GetNumAbsoluteTags( (CChoreoEvent::AbsTagType)tagtype ); t++ )
+ {
+ CEventAbsoluteTag *abstag = event->GetAbsoluteTag( (CChoreoEvent::AbsTagType)tagtype, t );
+ Assert( abstag );
+
+ names.AddToTail( abstag->GetName() );
+ percentages.AddToTail( abstag->GetPercentage() );
+ }
+ }
+ }
+ }
+
+ // IsUsingRelativeTag
+ if ( event->IsUsingRelativeTag() )
+ {
+ CDmElement *relativeTag = CreateElement< CDmElement >( "relative_tag", clip->GetFileId() );
+ if ( relativeTag )
+ {
+ clip->SetValue< CDmElement >( "relative_tag", relativeTag );
+
+ relativeTag->SetValue( "tagname", event->GetRelativeTagName() );
+ relativeTag->SetValue( "tagwav", event->GetRelativeWavName() );
+ }
+ }
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ CDmElement *speakProperties = CreateElement< CDmElement >( "SPEAK", clip->GetFileId() );
+ if ( speakProperties )
+ {
+ clip->SetValue< CDmElement >( "SPEAK", speakProperties );
+
+ speakProperties->SetValue( "closedcaption_type", CChoreoEvent::NameForCCType( event->GetCloseCaptionType() ) );
+ speakProperties->SetValue( "closedcaption_token", event->GetCloseCaptionToken() );
+ bool usingCombined = ( event->GetCloseCaptionType() != CChoreoEvent::CC_DISABLED && event->IsUsingCombinedFile() );
+ speakProperties->SetValue< bool >( "closedcaption_usingcombinedfile", usingCombined );
+ speakProperties->SetValue< bool >( "closedcaption_combinedusesgender", event->IsCombinedUsingGenderToken() );
+ speakProperties->SetValue< bool >( "closedcaption_noattenuate", event->IsSuppressingCaptionAttenuation() );
+ }
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CDmElement *gestureProperties = CreateElement< CDmElement >( "GESTURE", clip->GetFileId() );
+ if ( gestureProperties )
+ {
+ clip->SetValue< CDmElement >( "GESTURE", gestureProperties );
+
+ float duration;
+ event->GetGestureSequenceDuration( duration );
+ gestureProperties->SetValue< float >( "sequenceduration", duration );
+ }
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ // Save flex animation tracks as channels
+ CDmrElementArray<> array( clip, "channels" );
+ Assert( array.IsValid() );
+ if ( array.IsValid() )
+ {
+ // Now add a DmeChannel for each flex controller
+ int numTracks = event->GetNumFlexAnimationTracks();
+ for ( int i = 0 ; i < numTracks; ++i )
+ {
+ CFlexAnimationTrack *track = event->GetFlexAnimationTrack( i );
+
+ CDmeChannel *channel = CreateElement< CDmeChannel >( track->GetFlexControllerName(), clip->GetFileId() );
+ array.AddToTail( channel );
+
+ channel->SetValue< bool >( "flexchannel", true );
+
+ channel->SetValue< bool >( "disabled", !track->IsTrackActive() );
+ channel->SetValue< bool >( "combo", track->IsComboType() );
+
+ channel->SetValue< float >( "rangemin", track->GetMin() );
+ channel->SetValue< float >( "rangemax", track->GetMax() );
+
+ channel->SetValue< bool >( "isbalancechannel", false );
+
+ // Fill in values..., just log for now
+ channel->CreateLog( AT_FLOAT );
+ CDmeTypedLog<float> *log = static_cast< CDmeTypedLog<float> * >( channel->GetLog() );
+ if ( log )
+ {
+ CDmeFloatCurveInfo *pCurveInfo = CreateElement< CDmeFloatCurveInfo >( "floatcurveinfo", clip->GetFileId() );
+
+ pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) );
+ pCurveInfo->SetRightEdgeTime( DmeTime_t( event->GetDuration() ) );
+
+ pCurveInfo->SetUseEdgeInfo( true );
+ pCurveInfo->SetDefaultEdgeZeroValue( 0.0f );
+ // Left edge
+ pCurveInfo->SetEdgeInfo( 0,
+ track->IsEdgeActive( true ),
+ track->GetEdgeZeroValue( true ),
+ track->GetEdgeCurveType( true ) );
+ // Right edge
+ pCurveInfo->SetEdgeInfo( 1,
+ track->IsEdgeActive( false ),
+ track->GetEdgeZeroValue( false ),
+ track->GetEdgeCurveType( false ) );
+
+ log->SetCurveInfo( pCurveInfo );
+
+ // Now set up edge properties
+
+ int sampleCount = track->GetNumSamples();
+ for ( int j = 0; j < sampleCount; ++j )
+ {
+ CExpressionSample *sample = track->GetSample( j );
+ log->SetKey( DmeTime_t( sample->time ), sample->value, sample->GetCurveType() );
+ }
+ }
+
+ // Right out the stereo "balance" curve
+ if ( track->IsComboType() )
+ {
+ char balanceChannelName[ 512 ];
+ Q_snprintf( balanceChannelName, sizeof( balanceChannelName ), "%s_balance", track->GetFlexControllerName() );
+
+ CDmeChannel *balanceChannel = CreateElement< CDmeChannel >( balanceChannelName, clip->GetFileId() );
+ array.AddToTail( balanceChannel );
+
+ channel->SetValue( "balanceChannel", balanceChannel );
+
+ balanceChannel->SetValue< bool >( "flexchannel", true );
+
+ balanceChannel->SetValue< bool >( "disabled", !track->IsTrackActive() );
+ balanceChannel->SetValue< bool >( "isbalancechannel", true );
+
+ balanceChannel->CreateLog( AT_FLOAT );
+ CDmeTypedLog< float > *balance = static_cast< CDmeTypedLog< float > * >( balanceChannel->GetLog() );
+ if ( balance )
+ {
+ CDmeFloatCurveInfo *pCurveInfo = CreateElement< CDmeFloatCurveInfo >( "floatcurveinfo", clip->GetFileId() );
+
+ pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZEX, INTERPOLATE_CATMULL_ROM_NORMALIZEX ) );
+ pCurveInfo->SetRightEdgeTime( DmeTime_t( event->GetDuration() ) );
+
+ pCurveInfo->SetUseEdgeInfo( false );
+ pCurveInfo->SetDefaultEdgeZeroValue( 0.5f );
+
+ /*
+ // Don't need to support edge properties for balance curves?
+ // Left edge
+ pCurveInfo->SetEdgeInfo( 0,
+ track->IsEdgeActive( true ),
+ track->GetEdgeZeroValue( true ),
+ track->GetEdgeCurveType( true ) );
+ // Right edge
+ pCurveInfo->SetEdgeInfo( 1,
+ track->IsEdgeActive( false ),
+ track->GetEdgeZeroValue( false ),
+ track->GetEdgeCurveType( false ) );
+ */
+
+ balance->SetCurveInfo( pCurveInfo );
+
+ // Now set up edge properties
+
+ int sampleCount = track->GetNumSamples( 1 );
+ for ( int j = 0; j < sampleCount; ++j )
+ {
+ CExpressionSample *sample = track->GetSample( j, 1 );
+ balance->SetKey( DmeTime_t( sample->time ), sample->value, sample->GetCurveType() );
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ CDmElement *loopProperties = CreateElement< CDmElement >( "LOOP", clip->GetFileId() );
+ if ( loopProperties )
+ {
+ clip->SetValue< CDmElement >( "LOOP", loopProperties );
+
+ loopProperties->SetValue< int >( "loopcount", event->GetLoopCount() );
+ }
+ }
+ break;
+ }
+
+ return true;
+}
+
+bool ConvertSceneToDmx( CChoreoScene *scene, CDmeFilmClip *dmx )
+{
+ bool bret = true;
+
+ dmx->SetName( scene->GetFilename() );
+
+ CDmeTimeFrame *tf = dmx->GetTimeFrame();
+ Assert( tf );
+ if ( tf )
+ {
+ tf->SetDuration( DmeTime_t( scene->FindStopTime() ) );
+ }
+
+ CDmElement *scaleSettings = CreateElement< CDmElement >( "scalesettings", dmx->GetFileId() );
+ Assert( scaleSettings );
+ CDmAttribute *scaleAttribute = dmx->AddAttributeElement< CDmElement >( "scalesettings" );
+ Assert( scaleAttribute );
+ scaleAttribute->SetValue( scaleSettings->GetHandle() );
+ if ( scaleSettings )
+ {
+ for ( int i = scene->TimeZoomFirst(); i != scene->TimeZoomInvalid(); i = scene->TimeZoomNext( i ) )
+ {
+ const char *name = scene->TimeZoomName( i );
+ int value = scene->GetTimeZoom( name );
+
+ scaleSettings->SetValue< int >( name, value );
+ }
+ }
+
+ CDmeTrackGroup *pTrackGroup = dmx->FindOrAddTrackGroup( VCD_SCENE_RAMP_TRACK_GROUP_NAME );
+ CDmeTrack *track = pTrackGroup->FindOrAddTrack( VCD_SCENE_RAMP_TRACK_GROUP_NAME, DMECLIP_CHANNEL );
+ Assert( track );
+
+ // Set a CDmeChannel for the scene_ramp
+ CDmeChannelsClip *pClip = CreateElement< CDmeChannelsClip >( VCD_SCENE_RAMP_TRACK_GROUP_NAME, dmx->GetFileId() );
+ Assert( pClip );
+ track->AddClip( pClip );
+
+ int rampCount = scene->GetSceneRampCount();
+ if ( rampCount > 0 )
+ {
+ // scene_ramp is a channels
+ CDmrElementArray<> array( pClip, "channels" );
+ Assert( array.IsValid() );
+ if ( array.IsValid() )
+ {
+ CDmeChannel *channel = CreateElement< CDmeChannel >( VCD_SCENE_RAMP_TRACK_GROUP_NAME, dmx->GetFileId() );
+ array.AddToTail( channel );
+
+ // Fill in values..., just log for now
+ channel->CreateLog( AT_FLOAT );
+ CDmeTypedLog<float> *ramp = static_cast< CDmeTypedLog<float> * >( channel->GetLog() );
+ if ( ramp )
+ {
+ CDmeFloatCurveInfo *pCurveInfo = CreateElement< CDmeFloatCurveInfo >( "floatcurveinfo", dmx->GetFileId() );
+
+ pCurveInfo->SetDefaultCurveType( MAKE_CURVE_TYPE( INTERPOLATE_CATMULL_ROM_NORMALIZE, INTERPOLATE_CATMULL_ROM_NORMALIZE ) );
+ pCurveInfo->SetRightEdgeTime( DmeTime_t( scene->FindStopTime() ) );
+
+ pCurveInfo->SetUseEdgeInfo( true );
+ pCurveInfo->SetDefaultEdgeZeroValue( 0.0f );
+ // Left edge
+ pCurveInfo->SetEdgeInfo( 0,
+ scene->GetSceneRamp()->IsEdgeActive( true ),
+ scene->GetSceneRamp()->GetEdgeZeroValue( true ),
+ scene->GetSceneRamp()->GetEdgeCurveType( true ) );
+ // Right edge
+ pCurveInfo->SetEdgeInfo( 1,
+ scene->GetSceneRamp()->IsEdgeActive( false ),
+ scene->GetSceneRamp()->GetEdgeZeroValue( false ),
+ scene->GetSceneRamp()->GetEdgeCurveType( false ) );
+
+ ramp->SetCurveInfo( pCurveInfo );
+
+ for ( int j = 0; j < rampCount; ++j )
+ {
+ CExpressionSample *sample = scene->GetSceneRamp( j );
+ ramp->SetKey( DmeTime_t( sample->time ), sample->value, sample->GetCurveType() );
+ }
+ }
+ }
+ }
+
+ // Walk the actors and channels
+ int numActors = scene->GetNumActors();
+ for ( int actor = 0; actor < numActors; ++actor )
+ {
+ CChoreoActor *pActor = scene->GetActor( actor );
+ Assert( pActor );
+ if ( !pActor )
+ continue;
+
+ CDmeTrackGroup *pTrackGroup = dmx->FindOrAddTrackGroup( pActor->GetName() );
+ Assert( pTrackGroup );
+
+ pTrackGroup->SetValue< bool >( "isActor", true );
+ pTrackGroup->SetValue< bool >( "actorDisabled", !pActor->GetActive() );
+ pTrackGroup->SetValue( "actorModel", pActor->GetFacePoserModelName() );
+
+ int numChannels = pActor->GetNumChannels();
+ for ( int channel = 0; channel < numChannels; ++channel )
+ {
+ CChoreoChannel *pChannel = pActor->GetChannel( channel );
+ Assert( pChannel );
+ if ( !pChannel )
+ continue;
+
+ const char *channelName = pChannel->GetName();
+ CDmeTrack *track = pTrackGroup->FindOrAddTrack( channelName, DMECLIP_CHANNEL );
+ Assert( track );
+ if ( !track )
+ continue;
+
+ track->SetMute( !pChannel->GetActive() );
+
+ int numEvents = pChannel->GetNumEvents();
+ for ( int event = 0; event < numEvents; ++event )
+ {
+ CChoreoEvent *pEvent = pChannel->GetEvent( event );
+ Assert( pEvent );
+ if ( !pEvent )
+ continue;
+
+ // Set up event
+ CDmeChannelsClip *pClip = CreateElement< CDmeChannelsClip >( "", dmx->GetFileId() );
+ Assert( pClip );
+ track->AddClip( pClip );
+
+ // Fill in data
+ bool success = ConvertEventToDmx( pEvent, pClip );
+ if ( !success )
+ {
+ bret = false;
+ Assert( 0 );
+ break;
+ }
+ }
+ }
+ }
+
+ pTrackGroup = dmx->FindOrAddTrackGroup( VCD_GLOBAL_EVENTS_TRACK_GROUP_NAME );
+ Assert( pTrackGroup );
+
+ track = pTrackGroup->FindOrAddTrack( VCD_GLOBAL_EVENTS_TRACK_GROUP_NAME, DMECLIP_CHANNEL );
+ Assert( track );
+
+ // Now add global events
+ int numEvents = scene->GetNumEvents();
+ for ( int event = 0; event < numEvents; ++event )
+ {
+ CChoreoEvent *pEvent = scene->GetEvent( event );
+ if ( !pEvent || pEvent->GetActor() )
+ continue;
+
+ // Set up event
+ CDmeChannelsClip *pClip = CreateElement< CDmeChannelsClip >( "", dmx->GetFileId() );
+ Assert( pClip );
+
+ track->AddClip( pClip );
+
+ // Fill in data
+ bool success = ConvertEventToDmx( pEvent, pClip );
+ if ( !success )
+ {
+ bret = false;
+ Assert( 0 );
+ break;
+ }
+ }
+
+ dmx->SetValue( "associated_bsp", scene->GetMapname() );
+ dmx->SetValue< float >( "fps", scene->GetSceneFPS() );
+ dmx->SetValue< bool >( "snap", scene->IsUsingFrameSnap() );
+
+ return bret;
+}
+
+void EnsureActorAndChannelForTrack( CChoreoScene *scene, CDmeTrackGroup *pActor, CDmeTrack *pChannel )
+{
+ const char *actorName = pActor->GetName();
+ const char *channelName = pChannel->GetName();
+
+ CChoreoActor *a = scene->FindActor( actorName );
+ if ( !a )
+ {
+ a = scene->AllocActor();
+ Assert( a );
+ a->SetName( actorName );
+ a->SetActive( !pActor->GetValue< bool >( "actorDisabled" ) );
+ a->SetFacePoserModelName( pActor->GetValueString( "actorModel" ) );
+ }
+
+ CChoreoChannel *c = a->FindChannel( channelName );
+ if ( !c )
+ {
+ c = scene->AllocChannel();
+ Assert( c );
+ c->SetName( channelName );
+ c->SetActor( a );
+ c->SetActive( !pChannel->IsMute() );
+ a->AddChannel( c );
+ }
+}
+
+template< class T >
+T *FindAttributeInArray( const CDmrElementArray<> &array, const char *elementName )
+{
+ int c = array.Count();
+ for ( int i = 0; i < c; ++i )
+ {
+ T *element = CastElement< T >( array[ i ] );
+ if ( !element )
+ continue;
+
+ if ( !Q_stricmp( element->GetName(), elementName ) )
+ return element;
+ }
+
+ return NULL;
+}
+
+bool ConvertDmxToEvent( CChoreoScene *scene, CDmeTrackGroup *pActor, CDmeTrack *pChannel, CDmeChannelsClip *clip, bool globalEvent )
+{
+ bool bret = true;
+
+ // Allocate choreo event
+ CChoreoEvent *event = scene->AllocEvent();
+ Assert( event );
+ if ( !event )
+ {
+ bret = false;
+ return bret;
+ }
+
+ event->SetName( clip->GetName() );
+ event->SetType( CChoreoEvent::TypeForName( clip->GetValueString( "eventtype" ) ) );
+
+ if ( !globalEvent )
+ {
+ EnsureActorAndChannelForTrack( scene, pActor, pChannel );
+
+ const char *actorName = pActor->GetName();
+ const char *channelName = pChannel->GetName();
+
+ CChoreoActor *a = scene->FindActor( actorName );
+ Assert( a );
+ CChoreoChannel *c = a->FindChannel( channelName );
+ Assert( c );
+
+ if ( !a || !c )
+ {
+ bret = false;
+ return bret;
+ }
+
+ event->SetActor( a );
+ event->SetChannel( c );
+ c->AddEvent( event );
+ }
+
+ // Set timeframe info
+ CDmeTimeFrame *tf = clip->GetTimeFrame();
+ Assert( tf );
+ if ( tf )
+ {
+ event->SetStartTime( tf->GetStartTime().GetSeconds() );
+ float duration = tf->GetDuration().GetSeconds();
+ if ( duration <= 0.0f )
+ {
+ event->SetEndTime( -1.0f );
+ }
+ else
+ {
+ event->SetEndTime( event->GetStartTime() + duration );
+ }
+ }
+
+ event->SetParameters( clip->GetValueString( "parameters" ) );
+ event->SetParameters2( clip->GetValueString( "parameters2" ) );
+
+ const CDmrElementArray<> array( clip, "channels" );
+ Assert( array.IsValid() );
+ if ( array.IsValid() )
+ {
+ int c = array.Count();
+ for ( int i = 0 ; i < c; ++i )
+ {
+ CDmeChannel *channel = CastElement< CDmeChannel >( array[i] );
+ if ( !channel || Q_stricmp( channel->GetName(), "event_ramp" ) )
+ continue;
+
+ CDmeTypedLog< float > *ramp = static_cast< CDmeTypedLog< float > * >( channel->GetLog() );
+ if ( !ramp )
+ continue;
+
+ bool active[ 2 ];
+ float value[ 2 ];
+ int curveType[ 2 ];
+
+ ramp->GetEdgeInfo( 0, active[ 0 ], value[ 0 ], curveType[ 0 ] );
+ ramp->GetEdgeInfo( 1, active[ 1 ], value[ 1 ], curveType[ 1 ] );
+
+ event->GetRamp()->SetEdgeActive( true, active[ 0 ] );
+ event->GetRamp()->SetEdgeActive( false, active[ 1 ] );
+
+ event->GetRamp()->SetEdgeInfo( true, curveType[ 0 ], value[ 0 ] );
+ event->GetRamp()->SetEdgeInfo( false, curveType[ 1 ], value[ 1 ] );
+
+ int rampCount = ramp->GetKeyCount();
+ for ( int j = 0; j < rampCount; ++j )
+ {
+ CExpressionSample *sample = event->AddRamp( ramp->GetKeyTime( j ).GetSeconds(), ramp->GetKeyValue( j ), false );
+ sample->SetCurveType( ramp->GetKeyCurveType( j ) );
+ }
+ }
+ }
+
+ event->SetPitch( clip->GetValue< float >( "pitch" ) );
+ event->SetYaw( clip->GetValue< float >( "yaw" ) );
+
+ event->SetResumeCondition( clip->GetValue< bool >( "resumecondition" ) );
+ event->SetLockBodyFacing( clip->GetValue< bool >( "lockbodyfacing" ) );
+ event->SetDistanceToTarget( clip->GetValue< float >( "distancetotarget" ) );
+ event->SetFixedLength( clip->GetValue< bool >( "fixedlength" ) );
+ event->SetForceShortMovement( clip->GetValue< bool >( "forceshortmovement" ) );
+ event->SetActive( clip->GetValue< bool >( "active" ) );
+
+ if ( clip->HasAttribute( "tags" ) )
+ {
+ CDmElement *tags = clip->GetValueElement< CDmElement >( "tags" );
+ if ( tags )
+ {
+ // Now create arrays for the name and percentages
+ const CDmrStringArray names( tags, "tagname" );
+ const CDmrArray<float> percentages( tags, "tagpercentage" );
+ Assert( names.IsValid() && percentages.IsValid() );
+
+ Assert( names.Count() == percentages.Count() );
+ for ( int t = 0; t < names.Count(); t++ )
+ {
+ event->AddRelativeTag( names[t], percentages[t] );
+ }
+ }
+ }
+
+ if ( clip->HasAttribute( "timing_tags" ) )
+ {
+ CDmElement *tags = clip->GetValueElement< CDmElement >( "timing_tags" );
+ if ( tags )
+ {
+ // Now create arrays for the name and percentages
+ const CDmrStringArray names( tags, "tagname" );
+ const CDmrArray<float> percentages( tags, "tagpercentage" );
+ const CDmrArray<bool> lockstates( tags, "lockedstate" );
+
+ Assert( names.IsValid() && percentages.IsValid() && lockstates.IsValid() );
+ Assert( names.Count() == percentages.Count() && names.Count() == lockstates.Count() );
+
+ for ( int t = 0; t < names.Count(); t++ )
+ {
+ event->AddTimingTag( names[ t ], percentages[ t ], lockstates[ t ] );
+ }
+ }
+ }
+
+
+ // Abs tags
+ int tagtype;
+ for ( tagtype = 0; tagtype < CChoreoEvent::NUM_ABS_TAG_TYPES; tagtype++ )
+ {
+ char sz[ 512 ];
+ Q_snprintf( sz, sizeof( sz ), "absolutetags %s", CChoreoEvent::NameForAbsoluteTagType( (CChoreoEvent::AbsTagType)tagtype ) );
+
+ if ( clip->HasAttribute( sz ) )
+ {
+ CDmElement *tags = clip->GetValueElement< CDmElement >( sz );
+ if ( tags )
+ {
+ // Now create arrays for the name and percentages
+ const CDmrStringArray names( tags, "tagname" );
+ const CDmrArray<float> percentages( tags, "tagpercentage" );
+
+ Assert( names.IsValid() && percentages.IsValid() );
+ Assert( names.Count() == percentages.Count() );
+
+ for ( int t = 0; t < names.Count(); t++ )
+ {
+ event->AddAbsoluteTag( (CChoreoEvent::AbsTagType)tagtype, names[ t ], percentages[ t ] );
+ }
+ }
+ }
+ }
+
+ if ( clip->HasAttribute( "relative_tag" ) )
+ {
+ CDmElement *relativeTag = clip->GetValueElement< CDmElement >( "relative_tag" );
+ if ( relativeTag )
+ {
+ event->SetUsingRelativeTag
+ (
+ true,
+ relativeTag->GetValueString( "tagname" ),
+ relativeTag->GetValueString( "tagwav" )
+ );
+ }
+ }
+
+ switch ( event->GetType() )
+ {
+ default:
+ break;
+ case CChoreoEvent::SPEAK:
+ {
+ CDmElement *speakProperties = NULL;
+ if ( clip->HasAttribute( "SPEAK" ) )
+ {
+ speakProperties = clip->GetValueElement< CDmElement >( "SPEAK" );
+ }
+ if ( speakProperties )
+ {
+ event->SetCloseCaptionType( CChoreoEvent::CCTypeForName( speakProperties->GetValueString( "closedcaption_type" ) ) );
+ event->SetCloseCaptionToken( speakProperties->GetValueString( "closedcaption_token" ) );
+ event->SetUsingCombinedFile( speakProperties->GetValue< bool >( "closedcaption_usingcombinedfile" ) );
+ event->SetCombinedUsingGenderToken( speakProperties->GetValue< bool >( "closedcaption_combinedusesgender" ) );
+ event->SetSuppressingCaptionAttenuation( speakProperties->GetValue< bool >( "closedcaption_noattenuate" ) );
+ }
+ }
+ break;
+ case CChoreoEvent::GESTURE:
+ {
+ CDmElement *gestureProperties = NULL;
+ if ( clip->HasAttribute( "GESTURE" ) )
+ {
+ gestureProperties = clip->GetValueElement< CDmElement >( "GESTURE" );
+ }
+ if ( gestureProperties )
+ {
+ if ( Q_stricmp( clip->GetName(), "NULL" ) )
+ {
+ event->SetGestureSequenceDuration( gestureProperties->GetValue< float >( "sequenceduration" ) );
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::FLEXANIMATION:
+ {
+ // Save flex animation tracks as channels
+ const CDmrElementArray<> array( clip, "channels" );
+ if ( array.IsValid() )
+ {
+ // Now add a DmeChannel for each flex controller
+ int numTracks = array.Count();
+ for ( int i = 0 ; i < numTracks; ++i )
+ {
+ CDmeChannel *channel = CastElement< CDmeChannel >( array[ i ] );
+ Assert( channel );
+ if ( !channel )
+ {
+ bret = false;
+ break;
+ }
+
+ if ( !channel->HasAttribute( "flexchannel" ) )
+ continue;
+
+ // Skip all helper channels, only care about flexchannels
+ if ( !channel->GetValue< bool >( "flexchannel" ) )
+ continue;
+
+ // Skip the balance channels, we'll pull their data below
+ if ( channel->GetValue< bool >( "isbalancechannel" ) )
+ continue;
+
+ Assert( !Q_stristr( channel->GetName(), "_balance" ) );
+
+ CFlexAnimationTrack *track = event->AddTrack( channel->GetName() );
+ Assert( track );
+
+ if ( !track )
+ {
+ bret = false;
+ break;
+ }
+
+ track->SetTrackActive( !channel->GetValue< bool >( "disabled" ) );
+ track->SetComboType( channel->GetValue< bool >( "combo" ) );
+
+ track->SetMin( channel->GetValue< float >( "rangemin" ) );
+ track->SetMax( channel->GetValue< float >( "rangemax" ) );
+
+ CDmeTypedLog<float> *log = static_cast< CDmeTypedLog<float> * >( channel->GetLog() );
+ if ( log )
+ {
+ bool active[ 2 ];
+ float value[ 2 ];
+ int curveType[ 2 ];
+
+ log->GetEdgeInfo( 0, active[ 0 ], value[ 0 ], curveType[ 0 ] );
+ log->GetEdgeInfo( 1, active[ 1 ], value[ 1 ], curveType[ 1 ] );
+
+ track->SetEdgeActive( true, active[ 0 ] );
+ track->SetEdgeActive( false, active[ 1 ] );
+
+ track->SetEdgeInfo( true, curveType[ 0 ], value[ 0 ] );
+ track->SetEdgeInfo( false, curveType[ 1 ], value[ 1 ] );
+
+ int sampleCount = log->GetKeyCount();
+ for ( int j = 0; j < sampleCount; ++j )
+ {
+ int curveType = log->GetKeyCurveType( j );
+ float value = log->GetKeyValue( j );
+ DmeTime_t time = log->GetKeyTime( j );
+
+ CExpressionSample *sample = track->AddSample( time.GetSeconds(), value, 0 );
+ sample->SetCurveType( curveType );
+ }
+ }
+
+ // Right out the stereo "balance" curve
+ if ( track->IsComboType() )
+ {
+ char balanceChannelName[ 512 ];
+ Q_snprintf( balanceChannelName, sizeof( balanceChannelName ), "%s_balance", track->GetFlexControllerName() );
+
+ // Find the balance data
+ CDmeChannel *balanceChannel = FindAttributeInArray< CDmeChannel >( array, balanceChannelName );
+ if ( balanceChannel )
+ {
+ CDmeTypedLog< float > *balance = static_cast< CDmeTypedLog< float > * >( balanceChannel->GetLog() );
+ if ( balance )
+ {
+ // Now set up edge properties
+ int sampleCount = balance->GetKeyCount();
+ for ( int j = 0; j < sampleCount; ++j )
+ {
+ int curveType = balance->GetKeyCurveType( j );
+ float value = balance->GetKeyValue( j );
+ DmeTime_t time = balance->GetKeyTime( j );
+
+ CExpressionSample *sample = track->AddSample( time.GetSeconds(), value, 1 );
+ sample->SetCurveType( curveType );
+ }
+ }
+ else
+ {
+ char msg[ 512 ];
+ Q_snprintf( msg, sizeof( msg ), "Error: Missing balance channel for combo flex track (%s) in (%s)\n", track->GetFlexControllerName(), event->GetName() );
+ Warning( msg );
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ case CChoreoEvent::LOOP:
+ {
+ CDmElement *loopProperties = NULL;
+ if ( clip->HasAttribute( "LOOP" ) )
+ {
+ loopProperties = clip->GetValueElement< CDmElement >( "LOOP" );
+ }
+ if ( loopProperties )
+ {
+ event->SetLoopCount( loopProperties->GetValue< int >( "loopcount" ) );
+ }
+ }
+ break;
+ }
+
+ return true;
+}
+
+bool ConvertDmxToScene( CDmeFilmClip *dmx, CChoreoScene *scene )
+{
+ bool bret = true;
+
+ // This should have been created correctly already
+ // Assert( !Q_stricmp( scene->GetFilename(), dmx->GetName() ) );
+
+ CDmElement *scaleSettings = dmx->GetValueElement< CDmElement >( "scalesettings" );
+ Assert( scaleSettings );
+ if ( scaleSettings )
+ {
+ CDmAttribute *setting = scaleSettings->FirstAttribute();
+ for ( ; setting ; setting = setting->NextAttribute() )
+ {
+ if ( setting->GetType() != AT_INT )
+ continue;
+
+ scene->SetTimeZoom( setting->GetName(), setting->GetValue<int>() );
+ }
+ }
+
+ // Deal with the scene ramp
+ CDmeTrackGroup *pTrackGroup = dmx->FindTrackGroup( VCD_SCENE_RAMP_TRACK_GROUP_NAME );
+ if ( pTrackGroup )
+ {
+ CDmeTrack *track = pTrackGroup->FindTrack( VCD_SCENE_RAMP_TRACK_GROUP_NAME );
+ if ( track )
+ {
+ CDmeChannelsClip *pClip = CastElement< CDmeChannelsClip >( track->FindNamedClip( VCD_SCENE_RAMP_TRACK_GROUP_NAME ) );
+ if ( pClip )
+ {
+ // scene_ramp should be the first subchannel
+ const CDmrElementArray<> array( pClip, "channels" );
+ Assert( array.IsValid() );
+ if ( array.IsValid() && ( array.Count() > 0 ) )
+ {
+ CDmeChannel *channel = CastElement< CDmeChannel >( array[0] );
+ if ( channel )
+ {
+ CDmeTypedLog< float > *ramp = static_cast< CDmeTypedLog< float > * >( channel->GetLog() );
+ if ( ramp )
+ {
+ bool active[ 2 ];
+ float value[ 2 ];
+ int curveType[ 2 ];
+
+ ramp->GetEdgeInfo( 0, active[ 0 ], value[ 0 ], curveType[ 0 ] );
+ ramp->GetEdgeInfo( 1, active[ 1 ], value[ 1 ], curveType[ 1 ] );
+
+ scene->GetSceneRamp()->SetEdgeActive( true, active[ 0 ] );
+ scene->GetSceneRamp()->SetEdgeActive( false, active[ 1 ] );
+
+ scene->GetSceneRamp()->SetEdgeInfo( true, curveType[ 0 ], value[ 0 ] );
+ scene->GetSceneRamp()->SetEdgeInfo( false, curveType[ 1 ], value[ 1 ] );
+
+ int rampCount = ramp->GetKeyCount();
+ for ( int j = 0; j < rampCount; ++j )
+ {
+ CExpressionSample *sample = scene->AddSceneRamp( ramp->GetKeyTime( j ).GetSeconds(), ramp->GetKeyValue( j ), false );
+ sample->SetCurveType( ramp->GetKeyCurveType( j ) );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Deal with global events
+ pTrackGroup = dmx->FindTrackGroup( VCD_GLOBAL_EVENTS_TRACK_GROUP_NAME );
+ if ( pTrackGroup )
+ {
+ CDmeTrack *pTrack = pTrackGroup->FindTrack( VCD_GLOBAL_EVENTS_TRACK_GROUP_NAME );
+ if ( pTrack )
+ {
+ // Now add global events
+ int numEvents = pTrack->GetClipCount();
+ for ( int event = 0; event < numEvents; ++event )
+ {
+ CDmeChannelsClip *pClip = CastElement< CDmeChannelsClip >( pTrack->GetClip( event ) );
+ if ( !pClip )
+ continue;
+
+ bool success = ConvertDmxToEvent( scene, pTrackGroup, pTrack, pClip, true );
+ if ( !success )
+ {
+ bret = false;
+ Assert( 0 );
+ break;
+ }
+ }
+ }
+ }
+
+ int nNumTrackGroups = dmx->GetTrackGroupCount();
+ for ( int i = 0; i < nNumTrackGroups; ++i )
+ {
+ CDmeTrackGroup *pTrackGroup = dmx->GetTrackGroup( i );
+ if ( !pTrackGroup->GetValue< bool >( "isActor" ) )
+ continue;
+
+ // Walk the track groups
+ int nNumTracks = pTrackGroup->GetTrackCount();
+ for ( int j = 0 ; j < nNumTracks; ++j )
+ {
+ CDmeTrack *pTrack = pTrackGroup->GetTrack( j );
+
+ EnsureActorAndChannelForTrack( scene, pTrackGroup, pTrack );
+
+ int numEvents = pTrack->GetClipCount();
+ for ( int clip = 0; clip < numEvents; ++clip )
+ {
+ CDmeChannelsClip *pClip = CastElement< CDmeChannelsClip >( pTrack->GetClip( clip ) );
+ if ( !pClip )
+ continue;
+
+ bool success = ConvertDmxToEvent( scene, pTrackGroup, pTrack, pClip, false );
+ if ( !success )
+ {
+ bret = false;
+ Assert( 0 );
+ break;
+ }
+ }
+ }
+ }
+
+ scene->SetMapname( dmx->GetValueString( "associated_bsp" ) );
+ scene->SetSceneFPS( dmx->GetValue< float >( "fps" ) );
+ scene->SetUsingFrameSnap( dmx->GetValue< bool >( "snap" ) );
+
+ return bret;
+} \ No newline at end of file
diff --git a/movieobjects/importintovcd.cpp b/movieobjects/importintovcd.cpp
new file mode 100644
index 0000000..9b88605
--- /dev/null
+++ b/movieobjects/importintovcd.cpp
@@ -0,0 +1,863 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "movieobjects/importintovcd.h"
+#include "movieobjects/movieobjects.h"
+#include "tier3/scenetokenprocessor.h"
+#include "choreoscene.h"
+#include "choreoactor.h"
+#include "choreochannel.h"
+#include "choreoevent.h"
+#include "tier2/p4helpers.h"
+#include "tier1/utlbuffer.h"
+#include "tier3/tier3.h"
+#include "datacache/imdlcache.h"
+#include "filesystem.h"
+#include "studio.h"
+
+
+//-----------------------------------------------------------------------------
+// Helper wrapper class for log layers (necessary to avoid movieobjects dependence)
+//-----------------------------------------------------------------------------
+class CDmeLogLayerHelper
+{
+public:
+ CDmeLogLayerHelper( CDmElement *pLogLayer, int nDefaultCurveType );
+
+ // Finds a key
+ int FindKey( int nTime ) const;
+
+ // Gets a value at a particular time
+ float GetValue( int nTime ) const;
+
+ // Inserts keys
+ void AddToTail( int nTime, float flValue, int nCurveType );
+ void InsertAfter( int nAfter, int nTime, float flValue, int nCurveType );
+ int InsertKey( int nTime, float flValue, int nCurveType );
+
+ // Simplifies the curve
+ void Simplify( float flThreshhold );
+
+ void SetCurveType( int nKey, int nCurveType );
+
+ // Total simplified points
+ static int TotalRemovedPoints();
+
+private:
+ void CurveSimplify_R( float flThreshold, int nStartPoint, int nEndPoint, CDmeLogLayerHelper *pDest );
+
+ // Computes the total error
+ float ComputeTotalError( CDmeLogLayerHelper *pDest, int nStartPoint, int nEndPoint );
+
+ // Select the best fit curve type
+ void ChooseBestCurveType( int nKey, int nStartPoint, int nEndPoint, CDmeLogLayerHelper *pDest );
+
+ // Compute first + second derivatives of data
+ void ComputeDerivates( float *pSlope, float *pAccel, int nPoint, CDmeLogLayerHelper *pDest );
+
+ CDmElement *m_pLogLayer;
+ CDmrArray<int> m_times;
+ CDmrArray<float> m_values;
+ CDmrArray<int> m_curvetypes;
+ int m_nDefaultCurveType;
+
+ static int s_nTotalRemovedPoints;
+};
+
+
+//-----------------------------------------------------------------------------
+// Total simplified points
+//-----------------------------------------------------------------------------
+int CDmeLogLayerHelper::s_nTotalRemovedPoints = 0;
+int CDmeLogLayerHelper::TotalRemovedPoints()
+{
+ return s_nTotalRemovedPoints;
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CDmeLogLayerHelper::CDmeLogLayerHelper( CDmElement *pLogLayer, int nDefaultCurveType ) :
+ m_pLogLayer( pLogLayer ), m_times( pLogLayer, "times", true ),
+ m_values( pLogLayer, "values", true ), m_curvetypes( pLogLayer, "curvetypes", true )
+{
+ m_nDefaultCurveType = nDefaultCurveType;
+}
+
+
+//-----------------------------------------------------------------------------
+// Inserts keys
+//-----------------------------------------------------------------------------
+void CDmeLogLayerHelper::AddToTail( int nTime, float flValue, int nCurveType )
+{
+ m_times.AddToTail( nTime );
+ m_values.AddToTail( flValue );
+ m_curvetypes.AddToTail( nCurveType );
+}
+
+void CDmeLogLayerHelper::InsertAfter( int nAfter, int nTime, float flValue, int nCurveType )
+{
+ int nBefore = nAfter + 1;
+ m_times.InsertBefore( nBefore, nTime );
+ m_values.InsertBefore( nBefore, flValue );
+ m_curvetypes.InsertBefore( nBefore, nCurveType );
+}
+
+int CDmeLogLayerHelper::InsertKey( int nTime, float flValue, int nCurveType )
+{
+ int nAfter = FindKey( nTime );
+ InsertAfter( nAfter, nTime, flValue, nCurveType );
+ return nAfter + 1;
+}
+
+void CDmeLogLayerHelper::SetCurveType( int nKey, int nCurveType )
+{
+ m_curvetypes.Set( nKey, nCurveType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds a key
+//-----------------------------------------------------------------------------
+int CDmeLogLayerHelper::FindKey( int nTime ) const
+{
+ int tn = m_times.Count();
+ for ( int ti = tn - 1; ti >= 0; --ti )
+ {
+ if ( nTime >= m_times[ ti ] )
+ return ti;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets a value at a particular time
+//-----------------------------------------------------------------------------
+float CDmeLogLayerHelper::GetValue( int nTime ) const
+{
+ int tc = m_times.Count();
+
+ Assert( m_values.Count() == tc );
+
+ int ti = FindKey( nTime );
+ if ( ti < 0 )
+ {
+ if ( tc > 0 )
+ return m_values[ 0 ];
+ return 0.0f;
+ }
+
+ // Early out if we're at the end
+ if ( ti >= tc - 1 )
+ return m_values[ ti ];
+
+ // Figure out the lerp factor
+ int nDummy, nInterpolationType;
+ int nCurveType = m_curvetypes.Count() ? m_curvetypes[ti] : m_nDefaultCurveType;
+ Interpolator_CurveInterpolatorsForType( nCurveType, nInterpolationType, nDummy );
+
+ Vector vecOutput;
+ Vector vecArg1( 0.0f, m_values[ti], 0.0f );
+ Vector vecArg2( 1.0f, m_values[ti+1], 0.0f );
+ float t = (float)( nTime - m_times[ti] ) / (float)( m_times[ti+1] - m_times[ti] );
+ Interpolator_CurveInterpolate( nInterpolationType, vecArg1, vecArg1, vecArg2, vecArg2, t, vecOutput );
+ return vecOutput.y;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the total error
+//-----------------------------------------------------------------------------
+float CDmeLogLayerHelper::ComputeTotalError( CDmeLogLayerHelper *pDest, int nStartPoint, int nEndPoint )
+{
+ float flTotalDistance = 0.0f;
+
+ for ( int i = nStartPoint; i <= nEndPoint; ++i )
+ {
+ float flCheck = m_values[i];
+ float flCheck2 = pDest->GetValue( m_times[i] );
+ float flDistance = fabs( flCheck2 - flCheck );
+ flTotalDistance += flDistance;
+ }
+
+ return flTotalDistance;
+}
+
+
+//-----------------------------------------------------------------------------
+// Select the best fit curve type
+//-----------------------------------------------------------------------------
+static int s_nInterpTypes[] =
+{
+ INTERPOLATE_LINEAR_INTERP,
+ INTERPOLATE_EASE_INOUT,
+// INTERPOLATE_EASE_IN,
+// INTERPOLATE_EASE_OUT,
+// INTERPOLATE_EXPONENTIAL_DECAY,
+// INTERPOLATE_HOLD,
+ -1,
+};
+
+void CDmeLogLayerHelper::ChooseBestCurveType( int nKey, int nStartPoint, int nEndPoint, CDmeLogLayerHelper *pDest )
+{
+ return;
+
+ float flMinError = FLT_MAX;
+ int nBestInterpType = -1;
+ for ( int i = 0; s_nInterpTypes[i] >= 0; ++i )
+ {
+ pDest->SetCurveType( nKey, MAKE_CURVE_TYPE( s_nInterpTypes[i], s_nInterpTypes[i] ) );
+ float flError = ComputeTotalError( pDest, nStartPoint, nEndPoint );
+ if ( flMinError > flError )
+ {
+ nBestInterpType = s_nInterpTypes[i];
+ flMinError = flError;
+ }
+ }
+ Assert( nBestInterpType >= 0 );
+ pDest->SetCurveType( nKey, MAKE_CURVE_TYPE( nBestInterpType, nBestInterpType ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute first + second derivatives of data
+//-----------------------------------------------------------------------------
+void CDmeLogLayerHelper::ComputeDerivates( float *pSlope, float *pAccel, int nPoint, CDmeLogLayerHelper *pDest )
+{
+ // Central difference, assume linear slope between points.
+ // Find neighboring point with minimum distance
+ bool bLeftEdge = ( nPoint == 0 );
+ bool bRightEdge = ( nPoint == m_times.Count() - 1 );
+
+ int nTime = m_times[nPoint];
+ int nPrevTime = ( !bLeftEdge ) ? m_times[ nPoint - 1 ] : nTime - 1000;
+ int nNextTime = ( !bRightEdge ) ? m_times[ nPoint + 1 ] : nTime + 1000;
+ float flPrevPoint, flNextPoint;
+ if ( nTime - nPrevTime < nNextTime - nTime )
+ {
+ // prev point is closer
+ flPrevPoint = ( !bLeftEdge ) ? m_values[ nPoint - 1 ] : m_values[ nPoint ];
+ nNextTime = nTime + ( nTime - nPrevTime );
+ flNextPoint = GetValue( nNextTime );
+ }
+ else
+ {
+ // next point is closer
+ flNextPoint = ( !bRightEdge ) ? m_values[ nPoint + 1 ] : m_values[ nPoint ];
+ nPrevTime = nTime - ( nNextTime - nTime );
+ flPrevPoint = GetValue( nPrevTime );
+ }
+
+ // Central difference: slope = ( vnext - vprev ) / ( tnext - tprev );
+ // accel = ( vnext - 2 * vcurr + vprev ) / ( 0.5 * ( tnext - tprev ) )^2
+ float flCurrPoint = m_values[nPoint];
+ flPrevPoint -= pDest->GetValue( nPrevTime );
+ flCurrPoint -= pDest->GetValue( nTime );
+ flNextPoint -= pDest->GetValue( nNextTime );
+
+ float flDeltaTime = DMETIME_TO_SECONDS( nTime - nPrevTime );
+ *pSlope = ( flNextPoint - flPrevPoint ) / ( 2.0f * flDeltaTime );
+ *pAccel = ( flNextPoint - 2 * flCurrPoint + flPrevPoint ) / ( flDeltaTime * flDeltaTime );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Implementation of Douglas-Peucker curve simplification routine
+// (hacked to only care about error against original curve (sort of 1D)
+//-----------------------------------------------------------------------------
+void CDmeLogLayerHelper::CurveSimplify_R( float flThreshold, int nStartPoint, int nEndPoint, CDmeLogLayerHelper *pDest )
+{
+ if ( nEndPoint <= nStartPoint + 1 )
+ return;
+
+ int nMaxPoint = nStartPoint;
+ float flMaxDistance = 0.0f;
+
+ for ( int i = nStartPoint + 1 ; i < nEndPoint; ++i )
+ {
+ float flCheck = m_values[i];
+ float flCheck2 = pDest->GetValue( m_times[i] );
+ float flDistance = fabs( flCheck2 - flCheck );
+
+ if ( flDistance < flMaxDistance )
+ continue;
+
+ nMaxPoint = i;
+ flMaxDistance = flDistance;
+ }
+
+ /*
+ float flMaxAccel = 0.0f;
+ for ( int i = nStartPoint + 1 ; i < nEndPoint; ++i )
+ {
+ float flSlope, flAccel;
+ ComputeDerivates( &flSlope, &flAccel, i, pDest );
+ flAccel = fabs( flAccel );
+ if ( flAccel < flMaxAccel )
+ continue;
+
+ nMaxPoint = i;
+ flMaxAccel = flAccel;
+ }
+ */
+
+ if ( flMaxDistance > flThreshold )
+ {
+ int nKey = pDest->InsertKey( m_times[ nMaxPoint ], m_values[ nMaxPoint ], m_nDefaultCurveType );
+ Assert( nKey != 0 );
+ ChooseBestCurveType( nKey-1, nStartPoint, nMaxPoint, pDest );
+ ChooseBestCurveType( nKey, nMaxPoint, nEndPoint, pDest );
+
+ CurveSimplify_R( flThreshold, nStartPoint, nMaxPoint, pDest );
+ CurveSimplify_R( flThreshold, nMaxPoint, nEndPoint, pDest );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Simplifies the curve
+//-----------------------------------------------------------------------------
+void CDmeLogLayerHelper::Simplify( float flThreshhold )
+{
+ int nFirstKey, nLastKey;
+ int nKeys = m_values.Count();
+ if ( nKeys <= 1 )
+ return;
+
+ for ( nFirstKey = 1; nFirstKey < nKeys; ++nFirstKey )
+ {
+ // FIXME: Should we use a tolerance check here?
+ if ( m_values[ nFirstKey ] != m_values[ nFirstKey - 1 ] )
+ break;
+ }
+ --nFirstKey;
+
+ for ( nLastKey = nKeys; --nLastKey >= 1; )
+ {
+ // FIXME: Should we use a tolerance check here?
+ if ( m_values[ nLastKey ] != m_values[ nLastKey - 1 ] )
+ break;
+ }
+
+ if ( nLastKey <= nFirstKey )
+ {
+ m_times.RemoveMultiple( 1, nKeys - 1 );
+ m_values.RemoveMultiple( 1, nKeys - 1 );
+ s_nTotalRemovedPoints += nKeys - 1;
+ return;
+ }
+
+ CDmElement *pTemp = CreateElement< CDmElement >( "simplified" );
+ CDmeLogLayerHelper destLayer( pTemp, m_nDefaultCurveType );
+
+ destLayer.AddToTail( m_times[nFirstKey], m_values[nFirstKey], m_nDefaultCurveType );
+ destLayer.AddToTail( m_times[nLastKey], m_values[nLastKey], m_nDefaultCurveType );
+
+ // Recursively finds the point with the largest error from the "simplified curve"
+ // and subdivides the problem on both sides until the largest delta from the simplified
+ // curve is less than the tolerance
+ CurveSimplify_R( flThreshhold, nFirstKey, nLastKey, &destLayer );
+
+ m_times.CopyArray( destLayer.m_times.Base(), destLayer.m_times.Count() );
+ m_values.CopyArray( destLayer.m_values.Base(), destLayer.m_values.Count() );
+ m_curvetypes.CopyArray( destLayer.m_curvetypes.Base(), destLayer.m_curvetypes.Count() );
+
+ DestroyElement( pTemp );
+
+ s_nTotalRemovedPoints += nKeys - m_times.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds or adds actors, channels
+//-----------------------------------------------------------------------------
+static CChoreoActor* FindOrAddActor( CChoreoScene *pScene, const char *pActorName, const char *pActorModel )
+{
+ CChoreoActor *a = pScene->FindActor( pActorName );
+ if ( !a )
+ {
+ a = pScene->AllocActor();
+ Assert( a );
+ a->SetName( pActorName );
+ a->SetActive( true );
+ a->SetFacePoserModelName( pActorModel );
+ }
+ return a;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds animation events
+//-----------------------------------------------------------------------------
+static CChoreoEvent* FindOrAddAnimationEvent( CChoreoScene *pScene, CChoreoActor *pActor )
+{
+ int nEventCount = pScene->GetNumEvents();
+ for ( int i = 0; i < nEventCount; ++i )
+ {
+ CChoreoEvent* pEvent = pScene->GetEvent(i);
+ if ( pEvent->GetActor() != pActor )
+ continue;
+
+ if ( pEvent->GetType() != CChoreoEvent::FLEXANIMATION )
+ continue;
+
+ return pEvent;
+ }
+
+ // Allocate new channel
+ CChoreoChannel *pChannel = pScene->AllocChannel();
+ pChannel->SetName( "imported_flex" );
+ pChannel->SetActor( pActor );
+ pChannel->SetActive( true );
+ pActor->AddChannel( pChannel );
+
+ // Allocate choreo event
+ CChoreoEvent *pEvent = pScene->AllocEvent();
+ pEvent->SetName( pActor->GetName() );
+ pEvent->SetType( CChoreoEvent::FLEXANIMATION );
+ pEvent->SetActor( pActor );
+ pEvent->SetChannel( pChannel );
+ pEvent->SetActive( true );
+ pChannel->AddEvent( pEvent );
+
+ return pEvent;
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds sound events
+//-----------------------------------------------------------------------------
+static CChoreoEvent* FindOrAddSoundEvent( CChoreoScene *pScene, CChoreoActor *pActor, const char *pEventName )
+{
+ int nEventCount = pScene->GetNumEvents();
+ for ( int i = 0; i < nEventCount; ++i )
+ {
+ CChoreoEvent* pEvent = pScene->GetEvent(i);
+ if ( pEvent->GetActor() != pActor )
+ continue;
+
+ if ( pEvent->GetType() != CChoreoEvent::SPEAK )
+ continue;
+
+ if ( Q_stricmp( pEvent->GetName(), pEventName ) )
+ continue;
+
+ return pEvent;
+ }
+
+ // Allocate new channel
+ CChoreoChannel *pChannel = pScene->AllocChannel();
+ pChannel->SetName( "imported sounds" );
+ pChannel->SetActor( pActor );
+ pChannel->SetActive( true );
+ pActor->AddChannel( pChannel );
+
+ // Allocate sound event
+ CChoreoEvent *pEvent = pScene->AllocEvent();
+ pEvent->SetName( pEventName );
+ pEvent->SetType( CChoreoEvent::SPEAK );
+ pEvent->SetActor( pActor );
+ pEvent->SetChannel( pChannel );
+ pEvent->SetActive( true );
+ pChannel->AddEvent( pEvent );
+
+ return pEvent;
+}
+
+
+static CFlexAnimationTrack *FindOrCreateTrack( CChoreoEvent *pEvent, const char *pFlexControllerName )
+{
+ CFlexAnimationTrack *pTrack = pEvent->FindTrack( pFlexControllerName );
+ if ( pTrack )
+ {
+ pTrack->Clear();
+ }
+ else
+ {
+ pTrack = pEvent->AddTrack( pFlexControllerName );
+ pTrack->SetTrackActive( true );
+ }
+
+ pTrack->SetMin( 0.0f );
+ pTrack->SetMax( 1.0f );
+ pTrack->SetInverted( false );
+
+ return pTrack;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns flex controller ranges
+//-----------------------------------------------------------------------------
+void GetStereoFlexControllerRange( float *pMin, float *pMax, studiohdr_t *pStudioHdr, const char *pFlexName )
+{
+ char pRightBuf[MAX_PATH];
+ char pLeftBuf[MAX_PATH];
+ Q_snprintf( pRightBuf, sizeof(pRightBuf), "right_%s", pFlexName );
+ Q_snprintf( pLeftBuf, sizeof(pLeftBuf), "left_%s", pFlexName );
+
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < pStudioHdr->numflexcontrollers; ++i )
+ {
+ mstudioflexcontroller_t *pFlex = pStudioHdr->pFlexcontroller( i );
+ const char *pFlexControllerName = pFlex->pszName();
+ if ( !Q_stricmp( pFlexControllerName, pFlexName ) )
+ {
+ *pMin = pFlex->min;
+ *pMax = pFlex->max;
+ return;
+ }
+
+ // FIXME: Probably want to get the left + right controller + find the min and max of each, but this is unnecessary.
+ if ( !Q_stricmp( pFlexControllerName, pRightBuf ) )
+ {
+ *pMin = pFlex->min;
+ *pMax = pFlex->max;
+ return;
+ }
+
+ }
+ *pMin = 0.0f;
+ *pMax = 1.0f;
+}
+
+
+void GetFlexControllerRange( float *pMin, float *pMax, studiohdr_t *pStudioHdr, const char *pFlexName )
+{
+ for ( LocalFlexController_t i = LocalFlexController_t(0); i < pStudioHdr->numflexcontrollers; ++i )
+ {
+ mstudioflexcontroller_t *pFlex = pStudioHdr->pFlexcontroller( i );
+ const char *pFlexControllerName = pFlex->pszName();
+ if ( !Q_stricmp( pFlexControllerName, pFlexName ) )
+ {
+ *pMin = pFlex->min;
+ *pMax = pFlex->max;
+ return;
+ }
+ }
+ *pMin = 0.0f;
+ *pMax = 1.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Imports samples into a track
+//-----------------------------------------------------------------------------
+void ImportSamplesIntoTrack( CFlexAnimationTrack *pTrack, CDmElement *pLog, int nSampleType, int nTimeOffset, const ImportVCDInfo_t& info )
+{
+ CDmrArray<int> times( pLog, "times" );
+ CDmrArray<float> values( pLog, "values" );
+
+ // Add the samples
+ int nSampleCount = times.Count();
+ if ( nSampleCount == 0 )
+ return;
+
+ int nDefaultCurveType = MAKE_CURVE_TYPE( info.m_nInterpolationType, info.m_nInterpolationType );
+ if ( info.m_flSimplificationThreshhold > 0.0f )
+ {
+ CDmeLogLayerHelper helper( pLog, nDefaultCurveType );
+ helper.Simplify( info.m_flSimplificationThreshhold );
+ }
+
+ CDmrArray<int> curveTypes( pLog, "curvetypes" );
+
+ nSampleCount = times.Count();
+ bool bHasCurveTypeData = ( curveTypes.Count() > 0 );
+ for ( int j = 0; j < nSampleCount; ++j )
+ {
+ int nCurveType = bHasCurveTypeData ? curveTypes[j] : nDefaultCurveType;
+ float flValue = values[j];
+ float flTime = DMETIME_TO_SECONDS( times[j] - nTimeOffset );
+
+ CExpressionSample *pSample = pTrack->AddSample( flTime, flValue, nSampleType );
+ pSample->SetCurveType( nCurveType );
+ }
+
+ if ( nSampleType == 0 )
+ {
+ pTrack->SetEdgeActive( true, true );
+ pTrack->SetEdgeActive( false, true );
+
+ int nCurveType0, nCurveType1;
+ if ( bHasCurveTypeData )
+ {
+ nCurveType0 = curveTypes[0];
+ nCurveType1 = curveTypes[nSampleCount-1];
+ }
+ else
+ {
+ nCurveType0 = nCurveType1 = nDefaultCurveType;
+ }
+ pTrack->SetEdgeInfo( true, nCurveType0, values[ 0 ] );
+ pTrack->SetEdgeInfo( false, nCurveType1, values[ nSampleCount-1 ] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Imports mono log data into a event, creates a new track if necessary
+//-----------------------------------------------------------------------------
+void ImportMonoLogDataIntoEvent( studiohdr_t *pStudioHdr, CChoreoEvent *pEvent, const char *pTrackName, CDmElement *pLog, int nTimeOffset, const ImportVCDInfo_t& info )
+{
+ CDmrArray<int> times( pLog, "times" );
+ if ( times.Count() == 0 )
+ return;
+
+ float flMin, flMax;
+ GetFlexControllerRange( &flMin, &flMax, pStudioHdr, pTrackName );
+
+ CFlexAnimationTrack *pTrack = FindOrCreateTrack( pEvent, pTrackName );
+ pTrack->Clear();
+ pTrack->SetComboType( false );
+ pTrack->SetMin( flMin );
+ pTrack->SetMax( flMax );
+ ImportSamplesIntoTrack( pTrack, pLog, 0, nTimeOffset, info );
+}
+
+
+//-----------------------------------------------------------------------------
+// Imports stereo log data into a event, creates a new track if necessary
+//-----------------------------------------------------------------------------
+void ImportStereoLogDataIntoEvent( studiohdr_t *pStudioHdr, CChoreoEvent *pEvent, const char *pTrackName, CDmElement *pValueLog, CDmElement *pBalanceLog, int nTimeOffset, const ImportVCDInfo_t& info )
+{
+ CDmrArray<int> valueTimes( pValueLog, "times" );
+ CDmrArray<int> balanceTimes( pBalanceLog, "times" );
+ if ( valueTimes.Count() == 0 && balanceTimes.Count() == 0 )
+ return;
+
+ float flMin, flMax;
+ GetStereoFlexControllerRange( &flMin, &flMax, pStudioHdr, pTrackName );
+
+ CFlexAnimationTrack *pTrack = FindOrCreateTrack( pEvent, pTrackName );
+ pTrack->Clear();
+ pTrack->SetComboType( true );
+ pTrack->SetMin( flMin );
+ pTrack->SetMax( flMax );
+ ImportSamplesIntoTrack( pTrack, pValueLog, 0, nTimeOffset, info );
+ ImportSamplesIntoTrack( pTrack, pBalanceLog, 1, nTimeOffset, info );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute track start, end time
+//-----------------------------------------------------------------------------
+static int ComputeEventTime( CDmElement *pRoot, CChoreoEvent *pEvent )
+{
+ int nStartTime = INT_MAX;
+ int nEndTime = INT_MIN;
+
+ // Iterate over all elements in the animations attribute; each one refers to a log.
+ CDmrElementArray<> animations( pRoot, "animations" );
+ if ( !animations.IsValid() )
+ return 0;
+
+ int nCount = animations.Count();
+ for( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pLog = animations[i];
+ if ( !pLog )
+ continue;
+
+ CDmrArray<int> times( pLog, "times" );
+ int nSampleCount = times.Count();
+ if ( nSampleCount == 0 )
+ continue;
+
+ if ( nStartTime > times[0] )
+ {
+ nStartTime = times[0];
+ }
+ if ( nEndTime < times[nSampleCount-1] )
+ {
+ nEndTime = times[nSampleCount-1];
+ }
+ }
+
+ pEvent->SetStartTime( DMETIME_TO_SECONDS( nStartTime ) );
+ pEvent->SetEndTime( DMETIME_TO_SECONDS( nEndTime ) );
+ return nStartTime;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for importing animations
+//-----------------------------------------------------------------------------
+void ImportAnimations( CDmElement *pRoot, CChoreoScene *pChoreoScene, CChoreoActor *pActor, studiohdr_t *pStudioHdr, const ImportVCDInfo_t& info )
+{
+ CChoreoEvent *pEvent = FindOrAddAnimationEvent( pChoreoScene, pActor );
+ pEvent->SetDefaultCurveType( MAKE_CURVE_TYPE( info.m_nInterpolationType, info.m_nInterpolationType ) );
+ int nTimeOffset = ComputeEventTime( pRoot, pEvent );
+
+ // Iterate over all elements in the animations attribute; each one refers to a log.
+ CDmrElementArray<> animations( pRoot, "animations" );
+ if ( !animations.IsValid() )
+ return;
+ int nCount = animations.Count();
+ for( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pLog = animations[i];
+ if ( !pLog )
+ continue;
+
+ const char *pLogName = pLog->GetName();
+
+ // Balance is done at the same time as value
+ if ( StringHasPrefix( pLogName, "balance_" ) )
+ continue;
+
+ if ( StringHasPrefix( pLogName, "value_" ) )
+ {
+ if ( i == nCount - 1 )
+ continue;
+
+ char pBalanceName[256];
+ Q_snprintf( pBalanceName, sizeof(pBalanceName), "balance_%s", pLogName + 6 );
+ CDmElement *pBalanceLog = animations[i+1];
+ if ( !Q_stricmp( pBalanceName, pBalanceLog->GetName() ) )
+ {
+ ++i;
+ }
+ else
+ {
+ pBalanceLog = NULL;
+ }
+ if ( pBalanceLog )
+ {
+ ImportStereoLogDataIntoEvent( pStudioHdr, pEvent, pLogName + 6, pLog, pBalanceLog, nTimeOffset, info );
+ }
+ }
+ else
+ {
+ ImportMonoLogDataIntoEvent( pStudioHdr, pEvent, pLogName, pLog, nTimeOffset, info );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for importing sounds
+//-----------------------------------------------------------------------------
+void ImportSounds( CDmElement *pRoot, CChoreoScene *pChoreoScene, CChoreoActor *pActor, const ImportVCDInfo_t& info )
+{
+ // Iterate over all element in the sound attribute; each one refers to a sound
+ CDmrElementArray<> sounds( pRoot, "sounds" );
+ if ( !sounds.IsValid() )
+ return;
+ int nCount = sounds.Count();
+ for( int i = 0; i < nCount; ++i )
+ {
+ CDmElement *pSound = sounds[i];
+ if ( !pSound )
+ continue;
+
+ const char *pEventName = pSound->GetName();
+ CChoreoEvent *pEvent = FindOrAddSoundEvent( pChoreoScene, pActor, pEventName );
+
+ int nStart = pSound->GetValue<int>( "start" );
+ int nEnd = pSound->GetValue<int>( "end" );
+ const char *pGameSound = pSound->GetValueString( "gamesound" );
+ pEvent->SetStartTime( DMETIME_TO_SECONDS( nStart ) );
+ pEvent->SetEndTime( DMETIME_TO_SECONDS( nEnd ) );
+ pEvent->SetParameters( pGameSound );
+ pEvent->SetCloseCaptionType( CChoreoEvent::CC_MASTER );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for importing a .fac file into a .vcd file
+//-----------------------------------------------------------------------------
+bool ImportLogsIntoVCD( const char *pFacFullPath, CChoreoScene *pChoreoScene, const ImportVCDInfo_t& info )
+{
+ CDmElement *pRoot;
+ DmFileId_t id = g_pDataModel->RestoreFromFile( pFacFullPath, NULL, NULL, &pRoot, CR_FORCE_COPY );
+ if ( id == DMFILEID_INVALID )
+ {
+ Warning( "Unable to load file %s\n", pFacFullPath );
+ return false;
+ }
+
+ pChoreoScene->IgnorePhonemes( info.m_bIgnorePhonemes );
+
+ // Create the actor in the scene
+ const char *pActorName = pRoot->GetName();
+ const char *pActorModel = pRoot->GetValueString( "gamemodel" );
+
+ MDLHandle_t hMDL = g_pMDLCache->FindMDL( pActorModel );
+ if ( hMDL == MDLHANDLE_INVALID )
+ {
+ Warning( "vcdimport: Model %s doesn't exist!\n", pActorModel );
+ return false;
+ }
+
+ studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( hMDL );
+ if ( !pStudioHdr || g_pMDLCache->IsErrorModel( hMDL ) )
+ {
+ Warning( "vcdimport: Model %s doesn't exist!\n", pActorModel );
+ return false;
+ }
+
+ CChoreoActor *pActor = FindOrAddActor( pChoreoScene, pActorName, pActorModel );
+
+ ImportAnimations( pRoot, pChoreoScene, pActor, pStudioHdr, info );
+ ImportSounds( pRoot, pChoreoScene, pActor, info );
+
+ DestroyElement( pRoot, TD_DEEP );
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for importing a .fac file into a .vcd file
+//-----------------------------------------------------------------------------
+bool ImportLogsIntoVCD( const char *pFacFullPath, const char *pVCDInFullPath, const char *pVCDOutPath, const ImportVCDInfo_t& info )
+{
+ CUtlBuffer buf;
+ if ( !g_pFullFileSystem->ReadFile( pVCDInFullPath, NULL, buf ) )
+ {
+ Warning( "Unable to load file %s\n", pVCDInFullPath );
+ return false;
+ }
+
+ SetTokenProcessorBuffer( (char *)buf.Base() );
+ CChoreoScene *pScene = ChoreoLoadScene( pVCDInFullPath, NULL, GetTokenProcessor(), NULL );
+ if ( !pScene )
+ {
+ Warning( "Unable to parse file %s\n", pVCDInFullPath );
+ return false;
+ }
+
+ bool bOk = ImportLogsIntoVCD( pFacFullPath, pScene, info );
+ if ( !bOk )
+ return false;
+
+ Msg( "Removed %d samples\n", CDmeLogLayerHelper::TotalRemovedPoints() );
+
+ char pTemp[MAX_PATH];
+ if ( !Q_IsAbsolutePath( pVCDOutPath ) )
+ {
+ g_pFullFileSystem->RelativePathToFullPath( pVCDOutPath, NULL, pTemp, sizeof(pTemp) );
+ if ( !Q_IsAbsolutePath( pTemp ) )
+ {
+ char pDir[MAX_PATH];
+ if ( g_pFullFileSystem->GetCurrentDirectory( pDir, sizeof(pDir) ) )
+ {
+ Q_ComposeFileName( pDir, pVCDOutPath, pTemp, sizeof(pTemp) );
+ pVCDOutPath = pTemp;
+ }
+ }
+ else
+ {
+ pVCDOutPath = pTemp;
+ }
+ }
+
+ CP4AutoEditFile checkout( pVCDOutPath );
+ return pScene->SaveToFile( pVCDOutPath );
+} \ No newline at end of file
diff --git a/movieobjects/movieobjects.vpc b/movieobjects/movieobjects.vpc
new file mode 100644
index 0000000..90d28f8
--- /dev/null
+++ b/movieobjects/movieobjects.vpc
@@ -0,0 +1,162 @@
+//-----------------------------------------------------------------------------
+// MOVIEOBJECTS.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR ".."
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;MOVIEOBJECTS_LIB"
+ }
+}
+
+$Project "Movieobjects"
+{
+ $Folder "Header Files"
+ {
+ $File "movieobjects_interfaces.h"
+ }
+
+ $Folder "Source Files"
+ {
+ $File "dmeanimationlist.cpp"
+ $File "dmeanimationset.cpp"
+ $File "dmeattachment.cpp"
+ $File "dmebalancetostereocalculatoroperator.cpp"
+ $File "dmebookmark.cpp"
+ $File "dmecamera.cpp"
+ $File "dmechannel.cpp"
+ $File "dmeclip.cpp"
+ $File "dmecombinationoperator.cpp"
+ $File "dmedag.cpp"
+ $File "dmedccmakefile.cpp"
+ $File "dmeeditortypedictionary.cpp"
+ $File "dmeexpressionoperator.cpp"
+ $File "dmefaceset.cpp"
+ $File "dmegamemodel.cpp"
+ $File "dmegamemodelinput.cpp"
+ $File "dmeinput.cpp"
+ $File "dmejoint.cpp"
+ $File "dmekeyboardinput.cpp"
+ $File "dmelight.cpp"
+ $File "dmelog.cpp"
+ $File "dmemakefile.cpp"
+ $File "dmemakefileutils.cpp"
+ $File "dmematerial.cpp"
+ $File "dmematerialoverlayfxclip.cpp"
+ $File "dmemdl.cpp"
+ $File "dmemdlmakefile.cpp"
+ $File "dmemesh.cpp"
+ $File "dmemodel.cpp"
+ $File "dmemorphoperator.cpp"
+ $File "dmemouseinput.cpp"
+ $File "dmeoperator.cpp"
+ $File "dmepackoperators.cpp"
+ $File "dmeparticlesystemdefinition.cpp"
+ $File "dmephonememapping.cpp"
+ $File "dmeselection.cpp"
+ $File "dmeshape.cpp"
+ $File "dmesound.cpp"
+ $File "dmetimeframe.cpp"
+ $File "dmetimeselection.cpp"
+ $File "dmetrack.cpp"
+ $File "dmetrackgroup.cpp"
+ $File "dmetransform.cpp"
+ $File "dmetransforminput.cpp"
+ $File "dmetransformlist.cpp"
+ $File "dmetransformoperator.cpp"
+ $File "dmeunpackoperators.cpp"
+ $File "dmevertexdata.cpp"
+ $File "dmobjserializer.cpp"
+ $File "movieobjects_interfaces.cpp"
+ $File "$SRCDIR\common\movieobjects\timeutils.cpp"
+ $File "dmedrawsettings.cpp"
+ $File "dmmeshutils.cpp"
+ $File "dmmeshcomp.cpp"
+ $File "dmeeyeposition.cpp"
+ $File "dmeeyeball.cpp"
+ $File "dmmeshutils.cpp"
+ $File "dmsmdserializer.cpp"
+ }
+
+ $Folder "Interface"
+ {
+ $File "$SRCDIR\public\movieobjects\dmeanimationlist.h"
+ $File "$SRCDIR\public\movieobjects\dmeanimationset.h"
+ $File "$SRCDIR\public\movieobjects\dmebalancetostereocalculatoroperator.h"
+ $File "$SRCDIR\public\movieobjects\dmebookmark.h"
+ $File "$SRCDIR\public\movieobjects\dmecamera.h"
+ $File "$SRCDIR\public\movieobjects\dmechannel.h"
+ $File "$SRCDIR\public\movieobjects\dmeclip.h"
+ $File "$SRCDIR\public\movieobjects\dmecombinationoperator.h"
+ $File "$SRCDIR\public\movieobjects\dmedag.h"
+ $File "$SRCDIR\public\movieobjects\dmedccmakefile.h"
+ $File "$SRCDIR\public\movieobjects\dmeexpressionoperator.h"
+ $File "$SRCDIR\public\movieobjects\dmefaceset.h"
+ $File "$SRCDIR\public\movieobjects\dmegamemodel.h"
+ $File "$SRCDIR\public\movieobjects\dmegamemodelinput.h"
+ $File "$SRCDIR\public\movieobjects\dmeinput.h"
+ $File "$SRCDIR\public\movieobjects\dmejoint.h"
+ $File "$SRCDIR\public\movieobjects\dmekeyboardinput.h"
+ $File "$SRCDIR\public\movieobjects\dmelight.h"
+ $File "$SRCDIR\public\movieobjects\dmelog.h"
+ $File "$SRCDIR\public\movieobjects\dmemakefile.h"
+ $File "$SRCDIR\public\movieobjects\dmemakefileutils.h"
+ $File "$SRCDIR\public\movieobjects\dmematerial.h"
+ $File "$SRCDIR\public\movieobjects\dmemdl.h"
+ $File "$SRCDIR\public\movieobjects\dmemdlmakefile.h"
+ $File "$SRCDIR\public\movieobjects\dmemesh.h"
+ $File "$SRCDIR\public\movieobjects\dmemodel.h"
+ $File "$SRCDIR\public\movieobjects\dmemorphoperator.h"
+ $File "$SRCDIR\public\movieobjects\dmemouseinput.h"
+ $File "$SRCDIR\public\movieobjects\dmeoperator.h"
+ $File "$SRCDIR\public\movieobjects\dmepackoperators.h"
+ $File "$SRCDIR\public\movieobjects\dmephonememapping.h"
+ $File "$SRCDIR\public\movieobjects\dmeselection.h"
+ $File "$SRCDIR\public\movieobjects\dmeshape.h"
+ $File "$SRCDIR\public\movieobjects\dmesound.h"
+ $File "$SRCDIR\public\movieobjects\dmetestmesh.h"
+ $File "$SRCDIR\public\movieobjects\dmetimeframe.h"
+ $File "$SRCDIR\public\movieobjects\dmetimeselection.h"
+ $File "$SRCDIR\public\movieobjects\dmetimeselectiontimes.h"
+ $File "$SRCDIR\public\movieobjects\dmetrack.h"
+ $File "$SRCDIR\public\movieobjects\dmetrackgroup.h"
+ $File "$SRCDIR\public\movieobjects\dmetransform.h"
+ $File "$SRCDIR\public\movieobjects\dmetransforminput.h"
+ $File "$SRCDIR\public\movieobjects\dmetransformlist.h"
+ $File "$SRCDIR\public\movieobjects\dmetransformoperator.h"
+ $File "$SRCDIR\public\movieobjects\dmeunpackoperators.h"
+ $File "$SRCDIR\public\movieobjects\dmevertexdata.h"
+ $File "$SRCDIR\public\movieobjects\dmobjserializer.h"
+ $File "$SRCDIR\public\movieobjects\idmemakefileutils.h"
+ $File "$SRCDIR\public\movieobjects\movieobjects.h"
+ $File "$SRCDIR\public\movieobjects\timeutils.h"
+ $File "$SRCDIR\public\movieobjects\dmeattachment.h"
+ $File "$SRCDIR\public\movieobjects\dmeeditortypedictionary.h"
+ $File "$SRCDIR\public\movieobjects\dmeparticlesystemdefinition.h"
+ $File "$SRCDIR\public\movieobjects\dmematerialoverlayfxclip.h"
+ $File "$SRCDIR\public\movieobjects\dmedrawsettings.h"
+ $File "$SRCDIR\public\movieobjects\dmmeshutils.h"
+ $File "$SRCDIR\public\movieobjects\dmmeshcomp.h"
+ $File "$SRCDIR\public\movieobjects\dmeeyeposition.h"
+ $File "$SRCDIR\public\movieobjects\dmeeyeball.h"
+ $File "$SRCDIR\public\movieobjects\dmmeshutils.h"
+ $File "$SRCDIR\public\movieobjects\dmsmdserializer.h"
+ }
+
+ $Folder "external"
+ {
+ $File "$SRCDIR\public\bone_setup.cpp"
+ $File "$SRCDIR\public\collisionutils.cpp"
+ $File "$SRCDIR\public\mathlib\mathlib.h"
+ $File "$SRCDIR\public\phonemeconverter.cpp"
+ $File "$SRCDIR\public\phonemeconverter.h"
+ $File "$SRCDIR\public\studio.cpp"
+ $File "$SRCDIR\public\mathlib\vector.h"
+ }
+}
diff --git a/movieobjects/movieobjects_interfaces.cpp b/movieobjects/movieobjects_interfaces.cpp
new file mode 100644
index 0000000..18211dc
--- /dev/null
+++ b/movieobjects/movieobjects_interfaces.cpp
@@ -0,0 +1,8 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "movieobjects_interfaces.h"
+
+IGlobalFlexController *g_pGlobalFlexController = 0;
diff --git a/movieobjects/movieobjects_interfaces.h b/movieobjects/movieobjects_interfaces.h
new file mode 100644
index 0000000..1e92b7e
--- /dev/null
+++ b/movieobjects/movieobjects_interfaces.h
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef MOVIEOBJECTS_INTERFACE_H
+#define MOVIEOBJECTS_INTERFACE_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+//-----------------------------------------------------------------------------
+// typedefs that should be in platform.h
+//-----------------------------------------------------------------------------
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class IGlobalFlexController;
+
+
+//-----------------------------------------------------------------------------
+// Global interfaces used by the movieobjects library
+//-----------------------------------------------------------------------------
+extern IGlobalFlexController *g_pGlobalFlexController;
+
+
+#endif // MOVIEOBJECTS_INTERFACE_H \ No newline at end of file