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