mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 05:07:48 +00:00
948 lines
28 KiB
C++
948 lines
28 KiB
C++
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
|
#include "stdafx.h"
|
|
|
|
#include "Tool/Dragger.h"
|
|
#include "V8DataModel/PartInstance.h"
|
|
#include "V8DataModel/FastLogSettings.h"
|
|
#include "V8World/Primitive.h"
|
|
#include "V8World/ContactManager.h"
|
|
#include "V8World/Tolerance.h"
|
|
#include "Tool/DragUtilities.h"
|
|
#include "V8World/Tolerance.h"
|
|
#include "V8World/MegaClusterPoly.h"
|
|
#include "V8World/Mesh.h"
|
|
#include "V8World/Ball.h"
|
|
|
|
DYNAMIC_FASTINTVARIABLE(DraggerMaxMovePercent, 100)
|
|
DYNAMIC_FASTINTVARIABLE(DraggerMaxMoveSteps, 10000)
|
|
FASTFLAGVARIABLE(DraggerInfiniteRecursionFix, false)
|
|
|
|
namespace RBX {
|
|
|
|
Extents Dragger::computeExtentsRelative(const std::vector<Primitive*>& primitives, CoordinateFrame& relativeFrame)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
Extents answer = Extents::negativeMaxExtents();
|
|
for (size_t i = 0; i < primitives.size(); ++i)
|
|
{
|
|
answer.unionWith(primitives[i]->getExtentsLocal().express(primitives[i]->getCoordinateFrame(), relativeFrame));
|
|
}
|
|
return answer;
|
|
}
|
|
|
|
|
|
Extents Dragger::computeExtentsRelative(const G3D::Array<Primitive*>& primitives, CoordinateFrame& relativeFrame)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
Extents answer = Extents::negativeMaxExtents();
|
|
for (int i = 0; i < primitives.size(); ++i)
|
|
{
|
|
answer.unionWith(primitives[i]->getExtentsLocal().express(primitives[i]->getCoordinateFrame(), relativeFrame));
|
|
}
|
|
return answer;
|
|
}
|
|
|
|
PartInstance* Dragger::computePrimaryPart(const std::vector<Primitive*>& primitives)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
float maxSize = 0.0f;
|
|
int maxIndex = 0;
|
|
for (size_t i = 0; i < primitives.size(); ++i)
|
|
{
|
|
float area = primitives[i]->getExtentsWorld().areaXZ();
|
|
if (area > maxSize)
|
|
{
|
|
maxIndex = i;
|
|
}
|
|
}
|
|
return PartInstance::fromPrimitive(primitives[maxIndex]);
|
|
}
|
|
|
|
PartInstance* Dragger::computePrimaryPart(const G3D::Array<Primitive*>& primitives)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
float maxSize = 0.0f;
|
|
int maxIndex = 0;
|
|
for (int i = 0; i < primitives.size(); ++i)
|
|
{
|
|
float area = primitives[i]->getExtentsWorld().areaXZ();
|
|
if (area > maxSize)
|
|
{
|
|
maxIndex = i;
|
|
}
|
|
}
|
|
return PartInstance::fromPrimitive(primitives[maxIndex]);
|
|
}
|
|
|
|
|
|
Extents Dragger::computeExtents(const std::vector<Primitive*>& primitives)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
Extents answer = Extents::negativeMaxExtents();
|
|
for (size_t i = 0; i < primitives.size(); ++i) {
|
|
answer.unionWith(primitives[i]->getExtentsWorld());
|
|
}
|
|
return answer;
|
|
}
|
|
|
|
Extents Dragger::computeExtents(const G3D::Array<Primitive*>& primitives)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
Extents answer = Extents::negativeMaxExtents();
|
|
for (int i = 0; i < primitives.size(); ++i) {
|
|
answer.unionWith(primitives[i]->getExtentsWorld());
|
|
}
|
|
return answer;
|
|
}
|
|
|
|
bool Dragger::intersectingWorldOrOthers( PartInstance& partInstance,
|
|
ContactManager& contactManager,
|
|
const float tolerance,
|
|
const float bottomPlaneHeight )
|
|
{
|
|
G3D::Array<Primitive*> primitives;
|
|
primitives.append(partInstance.getPartPrimitive());
|
|
return intersectingWorldOrOthers(primitives, contactManager, tolerance, bottomPlaneHeight);
|
|
}
|
|
|
|
|
|
bool Dragger::intersectingWorldOrOthers(const G3D::Array<Primitive*>& primitives,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight )
|
|
{
|
|
return intersectingWorldOrOthers(primitives, contactManager, Tolerance::maxOverlapOrGap(), bottomPlaneHeight);
|
|
}
|
|
|
|
bool Dragger::intersectingGroundPlane(const G3D::Array<Primitive*>& primitives,
|
|
float yHeight)
|
|
{
|
|
for (int i = 0; i < primitives.size(); ++i) {
|
|
Primitive* p = primitives[i];
|
|
if ( (p->getFastFuzzyExtents().min().y < yHeight)
|
|
&& (p->getConstGeometry()->collidesWithGroundPlane(p->getCoordinateFrame(), yHeight))
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool Dragger::intersectingWorldOrOthers(const G3D::Array<Primitive*>& primitives,
|
|
ContactManager& contactManager,
|
|
const float tolerance,
|
|
const float bottomPlaneHeight )
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
return ( intersectingGroundPlane(primitives, bottomPlaneHeight)
|
|
|| contactManager.intersectingOthers(primitives, tolerance)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Dragger::movePrimitivesGoal( const G3D::Array<Primitive*>& primitives,
|
|
const Vector3& goal,
|
|
Vector3& movedSoFar,
|
|
bool snapToWorld)
|
|
{
|
|
Vector3 delta = goal - movedSoFar;
|
|
|
|
if (snapToWorld)
|
|
{
|
|
delta = DragUtilities::toGrid(delta);
|
|
}
|
|
else
|
|
{
|
|
delta = DragUtilities::toLocalGrid(delta);
|
|
}
|
|
|
|
if (delta != Vector3::zero()) {
|
|
movePrimitives(primitives, delta, snapToWorld);
|
|
movedSoFar = goal;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void Dragger::movePrimitivesDelta( const G3D::Array<Primitive*>& primitives,
|
|
const Vector3& delta,
|
|
Vector3& movedSoFar)
|
|
{
|
|
//RBXASSERT(delta == DragUtilities::toGrid(delta));
|
|
RBXASSERT(delta != Vector3::zero());
|
|
|
|
movePrimitives(primitives, delta);
|
|
movedSoFar = DragUtilities::toGrid(movedSoFar + delta);
|
|
//movedSoFar = movedSoFar + delta;
|
|
}
|
|
|
|
|
|
void Dragger::movePrimitives(const G3D::Array<Primitive*>& primitives,
|
|
const Vector3& delta,
|
|
bool snapToWorld)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
if (delta != Vector3::zero()) {
|
|
for (int i = 0; i < primitives.size(); ++i) {
|
|
Primitive* p = primitives[i];
|
|
PartInstance* part = PartInstance::fromPrimitive(p);
|
|
DragUtilities::moveByDelta(part, delta, snapToWorld);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Dragger::searchFine(const G3D::Array<Primitive*> primitives,
|
|
Vector3& movedSoFar,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight,
|
|
Vector3 insidePosition,
|
|
Vector3 outsidePosition)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
Vector3 currentPosition(0, 0, 0);
|
|
|
|
for (int i = 0; i < DFInt::DraggerMaxMoveSteps; ++i)
|
|
{
|
|
Vector3 newPosition((insidePosition + outsidePosition) * 0.5f);
|
|
|
|
if (Math::fuzzyEq(newPosition, currentPosition))
|
|
break;
|
|
|
|
movePrimitivesDelta(primitives, newPosition - currentPosition, movedSoFar);
|
|
|
|
if (Math::isNanInfVector3(movedSoFar))
|
|
{
|
|
RBXASSERT(0);
|
|
break;
|
|
}
|
|
|
|
currentPosition = newPosition;
|
|
|
|
if (intersectingWorldOrOthers(primitives, contactManager, 0.0f, bottomPlaneHeight))
|
|
{
|
|
insidePosition = currentPosition;
|
|
}
|
|
else
|
|
{
|
|
outsidePosition = currentPosition;
|
|
}
|
|
}
|
|
|
|
if (outsidePosition - currentPosition != Vector3::zero())
|
|
movePrimitivesDelta(primitives, outsidePosition - currentPosition, movedSoFar);
|
|
}
|
|
|
|
/*
|
|
0-100 move by 1 100
|
|
-200 move by 2 50
|
|
-400 move by 4 50
|
|
-800 move by 8 50
|
|
-1600 move by 16
|
|
-3200 move by 32
|
|
-6400 move by 64
|
|
-12800 move by 128
|
|
-25600 stop by 256
|
|
*/
|
|
|
|
const float maxMove() {return (16000.0f/DFInt::DraggerMaxMovePercent)*100;}
|
|
/*
|
|
float findSafeDelta(float movedY)
|
|
{
|
|
float answer = 1.2f;
|
|
|
|
while ((movedY > 100.0f) && (answer < (maxMove() * 0.01f)))
|
|
{
|
|
movedY *= 0.5f;
|
|
answer *= 2.0f;
|
|
}
|
|
return answer;
|
|
}
|
|
*/
|
|
|
|
|
|
// Note - recursive
|
|
void Dragger::searchUpGross( const G3D::Array<Primitive*>& primitives,
|
|
Vector3& movedSoFar,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
|
|
for (int i = 0; i < DFInt::DraggerMaxMoveSteps; ++i)
|
|
{
|
|
if (fabs(movedSoFar.y) > maxMove()) {
|
|
RBXASSERT_FISHING(0);
|
|
return;
|
|
}
|
|
|
|
// float delta = findSafeDelta(movedSoFar.y);
|
|
|
|
movePrimitivesDelta(primitives, Vector3(0.0f, 1.2f, 0.0f), movedSoFar);
|
|
|
|
// 3/1/08 - hack to try to prevent infinite looping - never got a good stack
|
|
if (Math::isNanInfVector3(movedSoFar)) {
|
|
RBXASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (!intersectingWorldOrOthers(primitives, contactManager, Tolerance::maxOverlapOrGap(), bottomPlaneHeight))
|
|
{
|
|
searchFine(primitives, movedSoFar, contactManager, bottomPlaneHeight, Vector3(0, -1.2f, 0), Vector3(0, 0, 0));
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Recursive
|
|
void Dragger::searchDownGross(const G3D::Array<Primitive*>& primitives,
|
|
Vector3& movedSoFar,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight)
|
|
{
|
|
RBXASSERT(primitives.size() > 0);
|
|
RBXASSERT(!intersectingWorldOrOthers(primitives, contactManager, Tolerance::maxOverlapOrGap(), bottomPlaneHeight));
|
|
|
|
for (int i = 0; i < DFInt::DraggerMaxMoveSteps; ++i)
|
|
{
|
|
movePrimitivesDelta(primitives, Vector3(0.0f,-1.2f, 0.0f), movedSoFar);
|
|
|
|
// 3/1/08 - hack to try to prevent infinite looping - never got a good stack
|
|
if (Math::isNanInfVector3(movedSoFar)) {
|
|
RBXASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (intersectingWorldOrOthers(primitives, contactManager, bottomPlaneHeight))
|
|
{
|
|
searchFine(primitives, movedSoFar, contactManager, bottomPlaneHeight, Vector3(0, 0, 0), Vector3(0, 1.2f, 0));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 1. Try moving to the end
|
|
// 2. If this fails, split the difference
|
|
|
|
void Dragger::safePlaceAlongLine( const G3D::Array<Primitive*>& primitives,
|
|
const Vector3& startMove,
|
|
const Vector3& endMove,
|
|
Vector3& movedSoFar,
|
|
ContactManager& contactManager,
|
|
bool snapToWorld)
|
|
{
|
|
RBXASSERT(startMove != endMove);
|
|
|
|
movePrimitivesGoal(primitives, endMove, movedSoFar, snapToWorld);
|
|
|
|
static const float overlapTolerance = 0.001f;
|
|
|
|
if (!intersectingWorldOrOthers(primitives, contactManager, overlapTolerance, maxDragDepth())) {
|
|
return; // We're done - moved to the end - no intersection
|
|
}
|
|
else {
|
|
// Moved to the end, intersection - must split
|
|
Vector3 middleMove = endMove - startMove; // will bias towards endMove if
|
|
middleMove = middleMove * 0.51f;
|
|
middleMove = startMove + middleMove;
|
|
|
|
if (snapToWorld)
|
|
{
|
|
middleMove = DragUtilities::toGrid(middleMove);
|
|
}
|
|
else
|
|
{
|
|
middleMove = DragUtilities::toLocalGrid(middleMove);
|
|
}
|
|
|
|
RBXASSERT(middleMove != startMove);
|
|
|
|
movePrimitivesGoal(primitives, middleMove, movedSoFar, snapToWorld);
|
|
|
|
bool intersecting = intersectingWorldOrOthers(primitives, contactManager, overlapTolerance, maxDragDepth());
|
|
|
|
if ((middleMove - endMove).length() < 0.001f || (FFlag::DraggerInfiniteRecursionFix && (middleMove - startMove).length() < 0.001f)) { // done - split down to a gap of 1 division
|
|
if (intersecting) {
|
|
movePrimitivesGoal(primitives, startMove, movedSoFar, snapToWorld);
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
if (intersecting) {
|
|
safePlaceAlongLine(primitives, startMove, middleMove, movedSoFar, contactManager, snapToWorld);
|
|
}
|
|
else {
|
|
safePlaceAlongLine(primitives, middleMove, endMove, movedSoFar, contactManager, snapToWorld);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SafeMoveAlongLine:
|
|
//
|
|
|
|
Vector3 Dragger::safeMoveAlongLine( const G3D::Array<Primitive*>& primitives,
|
|
const G3D::Vector3& tryMove,
|
|
ContactManager& contactManager,
|
|
const float customPlaneHeight,
|
|
bool snapToWorld )
|
|
{
|
|
if (primitives.size() == 0) {
|
|
RBXASSERT(0); // just wanted to see where this happens
|
|
return Vector3::zero();
|
|
}
|
|
|
|
Vector3 movedSoFar = Vector3::zero();
|
|
|
|
if (tryMove != Vector3::zero()) {
|
|
safePlaceAlongLine( primitives,
|
|
Vector3::zero(),
|
|
tryMove,
|
|
movedSoFar,
|
|
contactManager,
|
|
snapToWorld);
|
|
|
|
RBXASSERT( (movedSoFar == Vector3::zero())
|
|
|| !intersectingWorldOrOthers(primitives, contactManager, maxDragDepth())
|
|
);
|
|
}
|
|
|
|
return movedSoFar;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SafeMoveYDrop: If floating, move down until on something or 0 plane.
|
|
// If intersecting, move up
|
|
|
|
Vector3 Dragger::safeMoveYDrop( const G3D::Array<Primitive*>& primitives,
|
|
const G3D::Vector3& tryMove,
|
|
ContactManager& contactManager,
|
|
const float customPlaneHeight )
|
|
{
|
|
if (primitives.size() == 0) {
|
|
RBXASSERT(0); // just wanted to see where this happens
|
|
return Vector3::zero();
|
|
}
|
|
|
|
// if number of primitives are greater than 200 then only do optimized dragging
|
|
if (primitives.size() > RBX::ClientAppSettings::singleton().GetValueMinPartsForOptDragging())
|
|
return safeMoveYDrop_EXT(primitives, tryMove, contactManager, customPlaneHeight);
|
|
|
|
Vector3 moved = Math::toGrid(tryMove, Tolerance::mainGrid());
|
|
|
|
movePrimitives(primitives, moved);
|
|
|
|
if (intersectingWorldOrOthers(primitives, contactManager, customPlaneHeight)) {
|
|
searchUpGross(primitives, moved, contactManager, customPlaneHeight);
|
|
}
|
|
else {
|
|
searchDownGross(primitives, moved, contactManager, customPlaneHeight);
|
|
}
|
|
|
|
return moved;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// SafeMove: Move up (if necessary) until not overlapping
|
|
Vector3 Dragger::safeMoveNoDrop(const G3D::Array<Primitive*>& primitives,
|
|
const Vector3& tryMove,
|
|
ContactManager& contactManager)
|
|
{
|
|
Vector3 movedSoFar;
|
|
|
|
if (primitives.size() == 0) {
|
|
RBXASSERT(0); // just wanted to see where this happens
|
|
return Vector3::zero();
|
|
}
|
|
|
|
if (tryMove != Vector3::zero()) {
|
|
movePrimitives(primitives, tryMove);
|
|
movedSoFar += tryMove;
|
|
}
|
|
|
|
if (intersectingWorldOrOthers(primitives, contactManager, maxDragDepth())) {
|
|
searchUpGross(primitives, movedSoFar, contactManager, maxDragDepth());
|
|
}
|
|
|
|
return movedSoFar;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
void Dragger::safeRotate( const G3D::Array<Primitive*>& primitives,
|
|
const Matrix3& rotate,
|
|
ContactManager& contactManager)
|
|
{
|
|
RBXASSERT(primitives.size());
|
|
|
|
PartInstance* primaryPart = computePrimaryPart(primitives);
|
|
CoordinateFrame primaryPartLocation = primaryPart->getCoordinateFrame();
|
|
const Extents relativeExtents = computeExtentsRelative(primitives, primaryPartLocation);
|
|
Vector3 center = primaryPartLocation.pointToWorldSpace(relativeExtents.center());
|
|
|
|
const CoordinateFrame centerCoord(center);
|
|
const CoordinateFrame rotationCoord(rotate,Vector3::zero());
|
|
|
|
for (int i = 0; i < primitives.size(); ++i)
|
|
{
|
|
Primitive* p = primitives[i];
|
|
|
|
CoordinateFrame primInCenter = centerCoord.toObjectSpace(p->getCoordinateFrame());
|
|
CoordinateFrame newPrimInCenter = rotationCoord * primInCenter;
|
|
CoordinateFrame primInWorld = centerCoord * newPrimInCenter;
|
|
|
|
primInWorld.rotation.orthonormalize();
|
|
|
|
PartInstance* part = PartInstance::fromPrimitive(p);
|
|
part->setCoordinateFrame(primInWorld);
|
|
}
|
|
|
|
// detect intersection and move to safe place
|
|
// safeMoveNoDrop(primitives, Vector3::zero(), contactManager);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
void Dragger::safeRotate2( const G3D::Array<Primitive*>& primitives,
|
|
const Matrix3& rotate,
|
|
ContactManager& contactManager)
|
|
{
|
|
safeRotate(primitives, rotate, contactManager);
|
|
}
|
|
|
|
Vector3 Dragger::safeMoveYDrop_EXT( const G3D::Array<Primitive*>& primitives,
|
|
const Vector3& tryMove,
|
|
ContactManager& contactManager,
|
|
const float customPlaneHeight)
|
|
{
|
|
if ((primitives.size() == 0) || (tryMove == Vector3::zero()))
|
|
return Vector3::zero();
|
|
|
|
Vector3 moved = Math::toGrid(tryMove, Tolerance::mainGrid());
|
|
|
|
// populate data
|
|
RBX::Extents tmpExtent;
|
|
std::vector<RBX::Extents> primExtents;
|
|
primExtents.reserve(primitives.size());
|
|
|
|
// ContactManager requires unordered_set
|
|
boost::unordered_set<const Primitive*> ignorePrimitives;
|
|
|
|
for (G3D::Array<Primitive*>::const_iterator iter = primitives.begin(); iter != primitives.end(); ++iter) {
|
|
// extents data
|
|
tmpExtent = RBX::Extents(Math::toGrid((*iter)->getFastFuzzyExtents().min(), Tolerance::mainGrid()),
|
|
Math::toGrid((*iter)->getFastFuzzyExtents().max(), Tolerance::mainGrid()));
|
|
|
|
tmpExtent.expand(-Tolerance::maxOverlapOrGap());
|
|
tmpExtent.shift(moved); //instead of again looping we can move the extents now
|
|
|
|
primExtents.push_back(tmpExtent);
|
|
// for contact manager
|
|
ignorePrimitives.insert(*iter);
|
|
}
|
|
|
|
//sort prims based upon the y value
|
|
//TODO: do this only once when we select the prims?
|
|
G3D::Array<Primitive*> tempPrimitives(primitives);
|
|
Primitive* tempPrim = NULL;
|
|
for (int ii=0; ii < (int)primExtents.size(); ++ii)
|
|
{
|
|
for (int jj=0; jj < (int)primExtents.size() - ii -1; ++jj)
|
|
{
|
|
if (primExtents[jj].min().y > primExtents[ii].min().y)
|
|
{
|
|
tmpExtent = primExtents[jj];
|
|
primExtents[jj] = primExtents[ii];
|
|
primExtents[ii] = tmpExtent;
|
|
|
|
tempPrim = tempPrimitives[jj];
|
|
tempPrimitives[jj] = tempPrimitives[ii];
|
|
tempPrimitives[ii] = tempPrim;
|
|
}
|
|
}
|
|
}
|
|
|
|
//std::clock_t start = std::clock();
|
|
|
|
if (intersectingWorldOrOthers_EXT(primExtents, tempPrimitives, ignorePrimitives, contactManager, customPlaneHeight, moved)) {
|
|
searchUpGross_EXT(primExtents, tempPrimitives, ignorePrimitives, contactManager, customPlaneHeight, moved);
|
|
}
|
|
else {
|
|
searchDownGross_EXT(primExtents, tempPrimitives, ignorePrimitives, contactManager, customPlaneHeight, moved);
|
|
}
|
|
|
|
//RBX::StandardOut::singleton()->printf(RBX::MESSAGE_ERROR, "TryMove: %f - Moved: %f - Total Time: %lf", tryMove.y, moved.y, (std::clock() - start ) / (double) CLOCKS_PER_SEC);
|
|
|
|
movePrimitives(primitives, moved);
|
|
|
|
return moved;
|
|
}
|
|
|
|
void Dragger::searchUpGross_EXT( std::vector<RBX::Extents> &primExtents,
|
|
const G3D::Array<Primitive*>& primitives,
|
|
const boost::unordered_set<const Primitive*> &ignorePrimitives,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight,
|
|
Vector3& movedSoFar)
|
|
{
|
|
if (fabs(movedSoFar.y) > maxMove()) {
|
|
RBXASSERT_FISHING(0);
|
|
return;
|
|
}
|
|
|
|
moveExtentsDelta(primExtents, Vector3(0.0f, 1.2f, 0.0f), movedSoFar);
|
|
|
|
// hack to try to prevent infinite looping
|
|
if (Math::isNanInfVector3(movedSoFar)) {
|
|
RBXASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (intersectingWorldOrOthers_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar)) {
|
|
searchUpGross_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar);
|
|
}
|
|
else {
|
|
searchDownFine_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar);
|
|
}
|
|
}
|
|
|
|
void Dragger::searchDownGross_EXT( std::vector<RBX::Extents> &primExtents,
|
|
const G3D::Array<Primitive*>& primitives,
|
|
const boost::unordered_set<const Primitive*> &ignorePrimitives,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight,
|
|
Vector3& movedSoFar)
|
|
{
|
|
if (fabs(movedSoFar.y) > maxMove()) {
|
|
RBXASSERT_FISHING(0);
|
|
return;
|
|
}
|
|
|
|
moveExtentsDelta(primExtents, Vector3(0.0f, -1.2f, 0.0f), movedSoFar);
|
|
|
|
// hack to try to prevent infinite looping
|
|
if (Math::isNanInfVector3(movedSoFar)) {
|
|
RBXASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (!intersectingWorldOrOthers_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar)) {
|
|
searchDownGross_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar);
|
|
}
|
|
else {
|
|
searchUpFine_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar);
|
|
}
|
|
}
|
|
|
|
void Dragger::searchUpFine_EXT( std::vector<RBX::Extents> &primExtents,
|
|
const G3D::Array<Primitive*>& primitives,
|
|
const boost::unordered_set<const Primitive*> &ignorePrimitives,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight,
|
|
Vector3& movedSoFar)
|
|
{
|
|
if (fabs(movedSoFar.y) > maxMove()) {
|
|
RBXASSERT_FISHING(0);
|
|
return;
|
|
}
|
|
|
|
moveExtentsDelta(primExtents, Vector3(0.0f, 0.1f, 0.0f), movedSoFar);
|
|
|
|
// hack to try to prevent infinite looping
|
|
if (Math::isNanInfVector3(movedSoFar)) {
|
|
RBXASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (intersectingWorldOrOthers_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar)) {
|
|
searchUpFine_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar);
|
|
}
|
|
else {
|
|
//done
|
|
}
|
|
}
|
|
|
|
void Dragger::searchDownFine_EXT( std::vector<RBX::Extents> &primExtents,
|
|
const G3D::Array<Primitive*>& primitives,
|
|
const boost::unordered_set<const Primitive*> &ignorePrimitives,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight,
|
|
Vector3& movedSoFar)
|
|
{
|
|
if (fabs(movedSoFar.y) > maxMove()) {
|
|
RBXASSERT_FISHING(0);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
moveExtentsDelta(primExtents, Vector3(0.0f, -0.1f, 0.0f), movedSoFar);
|
|
|
|
// hack to try to prevent infinite looping
|
|
if (Math::isNanInfVector3(movedSoFar)) {
|
|
RBXASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (!intersectingWorldOrOthers_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar)) {
|
|
searchDownFine_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar);
|
|
}
|
|
else {
|
|
searchUpFine_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar);
|
|
}
|
|
*/
|
|
|
|
int min = 0;
|
|
int max = 12;
|
|
int mid = 0;
|
|
while (max > min)
|
|
{
|
|
mid = (min+max)/2;
|
|
moveExtentsDelta(primExtents, Vector3(0.0f, -(mid)/10.0f, 0.0f), movedSoFar);
|
|
|
|
// hack to try to prevent infinite looping
|
|
if (Math::isNanInfVector3(movedSoFar)) {
|
|
RBXASSERT(0);
|
|
return;
|
|
}
|
|
|
|
if (intersectingWorldOrOthers_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar)) {
|
|
searchUpFine_EXT(primExtents, primitives, ignorePrimitives, contactManager, bottomPlaneHeight, movedSoFar);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
max = mid;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Dragger::intersectingWorldOrOthers_EXT(std::vector<RBX::Extents> &primExtents,
|
|
const G3D::Array<Primitive*>& primitives,
|
|
const boost::unordered_set<const Primitive*> &ignorePrimitives,
|
|
ContactManager& contactManager,
|
|
const float bottomPlaneHeight,
|
|
const Vector3& movedSoFar)
|
|
{
|
|
//check if intersecting with ground
|
|
if (intersectingGroundPlane_EXT(primExtents, primitives, bottomPlaneHeight, movedSoFar))
|
|
return true;
|
|
|
|
G3D::Array<Primitive*> foundPrims;
|
|
for (int a = 0; a < (int)primExtents.size(); ++a) {
|
|
//look for only 1 prim instead of getting a complete list
|
|
foundPrims.fastClear();
|
|
contactManager.getPrimitivesTouchingExtents(primExtents[a], ignorePrimitives, 1, foundPrims);
|
|
if (foundPrims.size()) {
|
|
//ideally we must get only one prim here!!!
|
|
for (int zz = 0; zz < foundPrims.size(); ++zz) {
|
|
RBX::Extents tmpExtent(Math::toGrid(foundPrims[zz]->getFastFuzzyExtents().min(), Tolerance::mainGrid()),
|
|
Math::toGrid(foundPrims[zz]->getFastFuzzyExtents().max(), Tolerance::mainGrid()));
|
|
tmpExtent.expand(-Tolerance::maxOverlapOrGap()+0.001);
|
|
|
|
if (tmpExtent.overlapsOrTouches(primExtents[a]))
|
|
{
|
|
//if the found prim is a block and not rotated then return
|
|
if( foundPrims[zz]->getConstGeometry()->getGeometryType() == Geometry::GEOMETRY_BLOCK &&
|
|
Math::isAxisAligned(foundPrims[zz]->getCoordinateFrame().rotation) )
|
|
return true;
|
|
|
|
CoordinateFrame foundCFrame = foundPrims[zz]->getCoordinateFrame();
|
|
CoordinateFrame movingPrimCF(primitives[a]->getCoordinateFrame().rotation, primExtents[a].center());
|
|
|
|
if (isIntersecting(foundPrims[zz], foundCFrame, primitives[a], movingPrimCF))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
//check if there are any terrain cells in the extent's region
|
|
Region3 correctedRegion(primExtents[a].min(), primExtents[a].max());
|
|
if (contactManager.terrainCellsInRegion3(correctedRegion))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Dragger::intersectingGroundPlane_EXT( const std::vector<RBX::Extents>& primExtents,
|
|
const G3D::Array<Primitive*>& primitives,
|
|
const float yHeight,
|
|
const Vector3& movedSoFar)
|
|
{
|
|
for (int i = 0; i < primitives.size(); ++i) {
|
|
if ((primExtents[i].min().y < yHeight)&& (primitives[i]->getConstGeometry()->collidesWithGroundPlane(primExtents[i].center(), yHeight)))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Dragger::isIntersecting(const Primitive* prim1, const CoordinateFrame &cFrame1, const Primitive* prim2, const CoordinateFrame &cFrame2)
|
|
{
|
|
Geometry::GeometryType prim1GeomType(prim1->getConstGeometry()->getGeometryType());
|
|
Geometry::GeometryType prim2GeomType(prim2->getConstGeometry()->getGeometryType());
|
|
|
|
switch (prim1GeomType)
|
|
{
|
|
case Geometry::GEOMETRY_BALL:
|
|
{
|
|
switch (prim2GeomType)
|
|
{
|
|
case Geometry::GEOMETRY_BALL:
|
|
return checkBallBallIntersection(prim1, cFrame1, prim2, cFrame2);
|
|
default:
|
|
return checkBallPolyIntersection(prim1, cFrame1, prim2, cFrame2);
|
|
}
|
|
}
|
|
|
|
default:
|
|
{
|
|
switch (prim2GeomType)
|
|
{
|
|
case Geometry::GEOMETRY_BALL:
|
|
return checkBallPolyIntersection(prim2, cFrame2, prim1, cFrame1);
|
|
default:
|
|
return checkPolyPolyIntersection(prim1, cFrame1, prim2, cFrame2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Dragger::checkBallPolyIntersection(const Primitive* ballPrim, const CoordinateFrame &ballCFrame, const Primitive* polyPrim, const CoordinateFrame &polyCFrame)
|
|
{
|
|
const Ball* ballGeom = dynamic_cast<const Ball*>(ballPrim->getConstGeometry());
|
|
const Poly* polyGeom = dynamic_cast<const Poly*>(polyPrim->getConstGeometry());
|
|
if (!ballGeom || !polyGeom)
|
|
return true;
|
|
|
|
const POLY::Mesh* polyMesh = polyGeom->getMesh();
|
|
if (!polyMesh)
|
|
return true;
|
|
|
|
// check if polygon edge is lying inside of sphere
|
|
Sphere ballSphere(Vector3::zero(), ballGeom->getRadius());
|
|
for (unsigned int i = 0; i < polyMesh->numVertices(); i++)
|
|
{
|
|
if (ballSphere.contains(ballCFrame.pointToObjectSpace(polyCFrame.pointToWorldSpace(polyMesh->getVertex(i)->getOffset()))))
|
|
return true;
|
|
}
|
|
|
|
Vector3 p1, p2, hitPoint, unusedSurfaceNormal;
|
|
RbxRay edgeRay;
|
|
|
|
// check if edge is intersecting with sphere
|
|
for (unsigned int j = 0; j < polyMesh->numEdges(); j++)
|
|
{
|
|
p1 = ballCFrame.pointToObjectSpace(polyCFrame.pointToWorldSpace(polyMesh->getEdge(j)->getVertexOffset(NULL, 0)));
|
|
p2 = ballCFrame.pointToObjectSpace(polyCFrame.pointToWorldSpace(polyMesh->getEdge(j)->getVertexOffset(NULL, 1)));
|
|
edgeRay = RbxRay(p1, (p2 - p1).direction());
|
|
|
|
if ((const_cast<Ball*>(ballGeom))->hitTest(edgeRay, hitPoint, unusedSurfaceNormal))
|
|
{
|
|
if (((hitPoint - p1).magnitude() <= (p2 - p1).magnitude()))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Dragger::checkBallBallIntersection(const Primitive* ballPrim1, const CoordinateFrame &ballCFrame1, const Primitive* ballPrim2, const CoordinateFrame &ballCFrame2)
|
|
{
|
|
const Ball* ballGeom1 = dynamic_cast<const Ball*>(ballPrim1->getConstGeometry());
|
|
const Ball* ballGeom2 = dynamic_cast<const Ball*>(ballPrim2->getConstGeometry());
|
|
|
|
if (!ballGeom1 || !ballGeom2)
|
|
return true;
|
|
|
|
Vector3 delta = ballCFrame1.translation - ballCFrame2.translation;
|
|
float radiusSum = ballGeom1->getRadius() + ballGeom2->getRadius();
|
|
|
|
if (Math::longestVector3Component(delta) < radiusSum)
|
|
return (delta.length() < (radiusSum - Tolerance::maxOverlapOrGap()));
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Dragger::checkPolyPolyIntersection(const Primitive* polyPrim1, const CoordinateFrame &polyCFrame1, const Primitive* polyPrim2, const CoordinateFrame &polyCFrame2)
|
|
{
|
|
const Poly* polyGeom1 = dynamic_cast<const Poly*>(polyPrim1->getConstGeometry());
|
|
const Poly* polyGeom2 = dynamic_cast<const Poly*>(polyPrim2->getConstGeometry());
|
|
|
|
if (!polyGeom1 || !polyGeom2)
|
|
return true;
|
|
|
|
const POLY::Mesh* polyMesh1 = polyGeom1->getMesh();
|
|
const POLY::Mesh* polyMesh2 = polyGeom2->getMesh();
|
|
if (!polyMesh1 || !polyMesh2)
|
|
return true;
|
|
|
|
// Perform 4 checks for polyhedron intersection; return immediately if any are true
|
|
|
|
// #1: Are any movingPrimPoly verts inside of the foundPoly
|
|
for (unsigned int i = 0; i < polyMesh1->numVertices(); i++)
|
|
{
|
|
if (polyMesh2->pointInMesh(polyCFrame2.pointToObjectSpace(polyCFrame1.pointToWorldSpace(polyMesh1->getVertex(i)->getOffset()))))
|
|
return true;
|
|
}
|
|
|
|
// #2: Are any foundPoly verts inside of the movingPrimPoly
|
|
for (unsigned int i = 0; i < polyMesh2->numVertices(); i++)
|
|
{
|
|
if (polyMesh1->pointInMesh(polyCFrame1.pointToObjectSpace(polyCFrame2.pointToWorldSpace(polyMesh2->getVertex(i)->getOffset()))))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Dragger::moveExtents(std::vector<RBX::Extents> &primExtents, const Vector3& delta)
|
|
{
|
|
std::vector<RBX::Extents>::iterator end_iter = primExtents.end();
|
|
for (std::vector<RBX::Extents>::iterator iter = primExtents.begin(); iter != end_iter; ++iter)
|
|
(*iter).shift(delta);
|
|
}
|
|
|
|
void Dragger::moveExtentsDelta(std::vector<RBX::Extents> &primExtents, const Vector3& delta, Vector3& movedSoFar)
|
|
{
|
|
moveExtents(primExtents, delta);
|
|
//movedSoFar = DragUtilities::toGrid(movedSoFar + delta);
|
|
movedSoFar = movedSoFar + delta;
|
|
}
|
|
|
|
} // namespace
|