diff --git a/interface/resources/images/pin.svg b/interface/resources/images/pin.svg
new file mode 100644
index 0000000000..ec968a1ec1
--- /dev/null
+++ b/interface/resources/images/pin.svg
@@ -0,0 +1,98 @@
+
+
diff --git a/interface/resources/images/pinned.svg b/interface/resources/images/pinned.svg
new file mode 100644
index 0000000000..bda6f0e747
--- /dev/null
+++ b/interface/resources/images/pinned.svg
@@ -0,0 +1,106 @@
+
+
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index ad216458bd..84ce1f7efe 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -531,6 +531,42 @@ void Application::paintGL() {
_myCamera.setTargetPosition(_myAvatar->getHead()->calculateAverageEyePosition());
_myCamera.setTargetRotation(_myAvatar->getHead()->getCameraOrientation());
+ glm::vec3 planeNormal = _myCamera.getTargetRotation() * IDENTITY_FRONT;
+ const float BASE_PUSHBACK_RADIUS = 0.25f;
+ float pushbackRadius = _myCamera.getNearClip() + _myAvatar->getScale() * BASE_PUSHBACK_RADIUS;
+ glm::vec4 plane(planeNormal, -glm::dot(planeNormal, _myCamera.getTargetPosition()) - pushbackRadius);
+
+ // push camera out of any intersecting avatars
+ float pushback = 0.0f;
+ foreach (const AvatarSharedPointer& avatarData, _avatarManager.getAvatarHash()) {
+ Avatar* avatar = static_cast(avatarData.data());
+ if (avatar->isMyAvatar()) {
+ continue;
+ }
+ if (glm::distance(avatar->getPosition(), _myCamera.getTargetPosition()) >
+ avatar->getBoundingRadius() + pushbackRadius) {
+ continue;
+ }
+ float angle = angleBetween(avatar->getPosition() - _myCamera.getTargetPosition(), planeNormal);
+ if (angle > PI_OVER_TWO) {
+ continue;
+ }
+ float scale = 1.0f - angle / PI_OVER_TWO;
+ scale = qMin(1.0f, scale * 2.5f);
+ static CollisionList collisions(64);
+ collisions.clear();
+ if (!avatar->findPlaneCollisions(plane, collisions)) {
+ continue;
+ }
+ for (int i = 0; i < collisions.size(); i++) {
+ pushback = qMax(pushback, glm::length(collisions.getCollision(i)->_penetration) * scale);
+ }
+ }
+ const float MAX_PUSHBACK = 0.35f;
+ const float PUSHBACK_DECAY = 0.5f;
+ _myCamera.setDistance(qMax(qMin(pushback, MAX_PUSHBACK * _myAvatar->getScale()),
+ _myCamera.getDistance() * PUSHBACK_DECAY));
+
} else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
_myCamera.setTightness(0.0f); // Camera is directly connected to head without smoothing
_myCamera.setTargetPosition(_myAvatar->getUprightHeadPosition());
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index c68afdbf5f..0d3c66e3e7 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -1061,7 +1061,7 @@ void Menu::showMetavoxelEditor() {
void Menu::showChat() {
QMainWindow* mainWindow = Application::getInstance()->getWindow();
if (!_chatWindow) {
- mainWindow->addDockWidget(Qt::NoDockWidgetArea, _chatWindow = new ChatWindow());
+ mainWindow->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow());
}
if (!_chatWindow->toggleViewAction()->isChecked()) {
int width = _chatWindow->width();
diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp
index 4a6d0fbcf2..475e7a1abc 100644
--- a/interface/src/avatar/Avatar.cpp
+++ b/interface/src/avatar/Avatar.cpp
@@ -529,6 +529,11 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet
//return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
}
+bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) {
+ return _skeletonModel.findPlaneCollisions(plane, collisions) ||
+ getHead()->getFaceModel().findPlaneCollisions(plane, collisions);
+}
+
void Avatar::updateShapePositions() {
_skeletonModel.updateShapePositions();
Model& headModel = getHead()->getFaceModel();
diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h
index cad0ddcdf0..ca05e5dbbf 100755
--- a/interface/src/avatar/Avatar.h
+++ b/interface/src/avatar/Avatar.h
@@ -119,6 +119,12 @@ public:
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
CollisionList& collisions, int skeletonSkipIndex = -1);
+ /// Checks for penetration between the described plane and the avatar.
+ /// \param plane the penetration plane
+ /// \param collisions[out] a list to which collisions get appended
+ /// \return whether or not the plane penetrated
+ bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
+
/// Checks for collision between the a spherical particle and the avatar (including paddle hands)
/// \param collisionCenter the center of particle's bounding sphere
/// \param collisionRadius the radius of particle's bounding sphere
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 7a05c593db..a4f4cce79d 100644
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -647,7 +647,7 @@ void MyAvatar::renderBody(RenderMode renderMode) {
_skeletonModel.render(1.0f, modelRenderMode);
// Render head so long as the camera isn't inside it
- const float RENDER_HEAD_CUTOFF_DISTANCE = 0.40f;
+ const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f;
Camera* myCamera = Application::getInstance()->getCamera();
if (renderMode != NORMAL_RENDER_MODE || (glm::length(myCamera->getPosition() - getHead()->calculateAverageEyePosition()) >
RENDER_HEAD_CUTOFF_DISTANCE * _scale)) {
diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp
index 29a663ef25..2d47a077b7 100644
--- a/interface/src/renderer/Model.cpp
+++ b/interface/src/renderer/Model.cpp
@@ -634,6 +634,21 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi
return collided;
}
+bool Model::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) {
+ bool collided = false;
+ PlaneShape planeShape(plane);
+ for (int i = 0; i < _jointShapes.size(); i++) {
+ if (ShapeCollider::collideShapes(&planeShape, _jointShapes[i], collisions)) {
+ CollisionInfo* collision = collisions.getLastCollision();
+ collision->_type = MODEL_COLLISION;
+ collision->_data = (void*)(this);
+ collision->_flags = i;
+ collided = true;
+ }
+ }
+ return collided;
+}
+
class Blender : public QRunnable {
public:
diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h
index 30625cc16f..65b79fffdd 100644
--- a/interface/src/renderer/Model.h
+++ b/interface/src/renderer/Model.h
@@ -176,6 +176,8 @@ public:
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius,
CollisionList& collisions, int skipIndex = -1);
+
+ bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
/// \param collision details about the collisions
/// \return true if the collision is against a moveable joint
diff --git a/interface/src/ui/ChatWindow.cpp b/interface/src/ui/ChatWindow.cpp
index 0060cb839c..8a83ba680b 100644
--- a/interface/src/ui/ChatWindow.cpp
+++ b/interface/src/ui/ChatWindow.cpp
@@ -12,10 +12,12 @@
#include
#include
#include
+#include
#include
#include
#include
#include
+#include
#include "Application.h"
#include "FlowLayout.h"
@@ -35,7 +37,9 @@ ChatWindow::ChatWindow() :
{
ui->setupUi(this);
- // remove the title bar (see the Qt docs on setTitleBarWidget)
+ // remove the title bar (see the Qt docs on setTitleBarWidget), but we keep it for undocking
+ //
+ titleBar = titleBarWidget();
setTitleBarWidget(new QWidget());
FlowLayout* flowLayout = new FlowLayout(0, 4, 4);
@@ -260,3 +264,16 @@ void ChatWindow::messageReceived(const QXmppMessage& message) {
}
#endif
+
+void ChatWindow::togglePinned() {
+ QMainWindow* mainWindow = Application::getInstance()->getWindow();
+ mainWindow->removeDockWidget(this);
+ if (ui->togglePinnedButton->isChecked()) {
+ mainWindow->addDockWidget(ui->togglePinnedButton->isChecked() ? Qt::RightDockWidgetArea : Qt::NoDockWidgetArea, this);
+ }
+ if (!this->toggleViewAction()->isChecked()) {
+ this->toggleViewAction()->trigger();
+ }
+ this->setFloating(!ui->togglePinnedButton->isChecked());
+ setTitleBarWidget(ui->togglePinnedButton->isChecked()?new QWidget():titleBar);
+}
\ No newline at end of file
diff --git a/interface/src/ui/ChatWindow.h b/interface/src/ui/ChatWindow.h
index cb9619cc42..614afc1ef1 100644
--- a/interface/src/ui/ChatWindow.h
+++ b/interface/src/ui/ChatWindow.h
@@ -50,12 +50,14 @@ private:
void addTimeStamp();
Ui::ChatWindow* ui;
+ QWidget* titleBar;
int numMessagesAfterLastTimeStamp;
QDateTime lastMessageStamp;
private slots:
void connected();
void timeout();
+ void togglePinned();
#ifdef HAVE_QXMPP
void error(QXmppClient::Error error);
void participantsChanged();
diff --git a/interface/ui/chatWindow.ui b/interface/ui/chatWindow.ui
index 3c0fe4cd80..9f1f0527b8 100644
--- a/interface/ui/chatWindow.ui
+++ b/interface/ui/chatWindow.ui
@@ -20,7 +20,7 @@
font-family: Helvetica, Arial, sans-serif;
- QDockWidget::NoDockWidgetFeatures
+ QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable
Qt::NoDockWidgetArea
@@ -79,6 +79,45 @@
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+ Qt::NoFocus
+
+
+
+
+
+
+ ../resources/images/pin.svg
+ ../resources/images/pinned.svg../resources/images/pin.svg
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+
-
@@ -202,6 +241,22 @@
+
+ togglePinnedButton
+ clicked()
+ ChatWindow
+ togglePinned()
+
+
+ 390
+ 42
+
+
+ 550
+ 42
+
+
+
closeButton
clicked()
diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp
index 3e63b26b0a..b5291a6a2f 100644
--- a/libraries/script-engine/src/ScriptEngine.cpp
+++ b/libraries/script-engine/src/ScriptEngine.cpp
@@ -95,8 +95,9 @@ ScriptEngine::ScriptEngine(const QUrl& scriptURL,
QUrl url(scriptURL);
- // if the scheme is empty, maybe they typed in a file, let's try
- if (url.scheme().isEmpty()) {
+ // if the scheme length is one or lower, maybe they typed in a file, let's try
+ const int WINDOWS_DRIVE_LETTER_SIZE = 1;
+ if (url.scheme().size() <= WINDOWS_DRIVE_LETTER_SIZE) {
url = QUrl::fromLocalFile(scriptURLString);
}
diff --git a/libraries/shared/src/PlaneShape.cpp b/libraries/shared/src/PlaneShape.cpp
new file mode 100644
index 0000000000..a8b4468c93
--- /dev/null
+++ b/libraries/shared/src/PlaneShape.cpp
@@ -0,0 +1,36 @@
+//
+// PlaneShape.cpp
+// libraries/shared/src
+//
+// Created by Andrzej Kapolka on 4/10/2014.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "PlaneShape.h"
+#include "SharedUtil.h"
+
+const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f);
+
+PlaneShape::PlaneShape(const glm::vec4& coefficients) :
+ Shape(Shape::PLANE_SHAPE) {
+
+ glm::vec3 normal = glm::vec3(coefficients);
+ _position = -normal * coefficients.w;
+
+ float angle = acosf(glm::dot(normal, UNROTATED_NORMAL));
+ if (angle > EPSILON) {
+ if (angle > PI - EPSILON) {
+ _rotation = glm::angleAxis(PI, glm::vec3(1.0f, 0.0f, 0.0f));
+ } else {
+ _rotation = glm::angleAxis(angle, glm::normalize(glm::cross(UNROTATED_NORMAL, normal)));
+ }
+ }
+}
+
+glm::vec4 PlaneShape::getCoefficients() const {
+ glm::vec3 normal = _rotation * UNROTATED_NORMAL;
+ return glm::vec4(normal.x, normal.y, normal.z, -glm::dot(normal, _position));
+}
diff --git a/libraries/shared/src/PlaneShape.h b/libraries/shared/src/PlaneShape.h
new file mode 100644
index 0000000000..524d53ec73
--- /dev/null
+++ b/libraries/shared/src/PlaneShape.h
@@ -0,0 +1,24 @@
+//
+// PlaneShape.h
+// libraries/shared/src
+//
+// Created by Andrzej Kapolka on 4/9/2014.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_PlaneShape_h
+#define hifi_PlaneShape_h
+
+#include "Shape.h"
+
+class PlaneShape : public Shape {
+public:
+ PlaneShape(const glm::vec4& coefficients = glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
+
+ glm::vec4 getCoefficients() const;
+};
+
+#endif // hifi_PlaneShape_h
diff --git a/libraries/shared/src/Shape.h b/libraries/shared/src/Shape.h
index fd16eafeae..87b84ea73b 100644
--- a/libraries/shared/src/Shape.h
+++ b/libraries/shared/src/Shape.h
@@ -22,6 +22,7 @@ public:
UNKNOWN_SHAPE = 0,
SPHERE_SHAPE,
CAPSULE_SHAPE,
+ PLANE_SHAPE,
BOX_SHAPE,
LIST_SHAPE
};
diff --git a/libraries/shared/src/ShapeCollider.cpp b/libraries/shared/src/ShapeCollider.cpp
index 32789aa388..c53c7fab7d 100644
--- a/libraries/shared/src/ShapeCollider.cpp
+++ b/libraries/shared/src/ShapeCollider.cpp
@@ -34,6 +34,8 @@ bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& coll
return sphereSphere(sphereA, static_cast(shapeB), collisions);
} else if (typeB == Shape::CAPSULE_SHAPE) {
return sphereCapsule(sphereA, static_cast(shapeB), collisions);
+ } else if (typeB == Shape::PLANE_SHAPE) {
+ return spherePlane(sphereA, static_cast(shapeB), collisions);
}
} else if (typeA == Shape::CAPSULE_SHAPE) {
const CapsuleShape* capsuleA = static_cast(shapeA);
@@ -41,6 +43,17 @@ bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& coll
return capsuleSphere(capsuleA, static_cast(shapeB), collisions);
} else if (typeB == Shape::CAPSULE_SHAPE) {
return capsuleCapsule(capsuleA, static_cast(shapeB), collisions);
+ } else if (typeB == Shape::PLANE_SHAPE) {
+ return capsulePlane(capsuleA, static_cast(shapeB), collisions);
+ }
+ } else if (typeA == Shape::PLANE_SHAPE) {
+ const PlaneShape* planeA = static_cast(shapeA);
+ if (typeB == Shape::SPHERE_SHAPE) {
+ return planeSphere(planeA, static_cast(shapeB), collisions);
+ } else if (typeB == Shape::CAPSULE_SHAPE) {
+ return planeCapsule(planeA, static_cast(shapeB), collisions);
+ } else if (typeB == Shape::PLANE_SHAPE) {
+ return planePlane(planeA, static_cast(shapeB), collisions);
}
} else if (typeA == Shape::LIST_SHAPE) {
const ListShape* listA = static_cast(shapeA);
@@ -48,6 +61,8 @@ bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& coll
return listSphere(listA, static_cast(shapeB), collisions);
} else if (typeB == Shape::CAPSULE_SHAPE) {
return listCapsule(listA, static_cast(shapeB), collisions);
+ } else if (typeB == Shape::PLANE_SHAPE) {
+ return listPlane(listA, static_cast(shapeB), collisions);
}
}
return false;
@@ -168,6 +183,20 @@ bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, Col
return false;
}
+bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions) {
+ glm::vec3 penetration;
+ if (findSpherePlanePenetration(sphereA->getPosition(), sphereA->getRadius(), planeB->getCoefficients(), penetration)) {
+ CollisionInfo* collision = collisions.getNewCollision();
+ if (!collision) {
+ return false; // collision list is full
+ }
+ collision->_penetration = penetration;
+ collision->_contactPoint = sphereA->getPosition() + sphereA->getRadius() * glm::normalize(penetration);
+ return true;
+ }
+ return false;
+}
+
bool capsuleSphere(const CapsuleShape* capsuleA, const SphereShape* sphereB, CollisionList& collisions) {
// find sphereB's closest approach to axis of capsuleA
glm::vec3 AB = capsuleA->getPosition() - sphereB->getPosition();
@@ -374,6 +403,63 @@ bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB,
return false;
}
+bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions) {
+ glm::vec3 start, end, penetration;
+ capsuleA->getStartPoint(start);
+ capsuleA->getEndPoint(end);
+ glm::vec4 plane = planeB->getCoefficients();
+ if (findCapsulePlanePenetration(start, end, capsuleA->getRadius(), plane, penetration)) {
+ CollisionInfo* collision = collisions.getNewCollision();
+ if (!collision) {
+ return false; // collision list is full
+ }
+ collision->_penetration = penetration;
+ glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end;
+ collision->_contactPoint = deepestEnd + capsuleA->getRadius() * glm::normalize(penetration);
+ return true;
+ }
+ return false;
+}
+
+bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions) {
+ glm::vec3 penetration;
+ if (findSpherePlanePenetration(sphereB->getPosition(), sphereB->getRadius(), planeA->getCoefficients(), penetration)) {
+ CollisionInfo* collision = collisions.getNewCollision();
+ if (!collision) {
+ return false; // collision list is full
+ }
+ collision->_penetration = -penetration;
+ collision->_contactPoint = sphereB->getPosition() +
+ (sphereB->getRadius() / glm::length(penetration) - 1.0f) * penetration;
+ return true;
+ }
+ return false;
+}
+
+bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions) {
+ glm::vec3 start, end, penetration;
+ capsuleB->getStartPoint(start);
+ capsuleB->getEndPoint(end);
+ glm::vec4 plane = planeA->getCoefficients();
+ if (findCapsulePlanePenetration(start, end, capsuleB->getRadius(), plane, penetration)) {
+ CollisionInfo* collision = collisions.getNewCollision();
+ if (!collision) {
+ return false; // collision list is full
+ }
+ collision->_penetration = -penetration;
+ glm::vec3 deepestEnd = (glm::dot(start, glm::vec3(plane)) < glm::dot(end, glm::vec3(plane))) ? start : end;
+ collision->_contactPoint = deepestEnd + (capsuleB->getRadius() / glm::length(penetration) - 1.0f) * penetration;
+ return true;
+ }
+ return false;
+}
+
+bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions) {
+ // technically, planes always collide unless they're parallel and not coincident; however, that's
+ // not going to give us any useful information
+ return false;
+}
+
bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionList& collisions) {
bool touching = false;
for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) {
@@ -383,6 +469,8 @@ bool sphereList(const SphereShape* sphereA, const ListShape* listB, CollisionLis
touching = sphereSphere(sphereA, static_cast(subShape), collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = sphereCapsule(sphereA, static_cast(subShape), collisions) || touching;
+ } else if (subType == Shape::PLANE_SHAPE) {
+ touching = spherePlane(sphereA, static_cast(subShape), collisions) || touching;
}
}
return touching;
@@ -397,6 +485,24 @@ bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, Collision
touching = capsuleSphere(capsuleA, static_cast(subShape), collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = capsuleCapsule(capsuleA, static_cast(subShape), collisions) || touching;
+ } else if (subType == Shape::PLANE_SHAPE) {
+ touching = capsulePlane(capsuleA, static_cast(subShape), collisions) || touching;
+ }
+ }
+ return touching;
+}
+
+bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions) {
+ bool touching = false;
+ for (int i = 0; i < listB->size() && !collisions.isFull(); ++i) {
+ const Shape* subShape = listB->getSubShape(i);
+ int subType = subShape->getType();
+ if (subType == Shape::SPHERE_SHAPE) {
+ touching = planeSphere(planeA, static_cast(subShape), collisions) || touching;
+ } else if (subType == Shape::CAPSULE_SHAPE) {
+ touching = planeCapsule(planeA, static_cast(subShape), collisions) || touching;
+ } else if (subType == Shape::PLANE_SHAPE) {
+ touching = planePlane(planeA, static_cast(subShape), collisions) || touching;
}
}
return touching;
@@ -411,6 +517,8 @@ bool listSphere(const ListShape* listA, const SphereShape* sphereB, CollisionLis
touching = sphereSphere(static_cast(subShape), sphereB, collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = capsuleSphere(static_cast(subShape), sphereB, collisions) || touching;
+ } else if (subType == Shape::PLANE_SHAPE) {
+ touching = planeSphere(static_cast(subShape), sphereB, collisions) || touching;
}
}
return touching;
@@ -425,6 +533,24 @@ bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, Collision
touching = sphereCapsule(static_cast(subShape), capsuleB, collisions) || touching;
} else if (subType == Shape::CAPSULE_SHAPE) {
touching = capsuleCapsule(static_cast(subShape), capsuleB, collisions) || touching;
+ } else if (subType == Shape::PLANE_SHAPE) {
+ touching = planeCapsule(static_cast(subShape), capsuleB, collisions) || touching;
+ }
+ }
+ return touching;
+}
+
+bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions) {
+ bool touching = false;
+ for (int i = 0; i < listA->size() && !collisions.isFull(); ++i) {
+ const Shape* subShape = listA->getSubShape(i);
+ int subType = subShape->getType();
+ if (subType == Shape::SPHERE_SHAPE) {
+ touching = spherePlane(static_cast(subShape), planeB, collisions) || touching;
+ } else if (subType == Shape::CAPSULE_SHAPE) {
+ touching = capsulePlane(static_cast(subShape), planeB, collisions) || touching;
+ } else if (subType == Shape::PLANE_SHAPE) {
+ touching = planePlane(static_cast(subShape), planeB, collisions) || touching;
}
}
return touching;
diff --git a/libraries/shared/src/ShapeCollider.h b/libraries/shared/src/ShapeCollider.h
index 0b06b95029..d554775e7b 100644
--- a/libraries/shared/src/ShapeCollider.h
+++ b/libraries/shared/src/ShapeCollider.h
@@ -15,6 +15,7 @@
#include "CapsuleShape.h"
#include "CollisionInfo.h"
#include "ListShape.h"
+#include "PlaneShape.h"
#include "SharedUtil.h"
#include "SphereShape.h"
@@ -44,6 +45,12 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool sphereCapsule(const SphereShape* sphereA, const CapsuleShape* capsuleB, CollisionList& collisions);
+ /// \param sphereA pointer to first shape
+ /// \param planeB pointer to second shape
+ /// \param[out] collisions where to append collision details
+ /// \return true if shapes collide
+ bool spherePlane(const SphereShape* sphereA, const PlaneShape* planeB, CollisionList& collisions);
+
/// \param capsuleA pointer to first shape
/// \param sphereB pointer to second shape
/// \param[out] collisions where to append collision details
@@ -56,6 +63,30 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool capsuleCapsule(const CapsuleShape* capsuleA, const CapsuleShape* capsuleB, CollisionList& collisions);
+ /// \param capsuleA pointer to first shape
+ /// \param planeB pointer to second shape
+ /// \param[out] collisions where to append collision details
+ /// \return true if shapes collide
+ bool capsulePlane(const CapsuleShape* capsuleA, const PlaneShape* planeB, CollisionList& collisions);
+
+ /// \param planeA pointer to first shape
+ /// \param sphereB pointer to second shape
+ /// \param[out] collisions where to append collision details
+ /// \return true if shapes collide
+ bool planeSphere(const PlaneShape* planeA, const SphereShape* sphereB, CollisionList& collisions);
+
+ /// \param planeA pointer to first shape
+ /// \param capsuleB pointer to second shape
+ /// \param[out] collisions where to append collision details
+ /// \return true if shapes collide
+ bool planeCapsule(const PlaneShape* planeA, const CapsuleShape* capsuleB, CollisionList& collisions);
+
+ /// \param planeA pointer to first shape
+ /// \param planeB pointer to second shape
+ /// \param[out] collisions where to append collision details
+ /// \return true if shapes collide
+ bool planePlane(const PlaneShape* planeA, const PlaneShape* planeB, CollisionList& collisions);
+
/// \param sphereA pointer to first shape
/// \param listB pointer to second shape
/// \param[out] collisions where to append collision details
@@ -68,6 +99,12 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool capsuleList(const CapsuleShape* capsuleA, const ListShape* listB, CollisionList& collisions);
+ /// \param planeA pointer to first shape
+ /// \param listB pointer to second shape
+ /// \param[out] collisions where to append collision details
+ /// \return true if shapes collide
+ bool planeList(const PlaneShape* planeA, const ListShape* listB, CollisionList& collisions);
+
/// \param listA pointer to first shape
/// \param sphereB pointer to second shape
/// \param[out] collisions where to append collision details
@@ -80,6 +117,12 @@ namespace ShapeCollider {
/// \return true if shapes collide
bool listCapsule(const ListShape* listA, const CapsuleShape* capsuleB, CollisionList& collisions);
+ /// \param listA pointer to first shape
+ /// \param planeB pointer to second shape
+ /// \param[out] collisions where to append collision details
+ /// \return true if shapes collide
+ bool listPlane(const ListShape* listA, const PlaneShape* planeB, CollisionList& collisions);
+
/// \param listA pointer to first shape
/// \param capsuleB pointer to second shape
/// \param[out] collisions where to append collision details