]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/af/DialogAFBody.cpp
hello world
[icculus/iodoom3.git] / neo / tools / af / DialogAFBody.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "../../sys/win32/rc/AFEditor_resource.h"
33
34 #include "DialogAF.h"
35 #include "DialogAFName.h"
36 #include "DialogAFConstraint.h"
37 #include "DialogAFBody.h"
38
39 typedef struct {
40         traceModel_t type;
41         const char *name;
42 } cm_type_t;
43
44 cm_type_t modelTypes[] = {
45         { TRM_INVALID, "invalid" },
46         { TRM_BOX, "box" },
47         { TRM_OCTAHEDRON, "octahedron" },
48         { TRM_DODECAHEDRON, "dodecahedron" },
49         { TRM_CYLINDER, "cylinder" },
50         { TRM_CONE, "cone" },
51         { TRM_BONE, "bone" },
52         { TRM_CUSTOM, "custom" },
53         { TRM_INVALID, NULL }
54 };
55
56 const char *ModelTypeToString( int type ) {
57         for ( int i = 0; modelTypes[i].name; i++ ) {
58                 if ( modelTypes[i].type == type ) {
59                         return modelTypes[i].name;
60                 }
61         }
62         return "";
63 }
64
65 traceModel_t StringToModelType( const char *str ) {
66         for ( int i = 0; modelTypes[i].name; i++ ) {
67                 if ( idStr::Icmp( modelTypes[i].name, str ) == 0 ) {
68                         return modelTypes[i].type;
69                 }
70         }
71         return TRM_INVALID;
72 }
73
74 // DialogAFBody dialog
75
76 toolTip_t DialogAFBody::toolTips[] = {
77         { IDC_COMBO_BODIES, "select body for editing" },
78         { IDC_BUTTON_NEWBODY, "create a new body" },
79         { IDC_BUTTON_RENAMEBODY, "rename the selected body" },
80         { IDC_BUTTON_DELETEBODY, "delete the selected body" },
81         { IDC_COMBO_CM_TYPE, "collision model type" },
82         { IDC_EDIT_CM_NAME, "custom collision model" },
83         { IDC_BUTTON_CM_BROWSE, "browse custom collision model" },
84         { IDC_COMBO_BONE_JOINT1, "first joint of bone collision model" },
85         { IDC_COMBO_BONE_JOINT2, "second joint of bone collision model" },
86         { IDC_EDIT_CM_HEIGHT, "hieght of the collision model" },
87         { IDC_EDIT_CM_WIDTH, "width of the collision model" },
88         { IDC_EDIT_CM_LENGTH, "length of the collision model" },
89         { IDC_EDIT_CM_NUMSIDES, "number of sides of the collision model" },
90         { IDC_EDIT_CM_DENSITY, "collision model density" },
91         { IDC_EDIT_CM_INERTIASCALE, "inertia tensor scale" },
92         { IDC_RADIO_ORIGIN_COORDINATES, "use absolute coordinates for the body origin" },
93         { IDC_RADIO_ORIGIN_BONECENTER, "use the center of a bone for the body origin" },
94         { IDC_RADIO_ORIGIN_JOINT, "use the position of a joint for the body origin" },
95         { IDC_EDIT_AF_VECTOR_X, "x-coordinate" },
96         { IDC_EDIT_AF_VECTOR_Y, "y-coordinate" },
97         { IDC_EDIT_AF_VECTOR_Z, "z-coordinate" },
98         { IDC_COMBO_ORIGIN_BONECENTER_JOINT1, "bone start joint" },
99         { IDC_COMBO_ORIGIN_BONECENTER_JOINT2, "bone end joint" },
100         { IDC_COMBO_ORIGIN_JOINT, "joint name" },
101         { IDC_EDIT_ANGLES_PITCH, "pitch angle of body" },
102         { IDC_EDIT_ANGLES_YAW, "yaw angle of body" },
103         { IDC_EDIT_ANGLES_ROLL, "roll angle of body" },
104         { IDC_EDIT_LINEARFRICTION, "translational friction" },
105         { IDC_EDIT_ANGULARFRICTION, "rotational friction" },
106         { IDC_EDIT_CONTACTFRICTION, "friction with contact surfaces" },
107         { IDC_CHECK_SELFCOLLISION, "collide with other bodies of this articulated figure" },
108         { IDC_EDIT_CONTENTS, "content of body" },
109         { IDC_EDIT_CLIPMASK, "collide with these content types" },
110         { IDC_EDIT_FRICTIONDIRECTION, "single friction direction relative to body" },
111         { IDC_EDIT_CONTACTMOTORDIRECTION, "contact motor direction" },
112         { IDC_COMBO_MODIFIEDJOINT, "the joint modified by the relative rotation of the body" },
113         { IDC_RADIO_MODIFY_ORIENTATION, "modify the joint orientation" },
114         { IDC_RADIO_MODIFY_POSITION, "modify the joint position" },
115         { IDC_RADIO_MODIFY_BOTH, "modify the joint orientation and position" },
116         { IDC_EDIT_CONTAINEDJOINTS, "all the joints contained by this body" },
117         { 0, NULL }
118 };
119
120 IMPLEMENT_DYNAMIC(DialogAFBody, CDialog)
121
122 /*
123 ================
124 DialogAFBody::DialogAFBody
125 ================
126 */
127 DialogAFBody::DialogAFBody( CWnd* pParent /*=NULL*/ )
128         : CDialog(DialogAFBody::IDD, pParent)
129         , constraintDlg(NULL)
130         , numJoints(0)
131         , cm_length(0)
132         , cm_height(0)
133         , cm_width(0)
134         , cm_density(0)
135         , cm_numSides(3)
136         , cm_origin_x(0)
137         , cm_origin_y(0)
138         , cm_origin_z(0)
139         , cm_angles_pitch(0)
140         , cm_angles_yaw(0)
141         , cm_angles_roll(0)
142         , m_selfCollision(false)
143         , m_linearFriction(0)
144         , m_angularFriction(0)
145         , m_contactFriction(0)
146         , file(NULL)
147         , body(NULL)
148 {
149         Create( IDD_DIALOG_AF_BODY, pParent );
150         EnableToolTips( TRUE );
151 }
152
153 /*
154 ================
155 DialogAFBody::~DialogAFBody
156 ================
157 */
158 DialogAFBody::~DialogAFBody() {
159 }
160
161 /*
162 ================
163 DialogAFBody::DoDataExchange
164 ================
165 */
166 void DialogAFBody::DoDataExchange( CDataExchange* pDX ) {
167         CDialog::DoDataExchange( pDX );
168         //{{AFX_DATA_MAP(DialogAFBody)
169         DDX_Control(pDX, IDC_COMBO_BODIES, bodyList);
170         DDX_Control(pDX, IDC_COMBO_CM_TYPE, cm_comboType);
171         DDX_Text(pDX, IDC_EDIT_CM_LENGTH, cm_length);
172         DDX_Text(pDX, IDC_EDIT_CM_HEIGHT, cm_height);
173         DDX_Text(pDX, IDC_EDIT_CM_WIDTH, cm_width);
174         DDX_Text(pDX, IDC_EDIT_CM_NUMSIDES, cm_numSides);
175         DDX_Control(pDX, IDC_COMBO_BONE_JOINT1, cm_comboBoneJoint1 );
176         DDX_Control(pDX, IDC_COMBO_BONE_JOINT2, cm_comboBoneJoint2 );
177         DDX_Text(pDX, IDC_EDIT_CM_DENSITY, cm_density);
178         DDX_Control(pDX, IDC_EDIT_CM_INERTIASCALE, cm_inertiaScale);
179         DDX_Text(pDX, IDC_EDIT_AF_VECTOR_X, cm_origin_x);
180         DDX_Text(pDX, IDC_EDIT_AF_VECTOR_Y, cm_origin_y);
181         DDX_Text(pDX, IDC_EDIT_AF_VECTOR_Z, cm_origin_z);
182         DDX_Control(pDX, IDC_COMBO_ORIGIN_BONECENTER_JOINT1, cm_originBoneCenterJoint1);
183         DDX_Control(pDX, IDC_COMBO_ORIGIN_BONECENTER_JOINT2, cm_originBoneCenterJoint2);
184         DDX_Control(pDX, IDC_COMBO_ORIGIN_JOINT, cm_originJoint);
185         DDX_Text(pDX, IDC_EDIT_ANGLES_PITCH, cm_angles_pitch);
186         DDX_Text(pDX, IDC_EDIT_ANGLES_YAW, cm_angles_yaw);
187         DDX_Text(pDX, IDC_EDIT_ANGLES_ROLL, cm_angles_roll);
188         DDX_Check(pDX, IDC_CHECK_SELFCOLLISION, m_selfCollision);
189         DDX_Control(pDX, IDC_EDIT_CONTENTS, m_editContents);
190         DDX_Control(pDX, IDC_EDIT_CLIPMASK, m_editClipMask);
191         DDX_Text(pDX, IDC_EDIT_LINEARFRICTION, m_linearFriction);
192         DDX_Text(pDX, IDC_EDIT_ANGULARFRICTION, m_angularFriction);
193         DDX_Text(pDX, IDC_EDIT_CONTACTFRICTION, m_contactFriction);
194         DDX_Control(pDX, IDC_EDIT_FRICTIONDIRECTION, m_frictionDirection);
195         DDX_Control(pDX, IDC_EDIT_CONTACTMOTORDIRECTION, m_contactMotorDirection);
196         DDX_Control(pDX, IDC_COMBO_MODIFIEDJOINT, m_comboModifiedJoint);
197         DDX_Control(pDX, IDC_EDIT_CONTAINEDJOINTS, m_editContainedJoints);
198         //}}AFX_DATA_MAP
199 }
200
201 /*
202 ================
203 DialogAFBody::InitBodyList
204 ================
205 */
206 void DialogAFBody::InitBodyList( void ) {
207         CString str;
208
209         bodyList.ResetContent();
210         if ( !file ) {
211                 return;
212         }
213         for ( int i = 0; i < file->bodies.Num(); i++ ) {
214                 bodyList.AddString( file->bodies[i]->name.c_str() );
215         }
216         if ( bodyList.GetCount() != 0 ) {
217                 bodyList.SetCurSel( 0 );
218                 bodyList.GetLBText( 0, str );
219                 LoadBody( str );
220         }
221 }
222
223 /*
224 ================
225 DialogAFBody::InitJointLists
226
227   initialize the joint lists for bone collision models
228 ================
229 */
230 void DialogAFBody::InitJointLists( void ) {
231         idStrList joints;
232
233         cm_comboBoneJoint1.ResetContent();
234         cm_comboBoneJoint2.ResetContent();
235         cm_originBoneCenterJoint1.ResetContent();
236         cm_originBoneCenterJoint2.ResetContent();
237         cm_originJoint.ResetContent();
238         numJoints = 0;
239
240         if ( !file ) {
241                 return;
242         }
243
244         const idRenderModel *model = gameEdit->ANIM_GetModelFromName( file->model );
245         if ( !model ) {
246                 return;
247         }
248
249         numJoints = model->NumJoints();
250         for ( int i = 0; i < numJoints; i++ ) {
251                 const char *jointName = model->GetJointName( (jointHandle_t) i );
252                 cm_comboBoneJoint1.AddString( jointName );
253                 cm_comboBoneJoint2.AddString( jointName );
254                 cm_originBoneCenterJoint1.AddString( jointName );
255                 cm_originBoneCenterJoint2.AddString( jointName );
256                 cm_originJoint.AddString( jointName );
257         }
258 }
259
260 /*
261 ================
262 DialogAFBody::InitCollisionModelType
263 ================
264 */
265 void DialogAFBody::InitCollisionModelType( void ) {
266         int showBone, showOther;
267         bool enableOther;
268         CString str;
269
270         UpdateData( TRUE );
271         cm_comboType.GetLBText( cm_comboType.GetCurSel(), str );
272         traceModel_t type = StringToModelType( str );
273         if ( type == TRM_BONE ) {
274                 showBone = SW_SHOW;
275                 showOther = SW_HIDE;
276                 enableOther = false;
277                 GetDlgItem( IDC_STATIC_CM_LENGTH )->SetWindowText( "Joint 1" );
278                 GetDlgItem( IDC_STATIC_CM_HEIGHT )->SetWindowText( "Joint 2" );
279                 CheckRadioButton( IDC_RADIO_ORIGIN_COORDINATES,
280                                                         IDC_RADIO_ORIGIN_JOINT,
281                                                                 IDC_RADIO_ORIGIN_BONECENTER );
282         }
283         else {
284                 showBone = SW_HIDE;
285                 showOther = SW_SHOW;
286                 enableOther = true;
287                 GetDlgItem( IDC_STATIC_CM_LENGTH )->SetWindowText( "Length" );
288                 GetDlgItem( IDC_STATIC_CM_HEIGHT )->SetWindowText( "Height" );
289         }
290         GetDlgItem( IDC_COMBO_BONE_JOINT1 )->ShowWindow( showBone );
291         GetDlgItem( IDC_COMBO_BONE_JOINT2 )->ShowWindow( showBone );
292         GetDlgItem( IDC_EDIT_CM_LENGTH )->ShowWindow( showOther );
293         GetDlgItem( IDC_SPIN_CM_LENGTH )->ShowWindow( showOther );
294         GetDlgItem( IDC_EDIT_CM_HEIGHT )->ShowWindow( showOther );
295         GetDlgItem( IDC_SPIN_CM_HEIGHT )->ShowWindow( showOther );
296
297         // enable/disable origin controls
298         GetDlgItem( IDC_EDIT_AF_VECTOR_X )->EnableWindow( enableOther );
299         GetDlgItem( IDC_SPIN_AF_VECTOR_X )->EnableWindow( enableOther );
300         GetDlgItem( IDC_EDIT_AF_VECTOR_Y )->EnableWindow( enableOther );
301         GetDlgItem( IDC_SPIN_AF_VECTOR_Y )->EnableWindow( enableOther );
302         GetDlgItem( IDC_EDIT_AF_VECTOR_Z )->EnableWindow( enableOther );
303         GetDlgItem( IDC_SPIN_AF_VECTOR_Z )->EnableWindow( enableOther );
304
305         // enable/disable joint controls
306         GetDlgItem( IDC_COMBO_ORIGIN_BONECENTER_JOINT1 )->EnableWindow( enableOther );
307         GetDlgItem( IDC_COMBO_ORIGIN_BONECENTER_JOINT2 )->EnableWindow( enableOther );
308         GetDlgItem( IDC_COMBO_ORIGIN_JOINT )->EnableWindow( enableOther );
309
310         // enable/disable angles controls
311         GetDlgItem( IDC_STATIC_ANGLES_PITCH )->EnableWindow( enableOther );
312         GetDlgItem( IDC_EDIT_ANGLES_PITCH )->EnableWindow( enableOther );
313         GetDlgItem( IDC_SPIN_ANGLES_PITCH )->EnableWindow( enableOther );
314         GetDlgItem( IDC_STATIC_ANGLES_YAW )->EnableWindow( enableOther );
315         GetDlgItem( IDC_EDIT_ANGLES_YAW )->EnableWindow( enableOther );
316         GetDlgItem( IDC_SPIN_ANGLES_YAW )->EnableWindow( enableOther );
317         GetDlgItem( IDC_STATIC_ANGLES_ROLL )->EnableWindow( enableOther );
318         GetDlgItem( IDC_EDIT_ANGLES_ROLL )->EnableWindow( enableOther );
319         GetDlgItem( IDC_SPIN_ANGLES_ROLL )->EnableWindow( enableOther );
320
321         GetDlgItem( IDC_RADIO_ORIGIN_COORDINATES )->EnableWindow( enableOther );
322         GetDlgItem( IDC_RADIO_ORIGIN_BONECENTER )->EnableWindow( enableOther );
323         GetDlgItem( IDC_RADIO_ORIGIN_JOINT )->EnableWindow( enableOther );
324
325         if ( type == TRM_CONE || type == TRM_CYLINDER ) {
326                 GetDlgItem( IDC_EDIT_CM_NUMSIDES )->EnableWindow( true );
327                 GetDlgItem( IDC_SPIN_CM_NUMSIDES )->EnableWindow( true );
328         }
329         else {
330                 GetDlgItem( IDC_EDIT_CM_NUMSIDES )->EnableWindow( false );
331                 GetDlgItem( IDC_SPIN_CM_NUMSIDES )->EnableWindow( false );
332         }
333 }
334
335 /*
336 ================
337 DialogAFBody::InitModifiedJointList
338 ================
339 */
340 void DialogAFBody::InitModifiedJointList( void ) {
341         int i, j, numJoints;
342         CString str;
343
344         m_comboModifiedJoint.ResetContent();
345
346         if ( !file ) {
347                 return;
348         }
349
350         const idRenderModel *model = gameEdit->ANIM_GetModelFromName( file->model );
351         if ( !model ) {
352                 return;
353         }
354
355         numJoints = model->NumJoints();
356         for ( i = 0; i < numJoints; i++ ) {
357                 const char *jointName = model->GetJointName( (jointHandle_t) i );
358                 for ( j = 0; j < file->bodies.Num(); j++ ) {
359                         if ( file->bodies[j] == body ) {
360                                 continue;
361                         }
362                         if ( file->bodies[j]->jointName.Icmp( jointName ) == 0 ) {
363                                 break;
364                         }
365                 }
366                 if ( j < file->bodies.Num() ) {
367                         continue;
368                 }
369                 m_comboModifiedJoint.AddString( jointName );
370         }
371
372         if ( body ) {
373                 if ( body->jointName.Length() == 0 ) {
374                         m_comboModifiedJoint.GetLBText( 0, str );
375                         body->jointName = str;
376                 }
377                 SetSafeComboBoxSelection( &m_comboModifiedJoint, body->jointName, -1 );
378                 // cannot change the body which modifies the origin joint
379                 if ( body->jointName.Icmp( "origin" ) == 0 ) {
380                         // check if there is another body which modifies the "origin" joint
381                         for ( i = 0; i < file->bodies.Num(); i++ ) {
382                                 if ( file->bodies[i] == body ) {
383                                         continue;
384                                 }
385                                 if ( file->bodies[i]->jointName.Icmp( "origin" ) == 0 ) {
386                                         break;
387                                 }
388                         }
389                         // if there is another body which modifies the "origin" joint
390                         if ( i < file->bodies.Num() ) {
391                                 GetDlgItem( IDC_COMBO_MODIFIEDJOINT )->EnableWindow( true );
392                         }
393                         else {
394                                 GetDlgItem( IDC_COMBO_MODIFIEDJOINT )->EnableWindow( false );
395                         }
396                 }
397                 else {
398                         GetDlgItem( IDC_COMBO_MODIFIEDJOINT )->EnableWindow( true );
399                 }
400         }
401 }
402
403 /*
404 ================
405 DialogAFBody::InitNewRenameDeleteButtons
406 ================
407 */
408 void DialogAFBody::InitNewRenameDeleteButtons( void ) {
409         if ( file && numJoints > file->bodies.Num() ) {
410                 GetDlgItem( IDC_BUTTON_NEWBODY )->EnableWindow( true );
411         }
412         else {
413                 GetDlgItem( IDC_BUTTON_NEWBODY )->EnableWindow( false );
414         }
415
416         if ( file && bodyList.GetCount() >= 1 ) {
417                 GetDlgItem( IDC_BUTTON_RENAMEBODY )->EnableWindow( true );
418                 GetDlgItem( IDC_BUTTON_DELETEBODY )->EnableWindow( true );
419         }
420         else {
421                 GetDlgItem( IDC_BUTTON_RENAMEBODY )->EnableWindow( false );
422                 GetDlgItem( IDC_BUTTON_DELETEBODY )->EnableWindow( false );
423         }
424 }
425
426 /*
427 ================
428 DialogAFBody::LoadFile
429 ================
430 */
431 void DialogAFBody::LoadFile( idDeclAF *af ) {
432         file = af;
433         body = NULL;
434         InitJointLists();
435         InitBodyList();
436         InitNewRenameDeleteButtons();
437 }
438
439 /*
440 ================
441 DialogAFBody::SaveFile
442 ================
443 */
444 void DialogAFBody::SaveFile( void ) {
445         SaveBody();
446 }
447
448 /*
449 ================
450 DialogAFBody::LoadBody
451 ================
452 */
453 void DialogAFBody::LoadBody( const char *name ) {
454         int i, s1, s2;
455         idStr str;
456
457         if ( !file ) {
458                 return;
459         }
460         for ( i = 0; i < file->bodies.Num(); i++ ) {
461                 if ( file->bodies[i]->name.Icmp( name ) == 0 ) {
462                         break;
463                 }
464         }
465         if ( i >= file->bodies.Num() ) {
466                 return;
467         }
468         body = file->bodies[i];
469
470         // load collision model from the current idDeclAF_Body
471         SetSafeComboBoxSelection( &cm_comboType, ModelTypeToString( body->modelType ), -1 );
472         if ( body->modelType == TRM_BONE ) {
473                 s1 = SetSafeComboBoxSelection( &cm_comboBoneJoint1, body->v1.joint1.c_str(), -1 );
474                 s2 = SetSafeComboBoxSelection( &cm_comboBoneJoint2, body->v2.joint1.c_str(), s1 );
475                 cm_width = body->width;
476                 cm_length = cm_height = 20.0f;
477                 s1 = SetSafeComboBoxSelection( &cm_originBoneCenterJoint1, body->v1.joint1.c_str(), -1 );
478                 s2 = SetSafeComboBoxSelection( &cm_originBoneCenterJoint2, body->v2.joint1.c_str(), s1 );
479         }
480         else {
481                 cm_length = body->v2.ToVec3().x - body->v1.ToVec3().x;
482                 cm_height = body->v2.ToVec3().z - body->v1.ToVec3().z;
483                 cm_width = body->v2.ToVec3().y - body->v1.ToVec3().y;
484                 cm_origin_x = body->origin.ToVec3().x;
485                 cm_origin_y = body->origin.ToVec3().y;
486                 cm_origin_z = body->origin.ToVec3().z;
487                 s1 = SetSafeComboBoxSelection( &cm_originBoneCenterJoint1, body->origin.joint1.c_str(), -1 );
488                 s2 = SetSafeComboBoxSelection( &cm_originBoneCenterJoint2, body->origin.joint2.c_str(), s1 );
489                 s1 = SetSafeComboBoxSelection( &cm_originJoint, body->origin.joint1.c_str(), -1 );
490                 cm_angles_pitch = body->angles.pitch;
491                 cm_angles_yaw = body->angles.yaw;
492                 cm_angles_roll = body->angles.roll;
493                 if ( body->origin.type == idAFVector::VEC_BONECENTER ) {
494                         i = IDC_RADIO_ORIGIN_BONECENTER;
495                 }
496                 else if ( body->origin.type == idAFVector::VEC_JOINT ) {
497                         i = IDC_RADIO_ORIGIN_JOINT;
498                 }
499                 else {
500                         i = IDC_RADIO_ORIGIN_COORDINATES;
501                 }
502                 CheckRadioButton( IDC_RADIO_ORIGIN_COORDINATES, IDC_RADIO_ORIGIN_JOINT, i );
503         }
504         cm_numSides = body->numSides;
505         cm_density = body->density;
506         if ( body->inertiaScale == mat3_identity ) {
507                 cm_inertiaScale.SetWindowText( "none" );
508         }
509         else {
510                 cm_inertiaScale.SetWindowText( body->inertiaScale.ToString( 1 ) );
511         }
512
513         // load collision detection settings from the current idDeclAF_Body
514         m_selfCollision = body->selfCollision;
515         idDeclAF::ContentsToString( body->contents, str );
516         m_editContents.SetWindowText( str );
517         idDeclAF::ContentsToString( body->clipMask, str );
518         m_editClipMask.SetWindowText( str );
519
520         // load friction settings from the current idDeclAF_Body
521         m_linearFriction = body->linearFriction;
522         m_angularFriction = body->angularFriction;
523         m_contactFriction = body->contactFriction;
524
525         // friction direction and contact motor direction
526         if ( body->frictionDirection.ToVec3() != vec3_origin ) {
527                 idFile_Memory file( "frictionDirection" );
528                 file.WriteFloatString( "%f %f %f", body->frictionDirection.ToVec3().x, body->frictionDirection.ToVec3().y, body->frictionDirection.ToVec3().z );
529                 m_frictionDirection.SetWindowText( file.GetDataPtr() );
530         } else {
531                 m_frictionDirection.SetWindowText( "" );
532         }
533         if ( body->contactMotorDirection.ToVec3() != vec3_origin ) {
534                 idFile_Memory file( "contactMotorDirection" );
535                 file.WriteFloatString( "%f %f %f", body->contactMotorDirection.ToVec3().x, body->contactMotorDirection.ToVec3().y, body->contactMotorDirection.ToVec3().z );
536                 m_contactMotorDirection.SetWindowText( file.GetDataPtr() );
537         } else {
538                 m_contactMotorDirection.SetWindowText( "" );
539         }
540
541         // load joint settings from the current idDeclAF_Body
542         InitModifiedJointList();
543         if ( body->jointMod == DECLAF_JOINTMOD_AXIS ) {
544                 i = IDC_RADIO_MODIFY_ORIENTATION;
545         } else if ( body->jointMod == DECLAF_JOINTMOD_ORIGIN ) {
546                 i = IDC_RADIO_MODIFY_POSITION;
547         } else if ( body->jointMod == DECLAF_JOINTMOD_BOTH ) {
548                 i = IDC_RADIO_MODIFY_BOTH;
549         } else {
550                 i = IDC_RADIO_MODIFY_ORIENTATION;
551         }
552         CheckRadioButton( IDC_RADIO_MODIFY_ORIENTATION, IDC_RADIO_MODIFY_BOTH, i );
553         m_editContainedJoints.SetWindowText( body->containedJoints.c_str() );
554
555         // update displayed values
556         UpdateData( FALSE );
557
558         InitCollisionModelType();       // CComboBox::SetCurSel doesn't call this
559
560         if ( GetStyle() & WS_VISIBLE ) {
561                 // highlight the current body ingame
562                 cvarSystem->SetCVarString( "af_highlightBody", name );
563         }
564 }
565
566 /*
567 ================
568 DialogAFBody::SaveBody
569 ================
570 */
571 void DialogAFBody::SaveBody( void ) {
572         int s1, s2;
573         CString str;
574
575         if ( !file || !body ) {
576                 return;
577         }
578         UpdateData( TRUE );
579
580         // save the collision model to the current idDeclAF_Body
581         cm_comboType.GetLBText( cm_comboType.GetCurSel(), str );
582         body->modelType = StringToModelType( str );
583         if ( body->modelType == TRM_BONE ) {
584                 body->origin.type = idAFVector::VEC_BONECENTER;
585                 s1 = GetSafeComboBoxSelection( &cm_comboBoneJoint1, str, -1 );
586                 body->v1.type = idAFVector::VEC_JOINT;
587                 body->v1.joint1 = str;
588                 body->origin.joint1 = str;
589                 s2 = GetSafeComboBoxSelection( &cm_comboBoneJoint2, str, s1 );
590                 body->v2.type = idAFVector::VEC_JOINT;
591                 body->v2.joint1 = str;
592                 body->origin.joint2 = str;
593                 body->width = cm_width;
594                 body->angles.Zero();
595         } else {
596                 body->v1.type = idAFVector::VEC_COORDS;
597                 body->v1.ToVec3().x = -0.5f * cm_length;
598                 body->v1.ToVec3().y = -0.5f * cm_width;
599                 body->v1.ToVec3().z = -0.5f * cm_height;
600                 body->v2.type = idAFVector::VEC_COORDS;
601                 body->v2.ToVec3().x = 0.5f * cm_length;
602                 body->v2.ToVec3().y = 0.5f * cm_width;
603                 body->v2.ToVec3().z = 0.5f * cm_height;
604                 body->origin.ToVec3().x = cm_origin_x;
605                 body->origin.ToVec3().y = cm_origin_y;
606                 body->origin.ToVec3().z = cm_origin_z;
607                 body->angles.pitch = cm_angles_pitch;
608                 body->angles.yaw = cm_angles_yaw;
609                 body->angles.roll = cm_angles_roll;
610                 if ( body->origin.type == idAFVector::VEC_JOINT ) {
611                         s1 = GetSafeComboBoxSelection( &cm_originJoint, str, -1 );
612                         body->origin.joint1 = str;
613                 }
614                 else {
615                         s1 = GetSafeComboBoxSelection( &cm_originBoneCenterJoint1, str, -1 );
616                         body->origin.joint1 = str;
617                 }
618                 s2 = GetSafeComboBoxSelection( &cm_originBoneCenterJoint2, str, s1 );
619                 body->origin.joint2 = str;
620         }
621         body->numSides = cm_numSides;
622         body->density = cm_density;
623         cm_inertiaScale.GetWindowText( str );
624         if ( idStr::Icmp( str, "none" ) == 0 ) {
625                 body->inertiaScale.Identity();
626         } else {
627                 idLexer src( str, str.GetLength(), "inertiaScale" );
628                 src.SetFlags( LEXFL_NOERRORS | LEXFL_NOWARNINGS );
629                 for ( int i = 0; i < 3; i++ ) {
630                         for ( int j = 0; j < 3; j++ ) {
631                                 body->inertiaScale[i][j] = src.ParseFloat();
632                         }
633                 }
634         }
635
636         // save the collision detection settings to the current idDeclAF_Body
637         body->selfCollision = ( m_selfCollision != FALSE );
638         m_editContents.GetWindowText( str );
639         body->contents = idDeclAF::ContentsFromString( str );
640         m_editClipMask.GetWindowText( str );
641         body->clipMask = idDeclAF::ContentsFromString( str );
642
643         // save friction settings to the current idDeclAF_Body
644         body->linearFriction = m_linearFriction;
645         body->angularFriction = m_angularFriction;
646         body->contactFriction = m_contactFriction;
647
648         // friction direction and contact motor direction
649         m_frictionDirection.GetWindowText( str );
650         if ( str.GetLength() != 0 ) {
651                 body->frictionDirection.ToVec3().Zero();
652                 sscanf( str, "%f %f %f", &body->frictionDirection.ToVec3().x, &body->frictionDirection.ToVec3().y, &body->frictionDirection.ToVec3().z );
653         }
654         m_contactMotorDirection.GetWindowText( str );
655         if ( str.GetLength() != 0 ) {
656                 body->contactMotorDirection.ToVec3().Zero();
657                 sscanf( str, "%f %f %f", &body->contactMotorDirection.ToVec3().x, &body->contactMotorDirection.ToVec3().y, &body->contactMotorDirection.ToVec3().z );
658         }
659
660         // save joint settings to the current idDeclAF_Body
661         GetSafeComboBoxSelection( &m_comboModifiedJoint, str, -1 );
662         body->jointName = str;
663         m_editContainedJoints.GetWindowText( str );
664         body->containedJoints = str;
665
666         AFDialogSetFileModified();
667 }
668
669 /*
670 ================
671 DialogAFBody::UpdateFile
672 ================
673 */
674 void DialogAFBody::UpdateFile( void ) {
675         SaveBody();
676         if ( file ) {
677                 gameEdit->AF_UpdateEntities( file->GetName() );
678         }
679 }
680
681 /*
682 ================
683 DialogAFBody::OnInitDialog
684 ================
685 */
686 BOOL DialogAFBody::OnInitDialog()  {
687
688         CDialog::OnInitDialog();
689
690         // initialize the collision model types
691         cm_comboType.ResetContent();
692         for ( int i = 0; modelTypes[i].name; i++ ) {
693                 if ( modelTypes[i].type == TRM_INVALID || modelTypes[i].type == TRM_CUSTOM ) {
694                         continue;
695                 }
696                 cm_comboType.AddString( modelTypes[i].name );
697         }
698
699         InitNewRenameDeleteButtons();
700
701         return TRUE;  // return TRUE unless you set the focus to a control
702                       // EXCEPTION: OCX Property Pages should return FALSE
703 }
704
705 /*
706 ================
707 DialogAFBody::OnToolHitTest
708 ================
709 */
710 int DialogAFBody::OnToolHitTest( CPoint point, TOOLINFO* pTI ) const {
711         CDialog::OnToolHitTest( point, pTI );
712         return DefaultOnToolHitTest( toolTips, this, point, pTI );
713 }
714
715 BEGIN_MESSAGE_MAP(DialogAFBody, CDialog)
716         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
717         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
718         ON_WM_SHOWWINDOW()
719         ON_CBN_SELCHANGE(IDC_COMBO_BODIES, OnCbnSelchangeComboBodies)
720         ON_BN_CLICKED(IDC_BUTTON_NEWBODY, OnBnClickedButtonNewbody)
721         ON_BN_CLICKED(IDC_BUTTON_RENAMEBODY, OnBnClickedButtonRenamebody)
722         ON_BN_CLICKED(IDC_BUTTON_DELETEBODY, OnBnClickedButtonDeletebody)
723         ON_CBN_SELCHANGE(IDC_COMBO_CM_TYPE, OnCbnSelchangeComboCmType)
724         ON_EN_CHANGE(IDC_EDIT_CM_LENGTH, OnEnChangeEditCmLength)
725         ON_EN_CHANGE(IDC_EDIT_CM_HEIGHT, OnEnChangeEditCmHeight)
726         ON_EN_CHANGE(IDC_EDIT_CM_WIDTH, OnEnChangeEditCmWidth)
727         ON_EN_CHANGE(IDC_EDIT_CM_NUMSIDES, OnEnChangeEditCmNumsides)
728         ON_CBN_SELCHANGE(IDC_COMBO_BONE_JOINT1, OnCbnSelchangeComboBoneJoint1)
729         ON_CBN_SELCHANGE(IDC_COMBO_BONE_JOINT2, OnCbnSelchangeComboBoneJoint2)
730         ON_EN_CHANGE(IDC_EDIT_CM_DENSITY, OnEnChangeEditCmDensity)
731         ON_EN_CHANGE(IDC_EDIT_CM_INERTIASCALE, OnEnChangeEditCmInertiascale)
732         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_CM_LENGTH, OnDeltaposSpinCmLength)
733         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_CM_HEIGHT, OnDeltaposSpinCmHeight)
734         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_CM_WIDTH, OnDeltaposSpinCmWidth)
735         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_CM_NUMSIDES, OnDeltaposSpinCmNumsides)
736         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_CM_DENSITY, OnDeltaposSpinCmDensity)
737         ON_BN_CLICKED(IDC_RADIO_ORIGIN_COORDINATES, OnBnClickedRadioOriginCoordinates)
738         ON_BN_CLICKED(IDC_RADIO_ORIGIN_BONECENTER, OnBnClickedRadioOriginBonecenter)
739         ON_BN_CLICKED(IDC_RADIO_ORIGIN_JOINT, OnBnClickedRadioOriginJoint)
740         ON_EN_CHANGE(IDC_EDIT_AF_VECTOR_X, OnEnChangeEditAfVectorX)
741         ON_EN_CHANGE(IDC_EDIT_AF_VECTOR_Y, OnEnChangeEditAfVectorY)
742         ON_EN_CHANGE(IDC_EDIT_AF_VECTOR_Z, OnEnChangeEditAfVectorZ)
743         ON_CBN_SELCHANGE(IDC_COMBO_ORIGIN_BONECENTER_JOINT1, OnOnCbnSelchangeComboOriginBoneCenterJoint1)
744         ON_CBN_SELCHANGE(IDC_COMBO_ORIGIN_BONECENTER_JOINT2, OnOnCbnSelchangeComboOriginBoneCenterJoint2)
745         ON_CBN_SELCHANGE(IDC_COMBO_ORIGIN_JOINT, OnOnCbnSelchangeComboOriginJoint)
746         ON_EN_CHANGE(IDC_EDIT_ANGLES_PITCH, OnEnChangeEditAnglesPitch)
747         ON_EN_CHANGE(IDC_EDIT_ANGLES_YAW, OnEnChangeEditAnglesYaw)
748         ON_EN_CHANGE(IDC_EDIT_ANGLES_ROLL, OnEnChangeEditAnglesRoll)
749         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_AF_VECTOR_X, OnDeltaposSpinAfVectorX)
750         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_AF_VECTOR_Y, OnDeltaposSpinAfVectorY)
751         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_AF_VECTOR_Z, OnDeltaposSpinAfVectorZ)
752         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ANGLES_PITCH, OnDeltaposSpinAnglesPitch)
753         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ANGLES_YAW, OnDeltaposSpinAnglesYaw)
754         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ANGLES_ROLL, OnDeltaposSpinAnglesRoll)
755         ON_BN_CLICKED(IDC_CHECK_SELFCOLLISION, OnBnClickedCheckSelfcollision)
756         ON_EN_CHANGE(IDC_EDIT_CONTENTS, OnEnChangeEditContents)
757         ON_EN_CHANGE(IDC_EDIT_CLIPMASK, OnEnChangeEditClipmask)
758         ON_EN_CHANGE(IDC_EDIT_LINEARFRICTION, OnEnChangeEditLinearfriction)
759         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_LINEARFRICTION, OnDeltaposSpinLinearfriction)
760         ON_EN_CHANGE(IDC_EDIT_ANGULARFRICTION, OnEnChangeEditAngularfriction)
761         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_ANGULARFRICTION, OnDeltaposSpinAngularfriction)
762         ON_EN_CHANGE(IDC_EDIT_CONTACTFRICTION, OnEnChangeEditContactfriction)
763         ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_CONTACTFRICTION, OnDeltaposSpinContactfriction)
764         ON_EN_CHANGE(IDC_EDIT_FRICTIONDIRECTION, OnEnChangeEditFrictionDirection)
765         ON_EN_CHANGE(IDC_EDIT_CONTACTMOTORDIRECTION, OnEnChangeEditContactMotorDirection)
766         ON_CBN_SELCHANGE(IDC_COMBO_MODIFIEDJOINT, OnCbnSelchangeComboModifiedjoint)
767         ON_BN_CLICKED(IDC_RADIO_MODIFY_ORIENTATION, OnBnClickedRadioModifyOrientation)
768         ON_BN_CLICKED(IDC_RADIO_MODIFY_POSITION, OnBnClickedRadioModifyPosition)
769         ON_BN_CLICKED(IDC_RADIO_MODIFY_BOTH, OnBnClickedRadioModifyBoth)
770         ON_EN_CHANGE(IDC_EDIT_CONTAINEDJOINTS, OnEnChangeEditContainedjoints)
771 END_MESSAGE_MAP()
772
773
774 // DialogAFBody message handlers
775
776 BOOL DialogAFBody::OnToolTipNotify( UINT id, NMHDR *pNMHDR, LRESULT *pResult ) {
777         return DefaultOnToolTipNotify( toolTips, id, pNMHDR, pResult );
778 }
779
780 void DialogAFBody::OnShowWindow( BOOL bShow, UINT nStatus ) {
781         if ( bShow && body ) {
782                 cvarSystem->SetCVarString( "af_highlightBody", body->name.c_str() );
783         } else {
784                 cvarSystem->SetCVarString( "af_highlightBody", "" );
785         }
786         CDialog::OnShowWindow( bShow, nStatus );
787 }
788
789 void DialogAFBody::OnCbnSelchangeComboBodies() {
790         CString str;
791
792         GetSafeComboBoxSelection( &bodyList, str, -1 );
793         LoadBody( str );
794 }
795
796 void DialogAFBody::OnBnClickedButtonNewbody() {
797         DialogAFName nameDlg;
798         CString str;
799         INT_PTR res;
800
801         // the names 'origin' and 'world' are reserved for constraints bound to the world
802         bodyList.AddString( "origin" );
803         bodyList.AddString( "world" );
804
805         nameDlg.SetComboBox( &bodyList );
806         res = nameDlg.DoModal();
807
808         bodyList.DeleteString( bodyList.FindString( -1, "origin" ) );
809         bodyList.DeleteString( bodyList.FindString( -1, "world" ) );
810
811         if ( res == IDOK ) {
812                 nameDlg.GetName( str );
813                 // create new body
814                 file->NewBody( str );
815                 bodyList.SetCurSel( bodyList.AddString( str ) );
816                 LoadBody( str );
817                 constraintDlg->LoadFile( file );
818                 gameEdit->AF_UpdateEntities( file->GetName() );
819                 AFDialogSetFileModified();
820         }
821         InitNewRenameDeleteButtons();
822 }
823
824 void DialogAFBody::OnBnClickedButtonRenamebody() {
825         int i;
826         CString name, newName;
827         DialogAFName nameDlg;
828
829         if ( !file || !body ) {
830                 return;
831         }
832
833         i = bodyList.GetCurSel();
834         if ( i != CB_ERR ) {
835                 bodyList.GetLBText( i, name );
836                 nameDlg.SetName( name );
837                 nameDlg.SetComboBox( &bodyList );
838                 if ( nameDlg.DoModal() == IDOK ) {
839                         nameDlg.GetName( newName );
840                         // rename body
841                         file->RenameBody( name, newName );
842                         bodyList.DeleteString( i );
843                         bodyList.SetCurSel( bodyList.AddString( newName ) );
844                         LoadBody( newName );
845                         constraintDlg->LoadFile( file );
846                         gameEdit->AF_UpdateEntities( file->GetName() );
847                         AFDialogSetFileModified();
848                 }
849         }
850 }
851
852 void DialogAFBody::OnBnClickedButtonDeletebody() {
853         int i;
854         CString str;
855
856         if ( !file || !body ) {
857                 return;
858         }
859
860         i = bodyList.GetCurSel();
861         if ( i != CB_ERR ) {
862                 if ( MessageBox( "Are you sure you want to delete this body and all attached constraints ?", "Delete Body", MB_YESNO | MB_ICONQUESTION ) == IDYES ) {
863                         bodyList.GetLBText( i, str );
864                         // delete currently selected body
865                         file->DeleteBody( str );
866                         bodyList.DeleteString( i );
867                         body = NULL;
868                         OnCbnSelchangeComboBodies();
869                         constraintDlg->LoadFile( file );
870                         gameEdit->AF_UpdateEntities( file->GetName() );
871                         AFDialogSetFileModified();
872                 }
873         }
874         InitNewRenameDeleteButtons();
875 }
876
877 void DialogAFBody::OnCbnSelchangeComboCmType() {
878         InitCollisionModelType();
879         UpdateFile();
880 }
881
882 void DialogAFBody::ValidateCollisionModelLength( bool update ) {
883         if ( cm_length < 1.0f ) {
884                 cm_length = 1.0f;
885                 update = true;
886         }
887         if ( update ) {
888                 UpdateData( FALSE );
889         }
890 }
891
892 void DialogAFBody::OnEnChangeEditCmLength() {
893         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_CM_LENGTH ) ) ) {
894                 ValidateCollisionModelLength( false );
895                 UpdateFile();
896         }
897         else {
898                 cm_length = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_CM_LENGTH ), false );
899         }
900 }
901
902 void DialogAFBody::OnDeltaposSpinCmLength(NMHDR *pNMHDR, LRESULT *pResult) {
903         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
904         if ( pNMUpDown->iDelta < 0 ) {
905                 cm_length += 1.0f;
906         }
907         else if ( cm_length >= 2.0f ) {
908                 cm_length -= 1.0f;
909         }
910         ValidateCollisionModelLength( true );
911         UpdateFile();
912         *pResult = 0;
913 }
914
915 void DialogAFBody::ValidateCollisionModelHeight( bool update ) {
916         if ( cm_height < 1.0f ) {
917                 cm_height = 1.0f;
918                 update = true;
919         }
920         if ( update ) {
921                 UpdateData( FALSE );
922         }
923 }
924
925 void DialogAFBody::OnEnChangeEditCmHeight() {
926         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_CM_HEIGHT ) ) ) {
927                 ValidateCollisionModelHeight( false );
928                 UpdateFile();
929         }
930         else {
931                 cm_height = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_CM_HEIGHT ), false );
932         }
933 }
934
935 void DialogAFBody::OnDeltaposSpinCmHeight(NMHDR *pNMHDR, LRESULT *pResult) {
936         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
937         if ( pNMUpDown->iDelta < 0 ) {
938                 cm_height += 1.0f;
939         }
940         else if ( cm_height >= 2.0f ) {
941                 cm_height -= 1.0f;
942         }
943         ValidateCollisionModelHeight( true );
944         UpdateFile();
945         *pResult = 0;
946 }
947
948 void DialogAFBody::ValidateCollisionModelWidth( bool update ) {
949         if ( cm_width < 1.0f ) {
950                 cm_width = 1.0f;
951                 update = true;
952         }
953         if ( update ) {
954                 UpdateData( FALSE );
955         }
956 }
957
958 void DialogAFBody::OnEnChangeEditCmWidth() {
959         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_CM_WIDTH ) ) ) {
960                 ValidateCollisionModelWidth( false );
961                 UpdateFile();
962         }
963         else {
964                 cm_width = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_CM_WIDTH ), false );
965         }
966 }
967
968 void DialogAFBody::OnDeltaposSpinCmWidth(NMHDR *pNMHDR, LRESULT *pResult) {
969         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
970         if ( pNMUpDown->iDelta < 0 ) {
971                 cm_width += 1.0f;
972         }
973         else if ( cm_width >= 2.0f ) {
974                 cm_width -= 1.0f;
975         }
976         ValidateCollisionModelWidth( true );
977         UpdateFile();
978         *pResult = 0;
979 }
980
981 void DialogAFBody::ValidateCollisionModelNumSides( bool update ) {
982         cm_numSides = (int) cm_numSides;
983         if ( cm_numSides < 3 ) {
984                 cm_numSides = 3;
985                 update = true;
986         }
987         else if ( cm_numSides > 10 ) {
988                 cm_numSides = 10;
989                 update = true;
990         }
991         if ( update ) {
992                 UpdateData( FALSE );
993         }
994 }
995
996 void DialogAFBody::OnEnChangeEditCmNumsides() {
997         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_CM_NUMSIDES ) ) ) {
998                 ValidateCollisionModelNumSides( false );
999                 UpdateFile();
1000         }
1001 }
1002
1003 void DialogAFBody::OnDeltaposSpinCmNumsides(NMHDR *pNMHDR, LRESULT *pResult) {
1004         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1005         if ( pNMUpDown->iDelta < 0 ) {
1006                 cm_numSides += 1;
1007         }
1008         else if ( cm_numSides > 3 ) {
1009                 cm_numSides -= 1;
1010         }
1011         ValidateCollisionModelNumSides( true );
1012         UpdateFile();
1013         *pResult = 0;
1014 }
1015
1016 void DialogAFBody::OnCbnSelchangeComboBoneJoint1() {
1017         CString str;
1018         GetSafeComboBoxSelection( &cm_comboBoneJoint1, str, -1 );
1019         UnsetSafeComboBoxSelection( &cm_comboBoneJoint2, str );
1020         UpdateFile();
1021 }
1022
1023 void DialogAFBody::OnCbnSelchangeComboBoneJoint2() {
1024         CString str;
1025         GetSafeComboBoxSelection( &cm_comboBoneJoint2, str, -1 );
1026         UnsetSafeComboBoxSelection( &cm_comboBoneJoint1, str );
1027         UpdateFile();
1028 }
1029
1030 void DialogAFBody::ValidateCollisionModelDensity( bool update ) {
1031         if ( cm_density < 1e-6f ) {
1032                 cm_density = 1e-6f;
1033                 update = true;
1034         }
1035         if ( update ) {
1036                 UpdateData( FALSE );
1037         }
1038 }
1039
1040 void DialogAFBody::OnEnChangeEditCmDensity() {
1041         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_CM_DENSITY ) ) ) {
1042                 ValidateCollisionModelDensity( false );
1043                 UpdateFile();
1044         }
1045         else {
1046                 cm_density = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_CM_DENSITY ), false );
1047         }
1048 }
1049
1050 void DialogAFBody::OnDeltaposSpinCmDensity(NMHDR *pNMHDR, LRESULT *pResult) {
1051         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1052         cm_density = EditSpinFloat( (CEdit *)GetDlgItem( IDC_EDIT_CM_DENSITY ), pNMUpDown->iDelta < 0 );
1053         ValidateCollisionModelDensity( false );
1054         UpdateFile();
1055         *pResult = 0;
1056 }
1057
1058 void DialogAFBody::OnEnChangeEditCmInertiascale() {
1059         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_CM_INERTIASCALE ) ) ) {
1060                 UpdateFile();
1061         }
1062 }
1063
1064 void DialogAFBody::OnBnClickedRadioOriginCoordinates() {
1065         if ( IsDlgButtonChecked( IDC_RADIO_ORIGIN_COORDINATES ) ) {
1066                 if ( body ) {
1067                         body->origin.type = idAFVector::VEC_COORDS;
1068                         UpdateFile();
1069                 }
1070         }
1071 }
1072
1073 void DialogAFBody::OnBnClickedRadioOriginBonecenter() {
1074         if ( IsDlgButtonChecked( IDC_RADIO_ORIGIN_BONECENTER ) ) {
1075                 if ( body ) {
1076                         body->origin.type = idAFVector::VEC_BONECENTER;
1077                         UpdateFile();
1078                 }
1079         }
1080 }
1081
1082 void DialogAFBody::OnBnClickedRadioOriginJoint() {
1083         if ( IsDlgButtonChecked( IDC_RADIO_ORIGIN_JOINT ) ) {
1084                 if ( body ) {
1085                         body->origin.type = idAFVector::VEC_JOINT;
1086                         UpdateFile();
1087                 }
1088         }
1089 }
1090
1091 void DialogAFBody::OnEnChangeEditAfVectorX() {
1092         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_AF_VECTOR_X ) ) ) {
1093                 UpdateFile();
1094         }
1095         else {
1096                 cm_origin_x = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_AF_VECTOR_X ) );
1097         }
1098 }
1099
1100 void DialogAFBody::OnDeltaposSpinAfVectorX(NMHDR *pNMHDR, LRESULT *pResult) {
1101         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1102         if ( pNMUpDown->iDelta < 0 ) {
1103                 cm_origin_x += 1;
1104         }
1105         else {
1106                 cm_origin_x -= 1;
1107         }
1108         UpdateData( FALSE );
1109         UpdateFile();
1110         *pResult = 0;
1111 }
1112
1113 void DialogAFBody::OnEnChangeEditAfVectorY() {
1114         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_AF_VECTOR_Y ) ) ) {
1115                 UpdateFile();
1116         }
1117         else {
1118                 cm_origin_y = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_AF_VECTOR_Y ) );
1119         }
1120 }
1121
1122 void DialogAFBody::OnDeltaposSpinAfVectorY(NMHDR *pNMHDR, LRESULT *pResult) {
1123         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1124         if ( pNMUpDown->iDelta < 0 ) {
1125                 cm_origin_y += 1;
1126         }
1127         else {
1128                 cm_origin_y -= 1;
1129         }
1130         UpdateData( FALSE );
1131         UpdateFile();
1132         *pResult = 0;
1133 }
1134
1135 void DialogAFBody::OnEnChangeEditAfVectorZ() {
1136         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_AF_VECTOR_Z ) ) ) {
1137                 UpdateFile();
1138         }
1139         else {
1140                 cm_origin_z = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_AF_VECTOR_Z ) );
1141         }
1142 }
1143
1144 void DialogAFBody::OnDeltaposSpinAfVectorZ(NMHDR *pNMHDR, LRESULT *pResult) {
1145         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1146         if ( pNMUpDown->iDelta < 0 ) {
1147                 cm_origin_z += 1;
1148         }
1149         else {
1150                 cm_origin_z -= 1;
1151         }
1152         UpdateData( FALSE );
1153         UpdateFile();
1154         *pResult = 0;
1155 }
1156
1157 void DialogAFBody::OnOnCbnSelchangeComboOriginBoneCenterJoint1() {
1158         CString str;
1159         GetSafeComboBoxSelection( &cm_originBoneCenterJoint1, str, -1 );
1160         UnsetSafeComboBoxSelection( &cm_originBoneCenterJoint2, str );
1161         UpdateFile();
1162 }
1163
1164 void DialogAFBody::OnOnCbnSelchangeComboOriginBoneCenterJoint2() {
1165         CString str;
1166         GetSafeComboBoxSelection( &cm_originBoneCenterJoint2, str, -1 );
1167         UnsetSafeComboBoxSelection( &cm_originBoneCenterJoint1, str );
1168         UpdateFile();
1169 }
1170
1171 void DialogAFBody::OnOnCbnSelchangeComboOriginJoint() {
1172         UpdateFile();
1173 }
1174
1175 void DialogAFBody::OnEnChangeEditAnglesPitch() {
1176         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_ANGLES_PITCH ) ) ) {
1177                 UpdateFile();
1178         }
1179         else {
1180                 cm_angles_pitch = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_ANGLES_PITCH ) );
1181         }
1182 }
1183
1184 void DialogAFBody::OnDeltaposSpinAnglesPitch(NMHDR *pNMHDR, LRESULT *pResult) {
1185         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1186         if ( pNMUpDown->iDelta < 0 ) {
1187                 cm_angles_pitch += 1;
1188         }
1189         else {
1190                 cm_angles_pitch -= 1;
1191         }
1192         cm_angles_pitch = idMath::AngleNormalize360( cm_angles_pitch );
1193         UpdateData( FALSE );
1194         UpdateFile();
1195         *pResult = 0;
1196 }
1197
1198 void DialogAFBody::OnEnChangeEditAnglesYaw() {
1199         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_ANGLES_YAW ) ) ) {
1200                 UpdateFile();
1201         }
1202         else {
1203                 cm_angles_yaw = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_ANGLES_YAW ) );
1204         }
1205 }
1206
1207 void DialogAFBody::OnDeltaposSpinAnglesYaw(NMHDR *pNMHDR, LRESULT *pResult) {
1208         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1209         if ( pNMUpDown->iDelta < 0 ) {
1210                 cm_angles_yaw += 1;
1211         }
1212         else {
1213                 cm_angles_yaw -= 1;
1214         }
1215         cm_angles_yaw = idMath::AngleNormalize360( cm_angles_yaw );
1216         UpdateData( FALSE );
1217         UpdateFile();
1218         *pResult = 0;
1219 }
1220
1221 void DialogAFBody::OnEnChangeEditAnglesRoll() {
1222         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_ANGLES_ROLL ) ) ) {
1223                 UpdateFile();
1224         }
1225         else {
1226                 cm_angles_roll = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_ANGLES_ROLL ) );
1227         }
1228 }
1229
1230 void DialogAFBody::OnDeltaposSpinAnglesRoll(NMHDR *pNMHDR, LRESULT *pResult) {
1231         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1232         if ( pNMUpDown->iDelta < 0 ) {
1233                 cm_angles_roll += 1;
1234         }
1235         else {
1236                 cm_angles_roll -= 1;
1237         }
1238         cm_angles_roll = idMath::AngleNormalize360( cm_angles_roll );
1239         UpdateData( FALSE );
1240         UpdateFile();
1241         *pResult = 0;
1242 }
1243
1244 void DialogAFBody::OnBnClickedCheckSelfcollision() {
1245         UpdateFile();
1246 }
1247
1248 void DialogAFBody::OnEnChangeEditContents() {
1249         if ( EditControlEnterHit( &m_editContents ) ) {
1250                 UpdateFile();
1251         }
1252 }
1253
1254 void DialogAFBody::OnEnChangeEditClipmask() {
1255         if ( EditControlEnterHit( &m_editClipMask ) ) {
1256                 UpdateFile();
1257         }
1258 }
1259
1260 void DialogAFBody::OnEnChangeEditLinearfriction() {
1261         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_LINEARFRICTION ) ) ) {
1262                 UpdateFile();
1263         }
1264         else {
1265                 m_linearFriction = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_LINEARFRICTION ), false );
1266         }
1267 }
1268
1269 void DialogAFBody::OnDeltaposSpinLinearfriction(NMHDR *pNMHDR, LRESULT *pResult) {
1270         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1271         m_linearFriction = EditSpinFloat( (CEdit *)GetDlgItem( IDC_EDIT_LINEARFRICTION ), pNMUpDown->iDelta < 0 );
1272         UpdateFile();
1273         *pResult = 0;
1274 }
1275
1276 void DialogAFBody::OnEnChangeEditAngularfriction() {
1277         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_ANGULARFRICTION ) ) ) {
1278                 UpdateFile();
1279         }
1280         else {
1281                 m_angularFriction = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_ANGULARFRICTION ), false );
1282         }
1283 }
1284
1285 void DialogAFBody::OnDeltaposSpinAngularfriction(NMHDR *pNMHDR, LRESULT *pResult) {
1286         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1287         m_angularFriction = EditSpinFloat( (CEdit *)GetDlgItem( IDC_EDIT_ANGULARFRICTION ), pNMUpDown->iDelta < 0 );
1288         UpdateFile();
1289         *pResult = 0;
1290 }
1291
1292 void DialogAFBody::OnEnChangeEditContactfriction() {
1293         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_CONTACTFRICTION ) ) ) {
1294                 UpdateFile();
1295         }
1296         else {
1297                 m_contactFriction = EditVerifyFloat( (CEdit *) GetDlgItem( IDC_EDIT_CONTACTFRICTION ), false );
1298         }
1299 }
1300
1301 void DialogAFBody::OnDeltaposSpinContactfriction(NMHDR *pNMHDR, LRESULT *pResult) {
1302         LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
1303         m_contactFriction = EditSpinFloat( (CEdit *)GetDlgItem( IDC_EDIT_CONTACTFRICTION ), pNMUpDown->iDelta < 0 );
1304         UpdateFile();
1305         *pResult = 0;
1306 }
1307
1308 void DialogAFBody::OnEnChangeEditFrictionDirection() {
1309         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_FRICTIONDIRECTION ) ) ) {
1310                 UpdateFile();
1311         }
1312 }
1313
1314 void DialogAFBody::OnEnChangeEditContactMotorDirection() {
1315         if ( EditControlEnterHit( (CEdit *) GetDlgItem( IDC_EDIT_CONTACTMOTORDIRECTION ) ) ) {
1316                 UpdateFile();
1317         }
1318 }
1319
1320 void DialogAFBody::OnCbnSelchangeComboModifiedjoint() {
1321         UpdateFile();
1322 }
1323
1324 void DialogAFBody::OnBnClickedRadioModifyOrientation() {
1325         if ( IsDlgButtonChecked( IDC_RADIO_MODIFY_ORIENTATION ) ) {
1326                 if ( body ) {
1327                         body->jointMod = DECLAF_JOINTMOD_AXIS;
1328                         UpdateFile();
1329                 }
1330         }
1331 }
1332
1333 void DialogAFBody::OnBnClickedRadioModifyPosition() {
1334         if ( IsDlgButtonChecked( IDC_RADIO_MODIFY_POSITION ) ) {
1335                 if ( body ) {
1336                         body->jointMod = DECLAF_JOINTMOD_ORIGIN;
1337                         UpdateFile();
1338                 }
1339         }
1340 }
1341
1342 void DialogAFBody::OnBnClickedRadioModifyBoth() {
1343         if ( IsDlgButtonChecked( IDC_RADIO_MODIFY_BOTH ) ) {
1344                 if ( body ) {
1345                         body->jointMod = DECLAF_JOINTMOD_BOTH;
1346                         UpdateFile();
1347                 }
1348         }
1349 }
1350
1351 void DialogAFBody::OnEnChangeEditContainedjoints() {
1352         if ( EditControlEnterHit( &m_editContainedJoints ) ) {
1353                 UpdateFile();
1354         }
1355 }