gizmo.cpp
Engine/source/gui/worldEditor/gizmo.cpp
Public Functions
ConsoleDocClass(Gizmo , "@brief This class contains code <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> rendering and manipulating a 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gizmo\n\n</a>" "It is usually used as a helper within a TSEdit-derived control. " "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
ConsoleDocClass(GizmoProfile , "@brief This class contains behavior and rendering properties used " "by the <a href="/coding/class/classgizmo/">Gizmo</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">class\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
ImplementEnumType(GizmoAlignment , "Whether the gizmo should be aligned with the world, or with the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">internal\n\n</a>" )
ImplementEnumType(GizmoMode , "@internal" )
Detailed Description
Public Functions
ConsoleDocClass(Gizmo , "@brief This class contains code <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> rendering and manipulating a 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gizmo\n\n</a>" "It is usually used as a helper within a TSEdit-derived control. " "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
ConsoleDocClass(GizmoProfile , "@brief This class contains behavior and rendering properties used " "by the <a href="/coding/class/classgizmo/">Gizmo</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">class\n\n</a>" "Not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> game development, <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> editors or internal use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a> " " @internal" )
IMPLEMENT_CONOBJECT(Gizmo )
IMPLEMENT_CONOBJECT(GizmoProfile )
ImplementEnumType(GizmoAlignment , "Whether the gizmo should be aligned with the world, or with the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">internal\n\n</a>" )
ImplementEnumType(GizmoMode , "@internal" )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "gui/worldEditor/gizmo.h" 26 27#include "console/consoleTypes.h" 28#include "gfx/primBuilder.h" 29#include "gfx/gfxTransformSaver.h" 30#include "gfx/util/gfxFrustumSaver.h" 31#include "gfx/gfxDrawUtil.h" 32#include "T3D/gameBase/gameConnection.h" 33//#include "math/mathUtils.h" 34 35using namespace MathUtils; 36 37 38// Developer Notes: 39 40// How to... Calculate the SelectionAxis index representing the normal 41// of a plane, given a SelectionPlane index 42// normal = axisVector[2 - (planeIdx - 3 )]; 43 44// How to... Get the two AxisVectors of a selected plane 45// vec0 = mProjAxisVector[mAxisGizmoPlanarVectors[mSelectionIdx-3][0]]; 46// vec1 = mProjAxisVector[mAxisGizmoPlanarVectors[mSelectionIdx-3][1]]; 47 48ImplementEnumType(GizmoAlignment, 49 "Whether the gizmo should be aligned with the world, or with the object.\n" 50 "@internal\n\n") 51 { World, "World", "Align the gizmo with the world.\n" }, 52 { Object, "Object", "Align the gizmo with the object.\n" } 53EndImplementEnumType; 54 55ImplementEnumType( GizmoMode, 56 "@internal" ) 57 { NoneMode, "None" }, 58 { MoveMode, "Move" }, 59 { RotateMode, "Rotate" }, 60 { ScaleMode, "Scale" } 61EndImplementEnumType; 62 63 64//------------------------------------------------------------------------- 65// Unnamed namespace for static data 66//------------------------------------------------------------------------- 67 68namespace { 69 70 static S32 sgAxisRemap[3][3] = { 71 {0, 1, 2}, 72 {2, 0, 1}, 73 {1, 2, 0}, 74 }; 75 76 static VectorF sgAxisVectors[3] = { 77 VectorF(1.0f,0.0f,0.0f), 78 VectorF(0.0f,1.0f,0.0f), 79 VectorF(0.0f,0.0f,1.0f) 80 }; 81 82 static U32 sgPlanarVectors[3][2] = { 83 { 0, 1 }, // XY 84 { 0, 2 }, // XZ 85 { 1, 2 } // YZ 86 }; 87 88 static Point3F sgBoxPnts[] = { 89 Point3F(0.0f,0.0f,0.0f), 90 Point3F(0.0f,0.0f,1.0f), 91 Point3F(0.0f,1.0f,0.0f), 92 Point3F(0.0f,1.0f,1.0f), 93 Point3F(1.0f,0.0f,0.0f), 94 Point3F(1.0f,0.0f,1.0f), 95 Point3F(1.0f,1.0f,0.0f), 96 Point3F(1.0f,1.0f,1.0f) 97 }; 98 99 static U32 sgBoxVerts[][4] = { 100 {0,2,3,1}, // -x 101 {7,6,4,5}, // +x 102 {0,1,5,4}, // -y 103 {3,2,6,7}, // +y 104 {0,4,6,2}, // -z 105 {3,7,5,1} // +z 106 }; 107 108 static Point3F sgBoxNormals[] = { 109 Point3F(-1.0f, 0.0f, 0.0f), 110 Point3F( 1.0f, 0.0f, 0.0f), 111 Point3F( 0.0f,-1.0f, 0.0f), 112 Point3F( 0.0f, 1.0f, 0.0f), 113 Point3F( 0.0f, 0.0f,-1.0f), 114 Point3F( 0.0f, 0.0f, 1.0f) 115 }; 116 117 static Point3F sgConePnts[] = { 118 Point3F(0.0f, 0.0f, 0.0f), 119 Point3F(-1.0f, 0.0f, -0.25f), 120 Point3F(-1.0f, -0.217f, -0.125f), 121 Point3F(-1.0f, -0.217f, 0.125f), 122 Point3F(-1.0f, 0.0f, 0.25f), 123 Point3F(-1.0f, 0.217f, 0.125f), 124 Point3F(-1.0f, 0.217f, -0.125f), 125 Point3F(-1.0f, 0.0f, 0.0f) 126 }; 127 128 static U32 sgConeVerts[][3] = { 129 {0, 2, 1}, 130 {0, 3, 2}, 131 {0, 4, 3}, 132 {0, 5, 4}, 133 {0, 6, 5}, 134 {0, 1, 6}, 135 {7, 1, 6}, // Base 136 {7, 6, 5}, 137 {7, 5, 4}, 138 {7, 4, 3}, 139 {7, 3, 2}, 140 {7, 2, 1} 141 }; 142 143 static Point3F sgCenterBoxPnts[] = { 144 Point3F(-0.5f, -0.5f, -0.5f), 145 Point3F(-0.5f, -0.5f, 0.5f), 146 Point3F(-0.5f, 0.5f, -0.5f), 147 Point3F(-0.5f, 0.5f, 0.5f), 148 Point3F( 0.5f, -0.5f, -0.5f), 149 Point3F( 0.5f, -0.5f, 0.5f), 150 Point3F( 0.5f, 0.5f, -0.5f), 151 Point3F( 0.5f, 0.5f, 0.5f) 152 }; 153 154 static Point3F sgCirclePnts[] = { 155 Point3F(0.0f, 0.0f, -0.5f), 156 Point3F(0.0f, 0.354f, -0.354f), 157 Point3F(0.0f, 0.5f, 0), 158 Point3F(0.0f, 0.354f, 0.354f), 159 Point3F(0.0f, 0.0f, 0.5f), 160 Point3F(0.0f, -0.354f, 0.354f), 161 Point3F(0.0f, -0.5f, 0), 162 Point3F(0.0f, -0.354f, -0.354f), 163 Point3F(0.0f, 0.0f, -0.5f), 164 }; 165} 166 167 168//------------------------------------------------------------------------- 169// GizmoProfile Class 170//------------------------------------------------------------------------- 171 172GizmoProfile::GizmoProfile() 173{ 174 mode = MoveMode; 175 alignment = World; 176 screenLen = 100; 177 renderWhenUsed = false; 178 renderInfoText = true; 179 renderPlane = true; 180 renderPlaneHashes = true; 181 renderSolid = false; 182 renderMoveGrid = true; 183 gridColor.set(255,255,255,20); 184 planeDim = 500.0f; 185 186 gridSize.set(10,10,10); 187 snapToGrid = false; 188 allowSnapRotations = true; 189 rotationSnap = 15.0f; 190 allowSnapScale = true; 191 scaleSnap = 0.1f; 192 193 rotateScalar = 0.8f; 194 scaleScalar = 0.8f; 195 196 axisColors[0].set( 255, 0, 0 ); 197 axisColors[1].set( 0, 255, 0 ); 198 axisColors[2].set( 0, 0, 255 ); 199 200 activeColor.set( 237, 219, 0 ); 201 inActiveColor.set( 170, 170, 170 ); 202 203 centroidColor.set( 255, 255, 255 ); 204 centroidHighlightColor.set( 255, 0, 255 ); 205 206 restoreDefaultState(); 207} 208 209void GizmoProfile::restoreDefaultState() 210{ 211 flags = U32_MAX; 212 flags &= ~CanRotateUniform; 213 allAxesScaleUniform = false; 214} 215 216IMPLEMENT_CONOBJECT( GizmoProfile ); 217 218ConsoleDocClass( GizmoProfile, 219 "@brief This class contains behavior and rendering properties used " 220 "by the Gizmo class\n\n" 221 "Not intended for game development, for editors or internal use only.\n\n " 222 "@internal"); 223 224bool GizmoProfile::onAdd() 225{ 226 if ( !Parent::onAdd() ) 227 return false; 228 229 const char* fontCacheDirectory = Con::getVariable("$GUI::fontCacheDirectory"); 230 font = GFont::create( "Arial", 10, fontCacheDirectory, TGE_ANSI_CHARSET); 231 if ( !font ) 232 { 233 Con::errorf( "GizmoProfile::onAdd - failed to load font!" ); 234 return false; 235 } 236 237 return true; 238} 239 240void GizmoProfile::initPersistFields() 241{ 242 addField( "alignment", TYPEID< GizmoAlignment >(), Offset(alignment, GizmoProfile ) ); 243 addField( "mode", TYPEID< GizmoMode >(), Offset(mode, GizmoProfile ) ); 244 245 addField( "snapToGrid", TypeBool, Offset(snapToGrid, GizmoProfile) ); 246 addField( "allowSnapRotations", TypeBool, Offset(allowSnapRotations, GizmoProfile) ); 247 addField( "rotationSnap", TypeF32, Offset(rotationSnap, GizmoProfile) ); 248 addField( "allowSnapScale", TypeBool, Offset(allowSnapScale, GizmoProfile) ); 249 addField( "scaleSnap", TypeF32, Offset(scaleSnap, GizmoProfile) ); 250 addField( "renderWhenUsed", TypeBool, Offset(renderWhenUsed, GizmoProfile) ); 251 addField( "renderInfoText", TypeBool, Offset(renderInfoText, GizmoProfile) ); 252 addField( "renderPlane", TypeBool, Offset(renderPlane, GizmoProfile) ); 253 addField( "renderPlaneHashes", TypeBool, Offset(renderPlaneHashes, GizmoProfile) ); 254 addField( "renderSolid", TypeBool, Offset(renderSolid, GizmoProfile) ); 255 addField( "renderMoveGrid", TypeBool, Offset( renderMoveGrid, GizmoProfile ) ); 256 addField( "gridColor", TypeColorI, Offset(gridColor, GizmoProfile) ); 257 addField( "planeDim", TypeF32, Offset(planeDim, GizmoProfile) ); 258 addField( "gridSize", TypePoint3F, Offset(gridSize, GizmoProfile) ); 259 addField( "screenLength", TypeS32, Offset(screenLen, GizmoProfile) ); 260 addField( "rotateScalar", TypeF32, Offset(rotateScalar, GizmoProfile) ); 261 addField( "scaleScalar", TypeF32, Offset(scaleScalar, GizmoProfile) ); 262 addField( "flags", TypeS32, Offset(flags, GizmoProfile) ); 263} 264 265void GizmoProfile::consoleInit() 266{ 267 Parent::consoleInit(); 268 269 Con::setIntVariable( "$GizmoFlag::CanRotate", CanRotate ); 270 Con::setIntVariable( "$GizmoFlag::CanRotateX", CanRotateX ); 271 Con::setIntVariable( "$GizmoFlag::CanRotateY", CanRotateY ); 272 Con::setIntVariable( "$GizmoFlag::CanRotateZ", CanRotateZ ); 273 Con::setIntVariable( "$GizmoFlag::CanRotateScreen", CanRotateScreen ); 274 Con::setIntVariable( "$GizmoFlag::CanRotateUniform", CanRotateUniform ); 275 Con::setIntVariable( "$GizmoFlag::CanScale", CanScale ); 276 Con::setIntVariable( "$GizmoFlag::CanScaleX", CanScaleX ); 277 Con::setIntVariable( "$GizmoFlag::CanScaleY", CanScaleY ); 278 Con::setIntVariable( "$GizmoFlag::CanScaleZ", CanScaleZ ); 279 Con::setIntVariable( "$GizmoFlag::CanScaleUniform", CanScaleUniform ); 280 Con::setIntVariable( "$GizmoFlag::CanTranslate", CanTranslate ); 281 Con::setIntVariable( "$GizmoFlag::CanTranslateX", CanTranslateX ); 282 Con::setIntVariable( "$GizmoFlag::CanTranslateY", CanTranslateY ); 283 Con::setIntVariable( "$GizmoFlag::CanTranslateZ", CanTranslateZ ); 284 Con::setIntVariable( "$GizmoFlag::CanTranslateUniform", CanTranslateUniform ); 285 Con::setIntVariable( "$GizmoFlag::PlanarHandlesOn", PlanarHandlesOn ); 286} 287 288//------------------------------------------------------------------------- 289// Gizmo Class 290//------------------------------------------------------------------------- 291 292F32 Gizmo::smProjectDistance = 20000.0f; 293 294Gizmo::Gizmo() 295: mProfile( NULL ), 296 mSelectionIdx( -1 ), 297 mCameraMat( true ), 298 mTransform( true ), 299 mObjectMat( true ), 300 mObjectMatInv( true ), 301 mCurrentTransform( true ), 302 mSavedTransform( true ), 303 mSavedScale( 0,0,0 ), 304 mDeltaScale( 0,0,0 ), 305 mDeltaRot( 0,0,0 ), 306 mDeltaPos( 0,0,0 ), 307 mDeltaTotalPos( 0,0,0 ), 308 mDeltaTotalRot( 0,0,0 ), 309 mDeltaTotalScale( 0,0,0 ), 310 mCurrentAlignment( World ), 311 mCurrentMode( MoveMode ), 312 mDirty( false ), 313 mMouseDownPos( -1,-1 ), 314 mMouseDown( false ), 315 mLastWorldMat( true ), 316 mLastProjMat( true ), 317 mLastViewport( 0, 0, 10, 10 ), 318 mLastCameraFOV( 1.f ), 319 mElipseCursorCollideVecSS( 1.0f, 0.0f, 0.0f ), 320 mElipseCursorCollidePntSS( 0.0f, 0.0f, 0.0f ), 321 mHighlightCentroidHandle( false ), 322 mHighlightAll( false ), 323 mGridPlaneEnabled( true ), 324 mMoveGridEnabled( true ), 325 mMoveGridSize( 20.f ), 326 mMoveGridSpacing( 1.f ) 327{ 328 mUniformHandleEnabled = true; 329 mAxisEnabled[0] = mAxisEnabled[1] = mAxisEnabled[2] = true; 330} 331 332Gizmo::~Gizmo() 333{ 334} 335 336IMPLEMENT_CONOBJECT( Gizmo ); 337 338ConsoleDocClass( Gizmo, 339 "@brief This class contains code for rendering and manipulating a 3D gizmo\n\n" 340 "It is usually used as a helper within a TSEdit-derived control. " 341 "Not intended for game development, for editors or internal use only.\n\n " 342 "@internal"); 343 344// SimObject Methods... 345 346bool Gizmo::onAdd() 347{ 348 if ( !Parent::onAdd() ) 349 return false; 350 351 if ( !mProfile ) 352 return false; 353 354 mCurrentAlignment = mProfile->alignment; 355 mCurrentMode = mProfile->mode; 356 357 return true; 358} 359 360void Gizmo::onRemove() 361{ 362 Parent::onRemove(); 363} 364 365void Gizmo::initPersistFields() 366{ 367 Parent::initPersistFields(); 368 369 //addField( "profile",) 370} 371 372// Gizmo Accessors and Mutators... 373 374void Gizmo::set( const MatrixF &objMat, const Point3F &worldPos, const Point3F &objScale ) 375{ 376 if ( mMouseDown ) 377 return; 378 379 mCurrentAlignment = _filteredAlignment(); 380 381 if ( mCurrentAlignment == World ) 382 { 383 mTransform.identity(); 384 mTransform.setPosition( worldPos ); 385 mScale = objScale; 386 mObjectMat = objMat; 387 } 388 else 389 { 390 mTransform = objMat; 391 mTransform.setPosition( worldPos ); 392 mScale = objScale; 393 mObjectMat.identity(); 394 } 395 396 mCurrentTransform = objMat; 397 mObjectMat.invertTo( &mObjectMatInv ); 398} 399 400Gizmo::Selection Gizmo::getSelection() 401{ 402 if ( mProfile->mode == NoneMode ) 403 return None; 404 405 return (Selection)mSelectionIdx; 406} 407 408VectorF Gizmo::selectionToAxisVector( Selection axis ) 409{ 410 if ( axis < Axis_X || axis > Axis_Z ) 411 return VectorF(0,0,0); 412 413 return sgAxisVectors[(U32)axis]; 414} 415 416bool Gizmo::collideAxisGizmo( const Gui3DMouseEvent & event ) 417{ 418 if ( mProfile->mode == NoneMode ) 419 return false; 420 421 _calcAxisInfo(); 422 423 // Early out if we are in a mode that is disabled. 424 if ( mProfile->mode == RotateMode && !(mProfile->flags & GizmoProfile::CanRotate ) ) 425 return false; 426 if ( mProfile->mode == MoveMode && !(mProfile->flags & GizmoProfile::CanTranslate ) ) 427 return false; 428 if ( mProfile->mode == ScaleMode && !(mProfile->flags & GizmoProfile::CanScale ) ) 429 return false; 430 431 VectorF camPos; 432 if( GFX->isFrustumOrtho() ) 433 camPos = event.pos; 434 else 435 camPos = mCameraPos; 436 437 VectorF toGizmoVec; 438 439 // get the projected size... 440 441 toGizmoVec = mOrigin - mCameraPos; 442 toGizmoVec.normalizeSafe(); 443 444 PlaneF clipPlane( mOrigin, toGizmoVec ); 445 446 mSelectionIdx = -1; 447 Point3F end = camPos + event.vec * smProjectDistance; 448 449 if ( mProfile->mode == RotateMode ) 450 { 451 const Point3F mousePntSS( (F32)event.mousePoint.x, (F32)event.mousePoint.y, 0.0f ); 452 const F32 axisCollisionThresholdSS = 10.0f; 453 454 455 Point3F originSS; 456 MathUtils::mProjectWorldToScreen( mOrigin, &originSS, mLastViewport, mLastWorldMat, mLastProjMat ); 457 originSS.z = 0.0f; 458 459 const F32 originDistSS = mAbs( ( mousePntSS - originSS ).len() ); 460 461 // Check for camera facing axis rotation handle collision. 462 if ( mScreenRotateHandleEnabled ) 463 { 464 const F32 distSS = mAbs( ( (F32)mProfile->screenLen * 0.7f ) - originDistSS ); 465 466 if ( distSS < axisCollisionThresholdSS ) 467 { 468 mSelectionIdx = Custom1; 469 470 Point3F normal = mousePntSS - originSS; 471 normal.normalizeSafe(); 472 Point3F tangent = mCross( -normal, Point3F(0,0,1) ); 473 tangent.normalizeSafe(); 474 475 mElipseCursorCollidePntSS = mousePntSS; 476 mElipseCursorCollideVecSS = tangent; 477 mElipseCursorCollideVecSS.z = 0.0f; 478 mElipseCursorCollideVecSS.normalizeSafe(); 479 480 return true; 481 } 482 483 } 484 485 // Check for x/y/z axis ellipse handle collision. 486 // We do this as a screen-space pixel distance test between 487 // the cursor position and the ellipse handle projected to the screen 488 // as individual segments. 489 { 490 const F32 ellipseRadiusWS = mProjLen * 0.5f; 491 const U32 segments = 40; 492 const F32 stepRadians = mDegToRad(360.0f) / segments; 493 494 U32 x,y,z; 495 F32 ang0, ang1, distSS; 496 Point3F temp, pnt0, pnt1, closestPntSS; 497 bool valid0, valid1; 498 499 MatrixF worldToGizmo = mTransform; 500 worldToGizmo.inverse(); 501 PlaneF clipPlaneGS; // Clip plane in gizmo space. 502 mTransformPlane( worldToGizmo, Point3F(1,1,1), clipPlane, &clipPlaneGS ); 503 504 for ( U32 i = 0; i < 3; i++ ) 505 { 506 if ( !mAxisEnabled[i] ) 507 continue; 508 509 x = sgAxisRemap[i][0]; 510 y = sgAxisRemap[i][1]; 511 z = sgAxisRemap[i][2]; 512 513 for ( U32 j = 1; j <= segments; j++ ) 514 { 515 ang0 = (j-1) * stepRadians; 516 ang1 = j * stepRadians; 517 518 temp.x = 0.0f; 519 temp.y = mCos(ang0) * ellipseRadiusWS; 520 temp.z = mSin(ang0) * ellipseRadiusWS; 521 pnt0.set( temp[x], temp[y], temp[z] ); 522 523 temp.x = 0.0f; 524 temp.y = mCos(ang1) * ellipseRadiusWS; 525 temp.z = mSin(ang1) * ellipseRadiusWS; 526 pnt1.set( temp[x], temp[y], temp[z] ); 527 528 valid0 = ( clipPlaneGS.whichSide(pnt0) == PlaneF::Back ); 529 valid1 = ( clipPlaneGS.whichSide(pnt1) == PlaneF::Back ); 530 531 if ( !valid0 || !valid1 ) 532 continue; 533 534 // Transform points from gizmo space to world space. 535 536 mTransform.mulP( pnt0 ); 537 mTransform.mulP( pnt1 ); 538 539 // Transform points from gizmo space to screen space. 540 541 valid0 = MathUtils::mProjectWorldToScreen( pnt0, &pnt0, mLastViewport, mLastWorldMat, mLastProjMat ); 542 valid1 = MathUtils::mProjectWorldToScreen( pnt1, &pnt1, mLastViewport, mLastWorldMat, mLastProjMat ); 543 544 // Get distance from the cursor. 545 546 closestPntSS = MathUtils::mClosestPointOnSegment( Point3F( pnt0.x, pnt0.y, 0.0f ), Point3F( pnt1.x, pnt1.y, 0.0f ), mousePntSS ); 547 distSS = ( closestPntSS - mousePntSS ).len(); 548 549 if ( distSS < axisCollisionThresholdSS ) 550 { 551 mSelectionIdx = i; 552 mElipseCursorCollidePntSS = mousePntSS; 553 mElipseCursorCollideVecSS = pnt1 - pnt0; 554 mElipseCursorCollideVecSS.z = 0.0f; 555 mElipseCursorCollideVecSS.normalizeSafe(); 556 557 return true; 558 } 559 } 560 } 561 } 562 563 // Check for sphere surface collision 564 if ( originDistSS <= (F32)mProfile->screenLen * 0.5f ) 565 { 566 // If this style manipulation is desired it must also be implemented in onMouseDragged. 567 //mSelectionIdx = Custom2; 568 //return true; 569 } 570 } 571 572 // Check if we've hit the uniform scale handle... 573 if ( mUniformHandleEnabled ) 574 { 575 F32 tipScale = mProjLen * 0.1f; 576 Point3F sp( tipScale, tipScale, tipScale ); 577 578 Point3F min = mOrigin - sp; 579 Point3F max = mOrigin + sp; 580 Box3F uhandle(min, max); 581 if ( uhandle.collideLine( camPos, end ) ) 582 { 583 mSelectionIdx = Centroid; 584 return true; 585 } 586 } 587 588 // Check if we've hit the planar handles... 589 if ( ( mProfile->mode == MoveMode || mProfile->mode == ScaleMode ) && 590 ( mProfile->flags & GizmoProfile::PlanarHandlesOn ) ) 591 { 592 for ( U32 i = 0; i < 3; i++ ) 593 { 594 Point3F p1 = mProjAxisVector[sgPlanarVectors[i][0]]; 595 Point3F p2 = mProjAxisVector[sgPlanarVectors[i][1]]; 596 VectorF normal; 597 mCross(p1, p2, &normal); 598 599 if(normal.isZero()) 600 continue; 601 602 PlaneF plane(mOrigin, normal); 603 604 p1 *= mProjLen * 0.5f; 605 p2 *= mProjLen * 0.5f; 606 607 F32 scale = 0.5f; 608 609 Point3F poly [] = { 610 Point3F(mOrigin + p1 + p2 * scale), 611 Point3F(mOrigin + p1 + p2), 612 Point3F(mOrigin + p1 * scale + p2), 613 Point3F(mOrigin + (p1 + p2) * scale) 614 }; 615 616 Point3F end = camPos + event.vec * smProjectDistance; 617 F32 t = plane.intersect(camPos, end); 618 if ( t >= 0 && t <= 1 ) 619 { 620 Point3F pos; 621 pos.interpolate(camPos, end, t); 622 623 // check if inside our 'poly' of this axisIdx vector... 624 bool inside = true; 625 for(U32 j = 0; inside && (j < 4); j++) 626 { 627 U32 k = (j+1) % 4; 628 VectorF vec1 = poly[k] - poly[j]; 629 VectorF vec2 = pos - poly[k]; 630 631 if(mDot(vec1, vec2) > 0.f) 632 inside = false; 633 } 634 635 // 636 if ( inside ) 637 { 638 mSelectionIdx = i+3; 639 //mAxisGizmoSelPlane = plane; 640 //mAxisGizmoSelPlaneIndex = i; 641 //mAxisGizmoSelPlanePoint = pos; 642 //mAxisGizmoSelStart = camPos; 643 return true; 644 } 645 } 646 } 647 } 648 649 if ( mCurrentMode == RotateMode ) 650 return false; 651 652 // Check if we've hit an axis... 653 for ( U32 i = 0; i < 3; i++ ) 654 { 655 if ( !mAxisEnabled[i] ) 656 continue; 657 658 VectorF up, normal; 659 mCross(toGizmoVec, mProjAxisVector[i], &up); 660 mCross(up, mProjAxisVector[i], &normal); 661 662 if ( normal.isZero() ) 663 continue; 664 665 PlaneF plane( mOrigin, normal ); 666 667 // width of the axisIdx poly is 1/10 the run 668 Point3F a = up * mProjLen / 10; 669 Point3F b = mProjAxisVector[i] * mProjLen; 670 671 Point3F poly [] = { 672 Point3F(mOrigin + a), 673 Point3F(mOrigin + a + b), 674 Point3F(mOrigin - a + b), 675 Point3F(mOrigin - a) 676 }; 677 678 F32 t = plane.intersect(camPos, end); 679 if ( t >= 0 && t <= 1 ) 680 { 681 Point3F pos; 682 pos.interpolate(camPos, end, t); 683 684 // check if inside our 'poly' of this axisIdx vector... 685 bool inside = true; 686 for ( U32 j = 0; inside && (j < 4); j++ ) 687 { 688 U32 k = (j+1) % 4; 689 VectorF vec1 = poly[k] - poly[j]; 690 VectorF vec2 = pos - poly[k]; 691 692 if ( mDot(vec1, vec2) > 0.f ) 693 inside = false; 694 } 695 696 // 697 if(inside) 698 { 699 mSelectionIdx = i; 700 701 return true; 702 } 703 } 704 } 705 706 return false; 707} 708 709void Gizmo::on3DMouseDown( const Gui3DMouseEvent & event ) 710{ 711 _updateState(); 712 713 mMouseDown = true; 714 715 if ( mProfile->mode == NoneMode ) 716 return; 717 718 // Save the current transforms, need this for some 719 // operations that occur on3DMouseDragged. 720 721 mSavedTransform = mTransform; 722 mSavedScale = mScale; 723 mSavedRot = mTransform.toEuler(); 724 725 mMouseDownPos = event.mousePoint; 726 mLastAngle = 0.0f; 727 mLastScale = mScale; 728 mLastMouseEvent = event; 729 mSign = 0.0f; 730 731 _calcAxisInfo(); 732 733 // Calculate mMouseCollideLine and mMouseDownProjPnt 734 // which are used in on3DMouseDragged. 735 736 if ( mProfile->mode == MoveMode || mProfile->mode == ScaleMode ) 737 { 738 if ( mSelectionIdx >= Axis_X && mSelectionIdx <= Axis_Z ) 739 { 740 MathUtils::Line clickLine; 741 clickLine.origin = event.pos; 742 clickLine.direction = event.vec; 743 744 VectorF objectAxisVector = sgAxisVectors[mSelectionIdx]; 745 VectorF worldAxisVector = objectAxisVector; 746 mTransform.mulV( worldAxisVector ); 747 748 MathUtils::Line axisLine; 749 axisLine.origin = mTransform.getPosition(); 750 axisLine.direction = worldAxisVector; 751 752 mMouseCollideLine = axisLine; 753 754 LineSegment segment; 755 mShortestSegmentBetweenLines( clickLine, axisLine, &segment ); 756 757 mMouseDownProjPnt = segment.p1; 758 } 759 else if ( mSelectionIdx >= Plane_XY && mSelectionIdx <= Plane_YZ ) 760 { 761 VectorF objectPlaneNormal = sgAxisVectors[2 - (mSelectionIdx - 3 )]; 762 VectorF worldPlaneNormal = objectPlaneNormal; 763 mTransform.mulV( worldPlaneNormal ); 764 765 PlaneF plane( mTransform.getPosition(), worldPlaneNormal ); 766 767 mMouseCollidePlane = plane; 768 769 Point3F intersectPnt; 770 if ( plane.intersect( event.pos, event.vec, &intersectPnt ) ) 771 { 772 mMouseDownProjPnt = intersectPnt; 773 } 774 775 // We also calculate the line to be used later. 776 777 VectorF objectAxisVector(0,0,0); 778 objectAxisVector += sgAxisVectors[sgPlanarVectors[mSelectionIdx-3][0]]; 779 objectAxisVector += sgAxisVectors[sgPlanarVectors[mSelectionIdx-3][1]]; 780 objectAxisVector.normalize(); 781 782 VectorF worldAxisVector = objectAxisVector; 783 mTransform.mulV( worldAxisVector ); 784 785 MathUtils::Line axisLine; 786 axisLine.origin = mTransform.getPosition(); 787 axisLine.direction = worldAxisVector; 788 789 mMouseCollideLine = axisLine; 790 } 791 else if ( mSelectionIdx == Centroid ) 792 { 793 VectorF normal; 794 mCameraMat.getColumn(1,&normal); 795 normal = -normal; 796 797 PlaneF plane( mOrigin, normal ); 798 799 mMouseCollidePlane = plane; 800 801 Point3F intersectPnt; 802 if ( plane.intersect( event.pos, event.vec, &intersectPnt ) ) 803 { 804 mMouseDownProjPnt = intersectPnt; 805 } 806 } 807 } 808 else if ( mProfile->mode == RotateMode ) 809 { 810 VectorF camPos; 811 if( GFX->isFrustumOrtho() ) 812 camPos = event.pos; 813 else 814 camPos = mCameraPos; 815 816 if ( 0 <= mSelectionIdx && mSelectionIdx <= 2 ) 817 { 818 // Nothing to do, we already have mElipseCursorCollidePntSS 819 // and mElipseCursorCollideVecSS set. 820 } 821 else if ( mSelectionIdx == Custom1 ) 822 { 823 // Nothing to do, we already have mElipseCursorCollidePntSS 824 // and mElipseCursorCollideVecSS set. 825 } 826 else if ( mSelectionIdx == Centroid ) 827 { 828 // The Centroid handle for rotation mode is not implemented to do anything. 829 // It can be handled by the class making use of the Gizmo. 830 } 831 } 832} 833 834void Gizmo::on3DMouseUp( const Gui3DMouseEvent &event ) 835{ 836 _updateState(); 837 mMouseDown = false; 838 mDeltaTotalPos.zero(); 839 mDeltaTotalScale.zero(); 840 mDeltaTotalRot.zero(); 841 842 // Done with a drag operation, recenter our orientation to the world. 843 if ( mCurrentAlignment == World ) 844 { 845 Point3F pos = mTransform.getPosition(); 846 mTransform.identity(); 847 mTransform.setPosition( pos ); 848 } 849} 850 851void Gizmo::on3DMouseMove( const Gui3DMouseEvent & event ) 852{ 853 _updateState( false ); 854 855 if ( mProfile->mode == NoneMode ) 856 return; 857 858 collideAxisGizmo( event ); 859 860 mLastMouseEvent = event; 861} 862 863void Gizmo::on3DMouseDragged( const Gui3DMouseEvent & event ) 864{ 865 _updateState( false ); 866 867 if ( !mProfile || mProfile->mode == NoneMode || mSelectionIdx == None ) 868 return; 869 870 // If we got a dragged event without the mouseDown flag the drag operation 871 // must have been canceled by a mode change, ignore further dragged events. 872 if ( !mMouseDown ) 873 return; 874 875 _calcAxisInfo(); 876 877 if ( mProfile->mode == MoveMode || mProfile->mode == ScaleMode ) 878 { 879 Point3F projPnt = mOrigin; 880 881 // Project the mouse position onto the line/plane of manipulation... 882 883 if ( mSelectionIdx >= 0 && mSelectionIdx <= 2 ) 884 { 885 MathUtils::Line clickLine; 886 clickLine.origin = event.pos; 887 clickLine.direction = event.vec; 888 889 LineSegment segment; 890 mShortestSegmentBetweenLines( clickLine, mMouseCollideLine, &segment ); 891 892 projPnt = segment.p1; 893 894 // snap to the selected axisIdx, if required 895 Point3F snapPnt = _snapPoint(projPnt); 896 897 if ( mSelectionIdx < 3 ) 898 { 899 projPnt[mSelectionIdx] = snapPnt[mSelectionIdx]; 900 } 901 else 902 { 903 projPnt[sgPlanarVectors[mSelectionIdx-3][0]] = snapPnt[sgPlanarVectors[mSelectionIdx-3][0]]; 904 projPnt[sgPlanarVectors[mSelectionIdx-3][1]] = snapPnt[sgPlanarVectors[mSelectionIdx-3][1]]; 905 } 906 } 907 else if ( 3 <= mSelectionIdx && mSelectionIdx <= 5 ) 908 { 909 if ( mProfile->mode == MoveMode ) 910 { 911 Point3F intersectPnt; 912 if ( mMouseCollidePlane.intersect( event.pos, event.vec, &intersectPnt ) ) 913 { 914 projPnt = intersectPnt; 915 916 // snap to the selected axisIdx, if required 917 Point3F snapPnt = _snapPoint(projPnt); 918 projPnt[sgPlanarVectors[mSelectionIdx-3][0]] = snapPnt[sgPlanarVectors[mSelectionIdx-3][0]]; 919 projPnt[sgPlanarVectors[mSelectionIdx-3][1]] = snapPnt[sgPlanarVectors[mSelectionIdx-3][1]]; 920 } 921 } 922 else // ScaleMode 923 { 924 MathUtils::Line clickLine; 925 clickLine.origin = event.pos; 926 clickLine.direction = event.vec; 927 928 LineSegment segment; 929 mShortestSegmentBetweenLines( clickLine, mMouseCollideLine, &segment ); 930 931 projPnt = segment.p1; 932 } 933 } 934 else if ( mSelectionIdx == Centroid ) 935 { 936 Point3F intersectPnt; 937 if ( mMouseCollidePlane.intersect( event.pos, event.vec, &intersectPnt ) ) 938 { 939 projPnt = _snapPoint( intersectPnt ); 940 } 941 } 942 943 // Perform the manipulation... 944 945 if ( mProfile->mode == MoveMode ) 946 { 947 // Clear deltas we aren't using... 948 mDeltaRot.zero(); 949 mDeltaScale.zero(); 950 951 Point3F newPosition; 952 if( mProfile->snapToGrid ) 953 { 954 Point3F snappedMouseDownProjPnt = _snapPoint( mMouseDownProjPnt ); 955 mDeltaTotalPos = projPnt - snappedMouseDownProjPnt; 956 newPosition = projPnt; 957 } 958 else 959 { 960 mDeltaTotalPos = projPnt - mMouseDownProjPnt; 961 newPosition = mSavedTransform.getPosition() + mDeltaTotalPos; 962 } 963 964 mDeltaPos = newPosition - mTransform.getPosition(); 965 mTransform.setPosition( newPosition ); 966 967 mCurrentTransform.setPosition( newPosition ); 968 } 969 else // ScaleMode 970 { 971 // This is the world-space axis we want to scale 972 //VectorF axis = sgAxisVectors[mSelectionIdx]; 973 974 // Find its object-space components... 975 //MatrixF mat = mObjectMat; 976 //mat.inverse(); 977 //mat.mulV(axis); 978 979 // Which needs to always be positive, this is a 'scale' transformation 980 // not really a 'vector' transformation. 981 //for ( U32 i = 0; i < 3; i++ ) 982 // axis[i] = mFabs(axis[i]); 983 984 //axis.normalizeSafe(); 985 986 // Clear deltas we aren't using... 987 mDeltaRot.zero(); 988 mDeltaPos.zero(); 989 990 991 // Calculate the deltaScale... 992 VectorF deltaScale(0,0,0); 993 994 if ( 0 <= mSelectionIdx && mSelectionIdx <= 2 ) 995 { 996 // Are we above or below the starting position relative to this axis? 997 PlaneF plane( mMouseDownProjPnt, mProjAxisVector[mSelectionIdx] ); 998 F32 sign = ( plane.whichSide( projPnt ) == PlaneF::Front ) ? 1 : -1; 999 F32 diff = ( projPnt - mMouseDownProjPnt ).len(); 1000 1001 if ( mProfile->allAxesScaleUniform ) 1002 { 1003 deltaScale.set(1,1,1); 1004 deltaScale = deltaScale * sign * diff; 1005 } 1006 else 1007 deltaScale[mSelectionIdx] = diff * sign; 1008 } 1009 else if ( 3 <= mSelectionIdx && mSelectionIdx <= 5 ) 1010 { 1011 PlaneF plane( mMouseDownProjPnt, mMouseCollideLine.direction ); 1012 F32 sign = ( plane.whichSide( projPnt ) == PlaneF::Front ) ? 1 : -1; 1013 F32 diff = ( projPnt - mMouseDownProjPnt ).len(); 1014 1015 if ( mProfile->allAxesScaleUniform ) 1016 { 1017 deltaScale.set(1,1,1); 1018 deltaScale = deltaScale * sign * diff; 1019 } 1020 else 1021 { 1022 deltaScale[sgPlanarVectors[mSelectionIdx-3][0]] = diff * sign; 1023 deltaScale[sgPlanarVectors[mSelectionIdx-3][1]] = diff * sign; 1024 } 1025 } 1026 else // mSelectionIdx == 6 1027 { 1028 // Are we above or below the starting position relative to the camera? 1029 VectorF normal; 1030 mCameraMat.getColumn( 2, &normal ); 1031 1032 PlaneF plane( mMouseDownProjPnt, normal ); 1033 1034 F32 sign = ( plane.whichSide( projPnt ) == PlaneF::Front ) ? 1 : -1; 1035 F32 diff = ( projPnt - mMouseDownProjPnt ).len(); 1036 deltaScale.set(1,1,1); 1037 deltaScale = deltaScale * sign * diff; 1038 } 1039 1040 // Save current scale, then set mDeltaScale 1041 // to the amount it changes during this call. 1042 mDeltaScale = mScale; 1043 1044 mDeltaTotalScale = deltaScale; 1045 1046 mScale = mSavedScale; 1047 mScale += deltaScale * mProfile->scaleScalar; 1048 1049 mDeltaScale = mScale - mDeltaScale; 1050 1051 mScale.setMax( Point3F( 0.01f ) ); 1052 } 1053 1054 mDirty = true; 1055 } 1056 else if ( mProfile->mode == RotateMode && 1057 mSelectionIdx != Centroid ) 1058 { 1059 // Clear deltas we aren't using... 1060 mDeltaScale.zero(); 1061 mDeltaPos.zero(); 1062 1063 bool doScreenRot = ( mSelectionIdx == Custom1 ); 1064 1065 U32 rotAxisIdx = ( doScreenRot ) ? 1 : mSelectionIdx; 1066 1067 Point3F mousePntSS( event.mousePoint.x, event.mousePoint.y, 0.0f ); 1068 1069 Point3F pntSS0 = mElipseCursorCollidePntSS + mElipseCursorCollideVecSS * 10000.0f; 1070 Point3F pntSS1 = mElipseCursorCollidePntSS - mElipseCursorCollideVecSS * 10000.0f; 1071 1072 Point3F closestPntSS = MathUtils::mClosestPointOnSegment( pntSS0, pntSS1, mousePntSS ); 1073 1074 Point3F offsetDir = closestPntSS - mElipseCursorCollidePntSS; 1075 F32 offsetDist = offsetDir.len(); 1076 offsetDir.normalizeSafe(); 1077 1078 F32 dot = mDot( mElipseCursorCollideVecSS, offsetDir ); 1079 mSign = mIsZero( dot ) ? 0.0f : ( dot > 0.0f ) ? -1.0f : 1.0f; 1080 1081 // The angle that we will rotate the (saved) gizmo transform by to 1082 // generate the current gizmo transform. 1083 F32 angle = offsetDist * mSign * mProfile->rotateScalar; 1084 angle *= 0.02f; // scale down to not require rotate scalar to be microscopic 1085 1086 // 1087 if( mProfile->allowSnapRotations && event.modifier & SI_SHIFT ) 1088 angle = mDegToRad( _snapFloat( mRadToDeg( angle ), mProfile->rotationSnap ) ); 1089 1090 mDeltaAngle = angle - mLastAngle; 1091 mLastAngle = angle; 1092 1093 if ( doScreenRot ) 1094 { 1095 // Rotate relative to the camera. 1096 // We rotate around the y/forward vector pointing from the camera 1097 // to the gizmo. 1098 1099 // NOTE: This does NOT work 1100 1101 // Calculate mDeltaAngle and mDeltaTotalRot 1102 //{ 1103 // VectorF fvec( mOrigin - mCameraPos ); 1104 // fvec.normalizeSafe(); 1105 1106 // AngAxisF aa( fvec, mDeltaAngle ); 1107 // MatrixF mat; 1108 // aa.setMatrix( &mat ); 1109 1110 // mDeltaRot = mat.toEuler(); 1111 1112 // aa.set( fvec, mLastAngle ); 1113 // aa.setMatrix( &mat ); 1114 // mDeltaTotalRot = mat.toEuler(); 1115 //} 1116 1117 //MatrixF rotMat( mDeltaTotalRot ); 1118 1119 //if ( mCurrentAlignment == World ) 1120 //{ 1121 // //aa.setMatrix( &rotMat ); 1122 // mTransform = mSavedTransform * rotMat; 1123 // mTransform.setPosition( mOrigin ); 1124 1125 // rotMat.inverse(); 1126 // mCurrentTransform = mObjectMatInv * rotMat; 1127 // mCurrentTransform.inverse(); 1128 // mCurrentTransform.setPosition( mOrigin ); 1129 //} 1130 //else 1131 //{ 1132 // rotMat.inverse(); 1133 1134 // MatrixF m0; 1135 // mSavedTransform.invertTo(&m0); 1136 // 1137 // mTransform = m0 * rotMat; 1138 // mTransform.inverse(); 1139 // mTransform.setPosition( mOrigin ); 1140 1141 // mCurrentTransform = mTransform; 1142 //} 1143 } 1144 else 1145 { 1146 // Normal rotation, eg, not screen relative. 1147 1148 mDeltaRot.set(0,0,0); 1149 mDeltaRot[rotAxisIdx] = mDeltaAngle; 1150 1151 mDeltaTotalRot.set(0,0,0); 1152 mDeltaTotalRot[rotAxisIdx] = angle; 1153 1154 MatrixF rotMat( mDeltaTotalRot ); 1155 1156 mTransform = mSavedTransform * rotMat; 1157 mTransform.setPosition( mSavedTransform.getPosition() ); 1158 1159 if ( mCurrentAlignment == World ) 1160 { 1161 MatrixF mat0 = mCurrentTransform; 1162 1163 rotMat.inverse(); 1164 mCurrentTransform = mObjectMatInv * rotMat; 1165 mCurrentTransform.inverse(); 1166 mCurrentTransform.setPosition( mOrigin ); 1167 1168 MatrixF mat1 = mCurrentTransform; 1169 mat0.inverse(); 1170 MatrixF mrot; 1171 mrot = mat0 * mat1; 1172 mDeltaRot = mrot.toEuler(); 1173 } 1174 else 1175 { 1176 mCurrentTransform = mTransform; 1177 } 1178 } 1179 1180 mDirty = true; 1181 } 1182 1183 mLastMouseEvent = event; 1184} 1185 1186//------------------------------------------------------------------------------ 1187 1188void Gizmo::renderGizmo(const MatrixF &cameraTransform, F32 cameraFOV ) 1189{ 1190 mLastWorldMat = GFX->getWorldMatrix(); 1191 mLastProjMat = GFX->getProjectionMatrix(); 1192 mLastViewport = GFX->getViewport(); 1193 mLastWorldToScreenScale = GFX->getWorldToScreenScale(); 1194 mLastCameraFOV = cameraFOV; 1195 1196 // Save the Camera transform matrix, used all over... 1197 mCameraMat = cameraTransform; 1198 mCameraPos = mCameraMat.getPosition(); 1199 1200 GFXFrustumSaver fsaver; 1201 1202 // Change the far plane distance so that the gizmo is always visible. 1203 Frustum frustum = GFX->getFrustum(); 1204 frustum.setFarDist( 100000.0f ); 1205 GFX->setFrustum( frustum ); 1206 1207 _updateEnabledAxices(); 1208 1209 _updateState(); 1210 1211 _calcAxisInfo(); 1212 1213 if( mMouseDown ) 1214 { 1215 if( mProfile->renderMoveGrid && mMoveGridEnabled && mCurrentMode == MoveMode ) 1216 { 1217 GFXStateBlockDesc desc; 1218 1219 desc.setBlend( true ); 1220 desc.setZReadWrite( true, true ); 1221 1222 GFXDrawUtil::Plane plane = GFXDrawUtil::PlaneXY; 1223 ColorI color( 128, 128, 128, 200 ); 1224 1225 switch( mSelectionIdx ) 1226 { 1227 case Axis_Z: 1228 case Plane_XY: 1229 plane = GFXDrawUtil::PlaneXY; 1230 break; 1231 1232 case Axis_X: 1233 case Plane_YZ: 1234 plane = GFXDrawUtil::PlaneYZ; 1235 break; 1236 1237 case Axis_Y: 1238 case Plane_XZ: 1239 plane = GFXDrawUtil::PlaneXZ; 1240 break; 1241 } 1242 1243 GFX->getDrawUtil()->drawPlaneGrid( 1244 desc, 1245 mTransform.getPosition(), 1246 Point2F( mMoveGridSize, mMoveGridSize ), 1247 Point2F( mMoveGridSpacing, mMoveGridSpacing ), 1248 color, 1249 plane 1250 ); 1251 } 1252 1253 if( !mProfile->renderWhenUsed ) 1254 return; 1255 } 1256 1257 mHighlightAll = mProfile->allAxesScaleUniform && mSelectionIdx >= 0 && mSelectionIdx <= 3; 1258 1259 // Render plane (if set to render) behind the gizmo 1260 if ( mProfile->mode != NoneMode ) 1261 _renderPlane(); 1262 1263 _setStateBlock(); 1264 1265 // Special case for NoneMode, 1266 // we only render the primary axis with no tips. 1267 if ( mProfile->mode == NoneMode ) 1268 { 1269 _renderPrimaryAxis(); 1270 return; 1271 } 1272 1273 if ( mProfile->mode == RotateMode ) 1274 { 1275 PrimBuild::begin( GFXLineList, 6 ); 1276 1277 // Render the primary axisIdx 1278 for(U32 i = 0; i < 3; i++) 1279 { 1280 PrimBuild::color( ( mHighlightAll || i == mSelectionIdx ) ? mProfile->axisColors[i] : mProfile->inActiveColor ); 1281 PrimBuild::vertex3fv( mOrigin ); 1282 PrimBuild::vertex3fv( mOrigin + mProjAxisVector[i] * mProjLen * 0.25f ); 1283 } 1284 1285 PrimBuild::end(); 1286 1287 _renderAxisCircles(); 1288 } 1289 else 1290 { 1291 // Both Move and Scale modes render basis vectors as 1292 // large stick lines. 1293 _renderPrimaryAxis(); 1294 1295 // Render the tips based on current operation. 1296 1297 GFXTransformSaver saver( true, false ); 1298 1299 GFX->multWorld(mTransform); 1300 1301 if ( mProfile->mode == ScaleMode ) 1302 { 1303 _renderAxisBoxes(); 1304 } 1305 else if ( mProfile->mode == MoveMode ) 1306 { 1307 _renderAxisArrows(); 1308 } 1309 1310 saver.restore(); 1311 } 1312 1313 // Render the planar handles... 1314 1315 if ( mCurrentMode != RotateMode ) 1316 { 1317 Point3F midpnt[3]; 1318 for(U32 i = 0; i < 3; i++) 1319 midpnt[i] = mProjAxisVector[i] * mProjLen * 0.5f; 1320 1321 PrimBuild::begin( GFXLineList, 12 ); 1322 1323 for(U32 i = 0; i < 3; i++) 1324 { 1325 U32 axis0 = sgPlanarVectors[i][0]; 1326 U32 axis1 = sgPlanarVectors[i][1]; 1327 1328 const Point3F &p0 = midpnt[axis0]; 1329 const Point3F &p1 = midpnt[axis1]; 1330 1331 bool selected0 = false; 1332 bool selected1 = false; 1333 1334 if ( i + 3 == mSelectionIdx ) 1335 selected0 = selected1 = true; 1336 1337 bool inactive = !mAxisEnabled[axis0] || !mAxisEnabled[axis1] || !(mProfile->flags & GizmoProfile::PlanarHandlesOn); 1338 1339 if ( inactive ) 1340 PrimBuild::color( mProfile->hideDisabledAxes ? ColorI::ZERO : mProfile->inActiveColor ); 1341 else 1342 PrimBuild::color( selected0 ? mProfile->activeColor : mProfile->axisColors[axis0] ); 1343 1344 PrimBuild::vertex3fv( mOrigin + p0 ); 1345 PrimBuild::vertex3fv( mOrigin + p0 + p1 ); 1346 1347 if ( inactive ) 1348 PrimBuild::color( mProfile->hideDisabledAxes ? ColorI::ZERO : mProfile->inActiveColor ); 1349 else 1350 PrimBuild::color( selected1 ? mProfile->activeColor : mProfile->axisColors[axis1] ); 1351 1352 PrimBuild::vertex3fv( mOrigin + p1 ); 1353 PrimBuild::vertex3fv( mOrigin + p0 + p1 ); 1354 } 1355 1356 PrimBuild::end(); 1357 1358 // Render planar handle as solid if selected. 1359 1360 ColorI planeColorSEL( mProfile->activeColor ); 1361 planeColorSEL.alpha = 75; 1362 ColorI planeColorNA( 0, 0, 0, 0 ); 1363 1364 PrimBuild::begin( GFXTriangleList, 18 ); 1365 1366 for(U32 i = 0; i < 3; i++) 1367 { 1368 U32 axis0 = sgPlanarVectors[i][0]; 1369 U32 axis1 = sgPlanarVectors[i][1]; 1370 1371 const Point3F &p0 = midpnt[axis0]; 1372 const Point3F &p1 = midpnt[axis1]; 1373 1374 if ( i + 3 == mSelectionIdx ) 1375 PrimBuild::color( planeColorSEL ); 1376 else 1377 PrimBuild::color( planeColorNA ); 1378 1379 PrimBuild::vertex3fv( mOrigin ); 1380 PrimBuild::vertex3fv( mOrigin + p0 ); 1381 PrimBuild::vertex3fv( mOrigin + p0 + p1 ); 1382 1383 PrimBuild::vertex3fv( mOrigin ); 1384 PrimBuild::vertex3fv( mOrigin + p0 + p1 ); 1385 PrimBuild::vertex3fv( mOrigin + p1 ); 1386 } 1387 1388 PrimBuild::end(); 1389 } 1390 1391 // Render Centroid Handle... 1392 if ( mUniformHandleEnabled ) 1393 { 1394 F32 tipScale = mProjLen * 0.075f; 1395 GFXTransformSaver saver; 1396 GFX->multWorld( mTransform ); 1397 1398 if ( mSelectionIdx == Centroid || mHighlightAll || mHighlightCentroidHandle ) 1399 PrimBuild::color( mProfile->centroidHighlightColor ); 1400 else 1401 PrimBuild::color( mProfile->centroidColor ); 1402 1403 for(U32 j = 0; j < 6; j++) 1404 { 1405 PrimBuild::begin( GFXTriangleStrip, 4 ); 1406 1407 PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][0]] * tipScale); 1408 PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][1]] * tipScale); 1409 PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][2]] * tipScale); 1410 PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][3]] * tipScale); 1411 1412 PrimBuild::end(); 1413 } 1414 } 1415} 1416 1417void Gizmo::renderText( const RectI &viewPort, const MatrixF &modelView, const MatrixF &projection ) 1418{ 1419 if ( mProfile->mode == NoneMode ) 1420 return; 1421 1422 if ( mMouseDown && !mProfile->renderWhenUsed ) 1423 return; 1424 1425 GFXDrawUtil *drawer = GFX->getDrawUtil(); 1426 1427 _setStateBlock(); 1428 1429 char axisText[] = "xyz"; 1430 1431 F32 projLen = mProjLen * 1.05f; 1432 if ( mProfile->mode == RotateMode ) 1433 projLen *= 0.28f; 1434 1435 for ( U32 i = 0; i < 3; i++ ) 1436 { 1437 if ( !mAxisEnabled[i] && mProfile->hideDisabledAxes ) 1438 continue; 1439 1440 const Point3F & centroid = mOrigin; 1441 Point3F pos(centroid.x + mProjAxisVector[i].x * projLen, 1442 centroid.y + mProjAxisVector[i].y * projLen, 1443 centroid.z + mProjAxisVector[i].z * projLen); 1444 1445 Point3F sPos; 1446 1447 if ( MathUtils::mProjectWorldToScreen( pos, &sPos, viewPort, modelView, projection ) ) 1448 { 1449 ColorI textColor = ColorI(170,170,170); 1450 1451 if ( mProfile->mode == RotateMode ) 1452 { 1453 textColor.set(170,170,170); 1454 if ( i == mSelectionIdx ) 1455 textColor = mProfile->axisColors[i]; 1456 } 1457 else 1458 { 1459 if ( i == mSelectionIdx || !mAxisEnabled[i] ) 1460 textColor = mProfile->inActiveColor; 1461 else 1462 textColor = mProfile->axisColors[i]; 1463 } 1464 1465 char buf[2]; 1466 buf[0] = axisText[i]; buf[1] = '\0'; 1467 drawer->setBitmapModulation(textColor); 1468 drawer->drawText( mProfile->font, Point2I((S32)sPos.x, (S32)sPos.y), buf ); 1469 } 1470 } 1471} 1472 1473// Gizmo Internal Methods... 1474 1475void Gizmo::_calcAxisInfo() 1476{ 1477 mOrigin = mTransform.getPosition(); 1478 1479 for ( U32 i = 0; i < 3; i++ ) 1480 { 1481 VectorF tmp; 1482 mTransform.mulV(sgAxisVectors[i], &tmp); 1483 mProjAxisVector[i] = tmp; 1484 mProjAxisVector[i].normalizeSafe(); 1485 } 1486 1487 // get the projected size... 1488 1489 mProjLen = _getProjectionLength( mProfile->screenLen ); 1490} 1491 1492void Gizmo::_renderPrimaryAxis() 1493{ 1494 // Render the primary axis(s) 1495 for ( U32 i = 0; i < 3; i++ ) 1496 { 1497 ColorI color = mProfile->axisColors[i]; 1498 1499 if ( !mAxisEnabled[i] ) 1500 { 1501 color = mProfile->inActiveColor; 1502 if ( mProfile->hideDisabledAxes ) 1503 color.alpha = 0; 1504 } 1505 else 1506 { 1507 if ( 0 <= mSelectionIdx && mSelectionIdx <= 2 ) 1508 { 1509 if ( i == mSelectionIdx ) 1510 color = mProfile->activeColor; 1511 } 1512 else if ( 3 <= mSelectionIdx && mSelectionIdx <= 5 ) 1513 { 1514 if ( i == sgPlanarVectors[mSelectionIdx-3][0] || 1515 i == sgPlanarVectors[mSelectionIdx-3][1] ) 1516 color = mProfile->activeColor; 1517 } 1518 else if ( mSelectionIdx == 6 ) 1519 color = mProfile->activeColor; 1520 } 1521 1522 if ( mHighlightAll ) 1523 { 1524 // Previous logic is complex so do this outside. 1525 // Don't change the alpha calculated previously but override 1526 // the color to the activeColor. 1527 U8 saveAlpha = color.alpha; 1528 color = mProfile->activeColor; 1529 color.alpha = saveAlpha; 1530 } 1531 1532 PrimBuild::begin( GFXLineList, 2 ); 1533 PrimBuild::color( color ); 1534 PrimBuild::vertex3fv( mOrigin ); 1535 PrimBuild::vertex3fv( mOrigin + mProjAxisVector[i] * mProjLen ); 1536 PrimBuild::end(); 1537 } 1538} 1539 1540void Gizmo::_renderAxisArrows() 1541{ 1542 F32 tipScale = mProjLen * 0.25; 1543 S32 x, y, z; 1544 Point3F pnt; 1545 1546 for ( U32 axisIdx = 0; axisIdx < 3; axisIdx++ ) 1547 { 1548 if ( mProfile->hideDisabledAxes && !mAxisEnabled[axisIdx] ) 1549 continue; 1550 1551 PrimBuild::begin( GFXTriangleList, 12*3 ); 1552 1553 if ( !mAxisEnabled[axisIdx] ) 1554 PrimBuild::color( mProfile->inActiveColor ); 1555 else 1556 PrimBuild::color( mProfile->axisColors[axisIdx] ); 1557 1558 x = sgAxisRemap[axisIdx][0]; 1559 y = sgAxisRemap[axisIdx][1]; 1560 z = sgAxisRemap[axisIdx][2]; 1561 1562 for ( U32 i = 0; i < sizeof(sgConeVerts) / (sizeof(U32)*3); ++i ) 1563 { 1564 const Point3F& conePnt0 = sgConePnts[sgConeVerts[i][0]]; 1565 pnt.set(conePnt0[x], conePnt0[y], conePnt0[z]); 1566 PrimBuild::vertex3fv(pnt * tipScale + sgAxisVectors[axisIdx] * mProjLen); 1567 1568 const Point3F& conePnt1 = sgConePnts[sgConeVerts[i][1]]; 1569 pnt.set(conePnt1[x], conePnt1[y], conePnt1[z]); 1570 PrimBuild::vertex3fv(pnt * tipScale + sgAxisVectors[axisIdx] * mProjLen); 1571 1572 const Point3F& conePnt2 = sgConePnts[sgConeVerts[i][2]]; 1573 pnt.set(conePnt2[x], conePnt2[y], conePnt2[z]); 1574 PrimBuild::vertex3fv(pnt * tipScale + sgAxisVectors[axisIdx] * mProjLen); 1575 } 1576 1577 PrimBuild::end(); 1578 } 1579} 1580 1581void Gizmo::_renderAxisBoxes() 1582{ 1583 if ( mProfile->hideDisabledAxes && !( mProfile->flags & GizmoProfile::CanScale ) ) 1584 return; 1585 1586 F32 tipScale = mProjLen * 0.1; 1587 F32 pos = mProjLen - 0.5 * tipScale; 1588 1589 for( U32 axisIdx = 0; axisIdx < 3; ++axisIdx ) 1590 { 1591 if ( mProfile->hideDisabledAxes && !( mProfile->flags & ( GizmoProfile::CanScaleX << axisIdx ) ) ) 1592 continue; 1593 1594 if ( mAxisEnabled[axisIdx] ) 1595 PrimBuild::color( mProfile->axisColors[axisIdx] ); 1596 else 1597 PrimBuild::color( mProfile->inActiveColor ); 1598 1599 for(U32 j = 0; j < 6; j++) 1600 { 1601 PrimBuild::begin( GFXTriangleStrip, 4 ); 1602 1603 PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][0]] * tipScale + sgAxisVectors[axisIdx] * pos ); 1604 PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][1]] * tipScale + sgAxisVectors[axisIdx] * pos ); 1605 PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][2]] * tipScale + sgAxisVectors[axisIdx] * pos ); 1606 PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][3]] * tipScale + sgAxisVectors[axisIdx] * pos ); 1607 1608 PrimBuild::end(); 1609 } 1610 } 1611} 1612 1613void Gizmo::_renderAxisCircles() 1614{ 1615 if ( mProfile->hideDisabledAxes && !( mProfile->flags & GizmoProfile::CanRotate ) ) 1616 return; 1617 1618 // Setup the WorldMatrix for rendering in camera space. 1619 // Honestly not sure exactly why this works but it does... 1620 GFX->pushWorldMatrix(); 1621 MatrixF cameraXfm = GFX->getWorldMatrix(); 1622 cameraXfm.inverse(); 1623 const Point3F cameraPos = cameraXfm.getPosition(); 1624 cameraXfm.setPosition( mOrigin ); 1625 GFX->multWorld(cameraXfm); 1626 1627 // Render the ScreenSpace rotation circle... 1628 if ( !( mProfile->hideDisabledAxes && !mScreenRotateHandleEnabled ) ) 1629 { 1630 F32 radius = mProjLen * 0.7f; 1631 U32 segments = 40; 1632 F32 step = mDegToRad(360.0f)/ segments; 1633 Point3F pnt; 1634 1635 PrimBuild::color( ( mHighlightAll || mSelectionIdx == Custom1 ) ? mProfile->activeColor : mProfile->inActiveColor ); 1636 PrimBuild::begin( GFXLineStrip, segments+1 ); 1637 1638 for(U32 i = 0; i <= segments; i++) 1639 { 1640 F32 angle = i * step; 1641 1642 pnt.x = mCos(angle) * radius; 1643 pnt.y = 0.0f; 1644 pnt.z = mSin(angle) * radius; 1645 1646 PrimBuild::vertex3fv( pnt ); 1647 } 1648 1649 PrimBuild::end(); 1650 } 1651 1652 // Render the gizmo/sphere bounding circle... 1653 { 1654 F32 radius = mProjLen * 0.5f; 1655 U32 segments = 40; 1656 F32 step = mDegToRad(360.0f) / segments; 1657 Point3F pnt; 1658 1659 // Render as solid (with transparency) when the sphere is selected 1660 if ( mSelectionIdx == Custom2 ) 1661 { 1662 ColorI color = mProfile->inActiveColor; 1663 color.alpha = 100; 1664 PrimBuild::color( color ); 1665 PrimBuild::begin( GFXTriangleStrip, segments+2 ); 1666 1667 PrimBuild::vertex3fv( Point3F(0,0,0) ); 1668 1669 for(U32 i = 0; i <= segments; i++) 1670 { 1671 F32 angle = i * step; 1672 1673 pnt.x = mCos(angle) * radius; 1674 pnt.y = 0.0f; 1675 pnt.z = mSin(angle) * radius; 1676 1677 PrimBuild::vertex3fv( pnt ); 1678 } 1679 1680 PrimBuild::end(); 1681 } 1682 else 1683 { 1684 PrimBuild::color( mProfile->inActiveColor ); 1685 PrimBuild::begin( GFXLineStrip, segments+1 ); 1686 1687 for(U32 i = 0; i <= segments; i++) 1688 { 1689 F32 angle = i * step; 1690 1691 pnt.x = mCos(angle) * radius; 1692 pnt.y = 0.0f; 1693 pnt.z = mSin(angle) * radius; 1694 1695 PrimBuild::vertex3fv( pnt ); 1696 } 1697 1698 PrimBuild::end(); 1699 } 1700 } 1701 1702 // Done rendering in camera space. 1703 GFX->popWorldMatrix(); 1704 1705 // Setup WorldMatrix for Gizmo-Space rendering. 1706 GFX->pushWorldMatrix(); 1707 GFX->multWorld(mTransform); 1708 1709 // Render the axis-manipulation ellipses... 1710 { 1711 F32 radius = mProjLen * 0.5f; 1712 U32 segments = 40; 1713 F32 step = mDegToRad(360.0f) / segments; 1714 U32 x,y,z; 1715 1716 VectorF planeNormal; 1717 planeNormal = mOrigin - cameraPos; 1718 planeNormal.normalize(); 1719 PlaneF clipPlane( mOrigin, planeNormal ); 1720 1721 MatrixF worldToGizmo = mTransform; 1722 worldToGizmo.inverse(); 1723 mTransformPlane( worldToGizmo, Point3F(1,1,1), clipPlane, &clipPlane ); 1724 1725 for ( U32 axis = 0; axis < 3; axis++ ) 1726 { 1727 if ( mProfile->hideDisabledAxes && !( mProfile->flags & ( GizmoProfile::CanRotateX << axis ) ) ) 1728 continue; 1729 1730 if ( mAxisEnabled[axis] || mHighlightAll ) 1731 PrimBuild::color( (axis == mSelectionIdx) ? mProfile->activeColor : mProfile->axisColors[axis] ); 1732 else 1733 PrimBuild::color( mProfile->inActiveColor ); 1734 1735 x = sgAxisRemap[axis][0]; 1736 y = sgAxisRemap[axis][1]; 1737 z = sgAxisRemap[axis][2]; 1738 1739 PrimBuild::begin( GFXLineList, (segments+1) * 2 ); 1740 1741 for ( U32 i = 1; i <= segments; i++ ) 1742 { 1743 F32 ang0 = (i-1) * step; 1744 F32 ang1 = i * step; 1745 1746 Point3F temp; 1747 1748 temp.x = 0.0f; 1749 temp.y = mCos(ang0) * radius; 1750 temp.z = mSin(ang0) * radius; 1751 Point3F pnt0( temp[x], temp[y], temp[z] ); 1752 1753 temp.x = 0.0f; 1754 temp.y = mCos(ang1) * radius; 1755 temp.z = mSin(ang1) * radius; 1756 Point3F pnt1( temp[x], temp[y], temp[z] ); 1757 1758 bool valid0 = ( clipPlane.whichSide(pnt0) == PlaneF::Back ); 1759 bool valid1 = ( clipPlane.whichSide(pnt1) == PlaneF::Back ); 1760 1761 //if ( !valid0 && !valid1 ) 1762 // continue; 1763 1764 if ( !valid0 || !valid1 ) 1765 continue; 1766 1767 PrimBuild::vertex3fv( pnt0 ); 1768 PrimBuild::vertex3fv( pnt1 ); 1769 } 1770 1771 PrimBuild::end(); 1772 } 1773 } 1774 1775 // Done rendering in Gizmo-Space. 1776 GFX->popWorldMatrix(); 1777 1778 // Render hint-arrows... 1779 /* 1780 if ( mMouseDown && mSelectionIdx != -1 ) 1781 { 1782 PrimBuild::begin( GFXLineList, 4 ); 1783 1784 F32 hintArrowScreenLength = mProfile->screenLen * 0.5f; 1785 F32 hintArrowTipScreenLength = mProfile->screenLen * 0.25; 1786 1787 F32 worldZDist = ( mMouseCollideLine.origin - mCameraPos ).len(); 1788 F32 hintArrowLen = ( hintArrowScreenLength * worldZDist ) / mLastWorldToScreenScale.y; 1789 F32 hintArrowTipLen = ( hintArrowTipScreenLength * worldZDist ) / mLastWorldToScreenScale.y; 1790 1791 Point3F p0 = mMouseCollideLine.origin - mMouseCollideLine.direction * hintArrowLen; 1792 Point3F p1 = mMouseCollideLine.origin; 1793 Point3F p2 = mMouseCollideLine.origin + mMouseCollideLine.direction * hintArrowLen; 1794 1795 // For whatever reason, the sign is actually negative if we are on the 1796 // positive size of the MouseCollideLine direction. 1797 ColorI color0 = ( mSign > 0.0f ) ? mProfile->activeColor : mProfile->inActiveColor; 1798 ColorI color1 = ( mSign < 0.0f ) ? mProfile->activeColor : mProfile->inActiveColor; 1799 1800 PrimBuild::color( color0 ); 1801 PrimBuild::vertex3fv( p1 ); 1802 PrimBuild::vertex3fv( p0 ); 1803 1804 PrimBuild::color( color1 ); 1805 PrimBuild::vertex3fv( p1 ); 1806 PrimBuild::vertex3fv( p2 ); 1807 PrimBuild::end(); 1808 1809 GFXStateBlockDesc desc; 1810 desc.setBlend( true ); 1811 desc.setZReadWrite( false, false ); 1812 1813 GFXDrawUtil *drawer = GFX->getDrawUtil(); 1814 drawer->drawCone( desc, p0, p0 - mMouseCollideLine.direction * hintArrowTipLen, hintArrowTipLen * 0.5f, color0 ); 1815 drawer->drawCone( desc, p2, p2 + mMouseCollideLine.direction * hintArrowTipLen, hintArrowTipLen * 0.5f, color1 ); 1816 } 1817 */ 1818} 1819 1820void Gizmo::_renderPlane() 1821{ 1822 if( !mGridPlaneEnabled ) 1823 return; 1824 1825 Point2F size( mProfile->planeDim, mProfile->planeDim ); 1826 1827 GFXStateBlockDesc desc; 1828 desc.setBlend( true ); 1829 desc.setZReadWrite( true, false ); 1830 1831 GFXTransformSaver saver; 1832 GFX->multWorld( mTransform ); 1833 1834 if ( mProfile->renderPlane ) 1835 GFX->getDrawUtil()->drawSolidPlane( desc, Point3F::Zero, size, mProfile->gridColor ); 1836 1837 if ( mProfile->renderPlaneHashes ) 1838 { 1839 // TODO: This wasn't specified before... so it was 1840 // rendering lines that were invisible. Maybe we need 1841 // a new field for grid line color? 1842 ColorI gridColor( mProfile->gridColor ); 1843 gridColor.alpha *= 2; 1844 1845 GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, Point2F( mProfile->gridSize.x, mProfile->gridSize.y ), gridColor ); 1846 } 1847} 1848 1849 1850void Gizmo::_setStateBlock() 1851{ 1852 if ( !mStateBlock ) 1853 { 1854 GFXStateBlockDesc sb; 1855 sb.blendDefined = true; 1856 sb.blendEnable = true; 1857 sb.blendSrc = GFXBlendSrcAlpha; 1858 sb.blendDest = GFXBlendInvSrcAlpha; 1859 sb.zDefined = true; 1860 sb.zEnable = false; 1861 sb.cullDefined = true; 1862 sb.cullMode = GFXCullNone; 1863 mStateBlock = GFX->createStateBlock(sb); 1864 1865 sb.setZReadWrite( true, false ); 1866 mSolidStateBlock = GFX->createStateBlock(sb); 1867 } 1868 1869 //if ( mProfile->renderSolid ) 1870 // GFX->setStateBlock( mSolidStateBlock ); 1871 //else 1872 GFX->setStateBlock( mStateBlock ); 1873} 1874 1875Point3F Gizmo::_snapPoint( const Point3F &pnt ) const 1876{ 1877 if ( !mProfile->snapToGrid ) 1878 return pnt; 1879 1880 Point3F snap; 1881 snap.x = _snapFloat( pnt.x, mProfile->gridSize.x ); 1882 snap.y = _snapFloat( pnt.y, mProfile->gridSize.y ); 1883 snap.z = _snapFloat( pnt.z, mProfile->gridSize.z ); 1884 1885 return snap; 1886} 1887 1888F32 Gizmo::_snapFloat( const F32 &val, const F32 &snap ) const 1889{ 1890 if ( snap == 0.0f ) 1891 return val; 1892 1893 F32 a = mFmod( val, snap ); 1894 1895 F32 temp = val; 1896 1897 if ( mFabs(a) > (snap / 2) ) 1898 val < 0.0f ? temp -= snap : temp += snap; 1899 1900 return(temp - a); 1901} 1902 1903GizmoAlignment Gizmo::_filteredAlignment() 1904{ 1905 GizmoAlignment align = mProfile->alignment; 1906 1907 // Special case in ScaleMode, always be in object. 1908 if ( mProfile->mode == ScaleMode ) 1909 align = Object; 1910 1911 return align; 1912} 1913 1914void Gizmo::_updateState( bool collideGizmo ) 1915{ 1916 if ( !mProfile ) 1917 return; 1918 1919 // Update mCurrentMode 1920 1921 if ( mCurrentMode != mProfile->mode ) 1922 { 1923 // Changing the mode invalidates the prior selection since the gizmo 1924 // has changed shape. 1925 1926 mCurrentMode = mProfile->mode; 1927 mSelectionIdx = -1; 1928 1929 // Determine the new selection unless we have been told not to. 1930 if ( collideGizmo ) 1931 collideAxisGizmo( mLastMouseEvent ); 1932 1933 // Also cancel any current dragging operation since it would only be 1934 // valid if the mouse down event occurred first. 1935 1936 mMouseDown = false; 1937 } 1938 1939 // Update mCurrentAlignment 1940 1941 // Changing the alignment during a drag could be really bad. 1942 // Haven't actually tested this though. 1943 if ( mMouseDown ) 1944 return; 1945 1946 GizmoAlignment desired = _filteredAlignment(); 1947 1948 if ( desired == World && 1949 mCurrentAlignment == Object ) 1950 { 1951 mObjectMat = mTransform; 1952 mTransform.identity(); 1953 mTransform.setPosition( mObjectMat.getPosition() ); 1954 } 1955 else if ( desired == Object && 1956 mCurrentAlignment == World ) 1957 { 1958 Point3F pos = mTransform.getPosition(); 1959 mTransform = mObjectMat; 1960 mTransform.setPosition( pos ); 1961 mObjectMat.identity(); 1962 mObjectMat.setPosition( pos ); 1963 } 1964 1965 mCurrentAlignment = desired; 1966 1967 mObjectMat.invertTo( &mObjectMatInv ); 1968} 1969 1970void Gizmo::_updateEnabledAxices() 1971{ 1972 if ( ( mProfile->mode == ScaleMode && mProfile->flags & GizmoProfile::CanScaleUniform ) || 1973 ( mProfile->mode == MoveMode && mProfile->flags & GizmoProfile::CanTranslateUniform ) || 1974 ( mProfile->mode == RotateMode && mProfile->flags & GizmoProfile::CanRotateUniform ) ) 1975 mUniformHandleEnabled = true; 1976 else 1977 mUniformHandleEnabled = false; 1978 1979 // Screen / camera relative rotation disabled until it functions properly 1980 // 1981 //if ( mProfile->mode == RotateMode && mProfile->flags & GizmoProfile::CanRotateScreen ) 1982 // mScreenRotateHandleEnabled = true; 1983 //else 1984 mScreenRotateHandleEnabled = false; 1985 1986 // Early out if we are in a mode that is disabled. 1987 if ( mProfile->mode == RotateMode && !(mProfile->flags & GizmoProfile::CanRotate ) ) 1988 { 1989 mAxisEnabled[0] = mAxisEnabled[1] = mAxisEnabled[2] = false; 1990 return; 1991 } 1992 if ( mProfile->mode == MoveMode && !(mProfile->flags & GizmoProfile::CanTranslate ) ) 1993 { 1994 mAxisEnabled[0] = mAxisEnabled[1] = mAxisEnabled[2] = false; 1995 return; 1996 } 1997 if ( mProfile->mode == ScaleMode && !(mProfile->flags & GizmoProfile::CanScale ) ) 1998 { 1999 mAxisEnabled[0] = mAxisEnabled[1] = mAxisEnabled[2] = false; 2000 return; 2001 } 2002 2003 for ( U32 i = 0; i < 3; i++ ) 2004 { 2005 mAxisEnabled[i] = false; 2006 2007 // Some tricky enum math... x/y/z are sequential in the enum 2008 if ( ( mProfile->mode == RotateMode ) && 2009 !( mProfile->flags & ( GizmoProfile::CanRotateX << i ) ) ) 2010 continue; 2011 if ( ( mProfile->mode == MoveMode ) && 2012 !( mProfile->flags & ( GizmoProfile::CanTranslateX << i ) ) ) 2013 continue; 2014 if ( ( mProfile->mode == ScaleMode ) && 2015 !( mProfile->flags & ( GizmoProfile::CanScaleX << i ) ) ) 2016 continue; 2017 2018 mAxisEnabled[i] = true; 2019 } 2020} 2021
