mirror of
https://github.com/overte-org/overte.git
synced 2025-07-24 03:43:43 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into protocol
This commit is contained in:
commit
dfaa16d15c
54 changed files with 625 additions and 2892 deletions
78
examples/gridTest.js
Normal file
78
examples/gridTest.js
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// Created by Philip Rosedale on July 28, 2015
|
||||||
|
// Copyright 2015 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
|
||||||
|
//
|
||||||
|
// Creates a rectangular grid of objects, starting at the origin and proceeding along the X/Z plane.
|
||||||
|
// Useful for testing the rendering, LOD, and octree storage aspects of the system.
|
||||||
|
//
|
||||||
|
// Note that when creating things quickly, the entity server will ignore data if we send updates too quickly.
|
||||||
|
// like Internet MTU, these rates are set by th domain operator, so in this script there is a RATE_PER_SECOND
|
||||||
|
// variable letting you set this speed. If entities are missing from the grid after a relog, this number
|
||||||
|
// being too high may be the reason.
|
||||||
|
|
||||||
|
var SIZE = 10.0;
|
||||||
|
var SEPARATION = 20.0;
|
||||||
|
var ROWS_X = 30;
|
||||||
|
var ROWS_Z = 30;
|
||||||
|
var TYPE = "Sphere"; // Right now this can be "Box" or "Model" or "Sphere"
|
||||||
|
var MODEL_URL = "https://hifi-public.s3.amazonaws.com/models/props/LowPolyIsland/CypressTreeGroup.fbx";
|
||||||
|
var MODEL_DIMENSION = { x: 33, y: 16, z: 49 };
|
||||||
|
var RATE_PER_SECOND = 1000; // The entity server will drop data if we create things too fast.
|
||||||
|
var SCRIPT_INTERVAL = 100;
|
||||||
|
var LIFETIME = 600; // By default, these entities will live in the server for 10 minutes
|
||||||
|
|
||||||
|
var addRandom = false;
|
||||||
|
|
||||||
|
var x = 0;
|
||||||
|
var z = 0;
|
||||||
|
var totalCreated = 0;
|
||||||
|
|
||||||
|
Script.setInterval(function () {
|
||||||
|
if (!Entities.serversExist() || !Entities.canRez()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var numToCreate = RATE_PER_SECOND * (SCRIPT_INTERVAL / 1000.0);
|
||||||
|
for (var i = 0; i < numToCreate; i++) {
|
||||||
|
var position = { x: SIZE + (x * SEPARATION), y: SIZE, z: SIZE + (z * SEPARATION) };
|
||||||
|
if (TYPE == "Model") {
|
||||||
|
Entities.addEntity({
|
||||||
|
type: TYPE,
|
||||||
|
name: "gridTest",
|
||||||
|
modelURL: MODEL_URL,
|
||||||
|
position: position,
|
||||||
|
dimensions: MODEL_DIMENSION,
|
||||||
|
ignoreCollisions: true,
|
||||||
|
collisionsWillMove: false,
|
||||||
|
lifetime: LIFETIME
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Entities.addEntity({
|
||||||
|
type: TYPE,
|
||||||
|
name: "gridTest",
|
||||||
|
position: position,
|
||||||
|
dimensions: { x: SIZE, y: SIZE, z: SIZE },
|
||||||
|
color: { red: x / ROWS_X * 255, green: 50, blue: z / ROWS_Z * 255 },
|
||||||
|
ignoreCollisions: true,
|
||||||
|
collisionsWillMove: false,
|
||||||
|
lifetime: LIFETIME
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCreated++;
|
||||||
|
|
||||||
|
x++;
|
||||||
|
if (x == ROWS_X) {
|
||||||
|
x = 0;
|
||||||
|
z++;
|
||||||
|
print("Created: " + totalCreated);
|
||||||
|
}
|
||||||
|
if (z == ROWS_Z) {
|
||||||
|
Script.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, SCRIPT_INTERVAL);
|
||||||
|
|
64
interface/resources/meshes/defaultAvatar_full.fst
Normal file
64
interface/resources/meshes/defaultAvatar_full.fst
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
name = defaultAvatar_full
|
||||||
|
type = body+head
|
||||||
|
scale = 1
|
||||||
|
filename = defaultAvatar_full/defaultAvatar_full.fbx
|
||||||
|
texdir = defaultAvatar_full/textures
|
||||||
|
joint = jointNeck = Head
|
||||||
|
joint = jointLeftHand = LeftHand
|
||||||
|
joint = jointRoot = Hips
|
||||||
|
joint = jointHead = HeadTop_End
|
||||||
|
joint = jointRightHand = RightHand
|
||||||
|
joint = jointLean = Spine
|
||||||
|
freeJoint = LeftArm
|
||||||
|
freeJoint = LeftForeArm
|
||||||
|
freeJoint = RightArm
|
||||||
|
freeJoint = RightForeArm
|
||||||
|
jointIndex = LeftHand = 35
|
||||||
|
jointIndex = Reye = 3
|
||||||
|
jointIndex = Hips = 10
|
||||||
|
jointIndex = LeftHandIndex1 = 36
|
||||||
|
jointIndex = LeftHandIndex2 = 37
|
||||||
|
jointIndex = LeftHandIndex3 = 38
|
||||||
|
jointIndex = LeftHandIndex4 = 39
|
||||||
|
jointIndex = LeftShoulder = 32
|
||||||
|
jointIndex = RightLeg = 12
|
||||||
|
jointIndex = Grp_blendshapes = 0
|
||||||
|
jointIndex = Leye = 4
|
||||||
|
jointIndex = headphone = 8
|
||||||
|
jointIndex = RightForeArm = 26
|
||||||
|
jointIndex = Spine = 21
|
||||||
|
jointIndex = LeftFoot = 18
|
||||||
|
jointIndex = RightToeBase = 14
|
||||||
|
jointIndex = face = 1
|
||||||
|
jointIndex = LeftToe_End = 20
|
||||||
|
jointIndex = Spine1 = 22
|
||||||
|
jointIndex = body = 9
|
||||||
|
jointIndex = Spine2 = 23
|
||||||
|
jointIndex = RightUpLeg = 11
|
||||||
|
jointIndex = top1 = 7
|
||||||
|
jointIndex = Neck = 40
|
||||||
|
jointIndex = HeadTop_End = 42
|
||||||
|
jointIndex = RightShoulder = 24
|
||||||
|
jointIndex = RightArm = 25
|
||||||
|
jointIndex = Head = 41
|
||||||
|
jointIndex = LeftLeg = 17
|
||||||
|
jointIndex = LeftForeArm = 34
|
||||||
|
jointIndex = hair = 6
|
||||||
|
jointIndex = RightHand = 27
|
||||||
|
jointIndex = LeftToeBase = 19
|
||||||
|
jointIndex = LeftUpLeg = 16
|
||||||
|
jointIndex = mouth = 2
|
||||||
|
jointIndex = RightFoot = 13
|
||||||
|
jointIndex = LeftArm = 33
|
||||||
|
jointIndex = shield = 5
|
||||||
|
jointIndex = RightHandIndex1 = 28
|
||||||
|
jointIndex = RightHandIndex2 = 29
|
||||||
|
jointIndex = RightToe_End = 15
|
||||||
|
jointIndex = RightHandIndex3 = 30
|
||||||
|
jointIndex = RightHandIndex4 = 31
|
||||||
|
ry = 0
|
||||||
|
rz = 0
|
||||||
|
tx = 0
|
||||||
|
ty = 0
|
||||||
|
tz = 0
|
||||||
|
rx = 0
|
Binary file not shown.
BIN
interface/resources/meshes/defaultAvatar_full/textures/visor.png
Normal file
BIN
interface/resources/meshes/defaultAvatar_full/textures/visor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
|
@ -2012,6 +2012,7 @@ void Application::setActiveFaceTracker() {
|
||||||
#ifdef HAVE_DDE
|
#ifdef HAVE_DDE
|
||||||
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
|
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
|
||||||
Menu::getInstance()->getActionForOption(MenuOption::BinaryEyelidControl)->setVisible(isUsingDDE);
|
Menu::getInstance()->getActionForOption(MenuOption::BinaryEyelidControl)->setVisible(isUsingDDE);
|
||||||
|
Menu::getInstance()->getActionForOption(MenuOption::CoupleEyelids)->setVisible(isUsingDDE);
|
||||||
Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE);
|
Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE);
|
||||||
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
|
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
|
||||||
Menu::getInstance()->getActionForOption(MenuOption::CalibrateCamera)->setVisible(isUsingDDE);
|
Menu::getInstance()->getActionForOption(MenuOption::CalibrateCamera)->setVisible(isUsingDDE);
|
||||||
|
|
|
@ -421,6 +421,8 @@ Menu::Menu() {
|
||||||
faceTrackingMenu->addSeparator();
|
faceTrackingMenu->addSeparator();
|
||||||
QAction* binaryEyelidControl = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::BinaryEyelidControl, 0, true);
|
QAction* binaryEyelidControl = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::BinaryEyelidControl, 0, true);
|
||||||
binaryEyelidControl->setVisible(true); // DDE face tracking is on by default
|
binaryEyelidControl->setVisible(true); // DDE face tracking is on by default
|
||||||
|
QAction* coupleEyelids = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::CoupleEyelids, 0, true);
|
||||||
|
coupleEyelids->setVisible(true); // DDE face tracking is on by default
|
||||||
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
|
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
|
||||||
useAudioForMouth->setVisible(true); // DDE face tracking is on by default
|
useAudioForMouth->setVisible(true); // DDE face tracking is on by default
|
||||||
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
|
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
|
||||||
|
|
|
@ -165,6 +165,7 @@ namespace MenuOption {
|
||||||
const QString ControlWithSpeech = "Control With Speech";
|
const QString ControlWithSpeech = "Control With Speech";
|
||||||
const QString CopyAddress = "Copy Address to Clipboard";
|
const QString CopyAddress = "Copy Address to Clipboard";
|
||||||
const QString CopyPath = "Copy Path to Clipboard";
|
const QString CopyPath = "Copy Path to Clipboard";
|
||||||
|
const QString CoupleEyelids = "Couple Eyelids";
|
||||||
const QString DebugAmbientOcclusion = "Debug Ambient Occlusion";
|
const QString DebugAmbientOcclusion = "Debug Ambient Occlusion";
|
||||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||||
const QString DeleteBookmark = "Delete Bookmark...";
|
const QString DeleteBookmark = "Delete Bookmark...";
|
||||||
|
|
|
@ -936,8 +936,14 @@ void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
|
||||||
|
|
||||||
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
||||||
AvatarData::setSkeletonModelURL(skeletonModelURL);
|
AvatarData::setSkeletonModelURL(skeletonModelURL);
|
||||||
|
const QUrl DEFAULT_FULL_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_full.fst");
|
||||||
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst");
|
const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile(PathUtils::resourcesPath() + "meshes/defaultAvatar_body.fst");
|
||||||
|
if (isMyAvatar()) {
|
||||||
|
_skeletonModel.setURL(_skeletonModelURL,
|
||||||
|
getUseFullAvatar() ? DEFAULT_FULL_MODEL_URL : DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
|
||||||
|
} else {
|
||||||
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
|
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||||
|
|
|
@ -150,6 +150,7 @@ public:
|
||||||
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
||||||
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
Q_INVOKABLE glm::vec3 getAngularAcceleration() const { return _angularAcceleration; }
|
||||||
|
|
||||||
|
virtual bool getUseFullAvatar() const { return false; }
|
||||||
|
|
||||||
/// Scales a world space position vector relative to the avatar position and scale
|
/// Scales a world space position vector relative to the avatar position and scale
|
||||||
/// \param vector position to be scaled. Will store the result
|
/// \param vector position to be scaled. Will store the result
|
||||||
|
@ -169,9 +170,6 @@ public:
|
||||||
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
|
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
|
||||||
AvatarMotionState* getMotionState() { return _motionState; }
|
AvatarMotionState* getMotionState() { return _motionState; }
|
||||||
|
|
||||||
signals:
|
|
||||||
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SkeletonModel _skeletonModel;
|
SkeletonModel _skeletonModel;
|
||||||
glm::vec3 _skeletonOffset;
|
glm::vec3 _skeletonOffset;
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <ShapeCollider.h>
|
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <TextRenderer3D.h>
|
#include <TextRenderer3D.h>
|
||||||
#include <UserActivityLogger.h>
|
#include <UserActivityLogger.h>
|
||||||
|
@ -104,7 +103,6 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
||||||
_rig(rig),
|
_rig(rig),
|
||||||
_prevShouldDrawHead(true)
|
_prevShouldDrawHead(true)
|
||||||
{
|
{
|
||||||
ShapeCollider::initDispatchTable();
|
|
||||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||||
_driveKeys[i] = 0.0f;
|
_driveKeys[i] = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,7 @@ public:
|
||||||
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL,
|
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL,
|
||||||
const QString& headName = QString(), const QString& bodyName = QString());
|
const QString& headName = QString(), const QString& bodyName = QString());
|
||||||
|
|
||||||
Q_INVOKABLE bool getUseFullAvatar() const { return _useFullAvatar; }
|
Q_INVOKABLE virtual bool getUseFullAvatar() const { return _useFullAvatar; }
|
||||||
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
|
Q_INVOKABLE const QUrl& getFullAvatarURLFromPreferences() const { return _fullAvatarURLFromPreferences; }
|
||||||
Q_INVOKABLE const QUrl& getHeadURLFromPreferences() const { return _headURLFromPreferences; }
|
Q_INVOKABLE const QUrl& getHeadURLFromPreferences() const { return _headURLFromPreferences; }
|
||||||
Q_INVOKABLE const QUrl& getBodyURLFromPreferences() const { return _skeletonURLFromPreferences; }
|
Q_INVOKABLE const QUrl& getBodyURLFromPreferences() const { return _skeletonURLFromPreferences; }
|
||||||
|
|
|
@ -42,7 +42,23 @@ SkeletonModel::~SkeletonModel() {
|
||||||
void SkeletonModel::initJointStates(QVector<JointState> states) {
|
void SkeletonModel::initJointStates(QVector<JointState> states) {
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
_boundingRadius = _rig->initJointStates(states, parentTransform);
|
|
||||||
|
int rootJointIndex = geometry.rootJointIndex;
|
||||||
|
int leftHandJointIndex = geometry.leftHandJointIndex;
|
||||||
|
int leftElbowJointIndex = leftHandJointIndex >= 0 ? geometry.joints.at(leftHandJointIndex).parentIndex : -1;
|
||||||
|
int leftShoulderJointIndex = leftElbowJointIndex >= 0 ? geometry.joints.at(leftElbowJointIndex).parentIndex : -1;
|
||||||
|
int rightHandJointIndex = geometry.rightHandJointIndex;
|
||||||
|
int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1;
|
||||||
|
int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1;
|
||||||
|
|
||||||
|
_boundingRadius = _rig->initJointStates(states, parentTransform,
|
||||||
|
rootJointIndex,
|
||||||
|
leftHandJointIndex,
|
||||||
|
leftElbowJointIndex,
|
||||||
|
leftShoulderJointIndex,
|
||||||
|
rightHandJointIndex,
|
||||||
|
rightElbowJointIndex,
|
||||||
|
rightShoulderJointIndex);
|
||||||
|
|
||||||
// Determine the default eye position for avatar scale = 1.0
|
// Determine the default eye position for avatar scale = 1.0
|
||||||
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
|
int headJointIndex = _geometry->getFBXGeometry().headJointIndex;
|
||||||
|
@ -227,7 +243,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
|
||||||
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation;
|
palmRotation = rotationBetween(palmRotation * glm::vec3(-sign, 0.0f, 0.0f), fingerDirection) * palmRotation;
|
||||||
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) {
|
if (Menu::getInstance()->isOptionChecked(MenuOption::AlternateIK)) {
|
||||||
setHandPosition(jointIndex, palmPosition, palmRotation);
|
_rig->setHandPosition(jointIndex, palmPosition, palmRotation, extractUniformScale(_scale), PALM_PRIORITY);
|
||||||
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
|
||||||
float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale);
|
float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale);
|
||||||
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
|
glm::vec3 forearm = palmRotation * glm::vec3(sign * forearmLength, 0.0f, 0.0f);
|
||||||
|
@ -332,69 +348,6 @@ void SkeletonModel::renderOrientationDirections(gpu::Batch& batch, int jointInde
|
||||||
geometryCache->renderLine(batch, position, pFront, blue, jointLineIDs._front);
|
geometryCache->renderLine(batch, position, pFront, blue, jointLineIDs._front);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation) {
|
|
||||||
// this algorithm is from sample code from sixense
|
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
|
||||||
int elbowJointIndex = geometry.joints.at(jointIndex).parentIndex;
|
|
||||||
if (elbowJointIndex == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int shoulderJointIndex = geometry.joints.at(elbowJointIndex).parentIndex;
|
|
||||||
glm::vec3 shoulderPosition;
|
|
||||||
if (!getJointPosition(shoulderJointIndex, shoulderPosition)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// precomputed lengths
|
|
||||||
float scale = extractUniformScale(_scale);
|
|
||||||
float upperArmLength = geometry.joints.at(elbowJointIndex).distanceToParent * scale;
|
|
||||||
float lowerArmLength = geometry.joints.at(jointIndex).distanceToParent * scale;
|
|
||||||
|
|
||||||
// first set wrist position
|
|
||||||
glm::vec3 wristPosition = position;
|
|
||||||
|
|
||||||
glm::vec3 shoulderToWrist = wristPosition - shoulderPosition;
|
|
||||||
float distanceToWrist = glm::length(shoulderToWrist);
|
|
||||||
|
|
||||||
// prevent gimbal lock
|
|
||||||
if (distanceToWrist > upperArmLength + lowerArmLength - EPSILON) {
|
|
||||||
distanceToWrist = upperArmLength + lowerArmLength - EPSILON;
|
|
||||||
shoulderToWrist = glm::normalize(shoulderToWrist) * distanceToWrist;
|
|
||||||
wristPosition = shoulderPosition + shoulderToWrist;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cosine of angle from upper arm to hand vector
|
|
||||||
float cosA = (upperArmLength * upperArmLength + distanceToWrist * distanceToWrist - lowerArmLength * lowerArmLength) /
|
|
||||||
(2 * upperArmLength * distanceToWrist);
|
|
||||||
float mid = upperArmLength * cosA;
|
|
||||||
float height = sqrt(upperArmLength * upperArmLength + mid * mid - 2 * upperArmLength * mid * cosA);
|
|
||||||
|
|
||||||
// direction of the elbow
|
|
||||||
glm::vec3 handNormal = glm::cross(rotation * glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow rotating with wrist
|
|
||||||
glm::vec3 relaxedNormal = glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow pointing straight down
|
|
||||||
const float NORMAL_WEIGHT = 0.5f;
|
|
||||||
glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT);
|
|
||||||
|
|
||||||
bool rightHand = (jointIndex == geometry.rightHandJointIndex);
|
|
||||||
if (rightHand ? (finalNormal.y > 0.0f) : (finalNormal.y < 0.0f)) {
|
|
||||||
finalNormal.y = 0.0f; // dont allow elbows to point inward (y is vertical axis)
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 tangent = glm::normalize(glm::cross(shoulderToWrist, finalNormal));
|
|
||||||
|
|
||||||
// ik solution
|
|
||||||
glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height;
|
|
||||||
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
|
|
||||||
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
|
|
||||||
|
|
||||||
_rig->setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, PALM_PRIORITY);
|
|
||||||
_rig->setJointRotationInBindFrame(elbowJointIndex,
|
|
||||||
rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) *
|
|
||||||
shoulderRotation, PALM_PRIORITY);
|
|
||||||
_rig->setJointRotationInBindFrame(jointIndex, rotation, PALM_PRIORITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
|
bool SkeletonModel::getLeftHandPosition(glm::vec3& position) const {
|
||||||
return getJointPositionInWorldFrame(getLeftHandJointIndex(), position);
|
return getJointPositionInWorldFrame(getLeftHandJointIndex(), position);
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,11 +131,6 @@ private:
|
||||||
QHash<int, OrientationLineIDs> _jointOrientationLines;
|
QHash<int, OrientationLineIDs> _jointOrientationLines;
|
||||||
int _triangleFanID;
|
int _triangleFanID;
|
||||||
|
|
||||||
/// \param jointIndex index of joint in model
|
|
||||||
/// \param position position of joint in model-frame
|
|
||||||
/// \param rotation rotation of joint in model-frame
|
|
||||||
void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation);
|
|
||||||
|
|
||||||
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
bool getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const;
|
||||||
|
|
||||||
Avatar* _owningAvatar;
|
Avatar* _owningAvatar;
|
||||||
|
|
|
@ -564,6 +564,13 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
||||||
eyeCoefficients[1] = _filteredEyeBlinks[1];
|
eyeCoefficients[1] = _filteredEyeBlinks[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Couple eyelid values if configured - use the most "open" value for both
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::CoupleEyelids)) {
|
||||||
|
float eyeCoefficient = std::min(eyeCoefficients[0], eyeCoefficients[1]);
|
||||||
|
eyeCoefficients[0] = eyeCoefficient;
|
||||||
|
eyeCoefficients[1] = eyeCoefficient;
|
||||||
|
}
|
||||||
|
|
||||||
// Use EyeBlink values to control both EyeBlink and EyeOpen
|
// Use EyeBlink values to control both EyeBlink and EyeOpen
|
||||||
if (eyeCoefficients[0] > 0) {
|
if (eyeCoefficients[0] > 0) {
|
||||||
_coefficients[_leftBlinkIndex] = eyeCoefficients[0];
|
_coefficients[_leftBlinkIndex] = eyeCoefficients[0];
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
ControllerScriptingInterface::ControllerScriptingInterface() :
|
ControllerScriptingInterface::ControllerScriptingInterface() :
|
||||||
_mouseCaptured(false),
|
_mouseCaptured(false),
|
||||||
_touchCaptured(false),
|
_touchCaptured(false),
|
||||||
_wheelCaptured(false)
|
_wheelCaptured(false),
|
||||||
|
_actionsCaptured(false)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,81 @@ void AvatarRig::updateJointState(int index, glm::mat4 parentTransform) {
|
||||||
const FBXJoint& joint = state.getFBXJoint();
|
const FBXJoint& joint = state.getFBXJoint();
|
||||||
|
|
||||||
// compute model transforms
|
// compute model transforms
|
||||||
int parentIndex = joint.parentIndex;
|
if (index == _rootJointIndex) {
|
||||||
if (parentIndex == -1) {
|
// we always zero-out the translation part of an avatar's root join-transform.
|
||||||
state.computeTransform(parentTransform);
|
state.computeTransform(parentTransform);
|
||||||
clearJointTransformTranslation(index);
|
clearJointTransformTranslation(index);
|
||||||
} else {
|
} else {
|
||||||
// guard against out-of-bounds access to _jointStates
|
// guard against out-of-bounds access to _jointStates
|
||||||
if (joint.parentIndex >= 0 && joint.parentIndex < _jointStates.size()) {
|
int parentIndex = joint.parentIndex;
|
||||||
|
if (parentIndex >= 0 && parentIndex < _jointStates.size()) {
|
||||||
const JointState& parentState = _jointStates.at(parentIndex);
|
const JointState& parentState = _jointStates.at(parentIndex);
|
||||||
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
|
state.computeTransform(parentState.getTransform(), parentState.getTransformChanged());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvatarRig::setHandPosition(int jointIndex,
|
||||||
|
const glm::vec3& position, const glm::quat& rotation,
|
||||||
|
float scale, float priority) {
|
||||||
|
bool rightHand = (jointIndex == _rightHandJointIndex);
|
||||||
|
|
||||||
|
int elbowJointIndex = rightHand ? _rightElbowJointIndex : _leftElbowJointIndex;
|
||||||
|
int shoulderJointIndex = rightHand ? _rightShoulderJointIndex : _leftShoulderJointIndex;
|
||||||
|
|
||||||
|
// this algorithm is from sample code from sixense
|
||||||
|
if (elbowJointIndex == -1 || shoulderJointIndex == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 shoulderPosition;
|
||||||
|
if (!getJointPosition(shoulderJointIndex, shoulderPosition)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// precomputed lengths
|
||||||
|
float upperArmLength = _jointStates[elbowJointIndex].getFBXJoint().distanceToParent * scale;
|
||||||
|
float lowerArmLength = _jointStates[jointIndex].getFBXJoint().distanceToParent * scale;
|
||||||
|
|
||||||
|
// first set wrist position
|
||||||
|
glm::vec3 wristPosition = position;
|
||||||
|
|
||||||
|
glm::vec3 shoulderToWrist = wristPosition - shoulderPosition;
|
||||||
|
float distanceToWrist = glm::length(shoulderToWrist);
|
||||||
|
|
||||||
|
// prevent gimbal lock
|
||||||
|
if (distanceToWrist > upperArmLength + lowerArmLength - EPSILON) {
|
||||||
|
distanceToWrist = upperArmLength + lowerArmLength - EPSILON;
|
||||||
|
shoulderToWrist = glm::normalize(shoulderToWrist) * distanceToWrist;
|
||||||
|
wristPosition = shoulderPosition + shoulderToWrist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cosine of angle from upper arm to hand vector
|
||||||
|
float cosA = (upperArmLength * upperArmLength + distanceToWrist * distanceToWrist - lowerArmLength * lowerArmLength) /
|
||||||
|
(2 * upperArmLength * distanceToWrist);
|
||||||
|
float mid = upperArmLength * cosA;
|
||||||
|
float height = sqrt(upperArmLength * upperArmLength + mid * mid - 2 * upperArmLength * mid * cosA);
|
||||||
|
|
||||||
|
// direction of the elbow
|
||||||
|
glm::vec3 handNormal = glm::cross(rotation * glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow rotating with wrist
|
||||||
|
glm::vec3 relaxedNormal = glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), shoulderToWrist); // elbow pointing straight down
|
||||||
|
const float NORMAL_WEIGHT = 0.5f;
|
||||||
|
glm::vec3 finalNormal = glm::mix(relaxedNormal, handNormal, NORMAL_WEIGHT);
|
||||||
|
|
||||||
|
if (rightHand ? (finalNormal.y > 0.0f) : (finalNormal.y < 0.0f)) {
|
||||||
|
finalNormal.y = 0.0f; // dont allow elbows to point inward (y is vertical axis)
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 tangent = glm::normalize(glm::cross(shoulderToWrist, finalNormal));
|
||||||
|
|
||||||
|
// ik solution
|
||||||
|
glm::vec3 elbowPosition = shoulderPosition + glm::normalize(shoulderToWrist) * mid - tangent * height;
|
||||||
|
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
|
||||||
|
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
|
||||||
|
|
||||||
|
setJointRotationInBindFrame(shoulderJointIndex, shoulderRotation, priority);
|
||||||
|
setJointRotationInBindFrame(elbowJointIndex,
|
||||||
|
rotationBetween(shoulderRotation * forwardVector, wristPosition - elbowPosition) *
|
||||||
|
shoulderRotation, priority);
|
||||||
|
setJointRotationInBindFrame(jointIndex, rotation, priority);
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ class AvatarRig : public Rig {
|
||||||
public:
|
public:
|
||||||
~AvatarRig() {}
|
~AvatarRig() {}
|
||||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||||
|
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||||
|
float scale, float priority);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AvatarRig_h
|
#endif // hifi_AvatarRig_h
|
||||||
|
|
|
@ -22,6 +22,8 @@ class EntityRig : public Rig {
|
||||||
public:
|
public:
|
||||||
~EntityRig() {}
|
~EntityRig() {}
|
||||||
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
virtual void updateJointState(int index, glm::mat4 parentTransform);
|
||||||
|
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||||
|
float scale, float priority) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityRig_h
|
#endif // hifi_EntityRig_h
|
||||||
|
|
|
@ -58,15 +58,18 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) {
|
||||||
|
|
||||||
void Rig::startAnimation(const QString& url, float fps, float priority,
|
void Rig::startAnimation(const QString& url, float fps, float priority,
|
||||||
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
|
bool loop, bool hold, float firstFrame, float lastFrame, const QStringList& maskedJoints) {
|
||||||
//qCDebug(animation) << "startAnimation" << url << fps << priority << loop << hold << firstFrame << lastFrame << maskedJoints;
|
// This is different than startAnimationByRole, in which we use the existing values if the animation already exists.
|
||||||
|
// Here we reuse the animation handle if possible, but in any case, we set the values to those given (or defaulted).
|
||||||
|
AnimationHandlePointer handle = nullptr;
|
||||||
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
|
foreach (const AnimationHandlePointer& candidate, _animationHandles) {
|
||||||
if (candidate->getURL() == url) {
|
if (candidate->getURL() == url) {
|
||||||
candidate->start();
|
handle = candidate;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnimationHandlePointer handle = createAnimationHandle();
|
if (!handle) {
|
||||||
|
handle = createAnimationHandle();
|
||||||
handle->setURL(url);
|
handle->setURL(url);
|
||||||
|
}
|
||||||
handle->setFPS(fps);
|
handle->setFPS(fps);
|
||||||
handle->setPriority(priority);
|
handle->setPriority(priority);
|
||||||
handle->setLoop(loop);
|
handle->setLoop(loop);
|
||||||
|
@ -90,8 +93,34 @@ void Rig::addAnimationByRole(const QString& role, const QString& url, float fps,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnimationHandlePointer handle = createAnimationHandle();
|
AnimationHandlePointer handle = createAnimationHandle();
|
||||||
|
QString standard = "";
|
||||||
|
if (url.isEmpty()) { // Default animations for fight club
|
||||||
|
const QString& base = "https://hifi-public.s3.amazonaws.com/ozan/";
|
||||||
|
if (role == "walk") {
|
||||||
|
standard = base + "support/FightClubBotTest1/Animations/standard_walk.fbx";
|
||||||
|
lastFrame = 60;
|
||||||
|
} else if (role == "leftTurn") {
|
||||||
|
standard = base + "support/FightClubBotTest1/Animations/left_turn_noHipRotation.fbx";
|
||||||
|
lastFrame = 29;
|
||||||
|
} else if (role == "rightTurn") {
|
||||||
|
standard = base + "support/FightClubBotTest1/Animations/right_turn_noHipRotation.fbx";
|
||||||
|
lastFrame = 31;
|
||||||
|
} else if (role == "leftStrafe") {
|
||||||
|
standard = base + "animations/fightclub_bot_anims/side_step_left_inPlace.fbx";
|
||||||
|
lastFrame = 31;
|
||||||
|
} else if (role == "rightStrafe") {
|
||||||
|
standard = base + "animations/fightclub_bot_anims/side_step_right_inPlace.fbx";
|
||||||
|
lastFrame = 31;
|
||||||
|
} else if (role == "idle") {
|
||||||
|
standard = base + "support/FightClubBotTest1/Animations/standard_idle.fbx";
|
||||||
|
fps = 25.0f;
|
||||||
|
}
|
||||||
|
if (!standard.isEmpty()) {
|
||||||
|
loop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
handle->setRole(role);
|
handle->setRole(role);
|
||||||
handle->setURL(url);
|
handle->setURL(url.isEmpty() ? standard : url);
|
||||||
handle->setFPS(fps);
|
handle->setFPS(fps);
|
||||||
handle->setPriority(priority);
|
handle->setPriority(priority);
|
||||||
handle->setLoop(loop);
|
handle->setLoop(loop);
|
||||||
|
@ -135,6 +164,14 @@ void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) {
|
||||||
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
|
bool Rig::isRunningAnimation(AnimationHandlePointer animationHandle) {
|
||||||
return _runningAnimations.contains(animationHandle);
|
return _runningAnimations.contains(animationHandle);
|
||||||
}
|
}
|
||||||
|
bool Rig::isRunningRole(const QString& role) { //obviously, there are more efficient ways to do this
|
||||||
|
for (auto animation : _runningAnimations) {
|
||||||
|
if (animation->getRole() == role) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Rig::deleteAnimations() {
|
void Rig::deleteAnimations() {
|
||||||
for (auto animation : _animationHandles) {
|
for (auto animation : _animationHandles) {
|
||||||
|
@ -143,8 +180,24 @@ void Rig::deleteAnimations() {
|
||||||
_animationHandles.clear();
|
_animationHandles.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform) {
|
float Rig::initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
|
||||||
|
int rootJointIndex,
|
||||||
|
int leftHandJointIndex,
|
||||||
|
int leftElbowJointIndex,
|
||||||
|
int leftShoulderJointIndex,
|
||||||
|
int rightHandJointIndex,
|
||||||
|
int rightElbowJointIndex,
|
||||||
|
int rightShoulderJointIndex) {
|
||||||
_jointStates = states;
|
_jointStates = states;
|
||||||
|
|
||||||
|
_rootJointIndex = rootJointIndex;
|
||||||
|
_leftHandJointIndex = leftHandJointIndex;
|
||||||
|
_leftElbowJointIndex = leftElbowJointIndex;
|
||||||
|
_leftShoulderJointIndex = leftShoulderJointIndex;
|
||||||
|
_rightHandJointIndex = rightHandJointIndex;
|
||||||
|
_rightElbowJointIndex = rightElbowJointIndex;
|
||||||
|
_rightShoulderJointIndex = rightShoulderJointIndex;
|
||||||
|
|
||||||
initJointTransforms(parentTransform);
|
initJointTransforms(parentTransform);
|
||||||
|
|
||||||
int numStates = _jointStates.size();
|
int numStates = _jointStates.size();
|
||||||
|
@ -356,37 +409,38 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
|
void Rig::computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
|
||||||
if (_enableRig) {
|
if (!_enableRig) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool isMoving = false;
|
||||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||||
float forwardSpeed = glm::dot(worldVelocity, front);
|
float forwardSpeed = glm::dot(worldVelocity, front);
|
||||||
float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime;
|
float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT);
|
||||||
bool isWalking = std::abs(forwardSpeed) > 0.01f;
|
float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime;
|
||||||
bool isTurning = std::abs(rotationalSpeed) > 0.5f;
|
auto updateRole = [&](const QString& role, bool isOn) {
|
||||||
|
isMoving = isMoving || isOn;
|
||||||
// Crude, until we have blending:
|
if (isOn) {
|
||||||
isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins.
|
if (!isRunningRole(role)) {
|
||||||
isTurning = false; // FIXME
|
qCDebug(animation) << "Rig STARTING" << role;
|
||||||
bool isIdle = !isWalking && !isTurning;
|
startAnimationByRole(role);
|
||||||
auto singleRole = [](bool walking, bool turning, bool idling) {
|
}
|
||||||
return walking ? "walk" : (turning ? "turn" : (idling ? "idle" : ""));
|
} else {
|
||||||
|
if (isRunningRole(role)) {
|
||||||
|
qCDebug(animation) << "Rig stopping" << role;
|
||||||
|
stopAnimationByRole(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle);
|
updateRole("walk", std::abs(forwardSpeed) > 0.01f);
|
||||||
if (!toStop.isEmpty()) {
|
bool isTurning = std::abs(rightTurningSpeed) > 0.5f;
|
||||||
//qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed;
|
updateRole("rightTurn", isTurning && (rightTurningSpeed > 0));
|
||||||
stopAnimationByRole(toStop);
|
updateRole("leftTurn", isTurning && (rightTurningSpeed < 0));
|
||||||
}
|
bool isStrafing = std::abs(rightLateralSpeed) > 0.01f;
|
||||||
QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle);
|
updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f));
|
||||||
if (!newRole.isEmpty()) {
|
updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f));
|
||||||
startAnimationByRole(newRole);
|
updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus.
|
||||||
qCDebug(animation) << deltaTime << ":" << worldVelocity << "." << front << "=> " << forwardSpeed << newRole;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastPosition = worldPosition;
|
|
||||||
_lastFront = front;
|
_lastFront = front;
|
||||||
_isWalking = isWalking;
|
_lastPosition = worldPosition;
|
||||||
_isTurning = isTurning;
|
|
||||||
_isIdle = isIdle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
|
|
||||||
class AnimationHandle;
|
class AnimationHandle;
|
||||||
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
|
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
|
||||||
// typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
|
|
||||||
|
|
||||||
class Rig;
|
class Rig;
|
||||||
typedef std::shared_ptr<Rig> RigPointer;
|
typedef std::shared_ptr<Rig> RigPointer;
|
||||||
|
@ -76,6 +75,7 @@ public:
|
||||||
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
|
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
|
||||||
void addRunningAnimation(AnimationHandlePointer animationHandle);
|
void addRunningAnimation(AnimationHandlePointer animationHandle);
|
||||||
bool isRunningAnimation(AnimationHandlePointer animationHandle);
|
bool isRunningAnimation(AnimationHandlePointer animationHandle);
|
||||||
|
bool isRunningRole(const QString& role); // There can be multiple animations per role, so this is more general than isRunningAnimation.
|
||||||
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
|
const QList<AnimationHandlePointer>& getRunningAnimations() const { return _runningAnimations; }
|
||||||
void deleteAnimations();
|
void deleteAnimations();
|
||||||
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
|
const QList<AnimationHandlePointer>& getAnimationHandles() const { return _animationHandles; }
|
||||||
|
@ -90,7 +90,14 @@ public:
|
||||||
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
float priority = 1.0f, bool loop = false, bool hold = false, float firstFrame = 0.0f,
|
||||||
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false);
|
float lastFrame = FLT_MAX, const QStringList& maskedJoints = QStringList(), bool startAutomatically = false);
|
||||||
|
|
||||||
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform);
|
float initJointStates(QVector<JointState> states, glm::mat4 parentTransform,
|
||||||
|
int rootJointIndex,
|
||||||
|
int leftHandJointIndex,
|
||||||
|
int leftElbowJointIndex,
|
||||||
|
int leftShoulderJointIndex,
|
||||||
|
int rightHandJointIndex,
|
||||||
|
int rightElbowJointIndex,
|
||||||
|
int rightShoulderJointIndex);
|
||||||
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
|
bool jointStatesEmpty() { return _jointStates.isEmpty(); };
|
||||||
int getJointStateCount() const { return _jointStates.size(); }
|
int getJointStateCount() const { return _jointStates.size(); }
|
||||||
int indexOfJoint(const QString& jointName) ;
|
int indexOfJoint(const QString& jointName) ;
|
||||||
|
@ -149,6 +156,9 @@ public:
|
||||||
|
|
||||||
void updateFromHeadParameters(const HeadParameters& params);
|
void updateFromHeadParameters(const HeadParameters& params);
|
||||||
|
|
||||||
|
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||||
|
float scale, float priority) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
void updateLeanJoint(int index, float leanSideways, float leanForward, float torsoTwist);
|
||||||
|
@ -156,16 +166,22 @@ public:
|
||||||
void updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
void updateEyeJoint(int index, const glm::quat& worldHeadOrientation, const glm::vec3& lookAt, const glm::vec3& saccade);
|
||||||
|
|
||||||
QVector<JointState> _jointStates;
|
QVector<JointState> _jointStates;
|
||||||
|
int _rootJointIndex = -1;
|
||||||
|
|
||||||
|
int _leftHandJointIndex = -1;
|
||||||
|
int _leftElbowJointIndex = -1;
|
||||||
|
int _leftShoulderJointIndex = -1;
|
||||||
|
|
||||||
|
int _rightHandJointIndex = -1;
|
||||||
|
int _rightElbowJointIndex = -1;
|
||||||
|
int _rightShoulderJointIndex = -1;
|
||||||
|
|
||||||
QList<AnimationHandlePointer> _animationHandles;
|
QList<AnimationHandlePointer> _animationHandles;
|
||||||
QList<AnimationHandlePointer> _runningAnimations;
|
QList<AnimationHandlePointer> _runningAnimations;
|
||||||
|
|
||||||
bool _enableRig;
|
bool _enableRig;
|
||||||
bool _isWalking;
|
|
||||||
bool _isTurning;
|
|
||||||
bool _isIdle;
|
|
||||||
glm::vec3 _lastFront;
|
glm::vec3 _lastFront;
|
||||||
glm::vec3 _lastPosition;
|
glm::vec3 _lastPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* defined(__hifi__Rig__) */
|
#endif /* defined(__hifi__Rig__) */
|
||||||
|
|
|
@ -46,7 +46,6 @@ typedef unsigned long long quint64;
|
||||||
#include <QtScript/QScriptable>
|
#include <QtScript/QScriptable>
|
||||||
#include <QReadWriteLock>
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
#include <CollisionInfo.h>
|
|
||||||
#include <NLPacket.h>
|
#include <NLPacket.h>
|
||||||
#include <Node.h>
|
#include <Node.h>
|
||||||
#include <RegisteredMetaTypes.h>
|
#include <RegisteredMetaTypes.h>
|
||||||
|
@ -257,10 +256,6 @@ public:
|
||||||
const HeadData* getHeadData() const { return _headData; }
|
const HeadData* getHeadData() const { return _headData; }
|
||||||
const HandData* getHandData() const { return _handData; }
|
const HandData* getHandData() const { return _handData; }
|
||||||
|
|
||||||
virtual bool findSphereCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasIdentityChangedAfterParsing(NLPacket& packet);
|
bool hasIdentityChangedAfterParsing(NLPacket& packet);
|
||||||
QByteArray identityByteArray();
|
QByteArray identityByteArray();
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include <AnimationCache.h> // for Animation, AnimationCache, and AnimationPointer classes
|
#include <AnimationCache.h> // for Animation, AnimationCache, and AnimationPointer classes
|
||||||
#include <CollisionInfo.h>
|
|
||||||
#include <Octree.h> // for EncodeBitstreamParams class
|
#include <Octree.h> // for EncodeBitstreamParams class
|
||||||
#include <OctreeElement.h> // for OctreeElement::AppendState
|
#include <OctreeElement.h> // for OctreeElement::AppendState
|
||||||
#include <OctreePacketData.h>
|
#include <OctreePacketData.h>
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
#include <CollisionInfo.h>
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
#include <Octree.h>
|
#include <Octree.h>
|
||||||
#include <OctreeScriptingInterface.h>
|
#include <OctreeScriptingInterface.h>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include <ByteCountCoding.h>
|
#include <ByteCountCoding.h>
|
||||||
#include <PlaneShape.h>
|
#include <GeometryUtil.h>
|
||||||
|
|
||||||
#include "EntityTree.h"
|
#include "EntityTree.h"
|
||||||
#include "EntityTreeElement.h"
|
#include "EntityTreeElement.h"
|
||||||
|
@ -128,46 +128,13 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||||
void** intersectedObject, bool precisionPicking) const {
|
void** intersectedObject, bool precisionPicking) const {
|
||||||
|
|
||||||
RayIntersectionInfo rayInfo;
|
|
||||||
rayInfo._rayStart = origin;
|
|
||||||
rayInfo._rayDirection = direction;
|
|
||||||
rayInfo._rayLength = std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
PlaneShape plane;
|
|
||||||
|
|
||||||
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
|
|
||||||
glm::vec3 normal = getRotation() * UNROTATED_NORMAL;
|
|
||||||
plane.setNormal(normal);
|
|
||||||
plane.setPoint(getPosition()); // the position is definitely a point on our plane
|
|
||||||
|
|
||||||
bool intersects = plane.findRayIntersection(rayInfo);
|
|
||||||
|
|
||||||
if (intersects) {
|
|
||||||
glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance);
|
|
||||||
// now we know the point the ray hit our plane
|
|
||||||
|
|
||||||
glm::mat4 rotation = glm::mat4_cast(getRotation());
|
|
||||||
glm::mat4 translation = glm::translate(getPosition());
|
|
||||||
glm::mat4 entityToWorldMatrix = translation * rotation;
|
|
||||||
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
|
|
||||||
|
|
||||||
glm::vec3 dimensions = getDimensions();
|
glm::vec3 dimensions = getDimensions();
|
||||||
glm::vec3 registrationPoint = getRegistrationPoint();
|
glm::vec2 xyDimensions(dimensions.x, dimensions.y);
|
||||||
glm::vec3 corner = -(dimensions * registrationPoint);
|
glm::quat rotation = getRotation();
|
||||||
AABox entityFrameBox(corner, dimensions);
|
glm::vec3 position = getPosition() + rotation *
|
||||||
|
(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
|
||||||
glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
|
return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance);
|
||||||
|
|
||||||
intersects = entityFrameBox.contains(entityFrameHitAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersects) {
|
|
||||||
distance = rayInfo._hitDistance;
|
|
||||||
}
|
|
||||||
return intersects;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include <ByteCountCoding.h>
|
#include <ByteCountCoding.h>
|
||||||
#include <PlaneShape.h>
|
#include <GeometryUtil.h>
|
||||||
|
|
||||||
#include "EntityTree.h"
|
#include "EntityTree.h"
|
||||||
#include "EntityTreeElement.h"
|
#include "EntityTreeElement.h"
|
||||||
|
@ -98,48 +98,15 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
|
||||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl);
|
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||||
void** intersectedObject, bool precisionPicking) const {
|
void** intersectedObject, bool precisionPicking) const {
|
||||||
|
|
||||||
RayIntersectionInfo rayInfo;
|
|
||||||
rayInfo._rayStart = origin;
|
|
||||||
rayInfo._rayDirection = direction;
|
|
||||||
rayInfo._rayLength = std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
PlaneShape plane;
|
|
||||||
|
|
||||||
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
|
|
||||||
glm::vec3 normal = getRotation() * UNROTATED_NORMAL;
|
|
||||||
plane.setNormal(normal);
|
|
||||||
plane.setPoint(getPosition()); // the position is definitely a point on our plane
|
|
||||||
|
|
||||||
bool intersects = plane.findRayIntersection(rayInfo);
|
|
||||||
|
|
||||||
if (intersects) {
|
|
||||||
glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance);
|
|
||||||
// now we know the point the ray hit our plane
|
|
||||||
|
|
||||||
glm::mat4 rotation = glm::mat4_cast(getRotation());
|
|
||||||
glm::mat4 translation = glm::translate(getPosition());
|
|
||||||
glm::mat4 entityToWorldMatrix = translation * rotation;
|
|
||||||
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
|
|
||||||
|
|
||||||
glm::vec3 dimensions = getDimensions();
|
glm::vec3 dimensions = getDimensions();
|
||||||
glm::vec3 registrationPoint = getRegistrationPoint();
|
glm::vec2 xyDimensions(dimensions.x, dimensions.y);
|
||||||
glm::vec3 corner = -(dimensions * registrationPoint);
|
glm::quat rotation = getRotation();
|
||||||
AABox entityFrameBox(corner, dimensions);
|
glm::vec3 position = getPosition() + rotation *
|
||||||
|
(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
|
||||||
glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
|
return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance);
|
||||||
|
|
||||||
intersects = entityFrameBox.contains(entityFrameHitAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersects) {
|
|
||||||
distance = rayInfo._hitDistance;
|
|
||||||
}
|
|
||||||
return intersects;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebEntityItem::setSourceUrl(const QString& value) {
|
void WebEntityItem::setSourceUrl(const QString& value) {
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
#include <OctalCode.h>
|
#include <OctalCode.h>
|
||||||
#include <Shape.h>
|
|
||||||
#include <gpu/Format.h>
|
#include <gpu/Format.h>
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
#include "FBXReader.h"
|
#include "FBXReader.h"
|
||||||
#include "OBJReader.h"
|
#include "OBJReader.h"
|
||||||
#include "Shape.h"
|
|
||||||
#include "ModelFormatLogging.h"
|
#include "ModelFormatLogging.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
#include <OctalCode.h>
|
#include <OctalCode.h>
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
#include <SharedUtil.h>
|
#include <SharedUtil.h>
|
||||||
#include <Shape.h>
|
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
|
|
||||||
#include "CoverageMap.h"
|
#include "CoverageMap.h"
|
||||||
|
@ -791,13 +790,6 @@ public:
|
||||||
bool found;
|
bool found;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShapeArgs {
|
|
||||||
public:
|
|
||||||
const Shape* shape;
|
|
||||||
CollisionList& collisions;
|
|
||||||
bool found;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ContentArgs {
|
class ContentArgs {
|
||||||
public:
|
public:
|
||||||
AACube cube;
|
AACube cube;
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include <LogHandler.h>
|
#include <LogHandler.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <AACubeShape.h>
|
|
||||||
|
|
||||||
#include "AACube.h"
|
#include "AACube.h"
|
||||||
#include "OctalCode.h"
|
#include "OctalCode.h"
|
||||||
|
@ -640,50 +639,7 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float
|
||||||
// otherwise, we need to find which of our children we should recurse
|
// otherwise, we need to find which of our children we should recurse
|
||||||
glm::vec3 ourCenter = _cube.calcCenter();
|
glm::vec3 ourCenter = _cube.calcCenter();
|
||||||
|
|
||||||
int childIndex = CHILD_UNKNOWN;
|
int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z));
|
||||||
// left half
|
|
||||||
if (x > ourCenter.x) {
|
|
||||||
if (y > ourCenter.y) {
|
|
||||||
// top left
|
|
||||||
if (z > ourCenter.z) {
|
|
||||||
// top left far
|
|
||||||
childIndex = CHILD_TOP_LEFT_FAR;
|
|
||||||
} else {
|
|
||||||
// top left near
|
|
||||||
childIndex = CHILD_TOP_LEFT_NEAR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// bottom left
|
|
||||||
if (z > ourCenter.z) {
|
|
||||||
// bottom left far
|
|
||||||
childIndex = CHILD_BOTTOM_LEFT_FAR;
|
|
||||||
} else {
|
|
||||||
// bottom left near
|
|
||||||
childIndex = CHILD_BOTTOM_LEFT_NEAR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// right half
|
|
||||||
if (y > ourCenter.y) {
|
|
||||||
// top right
|
|
||||||
if (z > ourCenter.z) {
|
|
||||||
// top right far
|
|
||||||
childIndex = CHILD_TOP_RIGHT_FAR;
|
|
||||||
} else {
|
|
||||||
// top right near
|
|
||||||
childIndex = CHILD_TOP_RIGHT_NEAR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// bottom right
|
|
||||||
if (z > ourCenter.z) {
|
|
||||||
// bottom right far
|
|
||||||
childIndex = CHILD_BOTTOM_RIGHT_FAR;
|
|
||||||
} else {
|
|
||||||
// bottom right near
|
|
||||||
childIndex = CHILD_BOTTOM_RIGHT_NEAR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, check if we have a child at that location
|
// Now, check if we have a child at that location
|
||||||
child = getChildAtIndex(childIndex);
|
child = getChildAtIndex(childIndex);
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "ViewFrustum.h"
|
#include "ViewFrustum.h"
|
||||||
#include "OctreeConstants.h"
|
#include "OctreeConstants.h"
|
||||||
|
|
||||||
class CollisionList;
|
|
||||||
class EncodeBitstreamParams;
|
class EncodeBitstreamParams;
|
||||||
class Octree;
|
class Octree;
|
||||||
class OctreeElement;
|
class OctreeElement;
|
||||||
|
|
|
@ -16,12 +16,9 @@
|
||||||
#include <glm/gtx/transform.hpp>
|
#include <glm/gtx/transform.hpp>
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
#include <CapsuleShape.h>
|
|
||||||
#include <GeometryUtil.h>
|
#include <GeometryUtil.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
#include <ShapeCollider.h>
|
|
||||||
#include <SphereShape.h>
|
|
||||||
#include <ViewFrustum.h>
|
#include <ViewFrustum.h>
|
||||||
#include <render/Scene.h>
|
#include <render/Scene.h>
|
||||||
#include <gpu/Batch.h>
|
#include <gpu/Batch.h>
|
||||||
|
@ -476,7 +473,23 @@ bool Model::updateGeometry() {
|
||||||
void Model::initJointStates(QVector<JointState> states) {
|
void Model::initJointStates(QVector<JointState> states) {
|
||||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||||
_boundingRadius = _rig->initJointStates(states, parentTransform);
|
|
||||||
|
int rootJointIndex = geometry.rootJointIndex;
|
||||||
|
int leftHandJointIndex = geometry.leftHandJointIndex;
|
||||||
|
int leftElbowJointIndex = leftHandJointIndex >= 0 ? geometry.joints.at(leftHandJointIndex).parentIndex : -1;
|
||||||
|
int leftShoulderJointIndex = leftElbowJointIndex >= 0 ? geometry.joints.at(leftElbowJointIndex).parentIndex : -1;
|
||||||
|
int rightHandJointIndex = geometry.rightHandJointIndex;
|
||||||
|
int rightElbowJointIndex = rightHandJointIndex >= 0 ? geometry.joints.at(rightHandJointIndex).parentIndex : -1;
|
||||||
|
int rightShoulderJointIndex = rightElbowJointIndex >= 0 ? geometry.joints.at(rightElbowJointIndex).parentIndex : -1;
|
||||||
|
|
||||||
|
_boundingRadius = _rig->initJointStates(states, parentTransform,
|
||||||
|
rootJointIndex,
|
||||||
|
leftHandJointIndex,
|
||||||
|
leftElbowJointIndex,
|
||||||
|
leftShoulderJointIndex,
|
||||||
|
rightHandJointIndex,
|
||||||
|
rightElbowJointIndex,
|
||||||
|
rightShoulderJointIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const glm::vec3& direction, float& distance,
|
||||||
|
|
|
@ -18,9 +18,6 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
#include <CollisionInfo.h>
|
|
||||||
#include <RayIntersectionInfo.h>
|
|
||||||
|
|
||||||
class PhysicsEntity {
|
class PhysicsEntity {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
#include <AudioConstants.h>
|
#include <AudioConstants.h>
|
||||||
#include <AudioEffectOptions.h>
|
#include <AudioEffectOptions.h>
|
||||||
#include <AvatarData.h>
|
#include <AvatarData.h>
|
||||||
#include <CollisionInfo.h>
|
|
||||||
#include <EntityScriptingInterface.h>
|
#include <EntityScriptingInterface.h>
|
||||||
#include <NetworkAccessManager.h>
|
#include <NetworkAccessManager.h>
|
||||||
#include <NodeList.h>
|
#include <NodeList.h>
|
||||||
|
@ -366,8 +365,6 @@ void ScriptEngine::init() {
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
|
globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
|
||||||
globalObject().setProperty("COLLISION_GROUP_ENVIRONMENT", newVariant(QVariant(COLLISION_GROUP_ENVIRONMENT)));
|
|
||||||
globalObject().setProperty("COLLISION_GROUP_AVATARS", newVariant(QVariant(COLLISION_GROUP_AVATARS)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
|
QScriptValue ScriptEngine::registerGlobalObject(const QString& name, QObject* object) {
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
//
|
|
||||||
// AACubeShape.cpp
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 2014.08.22
|
|
||||||
// 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 <glm/glm.hpp>
|
|
||||||
#include <glm/gtx/norm.hpp>
|
|
||||||
|
|
||||||
#include "AACubeShape.h"
|
|
||||||
#include "NumericalConstants.h" // for SQUARE_ROOT_OF_3
|
|
||||||
|
|
||||||
glm::vec3 faceNormals[3] = { glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f) };
|
|
||||||
|
|
||||||
bool AACubeShape::findRayIntersection(RayIntersectionInfo& intersection) const {
|
|
||||||
// A = ray point
|
|
||||||
// B = cube center
|
|
||||||
glm::vec3 BA = _translation - intersection._rayStart;
|
|
||||||
|
|
||||||
// check for ray intersection with cube's bounding sphere
|
|
||||||
// a = distance along line to closest approach to B
|
|
||||||
float a = glm::dot(intersection._rayDirection, BA);
|
|
||||||
// b2 = squared distance from cube center to point of closest approach
|
|
||||||
float b2 = glm::length2(a * intersection._rayDirection - BA);
|
|
||||||
// r = bounding radius of cube
|
|
||||||
float halfSide = 0.5f * _scale;
|
|
||||||
const float r = SQUARE_ROOT_OF_3 * halfSide;
|
|
||||||
if (b2 > r * r) {
|
|
||||||
// line doesn't hit cube's bounding sphere
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for tuncated/short ray
|
|
||||||
// maxLength = maximum possible distance between rayStart and center of cube
|
|
||||||
const float maxLength = glm::min(intersection._rayLength, intersection._hitDistance) + r;
|
|
||||||
if (a * a + b2 > maxLength * maxLength) {
|
|
||||||
// ray is not long enough to reach cube's bounding sphere
|
|
||||||
// NOTE: we don't fall in here when ray's length if FLT_MAX because maxLength^2 will be inf or nan
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the trivial checks have been exhausted, so must trace to each face
|
|
||||||
bool hit = false;
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
for (float sign = -1.0f; sign < 2.0f; sign += 2.0f) {
|
|
||||||
glm::vec3 faceNormal = sign * faceNormals[i];
|
|
||||||
float rayDotPlane = glm::dot(intersection._rayDirection, faceNormal);
|
|
||||||
if (glm::abs(rayDotPlane) > EPSILON) {
|
|
||||||
float distanceToFace = (halfSide + glm::dot(BA, faceNormal)) / rayDotPlane;
|
|
||||||
if (distanceToFace >= 0.0f) {
|
|
||||||
glm::vec3 point = distanceToFace * intersection._rayDirection - BA;
|
|
||||||
int j = (i + 1) % 3;
|
|
||||||
int k = (i + 2) % 3;
|
|
||||||
glm::vec3 secondNormal = faceNormals[j];
|
|
||||||
glm::vec3 thirdNormal = faceNormals[k];
|
|
||||||
if (glm::abs(glm::dot(point, secondNormal)) > halfSide ||
|
|
||||||
glm::abs(glm::dot(point, thirdNormal)) > halfSide) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (distanceToFace < intersection._hitDistance && distanceToFace < intersection._rayLength) {
|
|
||||||
intersection._hitDistance = distanceToFace;
|
|
||||||
intersection._hitNormal = faceNormal;
|
|
||||||
intersection._hitShape = const_cast<AACubeShape*>(this);
|
|
||||||
hit = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hit;
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
//
|
|
||||||
// AACubeShape.h
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 2014.08.22
|
|
||||||
// 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_AACubeShape_h
|
|
||||||
#define hifi_AACubeShape_h
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include "Shape.h"
|
|
||||||
|
|
||||||
class AACubeShape : public Shape {
|
|
||||||
public:
|
|
||||||
AACubeShape() : Shape(AACUBE_SHAPE), _scale(1.0f) { }
|
|
||||||
AACubeShape(float scale) : Shape(AACUBE_SHAPE), _scale(scale) { }
|
|
||||||
AACubeShape(float scale, const glm::vec3& position) : Shape(AACUBE_SHAPE, position), _scale(scale) { }
|
|
||||||
|
|
||||||
virtual ~AACubeShape() { }
|
|
||||||
|
|
||||||
float getScale() const { return _scale; }
|
|
||||||
void setScale(float scale) { _scale = scale; }
|
|
||||||
|
|
||||||
bool findRayIntersection(RayIntersectionInfo& intersection) const;
|
|
||||||
|
|
||||||
float getVolume() const { return _scale * _scale * _scale; }
|
|
||||||
virtual QDebug& dumpToDebug(QDebug& debugConext) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
float _scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline QDebug& AACubeShape::dumpToDebug(QDebug& debugConext) const {
|
|
||||||
debugConext << "AACubeShape[ ("
|
|
||||||
<< "type: " << getType()
|
|
||||||
<< "position: "
|
|
||||||
<< getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z
|
|
||||||
<< "scale: "
|
|
||||||
<< getScale()
|
|
||||||
<< "]";
|
|
||||||
|
|
||||||
return debugConext;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // hifi_AACubeShape_h
|
|
|
@ -1,218 +0,0 @@
|
||||||
//
|
|
||||||
// CapsuleShape.cpp
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 02/20/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 <iostream>
|
|
||||||
#include <glm/gtx/vector_angle.hpp>
|
|
||||||
|
|
||||||
#include "CapsuleShape.h"
|
|
||||||
#include "GeometryUtil.h"
|
|
||||||
#include "NumericalConstants.h"
|
|
||||||
|
|
||||||
CapsuleShape::CapsuleShape() : Shape(CAPSULE_SHAPE), _radius(0.0f), _halfHeight(0.0f) {}
|
|
||||||
|
|
||||||
CapsuleShape::CapsuleShape(float radius, float halfHeight) : Shape(CAPSULE_SHAPE),
|
|
||||||
_radius(radius), _halfHeight(halfHeight) {
|
|
||||||
updateBoundingRadius();
|
|
||||||
}
|
|
||||||
|
|
||||||
CapsuleShape::CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation) :
|
|
||||||
Shape(CAPSULE_SHAPE, position, rotation), _radius(radius), _halfHeight(halfHeight) {
|
|
||||||
updateBoundingRadius();
|
|
||||||
}
|
|
||||||
|
|
||||||
CapsuleShape::CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint) :
|
|
||||||
Shape(CAPSULE_SHAPE), _radius(radius), _halfHeight(0.0f) {
|
|
||||||
setEndPoints(startPoint, endPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \param[out] startPoint is the center of start cap
|
|
||||||
void CapsuleShape::getStartPoint(glm::vec3& startPoint) const {
|
|
||||||
startPoint = _translation - _rotation * glm::vec3(0.0f, _halfHeight, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// \param[out] endPoint is the center of the end cap
|
|
||||||
void CapsuleShape::getEndPoint(glm::vec3& endPoint) const {
|
|
||||||
endPoint = _translation + _rotation * glm::vec3(0.0f, _halfHeight, 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CapsuleShape::computeNormalizedAxis(glm::vec3& axis) const {
|
|
||||||
// default axis of a capsule is along the yAxis
|
|
||||||
axis = _rotation * DEFAULT_CAPSULE_AXIS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CapsuleShape::setRadius(float radius) {
|
|
||||||
_radius = radius;
|
|
||||||
updateBoundingRadius();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CapsuleShape::setHalfHeight(float halfHeight) {
|
|
||||||
_halfHeight = halfHeight;
|
|
||||||
updateBoundingRadius();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CapsuleShape::setRadiusAndHalfHeight(float radius, float halfHeight) {
|
|
||||||
_radius = radius;
|
|
||||||
_halfHeight = halfHeight;
|
|
||||||
updateBoundingRadius();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CapsuleShape::setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint) {
|
|
||||||
glm::vec3 axis = endPoint - startPoint;
|
|
||||||
_translation = 0.5f * (endPoint + startPoint);
|
|
||||||
float height = glm::length(axis);
|
|
||||||
if (height > EPSILON) {
|
|
||||||
_halfHeight = 0.5f * height;
|
|
||||||
axis /= height;
|
|
||||||
_rotation = computeNewRotation(axis);
|
|
||||||
}
|
|
||||||
updateBoundingRadius();
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper
|
|
||||||
bool findRayIntersectionWithCap(const glm::vec3& sphereCenter, float sphereRadius,
|
|
||||||
const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) {
|
|
||||||
float r2 = sphereRadius * sphereRadius;
|
|
||||||
|
|
||||||
// compute closest approach (CA)
|
|
||||||
float a = glm::dot(sphereCenter - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA
|
|
||||||
float b2 = glm::distance2(sphereCenter, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA
|
|
||||||
if (b2 > r2) {
|
|
||||||
// ray does not hit sphere
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection
|
|
||||||
float d2 = glm::distance2(intersection._rayStart, sphereCenter); // d2 = squared distance from sphere-center to ray-start
|
|
||||||
float distance = FLT_MAX;
|
|
||||||
if (a < 0.0f) {
|
|
||||||
// ray points away from sphere-center
|
|
||||||
if (d2 > r2) {
|
|
||||||
// ray starts outside sphere
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// ray starts inside sphere
|
|
||||||
distance = c + a;
|
|
||||||
} else if (d2 > r2) {
|
|
||||||
// ray starts outside sphere
|
|
||||||
distance = a - c;
|
|
||||||
} else {
|
|
||||||
// ray starts inside sphere
|
|
||||||
distance = a + c;
|
|
||||||
}
|
|
||||||
if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) {
|
|
||||||
glm::vec3 sphereCenterToHitPoint = intersection._rayStart + distance * intersection._rayDirection - sphereCenter;
|
|
||||||
if (glm::dot(sphereCenterToHitPoint, sphereCenter - capsuleCenter) >= 0.0f) {
|
|
||||||
intersection._hitDistance = distance;
|
|
||||||
intersection._hitNormal = glm::normalize(sphereCenterToHitPoint);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CapsuleShape::findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const {
|
|
||||||
glm::vec3 capCenter;
|
|
||||||
getStartPoint(capCenter);
|
|
||||||
bool hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection);
|
|
||||||
getEndPoint(capCenter);
|
|
||||||
hit = findRayIntersectionWithCap(capCenter, _radius, capsuleCenter, intersection) || hit;
|
|
||||||
if (hit) {
|
|
||||||
intersection._hitShape = const_cast<CapsuleShape*>(this);
|
|
||||||
}
|
|
||||||
return hit;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CapsuleShape::findRayIntersection(RayIntersectionInfo& intersection) const {
|
|
||||||
// ray is U, capsule is V
|
|
||||||
glm::vec3 axisV;
|
|
||||||
computeNormalizedAxis(axisV);
|
|
||||||
glm::vec3 centerV = getTranslation();
|
|
||||||
|
|
||||||
// first handle parallel case
|
|
||||||
float uDotV = glm::dot(axisV, intersection._rayDirection);
|
|
||||||
glm::vec3 UV = intersection._rayStart - centerV;
|
|
||||||
if (glm::abs(1.0f - glm::abs(uDotV)) < EPSILON) {
|
|
||||||
// line and cylinder are parallel
|
|
||||||
float distanceV = glm::dot(UV, intersection._rayDirection);
|
|
||||||
if (glm::length2(UV - distanceV * intersection._rayDirection) <= _radius * _radius) {
|
|
||||||
// ray is inside cylinder's radius and might intersect caps
|
|
||||||
return findRayIntersectionWithCaps(centerV, intersection);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Given a line with point 'U' and normalized direction 'u' and
|
|
||||||
// a cylinder with axial point 'V', radius 'r', and normalized direction 'v'
|
|
||||||
// the intersection of the two is on the line at distance 't' from 'U'.
|
|
||||||
//
|
|
||||||
// Determining the values of t reduces to solving a quadratic equation: At^2 + Bt + C = 0
|
|
||||||
//
|
|
||||||
// where:
|
|
||||||
//
|
|
||||||
// UV = U-V
|
|
||||||
// w = u-(u.v)v
|
|
||||||
// Q = UV-(UV.v)v
|
|
||||||
//
|
|
||||||
// A = w^2
|
|
||||||
// B = 2(w.Q)
|
|
||||||
// C = Q^2 - r^2
|
|
||||||
|
|
||||||
glm::vec3 w = intersection._rayDirection - uDotV * axisV;
|
|
||||||
glm::vec3 Q = UV - glm::dot(UV, axisV) * axisV;
|
|
||||||
|
|
||||||
// we save a few multiplies by storing 2*A rather than just A
|
|
||||||
float A2 = 2.0f * glm::dot(w, w);
|
|
||||||
float B = 2.0f * glm::dot(w, Q);
|
|
||||||
|
|
||||||
// since C is only ever used once (in the determinant) we compute it inline
|
|
||||||
float determinant = B * B - 2.0f * A2 * (glm::dot(Q, Q) - _radius * _radius);
|
|
||||||
if (determinant < 0.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
float hitLow = (-B - sqrtf(determinant)) / A2;
|
|
||||||
float hitHigh = -(hitLow + 2.0f * B / A2);
|
|
||||||
|
|
||||||
if (hitLow > hitHigh) {
|
|
||||||
// re-arrange so hitLow is always the smaller value
|
|
||||||
float temp = hitHigh;
|
|
||||||
hitHigh = hitLow;
|
|
||||||
hitLow = temp;
|
|
||||||
}
|
|
||||||
if (hitLow < 0.0f) {
|
|
||||||
if (hitHigh < 0.0f) {
|
|
||||||
// capsule is completely behind rayStart
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
hitLow = hitHigh;
|
|
||||||
}
|
|
||||||
|
|
||||||
glm::vec3 p = intersection._rayStart + hitLow * intersection._rayDirection;
|
|
||||||
float d = glm::dot(p - centerV, axisV);
|
|
||||||
if (glm::abs(d) <= getHalfHeight()) {
|
|
||||||
// we definitely hit the cylinder wall
|
|
||||||
intersection._hitDistance = hitLow;
|
|
||||||
intersection._hitNormal = glm::normalize(p - centerV - d * axisV);
|
|
||||||
intersection._hitShape = const_cast<CapsuleShape*>(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ray still might hit the caps
|
|
||||||
return findRayIntersectionWithCaps(centerV, intersection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
glm::quat CapsuleShape::computeNewRotation(const glm::vec3& newAxis) {
|
|
||||||
float angle = glm::angle(newAxis, DEFAULT_CAPSULE_AXIS);
|
|
||||||
if (angle > EPSILON) {
|
|
||||||
glm::vec3 rotationAxis = glm::normalize(glm::cross(DEFAULT_CAPSULE_AXIS, newAxis));
|
|
||||||
return glm::angleAxis(angle, rotationAxis);
|
|
||||||
}
|
|
||||||
return glm::quat();
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
//
|
|
||||||
// CapsuleShape.h
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 02/20/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_CapsuleShape_h
|
|
||||||
#define hifi_CapsuleShape_h
|
|
||||||
|
|
||||||
#include "NumericalConstants.h"
|
|
||||||
#include "Shape.h"
|
|
||||||
|
|
||||||
// default axis of CapsuleShape is Y-axis
|
|
||||||
const glm::vec3 DEFAULT_CAPSULE_AXIS(0.0f, 1.0f, 0.0f);
|
|
||||||
|
|
||||||
|
|
||||||
class CapsuleShape : public Shape {
|
|
||||||
public:
|
|
||||||
CapsuleShape();
|
|
||||||
CapsuleShape(float radius, float halfHeight);
|
|
||||||
CapsuleShape(float radius, float halfHeight, const glm::vec3& position, const glm::quat& rotation);
|
|
||||||
CapsuleShape(float radius, const glm::vec3& startPoint, const glm::vec3& endPoint);
|
|
||||||
|
|
||||||
virtual ~CapsuleShape() {}
|
|
||||||
|
|
||||||
float getRadius() const { return _radius; }
|
|
||||||
virtual float getHalfHeight() const { return _halfHeight; }
|
|
||||||
|
|
||||||
/// \param[out] startPoint is the center of start cap
|
|
||||||
virtual void getStartPoint(glm::vec3& startPoint) const;
|
|
||||||
|
|
||||||
/// \param[out] endPoint is the center of the end cap
|
|
||||||
virtual void getEndPoint(glm::vec3& endPoint) const;
|
|
||||||
|
|
||||||
virtual void computeNormalizedAxis(glm::vec3& axis) const;
|
|
||||||
|
|
||||||
void setRadius(float radius);
|
|
||||||
virtual void setHalfHeight(float height);
|
|
||||||
virtual void setRadiusAndHalfHeight(float radius, float height);
|
|
||||||
|
|
||||||
/// Sets the endpoints and updates center, rotation, and halfHeight to agree.
|
|
||||||
virtual void setEndPoints(const glm::vec3& startPoint, const glm::vec3& endPoint);
|
|
||||||
|
|
||||||
bool findRayIntersection(RayIntersectionInfo& intersection) const;
|
|
||||||
|
|
||||||
virtual float getVolume() const { return (PI * _radius * _radius) * (1.3333333333f * _radius + getHalfHeight()); }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool findRayIntersectionWithCaps(const glm::vec3& capsuleCenter, RayIntersectionInfo& intersection) const;
|
|
||||||
virtual void updateBoundingRadius() { _boundingRadius = _radius + getHalfHeight(); }
|
|
||||||
static glm::quat computeNewRotation(const glm::vec3& newAxis);
|
|
||||||
|
|
||||||
float _radius;
|
|
||||||
float _halfHeight;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_CapsuleShape_h
|
|
|
@ -1,110 +0,0 @@
|
||||||
//
|
|
||||||
// CollisionInfo.cpp
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 02/14/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 "CollisionInfo.h"
|
|
||||||
#include "NumericalConstants.h"
|
|
||||||
#include "Shape.h"
|
|
||||||
|
|
||||||
CollisionInfo::CollisionInfo() :
|
|
||||||
_data(NULL),
|
|
||||||
_intData(0),
|
|
||||||
_shapeA(NULL),
|
|
||||||
_shapeB(NULL),
|
|
||||||
_damping(0.0f),
|
|
||||||
_elasticity(1.0f),
|
|
||||||
_contactPoint(0.0f),
|
|
||||||
_penetration(0.0f),
|
|
||||||
_addedVelocity(0.0f) {
|
|
||||||
}
|
|
||||||
|
|
||||||
quint64 CollisionInfo::getShapePairKey() const {
|
|
||||||
if (_shapeB == NULL || _shapeA == NULL) {
|
|
||||||
// zero is an invalid key
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
quint32 idA = _shapeA->getID();
|
|
||||||
quint32 idB = _shapeB->getID();
|
|
||||||
return idA < idB ? ((quint64)idA << 32) + (quint64)idB : ((quint64)idB << 32) + (quint64)idA;
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionList::CollisionList(int maxSize) :
|
|
||||||
_maxSize(maxSize),
|
|
||||||
_size(0) {
|
|
||||||
_collisions.resize(_maxSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollisionInfo::apply() {
|
|
||||||
assert(_shapeA);
|
|
||||||
// NOTE: Shape::computeEffectiveMass() has side effects: computes and caches partial Lagrangian coefficients
|
|
||||||
Shape* shapeA = const_cast<Shape*>(_shapeA);
|
|
||||||
float massA = shapeA->computeEffectiveMass(_penetration, _contactPoint);
|
|
||||||
float massB = MAX_SHAPE_MASS;
|
|
||||||
float totalMass = massA + massB;
|
|
||||||
if (_shapeB) {
|
|
||||||
Shape* shapeB = const_cast<Shape*>(_shapeB);
|
|
||||||
massB = shapeB->computeEffectiveMass(-_penetration, _contactPoint - _penetration);
|
|
||||||
totalMass = massA + massB;
|
|
||||||
if (totalMass < EPSILON) {
|
|
||||||
massA = massB = 1.0f;
|
|
||||||
totalMass = 2.0f;
|
|
||||||
}
|
|
||||||
// remember that _penetration points from A into B
|
|
||||||
shapeB->accumulateDelta(massA / totalMass, _penetration);
|
|
||||||
}
|
|
||||||
// NOTE: Shape::accumulateDelta() uses the coefficients from previous call to Shape::computeEffectiveMass()
|
|
||||||
// remember that _penetration points from A into B
|
|
||||||
shapeA->accumulateDelta(massB / totalMass, -_penetration);
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionInfo* CollisionList::getNewCollision() {
|
|
||||||
// return pointer to existing CollisionInfo, or NULL of list is full
|
|
||||||
return (_size < _maxSize) ? &(_collisions[_size++]) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollisionList::deleteLastCollision() {
|
|
||||||
if (_size > 0) {
|
|
||||||
--_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionInfo* CollisionList::getCollision(int index) {
|
|
||||||
return (index > -1 && index < _size) ? &(_collisions[index]) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionInfo* CollisionList::getLastCollision() {
|
|
||||||
return (_size > 0) ? &(_collisions[_size - 1]) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CollisionList::clear() {
|
|
||||||
// we rely on the external context to properly set or clear the data members of CollisionInfos
|
|
||||||
/*
|
|
||||||
for (int i = 0; i < _size; ++i) {
|
|
||||||
// we only clear the important stuff
|
|
||||||
CollisionInfo& collision = _collisions[i];
|
|
||||||
//collision._data = NULL;
|
|
||||||
//collision._intData = 0;
|
|
||||||
//collision._floatDAta = 0.0f;
|
|
||||||
//collision._vecData = glm::vec3(0.0f);
|
|
||||||
//collision._shapeA = NULL;
|
|
||||||
//collision._shapeB = NULL;
|
|
||||||
//collision._damping;
|
|
||||||
//collision._elasticity;
|
|
||||||
//collision._contactPoint;
|
|
||||||
//collision._penetration;
|
|
||||||
//collision._addedVelocity;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
CollisionInfo* CollisionList::operator[](int index) {
|
|
||||||
return (index > -1 && index < _size) ? &(_collisions[index]) : NULL;
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
//
|
|
||||||
// CollisionInfo.h
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 02/14/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_CollisionInfo_h
|
|
||||||
#define hifi_CollisionInfo_h
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
class Shape;
|
|
||||||
|
|
||||||
const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0;
|
|
||||||
const quint32 COLLISION_GROUP_AVATARS = 1U << 1;
|
|
||||||
const quint32 COLLISION_GROUP_VOXELS = 1U << 2;
|
|
||||||
const quint32 VALID_COLLISION_GROUPS = 0x0f;
|
|
||||||
|
|
||||||
// CollisionInfo contains details about the collision between two things: BodyA and BodyB.
|
|
||||||
// The assumption is that the context that analyzes the collision knows about BodyA but
|
|
||||||
// does not necessarily know about BodyB. Hence the data storred in the CollisionInfo
|
|
||||||
// is expected to be relative to BodyA (for example the penetration points from A into B).
|
|
||||||
|
|
||||||
class CollisionInfo {
|
|
||||||
public:
|
|
||||||
CollisionInfo();
|
|
||||||
~CollisionInfo() {}
|
|
||||||
|
|
||||||
// TODO: Andrew to get rid of these data members
|
|
||||||
void* _data;
|
|
||||||
int _intData;
|
|
||||||
float _floatData;
|
|
||||||
glm::vec3 _vecData;
|
|
||||||
|
|
||||||
/// accumulates position changes for the shapes in this collision to resolve penetration
|
|
||||||
void apply();
|
|
||||||
|
|
||||||
Shape* getShapeA() const { return const_cast<Shape*>(_shapeA); }
|
|
||||||
Shape* getShapeB() const { return const_cast<Shape*>(_shapeB); }
|
|
||||||
|
|
||||||
/// \return unique key for shape pair
|
|
||||||
quint64 getShapePairKey() const;
|
|
||||||
|
|
||||||
const Shape* _shapeA; // pointer to shapeA in this collision
|
|
||||||
const Shape* _shapeB; // pointer to shapeB in this collision
|
|
||||||
|
|
||||||
void* _extraData; // pointer to extraData for this collision, opaque to the collision info, useful for external data
|
|
||||||
|
|
||||||
float _damping; // range [0,1] of friction coeficient
|
|
||||||
float _elasticity; // range [0,1] of energy conservation
|
|
||||||
glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB
|
|
||||||
glm::vec3 _penetration; // depth that BodyA penetrates into BodyB
|
|
||||||
glm::vec3 _addedVelocity; // velocity of BodyB
|
|
||||||
};
|
|
||||||
|
|
||||||
// CollisionList is intended to be a recycled container. Fill the CollisionInfo's,
|
|
||||||
// use them, and then clear them for the next frame or context.
|
|
||||||
|
|
||||||
class CollisionList {
|
|
||||||
public:
|
|
||||||
CollisionList(int maxSize);
|
|
||||||
|
|
||||||
/// \return pointer to next collision. NULL if list is full.
|
|
||||||
CollisionInfo* getNewCollision();
|
|
||||||
|
|
||||||
/// \forget about collision at the end
|
|
||||||
void deleteLastCollision();
|
|
||||||
|
|
||||||
/// \return pointer to collision by index. NULL if index out of bounds.
|
|
||||||
CollisionInfo* getCollision(int index);
|
|
||||||
|
|
||||||
/// \return pointer to last collision on the list. NULL if list is empty
|
|
||||||
CollisionInfo* getLastCollision();
|
|
||||||
|
|
||||||
/// \return true if list is full
|
|
||||||
bool isFull() const { return _size == _maxSize; }
|
|
||||||
|
|
||||||
/// \return number of valid collisions
|
|
||||||
int size() const { return _size; }
|
|
||||||
|
|
||||||
/// Clear valid collisions.
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
CollisionInfo* operator[](int index);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int _maxSize; // the container cannot get larger than this
|
|
||||||
int _size; // the current number of valid collisions in the list
|
|
||||||
QVector<CollisionInfo> _collisions;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_CollisionInfo_h
|
|
|
@ -11,11 +11,10 @@
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
#include "GeometryUtil.h"
|
#include "GeometryUtil.h"
|
||||||
#include "NumericalConstants.h"
|
#include "NumericalConstants.h"
|
||||||
#include "PlaneShape.h"
|
|
||||||
#include "RayIntersectionInfo.h"
|
|
||||||
|
|
||||||
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
glm::vec3 computeVectorFromPointToSegment(const glm::vec3& point, const glm::vec3& start, const glm::vec3& end) {
|
||||||
// compute the projection of the point vector onto the segment vector
|
// compute the projection of the point vector onto the segment vector
|
||||||
|
@ -496,31 +495,45 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len
|
||||||
|
|
||||||
bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::quat& rotation,
|
bool findRayRectangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::quat& rotation,
|
||||||
const glm::vec3& position, const glm::vec2& dimensions, float& distance) {
|
const glm::vec3& position, const glm::vec2& dimensions, float& distance) {
|
||||||
RayIntersectionInfo rayInfo;
|
|
||||||
rayInfo._rayStart = origin;
|
|
||||||
rayInfo._rayDirection = direction;
|
|
||||||
rayInfo._rayLength = std::numeric_limits<float>::max();
|
|
||||||
|
|
||||||
PlaneShape plane;
|
|
||||||
|
|
||||||
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
|
const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f);
|
||||||
glm::vec3 normal = rotation * UNROTATED_NORMAL;
|
glm::vec3 normal = rotation * UNROTATED_NORMAL;
|
||||||
plane.setNormal(normal);
|
|
||||||
plane.setPoint(position); // the position is definitely a point on our plane
|
|
||||||
|
|
||||||
bool intersects = plane.findRayIntersection(rayInfo);
|
bool maybeIntersects = false;
|
||||||
|
float denominator = glm::dot(normal, direction);
|
||||||
|
glm::vec3 offset = origin - position;
|
||||||
|
float normDotOffset = glm::dot(offset, normal);
|
||||||
|
float d = 0.0f;
|
||||||
|
if (fabsf(denominator) < EPSILON) {
|
||||||
|
// line is perpendicular to plane
|
||||||
|
if (fabsf(normDotOffset) < EPSILON) {
|
||||||
|
// ray starts on the plane
|
||||||
|
maybeIntersects = true;
|
||||||
|
|
||||||
if (intersects) {
|
// compute distance to closest approach
|
||||||
distance = rayInfo._hitDistance;
|
d = - glm::dot(offset, direction); // distance to closest approach of center of rectangle
|
||||||
|
if (d < 0.0f) {
|
||||||
glm::vec3 hitPosition = origin + (distance * direction);
|
// ray points away from center of rectangle, so ray's start is the closest approach
|
||||||
glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position);
|
d = 0.0f;
|
||||||
|
}
|
||||||
glm::vec2 halfDimensions = 0.5f * dimensions;
|
}
|
||||||
|
} else {
|
||||||
intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x
|
d = - normDotOffset / denominator;
|
||||||
&& -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y;
|
if (d > 0.0f) {
|
||||||
|
// ray points toward plane
|
||||||
|
maybeIntersects = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return intersects;
|
if (maybeIntersects) {
|
||||||
|
glm::vec3 hitPosition = origin + (d * direction);
|
||||||
|
glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position);
|
||||||
|
glm::vec2 halfDimensions = 0.5f * dimensions;
|
||||||
|
if (fabsf(localHitPosition.x) < halfDimensions.x && fabsf(localHitPosition.y) < halfDimensions.y) {
|
||||||
|
// only update distance on intersection
|
||||||
|
distance = d;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
//
|
|
||||||
// ListShape.cpp
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 02/20/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 "ListShape.h"
|
|
||||||
|
|
||||||
// ListShapeEntry
|
|
||||||
|
|
||||||
void ListShapeEntry::updateTransform(const glm::vec3& rootPosition, const glm::quat& rootRotation) {
|
|
||||||
_shape->setTranslation(rootPosition + rootRotation * _localPosition);
|
|
||||||
_shape->setRotation(_localRotation * rootRotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListShape
|
|
||||||
|
|
||||||
ListShape::~ListShape() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListShape::setTranslation(const glm::vec3& position) {
|
|
||||||
_subShapeTransformsAreDirty = true;
|
|
||||||
Shape::setTranslation(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListShape::setRotation(const glm::quat& rotation) {
|
|
||||||
_subShapeTransformsAreDirty = true;
|
|
||||||
Shape::setRotation(rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Shape* ListShape::getSubShape(int index) const {
|
|
||||||
if (index < 0 || index > _subShapeEntries.size()) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return _subShapeEntries[index]._shape;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListShape::updateSubTransforms() {
|
|
||||||
if (_subShapeTransformsAreDirty) {
|
|
||||||
for (int i = 0; i < _subShapeEntries.size(); ++i) {
|
|
||||||
_subShapeEntries[i].updateTransform(_translation, _rotation);
|
|
||||||
}
|
|
||||||
_subShapeTransformsAreDirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListShape::addShape(Shape* shape, const glm::vec3& localPosition, const glm::quat& localRotation) {
|
|
||||||
if (shape) {
|
|
||||||
ListShapeEntry entry;
|
|
||||||
entry._shape = shape;
|
|
||||||
entry._localPosition = localPosition;
|
|
||||||
entry._localRotation = localRotation;
|
|
||||||
_subShapeEntries.push_back(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListShape::setShapes(QVector<ListShapeEntry>& shapes) {
|
|
||||||
clear();
|
|
||||||
_subShapeEntries.swap(shapes);
|
|
||||||
// TODO: audit our new list of entries and delete any that have null pointers
|
|
||||||
computeBoundingRadius();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListShape::clear() {
|
|
||||||
// the ListShape owns its subShapes, so they need to be deleted
|
|
||||||
for (int i = 0; i < _subShapeEntries.size(); ++i) {
|
|
||||||
delete _subShapeEntries[i]._shape;
|
|
||||||
}
|
|
||||||
_subShapeEntries.clear();
|
|
||||||
setBoundingRadius(0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ListShape::computeBoundingRadius() {
|
|
||||||
float maxRadius = 0.0f;
|
|
||||||
for (int i = 0; i < _subShapeEntries.size(); ++i) {
|
|
||||||
ListShapeEntry& entry = _subShapeEntries[i];
|
|
||||||
float radius = glm::length(entry._localPosition) + entry._shape->getBoundingRadius();
|
|
||||||
if (radius > maxRadius) {
|
|
||||||
maxRadius = radius;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setBoundingRadius(maxRadius);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
//
|
|
||||||
// ListShape.h
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 02/20/2014.
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
|
||||||
// ListShape: A collection of shapes, each with a local transform.
|
|
||||||
//
|
|
||||||
// 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_ListShape_h
|
|
||||||
#define hifi_ListShape_h
|
|
||||||
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
#include <glm/gtx/norm.hpp>
|
|
||||||
|
|
||||||
#include "Shape.h"
|
|
||||||
|
|
||||||
|
|
||||||
class ListShapeEntry {
|
|
||||||
public:
|
|
||||||
void updateTransform(const glm::vec3& position, const glm::quat& rotation);
|
|
||||||
|
|
||||||
Shape* _shape;
|
|
||||||
glm::vec3 _localPosition;
|
|
||||||
glm::quat _localRotation;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ListShape : public Shape {
|
|
||||||
public:
|
|
||||||
|
|
||||||
ListShape() : Shape(LIST_SHAPE), _subShapeEntries(), _subShapeTransformsAreDirty(false) {}
|
|
||||||
|
|
||||||
ListShape(const glm::vec3& position, const glm::quat& rotation) :
|
|
||||||
Shape(LIST_SHAPE, position, rotation), _subShapeEntries(), _subShapeTransformsAreDirty(false) {}
|
|
||||||
|
|
||||||
~ListShape();
|
|
||||||
|
|
||||||
void setTranslation(const glm::vec3& position);
|
|
||||||
void setRotation(const glm::quat& rotation);
|
|
||||||
|
|
||||||
const Shape* getSubShape(int index) const;
|
|
||||||
|
|
||||||
void updateSubTransforms();
|
|
||||||
|
|
||||||
int size() const { return _subShapeEntries.size(); }
|
|
||||||
|
|
||||||
void addShape(Shape* shape, const glm::vec3& localPosition, const glm::quat& localRotation);
|
|
||||||
|
|
||||||
void setShapes(QVector<ListShapeEntry>& shapes);
|
|
||||||
|
|
||||||
// TODO: either implement this or remove ListShape altogether
|
|
||||||
bool findRayIntersection(RayIntersectionInfo& intersection) const { return false; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void clear();
|
|
||||||
void computeBoundingRadius();
|
|
||||||
|
|
||||||
QVector<ListShapeEntry> _subShapeEntries;
|
|
||||||
bool _subShapeTransformsAreDirty;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ListShape(const ListShape& otherList); // don't implement this
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_ListShape_h
|
|
|
@ -1,78 +0,0 @@
|
||||||
//
|
|
||||||
// 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 "GLMHelpers.h"
|
|
||||||
#include "NumericalConstants.h"
|
|
||||||
#include "PlaneShape.h"
|
|
||||||
|
|
||||||
const glm::vec3 UNROTATED_NORMAL(0.0f, 1.0f, 0.0f);
|
|
||||||
|
|
||||||
PlaneShape::PlaneShape(const glm::vec4& coefficients) :
|
|
||||||
Shape(PLANE_SHAPE) {
|
|
||||||
|
|
||||||
glm::vec3 normal = glm::vec3(coefficients);
|
|
||||||
_translation = -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::vec3 PlaneShape::getNormal() const {
|
|
||||||
return _rotation * UNROTATED_NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlaneShape::setNormal(const glm::vec3& direction) {
|
|
||||||
glm::vec3 oldTranslation = _translation;
|
|
||||||
_rotation = rotationBetween(UNROTATED_NORMAL, direction);
|
|
||||||
glm::vec3 normal = getNormal();
|
|
||||||
_translation = glm::dot(oldTranslation, normal) * normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlaneShape::setPoint(const glm::vec3& point) {
|
|
||||||
glm::vec3 normal = getNormal();
|
|
||||||
_translation = glm::dot(point, 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, _translation));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlaneShape::findRayIntersection(RayIntersectionInfo& intersection) const {
|
|
||||||
glm::vec3 n = getNormal();
|
|
||||||
float denominator = glm::dot(n, intersection._rayDirection);
|
|
||||||
if (fabsf(denominator) < EPSILON) {
|
|
||||||
// line is parallel to plane
|
|
||||||
if (glm::dot(_translation - intersection._rayStart, n) < EPSILON) {
|
|
||||||
// ray starts on the plane
|
|
||||||
intersection._hitDistance = 0.0f;
|
|
||||||
intersection._hitNormal = n;
|
|
||||||
intersection._hitShape = const_cast<PlaneShape*>(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
float d = glm::dot(_translation - intersection._rayStart, n) / denominator;
|
|
||||||
if (d > 0.0f && d < intersection._rayLength && d < intersection._hitDistance) {
|
|
||||||
// ray points toward plane
|
|
||||||
intersection._hitDistance = d;
|
|
||||||
intersection._hitNormal = n;
|
|
||||||
intersection._hitShape = const_cast<PlaneShape*>(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
//
|
|
||||||
// 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::vec3 getNormal() const;
|
|
||||||
glm::vec4 getCoefficients() const;
|
|
||||||
|
|
||||||
void setNormal(const glm::vec3& normal);
|
|
||||||
void setPoint(const glm::vec3& point);
|
|
||||||
|
|
||||||
bool findRayIntersection(RayIntersectionInfo& intersection) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_PlaneShape_h
|
|
|
@ -1,37 +0,0 @@
|
||||||
//
|
|
||||||
// RayIntersectionInfo.h
|
|
||||||
// libraries/physcis/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows 2014.09.09
|
|
||||||
// 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_RayIntersectionInfo_h
|
|
||||||
#define hifi_RayIntersectionInfo_h
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
class Shape;
|
|
||||||
|
|
||||||
class RayIntersectionInfo {
|
|
||||||
public:
|
|
||||||
RayIntersectionInfo() : _rayStart(0.0f), _rayDirection(1.0f, 0.0f, 0.0f), _rayLength(FLT_MAX),
|
|
||||||
_hitDistance(FLT_MAX), _hitNormal(1.0f, 0.0f, 0.0f), _hitShape(NULL) { }
|
|
||||||
|
|
||||||
glm::vec3 getIntersectionPoint() const { return _rayStart + _hitDistance * _rayDirection; }
|
|
||||||
|
|
||||||
// input
|
|
||||||
glm::vec3 _rayStart;
|
|
||||||
glm::vec3 _rayDirection;
|
|
||||||
float _rayLength;
|
|
||||||
|
|
||||||
// output
|
|
||||||
float _hitDistance;
|
|
||||||
glm::vec3 _hitNormal;
|
|
||||||
Shape* _hitShape;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // hifi_RayIntersectionInfo_h
|
|
|
@ -1,144 +0,0 @@
|
||||||
//
|
|
||||||
// Shape.h
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 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_Shape_h
|
|
||||||
#define hifi_Shape_h
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QtGlobal>
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include "RayIntersectionInfo.h"
|
|
||||||
|
|
||||||
class PhysicsEntity;
|
|
||||||
|
|
||||||
const float MAX_SHAPE_MASS = 1.0e18f; // something less than sqrt(FLT_MAX)
|
|
||||||
|
|
||||||
// DANGER: until ShapeCollider goes away the order of these values matter. Specifically,
|
|
||||||
// UNKNOWN_SHAPE must be equal to the number of shapes that ShapeCollider actually supports.
|
|
||||||
const quint8 SPHERE_SHAPE = 0;
|
|
||||||
const quint8 CAPSULE_SHAPE = 1;
|
|
||||||
const quint8 PLANE_SHAPE = 2;
|
|
||||||
const quint8 AACUBE_SHAPE = 3;
|
|
||||||
const quint8 LIST_SHAPE = 4;
|
|
||||||
const quint8 UNKNOWN_SHAPE = 5;
|
|
||||||
const quint8 INVALID_SHAPE = 5;
|
|
||||||
|
|
||||||
// new shapes to be supported by Bullet
|
|
||||||
const quint8 BOX_SHAPE = 7;
|
|
||||||
const quint8 CYLINDER_SHAPE = 8;
|
|
||||||
|
|
||||||
class Shape {
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef quint8 Type;
|
|
||||||
|
|
||||||
static quint32 getNextID() { static quint32 nextID = 0; return ++nextID; }
|
|
||||||
|
|
||||||
Shape() : _type(UNKNOWN_SHAPE), _owningEntity(NULL), _boundingRadius(0.0f),
|
|
||||||
_translation(0.0f), _rotation(), _mass(MAX_SHAPE_MASS) {
|
|
||||||
_id = getNextID();
|
|
||||||
}
|
|
||||||
virtual ~Shape() { }
|
|
||||||
|
|
||||||
Type getType() const { return _type; }
|
|
||||||
quint32 getID() const { return _id; }
|
|
||||||
|
|
||||||
void setEntity(PhysicsEntity* entity) { _owningEntity = entity; }
|
|
||||||
PhysicsEntity* getEntity() const { return _owningEntity; }
|
|
||||||
|
|
||||||
float getBoundingRadius() const { return _boundingRadius; }
|
|
||||||
|
|
||||||
virtual const glm::quat& getRotation() const { return _rotation; }
|
|
||||||
virtual void setRotation(const glm::quat& rotation) { _rotation = rotation; }
|
|
||||||
|
|
||||||
virtual void setTranslation(const glm::vec3& translation) { _translation = translation; }
|
|
||||||
virtual const glm::vec3& getTranslation() const { return _translation; }
|
|
||||||
|
|
||||||
virtual void setMass(float mass) { _mass = mass; }
|
|
||||||
virtual float getMass() const { return _mass; }
|
|
||||||
|
|
||||||
virtual bool findRayIntersection(RayIntersectionInfo& intersection) const = 0;
|
|
||||||
|
|
||||||
/// \param penetration of collision
|
|
||||||
/// \param contactPoint of collision
|
|
||||||
/// \return the effective mass for the collision
|
|
||||||
/// For most shapes has side effects: computes and caches the partial Lagrangian coefficients which will be
|
|
||||||
/// used in the next accumulateDelta() call.
|
|
||||||
virtual float computeEffectiveMass(const glm::vec3& penetration, const glm::vec3& contactPoint) { return _mass; }
|
|
||||||
|
|
||||||
/// \param relativeMassFactor the final ingredient for partial Lagrangian coefficients from computeEffectiveMass()
|
|
||||||
/// \param penetration the delta movement
|
|
||||||
virtual void accumulateDelta(float relativeMassFactor, const glm::vec3& penetration) {}
|
|
||||||
|
|
||||||
virtual void applyAccumulatedDelta() {}
|
|
||||||
|
|
||||||
/// \return volume of shape in cubic meters
|
|
||||||
virtual float getVolume() const { return 1.0; }
|
|
||||||
|
|
||||||
virtual QDebug& dumpToDebug(QDebug& debugConext) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// these ctors are protected (used by derived classes only)
|
|
||||||
Shape(Type type) : _type(type), _owningEntity(NULL),
|
|
||||||
_boundingRadius(0.0f), _translation(0.0f),
|
|
||||||
_rotation(), _mass(MAX_SHAPE_MASS) {
|
|
||||||
_id = getNextID();
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape(Type type, const glm::vec3& position) :
|
|
||||||
_type(type), _owningEntity(NULL),
|
|
||||||
_boundingRadius(0.0f), _translation(position),
|
|
||||||
_rotation(), _mass(MAX_SHAPE_MASS) {
|
|
||||||
_id = getNextID();
|
|
||||||
}
|
|
||||||
|
|
||||||
Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL),
|
|
||||||
_boundingRadius(0.0f), _translation(position),
|
|
||||||
_rotation(rotation), _mass(MAX_SHAPE_MASS) {
|
|
||||||
_id = getNextID();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBoundingRadius(float radius) { _boundingRadius = radius; }
|
|
||||||
|
|
||||||
Type _type;
|
|
||||||
quint32 _id;
|
|
||||||
PhysicsEntity* _owningEntity;
|
|
||||||
float _boundingRadius;
|
|
||||||
glm::vec3 _translation;
|
|
||||||
glm::quat _rotation;
|
|
||||||
float _mass;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline QDebug& Shape::dumpToDebug(QDebug& debugConext) const {
|
|
||||||
debugConext << "Shape[ ("
|
|
||||||
<< "type: " << getType()
|
|
||||||
<< "position: "
|
|
||||||
<< getTranslation().x << ", " << getTranslation().y << ", " << getTranslation().z
|
|
||||||
<< "radius: "
|
|
||||||
<< getBoundingRadius()
|
|
||||||
<< "]";
|
|
||||||
|
|
||||||
return debugConext;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const Shape& shape) {
|
|
||||||
return shape.dumpToDebug(debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const Shape* shape) {
|
|
||||||
return shape->dumpToDebug(debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif // hifi_Shape_h
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,156 +0,0 @@
|
||||||
//
|
|
||||||
// ShapeCollider.h
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 02/20/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_ShapeCollider_h
|
|
||||||
#define hifi_ShapeCollider_h
|
|
||||||
|
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#include "CollisionInfo.h"
|
|
||||||
#include "RayIntersectionInfo.h"
|
|
||||||
#include "SharedUtil.h"
|
|
||||||
|
|
||||||
|
|
||||||
class Shape;
|
|
||||||
class SphereShape;
|
|
||||||
class CapsuleShape;
|
|
||||||
|
|
||||||
namespace ShapeCollider {
|
|
||||||
|
|
||||||
/// MUST CALL this FIRST before using the ShapeCollider
|
|
||||||
void initDispatchTable();
|
|
||||||
|
|
||||||
/// \param shapeA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param shapeB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param collisions[out] collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool collideShapes(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
|
|
||||||
|
|
||||||
bool collideShapeWithShapes(const Shape* shapeA, const QVector<Shape*>& shapes, int startIndex, CollisionList& collisions);
|
|
||||||
bool collideShapesWithShapes(const QVector<Shape*>& shapesA, const QVector<Shape*>& shapesB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param shapeA a pointer to a shape (cannot be NULL)
|
|
||||||
/// \param cubeCenter center of cube
|
|
||||||
/// \param cubeSide lenght of side of cube
|
|
||||||
/// \param collisions[out] average collision details
|
|
||||||
/// \return true if shapeA collides with axis aligned cube
|
|
||||||
bool collideShapeWithAACubeLegacy(const Shape* shapeA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param sphereA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param sphereB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool sphereVsSphere(const Shape* sphereA, const Shape* sphereB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param sphereA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param capsuleB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool sphereVsCapsule(const Shape* sphereA, const Shape* capsuleB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param sphereA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param planeB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool sphereVsPlane(const Shape* sphereA, const Shape* planeB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param capsuleA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param sphereB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool capsuleVsSphere(const Shape* capsuleA, const Shape* sphereB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param capsuleA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param capsuleB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool capsuleVsCapsule(const Shape* capsuleA, const Shape* capsuleB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param capsuleA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param planeB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool capsuleVsPlane(const Shape* capsuleA, const Shape* planeB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param planeA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param sphereB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool planeVsSphere(const Shape* planeA, const Shape* sphereB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param planeA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param capsuleB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool planeVsCapsule(const Shape* planeA, const Shape* capsuleB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param planeA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param planeB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool planeVsPlane(const Shape* planeA, const Shape* planeB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// helper function for *VsAACube() methods
|
|
||||||
/// \param sphereCenter center of sphere
|
|
||||||
/// \param sphereRadius radius of sphere
|
|
||||||
/// \param cubeCenter center of AACube
|
|
||||||
/// \param cubeSide scale of cube
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return valid pointer to CollisionInfo if sphere and cube overlap or NULL if not
|
|
||||||
CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereRadius,
|
|
||||||
const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
|
||||||
|
|
||||||
bool sphereVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
|
|
||||||
bool capsuleVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
|
|
||||||
bool aaCubeVsSphere(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
|
|
||||||
bool aaCubeVsCapsule(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
|
|
||||||
bool aaCubeVsAACube(const Shape* shapeA, const Shape* shapeB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param shapeA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param listB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool shapeVsList(const Shape* shapeA, const Shape* listB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param listA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param shapeB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool listVsShape(const Shape* listA, const Shape* shapeB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param listA pointer to first shape (cannot be NULL)
|
|
||||||
/// \param listB pointer to second shape (cannot be NULL)
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if shapes collide
|
|
||||||
bool listVsList(const Shape* listA, const Shape* listB, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param sphereA pointer to sphere (cannot be NULL)
|
|
||||||
/// \param cubeCenter center of cube
|
|
||||||
/// \param cubeSide lenght of side of cube
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if sphereA collides with axis aligned cube
|
|
||||||
bool sphereVsAACubeLegacy(const SphereShape* sphereA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param capsuleA pointer to capsule (cannot be NULL)
|
|
||||||
/// \param cubeCenter center of cube
|
|
||||||
/// \param cubeSide lenght of side of cube
|
|
||||||
/// \param[out] collisions where to append collision details
|
|
||||||
/// \return true if capsuleA collides with axis aligned cube
|
|
||||||
bool capsuleVsAACubeLegacy(const CapsuleShape* capsuleA, const glm::vec3& cubeCenter, float cubeSide, CollisionList& collisions);
|
|
||||||
|
|
||||||
/// \param shapes list of pointers to shapes (shape pointers may be NULL)
|
|
||||||
/// \param intersection[out] struct with info about Ray and hit
|
|
||||||
/// \return true if ray hits any shape in shapes
|
|
||||||
bool findRayIntersection(const QVector<Shape*>& shapes, RayIntersectionInfo& intersection);
|
|
||||||
|
|
||||||
} // namespace ShapeCollider
|
|
||||||
|
|
||||||
#endif // hifi_ShapeCollider_h
|
|
|
@ -1,51 +0,0 @@
|
||||||
//
|
|
||||||
// SphereShape.cpp
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 2014.06.17
|
|
||||||
// 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 <glm/gtx/norm.hpp>
|
|
||||||
|
|
||||||
#include "SphereShape.h"
|
|
||||||
|
|
||||||
bool SphereShape::findRayIntersection(RayIntersectionInfo& intersection) const {
|
|
||||||
float r2 = _boundingRadius * _boundingRadius;
|
|
||||||
|
|
||||||
// compute closest approach (CA)
|
|
||||||
float a = glm::dot(_translation - intersection._rayStart, intersection._rayDirection); // a = distance from ray-start to CA
|
|
||||||
float b2 = glm::distance2(_translation, intersection._rayStart + a * intersection._rayDirection); // b2 = squared distance from sphere-center to CA
|
|
||||||
if (b2 > r2) {
|
|
||||||
// ray does not hit sphere
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
float c = sqrtf(r2 - b2); // c = distance from CA to sphere surface along intersection._rayDirection
|
|
||||||
float d2 = glm::distance2(intersection._rayStart, _translation); // d2 = squared distance from sphere-center to ray-start
|
|
||||||
float distance = FLT_MAX;
|
|
||||||
if (a < 0.0f) {
|
|
||||||
// ray points away from sphere-center
|
|
||||||
if (d2 > r2) {
|
|
||||||
// ray starts outside sphere
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// ray starts inside sphere
|
|
||||||
distance = c + a;
|
|
||||||
} else if (d2 > r2) {
|
|
||||||
// ray starts outside sphere
|
|
||||||
distance = a - c;
|
|
||||||
} else {
|
|
||||||
// ray starts inside sphere
|
|
||||||
distance = a + c;
|
|
||||||
}
|
|
||||||
if (distance > 0.0f && distance < intersection._rayLength && distance < intersection._hitDistance) {
|
|
||||||
intersection._hitDistance = distance;
|
|
||||||
intersection._hitNormal = glm::normalize(intersection._rayStart + distance * intersection._rayDirection - _translation);
|
|
||||||
intersection._hitShape = const_cast<SphereShape*>(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
//
|
|
||||||
// SphereShape.h
|
|
||||||
// libraries/shared/src
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows on 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_SphereShape_h
|
|
||||||
#define hifi_SphereShape_h
|
|
||||||
|
|
||||||
#include "NumericalConstants.h"
|
|
||||||
#include "Shape.h"
|
|
||||||
|
|
||||||
class SphereShape : public Shape {
|
|
||||||
public:
|
|
||||||
SphereShape() : Shape(SPHERE_SHAPE) {}
|
|
||||||
|
|
||||||
SphereShape(float radius) : Shape(SPHERE_SHAPE) {
|
|
||||||
_boundingRadius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
SphereShape(float radius, const glm::vec3& position) : Shape(SPHERE_SHAPE, position) {
|
|
||||||
_boundingRadius = radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~SphereShape() {}
|
|
||||||
|
|
||||||
float getRadius() const { return _boundingRadius; }
|
|
||||||
|
|
||||||
void setRadius(float radius) { _boundingRadius = radius; }
|
|
||||||
|
|
||||||
bool findRayIntersection(RayIntersectionInfo& intersection) const;
|
|
||||||
|
|
||||||
float getVolume() const { return 1.3333333333f * PI * _boundingRadius * _boundingRadius * _boundingRadius; }
|
|
||||||
};
|
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const SphereShape& shape) {
|
|
||||||
debug << "SphereShape[ ("
|
|
||||||
<< "position: "
|
|
||||||
<< shape.getTranslation().x << ", " << shape.getTranslation().y << ", " << shape.getTranslation().z
|
|
||||||
<< "radius: "
|
|
||||||
<< shape.getRadius()
|
|
||||||
<< "]";
|
|
||||||
|
|
||||||
return debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const SphereShape* shape) {
|
|
||||||
debug << "SphereShape[ ("
|
|
||||||
<< "center: "
|
|
||||||
<< shape->getTranslation().x << ", " << shape->getTranslation().y << ", " << shape->getTranslation().z
|
|
||||||
<< "radius: "
|
|
||||||
<< shape->getRadius()
|
|
||||||
<< "]";
|
|
||||||
|
|
||||||
return debug;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // hifi_SphereShape_h
|
|
|
@ -65,38 +65,6 @@ QDataStream& operator>>(QDataStream& in, glm::quat& quaternion) {
|
||||||
return in >> quaternion.x >> quaternion.y >> quaternion.z >> quaternion.w;
|
return in >> quaternion.x >> quaternion.y >> quaternion.z >> quaternion.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
// less common utils can be enabled with DEBUG
|
|
||||||
// FIXME, remove the second defined clause once these compile, or remove the
|
|
||||||
// functions.
|
|
||||||
#if defined(DEBUG) && defined(FIXED_STREAMS)
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c) {
|
|
||||||
s << "{penetration=" << c._penetration
|
|
||||||
<< ", contactPoint=" << c._contactPoint
|
|
||||||
<< ", addedVelocity=" << c._addedVelocity
|
|
||||||
<< "}";
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& s, const SphereShape& sphere) {
|
|
||||||
s << "{type='sphere', center=" << sphere.getPosition()
|
|
||||||
<< ", radius=" << sphere.getRadius()
|
|
||||||
<< "}";
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule) {
|
|
||||||
s << "{type='capsule', center=" << capsule.getPosition()
|
|
||||||
<< ", radius=" << capsule.getRadius()
|
|
||||||
<< ", length=" << (2.0f * capsule.getHalfHeight())
|
|
||||||
<< ", begin=" << capsule.getStartPoint()
|
|
||||||
<< ", end=" << capsule.getEndPoint()
|
|
||||||
<< "}";
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // DEBUG
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
#ifndef QT_NO_DEBUG_STREAM
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
|
|
@ -37,16 +37,6 @@ QDataStream& operator>>(QDataStream& in, glm::vec3& vector);
|
||||||
QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion);
|
QDataStream& operator<<(QDataStream& out, const glm::quat& quaternion);
|
||||||
QDataStream& operator>>(QDataStream& in, glm::quat& quaternion);
|
QDataStream& operator>>(QDataStream& in, glm::quat& quaternion);
|
||||||
|
|
||||||
// less common utils can be enabled with DEBUG
|
|
||||||
#ifdef DEBUG
|
|
||||||
class CollisionInfo;
|
|
||||||
class SphereShape;
|
|
||||||
class CapsuleShape;
|
|
||||||
std::ostream& operator<<(std::ostream& s, const CollisionInfo& c);
|
|
||||||
std::ostream& operator<<(std::ostream& s, const SphereShape& shape);
|
|
||||||
std::ostream& operator<<(std::ostream& s, const CapsuleShape& capsule);
|
|
||||||
#endif // DEBUG
|
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG_STREAM
|
#ifndef QT_NO_DEBUG_STREAM
|
||||||
class QDebug;
|
class QDebug;
|
||||||
// Add support for writing these to qDebug().
|
// Add support for writing these to qDebug().
|
||||||
|
|
160
tests/shared/src/GeometryUtilTests.cpp
Normal file
160
tests/shared/src/GeometryUtilTests.cpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
//
|
||||||
|
// GeometryUtilTests.cpp
|
||||||
|
// tests/physics/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows on 2015.07.27
|
||||||
|
// Copyright 2015 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 <iostream>
|
||||||
|
|
||||||
|
#include "GeometryUtilTests.h"
|
||||||
|
|
||||||
|
#include <GeometryUtil.h>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
|
#include <../QTestExtensions.h>
|
||||||
|
|
||||||
|
|
||||||
|
QTEST_MAIN(GeometryUtilTests)
|
||||||
|
|
||||||
|
float getErrorDifference(const float& a, const float& b) {
|
||||||
|
return fabsf(a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getErrorDifference(const glm::vec3& a, const glm::vec3& b) {
|
||||||
|
return glm::distance(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeometryUtilTests::testLocalRayRectangleIntersection() {
|
||||||
|
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||||
|
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||||
|
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// create a rectangle in the local frame on the XY plane with normal along -zAxis
|
||||||
|
// (this is the assumed local orientation of the rectangle in findRayRectangleIntersection())
|
||||||
|
glm::vec2 rectDimensions(3.0f, 5.0f);
|
||||||
|
glm::vec3 rectCenter(0.0f, 0.0f, 0.0f);
|
||||||
|
glm::quat rectRotation = glm::quat(); // identity
|
||||||
|
|
||||||
|
// create points for generating rays that hit the plane and don't
|
||||||
|
glm::vec3 rayStart(1.0f, 2.0f, 3.0f);
|
||||||
|
float delta = 0.1f;
|
||||||
|
|
||||||
|
{ // verify hit
|
||||||
|
glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.x - delta) * xAxis);
|
||||||
|
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
|
||||||
|
float expectedDistance = glm::length(rayEnd - rayStart);
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
|
||||||
|
QCOMPARE(hit, true);
|
||||||
|
QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // verify miss
|
||||||
|
glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.y + delta) * yAxis);
|
||||||
|
glm::vec3 rayMissDirection = glm::normalize(rayEnd - rayStart);
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
bool hit = findRayRectangleIntersection(rayStart, rayMissDirection, rectRotation, rectCenter, rectDimensions, distance);
|
||||||
|
QCOMPARE(hit, false);
|
||||||
|
QCOMPARE(distance, FLT_MAX); // distance should be unchanged
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // hit with co-planer line
|
||||||
|
float yFraction = 0.25f;
|
||||||
|
rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + yFraction * rectDimensions.y * yAxis);
|
||||||
|
|
||||||
|
glm::vec3 rayEnd = rectCenter - rectRotation * (rectDimensions.x * xAxis + yFraction * rectDimensions.y * yAxis);
|
||||||
|
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
|
||||||
|
float expectedDistance = rectDimensions.x;
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
|
||||||
|
QCOMPARE(hit, true);
|
||||||
|
QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // miss with co-planer line
|
||||||
|
float yFraction = 0.75f;
|
||||||
|
rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
|
||||||
|
|
||||||
|
glm::vec3 rayEnd = rectCenter + rectRotation * (- rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
|
||||||
|
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
|
||||||
|
QCOMPARE(hit, false);
|
||||||
|
QCOMPARE(distance, FLT_MAX); // distance should be unchanged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GeometryUtilTests::testWorldRayRectangleIntersection() {
|
||||||
|
glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
|
||||||
|
glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
|
||||||
|
glm::vec3 zAxis(0.0f, 0.0f, 1.0f);
|
||||||
|
|
||||||
|
// create a rectangle in the local frame on the XY plane with normal along -zAxis
|
||||||
|
// (this is the assumed local orientation of the rectangle in findRayRectangleIntersection())
|
||||||
|
// and then rotate and shift everything into the world frame
|
||||||
|
glm::vec2 rectDimensions(3.0f, 5.0f);
|
||||||
|
glm::vec3 rectCenter(0.7f, 0.5f, -0.3f);
|
||||||
|
glm::quat rectRotation = glm::quat(); // identity
|
||||||
|
|
||||||
|
|
||||||
|
// create points for generating rays that hit the plane and don't
|
||||||
|
glm::vec3 rayStart(1.0f, 2.0f, 3.0f);
|
||||||
|
float delta = 0.1f;
|
||||||
|
|
||||||
|
{ // verify hit
|
||||||
|
glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.x - delta) * xAxis);
|
||||||
|
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
|
||||||
|
float expectedDistance = glm::length(rayEnd - rayStart);
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
|
||||||
|
QCOMPARE(hit, true);
|
||||||
|
QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // verify miss
|
||||||
|
glm::vec3 rayEnd = rectCenter + rectRotation * ((0.5f * rectDimensions.y + delta) * yAxis);
|
||||||
|
glm::vec3 rayMissDirection = glm::normalize(rayEnd - rayStart);
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
bool hit = findRayRectangleIntersection(rayStart, rayMissDirection, rectRotation, rectCenter, rectDimensions, distance);
|
||||||
|
QCOMPARE(hit, false);
|
||||||
|
QCOMPARE(distance, FLT_MAX); // distance should be unchanged
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // hit with co-planer line
|
||||||
|
float yFraction = 0.25f;
|
||||||
|
rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
|
||||||
|
|
||||||
|
glm::vec3 rayEnd = rectCenter - rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
|
||||||
|
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
|
||||||
|
float expectedDistance = rectDimensions.x;
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
|
||||||
|
QCOMPARE(hit, true);
|
||||||
|
QCOMPARE_WITH_ABS_ERROR(distance, expectedDistance, EPSILON);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // miss with co-planer line
|
||||||
|
float yFraction = 0.75f;
|
||||||
|
rayStart = rectCenter + rectRotation * (rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
|
||||||
|
|
||||||
|
glm::vec3 rayEnd = rectCenter + rectRotation * (-rectDimensions.x * xAxis + (yFraction * rectDimensions.y) * yAxis);
|
||||||
|
glm::vec3 rayHitDirection = glm::normalize(rayEnd - rayStart);
|
||||||
|
|
||||||
|
float distance = FLT_MAX;
|
||||||
|
bool hit = findRayRectangleIntersection(rayStart, rayHitDirection, rectRotation, rectCenter, rectDimensions, distance);
|
||||||
|
QCOMPARE(hit, false);
|
||||||
|
QCOMPARE(distance, FLT_MAX); // distance should be unchanged
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
28
tests/shared/src/GeometryUtilTests.h
Normal file
28
tests/shared/src/GeometryUtilTests.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// GeometryUtilTests.h
|
||||||
|
// tests/physics/src
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows on 2014.05.30
|
||||||
|
// 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_AngularConstraintTests_h
|
||||||
|
#define hifi_AngularConstraintTests_h
|
||||||
|
|
||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
class GeometryUtilTests : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void testLocalRayRectangleIntersection();
|
||||||
|
void testWorldRayRectangleIntersection();
|
||||||
|
};
|
||||||
|
|
||||||
|
float getErrorDifference(const float& a, const float& b);
|
||||||
|
float getErrorDifference(const glm::vec3& a, const glm::vec3& b);
|
||||||
|
|
||||||
|
#endif // hifi_AngularConstraintTests_h
|
Loading…
Reference in a new issue