gizmo.cpp

Engine/source/gui/worldEditor/gizmo.cpp

More...

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