Merge branch 'master' of https://github.com/highfidelity/hifi into black-bis

This commit is contained in:
Sam Gateau 2018-10-04 20:41:52 -07:00
commit d31b2c474b
51 changed files with 887 additions and 588 deletions

View file

@ -0,0 +1,24 @@
Copyright (c) 2006, Ivan Sagalaev
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
* Neither the name of highlight.js nor the names of its contributors may be used to
endorse or promote products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

23
docs/LICENSE_markdeep.txt Normal file
View file

@ -0,0 +1,23 @@
Copyright 2015-2017, Morgan McGuire
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,280 @@
<meta charset="utf-8">
**Pick Parenting**
# What is a pick?
A pick calculates spatial information about the world on on the client.
Picks are immutable to allow efficient use by scripts. There are currently four types of picks:
- Ray pick - Finds the first intersected object along a straight path
- Parabola pick - Finds the first intersected object along a parabolic path
- Stylus pick - Finds the distance between a point and the XZ planes of a list of whitelisted objects
- Collision pick - Uses a volume to check for collisions in the physics engine
With the exception of collision picks, all picks use the visible appearance of the object to check for collisions.
# What is parenting?
Parenting allows an object's position, orientation, and scale (where applicable) to be calculated to another object. This collection of state defines the transform of the object.
Pick parenting allows the pick's transform to be calculated relative to another object, without creating a new pick.
# What can be parented to what?
This object... | | Entity | My Avatar | 3D Overlay | Pick^1 | Pointer^1
---------------------|----------------|--------|-----------|------------|--------|-----------
Can be parented to...| Entity | yes | yes | yes | yes | yes
| My Avatar | yes | no | yes | yes | yes
| Other Avatar | yes | yes | yes | yes | yes
| 3D Overlay | yes | yes | yes | yes | yes
| Pick | no | no | no | yes | yes
| Pointer | no | no | no | no | no
| The Mouse | no | no | no | yes | yes
- ^1 Excluding stylus, which can only be parented to My Avatar's hands for now
# How pick parenting works
This section describes what happens when picks are parented to things.
Since pointers use picks to calculate their results, the rules below
also apply when pointers are parented to things.
## Parent transform exposed
The way a pick is moved/rescaled depends on its parent. In particular,
parenting to a pick actually parents to the pick's result, which
has no notion of scale. A pick can also be transformed relative to
a joint on a model, such as an avatar's hand.
Parent type: | Entity/Avatar | 3D Overlay | Pick
--------------------------|-----------------|--------------|--------
Has position | yes | yes | yes
Has orientation | yes | yes | no
Has scale | yes | yes | no
Can have joints | yes | no | no
## Entity or overlay parent
Parenting a pick to an entity or overlay works similarly to
parenting entities to other things. However, unlike entities,
picks with scale will rescale their dimensions when
their parent rescales.
************************************************************************
* May have a joint, otherwise parent to the parent's origin *
* | *
* scale ^ +--+------+ ^ --+ orientation *
* determined / / | /| |orientation \ offset *
* from / / v / | | v *
* dimensions / / o /..|...................... ^ *
* +-> +---------+ | ------------. . / *
* | | | / position +----> */ <----+ *
* | | | / offset child *
* | | |/ ^ transform*
* v +---------+ / <---+ *
* . +-> dimensions *
* . . . . . . . . . . . . . . | relative *
* . . . v to parent *
************************************************************************
!!! WARNING
Skew is not supported for collision picks. Scaling a parent entity or
overlay non-uniformly can lead to inaccurate shapes for the child
collision pick.
## Avatar parent
A pick parented to an avatar behaves like a wearable. It will maintain
its position relative to some point on the avatar's body. If the pick
has scale (currently only collision picks), then the pick will rescale
when the avatar rescales.
****************************************************************
* Avatar .---. hand ^ -. *
* | | | joint / | *
* '-----> | | \ /orientation | *
* '-+-' +-> o | *
* | / ^ position |relative *
* +---------' +----- |avatar *
* | |scale *
* | if not a joint, |factor *
* | parent to the |(default: 1) *
* | avatar's origin | *
* |\ | |uniform scale *
* | \ | | ^ *
* | \ | | +-> *
* | o \ <-+ -' v *
****************************************************************
***************************************************************
* orientation offset *
* <-----+ *
* ^ \ *
* \ *
* \ child *
* Avatar .---. hand .......... * <-- transform *
* | | | joint . ^ | *
* '-----> | | \ . / v *
* '-+-' +-> o ---------+ ^ *
* | / position +-> scale *
* +---------' offset v relative *
* | to avatar *
***************************************************************
## Pick parent
Picks can also be parented to other picks.
When this is done, the child pick is actually parented to
the parent pick's result, not the pick itself.
A pick parented to another pick will have its position changed,
but not its orientation or scale.
### Ray pick parent
**********************************
* position *
* o *
* \ \ orientation *
* \ \ *
* \ v . . . . *
* \ . *
* \ . result transform *
* * <-- no scale *
* . no orientation *
* . *
**********************************
### Parabola pick parent
**************************************************************
* .------. acceleration *
* + + | ^ *
* speed x ^ / \ . | | *
* orientation / / \ . v | *
* / / * y axis to rotate *
* | . ^ acceleration with *
* o . | *
* position result transform *
* no scale *
* no orientation *
**************************************************************
### Stylus pick parent
********************************************
* *
* *
* . *
* . *
* . *
* .---. avatar * *
* | | hand / ^. *
* | | \ / \ . *
* '-+-' +-> o \ *
* | / result transform *
* +---------' no scale *
* | no orientation *
********************************************
### Collision pick parent
********************************************
* *
* .---. *
* | | <-- collision pick *
* | | *
* | *<-+--- result transform at *
* | | collision pick position *
* | | no scale *
* . . . '---' . . no orientation *
* *
********************************************
# Effect of scale on picks
Scale affects the position and shape of picks, which in turn affects the pick result.
Scale currently does not affect the max distance of a pick.
## Ray and stylus pick scaling
Rescaling the parent of a ray pick or stylus pick can result in a translation of the pick.
***************************************
* before after *
* *
* pick *
* pick +--------------+ ^ *
* ^ | |/ *
* +-----+/ | o *
* | o | | *
* | | | | *
* +-----+ | | *
* parent | | *
* +--------------+ *
* parent *
* *
***************************************
## Parabola pick scaling
***************************************************************************
* before after after *
* (scaleWithParent (scaleWithParent *
* is false) is true) *
* *
* .----. *
* + + *
* .--. / \ *
* + + / \ *
* .--. / \ / \ *
* + + /pick \ /pick \ *
* / \ +-------o +-------o *
* /pick \ | | | | *
* +---o | | | | *
* | | | | | | *
* +---+ +-------+ +-------+ *
* parent parent parent *
***************************************************************************
## Collision pick scaling
Collision picks use the full transform of their parent (position, orientation, and scale/dimensions).
When first created, a collision pick's transform is defined in world space.
As the parent rescales, the collision pick rescales proportionally.
The collision pick's threshold also rescales. The change is proportional to the largest
dimension of the parent. So, if the largest dimension of the parent was 3.0 and is now 6.0,
the threshold doubles.
**************************************************************************************
* *
* before after after *
* (scaleWithParent (scaleWithParent *
* is false) is true) *
* *
* pick *
* +------------+ *
* | | *
* pick | ........ | *
* +-----+ | . . | *
* |.....| | . . | *
* |. .| theshold | . . | *
* pick |.....|___ | | . . | *
* +-----+ +-----+--- <-+ | ........ +___ *
* |.....| | | theshold *
* |. .| theshold +------------+--- *
* |.....|___ | *
* +-----+--- <-+ *
* +----------+ +----------+ *
* +---+ / \ / \ *
* +-----+ +--------------+ +--------------+ *
* parent parent parent *
* *
**************************************************************************************
<style class="fallback">body{visibility:hidden}</style><script>markdeepOptions={tocStyle:'medium'};</script>
<link rel="stylesheet" href="../../markdeep_apidoc.css?">
<!-- Markdeep: --><style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="../../markdeep.min.js"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>

7
docs/markdeep.min.js vendored Normal file

File diff suppressed because one or more lines are too long

170
docs/markdeep_apidoc.css Normal file
View file

@ -0,0 +1,170 @@
/* Custom stylesheet for API documentation by Aras Pranckevičius, http://aras-p.info/
and tweaked by Morgan McGuire.
Licensed as public domain or BSD 2-clause, whichever is more convenient for you.
Originally from https://github.com/aras-p/markdeep-docs-style */
body {
max-width: 50em;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
text-align: left;
margin: 1.5em;
padding: 0 1em;
}
/* if screen is wide enough, put table of contents on the right side */
@media screen and (min-width: 64em) {
.md .longTOC, .md .mediumTOC, .md .shortTOC {
max-width: 20em;
left: 54em;
display:block;
position: fixed;
top:0;
bottom:0;
overflow-y:scroll;
margin-top:0;
margin-bottom:0;
padding-top:1em;
}
}
/* for narrow screens or print, hide table of contents */
@media screen and (max-width: 64em) {
.md .longTOC, .md .mediumTOC, .md .shortTOC { display: none; }
}
@media print {
.md .longTOC, .md .mediumTOC, .md .shortTOC { display: none; }
body { max-width: 100%; }
}
/* reset heading/link fonts to that of body */
.md a,
.md div.title, contents, .md .tocHeader,
.md h1, .md h2, .md h3, .md h4, .md h5, .md h6,
.md .nonumberh1, .md .nonumberh2, .md .nonumberh3, .md .nonumberh4, .md .nonumberh5, .md .nonumberh6,
.md .shortTOC, .md .mediumTOC, .md .longTOC {
font-family: inherit;
}
.md div.title {
margin: 0.4em 0 0 0;
padding: 0;
text-align: inherit;
}
.md div.subtitle {
text-align: inherit;
}
/* faint border below headings */
.md h1, .md h2, .md h3, .md h4,
.md .nonumberh1, .md .nonumberh2, .md .nonumberh3, .md .nonumberh4 {
border-bottom: 1px solid rgba(0,0,0,.1);
}
/* heading font styles */
.md h1, .md .nonumberh1, .md div.title {
font-size: 150%;
font-weight: 600;
color: rgba(0,0,0,.7);
}
.md h2, .md .nonumberh2 {
font-size: 120%;
font-weight: 400;
color: rgba(0,0,0,.9);
}
.md h3, .md .nonumberh3 {
font-size: 110%;
font-weight: 400;
color: rgba(0,0,0,.7);
}
/* no numbering of headings */
.md h1:before, .md h2:before, .md h3:before, .md h4:before { content: none; }
/* link styling */
.md a:link, .md a:visited {
color: #3f51b5;
}
/* inline and block code */
.md code, .md pre.listing {
background-color: rgba(0,0,0,.05);
padding: 0.1em 0.2em;
border-radius: 0.15em;
}
.md pre.listing code {
background-color: transparent;
padding: 0;
border: none;
}
/* table of contents styling; make all 3 forms of it look the same */
.md .longTOC, .md .mediumTOC, .md .shortTOC {
font-size: inherit;
line-height: 120%;
margin: 1em 0;
padding: .4rem;
border-left: .1rem solid #3f51b5;
}
.md .tocHeader {
margin: 0;
padding: 0;
border: none;
font-size: inherit;
}
.md .tocNumber {
display: none;
}
.md .longTOC .level1, .md .mediumTOC .level1, .md .shortTOC .level1 {
font-weight: inherit;
padding: 0;
margin: 0;
}
.md .longTOC p, .md .mediumTOC p, .md .shortTOC p {
overflow: hidden;
text-overflow: ellipsis;
}
.md .longTOC center, .md .mediumTOC center, .md .shortTOC center, .md .tocHeader {
text-align: left;
}
.md .longTOC b, .md .mediumTOC b, .md .shortTOC b {
font-weight: 400;
}
.md .longTOC center b, .md .mediumTOC center b, .md .shortTOC center b {
font-weight: bold;
}
.md .longTOC a, .md .mediumTOC a, .md .shortTOC a {
color: black;
}
.md .longTOC .level1, .md .mediumTOC .level1, .md .shortTOC .level1,
.md .longTOC .level2, .md .mediumTOC .level2, .md .shortTOC .level2,
.md .longTOC .level3, .md .mediumTOC .level3, .md .shortTOC .level3 {
white-space: nowrap;
margin: 0;
padding: 0;
font-size: 90%;
}
/* tables; use fainter colors than regular markdeep style */
.md table.table {
font-size: 90%;
}
.md table.table th {
border: none;
background-color: #ccc;
color: rgba(0,0,0,.6);
}
.md table.table tr, .md table.table td {
border-color: #eee;
}
.md table.table tr:nth-child(even) {
background-color: #f4f4f4;
}

View file

@ -100,6 +100,25 @@ Rectangle {
wearablesModel.setProperty(wearableIndex, 'properties', wearableModelItemProperties);
}
function entityHasAvatarJoints(entityID) {
var hasAvatarJoint = false;
var props = Entities.getEntityProperties(entityID);
var avatarJointsCount = MyAvatar.getJointNames().length;
if (props && avatarJointsCount >= 0 ) {
var entityJointNames = Entities.getJointNames(entityID);
for (var index = 0; index < entityJointNames.length; index++) {
var avatarJointIndex = MyAvatar.getJointIndex(entityJointNames[index]);
if (avatarJointIndex >= 0) {
hasAvatarJoint = true;
break;
}
}
}
return hasAvatarJoint;
}
function getCurrentWearable() {
return wearablesCombobox.currentIndex !== -1 ? wearablesCombobox.model.get(wearablesCombobox.currentIndex) : null;
}
@ -109,6 +128,7 @@ Rectangle {
var wearable = wearablesCombobox.model.get(i);
if (wearable.id === entityID) {
wearablesCombobox.currentIndex = i;
softWearableTimer.restart();
break;
}
}
@ -118,6 +138,7 @@ Rectangle {
visible = false;
adjustWearablesClosed(status, avatarName);
}
HifiConstants { id: hifi }
@ -130,6 +151,20 @@ Rectangle {
hoverEnabled: true;
}
Timer {
id: softWearableTimer
interval: 1000
running: false
repeat: true
onTriggered: {
var currentWearable = getCurrentWearable();
var soft = currentWearable ? currentWearable.relayParentJoints : false;
var softEnabled = currentWearable ? entityHasAvatarJoints(currentWearable.id) : false;
isSoft.set(soft);
isSoft.enabled = softEnabled;
}
}
Column {
anchors.top: parent.top
anchors.topMargin: 12
@ -247,13 +282,13 @@ Rectangle {
var rotation = currentWearable ? currentWearable.localRotationAngles : { x : 0, y : 0, z : 0 };
var scale = currentWearable ? currentWearable.dimensions.x / currentWearable.naturalDimensions.x : 1.0;
var joint = currentWearable ? currentWearable.parentJointIndex : -1;
var soft = currentWearable ? currentWearable.relayParentJoints : false;
softWearableTimer.restart();
positionVector.set(position);
rotationVector.set(rotation);
scalespinner.set(scale);
jointsCombobox.set(joint);
isSoft.set(soft);
if (currentWearable) {
wearableSelected(currentWearable.id);

View file

@ -215,7 +215,8 @@
#include <raypick/LaserPointerScriptingInterface.h>
#include <raypick/PickScriptingInterface.h>
#include <raypick/PointerScriptingInterface.h>
#include <raypick/MouseRayPick.h>
#include <raypick/RayPick.h>
#include <raypick/MouseTransformNode.h>
#include <FadeEffect.h>
@ -2286,8 +2287,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
});
// Setup the mouse ray pick and related operators
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<MouseRayPick>(
PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE()), 0.0f, true)));
{
auto mouseRayPick = std::make_shared<RayPick>(Vectors::ZERO, Vectors::UP, PickFilter(PickScriptingInterface::PICK_ENTITIES() | PickScriptingInterface::PICK_INCLUDE_NONCOLLIDABLE()), 0.0f, true);
mouseRayPick->parentTransform = std::make_shared<MouseTransformNode>();
mouseRayPick->setJointState(PickQuery::JOINT_STATE_MOUSE);
auto mouseRayPickID = DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, mouseRayPick);
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickID(mouseRayPickID);
}
DependencyManager::get<EntityTreeRenderer>()->setMouseRayPickResultOperator([](unsigned int rayPickID) {
RayToEntityIntersectionResult entityResult;
entityResult.intersects = false;

View file

@ -275,7 +275,11 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
if (inView && avatar->hasNewJointData()) {
numAvatarsUpdated++;
}
avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig);
auto transitStatus = avatar->_transit.update(deltaTime, avatar->_globalPosition, _transitConfig);
if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) {
avatar->_transit.reset();
avatar->setIsNewAvatar(false);
}
avatar->simulate(deltaTime, inView);
avatar->updateRenderItem(renderTransaction);
avatar->updateSpaceProxy(workloadTransaction);

View file

@ -345,9 +345,9 @@ void CollisionPick::computeShapeInfo(const CollisionRegion& pick, ShapeInfo& sha
}
}
CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) :
Pick(filter, maxDistance, enabled),
_mathPick(collisionRegion),
CollisionPick::CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, bool scaleWithParent, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine) :
Pick(collisionRegion, filter, maxDistance, enabled),
_scaleWithParent(scaleWithParent),
_physicsEngine(physicsEngine) {
if (collisionRegion.shouldComputeShapeInfo()) {
_cachedResource = DependencyManager::get<ModelCache>()->getCollisionGeometryResource(collisionRegion.modelURL);
@ -361,9 +361,15 @@ CollisionRegion CollisionPick::getMathematicalPick() const {
if (parentTransform) {
Transform parentTransformValue = parentTransform->getTransform();
mathPick.transform = parentTransformValue.worldTransform(mathPick.transform);
glm::vec3 scale = parentTransformValue.getScale();
float largestDimension = glm::max(glm::max(scale.x, scale.y), scale.z);
mathPick.threshold *= largestDimension;
if (_scaleWithParent) {
glm::vec3 scale = parentTransformValue.getScale();
float largestDimension = glm::max(glm::max(scale.x, scale.y), scale.z);
mathPick.threshold *= largestDimension;
} else {
// We need to undo parent scaling after-the-fact because the parent's scale was needed to calculate this mathPick's position
mathPick.transform.setScale(_mathPick.transform.getScale());
}
}
return mathPick;
}
@ -424,5 +430,7 @@ PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick)
}
Transform CollisionPick::getResultTransform() const {
return Transform(getMathematicalPick().transform);
Transform transform;
transform.setTranslation(_mathPick.transform.getTranslation());
return transform;
}

View file

@ -47,7 +47,7 @@ public:
class CollisionPick : public Pick<CollisionRegion> {
public:
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine);
CollisionPick(const PickFilter& filter, float maxDistance, bool enabled, bool scaleWithParent, CollisionRegion collisionRegion, PhysicsEnginePointer physicsEngine);
CollisionRegion getMathematicalPick() const override;
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override {
@ -67,7 +67,8 @@ protected:
void computeShapeInfoDimensionsOnly(const CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer<GeometryResource> resource);
void filterIntersections(std::vector<ContactTestResult>& intersections) const;
CollisionRegion _mathPick;
bool _scaleWithParent;
PhysicsEnginePointer _physicsEngine;
QSharedPointer<GeometryResource> _cachedResource;

View file

@ -1,43 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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 "JointParabolaPick.h"
#include "avatar/AvatarManager.h"
JointParabolaPick::JointParabolaPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset,
float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool scaleWithAvatar, PickFilter& filter, float maxDistance, bool enabled) :
ParabolaPick(speed, accelerationAxis, rotateAccelerationWithAvatar, scaleWithAvatar, filter, maxDistance, enabled),
_jointName(jointName),
_posOffset(posOffset),
_dirOffset(dirOffset)
{
}
PickParabola JointParabolaPick::getMathematicalPick() const {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(QString::fromStdString(_jointName));
bool useAvatarHead = _jointName == "Avatar";
const int INVALID_JOINT = -1;
if (jointIndex != INVALID_JOINT || useAvatarHead) {
glm::vec3 jointPos = useAvatarHead ? myAvatar->getHeadPosition() : myAvatar->getAbsoluteJointTranslationInObjectFrame(jointIndex);
glm::quat jointRot = useAvatarHead ? myAvatar->getHeadOrientation() : myAvatar->getAbsoluteJointRotationInObjectFrame(jointIndex);
glm::vec3 avatarPos = myAvatar->getWorldPosition();
glm::quat avatarRot = myAvatar->getWorldOrientation();
glm::vec3 pos = useAvatarHead ? jointPos : avatarPos + (avatarRot * jointPos);
glm::quat rot = useAvatarHead ? jointRot * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT) : avatarRot * jointRot;
// Apply offset
pos = pos + (rot * (myAvatar->getSensorToWorldScale() * _posOffset));
glm::vec3 dir = glm::normalize(rot * glm::normalize(_dirOffset));
return PickParabola(pos, getSpeed() * dir, getAcceleration());
}
return PickParabola();
}

View file

@ -1,32 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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_JointParabolaPick_h
#define hifi_JointParabolaPick_h
#include "ParabolaPick.h"
class JointParabolaPick : public ParabolaPick {
public:
JointParabolaPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset,
float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool scaleWithAvatar,
PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickParabola getMathematicalPick() const override;
bool isLeftHand() const override { return (_jointName == "_CONTROLLER_LEFTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); }
bool isRightHand() const override { return (_jointName == "_CONTROLLER_RIGHTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); }
private:
std::string _jointName;
glm::vec3 _posOffset;
glm::vec3 _dirOffset;
};
#endif // hifi_JointParabolaPick_h

View file

@ -1,45 +0,0 @@
//
// JointRayPick.cpp
// interface/src/raypick
//
// Created by Sam Gondelman 7/11/2017
// Copyright 2017 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 "JointRayPick.h"
#include "avatar/AvatarManager.h"
JointRayPick::JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const PickFilter& filter, float maxDistance, bool enabled) :
RayPick(filter, maxDistance, enabled),
_jointName(jointName),
_posOffset(posOffset),
_dirOffset(dirOffset)
{
}
PickRay JointRayPick::getMathematicalPick() const {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(QString::fromStdString(_jointName));
bool useAvatarHead = _jointName == "Avatar";
const int INVALID_JOINT = -1;
if (jointIndex != INVALID_JOINT || useAvatarHead) {
glm::vec3 jointPos = useAvatarHead ? myAvatar->getHeadPosition() : myAvatar->getAbsoluteJointTranslationInObjectFrame(jointIndex);
glm::quat jointRot = useAvatarHead ? myAvatar->getHeadOrientation() : myAvatar->getAbsoluteJointRotationInObjectFrame(jointIndex);
glm::vec3 avatarPos = myAvatar->getWorldPosition();
glm::quat avatarRot = myAvatar->getWorldOrientation();
glm::vec3 pos = useAvatarHead ? jointPos : avatarPos + (avatarRot * jointPos);
glm::quat rot = useAvatarHead ? jointRot * glm::angleAxis(-PI / 2.0f, Vectors::RIGHT) : avatarRot * jointRot;
// Apply offset
pos = pos + (rot * (myAvatar->getSensorToWorldScale() * _posOffset));
glm::vec3 dir = glm::normalize(rot * glm::normalize(_dirOffset));
return PickRay(pos, dir);
}
return PickRay();
}

View file

@ -1,33 +0,0 @@
//
// JointRayPick.h
// interface/src/raypick
//
// Created by Sam Gondelman 7/11/2017
// Copyright 2017 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_JointRayPick_h
#define hifi_JointRayPick_h
#include "RayPick.h"
class JointRayPick : public RayPick {
public:
JointRayPick(const std::string& jointName, const glm::vec3& posOffset, const glm::vec3& dirOffset, const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickRay getMathematicalPick() const override;
bool isLeftHand() const override { return (_jointName == "_CONTROLLER_LEFTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND"); }
bool isRightHand() const override { return (_jointName == "_CONTROLLER_RIGHTHAND") || (_jointName == "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND"); }
private:
std::string _jointName;
glm::vec3 _posOffset;
glm::vec3 _dirOffset;
};
#endif // hifi_JointRayPick_h

View file

@ -14,13 +14,14 @@
#include "avatar/AvatarManager.h"
#include <DependencyManager.h>
#include "PickManager.h"
#include "RayPick.h"
LaserPointer::LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover,
const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalTime, bool centerEndY, bool lockEnd,
bool distanceScaleEnd, bool scaleWithAvatar, bool enabled) :
bool distanceScaleEnd, bool scaleWithParent, bool enabled) :
PathPointer(PickQuery::Ray, rayProps, renderStates, defaultRenderStates, hover, triggers, faceAvatar, followNormal, followNormalTime,
centerEndY, lockEnd, distanceScaleEnd, scaleWithAvatar, enabled)
centerEndY, lockEnd, distanceScaleEnd, scaleWithParent, enabled)
{
}
@ -146,9 +147,9 @@ void LaserPointer::RenderState::disable() {
}
}
void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
StartEndRenderState::update(origin, end, surfaceNormal, scaleWithAvatar, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
QVariant endVariant = vec3toVariant(end);
if (!getPathID().isNull()) {
QVariantMap pathProps;
@ -156,9 +157,7 @@ void LaserPointer::RenderState::update(const glm::vec3& origin, const glm::vec3&
pathProps.insert("end", endVariant);
pathProps.insert("visible", true);
pathProps.insert("ignoreRayIntersection", doesPathIgnoreRays());
if (scaleWithAvatar) {
pathProps.insert("lineWidth", getLineWidth() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale());
}
pathProps.insert("lineWidth", getLineWidth() * parentScale);
qApp->getOverlays().editOverlay(getPathID(), pathProps);
}
}

View file

@ -24,12 +24,12 @@ public:
const OverlayID& getPathID() const { return _pathID; }
const bool& doesPathIgnoreRays() const { return _pathIgnoreRays; }
void setLineWidth(const float& lineWidth) { _lineWidth = lineWidth; }
const float& getLineWidth() const { return _lineWidth; }
void setLineWidth(float width) { _lineWidth = width; }
float getLineWidth() const { return _lineWidth; }
void cleanup() override;
void disable() override;
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) override;
private:
@ -40,7 +40,7 @@ public:
};
LaserPointer(const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates, bool hover, const PointerTriggers& triggers,
bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd, bool distanceScaleEnd, bool scaleWithParent, bool enabled);
QVariantMap toVariantMap() const override;

View file

@ -1,28 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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 "MouseParabolaPick.h"
#include "Application.h"
#include "display-plugins/CompositorHelper.h"
MouseParabolaPick::MouseParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar,
bool scaleWithAvatar, const PickFilter& filter, float maxDistance, bool enabled) :
ParabolaPick(speed, accelerationAxis, rotateAccelerationWithAvatar, scaleWithAvatar, filter, maxDistance, enabled)
{
}
PickParabola MouseParabolaPick::getMathematicalPick() const {
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
if (position.isValid()) {
QVariantMap posMap = position.toMap();
PickRay pickRay = qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat());
return PickParabola(pickRay.origin, getSpeed() * pickRay.direction, getAcceleration());
}
return PickParabola();
}

View file

@ -1,24 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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_MouseParabolaPick_h
#define hifi_MouseParabolaPick_h
#include "ParabolaPick.h"
class MouseParabolaPick : public ParabolaPick {
public:
MouseParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool scaleWithAvatar,
const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickParabola getMathematicalPick() const override;
bool isMouse() const override { return true; }
};
#endif // hifi_MouseParabolaPick_h

View file

@ -1,29 +0,0 @@
//
// MouseRayPick.cpp
// interface/src/raypick
//
// Created by Sam Gondelman 7/19/2017
// Copyright 2017 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 "MouseRayPick.h"
#include "Application.h"
#include "display-plugins/CompositorHelper.h"
MouseRayPick::MouseRayPick(const PickFilter& filter, float maxDistance, bool enabled) :
RayPick(filter, maxDistance, enabled)
{
}
PickRay MouseRayPick::getMathematicalPick() const {
QVariant position = qApp->getApplicationCompositor().getReticleInterface()->getPosition();
if (position.isValid()) {
QVariantMap posMap = position.toMap();
return qApp->getCamera().computePickRay(posMap["x"].toFloat(), posMap["y"].toFloat());
}
return PickRay();
}

View file

@ -1,26 +0,0 @@
//
// MouseRayPick.h
// interface/src/raypick
//
// Created by Sam Gondelman 7/19/2017
// Copyright 2017 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_MouseRayPick_h
#define hifi_MouseRayPick_h
#include "RayPick.h"
class MouseRayPick : public RayPick {
public:
MouseRayPick(const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickRay getMathematicalPick() const override;
bool isMouse() const override { return true; }
};
#endif // hifi_MouseRayPick_h

View file

@ -15,6 +15,46 @@
#include "DependencyManager.h"
#include "PickManager.h"
ParabolaPick::ParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool rotateAccelerationWithParent, bool scaleWithParent, const PickFilter& filter, float maxDistance, bool enabled) :
Pick(PickParabola(position, speed * direction, accelerationAxis), filter, maxDistance, enabled),
_rotateAccelerationWithAvatar(rotateAccelerationWithAvatar),
_rotateAccelerationWithParent(rotateAccelerationWithParent),
_scaleWithParent(scaleWithParent),
_speed(speed) {
}
PickParabola ParabolaPick::getMathematicalPick() const {
if (!parentTransform) {
PickParabola mathPick = _mathPick;
if (_rotateAccelerationWithAvatar) {
mathPick.acceleration = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * mathPick.acceleration;
}
return mathPick;
}
Transform currentParentTransform = parentTransform->getTransform();
glm::vec3 position = currentParentTransform.transform(_mathPick.origin);
glm::vec3 velocity = _mathPick.velocity;
if (_scaleWithParent) {
velocity = currentParentTransform.transformDirection(velocity);
} else {
glm::vec3 transformedVelocity = currentParentTransform.transformDirection(velocity);
velocity = glm::normalize(transformedVelocity) * _speed;
}
glm::vec3 acceleration = _mathPick.acceleration;
if (_scaleWithParent) {
acceleration *= currentParentTransform.getScale();
}
if (_rotateAccelerationWithAvatar) {
acceleration = DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * acceleration;
} else if (_rotateAccelerationWithParent) {
acceleration = currentParentTransform.getRotation() * acceleration;
}
return PickParabola(position, velocity, acceleration);
}
PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) {
if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
@ -60,18 +100,6 @@ PickResultPointer ParabolaPick::getHUDIntersection(const PickParabola& pick) {
return std::make_shared<ParabolaPickResult>(pick.toVariantMap());
}
float ParabolaPick::getSpeed() const {
return (_scaleWithAvatar ? DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale() * _speed : _speed);
}
glm::vec3 ParabolaPick::getAcceleration() const {
float scale = (_scaleWithAvatar ? DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale() : 1.0f);
if (_rotateAccelerationWithAvatar) {
return scale * (DependencyManager::get<AvatarManager>()->getMyAvatar()->getWorldOrientation() * _accelerationAxis);
}
return scale * _accelerationAxis;
}
Transform ParabolaPick::getResultTransform() const {
PickResultPointer result = getPrevPickResult();
if (!result) {

View file

@ -74,9 +74,9 @@ public:
class ParabolaPick : public Pick<PickParabola> {
public:
ParabolaPick(float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar, bool scaleWithAvatar, const PickFilter& filter, float maxDistance, bool enabled) :
Pick(filter, maxDistance, enabled), _speed(speed), _accelerationAxis(accelerationAxis), _rotateAccelerationWithAvatar(rotateAccelerationWithAvatar),
_scaleWithAvatar(scaleWithAvatar) {}
ParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& acceleration, bool rotateAccelerationWithAvatar, bool rotateAccelerationWithParent, bool scaleWithParent, const PickFilter& filter, float maxDistance, bool enabled);
PickParabola getMathematicalPick() const override;
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { return std::make_shared<ParabolaPickResult>(pickVariant); }
PickResultPointer getEntityIntersection(const PickParabola& pick) override;
@ -86,13 +86,11 @@ public:
Transform getResultTransform() const override;
protected:
float _speed;
glm::vec3 _accelerationAxis;
bool _rotateAccelerationWithAvatar;
bool _scaleWithAvatar;
float getSpeed() const;
glm::vec3 getAcceleration() const;
bool _rotateAccelerationWithParent;
bool _scaleWithParent;
// Cached magnitude of _mathPick.velocity
float _speed;
};
#endif // hifi_ParabolaPick_h

View file

@ -204,9 +204,9 @@ void ParabolaPointer::RenderState::editParabola(const glm::vec3& color, float al
}
}
void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
StartEndRenderState::update(origin, end, surfaceNormal, scaleWithAvatar, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
StartEndRenderState::update(origin, end, surfaceNormal, parentScale, distanceScaleEnd, centerEndY, faceAvatar, followNormal, followNormalStrength, distance, pickResult);
auto parabolaPickResult = std::static_pointer_cast<ParabolaPickResult>(pickResult);
if (parabolaPickResult && render::Item::isValidID(_pathID)) {
render::Transaction transaction;
@ -216,7 +216,7 @@ void ParabolaPointer::RenderState::update(const glm::vec3& origin, const glm::ve
glm::vec3 velocity = parabola.velocity;
glm::vec3 acceleration = parabola.acceleration;
float parabolicDistance = distance > 0.0f ? distance : parabolaPickResult->parabolicDistance;
float width = scaleWithAvatar ? getPathWidth() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale() : getPathWidth();
float width = getPathWidth() * parentScale;
transaction.updateItem<ParabolaRenderItem>(_pathID, [origin, velocity, acceleration, parabolicDistance, width](ParabolaRenderItem& item) {
item.setVisible(true);
item.setOrigin(origin);

View file

@ -79,7 +79,7 @@ public:
};
RenderState() {}
RenderState(const OverlayID& startID, const OverlayID& endID, const glm::vec3& pathColor, float pathAlpha, float pathWidth,
RenderState(const OverlayID& startID, const OverlayID& endID, const glm::vec3& pathColor, float pathAlpha, float parentScale,
bool isVisibleInSecondaryCamera, bool drawInFront, bool pathEnabled);
void setPathWidth(float width) { _pathWidth = width; }
@ -87,7 +87,7 @@ public:
void cleanup() override;
void disable() override;
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) override;
void editParabola(const glm::vec3& color, float alpha, float width, bool isVisibleInSecondaryCamera, bool drawInFront, bool enabled);

View file

@ -17,7 +17,7 @@
PathPointer::PathPointer(PickQuery::PickType type, const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd,
bool distanceScaleEnd, bool scaleWithAvatar, bool enabled) :
bool distanceScaleEnd, bool scaleWithParent, bool enabled) :
Pointer(DependencyManager::get<PickScriptingInterface>()->createPick(type, rayProps), enabled, hover),
_renderStates(renderStates),
_defaultRenderStates(defaultRenderStates),
@ -28,7 +28,7 @@ PathPointer::PathPointer(PickQuery::PickType type, const QVariant& rayProps, con
_centerEndY(centerEndY),
_lockEnd(lockEnd),
_distanceScaleEnd(distanceScaleEnd),
_scaleWithAvatar(scaleWithAvatar)
_scaleWithParent(scaleWithParent)
{
for (auto& state : _renderStates) {
if (!enabled || state.first != _currentRenderState) {
@ -146,12 +146,18 @@ void PathPointer::updateVisuals(const PickResultPointer& pickResult) {
IntersectionType type = getPickedObjectType(pickResult);
auto renderState = _renderStates.find(_currentRenderState);
auto defaultRenderState = _defaultRenderStates.find(_currentRenderState);
float parentScale = 1.0f;
if (_enabled && _scaleWithParent) {
glm::vec3 dimensions = DependencyManager::get<PickManager>()->getParentTransform(_pickUID).getScale();
parentScale = glm::max(glm::max(dimensions.x, dimensions.y), dimensions.z);
}
if (_enabled && !_currentRenderState.empty() && renderState != _renderStates.end() &&
(type != IntersectionType::NONE || _pathLength > 0.0f)) {
glm::vec3 origin = getPickOrigin(pickResult);
glm::vec3 end = getPickEnd(pickResult, _pathLength);
glm::vec3 surfaceNormal = getPickedObjectNormal(pickResult);
renderState->second->update(origin, end, surfaceNormal, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, _faceAvatar,
renderState->second->update(origin, end, surfaceNormal, parentScale, _distanceScaleEnd, _centerEndY, _faceAvatar,
_followNormal, _followNormalStrength, _pathLength, pickResult);
if (defaultRenderState != _defaultRenderStates.end() && defaultRenderState->second.second->isEnabled()) {
defaultRenderState->second.second->disable();
@ -162,7 +168,7 @@ void PathPointer::updateVisuals(const PickResultPointer& pickResult) {
}
glm::vec3 origin = getPickOrigin(pickResult);
glm::vec3 end = getPickEnd(pickResult, defaultRenderState->second.first);
defaultRenderState->second.second->update(origin, end, Vectors::UP, _scaleWithAvatar, _distanceScaleEnd, _centerEndY,
defaultRenderState->second.second->update(origin, end, Vectors::UP, parentScale, _distanceScaleEnd, _centerEndY,
_faceAvatar, _followNormal, _followNormalStrength, defaultRenderState->second.first, pickResult);
} else if (!_currentRenderState.empty()) {
if (renderState != _renderStates.end() && renderState->second->isEnabled()) {
@ -281,15 +287,13 @@ void StartEndRenderState::disable() {
_enabled = false;
}
void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult) {
if (!getStartID().isNull()) {
QVariantMap startProps;
startProps.insert("position", vec3toVariant(origin));
startProps.insert("visible", true);
if (scaleWithAvatar) {
startProps.insert("dimensions", vec3toVariant(getStartDim() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale()));
}
startProps.insert("dimensions", vec3toVariant(getStartDim() * parentScale));
startProps.insert("ignoreRayIntersection", doesStartIgnoreRays());
qApp->getOverlays().editOverlay(getStartID(), startProps);
}
@ -300,8 +304,8 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end,
if (distanceScaleEnd) {
dim = getEndDim() * glm::distance(origin, end);
endProps.insert("dimensions", vec3toVariant(dim));
} else if (scaleWithAvatar) {
dim = getEndDim() * DependencyManager::get<AvatarManager>()->getMyAvatar()->getSensorToWorldScale();
} else {
dim = getEndDim() * parentScale;
endProps.insert("dimensions", vec3toVariant(dim));
}

View file

@ -45,7 +45,7 @@ public:
virtual void cleanup();
virtual void disable();
virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY,
virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, float parentScale, bool distanceScaleEnd, bool centerEndY,
bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult);
bool isEnabled() const { return _enabled; }
@ -74,7 +74,7 @@ class PathPointer : public Pointer {
public:
PathPointer(PickQuery::PickType type, const QVariant& rayProps, const RenderStateMap& renderStates, const DefaultRenderStateMap& defaultRenderStates,
bool hover, const PointerTriggers& triggers, bool faceAvatar, bool followNormal, float followNormalStrength, bool centerEndY, bool lockEnd,
bool distanceScaleEnd, bool scaleWithAvatar, bool enabled);
bool distanceScaleEnd, bool scaleWithParent, bool enabled);
virtual ~PathPointer();
void setRenderState(const std::string& state) override;
@ -98,7 +98,7 @@ protected:
bool _centerEndY;
bool _lockEnd;
bool _distanceScaleEnd;
bool _scaleWithAvatar;
bool _scaleWithParent;
LockEndObject _lockEndObject;
struct TriggerState {

View file

@ -14,13 +14,9 @@
#include "Application.h"
#include <PickManager.h>
#include "StaticRayPick.h"
#include "JointRayPick.h"
#include "MouseRayPick.h"
#include "RayPick.h"
#include "StylusPick.h"
#include "StaticParabolaPick.h"
#include "JointParabolaPick.h"
#include "MouseParabolaPick.h"
#include "ParabolaPick.h"
#include "CollisionPick.h"
#include "SpatialParentFinder.h"
@ -56,9 +52,9 @@ unsigned int PickScriptingInterface::createPick(const PickQuery::PickType type,
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {string} [joint] Only for Joint or Mouse Ray Picks. If "Mouse", it will create a Ray Pick that follows the system mouse, in desktop or HMD.
* If "Avatar", it will create a Joint Ray Pick that follows your avatar's head. Otherwise, it will create a Joint Ray Pick that follows the given joint, if it
* exists on your current avatar.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick.
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
* @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Ray Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral
* @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Ray Picks. A local joint direction offset. x = upward, y = forward, z = lateral
* @property {Vec3} [position] Only for Static Ray Picks. The world-space origin of the ray.
@ -82,38 +78,29 @@ unsigned int PickScriptingInterface::createRayPick(const QVariant& properties) {
maxDistance = propMap["maxDistance"].toFloat();
}
if (propMap["joint"].isValid()) {
std::string jointName = propMap["joint"].toString().toStdString();
if (jointName != "Mouse") {
// x = upward, y = forward, z = lateral
glm::vec3 posOffset = Vectors::ZERO;
if (propMap["posOffset"].isValid()) {
posOffset = vec3FromVariant(propMap["posOffset"]);
}
glm::vec3 dirOffset = Vectors::UP;
if (propMap["dirOffset"].isValid()) {
dirOffset = vec3FromVariant(propMap["dirOffset"]);
}
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<JointRayPick>(jointName, posOffset, dirOffset, filter, maxDistance, enabled));
} else {
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<MouseRayPick>(filter, maxDistance, enabled));
}
} else if (propMap["position"].isValid()) {
glm::vec3 position = vec3FromVariant(propMap["position"]);
glm::vec3 direction = -Vectors::UP;
if (propMap["direction"].isValid()) {
direction = vec3FromVariant(propMap["direction"]);
}
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, std::make_shared<StaticRayPick>(position, direction, filter, maxDistance, enabled));
glm::vec3 position = Vectors::ZERO;
if (propMap["position"].isValid()) {
position = vec3FromVariant(propMap["position"]);
} else if (propMap["posOffset"].isValid()) {
position = vec3FromVariant(propMap["posOffset"]);
}
return PickManager::INVALID_PICK_ID;
// direction has two defaults to ensure compatibility with older scripts
// Joint ray picks had default direction = Vec3.UP
// Static ray picks had default direction = -Vec3.UP
glm::vec3 direction = propMap["joint"].isValid() ? Vectors::UP : -Vectors::UP;
if (propMap["orientation"].isValid()) {
direction = quatFromVariant(propMap["orientation"]) * Vectors::UP;
} else if (propMap["direction"].isValid()) {
direction = vec3FromVariant(propMap["direction"]);
} else if (propMap["dirOffset"].isValid()) {
direction = vec3FromVariant(propMap["dirOffset"]);
}
auto rayPick = std::make_shared<RayPick>(position, direction, filter, maxDistance, enabled);
setParentTransform(rayPick, propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Ray, rayPick);
}
/**jsdoc
@ -153,23 +140,25 @@ unsigned int PickScriptingInterface::createStylusPick(const QVariant& properties
return DependencyManager::get<PickManager>()->addPick(PickQuery::Stylus, std::make_shared<StylusPick>(side, filter, maxDistance, enabled));
}
// NOTE: Laser pointer still uses scaleWithAvatar. Until scaleWithAvatar is also deprecated for pointers, scaleWithAvatar should not be removed from the pick API.
/**jsdoc
* A set of properties that can be passed to {@link Picks.createPick} to create a new Parabola Pick.
* @typedef {object} Picks.ParabolaPickProperties
* @property {boolean} [enabled=false] If this Pick should start enabled or not. Disabled Picks do not updated their pick results.
* @property {number} [filter=Picks.PICK_NOTHING] The filter for this Pick to use, constructed using filter flags combined using bitwise OR.
* @property {number} [maxDistance=0.0] The max distance at which this Pick will intersect. 0.0 = no max. < 0.0 is invalid.
* @property {string} [joint] Only for Joint or Mouse Parabola Picks. If "Mouse", it will create a Parabola Pick that follows the system mouse, in desktop or HMD.
* If "Avatar", it will create a Joint Parabola Pick that follows your avatar's head. Otherwise, it will create a Joint Parabola Pick that follows the given joint, if it
* exists on your current avatar.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick.
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
* @property {Vec3} [posOffset=Vec3.ZERO] Only for Joint Parabola Picks. A local joint position offset, in meters. x = upward, y = forward, z = lateral
* @property {Vec3} [dirOffset=Vec3.UP] Only for Joint Parabola Picks. A local joint direction offset. x = upward, y = forward, z = lateral
* @property {Vec3} [position] Only for Static Parabola Picks. The world-space origin of the parabola segment.
* @property {Vec3} [direction=-Vec3.FRONT] Only for Static Parabola Picks. The world-space direction of the parabola segment.
* @property {number} [speed=1] The initial speed of the parabola, i.e. the initial speed of the projectile whose trajectory defines the parabola.
* @property {Vec3} [accelerationAxis=-Vec3.UP] The acceleration of the parabola, i.e. the acceleration of the projectile whose trajectory defines the parabola, both magnitude and direction.
* @property {boolean} [rotateAccelerationWithAvatar=true] Whether or not the acceleration axis should rotate with your avatar's local Y axis.
* @property {boolean} [scaleWithAvatar=false] If true, the velocity and acceleration of the Pick will scale linearly with your avatar.
* @property {boolean} [rotateAccelerationWithAvatar=true] Whether or not the acceleration axis should rotate with the avatar's local Y axis.
* @property {boolean} [rotateAccelerationWithParent=false] Whether or not the acceleration axis should rotate with the parent's local Y axis, if available.
* @property {boolean} [scaleWithParent=true] If true, the velocity and acceleration of the Pick will scale linearly with the parent, if available. scaleWithAvatar is an alias but is deprecated.
*/
unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properties) {
QVariantMap propMap = properties.toMap();
@ -204,48 +193,37 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
rotateAccelerationWithAvatar = propMap["rotateAccelerationWithAvatar"].toBool();
}
bool scaleWithAvatar = false;
if (propMap["scaleWithAvatar"].isValid()) {
scaleWithAvatar = propMap["scaleWithAvatar"].toBool();
bool rotateAccelerationWithParent = false;
if (propMap["rotateAccelerationWithParent"].isValid()) {
rotateAccelerationWithParent = propMap["rotateAccelerationWithParent"].toBool();
}
if (propMap["joint"].isValid()) {
std::string jointName = propMap["joint"].toString().toStdString();
if (jointName != "Mouse") {
// x = upward, y = forward, z = lateral
glm::vec3 posOffset = Vectors::ZERO;
if (propMap["posOffset"].isValid()) {
posOffset = vec3FromVariant(propMap["posOffset"]);
}
glm::vec3 dirOffset = Vectors::UP;
if (propMap["dirOffset"].isValid()) {
dirOffset = vec3FromVariant(propMap["dirOffset"]);
}
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<JointParabolaPick>(jointName, posOffset, dirOffset,
speed, accelerationAxis, rotateAccelerationWithAvatar,
scaleWithAvatar, filter, maxDistance, enabled));
} else {
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<MouseParabolaPick>(speed, accelerationAxis, rotateAccelerationWithAvatar,
scaleWithAvatar, filter, maxDistance, enabled));
}
} else if (propMap["position"].isValid()) {
glm::vec3 position = vec3FromVariant(propMap["position"]);
glm::vec3 direction = -Vectors::FRONT;
if (propMap["direction"].isValid()) {
direction = vec3FromVariant(propMap["direction"]);
}
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, std::make_shared<StaticParabolaPick>(position, direction, speed, accelerationAxis,
rotateAccelerationWithAvatar, scaleWithAvatar,
filter, maxDistance, enabled));
bool scaleWithParent = true;
if (propMap["scaleWithParent"].isValid()) {
scaleWithParent = propMap["scaleWithParent"].toBool();
} else if (propMap["scaleWithAvatar"].isValid()) {
scaleWithParent = propMap["scaleWithAvatar"].toBool();
}
return PickManager::INVALID_PICK_ID;
glm::vec3 position = Vectors::ZERO;
glm::vec3 direction = propMap["joint"].isValid() ? Vectors::UP : -Vectors::FRONT;
if (propMap["position"].isValid()) {
position = vec3FromVariant(propMap["position"]);
} else if (propMap["posOffset"].isValid()) {
position = vec3FromVariant(propMap["posOffset"]);
}
if (propMap["orientation"].isValid()) {
direction = quatFromVariant(propMap["orientation"]) * Vectors::UP;
} else if (propMap["direction"].isValid()) {
direction = vec3FromVariant(propMap["direction"]);
} else if (propMap["dirOffset"].isValid()) {
direction = vec3FromVariant(propMap["dirOffset"]);
}
auto parabolaPick = std::make_shared<ParabolaPick>(position, direction, speed, accelerationAxis,
rotateAccelerationWithAvatar, rotateAccelerationWithParent, scaleWithParent, filter, maxDistance, enabled);
setParentTransform(parabolaPick, propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Parabola, parabolaPick);
}
/**jsdoc
@ -272,9 +250,10 @@ unsigned int PickScriptingInterface::createParabolaPick(const QVariant& properti
* The depth is measured in world space, but will scale with the parent if defined.
* @property {CollisionMask} [collisionGroup=8] - The type of object this collision pick collides as. Objects whose collision masks overlap with the pick's collision group
* will be considered colliding with the pick.
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, or an overlay.
* @property {number} parentJointIndex - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {Uuid} parentID - The ID of the parent, either an avatar, an entity, an overlay, or a pick.
* @property {number} [parentJointIndex=0] - The joint of the parent to parent to, for example, the joints on the model of an avatar. (default = 0, no joint)
* @property {string} joint - If "Mouse," parents the pick to the mouse. If "Avatar," parents the pick to MyAvatar's head. Otherwise, parents to the joint of the given name on MyAvatar.
* @property {boolean} [scaleWithParent=true] If true, the collision pick's dimensions and threshold will adjust according to the scale of the parent.
*/
unsigned int PickScriptingInterface::createCollisionPick(const QVariant& properties) {
QVariantMap propMap = properties.toMap();
@ -294,9 +273,14 @@ unsigned int PickScriptingInterface::createCollisionPick(const QVariant& propert
maxDistance = propMap["maxDistance"].toFloat();
}
bool scaleWithParent = true;
if (propMap["scaleWithParent"].isValid()) {
scaleWithParent = propMap["scaleWithParent"].toBool();
}
CollisionRegion collisionRegion(propMap);
auto collisionPick = std::make_shared<CollisionPick>(filter, maxDistance, enabled, collisionRegion, qApp->getPhysicsEngine());
collisionPick->parentTransform = createTransformNode(propMap);
auto collisionPick = std::make_shared<CollisionPick>(filter, maxDistance, enabled, scaleWithParent, collisionRegion, qApp->getPhysicsEngine());
setParentTransform(collisionPick, propMap);
return DependencyManager::get<PickManager>()->addPick(PickQuery::Collision, collisionPick);
}
@ -373,51 +357,63 @@ void PickScriptingInterface::setPerFrameTimeBudget(unsigned int numUsecs) {
DependencyManager::get<PickManager>()->setPerFrameTimeBudget(numUsecs);
}
std::shared_ptr<TransformNode> PickScriptingInterface::createTransformNode(const QVariantMap& propMap) {
if (propMap["parentID"].isValid()) {
QUuid parentUuid = propMap["parentID"].toUuid();
if (!parentUuid.isNull()) {
// Infer object type from parentID
// For now, assume a QUuuid is a SpatiallyNestable. This should change when picks are converted over to QUuids.
bool success;
std::weak_ptr<SpatiallyNestable> nestablePointer = DependencyManager::get<SpatialParentFinder>()->find(parentUuid, success, nullptr);
int parentJointIndex = 0;
if (propMap["parentJointIndex"].isValid()) {
parentJointIndex = propMap["parentJointIndex"].toInt();
}
auto sharedNestablePointer = nestablePointer.lock();
if (success && sharedNestablePointer) {
NestableType nestableType = sharedNestablePointer->getNestableType();
if (nestableType == NestableType::Avatar) {
return std::make_shared<AvatarTransformNode>(std::static_pointer_cast<Avatar>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Overlay) {
return std::make_shared<OverlayTransformNode>(std::static_pointer_cast<Base3DOverlay>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Entity) {
return std::make_shared<EntityTransformNode>(std::static_pointer_cast<EntityItem>(sharedNestablePointer), parentJointIndex);
} else {
return std::make_shared<NestableTransformNode>(nestablePointer, parentJointIndex);
}
}
}
void PickScriptingInterface::setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap) {
QUuid parentUuid;
int parentJointIndex = 0;
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
unsigned int pickID = propMap["parentID"].toUInt();
if (pickID != 0) {
return std::make_shared<PickTransformNode>(pickID);
if (propMap["parentID"].isValid()) {
parentUuid = propMap["parentID"].toUuid();
if (propMap["parentJointIndex"].isValid()) {
parentJointIndex = propMap["parentJointIndex"].toInt();
}
}
if (propMap["joint"].isValid()) {
} else if (propMap["joint"].isValid()) {
QString joint = propMap["joint"].toString();
if (joint == "Mouse") {
return std::make_shared<MouseTransformNode>();
pick->parentTransform = std::make_shared<MouseTransformNode>();
pick->setJointState(PickQuery::JOINT_STATE_MOUSE);
return;
} else if (joint == "Avatar") {
return std::make_shared<MyAvatarHeadTransformNode>();
} else if (!joint.isNull()) {
auto myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
int jointIndex = myAvatar->getJointIndex(joint);
return std::make_shared<AvatarTransformNode>(myAvatar, jointIndex);
pick->parentTransform = std::make_shared<MyAvatarHeadTransformNode>();
return;
} else {
parentUuid = myAvatar->getSessionUUID();
parentJointIndex = myAvatar->getJointIndex(joint);
}
}
return std::shared_ptr<TransformNode>();
if (parentUuid == myAvatar->getSessionUUID()) {
if (parentJointIndex == CONTROLLER_LEFTHAND_INDEX || parentJointIndex == CAMERA_RELATIVE_CONTROLLER_LEFTHAND_INDEX) {
pick->setJointState(PickQuery::JOINT_STATE_LEFT_HAND);
} else if (parentJointIndex == CONTROLLER_RIGHTHAND_INDEX || parentJointIndex == CAMERA_RELATIVE_CONTROLLER_RIGHTHAND_INDEX) {
pick->setJointState(PickQuery::JOINT_STATE_RIGHT_HAND);
}
pick->parentTransform = std::make_shared<AvatarTransformNode>(myAvatar, parentJointIndex);
} else if (!parentUuid.isNull()) {
// Infer object type from parentID
// For now, assume a QUuid is a SpatiallyNestable. This should change when picks are converted over to QUuids.
bool success;
std::weak_ptr<SpatiallyNestable> nestablePointer = DependencyManager::get<SpatialParentFinder>()->find(parentUuid, success, nullptr);
auto sharedNestablePointer = nestablePointer.lock();
if (success && sharedNestablePointer) {
NestableType nestableType = sharedNestablePointer->getNestableType();
if (nestableType == NestableType::Avatar) {
pick->parentTransform = std::make_shared<AvatarTransformNode>(std::static_pointer_cast<Avatar>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Overlay) {
pick->parentTransform = std::make_shared<OverlayTransformNode>(std::static_pointer_cast<Base3DOverlay>(sharedNestablePointer), parentJointIndex);
} else if (nestableType == NestableType::Entity) {
pick->parentTransform = std::make_shared<EntityTransformNode>(std::static_pointer_cast<EntityItem>(sharedNestablePointer), parentJointIndex);
} else {
pick->parentTransform = std::make_shared<NestableTransformNode>(nestablePointer, parentJointIndex);
}
}
} else {
unsigned int pickID = propMap["parentID"].toUInt();
if (pickID != 0) {
pick->parentTransform = std::make_shared<PickTransformNode>(pickID);
}
}
}

View file

@ -320,7 +320,7 @@ public slots:
static constexpr unsigned int INTERSECTED_HUD() { return IntersectionType::HUD; }
protected:
static std::shared_ptr<TransformNode> createTransformNode(const QVariantMap& propMap);
static void setParentTransform(std::shared_ptr<PickQuery> pick, const QVariantMap& propMap);
};
#endif // hifi_PickScriptingInterface_h

View file

@ -97,7 +97,7 @@ unsigned int PointerScriptingInterface::createStylus(const QVariant& properties)
* @property {boolean} [centerEndY=true] If false, the end of the Pointer will be moved up by half of its height.
* @property {boolean} [lockEnd=false] If true, the end of the Pointer will lock on to the center of the object at which the pointer is pointing.
* @property {boolean} [distanceScaleEnd=false] If true, the dimensions of the end of the Pointer will scale linearly with distance.
* @property {boolean} [scaleWithAvatar=false] If true, the width of the Pointer's path will scale linearly with your avatar's scale.
* @property {boolean} [scaleWithParent=false] If true, the width of the Pointer's path will scale linearly with the pick parent's scale. scaleWithAvatar is an alias but is deprecated.
* @property {boolean} [followNormal=false] If true, the end of the Pointer will rotate to follow the normal of the intersected surface.
* @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
* the normal will follow exactly.
@ -134,9 +134,11 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
distanceScaleEnd = propertyMap["distanceScaleEnd"].toBool();
}
bool scaleWithAvatar = false;
if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
bool scaleWithParent = false;
if (propertyMap["scaleWithParent"].isValid()) {
scaleWithParent = propertyMap["scaleWithParent"].toBool();
} else if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithParent = propertyMap["scaleWithAvatar"].toBool();
}
bool followNormal = false;
@ -207,7 +209,7 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<LaserPointer>(properties, renderStates, defaultRenderStates, hover, triggers,
faceAvatar, followNormal, followNormalStrength, centerEndY, lockEnd,
distanceScaleEnd, scaleWithAvatar, enabled));
distanceScaleEnd, scaleWithParent, enabled));
}
/**jsdoc
@ -249,7 +251,7 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
* @property {boolean} [centerEndY=true] If false, the end of the Pointer will be moved up by half of its height.
* @property {boolean} [lockEnd=false] If true, the end of the Pointer will lock on to the center of the object at which the pointer is pointing.
* @property {boolean} [distanceScaleEnd=false] If true, the dimensions of the end of the Pointer will scale linearly with distance.
* @property {boolean} [scaleWithAvatar=false] If true, the width of the Pointer's path will scale linearly with your avatar's scale.
* @property {boolean} [scaleWithParent=true] If true, the width of the Pointer's path will scale linearly with the pick parent's scale. scaleWithAvatar is an alias but is deprecated.
* @property {boolean} [followNormal=false] If true, the end of the Pointer will rotate to follow the normal of the intersected surface.
* @property {number} [followNormalStrength=0.0] The strength of the interpolation between the real normal and the visual normal if followNormal is true. <code>0-1</code>. If 0 or 1,
* the normal will follow exactly.
@ -286,9 +288,11 @@ unsigned int PointerScriptingInterface::createParabolaPointer(const QVariant& pr
distanceScaleEnd = propertyMap["distanceScaleEnd"].toBool();
}
bool scaleWithAvatar = false;
if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithAvatar = propertyMap["scaleWithAvatar"].toBool();
bool scaleWithParent = true;
if (propertyMap["scaleWithParent"].isValid()) {
scaleWithParent = propertyMap["scaleWithParent"].toBool();
} else if (propertyMap["scaleWithAvatar"].isValid()) {
scaleWithParent = propertyMap["scaleWithAvatar"].toBool();
}
bool followNormal = false;
@ -359,7 +363,7 @@ unsigned int PointerScriptingInterface::createParabolaPointer(const QVariant& pr
return DependencyManager::get<PointerManager>()->addPointer(std::make_shared<ParabolaPointer>(properties, renderStates, defaultRenderStates, hover, triggers,
faceAvatar, followNormal, followNormalStrength, centerEndY, lockEnd, distanceScaleEnd,
scaleWithAvatar, enabled));
scaleWithParent, enabled));
}
void PointerScriptingInterface::editRenderState(unsigned int uid, const QString& renderState, const QVariant& properties) const {

View file

@ -15,6 +15,17 @@
#include "DependencyManager.h"
#include "PickManager.h"
PickRay RayPick::getMathematicalPick() const {
if (!parentTransform) {
return _mathPick;
}
Transform currentParentTransform = parentTransform->getTransform();
glm::vec3 origin = currentParentTransform.transform(_mathPick.origin);
glm::vec3 direction = glm::normalize(currentParentTransform.transformDirection(_mathPick.direction));
return PickRay(origin, direction);
}
PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) {
bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get<PickManager>()->getForceCoarsePicking());
RayToEntityIntersectionResult entityRes =

View file

@ -70,7 +70,11 @@ public:
class RayPick : public Pick<PickRay> {
public:
RayPick(const PickFilter& filter, float maxDistance, bool enabled) : Pick(filter, maxDistance, enabled) {}
RayPick(glm::vec3 position, glm::vec3 direction, const PickFilter& filter, float maxDistance, bool enabled) :
Pick(PickRay(position, direction), filter, maxDistance, enabled) {
}
PickRay getMathematicalPick() const override;
PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { return std::make_shared<RayPickResult>(pickVariant); }
PickResultPointer getEntityIntersection(const PickRay& pick) override;

View file

@ -16,10 +16,6 @@
#include <PickManager.h>
#include "StaticRayPick.h"
#include "JointRayPick.h"
#include "MouseRayPick.h"
unsigned int RayPickScriptingInterface::createRayPick(const QVariant& properties) {
return DependencyManager::get<PickScriptingInterface>()->createRayPick(properties);
}

View file

@ -1,19 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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 "StaticParabolaPick.h"
StaticParabolaPick::StaticParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& accelerationAxis,
bool scaleWithAvatar, bool rotateAccelerationWithAvatar, const PickFilter& filter, float maxDistance, bool enabled) :
ParabolaPick(speed, accelerationAxis, rotateAccelerationWithAvatar, scaleWithAvatar, filter, maxDistance, enabled),
_position(position), _velocity(direction)
{
}
PickParabola StaticParabolaPick::getMathematicalPick() const {
return PickParabola(_position, getSpeed() * _velocity, getAcceleration());
}

View file

@ -1,26 +0,0 @@
//
// Created by Sam Gondelman 7/2/2018
// Copyright 2018 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_StaticParabolaPick_h
#define hifi_StaticParabolaPick_h
#include "ParabolaPick.h"
class StaticParabolaPick : public ParabolaPick {
public:
StaticParabolaPick(const glm::vec3& position, const glm::vec3& direction, float speed, const glm::vec3& accelerationAxis, bool rotateAccelerationWithAvatar,
bool scaleWithAvatar, const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickParabola getMathematicalPick() const override;
private:
glm::vec3 _position;
glm::vec3 _velocity;
};
#endif // hifi_StaticParabolaPick_h

View file

@ -1,18 +0,0 @@
//
// Created by Sam Gondelman 7/11/2017
// Copyright 2017 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 "StaticRayPick.h"
StaticRayPick::StaticRayPick(const glm::vec3& position, const glm::vec3& direction, const PickFilter& filter, float maxDistance, bool enabled) :
RayPick(filter, maxDistance, enabled),
_pickRay(position, direction)
{
}
PickRay StaticRayPick::getMathematicalPick() const {
return _pickRay;
}

View file

@ -1,25 +0,0 @@
//
// Created by Sam Gondelman 7/11/2017
// Copyright 2017 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_StaticRayPick_h
#define hifi_StaticRayPick_h
#include "RayPick.h"
class StaticRayPick : public RayPick {
public:
StaticRayPick(const glm::vec3& position, const glm::vec3& direction, const PickFilter& filter, float maxDistance = 0.0f, bool enabled = false);
PickRay getMathematicalPick() const override;
private:
PickRay _pickRay;
};
#endif // hifi_StaticRayPick_h

View file

@ -65,8 +65,7 @@ bool StylusPickResult::checkOrFilterAgainstMaxDistance(float maxDistance) {
}
StylusPick::StylusPick(Side side, const PickFilter& filter, float maxDistance, bool enabled) :
Pick(filter, maxDistance, enabled),
_side(side)
Pick(StylusTip(side), filter, maxDistance, enabled)
{
}
@ -130,9 +129,9 @@ static StylusTip getControllerWorldLocation(Side side) {
StylusTip StylusPick::getMathematicalPick() const {
StylusTip result;
if (qApp->getPreferAvatarFingerOverStylus()) {
result = getFingerWorldLocation(_side);
result = getFingerWorldLocation(_mathPick.side);
} else {
result = getControllerWorldLocation(_side);
result = getControllerWorldLocation(_mathPick.side);
}
return result;
}

View file

@ -68,11 +68,9 @@ public:
PickResultPointer getHUDIntersection(const StylusTip& pick) override;
Transform getResultTransform() const override;
bool isLeftHand() const override { return _side == Side::Left; }
bool isRightHand() const override { return _side == Side::Right; }
private:
const Side _side;
bool isLeftHand() const override { return _mathPick.side == Side::Left; }
bool isRightHand() const override { return _mathPick.side == Side::Right; }
bool isMouse() const override { return false; }
};
#endif // hifi_StylusPick_h

View file

@ -118,14 +118,24 @@ AvatarTransit::Status AvatarTransit::update(float deltaTime, const glm::vec3& av
float oneFrameDistance = glm::length(currentPosition - _lastPosition);
const float MAX_TRANSIT_DISTANCE = 30.0f;
float scaledMaxTransitDistance = MAX_TRANSIT_DISTANCE * _scale;
if (oneFrameDistance > config._triggerDistance && oneFrameDistance < scaledMaxTransitDistance && !_isTransiting) {
start(deltaTime, _lastPosition, currentPosition, config);
if (oneFrameDistance > config._triggerDistance && !_isTransiting) {
if (oneFrameDistance < scaledMaxTransitDistance) {
start(deltaTime, _lastPosition, currentPosition, config);
} else {
_lastPosition = currentPosition;
return Status::ABORT_TRANSIT;
}
}
_lastPosition = currentPosition;
_status = updatePosition(deltaTime);
return _status;
}
void AvatarTransit::reset() {
_lastPosition = _endPosition;
_currentPosition = _endPosition;
_isTransiting = false;
}
void AvatarTransit::start(float deltaTime, const glm::vec3& startPosition, const glm::vec3& endPosition, const AvatarTransit::TransitConfig& config) {
_startPosition = startPosition;
_endPosition = endPosition;
@ -510,9 +520,13 @@ void Avatar::relayJointDataToChildren() {
modelEntity->setLocalJointTranslation(jointIndex, jointTranslation);
}
}
Transform finalTransform;
Transform avatarTransform = _skeletonModel->getTransform();
avatarTransform.setScale(_skeletonModel->getScale());
modelEntity->setOverrideTransform(avatarTransform, _skeletonModel->getOffset());
Transform entityTransform = modelEntity->getLocalTransform();
Transform::mult(finalTransform, avatarTransform, entityTransform);
finalTransform.setScale(_skeletonModel->getScale());
modelEntity->setOverrideTransform(finalTransform, _skeletonModel->getOffset());
modelEntity->simulateRelayedJoints();
}
}

View file

@ -56,7 +56,8 @@ public:
IDLE = 0,
START_TRANSIT,
TRANSITING,
END_TRANSIT
END_TRANSIT,
ABORT_TRANSIT
};
enum EaseType {
@ -84,6 +85,7 @@ public:
glm::vec3 getEndPosition() { return _endPosition; }
float getTransitTime() { return _totalTime; }
void setScale(float scale) { _scale = scale; }
void reset();
private:
Status updatePosition(float deltaTime);

View file

@ -594,7 +594,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
// The dot product for larger rotations is a lower number.
// So if the dot() is less than the value, then the rotation is a larger angle of rotation
if (sendAll || last.rotationIsDefaultPose || (!cullSmallChanges && last.rotation != data.rotation)
|| (cullSmallChanges && glm::dot(last.rotation, data.rotation) < minRotationDOT) ) {
|| (cullSmallChanges && fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT) ) {
validity |= (1 << validityBit);
#ifdef WANT_DEBUG
rotationSentCount++;

View file

@ -1190,6 +1190,9 @@ public:
void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; }
int getReplicaIndex() { return _replicaIndex; }
void setIsNewAvatar(bool isNewAvatar) { _isNewAvatar = isNewAvatar; }
bool getIsNewAvatar() { return _isNewAvatar; }
signals:
/**jsdoc
@ -1452,6 +1455,7 @@ protected:
bool _hasProcessedFirstIdentity { false };
float _density;
int _replicaIndex { 0 };
bool _isNewAvatar { true };
// null unless MyAvatar or ScriptableAvatar sending traits data to mixer
std::unique_ptr<ClientTraitsHandler> _clientTraitsHandler;

View file

@ -259,18 +259,20 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer<ReceivedMessag
if (isNewAvatar) {
QWriteLocker locker(&_hashLock);
_pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar });
avatar->setIsNewAvatar(true);
auto replicaIDs = _replicas.getReplicaIDs(sessionUUID);
for (auto replicaID : replicaIDs) {
auto replicaAvatar = addAvatar(replicaID, sendingNode);
replicaAvatar->setIsNewAvatar(true);
_replicas.addReplica(sessionUUID, replicaAvatar);
}
}
// have the matching (or new) avatar parse the data from the packet
int bytesRead = avatar->parseDataFromBuffer(byteArray);
message->seek(positionBeforeRead + bytesRead);
_replicas.parseDataFromBuffer(sessionUUID, byteArray);
return avatar;
} else {

View file

@ -1754,8 +1754,9 @@ int EntityScriptingInterface::getJointIndex(const QUuid& entityID, const QString
return -1;
}
int result;
BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointIndex",
Q_RETURN_ARG(int, result), Q_ARG(QUuid, entityID), Q_ARG(QString, name));
_entityTree->withReadLock([&] {
result = _entityTree->getJointIndex(entityID, name);
});
return result;
}
@ -1764,8 +1765,9 @@ QStringList EntityScriptingInterface::getJointNames(const QUuid& entityID) {
return QStringList();
}
QStringList result;
BLOCKING_INVOKE_METHOD(_entityTree.get(), "getJointNames",
Q_RETURN_ARG(QStringList, result), Q_ARG(QUuid, entityID));
_entityTree->withReadLock([&] {
result = _entityTree->getJointNames(entityID);
});
return result;
}

View file

@ -171,6 +171,13 @@ public:
};
Q_ENUM(PickType)
enum JointState {
JOINT_STATE_NONE = 0,
JOINT_STATE_LEFT_HAND,
JOINT_STATE_RIGHT_HAND,
JOINT_STATE_MOUSE
};
void enable(bool enabled = true);
void disable() { enable(false); }
@ -211,9 +218,11 @@ public:
void setIgnoreItems(const QVector<QUuid>& items);
void setIncludeItems(const QVector<QUuid>& items);
virtual bool isLeftHand() const { return false; }
virtual bool isRightHand() const { return false; }
virtual bool isMouse() const { return false; }
virtual bool isLeftHand() const { return _jointState == JOINT_STATE_LEFT_HAND; }
virtual bool isRightHand() const { return _jointState == JOINT_STATE_RIGHT_HAND; }
virtual bool isMouse() const { return _jointState == JOINT_STATE_MOUSE; }
void setJointState(JointState jointState) { _jointState = jointState; }
virtual Transform getResultTransform() const = 0;
@ -227,13 +236,15 @@ private:
QVector<QUuid> _ignoreItems;
QVector<QUuid> _includeItems;
JointState _jointState { JOINT_STATE_NONE };
};
Q_DECLARE_METATYPE(PickQuery::PickType)
template<typename T>
class Pick : public PickQuery {
public:
Pick(const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled) {}
Pick(const T& mathPick, const PickFilter& filter, const float maxDistance, const bool enabled) : PickQuery(filter, maxDistance, enabled), _mathPick(mathPick) {}
virtual ~Pick() {}
virtual T getMathematicalPick() const = 0;
@ -242,6 +253,9 @@ public:
virtual PickResultPointer getOverlayIntersection(const T& pick) = 0;
virtual PickResultPointer getAvatarIntersection(const T& pick) = 0;
virtual PickResultPointer getHUDIntersection(const T& pick) = 0;
protected:
T _mathPick;
};
namespace std {

View file

@ -90,6 +90,17 @@ void PickManager::setIncludeItems(unsigned int uid, const QVector<QUuid>& includ
}
}
Transform PickManager::getParentTransform(unsigned int uid) const {
auto pick = findPick(uid);
if (pick) {
auto parentTransform = pick->parentTransform;
if (parentTransform) {
return parentTransform->getTransform();
}
}
return Transform();
}
Transform PickManager::getResultTransform(unsigned int uid) const {
auto pick = findPick(uid);
if (pick) {

View file

@ -43,6 +43,7 @@ public:
void setIgnoreItems(unsigned int uid, const QVector<QUuid>& ignore) const;
void setIncludeItems(unsigned int uid, const QVector<QUuid>& include) const;
Transform getParentTransform(unsigned int uid) const;
Transform getResultTransform(unsigned int uid) const;
bool isLeftHand(unsigned int uid);

View file

@ -202,6 +202,8 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay);
class StylusTip : public MathPick {
public:
StylusTip() : position(NAN), velocity(NAN) {}
StylusTip(const bilateral::Side& side, const glm::vec3& position = Vectors::ZERO, const glm::quat& orientation = Quaternions::IDENTITY, const glm::vec3& velocity = Vectors::ZERO) :
side(side), position(position), orientation(orientation), velocity(velocity) {}
StylusTip(const QVariantMap& pickVariant) : side(bilateral::Side(pickVariant["side"].toInt())), position(vec3FromVariant(pickVariant["position"])),
orientation(quatFromVariant(pickVariant["orientation"])), velocity(vec3FromVariant(pickVariant["velocity"])) {}

View file

@ -429,7 +429,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}],
posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true),
hover: true,
scaleWithAvatar: true,
scaleWithParent: true,
distanceScaleEnd: true,
hand: LEFT_HAND
});
@ -439,7 +439,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}],
posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true),
hover: true,
scaleWithAvatar: true,
scaleWithParent: true,
distanceScaleEnd: true,
hand: RIGHT_HAND
});
@ -450,7 +450,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
posOffset: getGrabPointSphereOffset(Controller.Standard.LeftHand, true),
triggers: [{action: Controller.Standard.LTClick, button: "Focus"}, {action: Controller.Standard.LTClick, button: "Primary"}],
hover: true,
scaleWithAvatar: true,
scaleWithParent: true,
distanceScaleEnd: true,
hand: LEFT_HAND
});
@ -461,7 +461,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js");
posOffset: getGrabPointSphereOffset(Controller.Standard.RightHand, true),
triggers: [{action: Controller.Standard.RTClick, button: "Focus"}, {action: Controller.Standard.RTClick, button: "Primary"}],
hover: true,
scaleWithAvatar: true,
scaleWithParent: true,
distanceScaleEnd: true,
hand: RIGHT_HAND
});

View file

@ -298,7 +298,7 @@ Script.include("/~/system/libraries/controllers.js");
posOffset: { x: (_this.hand === RIGHT_HAND) ? 0.03 : -0.03, y: 0.2, z: 0.02 },
filter: Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_INVISIBLE,
faceAvatar: true,
scaleWithAvatar: true,
scaleWithParent: true,
centerEndY: false,
speed: speed,
accelerationAxis: accelerationAxis,
@ -314,7 +314,7 @@ Script.include("/~/system/libraries/controllers.js");
posOffset: { x: (_this.hand === RIGHT_HAND) ? 0.03 : -0.03, y: 0.2, z: 0.02 },
filter: Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_INVISIBLE,
faceAvatar: true,
scaleWithAvatar: true,
scaleWithParent: true,
centerEndY: false,
speed: speed,
accelerationAxis: accelerationAxis,
@ -327,7 +327,7 @@ Script.include("/~/system/libraries/controllers.js");
joint: "Avatar",
filter: Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_INVISIBLE,
faceAvatar: true,
scaleWithAvatar: true,
scaleWithParent: true,
centerEndY: false,
speed: speed,
accelerationAxis: accelerationAxis,
@ -341,7 +341,7 @@ Script.include("/~/system/libraries/controllers.js");
joint: "Avatar",
filter: Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_INVISIBLE,
faceAvatar: true,
scaleWithAvatar: true,
scaleWithParent: true,
centerEndY: false,
speed: speed,
accelerationAxis: accelerationAxis,

View file

@ -272,7 +272,7 @@ function Grabber() {
joint: "Mouse",
filter: Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE,
faceAvatar: true,
scaleWithAvatar: true,
scaleWithParent: true,
enabled: true,
renderStates: renderStates
});