mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
677 lines
20 KiB
C++
677 lines
20 KiB
C++
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
|
#include "stdafx.h"
|
|
|
|
#include "Tool/AdvMoveTool.h"
|
|
|
|
#include "V8DataModel/Workspace.h"
|
|
#include "V8DataModel/PartInstance.h"
|
|
#include "Tool/Dragger.h"
|
|
#include "V8World/World.h"
|
|
#include "V8World/ContactManager.h"
|
|
#include "Util/Math.h"
|
|
#include "Util/HitTest.h"
|
|
#include "Util/Sound.h"
|
|
#include "AppDraw/DrawAdorn.h"
|
|
#include "v8datamodel/changehistory.h"
|
|
|
|
// TODO - move rotation specific stuff to AdvRotateTool
|
|
// TODO - move generic stuff down to the Arrow layer to reduce some redundancy with
|
|
// non-adv draggers
|
|
|
|
DYNAMIC_FASTFLAG(RestoreTransparencyOnToolChange)
|
|
FASTFLAG(StudioUseDraggerGrid)
|
|
DYNAMIC_FASTFLAGVARIABLE(DraggerUsesNewPartOnDuplicate, false)
|
|
|
|
namespace RBX {
|
|
|
|
const char* const sAdvMoveTool = "AdvMove";
|
|
static const float MinimumExtentsSize = 0.4f;
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
AdvMoveToolBase::AdvMoveToolBase(Workspace* workspace)
|
|
: AdvArrowToolBase(workspace)
|
|
, dragging(false)
|
|
, cursor("advCursor-default")
|
|
, overHandleNormalId(NORM_UNDEFINED)
|
|
, mExtents(Vector3::zero(),Vector3::zero())
|
|
, mMultiRotation(Matrix3::identity())
|
|
, mInitializedExtents(false)
|
|
{
|
|
}
|
|
|
|
|
|
void AdvMoveToolBase::onMouseHover(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
Super::onMouseHover(inputObject);
|
|
|
|
onMouseIdle(inputObject);
|
|
}
|
|
|
|
|
|
void AdvMoveToolBase::onMouseIdle(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
Super::onMouseIdle(inputObject);
|
|
|
|
if (dragging) {
|
|
;
|
|
}
|
|
else if (captured()) {
|
|
cursor = "advClosed-hand";
|
|
}
|
|
else if (getOverHandle(inputObject)) {
|
|
cursor = "advCursor-openedHand";
|
|
}
|
|
else {
|
|
cursor = Super::getCursorName();
|
|
}
|
|
}
|
|
|
|
|
|
shared_ptr<MouseCommand> AdvMoveToolBase::onMouseDown(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
NormalId normalId;
|
|
Vector3 tempPt;
|
|
|
|
Extents extents;
|
|
overHandleNormalId = NORM_UNDEFINED;
|
|
CoordinateFrame location;
|
|
bool isLocal = false;
|
|
|
|
getExtentsAndLocation(extents, location, isLocal);
|
|
|
|
if (getOverHandle(inputObject, tempPt, normalId))
|
|
{
|
|
if (DFFlag::DraggerUsesNewPartOnDuplicate)
|
|
{
|
|
RBX::Selection* selection = ServiceProvider::find<Selection>(workspace);
|
|
if (selection)
|
|
selectionChangedConnection = selection->selectionChanged.connect(boost::bind(&RBX::AdvMoveToolBase::setToSelection, this));
|
|
}
|
|
|
|
downPoint2d = inputObject->get2DPosition();
|
|
dragAxis = (int)(normalId) % 3;
|
|
dragAxisDirection = (normalId > 2) ? -1 : 1;
|
|
dragNormalId = normalId;
|
|
lastPoint3d = tempPt;
|
|
|
|
dragRay = Ray::fromOriginAndDirection(lastPoint3d, location.vectorToWorldSpace(normalIdToVector3(normalId)));
|
|
|
|
ServiceClient< FilteredSelection<Instance> > instanceSelection(workspace);
|
|
PartArray partArray;
|
|
DragUtilities::instancesToParts(instanceSelection->items(), partArray);
|
|
megaDragger.reset(new MegaDragger(
|
|
NULL,
|
|
partArray,
|
|
workspace) );
|
|
|
|
megaDragger->startDragging();
|
|
//IMPORTANT: Transparency modification should be done only after startDragging as it will update world correctly else issues with ChangeHistory
|
|
//if collision checks are turned off then make parts transparent after saving the original values
|
|
if (!RBX::AdvArrowTool::advCollisionCheckMode)
|
|
saveAndModifyPartsTransparency();
|
|
|
|
capture();
|
|
return shared_from(this);
|
|
}
|
|
else {
|
|
return Super::onMouseDown(inputObject);
|
|
}
|
|
}
|
|
|
|
void AdvMoveToolBase::setToSelection()
|
|
{
|
|
if (DFFlag::RestoreTransparencyOnToolChange)
|
|
AdvArrowToolBase::restoreSavedPartsTransparency();
|
|
megaDragger->setToSelection(workspace);
|
|
if (DFFlag::RestoreTransparencyOnToolChange && !RBX::AdvArrowTool::advCollisionCheckMode)
|
|
saveAndModifyPartsTransparency();
|
|
}
|
|
|
|
float AdvMoveToolBase::snapRotationAngle(float Angle) const
|
|
{
|
|
if ( AdvArrowToolBase::advGridMode == DRAG::ONE_STUD )
|
|
{
|
|
float increment = Math::pif() / 4;
|
|
Angle = floorf(Angle / increment) * increment;
|
|
}
|
|
else if ( AdvArrowToolBase::advGridMode == DRAG::QUARTER_STUD )
|
|
{
|
|
float increment = Math::pif() / 12;
|
|
Angle = floorf(Angle / increment) * increment;
|
|
}
|
|
|
|
return Angle;
|
|
}
|
|
|
|
void AdvMoveToolBase::onMouseMove(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
RBXASSERT(captured());
|
|
Super::onMouseMove(inputObject);
|
|
|
|
if ( !dragging
|
|
&& ((inputObject->get2DPosition() - downPoint2d).length() > 4))
|
|
{
|
|
megaDragger->alignAndCleanParts();
|
|
dragging = true;
|
|
}
|
|
|
|
|
|
if ( dragging
|
|
&& megaDragger->anyDragPartAlive())
|
|
{
|
|
if ( getDragType() == HANDLE_MOVE )
|
|
{
|
|
Vector3 closePoint = Math::closestPointOnRay(dragRay, getUnitMouseRay(inputObject));
|
|
Vector3 delta = closePoint - lastPoint3d; // snap point if required
|
|
bool isLocal = getLocalSpaceMode();
|
|
if (isLocal)
|
|
{
|
|
delta = DragUtilities::toLocalGrid(delta);
|
|
}
|
|
else
|
|
{
|
|
delta = DragUtilities::toGrid(delta);
|
|
}
|
|
|
|
if (delta != Vector3::zero())
|
|
{
|
|
megaDragger->continueDragging(); // do this every time - multiplayer
|
|
|
|
Vector3 move(delta);
|
|
|
|
if (!RBX::AdvArrowTool::advCollisionCheckMode)
|
|
megaDragger->moveAlongLine(delta);
|
|
else
|
|
move = megaDragger->safeMoveAlongLine(delta, !isLocal);
|
|
|
|
if (move != Vector3::zero())
|
|
{
|
|
lastPoint3d = lastPoint3d + move;
|
|
|
|
if (!isLocal)
|
|
lastPoint3d = DragUtilities::toGrid(lastPoint3d);
|
|
}
|
|
}
|
|
}
|
|
else if ( getDragType() == HANDLE_ROTATE )
|
|
{
|
|
Extents extents;
|
|
CoordinateFrame owner_frame;
|
|
bool is_local = false;
|
|
|
|
if ( !getExtentsAndLocation(extents,owner_frame,is_local) )
|
|
return;
|
|
|
|
// owner position and orientation
|
|
|
|
const Vector3& owner_translation = owner_frame.translation;
|
|
const Matrix3& owner_rotation = owner_frame.rotation;
|
|
|
|
// compute pick ray
|
|
|
|
const RbxRay mouse_ray = getUnitMouseRay(inputObject);
|
|
const Vector3 mouse_screen(inputObject->get2DPosition().x,inputObject->get2DPosition().y,0);
|
|
|
|
// project owner to screen space
|
|
|
|
const Camera& camera = *workspace->getConstCamera();
|
|
const Vector3 owner_screen = camera.project(owner_translation);
|
|
|
|
// project adjusted pick ray to screen space
|
|
|
|
const Vector3 screen_diff = mouse_screen - owner_screen;
|
|
const Vector3 mid_screen = owner_screen + screen_diff / 2;
|
|
const RbxRay projection_ray = camera.worldRay(mid_screen.x,mid_screen.y);
|
|
const Vector3 axis_vector = normalIdToVector3(dragNormalId);
|
|
|
|
// determine plane normal
|
|
|
|
Vector3 plane_normal;
|
|
if ( is_local )
|
|
plane_normal = normalize(owner_rotation * axis_vector);
|
|
else
|
|
plane_normal = axis_vector;
|
|
|
|
// compute intersection of pick ray to axis plane
|
|
|
|
Vector3 plane_hit;
|
|
const Plane plane(plane_normal,owner_translation);
|
|
Math::intersectRayPlane(projection_ray,plane,plane_hit);
|
|
|
|
// compute new vector
|
|
|
|
Vector3 new_vector = plane_hit - owner_translation;
|
|
if ( Math::isNanInfVector3(new_vector) )
|
|
return;
|
|
|
|
// compute new rotation matrix from new vector
|
|
|
|
Vector3 right;
|
|
Vector3 up;
|
|
Vector3 at;
|
|
|
|
const NormalId pos_normal = intToNormalId(dragNormalId % 3);
|
|
const int pos_neg = dragNormalId == pos_normal ? 1.0f : -1.0f;
|
|
switch ( pos_normal )
|
|
{
|
|
case NORM_X:
|
|
right = normalize(owner_rotation.column(0));
|
|
at = normalize(pos_neg * new_vector);
|
|
up = normalize(at.cross(right));
|
|
break;
|
|
case NORM_Y:
|
|
up = normalize(owner_rotation.column(1));
|
|
right = normalize(pos_neg * new_vector);
|
|
at = normalize(right.cross(up));
|
|
break;
|
|
case NORM_Z:
|
|
at = normalize(owner_rotation.column(2));
|
|
up = normalize(pos_neg * new_vector);
|
|
right = normalize(up.cross(at));
|
|
break;
|
|
default:
|
|
RBXASSERT(false);
|
|
}
|
|
|
|
Matrix3 rotation(
|
|
right.x,up.x,at.x,
|
|
right.y,up.y,at.y,
|
|
right.z,up.z,at.z );
|
|
|
|
// compute delta rotation
|
|
const Matrix3 rotate_by = owner_rotation.inverse() * rotation;
|
|
|
|
// determine angle of rotation so that we can snap
|
|
|
|
Vector3 rotation_axis;
|
|
float rotation_angle;
|
|
rotate_by.toAxisAngle(rotation_axis,rotation_angle);
|
|
const float snaped_angle = snapRotationAngle(rotation_angle);
|
|
|
|
// only rotate with a minimum angle
|
|
|
|
if ( snaped_angle > 0.001f )
|
|
{
|
|
Matrix3 snapped_rotate = Matrix3::fromAxisAngle(rotation_axis,snaped_angle);
|
|
snapped_rotate = megaDragger->rotateDragParts(snapped_rotate, RBX::AdvArrowTool::advCollisionCheckMode);
|
|
|
|
if ( !is_local )
|
|
{
|
|
mMultiRotation *= snapped_rotate;
|
|
Math::orthonormalizeIfNecessary(mMultiRotation);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
shared_ptr<MouseCommand> AdvMoveToolBase::onMouseUp(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
//restore transparency
|
|
if (DFFlag::RestoreTransparencyOnToolChange)
|
|
AdvArrowToolBase::restoreSavedPartsTransparency();
|
|
else if (!RBX::AdvArrowTool::advCollisionCheckMode)
|
|
restoreSavedPartsTransparency();
|
|
|
|
//make sure if megadragger is present, in case of CTRL+Select, it doesn't get created
|
|
if (megaDragger.get())
|
|
{
|
|
megaDragger->finishDragging();
|
|
megaDragger.release();
|
|
}
|
|
|
|
if (DFFlag::DraggerUsesNewPartOnDuplicate && selectionChangedConnection.connected())
|
|
selectionChangedConnection.disconnect();
|
|
|
|
dragging = false;
|
|
releaseCapture();
|
|
|
|
//baseclass will take care of setting waypoint for undo/redo
|
|
Super::onMouseUp(inputObject);
|
|
|
|
return shared_ptr<MouseCommand>();
|
|
}
|
|
|
|
shared_ptr<MouseCommand> AdvMoveToolBase::onKeyDown(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
Super::onKeyDown(inputObject);
|
|
|
|
return shared_from(this);
|
|
}
|
|
|
|
void AdvMoveToolBase::render2d(Adorn* adorn)
|
|
{
|
|
Extents worldExtents;
|
|
CoordinateFrame location;
|
|
bool isLocal = false;
|
|
|
|
if (getExtentsAndLocation(worldExtents, location, isLocal))
|
|
{
|
|
RBX::DrawAdorn::handles2d(
|
|
worldExtents.size(),
|
|
location,
|
|
*(workspace->getConstCamera()),
|
|
adorn,
|
|
HANDLE_MOVE,
|
|
DrawAdorn::resizeColor());
|
|
|
|
if (isLocal)
|
|
{
|
|
RBX::DrawAdorn::partInfoText2D(
|
|
worldExtents.size(),
|
|
location,
|
|
*(workspace->getConstCamera()),
|
|
adorn,
|
|
"L",
|
|
DrawAdorn::cornflowerblue);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdvMoveToolBase::render3dAdorn(Adorn* adorn)
|
|
{
|
|
renderHoverOver(adorn);
|
|
|
|
Extents worldExtents;
|
|
CoordinateFrame location;
|
|
bool isLocal = false;
|
|
|
|
if (getExtentsAndLocation(worldExtents, location, isLocal))
|
|
{
|
|
RBX::DrawAdorn::handles3d(
|
|
worldExtents.size(),
|
|
location,
|
|
adorn,
|
|
HANDLE_MOVE,
|
|
workspace->getConstCamera()->coordinateFrame().translation,
|
|
DrawAdorn::resizeColor(),
|
|
true,
|
|
NORM_ALL_MASK,
|
|
overHandleNormalId,
|
|
Color3(0.3f, 0.7f, 1.0f));
|
|
}
|
|
}
|
|
|
|
bool AdvMoveToolBase::getExtentsAndLocation(
|
|
Extents& extents,
|
|
CoordinateFrame& location,
|
|
bool& isLocal ) const
|
|
{
|
|
ServiceClient<Selection> selection(workspace);
|
|
std::vector<Primitive*> primitives;
|
|
|
|
std::for_each(
|
|
selection->begin(),
|
|
selection->end(),
|
|
boost::bind(
|
|
&DragUtilities::getPrimitives2,
|
|
_1,
|
|
boost::ref(primitives)) );
|
|
|
|
if ( primitives.empty() )
|
|
return false;
|
|
|
|
if ( primitives.size() == 1 && getLocalSpaceMode())
|
|
{
|
|
isLocal = true;
|
|
|
|
PartInstance* part = PartInstance::fromPrimitive(primitives[0]);
|
|
if ( !part )
|
|
return false;
|
|
|
|
extents = part->getPartPrimitive()->getExtentsLocal();
|
|
if ( extents.longestSide() < MinimumExtentsSize )
|
|
extents = Extents::fromCenterRadius(extents.center(),MinimumExtentsSize / 2);
|
|
|
|
location = part->getLocation();
|
|
}
|
|
else
|
|
{
|
|
isLocal = false;
|
|
|
|
if (getDragType() == HANDLE_ROTATE || getDragType() == HANDLE_MOVE)
|
|
{
|
|
Extents newExtents;
|
|
CoordinateFrame primaryPartLocation;
|
|
if (getDragType() == HANDLE_ROTATE)
|
|
{
|
|
PartInstance* part = Dragger::computePrimaryPart(primitives);
|
|
primaryPartLocation = part->getCoordinateFrame();
|
|
newExtents = Dragger::computeExtentsRelative(primitives, primaryPartLocation);
|
|
}
|
|
else
|
|
{
|
|
newExtents = Dragger::computeExtents(primitives);
|
|
}
|
|
// The minimum distance between two different world space pivots
|
|
// to cause a new extents bounds to be calculated.
|
|
const float minDistance = 0.01f;
|
|
|
|
if ((dragging && getDragType() == HANDLE_MOVE) || !mInitializedExtents ||
|
|
(newExtents.center() - mExtents.center()).length() > minDistance)
|
|
{
|
|
mInitializedExtents = true;
|
|
mExtents = newExtents;
|
|
if ( mExtents.longestSide() < MinimumExtentsSize )
|
|
mExtents = Extents::fromCenterRadius(mExtents.center(),MinimumExtentsSize / 2);
|
|
}
|
|
|
|
extents = mExtents;
|
|
location.rotation = mMultiRotation;
|
|
if (getDragType() == HANDLE_ROTATE)
|
|
{
|
|
location.translation = primaryPartLocation.pointToWorldSpace(mExtents.center());
|
|
}
|
|
else
|
|
{
|
|
location.translation = mExtents.center();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
location.translation = extents.center();
|
|
location.rotation = Matrix3::identity();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AdvMoveToolBase::getExtents(Extents& extents) const
|
|
{
|
|
ServiceClient<Selection> selection(workspace);
|
|
|
|
std::vector<Primitive*> primitives;
|
|
|
|
std::for_each(
|
|
selection->begin(),
|
|
selection->end(),
|
|
boost::bind( &DragUtilities::getPrimitives2,
|
|
_1,
|
|
boost::ref(primitives))
|
|
);
|
|
|
|
if (primitives.size() > 0)
|
|
{
|
|
extents = Dragger::computeExtents(primitives);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool AdvMoveToolBase::getOverHandle(const shared_ptr<InputObject>& inputObject) const
|
|
{
|
|
Vector3 tempV;
|
|
NormalId tempN;
|
|
return getOverHandle(inputObject, tempV, tempN);
|
|
}
|
|
|
|
bool AdvMoveToolBase::getOverHandle(const shared_ptr<InputObject>& inputObject, Vector3& hitPointWorld, NormalId& normalId) const
|
|
{
|
|
RBXASSERT(!captured());
|
|
|
|
Extents extents;
|
|
overHandleNormalId = NORM_UNDEFINED;
|
|
CoordinateFrame location;
|
|
bool isLocal = false;
|
|
|
|
if (getExtentsAndLocation(extents, location, isLocal))
|
|
{
|
|
if (!isLocal)
|
|
extents = Extents::fromCenterCorner(Vector3::zero(),extents.size() * 0.5f);
|
|
|
|
if (HandleHitTest::hitTestHandleLocal(extents,
|
|
location,
|
|
HANDLE_MOVE,
|
|
this->getUnitMouseRay(inputObject),
|
|
hitPointWorld,
|
|
normalId)) {
|
|
overHandleNormalId = normalId;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AdvMoveToolBase::getLocalSpaceMode() const
|
|
{
|
|
return AdvMoveTool::advLocalTranslationMode;
|
|
}
|
|
|
|
void AdvMoveToolBase::saveAndModifyPartsTransparency()
|
|
{
|
|
PartArray allSelectedParts;
|
|
ServiceClient< FilteredSelection<PVInstance> > selectedPVInstances(workspace);
|
|
DragUtilities::pvsToParts(selectedPVInstances->items(), allSelectedParts);
|
|
|
|
for (size_t ii = 0; ii < allSelectedParts.size(); ++ii)
|
|
{
|
|
const shared_ptr<PartInstance>& spPart = allSelectedParts[ii].lock();
|
|
if (PartInstance::nonNullInWorkspace(spPart))
|
|
{
|
|
if (DFFlag::RestoreTransparencyOnToolChange)
|
|
AdvArrowToolBase::savePartTransparency(spPart);
|
|
else
|
|
{
|
|
origPartsTransparency[spPart] = spPart->getTransparencyUi();
|
|
spPart->setTransparency(0.5f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdvMoveToolBase::restoreSavedPartsTransparency()
|
|
{
|
|
for (PartsTransparencyCollection::const_iterator it = origPartsTransparency.begin(); it != origPartsTransparency.end(); ++it)
|
|
{
|
|
if (!it->first.expired())
|
|
it->first.lock()->setTransparency(it->second);
|
|
}
|
|
|
|
origPartsTransparency.clear();
|
|
}
|
|
|
|
shared_ptr<MouseCommand> AdvMoveTool::onMouseDown(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
ServiceClient<Selection> selection(workspace);
|
|
|
|
std::vector<Primitive*> primitives;
|
|
std::for_each(selection->begin(), selection->end(), boost::bind(&DragUtilities::getPrimitives2,_1, boost::ref(primitives)));
|
|
if( primitives.size() > 0 )
|
|
originalLocation = primitives[0]->getCoordinateFrame().translation;
|
|
|
|
return AdvMoveToolBase::onMouseDown(inputObject);
|
|
}
|
|
|
|
void AdvMoveTool::render2d(Adorn* adorn)
|
|
{
|
|
AdvMoveToolBase::render2d(adorn);
|
|
|
|
if (FFlag::StudioUseDraggerGrid && showDraggerGrid && dragging && (AdvArrowToolBase::advGridMode != DRAG::OFF))
|
|
{
|
|
ServiceClient<Selection> selection(workspace);
|
|
std::vector<Primitive*> primitives;
|
|
std::for_each(selection->begin(), selection->end(), boost::bind(&DragUtilities::getPrimitives2, _1, boost::ref(primitives)));
|
|
|
|
if (primitives.size() > 0)
|
|
{
|
|
G3D::Color3 gridColor = Color3::gray();
|
|
|
|
int boxesPerStud = 1;
|
|
if( AdvArrowToolBase::advGridMode == DRAG::QUARTER_STUD )
|
|
boxesPerStud = 5;
|
|
|
|
Extents extents = Dragger::computeExtents(primitives);
|
|
float effectiveRadius = extents.longestSide();
|
|
Vector4 bounds(effectiveRadius, effectiveRadius, -effectiveRadius, -effectiveRadius);
|
|
|
|
//compute the best plane for grid drawing based upon camera direction
|
|
PartInstance* part = PartInstance::fromPrimitive(primitives[0]);
|
|
G3D::Vector3 gridXDir, gridYDir;
|
|
getGridXYUsingCamera(part, gridXDir, gridYDir);
|
|
|
|
CoordinateFrame cordFrame(originalLocation);
|
|
DrawAdorn::surfaceGridAtCoord(adorn, cordFrame, bounds, gridXDir, gridYDir, gridColor, boxesPerStud);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdvMoveTool::getGridXYUsingCamera(RBX::PartInstance *part, G3D::Vector3 &gridXDir, G3D::Vector3 &gridYDir)
|
|
{
|
|
Vector3 cameraDirection = workspace->getConstCamera()->getCameraCoordinateFrame().vectorToWorldSpace(Vector3::unitZ());
|
|
Vector3 bodyXWorld = part->getCoordinateFrame().vectorToWorldSpace(Vector3::unitX());
|
|
Vector3 bodyYWorld = part->getCoordinateFrame().vectorToWorldSpace(Vector3::unitY());
|
|
Vector3 bodyZWorld = part->getCoordinateFrame().vectorToWorldSpace(Vector3::unitZ());
|
|
|
|
switch(dragAxis)
|
|
{
|
|
case NORM_X:
|
|
case NORM_X_NEG:
|
|
if(fabs(bodyZWorld.dot(cameraDirection)) > fabs(bodyYWorld.dot(cameraDirection)))
|
|
{
|
|
gridXDir = Vector3::unitX();
|
|
gridYDir = Vector3::unitY();
|
|
}
|
|
else
|
|
{
|
|
gridXDir = Vector3::unitZ();
|
|
gridYDir = Vector3::unitX();
|
|
}
|
|
break;
|
|
case NORM_Y:
|
|
case NORM_Y_NEG:
|
|
if( fabs(bodyZWorld.dot(cameraDirection)) > fabs(bodyXWorld.dot(cameraDirection)) )
|
|
{
|
|
gridXDir = Vector3::unitX();
|
|
gridYDir = Vector3::unitY();
|
|
}
|
|
else
|
|
{
|
|
gridXDir = Vector3::unitY();
|
|
gridYDir = Vector3::unitZ();
|
|
}
|
|
break;
|
|
case NORM_Z:
|
|
case NORM_Z_NEG:
|
|
if( fabs(bodyYWorld.dot(cameraDirection)) > fabs(bodyXWorld.dot(cameraDirection)) )
|
|
{
|
|
gridXDir = Vector3::unitZ();
|
|
gridYDir = Vector3::unitX();
|
|
}
|
|
else
|
|
{
|
|
gridXDir = Vector3::unitY();
|
|
gridYDir = Vector3::unitZ();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace
|