Merge pull request #14075 from ctrlaltdavid/M18586

Teleport with play area
This commit is contained in:
John Conklin II 2018-10-03 11:02:33 -07:00 committed by GitHub
commit 4805d3564a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 758 additions and 77 deletions

View file

@ -34,7 +34,7 @@ Preference {
left: parent.left
right: parent.right
}
height: isFirstCheckBox ? hifi.dimensions.controlInterlineHeight : 0
height: isFirstCheckBox && !preference.indented ? 16 : 2
}
CheckBox {
@ -54,6 +54,7 @@ Preference {
left: parent.left
right: parent.right
bottom: parent.bottom
leftMargin: preference.indented ? 20 : 0
}
text: root.label
colorScheme: hifi.colorSchemes.dark

View file

@ -11,14 +11,27 @@
import QtQuick 2.5
import "../../controls-uit"
import "../../styles-uit"
Preference {
id: root
height: control.height + hifi.dimensions.controlInterlineHeight
property int value: 0
Component.onCompleted: {
repeater.itemAt(preference.value).checked = true
value = preference.value;
repeater.itemAt(preference.value).checked = true;
}
function updateValue() {
for (var i = 0; i < repeater.count; i++) {
if (repeater.itemAt(i).checked) {
value = i;
break;
}
}
}
function save() {
@ -33,24 +46,36 @@ Preference {
preference.save();
}
Row {
Column {
id: control
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
spacing: 5
spacing: 3
RalewaySemiBold {
id: heading
size: hifi.fontSizes.inputLabel
text: preference.heading
color: hifi.colors.lightGrayText
visible: text !== ""
bottomPadding: 3
}
Repeater {
id: repeater
model: preference.items.length
delegate: RadioButton {
text: preference.items[index]
letterSpacing: 0
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
}
leftPadding: 0
colorScheme: hifi.colorSchemes.dark
onClicked: updateValue();
}
}
}

View file

@ -138,11 +138,12 @@ Preference {
break;
case Preference.PrimaryHand:
checkBoxCount++;
checkBoxCount = 0;
builder = primaryHandBuilder;
break;
case Preference.RadioButtons:
checkBoxCount++;
checkBoxCount = 0;
builder = radioButtonsBuilder;
break;
};

View file

@ -122,6 +122,22 @@ Item {
}
}
// Runtime customization of preferences.
var locomotionPreference = findPreference("VR Movement", "Teleporting only / Walking and teleporting");
var flyingPreference = findPreference("VR Movement", "Jumping and flying");
if (locomotionPreference && flyingPreference) {
flyingPreference.visible = (locomotionPreference.value === 1);
locomotionPreference.valueChanged.connect(function () {
flyingPreference.visible = (locomotionPreference.value === 1);
});
}
if (HMD.isHeadControllerAvailable("Oculus")) {
var boundariesPreference = findPreference("VR Movement", "Show room boundaries while teleporting");
if (boundariesPreference) {
boundariesPreference.label = "Show room boundaries and sensors while teleporting";
}
}
if (sections.length) {
// Default sections to expanded/collapsed as appropriate for dialog.
if (sections.length === 1) {
@ -234,4 +250,32 @@ Item {
}
}
}
function findPreference(category, name) {
var section = null;
var preference = null;
var i;
// Find category section.
i = 0;
while (!section && i < sections.length) {
if (sections[i].name === category) {
section = sections[i];
}
i++;
}
// Find named preference.
if (section) {
i = 0;
while (!preference && i < section.preferences.length) {
if (section.preferences[i].preference && section.preferences[i].preference.name === name) {
preference = section.preferences[i];
}
i++;
}
}
return preference;
}
}

View file

@ -153,11 +153,12 @@ Preference {
break;
case Preference.PrimaryHand:
checkBoxCount++;
checkBoxCount = 0;
builder = primaryHandBuilder;
break;
case Preference.RadioButtons:
checkBoxCount++;
checkBoxCount = 0;
builder = radioButtonsBuilder;
break;
};

View file

@ -106,6 +106,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_realWorldFieldOfView("realWorldFieldOfView",
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
_useAdvancedMovementControls("advancedMovementForHandControllersIsChecked", true),
_showPlayArea("showPlayArea", true),
_smoothOrientationTimer(std::numeric_limits<float>::max()),
_smoothOrientationInitial(),
_smoothOrientationTarget(),
@ -3307,7 +3308,7 @@ float MyAvatar::getRawDriveKey(DriveKeys key) const {
}
void MyAvatar::relayDriveKeysToCharacterController() {
if (getDriveKey(TRANSLATE_Y) > 0.0f) {
if (getDriveKey(TRANSLATE_Y) > 0.0f && (!qApp->isHMDMode() || (useAdvancedMovementControls() && getFlyingHMDPref()))) {
_characterController.jump();
}
}

View file

@ -122,8 +122,10 @@ class MyAvatar : public Avatar {
* zone may disallow collisionless avatars.
* @property {boolean} characterControllerEnabled - Synonym of <code>collisionsEnabled</code>.
* <strong>Deprecated:</strong> Use <code>collisionsEnabled</code> instead.
* @property {boolean} useAdvancedMovementControls - Returns the value of the Interface setting, Settings > Advanced
* Movement for Hand Controller. Note: Setting the value has no effect unless Interface is restarted.
* @property {boolean} useAdvancedMovementControls - Returns and sets the value of the Interface setting, Settings >
* Walking and teleporting. Note: Setting the value has no effect unless Interface is restarted.
* @property {boolean} showPlayArea - Returns and sets the value of the Interface setting, Settings > Show room boundaries
* while teleporting. Note: Setting the value has no effect unless Interface is restarted.
* @property {number} yawSpeed=75
* @property {number} pitchSpeed=50
* @property {boolean} hmdRollControlEnabled=true - If <code>true</code>, the roll angle of your HMD turns your avatar
@ -223,6 +225,7 @@ class MyAvatar : public Avatar {
Q_PROPERTY(bool collisionsEnabled READ getCollisionsEnabled WRITE setCollisionsEnabled)
Q_PROPERTY(bool characterControllerEnabled READ getCharacterControllerEnabled WRITE setCharacterControllerEnabled)
Q_PROPERTY(bool useAdvancedMovementControls READ useAdvancedMovementControls WRITE setUseAdvancedMovementControls)
Q_PROPERTY(bool showPlayArea READ getShowPlayArea WRITE setShowPlayArea)
Q_PROPERTY(float yawSpeed MEMBER _yawSpeed)
Q_PROPERTY(float pitchSpeed MEMBER _pitchSpeed)
@ -542,6 +545,9 @@ public:
void setUseAdvancedMovementControls(bool useAdvancedMovementControls)
{ _useAdvancedMovementControls.set(useAdvancedMovementControls); }
bool getShowPlayArea() const { return _showPlayArea.get(); }
void setShowPlayArea(bool showPlayArea) { _showPlayArea.set(showPlayArea); }
void setHMDRollControlEnabled(bool value) { _hmdRollControlEnabled = value; }
bool getHMDRollControlEnabled() const { return _hmdRollControlEnabled; }
void setHMDRollControlDeadZone(float value) { _hmdRollControlDeadZone = value; }
@ -1631,6 +1637,7 @@ private:
Setting::Handle<float> _realWorldFieldOfView;
Setting::Handle<bool> _useAdvancedMovementControls;
Setting::Handle<bool> _showPlayArea;
// Smoothing.
const float SMOOTH_TIME_ORIENTATION = 0.5f;

View file

@ -18,6 +18,7 @@
const glm::vec4 ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_COLOR { 1.0f };
const float ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_WIDTH { 0.01f };
const bool ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA { false };
const bool ParabolaPointer::RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_DRAWINFRONT { false };
gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::_parabolaPipeline { nullptr };
gpu::PipelinePointer ParabolaPointer::RenderState::ParabolaRenderItem::_transparentParabolaPipeline { nullptr };
@ -46,6 +47,7 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria
float alpha = RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_COLOR.a;
float width = RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_WIDTH;
bool isVisibleInSecondaryCamera = RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA;
bool drawInFront = RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_DRAWINFRONT;
bool enabled = false;
if (!pathMap.isEmpty()) {
enabled = true;
@ -63,8 +65,11 @@ void ParabolaPointer::editRenderStatePath(const std::string& state, const QVaria
if (pathMap["isVisibleInSecondaryCamera"].isValid()) {
isVisibleInSecondaryCamera = pathMap["isVisibleInSecondaryCamera"].toBool();
}
if (pathMap["drawInFront"].isValid()) {
drawInFront = pathMap["drawInFront"].toBool();
}
}
renderState->editParabola(color, alpha, width, isVisibleInSecondaryCamera, enabled);
renderState->editParabola(color, alpha, width, isVisibleInSecondaryCamera, drawInFront, enabled);
}
}
@ -146,7 +151,7 @@ void ParabolaPointer::setVisualPickResultInternal(PickResultPointer pickResult,
}
ParabolaPointer::RenderState::RenderState(const OverlayID& startID, const OverlayID& endID, const glm::vec3& pathColor, float pathAlpha, float pathWidth,
bool isVisibleInSecondaryCamera, bool pathEnabled) :
bool isVisibleInSecondaryCamera, bool drawInFront, bool pathEnabled) :
StartEndRenderState(startID, endID)
{
render::Transaction transaction;
@ -154,7 +159,7 @@ ParabolaPointer::RenderState::RenderState(const OverlayID& startID, const Overla
_pathID = scene->allocateID();
_pathWidth = pathWidth;
if (render::Item::isValidID(_pathID)) {
auto renderItem = std::make_shared<ParabolaRenderItem>(pathColor, pathAlpha, pathWidth, isVisibleInSecondaryCamera, pathEnabled);
auto renderItem = std::make_shared<ParabolaRenderItem>(pathColor, pathAlpha, pathWidth, isVisibleInSecondaryCamera, drawInFront, pathEnabled);
transaction.resetItem(_pathID, std::make_shared<ParabolaRenderItem::Payload>(renderItem));
scene->enqueueTransaction(transaction);
}
@ -182,15 +187,16 @@ void ParabolaPointer::RenderState::disable() {
}
}
void ParabolaPointer::RenderState::editParabola(const glm::vec3& color, float alpha, float width, bool isVisibleInSecondaryCamera, bool enabled) {
void ParabolaPointer::RenderState::editParabola(const glm::vec3& color, float alpha, float width, bool isVisibleInSecondaryCamera, bool drawInFront, bool enabled) {
if (render::Item::isValidID(_pathID)) {
render::Transaction transaction;
auto scene = qApp->getMain3DScene();
transaction.updateItem<ParabolaRenderItem>(_pathID, [color, alpha, width, isVisibleInSecondaryCamera, enabled](ParabolaRenderItem& item) {
transaction.updateItem<ParabolaRenderItem>(_pathID, [color, alpha, width, isVisibleInSecondaryCamera, drawInFront, enabled](ParabolaRenderItem& item) {
item.setColor(color);
item.setAlpha(alpha);
item.setWidth(width);
item.setIsVisibleInSecondaryCamera(isVisibleInSecondaryCamera);
item.setDrawInFront(drawInFront);
item.setEnabled(enabled);
item.updateKey();
});
@ -238,6 +244,7 @@ std::shared_ptr<StartEndRenderState> ParabolaPointer::buildRenderState(const QVa
float alpha = RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_COLOR.a;
float width = RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_WIDTH;
bool isVisibleInSecondaryCamera = RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA;
bool drawInFront = RenderState::ParabolaRenderItem::DEFAULT_PARABOLA_DRAWINFRONT;
bool enabled = false;
if (propMap["path"].isValid()) {
enabled = true;
@ -258,6 +265,10 @@ std::shared_ptr<StartEndRenderState> ParabolaPointer::buildRenderState(const QVa
if (pathMap["isVisibleInSecondaryCamera"].isValid()) {
isVisibleInSecondaryCamera = pathMap["isVisibleInSecondaryCamera"].toBool();
}
if (pathMap["drawInFront"].isValid()) {
drawInFront = pathMap["drawInFront"].toBool();
}
}
QUuid endID;
@ -269,7 +280,7 @@ std::shared_ptr<StartEndRenderState> ParabolaPointer::buildRenderState(const QVa
}
}
return std::make_shared<RenderState>(startID, endID, color, alpha, width, isVisibleInSecondaryCamera, enabled);
return std::make_shared<RenderState>(startID, endID, color, alpha, width, isVisibleInSecondaryCamera, drawInFront, enabled);
}
PointerEvent ParabolaPointer::buildPointerEvent(const PickedObject& target, const PickResultPointer& pickResult, const std::string& button, bool hover) {
@ -321,8 +332,8 @@ glm::vec3 ParabolaPointer::findIntersection(const PickedObject& pickedObject, co
}
ParabolaPointer::RenderState::ParabolaRenderItem::ParabolaRenderItem(const glm::vec3& color, float alpha, float width,
bool isVisibleInSecondaryCamera, bool enabled) :
_isVisibleInSecondaryCamera(isVisibleInSecondaryCamera), _enabled(enabled)
bool isVisibleInSecondaryCamera, bool drawInFront, bool enabled) :
_isVisibleInSecondaryCamera(isVisibleInSecondaryCamera), _drawInFront(drawInFront), _enabled(enabled)
{
_uniformBuffer->resize(sizeof(ParabolaData));
setColor(color);
@ -358,6 +369,10 @@ void ParabolaPointer::RenderState::ParabolaRenderItem::updateKey() {
builder.withTagBits(render::hifi::TAG_MAIN_VIEW);
}
if (_drawInFront) {
builder.withLayer(render::hifi::LAYER_3D_FRONT);
}
_key = builder.build();
}

View file

@ -21,7 +21,7 @@ public:
using Pointer = Payload::DataPointer;
ParabolaRenderItem(const glm::vec3& color, float alpha, float width,
bool isVisibleInSecondaryCamera, bool enabled);
bool isVisibleInSecondaryCamera, bool drawInFront, bool enabled);
~ParabolaRenderItem() {}
static gpu::PipelinePointer _parabolaPipeline;
@ -46,11 +46,13 @@ public:
void setAcceleration(const glm::vec3& acceleration) { _parabolaData.acceleration = acceleration; }
void setOrigin(const glm::vec3& origin) { _origin = origin; }
void setIsVisibleInSecondaryCamera(const bool& isVisibleInSecondaryCamera) { _isVisibleInSecondaryCamera = isVisibleInSecondaryCamera; }
void setDrawInFront(const bool& drawInFront) { _drawInFront = drawInFront; }
void setEnabled(const bool& enabled) { _enabled = enabled; }
static const glm::vec4 DEFAULT_PARABOLA_COLOR;
static const float DEFAULT_PARABOLA_WIDTH;
static const bool DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA;
static const bool DEFAULT_PARABOLA_DRAWINFRONT;
private:
render::Item::Bound _bound;
@ -58,6 +60,7 @@ public:
glm::vec3 _origin { 0.0f };
bool _isVisibleInSecondaryCamera { DEFAULT_PARABOLA_ISVISIBLEINSECONDARYCAMERA };
bool _drawInFront { DEFAULT_PARABOLA_DRAWINFRONT };
bool _visible { false };
bool _enabled { false };
@ -77,7 +80,7 @@ public:
RenderState() {}
RenderState(const OverlayID& startID, const OverlayID& endID, const glm::vec3& pathColor, float pathAlpha, float pathWidth,
bool isVisibleInSecondaryCamera, bool pathEnabled);
bool isVisibleInSecondaryCamera, bool drawInFront, bool pathEnabled);
void setPathWidth(float width) { _pathWidth = width; }
float getPathWidth() const { return _pathWidth; }
@ -87,7 +90,7 @@ public:
void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, 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 enabled);
void editParabola(const glm::vec3& color, float alpha, float width, bool isVisibleInSecondaryCamera, bool drawInFront, bool enabled);
private:
int _pathID;

View file

@ -218,6 +218,7 @@ unsigned int PointerScriptingInterface::createLaserPointer(const QVariant& prope
* @property {number} alpha=1.0 The alpha of the parabola.
* @property {number} width=0.01 The width of the parabola, in meters.
* @property {boolean} isVisibleInSecondaryCamera=false The width of the parabola, in meters.
* @property {boolean} drawInFront=false If <code>true</code>, the parabola is rendered in front of other items in the scene.
*/
/**jsdoc
* A set of properties used to define the visual aspect of a Parabola Pointer in the case that the Pointer is not intersecting something. Same as a {@link Pointers.ParabolaPointerRenderState},
@ -393,4 +394,4 @@ QVariantMap PointerScriptingInterface::getPrevPickResult(unsigned int uid) const
QVariantMap PointerScriptingInterface::getPointerProperties(unsigned int uid) const {
return DependencyManager::get<PointerManager>()->getPointerProperties(uid);
}
}

View file

@ -205,7 +205,7 @@ public:
/**jsdoc
* Returns information about an existing Pointer
* @function Pointers.getPointerState
* @function Pointers.getPointerProperties
* @param {number} uid The ID of the Pointer, as returned by {@link Pointers.createPointer}.
* @returns {Pointers.LaserPointerProperties|Pointers.StylusPointerProperties|Pointers.ParabolaPointerProperties} The information about the Pointer.
* Currently only includes renderStates and defaultRenderStates with associated overlay IDs.

View file

@ -241,4 +241,4 @@ glm::vec2 StylusPointer::findPos2D(const PickedObject& pickedObject, const glm::
default:
return glm::vec2(NAN);
}
}
}

View file

@ -201,3 +201,12 @@ bool HMDScriptingInterface::isKeyboardVisible() {
void HMDScriptingInterface::centerUI() {
QMetaObject::invokeMethod(qApp, "centerUI", Qt::QueuedConnection);
}
QVariant HMDScriptingInterface::getPlayAreaRect() {
auto rect = qApp->getActiveDisplayPlugin()->getPlayAreaRect();
return qRectFToVariant(rect);
}
QVector<glm::vec3> HMDScriptingInterface::getSensorPositions() {
return qApp->getActiveDisplayPlugin()->getSensorPositions();
}

View file

@ -61,6 +61,8 @@ class QScriptEngine;
* @property {Uuid} miniTabletScreenID - The UUID of the mini tablet's screen overlay. <code>null</code> if not in HMD mode.
* @property {number} miniTabletHand - The hand that the mini tablet is displayed on: <code>0</code> for left hand,
* <code>1</code> for right hand, <code>-1</code> if not in HMD mode.
* @property {Rect} playArea=0,0,0,0 - The size and position of the HMD play area in sensor coordinates. <em>Read-only.</em>
* @property {Vec3[]} sensorPositions=[]] - The positions of the VR system sensors in sensor coordinates. <em>Read-only.</em>
*/
class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency {
Q_OBJECT
@ -75,6 +77,8 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
Q_PROPERTY(QUuid miniTabletID READ getCurrentMiniTabletID WRITE setCurrentMiniTabletID)
Q_PROPERTY(QUuid miniTabletScreenID READ getCurrentMiniTabletScreenID WRITE setCurrentMiniTabletScreenID)
Q_PROPERTY(int miniTabletHand READ getCurrentMiniTabletHand WRITE setCurrentMiniTabletHand)
Q_PROPERTY(QVariant playArea READ getPlayAreaRect);
Q_PROPERTY(QVector<glm::vec3> sensorPositions READ getSensorPositions);
public:
@ -384,6 +388,9 @@ public:
void setCurrentMiniTabletHand(int miniTabletHand) { _miniTabletHand = miniTabletHand; }
int getCurrentMiniTabletHand() const { return _miniTabletHand; }
QVariant getPlayAreaRect();
QVector<glm::vec3> getSensorPositions();
private:
bool _showTablet { false };
bool _tabletContextualMode { false };

View file

@ -226,18 +226,22 @@ void setupPreferences() {
static const QString VR_MOVEMENT{ "VR Movement" };
{
static const QString movementsControlChannel = QStringLiteral("Hifi-Advanced-Movement-Disabler");
auto getter = [myAvatar]()->bool { return myAvatar->useAdvancedMovementControls(); };
auto setter = [myAvatar](bool value) { myAvatar->setUseAdvancedMovementControls(value); };
preferences->addPreference(new CheckPreference(VR_MOVEMENT,
QStringLiteral("Advanced movement in VR (Teleport movement when unchecked)"),
getter, setter));
auto getter = [myAvatar]()->int { return myAvatar->useAdvancedMovementControls() ? 1 : 0; };
auto setter = [myAvatar](int value) { myAvatar->setUseAdvancedMovementControls(value == 1); };
auto preference =
new RadioButtonsPreference(VR_MOVEMENT, "Teleporting only / Walking and teleporting", getter, setter);
QStringList items;
items << "Teleporting only" << "Walking and teleporting";
preference->setHeading("Movement mode");
preference->setItems(items);
preferences->addPreference(preference);
}
{
auto getter = [myAvatar]()->bool { return myAvatar->getFlyingHMDPref(); };
auto setter = [myAvatar](bool value) { myAvatar->setFlyingHMDPref(value); };
preferences->addPreference(new CheckPreference(VR_MOVEMENT, "Flying & jumping (HMD)", getter, setter));
auto preference = new CheckPreference(VR_MOVEMENT, "Jumping and flying", getter, setter);
preference->setIndented(true);
preferences->addPreference(preference);
}
{
auto getter = [myAvatar]()->int { return myAvatar->getSnapTurn() ? 0 : 1; };
@ -245,9 +249,16 @@ void setupPreferences() {
auto preference = new RadioButtonsPreference(VR_MOVEMENT, "Snap turn / Smooth turn", getter, setter);
QStringList items;
items << "Snap turn" << "Smooth turn";
preference->setHeading("Rotation mode");
preference->setItems(items);
preferences->addPreference(preference);
}
{
auto getter = [myAvatar]()->bool { return myAvatar->getShowPlayArea(); };
auto setter = [myAvatar](bool value) { myAvatar->setShowPlayArea(value); };
auto preference = new CheckPreference(VR_MOVEMENT, "Show room boundaries while teleporting", getter, setter);
preferences->addPreference(preference);
}
{
auto getter = [=]()->float { return myAvatar->getUserHeight(); };
auto setter = [=](float value) { myAvatar->setUserHeight(value); };
@ -258,12 +269,6 @@ void setupPreferences() {
preference->setStep(0.001f);
preferences->addPreference(preference);
}
{
auto preference = new ButtonPreference(VR_MOVEMENT, "RESET SENSORS", [] {
qApp->resetSensors();
});
preferences->addPreference(preference);
}
static const QString AVATAR_CAMERA{ "Mouse Sensitivity" };
{

View file

@ -112,6 +112,9 @@ public:
virtual bool suppressKeyboard() { return false; }
virtual void unsuppressKeyboard() {};
virtual bool isKeyboardVisible() { return false; }
virtual QRectF getPlayAreaRect() { return QRectF(); }
virtual QVector<glm::vec3> getSensorPositions() { return QVector<glm::vec3>(); }
};
class DisplayPlugin : public Plugin, public HmdDisplay {

View file

@ -153,4 +153,4 @@ bool PointerManager::isMouse(unsigned int uid) {
return pointer->isMouse();
}
return false;
}
}

View file

@ -26,7 +26,6 @@ layout(location=0) in vec2 varTexCoord0;
layout(location=0) out vec4 outFragColor;
const float FAR_Z = 1.0;
const float LINEAR_DEPTH_BIAS = 5e-3;
const float OPACITY_EPSILON = 5e-3;
<@func main(IS_FILLED)@>
@ -46,7 +45,7 @@ void main(void) {
highlightedDepth = -evalZeyeFromZdb(highlightedDepth);
sceneDepth = -evalZeyeFromZdb(sceneDepth);
if (sceneDepth < (highlightedDepth-LINEAR_DEPTH_BIAS)) {
if (sceneDepth < highlightedDepth) {
outFragColor = vec4(params._fillOccludedColor, params._fillOccludedAlpha);
} else {
outFragColor = vec4(params._fillUnoccludedColor, params._fillUnoccludedAlpha);
@ -107,7 +106,7 @@ void main(void) {
sceneDepth = -evalZeyeFromZdb(sceneDepth);
// Are we occluded?
if (sceneDepth < (outlinedDepth/*-LINEAR_DEPTH_BIAS*/)) {
if (sceneDepth < outlinedDepth) {
outFragColor = vec4(params._outlineOccludedColor, intensity * params._outlineOccludedAlpha);
} else {
outFragColor = vec4(params._outlineUnoccludedColor, intensity * params._outlineUnoccludedAlpha);

View file

@ -340,10 +340,16 @@ public:
class CheckPreference : public BoolPreference {
Q_OBJECT
Q_PROPERTY(bool indented READ getIndented CONSTANT)
public:
CheckPreference(const QString& category, const QString& name, Getter getter, Setter setter)
: BoolPreference(category, name, getter, setter) { }
Type getType() override { return Checkbox; }
bool getIndented() { return _isIndented; }
void setIndented(const bool indented) { _isIndented = indented; }
protected:
bool _isIndented { false };
};
class PrimaryHandPreference : public StringPreference {
@ -356,16 +362,20 @@ public:
class RadioButtonsPreference : public IntPreference {
Q_OBJECT
Q_PROPERTY(QString heading READ getHeading CONSTANT)
Q_PROPERTY(QStringList items READ getItems CONSTANT)
public:
RadioButtonsPreference(const QString& category, const QString& name, Getter getter, Setter setter)
: IntPreference(category, name, getter, setter) { }
Type getType() override { return RadioButtons; }
const QString& getHeading() { return _heading; }
const QStringList& getItems() { return _items; }
void setHeading(const QString& heading) { _heading = heading; }
void setItems(const QStringList& items) { _items = items; }
protected:
QString _heading;
QStringList _items;
};
#endif

View file

@ -552,6 +552,14 @@ glm::vec2 vec2FromVariant(const QVariant &object) {
return vec2FromVariant(object, valid);
}
/**jsdoc
* Defines a rectangular portion of an image or screen, or similar.
* @typedef {object} Rect
* @property {number} x - Left, x-coordinate value.
* @property {number} y - Top, y-coordinate value.
* @property {number} width - Width of the rectangle.
* @property {number} height - Height of the rectangle.
*/
QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", rect.x());
@ -568,22 +576,6 @@ void qRectFromScriptValue(const QScriptValue &object, QRect& rect) {
rect.setHeight(object.property("height").toVariant().toInt());
}
QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) {
QScriptValue obj = engine->newObject();
obj.setProperty("red", color.red);
obj.setProperty("green", color.green);
obj.setProperty("blue", color.blue);
return obj;
}
/**jsdoc
* Defines a rectangular portion of an image or screen.
* @typedef {object} Rect
* @property {number} x - Integer left, x-coordinate value.
* @property {number} y - Integer top, y-coordinate value.
* @property {number} width - Integer width of the rectangle.
* @property {number} height - Integer height of the rectangle.
*/
QVariant qRectToVariant(const QRect& rect) {
QVariantMap obj;
obj["x"] = rect.x();
@ -615,6 +607,61 @@ QRect qRectFromVariant(const QVariant& object) {
return qRectFromVariant(object, valid);
}
QScriptValue qRectFToScriptValue(QScriptEngine* engine, const QRectF& rect) {
QScriptValue obj = engine->newObject();
obj.setProperty("x", rect.x());
obj.setProperty("y", rect.y());
obj.setProperty("width", rect.width());
obj.setProperty("height", rect.height());
return obj;
}
void qRectFFromScriptValue(const QScriptValue &object, QRectF& rect) {
rect.setX(object.property("x").toVariant().toFloat());
rect.setY(object.property("y").toVariant().toFloat());
rect.setWidth(object.property("width").toVariant().toFloat());
rect.setHeight(object.property("height").toVariant().toFloat());
}
QVariant qRectFToVariant(const QRectF& rect) {
QVariantMap obj;
obj["x"] = rect.x();
obj["y"] = rect.y();
obj["width"] = rect.width();
obj["height"] = rect.height();
return obj;
}
QRectF qRectFFromVariant(const QVariant& objectVar, bool& valid) {
QVariantMap object = objectVar.toMap();
QRectF rect;
valid = false;
rect.setX(object["x"].toFloat(&valid));
if (valid) {
rect.setY(object["y"].toFloat(&valid));
}
if (valid) {
rect.setWidth(object["width"].toFloat(&valid));
}
if (valid) {
rect.setHeight(object["height"].toFloat(&valid));
}
return rect;
}
QRectF qRectFFromVariant(const QVariant& object) {
bool valid;
return qRectFFromVariant(object, valid);
}
QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) {
QScriptValue obj = engine->newObject();
obj.setProperty("red", color.red);
obj.setProperty("green", color.green);
obj.setProperty("blue", color.blue);
return obj;
}
void xColorFromScriptValue(const QScriptValue &object, xColor& color) {
if (!object.isValid()) {

View file

@ -100,7 +100,11 @@ void qRectFromScriptValue(const QScriptValue& object, QRect& rect);
QRect qRectFromVariant(const QVariant& object, bool& isValid);
QRect qRectFromVariant(const QVariant& object);
QVariant qRectToVariant(const QRect& rect);
QScriptValue qRectFToScriptValue(QScriptEngine* engine, const QRectF& rect);
void qRectFFromScriptValue(const QScriptValue& object, QRectF& rect);
QRectF qRectFFromVariant(const QVariant& object, bool& isValid);
QRectF qRectFFromVariant(const QVariant& object);
QVariant qRectFToVariant(const QRectF& rect);
// xColor
QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color);

View file

@ -171,3 +171,53 @@ void OculusBaseDisplayPlugin::updatePresentPose() {
_currentPresentFrameInfo.presentPose = ovr::toGlm(trackingState.HeadPose.ThePose);
_currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose;
}
QRectF OculusBaseDisplayPlugin::getPlayAreaRect() {
if (!_session) {
return QRectF();
}
int floorPointsCount = 0;
auto result = ovr_GetBoundaryGeometry(_session, ovrBoundary_PlayArea, nullptr, &floorPointsCount);
if (!OVR_SUCCESS(result) || floorPointsCount != 4) {
return QRectF();
}
auto floorPoints = new ovrVector3f[floorPointsCount];
result = ovr_GetBoundaryGeometry(_session, ovrBoundary_PlayArea, floorPoints, nullptr);
if (!OVR_SUCCESS(result)) {
return QRectF();
}
auto minXZ = ovr::toGlm(floorPoints[0]);
auto maxXZ = minXZ;
for (int i = 1; i < floorPointsCount; i++) {
auto point = ovr::toGlm(floorPoints[i]);
minXZ.x = std::min(minXZ.x, point.x);
minXZ.z = std::min(minXZ.z, point.z);
maxXZ.x = std::max(maxXZ.x, point.x);
maxXZ.z = std::max(maxXZ.z, point.z);
}
glm::vec2 center = glm::vec2((minXZ.x + maxXZ.x) / 2, (minXZ.z + maxXZ.z) / 2);
glm::vec2 dimensions = glm::vec2(maxXZ.x - minXZ.x, maxXZ.z - minXZ.z);
return QRectF(center.x, center.y, dimensions.x, dimensions.y);
}
QVector<glm::vec3> OculusBaseDisplayPlugin::getSensorPositions() {
if (!_session) {
return QVector<glm::vec3>();
}
QVector<glm::vec3> result;
auto numTrackers = ovr_GetTrackerCount(_session);
for (uint i = 0; i < numTrackers; i++) {
auto trackerPose = ovr_GetTrackerPose(_session, i);
if (trackerPose.TrackerFlags & ovrTracker_PoseTracked) {
result.append(ovr::toGlm(trackerPose.Pose.Position));
}
}
return result;
}

View file

@ -27,6 +27,9 @@ public:
void resetSensors() override final;
bool beginFrameRender(uint32_t frameIndex) override;
float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; }
QRectF getPlayAreaRect() override;
QVector<glm::vec3> getSensorPositions() override;
protected:
void customizeContext() override;

View file

@ -750,3 +750,37 @@ QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const {
}
return device;
}
QRectF OpenVrDisplayPlugin::getPlayAreaRect() {
auto chaperone = vr::VRChaperone();
if (!chaperone) {
qWarning() << "No chaperone";
return QRectF();
}
if (chaperone->GetCalibrationState() >= vr::ChaperoneCalibrationState_Error) {
qWarning() << "Chaperone status =" << chaperone->GetCalibrationState();
return QRectF();
}
vr::HmdQuad_t rect;
if (!chaperone->GetPlayAreaRect(&rect)) {
qWarning() << "Chaperone rect not obtained";
return QRectF();
}
auto minXZ = transformPoint(_sensorResetMat, toGlm(rect.vCorners[0]));
auto maxXZ = minXZ;
for (int i = 1; i < 4; i++) {
auto point = transformPoint(_sensorResetMat, toGlm(rect.vCorners[i]));
minXZ.x = std::min(minXZ.x, point.x);
minXZ.z = std::min(minXZ.z, point.z);
maxXZ.x = std::max(maxXZ.x, point.x);
maxXZ.z = std::max(maxXZ.z, point.z);
}
glm::vec2 center = glm::vec2((minXZ.x + maxXZ.x) / 2, (minXZ.z + maxXZ.z) / 2);
glm::vec2 dimensions = glm::vec2(maxXZ.x - minXZ.x, maxXZ.z - minXZ.z);
return QRectF(center.x, center.y, dimensions.x, dimensions.y);
}

View file

@ -64,6 +64,8 @@ public:
QString getPreferredAudioInDevice() const override;
QString getPreferredAudioOutDevice() const override;
QRectF getPlayAreaRect() override;
protected:
bool internalActivate() override;
void internalDeactivate() override;

Binary file not shown.

Binary file not shown.

View file

@ -21,14 +21,10 @@ Script.include("/~/system/libraries/controllers.js");
(function() { // BEGIN LOCAL_SCOPE
var TARGET_MODEL_URL = Script.resolvePath("../../assets/models/teleport-destination.fbx");
var TARGET_MODEL_URL = Script.resolvePath("../../assets/models/teleportationSpotBasev8.fbx");
var SEAT_MODEL_URL = Script.resolvePath("../../assets/models/teleport-seat.fbx");
var TARGET_MODEL_DIMENSIONS = {
x: 1.15,
y: 0.5,
z: 1.15
};
var TARGET_MODEL_DIMENSIONS = { x: 0.6552, y: 0.3063, z: 0.6552 };
var COLORS_TELEPORT_SEAT = {
red: 255,
@ -59,20 +55,23 @@ Script.include("/~/system/libraries/controllers.js");
var cancelPath = {
color: COLORS_TELEPORT_CANCEL,
alpha: 1,
width: 0.025
alpha: 0.3,
width: 0.025,
drawInFront: true
};
var teleportPath = {
color: COLORS_TELEPORT_CAN_TELEPORT,
alpha: 1,
width: 0.025
alpha: 0.7,
width: 0.025,
drawInFront: true
};
var seatPath = {
color: COLORS_TELEPORT_SEAT,
alpha: 1,
width: 0.025
alpha: 0.7,
width: 0.025,
drawInFront: true
};
var teleportEnd = {
@ -150,19 +149,149 @@ Script.include("/~/system/libraries/controllers.js");
this.teleportParabolaHeadVisuals;
this.teleportParabolaHeadCollisions;
this.PLAY_AREA_OVERLAY_MODEL = Script.resolvePath("../../assets/models/trackingSpacev18.fbx");
this.PLAY_AREA_OVERLAY_MODEL_DIMENSIONS = { x: 1.969, y: 0.001, z: 1.969 };
this.PLAY_AREA_FLOAT_ABOVE_FLOOR = 0.005;
this.PLAY_AREA_OVERLAY_OFFSET = // Offset from floor.
{ x: 0, y: this.PLAY_AREA_OVERLAY_MODEL_DIMENSIONS.y / 2 + this.PLAY_AREA_FLOAT_ABOVE_FLOOR, z: 0 };
this.PLAY_AREA_SENSOR_OVERLAY_MODEL = Script.resolvePath("../../assets/models/oculusSensorv11.fbx");
this.PLAY_AREA_SENSOR_OVERLAY_DIMENSIONS = { x: 0.1198, y: 0.2981, z: 0.1198 };
this.PLAY_AREA_SENSOR_OVERLAY_ROTATION = Quat.fromVec3Degrees({ x: 0, y: -90, z: 0 });
this.PLAY_AREA_BOX_ALPHA = 1.0;
this.PLAY_AREA_SENSOR_ALPHA = 0.8;
this.playAreaSensorPositions = [];
this.playArea = { x: 0, y: 0 };
this.playAreaCenterOffset = this.PLAY_AREA_OVERLAY_OFFSET;
this.isPlayAreaVisible = false;
this.wasPlayAreaVisible = false;
this.isPlayAreaAvailable = false;
this.targetOverlayID = null;
this.playAreaOverlay = null;
this.playAreaSensorPositionOverlays = [];
this.TELEPORT_SCALE_DURATION = 130;
this.TELEPORT_SCALE_TIMEOUT = 25;
this.isTeleportVisible = false;
this.teleportScaleTimer = null;
this.teleportScaleStart = 0;
this.teleportScaleFactor = 0;
this.teleportScaleMode = "head";
this.TELEPORTED_FADE_DELAY_DURATION = 900;
this.TELEPORTED_FADE_DURATION = 200;
this.TELEPORTED_FADE_INTERVAL = 25;
this.TELEPORTED_FADE_DELAY_DELTA = this.TELEPORTED_FADE_INTERVAL / this.TELEPORTED_FADE_DELAY_DURATION;
this.TELEPORTED_FADE_DELTA = this.TELEPORTED_FADE_INTERVAL / this.TELEPORTED_FADE_DURATION;
this.teleportedFadeTimer = null;
this.teleportedFadeDelayFactor = 0;
this.teleportedFadeFactor = 0;
this.teleportedPosition = Vec3.ZERO;
this.TELEPORTED_TARGET_ALPHA = 1.0;
this.TELEPORTED_TARGET_ROTATION = Quat.fromVec3Degrees({ x: 0, y: 180, z: 0 });
this.teleportedTargetOverlay = null;
this.setPlayAreaDimensions = function () {
var avatarScale = MyAvatar.sensorToWorldScale;
var playAreaOverlayProperties = {
dimensions:
Vec3.multiply(this.teleportScaleFactor * avatarScale, {
x: this.playArea.width,
y: this.PLAY_AREA_OVERLAY_MODEL_DIMENSIONS.y,
z: this.playArea.height
})
};
if (this.teleportScaleFactor < 1) {
// Adjust position of playAreOverlay so that its base is at correct height.
// Always parenting to teleport target is good enough for this.
var sensorToWorldMatrix = MyAvatar.sensorToWorldMatrix;
var sensorToWorldRotation = Mat4.extractRotation(MyAvatar.sensorToWorldMatrix);
var worldToSensorMatrix = Mat4.inverse(sensorToWorldMatrix);
var avatarSensorPosition = Mat4.transformPoint(worldToSensorMatrix, MyAvatar.position);
avatarSensorPosition.y = 0;
var targetRotation = Overlays.getProperty(this.targetOverlayID, "rotation");
var relativePlayAreaCenterOffset =
Vec3.sum(this.playAreaCenterOffset, { x: 0, y: -TARGET_MODEL_DIMENSIONS.y / 2, z: 0 });
var localPosition = Vec3.multiplyQbyV(Quat.inverse(targetRotation),
Vec3.multiplyQbyV(sensorToWorldRotation,
Vec3.multiply(avatarScale, Vec3.subtract(relativePlayAreaCenterOffset, avatarSensorPosition))));
localPosition.y = this.teleportScaleFactor * localPosition.y;
playAreaOverlayProperties.parentID = this.targetOverlayID;
playAreaOverlayProperties.localPosition = localPosition;
}
Overlays.editOverlay(this.playAreaOverlay, playAreaOverlayProperties);
for (var i = 0; i < this.playAreaSensorPositionOverlays.length; i++) {
localPosition = this.playAreaSensorPositions[i];
localPosition = Vec3.multiply(avatarScale, localPosition);
// Position relative to the play area.
localPosition.y = avatarScale * (this.PLAY_AREA_SENSOR_OVERLAY_DIMENSIONS.y / 2
- this.PLAY_AREA_OVERLAY_MODEL_DIMENSIONS.y / 2);
Overlays.editOverlay(this.playAreaSensorPositionOverlays[i], {
dimensions: Vec3.multiply(this.teleportScaleFactor * avatarScale, this.PLAY_AREA_SENSOR_OVERLAY_DIMENSIONS),
parentID: this.playAreaOverlay,
localPosition: localPosition
});
}
};
this.updatePlayAreaScale = function () {
if (this.isPlayAreaAvailable) {
this.setPlayAreaDimensions();
}
};
this.teleporterSelectionName = "teleporterSelection" + hand.toString();
this.TELEPORTER_SELECTION_STYLE = {
outlineUnoccludedColor: { red: 0, green: 0, blue: 0 },
outlineUnoccludedAlpha: 0,
outlineOccludedColor: { red: 0, green: 0, blue: 0 },
outlineOccludedAlpha: 0,
fillUnoccludedColor: { red: 0, green: 0, blue: 0 },
fillUnoccludedAlpha: 0,
fillOccludedColor: { red: 0, green: 0, blue: 255 },
fillOccludedAlpha: 0.84,
outlineWidth: 0,
isOutlineSmooth: false
};
this.addToSelectedItemsList = function (properties) {
for (var i = 0, length = teleportRenderStates.length; i < length; i++) {
var state = properties.renderStates[teleportRenderStates[i].name];
if (state && state.end) {
Selection.addToSelectedItemsList(this.teleporterSelectionName, "overlay", state.end);
}
}
};
this.cleanup = function() {
Selection.removeListFromMap(_this.teleporterSelectionName);
Pointers.removePointer(_this.teleportParabolaHandVisuals);
Pointers.removePointer(_this.teleportParabolaHandCollisions);
Pointers.removePointer(_this.teleportParabolaHeadVisuals);
Pointers.removePointer(_this.teleportParabolaHeadCollisions);
Picks.removePick(_this.teleportHandCollisionPick);
Picks.removePick(_this.teleportHeadCollisionPick);
Overlays.deleteOverlay(_this.teleportedTargetOverlay);
Overlays.deleteOverlay(_this.playAreaOverlay);
for (var i = 0; i < _this.playAreaSensorPositionOverlays.length; i++) {
Overlays.deleteOverlay(_this.playAreaSensorPositionOverlays[i]);
}
_this.playAreaSensorPositionOverlays = [];
};
this.initPointers = function () {
this.initPointers = function() {
if (_this.init) {
_this.cleanup();
}
_this.teleportParabolaHandVisuals = Pointers.createPointer(PickType.Parabola, {
joint: (_this.hand === RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND",
dirOffset: { x: 0, y: 1, z: 0.1 },
@ -221,6 +350,9 @@ Script.include("/~/system/libraries/controllers.js");
maxDistance: 8.0
});
_this.addToSelectedItemsList(Pointers.getPointerProperties(_this.teleportParabolaHandVisuals));
_this.addToSelectedItemsList(Pointers.getPointerProperties(_this.teleportParabolaHeadVisuals));
var capsuleData = MyAvatar.getCollisionCapsule();
@ -262,11 +394,264 @@ Script.include("/~/system/libraries/controllers.js");
position: { x: 0, y: offset + height * 0.5, z: 0 },
threshold: _this.capsuleThreshold
});
_this.playAreaOverlay = Overlays.addOverlay("model", {
url: _this.PLAY_AREA_OVERLAY_MODEL,
drawInFront: false,
visible: false
});
_this.teleportedTargetOverlay = Overlays.addOverlay("model", {
url: TARGET_MODEL_URL,
alpha: _this.TELEPORTED_TARGET_ALPHA,
visible: false
});
Selection.addToSelectedItemsList(_this.teleporterSelectionName, "overlay", _this.playAreaOverlay);
Selection.addToSelectedItemsList(_this.teleporterSelectionName, "overlay", _this.teleportedTargetOverlay);
_this.playArea = HMD.playArea;
_this.isPlayAreaAvailable = HMD.active && _this.playArea.width !== 0 && _this.playArea.height !== 0;
if (_this.isPlayAreaAvailable) {
_this.playAreaCenterOffset = Vec3.sum({ x: _this.playArea.x, y: 0, z: _this.playArea.y },
_this.PLAY_AREA_OVERLAY_OFFSET);
_this.playAreaSensorPositions = HMD.sensorPositions;
for (var i = 0; i < _this.playAreaSensorPositions.length; i++) {
if (i > _this.playAreaSensorPositionOverlays.length - 1) {
var overlay = Overlays.addOverlay("model", {
url: _this.PLAY_AREA_SENSOR_OVERLAY_MODEL,
dimensions: _this.PLAY_AREA_SENSOR_OVERLAY_DIMENSIONS,
parentID: _this.playAreaOverlay,
localRotation: _this.PLAY_AREA_SENSOR_OVERLAY_ROTATION,
drawInFront: false,
visible: false
});
_this.playAreaSensorPositionOverlays.push(overlay);
Selection.addToSelectedItemsList(_this.teleporterSelectionName, "overlay", overlay);
}
}
_this.setPlayAreaDimensions();
}
_this.init = true;
}
};
_this.initPointers();
this.translateXAction = Controller.findAction("TranslateX");
this.translateYAction = Controller.findAction("TranslateY");
this.translateZAction = Controller.findAction("TranslateZ");
this.setPlayAreaVisible = function (visible, targetOverlayID, fade) {
if (!this.isPlayAreaAvailable || this.isPlayAreaVisible === visible) {
return;
}
this.wasPlayAreaVisible = this.isPlayAreaVisible;
this.isPlayAreaVisible = visible;
this.targetOverlayID = targetOverlayID;
if (this.teleportedFadeTimer !== null) {
Script.clearTimeout(this.teleportedFadeTimer);
this.teleportedFadeTimer = null;
}
if (visible || !fade) {
// Immediately make visible or invisible.
this.isPlayAreaVisible = visible;
Overlays.editOverlay(this.playAreaOverlay, {
dimensions: Vec3.ZERO,
alpha: this.PLAY_AREA_BOX_ALPHA,
visible: visible
});
for (var i = 0; i < this.playAreaSensorPositionOverlays.length; i++) {
Overlays.editOverlay(this.playAreaSensorPositionOverlays[i], {
dimensions: Vec3.ZERO,
alpha: this.PLAY_AREA_SENSOR_ALPHA,
visible: visible
});
}
Overlays.editOverlay(this.teleportedTargetOverlay, { visible: false });
} else {
// Fading out of overlays is initiated in setTeleportVisible().
}
};
this.updatePlayArea = function (position) {
var sensorToWorldMatrix = MyAvatar.sensorToWorldMatrix;
var sensorToWorldRotation = Mat4.extractRotation(MyAvatar.sensorToWorldMatrix);
var worldToSensorMatrix = Mat4.inverse(sensorToWorldMatrix);
var avatarSensorPosition = Mat4.transformPoint(worldToSensorMatrix, MyAvatar.position);
avatarSensorPosition.y = 0;
var targetXZPosition = { x: position.x, y: 0, z: position.z };
var avatarXZPosition = MyAvatar.position;
avatarXZPosition.y = 0;
var MIN_PARENTING_DISTANCE = 0.2; // Parenting under this distance results in the play area's rotation jittering.
if (Vec3.distance(targetXZPosition, avatarXZPosition) < MIN_PARENTING_DISTANCE) {
// Set play area position and rotation in world coordinates with no parenting.
Overlays.editOverlay(this.playAreaOverlay, {
parentID: Uuid.NULL,
position: Vec3.sum(position,
Vec3.multiplyQbyV(sensorToWorldRotation,
Vec3.multiply(MyAvatar.sensorToWorldScale,
Vec3.subtract(this.playAreaCenterOffset, avatarSensorPosition)))),
rotation: sensorToWorldRotation
});
} else {
// Set play area position and rotation in local coordinates with parenting.
var targetRotation = Overlays.getProperty(this.targetOverlayID, "rotation");
var sensorToTargetRotation = Quat.multiply(Quat.inverse(targetRotation), sensorToWorldRotation);
var relativePlayAreaCenterOffset =
Vec3.sum(this.playAreaCenterOffset, { x: 0, y: -TARGET_MODEL_DIMENSIONS.y / 2, z: 0 });
Overlays.editOverlay(this.playAreaOverlay, {
parentID: this.targetOverlayID,
localPosition: Vec3.multiplyQbyV(Quat.inverse(targetRotation),
Vec3.multiplyQbyV(sensorToWorldRotation,
Vec3.multiply(MyAvatar.sensorToWorldScale,
Vec3.subtract(relativePlayAreaCenterOffset, avatarSensorPosition)))),
localRotation: sensorToTargetRotation
});
}
};
this.scaleInTeleport = function () {
_this.teleportScaleFactor = Math.min((Date.now() - _this.teleportScaleStart) / _this.TELEPORT_SCALE_DURATION, 1);
Pointers.editRenderState(
_this.teleportScaleMode === "head" ? _this.teleportParabolaHeadVisuals : _this.teleportParabolaHandVisuals,
"teleport",
{
path: teleportPath, // Teleport beam disappears if not included.
end: { dimensions: Vec3.multiply(_this.teleportScaleFactor, TARGET_MODEL_DIMENSIONS) }
}
);
if (_this.isPlayAreaVisible) {
_this.setPlayAreaDimensions();
}
if (_this.teleportScaleFactor < 1) {
_this.teleportScaleTimer = Script.setTimeout(_this.scaleInTeleport, _this.TELEPORT_SCALE_TIMEOUT);
} else {
_this.teleportScaleTimer = null;
}
};
this.fadeOutTeleport = function () {
var isAvatarMoving,
i, length;
isAvatarMoving = Controller.getActionValue(_this.translateXAction) !== 0
|| Controller.getActionValue(_this.translateYAction) !== 0
|| Controller.getActionValue(_this.translateZAction) !== 0;
if (_this.teleportedFadeDelayFactor > 0 && !_this.isTeleportVisible && !isAvatarMoving) {
// Delay fade.
_this.teleportedFadeDelayFactor = _this.teleportedFadeDelayFactor - _this.TELEPORTED_FADE_DELAY_DELTA;
_this.teleportedFadeTimer = Script.setTimeout(_this.fadeOutTeleport, _this.TELEPORTED_FADE_INTERVAL);
} else if (_this.teleportedFadeFactor > 0 && !_this.isTeleportVisible && !isAvatarMoving) {
// Fade.
_this.teleportedFadeFactor = _this.teleportedFadeFactor - _this.TELEPORTED_FADE_DELTA;
Overlays.editOverlay(_this.teleportedTargetOverlay, {
alpha: _this.teleportedFadeFactor * _this.TELEPORTED_TARGET_ALPHA
});
if (_this.wasPlayAreaVisible) {
Overlays.editOverlay(_this.playAreaOverlay, {
alpha: _this.teleportedFadeFactor * _this.PLAY_AREA_BOX_ALPHA
});
var sensorAlpha = _this.teleportedFadeFactor * _this.PLAY_AREA_SENSOR_ALPHA;
for (i = 0, length = _this.playAreaSensorPositionOverlays.length; i < length; i++) {
Overlays.editOverlay(_this.playAreaSensorPositionOverlays[i], { alpha: sensorAlpha });
}
}
_this.teleportedFadeTimer = Script.setTimeout(_this.fadeOutTeleport, _this.TELEPORTED_FADE_INTERVAL);
} else {
// Make invisible.
Overlays.editOverlay(_this.teleportedTargetOverlay, { visible: false });
if (_this.wasPlayAreaVisible) {
Overlays.editOverlay(_this.playAreaOverlay, { visible: false });
for (i = 0, length = _this.playAreaSensorPositionOverlays.length; i < length; i++) {
Overlays.editOverlay(_this.playAreaSensorPositionOverlays[i], { visible: false });
}
}
_this.teleportedFadeTimer = null;
Selection.disableListHighlight(this.teleporterSelectionName);
}
};
this.cancelFade = function () {
// Other hand may call this to immediately hide fading overlays.
var i, length;
if (this.teleportedFadeTimer) {
Overlays.editOverlay(this.teleportedTargetOverlay, { visible: false });
if (this.wasPlayAreaVisible) {
Overlays.editOverlay(this.playAreaOverlay, { visible: false });
for (i = 0, length = this.playAreaSensorPositionOverlays.length; i < length; i++) {
Overlays.editOverlay(this.playAreaSensorPositionOverlays[i], { visible: false });
}
}
this.teleportedFadeTimer = null;
}
};
this.setTeleportVisible = function (visible, mode, fade) {
// Scales in teleport target and play area when start displaying them.
if (visible === this.isTeleportVisible) {
return;
}
if (visible) {
this.teleportScaleMode = mode;
Pointers.editRenderState(
mode === "head" ? _this.teleportParabolaHeadVisuals : _this.teleportParabolaHandVisuals,
"teleport",
{
path: teleportPath, // Teleport beam disappears if not included.
end: { dimensions: Vec3.ZERO }
}
);
this.getOtherModule().cancelFade();
this.teleportScaleStart = Date.now();
this.teleportScaleFactor = 0;
this.scaleInTeleport();
Selection.enableListHighlight(this.teleporterSelectionName, this.TELEPORTER_SELECTION_STYLE);
} else {
if (this.teleportScaleTimer !== null) {
Script.clearTimeout(this.teleportScaleTimer);
this.teleportScaleTimer = null;
}
if (fade) {
// Copy of target at teleported position for fading.
var avatarScale = MyAvatar.sensorToWorldScale;
Overlays.editOverlay(this.teleportedTargetOverlay, {
position: Vec3.sum(this.teleportedPosition, {
x: 0,
y: -getAvatarFootOffset() + avatarScale * TARGET_MODEL_DIMENSIONS.y / 2,
z: 0
}),
rotation: Quat.multiply(this.TELEPORTED_TARGET_ROTATION, MyAvatar.orientation),
dimensions: Vec3.multiply(avatarScale, TARGET_MODEL_DIMENSIONS),
alpha: this.TELEPORTED_TARGET_ALPHA,
visible: true
});
// Fade out over time.
this.teleportedFadeDelayFactor = 1.0;
this.teleportedFadeFactor = 1.0;
this.teleportedFadeTimer = Script.setTimeout(this.fadeOutTeleport, this.TELEPORTED_FADE_DELAY);
} else {
Selection.disableListHighlight(this.teleporterSelectionName);
}
}
this.isTeleportVisible = visible;
};
this.axisButtonStateX = 0; // Left/right axis button pressed.
this.axisButtonStateY = 0; // Up/down axis button pressed.
this.BUTTON_TRANSITION_DELAY = 100; // Allow time for transition from direction buttons to touch-pad.
@ -379,6 +764,7 @@ Script.include("/~/system/libraries/controllers.js");
this.setTeleportState(mode, "cancel", "collision");
} else if (teleportLocationType === TARGET.SURFACE || teleportLocationType === TARGET.DISCREPANCY) {
this.setTeleportState(mode, "teleport", "collision");
this.updatePlayArea(result.intersection);
} else if (teleportLocationType === TARGET.SEAT) {
this.setTeleportState(mode, "collision", "seat");
}
@ -387,6 +773,7 @@ Script.include("/~/system/libraries/controllers.js");
this.teleport = function(newResult, target) {
var result = newResult;
this.teleportedPosition = newResult.intersection;
if (_this.buttonValue !== 0) {
return makeRunningValues(true, [], []);
}
@ -410,6 +797,8 @@ Script.include("/~/system/libraries/controllers.js");
};
this.disableLasers = function() {
this.setPlayAreaVisible(false, null, true);
this.setTeleportVisible(false, null, true);
Pointers.disablePointer(_this.teleportParabolaHandVisuals);
Pointers.disablePointer(_this.teleportParabolaHandCollisions);
Pointers.disablePointer(_this.teleportParabolaHeadVisuals);
@ -418,14 +807,29 @@ Script.include("/~/system/libraries/controllers.js");
Picks.disablePick(_this.teleportHandCollisionPick);
};
this.setTeleportState = function(mode, visibleState, invisibleState) {
this.teleportState = "";
this.setTeleportState = function (mode, visibleState, invisibleState) {
var teleportState = mode + visibleState + invisibleState;
if (teleportState === this.teleportState) {
return;
}
this.teleportState = teleportState;
var pointerID;
if (mode === 'head') {
Pointers.setRenderState(_this.teleportParabolaHeadVisuals, visibleState);
Pointers.setRenderState(_this.teleportParabolaHeadCollisions, invisibleState);
pointerID = _this.teleportParabolaHeadVisuals;
} else {
Pointers.setRenderState(_this.teleportParabolaHandVisuals, visibleState);
Pointers.setRenderState(_this.teleportParabolaHandCollisions, invisibleState);
pointerID = _this.teleportParabolaHandVisuals;
}
var visible = visibleState === "teleport";
this.setPlayAreaVisible(visible && MyAvatar.showPlayArea,
Pointers.getPointerProperties(pointerID).renderStates.teleport.end, false);
this.setTeleportVisible(visible, mode, false);
};
this.setIgnoreEntities = function(entitiesToIgnore) {
@ -642,4 +1046,9 @@ Script.include("/~/system/libraries/controllers.js");
Messages.subscribe('Hifi-Teleport-Ignore-Remove');
Messages.messageReceived.connect(handleTeleportMessages);
MyAvatar.sensorToWorldScaleChanged.connect(function () {
leftTeleporter.updatePlayAreaScale();
rightTeleporter.updatePlayAreaScale();
});
}()); // END LOCAL_SCOPE