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 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