VPathEditor.cpp
Engine/source/Verve/VPath/VPathEditor.cpp
Public Variables
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] )" )
ImplementEnumType(VPathEditorMode , "" )
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