4240 lines
128 KiB
C++
4240 lines
128 KiB
C++
//======== Copyright © 1996-2009, Valve, L.L.C., All rights reserved. ========
|
|
//
|
|
// The copyright to the contents herein is the property of Valve, L.L.C.
|
|
// The contents may be used and/or copied only with the written permission of
|
|
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
|
|
// the agreement/contract under which the contents have been supplied.
|
|
//
|
|
// $Header: $
|
|
// $NoKeywords: $
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================
|
|
|
|
|
|
// Valve includes
|
|
#include "datamodel/dmelement.h"
|
|
#include "datamodel/idatamodel.h"
|
|
#include "dmxeditlib/dmxedit.h"
|
|
#include "mdlobjects/dmeeyeball.h"
|
|
#include "mdlobjects/dmeeyelid.h"
|
|
#include "mdlobjects/dmemouth.h"
|
|
#include "movieobjects/dmobjserializer.h"
|
|
#include "movieobjects/dmemakefile.h"
|
|
#include "movieobjects/dmemodel.h"
|
|
#include "movieobjects/dmeflexrules.h"
|
|
#include "tier1/utlstack.h"
|
|
#include "dmeutils/dmmeshutils.h"
|
|
#include "meshutils/mesh.h"
|
|
#include <time.h>
|
|
|
|
|
|
#ifndef __func__
|
|
# ifdef __FUNCTION__
|
|
# define __func__ __FUNCTION__
|
|
# else
|
|
# define __func__ DmxEdit
|
|
# endif
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
class CDmxEdit : public CBaseAppSystem< IDmxEdit >
|
|
{
|
|
typedef CBaseAppSystem< IDmxEdit > BaseClass;
|
|
|
|
public:
|
|
|
|
CDmxEdit()
|
|
: m_nDistanceType( CDmeMesh::DIST_ABSOLUTE )
|
|
, m_pDmeMakefile( NULL )
|
|
{
|
|
}
|
|
|
|
CDmeMakefile *GetMakefile()
|
|
{
|
|
if ( !m_pDmeMakefile )
|
|
{
|
|
m_pDmeMakefile = CreateElement< CDmeMakefile >( "python dmxedit", DMFILEID_INVALID );
|
|
}
|
|
|
|
if ( !m_pDmeMakefile )
|
|
return NULL;
|
|
|
|
return m_pDmeMakefile;
|
|
}
|
|
|
|
CDmeSource *AddSource( const char *pszSource, bool bDmx )
|
|
{
|
|
CDmeMakefile *pDmeMakefile = GetMakefile();
|
|
if ( !pDmeMakefile )
|
|
return NULL;
|
|
|
|
CDmeSource *pDmeSource = pDmeMakefile->AddSource< CDmeSource >( pszSource );
|
|
if ( pDmeSource )
|
|
{
|
|
if ( bDmx )
|
|
{
|
|
pDmeSource->SetValue( "LoadDmx", true );
|
|
}
|
|
else
|
|
{
|
|
pDmeSource->SetValue( "LoadObj", true );
|
|
}
|
|
}
|
|
|
|
return pDmeSource;
|
|
}
|
|
|
|
virtual DmxEditErrorState_t GetErrorState() const
|
|
{
|
|
return m_nErrorState;
|
|
}
|
|
|
|
virtual void ResetErrorState()
|
|
{
|
|
m_nErrorState = DMXEDIT_OK;
|
|
m_sErrorString.Set( "" );
|
|
}
|
|
|
|
virtual const char *GetErrorString() const
|
|
{
|
|
return m_nErrorState == DMXEDIT_OK ? NULL : m_sErrorString.Get();
|
|
}
|
|
|
|
int SetErrorString( DmxEditErrorState_t nErrorState, const char *pszErrorString, ... )
|
|
{
|
|
va_list marker;
|
|
va_start( marker, pszErrorString );
|
|
const int nRetVal = IntSetErrorString( nErrorState, pszErrorString, marker );
|
|
va_end( marker );
|
|
return nRetVal;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This is a bit of a hack but CDmxEdit is already a friend of CDmeMesh...
|
|
//-----------------------------------------------------------------------------
|
|
static bool RemoveBaseState( CDmeMesh *pDmeMesh, CDmeVertexData *pDmeVertexData )
|
|
{
|
|
return pDmeMesh->RemoveBaseState( pDmeVertexData );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This is a bit of a hack but CDmxEdit is already a friend of CDmeMesh...
|
|
//-----------------------------------------------------------------------------
|
|
static CDmeVertexData *FindOrAddBaseState( CDmeMesh *pDmeMesh, CDmeVertexData *pDmeVertexData )
|
|
{
|
|
return pDmeMesh->FindOrAddBaseState( pDmeVertexData );
|
|
}
|
|
|
|
void SetDistanceType( CDmeMesh::Distance_t nDistanceType )
|
|
{
|
|
m_nDistanceType = nDistanceType;
|
|
}
|
|
|
|
CDmeMesh::Distance_t GetDistanceType() const
|
|
{
|
|
return m_nDistanceType;
|
|
}
|
|
|
|
protected:
|
|
DmxEditErrorState_t m_nErrorState;
|
|
CUtlString m_sErrorString;
|
|
|
|
int IntSetErrorString( DmxEditErrorState_t nErrorState, const char *pszErrorString, va_list vaList )
|
|
{
|
|
m_nErrorState = nErrorState;
|
|
|
|
char tmpBuf[ BUFSIZ ] = "dmxedit.";
|
|
enum {
|
|
kOffset = 8
|
|
};
|
|
|
|
#ifdef _WIN32
|
|
int len = _vsnprintf( tmpBuf + kOffset, sizeof( tmpBuf ) - 1 - kOffset, pszErrorString, vaList );
|
|
#elif POSIX
|
|
int len = vsnprintf( tmpBuf + kOffset, sizeof( tmpBuf ) - 1 - kOffset, pszErrorString, vaList );
|
|
#else
|
|
#error "define vsnprintf type."
|
|
#endif
|
|
|
|
// Len < 0 represents an overflow
|
|
if( len < 0 )
|
|
{
|
|
len = sizeof( tmpBuf ) - 1;
|
|
tmpBuf[sizeof( tmpBuf ) - 1] = 0;
|
|
}
|
|
|
|
m_sErrorString.Set( tmpBuf );
|
|
|
|
return len;
|
|
}
|
|
|
|
CDmeMesh::Distance_t m_nDistanceType;
|
|
|
|
CDmeMakefile *m_pDmeMakefile;
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
static CDmxEdit g_DmxEdit;
|
|
CDmxEdit *g_pDmxEditImpl = &g_DmxEdit;
|
|
IDmxEdit *g_pDmxEdit = &g_DmxEdit;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Macros for error
|
|
//-----------------------------------------------------------------------------
|
|
#define DMXEDIT_ERROR( ... ) \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_ERROR, __func__ ": " __VA_ARGS__ );
|
|
|
|
|
|
#define DMXEDIT_ERROR_RETURN_NULL( ... ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_ERROR, __func__ ": " __VA_ARGS__ ); \
|
|
return NULL; \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_ERROR_RETURN_FALSE( ... ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_ERROR, __func__ ": " __VA_ARGS__ ); \
|
|
return NULL; \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_ERROR_RETURN_EMPTY_STRING( ... ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_ERROR, __func__ ": " __VA_ARGS__ ); \
|
|
return ""; \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_WARNING( ... ) \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, __func__ ": " __VA_ARGS__ );
|
|
|
|
|
|
#define DMXEDIT_WARNING_RETURN_NULL( ... ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, __func__ ": " __VA_ARGS__ ); \
|
|
return NULL; \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_WARNING_RETURN_FALSE( ... ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, __func__ ": " __VA_ARGS__ ); \
|
|
return NULL; \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_WARNING_RETURN_EMPTY_STRING( ... ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, __func__ ": " __VA_ARGS__ ); \
|
|
return ""; \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_MESH_ERROR_RETURN_NULL( pDmeMesh ) \
|
|
{ \
|
|
if ( !pDmeMesh ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_ERROR, __func__ ": No mesh specified" __VA_ARGS__ ); \
|
|
return NULL; \
|
|
} \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_MESH_ERROR_RETURN_FALSE( pDmeMesh ) \
|
|
{ \
|
|
if ( !pDmeMesh ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_ERROR, __func__ ": No mesh specified" __VA_ARGS__ ); \
|
|
return false; \
|
|
} \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_MESH_ERROR_RETURN_EMPTY_STRING( pDmeMesh ) \
|
|
{ \
|
|
if ( !pDmeMesh ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_ERROR, __func__ ": No mesh specified" __VA_ARGS__ ); \
|
|
return ""; \
|
|
} \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh ) \
|
|
{ \
|
|
if ( !pDmeMesh ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, __func__ ": No mesh specified" __VA_ARGS__ ); \
|
|
return NULL; \
|
|
} \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh ) \
|
|
{ \
|
|
if ( !pDmeMesh ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, __func__ ": No mesh specified" __VA_ARGS__ ); \
|
|
return false; \
|
|
} \
|
|
}
|
|
|
|
|
|
#define DMXEDIT_MESH_WARNING_RETURN_EMPTY_STRING( pDmeMesh ) \
|
|
{ \
|
|
if ( !pDmeMesh ) \
|
|
{ \
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, __func__ ": No mesh specified" __VA_ARGS__ ); \
|
|
return false; \
|
|
} \
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Checks whether the specified mesh & base state are valid and the base
|
|
// state actually belongs to the mesh
|
|
//-----------------------------------------------------------------------------
|
|
bool BaseStateSanityCheck( CDmeMesh *pDmeMesh, CDmeVertexData *pDmeBaseState, const char *pszFuncName )
|
|
{
|
|
if ( !pDmeMesh )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: No mesh specified", pszFuncName );
|
|
return false;
|
|
}
|
|
|
|
if ( !pDmeBaseState )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: No base state specified", pszFuncName );
|
|
return false;
|
|
}
|
|
|
|
CDmeVertexData *pDmeBaseStateCheck = pDmeMesh->FindBaseState( pDmeBaseState->GetName() );
|
|
if ( !pDmeBaseStateCheck || pDmeBaseState != pDmeBaseStateCheck )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: Base state \"%s\" doesn't belong to mesh \"%s\"", pszFuncName, pDmeBaseState->GetName(), pDmeMesh->GetName() );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
static const char s_szEditBaseStateName[] = "__dmxedit_edit";
|
|
static const char s_szEditOldCurrentState[] = "__dmxedit_oldCurrentState";
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool CleanupMeshEditBaseState( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexData *pDmeBindBaseState = pDmeMesh->GetBindBaseState();
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeBindBaseState, __func__ ) )
|
|
return false;
|
|
|
|
if ( !Q_strcmp( pDmeBindBaseState->GetName(), s_szEditOldCurrentState ) )
|
|
return false;
|
|
|
|
// Remove edit base state and restore current base state
|
|
if ( pDmeMesh->HasAttribute( s_szEditOldCurrentState ) )
|
|
{
|
|
CDmeVertexData *pDmeOldCurrentBaseState = pDmeMesh->GetValueElement< CDmeVertexData >( s_szEditOldCurrentState );
|
|
if ( pDmeOldCurrentBaseState )
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeOldCurrentBaseState->GetName() );
|
|
}
|
|
pDmeMesh->RemoveAttribute( s_szEditOldCurrentState );
|
|
}
|
|
else
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeBindBaseState->GetName() );
|
|
}
|
|
|
|
pDmeMesh->DeleteBaseState( s_szEditBaseStateName );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeVertexData *FindMeshEditBaseState( CDmeMesh *pDmeMesh, const char *pszFuncName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmeVertexData *pDmeEditBaseState = pDmeMesh->FindBaseState( s_szEditBaseStateName );
|
|
|
|
if ( pDmeEditBaseState && BaseStateSanityCheck( pDmeMesh, pDmeEditBaseState, pszFuncName ) )
|
|
return pDmeEditBaseState;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeVertexData *FindOrCreateMeshEditBaseState( CDmeMesh *pDmeMesh, const char *pszFuncName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmeVertexData *pDmeEditBaseState = pDmeMesh->FindBaseState( s_szEditBaseStateName );
|
|
if ( pDmeEditBaseState )
|
|
{
|
|
if ( BaseStateSanityCheck( pDmeMesh, pDmeEditBaseState, pszFuncName ) )
|
|
return pDmeEditBaseState;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
CDmeVertexData *pDmeBindBaseState = pDmeMesh->GetBindBaseState();
|
|
if ( !pDmeBindBaseState )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: No bind base state found on mesh \"%s\"", pszFuncName, pDmeMesh->GetName() );
|
|
return NULL;
|
|
}
|
|
|
|
pDmeEditBaseState = pDmeMesh->FindOrCreateBaseState( s_szEditBaseStateName );
|
|
if ( !pDmeEditBaseState )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: Couldn't create edit base state on mesh \"%s\"", pszFuncName, pDmeMesh->GetName() );
|
|
return NULL;
|
|
}
|
|
|
|
pDmeBindBaseState->CopyTo( pDmeEditBaseState );
|
|
pDmeEditBaseState->SetFileId( DMFILEID_INVALID, TD_ALL );
|
|
|
|
// Save the current base state so we can restore it on save
|
|
CDmAttribute *pDmeOldCurrentStateAttr = NULL;
|
|
|
|
if ( pDmeMesh->HasAttribute( s_szEditOldCurrentState ) )
|
|
{
|
|
pDmeOldCurrentStateAttr = pDmeMesh->GetAttribute( s_szEditOldCurrentState );
|
|
if ( !pDmeOldCurrentStateAttr )
|
|
{
|
|
Msg( "WARNING %s: Attribute %s.%s is of type %s, not AT_ELEMENT, removing", pszFuncName, pDmeMesh->GetName(), pDmeOldCurrentStateAttr->GetName(), pDmeOldCurrentStateAttr->GetTypeString() );
|
|
pDmeMesh->RemoveAttribute( s_szEditOldCurrentState );
|
|
pDmeOldCurrentStateAttr = NULL;
|
|
}
|
|
}
|
|
|
|
if ( pDmeOldCurrentStateAttr == NULL )
|
|
{
|
|
pDmeOldCurrentStateAttr = pDmeMesh->AddAttributeElement< CDmeVertexData >( s_szEditOldCurrentState );
|
|
if ( !pDmeOldCurrentStateAttr )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: Couldn't create %s.%s attribute", pszFuncName, pDmeMesh->GetName(), s_szEditOldCurrentState );
|
|
return NULL;
|
|
}
|
|
|
|
pDmeOldCurrentStateAttr->AddFlag( FATTRIB_DONTSAVE );
|
|
}
|
|
|
|
pDmeOldCurrentStateAttr->SetValue< CDmeVertexData >( pDmeMesh->GetCurrentBaseState() );
|
|
|
|
pDmeMesh->SetCurrentBaseState( pDmeEditBaseState->GetName() );
|
|
|
|
CDmeVertexData *pDmeBaseState = pDmeMesh->GetCurrentBaseState();
|
|
|
|
if ( BaseStateSanityCheck( pDmeMesh, pDmeBaseState, pszFuncName ) )
|
|
return pDmeBaseState;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmElement *LoadDmx( const char *pszFilename )
|
|
{
|
|
CDmElement *pRoot = NULL;
|
|
|
|
if ( !Q_stricmp( "dmx", Q_GetFileExtension( pszFilename ) ) )
|
|
{
|
|
g_pDmxEditImpl->AddSource( pszFilename, true );
|
|
g_pDataModel->RestoreFromFile( pszFilename, NULL, NULL, &pRoot, CR_COPY_NEW );
|
|
if ( !pRoot )
|
|
DMXEDIT_ERROR_RETURN_NULL( "DMX Load Fail: \"%s\"", pszFilename );
|
|
}
|
|
else
|
|
DMXEDIT_ERROR_RETURN_NULL( "File without .dmx extension passed to LoadDmx: \"%s\"", pszFilename );
|
|
|
|
return pRoot;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmElement *LoadObj( const char *pszFilename, const char *pszLoadType /* = "ABSOLUTE" */ )
|
|
{
|
|
CDmElement *pRoot = NULL;
|
|
|
|
if ( !Q_stricmp( "obj", Q_GetFileExtension( pszFilename ) ) )
|
|
{
|
|
g_pDmxEditImpl->AddSource( pszFilename, false );
|
|
// Load OBJs
|
|
bool bAbsoluteObjs = true;
|
|
if ( pszLoadType )
|
|
{
|
|
if ( !Q_stricmp( "absolute", pszLoadType ) )
|
|
{
|
|
bAbsoluteObjs = true;
|
|
}
|
|
else if ( !Q_stricmp( "relative", pszLoadType ) )
|
|
{
|
|
bAbsoluteObjs = false;
|
|
}
|
|
else
|
|
DMXEDIT_ERROR_RETURN_NULL( "Invalid OBJ loadType specified (%s), must be \"ABSOLUTE\" or \"RELATIVE\"", pszLoadType );
|
|
}
|
|
|
|
pRoot = CDmObjSerializer().ReadOBJ( pszFilename, NULL, true, bAbsoluteObjs );
|
|
if ( !pRoot )
|
|
DMXEDIT_ERROR_RETURN_NULL( "OBJ Load Fail: \"%s\"", pszFilename );
|
|
}
|
|
else
|
|
DMXEDIT_ERROR_RETURN_NULL( "File without .obj extension passed to LoadObj: \"%s\"", pszFilename );
|
|
|
|
return pRoot;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Not really pushing and popping as it's not implemented as a stack
|
|
// only one level or push allowed. Could be a DM_ELEMENT_ARRAY if needed
|
|
// to support arbitrary nesting if required but currently only called
|
|
// by Save
|
|
//-----------------------------------------------------------------------------
|
|
void PushPopEditState( CDmeMesh *pDmeMesh, bool bPush )
|
|
{
|
|
if ( !pDmeMesh )
|
|
return;
|
|
|
|
const char szPushEdit[] = "__dmxedit_pushEditBase";
|
|
const char szPushCurr[] = "__dmxedit_pushCurrBase";
|
|
|
|
if ( bPush )
|
|
{
|
|
CDmeVertexData *pDmeEditBaseState = FindMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !pDmeEditBaseState || pDmeMesh->BaseStateCount() <= 1 )
|
|
return;
|
|
|
|
pDmeMesh->SetValue( szPushEdit, pDmeEditBaseState );
|
|
pDmeMesh->GetAttribute( szPushEdit )->AddFlag( FATTRIB_DONTSAVE );
|
|
|
|
CDmeVertexData *pDmeCurrentBaseState = pDmeMesh->GetCurrentBaseState();
|
|
if ( pDmeCurrentBaseState == pDmeEditBaseState )
|
|
{
|
|
pDmeMesh->SetValue( szPushCurr, pDmeCurrentBaseState );
|
|
pDmeMesh->GetAttribute( szPushCurr )->AddFlag( FATTRIB_DONTSAVE );
|
|
}
|
|
|
|
CDmxEdit::RemoveBaseState( pDmeMesh, pDmeEditBaseState );
|
|
|
|
if ( pDmeMesh->HasAttribute( s_szEditOldCurrentState ) )
|
|
{
|
|
CDmeVertexData *pDmeOldCurrentVertexData = pDmeMesh->GetValueElement< CDmeVertexData >( s_szEditOldCurrentState );
|
|
if ( pDmeOldCurrentVertexData )
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeOldCurrentVertexData->GetName() );
|
|
}
|
|
else
|
|
{
|
|
pDmeOldCurrentVertexData = pDmeMesh->GetBindBaseState();
|
|
if ( pDmeOldCurrentVertexData )
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeOldCurrentVertexData->GetName() );
|
|
}
|
|
else
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeMesh->GetBaseState( 0 )->GetName() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pDmeMesh->HasAttribute( szPushEdit ) )
|
|
{
|
|
CDmxEdit::FindOrAddBaseState( pDmeMesh, pDmeMesh->GetValueElement< CDmeVertexData >( szPushEdit ) );
|
|
pDmeMesh->RemoveAttribute( szPushEdit );
|
|
}
|
|
|
|
if ( pDmeMesh->HasAttribute( szPushCurr ) )
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeMesh->GetValueElement< CDmeVertexData >( szPushCurr )->GetName() );
|
|
pDmeMesh->RemoveAttribute( szPushCurr );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void PushPopEditStates( CDmElement *pDmRoot, bool bPush )
|
|
{
|
|
if ( !pDmRoot )
|
|
return;
|
|
|
|
CDmeDag *pDmeDag = CastElement< CDmeDag >( pDmRoot );
|
|
if ( !pDmeDag )
|
|
{
|
|
pDmeDag = pDmRoot->GetValueElement< CDmeDag >( "model" );
|
|
}
|
|
|
|
if ( !pDmeDag )
|
|
return;
|
|
|
|
CUtlStack< CDmeDag * > traverseStack;
|
|
traverseStack.Push( pDmeDag );
|
|
|
|
while ( traverseStack.Count() )
|
|
{
|
|
traverseStack.Pop( pDmeDag );
|
|
if ( !pDmeDag )
|
|
continue;
|
|
|
|
for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
|
|
{
|
|
traverseStack.Push( pDmeDag->GetChild( i ) );
|
|
}
|
|
|
|
CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( pDmeDag->GetShape() );
|
|
if ( !pDmeMesh )
|
|
continue;
|
|
|
|
PushPopEditState( pDmeMesh, bPush );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void CleanupDmxEdit( CDmeMesh *pDmeMesh )
|
|
{
|
|
if ( !pDmeMesh )
|
|
return;
|
|
|
|
const char szPushEdit[] = "__dmxedit_pushEditBase";
|
|
const char szPushCurr[] = "__dmxedit_pushCurrBase";
|
|
|
|
CDmeVertexData *pDmeEditBaseState = FindMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !pDmeEditBaseState || pDmeMesh->BaseStateCount() <= 1 )
|
|
return;
|
|
|
|
pDmeMesh->SetValue( szPushEdit, pDmeEditBaseState );
|
|
pDmeMesh->GetAttribute( szPushEdit )->AddFlag( FATTRIB_DONTSAVE );
|
|
|
|
CDmeVertexData *pDmeCurrentBaseState = pDmeMesh->GetCurrentBaseState();
|
|
if ( pDmeCurrentBaseState == pDmeEditBaseState )
|
|
{
|
|
pDmeMesh->SetValue( szPushCurr, pDmeCurrentBaseState );
|
|
pDmeMesh->GetAttribute( szPushCurr )->AddFlag( FATTRIB_DONTSAVE );
|
|
}
|
|
|
|
CDmxEdit::RemoveBaseState( pDmeMesh, pDmeEditBaseState );
|
|
|
|
if ( pDmeMesh->HasAttribute( s_szEditOldCurrentState ) )
|
|
{
|
|
CDmeVertexData *pDmeOldCurrentVertexData = pDmeMesh->GetValueElement< CDmeVertexData >( s_szEditOldCurrentState );
|
|
if ( pDmeOldCurrentVertexData )
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeOldCurrentVertexData->GetName() );
|
|
}
|
|
else
|
|
{
|
|
pDmeOldCurrentVertexData = pDmeMesh->GetBindBaseState();
|
|
if ( pDmeOldCurrentVertexData )
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeOldCurrentVertexData->GetName() );
|
|
}
|
|
else
|
|
{
|
|
pDmeMesh->SetCurrentBaseState( pDmeMesh->GetBaseState( 0 )->GetName() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void UpdateMakefile( CDmElement *pDmeRoot )
|
|
{
|
|
if ( !pDmeRoot )
|
|
return;
|
|
|
|
CDmeMakefile *pDmeMakefile = g_pDmxEditImpl->GetMakefile();
|
|
if ( !pDmeMakefile )
|
|
return;
|
|
|
|
pDmeRoot->SetValue( "makefile", pDmeMakefile );
|
|
pDmeMakefile->SetFileId( pDmeRoot->GetFileId(), TD_ALL );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// In winstuff.cpp
|
|
//-----------------------------------------------------------------------------
|
|
void MyGetUserName( char *pszBuf, unsigned long *pBufSiz );
|
|
void MyGetComputerName( char *pszBuf, unsigned long *pBufSiz );
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void AddExportTags( CDmElement *pDmeRoot, const char *pszFilename )
|
|
{
|
|
if ( !pDmeRoot )
|
|
return;
|
|
|
|
CDmElement *pExportTags = CreateElement< CDmElement >( "python_dmxedit_exportTags", pDmeRoot->GetFileId() );
|
|
|
|
char szTmpBuf[ BUFSIZ ];
|
|
|
|
_strdate( szTmpBuf );
|
|
pExportTags->SetValue( "date", szTmpBuf );
|
|
|
|
_strtime( szTmpBuf );
|
|
pExportTags->SetValue( "time", szTmpBuf );
|
|
|
|
unsigned long dwSize( sizeof( szTmpBuf ) );
|
|
|
|
*szTmpBuf ='\0';
|
|
MyGetUserName( szTmpBuf, &dwSize);
|
|
pExportTags->SetValue( "user", szTmpBuf );
|
|
|
|
*szTmpBuf ='\0';
|
|
dwSize = sizeof( szTmpBuf );
|
|
MyGetComputerName( szTmpBuf, &dwSize);
|
|
pExportTags->SetValue( "machine", szTmpBuf );
|
|
|
|
pExportTags->SetValue( "app", "python" );
|
|
pExportTags->SetValue( "cmdLine", "python <wuzza>" );
|
|
|
|
CDmAttribute *pDmeLoadDmxAttr = pExportTags->AddAttribute( "LoadDmx", AT_STRING_ARRAY );
|
|
CDmAttribute *pDmeLoadObjAttr = pExportTags->AddAttribute( "LoadObj", AT_STRING_ARRAY );
|
|
CDmeMakefile *pDmeMakefile = g_pDmxEditImpl->GetMakefile();
|
|
if ( pDmeMakefile )
|
|
{
|
|
const int nSourceCount = pDmeMakefile->GetSourceCount();
|
|
for ( int i = 0; i < nSourceCount; ++i )
|
|
{
|
|
CDmeSource *pDmeSource = pDmeMakefile->GetSource( i );
|
|
if ( pDmeSource->HasAttribute( "LoadDmx" ) )
|
|
{
|
|
CDmrStringArray( pDmeLoadDmxAttr ).AddToTail( pDmeSource->GetName() );
|
|
}
|
|
else
|
|
{
|
|
CDmrStringArray( pDmeLoadObjAttr ).AddToTail( pDmeSource->GetName() );
|
|
}
|
|
}
|
|
}
|
|
pExportTags->SetValue( "Save", pszFilename );
|
|
|
|
pDmeRoot->SetValue( "python_dmxedit_exportTags", pExportTags );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void RemoveExportTags( CDmElement *pRoot, const char *pExportTagsName )
|
|
{
|
|
if ( !pRoot )
|
|
return;
|
|
|
|
pRoot->RemoveAttribute( pExportTagsName );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SaveDmx( CDmElement *pDmeRoot, const char *pszFilename )
|
|
{
|
|
if ( !pszFilename )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No filename specified" );
|
|
|
|
if ( !pDmeRoot )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No root DmElement specified" );
|
|
|
|
RemoveExportTags( pDmeRoot, "vsDmxIO_exportTags" );
|
|
AddExportTags( pDmeRoot, pszFilename );
|
|
UpdateMakefile( pDmeRoot );
|
|
|
|
PushPopEditStates( pDmeRoot, true ); // push - hide them on save
|
|
|
|
bool bRetVal = false;
|
|
|
|
if ( !Q_stricmp( "dmx", Q_GetFileExtension( pszFilename ) ) )
|
|
{
|
|
bRetVal = g_pDataModel->SaveToFile( pszFilename, NULL, "keyvalues2", "model", pDmeRoot );
|
|
if ( !bRetVal )
|
|
{
|
|
DMXEDIT_WARNING( "Couldn't write dmx file \"%s\"", pszFilename );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DMXEDIT_WARNING( "Filename without .dmx extension passed to SaveDmx( \"%s\" )", pszFilename );
|
|
}
|
|
|
|
PushPopEditStates( pDmeRoot, false ); // pop
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SaveObj( CDmElement *pDmeRoot, const char *pszFilename, const char *pszObjSaveType /* = "ABSOLUTE" */, const char *pszDeltaName /* = NULL */ )
|
|
{
|
|
if ( !pszFilename )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No filename specified" );
|
|
|
|
if ( !pDmeRoot )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No root DmElement specified" );
|
|
|
|
PushPopEditStates( pDmeRoot, true ); // push - hide them on save
|
|
|
|
bool bRetVal = false;
|
|
|
|
if ( !Q_stricmp( "obj", Q_GetFileExtension( pszFilename ) ) )
|
|
{
|
|
bool bAbsoluteObjs = true;
|
|
if ( pszObjSaveType )
|
|
{
|
|
if ( !Q_stricmp( "absolute", pszObjSaveType ) )
|
|
{
|
|
bAbsoluteObjs = true;
|
|
}
|
|
else if ( !Q_stricmp( "relative", pszObjSaveType ) )
|
|
{
|
|
bAbsoluteObjs = false;
|
|
}
|
|
else
|
|
{
|
|
DMXEDIT_ERROR( "Invalid OBJ Save specified (%s), must be \"ABSOLUTE\" or \"RELATIVE\"", pszObjSaveType );
|
|
}
|
|
}
|
|
|
|
if ( pszDeltaName )
|
|
{
|
|
if ( !Q_stricmp( "base", pszDeltaName ) || !Q_stricmp( "bind", pszDeltaName ) )
|
|
{
|
|
bRetVal = CDmObjSerializer().WriteOBJ( pszFilename, pDmeRoot, false, NULL, bAbsoluteObjs );
|
|
}
|
|
else
|
|
{
|
|
bRetVal = CDmObjSerializer().WriteOBJ( pszFilename, pDmeRoot, true, pszDeltaName, bAbsoluteObjs );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRetVal = CDmObjSerializer().WriteOBJ( pszFilename, pDmeRoot, true, NULL, bAbsoluteObjs );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DMXEDIT_WARNING( "Filename without .obj extension passed to SaveDmx( \"%s\" )", pszFilename );
|
|
}
|
|
|
|
PushPopEditStates( pDmeRoot, false ); // pop
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// The internal version of FindMesh
|
|
//-----------------------------------------------------------------------------
|
|
CDmeMesh *FindMesh( CDmElement *pRoot, const char *pszMeshSearchName, bool bComboOnly )
|
|
{
|
|
if ( !pRoot )
|
|
return NULL;
|
|
|
|
CDmeDag *pDmeDag = CastElement< CDmeDag >( pRoot );
|
|
if ( !pDmeDag )
|
|
{
|
|
pDmeDag = pRoot->GetValueElement< CDmeDag >( "model" );
|
|
}
|
|
|
|
if ( !pDmeDag )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Invalid DmElement passed, DmeDag or element with \"model\" attribute required" );
|
|
|
|
CUtlStack< CDmeDag * > traverseStack;
|
|
traverseStack.Push( pDmeDag );
|
|
|
|
while ( traverseStack.Count() )
|
|
{
|
|
traverseStack.Pop( pDmeDag );
|
|
if ( !pDmeDag )
|
|
continue;
|
|
|
|
for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
|
|
{
|
|
traverseStack.Push( pDmeDag->GetChild( i ) );
|
|
}
|
|
|
|
CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( pDmeDag->GetShape() );
|
|
if ( !pDmeMesh )
|
|
continue;
|
|
|
|
// Looking for a named mesh? Return if found
|
|
if ( pszMeshSearchName && ( !Q_strcmp( pszMeshSearchName, pDmeDag->GetName() ) || !Q_strcmp( pszMeshSearchName, pDmeMesh->GetName() ) ) )
|
|
return pDmeMesh;
|
|
|
|
// Looking for a combo mesh? Return if found
|
|
if ( bComboOnly && pDmeMesh->DeltaStateCount() )
|
|
return pDmeMesh;
|
|
|
|
// Looking for a named or combo mesh, this wasn't it so keep looking
|
|
if ( bComboOnly || pszMeshSearchName )
|
|
continue;
|
|
|
|
// Looking for the first mesh? Ok!
|
|
return pDmeMesh;
|
|
}
|
|
|
|
// No mesh found
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeMesh *GetFirstComboMesh( CDmElement *pRoot )
|
|
{
|
|
CDmeMesh *pDmeMesh = FindMesh( pRoot, NULL, true );
|
|
if ( !pDmeMesh )
|
|
DMXEDIT_WARNING_RETURN_NULL( "No mesh with combinations found" );
|
|
|
|
return pDmeMesh;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeMesh *GetNamedMesh( CDmElement *pRoot, const char *pszMeshSearchName )
|
|
{
|
|
CDmeMesh *pDmeMesh = FindMesh( pRoot, pszMeshSearchName, false );
|
|
if ( !pDmeMesh )
|
|
DMXEDIT_WARNING_RETURN_NULL( "No mesh named \"%s\" found", pszMeshSearchName );
|
|
|
|
return pDmeMesh;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeMesh *GetFirstMesh( CDmElement *pDmeRoot )
|
|
{
|
|
CDmeMesh *pDmeMesh = FindMesh( pDmeRoot, NULL, false );
|
|
if ( !pDmeMesh )
|
|
DMXEDIT_WARNING_RETURN_NULL( "No mesh found" );
|
|
|
|
return pDmeMesh;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Do a depth first walk of all siblings of the dmeDag owning this mesh
|
|
//-----------------------------------------------------------------------------
|
|
CDmeMesh *GetNextMesh( CDmeMesh *pCurrentDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pCurrentDmeMesh );
|
|
|
|
CDmeDag *pDmeDag = FindReferringElement< CDmeDag >( pCurrentDmeMesh, "shape" );
|
|
if ( !pDmeDag )
|
|
DMXEDIT_WARNING_RETURN_NULL( "No dmeDag owning mesh \"%s\"", pCurrentDmeMesh->GetName() );
|
|
|
|
// Walk up to the root
|
|
CDmeDag *pDmeDagParent = NULL;
|
|
for ( ;; )
|
|
{
|
|
pDmeDagParent = FindReferringElement< CDmeDag >( pDmeDag, "children" );
|
|
if ( !pDmeDagParent )
|
|
break;
|
|
pDmeDag = pDmeDagParent;
|
|
}
|
|
|
|
CUtlStack< CDmeDag * > traverseStack;
|
|
for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
|
|
{
|
|
traverseStack.Push( pDmeDag->GetChild( i ) );
|
|
}
|
|
|
|
bool bNext = false;
|
|
while ( traverseStack.Count() )
|
|
{
|
|
traverseStack.Pop( pDmeDag );
|
|
if ( !pDmeDag )
|
|
continue;
|
|
|
|
for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
|
|
{
|
|
traverseStack.Push( pDmeDag->GetChild( i ) );
|
|
}
|
|
|
|
CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( pDmeDag->GetShape() );
|
|
if ( !pDmeMesh )
|
|
continue;
|
|
|
|
if ( pDmeMesh == pCurrentDmeMesh )
|
|
{
|
|
bNext = true;
|
|
continue;
|
|
}
|
|
|
|
if ( bNext )
|
|
return pDmeMesh;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Prints a list of all of the deltas present in the specified mesh
|
|
//-----------------------------------------------------------------------------
|
|
bool ListDeltas( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
const int nDeltas = pDmeMesh->DeltaStateCount();
|
|
if ( nDeltas <= 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Mesh \"%s\" has no deltas", pDmeMesh->GetName() );
|
|
|
|
for ( int i( 0 ); i < nDeltas; ++i )
|
|
{
|
|
Msg( "# Delta %d: %s\n", i, pDmeMesh->GetDeltaState( i )->GetName() );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
int DeltaCount( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
return pDmeMesh->DeltaStateCount();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
const char *DeltaName( CDmeMesh *pDmeMesh, int nDeltaIndex )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_EMPTY_STRING( pDmeMesh );
|
|
|
|
const int nDeltaStateCount = pDmeMesh->DeltaStateCount();
|
|
|
|
if ( nDeltaStateCount <= 0 )
|
|
DMXEDIT_WARNING_RETURN_EMPTY_STRING( "Mesh \"%s\" has no deltas", pDmeMesh->GetName() );
|
|
|
|
if ( nDeltaIndex < 0 && nDeltaIndex >= nDeltaStateCount )
|
|
DMXEDIT_WARNING_RETURN_EMPTY_STRING( "Delta %n out of range, Mesh \"%s\" has %d deltas", nDeltaIndex, pDmeMesh->GetName(), nDeltaStateCount );
|
|
|
|
return pDmeMesh->GetDeltaState( nDeltaIndex )->GetName();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeVertexDeltaData *GetDeltaState( CDmeMesh *pDmeMesh, int nDeltaIndex )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
const int nDeltaStateCount = pDmeMesh->DeltaStateCount();
|
|
|
|
if ( nDeltaStateCount <= 0 )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Mesh \"%s\" has no deltas", pDmeMesh->GetName() );
|
|
|
|
if ( nDeltaIndex < 0 && nDeltaIndex >= nDeltaStateCount )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Delta %n out of range, Mesh \"%s\" has %d deltas", nDeltaIndex, pDmeMesh->GetName(), nDeltaStateCount );
|
|
|
|
return pDmeMesh->GetDeltaState( nDeltaIndex );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeVertexDeltaData *GetDeltaState( CDmeMesh *pDmeMesh, const char *pszDeltaName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( pDmeDelta )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Mesh \"%s\" has no deltas", pDmeMesh->GetName() );
|
|
|
|
return pDmeDelta;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Checks whether the specified mesh & base state are valid and the base
|
|
// state actually belongs to the mesh
|
|
// TODO: verify base state is the same as bind state?
|
|
// TODO: Size checks?
|
|
//-----------------------------------------------------------------------------
|
|
bool DeltaStateSanityCheck( CDmeMesh *pDmeMesh, CDmeVertexDeltaData *pDmeDeltaState, const char *pszFuncName )
|
|
{
|
|
if ( !pDmeMesh )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: No mesh specified", pszFuncName );
|
|
return false;
|
|
}
|
|
|
|
if ( !pDmeDeltaState )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: Non-existent delta state specified", pszFuncName );
|
|
return false;
|
|
}
|
|
|
|
CDmeVertexDeltaData *pDmeDeltaStateCheck = pDmeMesh->FindDeltaState( pDmeDeltaState->GetName() );
|
|
if ( !pDmeDeltaStateCheck || pDmeDeltaState != pDmeDeltaStateCheck )
|
|
{
|
|
g_pDmxEditImpl->SetErrorString( DMXEDIT_WARNING, "%s: Delta state \"%s\" doesn't belong to mesh \"%s\"", pszFuncName, pDmeDeltaState->GetName(), pDmeMesh->GetName() );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ResetState( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexData *pDmeEditBaseState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditBaseState, __func__ ) )
|
|
return false;
|
|
|
|
return pDmeMesh->SetBaseStateToDelta( NULL, pDmeEditBaseState );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetState( CDmeMesh *pDmeMesh, const char *pszDeltaName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexData *pDmeEditBaseState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !pDmeEditBaseState )
|
|
return false;
|
|
|
|
if ( !Q_stricmp( "base", pszDeltaName ) || !Q_stricmp( "bind", pszDeltaName ) )
|
|
return ResetState( pDmeMesh );
|
|
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( !DeltaStateSanityCheck( pDmeMesh, pDmeDelta, __func__ ) )
|
|
return false;
|
|
pDmeDelta->Resolve();
|
|
|
|
return pDmeMesh->SetBaseStateToDelta( pDmeDelta, pDmeEditBaseState );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool RemoveFacesWithMaterial( CDmeMesh *pDmeMesh, const char *pszMaterialName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
return CDmMeshUtils::RemoveFacesWithMaterial( pDmeMesh, pszMaterialName );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool RemoveFacesWithMoreThanNVerts( CDmeMesh *pDmeMesh, int nVertexCount )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
return CDmMeshUtils::RemoveFacesWithMoreThanNVerts( pDmeMesh, nVertexCount );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// After an operation to the bind base state, copy the bind data around to all
|
|
// other base states...
|
|
//-----------------------------------------------------------------------------
|
|
void FixupBaseStates( CDmeMesh * pDmeMesh )
|
|
{
|
|
CDmeVertexData *pBindState = pDmeMesh->GetBindBaseState();
|
|
if ( pBindState )
|
|
{
|
|
const int nBaseStateCount = pDmeMesh->BaseStateCount();
|
|
for ( int i = 0; i < nBaseStateCount; ++i )
|
|
{
|
|
CDmeVertexData *pBaseState = pDmeMesh->GetBaseState( i );
|
|
if ( pBindState != pBaseState )
|
|
{
|
|
pBindState->CopyTo( pBaseState );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Mirror( CDmeMesh *pDmeMesh, const char *pszAxis /* = "x" */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
if ( !pszAxis || !*pszAxis )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No axis specified" );
|
|
|
|
int nAxis = -1;
|
|
switch ( *pszAxis )
|
|
{
|
|
case 'x':
|
|
case 'X':
|
|
nAxis = 0;
|
|
break;
|
|
case 'y':
|
|
case 'Y':
|
|
nAxis = 1;
|
|
break;
|
|
case 'z':
|
|
case 'Z':
|
|
nAxis = 2;
|
|
break;
|
|
}
|
|
|
|
if ( nAxis < 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Invalid axis \"%s\" specified, must be one of \"x\", \"y\" or \"z\"", pszAxis );
|
|
|
|
// Mirror operates on "bind" state
|
|
const bool bRetVal = CDmMeshUtils::Mirror( pDmeMesh, nAxis );
|
|
|
|
FixupBaseStates( pDmeMesh );
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ComputeNormals( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
pDmeMesh->ComputeDeltaStateNormals();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the CDmeCombinationOperator ultimately controlling the specified
|
|
// DmeMesh by searched backwards on elements referring to "targets"
|
|
// Returns NULL if not found
|
|
//-----------------------------------------------------------------------------
|
|
CDmeCombinationOperator *GetComboOpFromMesh( CDmeMesh *pDmeMesh )
|
|
{
|
|
if ( !pDmeMesh )
|
|
return NULL;
|
|
|
|
CUtlRBTree< CDmElement * > visited( CDefOps< CDmElement * >::LessFunc );
|
|
visited.Insert( pDmeMesh );
|
|
|
|
const CUtlSymbolLarge sTargets = g_pDataModel->GetSymbol( "targets" );
|
|
const CUtlSymbolLarge sTarget = g_pDataModel->GetSymbol( "target" );
|
|
|
|
CDmElement *pDmThisElement = pDmeMesh;
|
|
CDmElement *pDmNextElement = NULL;
|
|
|
|
while ( pDmThisElement )
|
|
{
|
|
pDmNextElement = FindReferringElement< CDmElement >( pDmThisElement, sTargets );
|
|
if ( !pDmNextElement )
|
|
{
|
|
pDmNextElement = FindReferringElement< CDmElement >( pDmThisElement, sTarget );
|
|
}
|
|
|
|
if ( !pDmNextElement )
|
|
break;
|
|
|
|
pDmThisElement = pDmNextElement;
|
|
|
|
if ( CastElement< CDmeCombinationOperator >( pDmThisElement ) )
|
|
return CastElement< CDmeCombinationOperator >( pDmThisElement );
|
|
|
|
if ( visited.IsValidIndex( visited.Find( pDmThisElement ) ) )
|
|
break;
|
|
|
|
visited.Insert( pDmThisElement );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ComputeWrinkles( CDmeMesh *pDmeMesh, bool bOverwrite /* = false */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
pDmeCombo->GenerateWrinkleDeltas( bOverwrite );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ComputeNormalWrinkles( CDmeMesh *pDmeMesh, float flScale, bool bOverwrite /* = false */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
|
|
pDmeCombo->GenerateWrinkleDeltas( bOverwrite, true, flScale );
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ComputeWrinkle( CDmeMesh *pDmeMesh, const char *pszDeltaName, float flScale, const char *pszOperation /* = "replace" */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( !pDmeDelta )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Cannot find Delta state \"%s\" on mesh \"%s\"", pszDeltaName, pDmeMesh->GetName() );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
CDmMeshUtils::WrinkleOp wrinkleOp = StringHasPrefix( pszOperation, "r" ) ? CDmMeshUtils::kReplace : CDmMeshUtils::kAdd;
|
|
|
|
const int nControlCount = pDmeCombo->GetControlCount();
|
|
for ( int nControlIndex = 0; nControlIndex < nControlCount; ++nControlIndex )
|
|
{
|
|
const int nRawControlCount = pDmeCombo->GetRawControlCount( nControlIndex );
|
|
for ( int nRawControlIndex = 0; nRawControlIndex < nRawControlCount; ++nRawControlIndex )
|
|
{
|
|
if ( Q_strcmp( pszDeltaName, pDmeCombo->GetRawControlName( nControlIndex, nRawControlIndex ) ) )
|
|
continue;
|
|
|
|
pDmeCombo->SetWrinkleScale( nControlIndex, pszDeltaName, 1.0f );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
CDmeVertexData *pDmeBindState = pDmeMesh->GetBindBaseState();
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return false;
|
|
|
|
return CDmMeshUtils::CreateWrinkleDeltaFromBaseState( pDmeDelta, flScale, wrinkleOp, pDmeMesh, pDmeBindState, pDmeEditState );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ComputeNormalWrinkle( CDmeMesh *pDmeMesh, const char *pszDeltaName, float flScale, const char *pszOperation /* = "replace" */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( !pDmeDelta )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Cannot find Delta state \"%s\" on mesh \"%s\"", pszDeltaName, pDmeMesh->GetName() );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
CDmMeshUtils::WrinkleOp wrinkleOp = StringHasPrefix( pszOperation, "r" ) ? CDmMeshUtils::kReplace : CDmMeshUtils::kAdd;
|
|
|
|
const int nControlCount = pDmeCombo->GetControlCount();
|
|
for ( int nControlIndex = 0; nControlIndex < nControlCount; ++nControlIndex )
|
|
{
|
|
const int nRawControlCount = pDmeCombo->GetRawControlCount( nControlIndex );
|
|
for ( int nRawControlIndex = 0; nRawControlIndex < nRawControlCount; ++nRawControlIndex )
|
|
{
|
|
if ( Q_strcmp( pszDeltaName, pDmeCombo->GetRawControlName( nControlIndex, nRawControlIndex ) ) )
|
|
continue;
|
|
|
|
pDmeCombo->SetWrinkleScale( nControlIndex, pszDeltaName, 1.0f );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
CDmeVertexData *pDmeBindState = pDmeMesh->GetBindBaseState();
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return false;
|
|
|
|
return CDmMeshUtils::CreateWrinkleDeltaFromBaseState( pDmeDelta, flScale, wrinkleOp, pDmeMesh, pDmeBindState, pDmeEditState, true );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SaveDelta( CDmeMesh *pDmeMesh, const char *pszDeltaName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return false;
|
|
|
|
// See if it's the "base" state we're updating and not a new delta state at all
|
|
if ( !Q_stricmp( "base", pszDeltaName ) || !Q_stricmp( "bind", pszDeltaName ) )
|
|
{
|
|
CDmeVertexData *pDmeBind = pDmeMesh->GetBindBaseState();
|
|
if ( !pDmeBind )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Couldn't get bind base state from mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
if ( pDmeEditState == pDmeBind )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Current state on mesh is the bind state on mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
pDmeEditState->CopyTo( pDmeBind );
|
|
|
|
return true;
|
|
}
|
|
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->ModifyOrCreateDeltaStateFromBaseState( pszDeltaName, pDmeEditState );
|
|
if ( pDmeDelta )
|
|
{
|
|
pDmeDelta->Resolve();
|
|
return true;
|
|
}
|
|
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Couldn't create new delta state from base state on mesh \"%s\"", pDmeMesh->GetName() );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool DeleteDelta( CDmeMesh *pDmeMesh, const char *pszDeltaName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( !pDmeDelta )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Cannot find Delta state \"%s\" on mesh \"%s\"", pszDeltaName, pDmeMesh->GetName() );
|
|
|
|
return pDmeMesh->DeleteDeltaState( pszDeltaName );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helper function for Scale
|
|
//-----------------------------------------------------------------------------
|
|
static void ScaleDeltaPositions(
|
|
const CDmrArrayConst< Vector > &bindPosData,
|
|
CDmeVertexDeltaData *pDmeDelta,
|
|
float flScaleX,
|
|
float flScaleY,
|
|
float flScaleZ )
|
|
{
|
|
const int nPosIndex = pDmeDelta->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
|
|
if ( nPosIndex < 0 )
|
|
return;
|
|
|
|
CDmrArray< Vector > posData = pDmeDelta->GetVertexData( nPosIndex );
|
|
const int nPosDataCount = posData.Count();
|
|
if ( nPosDataCount <= 0 )
|
|
return;
|
|
|
|
Vector *pPosArray = reinterpret_cast< Vector * >( alloca( nPosDataCount * sizeof( Vector ) ) );
|
|
|
|
for ( int j = 0; j < nPosDataCount; ++j )
|
|
{
|
|
const Vector &s = posData.Get( j );
|
|
Vector &d = pPosArray[ j ];
|
|
d.x = s.x * flScaleX;
|
|
d.y = s.y * flScaleY;
|
|
d.z = s.z * flScaleZ;
|
|
}
|
|
|
|
posData.SetMultiple( 0, nPosDataCount, pPosArray );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Scale( CDmeMesh *pDmeMesh, float flScaleX, float flScaleY, float flScaleZ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
int nArraySize = 0;
|
|
Vector *pPosArray = NULL;
|
|
|
|
const int nBaseStateCount = pDmeMesh->BaseStateCount();
|
|
for ( int i = 0; i < nBaseStateCount; ++i )
|
|
{
|
|
CDmeVertexData *pBase = pDmeMesh->GetBaseState( i );
|
|
const int nPosIndex = pBase->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
|
|
if ( nPosIndex < 0 )
|
|
continue;
|
|
|
|
CDmrArray< Vector > posData = pBase->GetVertexData( nPosIndex );
|
|
const int nPosDataCount = posData.Count();
|
|
if ( nPosDataCount <= 0 )
|
|
continue;
|
|
|
|
if ( nArraySize < nPosDataCount || pPosArray == NULL )
|
|
{
|
|
pPosArray = reinterpret_cast< Vector * >( alloca( nPosDataCount * sizeof( Vector ) ) );
|
|
if ( pPosArray )
|
|
{
|
|
nArraySize = nPosDataCount;
|
|
}
|
|
}
|
|
|
|
if ( nArraySize < nPosDataCount )
|
|
continue;
|
|
|
|
for ( int j = 0; j < nPosDataCount; ++j )
|
|
{
|
|
const Vector &s = posData.Get( j );
|
|
Vector &d = pPosArray[ j ];
|
|
d.x = s.x * flScaleX;
|
|
d.y = s.y * flScaleY;
|
|
d.z = s.z * flScaleZ;
|
|
}
|
|
|
|
posData.SetMultiple( 0, nPosDataCount, pPosArray );
|
|
}
|
|
|
|
{
|
|
CDmeVertexData *pBind = pDmeMesh->GetBindBaseState();
|
|
const int nPosIndex = pBind ? pBind->FindFieldIndex( CDmeVertexData::FIELD_POSITION ) : -1;
|
|
|
|
if ( !pBind || nPosIndex < 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Can't scale delta states on mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
const CDmrArrayConst< Vector > posData = pBind->GetVertexData( nPosIndex );
|
|
|
|
const int nDeltaStateCount = pDmeMesh->DeltaStateCount();
|
|
for ( int i = 0; i < nDeltaStateCount; ++i )
|
|
{
|
|
ScaleDeltaPositions( posData, pDmeMesh->GetDeltaState( i ), flScaleX, flScaleY, flScaleZ );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Scale( CDmeMesh *pDmeMesh, float flScale )
|
|
{
|
|
return Scale( pDmeMesh, flScale, flScale, flScale );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetStereoControl( CDmeMesh *pDmeMesh, const char *pszControlName, bool bStereo /* = true */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
const ControlIndex_t nControlIndex = pDmeCombo->FindControlIndex( pszControlName );
|
|
if ( nControlIndex < 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No control named \"%s\" found on combo op \"%s\" on mesh \"%s\"", pszControlName, pDmeCombo->GetName(), pDmeMesh->GetName() );
|
|
|
|
pDmeCombo->SetStereoControl( nControlIndex, bStereo );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetEyelidControl( CDmeMesh *pDmeMesh, const char *pszControlName, bool bEyelid /* = true */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
const ControlIndex_t nControlIndex = pDmeCombo->FindControlIndex( pszControlName );
|
|
if ( nControlIndex < 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No control named \"%s\" found on combo op \"%s\" on mesh \"%s\"", pszControlName, pDmeCombo->GetName(), pDmeMesh->GetName() );
|
|
|
|
pDmeCombo->SetEyelidControl( nControlIndex, bEyelid );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
float MaxDeltaDistance( CDmeMesh *pDmeMesh, const char *pszDeltaName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( !pDmeDelta )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Cannot find Delta state \"%s\" on mesh \"%s\"", pszDeltaName, pDmeMesh->GetName() );
|
|
|
|
if ( !pDmeDelta )
|
|
return 0.0f;
|
|
|
|
float fSqMaxDelta = 0.0f;
|
|
float fTmpSqLength;
|
|
|
|
const CUtlVector< Vector > &positions = pDmeDelta->GetPositionData();
|
|
|
|
const int nPositionCount = positions.Count();
|
|
for ( int i = 0; i < nPositionCount; ++i )
|
|
{
|
|
fTmpSqLength = positions[ i ].LengthSqr();
|
|
if ( fTmpSqLength < fSqMaxDelta )
|
|
continue;
|
|
|
|
fSqMaxDelta = fTmpSqLength;
|
|
}
|
|
|
|
return sqrt( fSqMaxDelta );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetWrinkleScale( CDmeMesh *pDmeMesh, const char *pszControlName, const char *pszRawControlName, float flScale )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
const ControlIndex_t nControlIndex = pDmeCombo->FindControlIndex( pszControlName );
|
|
if ( nControlIndex < 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No control named \"%s\" found on combo op \"%s\" on mesh \"%s\"", pszControlName, pDmeCombo->GetName(), pDmeMesh->GetName() );
|
|
|
|
// Check to see if the raw control exists
|
|
bool bFoundRawControl = false;
|
|
for ( int nRawControlIndex = 0; nRawControlIndex < pDmeCombo->GetRawControlCount( nControlIndex ); ++nRawControlIndex )
|
|
{
|
|
if ( !Q_strcmp( pszRawControlName, pDmeCombo->GetRawControlName( nControlIndex, nRawControlIndex ) ) )
|
|
{
|
|
bFoundRawControl = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !bFoundRawControl )
|
|
{
|
|
CUtlString rawControls;
|
|
for ( int nRawControlIndex = 0; nRawControlIndex < pDmeCombo->GetRawControlCount( nControlIndex ); ++nRawControlIndex )
|
|
{
|
|
if ( rawControls.Length() > 0 )
|
|
{
|
|
rawControls += ", ";
|
|
}
|
|
rawControls += pDmeCombo->GetRawControlName( nControlIndex, nRawControlIndex );
|
|
}
|
|
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Control \"%s\" does not have Raw Control \"%s\" on combo op \"%s\" on mesh \"%s\"", pszControlName, pszRawControlName, pDmeCombo->GetName(), pDmeMesh->GetName() );
|
|
}
|
|
|
|
pDmeCombo->SetWrinkleScale( nControlIndex, pszRawControlName, flScale );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// If it finds a duplicate control name, reports an error message and
|
|
// returns it found one
|
|
// Helper function for GroupControls
|
|
//-----------------------------------------------------------------------------
|
|
bool HasDuplicateControlName(
|
|
CDmeCombinationOperator *pDmeCombo,
|
|
const char *pControlName,
|
|
CUtlVector< const char * > &retiredControlNames )
|
|
{
|
|
int i;
|
|
int nRetiredControlNameCount = retiredControlNames.Count();
|
|
for ( i = 0; i < nRetiredControlNameCount; ++i )
|
|
{
|
|
if ( !Q_stricmp( retiredControlNames[i], pControlName ) )
|
|
break;
|
|
}
|
|
|
|
if ( i == nRetiredControlNameCount )
|
|
{
|
|
if ( pDmeCombo->FindControlIndex( pControlName ) >= 0 )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool GroupControls( CDmeMesh *pDmeMesh, const char *pszGroupName, CUtlVector< const char * > &rawControlNames )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
// Loop through controls to see if any are already group controls, warn and remove
|
|
|
|
CUtlVector< const char * > validControlNames;
|
|
bool bStereo = false;
|
|
bool bEyelid = false;
|
|
|
|
for ( int i = 0; i < rawControlNames.Count(); ++i )
|
|
{
|
|
ControlIndex_t nControlIndex = pDmeCombo->FindControlIndex( rawControlNames[ i ] );
|
|
if ( nControlIndex < 0 )
|
|
{
|
|
DMXEDIT_WARNING( "Control \"%s\" Doesn't Exist, Ignoring", pszGroupName );
|
|
continue;
|
|
}
|
|
|
|
if ( pDmeCombo->GetRawControlCount( nControlIndex ) > 1 )
|
|
{
|
|
DMXEDIT_WARNING( "Control \"%s\" Isn't A Raw Control, Ignoring", pszGroupName );
|
|
continue;
|
|
}
|
|
|
|
validControlNames.AddToTail( rawControlNames[ i ] );
|
|
|
|
if ( pDmeCombo->IsStereoControl( nControlIndex ) )
|
|
{
|
|
bStereo = true;
|
|
}
|
|
|
|
if ( pDmeCombo->IsEyelidControl( nControlIndex ) )
|
|
{
|
|
bEyelid = true;
|
|
}
|
|
}
|
|
|
|
if ( HasDuplicateControlName( pDmeCombo, pszGroupName, validControlNames ) )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Duplicate Control \"%s\" Found", pszGroupName );
|
|
|
|
if ( validControlNames.Count() <= 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No Valid Controls Specified" );
|
|
|
|
// Remove the old controls
|
|
for ( int i = 0; i < validControlNames.Count(); ++i )
|
|
{
|
|
pDmeCombo->RemoveControl( validControlNames[i] );
|
|
}
|
|
|
|
// Create new control
|
|
ControlIndex_t nNewControl = pDmeCombo->FindOrCreateControl( pszGroupName, bStereo );
|
|
pDmeCombo->SetEyelidControl( nNewControl, bEyelid );
|
|
for ( int i = 0; i < validControlNames.Count(); ++i )
|
|
{
|
|
pDmeCombo->AddRawControl( nNewControl, validControlNames[i] );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void GetDeltaNames( CDmeMesh *pDmeMesh, CUtlVector< const char * > *pOutStringList )
|
|
{
|
|
if ( !pOutStringList )
|
|
{
|
|
DMXEDIT_WARNING( "No storage passed for result" );
|
|
return;
|
|
}
|
|
|
|
pOutStringList->RemoveAll();
|
|
|
|
if ( !pDmeMesh )
|
|
{
|
|
DMXEDIT_WARNING( "No mesh specified" );
|
|
return;
|
|
}
|
|
|
|
const int nDeltaStateCount = pDmeMesh->DeltaStateCount();
|
|
|
|
for ( int i = 0; i < nDeltaStateCount; ++i )
|
|
{
|
|
const CDmeVertexDeltaData *pDmeDelta = pDmeMesh->GetDeltaState( i );
|
|
if ( !pDmeDelta )
|
|
{
|
|
pOutStringList->AddToTail( "<unknown>" );
|
|
}
|
|
else
|
|
{
|
|
pOutStringList->AddToTail( pDmeDelta->GetName() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void GetRawControlNames( CDmeMesh *pDmeMesh, CUtlVector< const char * > *pOutStringList, const char *pszControlName /* = NULL */ )
|
|
{
|
|
if ( !pOutStringList )
|
|
{
|
|
DMXEDIT_WARNING( "No storage passed for result" );
|
|
return;
|
|
}
|
|
|
|
pOutStringList->RemoveAll();
|
|
|
|
if ( !pDmeMesh )
|
|
{
|
|
DMXEDIT_WARNING( "No mesh specified" );
|
|
return;
|
|
}
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
{
|
|
DMXEDIT_WARNING( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
return;
|
|
}
|
|
|
|
if ( pszControlName )
|
|
{
|
|
const ControlIndex_t nControlIndex = pDmeCombo->FindControlIndex( pszControlName );
|
|
if ( nControlIndex < 0 )
|
|
{
|
|
DMXEDIT_WARNING( "No control named \"%s\" on DmeCombinationOperator \"%s\" controlling mesh \"%s\"", pszControlName, pDmeCombo->GetName(), pDmeMesh->GetName() );
|
|
}
|
|
else
|
|
{
|
|
const int nRawControlCount = pDmeCombo->GetRawControlCount( nControlIndex );
|
|
for ( int i = 0; i < nRawControlCount; ++i )
|
|
{
|
|
pOutStringList->AddToTail( pDmeCombo->GetRawControlName( nControlIndex, i ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int nRawControlCount = pDmeCombo->GetRawControlCount();
|
|
for ( int i = 0; i < nRawControlCount; ++i )
|
|
{
|
|
pOutStringList->AddToTail( pDmeCombo->GetRawControlName( i ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void GetControlNames( CDmeMesh *pDmeMesh, CUtlVector< const char * > *pOutStringList )
|
|
{
|
|
if ( !pOutStringList )
|
|
{
|
|
DMXEDIT_WARNING( "No storage passed for result" );
|
|
return;
|
|
}
|
|
|
|
pOutStringList->RemoveAll();
|
|
|
|
if ( !pDmeMesh )
|
|
{
|
|
DMXEDIT_WARNING( "No mesh specified" );
|
|
return;
|
|
}
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
{
|
|
DMXEDIT_WARNING( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
return;
|
|
}
|
|
|
|
const int nControlCount = pDmeCombo->GetControlCount();
|
|
for ( int i = 0; i < nControlCount; ++i )
|
|
{
|
|
pOutStringList->AddToTail( pDmeCombo->GetControlName( i ) );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ReorderControls( CDmeMesh *pDmeMesh, CUtlVector< const char * > &controlNames )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
// Loop through controls to see if any are already group controls, warn and remove
|
|
|
|
CUtlVector< const char * > validControlNames;
|
|
|
|
for ( int i = 0; i < controlNames.Count(); ++i )
|
|
{
|
|
ControlIndex_t nControlIndex = pDmeCombo->FindControlIndex( controlNames[ i ] );
|
|
if ( nControlIndex < 0 )
|
|
{
|
|
DMXEDIT_WARNING( "Control \"%s\" doesn't exist, ignoring", controlNames[ i ] );
|
|
continue;
|
|
}
|
|
|
|
validControlNames.AddToTail( controlNames[ i ] );
|
|
}
|
|
|
|
if ( validControlNames.Count() <= 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No Valid Controls Specified" );
|
|
|
|
for ( int i = 0; i < validControlNames.Count(); ++i )
|
|
{
|
|
pDmeCombo->MoveControlBefore( validControlNames[ i ], pDmeCombo->GetControlName( i ) );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool AddDominationRule( CDmeMesh *pDmeMesh, CUtlVector< const char * > &dominators, CUtlVector< const char * > &supressed )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeCombo = GetComboOpFromMesh( pDmeMesh );
|
|
if ( !pDmeCombo )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No DmeCombinationOperator found controlling mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
pDmeCombo->AddDominationRule(
|
|
dominators.Count(), ( const char ** )dominators.Base(),
|
|
supressed.Count(), ( const char ** )supressed.Base() );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
static const char s_szSelAttrName[] = "__dmxedit_selection";
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeSingleIndexedComponent *FindOrCreateMeshSelection( CDmeMesh *pDmeMesh, CDmeSingleIndexedComponent *pDmePassedSelection )
|
|
{
|
|
if ( pDmePassedSelection )
|
|
return pDmePassedSelection;
|
|
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmAttribute *pDmeSelAttr = NULL;
|
|
|
|
if ( pDmeMesh->HasAttribute( s_szSelAttrName ) )
|
|
{
|
|
pDmeSelAttr = pDmeMesh->GetAttribute( s_szSelAttrName, AT_ELEMENT );
|
|
if ( !pDmeSelAttr )
|
|
{
|
|
DMXEDIT_WARNING( "Attribute %s.%s is of type %s, not AT_ELEMENT, removing", pDmeMesh->GetName(), pDmeSelAttr->GetName(), pDmeSelAttr->GetTypeString() );
|
|
pDmeMesh->RemoveAttribute( s_szSelAttrName );
|
|
pDmeSelAttr = NULL;
|
|
}
|
|
}
|
|
|
|
if ( pDmeSelAttr == NULL )
|
|
{
|
|
CDmeSingleIndexedComponent *pTempSelection = CreateElement< CDmeSingleIndexedComponent >( s_szSelAttrName, DMFILEID_INVALID );
|
|
if ( !pTempSelection )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't create CDmeSingleIndexedComponent %s element", s_szSelAttrName );
|
|
|
|
pDmeSelAttr = pDmeMesh->AddAttributeElement< CDmeSingleIndexedComponent >( s_szSelAttrName );
|
|
if ( !pDmeSelAttr )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't create %s.%s attribute", pDmeMesh->GetName(), s_szSelAttrName );
|
|
|
|
pDmeSelAttr->AddFlag( FATTRIB_DONTSAVE );
|
|
pDmeSelAttr->SetValue< CDmeSingleIndexedComponent >( pTempSelection );
|
|
}
|
|
|
|
return pDmeSelAttr->GetValueElement< CDmeSingleIndexedComponent >();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool CleanupMeshSelection( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
pDmeMesh->RemoveAttribute( s_szSelAttrName );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Combines the two selections via the selectOp and puts result into pOriginal
|
|
//-----------------------------------------------------------------------------
|
|
void DoSelectOp( const SelectOp_t &nSelectOp, CDmeSingleIndexedComponent *pDmeOriginalSel, const CDmeSingleIndexedComponent *pDmeNewSel )
|
|
{
|
|
if ( !pDmeOriginalSel || !pDmeNewSel )
|
|
return;
|
|
|
|
switch ( nSelectOp )
|
|
{
|
|
case ADD_SELECT_OP:
|
|
pDmeOriginalSel->Add( pDmeNewSel );
|
|
break;
|
|
case SUBTRACT_SELECT_OP:
|
|
pDmeOriginalSel->Subtract( pDmeNewSel );
|
|
break;
|
|
case TOGGLE_SELECT_OP:
|
|
{
|
|
CDmeSingleIndexedComponent *pIntersection = CreateElement< CDmeSingleIndexedComponent >( "__dmxedit_intersection", DMFILEID_INVALID );
|
|
if ( !pIntersection )
|
|
return;
|
|
|
|
CDmeSingleIndexedComponent *pNewCopy = CreateElement< CDmeSingleIndexedComponent >( "__dmxedit_newCopy", DMFILEID_INVALID );
|
|
if ( !pNewCopy )
|
|
{
|
|
g_pDataModel->DestroyElement( pIntersection->GetHandle() );
|
|
return;
|
|
}
|
|
|
|
pDmeOriginalSel->CopyAttributesTo( pIntersection );
|
|
pIntersection->Intersection( pDmeNewSel );
|
|
pDmeOriginalSel->Subtract( pIntersection );
|
|
|
|
pDmeNewSel->CopyAttributesTo( pNewCopy );
|
|
pNewCopy->Subtract( pIntersection );
|
|
pDmeOriginalSel->Add( pNewCopy );
|
|
|
|
g_pDataModel->DestroyElement( pIntersection->GetHandle() );
|
|
g_pDataModel->DestroyElement( pNewCopy->GetHandle() );
|
|
}
|
|
break;
|
|
case INTERSECT_SELECT_OP:
|
|
pDmeOriginalSel->Intersection( pDmeNewSel );
|
|
break;
|
|
case REPLACE_SELECT_OP:
|
|
{
|
|
CUtlString originalName = pDmeOriginalSel->GetName();
|
|
pDmeNewSel->CopyAttributesTo( pDmeOriginalSel );
|
|
pDmeOriginalSel->SetName( originalName );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeSingleIndexedComponent *Select(
|
|
CDmeMesh *pDmeMesh,
|
|
SelectOp_t nSelectOp,
|
|
const char *pszSelectString,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
if ( nSelectOp < 0 || nSelectOp >= INVALID_SELECT_OP )
|
|
return NULL;
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
|
|
// Figure out if pszSelectString is one of the keywords, all, none or a delta state
|
|
// NOTE: This means that delta states with a name of all or none are not selectable
|
|
|
|
if ( !Q_stricmp( "ALL", pszSelectString ) )
|
|
{
|
|
pDmeMesh->SelectAllVertices( pDmeSelection );
|
|
}
|
|
else if ( !Q_stricmp( "NONE", pszSelectString ) )
|
|
{
|
|
pDmeSelection->Clear();
|
|
}
|
|
else
|
|
{
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->FindDeltaState( pszSelectString );
|
|
if ( !pDmeDelta )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Mesh \"%s\" does not have a delta state named \"%s\" to select", pDmeMesh->GetName(), pszSelectString );
|
|
|
|
if ( nSelectOp == REPLACE_SELECT_OP )
|
|
{
|
|
pDmeMesh->SelectVerticesFromDelta( pDmeDelta, pDmeSelection );
|
|
}
|
|
else
|
|
{
|
|
CDmeSingleIndexedComponent *pDmeTmpSelection = CreateElement< CDmeSingleIndexedComponent >( "__dmxedit_tmpSelection", DMFILEID_INVALID );
|
|
if ( !pDmeTmpSelection )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't create a tmp selection element while selecting delta \"%s\" on mesh \"%s\"", pszSelectString, pDmeMesh->GetName() );
|
|
|
|
pDmeMesh->SelectVerticesFromDelta( pDmeDelta, pDmeTmpSelection );
|
|
DoSelectOp( nSelectOp, pDmeSelection, pDmeTmpSelection );
|
|
|
|
g_pDataModel->DestroyElement( pDmeTmpSelection->GetHandle() );
|
|
}
|
|
}
|
|
|
|
return pDmeSelection;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
SelectOp_t StringToSelectOp_t( const char *pszSelectOpString )
|
|
{
|
|
if ( StringHasPrefix( pszSelectOpString, "A" ) )
|
|
return ADD_SELECT_OP;
|
|
|
|
if ( StringHasPrefix( pszSelectOpString, "S" ) )
|
|
return SUBTRACT_SELECT_OP;
|
|
|
|
if ( StringHasPrefix( pszSelectOpString, "T" ) )
|
|
return TOGGLE_SELECT_OP;
|
|
|
|
if ( StringHasPrefix( pszSelectOpString, "I" ) )
|
|
return INTERSECT_SELECT_OP;
|
|
|
|
if ( StringHasPrefix( pszSelectOpString, "R" ) )
|
|
return REPLACE_SELECT_OP;
|
|
|
|
DMXEDIT_WARNING( "Invalid Selection Operation string specified: \"%s\"", pszSelectOpString );
|
|
|
|
return INVALID_SELECT_OP;
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeSingleIndexedComponent *Select(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pszSelectString,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
return Select( pDmeMesh, REPLACE_SELECT_OP, pszSelectString, pDmePassedSelection );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeSingleIndexedComponent *Select(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pszSelectOpString,
|
|
const char *pszSelectString,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
return Select( pDmeMesh, StringToSelectOp_t( pszSelectOpString ), pszSelectString, pDmePassedSelection );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// An Efficient Bounding Sphere
|
|
// by Jack Ritter
|
|
// from "Graphics Gems", Academic Press, 1990
|
|
//
|
|
// Routine to calculate tight bounding sphere over
|
|
// a set of points in 3D
|
|
// This contains the routine find_bounding_sphere(),
|
|
// the struct definition, and the globals used for parameters.
|
|
// The abs() of all coordinates must be < BIGNUMBER
|
|
// Code written by Jack Ritter and Lyle Rains.
|
|
//-----------------------------------------------------------------------------
|
|
void FindBoundingSphere( CUtlVector< Vector > &points, Vector &cen, float &fRad )
|
|
{
|
|
if ( points.Count() <= 0 )
|
|
{
|
|
cen.Zero();
|
|
fRad = 0.0f;
|
|
|
|
return;
|
|
}
|
|
|
|
double dx,dy,dz;
|
|
double rad_sq,xspan,yspan,zspan,maxspan;
|
|
double old_to_p,old_to_p_sq,old_to_new;
|
|
Vector xmin,xmax,ymin,ymax,zmin,zmax,dia1,dia2;
|
|
|
|
// DVec cen;
|
|
double rad;
|
|
|
|
cen = points[ 0 ];
|
|
fRad = 0.0f;
|
|
|
|
xmin.x=ymin.y=zmin.z= FLT_MAX; /* initialize for min/max compare */
|
|
xmax.x=ymax.y=zmax.z= -FLT_MAX;
|
|
|
|
for ( int i = 0; i < points.Count(); ++i )
|
|
{
|
|
const Vector &caller_p = points[ i ];
|
|
|
|
if (caller_p.x<xmin.x)
|
|
xmin = caller_p; /* New xminimum point */
|
|
if (caller_p.x>xmax.x)
|
|
xmax = caller_p;
|
|
if (caller_p.y<ymin.y)
|
|
ymin = caller_p;
|
|
if (caller_p.y>ymax.y)
|
|
ymax = caller_p;
|
|
if (caller_p.z<zmin.z)
|
|
zmin = caller_p;
|
|
if (caller_p.z>zmax.z)
|
|
zmax = caller_p;
|
|
}
|
|
|
|
/* Set xspan = distance between the 2 points xmin & xmax (squared) */
|
|
dx = xmax.x - xmin.x;
|
|
dy = xmax.y - xmin.y;
|
|
dz = xmax.z - xmin.z;
|
|
xspan = dx*dx + dy*dy + dz*dz;
|
|
|
|
/* Same for y & z spans */
|
|
dx = ymax.x - ymin.x;
|
|
dy = ymax.y - ymin.y;
|
|
dz = ymax.z - ymin.z;
|
|
yspan = dx*dx + dy*dy + dz*dz;
|
|
|
|
dx = zmax.x - zmin.x;
|
|
dy = zmax.y - zmin.y;
|
|
dz = zmax.z - zmin.z;
|
|
zspan = dx*dx + dy*dy + dz*dz;
|
|
|
|
/* Set points dia1 & dia2 to the maximally separated pair */
|
|
dia1 = xmin; dia2 = xmax; /* assume xspan biggest */
|
|
maxspan = xspan;
|
|
if (yspan>maxspan)
|
|
{
|
|
maxspan = yspan;
|
|
dia1 = ymin; dia2 = ymax;
|
|
}
|
|
if (zspan>maxspan)
|
|
{
|
|
dia1 = zmin; dia2 = zmax;
|
|
}
|
|
|
|
/* dia1,dia2 is a diameter of initial sphere */
|
|
/* calc initial center */
|
|
cen.x = (dia1.x+dia2.x)/2.0;
|
|
cen.y = (dia1.y+dia2.y)/2.0;
|
|
cen.z = (dia1.z+dia2.z)/2.0;
|
|
/* calculate initial radius**2 and radius */
|
|
dx = dia2.x-cen.x; /* x component of radius vector */
|
|
dy = dia2.y-cen.y; /* y component of radius vector */
|
|
dz = dia2.z-cen.z; /* z component of radius vector */
|
|
rad_sq = dx*dx + dy*dy + dz*dz;
|
|
rad = sqrt(rad_sq);
|
|
|
|
/* SECOND PASS: increment current sphere */
|
|
|
|
for ( int i = 0; i < points.Count(); ++i )
|
|
{
|
|
const Vector &caller_p = points[ i ];
|
|
|
|
dx = caller_p.x-cen.x;
|
|
dy = caller_p.y-cen.y;
|
|
dz = caller_p.z-cen.z;
|
|
old_to_p_sq = dx*dx + dy*dy + dz*dz;
|
|
if (old_to_p_sq > rad_sq) /* do r**2 test first */
|
|
{ /* this point is outside of current sphere */
|
|
old_to_p = sqrt(old_to_p_sq);
|
|
/* calc radius of new sphere */
|
|
rad = (rad + old_to_p) / 2.0;
|
|
rad_sq = rad*rad; /* for next r**2 compare */
|
|
old_to_new = old_to_p - rad;
|
|
/* calc center of new sphere */
|
|
cen.x = (rad*cen.x + old_to_new*caller_p.x) / old_to_p;
|
|
cen.y = (rad*cen.y + old_to_new*caller_p.y) / old_to_p;
|
|
cen.z = (rad*cen.z + old_to_new*caller_p.z) / old_to_p;
|
|
}
|
|
}
|
|
|
|
/*
|
|
fCen.x = cen.x;
|
|
fCen.y = cen.y;
|
|
fCen.z = cen.z;
|
|
*/
|
|
fRad = rad;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
float DeltaRadius( CDmeMesh *pDmeMesh, const char *pszDeltaName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmeVertexData *pBind = pDmeMesh->GetBindBaseState();
|
|
if ( !pBind || !BaseStateSanityCheck( pDmeMesh, pBind, __func__ ) )
|
|
return 0.0f;
|
|
|
|
CDmeVertexDeltaData *pDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( !pDelta || !DeltaStateSanityCheck( pDmeMesh, pDelta, __func__ ) )
|
|
return 0.0f;
|
|
|
|
const CUtlVector< Vector > &bindPos = pBind->GetPositionData();
|
|
const CUtlVector< int > &deltaPosIndices = pDelta->GetVertexIndexData( CDmeVertexData::FIELD_POSITION );
|
|
const CUtlVector< Vector > &deltaPos = pDelta->GetPositionData();
|
|
|
|
Assert( deltaPosIndices.Count() == deltaPos.Count() );
|
|
|
|
CUtlVector< Vector > newPos;
|
|
newPos.SetSize( deltaPos.Count() );
|
|
|
|
for ( int i = 0; i < newPos.Count(); ++i )
|
|
{
|
|
newPos[ i ] = bindPos[ deltaPosIndices[ i ] ] + deltaPos[ i ];
|
|
}
|
|
|
|
Vector vCenter;
|
|
float flRadius = 0.0;
|
|
|
|
FindBoundingSphere( newPos, vCenter, flRadius );
|
|
|
|
return flRadius;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
float SelectionRadius( CDmeMesh *pDmeMesh, CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
{
|
|
DMXEDIT_WARNING( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
return 0.0f;
|
|
}
|
|
|
|
if ( pDmeSelection->Count() == 0 )
|
|
return 0.0f;
|
|
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return 0.0f;
|
|
|
|
CUtlVector< int > selection;
|
|
pDmeSelection->GetComponents( selection );
|
|
const CUtlVector< Vector > &pos = pDmeEditState->GetPositionData();
|
|
|
|
CUtlVector< Vector > newPos;
|
|
newPos.SetSize( selection.Count() );
|
|
|
|
for ( int i = 0; i < newPos.Count(); ++i )
|
|
{
|
|
newPos[ i ] = pos[ selection[ i ] ];
|
|
}
|
|
|
|
Vector vCenter;
|
|
float flRadius = 0.0;
|
|
|
|
FindBoundingSphere( newPos, vCenter, flRadius );
|
|
|
|
return flRadius;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool GrowSelection( CDmeMesh *pDmeMesh, int nSize /* = 1 */, CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
{
|
|
DMXEDIT_WARNING( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
return 0.0f;
|
|
}
|
|
|
|
// TODO: Cache the CDmMeshComp object, make it into winged or half edge data structure
|
|
pDmeMesh->GrowSelection( nSize, pDmeSelection, NULL );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ShrinkSelection( CDmeMesh *pDmeMesh, int nSize /* = 1 */, CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
{
|
|
DMXEDIT_WARNING( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
return 0.0f;
|
|
}
|
|
|
|
// TODO: Cache the CDmMeshComp object, make it into winged or half edge data structure
|
|
pDmeMesh->ShrinkSelection( nSize, pDmeSelection, NULL );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
int StringToDistanceType( const char *pszDistanceTypeString )
|
|
{
|
|
if ( pszDistanceTypeString && ( *pszDistanceTypeString == 'a' || *pszDistanceTypeString == 'A' ) )
|
|
return CDmeMesh::DIST_ABSOLUTE;
|
|
|
|
if ( pszDistanceTypeString && ( *pszDistanceTypeString == 'r' || *pszDistanceTypeString == 'R' ) )
|
|
return CDmeMesh::DIST_RELATIVE;
|
|
|
|
if ( pszDistanceTypeString && ( *pszDistanceTypeString == 'd' || *pszDistanceTypeString == 'D' ) )
|
|
return CDmeMesh::DIST_DEFAULT;
|
|
|
|
DMXEDIT_WARNING( "Invalid Distance Type string specified: \"%s\"", pszDistanceTypeString );
|
|
return -1;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetDistanceType( CDmeMesh::Distance_t nDistanceType )
|
|
{
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown distance type: %d", nDistanceType );
|
|
|
|
g_pDmxEditImpl->SetDistanceType( nDistanceType );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetDistanceType( const char *pszDistanceType )
|
|
{
|
|
const int nDistanceType = StringToDistanceType( pszDistanceType );
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
return false;
|
|
|
|
return SetDistanceType( static_cast< CDmeMesh::Distance_t >( nDistanceType ) );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
int StringToFalloffType( const char *pszFalloffTypeString )
|
|
{
|
|
if ( !Q_strnicmp( pszFalloffTypeString, "L", 1 ) )
|
|
return CDmeMesh::LINEAR;
|
|
|
|
else if ( !Q_strnicmp( pszFalloffTypeString, "ST", 2 ) )
|
|
return CDmeMesh::STRAIGHT;
|
|
|
|
else if ( !Q_strnicmp( pszFalloffTypeString, "B", 1 ) )
|
|
return CDmeMesh::BELL;
|
|
|
|
else if ( !Q_strnicmp( pszFalloffTypeString, "SM", 2 ) )
|
|
return CDmeMesh::SMOOTH;
|
|
|
|
else if ( !Q_strnicmp( pszFalloffTypeString, "SP", 2 ) )
|
|
return CDmeMesh::SPIKE;
|
|
|
|
else if ( !Q_strnicmp( pszFalloffTypeString, "D", 1 ) )
|
|
return CDmeMesh::DOME;
|
|
|
|
DMXEDIT_WARNING( "Invalid Falloff Type string specified: \"%s\"", pszFalloffTypeString );
|
|
return -1;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Interp(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pszDeltaName,
|
|
float flWeight /* = 1.0f */,
|
|
float flFeatherDistance /* = 0.0f */,
|
|
CDmeMesh::Falloff_t nFalloffType /* CDmeMesh::STRAIGHT */,
|
|
CDmeMesh::Distance_t nDistanceType /* = CDmeMesh::DIST_DEFAULT */,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
if ( nFalloffType < 0 || nFalloffType > CDmeMesh::DOME )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown falloff type: %d", nFalloffType );
|
|
|
|
nDistanceType = nDistanceType == CDmeMesh::DIST_DEFAULT ? g_pDmxEditImpl->GetDistanceType() : nDistanceType;
|
|
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown distance type: %d", nDistanceType );
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
|
|
CDmeSingleIndexedComponent *pNewSelection = flFeatherDistance > 0.0f ? pDmeMesh->FeatherSelection( flFeatherDistance, nFalloffType, nDistanceType, pDmeSelection, NULL ) : NULL;
|
|
|
|
bool bRetVal = false;
|
|
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return false;
|
|
|
|
if ( !Q_stricmp( "base", pszDeltaName ) || !Q_stricmp( "bind", pszDeltaName ) )
|
|
{
|
|
bRetVal = pDmeMesh->InterpMaskedDelta( NULL, pDmeEditState, flWeight, pNewSelection ? pNewSelection : pDmeSelection );
|
|
}
|
|
else
|
|
{
|
|
CDmeVertexDeltaData *pDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( !DeltaStateSanityCheck( pDmeMesh, pDelta, __func__ ) )
|
|
return false;
|
|
|
|
bRetVal = pDmeMesh->InterpMaskedDelta( pDelta, pDmeEditState, flWeight, pNewSelection ? pNewSelection : pDmeSelection );
|
|
}
|
|
|
|
if ( pNewSelection )
|
|
{
|
|
g_pDataModel->DestroyElement( pNewSelection->GetHandle() );
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Interp(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pszDeltaName,
|
|
float flWeight /* = 1.0f */,
|
|
float flFeatherDistance /* = 0.0f */,
|
|
const char *pszFalloffType /* = "STRAIGHT" */,
|
|
const char *pszDistanceType /* = "DEFAULT" */,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
const int nFalloffType = StringToFalloffType( pszFalloffType );
|
|
if ( nFalloffType < 0 || nFalloffType > CDmeMesh::DOME )
|
|
return false;
|
|
|
|
const int nDistanceType = StringToDistanceType( pszDistanceType );
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
return false;
|
|
|
|
return Interp( pDmeMesh, pszDeltaName, flWeight, flFeatherDistance, static_cast< CDmeMesh::Falloff_t >( nFalloffType ), CDmeMesh::Distance_t( nDistanceType ), pDmePassedSelection );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Add(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pDeltaName,
|
|
float flWeight /* = 1.0f */,
|
|
float flFeatherDistance /* = 0.0f */,
|
|
CDmeMesh::Falloff_t nFalloffType /* = CDmeMesh::STRAIGHT */,
|
|
CDmeMesh::Distance_t nDistanceType /* = CDmeMesh::DIST_DEFAULT */,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
if ( nFalloffType < 0 || nFalloffType > CDmeMesh::DOME )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown falloff type: %d", nFalloffType );
|
|
|
|
nDistanceType = nDistanceType == CDmeMesh::DIST_DEFAULT ? g_pDmxEditImpl->GetDistanceType() : nDistanceType;
|
|
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown distance type: %d", nDistanceType );
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
|
|
CDmeSingleIndexedComponent *pNewSelection = flFeatherDistance > 0.0f ? pDmeMesh->FeatherSelection( flFeatherDistance, nFalloffType, nDistanceType, pDmeSelection, NULL ) : NULL;
|
|
|
|
bool bRetVal = false;
|
|
|
|
CDmeVertexDeltaData *pDelta = pDmeMesh->FindDeltaState( pDeltaName );
|
|
if ( !DeltaStateSanityCheck( pDmeMesh, pDelta, __func__ ) )
|
|
return false;
|
|
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return false;
|
|
|
|
bRetVal = pDmeMesh->AddMaskedDelta( pDelta, pDmeEditState, flWeight, pNewSelection ? pNewSelection : pDmeSelection );
|
|
|
|
if ( pNewSelection )
|
|
{
|
|
g_pDataModel->DestroyElement( pNewSelection->GetHandle() );
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Add(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pszDeltaName,
|
|
float flWeight /* = 1.0f */,
|
|
float flFeatherDistance /* = 0.0f */,
|
|
const char *pszFalloffType /* = "STRAIGHT" */,
|
|
const char *pszDistanceType /* = "DEFAULT" */,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
const int nFalloffType = StringToFalloffType( pszFalloffType );
|
|
if ( nFalloffType < 0 || nFalloffType > CDmeMesh::DOME )
|
|
return false;
|
|
|
|
const int nDistanceType = StringToDistanceType( pszDistanceType );
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
return false;
|
|
|
|
return Add( pDmeMesh, pszDeltaName, flWeight, flFeatherDistance, static_cast< CDmeMesh::Falloff_t >( nFalloffType ), CDmeMesh::Distance_t( nDistanceType ), pDmePassedSelection );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool AddCorrected(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pDeltaName,
|
|
float flWeight /* = 1.0f */,
|
|
float flFeatherDistance /* = 0.0f */,
|
|
CDmeMesh::Falloff_t nFalloffType /* = CDmeMesh::STRAIGHT */,
|
|
CDmeMesh::Distance_t nDistanceType /* = CDmeMesh::DIST_DEFAULT */,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
if ( nFalloffType < 0 || nFalloffType > CDmeMesh::DOME )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown falloff type: %d", nFalloffType );
|
|
|
|
nDistanceType = nDistanceType == CDmeMesh::DIST_DEFAULT ? g_pDmxEditImpl->GetDistanceType() : nDistanceType;
|
|
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown distance type: %d", nDistanceType );
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
|
|
CDmeSingleIndexedComponent *pNewSelection = flFeatherDistance > 0.0f ? pDmeMesh->FeatherSelection( flFeatherDistance, nFalloffType, nDistanceType, pDmeSelection, NULL ) : NULL;
|
|
|
|
bool bRetVal = false;
|
|
|
|
CDmeVertexDeltaData *pDelta = pDmeMesh->FindDeltaState( pDeltaName );
|
|
if ( !DeltaStateSanityCheck( pDmeMesh, pDelta, __func__ ) )
|
|
return false;
|
|
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return false;
|
|
|
|
bRetVal = pDmeMesh->AddCorrectedMaskedDelta( pDelta, pDmeEditState, flWeight, pNewSelection ? pNewSelection : pDmeSelection );
|
|
|
|
if ( pNewSelection )
|
|
{
|
|
g_pDataModel->DestroyElement( pNewSelection->GetHandle() );
|
|
}
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool AddCorrected(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pszDeltaName,
|
|
float flWeight /* = 1.0f */,
|
|
float flFeatherDistance /* = 0.0f */,
|
|
const char *pszFalloffType /* = "STRAIGHT" */,
|
|
const char *pszDistanceType /* = "DEFAULT" */,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
const int nFalloffType = StringToFalloffType( pszFalloffType );
|
|
if ( nFalloffType < 0 || nFalloffType > CDmeMesh::DOME )
|
|
return false;
|
|
|
|
const int nDistanceType = StringToDistanceType( pszDistanceType );
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
return false;
|
|
|
|
return AddCorrected( pDmeMesh, pszDeltaName, flWeight, flFeatherDistance, static_cast< CDmeMesh::Falloff_t >( nFalloffType ), CDmeMesh::Distance_t( nDistanceType ), pDmePassedSelection );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Translate(
|
|
CDmeMesh *pDmeMesh,
|
|
Vector vT,
|
|
float flFeatherDistance,
|
|
CDmeMesh::Falloff_t nFalloffType,
|
|
CDmeMesh::Distance_t nDistanceType,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
nDistanceType = nDistanceType == CDmeMesh::DIST_DEFAULT ? g_pDmxEditImpl->GetDistanceType() : nDistanceType;
|
|
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown distance type: %d", nDistanceType );
|
|
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return false;
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
|
|
CDmeSingleIndexedComponent *pTmpSelection = NULL;
|
|
|
|
if ( !pDmeSelection || pDmeSelection->Count() == 0 )
|
|
{
|
|
pTmpSelection = CreateElement< CDmeSingleIndexedComponent >( "__selectAll", DMFILEID_INVALID );
|
|
pDmeSelection = pTmpSelection;
|
|
pDmeMesh->SelectAllVertices( pDmeSelection );
|
|
}
|
|
|
|
CDmeSingleIndexedComponent *pNewSelection = flFeatherDistance > 0.0f ? pDmeMesh->FeatherSelection( flFeatherDistance, nFalloffType, nDistanceType, pDmeSelection, NULL ) : pDmeSelection;
|
|
|
|
int nArraySize = 0;
|
|
Vector *pPosArray = NULL;
|
|
|
|
const int nPosIndex = pDmeEditState->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
|
|
if ( nPosIndex < 0 )
|
|
return false;
|
|
|
|
CDmrArray< Vector > posData = pDmeEditState->GetVertexData( nPosIndex );
|
|
const int nPosDataCount = posData.Count();
|
|
if ( nPosDataCount <= 0 )
|
|
return false;
|
|
|
|
if ( nArraySize < nPosDataCount || pPosArray == NULL )
|
|
{
|
|
pPosArray = reinterpret_cast< Vector * >( alloca( nPosDataCount * sizeof( Vector ) ) );
|
|
if ( pPosArray )
|
|
{
|
|
nArraySize = nPosDataCount;
|
|
}
|
|
}
|
|
|
|
if ( nArraySize < nPosDataCount )
|
|
return false;
|
|
|
|
if ( nDistanceType == CDmeMesh::DIST_RELATIVE )
|
|
{
|
|
Vector vCenter;
|
|
float fRadius;
|
|
|
|
pDmeMesh->GetBoundingSphere( vCenter, fRadius, pDmeEditState, pDmeSelection );
|
|
|
|
vT *= fRadius;
|
|
}
|
|
|
|
if ( pNewSelection )
|
|
{
|
|
memcpy( pPosArray, posData.Base(), nPosDataCount * sizeof( Vector ) );
|
|
|
|
const int nSelectionCount = pNewSelection->Count();
|
|
int nIndex;
|
|
float fWeight;
|
|
for ( int j = 0; j < nSelectionCount; ++j )
|
|
{
|
|
pNewSelection->GetComponent( j, nIndex, fWeight );
|
|
const Vector &s = posData.Get( nIndex );
|
|
Vector &d = pPosArray[ nIndex ];
|
|
d = s + vT * fWeight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( int j = 0; j < nPosDataCount; ++j )
|
|
{
|
|
const Vector &s = posData.Get( j );
|
|
Vector &d = pPosArray[ j ];
|
|
d = s + vT;
|
|
}
|
|
}
|
|
|
|
posData.SetMultiple( 0, nPosDataCount, pPosArray );
|
|
|
|
if ( pTmpSelection )
|
|
{
|
|
g_pDataModel->DestroyElement( pTmpSelection->GetHandle() );
|
|
}
|
|
|
|
if ( pNewSelection != pDmeSelection )
|
|
{
|
|
g_pDataModel->DestroyElement( pNewSelection->GetHandle() );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Translate(
|
|
CDmeMesh *pDmeMesh,
|
|
float flTx,
|
|
float flTy,
|
|
float flTz,
|
|
float flFeatherDistance /* = 0.0f */,
|
|
const char *pszFalloffType /* = "STRAIGHT" */,
|
|
const char *pszDistanceType /* = "DEFAULT" */,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
const int nFalloffType = StringToFalloffType( pszFalloffType );
|
|
if ( nFalloffType < 0 || nFalloffType > CDmeMesh::DOME )
|
|
return false;
|
|
|
|
const int nDistanceType = StringToDistanceType( pszDistanceType );
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
return false;
|
|
|
|
return Translate( pDmeMesh, Vector( flTx, flTy, flTz ), flFeatherDistance, static_cast< CDmeMesh::Falloff_t >( nFalloffType ), static_cast< CDmeMesh::Distance_t >( nDistanceType ), pDmePassedSelection );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Rotate(
|
|
CDmeMesh *pDmeMesh,
|
|
Vector vR,
|
|
Vector vO,
|
|
float flFeatherDistance,
|
|
CDmeMesh::Falloff_t nFalloffType,
|
|
CDmeMesh::Distance_t nDistanceType,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
nDistanceType = nDistanceType == CDmeMesh::DIST_DEFAULT ? g_pDmxEditImpl->GetDistanceType() : nDistanceType;
|
|
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Unknown distance type: %d", nDistanceType );
|
|
|
|
CDmeVertexData *pDmeEditState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditState, __func__ ) )
|
|
return false;
|
|
|
|
CDmeSingleIndexedComponent *pDmeSelection = FindOrCreateMeshSelection( pDmeMesh, pDmePassedSelection );
|
|
if ( !pDmeSelection )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't find or create selection for mesh %s\n", pDmeMesh->GetName() );
|
|
|
|
CDmeSingleIndexedComponent *pTmpSelection = NULL;
|
|
|
|
if ( !pDmeSelection || pDmeSelection->Count() == 0 )
|
|
{
|
|
pTmpSelection = CreateElement< CDmeSingleIndexedComponent >( "__selectAll", DMFILEID_INVALID );
|
|
pDmeSelection = pTmpSelection;
|
|
pDmeMesh->SelectAllVertices( pDmeSelection );
|
|
}
|
|
|
|
CDmeSingleIndexedComponent *pNewSelection = flFeatherDistance > 0.0f ? pDmeMesh->FeatherSelection( flFeatherDistance, nFalloffType, nDistanceType, pDmeSelection, NULL ) : pDmeSelection;
|
|
int nArraySize = 0;
|
|
Vector *pPosArray = NULL;
|
|
|
|
const int nPosIndex = pDmeEditState->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
|
|
if ( nPosIndex < 0 )
|
|
return false;
|
|
|
|
CDmrArray< Vector > posData = pDmeEditState->GetVertexData( nPosIndex );
|
|
const int nPosDataCount = posData.Count();
|
|
if ( nPosDataCount <= 0 )
|
|
return false;
|
|
|
|
if ( nArraySize < nPosDataCount || pPosArray == NULL )
|
|
{
|
|
pPosArray = reinterpret_cast< Vector * >( alloca( nPosDataCount * sizeof( Vector ) ) );
|
|
if ( pPosArray )
|
|
{
|
|
nArraySize = nPosDataCount;
|
|
}
|
|
}
|
|
|
|
if ( nArraySize < nPosDataCount )
|
|
return false;
|
|
|
|
Vector vCenter;
|
|
float fRadius;
|
|
|
|
pDmeMesh->GetBoundingSphere( vCenter, fRadius, pDmeEditState, pDmeSelection );
|
|
|
|
if ( nDistanceType == CDmeMesh::DIST_RELATIVE )
|
|
{
|
|
vR *= fRadius;
|
|
}
|
|
|
|
VectorAdd( vCenter, vO, vCenter );
|
|
|
|
matrix3x4_t rpMat;
|
|
SetIdentityMatrix( rpMat );
|
|
PositionMatrix( vCenter, rpMat );
|
|
|
|
matrix3x4_t rpiMat;
|
|
SetIdentityMatrix( rpiMat );
|
|
PositionMatrix( -vCenter, rpiMat );
|
|
|
|
matrix3x4_t rMat;
|
|
SetIdentityMatrix( rMat );
|
|
|
|
if ( pNewSelection )
|
|
{
|
|
memcpy( pPosArray, posData.Base(), nPosDataCount * sizeof( Vector ) );
|
|
|
|
const int nSelectionCount = pNewSelection->Count();
|
|
int nIndex;
|
|
float fWeight;
|
|
for ( int j = 0; j < nSelectionCount; ++j )
|
|
{
|
|
pNewSelection->GetComponent( j, nIndex, fWeight );
|
|
const Vector &s = posData.Get( nIndex );
|
|
Vector &d = pPosArray[ nIndex ];
|
|
AngleMatrix( RadianEuler( DEG2RAD( vR.x * fWeight ), DEG2RAD( vR.y * fWeight ), DEG2RAD( vR.z * fWeight ) ), rMat );
|
|
|
|
ConcatTransforms( rpMat, rMat, rMat );
|
|
ConcatTransforms( rMat, rpiMat, rMat );
|
|
|
|
VectorTransform( s, rMat, d );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AngleMatrix( RadianEuler( DEG2RAD( vR.x ), DEG2RAD( vR.y ), DEG2RAD( vR.z ) ), rMat );
|
|
|
|
ConcatTransforms( rpMat, rMat, rMat );
|
|
ConcatTransforms( rMat, rpiMat, rMat );
|
|
|
|
for ( int j = 0; j < nPosDataCount; ++j )
|
|
{
|
|
const Vector &s = posData.Get( j );
|
|
Vector &d = pPosArray[ j ];
|
|
VectorTransform( s, rMat, d );
|
|
}
|
|
}
|
|
|
|
posData.SetMultiple( 0, nPosDataCount, pPosArray );
|
|
|
|
if ( pTmpSelection )
|
|
{
|
|
g_pDataModel->DestroyElement( pTmpSelection->GetHandle() );
|
|
}
|
|
|
|
if ( pNewSelection != pDmeSelection )
|
|
{
|
|
g_pDataModel->DestroyElement( pNewSelection->GetHandle() );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Rotate(
|
|
CDmeMesh *pDmeMesh,
|
|
float flRx,
|
|
float flRy,
|
|
float flRz,
|
|
float flOx /* = 0.0f */,
|
|
float flOy /* = 0.0f */,
|
|
float flOz /* = 0.0f */,
|
|
float flFeatherDistance /* = 0.0f */,
|
|
const char *pszFalloffType /* = "STRAIGHT" */,
|
|
const char *pszDistanceType /* = "DEFAULT" */,
|
|
CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
const int nFalloffType = StringToFalloffType( pszFalloffType );
|
|
if ( nFalloffType < 0 || nFalloffType > CDmeMesh::DOME )
|
|
return false;
|
|
|
|
const int nDistanceType = StringToDistanceType( pszDistanceType );
|
|
if ( nDistanceType < 0 || nDistanceType > CDmeMesh::DIST_DEFAULT )
|
|
return false;
|
|
|
|
return Rotate( pDmeMesh, Vector( flRx, flRy, flRz ), Vector( flOx, flOy, flOz ), flFeatherDistance, static_cast< CDmeMesh::Falloff_t >( nFalloffType ), static_cast< CDmeMesh::Distance_t >( nDistanceType ), pDmePassedSelection );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool RemapMaterial( CDmeMesh *pDmeMesh, int nMaterialIndex, const char *pszNewMaterialName )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
return CDmMeshUtils::RemapMaterial( pDmeMesh, nMaterialIndex, pszNewMaterialName );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Adds the src mesh into the dst mesh
|
|
//-----------------------------------------------------------------------------
|
|
bool Combine( CDmeMesh *pDmeMesh, CDmeMesh *pDmeSrcMesh, const char *pszDstSkinningBoneName /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeSrcMesh );
|
|
|
|
int nSkinningJointIndex = -1;
|
|
|
|
if ( pszDstSkinningBoneName )
|
|
{
|
|
// Find the DmeModel of the destination mesh scene
|
|
CDmeDag *pDmeDag = FindReferringElement< CDmeDag >( pDmeMesh, "shape" );
|
|
while ( pDmeDag && !pDmeDag->IsA( CDmeModel::GetStaticTypeSymbol() ) )
|
|
{
|
|
pDmeDag = pDmeDag->GetParent();
|
|
}
|
|
|
|
CDmeModel *pDmeModel = CastElement< CDmeModel >( pDmeDag );
|
|
if ( pDmeModel )
|
|
{
|
|
CDmeDag *pDmeJoint = pDmeModel->GetJoint( pszDstSkinningBoneName );
|
|
if ( pDmeJoint )
|
|
{
|
|
nSkinningJointIndex = pDmeModel->GetJointIndex( pDmeJoint );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DMXEDIT_WARNING( "Couldn't find the DmeModel associated with mesh \"%s\"", pDmeMesh->GetName() );
|
|
}
|
|
|
|
if ( nSkinningJointIndex < 0 )
|
|
{
|
|
DMXEDIT_WARNING( "Couldn't find bone named \"%s\", for skinning", pszDstSkinningBoneName );
|
|
}
|
|
}
|
|
|
|
const bool bRetVal = CDmMeshUtils::Merge( pDmeSrcMesh, pDmeMesh, nSkinningJointIndex );
|
|
|
|
FixupBaseStates( pDmeMesh );
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Merge( CDmElement *pDmDstRoot, CDmeMesh *pSrcMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pSrcMesh );
|
|
|
|
if ( !pDmDstRoot )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No element passed" );
|
|
|
|
CDmMeshComp srcComp( pSrcMesh );
|
|
|
|
CUtlVector< CUtlVector< CDmMeshComp::CEdge * > > srcBorderEdgesList;
|
|
if ( srcComp.GetBorderEdges( srcBorderEdgesList ) == 0 )
|
|
return false;
|
|
|
|
CDmeMesh *pDmeSocketMesh = CastElement< CDmeMesh >( pDmDstRoot );
|
|
if ( pDmeSocketMesh )
|
|
{
|
|
const int nEdgeListIndex = CDmMeshUtils::FindMergeSocket( srcBorderEdgesList, pDmeSocketMesh );
|
|
if ( nEdgeListIndex < 0 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No merge socket found on specified mesh: \"%s\"\n", pDmeSocketMesh->GetName() );
|
|
|
|
CleanupMeshEditBaseState( pSrcMesh );
|
|
CleanupMeshSelection( pSrcMesh );
|
|
|
|
bool bRetVal = CDmMeshUtils::Merge( srcComp, srcBorderEdgesList[ nEdgeListIndex ], pDmeSocketMesh );
|
|
|
|
FixupBaseStates( pDmeSocketMesh );
|
|
|
|
pDmeSocketMesh->Resolve();
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
CDmeDag *pDmeDag = CastElement< CDmeDag >( pDmDstRoot );
|
|
if ( !pDmeDag )
|
|
{
|
|
pDmeDag = pDmDstRoot->GetValueElement< CDmeDag >( "model" );
|
|
}
|
|
|
|
if ( !pDmeDag )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Invalid DmElement passed, DmeMesh, DmeDag or DmElement with \"model\" attribute required" );
|
|
|
|
Vector vSrcCenter;
|
|
float flSrcRadius;
|
|
|
|
pSrcMesh->GetBoundingSphere( vSrcCenter, flSrcRadius );
|
|
|
|
Vector vDstCenter;
|
|
float flDstRadius;
|
|
|
|
float flSqDist = FLT_MAX;
|
|
|
|
int nEdgeListIndex = -1;
|
|
|
|
CUtlStack< CDmeDag * > traverseStack;
|
|
traverseStack.Push( pDmeDag );
|
|
|
|
while ( traverseStack.Count() )
|
|
{
|
|
traverseStack.Pop( pDmeDag );
|
|
if ( !pDmeDag )
|
|
continue;
|
|
|
|
for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
|
|
{
|
|
traverseStack.Push( pDmeDag->GetChild( i ) );
|
|
}
|
|
|
|
CDmeMesh *pDmeTmpMesh = CastElement< CDmeMesh >( pDmeDag->GetShape() );
|
|
if ( !pDmeTmpMesh )
|
|
continue;
|
|
|
|
const int nTmpEdgeListIndex = CDmMeshUtils::FindMergeSocket( srcBorderEdgesList, pDmeTmpMesh );
|
|
if ( nTmpEdgeListIndex < 0 )
|
|
continue;
|
|
|
|
pDmeTmpMesh->GetBoundingSphere( vDstCenter, flDstRadius );
|
|
flDstRadius = vDstCenter.DistToSqr( vSrcCenter );
|
|
|
|
if ( flDstRadius < flSqDist )
|
|
{
|
|
flSqDist = flDstRadius;
|
|
pDmeSocketMesh = pDmeTmpMesh;
|
|
nEdgeListIndex = nTmpEdgeListIndex;
|
|
}
|
|
}
|
|
|
|
if ( pDmeSocketMesh && nEdgeListIndex >= 0 )
|
|
{
|
|
CleanupMeshEditBaseState( pSrcMesh );
|
|
CleanupMeshSelection( pSrcMesh );
|
|
|
|
bool bRetVal = CDmMeshUtils::Merge( srcComp, srcBorderEdgesList[ nEdgeListIndex ], pDmeSocketMesh );
|
|
|
|
FixupBaseStates( pDmeSocketMesh );
|
|
|
|
pDmeSocketMesh->Resolve();
|
|
|
|
return bRetVal;
|
|
}
|
|
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No merge socket found in specified scene, i.e. a Set of border edges on the source model that are found on the merge model" );
|
|
}
|
|
|
|
|
|
bool ApplyMaskToDelta( CDmeVertexDeltaData *pTheDelta, CDmeSingleIndexedComponent *pDmePassedSelection )
|
|
{
|
|
CDmrArray< int > positionIndices = pTheDelta->GetAttribute( "positionsIndices" );
|
|
CDmrArray< Vector > positions = pTheDelta->GetAttribute( "positions" );
|
|
CDmrArray< float > weightData = pDmePassedSelection->GetAttribute( "weights" );
|
|
int numVerts = positionIndices.Count();
|
|
|
|
for ( int n=0; n < numVerts; n++ )
|
|
{
|
|
int idx = positionIndices[ n ];
|
|
float fWeight = weightData[ idx ];
|
|
const Vector vNewPos = positions[ n ] * fWeight;
|
|
|
|
positions.Set( n, vNewPos );
|
|
}
|
|
|
|
//do we have normals to deal with?
|
|
CDmAttribute *pNormalsIndicesAttr = pTheDelta->GetAttribute( "normalsIndices" );
|
|
if ( pNormalsIndicesAttr )
|
|
{
|
|
CDmrArray< int > normalsIndices = pNormalsIndicesAttr;
|
|
CDmrArray< Vector > normals = pTheDelta->GetAttribute( "normals" );
|
|
|
|
for ( int n=0; n < numVerts; n++ )
|
|
{
|
|
int idx = normalsIndices[ n ];
|
|
float fWeight = weightData[ idx ];
|
|
|
|
Vector vNewNormal = normals[ n ] * fWeight;
|
|
vNewNormal.NormalizeInPlace();
|
|
|
|
normals.Set( n, Vector( vNewNormal ) );
|
|
}
|
|
}
|
|
|
|
return pTheDelta == NULL;
|
|
}
|
|
|
|
|
|
bool CreateDeltaFromMesh( CDmeMesh *pBaseMesh, CDmeMesh *pMeshToUseAsDelta, const char *pDeltaName, CDmeSingleIndexedComponent *pDmePassedSelection /* = NULL */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pBaseMesh );
|
|
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pMeshToUseAsDelta );
|
|
|
|
CDmeVertexDeltaData *pRet = pBaseMesh->ModifyOrCreateDeltaStateFromBaseState( pDeltaName, pMeshToUseAsDelta->GetCurrentBaseState(), true );
|
|
|
|
if ( pDmePassedSelection )
|
|
{
|
|
ApplyMaskToDelta( pRet, pDmePassedSelection );
|
|
}
|
|
|
|
return pRet == NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeMesh *ComputeConvexHull3D( CDmeMesh *pDmeMesh, float flCoplanarEpsilon /* = ONE_32ND_UNIT */ )
|
|
{
|
|
CMesh mesh;
|
|
|
|
if ( ConvertMeshFromDMX( &mesh, pDmeMesh ) )
|
|
{
|
|
CMesh convexHullMesh;
|
|
ConvexHull3D( &convexHullMesh, mesh, flCoplanarEpsilon );
|
|
|
|
CUtlString meshName( pDmeMesh->GetName() );
|
|
meshName += "_convexHull";
|
|
|
|
CDmeMesh *pDmeConvexHullMesh = CreateElement< CDmeMesh >( meshName.Get(), pDmeMesh->GetFileId() );
|
|
if ( pDmeConvexHullMesh )
|
|
{
|
|
ConvertMeshToDMX( pDmeConvexHullMesh, &convexHullMesh, true );
|
|
|
|
return pDmeConvexHullMesh;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeCombinationOperator *FindOrCreateComboOp( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeComboOp = GetComboOpFromMesh( pDmeMesh );
|
|
if ( pDmeComboOp )
|
|
return pDmeComboOp;
|
|
|
|
// Find the DmeModel of the destination mesh scene
|
|
CDmeDag *pDmeDag = FindReferringElement< CDmeDag >( pDmeMesh, "shape" );
|
|
while ( pDmeDag && !pDmeDag->IsA( CDmeModel::GetStaticTypeSymbol() ) )
|
|
{
|
|
pDmeDag = pDmeDag->GetParent();
|
|
}
|
|
|
|
CDmeModel *pDmeModel = CastElement< CDmeModel >( pDmeDag );
|
|
if ( !pDmeModel )
|
|
DMXEDIT_ERROR_RETURN_NULL( "Couldn't Find The DmeModel for the mesh: \"%s\"", pDmeMesh->GetName() );
|
|
|
|
// Find the root node for the DmeModel (by "model" or "skeleton" attribute):
|
|
CDmElement *pDmRoot = FindReferringElement< CDmElement >( pDmeModel, "model" );
|
|
if ( !pDmRoot )
|
|
{
|
|
pDmRoot = FindReferringElement< CDmElement >( pDmeModel, "skeleton" );
|
|
}
|
|
|
|
if ( !pDmRoot )
|
|
DMXEDIT_ERROR_RETURN_NULL( "Couldn't Find The root Dme node referring to the DmeModel \"%s\" for the mesh: \"%s\"", pDmeModel->GetName(), pDmeMesh->GetName() );
|
|
|
|
pDmeComboOp = pDmRoot->GetValueElement< CDmeCombinationOperator >( "combinationOperator" );
|
|
if ( !pDmeComboOp )
|
|
{
|
|
|
|
CUtlString comboOpName = pDmeMesh->GetName();
|
|
comboOpName += "_comboOp";
|
|
|
|
pDmeComboOp = CreateElement< CDmeCombinationOperator >( comboOpName.Get(), pDmeMesh->GetFileId() );
|
|
|
|
if ( !pDmeComboOp )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Couldn't create DmeCombinationOperator for \"%s\"", pDmeMesh->GetName() );
|
|
|
|
pDmRoot->SetValue( "combinationOperator", pDmeComboOp );
|
|
}
|
|
|
|
pDmeComboOp->AddTarget( pDmeMesh );
|
|
return pDmeComboOp;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetMeshFromSkeleton( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexData *pDmeBindBaseState = pDmeMesh->GetBindBaseState();
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeBindBaseState, __func__ ) )
|
|
return false;
|
|
|
|
CDmeVertexData *pDmeEditBaseState = FindOrCreateMeshEditBaseState( pDmeMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeEditBaseState, __func__ ) )
|
|
return false;
|
|
|
|
if ( !pDmeBindBaseState->HasSkinningData() )
|
|
DMXEDIT_ERROR_RETURN_FALSE( "No skinning data on specified mesh: \"%s\"", pDmeMesh->GetName() );
|
|
|
|
const FieldIndex_t nBindPosField = pDmeBindBaseState->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
|
|
if ( nBindPosField < 0 )
|
|
DMXEDIT_ERROR_RETURN_FALSE( "Cannot find position data on mesh \"%s\" base state", pDmeMesh->GetName() );
|
|
|
|
const FieldIndex_t nEditPosField = pDmeEditBaseState->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
|
|
if ( nEditPosField < 0 )
|
|
DMXEDIT_ERROR_RETURN_FALSE( "Cannot find position data on mesh \"%s\" edit state", pDmeMesh->GetName() );
|
|
|
|
CDmeDag *pDmeDag = pDmeMesh->GetParent();
|
|
while ( pDmeDag && !pDmeDag->IsA( CDmeModel::GetStaticTypeSymbol() ) )
|
|
{
|
|
pDmeDag = pDmeDag->GetParent();
|
|
}
|
|
CDmeModel *pDmeModel = CastElement< CDmeModel >( pDmeDag );
|
|
if ( !pDmeModel )
|
|
DMXEDIT_ERROR_RETURN_NULL( "Couldn't Find The DmeModel for the mesh: \"%s\"", pDmeMesh->GetName() );
|
|
|
|
const CUtlVector< Vector > &posData = CDmrArrayConst< Vector >( pDmeBindBaseState->GetVertexData( nBindPosField ) ).Get();
|
|
const int nVertexCount = posData.Count();
|
|
|
|
const CUtlVector< Vector > &editPosData = CDmrArrayConst< Vector >( pDmeEditBaseState->GetVertexData( nEditPosField ) ).Get();
|
|
const int nEditVertexCount = editPosData.Count();
|
|
|
|
Assert( nVertexCount == nEditVertexCount );
|
|
if ( nVertexCount != nEditVertexCount )
|
|
DMXEDIT_ERROR_RETURN_FALSE( "Base Pos Count: %d Versus Edit Pos Count: %d\n", nVertexCount, nEditVertexCount );
|
|
|
|
Vector *pVertices = reinterpret_cast< Vector * >( alloca( nVertexCount * sizeof( Vector ) ) );
|
|
|
|
matrix3x4_t shapeToWorld;
|
|
SetIdentityMatrix( shapeToWorld );
|
|
|
|
CDmeModel::s_ModelStack.Push( pDmeModel );
|
|
matrix3x4_t *pPoseToWorld = CDmeModel::SetupModelRenderState( shapeToWorld, pDmeBindBaseState->HasSkinningData(), true );
|
|
CDmeModel::s_ModelStack.Pop();
|
|
|
|
CDmeMeshRenderInfo renderInfo( pDmeBindBaseState );
|
|
Assert( renderInfo.HasPositionData() );
|
|
|
|
for ( int i = 0; i < nVertexCount; ++i )
|
|
{
|
|
renderInfo.ComputePosition( i, pPoseToWorld, static_cast< Vector * >( NULL ), pVertices );
|
|
}
|
|
|
|
pDmeEditBaseState->SetVertexData( nEditPosField, 0, nVertexCount, AT_VECTOR3, pVertices );
|
|
|
|
CDmeModel::CleanupModelRenderState();
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets wrinkle deltas from the specified selection
|
|
//-----------------------------------------------------------------------------
|
|
void SetWrinkleWeight( CDmeVertexData *pDmeBindBaseState, CDmeVertexDeltaData *pDmeDelta, CDmeSingleIndexedComponent *pDmeSelection, float flScale )
|
|
{
|
|
FieldIndex_t nPosIndex = pDmeDelta->FindFieldIndex( CDmeVertexDeltaData::FIELD_POSITION );
|
|
if ( nPosIndex < 0 )
|
|
return;
|
|
|
|
FieldIndex_t nBaseTexCoordIndex = pDmeBindBaseState->FindFieldIndex( CDmeVertexData::FIELD_TEXCOORD );
|
|
if ( nBaseTexCoordIndex < 0 )
|
|
return;
|
|
|
|
FieldIndex_t nWrinkleIndex = pDmeDelta->FindFieldIndex( CDmeVertexDeltaData::FIELD_WRINKLE );
|
|
if ( nWrinkleIndex < 0 )
|
|
{
|
|
nWrinkleIndex = pDmeDelta->CreateField( CDmeVertexDeltaData::FIELD_WRINKLE );
|
|
}
|
|
|
|
const CUtlVector<int> &baseTexCoordIndices = pDmeBindBaseState->GetVertexIndexData( nBaseTexCoordIndex );
|
|
|
|
CDmrArrayConst<Vector2D> texData( pDmeBindBaseState->GetVertexData( nBaseTexCoordIndex ) );
|
|
const int nBaseTexCoordCount = texData.Count();
|
|
const int nBufSize = ( ( nBaseTexCoordCount + 7 ) >> 3 );
|
|
unsigned char *pUsedBits = (unsigned char*)_alloca( nBufSize );
|
|
memset( pUsedBits, 0, nBufSize );
|
|
|
|
// Construct a temporary array of wrinkle values for the entire mesh
|
|
// Same size as the number of texture coordinate values
|
|
// 0 means no wrinkle data, so initialize to 0
|
|
CUtlVector< float > wrinkleData;
|
|
wrinkleData.SetCount( nBaseTexCoordCount );
|
|
memset( wrinkleData.Base(), 0, wrinkleData.Count() * sizeof( float ) );
|
|
|
|
// Copy the old wrinkle data if any exists
|
|
CDmAttribute *pWrinkleDeltaAttr = pDmeDelta->GetVertexData( nWrinkleIndex );
|
|
if ( pWrinkleDeltaAttr )
|
|
{
|
|
CDmrArrayConst< float > wrinkleDeltaArray( pWrinkleDeltaAttr );
|
|
if ( wrinkleDeltaArray.Count() )
|
|
{
|
|
const CUtlVector< int > &wrinkleDeltaIndices = pDmeDelta->GetVertexIndexData( nWrinkleIndex );
|
|
Assert( wrinkleDeltaIndices.Count() == wrinkleDeltaArray.Count() );
|
|
|
|
int nWrinkleIndex;
|
|
for ( int i = 0; i < wrinkleDeltaIndices.Count(); ++i )
|
|
{
|
|
nWrinkleIndex = wrinkleDeltaIndices[ i ];
|
|
wrinkleData[ nWrinkleIndex ] = wrinkleDeltaArray[ i ];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write new wrinkle values overtop of existing wrinkle values
|
|
int nComponentIndex;
|
|
float flWeight;
|
|
|
|
for ( int i = 0; i < pDmeSelection->Count(); ++i )
|
|
{
|
|
if ( !pDmeSelection->GetComponent( i, nComponentIndex, flWeight ) )
|
|
continue;
|
|
|
|
flWeight *= 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 = pDmeBindBaseState->FindVertexIndicesFromDataIndex( CDmeVertexData::FIELD_POSITION, nComponentIndex );
|
|
for ( int j = 0; j < baseVerts.Count(); ++j )
|
|
{
|
|
// See if we have a delta for this texcoord...
|
|
const int nTexCoordIndex = baseTexCoordIndices[ baseVerts[j] ];
|
|
if ( pUsedBits[ nTexCoordIndex >> 3 ] & ( 1 << ( nTexCoordIndex & 0x7 ) ) )
|
|
continue;
|
|
|
|
pUsedBits[ nTexCoordIndex >> 3 ] |= 1 << ( nTexCoordIndex & 0x7 );
|
|
|
|
wrinkleData[ nTexCoordIndex ] = flWeight;
|
|
}
|
|
}
|
|
|
|
// Remove previous wrinkle data (if any exists)
|
|
pDmeDelta->RemoveAllVertexData( nWrinkleIndex );
|
|
|
|
// Write new non-zero wrinkles
|
|
for ( int i = 0; i < wrinkleData.Count(); ++i )
|
|
{
|
|
if ( fabs( wrinkleData[ i ] ) < 0.00001 )
|
|
continue;
|
|
|
|
int nDeltaIndex = pDmeDelta->AddVertexData( nWrinkleIndex, 1 );
|
|
pDmeDelta->SetVertexIndices( nWrinkleIndex, nDeltaIndex, 1, &i );
|
|
pDmeDelta->SetVertexData( nWrinkleIndex, nDeltaIndex, 1, AT_FLOAT, &wrinkleData[ i ] );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetWrinkleWeight( CDmeMesh *pDmeMesh, const char *pszDeltaName, CDmeSingleIndexedComponent *pDmeSelection, float flScale /* = 1.0 */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeVertexData *pDmeBindBaseState = pDmeMesh->GetBindBaseState();
|
|
if ( !BaseStateSanityCheck( pDmeMesh, pDmeBindBaseState, __func__ ) )
|
|
return false;
|
|
|
|
CDmeVertexDeltaData *pDmeDelta = pDmeMesh->FindDeltaState( pszDeltaName );
|
|
if ( !pDmeDelta )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Mesh \"%s\" has no delta named \"%s\"", pDmeMesh->GetName(), pszDeltaName );
|
|
|
|
if ( !pDmeSelection )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No selection specified\n" );
|
|
|
|
SetWrinkleWeight( pDmeBindBaseState, pDmeDelta, pDmeSelection, flScale );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
float WrapDeform_ComputeOptimalDistSqr( const CUtlVector< Vector > &iPos, const CUtlVector< Vector > &tPos )
|
|
{
|
|
float flRetDistSqr = 0.0f;
|
|
float flMaxDistSqr;
|
|
float flTmpDistSqr;
|
|
|
|
for ( int i = 0; i < tPos.Count(); ++i )
|
|
{
|
|
flMaxDistSqr = 0.0f;
|
|
const Vector &vt = tPos[i];
|
|
|
|
for ( int j = 0; j < iPos.Count(); ++j )
|
|
{
|
|
flTmpDistSqr = vt.DistToSqr( iPos[j] );
|
|
if ( flTmpDistSqr > flMaxDistSqr )
|
|
{
|
|
flMaxDistSqr = flTmpDistSqr;
|
|
}
|
|
}
|
|
|
|
if ( flMaxDistSqr > flRetDistSqr )
|
|
{
|
|
flRetDistSqr = flMaxDistSqr;
|
|
}
|
|
}
|
|
|
|
return flRetDistSqr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
class CWrapVert
|
|
{
|
|
public:
|
|
CWrapVert()
|
|
: m_flTotalDist( 0.0f )
|
|
{}
|
|
|
|
void AddInfluenceVert( int nInfluenceVert, float flDistSqr )
|
|
{
|
|
const float flDist = sqrtf( flDistSqr );
|
|
|
|
if ( Count() < s_nMaxInfluence )
|
|
{
|
|
WrapVert_s &wrapVert = m_wrapVerts[ m_wrapVerts.AddToTail() ];
|
|
wrapVert.m_nInfluenceIndex = nInfluenceVert;
|
|
wrapVert.m_flDist = sqrtf( flDistSqr );
|
|
m_flTotalDist += wrapVert.m_flDist;
|
|
}
|
|
else
|
|
{
|
|
WrapVert_s &wrapVert = m_wrapVerts.Tail();
|
|
if ( flDist >= wrapVert.m_flDist )
|
|
return;
|
|
|
|
wrapVert.m_nInfluenceIndex = nInfluenceVert;
|
|
wrapVert.m_flDist = sqrtf( flDistSqr );
|
|
m_flTotalDist = 0.0f;
|
|
for ( int i = 0; i < m_wrapVerts.Count(); ++i )
|
|
{
|
|
m_flTotalDist += m_wrapVerts[i].m_flDist;
|
|
}
|
|
}
|
|
|
|
qsort( m_wrapVerts.Base(), m_wrapVerts.Count(), sizeof( WrapVert_s ), SortWrapVertFunc );
|
|
}
|
|
|
|
int Count() const
|
|
{
|
|
return m_wrapVerts.Count();
|
|
}
|
|
|
|
bool GetInfluenceIndexAndWeight( int &nInfluenceIndex, float &flWeight, int nLocalIndex ) const
|
|
{
|
|
const int nCount = Count();
|
|
|
|
if ( nCount <= 0 || nLocalIndex >= nCount || nLocalIndex < 0 || m_flTotalDist <= 0.0f )
|
|
return false;
|
|
|
|
const WrapVert_s &wrapVert = m_wrapVerts[nLocalIndex];
|
|
nInfluenceIndex = wrapVert.m_nInfluenceIndex;
|
|
if ( nCount == 1 )
|
|
{
|
|
flWeight = 1.0f;
|
|
}
|
|
else
|
|
{
|
|
float flTotal = 0.0f;
|
|
for ( int i = 0; i < m_wrapVerts.Count(); ++i )
|
|
{
|
|
flTotal += ( m_flTotalDist - m_wrapVerts[i].m_flDist );
|
|
}
|
|
flWeight = ( m_flTotalDist - wrapVert.m_flDist ) / flTotal;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
|
|
static int SortWrapVertFunc( const void *pLhs, const void *pRhs )
|
|
{
|
|
const WrapVert_s &lWrapVert = *reinterpret_cast< const WrapVert_s * >( pLhs );
|
|
const WrapVert_s &rWrapVert = *reinterpret_cast< const WrapVert_s * >( pRhs );
|
|
|
|
if ( lWrapVert.m_flDist < rWrapVert.m_flDist )
|
|
return -1;
|
|
|
|
if ( lWrapVert.m_flDist > rWrapVert.m_flDist )
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct WrapVert_s
|
|
{
|
|
int m_nInfluenceIndex;
|
|
float m_flDist;
|
|
};
|
|
|
|
CUtlVector< WrapVert_s > m_wrapVerts;
|
|
float m_flTotalDist;
|
|
|
|
static int s_nMaxInfluence;
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
int CWrapVert::s_nMaxInfluence = 10;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void WrapDeform_ComputeInfluence( CUtlVector< CWrapVert > &influenceIndices, const CUtlVector< Vector > &iPos, const CUtlVector< Vector > &tPos, float flMaxDistSqr )
|
|
{
|
|
influenceIndices.SetCount( tPos.Count() );
|
|
|
|
float flTmpDistSqr;
|
|
|
|
for ( int i = 0; i < tPos.Count(); ++i )
|
|
{
|
|
const Vector &vt = tPos[i];
|
|
CWrapVert &wrapVert = influenceIndices[i];
|
|
|
|
for ( int j = 0; j < iPos.Count(); ++j )
|
|
{
|
|
flTmpDistSqr = vt.DistToSqr( iPos[j] );
|
|
if ( flTmpDistSqr <= flMaxDistSqr )
|
|
{
|
|
wrapVert.AddInfluenceVert( j, flTmpDistSqr );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void WrapDeform_ComputeWrap(
|
|
CUtlVector< Vector > &tPosCurr,
|
|
const CUtlVector< CWrapVert > &influenceIndices,
|
|
const CUtlVector< Vector > &iPosBind,
|
|
const CUtlVector< Vector > &iPosCurr,
|
|
const CUtlVector< Vector > &tPosBind )
|
|
{
|
|
Assert( tPosBind.Count() == influenceIndices.Count() );
|
|
|
|
tPosCurr = tPosBind; // Copy as a default and to set size
|
|
|
|
int nInfluenceIndex;
|
|
float flInfluenceWeight;
|
|
Vector vTemp0;
|
|
Vector vTemp1;
|
|
|
|
for ( int i = 0; i < tPosCurr.Count(); ++i )
|
|
{
|
|
Vector &vt = tPosCurr[i];
|
|
|
|
const CWrapVert &wrapVert = influenceIndices[i];
|
|
for ( int j = 0; j < wrapVert.Count(); ++j )
|
|
{
|
|
wrapVert.GetInfluenceIndexAndWeight( nInfluenceIndex, flInfluenceWeight, j );
|
|
VectorSubtract( iPosCurr[nInfluenceIndex], iPosBind[nInfluenceIndex], vTemp0 );
|
|
VectorScale( vTemp0, flInfluenceWeight, vTemp1 );
|
|
VectorAdd( vTemp1, vt, vt );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool WrapDeform( CDmeMesh *pDmeInfluenceMesh, CDmeMesh *pDmeTargetMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeInfluenceMesh );
|
|
CDmeVertexData *pIBind = pDmeInfluenceMesh->GetBindBaseState();
|
|
if ( !BaseStateSanityCheck( pDmeInfluenceMesh, pIBind, __func__ ) )
|
|
return false;
|
|
pIBind->Resolve();
|
|
|
|
CDmeVertexData *pICurrent = pDmeInfluenceMesh->GetCurrentBaseState();
|
|
if ( !BaseStateSanityCheck( pDmeInfluenceMesh, pICurrent, __func__ ) )
|
|
return false;
|
|
pICurrent->Resolve();
|
|
|
|
if ( pIBind == pICurrent )
|
|
DMXEDIT_ERROR_RETURN_FALSE( "Bind base state must be different from current base state on influence mesh in order for Wrap to do anything" );
|
|
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeTargetMesh );
|
|
CDmeVertexData *pTBind = pDmeTargetMesh->GetBindBaseState();
|
|
if ( !BaseStateSanityCheck( pDmeTargetMesh, pTBind, __func__ ) )
|
|
return false;
|
|
pTBind->Resolve();
|
|
|
|
CDmeVertexData *pTEdit = FindOrCreateMeshEditBaseState( pDmeTargetMesh, __func__ );
|
|
if ( !BaseStateSanityCheck( pDmeTargetMesh, pTEdit, __func__ ) )
|
|
return false;
|
|
|
|
const CUtlVector< Vector > &iPosBind = pIBind->GetPositionData();
|
|
const CUtlVector< Vector > &tPosBind = pTBind->GetPositionData();
|
|
|
|
const float flMinDistSqr = WrapDeform_ComputeOptimalDistSqr( iPosBind, tPosBind ) + 4.0 + FLT_EPSILON;
|
|
|
|
CUtlVector< CWrapVert > influenceIndices;
|
|
WrapDeform_ComputeInfluence( influenceIndices, iPosBind, tPosBind, flMinDistSqr );
|
|
|
|
const CUtlVector< Vector > &iPosCurr = pICurrent->GetPositionData();
|
|
CUtlVector< Vector > tPosCurr;
|
|
|
|
WrapDeform_ComputeWrap( tPosCurr, influenceIndices, iPosBind, iPosCurr, tPosBind );
|
|
|
|
const int nEditPosField = pTEdit->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
|
|
pTEdit->RemoveAllVertexData( nEditPosField );
|
|
pTEdit->AddVertexData( nEditPosField, tPosCurr.Count() );
|
|
pTEdit->SetVertexData( nEditPosField, 0, tPosCurr.Count(), AT_VECTOR3, tPosCurr.Base() );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
Vector ComputeMean( const CUtlVector< Vector > &pointList )
|
|
{
|
|
Vector vMean;
|
|
vMean.Zero();
|
|
|
|
const int nPointCount = pointList.Count();
|
|
for ( int i = 0; i < nPointCount; ++i )
|
|
{
|
|
VectorAdd( vMean, pointList[i], vMean );
|
|
}
|
|
|
|
VectorDivide( vMean, static_cast< float >( nPointCount ), vMean );
|
|
|
|
return vMean;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
matrix3x4_t ComputeCovariantMatrix( const CUtlVector< Vector > &pointList, const Vector &vMean )
|
|
{
|
|
matrix3x4_t mCovariant;
|
|
SetIdentityMatrix( mCovariant );
|
|
|
|
const int nPointCount = pointList.Count();
|
|
|
|
if ( nPointCount <= 0 )
|
|
return mCovariant;
|
|
|
|
CUtlVector< Vector > skewedPointList;
|
|
skewedPointList = pointList;
|
|
|
|
Assert( nPointCount == skewedPointList.Count() );
|
|
|
|
for ( int i = 0; i < nPointCount; ++i )
|
|
{
|
|
VectorSubtract( skewedPointList[i], vMean, skewedPointList[i] );
|
|
}
|
|
|
|
float flCovariance;
|
|
|
|
for ( int i = 0; i < 3; ++i )
|
|
{
|
|
for ( int j = 0; j < 3; ++j )
|
|
{
|
|
flCovariance = 0.0f;
|
|
|
|
for ( int k = 0; k < nPointCount; ++k )
|
|
{
|
|
flCovariance += skewedPointList[k][i] * skewedPointList[k][j];
|
|
}
|
|
|
|
mCovariant[i][j] = flCovariance / static_cast< float >( nPointCount );
|
|
}
|
|
}
|
|
|
|
return mCovariant;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
matrix3x4_t ComputeCovariantMatrix( const CUtlVector< Vector > &pointList )
|
|
{
|
|
const Vector vMean = ComputeMean( pointList );
|
|
|
|
return ComputeCovariantMatrix( pointList, vMean );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool MergeMeshAndSkeleton( CDmeMesh *pDstMesh, CDmeMesh *pSrcMesh )
|
|
{
|
|
return CDmMeshUtils::MergeMeshAndSkeleton( pDstMesh, pSrcMesh );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool FlexLocalVar( CDmeMesh *pDmeMesh, const char *pszFlexRuleLocalVar )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeFlexRules *pDmeFlexRules = FindOrAddDmeFlexRules( pDmeMesh );
|
|
if ( !pDmeFlexRules )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "DmeFlexRules could not be created for mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
CDmeFlexRuleLocalVar *pDmeFlexRuleLocalVar = CreateElement< CDmeFlexRuleLocalVar >( pszFlexRuleLocalVar, pDmeMesh->GetFileId() );
|
|
pDmeFlexRules->AddFlexRule( pDmeFlexRuleLocalVar );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool FlexRuleExpression( CDmeMesh *pDmeMesh, const char *pszExpression )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeFlexRules *pDmeFlexRules = FindOrAddDmeFlexRules( pDmeMesh );
|
|
if ( !pDmeFlexRules )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "DmeFlexRules could not be created for mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
char szBuf[ 512 ];
|
|
if ( sscanf( pszExpression, "%512s =", szBuf ) != 1 )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Improperly formatted expression: \"%s\"", pszExpression );
|
|
|
|
CDmeFlexRuleExpression *pDmeFlexRuleExpression = CreateElement< CDmeFlexRuleExpression >( szBuf, pDmeMesh->GetFileId() );
|
|
pDmeFlexRuleExpression->SetExpression( pszExpression );
|
|
pDmeFlexRules->AddFlexRule( pDmeFlexRuleExpression );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the CDmeFlexRules ultimately controlling the specified
|
|
// DmeMesh by searched backwards on elements referring to "targets" or "target"
|
|
// Returns NULL if not found
|
|
//-----------------------------------------------------------------------------
|
|
static CDmeFlexRules *GetFlexRulesForMesh( CDmeMesh *pDmeMesh )
|
|
{
|
|
if ( !pDmeMesh )
|
|
return NULL;
|
|
|
|
CUtlRBTree< CDmElement * > visited( CDefOps< CDmElement * >::LessFunc );
|
|
visited.Insert( pDmeMesh );
|
|
|
|
const CUtlSymbolLarge sTargets = g_pDataModel->GetSymbol( "targets" );
|
|
const CUtlSymbolLarge sTarget = g_pDataModel->GetSymbol( "target" );
|
|
|
|
CDmElement *pDmThisElement = pDmeMesh;
|
|
CDmElement *pDmNextElement = NULL;
|
|
|
|
while ( pDmThisElement )
|
|
{
|
|
pDmNextElement = FindReferringElement< CDmElement >( pDmThisElement, sTargets );
|
|
if ( !pDmNextElement )
|
|
{
|
|
pDmNextElement = FindReferringElement< CDmElement >( pDmThisElement, sTarget );
|
|
}
|
|
|
|
if ( !pDmNextElement )
|
|
break;
|
|
|
|
pDmThisElement = pDmNextElement;
|
|
|
|
if ( CastElement< CDmeFlexRules >( pDmThisElement ) )
|
|
return CastElement< CDmeFlexRules >( pDmThisElement );
|
|
|
|
if ( visited.IsValidIndex( visited.Find( pDmThisElement ) ) )
|
|
break;
|
|
|
|
visited.Insert( pDmThisElement );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmeFlexRules *FindOrAddDmeFlexRules( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmeFlexRules *pDmeFlexRules = GetFlexRulesForMesh( pDmeMesh );
|
|
if ( pDmeFlexRules )
|
|
return pDmeFlexRules;
|
|
|
|
CDmeCombinationOperator *pDmeComboOp = FindOrCreateComboOp( pDmeMesh );
|
|
if ( !pDmeComboOp )
|
|
DMXEDIT_WARNING_RETURN_NULL( "DmeCombinationOperator could not be created for mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
CUtlString flexRulesName = pDmeMesh->GetName();
|
|
flexRulesName += "_flexRules";
|
|
|
|
pDmeFlexRules = CreateElement< CDmeFlexRules >( flexRulesName.Get(), pDmeMesh->GetFileId() );
|
|
if ( !pDmeFlexRules )
|
|
DMXEDIT_WARNING_RETURN_NULL( "DmeFlexRules could not be created for mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
pDmeComboOp->RemoveAllTargets();
|
|
|
|
pDmeComboOp->AddTarget( pDmeFlexRules );
|
|
|
|
pDmeFlexRules->SetTarget( pDmeMesh );
|
|
|
|
return pDmeFlexRules;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
ControlIndex_t FlexControl( CDmeMesh *pDmeMesh, const char *pszName, float flMin /* = 0.0f */, float flMax /* = 1.0f */ )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeComboOp = FindOrCreateComboOp( pDmeMesh );
|
|
if ( !pDmeComboOp )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "DmeCombinationOperator could not be created for mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
const ControlIndex_t nIndex = pDmeComboOp->FindOrCreateControl( pszName, false, true );
|
|
|
|
if ( nIndex >= 0 && ( flMin != 0.0f || flMax != 1.0f ) )
|
|
{
|
|
SetControlMinMax( pDmeMesh, nIndex, flMin, flMax );
|
|
}
|
|
|
|
return nIndex;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool SetControlMinMax( CDmeMesh *pDmeMesh, ControlIndex_t nControlIndex, float flMin, float flMax )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_FALSE( pDmeMesh );
|
|
|
|
CDmeCombinationOperator *pDmeComboOp = FindOrCreateComboOp( pDmeMesh );
|
|
if ( !pDmeComboOp )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "DmeCombinationOperator could not be created for mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
CDmAttribute *pControlsAttr = pDmeComboOp->GetAttribute( "controls", AT_ELEMENT_ARRAY );
|
|
if ( !pControlsAttr )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "No 'controls' attribute on ComboOp \"%s\"", pDmeComboOp->GetName() );
|
|
|
|
CDmrElementArray< CDmElement > controlsAttr( pControlsAttr );
|
|
if ( nControlIndex < controlsAttr.Count() )
|
|
{
|
|
CDmElement *pControlElement = controlsAttr[ nControlIndex ];
|
|
if ( pControlElement )
|
|
{
|
|
pControlElement->SetValue( "flexMin", flMin );
|
|
pControlElement->SetValue( "flexMax", flMax );
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
static CDmeModel *GetDmeModel( CDmeMesh *pDmeMesh )
|
|
{
|
|
if ( !pDmeMesh )
|
|
return NULL;
|
|
|
|
// Find the DmeModel of the destination mesh scene
|
|
CDmeDag *pDmeDag = pDmeMesh->GetParent();
|
|
while ( pDmeDag && !pDmeDag->IsA( CDmeModel::GetStaticTypeSymbol() ) )
|
|
{
|
|
pDmeDag = pDmeDag->GetParent();
|
|
}
|
|
|
|
return CastElement< CDmeModel >( pDmeDag );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
CDmAttribute *GetQcModelElementsAttr( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmeModel *pDmeModel = GetDmeModel( pDmeMesh );
|
|
if ( !pDmeModel )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Can't Find DmeModel for mesh \"%s\"", pDmeMesh->GetName() );
|
|
|
|
CDmAttribute *pDmAttr = pDmeModel->GetAttribute( "qcModelElements" );
|
|
if ( !pDmAttr )
|
|
{
|
|
pDmAttr = pDmeModel->AddAttribute( "qcModelElements", AT_ELEMENT_ARRAY );
|
|
}
|
|
|
|
if ( !pDmAttr || pDmAttr->GetType() != AT_ELEMENT_ARRAY )
|
|
DMXEDIT_WARNING_RETURN_NULL( "Can't find or create %s.qcModelElements element array attribute", pDmeModel->GetName() );
|
|
|
|
return pDmAttr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Eyeball(
|
|
CDmeMesh *pDmeMesh,
|
|
const char *pszName,
|
|
const char *pszBoneName,
|
|
const Vector &vPosition,
|
|
const char *pszMaterialName,
|
|
const float flDiameter,
|
|
const float flAngle,
|
|
const float flPupilScale )
|
|
{
|
|
CDmAttribute *pDmAttr = GetQcModelElementsAttr( pDmeMesh );
|
|
if ( !pDmAttr )
|
|
return false;
|
|
|
|
CDmrElementArray<> qcModelElements( pDmAttr );
|
|
|
|
CDmeEyeball *pDmeEyeball = CreateElement< CDmeEyeball >( pszName, pDmAttr->GetOwner()->GetFileId() );
|
|
if ( !pDmeEyeball )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Can't create DmeEyeball element" );
|
|
|
|
pDmeEyeball->m_sParentBoneName.Set( pszBoneName );
|
|
pDmeEyeball->m_vPosition.Set( vPosition );
|
|
pDmeEyeball->m_sMaterialName.Set( pszMaterialName );
|
|
pDmeEyeball->m_flRadius.Set( flDiameter / 2.0f );
|
|
pDmeEyeball->m_flYawAngle.Set( flAngle );
|
|
pDmeEyeball->m_flIrisScale.Set( flPupilScale );
|
|
|
|
qcModelElements.AddToTail( pDmeEyeball );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Eyelid(
|
|
CDmeMesh *pDmeMesh,
|
|
bool bUpper,
|
|
const char *pszLowererFlex,
|
|
float flLowererHeight,
|
|
const char *pszNeutralFlex,
|
|
float flNeutralHeight,
|
|
const char *pszRaiserFlex,
|
|
float flRaiserHeight,
|
|
const char *pszRightMaterialName,
|
|
const char *pszLeftMaterialName )
|
|
{
|
|
CDmAttribute *pDmAttr = GetQcModelElementsAttr( pDmeMesh );
|
|
if ( !pDmAttr )
|
|
return false;
|
|
|
|
CDmrElementArray<> qcModelElements( pDmAttr );
|
|
|
|
CDmeEyelid *pDmeEyelid = CreateElement< CDmeEyelid >( bUpper ? "upper" : "lower", pDmAttr->GetOwner()->GetFileId() );
|
|
if ( !pDmeEyelid )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Can't create DmeEyelid element" );
|
|
|
|
pDmeEyelid->m_bUpper.Set( bUpper );
|
|
pDmeEyelid->m_sLowererFlex.Set( pszLowererFlex );
|
|
pDmeEyelid->m_flLowererHeight.Set( flLowererHeight );
|
|
pDmeEyelid->m_sNeutralFlex.Set( pszNeutralFlex );
|
|
pDmeEyelid->m_flNeutralHeight.Set( flNeutralHeight );
|
|
pDmeEyelid->m_sRaiserFlex.Set( pszRaiserFlex );
|
|
pDmeEyelid->m_flRaiserHeight.Set( flRaiserHeight );
|
|
pDmeEyelid->m_sRightEyeballName.Set( pszRightMaterialName );
|
|
pDmeEyelid->m_sLeftEyeballName.Set( pszLeftMaterialName );
|
|
|
|
qcModelElements.AddToTail( pDmeEyelid );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool Mouth(
|
|
CDmeMesh *pDmeMesh,
|
|
int nMouthNumber,
|
|
const char *pszFlexControllerName,
|
|
const char *pszBoneName,
|
|
const Vector &vForward )
|
|
{
|
|
CDmAttribute *pDmAttr = GetQcModelElementsAttr( pDmeMesh );
|
|
if ( !pDmAttr )
|
|
return false;
|
|
|
|
CDmrElementArray<> qcModelElements( pDmAttr );
|
|
|
|
CUtlString sMouthName( "mouth" );
|
|
sMouthName += nMouthNumber;
|
|
|
|
CDmeMouth *pDmeMouth = CreateElement< CDmeMouth >( sMouthName.Get(), pDmAttr->GetOwner()->GetFileId() );
|
|
if ( !pDmeMouth )
|
|
DMXEDIT_WARNING_RETURN_FALSE( "Can't create DmeMouth element" );
|
|
|
|
pDmeMouth->m_nMouthNumber.Set( nMouthNumber );
|
|
pDmeMouth->m_sFlexControllerName.Set( pszFlexControllerName );
|
|
pDmeMouth->m_sBoneName.Set( pszBoneName );
|
|
pDmeMouth->m_vForward.Set( vForward );
|
|
|
|
qcModelElements.AddToTail( pDmeMouth );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
bool ClearFlexRules( CDmeMesh *pDmeMesh )
|
|
{
|
|
DMXEDIT_MESH_WARNING_RETURN_NULL( pDmeMesh );
|
|
|
|
CDmeFlexRules *pDmeFlexRules = GetFlexRulesForMesh( pDmeMesh );
|
|
if ( pDmeFlexRules )
|
|
{
|
|
pDmeFlexRules->RemoveAllRules();
|
|
}
|
|
|
|
CDmeCombinationOperator *pDmeComboOp = GetComboOpFromMesh( pDmeMesh );
|
|
if ( pDmeComboOp )
|
|
{
|
|
pDmeComboOp->RemoveAllControls();
|
|
}
|
|
|
|
return true;
|
|
} |