mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 05:58:35 +02:00
Merge branch 'master' into edit-model-ui
This commit is contained in:
commit
d75c2e2a6f
26 changed files with 735 additions and 253 deletions
|
@ -62,7 +62,7 @@
|
||||||
#include "AudioMixer.h"
|
#include "AudioMixer.h"
|
||||||
|
|
||||||
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
|
const float LOUDNESS_TO_DISTANCE_RATIO = 0.00001f;
|
||||||
const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.18f;
|
const float DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE = 0.5f; // attenuation = -6dB * log2(distance)
|
||||||
const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f;
|
const float DEFAULT_NOISE_MUTING_THRESHOLD = 0.003f;
|
||||||
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
|
const QString AUDIO_MIXER_LOGGING_TARGET_NAME = "audio-mixer";
|
||||||
const QString AUDIO_ENV_GROUP_KEY = "audio_env";
|
const QString AUDIO_ENV_GROUP_KEY = "audio_env";
|
||||||
|
@ -141,13 +141,14 @@ float AudioMixer::gainForSource(const PositionalAudioStream& streamToAdd,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE) {
|
if (distanceBetween >= ATTENUATION_BEGINS_AT_DISTANCE) {
|
||||||
// calculate the distance coefficient using the distance to this node
|
|
||||||
float distanceCoefficient = 1.0f - (logf(distanceBetween / ATTENUATION_BEGINS_AT_DISTANCE) / logf(2.0f)
|
|
||||||
* attenuationPerDoublingInDistance);
|
|
||||||
|
|
||||||
if (distanceCoefficient < 0) {
|
// translate the zone setting to gain per log2(distance)
|
||||||
distanceCoefficient = 0;
|
float g = 1.0f - attenuationPerDoublingInDistance;
|
||||||
}
|
g = (g < EPSILON) ? EPSILON : g;
|
||||||
|
g = (g > 1.0f) ? 1.0f : g;
|
||||||
|
|
||||||
|
// calculate the distance coefficient using the distance to this node
|
||||||
|
float distanceCoefficient = exp2f(log2f(g) * log2f(distanceBetween/ATTENUATION_BEGINS_AT_DISTANCE));
|
||||||
|
|
||||||
// multiply the current attenuation coefficient by the distance coefficient
|
// multiply the current attenuation coefficient by the distance coefficient
|
||||||
gain *= distanceCoefficient;
|
gain *= distanceCoefficient;
|
||||||
|
|
|
@ -589,8 +589,8 @@
|
||||||
"name": "attenuation_per_doubling_in_distance",
|
"name": "attenuation_per_doubling_in_distance",
|
||||||
"label": "Default Domain Attenuation",
|
"label": "Default Domain Attenuation",
|
||||||
"help": "Factor between 0 and 1.0 (0: No attenuation, 1.0: extreme attenuation)",
|
"help": "Factor between 0 and 1.0 (0: No attenuation, 1.0: extreme attenuation)",
|
||||||
"placeholder": "0.18",
|
"placeholder": "0.5",
|
||||||
"default": "0.18",
|
"default": "0.5",
|
||||||
"advanced": false
|
"advanced": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -686,7 +686,7 @@
|
||||||
"name": "coefficient",
|
"name": "coefficient",
|
||||||
"label": "Attenuation coefficient",
|
"label": "Attenuation coefficient",
|
||||||
"can_set": true,
|
"can_set": true,
|
||||||
"placeholder": "0.18"
|
"placeholder": "0.5"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -50,12 +50,12 @@ Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: asyncClickSender.start();
|
onClicked: asyncClickSender.start();
|
||||||
onEntered: {
|
onEntered: {
|
||||||
if (hoverState > 0) {
|
if (hoverState >= 0) {
|
||||||
buttonState = hoverState;
|
buttonState = hoverState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onExited: {
|
onExited: {
|
||||||
if (defaultState > 0) {
|
if (defaultState >= 0) {
|
||||||
buttonState = defaultState;
|
buttonState = defaultState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,23 @@ Rectangle {
|
||||||
drag.target: window
|
drag.target: window
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onEntered: window.mouseEntered();
|
onEntered: window.mouseEntered();
|
||||||
onExited: window.mouseExited();
|
onExited: {
|
||||||
|
if (!containsMouseGlobal()) {
|
||||||
|
window.mouseExited();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsMouseGlobal() {
|
||||||
|
var reticlePos = Reticle.position;
|
||||||
|
var globalPosition = decorationMouseArea.mapToItem(desktop, 0, 0);
|
||||||
|
var localPosition = {
|
||||||
|
x: reticlePos.x - globalPosition.x,
|
||||||
|
y: reticlePos.y - globalPosition.y,
|
||||||
|
};
|
||||||
|
return localPosition.x >= 0 && localPosition.x <= width &&
|
||||||
|
localPosition.y >= 0 && localPosition.y <= height;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: window
|
target: window
|
||||||
|
@ -57,7 +73,9 @@ Rectangle {
|
||||||
root.inflateDecorations()
|
root.inflateDecorations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onMouseExited: root.deflateDecorations();
|
onMouseExited: {
|
||||||
|
root.deflateDecorations();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
target: desktop
|
target: desktop
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "Image3DOverlay.h"
|
#include "Image3DOverlay.h"
|
||||||
#include "Circle3DOverlay.h"
|
#include "Circle3DOverlay.h"
|
||||||
#include "Cube3DOverlay.h"
|
#include "Cube3DOverlay.h"
|
||||||
|
#include "Shape3DOverlay.h"
|
||||||
#include "ImageOverlay.h"
|
#include "ImageOverlay.h"
|
||||||
#include "Line3DOverlay.h"
|
#include "Line3DOverlay.h"
|
||||||
#include "LocalModelsOverlay.h"
|
#include "LocalModelsOverlay.h"
|
||||||
|
@ -157,6 +158,8 @@ unsigned int Overlays::addOverlay(const QString& type, const QVariant& propertie
|
||||||
thisOverlay = std::make_shared<TextOverlay>();
|
thisOverlay = std::make_shared<TextOverlay>();
|
||||||
} else if (type == Text3DOverlay::TYPE) {
|
} else if (type == Text3DOverlay::TYPE) {
|
||||||
thisOverlay = std::make_shared<Text3DOverlay>();
|
thisOverlay = std::make_shared<Text3DOverlay>();
|
||||||
|
} else if (type == Shape3DOverlay::TYPE) {
|
||||||
|
thisOverlay = std::make_shared<Shape3DOverlay>();
|
||||||
} else if (type == Cube3DOverlay::TYPE) {
|
} else if (type == Cube3DOverlay::TYPE) {
|
||||||
thisOverlay = std::make_shared<Cube3DOverlay>();
|
thisOverlay = std::make_shared<Cube3DOverlay>();
|
||||||
} else if (type == Sphere3DOverlay::TYPE) {
|
} else if (type == Sphere3DOverlay::TYPE) {
|
||||||
|
|
130
interface/src/ui/overlays/Shape3DOverlay.cpp
Normal file
130
interface/src/ui/overlays/Shape3DOverlay.cpp
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
//
|
||||||
|
// Shape3DOverlay.cpp
|
||||||
|
// interface/src/ui/overlays
|
||||||
|
//
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
// include this before QGLWidget, which includes an earlier version of OpenGL
|
||||||
|
#include "Shape3DOverlay.h"
|
||||||
|
|
||||||
|
#include <SharedUtil.h>
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
#include <GeometryCache.h>
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
|
||||||
|
QString const Shape3DOverlay::TYPE = "shape";
|
||||||
|
|
||||||
|
Shape3DOverlay::Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay) :
|
||||||
|
Volume3DOverlay(Shape3DOverlay)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shape3DOverlay::render(RenderArgs* args) {
|
||||||
|
if (!_visible) {
|
||||||
|
return; // do nothing if we're not visible
|
||||||
|
}
|
||||||
|
|
||||||
|
float alpha = getAlpha();
|
||||||
|
xColor color = getColor();
|
||||||
|
const float MAX_COLOR = 255.0f;
|
||||||
|
glm::vec4 cubeColor(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
|
||||||
|
|
||||||
|
// TODO: handle registration point??
|
||||||
|
glm::vec3 position = getPosition();
|
||||||
|
glm::vec3 dimensions = getDimensions();
|
||||||
|
glm::quat rotation = getRotation();
|
||||||
|
|
||||||
|
auto batch = args->_batch;
|
||||||
|
|
||||||
|
if (batch) {
|
||||||
|
Transform transform;
|
||||||
|
transform.setTranslation(position);
|
||||||
|
transform.setRotation(rotation);
|
||||||
|
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||||
|
auto pipeline = args->_pipeline;
|
||||||
|
if (!pipeline) {
|
||||||
|
pipeline = _isSolid ? geometryCache->getShapePipeline() : geometryCache->getWireShapePipeline();
|
||||||
|
}
|
||||||
|
|
||||||
|
transform.setScale(dimensions);
|
||||||
|
batch->setModelTransform(transform);
|
||||||
|
if (_isSolid) {
|
||||||
|
geometryCache->renderSolidShapeInstance(*batch, _shape, cubeColor, pipeline);
|
||||||
|
} else {
|
||||||
|
geometryCache->renderWireShapeInstance(*batch, _shape, cubeColor, pipeline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const render::ShapeKey Shape3DOverlay::getShapeKey() {
|
||||||
|
auto builder = render::ShapeKey::Builder();
|
||||||
|
if (getAlpha() != 1.0f) {
|
||||||
|
builder.withTranslucent();
|
||||||
|
}
|
||||||
|
if (!getIsSolid()) {
|
||||||
|
builder.withUnlit().withDepthBias();
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Shape3DOverlay* Shape3DOverlay::createClone() const {
|
||||||
|
return new Shape3DOverlay(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const std::array<QString, GeometryCache::Shape::NUM_SHAPES> shapeStrings { {
|
||||||
|
"Line",
|
||||||
|
"Triangle",
|
||||||
|
"Quad",
|
||||||
|
"Hexagon",
|
||||||
|
"Octagon",
|
||||||
|
"Circle",
|
||||||
|
"Cube",
|
||||||
|
"Sphere",
|
||||||
|
"Tetrahedron",
|
||||||
|
"Octahedron",
|
||||||
|
"Dodecahedron",
|
||||||
|
"Icosahedron",
|
||||||
|
"Torus",
|
||||||
|
"Cone",
|
||||||
|
"Cylinder"
|
||||||
|
} };
|
||||||
|
|
||||||
|
|
||||||
|
void Shape3DOverlay::setProperties(const QVariantMap& properties) {
|
||||||
|
Volume3DOverlay::setProperties(properties);
|
||||||
|
|
||||||
|
auto shape = properties["shape"];
|
||||||
|
if (shape.isValid()) {
|
||||||
|
const QString shapeStr = shape.toString();
|
||||||
|
for (size_t i = 0; i < shapeStrings.size(); ++i) {
|
||||||
|
if (shapeStr == shapeStrings[i]) {
|
||||||
|
this->_shape = static_cast<GeometryCache::Shape>(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto borderSize = properties["borderSize"];
|
||||||
|
|
||||||
|
if (borderSize.isValid()) {
|
||||||
|
float value = borderSize.toFloat();
|
||||||
|
setBorderSize(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant Shape3DOverlay::getProperty(const QString& property) {
|
||||||
|
if (property == "borderSize") {
|
||||||
|
return _borderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property == "shape") {
|
||||||
|
return shapeStrings[_shape];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Volume3DOverlay::getProperty(property);
|
||||||
|
}
|
46
interface/src/ui/overlays/Shape3DOverlay.h
Normal file
46
interface/src/ui/overlays/Shape3DOverlay.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
//
|
||||||
|
// Shape3DOverlay.h
|
||||||
|
// interface/src/ui/overlays
|
||||||
|
//
|
||||||
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_Shape3DOverlay_h
|
||||||
|
#define hifi_Shape3DOverlay_h
|
||||||
|
|
||||||
|
#include "Volume3DOverlay.h"
|
||||||
|
|
||||||
|
#include <GeometryCache.h>
|
||||||
|
|
||||||
|
class Shape3DOverlay : public Volume3DOverlay {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
static QString const TYPE;
|
||||||
|
virtual QString getType() const override { return TYPE; }
|
||||||
|
|
||||||
|
Shape3DOverlay() {}
|
||||||
|
Shape3DOverlay(const Shape3DOverlay* Shape3DOverlay);
|
||||||
|
|
||||||
|
virtual void render(RenderArgs* args) override;
|
||||||
|
virtual const render::ShapeKey getShapeKey() override;
|
||||||
|
|
||||||
|
virtual Shape3DOverlay* createClone() const override;
|
||||||
|
|
||||||
|
float getBorderSize() const { return _borderSize; }
|
||||||
|
|
||||||
|
void setBorderSize(float value) { _borderSize = value; }
|
||||||
|
|
||||||
|
void setProperties(const QVariantMap& properties) override;
|
||||||
|
QVariant getProperty(const QString& property) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float _borderSize;
|
||||||
|
GeometryCache::Shape _shape { GeometryCache::Hexagon };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // hifi_Shape3DOverlay_h
|
|
@ -1308,7 +1308,7 @@ float AudioClient::gainForSource(float distance, float volume) {
|
||||||
|
|
||||||
// attenuate based on distance
|
// attenuate based on distance
|
||||||
if (distance >= ATTENUATION_BEGINS_AT_DISTANCE) {
|
if (distance >= ATTENUATION_BEGINS_AT_DISTANCE) {
|
||||||
gain /= distance; // attenuation = -6dB * log2(distance)
|
gain /= (distance/ATTENUATION_BEGINS_AT_DISTANCE); // attenuation = -6dB * log2(distance)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gain;
|
return gain;
|
||||||
|
|
|
@ -442,3 +442,12 @@ glm::mat4 CompositorHelper::getReticleTransform(const glm::mat4& eyePose, const
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVariant ReticleInterface::getPosition() const {
|
||||||
|
return vec2toVariant(_compositor->getReticlePosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReticleInterface::setPosition(QVariant position) {
|
||||||
|
_compositor->setReticlePosition(vec2FromVariant(position));
|
||||||
|
}
|
||||||
|
|
|
@ -174,7 +174,7 @@ private:
|
||||||
// Scripting interface available to control the Reticle
|
// Scripting interface available to control the Reticle
|
||||||
class ReticleInterface : public QObject {
|
class ReticleInterface : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
Q_PROPERTY(QVariant position READ getPosition WRITE setPosition)
|
||||||
Q_PROPERTY(bool visible READ getVisible WRITE setVisible)
|
Q_PROPERTY(bool visible READ getVisible WRITE setVisible)
|
||||||
Q_PROPERTY(float depth READ getDepth WRITE setDepth)
|
Q_PROPERTY(float depth READ getDepth WRITE setDepth)
|
||||||
Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition)
|
Q_PROPERTY(glm::vec2 maximumPosition READ getMaximumPosition)
|
||||||
|
@ -198,8 +198,8 @@ public:
|
||||||
Q_INVOKABLE float getDepth() { return _compositor->getReticleDepth(); }
|
Q_INVOKABLE float getDepth() { return _compositor->getReticleDepth(); }
|
||||||
Q_INVOKABLE void setDepth(float depth) { _compositor->setReticleDepth(depth); }
|
Q_INVOKABLE void setDepth(float depth) { _compositor->setReticleDepth(depth); }
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec2 getPosition() { return _compositor->getReticlePosition(); }
|
Q_INVOKABLE QVariant getPosition() const;
|
||||||
Q_INVOKABLE void setPosition(glm::vec2 position) { _compositor->setReticlePosition(position); }
|
Q_INVOKABLE void setPosition(QVariant position);
|
||||||
|
|
||||||
Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); }
|
Q_INVOKABLE glm::vec2 getMaximumPosition() { return _compositor->getReticleMaximumPosition(); }
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,11 @@
|
||||||
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
|
// is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down.
|
||||||
static const float SPHERE_ENTITY_SCALE = 0.5f;
|
static const float SPHERE_ENTITY_SCALE = 0.5f;
|
||||||
|
|
||||||
static GeometryCache::Shape MAPPING[entity::NUM_SHAPES] = {
|
static std::array<GeometryCache::Shape, entity::NUM_SHAPES> MAPPING { {
|
||||||
GeometryCache::Triangle,
|
GeometryCache::Triangle,
|
||||||
GeometryCache::Quad,
|
GeometryCache::Quad,
|
||||||
|
GeometryCache::Hexagon,
|
||||||
|
GeometryCache::Octagon,
|
||||||
GeometryCache::Circle,
|
GeometryCache::Circle,
|
||||||
GeometryCache::Cube,
|
GeometryCache::Cube,
|
||||||
GeometryCache::Sphere,
|
GeometryCache::Sphere,
|
||||||
|
@ -36,7 +38,7 @@ static GeometryCache::Shape MAPPING[entity::NUM_SHAPES] = {
|
||||||
GeometryCache::Torus,
|
GeometryCache::Torus,
|
||||||
GeometryCache::Cone,
|
GeometryCache::Cone,
|
||||||
GeometryCache::Cylinder,
|
GeometryCache::Cylinder,
|
||||||
};
|
} };
|
||||||
|
|
||||||
|
|
||||||
RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||||
|
|
|
@ -20,17 +20,19 @@
|
||||||
#include "ShapeEntityItem.h"
|
#include "ShapeEntityItem.h"
|
||||||
|
|
||||||
namespace entity {
|
namespace entity {
|
||||||
static const std::vector<QString> shapeStrings { {
|
static const std::array<QString, Shape::NUM_SHAPES> shapeStrings { {
|
||||||
"Triangle",
|
"Triangle",
|
||||||
"Quad",
|
"Quad",
|
||||||
"Circle",
|
"Hexagon",
|
||||||
|
"Octagon",
|
||||||
|
"Circle",
|
||||||
"Cube",
|
"Cube",
|
||||||
"Sphere",
|
"Sphere",
|
||||||
"Tetrahedron",
|
"Tetrahedron",
|
||||||
"Octahedron",
|
"Octahedron",
|
||||||
"Dodecahedron",
|
"Dodecahedron",
|
||||||
"Icosahedron",
|
"Icosahedron",
|
||||||
"Torus",
|
"Torus",
|
||||||
"Cone",
|
"Cone",
|
||||||
"Cylinder"
|
"Cylinder"
|
||||||
} };
|
} };
|
||||||
|
|
|
@ -15,6 +15,8 @@ namespace entity {
|
||||||
enum Shape {
|
enum Shape {
|
||||||
Triangle,
|
Triangle,
|
||||||
Quad,
|
Quad,
|
||||||
|
Hexagon,
|
||||||
|
Octagon,
|
||||||
Circle,
|
Circle,
|
||||||
Cube,
|
Cube,
|
||||||
Sphere,
|
Sphere,
|
||||||
|
|
|
@ -97,7 +97,8 @@ public:
|
||||||
ICEServerHeartbeatACK,
|
ICEServerHeartbeatACK,
|
||||||
NegotiateAudioFormat,
|
NegotiateAudioFormat,
|
||||||
SelectedAudioFormat,
|
SelectedAudioFormat,
|
||||||
LAST_PACKET_TYPE = SelectedAudioFormat
|
MoreEntityShapes,
|
||||||
|
LAST_PACKET_TYPE = MoreEntityShapes
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,18 @@ static const uint SHAPE_NORMALS_OFFSET = sizeof(glm::vec3);
|
||||||
static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT32;
|
static const gpu::Type SHAPE_INDEX_TYPE = gpu::UINT32;
|
||||||
static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint32);
|
static const uint SHAPE_INDEX_SIZE = sizeof(gpu::uint32);
|
||||||
|
|
||||||
|
template <size_t SIDES>
|
||||||
|
std::vector<vec3> polygon() {
|
||||||
|
std::vector<vec3> result;
|
||||||
|
result.reserve(SIDES);
|
||||||
|
double angleIncrement = 2.0 * M_PI / SIDES;
|
||||||
|
for (size_t i = 0; i < SIDES; ++i) {
|
||||||
|
double angle = (double)i * angleIncrement;
|
||||||
|
result.push_back(vec3{ cos(angle) * 0.5, 0.0, sin(angle) * 0.5 });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const geometry::VertexVector& vertices) {
|
void GeometryCache::ShapeData::setupVertices(gpu::BufferPointer& vertexBuffer, const geometry::VertexVector& vertices) {
|
||||||
vertexBuffer->append(vertices);
|
vertexBuffer->append(vertices);
|
||||||
|
|
||||||
|
@ -239,6 +251,75 @@ void setupSmoothShape(GeometryCache::ShapeData& shapeData, const geometry::Solid
|
||||||
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <uint32_t N>
|
||||||
|
void extrudePolygon(GeometryCache::ShapeData& shapeData, gpu::BufferPointer& vertexBuffer, gpu::BufferPointer& indexBuffer) {
|
||||||
|
using namespace geometry;
|
||||||
|
Index baseVertex = (Index)(vertexBuffer->getSize() / SHAPE_VERTEX_STRIDE);
|
||||||
|
VertexVector vertices;
|
||||||
|
IndexVector solidIndices, wireIndices;
|
||||||
|
|
||||||
|
// Top and bottom faces
|
||||||
|
std::vector<vec3> shape = polygon<N>();
|
||||||
|
for (const vec3& v : shape) {
|
||||||
|
vertices.push_back(vec3(v.x, 0.5f, v.z));
|
||||||
|
vertices.push_back(vec3(0, 1, 0));
|
||||||
|
}
|
||||||
|
for (const vec3& v : shape) {
|
||||||
|
vertices.push_back(vec3(v.x, -0.5f, v.z));
|
||||||
|
vertices.push_back(vec3(0, -1, 0));
|
||||||
|
}
|
||||||
|
for (uint32_t i = 2; i < N; ++i) {
|
||||||
|
solidIndices.push_back(baseVertex + 0);
|
||||||
|
solidIndices.push_back(baseVertex + i);
|
||||||
|
solidIndices.push_back(baseVertex + i - 1);
|
||||||
|
solidIndices.push_back(baseVertex + N);
|
||||||
|
solidIndices.push_back(baseVertex + i + N - 1);
|
||||||
|
solidIndices.push_back(baseVertex + i + N);
|
||||||
|
}
|
||||||
|
for (uint32_t i = 1; i <= N; ++i) {
|
||||||
|
wireIndices.push_back(baseVertex + (i % N));
|
||||||
|
wireIndices.push_back(baseVertex + i - 1);
|
||||||
|
wireIndices.push_back(baseVertex + (i % N) + N);
|
||||||
|
wireIndices.push_back(baseVertex + (i - 1) + N);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now do the sides
|
||||||
|
baseVertex += 2 * N;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < N; ++i) {
|
||||||
|
vec3 left = shape[i];
|
||||||
|
vec3 right = shape[(i + 1) % N];
|
||||||
|
vec3 normal = glm::normalize(left + right);
|
||||||
|
vec3 topLeft = vec3(left.x, 0.5f, left.z);
|
||||||
|
vec3 topRight = vec3(right.x, 0.5f, right.z);
|
||||||
|
vec3 bottomLeft = vec3(left.x, -0.5f, left.z);
|
||||||
|
vec3 bottomRight = vec3(right.x, -0.5f, right.z);
|
||||||
|
|
||||||
|
vertices.push_back(topLeft);
|
||||||
|
vertices.push_back(normal);
|
||||||
|
vertices.push_back(bottomLeft);
|
||||||
|
vertices.push_back(normal);
|
||||||
|
vertices.push_back(topRight);
|
||||||
|
vertices.push_back(normal);
|
||||||
|
vertices.push_back(bottomRight);
|
||||||
|
vertices.push_back(normal);
|
||||||
|
|
||||||
|
solidIndices.push_back(baseVertex + 0);
|
||||||
|
solidIndices.push_back(baseVertex + 2);
|
||||||
|
solidIndices.push_back(baseVertex + 1);
|
||||||
|
solidIndices.push_back(baseVertex + 1);
|
||||||
|
solidIndices.push_back(baseVertex + 2);
|
||||||
|
solidIndices.push_back(baseVertex + 3);
|
||||||
|
wireIndices.push_back(baseVertex + 0);
|
||||||
|
wireIndices.push_back(baseVertex + 1);
|
||||||
|
wireIndices.push_back(baseVertex + 3);
|
||||||
|
wireIndices.push_back(baseVertex + 2);
|
||||||
|
baseVertex += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
shapeData.setupVertices(vertexBuffer, vertices);
|
||||||
|
shapeData.setupIndices(indexBuffer, solidIndices, wireIndices);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME solids need per-face vertices, but smooth shaded
|
// FIXME solids need per-face vertices, but smooth shaded
|
||||||
// components do not. Find a way to support using draw elements
|
// components do not. Find a way to support using draw elements
|
||||||
|
@ -285,10 +366,13 @@ void GeometryCache::buildShapes() {
|
||||||
// Not implememented yet:
|
// Not implememented yet:
|
||||||
|
|
||||||
//Triangle,
|
//Triangle,
|
||||||
|
extrudePolygon<3>(_shapes[Triangle], _shapeVertices, _shapeIndices);
|
||||||
|
//Hexagon,
|
||||||
|
extrudePolygon<6>(_shapes[Hexagon], _shapeVertices, _shapeIndices);
|
||||||
|
//Octagon,
|
||||||
|
extrudePolygon<8>(_shapes[Octagon], _shapeVertices, _shapeIndices);
|
||||||
//Quad,
|
//Quad,
|
||||||
//Circle,
|
//Circle,
|
||||||
//Octahetron,
|
|
||||||
//Dodecahedron,
|
|
||||||
//Torus,
|
//Torus,
|
||||||
//Cone,
|
//Cone,
|
||||||
//Cylinder,
|
//Cylinder,
|
||||||
|
@ -1757,6 +1841,11 @@ void GeometryCache::renderSolidShapeInstance(gpu::Batch& batch, GeometryCache::S
|
||||||
renderInstances(batch, color, false, pipeline, shape);
|
renderInstances(batch, color, false, pipeline, shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GeometryCache::renderWireShapeInstance(gpu::Batch& batch, GeometryCache::Shape shape, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
||||||
|
renderInstances(batch, color, true, pipeline, shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GeometryCache::renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
void GeometryCache::renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color, const render::ShapePipelinePointer& pipeline) {
|
||||||
renderInstances(batch, color, false, pipeline, GeometryCache::Sphere);
|
renderInstances(batch, color, false, pipeline, GeometryCache::Sphere);
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,8 @@ public:
|
||||||
Line,
|
Line,
|
||||||
Triangle,
|
Triangle,
|
||||||
Quad,
|
Quad,
|
||||||
|
Hexagon,
|
||||||
|
Octagon,
|
||||||
Circle,
|
Circle,
|
||||||
Cube,
|
Cube,
|
||||||
Sphere,
|
Sphere,
|
||||||
|
@ -139,10 +141,9 @@ public:
|
||||||
Octahedron,
|
Octahedron,
|
||||||
Dodecahedron,
|
Dodecahedron,
|
||||||
Icosahedron,
|
Icosahedron,
|
||||||
Torus,
|
Torus, // not yet implemented
|
||||||
Cone,
|
Cone, // not yet implemented
|
||||||
Cylinder,
|
Cylinder, // not yet implemented
|
||||||
|
|
||||||
NUM_SHAPES,
|
NUM_SHAPES,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -170,6 +171,13 @@ public:
|
||||||
renderSolidShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline);
|
renderSolidShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void renderWireShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1),
|
||||||
|
const render::ShapePipelinePointer& pipeline = _simplePipeline);
|
||||||
|
void renderWireShapeInstance(gpu::Batch& batch, Shape shape, const glm::vec3& color,
|
||||||
|
const render::ShapePipelinePointer& pipeline = _simplePipeline) {
|
||||||
|
renderWireShapeInstance(batch, shape, glm::vec4(color, 1.0f), pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color,
|
void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec4& color,
|
||||||
const render::ShapePipelinePointer& pipeline = _simplePipeline);
|
const render::ShapePipelinePointer& pipeline = _simplePipeline);
|
||||||
void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color,
|
void renderSolidSphereInstance(gpu::Batch& batch, const glm::vec3& color,
|
||||||
|
|
|
@ -71,28 +71,24 @@ void main() {
|
||||||
lineOrthogonal *= 0.02;
|
lineOrthogonal *= 0.02;
|
||||||
|
|
||||||
gl_Position = gl_PositionIn[0];
|
gl_Position = gl_PositionIn[0];
|
||||||
gl_Position.xy -= lineNormal;
|
|
||||||
gl_Position.xy -= lineOrthogonal;
|
gl_Position.xy -= lineOrthogonal;
|
||||||
outColor = inColor[0];
|
outColor = inColor[0];
|
||||||
outLineDistance = vec3(-1.02, -1, gl_Position.z);
|
outLineDistance = vec3(-1.02, -1, gl_Position.z);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
|
|
||||||
gl_Position = gl_PositionIn[0];
|
gl_Position = gl_PositionIn[0];
|
||||||
gl_Position.xy -= lineNormal;
|
|
||||||
gl_Position.xy += lineOrthogonal;
|
gl_Position.xy += lineOrthogonal;
|
||||||
outColor = inColor[0];
|
outColor = inColor[0];
|
||||||
outLineDistance = vec3(-1.02, 1, gl_Position.z);
|
outLineDistance = vec3(-1.02, 1, gl_Position.z);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
|
|
||||||
gl_Position = gl_PositionIn[1];
|
gl_Position = gl_PositionIn[1];
|
||||||
gl_Position.xy += lineNormal;
|
|
||||||
gl_Position.xy -= lineOrthogonal;
|
gl_Position.xy -= lineOrthogonal;
|
||||||
outColor = inColor[1];
|
outColor = inColor[1];
|
||||||
outLineDistance = vec3(1.02, -1, gl_Position.z);
|
outLineDistance = vec3(1.02, -1, gl_Position.z);
|
||||||
EmitVertex();
|
EmitVertex();
|
||||||
|
|
||||||
gl_Position = gl_PositionIn[1];
|
gl_Position = gl_PositionIn[1];
|
||||||
gl_Position.xy += lineNormal;
|
|
||||||
gl_Position.xy += lineOrthogonal;
|
gl_Position.xy += lineOrthogonal;
|
||||||
outColor = inColor[1];
|
outColor = inColor[1];
|
||||||
outLineDistance = vec3(1.02, 1, gl_Position.z);
|
outLineDistance = vec3(1.02, 1, gl_Position.z);
|
||||||
|
|
|
@ -42,10 +42,6 @@ var HAND_HEAD_MIX_RATIO = 0.0; // 0 = only use hands for search/move. 1 = only
|
||||||
|
|
||||||
var PICK_WITH_HAND_RAY = true;
|
var PICK_WITH_HAND_RAY = true;
|
||||||
|
|
||||||
var DRAW_GRAB_BOXES = false;
|
|
||||||
var DRAW_HAND_SPHERES = false;
|
|
||||||
var DROP_WITHOUT_SHAKE = false;
|
|
||||||
|
|
||||||
var EQUIP_SPHERE_COLOR = { red: 179, green: 120, blue: 211 };
|
var EQUIP_SPHERE_COLOR = { red: 179, green: 120, blue: 211 };
|
||||||
var EQUIP_SPHERE_ALPHA = 0.15;
|
var EQUIP_SPHERE_ALPHA = 0.15;
|
||||||
var EQUIP_SPHERE_SCALE_FACTOR = 0.65;
|
var EQUIP_SPHERE_SCALE_FACTOR = 0.65;
|
||||||
|
@ -79,8 +75,6 @@ var LINE_ENTITY_DIMENSIONS = {
|
||||||
var LINE_LENGTH = 500;
|
var LINE_LENGTH = 500;
|
||||||
var PICK_MAX_DISTANCE = 500; // max length of pick-ray
|
var PICK_MAX_DISTANCE = 500; // max length of pick-ray
|
||||||
|
|
||||||
var EQUIP_RADIUS_EMBIGGEN_FACTOR = 1.1;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// near grabbing
|
// near grabbing
|
||||||
//
|
//
|
||||||
|
@ -208,7 +202,8 @@ CONTROLLER_STATE_MACHINE[STATE_NEAR_GRABBING] = {
|
||||||
CONTROLLER_STATE_MACHINE[STATE_HOLD] = {
|
CONTROLLER_STATE_MACHINE[STATE_HOLD] = {
|
||||||
name: "hold",
|
name: "hold",
|
||||||
enterMethod: "nearGrabbingEnter",
|
enterMethod: "nearGrabbingEnter",
|
||||||
updateMethod: "nearGrabbing"
|
updateMethod: "nearGrabbing",
|
||||||
|
exitMethod: "holdExit"
|
||||||
};
|
};
|
||||||
CONTROLLER_STATE_MACHINE[STATE_NEAR_TRIGGER] = {
|
CONTROLLER_STATE_MACHINE[STATE_NEAR_TRIGGER] = {
|
||||||
name: "trigger",
|
name: "trigger",
|
||||||
|
@ -229,6 +224,14 @@ function getTag() {
|
||||||
return "grab-" + MyAvatar.sessionUUID;
|
return "grab-" + MyAvatar.sessionUUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function colorPow(color, power) {
|
||||||
|
return {
|
||||||
|
red: Math.pow(color.red / 255.0, power) * 255,
|
||||||
|
green: Math.pow(color.green / 255.0, power) * 255,
|
||||||
|
blue: Math.pow(color.blue / 255.0, power) * 255,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function entityHasActions(entityID) {
|
function entityHasActions(entityID) {
|
||||||
return Entities.getActionIDs(entityID).length > 0;
|
return Entities.getActionIDs(entityID).length > 0;
|
||||||
}
|
}
|
||||||
|
@ -268,6 +271,51 @@ function propsArePhysical(props) {
|
||||||
return isPhysical;
|
return isPhysical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// currently disabled.
|
||||||
|
var USE_ATTACH_POINT_SETTINGS = false;
|
||||||
|
|
||||||
|
var ATTACH_POINT_SETTINGS = "io.highfidelity.attachPoints";
|
||||||
|
function getAttachPointSettings() {
|
||||||
|
try {
|
||||||
|
var str = Settings.getValue(ATTACH_POINT_SETTINGS);
|
||||||
|
print("getAttachPointSettings = " + str);
|
||||||
|
if (str === "false") {
|
||||||
|
return {};
|
||||||
|
} else {
|
||||||
|
return JSON.parse(str);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
print("Error parsing attachPointSettings: " + err);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setAttachPointSettings(attachPointSettings) {
|
||||||
|
var str = JSON.stringify(attachPointSettings);
|
||||||
|
print("setAttachPointSettings = " + str);
|
||||||
|
Settings.setValue(ATTACH_POINT_SETTINGS, str);
|
||||||
|
}
|
||||||
|
function getAttachPointForHotspotFromSettings(hotspot, hand) {
|
||||||
|
var attachPointSettings = getAttachPointSettings();
|
||||||
|
var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand";
|
||||||
|
var joints = attachPointSettings[hotspot.key];
|
||||||
|
if (joints) {
|
||||||
|
return joints[jointName];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function storeAttachPointForHotspotInSettings(hotspot, hand, offsetPosition, offsetRotation) {
|
||||||
|
var attachPointSettings = getAttachPointSettings();
|
||||||
|
var jointName = (hand === RIGHT_HAND) ? "RightHand" : "LeftHand";
|
||||||
|
var joints = attachPointSettings[hotspot.key];
|
||||||
|
if (!joints) {
|
||||||
|
joints = {};
|
||||||
|
attachPointSettings[hotspot.key] = joints;
|
||||||
|
}
|
||||||
|
joints[jointName] = [offsetPosition, offsetRotation];
|
||||||
|
setAttachPointSettings(attachPointSettings);
|
||||||
|
}
|
||||||
|
|
||||||
function removeMyAvatarFromCollidesWith(origCollidesWith) {
|
function removeMyAvatarFromCollidesWith(origCollidesWith) {
|
||||||
var collidesWithSplit = origCollidesWith.split(",");
|
var collidesWithSplit = origCollidesWith.split(",");
|
||||||
// remove myAvatar from the array
|
// remove myAvatar from the array
|
||||||
|
@ -302,20 +350,32 @@ function restore2DMode() {
|
||||||
|
|
||||||
// EntityPropertiesCache is a helper class that contains a cache of entity properties.
|
// EntityPropertiesCache is a helper class that contains a cache of entity properties.
|
||||||
// the hope is to prevent excess calls to Entity.getEntityProperties()
|
// the hope is to prevent excess calls to Entity.getEntityProperties()
|
||||||
|
//
|
||||||
|
// usage:
|
||||||
|
// call EntityPropertiesCache.addEntities with all the entities that you are interested in.
|
||||||
|
// This will fetch their properties. Then call EntityPropertiesCache.getProps to receive an object
|
||||||
|
// containing a cache of all the properties previously fetched.
|
||||||
function EntityPropertiesCache() {
|
function EntityPropertiesCache() {
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
}
|
}
|
||||||
EntityPropertiesCache.prototype.clear = function () {
|
EntityPropertiesCache.prototype.clear = function () {
|
||||||
this.cache = {};
|
this.cache = {};
|
||||||
};
|
};
|
||||||
EntityPropertiesCache.prototype.findEntities = function (position, radius) {
|
EntityPropertiesCache.prototype.addEntity = function (entityID) {
|
||||||
var entities = Entities.findEntities(position, radius);
|
var cacheEntry = this.cache[entityID];
|
||||||
|
if (cacheEntry && cacheEntry.refCount) {
|
||||||
|
cacheEntry.refCount += 1;
|
||||||
|
} else {
|
||||||
|
this._updateCacheEntry(entityID);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
EntityPropertiesCache.prototype.addEntities = function (entities) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
entities.forEach(function (x) {
|
entities.forEach(function (entityID) {
|
||||||
_this.updateEntity(x);
|
_this.addEntity(entityID);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
EntityPropertiesCache.prototype.updateEntity = function (entityID) {
|
EntityPropertiesCache.prototype._updateCacheEntry = function (entityID) {
|
||||||
var props = Entities.getEntityProperties(entityID, GRABBABLE_PROPERTIES);
|
var props = Entities.getEntityProperties(entityID, GRABBABLE_PROPERTIES);
|
||||||
|
|
||||||
// convert props.userData from a string to an object.
|
// convert props.userData from a string to an object.
|
||||||
|
@ -328,11 +388,21 @@ EntityPropertiesCache.prototype.updateEntity = function (entityID) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
props.userData = userData;
|
props.userData = userData;
|
||||||
|
props.refCount = 1;
|
||||||
|
|
||||||
this.cache[entityID] = props;
|
this.cache[entityID] = props;
|
||||||
};
|
};
|
||||||
EntityPropertiesCache.prototype.getEntities = function () {
|
EntityPropertiesCache.prototype.update = function () {
|
||||||
return Object.keys(this.cache);
|
// delete any cacheEntries with zero refCounts.
|
||||||
|
var entities = Object.keys(this.cache);
|
||||||
|
for (var i = 0; i < entities.length; i++) {
|
||||||
|
var props = this.cache[entities[i]];
|
||||||
|
if (props.refCount === 0) {
|
||||||
|
delete this.cache[entities[i]];
|
||||||
|
} else {
|
||||||
|
props.refCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
EntityPropertiesCache.prototype.getProps = function (entityID) {
|
EntityPropertiesCache.prototype.getProps = function (entityID) {
|
||||||
var obj = this.cache[entityID];
|
var obj = this.cache[entityID];
|
||||||
|
@ -371,6 +441,158 @@ EntityPropertiesCache.prototype.getEquipHotspotsProps = function (entityID) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// global cache
|
||||||
|
var entityPropertiesCache = new EntityPropertiesCache();
|
||||||
|
|
||||||
|
// Each overlayInfoSet describes a single equip hotspot.
|
||||||
|
// It is an object with the following keys:
|
||||||
|
// timestamp - last time this object was updated, used to delete stale hotspot overlays.
|
||||||
|
// entityID - entity assosicated with this hotspot
|
||||||
|
// localPosition - position relative to the entity
|
||||||
|
// hotspot - hotspot object
|
||||||
|
// overlays - array of overlay objects created by Overlay.addOverlay()
|
||||||
|
// currentSize - current animated scale value
|
||||||
|
// targetSize - the target of our scale animations
|
||||||
|
// type - "sphere" or "model".
|
||||||
|
function EquipHotspotBuddy() {
|
||||||
|
// holds map from {string} hotspot.key to {object} overlayInfoSet.
|
||||||
|
this.map = {};
|
||||||
|
|
||||||
|
// array of all hotspots that are highlighed.
|
||||||
|
this.highlightedHotspots = [];
|
||||||
|
}
|
||||||
|
EquipHotspotBuddy.prototype.clear = function () {
|
||||||
|
var keys = Object.keys(this.map);
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
var overlayInfoSet = this.map[keys[i]];
|
||||||
|
this.deleteOverlayInfoSet(overlayInfoSet);
|
||||||
|
}
|
||||||
|
this.map = {};
|
||||||
|
this.highlightedHotspots = [];
|
||||||
|
};
|
||||||
|
EquipHotspotBuddy.prototype.highlightHotspot = function (hotspot) {
|
||||||
|
this.highlightedHotspots.push(hotspot.key);
|
||||||
|
};
|
||||||
|
EquipHotspotBuddy.prototype.updateHotspot = function (hotspot, timestamp) {
|
||||||
|
var overlayInfoSet = this.map[hotspot.key];
|
||||||
|
if (!overlayInfoSet) {
|
||||||
|
// create a new overlayInfoSet
|
||||||
|
overlayInfoSet = {
|
||||||
|
timestamp: timestamp,
|
||||||
|
entityID: hotspot.entityID,
|
||||||
|
localPosition: hotspot.localPosition,
|
||||||
|
hotspot: hotspot,
|
||||||
|
currentSize: 0,
|
||||||
|
targetSize: 1,
|
||||||
|
overlays: []
|
||||||
|
};
|
||||||
|
|
||||||
|
var diameter = hotspot.radius * 2;
|
||||||
|
|
||||||
|
if (hotspot.modelURL) {
|
||||||
|
// override default sphere with a user specified model
|
||||||
|
overlayInfoSet.overlays.push(Overlays.addOverlay("model", {
|
||||||
|
url: hotspot.modelURL,
|
||||||
|
position: hotspot.worldPosition,
|
||||||
|
rotation: {x: 0, y: 0, z: 0, w: 1},
|
||||||
|
dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR,
|
||||||
|
scale: hotspot.modelScale,
|
||||||
|
ignoreRayIntersection: true
|
||||||
|
}));
|
||||||
|
overlayInfoSet.type = "model";
|
||||||
|
} else {
|
||||||
|
// default sphere overlay
|
||||||
|
overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", {
|
||||||
|
position: hotspot.worldPosition,
|
||||||
|
rotation: {x: 0, y: 0, z: 0, w: 1},
|
||||||
|
dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR,
|
||||||
|
color: EQUIP_SPHERE_COLOR,
|
||||||
|
alpha: EQUIP_SPHERE_ALPHA,
|
||||||
|
solid: true,
|
||||||
|
visible: true,
|
||||||
|
ignoreRayIntersection: true,
|
||||||
|
drawInFront: false
|
||||||
|
}));
|
||||||
|
overlayInfoSet.type = "sphere";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.map[hotspot.key] = overlayInfoSet;
|
||||||
|
} else {
|
||||||
|
overlayInfoSet.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
EquipHotspotBuddy.prototype.updateHotspots = function (hotspots, timestamp) {
|
||||||
|
var _this = this;
|
||||||
|
hotspots.forEach(function (hotspot) {
|
||||||
|
_this.updateHotspot(hotspot, timestamp);
|
||||||
|
});
|
||||||
|
this.highlightedHotspots = [];
|
||||||
|
};
|
||||||
|
EquipHotspotBuddy.prototype.update = function (deltaTime, timestamp) {
|
||||||
|
|
||||||
|
var HIGHLIGHT_SIZE = 1.1;
|
||||||
|
var NORMAL_SIZE = 1.0;
|
||||||
|
|
||||||
|
var keys = Object.keys(this.map);
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
var overlayInfoSet = this.map[keys[i]];
|
||||||
|
|
||||||
|
// this overlayInfo is highlighted.
|
||||||
|
if (this.highlightedHotspots.indexOf(keys[i]) != -1) {
|
||||||
|
overlayInfoSet.targetSize = HIGHLIGHT_SIZE;
|
||||||
|
} else {
|
||||||
|
overlayInfoSet.targetSize = NORMAL_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start to fade out this hotspot.
|
||||||
|
if (overlayInfoSet.timestamp != timestamp) {
|
||||||
|
// because this item timestamp has expired, it might not be in the cache anymore....
|
||||||
|
entityPropertiesCache.addEntity(overlayInfoSet.entityID);
|
||||||
|
overlayInfoSet.targetSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// animate the size.
|
||||||
|
var SIZE_TIMESCALE = 0.1;
|
||||||
|
var tau = deltaTime / SIZE_TIMESCALE;
|
||||||
|
if (tau > 1.0) {
|
||||||
|
tau = 1.0;
|
||||||
|
}
|
||||||
|
overlayInfoSet.currentSize += (overlayInfoSet.targetSize - overlayInfoSet.currentSize) * tau;
|
||||||
|
|
||||||
|
if (overlayInfoSet.timestamp != timestamp && overlayInfoSet.currentSize <= 0.05) {
|
||||||
|
// this is an old overlay, that has finished fading out, delete it!
|
||||||
|
overlayInfoSet.overlays.forEach(function (overlay) {
|
||||||
|
Overlays.deleteOverlay(overlay);
|
||||||
|
});
|
||||||
|
delete this.map[keys[i]];
|
||||||
|
} else {
|
||||||
|
// update overlay position, rotation to follow the object it's attached to.
|
||||||
|
|
||||||
|
var props = entityPropertiesCache.getProps(overlayInfoSet.entityID);
|
||||||
|
var entityXform = new Xform(props.rotation, props.position);
|
||||||
|
var position = entityXform.xformPoint(overlayInfoSet.localPosition);
|
||||||
|
|
||||||
|
var dimensions;
|
||||||
|
if (overlayInfoSet.type == "sphere") {
|
||||||
|
dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize * EQUIP_SPHERE_SCALE_FACTOR;
|
||||||
|
} else {
|
||||||
|
dimensions = overlayInfoSet.hotspot.radius * 2 * overlayInfoSet.currentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
overlayInfoSet.overlays.forEach(function (overlay) {
|
||||||
|
Overlays.editOverlay(overlay, {
|
||||||
|
position: position,
|
||||||
|
rotation: props.rotation,
|
||||||
|
dimensions: dimensions
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// global EquipHotspotBuddy instance
|
||||||
|
var equipHotspotBuddy = new EquipHotspotBuddy();
|
||||||
|
|
||||||
function MyController(hand) {
|
function MyController(hand) {
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
if (this.hand === RIGHT_HAND) {
|
if (this.hand === RIGHT_HAND) {
|
||||||
|
@ -421,8 +643,6 @@ function MyController(hand) {
|
||||||
this.lastPickTime = 0;
|
this.lastPickTime = 0;
|
||||||
this.lastUnequipCheckTime = 0;
|
this.lastUnequipCheckTime = 0;
|
||||||
|
|
||||||
this.entityPropertyCache = new EntityPropertiesCache();
|
|
||||||
|
|
||||||
this.equipOverlayInfoSetMap = {};
|
this.equipOverlayInfoSetMap = {};
|
||||||
|
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
@ -434,7 +654,7 @@ function MyController(hand) {
|
||||||
return (-1 !== suppressedIn2D.indexOf(this.state)) && isIn2DMode();
|
return (-1 !== suppressedIn2D.indexOf(this.state)) && isIn2DMode();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update = function (deltaTime) {
|
this.update = function (deltaTime, timestamp) {
|
||||||
|
|
||||||
this.updateSmoothedTrigger();
|
this.updateSmoothedTrigger();
|
||||||
|
|
||||||
|
@ -447,7 +667,7 @@ function MyController(hand) {
|
||||||
var updateMethodName = CONTROLLER_STATE_MACHINE[this.state].updateMethod;
|
var updateMethodName = CONTROLLER_STATE_MACHINE[this.state].updateMethod;
|
||||||
var updateMethod = this[updateMethodName];
|
var updateMethod = this[updateMethodName];
|
||||||
if (updateMethod) {
|
if (updateMethod) {
|
||||||
updateMethod.call(this, deltaTime);
|
updateMethod.call(this, deltaTime, timestamp);
|
||||||
} else {
|
} else {
|
||||||
print("WARNING: could not find updateMethod for state " + stateToName(this.state));
|
print("WARNING: could not find updateMethod for state " + stateToName(this.state));
|
||||||
}
|
}
|
||||||
|
@ -547,23 +767,33 @@ function MyController(hand) {
|
||||||
|
|
||||||
var SEARCH_SPHERE_ALPHA = 0.5;
|
var SEARCH_SPHERE_ALPHA = 0.5;
|
||||||
this.searchSphereOn = function (location, size, color) {
|
this.searchSphereOn = function (location, size, color) {
|
||||||
|
|
||||||
|
var rotation = Quat.lookAt(location, Camera.getPosition(), Vec3.UP);
|
||||||
|
var brightColor = colorPow(color, 0.06);
|
||||||
if (this.searchSphere === null) {
|
if (this.searchSphere === null) {
|
||||||
var sphereProperties = {
|
var sphereProperties = {
|
||||||
position: location,
|
position: location,
|
||||||
size: size,
|
rotation: rotation,
|
||||||
color: color,
|
outerRadius: size * 1.2,
|
||||||
alpha: SEARCH_SPHERE_ALPHA,
|
innerColor: brightColor,
|
||||||
|
outerColor: color,
|
||||||
|
innerAlpha: 0.9,
|
||||||
|
outerAlpha: 0.0,
|
||||||
solid: true,
|
solid: true,
|
||||||
ignoreRayIntersection: true,
|
ignoreRayIntersection: true,
|
||||||
drawInFront: true, // Even when burried inside of something, show it.
|
drawInFront: true, // Even when burried inside of something, show it.
|
||||||
visible: true
|
visible: true
|
||||||
};
|
};
|
||||||
this.searchSphere = Overlays.addOverlay("sphere", sphereProperties);
|
this.searchSphere = Overlays.addOverlay("circle3d", sphereProperties);
|
||||||
} else {
|
} else {
|
||||||
Overlays.editOverlay(this.searchSphere, {
|
Overlays.editOverlay(this.searchSphere, {
|
||||||
position: location,
|
position: location,
|
||||||
size: size,
|
rotation: rotation,
|
||||||
color: color,
|
innerColor: brightColor,
|
||||||
|
outerColor: color,
|
||||||
|
innerAlpha: 1.0,
|
||||||
|
outerAlpha: 0.0,
|
||||||
|
outerRadius: size * 1.2,
|
||||||
visible: true
|
visible: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -907,7 +1137,7 @@ function MyController(hand) {
|
||||||
return _this.rawThumbValue < THUMB_ON_VALUE;
|
return _this.rawThumbValue < THUMB_ON_VALUE;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.off = function () {
|
this.off = function (deltaTime, timestamp) {
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
this.waitForTriggerRelease = false;
|
this.waitForTriggerRelease = false;
|
||||||
}
|
}
|
||||||
|
@ -921,17 +1151,18 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.entityPropertyCache.clear();
|
var candidateEntities = Entities.findEntities(this.getHandPosition(), EQUIP_HOTSPOT_RENDER_RADIUS);
|
||||||
this.entityPropertyCache.findEntities(this.getHandPosition(), EQUIP_HOTSPOT_RENDER_RADIUS);
|
entityPropertiesCache.addEntities(candidateEntities);
|
||||||
var candidateEntities = this.entityPropertyCache.getEntities();
|
|
||||||
|
|
||||||
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
|
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
|
||||||
if (!this.waitForTriggerRelease) {
|
if (!this.waitForTriggerRelease) {
|
||||||
this.updateEquipHaptics(potentialEquipHotspot);
|
this.updateEquipHaptics(potentialEquipHotspot);
|
||||||
}
|
}
|
||||||
|
|
||||||
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
|
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
|
||||||
this.updateEquipHotspotRendering(nearEquipHotspots, potentialEquipHotspot);
|
equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp);
|
||||||
|
if (potentialEquipHotspot) {
|
||||||
|
equipHotspotBuddy.highlightHotspot(potentialEquipHotspot);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.clearEquipHaptics = function () {
|
this.clearEquipHaptics = function () {
|
||||||
|
@ -946,95 +1177,6 @@ function MyController(hand) {
|
||||||
this.prevPotentialEquipHotspot = potentialEquipHotspot;
|
this.prevPotentialEquipHotspot = potentialEquipHotspot;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.clearEquipHotspotRendering = function () {
|
|
||||||
var keys = Object.keys(this.equipOverlayInfoSetMap);
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var overlayInfoSet = this.equipOverlayInfoSetMap[keys[i]];
|
|
||||||
this.deleteOverlayInfoSet(overlayInfoSet);
|
|
||||||
}
|
|
||||||
this.equipOverlayInfoSetMap = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
this.createOverlayInfoSet = function (hotspot, timestamp) {
|
|
||||||
var overlayInfoSet = {
|
|
||||||
timestamp: timestamp,
|
|
||||||
entityID: hotspot.entityID,
|
|
||||||
localPosition: hotspot.localPosition,
|
|
||||||
hotspot: hotspot,
|
|
||||||
overlays: []
|
|
||||||
};
|
|
||||||
|
|
||||||
var diameter = hotspot.radius * 2;
|
|
||||||
|
|
||||||
overlayInfoSet.overlays.push(Overlays.addOverlay("sphere", {
|
|
||||||
position: hotspot.worldPosition,
|
|
||||||
rotation: {x: 0, y: 0, z: 0, w: 1},
|
|
||||||
dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR,
|
|
||||||
color: EQUIP_SPHERE_COLOR,
|
|
||||||
alpha: EQUIP_SPHERE_ALPHA,
|
|
||||||
solid: true,
|
|
||||||
visible: true,
|
|
||||||
ignoreRayIntersection: true,
|
|
||||||
drawInFront: false
|
|
||||||
}));
|
|
||||||
|
|
||||||
return overlayInfoSet;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateOverlayInfoSet = function (overlayInfoSet, timestamp, potentialEquipHotspot) {
|
|
||||||
overlayInfoSet.timestamp = timestamp;
|
|
||||||
|
|
||||||
var diameter = overlayInfoSet.hotspot.radius * 2;
|
|
||||||
|
|
||||||
// embiggen the overlays if it maches the potentialEquipHotspot
|
|
||||||
if (potentialEquipHotspot && overlayInfoSet.entityID == potentialEquipHotspot.entityID &&
|
|
||||||
Vec3.equal(overlayInfoSet.localPosition, potentialEquipHotspot.localPosition)) {
|
|
||||||
diameter = diameter * EQUIP_RADIUS_EMBIGGEN_FACTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
var props = _this.entityPropertyCache.getProps(overlayInfoSet.entityID);
|
|
||||||
var entityXform = new Xform(props.rotation, props.position);
|
|
||||||
var position = entityXform.xformPoint(overlayInfoSet.localPosition);
|
|
||||||
|
|
||||||
overlayInfoSet.overlays.forEach(function (overlay) {
|
|
||||||
Overlays.editOverlay(overlay, {
|
|
||||||
position: position,
|
|
||||||
rotation: props.rotation,
|
|
||||||
dimensions: diameter * EQUIP_SPHERE_SCALE_FACTOR
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.deleteOverlayInfoSet = function (overlayInfoSet) {
|
|
||||||
overlayInfoSet.overlays.forEach(function (overlay) {
|
|
||||||
Overlays.deleteOverlay(overlay);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateEquipHotspotRendering = function (hotspots, potentialEquipHotspot) {
|
|
||||||
var now = Date.now();
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
hotspots.forEach(function (hotspot) {
|
|
||||||
var overlayInfoSet = _this.equipOverlayInfoSetMap[hotspot.key];
|
|
||||||
if (overlayInfoSet) {
|
|
||||||
_this.updateOverlayInfoSet(overlayInfoSet, now, potentialEquipHotspot);
|
|
||||||
} else {
|
|
||||||
_this.equipOverlayInfoSetMap[hotspot.key] = _this.createOverlayInfoSet(hotspot, now);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// delete sets with old timestamps.
|
|
||||||
var keys = Object.keys(this.equipOverlayInfoSetMap);
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
|
||||||
var overlayInfoSet = this.equipOverlayInfoSetMap[keys[i]];
|
|
||||||
if (overlayInfoSet.timestamp !== now) {
|
|
||||||
this.deleteOverlayInfoSet(overlayInfoSet);
|
|
||||||
delete this.equipOverlayInfoSetMap[keys[i]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Performs ray pick test from the hand controller into the world
|
// Performs ray pick test from the hand controller into the world
|
||||||
// @param {number} which hand to use, RIGHT_HAND or LEFT_HAND
|
// @param {number} which hand to use, RIGHT_HAND or LEFT_HAND
|
||||||
// @returns {object} returns object with two keys entityID and distance
|
// @returns {object} returns object with two keys entityID and distance
|
||||||
|
@ -1093,7 +1235,7 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.entityWantsTrigger = function (entityID) {
|
this.entityWantsTrigger = function (entityID) {
|
||||||
var grabbableProps = this.entityPropertyCache.getGrabbableProps(entityID);
|
var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID);
|
||||||
return grabbableProps && grabbableProps.wantsTrigger;
|
return grabbableProps && grabbableProps.wantsTrigger;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1107,11 +1249,13 @@ function MyController(hand) {
|
||||||
// * radius {number} radius of equip hotspot
|
// * radius {number} radius of equip hotspot
|
||||||
// * joints {Object} keys are joint names values are arrays of two elements:
|
// * joints {Object} keys are joint names values are arrays of two elements:
|
||||||
// offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint.
|
// offset position {Vec3} and offset rotation {Quat}, both are in the coordinate system of the joint.
|
||||||
|
// * modelURL {string} url for model to use instead of default sphere.
|
||||||
|
// * modelScale {Vec3} scale factor for model
|
||||||
this.collectEquipHotspots = function (entityID) {
|
this.collectEquipHotspots = function (entityID) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var props = this.entityPropertyCache.getProps(entityID);
|
var props = entityPropertiesCache.getProps(entityID);
|
||||||
var entityXform = new Xform(props.rotation, props.position);
|
var entityXform = new Xform(props.rotation, props.position);
|
||||||
var equipHotspotsProps = this.entityPropertyCache.getEquipHotspotsProps(entityID);
|
var equipHotspotsProps = entityPropertiesCache.getEquipHotspotsProps(entityID);
|
||||||
if (equipHotspotsProps && equipHotspotsProps.length > 0) {
|
if (equipHotspotsProps && equipHotspotsProps.length > 0) {
|
||||||
var i, length = equipHotspotsProps.length;
|
var i, length = equipHotspotsProps.length;
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
|
@ -1123,12 +1267,14 @@ function MyController(hand) {
|
||||||
localPosition: hotspot.position,
|
localPosition: hotspot.position,
|
||||||
worldPosition: entityXform.xformPoint(hotspot.position),
|
worldPosition: entityXform.xformPoint(hotspot.position),
|
||||||
radius: hotspot.radius,
|
radius: hotspot.radius,
|
||||||
joints: hotspot.joints
|
joints: hotspot.joints,
|
||||||
|
modelURL: hotspot.modelURL,
|
||||||
|
modelScale: hotspot.modelScale
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var wearableProps = this.entityPropertyCache.getWearableProps(entityID);
|
var wearableProps = entityPropertiesCache.getWearableProps(entityID);
|
||||||
if (wearableProps && wearableProps.joints) {
|
if (wearableProps && wearableProps.joints) {
|
||||||
result.push({
|
result.push({
|
||||||
key: entityID.toString() + "0",
|
key: entityID.toString() + "0",
|
||||||
|
@ -1136,7 +1282,9 @@ function MyController(hand) {
|
||||||
localPosition: {x: 0, y: 0, z: 0},
|
localPosition: {x: 0, y: 0, z: 0},
|
||||||
worldPosition: entityXform.pos,
|
worldPosition: entityXform.pos,
|
||||||
radius: EQUIP_RADIUS,
|
radius: EQUIP_RADIUS,
|
||||||
joints: wearableProps.joints
|
joints: wearableProps.joints,
|
||||||
|
modelURL: null,
|
||||||
|
modelScale: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1144,8 +1292,8 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.hotspotIsEquippable = function (hotspot) {
|
this.hotspotIsEquippable = function (hotspot) {
|
||||||
var props = this.entityPropertyCache.getProps(hotspot.entityID);
|
var props = entityPropertiesCache.getProps(hotspot.entityID);
|
||||||
var grabProps = this.entityPropertyCache.getGrabProps(hotspot.entityID);
|
var grabProps = entityPropertiesCache.getGrabProps(hotspot.entityID);
|
||||||
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
||||||
|
|
||||||
var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0;
|
var refCount = ("refCount" in grabProps) ? grabProps.refCount : 0;
|
||||||
|
@ -1163,9 +1311,9 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.entityIsGrabbable = function (entityID) {
|
this.entityIsGrabbable = function (entityID) {
|
||||||
var grabbableProps = this.entityPropertyCache.getGrabbableProps(entityID);
|
var grabbableProps = entityPropertiesCache.getGrabbableProps(entityID);
|
||||||
var grabProps = this.entityPropertyCache.getGrabProps(entityID);
|
var grabProps = entityPropertiesCache.getGrabProps(entityID);
|
||||||
var props = this.entityPropertyCache.getProps(entityID);
|
var props = entityPropertiesCache.getProps(entityID);
|
||||||
var physical = propsArePhysical(props);
|
var physical = propsArePhysical(props);
|
||||||
var grabbable = false;
|
var grabbable = false;
|
||||||
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
||||||
|
@ -1219,7 +1367,7 @@ function MyController(hand) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var props = this.entityPropertyCache.getProps(entityID);
|
var props = entityPropertiesCache.getProps(entityID);
|
||||||
var distance = Vec3.distance(props.position, handPosition);
|
var distance = Vec3.distance(props.position, handPosition);
|
||||||
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
||||||
|
|
||||||
|
@ -1257,7 +1405,7 @@ function MyController(hand) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var props = this.entityPropertyCache.getProps(entityID);
|
var props = entityPropertiesCache.getProps(entityID);
|
||||||
var distance = Vec3.distance(props.position, handPosition);
|
var distance = Vec3.distance(props.position, handPosition);
|
||||||
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
var debug = (WANT_DEBUG_SEARCH_NAME && props.name === WANT_DEBUG_SEARCH_NAME);
|
||||||
|
|
||||||
|
@ -1298,7 +1446,7 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.search = function () {
|
this.search = function (deltaTime, timestamp) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var name;
|
var name;
|
||||||
|
|
||||||
|
@ -1315,16 +1463,15 @@ function MyController(hand) {
|
||||||
|
|
||||||
var handPosition = this.getHandPosition();
|
var handPosition = this.getHandPosition();
|
||||||
|
|
||||||
this.entityPropertyCache.clear();
|
var candidateEntities = Entities.findEntities(handPosition, NEAR_GRAB_RADIUS);
|
||||||
this.entityPropertyCache.findEntities(handPosition, NEAR_GRAB_RADIUS);
|
entityPropertiesCache.addEntities(candidateEntities);
|
||||||
var candidateEntities = this.entityPropertyCache.getEntities();
|
|
||||||
|
|
||||||
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
|
var potentialEquipHotspot = this.chooseBestEquipHotspot(candidateEntities);
|
||||||
if (potentialEquipHotspot) {
|
if (potentialEquipHotspot) {
|
||||||
if (this.triggerSmoothedGrab()) {
|
if (this.triggerSmoothedGrab()) {
|
||||||
this.grabbedHotspot = potentialEquipHotspot;
|
this.grabbedHotspot = potentialEquipHotspot;
|
||||||
this.grabbedEntity = potentialEquipHotspot.entityID;
|
this.grabbedEntity = potentialEquipHotspot.entityID;
|
||||||
this.setState(STATE_HOLD, "eqipping '" + this.entityPropertyCache.getProps(this.grabbedEntity).name + "'");
|
this.setState(STATE_HOLD, "eqipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1336,7 +1483,7 @@ function MyController(hand) {
|
||||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||||
if (rayPickInfo.entityID) {
|
if (rayPickInfo.entityID) {
|
||||||
this.intersectionDistance = rayPickInfo.distance;
|
this.intersectionDistance = rayPickInfo.distance;
|
||||||
this.entityPropertyCache.updateEntity(rayPickInfo.entityID);
|
entityPropertiesCache.addEntity(rayPickInfo.entityID);
|
||||||
if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) {
|
if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) {
|
||||||
grabbableEntities.push(rayPickInfo.entityID);
|
grabbableEntities.push(rayPickInfo.entityID);
|
||||||
}
|
}
|
||||||
|
@ -1350,12 +1497,12 @@ function MyController(hand) {
|
||||||
if (grabbableEntities.length > 0) {
|
if (grabbableEntities.length > 0) {
|
||||||
// sort by distance
|
// sort by distance
|
||||||
grabbableEntities.sort(function (a, b) {
|
grabbableEntities.sort(function (a, b) {
|
||||||
var aDistance = Vec3.distance(_this.entityPropertyCache.getProps(a).position, handPosition);
|
var aDistance = Vec3.distance(entityPropertiesCache.getProps(a).position, handPosition);
|
||||||
var bDistance = Vec3.distance(_this.entityPropertyCache.getProps(b).position, handPosition);
|
var bDistance = Vec3.distance(entityPropertiesCache.getProps(b).position, handPosition);
|
||||||
return aDistance - bDistance;
|
return aDistance - bDistance;
|
||||||
});
|
});
|
||||||
entity = grabbableEntities[0];
|
entity = grabbableEntities[0];
|
||||||
name = this.entityPropertyCache.getProps(entity).name;
|
name = entityPropertiesCache.getProps(entity).name;
|
||||||
this.grabbedEntity = entity;
|
this.grabbedEntity = entity;
|
||||||
if (this.entityWantsTrigger(entity)) {
|
if (this.entityWantsTrigger(entity)) {
|
||||||
if (this.triggerSmoothedGrab()) {
|
if (this.triggerSmoothedGrab()) {
|
||||||
|
@ -1366,8 +1513,8 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.triggerSmoothedGrab()) {
|
if (this.triggerSmoothedGrab()) {
|
||||||
var props = this.entityPropertyCache.getProps(entity);
|
var props = entityPropertiesCache.getProps(entity);
|
||||||
var grabProps = this.entityPropertyCache.getGrabProps(entity);
|
var grabProps = entityPropertiesCache.getGrabProps(entity);
|
||||||
var refCount = grabProps.refCount ? grabProps.refCount : 0;
|
var refCount = grabProps.refCount ? grabProps.refCount : 0;
|
||||||
if (refCount >= 1) {
|
if (refCount >= 1) {
|
||||||
// if another person is holding the object, remember to restore the
|
// if another person is holding the object, remember to restore the
|
||||||
|
@ -1387,7 +1534,7 @@ function MyController(hand) {
|
||||||
|
|
||||||
if (rayPickInfo.entityID) {
|
if (rayPickInfo.entityID) {
|
||||||
entity = rayPickInfo.entityID;
|
entity = rayPickInfo.entityID;
|
||||||
name = this.entityPropertyCache.getProps(entity).name;
|
name = entityPropertiesCache.getProps(entity).name;
|
||||||
if (this.entityWantsTrigger(entity)) {
|
if (this.entityWantsTrigger(entity)) {
|
||||||
if (this.triggerSmoothedGrab()) {
|
if (this.triggerSmoothedGrab()) {
|
||||||
this.grabbedEntity = entity;
|
this.grabbedEntity = entity;
|
||||||
|
@ -1410,7 +1557,10 @@ function MyController(hand) {
|
||||||
this.updateEquipHaptics(potentialEquipHotspot);
|
this.updateEquipHaptics(potentialEquipHotspot);
|
||||||
|
|
||||||
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
|
var nearEquipHotspots = this.chooseNearEquipHotspots(candidateEntities, EQUIP_HOTSPOT_RENDER_RADIUS);
|
||||||
this.updateEquipHotspotRendering(nearEquipHotspots, potentialEquipHotspot);
|
equipHotspotBuddy.updateHotspots(nearEquipHotspots, timestamp);
|
||||||
|
if (potentialEquipHotspot) {
|
||||||
|
equipHotspotBuddy.highlightHotspot(potentialEquipHotspot);
|
||||||
|
}
|
||||||
|
|
||||||
// search line visualizations
|
// search line visualizations
|
||||||
if (USE_ENTITY_LINES_FOR_SEARCHING === true) {
|
if (USE_ENTITY_LINES_FOR_SEARCHING === true) {
|
||||||
|
@ -1439,7 +1589,6 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.distanceHoldingEnter = function () {
|
this.distanceHoldingEnter = function () {
|
||||||
|
|
||||||
this.clearEquipHotspotRendering();
|
|
||||||
this.clearEquipHaptics();
|
this.clearEquipHaptics();
|
||||||
|
|
||||||
// controller pose is in avatar frame
|
// controller pose is in avatar frame
|
||||||
|
@ -1499,7 +1648,7 @@ function MyController(hand) {
|
||||||
this.previousControllerRotation = controllerRotation;
|
this.previousControllerRotation = controllerRotation;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.distanceHolding = function () {
|
this.distanceHolding = function (deltaTime, timestamp) {
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||||
this.setState(STATE_OFF, "trigger released");
|
this.setState(STATE_OFF, "trigger released");
|
||||||
|
@ -1668,47 +1817,35 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dropGestureReset = function () {
|
this.dropGestureReset = function () {
|
||||||
this.fastHandMoveDetected = false;
|
this.prevHandIsUpsideDown = false;
|
||||||
this.fastHandMoveTimer = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dropGestureProcess = function (deltaTime) {
|
this.dropGestureProcess = function (deltaTime) {
|
||||||
var standardControllerValue = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
var standardControllerValue = (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||||
var pose = Controller.getPoseValue(standardControllerValue);
|
var pose = Controller.getPoseValue(standardControllerValue);
|
||||||
var worldHandVelocity = Vec3.multiplyQbyV(MyAvatar.orientation, pose.velocity);
|
|
||||||
var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation);
|
var worldHandRotation = Quat.multiply(MyAvatar.orientation, pose.rotation);
|
||||||
|
|
||||||
if (this.fastHandMoveDetected) {
|
|
||||||
this.fastHandMoveTimer -= deltaTime;
|
|
||||||
}
|
|
||||||
if (this.fastHandMoveTimer < 0) {
|
|
||||||
this.fastHandMoveDetected = false;
|
|
||||||
}
|
|
||||||
var FAST_HAND_SPEED_REST_TIME = 1; // sec
|
|
||||||
var FAST_HAND_SPEED_THRESHOLD = 0.4; // m/sec
|
|
||||||
if (Vec3.length(worldHandVelocity) > FAST_HAND_SPEED_THRESHOLD) {
|
|
||||||
this.fastHandMoveDetected = true;
|
|
||||||
this.fastHandMoveTimer = FAST_HAND_SPEED_REST_TIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
var localHandUpAxis = this.hand === RIGHT_HAND ? {x: 1, y: 0, z: 0} : {x: -1, y: 0, z: 0};
|
var localHandUpAxis = this.hand === RIGHT_HAND ? {x: 1, y: 0, z: 0} : {x: -1, y: 0, z: 0};
|
||||||
var worldHandUpAxis = Vec3.multiplyQbyV(worldHandRotation, localHandUpAxis);
|
var worldHandUpAxis = Vec3.multiplyQbyV(worldHandRotation, localHandUpAxis);
|
||||||
var DOWN = {x: 0, y: -1, z: 0};
|
var DOWN = {x: 0, y: -1, z: 0};
|
||||||
var ROTATION_THRESHOLD = Math.cos(Math.PI / 8);
|
|
||||||
|
var DROP_ANGLE = Math.PI / 7;
|
||||||
|
var HYSTERESIS_FACTOR = 1.1;
|
||||||
|
var ROTATION_ENTER_THRESHOLD = Math.cos(DROP_ANGLE);
|
||||||
|
var ROTATION_EXIT_THRESHOLD = Math.cos(DROP_ANGLE * HYSTERESIS_FACTOR);
|
||||||
|
var rotationThreshold = this.prevHandIsUpsideDown ? ROTATION_EXIT_THRESHOLD : ROTATION_ENTER_THRESHOLD;
|
||||||
|
|
||||||
var handIsUpsideDown = false;
|
var handIsUpsideDown = false;
|
||||||
if (Vec3.dot(worldHandUpAxis, DOWN) > ROTATION_THRESHOLD) {
|
if (Vec3.dot(worldHandUpAxis, DOWN) > rotationThreshold) {
|
||||||
handIsUpsideDown = true;
|
handIsUpsideDown = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var WANT_DEBUG = false;
|
if (handIsUpsideDown != this.prevHandIsUpsideDown) {
|
||||||
if (WANT_DEBUG) {
|
this.prevHandIsUpsideDown = handIsUpsideDown;
|
||||||
print("zAxis = " + worldHandUpAxis.x + ", " + worldHandUpAxis.y + ", " + worldHandUpAxis.z);
|
Controller.triggerShortHapticPulse(0.5, this.hand);
|
||||||
print("dot = " + Vec3.dot(worldHandUpAxis, DOWN) + ", ROTATION_THRESHOLD = " + ROTATION_THRESHOLD);
|
|
||||||
print("handMove = " + this.fastHandMoveDetected + ", handIsUpsideDown = " + handIsUpsideDown);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (DROP_WITHOUT_SHAKE || this.fastHandMoveDetected) && handIsUpsideDown;
|
return handIsUpsideDown;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.nearGrabbingEnter = function () {
|
this.nearGrabbingEnter = function () {
|
||||||
|
@ -1718,7 +1855,6 @@ function MyController(hand) {
|
||||||
|
|
||||||
this.dropGestureReset();
|
this.dropGestureReset();
|
||||||
this.clearEquipHaptics();
|
this.clearEquipHaptics();
|
||||||
this.clearEquipHotspotRendering();
|
|
||||||
|
|
||||||
Controller.triggerShortHapticPulse(1.0, this.hand);
|
Controller.triggerShortHapticPulse(1.0, this.hand);
|
||||||
|
|
||||||
|
@ -1747,11 +1883,18 @@ function MyController(hand) {
|
||||||
// if an object is "equipped" and has a predefined offset, use it.
|
// if an object is "equipped" and has a predefined offset, use it.
|
||||||
this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false;
|
this.ignoreIK = grabbableData.ignoreIK ? grabbableData.ignoreIK : false;
|
||||||
|
|
||||||
var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand";
|
var offsets = USE_ATTACH_POINT_SETTINGS && getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand);
|
||||||
if (this.grabbedHotspot.joints[handJointName]) {
|
if (offsets) {
|
||||||
this.offsetPosition = this.grabbedHotspot.joints[handJointName][0];
|
this.offsetPosition = offsets[0];
|
||||||
this.offsetRotation = this.grabbedHotspot.joints[handJointName][1];
|
this.offsetRotation = offsets[1];
|
||||||
hasPresetPosition = true;
|
hasPresetPosition = true;
|
||||||
|
} else {
|
||||||
|
var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand";
|
||||||
|
if (this.grabbedHotspot.joints[handJointName]) {
|
||||||
|
this.offsetPosition = this.grabbedHotspot.joints[handJointName][0];
|
||||||
|
this.offsetRotation = this.grabbedHotspot.joints[handJointName][1];
|
||||||
|
hasPresetPosition = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.ignoreIK = false;
|
this.ignoreIK = false;
|
||||||
|
@ -1820,7 +1963,7 @@ function MyController(hand) {
|
||||||
this.currentAngularVelocity = ZERO_VEC;
|
this.currentAngularVelocity = ZERO_VEC;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.nearGrabbing = function (deltaTime) {
|
this.nearGrabbing = function (deltaTime, timestamp) {
|
||||||
|
|
||||||
var dropDetected = this.dropGestureProcess(deltaTime);
|
var dropDetected = this.dropGestureProcess(deltaTime);
|
||||||
|
|
||||||
|
@ -1831,6 +1974,14 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state == STATE_HOLD) {
|
if (this.state == STATE_HOLD) {
|
||||||
|
|
||||||
|
// highlight the grabbed hotspot when the dropGesture is detected.
|
||||||
|
if (dropDetected) {
|
||||||
|
entityPropertiesCache.addEntity(this.grabbedHotspot.entityID);
|
||||||
|
equipHotspotBuddy.updateHotspot(this.grabbedHotspot, timestamp);
|
||||||
|
equipHotspotBuddy.highlightHotspot(this.grabbedHotspot);
|
||||||
|
}
|
||||||
|
|
||||||
if (dropDetected && this.triggerSmoothedGrab()) {
|
if (dropDetected && this.triggerSmoothedGrab()) {
|
||||||
this.callEntityMethodOnGrabbed("releaseEquip");
|
this.callEntityMethodOnGrabbed("releaseEquip");
|
||||||
this.setState(STATE_OFF, "drop gesture detected");
|
this.setState(STATE_OFF, "drop gesture detected");
|
||||||
|
@ -1937,9 +2088,24 @@ function MyController(hand) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.holdExit = function () {
|
||||||
|
// store the offset attach points into preferences.
|
||||||
|
if (USE_ATTACH_POINT_SETTINGS && this.grabbedHotspot && this.grabbedEntity) {
|
||||||
|
entityPropertiesCache.addEntity(this.grabbedEntity);
|
||||||
|
var props = entityPropertiesCache.getProps(this.grabbedEntity);
|
||||||
|
var entityXform = new Xform(props.rotation, props.position);
|
||||||
|
var avatarXform = new Xform(MyAvatar.orientation, MyAvatar.position);
|
||||||
|
var handRot = (this.hand === RIGHT_HAND) ? MyAvatar.getRightPalmRotation() : MyAvatar.getLeftPalmRotation();
|
||||||
|
var avatarHandPos = (this.hand === RIGHT_HAND) ? MyAvatar.rightHandPosition : MyAvatar.leftHandPosition;
|
||||||
|
var palmXform = new Xform(handRot, avatarXform.xformPoint(avatarHandPos));
|
||||||
|
var offsetXform = Xform.mul(palmXform.inv(), entityXform);
|
||||||
|
|
||||||
|
storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand, offsetXform.pos, offsetXform.rot);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.nearTriggerEnter = function () {
|
this.nearTriggerEnter = function () {
|
||||||
|
|
||||||
this.clearEquipHotspotRendering();
|
|
||||||
this.clearEquipHaptics();
|
this.clearEquipHaptics();
|
||||||
|
|
||||||
Controller.triggerShortHapticPulse(1.0, this.hand);
|
Controller.triggerShortHapticPulse(1.0, this.hand);
|
||||||
|
@ -1947,13 +2113,12 @@ function MyController(hand) {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.farTriggerEnter = function () {
|
this.farTriggerEnter = function () {
|
||||||
this.clearEquipHotspotRendering();
|
|
||||||
this.clearEquipHaptics();
|
this.clearEquipHaptics();
|
||||||
|
|
||||||
this.callEntityMethodOnGrabbed("startFarTrigger");
|
this.callEntityMethodOnGrabbed("startFarTrigger");
|
||||||
};
|
};
|
||||||
|
|
||||||
this.nearTrigger = function () {
|
this.nearTrigger = function (deltaTime, timestamp) {
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
this.callEntityMethodOnGrabbed("stopNearTrigger");
|
this.callEntityMethodOnGrabbed("stopNearTrigger");
|
||||||
this.setState(STATE_OFF, "trigger released");
|
this.setState(STATE_OFF, "trigger released");
|
||||||
|
@ -1962,7 +2127,7 @@ function MyController(hand) {
|
||||||
this.callEntityMethodOnGrabbed("continueNearTrigger");
|
this.callEntityMethodOnGrabbed("continueNearTrigger");
|
||||||
};
|
};
|
||||||
|
|
||||||
this.farTrigger = function () {
|
this.farTrigger = function (deltaTime, timestamp) {
|
||||||
if (this.triggerSmoothedReleased()) {
|
if (this.triggerSmoothedReleased()) {
|
||||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||||
this.setState(STATE_OFF, "trigger released");
|
this.setState(STATE_OFF, "trigger released");
|
||||||
|
@ -2010,7 +2175,7 @@ function MyController(hand) {
|
||||||
// and rotation of the held thing to help content creators set the userData.
|
// and rotation of the held thing to help content creators set the userData.
|
||||||
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
var grabData = getEntityCustomData(GRAB_USER_DATA_KEY, this.grabbedEntity, {});
|
||||||
if (grabData.refCount > 1) {
|
if (grabData.refCount > 1) {
|
||||||
grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]);
|
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]);
|
||||||
if (grabbedProperties && grabbedProperties.localPosition && grabbedProperties.localRotation) {
|
if (grabbedProperties && grabbedProperties.localPosition && grabbedProperties.localRotation) {
|
||||||
print((this.hand === RIGHT_HAND ? '"LeftHand"' : '"RightHand"') + ":" +
|
print((this.hand === RIGHT_HAND ? '"LeftHand"' : '"RightHand"') + ":" +
|
||||||
'[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y +
|
'[{"x":' + grabbedProperties.localPosition.x + ', "y":' + grabbedProperties.localPosition.y +
|
||||||
|
@ -2214,7 +2379,7 @@ function MyController(hand) {
|
||||||
delayedDeactivateTimeout = null;
|
delayedDeactivateTimeout = null;
|
||||||
_this.delayedDeactivateEntity(delayedEntityID, delayedCollidesWith);
|
_this.delayedDeactivateEntity(delayedEntityID, delayedCollidesWith);
|
||||||
return delayedCollidesWith;
|
return delayedCollidesWith;
|
||||||
}
|
};
|
||||||
delayedDeactivateTimeout =
|
delayedDeactivateTimeout =
|
||||||
Script.setTimeout(delayedDeactivateFunc, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY * MSECS_PER_SEC);
|
Script.setTimeout(delayedDeactivateFunc, COLLIDE_WITH_AV_AFTER_RELEASE_DELAY * MSECS_PER_SEC);
|
||||||
delayedDeactivateEntityID = entityID;
|
delayedDeactivateEntityID = entityID;
|
||||||
|
@ -2317,12 +2482,16 @@ Controller.enableMapping(MAPPING_NAME);
|
||||||
var handToDisable = 'none';
|
var handToDisable = 'none';
|
||||||
|
|
||||||
function update(deltaTime) {
|
function update(deltaTime) {
|
||||||
|
var timestamp = Date.now();
|
||||||
|
|
||||||
if (handToDisable !== LEFT_HAND && handToDisable !== 'both') {
|
if (handToDisable !== LEFT_HAND && handToDisable !== 'both') {
|
||||||
leftController.update(deltaTime);
|
leftController.update(deltaTime, timestamp);
|
||||||
}
|
}
|
||||||
if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') {
|
if (handToDisable !== RIGHT_HAND && handToDisable !== 'both') {
|
||||||
rightController.update(deltaTime);
|
rightController.update(deltaTime, timestamp);
|
||||||
}
|
}
|
||||||
|
equipHotspotBuddy.update(deltaTime, timestamp);
|
||||||
|
entityPropertiesCache.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
Messages.subscribe('Hifi-Hand-Disabler');
|
Messages.subscribe('Hifi-Hand-Disabler');
|
||||||
|
@ -2392,38 +2561,7 @@ function cleanup() {
|
||||||
leftController.cleanup();
|
leftController.cleanup();
|
||||||
Controller.disableMapping(MAPPING_NAME);
|
Controller.disableMapping(MAPPING_NAME);
|
||||||
Reticle.setVisible(true);
|
Reticle.setVisible(true);
|
||||||
Menu.removeMenuItem("Developer > Hands", "Drop Without Shake");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.scriptEnding.connect(cleanup);
|
Script.scriptEnding.connect(cleanup);
|
||||||
Script.update.connect(update);
|
Script.update.connect(update);
|
||||||
|
|
||||||
if (!Menu.menuExists("Developer > Grab Script")) {
|
|
||||||
Menu.addMenu("Developer > Grab Script");
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu.addMenuItem({
|
|
||||||
menuName: "Developer > Grab Script",
|
|
||||||
menuItemName: "Drop Without Shake",
|
|
||||||
isCheckable: true,
|
|
||||||
isChecked: DROP_WITHOUT_SHAKE
|
|
||||||
});
|
|
||||||
|
|
||||||
Menu.addMenuItem({
|
|
||||||
menuName: "Developer > Grab Script",
|
|
||||||
menuItemName: "Draw Grab Boxes",
|
|
||||||
isCheckable: true,
|
|
||||||
isChecked: DRAW_GRAB_BOXES
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleMenuItemEvent(menuItem) {
|
|
||||||
if (menuItem === "Drop Without Shake") {
|
|
||||||
DROP_WITHOUT_SHAKE = Menu.isOptionChecked("Drop Without Shake");
|
|
||||||
}
|
|
||||||
if (menuItem === "Draw Grab Boxes") {
|
|
||||||
DRAW_GRAB_BOXES = Menu.isOptionChecked("Draw Grab Boxes");
|
|
||||||
DRAW_HAND_SPHERES = DRAW_GRAB_BOXES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu.menuItemEvent.connect(handleMenuItemEvent);
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ function setupHandler(event, handler) {
|
||||||
event.disconnect(handler);
|
event.disconnect(handler);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If some capability is not available until expiration milliseconds after the last update.
|
// If some capability is not available until expiration milliseconds after the last update.
|
||||||
function TimeLock(expiration) {
|
function TimeLock(expiration) {
|
||||||
var last = 0;
|
var last = 0;
|
||||||
|
@ -42,6 +43,7 @@ function TimeLock(expiration) {
|
||||||
return ((optionalNow || Date.now()) - last) > expiration;
|
return ((optionalNow || Date.now()) - last) > expiration;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var handControllerLockOut = new TimeLock(2000);
|
var handControllerLockOut = new TimeLock(2000);
|
||||||
|
|
||||||
function Trigger(label) {
|
function Trigger(label) {
|
||||||
|
@ -118,6 +120,10 @@ function ignoreMouseActivity() {
|
||||||
if (!Reticle.allowMouseCapture) {
|
if (!Reticle.allowMouseCapture) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
var pos = Reticle.position;
|
||||||
|
if (pos.x == -1 && pos.y == -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
// Only we know if we moved it, which is why this script has to replace depthReticle.js
|
// Only we know if we moved it, which is why this script has to replace depthReticle.js
|
||||||
if (!weMovedReticle) {
|
if (!weMovedReticle) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -433,11 +439,12 @@ function clearSystemLaser() {
|
||||||
}
|
}
|
||||||
HMD.disableHandLasers(BOTH_HUD_LASERS);
|
HMD.disableHandLasers(BOTH_HUD_LASERS);
|
||||||
systemLaserOn = false;
|
systemLaserOn = false;
|
||||||
|
weMovedReticle = true;
|
||||||
|
Reticle.position = { x: -1, y: -1 };
|
||||||
}
|
}
|
||||||
function setColoredLaser() { // answer trigger state if lasers supported, else falsey.
|
function setColoredLaser() { // answer trigger state if lasers supported, else falsey.
|
||||||
var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW;
|
var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW;
|
||||||
return HMD.setHandLasers(activeHudLaser, true, color, SYSTEM_LASER_DIRECTION) && activeTrigger.state;
|
return HMD.setHandLasers(activeHudLaser, true, color, SYSTEM_LASER_DIRECTION) && activeTrigger.state;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MAIN OPERATIONS -----------
|
// MAIN OPERATIONS -----------
|
||||||
|
|
|
@ -235,11 +235,16 @@ var toolBar = (function () {
|
||||||
objectName: EDIT_TOGGLE_BUTTON,
|
objectName: EDIT_TOGGLE_BUTTON,
|
||||||
imageURL: TOOL_ICON_URL + "edit.svg",
|
imageURL: TOOL_ICON_URL + "edit.svg",
|
||||||
visible: true,
|
visible: true,
|
||||||
buttonState: 1
|
alpha: 0.9,
|
||||||
|
buttonState: 1,
|
||||||
|
hoverState: 3,
|
||||||
|
defaultState: 1
|
||||||
});
|
});
|
||||||
activeButton.clicked.connect(function () {
|
activeButton.clicked.connect(function () {
|
||||||
that.setActive(!isActive);
|
that.setActive(!isActive);
|
||||||
activeButton.writeProperty("buttonState", isActive ? 0 : 1);
|
activeButton.writeProperty("buttonState", isActive ? 0 : 1);
|
||||||
|
activeButton.writeProperty("defaultState", isActive ? 0 : 1);
|
||||||
|
activeButton.writeProperty("hoverState", isActive ? 2 : 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
toolBar = Toolbars.getToolbar(EDIT_TOOLBAR);
|
toolBar = Toolbars.getToolbar(EDIT_TOOLBAR);
|
||||||
|
|
|
@ -56,11 +56,15 @@ var browseExamplesButton = toolBar.addButton({
|
||||||
imageURL: toolIconUrl + "market.svg",
|
imageURL: toolIconUrl + "market.svg",
|
||||||
objectName: "examples",
|
objectName: "examples",
|
||||||
buttonState: 1,
|
buttonState: 1,
|
||||||
|
defaultState: 1,
|
||||||
|
hoverState: 3,
|
||||||
alpha: 0.9
|
alpha: 0.9
|
||||||
});
|
});
|
||||||
|
|
||||||
function onExamplesWindowVisibilityChanged() {
|
function onExamplesWindowVisibilityChanged() {
|
||||||
browseExamplesButton.writeProperty('buttonState', examplesWindow.visible ? 0 : 1);
|
browseExamplesButton.writeProperty('buttonState', examplesWindow.visible ? 0 : 1);
|
||||||
|
browseExamplesButton.writeProperty('defaultState', examplesWindow.visible ? 0 : 1);
|
||||||
|
browseExamplesButton.writeProperty('hoverState', examplesWindow.visible ? 2 : 3);
|
||||||
}
|
}
|
||||||
function onClick() {
|
function onClick() {
|
||||||
toggleExamples();
|
toggleExamples();
|
||||||
|
|
|
@ -17,11 +17,15 @@ var button = toolBar.addButton({
|
||||||
imageURL: Script.resolvePath("assets/images/tools/directory.svg"),
|
imageURL: Script.resolvePath("assets/images/tools/directory.svg"),
|
||||||
visible: true,
|
visible: true,
|
||||||
buttonState: 1,
|
buttonState: 1,
|
||||||
|
defaultState: 1,
|
||||||
|
hoverState: 3,
|
||||||
alpha: 0.9,
|
alpha: 0.9,
|
||||||
});
|
});
|
||||||
|
|
||||||
function onAddressBarShown(visible) {
|
function onAddressBarShown(visible) {
|
||||||
button.writeProperty('buttonState', visible ? 0 : 1);
|
button.writeProperty('buttonState', visible ? 0 : 1);
|
||||||
|
button.writeProperty('defaultState', visible ? 0 : 1);
|
||||||
|
button.writeProperty('hoverState', visible ? 2 : 3);
|
||||||
}
|
}
|
||||||
function onClicked(){
|
function onClicked(){
|
||||||
DialogsManager.toggleAddressBar();
|
DialogsManager.toggleAddressBar();
|
||||||
|
|
|
@ -22,6 +22,8 @@ var toolBar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
||||||
var button;
|
var button;
|
||||||
function onHmdChanged(isHmd) {
|
function onHmdChanged(isHmd) {
|
||||||
button.writeProperty('buttonState', isHmd ? 0 : 1);
|
button.writeProperty('buttonState', isHmd ? 0 : 1);
|
||||||
|
button.writeProperty('defaultState', isHmd ? 0 : 1);
|
||||||
|
button.writeProperty('hoverState', isHmd ? 2 : 3);
|
||||||
}
|
}
|
||||||
function onClicked(){
|
function onClicked(){
|
||||||
var isDesktop = Menu.isOptionChecked(desktopMenuItemName);
|
var isDesktop = Menu.isOptionChecked(desktopMenuItemName);
|
||||||
|
@ -32,6 +34,8 @@ if (headset) {
|
||||||
objectName: "hmdToggle",
|
objectName: "hmdToggle",
|
||||||
imageURL: Script.resolvePath("assets/images/tools/switch.svg"),
|
imageURL: Script.resolvePath("assets/images/tools/switch.svg"),
|
||||||
visible: true,
|
visible: true,
|
||||||
|
hoverState: 2,
|
||||||
|
defaultState: 0,
|
||||||
alpha: 0.9,
|
alpha: 0.9,
|
||||||
});
|
});
|
||||||
onHmdChanged(HMD.active);
|
onHmdChanged(HMD.active);
|
||||||
|
|
|
@ -1354,6 +1354,9 @@
|
||||||
<option value="Octahedron">Octahedron</option>
|
<option value="Octahedron">Octahedron</option>
|
||||||
<option value="Icosahedron">Icosahedron</option>
|
<option value="Icosahedron">Icosahedron</option>
|
||||||
<option value="Dodecahedron">Dodecahedron</option>
|
<option value="Dodecahedron">Dodecahedron</option>
|
||||||
|
<option value="Hexagon">Hexagon</option>
|
||||||
|
<option value="Triangle">Triangle</option>
|
||||||
|
<option value="Octagon">Octagon</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="property text">
|
<div class="property text">
|
||||||
|
|
|
@ -18,6 +18,8 @@ var button = toolbar.addButton({
|
||||||
imageURL: Script.resolvePath("assets/images/tools/ignore.svg"),
|
imageURL: Script.resolvePath("assets/images/tools/ignore.svg"),
|
||||||
visible: true,
|
visible: true,
|
||||||
buttonState: 1,
|
buttonState: 1,
|
||||||
|
defaultState: 2,
|
||||||
|
hoverState: 3,
|
||||||
alpha: 0.9
|
alpha: 0.9
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -46,6 +48,8 @@ function buttonClicked(){
|
||||||
}
|
}
|
||||||
|
|
||||||
button.writeProperty('buttonState', isShowingOverlays ? 0 : 1);
|
button.writeProperty('buttonState', isShowingOverlays ? 0 : 1);
|
||||||
|
button.writeProperty('defaultState', isShowingOverlays ? 0 : 1);
|
||||||
|
button.writeProperty('hoverState', isShowingOverlays ? 2 : 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.clicked.connect(buttonClicked);
|
button.clicked.connect(buttonClicked);
|
||||||
|
|
|
@ -17,13 +17,19 @@ var button = toolBar.addButton({
|
||||||
imageURL: Script.resolvePath("assets/images/tools/mic.svg"),
|
imageURL: Script.resolvePath("assets/images/tools/mic.svg"),
|
||||||
visible: true,
|
visible: true,
|
||||||
buttonState: 1,
|
buttonState: 1,
|
||||||
|
defaultState: 1,
|
||||||
|
hoverState: 3,
|
||||||
alpha: 0.9
|
alpha: 0.9
|
||||||
});
|
});
|
||||||
|
|
||||||
function onMuteToggled() {
|
function onMuteToggled() {
|
||||||
// We could just toggle state, but we're less likely to get out of wack if we read the AudioDevice.
|
// We could just toggle state, but we're less likely to get out of wack if we read the AudioDevice.
|
||||||
// muted => button "on" state => 1. go figure.
|
// muted => button "on" state => 1. go figure.
|
||||||
button.writeProperty('buttonState', AudioDevice.getMuted() ? 0 : 1);
|
var state = AudioDevice.getMuted() ? 0 : 1;
|
||||||
|
var hoverState = AudioDevice.getMuted() ? 2 : 3;
|
||||||
|
button.writeProperty('buttonState', state);
|
||||||
|
button.writeProperty('defaultState', state);
|
||||||
|
button.writeProperty('hoverState', hoverState);
|
||||||
}
|
}
|
||||||
onMuteToggled();
|
onMuteToggled();
|
||||||
function onClicked(){
|
function onClicked(){
|
||||||
|
|
Loading…
Reference in a new issue