Torque3D Documentation / _generateds / VPathEditor.cpp

VPathEditor.cpp

Engine/source/Verve/VPath/VPathEditor.cpp

More...

Public Functions

DefineEngineMethod(VPathEditor , clearSelection , void , () , "( void )" )
DefineEngineMethod(VPathEditor , deleteSelection , void , () , "( void )" )
DefineEngineMethod(VPathEditor , getSelectedNode , S32 , () , "( void )" )
DefineEngineMethod(VPathEditor , getSelectedPath , S32 , () , "( void )" )
DefineEngineMethod(VPathEditor , isValidSelection , bool , () , "( void )" )
DefineEngineMethod(VPathEditor , setNodeOrientationMode , void , (String orientationType, Point3F lookAtPoint) , ("", Point3F::One) , "( string pOrientationType, [vector pPoint] )" )
DefineEngineMethod(VPathEditor , setNodePosition , void , (Point3F position) , (Point3F::Zero) , "( pPosition )" )
DefineEngineMethod(VPathEditor , setNodeRotation , void , (AngAxisF aa) , (AngAxisF(Point3F(0, 0, 1), 0)) , "( pRotation )" )
DefineEngineMethod(VPathEditor , setNodeWeight , void , (F32 weight) , (1) , "( pWeight )" )
DefineEngineMethod(VPathEditor , setSelection , void , (SceneObject *sceneObject, S32 nodeIndex) , (nullAsType< SceneObject * >(), -1) , "( pObject, [pNodeIndex] )" )

Detailed Description

Public Variables

 EndImplementEnumType 
ColorI gNodeLookAtPointColor (255, 127, 39)
ColorI gPathColor (255, 255, 255)
ColorI gPathColorSel (0, 255, 255)
F32 gProjectDistance 
F32 gSelectionDistance 

Public Functions

DefineEngineMethod(VPathEditor , clearSelection , void , () , "( void )" )

DefineEngineMethod(VPathEditor , deleteSelection , void , () , "( void )" )

DefineEngineMethod(VPathEditor , getSelectedNode , S32 , () , "( void )" )

DefineEngineMethod(VPathEditor , getSelectedPath , S32 , () , "( void )" )

DefineEngineMethod(VPathEditor , isValidSelection , bool , () , "( void )" )

DefineEngineMethod(VPathEditor , setNodeOrientationMode , void , (String orientationType, Point3F lookAtPoint) , ("", Point3F::One) , "( string pOrientationType, [vector pPoint] )" )

DefineEngineMethod(VPathEditor , setNodePosition , void , (Point3F position) , (Point3F::Zero) , "( pPosition )" )

DefineEngineMethod(VPathEditor , setNodeRotation , void , (AngAxisF aa) , (AngAxisF(Point3F(0, 0, 1), 0)) , "( pRotation )" )

DefineEngineMethod(VPathEditor , setNodeWeight , void , (F32 weight) , (1) , "( pWeight )" )

DefineEngineMethod(VPathEditor , setSelection , void , (SceneObject *sceneObject, S32 nodeIndex) , (nullAsType< SceneObject * >(), -1) , "( pObject, [pNodeIndex] )" )

IMPLEMENT_CONOBJECT(VPathEditor )

ImplementEnumType(VPathEditorMode , "" )

   1
   2//-----------------------------------------------------------------------------
   3// Verve
   4// Copyright (C) 2014 - Violent Tulip
   5//
   6// Permission is hereby granted, free of charge, to any person obtaining a copy
   7// of this software and associated documentation files (the "Software"), to
   8// deal in the Software without restriction, including without limitation the
   9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  10// sell copies of the Software, and to permit persons to whom the Software is
  11// furnished to do so, subject to the following conditions:
  12//
  13// The above copyright notice and this permission notice shall be included in
  14// all copies or substantial portions of the Software.
  15//
  16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  22// IN THE SOFTWARE.
  23//-----------------------------------------------------------------------------
  24#include "VPathEditor.h"
  25
  26#include "console/consoleTypes.h"
  27#include "gfx/gfxDrawUtil.h"
  28#include "gfx/primBuilder.h"
  29#include "gui/worldEditor/worldEditor.h"
  30#include "math/mathUtils.h"
  31#include "sim/netConnection.h"
  32
  33//-----------------------------------------------------------------------------
  34
  35static F32     gProjectDistance   = 2000.f;
  36static F32     gSelectionDistance = 2.f;
  37
  38static ColorI  gPathColor( 255, 255, 255 );
  39static ColorI  gPathColorSel( 0, 255, 255 );
  40static ColorI  gNodeLookAtPointColor( 255, 127, 39 );
  41
  42//-----------------------------------------------------------------------------
  43
  44// Implement the Edit Mode enum list.
  45ImplementEnumType( VPathEditorMode, "" )
  46    { VPathEditor::k_Gizmo,      "GIZMO"      },
  47    { VPathEditor::k_AddNode,    "ADDNODE"    },
  48    { VPathEditor::k_DeleteNode, "DELETENODE" },
  49EndImplementEnumType;
  50
  51//-----------------------------------------------------------------------------
  52IMPLEMENT_CONOBJECT( VPathEditor );
  53//-----------------------------------------------------------------------------
  54
  55VPathEditor::VPathEditor( void ) :
  56        mIsDirty( false ),
  57        mEditMode( k_Gizmo ),
  58        mEditWeight( false ),
  59        mEditWeightHandle( -1 )
  60{
  61    // Void.
  62}
  63
  64bool VPathEditor::onAdd( void )
  65{
  66    if ( !Parent::onAdd() )
  67    {
  68        return false;
  69    }
  70
  71    // Assign Gizmo Name.
  72    mGizmo->assignName( "VPathEditorGizmo" );
  73
  74    return true;
  75}
  76
  77bool VPathEditor::onWake( void )
  78{
  79    // Clear Selection.
  80    updateSelection( NULL, -1 );
  81
  82    // Return Parent Value.
  83    return Parent::onWake();
  84}
  85
  86void VPathEditor::initPersistFields( void )
  87{
  88    addField( "IsDirty",  TypeBool,           Offset( mIsDirty,  VPathEditor ) );
  89    addField( "EditMode", TYPEID<EditMode>(), Offset( mEditMode, VPathEditor ) );
  90
  91    Parent::initPersistFields();
  92}
  93
  94//-----------------------------------------------------------------------------
  95//
  96// Gui Events
  97//
  98//-----------------------------------------------------------------------------
  99
 100void VPathEditor::on3DMouseDown( const Gui3DMouseEvent &pEvent )
 101{
 102    // Using the Gizmo?
 103    if ( mEditMode != k_Gizmo )
 104    {
 105        // No, Quit Now.
 106        return;
 107    }
 108
 109    // Gizmo Event.
 110    mGizmo->on3DMouseDown( pEvent );
 111
 112    if ( isValidSelection() )
 113    {
 114        // Store Node Information.
 115        pushNodeEdit();
 116
 117        switch( mGizmoProfile->mode )
 118        {
 119            case MoveMode:
 120            case RotateMode:
 121                {
 122                    if ( mGizmo->getSelection() != Gizmo::None )
 123                    {
 124                        // Using Gizmo.
 125                        return;
 126                    }
 127
 128                } break;
 129
 130            case ScaleMode:
 131                {
 132                    if ( isEditingWeight( pEvent ) )
 133                    {
 134                        // Editing Weights.
 135                        return;
 136                    }
 137
 138                } break;
 139        }
 140    }
 141    else if ( mSelection.Path )
 142    {
 143        // Store Path Information.
 144        pushPathEdit();
 145
 146        if ( mGizmo->getSelection() != Gizmo::None )
 147        {
 148            // Using Gizmo.
 149            return;
 150        }
 151    }
 152
 153    // Update Selection.
 154    if ( !updateSelection( pEvent ) )
 155    {
 156        // Clear Selection.
 157        updateSelection( NULL, -1 );
 158    }
 159}
 160
 161void VPathEditor::on3DMouseUp( const Gui3DMouseEvent &pEvent )
 162{
 163    switch ( mEditMode )
 164    {
 165        case k_Gizmo :
 166            {
 167                // Gizmo Event.
 168                mGizmo->on3DMouseUp( pEvent );
 169
 170                // Handle History Actions.
 171                popPathEdit();
 172                popNodeEdit();
 173
 174                // Clear Editing.
 175                mEditWeight = false;
 176
 177            } break;
 178
 179        case k_AddNode :
 180            {
 181                if ( mSelection.Path != NULL )
 182                {
 183                    // Add New!
 184                    addNode( pEvent );
 185
 186                    // Dirty.
 187                    mIsDirty = true;
 188                }
 189
 190            } break;
 191
 192        case k_DeleteNode :
 193            {
 194                // Update Selection.
 195                if ( updateSelection( pEvent ) )
 196                {
 197                    if ( isValidSelection() )
 198                    {
 199                        // Delete Node.
 200                        deleteNode( mSelection.Node );
 201
 202                        // Dirty.
 203                        mIsDirty = true;
 204                    }
 205
 206                    // Clear Node Selection.
 207                    updateSelection( mSelection.Path, -1 );
 208                }
 209
 210            } break;
 211    }
 212}
 213
 214void VPathEditor::on3DMouseMove( const Gui3DMouseEvent &pEvent )
 215{
 216    // Update?
 217    if ( mEditMode != k_Gizmo || !mSelection.Path )
 218    {
 219        return;
 220    }
 221
 222    // Update Gizmo?
 223    if ( mSelection.Node == -1 || mGizmoProfile->mode != ScaleMode )
 224    {
 225        // Gizmo Event.
 226        mGizmo->on3DMouseMove( pEvent );
 227    }
 228}
 229
 230void VPathEditor::on3DMouseDragged( const Gui3DMouseEvent &pEvent )
 231{
 232    // Update?
 233    if ( mEditMode != k_Gizmo || !mSelection.Path )
 234    {
 235        return;
 236    }
 237
 238    // Update Gizmo?
 239    if ( mSelection.Node == -1 || mGizmoProfile->mode != ScaleMode )
 240    {
 241        // Gizmo Event.
 242        mGizmo->on3DMouseDragged( pEvent );
 243
 244        // Handle Gizmo?
 245        if ( mGizmo->getSelection() == Gizmo::None )
 246        {
 247            // Return.
 248            return;
 249        }
 250    }
 251
 252    // Editing the Path?
 253    if ( mSelection.Node == -1 )
 254    {
 255        switch ( mGizmoProfile->mode )
 256        {
 257            case MoveMode :
 258                {
 259                    // Fetch Node Position.
 260                    const Point3F oldPosition = mSelection.Path->getPosition();
 261                    // Determine New Position.
 262                    const Point3F newPosition = ( oldPosition + mGizmo->getOffset() );
 263
 264                    // Apply New Position.
 265                    setPathPosition( newPosition );
 266
 267                    // Dirty.
 268                    mIsDirty        = true;
 269                    mPathEdit.Dirty = true;
 270
 271                } break;
 272            /*
 273            case RotateMode :
 274                {
 275
 276                    // Rotation Delta.
 277                    MatrixF deltaRotation( EulerF( mGizmo->getDeltaRot() ) );
 278
 279                    // Fetch Current Transform.
 280                    MatrixF mat = mSelection.Path->getTransform();
 281                    mat.mul( deltaRotation );
 282
 283                    // Apply New Transform.
 284                    setPathTransform( mat );
 285
 286                    // Dirty.
 287                    mIsDirty        = true;
 288                    mPathEdit.Dirty = true;
 289
 290                } break;
 291
 292            case ScaleMode :
 293                {
 294
 295                    // Apply New Scale.
 296                    setPathScale( mGizmo->getScale() );
 297
 298                    // Dirty.
 299                    mIsDirty        = true;
 300                    mPathEdit.Dirty = true;
 301
 302                } break;
 303            */
 304        }
 305    }
 306
 307    // No, Editing a Node
 308    else
 309    {
 310        switch ( mGizmoProfile->mode )
 311        {
 312            case MoveMode :
 313                {
 314
 315                    // Fetch Node.
 316                    VPathNode *node = mSelection.Path->getNode( mSelection.Node );
 317
 318                    // Fetch Node Position.
 319                    const Point3F oldPosition = node->getLocalPosition();
 320
 321                    // Invert Transform.
 322                    MatrixF pathTransform = mSelection.Path->getTransform();
 323                    pathTransform.setPosition( Point3F::Zero );
 324                    pathTransform.inverse();
 325
 326                    Point3F deltaPosition = mGizmo->getOffset();
 327                    pathTransform.mulP( deltaPosition );
 328
 329                    // Apply New Position.
 330                    setNodePosition( mSelection.Node, ( oldPosition + deltaPosition ) );
 331
 332                } break;
 333
 334            case RotateMode :
 335                {
 336
 337                    // Fetch Node.
 338                    VPathNode *node = mSelection.Path->getNode( mSelection.Node );
 339
 340                    // Invert Transform.
 341                    MatrixF pathTransform = mSelection.Path->getTransform();
 342                    pathTransform.setPosition( Point3F::Zero );
 343                    pathTransform.inverse();
 344
 345                    // Rotation Delta.
 346                    MatrixF deltaRotation( EulerF( mGizmo->getDeltaRot() ) );
 347                    pathTransform.mul( deltaRotation );
 348
 349                    // Fetch Current Transform.
 350                    MatrixF mat = node->getWorldTransform();
 351                    mat.mul( deltaRotation );
 352
 353                    // Construct Quat.
 354                    QuatF newRotation;
 355                    newRotation.set( mat );
 356
 357                    // Apply New Rotation.
 358                    setNodeRotation( mSelection.Node, newRotation );
 359
 360                } break;
 361
 362            case ScaleMode :
 363                {
 364
 365                    if ( isEditingWeight() )
 366                    {
 367                        // Edit Weight.
 368                        updateWeight( pEvent );
 369                    }
 370
 371                } break;
 372        }
 373    }
 374}
 375
 376//-----------------------------------------------------------------------------
 377//
 378// Reference Methods.
 379//
 380//-----------------------------------------------------------------------------
 381
 382VPath *VPathEditor::getClientPath( VPath *pPath )
 383{
 384    if ( !pPath )
 385    {
 386        return NULL;
 387    }
 388
 389    NetConnection *toServer = NetConnection::getConnectionToServer();
 390    NetConnection *toClient = NetConnection::getLocalClientConnection();
 391    if ( !toServer || !toClient )
 392    {
 393        return NULL;
 394    }
 395
 396    const S32 ghostIndex = toClient->getGhostIndex( pPath );
 397    if ( ghostIndex == -1 )
 398    {
 399        return NULL;
 400    }
 401
 402    return dynamic_cast<VPath*>( toServer->resolveGhost( ghostIndex ) );
 403}
 404
 405//-----------------------------------------------------------------------------
 406//
 407// Selection Methods.
 408//
 409//-----------------------------------------------------------------------------
 410
 411bool VPathEditor::updateSelection( const Gui3DMouseEvent &pEvent )
 412{
 413    const Point3F pt0 = pEvent.pos;
 414    const Point3F pt1 = pEvent.pos + pEvent.vec * gProjectDistance;
 415
 416    RayInfo ri;
 417    if ( !gServerContainer.collideBox( pt0, pt1, MarkerObjectType, &ri ) )
 418    {
 419        // No Object.
 420        return false;
 421    }
 422
 423    VPath *path = dynamic_cast<VPath*>( ri.object );
 424    if ( !path )
 425    {
 426        // No Path Object.
 427        return false;
 428    }
 429
 430    // No Node.
 431    S32 nodeIndex = -1;
 432
 433    for ( VPathNodeIterator itr = path->mNodeList.begin(); itr != path->mNodeList.end(); itr++ )
 434    {
 435        VPathNode *node = ( *itr );
 436
 437        Point3F projPosition;
 438        project( node->getWorldPosition(), &projPosition );
 439
 440        if ( projPosition.z <= 0.0f )
 441        {
 442            continue;
 443        }
 444
 445        const Point2I rectHalfSize( 8, 8 );
 446        const Point2I screenPosition( ( S32 )projPosition.x, ( S32 )projPosition.y );
 447        const RectI   screenRect( screenPosition - rectHalfSize, 2 * rectHalfSize );
 448
 449        // Mouse Close Enough?
 450        if ( screenRect.pointInRect( pEvent.mousePoint ) )
 451        {
 452            // Select Node.
 453            nodeIndex = ( itr - path->mNodeList.begin() );
 454        }
 455    }
 456
 457    // Set Selection.
 458    updateSelection( path, nodeIndex );
 459
 460    // Valid Selection.
 461    return true;
 462}
 463
 464void VPathEditor::updateSelection( VPath *pPathObject, const S32 &pNodeIndex )
 465{
 466    // Store Selection.
 467    mSelection.Path = pPathObject;
 468    mSelection.Node = pNodeIndex;
 469
 470    // Quick Update.
 471    updateSelection();
 472
 473    // Return Buffer.
 474    char buffer[2][32];
 475    dSprintf( buffer[0], sizeof( buffer[0] ), "%d", ( pPathObject ) ? pPathObject->getId() : 0 );
 476    dSprintf( buffer[1], sizeof( buffer[1] ), "%d", pNodeIndex );
 477
 478    // Callback.
 479    Con::executef( this, "onUpdateSelection", buffer[0], buffer[1] );
 480}
 481
 482void VPathEditor::updateSelection( void )
 483{
 484    if ( !isValidSelection() )
 485    {
 486        // No Further Updates.
 487        return;
 488    }
 489
 490    // Fetch Node.
 491    VPathNode *node = mSelection.Path->getNode( mSelection.Node );
 492
 493    // Fetch Node Rotation Matrix.
 494    MatrixF mat;
 495    node->getWorldRotation().setMatrix( &mat );
 496
 497    // Determine Tangent Axis.
 498    Point3F pt0(  VPath::gBezierAxis * node->getWeight() );
 499    Point3F pt1( -VPath::gBezierAxis * node->getWeight() );
 500
 501    // Rotate Axis.
 502    mat.mulP( pt0 );
 503    mat.mulP( pt1 );
 504
 505    // Offset Points.
 506    pt0 += node->getWorldPosition();
 507    pt1 += node->getWorldPosition();
 508
 509    // Store Points.
 510    mSelection.TangentHandle[0] = pt0;
 511    mSelection.TangentHandle[1] = pt1;
 512}
 513
 514DefineEngineMethod( VPathEditor, clearSelection, void, (),, "( void )" )
 515{
 516    // Clear Selection.
 517    object->updateSelection( NULL, -1 );
 518}
 519
 520DefineEngineMethod(VPathEditor, setSelection, void, (SceneObject* sceneObject, S32 nodeIndex), (nullAsType<SceneObject*>(), -1), "( pObject, [pNodeIndex] )")
 521{
 522   if (sceneObject == nullptr)
 523   {
 524      Con::errorf("VPathEditor::setSelection() - Unable to select target Object.");
 525      return;
 526   }
 527    // Fetch Path.
 528    VPath *path = dynamic_cast<VPath*>(sceneObject);
 529    if ( !path )
 530    {
 531        Con::errorf( "VPathEditor::setSelection() - Unable to select target Object." );
 532        return;
 533    }
 534
 535    object->updateSelection( path, nodeIndex);
 536}
 537
 538DefineEngineMethod( VPathEditor, isValidSelection, bool, (),, "( void )" )
 539{
 540    return object->isValidSelection();
 541}
 542
 543DefineEngineMethod( VPathEditor, getSelectedPath, S32, (),, "( void )" )
 544{
 545    // Fetch Path.
 546    VPath *path = object->mSelection.Path;
 547
 548    // Return ID.
 549    return ( path ) ? path->getId() : 0;
 550}
 551
 552DefineEngineMethod( VPathEditor, getSelectedNode, S32, (),, "( void )" )
 553{
 554    // Return Node Index.
 555    return ( object->mSelection.Path ) ? object->mSelection.Node : -1;
 556}
 557
 558DefineEngineMethod( VPathEditor, deleteSelection, void, (),, "( void )" )
 559{
 560    // Valid Selection?
 561    if ( object->isValidSelection() )
 562    {
 563        object->deleteNode( object->mSelection.Node );
 564    }
 565}
 566
 567//-----------------------------------------------------------------------------
 568//
 569// Weight Editing Methods.
 570//
 571//-----------------------------------------------------------------------------
 572
 573bool VPathEditor::isEditingWeight( const Gui3DMouseEvent &pEvent )
 574{
 575    if ( !isValidSelection() || mSelection.Path->mPathType != VPath::k_PathBezier )
 576    {
 577        // False.
 578        mEditWeight = false;
 579
 580        // Invalid Selection.
 581        return false;
 582    }
 583
 584    const Point3F pt0 = pEvent.pos;
 585    const Point3F pt1 = pEvent.pos + pEvent.vec * gProjectDistance;
 586
 587    // Min Index.
 588    S32 minNode     = -1;
 589    F32 minDistance = F32_MAX;
 590
 591    for ( S32 i = 0; i < 2; i++ )
 592    {
 593        Point3F pt;
 594        if ( !Utility::FindNearestPointOnLine( mSelection.TangentHandle[i], pt0, pt1, &pt ) )
 595        {
 596            // Skip.
 597            continue;
 598        }
 599
 600        // Distance.
 601        const F32 ptDistance = ( pt - mSelection.TangentHandle[i] ).len();
 602        if ( ptDistance < minDistance )
 603        {
 604            // Store Index.
 605            minNode = i;
 606
 607            // Store Distance.
 608            minDistance = ptDistance;
 609        }
 610    }
 611
 612    if ( minDistance > gSelectionDistance )
 613    {
 614        // False.
 615        mEditWeight = false;
 616
 617        // Too Far Away.
 618        return false;
 619    }
 620
 621    // True.
 622    mEditWeight       = true;
 623    mEditWeightHandle = minNode;
 624
 625    return true;
 626}
 627
 628void VPathEditor::updateWeight( const Gui3DMouseEvent &pEvent )
 629{
 630    if ( !isEditingWeight() )
 631    {
 632        // Woops!
 633        return;
 634    }
 635
 636    // Fetch Current Node.
 637    VPathNode *node    = mSelection.Path->getNode( mSelection.Node );
 638    Point3F    nodePos = node->getWorldPosition();
 639
 640    // Fetch Node Transform.
 641    MatrixF mat = node->getWorldTransform();
 642
 643    // Fetch the Normal.
 644    const VectorF planeNormal = mat.getColumn3F( 0 );
 645
 646    // Construct Plane.
 647    const PlaneF plane( nodePos, planeNormal );
 648
 649    Point3F iPt;
 650    if ( plane.intersect( pEvent.pos, pEvent.vec, &iPt ) )
 651    {
 652/*
 653        // Fetch Edit Vector.
 654        VectorF tangentVect( mSelection.TangentHandle[mEditWeightHandle] - nodePos );
 655        tangentVect.normalize();
 656
 657        // Fetch Mouse Vector.
 658        VectorF mouseVec( iPt - nodePos );
 659        F32 mouseDist = mouseVec.len();
 660        mouseVec.normalize();
 661
 662        // Find the Angles.
 663        F32 tangentAngle = mAtan2( -tangentVect.z, tangentVect.x );
 664        F32 mouseAngle   = mAtan2( -mouseVec.z, mouseVec.x );
 665
 666        // Determine Sign.
 667        const S32 sign = ( planeNormal.y > 0.f ) ? -1.f : 1.f;
 668
 669        // Delta Rotation..
 670        const QuatF deltaRotation( AngAxisF( planeNormal, sign * ( mouseAngle - tangentAngle ) ) );
 671
 672        // Calculate New Rotation.
 673        QuatF newRotation;
 674        newRotation.mul( nodePos, deltaRotation ); 
 675
 676        // Apply Rotation.
 677        setNodeRotation( mSelection.Node, newRotation );
 678*/
 679/*
 680        // Fetch Edit Vector.
 681        VectorF handleVec( mSelection.TangentHandle[mEditWeightHandle] - nodePos );
 682        handleVec.normalize();
 683
 684        // Fetch Mouse Vector.
 685        VectorF mouseVec( iPt - nodePos );
 686        mouseVec.normalize();
 687
 688        // Find the Angles.
 689        F32 handleAngle = Utility::GetPitch( handleVec ); //mAtan2( -handleVec.z, handleVec.x );
 690        F32 mouseAngle  = Utility::GetPitch( mouseVec );  //mAtan2( -mouseVec.z, mouseVec.x );
 691
 692        // Determine Sign.
 693        const S32 sign = ( planeNormal.y > 0.f ) ? -1.f : 1.f;
 694
 695        // Delta Rotation.
 696        MatrixF rotMat;
 697        AngAxisF::RotateY( sign * ( mouseAngle - handleAngle ), &rotMat );
 698
 699        // Rotate.
 700        mat.mul( rotMat );
 701
 702        QuatF newRotation;
 703        newRotation.set( mat );
 704
 705        // Apply Rotation.
 706        setNodeRotation( mSelection.Node, newRotation );
 707*/
 708        // Apply Weight.
 709        setNodeWeight( mSelection.Node, ( iPt - nodePos ).len() );
 710    }
 711}
 712
 713//-----------------------------------------------------------------------------
 714//
 715// Path Editing Methods.
 716//
 717//-----------------------------------------------------------------------------
 718
 719void VPathEditor::setPathPosition( const Point3F &pPosition )
 720{
 721    // Fetch Paths.
 722    VPath *serverPath = mSelection.Path;
 723    VPath *clientPath = getClientPath( serverPath );
 724
 725    // Update Position.
 726    serverPath->setPosition( pPosition );
 727    clientPath->setPosition( pPosition );
 728
 729    // Update Selection.
 730    updateSelection();
 731}
 732
 733void VPathEditor::setPathRotation( const QuatF &pRotation )
 734{
 735    // Determine the Matrix.
 736    MatrixF mat;
 737    pRotation.setMatrix( &mat );
 738    mat.setPosition( mSelection.Path->getPosition() );
 739
 740    // Update Transform.
 741    setPathTransform( mat );
 742}
 743
 744void VPathEditor::setPathTransform( const MatrixF &pTransform )
 745{
 746    // Fetch Paths.
 747    VPath *serverPath = mSelection.Path;
 748    VPath *clientPath = getClientPath( serverPath );
 749
 750    // Update Transform.
 751    serverPath->setTransform( pTransform );
 752    clientPath->setTransform( pTransform );
 753
 754    // Update Selection.
 755    updateSelection();
 756}
 757
 758void VPathEditor::setPathScale( const VectorF &pScale )
 759{
 760    // Fetch Paths.
 761    VPath *serverPath = mSelection.Path;
 762    VPath *clientPath = getClientPath( serverPath );
 763
 764    // Fetch Current Scale.
 765    VectorF scale = serverPath->getScale();
 766    scale.convolve( pScale );
 767
 768    // Update Scale.
 769    serverPath->setScale( scale );
 770    clientPath->setScale( scale );
 771
 772    // Update Selection.
 773    updateSelection();
 774}
 775
 776//-----------------------------------------------------------------------------
 777//
 778// Node Editing Methods.
 779//
 780//-----------------------------------------------------------------------------
 781
 782bool VPathEditor::getPointOnPath( VPath *pPath, const Gui3DMouseEvent &pEvent, S32 &pNode, MatrixF &pTransform )
 783{
 784    if ( pPath->getNodeCount() < 2 )
 785    {
 786        // Start / End Points.
 787        const Point3F pt0 = pEvent.pos;
 788        const Point3F pt1 = pEvent.pos + pEvent.vec * gProjectDistance;
 789
 790        // Create Intersection Plane.
 791        const PlaneF plane( pPath->getPosition(), VPath::gBezierUp );
 792
 793        // Intersection Point.
 794        Point3F intersectionPoint;
 795        if ( !plane.intersect( pEvent.pos, pEvent.vec, &intersectionPoint ) )
 796        {
 797            // No Intersection.
 798            return false;
 799        }
 800
 801        // I'th Node.
 802        pNode = pPath->getNodeCount();
 803        // Set Identity.
 804        pTransform.identity();
 805        // Set Position.
 806        pTransform.setPosition( intersectionPoint );
 807
 808        // Return.
 809        return true;
 810    }
 811
 812    switch ( pPath->mPathType )
 813    {
 814        case VPath::k_PathLinear : 
 815            {
 816
 817                return getPointOnLinearPath( pPath, pEvent, pNode, pTransform );
 818
 819            } break;
 820
 821        case VPath::k_PathBezier :
 822            {
 823
 824                return getPointOnBezierPath( pPath, pEvent, pNode, pTransform );
 825
 826            } break;
 827    }
 828
 829    return false;
 830}
 831
 832bool VPathEditor::getPointOnLinearPath( VPath *pPath, const Gui3DMouseEvent &pEvent, S32 &pNode, MatrixF &pTransform )
 833{
 834    // Start / End Points.
 835    const Point3F pt0 = pEvent.pos;
 836    const Point3F pt1 = pEvent.pos + pEvent.vec * gProjectDistance;
 837
 838    S32 minNode = -1;
 839    F32 minDistance = F32_MAX;
 840    Point3F minPoint( 0.f, 0.f, 0.f );
 841    for ( VPathNodeIterator itr = pPath->mNodeList.begin(); itr != pPath->mNodeList.end(); itr++ )
 842    {
 843        // Fetch Nodes.
 844        VPathNode *srcNode = ( *itr );
 845        VPathNode *dstNode = ( itr == ( pPath->mNodeList.end() - 1 ) ) ? ( *( pPath->mNodeList.begin() ) ) : ( *( itr + 1 ) );
 846
 847        // Project to Screen.
 848        Point3F srcNodeScreenPosition, dstNodeScreenPosition;
 849        project( srcNode->getWorldPosition(), &srcNodeScreenPosition );
 850        project( dstNode->getWorldPosition(), &dstNodeScreenPosition );
 851
 852        // Skip?
 853        if ( srcNodeScreenPosition.z > 1.f && dstNodeScreenPosition.z > 1.f )
 854        {
 855            continue;
 856        }
 857
 858        Point3F ptOut0, ptOut1;
 859        F32 ptOutDistance;
 860        if ( !Utility::FindNearestDistanceBetweenLines( pt0, pt1,
 861                                                        srcNode->getWorldPosition(), dstNode->getWorldPosition(),
 862                                                        &ptOut0, &ptOut1, &ptOutDistance ) )
 863        {
 864            continue;
 865        }
 866
 867        if ( ptOutDistance < minDistance )
 868        {
 869            minDistance = ptOutDistance;
 870            minPoint = ptOut1;
 871            minNode = ( itr - pPath->mNodeList.begin() );
 872        }
 873    }
 874
 875    // Distance too Large?
 876    if ( minDistance > 0.25f )
 877    {
 878        // Invalid.
 879        return false;
 880    }
 881
 882    // Setup.
 883    pTransform.identity();
 884    pTransform.setPosition( minPoint );
 885
 886    // Store Node.
 887    pNode = minNode;
 888
 889    return true;
 890}
 891
 892bool VPathEditor::getPointOnBezierPath( VPath *pPath, const Gui3DMouseEvent &pEvent, S32 &pNode, MatrixF &pTransform )
 893{
 894    S32 minNode = -1;
 895    F32 minInterp = 0.f;
 896    F32 minDistance = F32_MAX;
 897    Point3F minPoint( 0.f, 0.f, 0.f );
 898    for ( VPathNodeIterator itr = pPath->mNodeList.begin(); itr != pPath->mNodeList.end(); itr++ )
 899    {
 900        // Fetch Nodes.
 901        VPathNode *srcNode = ( *itr );
 902        VPathNode *dstNode = ( itr == ( pPath->mNodeList.end() - 1 ) ) ? ( *( pPath->mNodeList.begin() ) ) : ( *( itr + 1 ) );
 903
 904        // Project to Screen.
 905        Point3F srcNodeScreenPosition, dstNodeScreenPosition;
 906        project( srcNode->getWorldPosition(), &srcNodeScreenPosition );
 907        project( dstNode->getWorldPosition(), &dstNodeScreenPosition );
 908
 909        // Skip?
 910        if ( srcNodeScreenPosition.z > 1.f && dstNodeScreenPosition.z > 1.f )
 911        {
 912            continue;
 913        }
 914
 915        // Positions.
 916        const Point3F &pt0 = srcNode->getWorldPosition();
 917        const Point3F &pt3 = dstNode->getWorldPosition();
 918
 919        // Fetch Node Rotation Matrices.
 920        MatrixF mat0, mat1;
 921        srcNode->getWorldRotation().setMatrix( &mat0 );
 922        dstNode->getWorldRotation().setMatrix( &mat1 );
 923
 924        // Determine Tangent Axis.
 925        Point3F pt1(  VPath::gBezierAxis * srcNode->getWeight() );
 926        Point3F pt2( -VPath::gBezierAxis * dstNode->getWeight() );
 927
 928        // Rotate Axis.
 929        mat0.mulP( pt1 );
 930        mat1.mulP( pt2 );
 931
 932        // Offset Points.
 933        pt1 += pt0;
 934        pt2 += pt3;
 935
 936        for ( F32 t = 0.f, it = 1.f; t <= 1.f; t += 0.1f, it = ( 1.f - t ) )
 937        {
 938            // Calculate Position.
 939            Point3F pos = ( pt0 * it * it * it ) + ( 3 * pt1 * it * it * t ) + ( 3 * pt2 * it * t * t ) + ( pt3 * t * t * t );
 940
 941            // Determine the Screen Position.
 942            Point3F screenPos;
 943            project( pos, &screenPos );
 944            // Behind?
 945            if ( screenPos.z > 1.f )
 946            {
 947                // Skip Point.
 948                continue;
 949            }
 950
 951            // Determine the Distance.
 952            F32 screenDistance = Point2F( screenPos.x - pEvent.mousePoint.x, screenPos.y - pEvent.mousePoint.y ).lenSquared();
 953            // Min Distance?
 954            if ( screenDistance < minDistance )
 955            {
 956                // Store.
 957                minDistance = screenDistance;
 958                minInterp = t;
 959                minPoint = pos;
 960                minNode = ( itr - pPath->mNodeList.begin() );
 961            }
 962        }
 963    }
 964
 965    // Distance too Large?
 966    if ( minDistance > 1000.f )
 967    {
 968        // Invalid.
 969        return false;
 970    }
 971
 972    // Fetch Orientation.
 973    const VectorF &orientation = pPath->getPathOrientation( pPath->getNode( minNode ), 
 974                                                            pPath->getNode( ( minNode + 1 ) % pPath->getNodeCount() ),
 975                                                            minInterp, true );
 976
 977    // Z-Axis.
 978    VectorF zVec = -orientation;
 979    zVec.normalize();
 980
 981    // X-Axis.
 982    VectorF xVec = mCross( VPath::gBezierUp, zVec );
 983    xVec.normalize();
 984
 985    // Y-Axis.
 986    VectorF yVec = mCross( zVec, xVec );
 987    yVec.normalize();
 988
 989    // Setup Object Transform.
 990    pTransform.identity();
 991    pTransform.setColumn( 0,  xVec );
 992    pTransform.setColumn( 1, -zVec );
 993    pTransform.setColumn( 2,  yVec );
 994    // Set the Position.
 995    pTransform.setPosition( minPoint );
 996
 997    // Store Node.
 998    pNode = minNode;
 999
1000    return true;
1001}
1002
1003void VPathEditor::addNode( const Gui3DMouseEvent &pEvent )
1004{
1005    VPath *path = mSelection.Path;
1006    if ( !path )
1007    {
1008        // Woops!
1009        return;
1010    }
1011
1012    // Min Index.
1013    S32 nodeIndex = -1;
1014    MatrixF nodeTransform( true );
1015    if ( !getPointOnPath( path, pEvent, nodeIndex, nodeTransform ) )
1016    {
1017        // Can't Add.
1018        return;
1019    }
1020
1021    // Invert Transform.
1022    MatrixF pathTransform = mSelection.Path->getTransform();
1023    pathTransform.setPosition( Point3F::Zero );
1024    pathTransform.inverse();
1025
1026    Point3F nodePosition = ( nodeTransform.getPosition() - mSelection.Path->getPosition() );
1027    pathTransform.mulP( nodePosition );
1028
1029    // Node Rotation.
1030    nodeTransform.mul( pathTransform );
1031    QuatF nodeRotation( nodeTransform );
1032
1033    // Node Weights.
1034    F32 nodeWeight = 10.f;
1035
1036    // Add New Node.
1037    VPathNode *node = path->addNode( nodePosition, nodeRotation, nodeWeight, ++nodeIndex );
1038
1039    // Valid Node?
1040    if ( !node )
1041    {
1042        return;
1043    }
1044
1045    // Update Size.
1046    path->updateContainer();
1047
1048    // Calculate Path.
1049    path->calculatePath();
1050
1051    UndoManager *historyManager = NULL;
1052    if ( !Sim::findObject( "EUndoManager", historyManager ) )
1053    {
1054        Con::errorf( "VPathEditor::addNode() - EUndoManager not found!" );
1055        return;           
1056    }
1057
1058    // Create Undo Action.
1059    VPathEditorAddNodeAction *editAction = new VPathEditorAddNodeAction();
1060
1061    // Store Editor.
1062    editAction->mEditor       = this;
1063
1064    // Store Node Details.
1065    editAction->mPath         = path;
1066    editAction->mNodeIndex    = nodeIndex;
1067
1068    editAction->mNodePosition = nodePosition;
1069    editAction->mNodeRotation = nodeRotation;
1070    editAction->mNodeWeight   = nodeWeight;
1071
1072    // Add To Manager.
1073    historyManager->addAction( editAction );
1074
1075    // Set World Editor Dirty.
1076    setWorldEditorDirty();
1077}
1078
1079void VPathEditor::deleteNode( const S32 &pNodeIndex )
1080{
1081    VPath *path = mSelection.Path;
1082    if ( !path )
1083    {
1084        // Woops!
1085        return;
1086    }
1087
1088    // Fetch Node Properites.
1089    VPathNode *node        = path->getNode( pNodeIndex );
1090    const Point3F position = node->getLocalPosition();
1091    const QuatF   rotation = node->getLocalRotation();
1092    const F32     weight   = node->getWeight();
1093
1094    // Delete Node.
1095    path->deleteNode( pNodeIndex );
1096    
1097    // Update Path.
1098    path->updateContainer();
1099
1100    // Calculate Path.
1101    path->calculatePath();
1102
1103    // Selected Node?
1104    const S32 _nodeIndex = pNodeIndex;
1105    if ( pNodeIndex == mSelection.Node )
1106    {
1107        // Update Selection.
1108        updateSelection( mSelection.Path, -1 );
1109    }
1110
1111    UndoManager *historyManager = NULL;
1112    if ( !Sim::findObject( "EUndoManager", historyManager ) )
1113    {
1114        Con::errorf( "VPathEditor::deleteNode() - EUndoManager not found!" );
1115        return;           
1116    }
1117
1118    // Create Undo Action.
1119    VPathEditorDeleteNodeAction *editAction = new VPathEditorDeleteNodeAction();
1120
1121    // Store Editor.
1122    editAction->mEditor       = this;
1123
1124    // Store Node Details.
1125    editAction->mPath         = path;
1126    editAction->mNodeIndex    = _nodeIndex;
1127
1128    editAction->mNodePosition = position;
1129    editAction->mNodeRotation = rotation;
1130    editAction->mNodeWeight   = weight;
1131
1132    // Add To Manager.
1133    historyManager->addAction( editAction );
1134
1135    // Set World Editor Dirty.
1136    setWorldEditorDirty();
1137}
1138
1139void VPathEditor::setNodePosition( const S32 &pNodeIndex, const Point3F &pPosition )
1140{
1141    // Fetch Paths.
1142    VPath *serverPath = mSelection.Path;
1143    VPath *clientPath = getClientPath( serverPath );
1144
1145    // Sanity!
1146    if ( !serverPath || !clientPath )
1147    {
1148        return;
1149    }
1150
1151    // Change?
1152    if ( serverPath->getNodeLocalPosition( pNodeIndex ) == pPosition )
1153    {
1154        return;
1155    }
1156
1157    // Set Position.
1158    serverPath->setNodePosition( pNodeIndex, pPosition );
1159    clientPath->setNodePosition( pNodeIndex, pPosition );
1160
1161    // Update Selection.
1162    updateSelection();
1163    
1164    // Dirty.
1165    mIsDirty    = true;
1166    mNodeEdit.Dirty = true;
1167
1168    // Arg Buffer.
1169    char buffer[3][32];
1170    dSprintf( buffer[0], sizeof( buffer[0] ), "%d", mSelection.Path->getId() );
1171    dSprintf( buffer[1], sizeof( buffer[1] ), "%d", pNodeIndex );
1172    dSprintf( buffer[2], sizeof( buffer[2] ), "%d", ( mSelection.Node == pNodeIndex ) );
1173
1174    // Callback.
1175    Con::executef( this, "onUpdateNodePosition", buffer[0], buffer[1], buffer[2] );
1176}
1177
1178void VPathEditor::setNodeRotation( const S32 &pNodeIndex, const QuatF &pRotation )
1179{
1180    // Fetch Paths.
1181    VPath *serverPath = mSelection.Path;
1182    VPath *clientPath = getClientPath( serverPath );
1183
1184    // Sanity!
1185    if ( !serverPath || !clientPath )
1186    {
1187        return;
1188    }
1189
1190    // Change?
1191    if ( serverPath->getNodeLocalRotation( pNodeIndex ) == pRotation )
1192    {
1193        return;
1194    }
1195
1196    // Set Position.
1197    serverPath->setNodeRotation( pNodeIndex, pRotation );
1198    clientPath->setNodeRotation( pNodeIndex, pRotation );
1199
1200    // Update Selection.
1201    updateSelection();
1202    
1203    // Dirty.
1204    mIsDirty    = true;
1205    mNodeEdit.Dirty = true;
1206
1207    // Arg Buffer.
1208    char buffer[3][32];
1209    dSprintf( buffer[0], sizeof( buffer[0] ), "%d", mSelection.Path->getId() );
1210    dSprintf( buffer[1], sizeof( buffer[1] ), "%d", pNodeIndex );
1211    dSprintf( buffer[2], sizeof( buffer[2] ), "%d", ( mSelection.Node == pNodeIndex ) );
1212
1213    // Callback.
1214    Con::executef( this, "onUpdateNodeRotation", buffer[0], buffer[1], buffer[2] );
1215}
1216
1217void VPathEditor::setNodeWeight( const S32 &pNodeIndex, const F32 &pWeight )
1218{
1219    // Fetch Paths.
1220    VPath *serverPath = mSelection.Path;
1221    VPath *clientPath = getClientPath( serverPath );
1222
1223    // Sanity!
1224    if ( !serverPath || !clientPath )
1225    {
1226        return;
1227    }
1228
1229    // Change?
1230    if ( serverPath->getNodeWeight( pNodeIndex ) == pWeight )
1231    {
1232        return;
1233    }
1234
1235    // Set Weight.
1236    serverPath->setNodeWeight( pNodeIndex, pWeight );
1237    clientPath->setNodeWeight( pNodeIndex, pWeight );
1238
1239    // Update Selection.
1240    updateSelection();
1241    
1242    // Dirty.
1243    mIsDirty    = true;
1244    mNodeEdit.Dirty = true;
1245
1246    // Arg Buffer.
1247    char buffer[3][32];
1248    dSprintf( buffer[0], sizeof( buffer[0] ), "%d", mSelection.Path->getId() );
1249    dSprintf( buffer[1], sizeof( buffer[1] ), "%d", pNodeIndex );
1250    dSprintf( buffer[2], sizeof( buffer[2] ), "%d", ( mSelection.Node == pNodeIndex ) );
1251
1252    // Callback.
1253    Con::executef( this, "onUpdateNodeWeight", buffer[0], buffer[1], buffer[2] );
1254}
1255
1256void VPathEditor::setNodeOrientationMode( const S32 &pNodeIndex, const VPathNode::eOrientationType &pType )
1257{
1258    // Fetch Paths.
1259    VPath *serverPath = mSelection.Path;
1260    VPath *clientPath = getClientPath( serverPath );
1261
1262    // Sanity!
1263    if ( !serverPath || !clientPath )
1264    {
1265        return;
1266    }
1267
1268    // Set Orientation Mode.
1269    serverPath->setNodeOrientationMode( pNodeIndex, pType );
1270    clientPath->setNodeOrientationMode( pNodeIndex, pType );
1271    
1272    // Dirty.
1273    mIsDirty    = true;
1274    mNodeEdit.Dirty = true;
1275
1276    // Arg Buffer.
1277    char buffer[3][32];
1278    dSprintf( buffer[0], sizeof( buffer[0] ), "%d", mSelection.Path->getId() );
1279    dSprintf( buffer[1], sizeof( buffer[1] ), "%d", pNodeIndex );
1280    dSprintf( buffer[2], sizeof( buffer[2] ), "%d", ( mSelection.Node == pNodeIndex ) );
1281
1282    // Callback.
1283    Con::executef( this, "onUpdateNodeOrientation", buffer[0], buffer[1], buffer[2] );
1284}
1285
1286void VPathEditor::setNodeOrientationMode( const S32 &pNodeIndex, const VPathNode::eOrientationType &pType, const Point3F &pPoint )
1287{
1288    // Fetch Paths.
1289    VPath *serverPath = mSelection.Path;
1290    VPath *clientPath = getClientPath( serverPath );
1291
1292    // Sanity!
1293    if ( !serverPath || !clientPath )
1294    {
1295        return;
1296    }
1297
1298    // Set Orientation Mode.
1299    serverPath->setNodeOrientationMode( pNodeIndex, pType, pPoint );
1300    clientPath->setNodeOrientationMode( pNodeIndex, pType, pPoint );
1301    
1302    // Dirty.
1303    mIsDirty    = true;
1304    mNodeEdit.Dirty = true;
1305
1306    // Arg Buffer.
1307    char buffer[3][32];
1308    dSprintf( buffer[0], sizeof( buffer[0] ), "%d", mSelection.Path->getId() );
1309    dSprintf( buffer[1], sizeof( buffer[1] ), "%d", pNodeIndex );
1310    dSprintf( buffer[2], sizeof( buffer[2] ), "%d", ( mSelection.Node == pNodeIndex ) );
1311
1312    // Callback.
1313    Con::executef( this, "onUpdateNodeOrientation", buffer[0], buffer[1], buffer[2] );
1314}
1315
1316void VPathEditor::pushPathEdit( void )
1317{
1318    // Clear Current Edit Dirty.
1319    mPathEdit.Dirty = false;
1320
1321    if ( mSelection.Path != NULL )
1322    {
1323        // Store Node Details.
1324        mPathEdit.Transform = mSelection.Path->getTransform();
1325    }
1326}
1327
1328void VPathEditor::popPathEdit( void )
1329{
1330    // Did Edit?
1331    if ( mPathEdit.Dirty && mSelection.Path != NULL )
1332    {
1333        UndoManager *historyManager = NULL;
1334        if ( !Sim::findObject( "EUndoManager", historyManager ) )
1335        {
1336            Con::errorf( "VPathEditor - EUndoManager not found!" );
1337            return;           
1338        }
1339
1340        // Create Undo Action.
1341        VPathEditorEditPathAction *editAction = new VPathEditorEditPathAction( "Edit Path" );
1342
1343        // Store Editor.
1344        editAction->mEditor   = this;
1345
1346        // Store Path Details.
1347        editAction->mPath      = mSelection.Path;
1348        editAction->mTransform = mPathEdit.Transform;
1349
1350        // Add To Manager.
1351        historyManager->addAction( editAction );
1352
1353        // Clear Dirty.
1354        mPathEdit.Dirty = false;
1355
1356        // Set World Editor Dirty.
1357        setWorldEditorDirty();
1358    }
1359}
1360
1361void VPathEditor::pushNodeEdit( void )
1362{
1363    // Clear Current Edit Dirty.
1364    mNodeEdit.Dirty = false;
1365
1366    if ( isValidSelection() )
1367    {
1368        // Fetch Node.
1369        VPathNode *node = mSelection.Path->getNode( mSelection.Node );
1370
1371        // Store Node Details.
1372        mNodeEdit.Position = node->getLocalPosition();
1373        mNodeEdit.Rotation = node->getLocalRotation();
1374        mNodeEdit.Weight   = node->getWeight();
1375    }
1376}
1377
1378void VPathEditor::popNodeEdit( void )
1379{
1380    // Did Edit?
1381    if ( mNodeEdit.Dirty && isValidSelection() )
1382    {
1383        UndoManager *historyManager = NULL;
1384        if ( !Sim::findObject( "EUndoManager", historyManager ) )
1385        {
1386            Con::errorf( "VPathEditor - EUndoManager not found!" );
1387            return;           
1388        }
1389
1390        // Create Undo Action.
1391        VPathEditorEditNodeAction *editAction = new VPathEditorEditNodeAction( "Edit Node" );
1392
1393        // Store Editor.
1394        editAction->mEditor       = this;
1395
1396        // Store Node Details.
1397        editAction->mPath         = mSelection.Path;
1398        editAction->mNodeIndex    = mSelection.Node;
1399
1400        editAction->mNodePosition = mNodeEdit.Position;
1401        editAction->mNodeRotation = mNodeEdit.Rotation;
1402        editAction->mNodeWeight   = mNodeEdit.Weight;
1403
1404        editAction->mNodeOrientation = mSelection.Path->getNode( mSelection.Node )->getOrientationMode();
1405
1406        // Add To Manager.
1407        historyManager->addAction( editAction );
1408
1409        // Clear Dirty.
1410        mNodeEdit.Dirty = false;
1411
1412        // Set World Editor Dirty.
1413        setWorldEditorDirty();
1414    }
1415}
1416
1417void VPathEditor::setWorldEditorDirty( void )
1418{
1419    WorldEditor *worldEditor;
1420    if ( Sim::findObject( "EWorldEditor", worldEditor ) )
1421    {
1422        worldEditor->setDirty();
1423    }
1424}
1425
1426//-----------------------------------------------------------------------------
1427//
1428// Render Methods.
1429//
1430//-----------------------------------------------------------------------------
1431
1432void VPathEditor::setStateBlock( void )
1433{
1434    // Valid State Block?
1435    if ( !mStateBlock )
1436    {
1437        // Setup Definition.
1438        GFXStateBlockDesc def;
1439        def.blendDefined = true;
1440        def.blendEnable = true;
1441        def.blendSrc = GFXBlendSrcAlpha;
1442        def.blendDest = GFXBlendInvSrcAlpha;
1443        def.zDefined = true;
1444        def.cullDefined = false;
1445
1446        // Create State Block.
1447        mStateBlock = GFX->createStateBlock( def );
1448    }
1449
1450    // Set State Block.
1451    GFX->setStateBlock( mStateBlock );
1452}
1453
1454void VPathEditor::renderScene( const RectI &pUpdateRect )
1455{
1456    // Setup State Block.
1457    setStateBlock();
1458
1459    if ( isValidSelection() )
1460    {
1461        // Fetch Current Node.
1462        VPathNode *node = mSelection.Path->getNode( mSelection.Node );
1463
1464        // Render Gizmo?
1465        if ( mEditMode == k_Gizmo && mGizmoProfile->mode != ScaleMode )
1466        {
1467            // Fetch Node Transform.
1468            MatrixF mat= node->getWorldTransform();
1469
1470            // Move Gizmo.
1471            mGizmo->set( mat, node->getWorldPosition(), Point3F( 1.0f, 1.0f, 1.0f ) );
1472
1473            // Render Gizmo.
1474            mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix );
1475        }
1476
1477        // Render Handles?
1478        if ( mSelection.Path->mPathType == VPath::k_PathBezier )
1479        {
1480            // Fetch Tangent Handles.
1481            const Point3F &pt0 = mSelection.TangentHandle[0];
1482            const Point3F &pt1 = mSelection.TangentHandle[1];
1483
1484            // State Block.
1485            GFXStateBlockDesc desc;
1486            desc.setZReadWrite( true, true );
1487            desc.fillMode = GFXFillSolid;
1488
1489            // Set Color.
1490            PrimBuild::color( gPathColorSel );
1491
1492            // Render Line.
1493            PrimBuild::begin( GFXLineList, 2 );
1494                PrimBuild::vertex3fv( pt0 );
1495                PrimBuild::vertex3fv( pt1 );
1496            PrimBuild::end();
1497
1498            // Render Handles.
1499            GFX->getDrawUtil()->drawSphere( desc, 0.1f, pt0, gPathColorSel );
1500            GFX->getDrawUtil()->drawSphere( desc, 0.1f, pt1, gPathColorSel );
1501        }
1502
1503        // ToPoint Node?
1504        if ( node->getOrientationMode().Type == VPathNode::k_OrientationToPoint )
1505        {
1506            PrimBuild::color( gNodeLookAtPointColor );
1507            PrimBuild::begin( GFXLineStrip, 2 );
1508
1509            PrimBuild::vertex3fv( node->getWorldPosition() );
1510            PrimBuild::vertex3fv( node->getOrientationMode().Point );
1511
1512            PrimBuild::end();
1513        }
1514    }
1515    else if ( mSelection.Path && mEditMode == k_Gizmo )
1516    {
1517        switch ( mGizmoProfile->mode )
1518        {
1519            case MoveMode:
1520                {
1521                    // Fetch Path Transform.
1522                    const MatrixF &mat = mSelection.Path->getTransform();
1523
1524                    // Fetch the Path's Box Center.
1525                    const Point3F &pos = mSelection.Path->getWorldBox().getCenter();
1526
1527                    // Move Gizmo.
1528                    mGizmo->set( mat, pos, Point3F( 1.0f, 1.0f, 1.0f ) );
1529
1530                    // Render Gizmo.
1531                    mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix );
1532
1533                } break;
1534        }
1535    }
1536
1537    // Render Path Segments.
1538    renderPaths( k_RenderSegments );
1539
1540    // Set Clip Rect.
1541    GFX->setClipRect( pUpdateRect ); 
1542
1543    // Render Path Nodes.
1544    renderPaths( k_RenderNodes );
1545
1546    if ( isValidSelection() )
1547    {
1548        // Fetch Current Node.
1549        VPathNode *node = mSelection.Path->getNode( mSelection.Node );
1550
1551        // ToPoint Node?
1552        if ( node->getOrientationMode().Type == VPathNode::k_OrientationToPoint )
1553        {
1554            // Project to Screen.
1555            Point3F screenPosition;
1556            project( node->getOrientationMode().Point, &screenPosition );
1557            if ( screenPosition.z <= 1.0f )
1558            {
1559                // Determine the center & size of the node rectangle.
1560                Point2I nodeCenter = Point2I( screenPosition.x, screenPosition.y );
1561                Point2I nodeHalfSize = Point2I( 8, 8 );
1562                // Determine Render Rectangle.
1563                RectI nodeRect;
1564                nodeRect.point  = nodeCenter - nodeHalfSize;
1565                nodeRect.extent = ( 2 * nodeHalfSize );
1566
1567                // Draw?
1568                if ( getBounds().overlaps( nodeRect ) )
1569                {
1570                    // Render the Point.
1571                    GFX->getDrawUtil()->drawRectFill( nodeRect, gNodeLookAtPointColor );
1572                }
1573            }
1574        }
1575    }
1576}
1577
1578void VPathEditor::renderPaths( const RenderType &pRenderType )
1579{
1580    SimSet *objectSet = VPath::getServerSet();
1581    for ( SimSetIterator itr( objectSet ); *itr; ++itr )
1582    {
1583        VPath *path = dynamic_cast<VPath*>( *itr );
1584        if ( path )
1585        {
1586            // Render Path.
1587            renderPath( pRenderType, path, ( path == mSelection.Path ) ? gPathColorSel : gPathColor );
1588        }
1589    }
1590}
1591
1592void VPathEditor::renderPath( const RenderType &pRenderType, VPath *pPath, const ColorI &pColor )
1593{
1594    if ( !pPath )
1595    {
1596        // Sanity!
1597        return;
1598    }
1599
1600    switch ( pRenderType )
1601    {
1602        case k_RenderSegments :
1603            {
1604                switch ( pPath->mPathType )
1605                {
1606                    case VPath::k_PathLinear : 
1607                        {
1608                            renderLinearPath( pPath, pColor );
1609
1610                        } break;
1611
1612                    case VPath::k_PathBezier :
1613                        {
1614                            renderBezierPath( pPath, pColor );
1615
1616                        } break;
1617                }
1618
1619            } break;
1620
1621        case k_RenderNodes :
1622            {
1623                // Fetch Draw Util.
1624                GFXDrawUtil *drawUtil = GFX->getDrawUtil();
1625
1626                // Fetch Bounds.
1627                RectI bounds = getBounds();
1628
1629                const Point2I nodeMinHalfSize( 8, 8 );
1630                for ( VPathNodeIterator itr = pPath->mNodeList.begin(); itr != pPath->mNodeList.end(); itr++ )
1631                {
1632                    // Fetch Node.
1633                    VPathNode *node = ( *itr );
1634
1635                    // Project to Screen.
1636                    Point3F screenPosition;
1637                    project( node->getWorldPosition(), &screenPosition );
1638                    if ( screenPosition.z > 1.0f )
1639                    {
1640                        continue;
1641                    }
1642
1643                    // Determine the node text information.
1644                    const char *nodeText = avar( "%d", ( itr - pPath->mNodeList.begin() ) );
1645                    const Point2I nodeTextHalfSize = Point2I( 0.5f * (F32)getControlProfile()->mFont->getStrWidth( nodeText ),
1646                                                              0.5f * (F32)getControlProfile()->mFont->getHeight() );
1647
1648                    // Determine the center & size of the node rectangle.
1649                    Point2I nodeCenter = Point2I( screenPosition.x, screenPosition.y );
1650                    Point2I nodeHalfSize = Point2I( nodeTextHalfSize.x + 3, nodeTextHalfSize.y + 3 );
1651                    nodeHalfSize.setMax( nodeMinHalfSize );
1652                    // Determine Render Rectangle.
1653                    RectI nodeRect;
1654                    nodeRect.point  = nodeCenter - nodeHalfSize;
1655                    nodeRect.extent = ( 2 * nodeHalfSize );
1656
1657                    // Draw?
1658                    if ( !bounds.overlaps( nodeRect ) )
1659                    {
1660                        continue;
1661                    }
1662
1663                    // Render the Point.
1664                    drawUtil->drawRectFill( nodeRect, pColor );
1665
1666                    // Draw the node index text.
1667                    drawUtil->setBitmapModulation( getControlProfile()->mFontColor );
1668                    drawUtil->drawText( getControlProfile()->mFont, nodeCenter - nodeTextHalfSize, nodeText );
1669                }
1670
1671            } break;
1672    }
1673}
1674
1675void VPathEditor::renderLinearPath( VPath *pPath, const ColorI &pColor )
1676{
1677    if ( pPath->mNodeList.size() < 2 )
1678    {
1679        // No Lines.
1680        return;
1681    }
1682
1683    PrimBuild::color( pColor );
1684    PrimBuild::begin( GFXLineStrip, ( pPath->mNodeList.size() + 1 ) );
1685
1686    for ( VPathNodeIterator itr = pPath->mNodeList.begin(); itr != pPath->mNodeList.end(); itr++ )
1687    {
1688        // Apply Vertex.
1689        PrimBuild::vertex3fv( ( *itr )->getWorldPosition() );
1690    }
1691
1692    // Loop Back.
1693    PrimBuild::vertex3fv( pPath->mNodeList.front()->getWorldPosition() );
1694
1695    PrimBuild::end();
1696}
1697
1698void VPathEditor::renderBezierPath( VPath *pPath, const ColorI &pColor )
1699{
1700    if ( pPath->mNodeList.size() < 2 )
1701    {
1702        // No Lines.
1703        return;
1704    }
1705
1706    PrimBuild::color( pColor );
1707    PrimBuild::begin( GFXLineStrip, U32( ( ( 1.01f / 0.01f ) + 1 ) * pPath->mNodeList.size() ) );
1708
1709    for ( VPathNodeIterator itr = pPath->mNodeList.begin(); itr != pPath->mNodeList.end(); itr++ )
1710    {
1711        // Fetch Nodes.
1712        VPathNode *srcNode = ( *itr );
1713        VPathNode *dstNode = ( itr == ( pPath->mNodeList.end() - 1 ) ) ? ( *( pPath->mNodeList.begin() ) ) : ( *( itr + 1 ) );
1714
1715        // Positions.
1716        const Point3F &pt0 = srcNode->getWorldPosition();
1717        const Point3F &pt3 = dstNode->getWorldPosition();
1718
1719        // Fetch Node Rotation Matrices.
1720        MatrixF mat0, mat1;
1721        srcNode->getWorldRotation().setMatrix( &mat0 );
1722        dstNode->getWorldRotation().setMatrix( &mat1 );
1723
1724        // Determine Tangent Axis.
1725        Point3F pt1(  VPath::gBezierAxis * srcNode->getWeight() );
1726        Point3F pt2( -VPath::gBezierAxis * dstNode->getWeight() );
1727
1728        // Rotate Axis.
1729        mat0.mulP( pt1 );
1730        mat1.mulP( pt2 );
1731
1732        // Offset Points.
1733        pt1 += pt0;
1734        pt2 += pt3;
1735
1736        for ( F32 t = 0.f, it = 1.f; t <= 1.f; t += 0.01f, it = ( 1.f - t ) )
1737        {
1738            // Calculate Position.
1739            Point3F pos = ( pt0 * it * it * it ) + ( 3 * pt1 * it * it * t ) + ( 3 * pt2 * it * t * t ) + ( pt3 * t * t * t );
1740            // Apply Vertex.
1741            PrimBuild::vertex3fv( pos );
1742        }
1743    }
1744
1745    PrimBuild::end();
1746}
1747
1748//-----------------------------------------------------------------------------
1749//
1750// History Events
1751//
1752//-----------------------------------------------------------------------------
1753
1754void VPathEditor::VPathEditorEditPathAction::undo( void )
1755{
1756    const MatrixF oldTransform = mTransform;
1757    const MatrixF newTransform = mPath->getTransform();
1758
1759    // Apply Old Values.
1760    mEditor->setPathTransform( oldTransform );
1761
1762    // The ol' Switcheroo.
1763    mTransform = newTransform;
1764
1765    // Update Selection.
1766    mEditor->updateSelection();
1767
1768    if ( mPath == mEditor->mSelection.Path )
1769    {
1770        // Arg Buffer.
1771        char buffer[32];
1772        dSprintf( buffer, sizeof( buffer ), "%d", mPath->getId() );
1773
1774        // Callback.
1775        Con::executef( mEditor, "onUpdatePath", buffer );
1776    }
1777
1778    // Set World Editor Dirty.
1779    mEditor->setWorldEditorDirty();
1780}
1781
1782void VPathEditor::VPathEditorEditPathAction::redo( void )
1783{
1784    // Undo.
1785    undo();
1786}
1787
1788void VPathEditor::VPathEditorEditNodeAction::undo( void )
1789{
1790    // Fetch Properties.
1791    const Point3F oldPosition = mNodePosition;
1792    const QuatF   oldRotation = mNodeRotation;
1793    const F32     oldWeight   = mNodeWeight;
1794    const VPathNode::sOrientation oldOrientation = mNodeOrientation;
1795
1796    VPathNode *node           = mPath->getNode( mNodeIndex );
1797    const Point3F newPosition = node->getLocalPosition();
1798    const QuatF   newRotation = node->getLocalRotation();
1799    const F32     newWeight   = node->getWeight();
1800    const VPathNode::sOrientation newOrientation = node->getOrientationMode();
1801
1802    // Apply Old Values.
1803    mPath->setNodePosition( mNodeIndex, oldPosition );
1804    mPath->setNodeRotation( mNodeIndex, oldRotation );
1805    mPath->setNodeWeight( mNodeIndex, oldWeight );
1806
1807    switch( oldOrientation.Type )
1808    {
1809    case VPathNode::k_OrientationFree :
1810        {
1811
1812            // Orient Free.
1813            mPath->setNodeOrientationMode( mNodeIndex, oldOrientation.Type );
1814
1815        } break;
1816
1817    case VPathNode::k_OrientationToPoint :
1818        {
1819
1820            // Orient To Point.
1821            mPath->setNodeOrientationMode( mNodeIndex, oldOrientation.Type, oldOrientation.Point );
1822
1823        } break;
1824    }
1825
1826    // The ol' Switcheroo.
1827    mNodePosition    = newPosition;
1828    mNodeRotation    = newRotation;
1829    mNodeWeight      = newWeight;
1830    mNodeOrientation = newOrientation;
1831
1832    // Update Selection.
1833    mEditor->updateSelection();
1834
1835    if ( mPath == mEditor->mSelection.Path )
1836    {
1837        // Arg Buffer.
1838        char buffer[3][32];
1839        dSprintf( buffer[0], sizeof( buffer[0] ), "%d", mPath->getId() );
1840        dSprintf( buffer[1], sizeof( buffer[1] ), "%d", mNodeIndex );
1841        dSprintf( buffer[2], sizeof( buffer[2] ), "%d", ( mEditor->mSelection.Node == mNodeIndex ) );
1842
1843        // Callback.
1844        Con::executef( mEditor, "onUpdateNode", buffer[0], buffer[1], buffer[2] );
1845    }
1846
1847    // Set World Editor Dirty.
1848    mEditor->setWorldEditorDirty();
1849}
1850
1851void VPathEditor::VPathEditorEditNodeAction::redo( void )
1852{
1853    // Undo.
1854    undo();
1855}
1856
1857void VPathEditor::VPathEditorAddNodeAction::undo( void )
1858{
1859    // Selected Node?
1860    if ( mNodeIndex == mEditor->mSelection.Node )
1861    {
1862        // Update Selection.
1863        mEditor->updateSelection( mEditor->mSelection.Path, -1 );
1864    }
1865
1866    // Delete Node.
1867    mPath->deleteNode( mNodeIndex );
1868
1869    // Update Size.
1870    mPath->updateContainer();
1871
1872    // Calculate Path.
1873    mPath->calculatePath();
1874
1875    // Set World Editor Dirty.
1876    mEditor->setWorldEditorDirty();
1877}
1878
1879void VPathEditor::VPathEditorAddNodeAction::redo( void )
1880{
1881    // Add Node.
1882    VPathNode *node = mPath->addNode( mNodePosition, mNodeRotation, mNodeWeight, mNodeIndex );
1883
1884    // Valid Node?
1885    if ( node )
1886    {
1887        // Update Size.
1888        mPath->updateContainer();
1889
1890        // Calculate Path.
1891        mPath->calculatePath();
1892    }
1893
1894    // Set World Editor Dirty.
1895    mEditor->setWorldEditorDirty();
1896}
1897
1898void VPathEditor::VPathEditorDeleteNodeAction::undo( void )
1899{
1900    // Add Node.
1901    VPathNode *node = mPath->addNode( mNodePosition, mNodeRotation, mNodeWeight, mNodeIndex );
1902
1903    // Valid Node?
1904    if ( node )
1905    {
1906        // Update Size.
1907        mPath->updateContainer();
1908
1909        // Calculate Path.
1910        mPath->calculatePath();
1911    }
1912
1913    // Set World Editor Dirty.
1914    mEditor->setWorldEditorDirty();
1915}
1916
1917void VPathEditor::VPathEditorDeleteNodeAction::redo( void )
1918{
1919    // Delete Node.
1920    mPath->deleteNode( mNodeIndex );
1921
1922    // Update Size.
1923    mPath->updateContainer();
1924
1925    // Calculate Path.
1926    mPath->calculatePath();
1927
1928    // Set World Editor Dirty.
1929    mEditor->setWorldEditorDirty();
1930}
1931
1932//-----------------------------------------------------------------------------
1933//
1934// Script Edit Methods
1935//
1936//-----------------------------------------------------------------------------
1937
1938DefineEngineMethod( VPathEditor, setNodePosition, void, (Point3F position), (Point3F::Zero), "( pPosition )" )
1939{
1940    // Valid Selection?
1941    if ( !object->isValidSelection() )
1942    {
1943        Con::warnf( "VPathEditor::setNodePosition() - Invalid Node Selection." );
1944        return;
1945    }
1946
1947    // Store.
1948    object->pushNodeEdit();
1949
1950    // Apply Update.
1951    object->setNodePosition( object->mSelection.Node, position );
1952
1953    // Create Undo Action.
1954    object->popNodeEdit();
1955}
1956
1957DefineEngineMethod( VPathEditor, setNodeRotation, void, (AngAxisF aa), (AngAxisF( Point3F( 0, 0, 1 ), 0)), "( pRotation )" )
1958{
1959    // Valid Selection?
1960    if ( !object->isValidSelection() )
1961    {
1962        Con::warnf( "VPathEditor::setNodeRotation() - Invalid Node Selection." );
1963        return;
1964    }
1965
1966    // Fetch Rotation.
1967    QuatF    rotation;
1968
1969    // Set Rotation.
1970    rotation.set( aa );
1971
1972    // Store.
1973    object->pushNodeEdit();
1974
1975    // Apply Update.
1976    object->setNodeRotation( object->mSelection.Node, rotation );
1977
1978    // Create Undo Action.
1979    object->popNodeEdit();
1980}
1981
1982DefineEngineMethod( VPathEditor, setNodeWeight, void, (F32 weight), (1), "( pWeight )" )
1983{
1984    // Valid Selection?
1985    if ( !object->isValidSelection() )
1986    {
1987        Con::warnf( "VPathEditor::setNodeWeight() - Invalid Node Selection." );
1988        return;
1989    }
1990
1991    // Store.
1992    object->pushNodeEdit();
1993
1994    // Apply Update.
1995    object->setNodeWeight( object->mSelection.Node, weight);
1996
1997    // Create Undo Action.
1998    object->popNodeEdit();
1999}
2000
2001DefineEngineMethod( VPathEditor, setNodeOrientationMode, void, (String orientationType, Point3F lookAtPoint), ("", Point3F::One), "( string pOrientationType, [vector pPoint] )" )
2002{
2003    // Valid Selection?
2004    if ( !object->isValidSelection() )
2005    {
2006        Con::warnf( "VPathEditor::setNodeOrientationMode() - Invalid Node Selection." );
2007        return;
2008    }
2009
2010    // Store.
2011    object->pushNodeEdit();
2012
2013    // Orient?
2014    const VPathNode::eOrientationType type = VPathNode::getOrientationTypeEnum(orientationType);
2015
2016    switch ( type )
2017    {
2018        case VPathNode::k_OrientationFree :
2019            {
2020
2021                // Apply Mode.
2022                object->setNodeOrientationMode( object->mSelection.Node, type );
2023
2024            } break;
2025
2026        case VPathNode::k_OrientationToPoint:
2027            {
2028                // Apply Mode.
2029                object->setNodeOrientationMode( object->mSelection.Node, type, lookAtPoint );
2030            } break;
2031    }
2032
2033    // Create Undo Action.
2034    object->popNodeEdit();
2035}
2036
2037//-----------------------------------------------------------------------------
2038//
2039// Utility
2040//
2041//-----------------------------------------------------------------------------
2042
2043bool Utility::FindNearestDistanceBetweenLines( const Point3F &pA0, const Point3F &pA1, const Point3F &pB0, const Point3F &pB1, Point3F *pOutA, Point3F *pOutB, F32 *pDist )
2044{
2045    const Point3F pA1A0 = ( pA1 - pA0 );
2046    if ( pA1A0.isZero() )
2047    {
2048        return false;
2049    }
2050
2051    const Point3F pB1B0 = ( pB1 - pB0 );
2052    if ( pB1B0.isZero() )
2053    {
2054        return false;
2055    }
2056
2057    const Point3F pA0B0 = ( pA0 - pB0 );
2058
2059    const F32 &d1343 = pA0B0.x * pB1B0.x + pA0B0.y * pB1B0.y + pA0B0.z * pB1B0.z;
2060    const F32 &d4321 = pB1B0.x * pA1A0.x + pB1B0.y * pA1A0.y + pB1B0.z * pA1A0.z;
2061    const F32 &d1321 = pA0B0.x * pA1A0.x + pA0B0.y * pA1A0.y + pA0B0.z * pA1A0.z;
2062    const F32 &d4343 = pB1B0.x * pB1B0.x + pB1B0.y * pB1B0.y + pB1B0.z * pB1B0.z;
2063    const F32 &d2121 = pA1A0.x * pA1A0.x + pA1A0.y * pA1A0.y + pA1A0.z * pA1A0.z;
2064
2065    const F32 &denom = d2121 * d4343 - d4321 * d4321;
2066    if ( mIsZero( denom ) )
2067    {
2068        return false;
2069    }
2070
2071    const F32 &mua = ( d1343 * d4321 - d1321 * d4343 ) / denom;
2072    const F32 &mub = ( d1343 + d4321 * mua ) / d4343;
2073
2074    *pOutA = pA0 + mua *pA1A0;
2075    *pOutB = pB0 + mub *pB1B0;
2076
2077    // Store Distance.
2078    *pDist = ( ( *pOutA ) - ( *pOutB ) ).len();
2079
2080    return true;
2081}
2082
2083bool Utility::IntersectLineSegment( const Point3F &pA0, const Point3F &pA1, const Point3F &pB0, const Point3F &pB1, const bool pSnap, Point3F *pX )
2084{
2085    //
2086    // Finding the intersection with the following method:
2087    // We have line a going from P1 to P2:
2088    //    Pa = P1 + ua( P2 - P1 )
2089    // and line b going from P3 to P4:
2090    //    Pb = P3 + ub( P4 - P3 )
2091    //
2092    // Solving for Pa = Pb:
2093    //    x1 + ua( x2 - x1 ) = x3 + ub( x4 - x3 )
2094    //    y1 + ua( y2 - y1 ) = y3 + ub( y4 - y3 )
2095    //
2096    // Solving for ua and ub:
2097    //    ua = ( ( x4 - x3 )( y1 - y3 ) - ( y4 - y3 )( x1 - x3 ) ) / d
2098    //    ub = ( ( x2 - x1 )( y1 - y3 ) - ( y2 - y1 )( x1 - x3 ) ) / d
2099    //    denom = ( y4 - y3 )( x2 - x1 ) - ( x4 - x3 )( y2 - y1 )
2100    //
2101    // x = x1 + ua( x2 - x1 )
2102    // y = y1 + ua( y2 - y1 )
2103    //
2104
2105    const F32 d = ( ( pB1.y - pB0.y ) * ( pA1.x - pA0.x ) ) - ( ( pB1.x - pB0.x ) * ( pA1.y - pA0.y ) );
2106
2107    if ( d == 0.0f )
2108    {
2109        // Lines are parallel
2110        return false;
2111    }
2112
2113    // Find the point of intersection
2114    const F32 uA = ( ( ( pB1.x - pB0.x ) * ( pA0.y - pB0.y ) ) - ( ( pB1.y - pB0.y ) * ( pA0.x - pB0.x ) ) ) / d;
2115    const F32 uB = ( ( ( pA1.x - pA0.x ) * ( pA0.y - pB0.y ) ) - ( ( pA1.y - pA0.y ) * ( pA0.x - pB0.x ) ) ) / d;
2116
2117    if ( !pSnap
2118         && ( ( uA < 0.0f ) || ( uA > 1.0f )
2119           || ( uB < 0.0f ) || ( uB > 1.0f ) ) )
2120    {
2121        return false;
2122    }
2123
2124    if ( pX )
2125    {
2126        if ( uA < 0.0f )
2127        {
2128            *pX = pA0;
2129        }
2130        else if ( uA > 1.f )
2131        {
2132            *pX = pA1;
2133        }
2134        else
2135        {
2136            // The path intersects the segment
2137            *pX = pA0 + uA * ( pA1 - pA0 );
2138        }
2139    }
2140
2141    return true;
2142}
2143
2144bool Utility::FindNearestPointOnLine( const Point3F &pSrcPosition, const Point3F &pA0, const Point3F &pA1, Point3F *pDstPosition )
2145{
2146    const Point3F up( 0.0f, 0.0f, 1.0f );
2147    
2148    Point3F dir = ( pA1 - pA0 );
2149    dir.normalize();
2150
2151    Point3F normal = mCross( dir, up );
2152    normal.normalize();
2153
2154    // Find the nearest intersection point between the point and the line
2155
2156    const Point3F b0 = pSrcPosition + ( normal * 100000.0f );
2157    const Point3F b1 = pSrcPosition - ( normal * 100000.0f );
2158
2159    return IntersectLineSegment( pA0, pA1, b0, b1, true, pDstPosition );
2160}
2161
2162F32 Utility::GetPitch( const VectorF &pVec )
2163{
2164    F32 pitch;
2165    if ( mFabs( pVec.x ) > mFabs( pVec.y ) )
2166    {
2167        pitch = mAtan2( mFabs( pVec.z ), mFabs( pVec.x ) );
2168    }
2169    else
2170    {
2171        pitch = mAtan2( mFabs( pVec.z ), mFabs( pVec.y ) );
2172    }
2173
2174    if ( pVec.z < 0.f )
2175    {
2176        pitch = -pitch;
2177    }
2178
2179    return pitch;
2180}
2181
2182F32 Utility::GetYaw( const VectorF &pVec )
2183{
2184    F32 yaw = mAtan2( pVec.x, pVec.y );
2185    if ( yaw < 0.f )
2186    {
2187        yaw += M_2PI_F;
2188    }
2189
2190    return yaw;
2191}
2192