mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
545 lines
16 KiB
C++
545 lines
16 KiB
C++
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
|
#include "stdafx.h"
|
|
|
|
#include "Tool/ToolsArrow.h"
|
|
#include "Tool/DragTool.h"
|
|
#include "Tool/AdvDragTool.h"
|
|
#include "V8DataModel/Workspace.h"
|
|
#include "V8DataModel/PartInstance.h"
|
|
#include "V8DataModel/Camera.h"
|
|
#include "Tool/Dragger.h"
|
|
#include "V8World/World.h"
|
|
#include "V8World/ContactManager.h"
|
|
#include "AppDraw/Draw.h"
|
|
#include "GfxBase/Adorn.h"
|
|
#include "Util/Sound.h"
|
|
#include "Util/UserInputBase.h"
|
|
#include "Util/Selectable.h"
|
|
#include "V8DataModel/Decal.h"
|
|
#include "V8DataModel/ToolsSurface.h"
|
|
#include "v8datamodel/changehistory.h"
|
|
#include "v8datamodel/UserInputService.h"
|
|
|
|
FASTFLAGVARIABLE(StudioDE6194FixEnabled, false)
|
|
|
|
DYNAMIC_FASTFLAGVARIABLE(RestoreTransparencyOnToolChange, false)
|
|
|
|
namespace RBX {
|
|
|
|
const char* const sBoxSelectCommand = "BoxSelect";
|
|
const char* const sAdvArrowTool = "AdvArrow";
|
|
|
|
bool ArrowToolBase::showDraggerGrid = false;
|
|
|
|
DRAG::DraggerGridMode AdvArrowToolBase::advGridMode = DRAG::ONE_STUD;
|
|
bool AdvArrowToolBase::advManualJointMode = true;
|
|
DRAG::ManualJointType AdvArrowToolBase::advManualJointType = DRAG::STRONG_MANUAL_JOINT;
|
|
bool AdvArrowToolBase::advManipInProgress = false;
|
|
|
|
bool AdvArrowToolBase::advCollisionCheckMode = true;
|
|
|
|
bool AdvArrowToolBase::advLocalTranslationMode = false;
|
|
bool AdvArrowToolBase::advLocalRotationMode = true;
|
|
|
|
bool AdvArrowToolBase::advCreateJointsMode = true;
|
|
AdvArrowToolBase::PartsTransparencyCollection AdvArrowToolBase::originalPartsTransparency;
|
|
|
|
void ArrowToolBase::onMouseHover(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
onMouseIdle(inputObject);
|
|
}
|
|
|
|
void ArrowToolBase::onMouseIdle(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
overInstance = getUnlockedPart(inputObject);
|
|
|
|
// capture state of the alt key, we need it when rendering the adornments
|
|
if(UserInputService* userInputService = ServiceProvider::find<UserInputService>(workspace))
|
|
altKeyDown = userInputService->isAltDown();
|
|
}
|
|
|
|
const std::string ArrowToolBase::getCursorName() const
|
|
{
|
|
return overInstance ? "DragCursor" : "ArrowCursor";
|
|
}
|
|
|
|
Decal* ArrowToolBase::findDecal(PartInstance* p, const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
// try to grab decal if there is one and this part is selected already
|
|
int surfId;
|
|
Surface s = getSurface(workspace, inputObject, NULL, p, surfId);
|
|
if (s.getPartInstance())
|
|
{
|
|
if (p->getChildren())
|
|
{
|
|
Instances::const_iterator end = p->getChildren()->end();
|
|
for (Instances::const_iterator iter = p->getChildren()->begin(); iter != end; ++iter)
|
|
{
|
|
if (Decal* d = Instance::fastDynamicCast<Decal>(iter->get()))
|
|
{
|
|
if (d->getFace() == s.getNormalId()) // the decal is on the selected face
|
|
{
|
|
return d;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
shared_ptr<MouseCommand> ArrowToolBase::onMouseDown(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
RBXASSERT(inputObject->isLeftMouseDownEvent());
|
|
Vector3 hitWorld;
|
|
|
|
PartInstance* frontPart = getUnlockedPart(inputObject, hitWorld);
|
|
|
|
if (frontPart)
|
|
{
|
|
Instance* selectable3d;
|
|
|
|
UserInputService* userInputService = ServiceProvider::find<UserInputService>(workspace);
|
|
bool isAltKeyDown = false;
|
|
|
|
if(userInputService)
|
|
isAltKeyDown = userInputService->isAltDown();
|
|
|
|
if ( isAltKeyDown )
|
|
selectable3d = frontPart;
|
|
else
|
|
selectable3d = getTopSelectable3d(frontPart);
|
|
|
|
ServiceClient< Selection > selection(workspace);
|
|
|
|
if ( userInputService->isShiftDown() || userInputService->isCtrlDown() )
|
|
{
|
|
if (selection->isSelected(selectable3d))
|
|
selection->removeFromSelection(selectable3d);
|
|
else
|
|
selection->addToSelection(selectable3d);
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<Instance> selectIfNoDrag;
|
|
|
|
if (selectable3d && selection->isSelected(selectable3d))
|
|
selectIfNoDrag = shared_from<Instance>(findDecal(frontPart, inputObject));
|
|
else if(selectable3d)
|
|
selection->setSelection(selectable3d);
|
|
|
|
ServiceClient< FilteredSelection<Instance> > instanceSelection(workspace);
|
|
return DragTool::onMouseDown(
|
|
frontPart,
|
|
hitWorld,
|
|
instanceSelection->items(),
|
|
inputObject,
|
|
workspace,
|
|
selectIfNoDrag );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<MouseCommand> boxSelect = Creatable<MouseCommand>::create<BoxSelectCommand>(workspace);
|
|
return boxSelect->onMouseDown(inputObject);
|
|
}
|
|
|
|
return shared_ptr<MouseCommand>();
|
|
}
|
|
|
|
shared_ptr<MouseCommand> ArrowToolBase::onPeekKeyDown(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
#ifdef STUDIO_CAMERA_CONTROL_SHORTCUTS
|
|
RBXASSERT(inputObject->isKeyDownEvent());
|
|
switch(inputObject->getKeyCode())
|
|
{
|
|
case SDLK_f: // we want to zoom in on any selected object
|
|
if(UserInputService* userInputService = ServiceProvider::find<UserInputService>(workspace))
|
|
if( userInputService->isCtrlDown() && !workspace->getCamera()->isCharacterCamera() )
|
|
if(Verb* zoomExtentsVerb = workspace->getWhitelistVerb("Camera", "Zoom", "Extents"))
|
|
zoomExtentsVerb->doIt(&workspace->getDataState());
|
|
break;
|
|
case SDLK_LEFTBRACKET: // take us back one in our camera history
|
|
if(Camera* camera = workspace->getCamera())
|
|
camera->stepCameraHistoryBackward();
|
|
break;
|
|
case SDLK_RIGHTBRACKET: // take us forward one in our camera history
|
|
if(Camera* camera = workspace->getCamera())
|
|
camera->stepCameraHistoryForward();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
return Super::onPeekKeyDown(inputObject);
|
|
}
|
|
|
|
void ArrowToolBase::render3dAdorn(Adorn* adorn)
|
|
{
|
|
Super::render3dAdorn(adorn);
|
|
renderHoverOver(adorn,false);
|
|
}
|
|
|
|
/**
|
|
* Renders a hover over selection box when the mouse cursor is over an instance.
|
|
* When the drill down key is held, the part under the cursor is displayed rather than
|
|
* the top level instance.
|
|
*
|
|
* @param adorn used to render the selection box
|
|
* @param drillDownOnly will only display box if the drill down key is held down
|
|
*/
|
|
void ArrowToolBase::renderHoverOver(Adorn* adorn,bool drillDownOnly)
|
|
{
|
|
if ( overInstance && (!drillDownOnly || (drillDownOnly && altKeyDown)) )
|
|
{
|
|
Instance* instance;
|
|
|
|
if ( altKeyDown )
|
|
instance = overInstance;
|
|
else
|
|
instance = getTopSelectable3d(overInstance);
|
|
|
|
if ( instance )
|
|
{
|
|
IAdornable* adornable = dynamic_cast<IAdornable*>(instance);
|
|
if ( adornable )
|
|
adornable->render3dSelect(adorn,SELECT_HOVER);
|
|
}
|
|
}
|
|
}
|
|
|
|
AdvArrowToolBase::JointCreationMode AdvArrowToolBase::getJointCreationMode()
|
|
{
|
|
if (advCreateJointsMode)
|
|
{
|
|
if (advManualJointMode)
|
|
return AdvArrowToolBase::WELD_ALL;
|
|
|
|
return AdvArrowToolBase::SURFACE_JOIN_ONLY;
|
|
}
|
|
return AdvArrowToolBase::NO_JOIN;
|
|
}
|
|
|
|
void AdvArrowToolBase::restoreSavedPartsTransparency()
|
|
{
|
|
for (PartsTransparencyCollection::const_iterator it = originalPartsTransparency.begin(); it != originalPartsTransparency.end(); ++it)
|
|
{
|
|
if (shared_ptr<PartInstance> part = it->first.lock())
|
|
part->setTransparency(it->second);
|
|
}
|
|
|
|
originalPartsTransparency.clear();
|
|
}
|
|
|
|
void AdvArrowToolBase::savePartTransparency(shared_ptr<PartInstance> part)
|
|
{
|
|
originalPartsTransparency[part] = part->getTransparencyUi();
|
|
part->setTransparency(0.5);
|
|
}
|
|
|
|
|
|
namespace Reflection
|
|
{
|
|
template<> EnumDesc<AdvArrowToolBase::JointCreationMode>::EnumDesc()
|
|
:EnumDescriptor("JointCreationMode")
|
|
{
|
|
addPair(AdvArrowToolBase::WELD_ALL, "All");
|
|
addPair(AdvArrowToolBase::SURFACE_JOIN_ONLY, "Surface");
|
|
addPair(AdvArrowToolBase::NO_JOIN, "None");
|
|
}
|
|
|
|
template<>
|
|
AdvArrowToolBase::JointCreationMode& Variant::convert<AdvArrowToolBase::JointCreationMode>(void)
|
|
{
|
|
return genericConvert<AdvArrowToolBase::JointCreationMode>();
|
|
}
|
|
}
|
|
|
|
template<>
|
|
bool StringConverter<AdvArrowToolBase::JointCreationMode>::convertToValue(const std::string& text, AdvArrowToolBase::JointCreationMode& value)
|
|
{
|
|
return Reflection::EnumDesc<AdvArrowToolBase::JointCreationMode>::singleton().convertToValue(text.c_str(),value);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
// Advanced Arrow Tool
|
|
/*****************************************************************************/
|
|
|
|
const std::string AdvArrowToolBase::getCursorName() const
|
|
{
|
|
return overInstance ? "advCursor-openedHand" : "advCursor-default";
|
|
}
|
|
|
|
shared_ptr<MouseCommand> AdvArrowToolBase::onKeyDown(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
switch (inputObject->getKeyCode())
|
|
{
|
|
case SDLK_g: // _G_rid mode cycle (1-stud -> 1/5-stud -> off -> 1-stud ->...)
|
|
{
|
|
switch(advGridMode)
|
|
{
|
|
case DRAG::ONE_STUD:
|
|
{
|
|
advGridMode = DRAG::QUARTER_STUD;
|
|
break;
|
|
}
|
|
case DRAG::QUARTER_STUD:
|
|
{
|
|
advGridMode = DRAG::OFF;
|
|
break;
|
|
}
|
|
case DRAG::OFF:
|
|
{
|
|
advGridMode = DRAG::ONE_STUD;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case SDLK_j:
|
|
{
|
|
if(AdvArrowToolBase::advManualJointMode)
|
|
AdvArrowToolBase::advManualJointMode = false;
|
|
else
|
|
AdvArrowToolBase::advManualJointMode = true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#ifdef STUDIO_CAMERA_CONTROL_SHORTCUTS
|
|
return Super::onKeyDown(inputObject);
|
|
#else
|
|
return shared_ptr<MouseCommand>();
|
|
#endif
|
|
}
|
|
|
|
shared_ptr<MouseCommand> AdvArrowToolBase::onMouseDown(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
RBXASSERT(inputObject->isLeftMouseDownEvent());
|
|
Vector3 hitWorld;
|
|
|
|
PartInstance* frontPart = getUnlockedPart(inputObject, hitWorld);
|
|
|
|
if (frontPart)
|
|
{
|
|
Instance* selectable3d;
|
|
|
|
UserInputService* userInputService = ServiceProvider::find<UserInputService>(workspace);
|
|
|
|
if ( userInputService->isAltDown() )
|
|
selectable3d = frontPart;
|
|
else
|
|
selectable3d = getTopSelectable3d(frontPart);
|
|
|
|
ServiceClient< Selection > selection(workspace);
|
|
|
|
if ( userInputService->isCtrlDown() || userInputService->isShiftDown() )
|
|
{
|
|
if (selection->isSelected(selectable3d))
|
|
selection->removeFromSelection(selectable3d);
|
|
else
|
|
selection->addToSelection(selectable3d);
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<Instance> selectIfNoDrag;
|
|
|
|
if (selectable3d && selection->isSelected(selectable3d))
|
|
selectIfNoDrag = shared_from<Instance>(findDecal(frontPart, inputObject));
|
|
else if(selectable3d)
|
|
selection->setSelection(selectable3d);
|
|
|
|
ServiceClient< FilteredSelection<Instance> > instanceSelection(workspace);
|
|
return AdvDragTool::onMouseDown(
|
|
frontPart,
|
|
hitWorld,
|
|
instanceSelection->items(),
|
|
inputObject,
|
|
workspace,
|
|
selectIfNoDrag );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shared_ptr<MouseCommand> boxSelect = Creatable<MouseCommand>::create<BoxSelectCommand>(workspace);
|
|
return boxSelect->onMouseDown(inputObject);
|
|
}
|
|
|
|
return shared_ptr<MouseCommand>();
|
|
}
|
|
|
|
void AdvArrowToolBase::onMouseMove(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
AdvArrowToolBase::advManipInProgress = true;
|
|
determineManualJointConditions();
|
|
}
|
|
|
|
void AdvArrowToolBase::determineManualJointConditions(void)
|
|
{
|
|
ServiceClient< FilteredSelection<Instance> > instanceSelection(workspace);
|
|
manualJointHelper.setDisplayPotentialJoints(AdvArrowToolBase::advManualJointMode);
|
|
manualJointHelper.setSelectedPrimitives(instanceSelection->items());
|
|
if(AdvArrowToolBase::advManualJointMode || AdvArrowTool::advCreateJointsMode)
|
|
manualJointHelper.findPermissibleJointSurfacePairs();
|
|
|
|
if((manualJointHelper.getNumJointSurfacePairs() > 0 && AdvArrowTool::advManualJointMode) || manualJointHelper.autoJointsWereDetected())
|
|
setCursor("advClosed-hand-weld");
|
|
else if( instanceSelection->items().size() > 0 )
|
|
setCursor("advClosed-hand-no-weld");
|
|
}
|
|
|
|
shared_ptr<MouseCommand> AdvArrowToolBase::onMouseUp(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
if (DFFlag::RestoreTransparencyOnToolChange)
|
|
restoreSavedPartsTransparency();
|
|
|
|
if (AdvArrowToolBase::advManipInProgress)
|
|
{
|
|
AdvArrowToolBase::advManipInProgress = false;
|
|
manualJointHelper.createJointsIfEnabledFromGui();
|
|
ChangeHistoryService::requestWaypoint(getName().c_str(), workspace);
|
|
}
|
|
|
|
return shared_ptr<MouseCommand>();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Box Select
|
|
|
|
BoxSelectCommand::BoxSelectCommand(Workspace* workspace)
|
|
: Named<MouseCommand, sBoxSelectCommand>(workspace)
|
|
, selection(workspace)
|
|
{
|
|
FASTLOG1(FLog::MouseCommandLifetime, "BoxSelectCommand created: %p", this);
|
|
}
|
|
|
|
BoxSelectCommand::~BoxSelectCommand()
|
|
{
|
|
FASTLOG1(FLog::MouseCommandLifetime, "BoxSelectCommand destriyed: %p", this);
|
|
}
|
|
|
|
void BoxSelectCommand::selectAnd(const std::set< shared_ptr<Instance> >& newItemsInBox)
|
|
{
|
|
if (newItemsInBox == previousItemsInBox)
|
|
return;
|
|
|
|
Selection* selection = ServiceProvider::find<Selection>(DataModel::get(workspace));
|
|
|
|
shared_ptr<Instances> instances(shared_ptr<Instances>(new Instances));
|
|
for (std::set<shared_ptr<Instance> >::const_iterator iter = newItemsInBox.begin(); iter != newItemsInBox.end(); ++iter)
|
|
instances->push_back(*iter);
|
|
|
|
selection->setSelection(instances);
|
|
|
|
previousItemsInBox = newItemsInBox;
|
|
}
|
|
|
|
void BoxSelectCommand::selectReverse(const std::set< shared_ptr<Instance> >& newItemsInBox)
|
|
{
|
|
std::set<shared_ptr<Instance> > toggleItems;
|
|
std::set_difference(
|
|
newItemsInBox.begin(), newItemsInBox.end(),
|
|
previousItemsInBox.begin(), previousItemsInBox.end(),
|
|
std::inserter(toggleItems, toggleItems.begin())
|
|
);
|
|
|
|
std::set_difference(
|
|
previousItemsInBox.begin(), previousItemsInBox.end(),
|
|
newItemsInBox.begin(), newItemsInBox.end(),
|
|
std::inserter(toggleItems, toggleItems.begin())
|
|
);
|
|
|
|
if (toggleItems.size() > 0)
|
|
{
|
|
shared_ptr<Instances> instances(shared_ptr<Instances>(new Instances));
|
|
for (std::set<shared_ptr<Instance> >::const_iterator iter = toggleItems.begin(); iter != toggleItems.end(); ++iter)
|
|
instances->push_back(*iter);
|
|
|
|
selection->toggleSelection(instances);
|
|
}
|
|
|
|
previousItemsInBox = newItemsInBox;
|
|
}
|
|
|
|
shared_ptr<MouseCommand> BoxSelectCommand::onMouseDown(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
previousItemsInBox.clear();
|
|
|
|
capture();
|
|
|
|
UserInputService* userInputService = ServiceProvider::find<UserInputService>(workspace);
|
|
|
|
reverseSelecting = userInputService->isShiftDown();
|
|
|
|
if (!reverseSelecting)
|
|
selection->setSelection(shared_ptr<const Instances>());
|
|
|
|
mouseDownView = inputObject->get2DPosition();
|
|
mouseCurrentView = inputObject->get2DPosition();
|
|
|
|
return Super::onMouseDown(inputObject);
|
|
}
|
|
|
|
void BoxSelectCommand::onMouseMove(const shared_ptr<InputObject>& inputObject)
|
|
{
|
|
mouseCurrentView = inputObject->get2DPosition();
|
|
|
|
std::set< shared_ptr<Instance> > instances;
|
|
|
|
Rect2D r = Rect2D::xyxy(mouseDownView.x, mouseDownView.y, mouseCurrentView.x, mouseCurrentView.y);
|
|
const Camera* camera = workspace->getConstCamera();
|
|
getMouseInstances(instances, inputObject, r, camera, workspace);
|
|
|
|
if (reverseSelecting)
|
|
selectReverse(instances);
|
|
else
|
|
selectAnd(instances);
|
|
}
|
|
|
|
void BoxSelectCommand::getMouseInstances(std::set< shared_ptr<Instance> >& instances,
|
|
const shared_ptr<InputObject>& inputObject,
|
|
const Rect2D& selectBox, const Camera* camera,
|
|
Instance* currentInstance)
|
|
{
|
|
for (size_t i = 0; i < currentInstance->numChildren(); i++)
|
|
{
|
|
Instance* instance = currentInstance->getChild(i);
|
|
IHasLocation* iLocation = currentInstance->queryTypedChild<IHasLocation>(i);
|
|
if (!iLocation)
|
|
{
|
|
getMouseInstances(instances, inputObject, selectBox, camera, instance);
|
|
continue;
|
|
}
|
|
|
|
Selectable* selectable = currentInstance->queryTypedChild<Selectable>(i);
|
|
if (!selectable)
|
|
continue;
|
|
|
|
if (FFlag::StudioDE6194FixEnabled && !selectable->isSelectable3d())
|
|
continue;
|
|
|
|
if (!PartInstance::getLocked(instance))
|
|
{
|
|
// test intersection in Grid Units
|
|
Vector3 gridPos = iLocation->getLocation().translation;
|
|
Vector3 projection = camera->project(gridPos);
|
|
|
|
if (selectBox.contains(projection.xy()))
|
|
instances.insert(shared_from(instance));
|
|
}
|
|
}
|
|
}
|
|
|
|
void BoxSelectCommand::render2d(Adorn* adorn)
|
|
{
|
|
G3D::Rect2D temp = G3D::Rect2D::xyxy(mouseDownView.x, mouseDownView.y,
|
|
mouseCurrentView.x, mouseCurrentView.y);
|
|
adorn->outlineRect2d(temp, 0.75f, Color3::gray());
|
|
}
|
|
|
|
|
|
} // namespace
|