mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +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
|
||||
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
|
||||
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::VelocityFilter)->setVisible(isUsingDDE);
|
||||
Menu::getInstance()->getActionForOption(MenuOption::CalibrateCamera)->setVisible(isUsingDDE);
|
||||
|
|
|
@ -421,6 +421,8 @@ Menu::Menu() {
|
|||
faceTrackingMenu->addSeparator();
|
||||
QAction* binaryEyelidControl = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::BinaryEyelidControl, 0, true);
|
||||
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);
|
||||
useAudioForMouth->setVisible(true); // DDE face tracking is on by default
|
||||
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
|
||||
|
|
|
@ -165,6 +165,7 @@ namespace MenuOption {
|
|||
const QString ControlWithSpeech = "Control With Speech";
|
||||
const QString CopyAddress = "Copy Address to Clipboard";
|
||||
const QString CopyPath = "Copy Path to Clipboard";
|
||||
const QString CoupleEyelids = "Couple Eyelids";
|
||||
const QString DebugAmbientOcclusion = "Debug Ambient Occlusion";
|
||||
const QString DecreaseAvatarSize = "Decrease Avatar Size";
|
||||
const QString DeleteBookmark = "Delete Bookmark...";
|
||||
|
|
|
@ -936,8 +936,14 @@ void Avatar::setFaceModelURL(const QUrl& faceModelURL) {
|
|||
|
||||
void Avatar::setSkeletonModelURL(const QUrl& 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");
|
||||
_skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL, true, !isMyAvatar());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
|
|
|
@ -150,6 +150,7 @@ public:
|
|||
Q_INVOKABLE glm::vec3 getAngularVelocity() const { return _angularVelocity; }
|
||||
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
|
||||
/// \param vector position to be scaled. Will store the result
|
||||
|
@ -169,9 +170,6 @@ public:
|
|||
void setMotionState(AvatarMotionState* motionState) { _motionState = motionState; }
|
||||
AvatarMotionState* getMotionState() { return _motionState; }
|
||||
|
||||
signals:
|
||||
void collisionWithAvatar(const QUuid& myUUID, const QUuid& theirUUID, const CollisionInfo& collision);
|
||||
|
||||
protected:
|
||||
SkeletonModel _skeletonModel;
|
||||
glm::vec3 _skeletonOffset;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <udt/PacketHeaders.h>
|
||||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <ShapeCollider.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <TextRenderer3D.h>
|
||||
#include <UserActivityLogger.h>
|
||||
|
@ -104,7 +103,6 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
|||
_rig(rig),
|
||||
_prevShouldDrawHead(true)
|
||||
{
|
||||
ShapeCollider::initDispatchTable();
|
||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||
_driveKeys[i] = 0.0f;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ public:
|
|||
Q_INVOKABLE void useHeadAndBodyURLs(const QUrl& headURL, const QUrl& bodyURL,
|
||||
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& getHeadURLFromPreferences() const { return _headURLFromPreferences; }
|
||||
Q_INVOKABLE const QUrl& getBodyURLFromPreferences() const { return _skeletonURLFromPreferences; }
|
||||
|
|
|
@ -42,7 +42,23 @@ SkeletonModel::~SkeletonModel() {
|
|||
void SkeletonModel::initJointStates(QVector<JointState> states) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
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
|
||||
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;
|
||||
|
||||
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)) {
|
||||
float forearmLength = geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 {
|
||||
return getJointPositionInWorldFrame(getLeftHandJointIndex(), position);
|
||||
}
|
||||
|
|
|
@ -131,11 +131,6 @@ private:
|
|||
QHash<int, OrientationLineIDs> _jointOrientationLines;
|
||||
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;
|
||||
|
||||
Avatar* _owningAvatar;
|
||||
|
|
|
@ -564,6 +564,13 @@ void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
|
|||
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
|
||||
if (eyeCoefficients[0] > 0) {
|
||||
_coefficients[_leftBlinkIndex] = eyeCoefficients[0];
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
ControllerScriptingInterface::ControllerScriptingInterface() :
|
||||
_mouseCaptured(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();
|
||||
|
||||
// compute model transforms
|
||||
int parentIndex = joint.parentIndex;
|
||||
if (parentIndex == -1) {
|
||||
if (index == _rootJointIndex) {
|
||||
// we always zero-out the translation part of an avatar's root join-transform.
|
||||
state.computeTransform(parentTransform);
|
||||
clearJointTransformTranslation(index);
|
||||
} else {
|
||||
// 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);
|
||||
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:
|
||||
~AvatarRig() {}
|
||||
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
|
||||
|
|
|
@ -22,6 +22,8 @@ class EntityRig : public Rig {
|
|||
public:
|
||||
~EntityRig() {}
|
||||
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
|
||||
|
|
|
@ -58,15 +58,18 @@ void Rig::removeAnimationHandle(const AnimationHandlePointer& handle) {
|
|||
|
||||
void Rig::startAnimation(const QString& url, float fps, float priority,
|
||||
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) {
|
||||
if (candidate->getURL() == url) {
|
||||
candidate->start();
|
||||
return;
|
||||
handle = candidate;
|
||||
}
|
||||
}
|
||||
AnimationHandlePointer handle = createAnimationHandle();
|
||||
handle->setURL(url);
|
||||
if (!handle) {
|
||||
handle = createAnimationHandle();
|
||||
handle->setURL(url);
|
||||
}
|
||||
handle->setFPS(fps);
|
||||
handle->setPriority(priority);
|
||||
handle->setLoop(loop);
|
||||
|
@ -90,8 +93,34 @@ void Rig::addAnimationByRole(const QString& role, const QString& url, float fps,
|
|||
}
|
||||
}
|
||||
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->setURL(url);
|
||||
handle->setURL(url.isEmpty() ? standard : url);
|
||||
handle->setFPS(fps);
|
||||
handle->setPriority(priority);
|
||||
handle->setLoop(loop);
|
||||
|
@ -135,6 +164,14 @@ void Rig::addRunningAnimation(AnimationHandlePointer animationHandle) {
|
|||
bool Rig::isRunningAnimation(AnimationHandlePointer 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() {
|
||||
for (auto animation : _animationHandles) {
|
||||
|
@ -143,8 +180,24 @@ void Rig::deleteAnimations() {
|
|||
_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;
|
||||
|
||||
_rootJointIndex = rootJointIndex;
|
||||
_leftHandJointIndex = leftHandJointIndex;
|
||||
_leftElbowJointIndex = leftElbowJointIndex;
|
||||
_leftShoulderJointIndex = leftShoulderJointIndex;
|
||||
_rightHandJointIndex = rightHandJointIndex;
|
||||
_rightElbowJointIndex = rightElbowJointIndex;
|
||||
_rightShoulderJointIndex = rightShoulderJointIndex;
|
||||
|
||||
initJointTransforms(parentTransform);
|
||||
|
||||
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) {
|
||||
if (_enableRig) {
|
||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||
float forwardSpeed = glm::dot(worldVelocity, front);
|
||||
float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime;
|
||||
bool isWalking = std::abs(forwardSpeed) > 0.01f;
|
||||
bool isTurning = std::abs(rotationalSpeed) > 0.5f;
|
||||
|
||||
// Crude, until we have blending:
|
||||
isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins.
|
||||
isTurning = false; // FIXME
|
||||
bool isIdle = !isWalking && !isTurning;
|
||||
auto singleRole = [](bool walking, bool turning, bool idling) {
|
||||
return walking ? "walk" : (turning ? "turn" : (idling ? "idle" : ""));
|
||||
};
|
||||
QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle);
|
||||
if (!toStop.isEmpty()) {
|
||||
//qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed;
|
||||
stopAnimationByRole(toStop);
|
||||
}
|
||||
QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle);
|
||||
if (!newRole.isEmpty()) {
|
||||
startAnimationByRole(newRole);
|
||||
qCDebug(animation) << deltaTime << ":" << worldVelocity << "." << front << "=> " << forwardSpeed << newRole;
|
||||
}
|
||||
|
||||
_lastPosition = worldPosition;
|
||||
_lastFront = front;
|
||||
_isWalking = isWalking;
|
||||
_isTurning = isTurning;
|
||||
_isIdle = isIdle;
|
||||
if (!_enableRig) {
|
||||
return;
|
||||
}
|
||||
bool isMoving = false;
|
||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||
float forwardSpeed = glm::dot(worldVelocity, front);
|
||||
float rightLateralSpeed = glm::dot(worldVelocity, worldRotation * IDENTITY_RIGHT);
|
||||
float rightTurningSpeed = glm::orientedAngle(front, _lastFront, IDENTITY_UP) / deltaTime;
|
||||
auto updateRole = [&](const QString& role, bool isOn) {
|
||||
isMoving = isMoving || isOn;
|
||||
if (isOn) {
|
||||
if (!isRunningRole(role)) {
|
||||
qCDebug(animation) << "Rig STARTING" << role;
|
||||
startAnimationByRole(role);
|
||||
}
|
||||
} else {
|
||||
if (isRunningRole(role)) {
|
||||
qCDebug(animation) << "Rig stopping" << role;
|
||||
stopAnimationByRole(role);
|
||||
}
|
||||
}
|
||||
};
|
||||
updateRole("walk", std::abs(forwardSpeed) > 0.01f);
|
||||
bool isTurning = std::abs(rightTurningSpeed) > 0.5f;
|
||||
updateRole("rightTurn", isTurning && (rightTurningSpeed > 0));
|
||||
updateRole("leftTurn", isTurning && (rightTurningSpeed < 0));
|
||||
bool isStrafing = std::abs(rightLateralSpeed) > 0.01f;
|
||||
updateRole("rightStrafe", isStrafing && (rightLateralSpeed > 0.0f));
|
||||
updateRole("leftStrafe", isStrafing && (rightLateralSpeed < 0.0f));
|
||||
updateRole("idle", !isMoving); // Must be last, as it makes isMoving bogus.
|
||||
_lastFront = front;
|
||||
_lastPosition = worldPosition;
|
||||
}
|
||||
|
||||
void Rig::updateAnimations(float deltaTime, glm::mat4 parentTransform) {
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
|
||||
class AnimationHandle;
|
||||
typedef std::shared_ptr<AnimationHandle> AnimationHandlePointer;
|
||||
// typedef QWeakPointer<AnimationHandle> WeakAnimationHandlePointer;
|
||||
|
||||
class Rig;
|
||||
typedef std::shared_ptr<Rig> RigPointer;
|
||||
|
@ -76,6 +75,7 @@ public:
|
|||
bool removeRunningAnimation(AnimationHandlePointer animationHandle);
|
||||
void addRunningAnimation(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; }
|
||||
void deleteAnimations();
|
||||
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 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(); };
|
||||
int getJointStateCount() const { return _jointStates.size(); }
|
||||
int indexOfJoint(const QString& jointName) ;
|
||||
|
@ -149,6 +156,9 @@ public:
|
|||
|
||||
void updateFromHeadParameters(const HeadParameters& params);
|
||||
|
||||
virtual void setHandPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation,
|
||||
float scale, float priority) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
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);
|
||||
|
||||
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> _runningAnimations;
|
||||
|
||||
bool _enableRig;
|
||||
bool _isWalking;
|
||||
bool _isTurning;
|
||||
bool _isIdle;
|
||||
glm::vec3 _lastFront;
|
||||
glm::vec3 _lastPosition;
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Rig__) */
|
||||
|
|
|
@ -46,7 +46,6 @@ typedef unsigned long long quint64;
|
|||
#include <QtScript/QScriptable>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
#include <CollisionInfo.h>
|
||||
#include <NLPacket.h>
|
||||
#include <Node.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
@ -257,10 +256,6 @@ public:
|
|||
const HeadData* getHeadData() const { return _headData; }
|
||||
const HandData* getHandData() const { return _handData; }
|
||||
|
||||
virtual bool findSphereCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasIdentityChangedAfterParsing(NLPacket& packet);
|
||||
QByteArray identityByteArray();
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include <glm/glm.hpp>
|
||||
|
||||
#include <AnimationCache.h> // for Animation, AnimationCache, and AnimationPointer classes
|
||||
#include <CollisionInfo.h>
|
||||
#include <Octree.h> // for EncodeBitstreamParams class
|
||||
#include <OctreeElement.h> // for OctreeElement::AppendState
|
||||
#include <OctreePacketData.h>
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
#include <CollisionInfo.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <Octree.h>
|
||||
#include <OctreeScriptingInterface.h>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <PlaneShape.h>
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "EntityTree.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& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
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 registrationPoint = getRegistrationPoint();
|
||||
glm::vec3 corner = -(dimensions * registrationPoint);
|
||||
AABox entityFrameBox(corner, dimensions);
|
||||
|
||||
glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
|
||||
|
||||
intersects = entityFrameBox.contains(entityFrameHitAt);
|
||||
}
|
||||
|
||||
if (intersects) {
|
||||
distance = rayInfo._hitDistance;
|
||||
}
|
||||
return intersects;
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::vec2 xyDimensions(dimensions.x, dimensions.y);
|
||||
glm::quat rotation = getRotation();
|
||||
glm::vec3 position = getPosition() + rotation *
|
||||
(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
|
||||
return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <PlaneShape.h>
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include "EntityTree.h"
|
||||
#include "EntityTreeElement.h"
|
||||
|
@ -98,50 +98,17 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
|
|||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, _sourceUrl);
|
||||
}
|
||||
|
||||
|
||||
bool WebEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
|
||||
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 registrationPoint = getRegistrationPoint();
|
||||
glm::vec3 corner = -(dimensions * registrationPoint);
|
||||
AABox entityFrameBox(corner, dimensions);
|
||||
|
||||
glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f));
|
||||
|
||||
intersects = entityFrameBox.contains(entityFrameHitAt);
|
||||
}
|
||||
|
||||
if (intersects) {
|
||||
distance = rayInfo._hitDistance;
|
||||
}
|
||||
return intersects;
|
||||
glm::vec3 dimensions = getDimensions();
|
||||
glm::vec2 xyDimensions(dimensions.x, dimensions.y);
|
||||
glm::quat rotation = getRotation();
|
||||
glm::vec3 position = getPosition() + rotation *
|
||||
(dimensions * (getRegistrationPoint() - ENTITY_ITEM_DEFAULT_REGISTRATION_POINT));
|
||||
return findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance);
|
||||
}
|
||||
|
||||
|
||||
void WebEntityItem::setSourceUrl(const QString& value) {
|
||||
if (_sourceUrl != value) {
|
||||
_sourceUrl = value;
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <NumericalConstants.h>
|
||||
#include <OctalCode.h>
|
||||
#include <Shape.h>
|
||||
#include <gpu/Format.h>
|
||||
#include <LogHandler.h>
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <NetworkAccessManager.h>
|
||||
#include "FBXReader.h"
|
||||
#include "OBJReader.h"
|
||||
#include "Shape.h"
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include <OctalCode.h>
|
||||
#include <udt/PacketHeaders.h>
|
||||
#include <SharedUtil.h>
|
||||
#include <Shape.h>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "CoverageMap.h"
|
||||
|
@ -791,13 +790,6 @@ public:
|
|||
bool found;
|
||||
};
|
||||
|
||||
class ShapeArgs {
|
||||
public:
|
||||
const Shape* shape;
|
||||
CollisionList& collisions;
|
||||
bool found;
|
||||
};
|
||||
|
||||
class ContentArgs {
|
||||
public:
|
||||
AACube cube;
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <LogHandler.h>
|
||||
#include <NodeList.h>
|
||||
#include <PerfStat.h>
|
||||
#include <AACubeShape.h>
|
||||
|
||||
#include "AACube.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
|
||||
glm::vec3 ourCenter = _cube.calcCenter();
|
||||
|
||||
int childIndex = CHILD_UNKNOWN;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
int childIndex = getMyChildContainingPoint(glm::vec3(x, y, z));
|
||||
|
||||
// Now, check if we have a child at that location
|
||||
child = getChildAtIndex(childIndex);
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "ViewFrustum.h"
|
||||
#include "OctreeConstants.h"
|
||||
|
||||
class CollisionList;
|
||||
class EncodeBitstreamParams;
|
||||
class Octree;
|
||||
class OctreeElement;
|
||||
|
|
|
@ -16,12 +16,9 @@
|
|||
#include <glm/gtx/transform.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include <CapsuleShape.h>
|
||||
#include <GeometryUtil.h>
|
||||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include <ShapeCollider.h>
|
||||
#include <SphereShape.h>
|
||||
#include <ViewFrustum.h>
|
||||
#include <render/Scene.h>
|
||||
#include <gpu/Batch.h>
|
||||
|
@ -476,7 +473,23 @@ bool Model::updateGeometry() {
|
|||
void Model::initJointStates(QVector<JointState> states) {
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
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,
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include <CollisionInfo.h>
|
||||
#include <RayIntersectionInfo.h>
|
||||
|
||||
class PhysicsEntity {
|
||||
|
||||
public:
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include <AudioConstants.h>
|
||||
#include <AudioEffectOptions.h>
|
||||
#include <AvatarData.h>
|
||||
#include <CollisionInfo.h>
|
||||
#include <EntityScriptingInterface.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <NodeList.h>
|
||||
|
@ -366,8 +365,6 @@ void ScriptEngine::init() {
|
|||
|
||||
// constants
|
||||
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) {
|
||||
|
|
|
@ -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 <cmath>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
#include "GeometryUtil.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) {
|
||||
// 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,
|
||||
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);
|
||||
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) {
|
||||
distance = rayInfo._hitDistance;
|
||||
|
||||
glm::vec3 hitPosition = origin + (distance * direction);
|
||||
glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - position);
|
||||
|
||||
glm::vec2 halfDimensions = 0.5f * dimensions;
|
||||
|
||||
intersects = -halfDimensions.x <= localHitPosition.x && localHitPosition.x <= halfDimensions.x
|
||||
&& -halfDimensions.y <= localHitPosition.y && localHitPosition.y <= halfDimensions.y;
|
||||
// compute distance to closest approach
|
||||
d = - glm::dot(offset, direction); // distance to closest approach of center of rectangle
|
||||
if (d < 0.0f) {
|
||||
// ray points away from center of rectangle, so ray's start is the closest approach
|
||||
d = 0.0f;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
d = - normDotOffset / denominator;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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
|
||||
#include <QDebug>
|
||||
|
||||
|
|
|
@ -37,16 +37,6 @@ QDataStream& operator>>(QDataStream& in, glm::vec3& vector);
|
|||
QDataStream& operator<<(QDataStream& out, const 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
|
||||
class 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