| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #include "blender2.h"
- #include <istdplug.h>
- #include "utilapi.h"
- #include "appdata.h"
- //----------------------------------------------------------------------------
- // Blender_Class
- //----------------------------------------------------------------------------
- class Blender_Class : public UtilityObj
- {
- public:
- Blender_Class();
- void BeginEditParams(Interface *ip,IUtil *iu);
- void EndEditParams(Interface *ip,IUtil *iu);
- void SelectionSetChanged ( Interface * ip, IUtil * ui );
- void DeleteThis() {}
- void Init(HWND hWnd);
- void Destroy(HWND hWnd);
- void Close () { iu->CloseUtility (); }
- void Get_Active_Time_Range ();
- void Blend_Keys ();
- void Loop_Controllers ();
- void OutputObject(INode *node,TCHAR *fname);
- private:
- BOOL Is_Root ( INode * node );
- float Heading_Delta_From_Quat ( Quat q );
- void Set_Data_Chunk ( INode * node, const Blender_Data_Chunk & new_data );
- void Remove_Data_Chunk ( INode * node );
- int first_frame;
- int first_match;
- int last_frame;
- int last_match;
- ISpinnerControl * first_frame_spin;
- ISpinnerControl * first_match_spin;
- ISpinnerControl * last_frame_spin;
- ISpinnerControl * last_match_spin;
- IUtil * iu;
- Interface *ip;
- HWND hPanel;
- };
- //----------------------------------------------------------------------------
- // the_blender
- //----------------------------------------------------------------------------
- static Blender_Class the_blender;
- //----------------------------------------------------------------------------
- // Blender_Desc_Class
- //----------------------------------------------------------------------------
- class Blender_Desc_Class:public ClassDesc
- {
- public:
- int IsPublic() {return 1;}
- void * Create(BOOL) {return &the_blender;}
- const TCHAR * ClassName() {return _T("Key Blender");}
- SClass_ID SuperClassID() {return UTILITY_CLASS_ID;}
- Class_ID ClassID() {return Blender_Class_ID;}
- const TCHAR* Category() {return _T("Westwood Tools");}
- };
- //----------------------------------------------------------------------------
- // blender_desc
- //----------------------------------------------------------------------------
- static Blender_Desc_Class blender_desc;
- //----------------------------------------------------------------------------
- // BlenderDesc
- //----------------------------------------------------------------------------
- ClassDesc* BlenderDesc() {return &blender_desc;}
- //----------------------------------------------------------------------------
- // BlenderDlgProc
- //----------------------------------------------------------------------------
- static BOOL CALLBACK BlenderDlgProc
- (
- HWND hWnd,
- UINT msg,
- WPARAM wParam,
- LPARAM lParam
- )
- {
- switch (msg)
- {
- case WM_INITDIALOG:
- the_blender.Init(hWnd);
- break;
-
- case WM_DESTROY:
- the_blender.Destroy(hWnd);
- break;
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case ID_CLOSE:
- the_blender.Close ();
- break;
- case ID_GET_ACTIVE_TIME_RANGE:
- the_blender.Get_Active_Time_Range ();
- break;
- case ID_APPLY:
- the_blender.Blend_Keys ();
- break;
- }
- break;
- default:
- return FALSE;
- }
- return TRUE;
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Blender_Class
- //----------------------------------------------------------------------------
- Blender_Class::Blender_Class()
- {
- iu = NULL;
- ip = NULL;
- hPanel = NULL;
- first_frame = 0;
- first_match = 0;
- last_frame = 0;
- last_match = 0;
- }
- //----------------------------------------------------------------------------
- // Blender_Class::BeginEditParams
- //----------------------------------------------------------------------------
- void Blender_Class::BeginEditParams(Interface *ip,IUtil *iu)
- {
- this->iu = iu;
- this->ip = ip;
- hPanel = ip->AddRollupPage
- (
- hInstance,
- MAKEINTRESOURCE(IDD_ASCIIOUT_PANEL),
- BlenderDlgProc,
- _T("Key Blender"),
- 0
- );
- }
-
- //----------------------------------------------------------------------------
- // Blender_Class::EndEditParams
- //----------------------------------------------------------------------------
- void Blender_Class::EndEditParams ( Interface *ip, IUtil *iu )
- {
- this->iu = NULL;
- this->ip = NULL;
- ip->DeleteRollupPage(hPanel);
- hPanel = NULL;
- }
- //----------------------------------------------------------------------------
- // Blender_Class::SelectionSetChanged
- //----------------------------------------------------------------------------
- void Blender_Class::SelectionSetChanged ( Interface * ip, IUtil * iu )
- {
- if ( ip->GetSelNodeCount () == 0 )
- {
- EnableWindow ( GetDlgItem ( hPanel, ID_APPLY ), FALSE );
- }
- else
- {
- EnableWindow ( GetDlgItem ( hPanel, ID_APPLY ), TRUE );
- }
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Init
- //----------------------------------------------------------------------------
- void Blender_Class::Init ( HWND hWnd )
- {
- if ( ip->GetSelNodeCount () == 0 )
- {
- EnableWindow ( GetDlgItem ( hWnd, ID_APPLY ), FALSE );
- }
- else
- {
- EnableWindow ( GetDlgItem ( hWnd, ID_APPLY ), TRUE );
- }
- int ticks_per_frame = GetTicksPerFrame ();
- int start_frame = ip->GetAnimRange ().Start () / ticks_per_frame;
- int end_frame = ip->GetAnimRange ().End () / ticks_per_frame;
- first_frame = start_frame;
- first_match = start_frame;
- last_frame = end_frame;
- last_match = start_frame;
- first_frame_spin = SetupIntSpinner
- (
- hWnd,
- IDC_FIRST_SPIN,
- IDC_FIRST_EDIT,
- -65536,
- 65535,
- first_frame
- );
- first_frame_spin->SetResetValue ( first_frame );
- first_match_spin = SetupIntSpinner
- (
- hWnd,
- IDC_FIRST_MATCH_SPIN,
- IDC_FIRST_MATCH_EDIT,
- -65536,
- 65535,
- first_match
- );
- first_match_spin->SetResetValue ( first_match );
- last_frame_spin = SetupIntSpinner
- (
- hWnd,
- IDC_LAST_SPIN,
- IDC_LAST_EDIT,
- -65536,
- 65535,
- last_frame
- );
- last_frame_spin->SetResetValue ( last_frame );
- last_match_spin = SetupIntSpinner
- (
- hWnd,
- IDC_LAST_MATCH_SPIN,
- IDC_LAST_MATCH_EDIT,
- -65536,
- 65535,
- last_match
- );
- last_match_spin->SetResetValue ( last_match );
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Destroy
- //----------------------------------------------------------------------------
- void Blender_Class::Destroy(HWND hWnd)
- {
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Get_Active_Time_Range
- //----------------------------------------------------------------------------
- void Blender_Class::Get_Active_Time_Range ()
- {
- int ticks_per_frame = GetTicksPerFrame ();
- int start_frame = ip->GetAnimRange ().Start () / ticks_per_frame;
- int end_frame = ip->GetAnimRange ().End () / ticks_per_frame;
- first_frame_spin->SetValue ( start_frame, FALSE );
- last_frame_spin->SetValue ( end_frame, FALSE );
- first_match_spin->SetValue ( start_frame, FALSE );
- last_match_spin->SetValue ( start_frame, FALSE );
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Blend_Keys
- //----------------------------------------------------------------------------
- static const Quat zero (0.0,0.0,0.0,1.0);
- void Blender_Class::Blend_Keys ()
- {
- Interval interval; // Used for returned validity intervals (which are not used).
- int ticks_per_frame = GetTicksPerFrame ();
- TimeValue start_time = first_frame_spin->GetIVal () * ticks_per_frame;
- TimeValue end_time = last_frame_spin->GetIVal () * ticks_per_frame;
- TimeValue start_match = first_match_spin->GetIVal () * ticks_per_frame;
- TimeValue end_match = last_match_spin->GetIVal () * ticks_per_frame;
- float t_scale = 1.0f / (float) (end_time - start_time);
- BOOL bad_controller_found = FALSE;
- theHold.Begin ();
- SetCursor ( LoadCursor (NULL, IDC_WAIT) );
- SuspendAnimate ();
- AnimateOn ();
- int number_of_nodes = ip->GetSelNodeCount ();
- for ( int i = 0; i < number_of_nodes; ++ i )
- {
- // Get the inode.
- INode * inode = ip->GetSelNode ( i );
- Control * tm_controller = inode->GetTMController ();
- if ( tm_controller == NULL )
- continue;
- Quat rot_start_delta = zero;
- Quat rot_end_delta = zero;
- Blender_Data_Chunk data;
- data.position_delta = Point3 (0.0f, 0.0f, 0.0f);
- data.heading_delta = 0.0f;
- Control * c;
- //--------------------------------------------------------------------
- // Get the rotation controller and change its keys.
- c = tm_controller->GetRotationController ();
- if ( c != NULL )
- {
- if ( c->ClassID () != Class_ID (TCBINTERP_ROTATION_CLASS_ID, 0) )
- {
- #if 0
- char m [ 256 ];
- sprintf ( m, "Node %s has rotation controller ID %u.",
- inode->GetName (), c->ClassID ().PartA () );
- MessageBox ( GetActiveWindow (), m, "Debug", MB_OK );
- #endif
- bad_controller_found = TRUE;
- }
- else
- {
- Quat actual_start_orientation;
- Quat desired_start_orientation;
- Quat actual_end_orientation;
- Quat desired_end_orientation;
- c->GetValue ( start_time, & actual_start_orientation, interval );
- c->GetValue ( start_match, & desired_start_orientation, interval );
- c->GetValue ( end_time, & actual_end_orientation, interval );
- c->GetValue ( end_match, & desired_end_orientation, interval );
- // Ensure there are keys at the beginning and end of the blend interval.
- c->SetValue ( start_time, & actual_start_orientation );
- c->SetValue ( end_time, & actual_end_orientation );
- actual_end_orientation.MakeClosest ( actual_start_orientation );
- desired_start_orientation.MakeClosest ( actual_start_orientation );
- desired_end_orientation.MakeClosest ( actual_end_orientation );
- rot_start_delta = desired_start_orientation / actual_start_orientation;
- rot_end_delta = desired_end_orientation / actual_end_orientation;
- data.heading_delta = Heading_Delta_From_Quat
- ( actual_end_orientation / actual_start_orientation );
- #if 1
- int number_of_keys = c->NumKeys ();
- for ( int j = 0; j < number_of_keys; ++ j )
- {
- TimeValue key_time = c->GetKeyTime ( j );
- if ( key_time >= start_time && key_time <= end_time )
- {
- Quat orientation;
- c->GetValue ( key_time, & orientation, interval );
- float t = (float) (key_time - start_time) * t_scale;
- Quat delta = Slerp ( rot_start_delta, zero, t ) *
- Slerp ( zero, rot_end_delta, t );
- orientation = orientation * delta;
- c->SetValue ( key_time, & orientation );
- }
- }
- #else
- IKeyControl * keys = GetKeyControlInterface ( c );
- if ( keys != NULL )
- {
- int number_of_keys = keys->GetNumKeys ();
- Quat prev_key_absolute (0.0,0.0,0.0,1.0);
- Quat new_prev_key_absolute (0.0,0.0,0.0,1.0);
- for ( int j = 0; j < number_of_keys; ++ j )
- {
- ITCBRotKey key;
- keys->GetKey ( j, & key );
- Quat this_key_absolute = prev_key_absolute * (Quat) key.val;
- Quat new_key_absolute = this_key_absolute;
- if ( key.time >= start_time && key.time <= end_time )
- {
- float t = (float) (key.time - start_time) * t_scale;
- Quat delta = Slerp ( rot_start_delta, zero, t ) *
- Slerp ( zero, rot_end_delta, t );
- new_key_absolute = this_key_absolute * delta;
- key.val = new_key_absolute / new_prev_key_absolute;
- keys->SetKey ( j, & key );
- }
- prev_key_absolute = this_key_absolute;
- new_prev_key_absolute = new_key_absolute;
- }
- }
- #endif
- }
- }
- //--------------------------------------------------------------------
- // Get the position controller and change its keys.
- c = tm_controller->GetPositionController ();
- if ( c != NULL )
- {
- if ( c->ClassID () != Class_ID (TCBINTERP_POSITION_CLASS_ID, 0) )
- {
- bad_controller_found = TRUE;
- }
- else
- {
- Point3 actual_start_position;
- Point3 desired_start_position;
- Point3 actual_end_position;
- Point3 desired_end_position;
- c->GetValue ( start_time, & actual_start_position, interval );
- c->GetValue ( start_match, & desired_start_position, interval );
- c->GetValue ( end_time, & actual_end_position, interval );
- c->GetValue ( end_match, & desired_end_position, interval );
- // Ensure there are keys at the beginning and end of the blend interval.
- c->SetValue ( start_time, & actual_start_position );
- c->SetValue ( end_time, & actual_end_position );
- data.position_delta = actual_end_position - actual_start_position;
- #if 1
- int number_of_keys = c->NumKeys ();
- for ( int j = 0; j < number_of_keys; ++ j )
- {
- TimeValue key_time = c->GetKeyTime ( j );
- if ( key_time >= start_time && key_time <= end_time )
- {
- Point3 position;
- c->GetValue ( key_time, & position, interval );
- float t = (float) (key_time - start_time) * t_scale;
- // Calculate the position the node would be in if it
- // traveled in a straight line.
- Point3 actual_position = actual_start_position * (1.0f - t) +
- actual_end_position * t;
- // Find the delta between the straight-line position and
- // the real position.
- Point3 delta = position - actual_position;
- // Rotate the delta by the change in orientation
- // at this time.
- Quat rot_delta = Slerp ( rot_start_delta, zero, t ) *
- Slerp ( zero, rot_end_delta, t );
- Matrix3 rot_matrix;
- rot_delta.MakeMatrix ( rot_matrix );
- delta = delta * rot_matrix;
- // Add the delta to the straight-line position on the
- // desired line.
- Point3 desired_position = desired_start_position * (1.0f - t) +
- desired_end_position * t;
- position = desired_position + delta;
- // Store the new key value.
- c->SetValue ( key_time, & position );
- }
- }
- #else
- IKeyControl * keys = GetKeyControlInterface ( c );
- if ( keys != NULL )
- {
- int number_of_keys = keys->GetNumKeys ();
- for ( int j = 0; j < number_of_keys; ++ j )
- {
- ITCBPoint3Key key;
- keys->GetKey ( j, & key );
- if ( key.time >= start_time && key.time <= end_time )
- {
- float t = (float) (key.time - start_time) * t_scale;
- // Calculate the position the node would be in if it
- // traveled in a straight line.
- Point3 actual_position = actual_start_position * (1.0f - t) +
- actual_end_position * t;
- // Find the delta between the straight-line position and
- // the real position.
- Point3 delta = key.val - actual_position;
- // Rotate the delta by the change in orientation
- // at this time.
- Quat rot_delta = Slerp ( rot_start_delta, zero, t ) *
- Slerp ( zero, rot_end_delta, t );
- Matrix3 rot_matrix;
- rot_delta.MakeMatrix ( rot_matrix );
- delta = delta * rot_matrix;
- // Add the delta to the straight-line position on the
- // desired line.
- Point3 desired_position = desired_start_position * (1.0f - t) +
- desired_end_position * t;
- key.val = desired_position + delta;
- // Store the new key value.
- keys->SetKey ( j, & key );
- }
- }
- }
- #endif
- }
- }
- // If this node is a root node (its parent is not selected), attach
- // a data chunk that describes its motion during the blended section.
- if ( Is_Root ( inode ) )
- Set_Data_Chunk ( inode, data );
- else
- Remove_Data_Chunk ( inode );
- inode->InvalidateTM ();
- }
- ResumeAnimate ();
- TSTR undostr;
- undostr.printf ( "Blend Keys" );
- theHold.Accept ( undostr );
- SetCursor ( LoadCursor (NULL, IDC_ARROW) );
- ip->RedrawViews ( ip->GetTime () );
- if ( bad_controller_found )
- {
- MessageBox ( GetActiveWindow (),
- "Warning: Some controllers were not blended\n"
- "because they were not TCB controllers.", "Warning", MB_OK );
- }
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Is_Root
- //----------------------------------------------------------------------------
- BOOL Blender_Class::Is_Root ( INode * node )
- {
- node = node->GetParentNode ();
- int number_of_nodes = ip->GetSelNodeCount ();
- for ( int i = 0; i < number_of_nodes; ++ i )
- {
- if ( ip->GetSelNode ( i ) == node )
- return FALSE;
- }
- return TRUE;
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Heading_Delta_From_Quat
- //----------------------------------------------------------------------------
- float Blender_Class::Heading_Delta_From_Quat ( Quat q )
- {
- Matrix3 rot_matrix;
- q.MakeMatrix ( rot_matrix );
- Point3 row = rot_matrix.GetRow ( 0 );
- return (float) atan2 ( row.y, row.x );
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Set_Data_Chunk
- //----------------------------------------------------------------------------
- void Blender_Class::Set_Data_Chunk
- (
- INode * node,
- const Blender_Data_Chunk & new_data
- )
- {
- AppDataChunk * chunk = node->GetAppDataChunk
- (
- Blender_Class_ID,
- UTILITY_CLASS_ID,
- 1
- );
- Blender_Data_Chunk * data;
- if ( chunk != NULL )
- {
- data = (Blender_Data_Chunk *) chunk->data;
- }
- else
- {
- data = (Blender_Data_Chunk *) malloc ( sizeof (Blender_Data_Chunk) );
- node->AddAppDataChunk
- (
- Blender_Class_ID,
- UTILITY_CLASS_ID,
- 1,
- sizeof (Blender_Data_Chunk),
- data
- );
- }
- *data = new_data;
- #if 0
- char m [ 256 ];
- sprintf ( m, "Setting data chunk for %s:\n"
- "Position delta = (%.1f, %.1f, %.1f)\n"
- "Heading delta = %.1f",
- node->GetName (),
- data->position_delta.x,
- data->position_delta.y,
- data->position_delta.z,
- data->heading_delta * 180 / PI );
- MessageBox ( GetActiveWindow (), m, "Debug", MB_OK );
- #endif
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Remove_Data_Chunk
- //----------------------------------------------------------------------------
- void Blender_Class::Remove_Data_Chunk ( INode * node )
- {
- node->RemoveAppDataChunk
- (
- Blender_Class_ID,
- UTILITY_CLASS_ID,
- 1
- );
- }
- //----------------------------------------------------------------------------
- // Blender_Class::Loop_Controllers
- //----------------------------------------------------------------------------
- void Blender_Class::Loop_Controllers ()
- {
- int number_of_nodes = ip->GetSelNodeCount ();
- for ( int i = 0; i < number_of_nodes; ++ i )
- {
- // Get the inode.
- INode * inode = ip->GetSelNode ( i );
- Control * tm_controller = inode->GetTMController ();
- if ( tm_controller == NULL )
- continue;
- Control * c;
- c = tm_controller->GetRotationController ();
- if ( c )
- {
- c->SetORT ( ORT_LOOP, ORT_BEFORE );
- c->SetORT ( ORT_LOOP, ORT_AFTER );
- }
- c = tm_controller->GetPositionController ();
- if ( c )
- {
- c->SetORT ( ORT_LOOP, ORT_BEFORE );
- c->SetORT ( ORT_LOOP, ORT_AFTER );
- }
- c = tm_controller->GetScaleController ();
- if ( c )
- {
- c->SetORT ( ORT_LOOP, ORT_BEFORE );
- c->SetORT ( ORT_LOOP, ORT_AFTER );
- }
- }
- }
|