mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 13:17:49 +00:00
427 lines
13 KiB
C++
427 lines
13 KiB
C++
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
|
#include "stdafx.h"
|
|
|
|
#include "Tool/ToolsArrow.h"
|
|
#include "Tool/AdvLuaDragger.h"
|
|
#include "Tool/DragUtilities.h"
|
|
#include "Tool/Dragger.h"
|
|
#include "Tool/AdvRunDragger.h"
|
|
#include "V8DataModel/Workspace.h"
|
|
#include "V8DataModel/PartInstance.h"
|
|
#include "V8DataModel/MouseCommand.h"
|
|
#include "V8World/ContactManager.h"
|
|
#include "V8World/World.h"
|
|
#include "V8World/Joint.h"
|
|
#include "Tool/DragTypes.h"
|
|
|
|
#define DISABLE_GROUP_SNAP
|
|
|
|
LOGGROUP(DragProfile)
|
|
DYNAMIC_FASTFLAGVARIABLE(UnifyDragGridSizes, true)
|
|
|
|
namespace RBX {
|
|
|
|
const char* const sAdvLuaDragger = "AdvancedDragger";
|
|
|
|
AdvLuaDragger::AdvLuaDragger()
|
|
: DescribedCreatable<AdvLuaDragger, Instance, sAdvLuaDragger>(sAdvLuaDragger)
|
|
, dragPhase(NO_PARTS)
|
|
, hitPointHeight(0.0f)
|
|
{
|
|
lockParent();
|
|
}
|
|
|
|
AdvLuaDragger::~AdvLuaDragger()
|
|
{
|
|
}
|
|
|
|
static void addPart(shared_ptr<Instance> part, std::vector<weak_ptr<PartInstance> >* outputParts)
|
|
{
|
|
if(shared_ptr<PartInstance> partInstance = boost::dynamic_pointer_cast<PartInstance>(part)){
|
|
if (Workspace::getWorkspaceIfInWorkspace(partInstance.get())) {
|
|
weak_ptr<PartInstance> weakPartInstance(partInstance);
|
|
outputParts->push_back(weakPartInstance);
|
|
}
|
|
else {
|
|
throw std::runtime_error("Only Part objects in the Workspace should be passed to a Dragger:MouseDown function");
|
|
}
|
|
}
|
|
else{
|
|
throw std::runtime_error("Only Part objects should be passed to a Dragger:MouseDown function");
|
|
}
|
|
}
|
|
|
|
static void copyParts(shared_ptr<const Instances> inputParts, std::vector<weak_ptr<PartInstance> >& outputParts)
|
|
{
|
|
std::for_each(inputParts->begin(), inputParts->end(), boost::bind(&addPart, _1, &outputParts));
|
|
}
|
|
|
|
void AdvLuaDragger::mouseDownPublic(shared_ptr<Instance> _mousePart,
|
|
Vector3 _pointOnMousePart,
|
|
shared_ptr<const Instances> _dragParts)
|
|
{
|
|
if (std::find(_dragParts->begin(), _dragParts->end(), _mousePart) == _dragParts->end())
|
|
throw std::runtime_error("Mouse part needs to be in the list of drag parts when you call Dragger::MouseDown");
|
|
|
|
shared_ptr<PartInstance> mousePartInstance = boost::dynamic_pointer_cast<PartInstance>(_mousePart);
|
|
std::vector<weak_ptr<PartInstance> > dragPartInstances;
|
|
copyParts(_dragParts, dragPartInstances);
|
|
if(mousePartInstance.get() == NULL)
|
|
throw std::runtime_error("You must have a non-nil MousePart when you call Dragger:MouseDown");
|
|
if (!Workspace::getWorkspaceIfInWorkspace(mousePartInstance.get()))
|
|
throw std::runtime_error("You must have a MousePart that is in the Workspace when you call Dragger:MouseDown");
|
|
if(dragPartInstances.size() == 0)
|
|
throw std::runtime_error("You must have some parts when you call Dragger:MouseDown");
|
|
|
|
mouseDown(mousePartInstance, _pointOnMousePart, dragPartInstances);
|
|
}
|
|
|
|
|
|
void AdvLuaDragger::mouseDown(shared_ptr<PartInstance> _mousePart,
|
|
const Vector3& _pointOnMousePart,
|
|
const std::vector<weak_ptr<PartInstance> > _dragParts)
|
|
{
|
|
RBXASSERT(Workspace::getWorkspaceIfInWorkspace(_mousePart.get()));
|
|
|
|
if ((dragPhase == MOUSE_DOWN) || (dragPhase == DRAGGING)) {
|
|
throw std::runtime_error("Call to AdvLuaDragger::mouseDown when already dragging");
|
|
}
|
|
|
|
RBXASSERT(jointsIMade.size() == 0);
|
|
|
|
dragParts = _dragParts;
|
|
mousePart = _mousePart;
|
|
pointOnMousePart = _pointOnMousePart;
|
|
|
|
for (WeakParts::const_iterator iter = dragParts.begin(); iter != dragParts.end(); ++iter)
|
|
{
|
|
if (shared_ptr<PartInstance> partInstance = iter->lock())
|
|
m_originalPositions.push_back(_mousePart->getCoordinateFrame().toObjectSpace(partInstance->getCoordinateFrame()));
|
|
else
|
|
m_originalPositions.push_back(CoordinateFrame());
|
|
}
|
|
|
|
dragPhase = MOUSE_DOWN;
|
|
}
|
|
|
|
void AdvLuaDragger::mouseMove(RbxRay mouseRay)
|
|
{
|
|
FASTLOG3F(FLog::DragProfile, "AdvLuaDragger - received mouse ray, origin: %fx%fx%f",
|
|
mouseRay.origin().x, mouseRay.origin().y, mouseRay.origin().z);
|
|
|
|
FASTLOG3F(FLog::DragProfile, "AdvLuaDragger - received mouse ray, direction: %fx%fx%f",
|
|
mouseRay.direction().x, mouseRay.direction().y, mouseRay.direction().z);
|
|
|
|
RbxRay unitRay = mouseRay.unit();
|
|
|
|
if (dragPhase == NO_PARTS) {
|
|
throw std::runtime_error("Call to AdvLuaDragger::mouseMove without mouseDown");
|
|
}
|
|
else if ((dragPhase == MOUSE_UP_DRAGGED) || (dragPhase == MOUSE_UP_NO_DRAG)) {
|
|
throw std::runtime_error("Call to AdvLuaDragger::mouseMove after mouseUp");
|
|
}
|
|
else if (dragPhase == MOUSE_DOWN) {
|
|
tryStartDragging(unitRay); // can change phase to DRAGGING
|
|
}
|
|
|
|
if (dragPhase == DRAGGING) {
|
|
doDrag(unitRay);
|
|
}
|
|
}
|
|
|
|
void AdvLuaDragger::mouseUp()
|
|
{
|
|
if (dragPhase == DRAGGING) {
|
|
|
|
if(shared_ptr<PartInstance> safeMousePart = mousePart.lock())
|
|
{
|
|
if(Workspace::getWorkspaceIfInWorkspace(safeMousePart.get()))
|
|
{
|
|
G3D::Array<Primitive*> primitives;
|
|
DragUtilities::partsToPrimitives(dragParts, primitives);
|
|
RBXASSERT(primitives.size() > 0);
|
|
}
|
|
}
|
|
|
|
DragUtilities::joinToOutsiders(dragParts);
|
|
|
|
DragUtilities::stopDragging(dragParts);
|
|
advRunDragger.reset();
|
|
|
|
dragPhase = MOUSE_UP_DRAGGED;
|
|
|
|
|
|
|
|
}
|
|
else if (dragPhase == MOUSE_DOWN) {
|
|
dragPhase = MOUSE_UP_NO_DRAG;
|
|
}
|
|
else {
|
|
throw std::runtime_error("Call to AdvLuaDragger::mouseUp without mouseDown");
|
|
}
|
|
|
|
RBXASSERT(jointsIMade.size() == 0);
|
|
}
|
|
|
|
void AdvLuaDragger::tryStartDragging(const RbxRay& unitMouseRay)
|
|
{
|
|
if (shared_ptr<PartInstance> safeMousePart = mousePart.lock()){
|
|
if (Workspace::getWorkspaceIfInWorkspace(safeMousePart.get())) {
|
|
Vector3 mousePartWorld = safeMousePart->getCoordinateFrame().pointToWorldSpace(pointOnMousePart);
|
|
Vector3 rayToPart = mousePartWorld - unitMouseRay.origin();
|
|
float distanceToPart = rayToPart.magnitude();
|
|
Vector3 rayToSameDistance = unitMouseRay.direction() * distanceToPart;
|
|
float movementInWorld = (rayToPart - rayToSameDistance).magnitude();
|
|
float breakFreeDis = breakFreeDistance();
|
|
|
|
if (movementInWorld > breakFreeDis) {
|
|
Vector3 hitPointOnBackground;
|
|
if (getSnapHitPoint(safeMousePart.get(), unitMouseRay, hitPointOnBackground))
|
|
{
|
|
hitPointOffset = mousePartWorld - hitPointOnBackground; // stays constant during the drag operation
|
|
hitPointHeight = 0.0f; // init
|
|
startDragging();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdvLuaDragger::startDragging()
|
|
{
|
|
if(shared_ptr<PartInstance> safeMousePart = mousePart.lock()){
|
|
RBXASSERT(Workspace::getWorkspaceIfInWorkspace(safeMousePart.get()));
|
|
|
|
DragUtilities::unJoinFromOutsiders(dragParts); // destroy joints outside the parts....
|
|
|
|
DragUtilities::setDragging(dragParts);
|
|
|
|
DragUtilities::clean(dragParts);
|
|
|
|
dragPhase = DRAGGING;
|
|
|
|
advRunDragger.reset(new AdvRunDragger());
|
|
advRunDragger->initLocal( Workspace::findWorkspace(safeMousePart.get()),
|
|
mousePart,
|
|
pointOnMousePart,
|
|
dragParts);
|
|
}
|
|
}
|
|
|
|
void AdvLuaDragger::doDrag(const RbxRay& unitMouseRay)
|
|
{
|
|
FASTLOG(FLog::DragProfile, "AdvLuaDragger - doDrag");
|
|
|
|
RBXASSERT(dragPhase == DRAGGING);
|
|
if (shared_ptr<PartInstance> safeMousePart = mousePart.lock()){
|
|
if (Workspace::getWorkspaceIfInWorkspace(safeMousePart.get()))
|
|
{
|
|
if( dragParts.size() == 1 )
|
|
{
|
|
if (advRunDragger.get()) { // single part move
|
|
advRunDragger->snap(unitMouseRay);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (AdvRunDragger::dragMultiPartsAsSinglePart)
|
|
{
|
|
if (advRunDragger.get()) {
|
|
//std::clock_t start = std::clock();
|
|
advRunDragger->snap(unitMouseRay);
|
|
//RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "Time to snap (multiple part new): %lf", ( std::clock() - start ) / (double) CLOCKS_PER_SEC);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FASTLOG1(FLog::DragProfile, "Group drag, part: %p", safeMousePart.get());
|
|
#ifndef DISABLE_GROUP_SNAP
|
|
if (advRunDragger.get()) {
|
|
advRunDragger->snapGroup(unitMouseRay);
|
|
}
|
|
#else
|
|
Vector3 hitSnapped;
|
|
if (getSnapHitPoint(safeMousePart.get(), unitMouseRay, hitSnapped))
|
|
{
|
|
Vector3 currentLoc = safeMousePart->getCoordinateFrame().translation;
|
|
|
|
// uncomment this to have the hitpoint project on the y-axis down to the drag surface
|
|
// instead of using the mouse ray intersection with the drag surface
|
|
// it is commented for now since there are a couple edge cases that do not behave ideally (Tim L)
|
|
// hitSnapped += hitPointOffset;
|
|
currentLoc = safeMousePart->getCoordinateFrame().pointToWorldSpace(pointOnMousePart);
|
|
|
|
Vector3 currentBottom = currentLoc - Vector3(0.0f, hitPointHeight, 0.0f);
|
|
Vector3 delta = DragUtilities::toGrid(hitSnapped - currentBottom);
|
|
|
|
FASTLOG3F(FLog::DragProfile, "Delta, %fx%fx%f", delta.x, delta.y, delta.z);
|
|
|
|
if (delta != Vector3::zero())
|
|
{
|
|
Vector3 hitLocation;
|
|
if (DragUtilities::hitObject(dragParts, unitMouseRay, getContactManager(safeMousePart.get()), hitLocation))
|
|
DragUtilities::safeMoveYDrop(dragParts, delta, getContactManager(safeMousePart.get()), hitLocation.y);
|
|
else
|
|
DragUtilities::safeMoveYDrop(dragParts, delta, getContactManager(safeMousePart.get()), Dragger::groundPlaneDepth());
|
|
|
|
CoordinateFrame finalPosition = safeMousePart->getCoordinateFrame();
|
|
|
|
size_t coordinateFrameOffset = 0;
|
|
for (WeakParts::const_iterator iter = dragParts.begin(); iter != dragParts.end() && coordinateFrameOffset < m_originalPositions.size(); ++iter)
|
|
{
|
|
if (shared_ptr<PartInstance> partInstance = iter->lock())
|
|
partInstance->setCoordinateFrame(finalPosition * m_originalPositions[coordinateFrameOffset]);
|
|
|
|
++coordinateFrameOffset;
|
|
}
|
|
|
|
float requestedUpMovement = delta.y;
|
|
float actualUpMovement = finalPosition.translation.y - currentLoc.y;
|
|
float error = actualUpMovement - requestedUpMovement;
|
|
if (error != 0.0f) {
|
|
hitPointHeight += error;
|
|
}
|
|
}
|
|
|
|
FASTLOG(FLog::DragProfile, "Done dragging");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AdvLuaDragger::getSnapHitPoint(PartInstance* part, const RbxRay& unitMouseRay, Vector3& hitPoint)
|
|
{
|
|
if (Math::intersectRayPlane(MouseCommand::getSearchRay(unitMouseRay),
|
|
Plane(Vector3::unitY(), Vector3::zero()),
|
|
hitPoint)) {
|
|
hitPoint = DragUtilities::hitObjectOrPlane( dragParts,
|
|
unitMouseRay,
|
|
getContactManager(part) );
|
|
|
|
hitPoint = DragUtilities::toGrid(hitPoint);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void AdvLuaDragger::axisRotate(Vector3::Axis axis)
|
|
{
|
|
switch(axis)
|
|
{
|
|
case Vector3::X_AXIS:
|
|
rotateOnSnapFace(Vector3::X_AXIS,Math::matrixRotateX());
|
|
break;
|
|
case Vector3::Y_AXIS:
|
|
rotateOnSnapFace(Vector3::Y_AXIS,Math::matrixRotateY());
|
|
break;
|
|
case Vector3::Z_AXIS:
|
|
rotateOnSnapFace(Vector3::Z_AXIS,Math::matrixTiltZ());
|
|
break;
|
|
default:
|
|
RBXASSERT(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void AdvLuaDragger::rotateOnSnapFace(Vector3::Axis axis, const Matrix3& rotMatrix)
|
|
{
|
|
if (dragPhase == DRAGGING){
|
|
if(shared_ptr<PartInstance> safeMousePart = mousePart.lock()){
|
|
if(Workspace::getWorkspaceIfInWorkspace(safeMousePart.get())){
|
|
G3D::Array<Primitive*> primitives;
|
|
DragUtilities::partsToPrimitives(dragParts, primitives);
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
if( primitives.size() == 1 )
|
|
{
|
|
// use the single part run dragger
|
|
advRunDragger.get()->rotatePart90DegAboutSnapFaceAxis(axis);
|
|
}
|
|
else
|
|
Dragger::safeRotate(primitives, rotMatrix, getContactManager(safeMousePart.get()));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
RBXASSERT(0);
|
|
}
|
|
}
|
|
|
|
const float AdvLuaDragger::breakFreeDistance( void )
|
|
{
|
|
DRAG::DraggerGridMode mode = AdvArrowTool::advGridMode;
|
|
switch(mode)
|
|
{
|
|
case DRAG::ONE_STUD:
|
|
default:
|
|
return 0.5f;
|
|
break;
|
|
case DRAG::QUARTER_STUD:
|
|
return 0.1f;
|
|
break;
|
|
case DRAG::OFF:
|
|
if (DFFlag::UnifyDragGridSizes)
|
|
return 0.005f;
|
|
else
|
|
return 0.001f;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void AdvLuaDragger::toggleRunDraggerJointCreateMode( void )
|
|
{
|
|
if (dragPhase == DRAGGING)
|
|
{
|
|
if(shared_ptr<PartInstance> safeMousePart = mousePart.lock())
|
|
{
|
|
if(Workspace::getWorkspaceIfInWorkspace(safeMousePart.get()))
|
|
{
|
|
G3D::Array<Primitive*> primitives;
|
|
DragUtilities::partsToPrimitives(dragParts, primitives);
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
if( primitives.size() == 1 )
|
|
{
|
|
bool jointCreateOn = advRunDragger.get()->getJointCreateMode();
|
|
if( jointCreateOn )
|
|
advRunDragger.get()->setJointCreateMode(false);
|
|
else
|
|
advRunDragger.get()->setJointCreateMode(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AdvLuaDragger::alignPartToGrid( void )
|
|
{
|
|
// only for single part dragging
|
|
if (dragPhase == DRAGGING)
|
|
{
|
|
if(shared_ptr<PartInstance> safeMousePart = mousePart.lock())
|
|
{
|
|
if(Workspace::getWorkspaceIfInWorkspace(safeMousePart.get()))
|
|
{
|
|
G3D::Array<Primitive*> primitives;
|
|
DragUtilities::partsToPrimitives(dragParts, primitives);
|
|
RBXASSERT(primitives.size() > 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ContactManager& AdvLuaDragger::getContactManager(PartInstance* part)
|
|
{
|
|
return *Workspace::findWorkspace(part)->getWorld()->getContactManager();
|
|
}
|
|
|
|
|
|
} // namespace
|