mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 03:44:02 +02:00
Merge branch 'master' of https://github.com/worklist/hifi into 19494
This commit is contained in:
commit
f002cda1de
21 changed files with 384 additions and 126 deletions
|
@ -69,6 +69,12 @@ function checkCamera() {
|
|||
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
|
||||
yaw -= viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime;
|
||||
pitch += viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime;
|
||||
if (yaw > 360) {
|
||||
yaw -= 360;
|
||||
}
|
||||
if (yaw < -360) {
|
||||
yaw += 360;
|
||||
}
|
||||
var orientation = Quat.fromPitchYawRoll(pitch, yaw, roll);
|
||||
Camera.setOrientation(orientation);
|
||||
}
|
||||
|
|
210
examples/hydraMove.js
Normal file
210
examples/hydraMove.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
//
|
||||
// hydraMove.js
|
||||
// hifi
|
||||
//
|
||||
// Created by Brad Hefta-Gaub on 2/10/14.
|
||||
// Copyright (c) 2014 HighFidelity, Inc. All rights reserved.
|
||||
//
|
||||
// This is an example script that demonstrates use of the Controller and MyAvatar classes to implement
|
||||
// avatar flying through the hydra/controller joysticks
|
||||
//
|
||||
//
|
||||
|
||||
var damping = 0.9;
|
||||
var position = { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z };
|
||||
var joysticksCaptured = false;
|
||||
var THRUST_CONTROLLER = 0;
|
||||
var VIEW_CONTROLLER = 1;
|
||||
var INITIAL_THRUST_MULTPLIER = 1.0;
|
||||
var THRUST_INCREASE_RATE = 1.05;
|
||||
var MAX_THRUST_MULTIPLIER = 75.0;
|
||||
var thrustMultiplier = INITIAL_THRUST_MULTPLIER;
|
||||
var grabDelta = { x: 0, y: 0, z: 0};
|
||||
var grabDeltaVelocity = { x: 0, y: 0, z: 0};
|
||||
var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
|
||||
var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
|
||||
var grabbingWithRightHand = false;
|
||||
var wasGrabbingWithRightHand = false;
|
||||
var grabbingWithLeftHand = false;
|
||||
var wasGrabbingWithLeftHand = false;
|
||||
var EPSILON = 0.000001;
|
||||
var velocity = { x: 0, y: 0, z: 0};
|
||||
var deltaTime = 1/60; // approximately our FPS - maybe better to be elapsed time since last call
|
||||
var THRUST_MAG_UP = 800.0;
|
||||
var THRUST_MAG_DOWN = 300.0;
|
||||
var THRUST_MAG_FWD = 500.0;
|
||||
var THRUST_MAG_BACK = 300.0;
|
||||
var THRUST_MAG_LATERAL = 250.0;
|
||||
var THRUST_JUMP = 120.0;
|
||||
|
||||
var YAW_MAG = 500.0;
|
||||
var PITCH_MAG = 100.0;
|
||||
var THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
|
||||
var JOYSTICK_YAW_MAG = YAW_MAG;
|
||||
var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
|
||||
|
||||
|
||||
var LEFT_PALM = 0;
|
||||
var LEFT_BUTTON_4 = 4;
|
||||
var RIGHT_PALM = 2;
|
||||
var RIGHT_BUTTON_4 = 10;
|
||||
|
||||
// Used by handleGrabBehavior() for managing the grab position changes
|
||||
function getAndResetGrabDelta() {
|
||||
var HAND_GRAB_SCALE_DISTANCE = 2.0;
|
||||
var delta = Vec3.multiply(grabDelta, (MyAvatar.scale * HAND_GRAB_SCALE_DISTANCE));
|
||||
grabDelta = { x: 0, y: 0, z: 0};
|
||||
var avatarRotation = MyAvatar.orientation;
|
||||
var result = Vec3.multiplyQbyV(avatarRotation, Vec3.multiply(delta, -1));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Used by handleGrabBehavior() for managing the grab velocity feature
|
||||
function getAndResetGrabDeltaVelocity() {
|
||||
var HAND_GRAB_SCALE_VELOCITY = 50.0;
|
||||
var delta = Vec3.multiply(grabDeltaVelocity, (MyAvatar.scale * HAND_GRAB_SCALE_VELOCITY));
|
||||
grabDeltaVelocity = { x: 0, y: 0, z: 0};
|
||||
var avatarRotation = MyAvatar.orientation;
|
||||
var result = Quat.multiply(avatarRotation, Vec3.multiply(delta, -1));
|
||||
return result;
|
||||
}
|
||||
|
||||
// Used by handleGrabBehavior() for managing the grab rotation feature
|
||||
function getAndResetGrabRotation() {
|
||||
var quatDiff = Quat.multiply(grabCurrentRotation, Quat.inverse(grabStartRotation));
|
||||
grabStartRotation = grabCurrentRotation;
|
||||
return quatDiff;
|
||||
}
|
||||
|
||||
// handles all the grab related behavior: position (crawl), velocity (flick), and rotate (twist)
|
||||
function handleGrabBehavior() {
|
||||
// check for and handle grab behaviors
|
||||
grabbingWithRightHand = Controller.isButtonPressed(RIGHT_BUTTON_4);
|
||||
grabbingWithLeftHand = Controller.isButtonPressed(LEFT_BUTTON_4);
|
||||
stoppedGrabbingWithLeftHand = false;
|
||||
stoppedGrabbingWithRightHand = false;
|
||||
|
||||
if (grabbingWithRightHand && !wasGrabbingWithRightHand) {
|
||||
// Just starting grab, capture starting rotation
|
||||
grabStartRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
|
||||
}
|
||||
if (grabbingWithRightHand) {
|
||||
grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(RIGHT_PALM), deltaTime));
|
||||
grabCurrentRotation = Controller.getSpatialControlRawRotation(RIGHT_PALM);
|
||||
}
|
||||
if (!grabbingWithRightHand && wasGrabbingWithRightHand) {
|
||||
// Just ending grab, capture velocity
|
||||
grabDeltaVelocity = Controller.getSpatialControlVelocity(RIGHT_PALM);
|
||||
stoppedGrabbingWithRightHand = true;
|
||||
}
|
||||
|
||||
if (grabbingWithLeftHand && !wasGrabbingWithLeftHand) {
|
||||
// Just starting grab, capture starting rotation
|
||||
grabStartRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
|
||||
}
|
||||
|
||||
if (grabbingWithLeftHand) {
|
||||
grabDelta = Vec3.sum(grabDelta, Vec3.multiply(Controller.getSpatialControlVelocity(LEFT_PALM), deltaTime));
|
||||
grabCurrentRotation = Controller.getSpatialControlRawRotation(LEFT_PALM);
|
||||
}
|
||||
if (!grabbingWithLeftHand && wasGrabbingWithLeftHand) {
|
||||
// Just ending grab, capture velocity
|
||||
grabDeltaVelocity = Controller.getSpatialControlVelocity(LEFT_PALM);
|
||||
stoppedGrabbingWithLeftHand = true;
|
||||
}
|
||||
|
||||
grabbing = grabbingWithRightHand || grabbingWithLeftHand;
|
||||
stoppedGrabbing = stoppedGrabbingWithRightHand || stoppedGrabbingWithLeftHand;
|
||||
|
||||
if (grabbing) {
|
||||
|
||||
// move position
|
||||
var moveFromGrab = getAndResetGrabDelta();
|
||||
if (Vec3.length(moveFromGrab) > EPSILON) {
|
||||
MyAvatar.position = Vec3.sum(MyAvatar.position, moveFromGrab);
|
||||
velocity = { x: 0, y: 0, z: 0};
|
||||
}
|
||||
|
||||
// add some rotation...
|
||||
var deltaRotation = getAndResetGrabRotation();
|
||||
var GRAB_CONTROLLER_TURN_SCALING = 0.5;
|
||||
var euler = Vec3.multiply(Quat.safeEulerAngles(deltaRotation), GRAB_CONTROLLER_TURN_SCALING);
|
||||
|
||||
// Adjust body yaw by yaw from controller
|
||||
var orientation = Quat.multiply(Quat.angleAxis(-euler.y, {x:0, y: 1, z:0}), MyAvatar.orientation);
|
||||
MyAvatar.orientation = orientation;
|
||||
|
||||
// Adjust head pitch from controller
|
||||
MyAvatar.headPitch = MyAvatar.headPitch - euler.x;
|
||||
}
|
||||
|
||||
// add some velocity...
|
||||
if (stoppedGrabbing) {
|
||||
velocity = Vec3.sum(velocity, getAndResetGrabDeltaVelocity());
|
||||
}
|
||||
|
||||
// handle residual velocity
|
||||
if(Vec3.length(velocity) > EPSILON) {
|
||||
MyAvatar.position = Vec3.sum(MyAvatar.position, Vec3.multiply(velocity, deltaTime));
|
||||
// damp velocity
|
||||
velocity = Vec3.multiply(velocity, damping);
|
||||
}
|
||||
|
||||
|
||||
wasGrabbingWithRightHand = grabbingWithRightHand;
|
||||
wasGrabbingWithLeftHand = grabbingWithLeftHand;
|
||||
}
|
||||
|
||||
// Main update function that handles flying and grabbing behaviort
|
||||
function flyWithHydra() {
|
||||
var thrustJoystickPosition = Controller.getJoystickPosition(THRUST_CONTROLLER);
|
||||
|
||||
if (thrustJoystickPosition.x != 0 || thrustJoystickPosition.y != 0) {
|
||||
if (thrustMultiplier < MAX_THRUST_MULTIPLIER) {
|
||||
thrustMultiplier *= 1 + (deltaTime * THRUST_INCREASE_RATE);
|
||||
}
|
||||
var currentOrientation = MyAvatar.orientation;
|
||||
|
||||
var front = Quat.getFront(currentOrientation);
|
||||
var right = Quat.getRight(currentOrientation);
|
||||
var up = Quat.getUp(currentOrientation);
|
||||
|
||||
var thrustFront = Vec3.multiply(front, MyAvatar.scale * THRUST_MAG_HAND_JETS *
|
||||
thrustJoystickPosition.y * thrustMultiplier * deltaTime);
|
||||
MyAvatar.addThrust(thrustFront);
|
||||
var thrustRight = Vec3.multiply(right, MyAvatar.scale * THRUST_MAG_HAND_JETS *
|
||||
thrustJoystickPosition.x * thrustMultiplier * deltaTime);
|
||||
MyAvatar.addThrust(thrustRight);
|
||||
} else {
|
||||
thrustMultiplier = INITIAL_THRUST_MULTPLIER;
|
||||
}
|
||||
|
||||
// View Controller
|
||||
var viewJoystickPosition = Controller.getJoystickPosition(VIEW_CONTROLLER);
|
||||
if (viewJoystickPosition.x != 0 || viewJoystickPosition.y != 0) {
|
||||
|
||||
// change the body yaw based on our x controller
|
||||
var orientation = MyAvatar.orientation;
|
||||
var deltaOrientation = Quat.fromPitchYawRoll(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0);
|
||||
MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
|
||||
|
||||
// change the headPitch based on our x controller
|
||||
//pitch += viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime;
|
||||
var newPitch = MyAvatar.headPitch + (viewJoystickPosition.y * JOYSTICK_PITCH_MAG * deltaTime);
|
||||
MyAvatar.headPitch = newPitch;
|
||||
}
|
||||
handleGrabBehavior();
|
||||
}
|
||||
|
||||
Script.willSendVisualDataCallback.connect(flyWithHydra);
|
||||
Controller.captureJoystick(THRUST_CONTROLLER);
|
||||
Controller.captureJoystick(VIEW_CONTROLLER);
|
||||
|
||||
// Map keyPress and mouse move events to our callbacks
|
||||
function scriptEnding() {
|
||||
// re-enabled the standard application for touch events
|
||||
Controller.releaseJoystick(THRUST_CONTROLLER);
|
||||
Controller.releaseJoystick(VIEW_CONTROLLER);
|
||||
}
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
|
|
@ -4059,7 +4059,7 @@ void Application::loadScript(const QString& fileNameString) {
|
|||
scriptEngine->getParticlesScriptingInterface()->setParticleTree(_particles.getTree());
|
||||
|
||||
// hook our avatar object into this script engine
|
||||
scriptEngine->setAvatarData( static_cast<Avatar*>(_myAvatar), "MyAvatar");
|
||||
scriptEngine->setAvatarData(_myAvatar, "MyAvatar"); // leave it as a MyAvatar class to expose thrust features
|
||||
|
||||
CameraScriptableObject* cameraScriptable = new CameraScriptableObject(&_myCamera, &_viewFrustum);
|
||||
scriptEngine->registerGlobalObject("Camera", cameraScriptable);
|
||||
|
|
|
@ -172,6 +172,21 @@ glm::vec3 ControllerScriptingInterface::getSpatialControlVelocity(int controlInd
|
|||
return glm::vec3(0); // bad index
|
||||
}
|
||||
|
||||
glm::quat ControllerScriptingInterface::getSpatialControlRawRotation(int controlIndex) const {
|
||||
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
const PalmData* palmData = getActivePalm(palmIndex);
|
||||
if (palmData) {
|
||||
switch (controlOfPalm) {
|
||||
case PALM_SPATIALCONTROL:
|
||||
return palmData->getRawRotation();
|
||||
case TIP_SPATIALCONTROL:
|
||||
return palmData->getRawRotation(); // currently the tip doesn't have a unique rotation, use the palm rotation
|
||||
}
|
||||
}
|
||||
return glm::quat(); // bad index
|
||||
}
|
||||
|
||||
glm::vec3 ControllerScriptingInterface::getSpatialControlNormal(int controlIndex) const {
|
||||
int palmIndex = controlIndex / NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
int controlOfPalm = controlIndex % NUMBER_OF_SPATIALCONTROLS_PER_PALM;
|
||||
|
|
|
@ -58,7 +58,7 @@ public slots:
|
|||
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const;
|
||||
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const;
|
||||
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const;
|
||||
|
||||
virtual glm::quat getSpatialControlRawRotation(int controlIndex) const;
|
||||
virtual void captureKeyEvents(const KeyEvent& event);
|
||||
virtual void releaseKeyEvents(const KeyEvent& event);
|
||||
|
||||
|
|
|
@ -445,9 +445,16 @@ float Avatar::getHeight() const {
|
|||
return extents.maximum.y - extents.minimum.y;
|
||||
}
|
||||
|
||||
bool Avatar::poke(ModelCollisionInfo& collision) {
|
||||
// ATM poke() can only affect the Skeleton (not the head)
|
||||
bool Avatar::isPokeable(ModelCollisionInfo& collision) const {
|
||||
// ATM only the Skeleton is pokeable
|
||||
// TODO: make poke affect head
|
||||
if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
|
||||
return _skeletonModel.isPokeable(collision);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Avatar::poke(ModelCollisionInfo& collision) {
|
||||
if (collision._model == &_skeletonModel && collision._jointIndex != -1) {
|
||||
return _skeletonModel.poke(collision);
|
||||
}
|
||||
|
|
|
@ -128,6 +128,9 @@ public:
|
|||
|
||||
float getHeight() const;
|
||||
|
||||
/// \return true if we expect the avatar would move as a result of the collision
|
||||
bool isPokeable(ModelCollisionInfo& collision) const;
|
||||
|
||||
/// \param collision a data structure for storing info about collisions against Models
|
||||
/// \return true if the collision affects the Avatar models
|
||||
bool poke(ModelCollisionInfo& collision);
|
||||
|
|
|
@ -32,12 +32,7 @@ Hand::Hand(Avatar* owningAvatar) :
|
|||
_ballColor(0.0, 0.0, 0.4),
|
||||
_collisionCenter(0,0,0),
|
||||
_collisionAge(0),
|
||||
_collisionDuration(0),
|
||||
_pitchUpdate(0),
|
||||
_grabDelta(0, 0, 0),
|
||||
_grabDeltaVelocity(0, 0, 0),
|
||||
_grabStartRotation(0, 0, 0, 1),
|
||||
_grabCurrentRotation(0, 0, 0, 1)
|
||||
_collisionDuration(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -54,28 +49,6 @@ void Hand::init() {
|
|||
void Hand::reset() {
|
||||
}
|
||||
|
||||
glm::vec3 Hand::getAndResetGrabDelta() {
|
||||
const float HAND_GRAB_SCALE_DISTANCE = 2.f;
|
||||
glm::vec3 delta = _grabDelta * _owningAvatar->getScale() * HAND_GRAB_SCALE_DISTANCE;
|
||||
_grabDelta = glm::vec3(0,0,0);
|
||||
glm::quat avatarRotation = _owningAvatar->getOrientation();
|
||||
return avatarRotation * -delta;
|
||||
}
|
||||
|
||||
glm::vec3 Hand::getAndResetGrabDeltaVelocity() {
|
||||
const float HAND_GRAB_SCALE_VELOCITY = 5.f;
|
||||
glm::vec3 delta = _grabDeltaVelocity * _owningAvatar->getScale() * HAND_GRAB_SCALE_VELOCITY;
|
||||
_grabDeltaVelocity = glm::vec3(0,0,0);
|
||||
glm::quat avatarRotation = _owningAvatar->getOrientation();
|
||||
return avatarRotation * -delta;
|
||||
|
||||
}
|
||||
glm::quat Hand::getAndResetGrabRotation() {
|
||||
glm::quat diff = _grabCurrentRotation * glm::inverse(_grabStartRotation);
|
||||
_grabStartRotation = _grabCurrentRotation;
|
||||
return diff;
|
||||
}
|
||||
|
||||
void Hand::simulate(float deltaTime, bool isMine) {
|
||||
|
||||
if (_collisionAge > 0.f) {
|
||||
|
@ -99,19 +72,6 @@ void Hand::simulate(float deltaTime, bool isMine) {
|
|||
|
||||
_buckyBalls.grab(palm, fingerTipPosition, _owningAvatar->getOrientation(), deltaTime);
|
||||
|
||||
if (palm.getControllerButtons() & BUTTON_4) {
|
||||
_grabDelta += palm.getRawVelocity() * deltaTime;
|
||||
_grabCurrentRotation = palm.getRawRotation();
|
||||
}
|
||||
if ((palm.getLastControllerButtons() & BUTTON_4) && !(palm.getControllerButtons() & BUTTON_4)) {
|
||||
// Just ending grab, capture velocity
|
||||
_grabDeltaVelocity = palm.getRawVelocity();
|
||||
}
|
||||
if (!(palm.getLastControllerButtons() & BUTTON_4) && (palm.getControllerButtons() & BUTTON_4)) {
|
||||
// Just starting grab, capture starting rotation
|
||||
_grabStartRotation = palm.getRawRotation();
|
||||
}
|
||||
|
||||
if (palm.getControllerButtons() & BUTTON_1) {
|
||||
if (glm::length(fingerTipPosition - _lastFingerAddVoxel) > (FINGERTIP_VOXEL_SIZE / 2.f)) {
|
||||
QColor paintColor = Menu::getInstance()->getActionForOption(MenuOption::VoxelPaintColor)->data().value<QColor>();
|
||||
|
@ -223,8 +183,9 @@ void Hand::updateCollisions() {
|
|||
}
|
||||
}
|
||||
if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) {
|
||||
for (size_t j = 0; j < collisions.size(); ++j) {
|
||||
if (!avatar->poke(collisions[j])) {
|
||||
for (int j = 0; j < collisions.size(); ++j) {
|
||||
// we don't resolve penetrations that would poke the other avatar
|
||||
if (!avatar->isPokeable(collisions[j])) {
|
||||
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +201,7 @@ void Hand::updateCollisions() {
|
|||
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
|
||||
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
|
||||
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) {
|
||||
for (size_t j = 0; j < collisions.size(); ++j) {
|
||||
for (int j = 0; j < collisions.size(); ++j) {
|
||||
totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,15 +58,6 @@ public:
|
|||
const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;}
|
||||
const glm::vec3& getLeapFingerRootBallPosition(int ball) const { return _leapFingerRootBalls[ball].position;}
|
||||
|
||||
// Pitch from controller input to view
|
||||
const float getPitchUpdate() const { return _pitchUpdate; }
|
||||
void setPitchUpdate(float pitchUpdate) { _pitchUpdate = pitchUpdate; }
|
||||
|
||||
// Get the drag distance to move
|
||||
glm::vec3 getAndResetGrabDelta();
|
||||
glm::vec3 getAndResetGrabDeltaVelocity();
|
||||
glm::quat getAndResetGrabRotation();
|
||||
|
||||
private:
|
||||
// disallow copies of the Hand, copy of owning Avatar is disallowed too
|
||||
Hand(const Hand&);
|
||||
|
@ -100,13 +91,6 @@ private:
|
|||
void calculateGeometry();
|
||||
|
||||
void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime);
|
||||
|
||||
float _pitchUpdate;
|
||||
|
||||
glm::vec3 _grabDelta;
|
||||
glm::vec3 _grabDeltaVelocity;
|
||||
glm::quat _grabStartRotation;
|
||||
glm::quat _grabCurrentRotation;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -225,8 +225,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
updateCollisionWithVoxels(deltaTime, radius);
|
||||
}
|
||||
if (_collisionFlags & COLLISION_GROUP_AVATARS) {
|
||||
// Note, hand-vs-avatar collisions are done elsewhere
|
||||
// This is where we avatar-vs-avatar bounding capsule
|
||||
updateCollisionWithAvatars(deltaTime);
|
||||
}
|
||||
}
|
||||
|
@ -328,21 +326,6 @@ void MyAvatar::simulate(float deltaTime) {
|
|||
|
||||
updateChatCircle(deltaTime);
|
||||
|
||||
// Get any position, velocity, or rotation update from Grab Drag controller
|
||||
glm::vec3 moveFromGrab = _hand.getAndResetGrabDelta();
|
||||
if (glm::length(moveFromGrab) > EPSILON) {
|
||||
_position += moveFromGrab;
|
||||
_velocity = glm::vec3(0, 0, 0);
|
||||
}
|
||||
_velocity += _hand.getAndResetGrabDeltaVelocity();
|
||||
glm::quat deltaRotation = _hand.getAndResetGrabRotation();
|
||||
const float GRAB_CONTROLLER_TURN_SCALING = 0.5f;
|
||||
glm::vec3 euler = safeEulerAngles(deltaRotation) * GRAB_CONTROLLER_TURN_SCALING;
|
||||
// Adjust body yaw by yaw from controller
|
||||
setOrientation(glm::angleAxis(-euler.y, glm::vec3(0, 1, 0)) * getOrientation());
|
||||
// Adjust head pitch from controller
|
||||
getHead().setPitch(getHead().getPitch() - euler.x);
|
||||
|
||||
_position += _velocity * deltaTime;
|
||||
|
||||
// update avatar skeleton and simulate hand and head
|
||||
|
@ -807,39 +790,6 @@ void MyAvatar::updateThrust(float deltaTime) {
|
|||
up;
|
||||
}
|
||||
}
|
||||
// Add thrust and rotation from hand controllers
|
||||
const float THRUST_MAG_HAND_JETS = THRUST_MAG_FWD;
|
||||
const float JOYSTICK_YAW_MAG = YAW_MAG;
|
||||
const float JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5f;
|
||||
const int THRUST_CONTROLLER = 0;
|
||||
const int VIEW_CONTROLLER = 1;
|
||||
for (size_t i = 0; i < getHand().getPalms().size(); ++i) {
|
||||
PalmData& palm = getHand().getPalms()[i];
|
||||
|
||||
// If the script hasn't captured this joystick, then let the default behavior work
|
||||
if (!Application::getInstance()->getControllerScriptingInterface()->isJoystickCaptured(palm.getSixenseID())) {
|
||||
if (palm.isActive() && (palm.getSixenseID() == THRUST_CONTROLLER)) {
|
||||
if (palm.getJoystickY() != 0.f) {
|
||||
FingerData& finger = palm.getFingers()[0];
|
||||
if (finger.isActive()) {
|
||||
}
|
||||
_thrust += front * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickY() * _thrustMultiplier * deltaTime;
|
||||
}
|
||||
if (palm.getJoystickX() != 0.f) {
|
||||
_thrust += right * _scale * THRUST_MAG_HAND_JETS * palm.getJoystickX() * _thrustMultiplier * deltaTime;
|
||||
}
|
||||
} else if (palm.isActive() && (palm.getSixenseID() == VIEW_CONTROLLER)) {
|
||||
if (palm.getJoystickX() != 0.f) {
|
||||
_bodyYawDelta -= palm.getJoystickX() * JOYSTICK_YAW_MAG * deltaTime;
|
||||
}
|
||||
if (palm.getJoystickY() != 0.f) {
|
||||
getHand().setPitchUpdate(getHand().getPitchUpdate() +
|
||||
(palm.getJoystickY() * JOYSTICK_PITCH_MAG * deltaTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Update speed brake status
|
||||
const float MIN_SPEED_BRAKE_VELOCITY = _scale * 0.4f;
|
||||
|
@ -974,7 +924,43 @@ void MyAvatar::updateCollisionSound(const glm::vec3 &penetration, float deltaTim
|
|||
}
|
||||
}
|
||||
|
||||
const float DEFAULT_HAND_RADIUS = 0.1f;
|
||||
bool findAvatarAvatarPenetration(const glm::vec3 positionA, float radiusA, float heightA,
|
||||
const glm::vec3 positionB, float radiusB, float heightB, glm::vec3& penetration) {
|
||||
glm::vec3 positionBA = positionB - positionA;
|
||||
float xzDistance = sqrt(positionBA.x * positionBA.x + positionBA.z * positionBA.z);
|
||||
if (xzDistance < (radiusA + radiusB)) {
|
||||
float yDistance = fabs(positionBA.y);
|
||||
float halfHeights = 0.5 * (heightA + heightB);
|
||||
if (yDistance < halfHeights) {
|
||||
// cylinders collide
|
||||
if (xzDistance > 0.f) {
|
||||
positionBA.y = 0.f;
|
||||
// note, penetration should point from A into B
|
||||
penetration = positionBA * ((radiusA + radiusB - xzDistance) / xzDistance);
|
||||
return true;
|
||||
} else {
|
||||
// exactly coaxial -- we'll return false for this case
|
||||
return false;
|
||||
}
|
||||
} else if (yDistance < halfHeights + radiusA + radiusB) {
|
||||
// caps collide
|
||||
if (positionBA.y < 0.f) {
|
||||
// A is above B
|
||||
positionBA.y += halfHeights;
|
||||
float BA = glm::length(positionBA);
|
||||
penetration = positionBA * (radiusA + radiusB - BA) / BA;
|
||||
return true;
|
||||
} else {
|
||||
// A is below B
|
||||
positionBA.y -= halfHeights;
|
||||
float BA = glm::length(positionBA);
|
||||
penetration = positionBA * (radiusA + radiusB - BA) / BA;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
||||
// Reset detector for nearest avatar
|
||||
|
@ -984,7 +970,14 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
// no need to compute a bunch of stuff if we have one or fewer avatars
|
||||
return;
|
||||
}
|
||||
float myRadius = getHeight();
|
||||
float myBoundingRadius = 0.5f * getHeight();
|
||||
|
||||
// HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis
|
||||
// TODO: make the collision work without assuming avatar orientation
|
||||
Extents myStaticExtents = _skeletonModel.getStaticExtents();
|
||||
glm::vec3 staticScale = myStaticExtents.maximum - myStaticExtents.minimum;
|
||||
float myCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
|
||||
float myCapsuleHeight = staticScale.y;
|
||||
|
||||
CollisionInfo collisionInfo;
|
||||
foreach (const AvatarSharedPointer& avatarPointer, avatars) {
|
||||
|
@ -997,9 +990,20 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) {
|
|||
if (_distanceToNearestAvatar > distance) {
|
||||
_distanceToNearestAvatar = distance;
|
||||
}
|
||||
float theirRadius = avatar->getHeight();
|
||||
if (distance < myRadius + theirRadius) {
|
||||
// TODO: Andrew to make avatar-avatar capsule collisions work here
|
||||
float theirBoundingRadius = 0.5f * avatar->getHeight();
|
||||
if (distance < myBoundingRadius + theirBoundingRadius) {
|
||||
Extents theirStaticExtents = _skeletonModel.getStaticExtents();
|
||||
glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum;
|
||||
float theirCapsuleRadius = 0.25f * (staticScale.x + staticScale.z);
|
||||
float theirCapsuleHeight = staticScale.y;
|
||||
|
||||
glm::vec3 penetration(0.f);
|
||||
if (findAvatarAvatarPenetration(_position, myCapsuleRadius, myCapsuleHeight,
|
||||
avatar->getPosition(), theirCapsuleRadius, theirCapsuleHeight, penetration)) {
|
||||
// move the avatar out by half the penetration
|
||||
setPosition(_position - 0.5f * penetration);
|
||||
glm::vec3 pushOut = 0.5f * penetration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,6 @@ public:
|
|||
|
||||
// setters
|
||||
void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; }
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
void setVelocity(const glm::vec3 velocity) { _velocity = velocity; }
|
||||
void setLeanScale(float scale) { _leanScale = scale; }
|
||||
void setGravity(glm::vec3 gravity);
|
||||
|
@ -78,9 +77,6 @@ public:
|
|||
|
||||
static void sendKillAvatar();
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
|
||||
void orbit(const glm::vec3& position, int deltaX, int deltaY);
|
||||
|
||||
|
@ -94,9 +90,13 @@ public slots:
|
|||
void increaseSize();
|
||||
void decreaseSize();
|
||||
void resetSize();
|
||||
|
||||
void sendIdentityPacket();
|
||||
|
||||
// Set/Get update the thrust that will move the avatar around
|
||||
void addThrust(glm::vec3 newThrust) { _thrust += newThrust; };
|
||||
glm::vec3 getThrust() { return _thrust; };
|
||||
void setThrust(glm::vec3 newThrust) { _thrust = newThrust; }
|
||||
|
||||
private:
|
||||
bool _mousePressed;
|
||||
float _bodyPitchDelta;
|
||||
|
|
|
@ -1274,6 +1274,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
|
||||
geometry.bindExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
geometry.bindExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
geometry.staticExtents.minimum = glm::vec3(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
geometry.staticExtents.maximum = glm::vec3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||
|
||||
QVariantHash springs = mapping.value("spring").toHash();
|
||||
QVariant defaultSpring = springs.value("default");
|
||||
|
@ -1430,6 +1432,8 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
boneDirection /= boneLength;
|
||||
}
|
||||
}
|
||||
bool jointIsStatic = joint.freeLineage.isEmpty();
|
||||
glm::vec3 jointTranslation = extractTranslation(geometry.offset * joint.bindTransform);
|
||||
float radiusScale = extractUniformScale(joint.transform * fbxCluster.inverseBindMatrix);
|
||||
float totalWeight = 0.0f;
|
||||
for (int j = 0; j < cluster.indices.size(); j++) {
|
||||
|
@ -1447,6 +1451,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping)
|
|||
joint.boneRadius = glm::max(joint.boneRadius, radiusScale * glm::distance(
|
||||
vertex, boneEnd + boneDirection * proj));
|
||||
}
|
||||
if (jointIsStatic) {
|
||||
// expand the extents of static (nonmovable) joints
|
||||
geometry.staticExtents.minimum = glm::min(geometry.staticExtents.minimum, vertex + jointTranslation);
|
||||
geometry.staticExtents.maximum = glm::max(geometry.staticExtents.maximum, vertex + jointTranslation);
|
||||
}
|
||||
}
|
||||
|
||||
// look for an unused slot in the weights vector
|
||||
|
|
|
@ -159,6 +159,7 @@ public:
|
|||
glm::vec3 neckPivot;
|
||||
|
||||
Extents bindExtents;
|
||||
Extents staticExtents;
|
||||
|
||||
QVector<FBXAttachment> attachments;
|
||||
};
|
||||
|
|
|
@ -305,6 +305,15 @@ Extents Model::getBindExtents() const {
|
|||
return scaledExtents;
|
||||
}
|
||||
|
||||
Extents Model::getStaticExtents() const {
|
||||
if (!isActive()) {
|
||||
return Extents();
|
||||
}
|
||||
const Extents& staticExtents = _geometry->getFBXGeometry().staticExtents;
|
||||
Extents scaledExtents = { staticExtents.minimum * _scale, staticExtents.maximum * _scale };
|
||||
return scaledExtents;
|
||||
}
|
||||
|
||||
int Model::getParentJointIndex(int jointIndex) const {
|
||||
return (isActive() && jointIndex != -1) ? _geometry->getFBXGeometry().joints.at(jointIndex).parentIndex : -1;
|
||||
}
|
||||
|
@ -713,6 +722,18 @@ void Model::renderCollisionProxies(float alpha) {
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
bool Model::isPokeable(ModelCollisionInfo& collision) const {
|
||||
// the joint is pokable by a collision if it exists and is free to move
|
||||
const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._jointIndex];
|
||||
if (joint.parentIndex == -1 || _jointStates.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
// an empty freeLineage means the joint can't move
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
const QVector<int>& freeLineage = geometry.joints.at(collision._jointIndex).freeLineage;
|
||||
return !freeLineage.isEmpty();
|
||||
}
|
||||
|
||||
bool Model::poke(ModelCollisionInfo& collision) {
|
||||
// This needs work. At the moment it can wiggle joints that are free to move (such as arms)
|
||||
// but unmovable joints (such as torso) cannot be influenced at all.
|
||||
|
|
|
@ -66,6 +66,9 @@ public:
|
|||
|
||||
/// Returns the extents of the model in its bind pose.
|
||||
Extents getBindExtents() const;
|
||||
|
||||
/// Returns the extents of the unmovable joints of the model.
|
||||
Extents getStaticExtents() const;
|
||||
|
||||
/// Returns a reference to the shared geometry.
|
||||
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
|
||||
|
@ -164,6 +167,9 @@ public:
|
|||
|
||||
void renderCollisionProxies(float alpha);
|
||||
|
||||
/// \return true if the collision would move the model
|
||||
bool isPokeable(ModelCollisionInfo& collision) const;
|
||||
|
||||
/// \param collisionInfo info about the collision
|
||||
/// \return true if collision affects the Model
|
||||
bool poke(ModelCollisionInfo& collisionInfo);
|
||||
|
|
|
@ -67,6 +67,7 @@ class AvatarData : public NodeData {
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(glm::vec3 position READ getPosition WRITE setPosition)
|
||||
Q_PROPERTY(float scale READ getTargetScale WRITE setTargetScale)
|
||||
Q_PROPERTY(glm::vec3 handPosition READ getHandPosition WRITE setHandPosition)
|
||||
Q_PROPERTY(float bodyYaw READ getBodyYaw WRITE setBodyYaw)
|
||||
Q_PROPERTY(float bodyPitch READ getBodyPitch WRITE setBodyPitch)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QtCore/QObject>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
#include "EventTypes.h"
|
||||
|
||||
|
@ -37,6 +38,7 @@ public slots:
|
|||
virtual glm::vec3 getSpatialControlPosition(int controlIndex) const = 0;
|
||||
virtual glm::vec3 getSpatialControlVelocity(int controlIndex) const = 0;
|
||||
virtual glm::vec3 getSpatialControlNormal(int controlIndex) const = 0;
|
||||
virtual glm::quat getSpatialControlRawRotation(int controlIndex) const = 0;
|
||||
|
||||
virtual void captureKeyEvents(const KeyEvent& event) = 0;
|
||||
virtual void releaseKeyEvents(const KeyEvent& event) = 0;
|
||||
|
|
|
@ -9,7 +9,10 @@
|
|||
//
|
||||
//
|
||||
|
||||
#include <glm/gtx/vector_angle.hpp>
|
||||
|
||||
#include <OctreeConstants.h>
|
||||
#include <SharedUtil.h>
|
||||
#include "Quat.h"
|
||||
|
||||
glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
|
||||
|
@ -24,6 +27,11 @@ glm::quat Quat::fromPitchYawRoll(float pitch, float yaw, float roll) {
|
|||
return glm::quat(glm::radians(glm::vec3(pitch, yaw, roll)));
|
||||
}
|
||||
|
||||
glm::quat Quat::inverse(const glm::quat& q) {
|
||||
return glm::inverse(q);
|
||||
}
|
||||
|
||||
|
||||
glm::vec3 Quat::getFront(const glm::quat& orientation) {
|
||||
return orientation * IDENTITY_FRONT;
|
||||
}
|
||||
|
@ -35,3 +43,12 @@ glm::vec3 Quat::getRight(const glm::quat& orientation) {
|
|||
glm::vec3 Quat::getUp(const glm::quat& orientation) {
|
||||
return orientation * IDENTITY_UP;
|
||||
}
|
||||
|
||||
glm::vec3 Quat::safeEulerAngles(const glm::quat& orientation) {
|
||||
return ::safeEulerAngles(orientation);
|
||||
}
|
||||
|
||||
glm::quat Quat::angleAxis(float angle, const glm::vec3& v) {
|
||||
return glm::angleAxis(angle, v);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,11 +23,12 @@ public slots:
|
|||
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
|
||||
glm::quat fromVec3(const glm::vec3& vec3);
|
||||
glm::quat fromPitchYawRoll(float pitch, float yaw, float roll);
|
||||
glm::quat inverse(const glm::quat& q);
|
||||
glm::vec3 getFront(const glm::quat& orientation);
|
||||
glm::vec3 getRight(const glm::quat& orientation);
|
||||
glm::vec3 getUp(const glm::quat& orientation);
|
||||
glm::vec3 safeEulerAngles(const glm::quat& orientation);
|
||||
glm::quat angleAxis(float angle, const glm::vec3& v);
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* defined(__hifi__Quat__) */
|
||||
|
|
|
@ -19,6 +19,14 @@ glm::vec3 Vec3::multiply(const glm::vec3& v1, float f) {
|
|||
return v1 * f;
|
||||
}
|
||||
|
||||
glm::vec3 Vec3::multiplyQbyV(const glm::quat& q, const glm::vec3& v) {
|
||||
return q * v;
|
||||
}
|
||||
|
||||
glm::vec3 Vec3::sum(const glm::vec3& v1, const glm::vec3& v2) {
|
||||
return v1 + v2;
|
||||
}
|
||||
|
||||
float Vec3::length(const glm::vec3& v) {
|
||||
return glm::length(v);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@ class Vec3 : public QObject {
|
|||
public slots:
|
||||
glm::vec3 multiply(const glm::vec3& v1, const glm::vec3& v2);
|
||||
glm::vec3 multiply(const glm::vec3& v1, float f);
|
||||
glm::vec3 multiplyQbyV(const glm::quat& q, const glm::vec3& v);
|
||||
glm::vec3 sum(const glm::vec3& v1, const glm::vec3& v2);
|
||||
float length(const glm::vec3& v);
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue