mirror of
https://github.com/overte-org/overte.git
synced 2025-08-07 14:10:40 +02:00
Merge branch 'master' into polylineOptimizations
This commit is contained in:
commit
1cdee11784
62 changed files with 1911 additions and 481 deletions
|
@ -33,7 +33,7 @@ bool OctreeQueryNode::packetIsDuplicate() const {
|
||||||
// of the entire packet, we need to compare only the packet content...
|
// of the entire packet, we need to compare only the packet content...
|
||||||
|
|
||||||
if (_lastOctreePacketLength == _octreePacket->getPayloadSize()) {
|
if (_lastOctreePacketLength == _octreePacket->getPayloadSize()) {
|
||||||
if (memcmp(&_lastOctreePayload + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
|
if (memcmp(_lastOctreePayload.data() + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
|
||||||
_octreePacket->getPayload() + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
|
_octreePacket->getPayload() + OCTREE_PACKET_EXTRA_HEADERS_SIZE,
|
||||||
_octreePacket->getPayloadSize() - OCTREE_PACKET_EXTRA_HEADERS_SIZE) == 0) {
|
_octreePacket->getPayloadSize() - OCTREE_PACKET_EXTRA_HEADERS_SIZE) == 0) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -101,7 +101,7 @@ void OctreeQueryNode::resetOctreePacket() {
|
||||||
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
|
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
|
||||||
// packet send rate.
|
// packet send rate.
|
||||||
_lastOctreePacketLength = _octreePacket->getPayloadSize();
|
_lastOctreePacketLength = _octreePacket->getPayloadSize();
|
||||||
memcpy(&_lastOctreePayload, _octreePacket->getPayload(), _lastOctreePacketLength);
|
memcpy(_lastOctreePayload.data(), _octreePacket->getPayload(), _lastOctreePacketLength);
|
||||||
|
|
||||||
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
|
||||||
// the clients requested color state.
|
// the clients requested color state.
|
||||||
|
|
|
@ -40,7 +40,7 @@ macro(PACKAGE_LIBRARIES_FOR_DEPLOYMENT)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET ${TARGET_NAME}
|
TARGET ${TARGET_NAME}
|
||||||
POST_BUILD
|
POST_BUILD
|
||||||
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> $<TARGET_FILE:${TARGET_NAME}>"
|
COMMAND CMD /C "SET PATH=%PATH%;${QT_DIR}/bin && ${WINDEPLOYQT_COMMAND} ${EXTRA_DEPLOY_OPTIONS} $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>,$<CONFIG:RelWithDebInfo>>:--release> $<TARGET_FILE:${TARGET_NAME}>"
|
||||||
)
|
)
|
||||||
elseif (DEFINED BUILD_BUNDLE AND BUILD_BUNDLE AND APPLE)
|
elseif (DEFINED BUILD_BUNDLE AND BUILD_BUNDLE AND APPLE)
|
||||||
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH)
|
find_program(MACDEPLOYQT_COMMAND macdeployqt PATHS ${QT_DIR}/bin NO_DEFAULT_PATH)
|
||||||
|
|
87
examples/controllers/philipsVersion.js
Normal file
87
examples/controllers/philipsVersion.js
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
//
|
||||||
|
// reticleTest.js
|
||||||
|
// examples/controllers
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2015/12/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
function length(posA, posB) {
|
||||||
|
var dx = posA.x - posB.x;
|
||||||
|
var dy = posA.y - posB.y;
|
||||||
|
var length = Math.sqrt((dx*dx) + (dy*dy))
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var PITCH_DEADZONE = 1.0;
|
||||||
|
var PITCH_MAX = 20.0;
|
||||||
|
var YAW_DEADZONE = 1.0;
|
||||||
|
var YAW_MAX = 20.0;
|
||||||
|
var PITCH_SCALING = 10.0;
|
||||||
|
var YAW_SCALING = 10.0;
|
||||||
|
|
||||||
|
var EXPECTED_CHANGE = 50;
|
||||||
|
var lastPos = Controller.getReticlePosition();
|
||||||
|
function moveReticle(dY, dX) {
|
||||||
|
var globalPos = Controller.getReticlePosition();
|
||||||
|
|
||||||
|
// some debugging to see if position is jumping around on us...
|
||||||
|
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||||
|
if (distanceSinceLastMove > EXPECTED_CHANGE) {
|
||||||
|
print("distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.abs(dX) > EXPECTED_CHANGE) {
|
||||||
|
print("UNEXPECTED dX:" + dX + "----------------------------");
|
||||||
|
dX = 0;
|
||||||
|
}
|
||||||
|
if (Math.abs(dY) > EXPECTED_CHANGE) {
|
||||||
|
print("UNEXPECTED dY:" + dY + "----------------------------");
|
||||||
|
dY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalPos.x += dX;
|
||||||
|
globalPos.y += dY;
|
||||||
|
Controller.setReticlePosition(globalPos);
|
||||||
|
lastPos = globalPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand";
|
||||||
|
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||||
|
|
||||||
|
var lastHandPitch = 0;
|
||||||
|
var lastHandYaw = 0;
|
||||||
|
|
||||||
|
mapping.from(Controller.Standard.LeftHand).peek().to(function(pose) {
|
||||||
|
var handEulers = Quat.safeEulerAngles(pose.rotation);
|
||||||
|
//Vec3.print("handEulers:", handEulers);
|
||||||
|
|
||||||
|
var handPitch = handEulers.y;
|
||||||
|
var handYaw = handEulers.x;
|
||||||
|
var changePitch = (handPitch - lastHandPitch) * PITCH_SCALING;
|
||||||
|
var changeYaw = (handYaw - lastHandYaw) * YAW_SCALING;
|
||||||
|
if (Math.abs(changePitch) > PITCH_MAX) {
|
||||||
|
print("Pitch: " + changePitch);
|
||||||
|
changePitch = 0;
|
||||||
|
}
|
||||||
|
if (Math.abs(changeYaw) > YAW_MAX) {
|
||||||
|
print("Yaw: " + changeYaw);
|
||||||
|
changeYaw = 0;
|
||||||
|
}
|
||||||
|
changePitch = Math.abs(changePitch) < PITCH_DEADZONE ? 0 : changePitch;
|
||||||
|
changeYaw = Math.abs(changeYaw) < YAW_DEADZONE ? 0 : changeYaw;
|
||||||
|
moveReticle(changePitch, changeYaw);
|
||||||
|
lastHandPitch = handPitch;
|
||||||
|
lastHandYaw = handYaw;
|
||||||
|
|
||||||
|
});
|
||||||
|
mapping.enable();
|
||||||
|
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function(){
|
||||||
|
mapping.disable();
|
||||||
|
});
|
76
examples/controllers/proceduralHandPoseExample.js
Normal file
76
examples/controllers/proceduralHandPoseExample.js
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// proceduralHandPoseExample.js
|
||||||
|
// examples/controllers
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2015/12/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
var MAPPING_NAME = "com.highfidelity.examples.proceduralHandPose";
|
||||||
|
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||||
|
var translation = { x: 0, y: 0.1, z: 0 };
|
||||||
|
var translationDx = 0.01;
|
||||||
|
var translationDy = 0.01;
|
||||||
|
var translationDz = -0.01;
|
||||||
|
var TRANSLATION_LIMIT = 0.5;
|
||||||
|
|
||||||
|
var pitch = 45;
|
||||||
|
var yaw = 0;
|
||||||
|
var roll = 45;
|
||||||
|
var pitchDelta = 1;
|
||||||
|
var yawDelta = -1;
|
||||||
|
var rollDelta = 1;
|
||||||
|
var ROTATION_MIN = -90;
|
||||||
|
var ROTATION_MAX = 90;
|
||||||
|
|
||||||
|
mapping.from(function() {
|
||||||
|
|
||||||
|
// adjust the hand translation in a periodic back and forth motion for each of the 3 axes
|
||||||
|
translation.x = translation.x + translationDx;
|
||||||
|
translation.y = translation.y + translationDy;
|
||||||
|
translation.z = translation.z + translationDz;
|
||||||
|
if ((translation.x > TRANSLATION_LIMIT) || (translation.x < (-1 * TRANSLATION_LIMIT))) {
|
||||||
|
translationDx = translationDx * -1;
|
||||||
|
}
|
||||||
|
if ((translation.y > TRANSLATION_LIMIT) || (translation.y < (-1 * TRANSLATION_LIMIT))) {
|
||||||
|
translationDy = translationDy * -1;
|
||||||
|
}
|
||||||
|
if ((translation.z > TRANSLATION_LIMIT) || (translation.z < (-1 * TRANSLATION_LIMIT))) {
|
||||||
|
translationDz = translationDz * -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjust the hand rotation in a periodic back and forth motion for each of pitch/yaw/roll
|
||||||
|
pitch = pitch + pitchDelta;
|
||||||
|
yaw = yaw + yawDelta;
|
||||||
|
roll = roll + rollDelta;
|
||||||
|
if ((pitch > ROTATION_MAX) || (pitch < ROTATION_MIN)) {
|
||||||
|
pitchDelta = pitchDelta * -1;
|
||||||
|
}
|
||||||
|
if ((yaw > ROTATION_MAX) || (yaw < ROTATION_MIN)) {
|
||||||
|
yawDelta = yawDelta * -1;
|
||||||
|
}
|
||||||
|
if ((roll > ROTATION_MAX) || (roll < ROTATION_MIN)) {
|
||||||
|
rollDelta = rollDelta * -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll);
|
||||||
|
|
||||||
|
var pose = {
|
||||||
|
translation: translation,
|
||||||
|
rotation: rotation,
|
||||||
|
velocity: { x: 0, y: 0, z: 0 },
|
||||||
|
angularVelocity: { x: 0, y: 0, z: 0 }
|
||||||
|
};
|
||||||
|
return pose;
|
||||||
|
}).debug(true).to(Controller.Standard.LeftHand);
|
||||||
|
|
||||||
|
Controller.enableMapping(MAPPING_NAME);
|
||||||
|
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function(){
|
||||||
|
mapping.disable();
|
||||||
|
});
|
121
examples/controllers/reticleHandAngularVelocityTest.js
Normal file
121
examples/controllers/reticleHandAngularVelocityTest.js
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
//
|
||||||
|
// reticleHandAngularVelocityTest.js
|
||||||
|
// examples/controllers
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2015/12/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
// If you set this to true, you will get the raw instantaneous angular velocity.
|
||||||
|
// note: there is a LOT of noise in the hydra rotation, you will probably be very
|
||||||
|
// frustrated with the level of jitter.
|
||||||
|
var USE_INSTANTANEOUS_ANGULAR_VELOCITY = false;
|
||||||
|
var whichHand = Controller.Standard.RightHand;
|
||||||
|
var whichTrigger = Controller.Standard.RT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function msecTimestampNow() {
|
||||||
|
var d = new Date();
|
||||||
|
return d.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
function length(posA, posB) {
|
||||||
|
var dx = posA.x - posB.x;
|
||||||
|
var dy = posA.y - posB.y;
|
||||||
|
var length = Math.sqrt((dx*dx) + (dy*dy))
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var EXPECTED_CHANGE = 50;
|
||||||
|
var lastPos = Controller.getReticlePosition();
|
||||||
|
function moveReticle(dX, dY) {
|
||||||
|
var globalPos = Controller.getReticlePosition();
|
||||||
|
|
||||||
|
// some debugging to see if position is jumping around on us...
|
||||||
|
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||||
|
if (distanceSinceLastMove > EXPECTED_CHANGE) {
|
||||||
|
print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.abs(dX) > EXPECTED_CHANGE) {
|
||||||
|
print("surpressing unexpectedly large change dX:" + dX + "----------------------------");
|
||||||
|
dX = 0;
|
||||||
|
}
|
||||||
|
if (Math.abs(dY) > EXPECTED_CHANGE) {
|
||||||
|
print("surpressing unexpectedly large change dY:" + dY + "----------------------------");
|
||||||
|
dY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalPos.x += dX;
|
||||||
|
globalPos.y += dY;
|
||||||
|
Controller.setReticlePosition(globalPos);
|
||||||
|
lastPos = globalPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstTime = true;
|
||||||
|
var lastTime = msecTimestampNow();
|
||||||
|
var previousRotation;
|
||||||
|
|
||||||
|
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHand";
|
||||||
|
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||||
|
mapping.from(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
|
||||||
|
mapping.from(whichHand).peek().to(function(pose) {
|
||||||
|
|
||||||
|
var MSECS_PER_SECOND = 1000;
|
||||||
|
var now = msecTimestampNow();
|
||||||
|
var deltaMsecs = (now - lastTime);
|
||||||
|
var deltaTime = deltaMsecs / MSECS_PER_SECOND;
|
||||||
|
|
||||||
|
if (firstTime) {
|
||||||
|
previousRotation = pose.rotation;
|
||||||
|
lastTime = msecTimestampNow();
|
||||||
|
firstTime = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pose.angularVelocity - is the angularVelocity in a "physics" sense, that
|
||||||
|
// means the direction of the vector is the axis of symetry of rotation
|
||||||
|
// and the scale of the vector is the speed in radians/second of rotation
|
||||||
|
// around that axis.
|
||||||
|
//
|
||||||
|
// we want to deconstruct that in the portion of the rotation on the Y axis
|
||||||
|
// and make that portion move our reticle in the horizontal/X direction
|
||||||
|
// and the portion of the rotation on the X axis and make that portion
|
||||||
|
// move our reticle in the veritcle/Y direction
|
||||||
|
var xPart = -pose.angularVelocity.y;
|
||||||
|
var yPart = -pose.angularVelocity.x;
|
||||||
|
|
||||||
|
// pose.angularVelocity is "smoothed", we can calculate our own instantaneous
|
||||||
|
// angular velocity as such:
|
||||||
|
if (USE_INSTANTANEOUS_ANGULAR_VELOCITY) {
|
||||||
|
var previousConjugate = Quat.conjugate(previousRotation);
|
||||||
|
var deltaRotation = Quat.multiply(pose.rotation, previousConjugate);
|
||||||
|
var normalizedDeltaRotation = Quat.normalize(deltaRotation);
|
||||||
|
var axis = Quat.axis(normalizedDeltaRotation);
|
||||||
|
var speed = Quat.angle(normalizedDeltaRotation) / deltaTime;
|
||||||
|
var instantaneousAngularVelocity = Vec3.multiply(speed, axis);
|
||||||
|
|
||||||
|
xPart = -instantaneousAngularVelocity.y;
|
||||||
|
yPart = -instantaneousAngularVelocity.x;
|
||||||
|
|
||||||
|
previousRotation = pose.rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
var MOVE_SCALE = 1;
|
||||||
|
lastTime = now;
|
||||||
|
|
||||||
|
var dX = (xPart * MOVE_SCALE) / deltaTime;
|
||||||
|
var dY = (yPart * MOVE_SCALE) / deltaTime;
|
||||||
|
|
||||||
|
moveReticle(dX, dY);
|
||||||
|
});
|
||||||
|
mapping.enable();
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function(){
|
||||||
|
mapping.disable();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
103
examples/controllers/reticleHandRotationTest.js
Normal file
103
examples/controllers/reticleHandRotationTest.js
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
//
|
||||||
|
// reticleHandRotationTest.js
|
||||||
|
// examples/controllers
|
||||||
|
//
|
||||||
|
// Created by Brad Hefta-Gaub on 2015/12/15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
Math.clamp=function(a,b,c) {
|
||||||
|
return Math.max(b,Math.min(c,a));
|
||||||
|
}
|
||||||
|
|
||||||
|
var whichHand = Controller.Standard.RightHand;
|
||||||
|
var whichTrigger = Controller.Standard.RT;
|
||||||
|
|
||||||
|
function length(posA, posB) {
|
||||||
|
var dx = posA.x - posB.x;
|
||||||
|
var dy = posA.y - posB.y;
|
||||||
|
var length = Math.sqrt((dx*dx) + (dy*dy))
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var EXPECTED_CHANGE = 50;
|
||||||
|
var lastPos = Controller.getReticlePosition();
|
||||||
|
function moveReticleAbsolute(x, y) {
|
||||||
|
var globalPos = Controller.getReticlePosition();
|
||||||
|
var dX = x - globalPos.x;
|
||||||
|
var dY = y - globalPos.y;
|
||||||
|
|
||||||
|
// some debugging to see if position is jumping around on us...
|
||||||
|
var distanceSinceLastMove = length(lastPos, globalPos);
|
||||||
|
if (distanceSinceLastMove > EXPECTED_CHANGE) {
|
||||||
|
print("------------------ distanceSinceLastMove:" + distanceSinceLastMove + "----------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.abs(dX) > EXPECTED_CHANGE) {
|
||||||
|
print("surpressing unexpectedly large change dX:" + dX + "----------------------------");
|
||||||
|
}
|
||||||
|
if (Math.abs(dY) > EXPECTED_CHANGE) {
|
||||||
|
print("surpressing unexpectedly large change dY:" + dY + "----------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
globalPos.x = x;
|
||||||
|
globalPos.y = y;
|
||||||
|
Controller.setReticlePosition(globalPos);
|
||||||
|
lastPos = globalPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var MAPPING_NAME = "com.highfidelity.testing.reticleWithHandRotation";
|
||||||
|
var mapping = Controller.newMapping(MAPPING_NAME);
|
||||||
|
mapping.from(whichTrigger).peek().constrainToInteger().to(Controller.Actions.ReticleClick);
|
||||||
|
mapping.from(whichHand).peek().to(function(pose) {
|
||||||
|
|
||||||
|
// NOTE: hack for now
|
||||||
|
var screenSizeX = 1920;
|
||||||
|
var screenSizeY = 1080;
|
||||||
|
|
||||||
|
var rotated = Vec3.multiplyQbyV(pose.rotation, Vec3.UNIT_NEG_Y); //
|
||||||
|
var absolutePitch = rotated.y; // from 1 down to -1 up ... but note: if you rotate down "too far" it starts to go up again...
|
||||||
|
var absoluteYaw = rotated.z; // from -1 left to 1 right
|
||||||
|
//print("absolutePitch:" + absolutePitch);
|
||||||
|
//print("absoluteYaw:" + absoluteYaw);
|
||||||
|
//Vec3.print("rotated:", rotated);
|
||||||
|
|
||||||
|
var ROTATION_BOUND = 0.6;
|
||||||
|
var clampYaw = Math.clamp(absoluteYaw, -ROTATION_BOUND, ROTATION_BOUND);
|
||||||
|
var clampPitch = Math.clamp(absolutePitch, -ROTATION_BOUND, ROTATION_BOUND);
|
||||||
|
//var clampYaw = absoluteYaw;
|
||||||
|
//print("clampYaw:" + clampYaw);
|
||||||
|
//print("clampPitch:" + clampPitch);
|
||||||
|
|
||||||
|
// if using entire span...
|
||||||
|
//var xRatio = (absoluteYaw + 1) / 2;
|
||||||
|
//var yRatio = (absolutePitch + 1) / 2;
|
||||||
|
|
||||||
|
// if using only from -0.5 to 0.5
|
||||||
|
var xRatio = (clampYaw + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||||
|
var yRatio = (clampPitch + ROTATION_BOUND) / (2 * ROTATION_BOUND);
|
||||||
|
|
||||||
|
//print("xRatio:" + xRatio);
|
||||||
|
//print("yRatio:" + yRatio);
|
||||||
|
|
||||||
|
//print("ratio x:" + xRatio + " y:" + yRatio);
|
||||||
|
|
||||||
|
var x = screenSizeX * xRatio;
|
||||||
|
var y = screenSizeY * yRatio;
|
||||||
|
|
||||||
|
//print("position x:" + x + " y:" + y);
|
||||||
|
if (!(xRatio == 0.5 && yRatio == 0)) {
|
||||||
|
moveReticleAbsolute(x, y);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mapping.enable();
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function(){
|
||||||
|
mapping.disable();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@ var mappingJSON = {
|
||||||
|
|
||||||
mapping = Controller.parseMapping(JSON.stringify(mappingJSON));
|
mapping = Controller.parseMapping(JSON.stringify(mappingJSON));
|
||||||
mapping.enable();
|
mapping.enable();
|
||||||
|
|
||||||
Script.scriptEnding.connect(function(){
|
Script.scriptEnding.connect(function(){
|
||||||
mapping.disable();
|
mapping.disable();
|
||||||
});
|
});
|
||||||
|
|
|
@ -62,7 +62,7 @@ var directory = (function () {
|
||||||
function setUp() {
|
function setUp() {
|
||||||
viewport = Controller.getViewportDimensions();
|
viewport = Controller.getViewportDimensions();
|
||||||
|
|
||||||
directoryWindow = new WebWindow('Directory', DIRECTORY_URL, 900, 700, false);
|
directoryWindow = new OverlayWebWindow('Directory', DIRECTORY_URL, 900, 700, false);
|
||||||
directoryWindow.setVisible(false);
|
directoryWindow.setVisible(false);
|
||||||
|
|
||||||
directoryButton = Overlays.addOverlay("image", {
|
directoryButton = Overlays.addOverlay("image", {
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
print("Radius spread");
|
print("Radius spread - temporarily not working");
|
||||||
Entities.editEntity(particles, {
|
Entities.editEntity(particles, {
|
||||||
accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 },
|
accelerationSpread: { x: 0.0, y: 0.0, z: 0.0 },
|
||||||
radiusSpread: 0.035
|
radiusSpread: 0.035
|
||||||
|
@ -71,6 +71,7 @@
|
||||||
Entities.editEntity(particles, {
|
Entities.editEntity(particles, {
|
||||||
radiusSpread: 0.0,
|
radiusSpread: 0.0,
|
||||||
radiusStart: 0.0,
|
radiusStart: 0.0,
|
||||||
|
particleRadius: 2 * PARTICLE_RADIUS, // Bezier interpolation used means that middle value isn't intersected
|
||||||
radiusFinish: 0.0
|
radiusFinish: 0.0
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -78,12 +79,13 @@
|
||||||
print("Alpha 0.5");
|
print("Alpha 0.5");
|
||||||
Entities.editEntity(particles, {
|
Entities.editEntity(particles, {
|
||||||
radiusStart: PARTICLE_RADIUS,
|
radiusStart: PARTICLE_RADIUS,
|
||||||
|
particleRadius: PARTICLE_RADIUS,
|
||||||
radiusFinish: PARTICLE_RADIUS,
|
radiusFinish: PARTICLE_RADIUS,
|
||||||
alpha: 0.5
|
alpha: 0.5
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
print("Alpha spread");
|
print("Alpha spread - temporarily not working");
|
||||||
Entities.editEntity(particles, {
|
Entities.editEntity(particles, {
|
||||||
alpha: 0.5,
|
alpha: 0.5,
|
||||||
alphaSpread: 0.5
|
alphaSpread: 0.5
|
||||||
|
@ -99,7 +101,7 @@
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
print("Color spread");
|
print("Color spread - temporarily not working");
|
||||||
Entities.editEntity(particles, {
|
Entities.editEntity(particles, {
|
||||||
alpha: 1.0,
|
alpha: 1.0,
|
||||||
alphaStart: 1.0,
|
alphaStart: 1.0,
|
||||||
|
@ -255,7 +257,6 @@
|
||||||
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
|
||||||
color: { red: 255, green: 255, blue: 255 },
|
color: { red: 255, green: 255, blue: 255 },
|
||||||
lifespan: 5.0,
|
lifespan: 5.0,
|
||||||
visible: false,
|
|
||||||
locked: false,
|
locked: false,
|
||||||
isEmitting: false,
|
isEmitting: false,
|
||||||
lifetime: 3600 // 1 hour; just in case
|
lifetime: 3600 // 1 hour; just in case
|
||||||
|
|
59
examples/html/eventBridgeLoader.js
Normal file
59
examples/html/eventBridgeLoader.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
//public slots:
|
||||||
|
// void emitWebEvent(const QString& data);
|
||||||
|
// void emitScriptEvent(const QString& data);
|
||||||
|
//
|
||||||
|
//signals:
|
||||||
|
// void webEventReceived(const QString& data);
|
||||||
|
// void scriptEventReceived(const QString& data);
|
||||||
|
//
|
||||||
|
|
||||||
|
EventBridgeConnectionProxy = function(parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.realSignal = this.parent.realBridge.scriptEventReceived
|
||||||
|
this.webWindowId = this.parent.webWindow.windowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
EventBridgeConnectionProxy.prototype.connect = function(callback) {
|
||||||
|
var that = this;
|
||||||
|
this.realSignal.connect(function(id, message) {
|
||||||
|
if (id === that.webWindowId) { callback(message); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
EventBridgeProxy = function(webWindow) {
|
||||||
|
this.webWindow = webWindow;
|
||||||
|
this.realBridge = this.webWindow.eventBridge;
|
||||||
|
this.scriptEventReceived = new EventBridgeConnectionProxy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventBridgeProxy.prototype.emitWebEvent = function(data) {
|
||||||
|
this.realBridge.emitWebEvent(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
openEventBridge = function(callback) {
|
||||||
|
EVENT_BRIDGE_URI = "ws://localhost:51016";
|
||||||
|
socket = new WebSocket(this.EVENT_BRIDGE_URI);
|
||||||
|
|
||||||
|
socket.onclose = function() {
|
||||||
|
console.error("web channel closed");
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onerror = function(error) {
|
||||||
|
console.error("web channel error: " + error);
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onopen = function() {
|
||||||
|
channel = new QWebChannel(socket, function(channel) {
|
||||||
|
console.log("Document url is " + document.URL);
|
||||||
|
for(var key in channel.objects){
|
||||||
|
console.log("registered object: " + key);
|
||||||
|
}
|
||||||
|
var webWindow = channel.objects[document.URL.toLowerCase()];
|
||||||
|
console.log("WebWindow is " + webWindow)
|
||||||
|
eventBridgeProxy = new EventBridgeProxy(webWindow);
|
||||||
|
if (callback) { callback(eventBridgeProxy); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
31
examples/html/qmlWebTest.html
Normal file
31
examples/html/qmlWebTest.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Properties</title>
|
||||||
|
<script type="text/javascript" src="jquery-2.1.4.min.js"></script>
|
||||||
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
|
<script type="text/javascript" src="eventBridgeLoader.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var myBridge;
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
openEventBridge(function(eventBridge) {
|
||||||
|
myBridge = eventBridge;
|
||||||
|
myBridge.scriptEventReceived.connect(function(message) {
|
||||||
|
console.log("HTML side received message: " + message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
testClick = function() {
|
||||||
|
myBridge.emitWebEvent("HTML side sending message - button click");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="properties">
|
||||||
|
<button name="Test" title="Test" onclick="testClick()">Test</button>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
</body>
|
32
examples/tests/qmlWebTest.js
Normal file
32
examples/tests/qmlWebTest.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
print("Launching web window");
|
||||||
|
|
||||||
|
webWindow = new OverlayWebWindow('Test Event Bridge', "file:///C:/Users/bdavis/Git/hifi/examples/html/qmlWebTest.html", 320, 240, false);
|
||||||
|
print("JS Side window: " + webWindow);
|
||||||
|
print("JS Side bridge: " + webWindow.eventBridge);
|
||||||
|
webWindow.eventBridge.webEventReceived.connect(function(data) {
|
||||||
|
print("JS Side event received: " + data);
|
||||||
|
});
|
||||||
|
|
||||||
|
var titles = ["A", "B", "C"];
|
||||||
|
var titleIndex = 0;
|
||||||
|
|
||||||
|
Script.setInterval(function() {
|
||||||
|
webWindow.eventBridge.emitScriptEvent("JS Event sent");
|
||||||
|
var size = webWindow.size;
|
||||||
|
var position = webWindow.position;
|
||||||
|
print("Window url: " + webWindow.url)
|
||||||
|
print("Window visible: " + webWindow.visible)
|
||||||
|
print("Window size: " + size.x + "x" + size.y)
|
||||||
|
print("Window pos: " + position.x + "x" + position.y)
|
||||||
|
webWindow.setVisible(!webWindow.visible);
|
||||||
|
webWindow.setTitle(titles[titleIndex]);
|
||||||
|
webWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100);
|
||||||
|
titleIndex += 1;
|
||||||
|
titleIndex %= titles.length;
|
||||||
|
}, 2 * 1000);
|
||||||
|
|
||||||
|
Script.setTimeout(function() {
|
||||||
|
print("Closing script");
|
||||||
|
webWindow.close();
|
||||||
|
Script.stop();
|
||||||
|
}, 15 * 1000)
|
|
@ -45,7 +45,9 @@ else ()
|
||||||
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
|
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets WebSockets)
|
find_package(Qt5 COMPONENTS
|
||||||
|
Gui Multimedia Network OpenGL Qml Quick Script Svg
|
||||||
|
WebChannel WebEngine WebEngineWidgets WebKitWidgets WebSockets)
|
||||||
|
|
||||||
# grab the ui files in resources/ui
|
# grab the ui files in resources/ui
|
||||||
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
|
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)
|
||||||
|
@ -175,9 +177,17 @@ include_directories("${PROJECT_SOURCE_DIR}/src")
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${TARGET_NAME}
|
${TARGET_NAME}
|
||||||
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Script Qt5::Svg Qt5::WebKitWidgets
|
Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL
|
||||||
|
Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg
|
||||||
|
Qt5::WebChannel Qt5::WebEngine Qt5::WebEngineWidgets Qt5::WebKitWidgets
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Issue causes build failure unless we add this directory.
|
||||||
|
# See https://bugreports.qt.io/browse/QTBUG-43351
|
||||||
|
if (WIN32)
|
||||||
|
add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine)
|
||||||
|
endif()
|
||||||
|
|
||||||
# assume we are using a Qt build without bearer management
|
# assume we are using a Qt build without bearer management
|
||||||
add_definitions(-DQT_NO_BEARERMANAGEMENT)
|
add_definitions(-DQT_NO_BEARERMANAGEMENT)
|
||||||
|
|
||||||
|
@ -209,5 +219,9 @@ else (APPLE)
|
||||||
endif()
|
endif()
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/resources/qml")
|
||||||
|
endif()
|
||||||
|
|
||||||
package_libraries_for_deployment()
|
package_libraries_for_deployment()
|
||||||
consolidate_stack_components()
|
consolidate_stack_components()
|
||||||
|
|
|
@ -415,10 +415,25 @@
|
||||||
"states": [
|
"states": [
|
||||||
{
|
{
|
||||||
"id": "idle",
|
"id": "idle",
|
||||||
"interpTarget": 15,
|
"interpTarget": 10,
|
||||||
"interpDuration": 15,
|
"interpDuration": 10,
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{ "var": "isMovingForward", "state": "walkFwd" },
|
{ "var": "isMovingForward", "state": "idleToWalkFwd" },
|
||||||
|
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||||
|
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||||
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
|
{ "var": "isTurningRight", "state": "turnRight" },
|
||||||
|
{ "var": "isTurningLeft", "state": "turnLeft" },
|
||||||
|
{ "var": "isAway", "state": "awayIntro" }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "idleToWalkFwd",
|
||||||
|
"interpTarget": 3,
|
||||||
|
"interpDuration": 3,
|
||||||
|
"transitions": [
|
||||||
|
{ "var": "idleToWalkFwdOnDone", "state": "walkFwd" },
|
||||||
|
{ "var": "isNotMoving", "state": "idle" },
|
||||||
{ "var": "isMovingBackward", "state": "walkBwd" },
|
{ "var": "isMovingBackward", "state": "walkBwd" },
|
||||||
{ "var": "isMovingRight", "state": "strafeRight" },
|
{ "var": "isMovingRight", "state": "strafeRight" },
|
||||||
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
{ "var": "isMovingLeft", "state": "strafeLeft" },
|
||||||
|
@ -429,7 +444,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "walkFwd",
|
"id": "walkFwd",
|
||||||
"interpTarget": 6,
|
"interpTarget": 15,
|
||||||
"interpDuration": 6,
|
"interpDuration": 6,
|
||||||
"transitions": [
|
"transitions": [
|
||||||
{ "var": "isNotMoving", "state": "idle" },
|
{ "var": "isNotMoving", "state": "idle" },
|
||||||
|
@ -638,6 +653,18 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "idleToWalkFwd",
|
||||||
|
"type": "clip",
|
||||||
|
"data": {
|
||||||
|
"url": "http://hifi-content.s3.amazonaws.com/ozan/dev/anim/standard_anims/idle_to_walk.fbx",
|
||||||
|
"startFrame": 1.0,
|
||||||
|
"endFrame": 19.0,
|
||||||
|
"timeScale": 1.0,
|
||||||
|
"loopFlag": false
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "walkBwd",
|
"id": "walkBwd",
|
||||||
"type": "blendLinearMove",
|
"type": "blendLinearMove",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtWebKit 3.0
|
import QtWebEngine 1.1
|
||||||
import "controls"
|
import "controls"
|
||||||
import "styles"
|
import "styles"
|
||||||
|
|
||||||
|
@ -39,9 +39,10 @@ VrDialog {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: scrollView.top
|
anchors.bottom: webview.top
|
||||||
color: "white"
|
color: "white"
|
||||||
}
|
}
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
id: buttons
|
id: buttons
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
@ -112,26 +113,22 @@ VrDialog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
WebEngineView {
|
||||||
id: scrollView
|
id: webview
|
||||||
|
url: "http://highfidelity.com"
|
||||||
anchors.top: buttons.bottom
|
anchors.top: buttons.bottom
|
||||||
anchors.topMargin: 8
|
anchors.topMargin: 8
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
WebView {
|
onLoadingChanged: {
|
||||||
id: webview
|
if (loadRequest.status == WebEngineView.LoadSucceededStatus) {
|
||||||
url: "http://highfidelity.com"
|
addressBar.text = loadRequest.url
|
||||||
anchors.fill: parent
|
|
||||||
onLoadingChanged: {
|
|
||||||
if (loadRequest.status == WebView.LoadSucceededStarted) {
|
|
||||||
addressBar.text = loadRequest.url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onIconChanged: {
|
|
||||||
barIcon.source = icon
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onIconChanged: {
|
||||||
|
console.log("New icon: " + icon)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // item
|
} // item
|
||||||
|
|
||||||
|
@ -146,5 +143,4 @@ VrDialog {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // dialog
|
} // dialog
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Hifi 1.0 as Hifi
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtQuick.Controls.Styles 1.3
|
import QtQuick.Controls.Styles 1.3
|
||||||
import QtWebKit 3.0
|
import QtWebEngine 1.1
|
||||||
import "controls"
|
import "controls"
|
||||||
|
|
||||||
VrDialog {
|
VrDialog {
|
||||||
|
@ -18,15 +18,11 @@ VrDialog {
|
||||||
anchors.margins: parent.margins
|
anchors.margins: parent.margins
|
||||||
anchors.topMargin: parent.topMargin
|
anchors.topMargin: parent.topMargin
|
||||||
|
|
||||||
ScrollView {
|
WebEngineView {
|
||||||
|
id: webview
|
||||||
|
objectName: "WebView"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
WebView {
|
url: infoView.url
|
||||||
objectName: "WebView"
|
|
||||||
id: webview
|
|
||||||
url: infoView.url
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Hifi 1.0
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtQuick.Controls.Styles 1.3
|
import QtQuick.Controls.Styles 1.3
|
||||||
import QtWebKit 3.0
|
import QtWebEngine 1.1
|
||||||
import "controls"
|
import "controls"
|
||||||
|
|
||||||
VrDialog {
|
VrDialog {
|
||||||
|
@ -24,27 +24,22 @@ VrDialog {
|
||||||
anchors.margins: parent.margins
|
anchors.margins: parent.margins
|
||||||
anchors.topMargin: parent.topMargin
|
anchors.topMargin: parent.topMargin
|
||||||
|
|
||||||
|
WebEngineView {
|
||||||
ScrollView {
|
objectName: "WebView"
|
||||||
|
id: webview
|
||||||
|
url: "https://metaverse.highfidelity.com/marketplace"
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
WebView {
|
onNavigationRequested: {
|
||||||
objectName: "WebView"
|
console.log(request.url)
|
||||||
id: webview
|
if (!marketplaceDialog.navigationRequested(request.url)) {
|
||||||
url: "https://metaverse.highfidelity.com/marketplace"
|
console.log("Application absorbed the request")
|
||||||
anchors.fill: parent
|
request.action = WebView.IgnoreRequest;
|
||||||
onNavigationRequested: {
|
|
||||||
console.log(request.url)
|
|
||||||
if (!marketplaceDialog.navigationRequested(request.url)) {
|
|
||||||
console.log("Application absorbed the request")
|
|
||||||
request.action = WebView.IgnoreRequest;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("Application passed on the request")
|
|
||||||
request.action = WebView.AcceptRequest;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log("Application passed on the request")
|
||||||
|
request.action = WebView.AcceptRequest;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
62
interface/resources/qml/QmlWebWindow.qml
Normal file
62
interface/resources/qml/QmlWebWindow.qml
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
|
||||||
|
import QtQuick 2.3
|
||||||
|
import QtQuick.Controls 1.2
|
||||||
|
import QtWebEngine 1.1
|
||||||
|
import QtWebChannel 1.0
|
||||||
|
import QtWebSockets 1.0
|
||||||
|
|
||||||
|
import "controls"
|
||||||
|
import "styles"
|
||||||
|
|
||||||
|
VrDialog {
|
||||||
|
id: root
|
||||||
|
HifiConstants { id: hifi }
|
||||||
|
title: "WebWindow"
|
||||||
|
resizable: true
|
||||||
|
contentImplicitWidth: clientArea.implicitWidth
|
||||||
|
contentImplicitHeight: clientArea.implicitHeight
|
||||||
|
backgroundColor: "#7f000000"
|
||||||
|
property url source: "about:blank"
|
||||||
|
|
||||||
|
signal navigating(string url)
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
enabled = true
|
||||||
|
console.log("Web Window Created " + root);
|
||||||
|
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
|
||||||
|
console.log("Web Window JS message: " + sourceID + " " + lineNumber + " " + message);
|
||||||
|
});
|
||||||
|
|
||||||
|
webview.loadingChanged.connect(handleWebviewLoading)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleWebviewLoading(loadRequest) {
|
||||||
|
var HIFI_URL_PATTERN = /^hifi:\/\//;
|
||||||
|
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
|
||||||
|
var newUrl = loadRequest.url.toString();
|
||||||
|
if (newUrl.match(HIFI_URL_PATTERN)) {
|
||||||
|
root.navigating(newUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: clientArea
|
||||||
|
implicitHeight: 600
|
||||||
|
implicitWidth: 800
|
||||||
|
x: root.clientX
|
||||||
|
y: root.clientY
|
||||||
|
width: root.clientWidth
|
||||||
|
height: root.clientHeight
|
||||||
|
|
||||||
|
WebEngineView {
|
||||||
|
id: webview
|
||||||
|
url: root.source
|
||||||
|
anchors.fill: parent
|
||||||
|
profile: WebEngineProfile {
|
||||||
|
httpUserAgent: "Mozilla/5.0 (HighFidelityInterface)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // item
|
||||||
|
} // dialog
|
|
@ -4,116 +4,7 @@ import Hifi 1.0
|
||||||
|
|
||||||
// Currently for testing a pure QML replacement menu
|
// Currently for testing a pure QML replacement menu
|
||||||
Item {
|
Item {
|
||||||
Item {
|
|
||||||
objectName: "AllActions"
|
|
||||||
Action {
|
|
||||||
id: aboutApp
|
|
||||||
objectName: "HifiAction_" + MenuConstants.AboutApp
|
|
||||||
text: qsTr("About Interface")
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// File Menu
|
|
||||||
//
|
|
||||||
Action {
|
|
||||||
id: login
|
|
||||||
objectName: "HifiAction_" + MenuConstants.Login
|
|
||||||
text: qsTr("Login")
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
id: quit
|
|
||||||
objectName: "HifiAction_" + MenuConstants.Quit
|
|
||||||
text: qsTr("Quit")
|
|
||||||
//shortcut: StandardKey.Quit
|
|
||||||
shortcut: "Ctrl+Q"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Edit menu
|
|
||||||
//
|
|
||||||
Action {
|
|
||||||
id: undo
|
|
||||||
text: "Undo"
|
|
||||||
shortcut: StandardKey.Undo
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: redo
|
|
||||||
text: "Redo"
|
|
||||||
shortcut: StandardKey.Redo
|
|
||||||
}
|
|
||||||
|
|
||||||
Action {
|
|
||||||
id: animations
|
|
||||||
objectName: "HifiAction_" + MenuConstants.Animations
|
|
||||||
text: qsTr("Animations...")
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
id: attachments
|
|
||||||
text: qsTr("Attachments...")
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
id: explode
|
|
||||||
text: qsTr("Explode on quit")
|
|
||||||
checkable: true
|
|
||||||
checked: true
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
id: freeze
|
|
||||||
text: qsTr("Freeze on quit")
|
|
||||||
checkable: true
|
|
||||||
checked: false
|
|
||||||
}
|
|
||||||
ExclusiveGroup {
|
|
||||||
Action {
|
|
||||||
id: visibleToEveryone
|
|
||||||
objectName: "HifiAction_" + MenuConstants.VisibleToEveryone
|
|
||||||
text: qsTr("Everyone")
|
|
||||||
checkable: true
|
|
||||||
checked: true
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
id: visibleToFriends
|
|
||||||
objectName: "HifiAction_" + MenuConstants.VisibleToFriends
|
|
||||||
text: qsTr("Friends")
|
|
||||||
checkable: true
|
|
||||||
}
|
|
||||||
Action {
|
|
||||||
id: visibleToNoOne
|
|
||||||
objectName: "HifiAction_" + MenuConstants.VisibleToNoOne
|
|
||||||
text: qsTr("No one")
|
|
||||||
checkable: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
objectName: "rootMenu";
|
objectName: "rootMenu";
|
||||||
Menu {
|
|
||||||
title: "File"
|
|
||||||
MenuItem { action: login }
|
|
||||||
MenuItem { action: explode }
|
|
||||||
MenuItem { action: freeze }
|
|
||||||
MenuItem { action: quit }
|
|
||||||
}
|
|
||||||
Menu {
|
|
||||||
title: "Tools"
|
|
||||||
Menu {
|
|
||||||
title: "I Am Visible To"
|
|
||||||
MenuItem { action: visibleToEveryone }
|
|
||||||
MenuItem { action: visibleToFriends }
|
|
||||||
MenuItem { action: visibleToNoOne }
|
|
||||||
}
|
|
||||||
MenuItem { action: animations }
|
|
||||||
}
|
|
||||||
Menu {
|
|
||||||
title: "Long menu name top menu"
|
|
||||||
MenuItem { action: aboutApp }
|
|
||||||
}
|
|
||||||
Menu {
|
|
||||||
title: "Help"
|
|
||||||
MenuItem { action: aboutApp }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import QtQuick 2.3
|
import QtQuick 2.3
|
||||||
import QtQuick.Controls 1.2
|
import QtQuick.Controls 1.2
|
||||||
import QtWebKit 3.0
|
import QtWebEngine 1.1
|
||||||
|
|
||||||
WebView {
|
WebEngineView {
|
||||||
id: root
|
id: root
|
||||||
objectName: "webview"
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
objectName: "webview"
|
||||||
url: "about:blank"
|
url: "about:blank"
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,7 @@
|
||||||
#include <VrMenu.h>
|
#include <VrMenu.h>
|
||||||
#include <recording/Deck.h>
|
#include <recording/Deck.h>
|
||||||
#include <recording/Recorder.h>
|
#include <recording/Recorder.h>
|
||||||
|
#include <QmlWebWindowClass.h>
|
||||||
|
|
||||||
#include "AnimDebugDraw.h"
|
#include "AnimDebugDraw.h"
|
||||||
#include "AudioClient.h"
|
#include "AudioClient.h"
|
||||||
|
@ -362,6 +363,17 @@ Cube3DOverlay* _keyboardFocusHighlight{ nullptr };
|
||||||
int _keyboardFocusHighlightID{ -1 };
|
int _keyboardFocusHighlightID{ -1 };
|
||||||
PluginContainer* _pluginContainer;
|
PluginContainer* _pluginContainer;
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME hack access to the internal share context for the Chromium helper
|
||||||
|
// Normally we'd want to use QWebEngine::initialize(), but we can't because
|
||||||
|
// our primary context is a QGLWidget, which can't easily be initialized to share
|
||||||
|
// from a QOpenGLContext.
|
||||||
|
//
|
||||||
|
// So instead we create a new offscreen context to share with the QGLWidget,
|
||||||
|
// and manually set THAT to be the shared context for the Chromium helper
|
||||||
|
OffscreenGLCanvas* _chromiumShareContext { nullptr };
|
||||||
|
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
||||||
|
|
||||||
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
QApplication(argc, argv),
|
QApplication(argc, argv),
|
||||||
_dependencyManagerIsSetup(setupEssentials(argc, argv)),
|
_dependencyManagerIsSetup(setupEssentials(argc, argv)),
|
||||||
|
@ -623,6 +635,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
_glWidget->makeCurrent();
|
_glWidget->makeCurrent();
|
||||||
_glWidget->initializeGL();
|
_glWidget->initializeGL();
|
||||||
|
|
||||||
|
_chromiumShareContext = new OffscreenGLCanvas();
|
||||||
|
_chromiumShareContext->create(_glWidget->context()->contextHandle());
|
||||||
|
_chromiumShareContext->makeCurrent();
|
||||||
|
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||||
|
|
||||||
_offscreenContext = new OffscreenGLCanvas();
|
_offscreenContext = new OffscreenGLCanvas();
|
||||||
_offscreenContext->create(_glWidget->context()->contextHandle());
|
_offscreenContext->create(_glWidget->context()->contextHandle());
|
||||||
_offscreenContext->makeCurrent();
|
_offscreenContext->makeCurrent();
|
||||||
|
@ -686,13 +703,37 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) :
|
||||||
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
} else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) {
|
||||||
VrMenu::toggle(); // show context menu even on non-stereo displays
|
VrMenu::toggle(); // show context menu even on non-stereo displays
|
||||||
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
} else if (action == controller::toInt(controller::Action::RETICLE_X)) {
|
||||||
auto globalPos = QCursor::pos();
|
auto oldPos = QCursor::pos();
|
||||||
globalPos.setX(globalPos.x() + state);
|
auto newPos = oldPos;
|
||||||
QCursor::setPos(globalPos);
|
newPos.setX(oldPos.x() + state);
|
||||||
|
QCursor::setPos(newPos);
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
||||||
|
// remove it after we're done
|
||||||
|
const float REASONABLE_CHANGE = 50.0f;
|
||||||
|
glm::vec2 oldPosG = { oldPos.x(), oldPos.y() };
|
||||||
|
glm::vec2 newPosG = { newPos.x(), newPos.y() };
|
||||||
|
auto distance = glm::distance(oldPosG, newPosG);
|
||||||
|
if (distance > REASONABLE_CHANGE) {
|
||||||
|
qDebug() << "Action::RETICLE_X... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG;
|
||||||
|
}
|
||||||
|
|
||||||
} else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
|
} else if (action == controller::toInt(controller::Action::RETICLE_Y)) {
|
||||||
auto globalPos = QCursor::pos();
|
auto oldPos = QCursor::pos();
|
||||||
globalPos.setY(globalPos.y() + state);
|
auto newPos = oldPos;
|
||||||
QCursor::setPos(globalPos);
|
newPos.setY(oldPos.y() + state);
|
||||||
|
QCursor::setPos(newPos);
|
||||||
|
|
||||||
|
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
||||||
|
// remove it after we're done
|
||||||
|
const float REASONABLE_CHANGE = 50.0f;
|
||||||
|
glm::vec2 oldPosG = { oldPos.x(), oldPos.y() };
|
||||||
|
glm::vec2 newPosG = { newPos.x(), newPos.y() };
|
||||||
|
auto distance = glm::distance(oldPosG, newPosG);
|
||||||
|
if (distance > REASONABLE_CHANGE) {
|
||||||
|
qDebug() << "Action::RETICLE_Y... UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPosG << " newPos:" << newPosG;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2988,6 +3029,9 @@ void Application::update(float deltaTime) {
|
||||||
_physicsEngine->changeObjects(motionStates);
|
_physicsEngine->changeObjects(motionStates);
|
||||||
|
|
||||||
myAvatar->prepareForPhysicsSimulation();
|
myAvatar->prepareForPhysicsSimulation();
|
||||||
|
_physicsEngine->forEachAction([&](EntityActionPointer action) {
|
||||||
|
action->prepareForPhysicsSimulation();
|
||||||
|
});
|
||||||
|
|
||||||
getEntities()->getTree()->withWriteLock([&] {
|
getEntities()->getTree()->withWriteLock([&] {
|
||||||
_physicsEngine->stepSimulation();
|
_physicsEngine->stepSimulation();
|
||||||
|
@ -4139,6 +4183,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
|
||||||
LocationScriptingInterface::locationSetter);
|
LocationScriptingInterface::locationSetter);
|
||||||
|
|
||||||
scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
|
scriptEngine->registerFunction("WebWindow", WebWindowClass::constructor, 1);
|
||||||
|
scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor);
|
||||||
|
|
||||||
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance());
|
||||||
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
scriptEngine->registerGlobalObject("Stats", Stats::getInstance());
|
||||||
|
|
|
@ -157,7 +157,7 @@ void Avatar::animateScaleChanges(float deltaTime) {
|
||||||
|
|
||||||
// snap to the end when we get close enough
|
// snap to the end when we get close enough
|
||||||
const float MIN_RELATIVE_SCALE_ERROR = 0.03f;
|
const float MIN_RELATIVE_SCALE_ERROR = 0.03f;
|
||||||
if (fabsf(_targetScale - currentScale) / _targetScale < 0.03f) {
|
if (fabsf(_targetScale - currentScale) / _targetScale < MIN_RELATIVE_SCALE_ERROR) {
|
||||||
animatedScale = _targetScale;
|
animatedScale = _targetScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <QVariantGLM.h>
|
#include <QVariantGLM.h>
|
||||||
|
|
||||||
#include "avatar/AvatarManager.h"
|
#include "avatar/AvatarManager.h"
|
||||||
|
#include "CharacterController.h"
|
||||||
|
|
||||||
const uint16_t AvatarActionHold::holdVersion = 1;
|
const uint16_t AvatarActionHold::holdVersion = 1;
|
||||||
|
|
||||||
|
@ -32,6 +33,64 @@ AvatarActionHold::~AvatarActionHold() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AvatarActionHold::getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) {
|
||||||
|
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
|
||||||
|
MyCharacterController* controller = myAvatar ? myAvatar->getCharacterController() : nullptr;
|
||||||
|
if (!controller) {
|
||||||
|
qDebug() << "AvatarActionHold::getAvatarRigidBodyLocation failed to get character controller";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
controller->getRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AvatarActionHold::prepareForPhysicsSimulation() {
|
||||||
|
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
|
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
||||||
|
|
||||||
|
if (!holdingAvatar || !holdingAvatar->isMyAvatar()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
withWriteLock([&]{
|
||||||
|
if (_ignoreIK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 palmPosition;
|
||||||
|
glm::quat palmRotation;
|
||||||
|
if (_hand == "right") {
|
||||||
|
palmPosition = holdingAvatar->getRightPalmPosition();
|
||||||
|
palmRotation = holdingAvatar->getRightPalmRotation();
|
||||||
|
} else {
|
||||||
|
palmPosition = holdingAvatar->getLeftPalmPosition();
|
||||||
|
palmRotation = holdingAvatar->getLeftPalmRotation();
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 avatarRigidBodyPosition;
|
||||||
|
glm::quat avatarRigidBodyRotation;
|
||||||
|
getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
|
||||||
|
|
||||||
|
// determine the difference in translation and rotation between the avatar's
|
||||||
|
// rigid body and the palm position. The avatar's rigid body will be moved by bullet
|
||||||
|
// between this call and the call to getTarget, below. A call to get*PalmPosition in
|
||||||
|
// getTarget would get the palm position of the previous location of the avatar (because
|
||||||
|
// bullet has moved the av's rigid body but the rigid body's location has not yet been
|
||||||
|
// copied out into the Avatar class.
|
||||||
|
//glm::quat avatarRotationInverse = glm::inverse(avatarRigidBodyRotation);
|
||||||
|
|
||||||
|
// the offset should be in the frame of the avatar, but something about the order
|
||||||
|
// things are updated makes this wrong:
|
||||||
|
// _palmOffsetFromRigidBody = avatarRotationInverse * (palmPosition - avatarRigidBodyPosition);
|
||||||
|
// I'll leave it here as a comment in case avatar handling changes.
|
||||||
|
_palmOffsetFromRigidBody = palmPosition - avatarRigidBodyPosition;
|
||||||
|
|
||||||
|
// rotation should also be needed, but again, the order of updates makes this unneeded. leaving
|
||||||
|
// code here for future reference.
|
||||||
|
// _palmRotationFromRigidBody = avatarRotationInverse * palmRotation;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
|
std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::vec3& position) {
|
||||||
auto avatarManager = DependencyManager::get<AvatarManager>();
|
auto avatarManager = DependencyManager::get<AvatarManager>();
|
||||||
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
auto holdingAvatar = std::static_pointer_cast<Avatar>(avatarManager->getAvatarBySessionID(_holderID));
|
||||||
|
@ -40,7 +99,7 @@ std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::ve
|
||||||
return holdingAvatar;
|
return holdingAvatar;
|
||||||
}
|
}
|
||||||
|
|
||||||
withTryReadLock([&]{
|
withReadLock([&]{
|
||||||
bool isRightHand = (_hand == "right");
|
bool isRightHand = (_hand == "right");
|
||||||
glm::vec3 palmPosition { Vectors::ZERO };
|
glm::vec3 palmPosition { Vectors::ZERO };
|
||||||
glm::quat palmRotation { Quaternions::IDENTITY };
|
glm::quat palmRotation { Quaternions::IDENTITY };
|
||||||
|
@ -55,6 +114,31 @@ std::shared_ptr<Avatar> AvatarActionHold::getTarget(glm::quat& rotation, glm::ve
|
||||||
palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getPosition();
|
palmPosition = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getPosition();
|
||||||
palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getRotation();
|
palmRotation = holdingAvatar->getHand()->getCopyOfPalmData(HandData::LeftHand).getRotation();
|
||||||
}
|
}
|
||||||
|
} else if (holdingAvatar->isMyAvatar()) {
|
||||||
|
glm::vec3 avatarRigidBodyPosition;
|
||||||
|
glm::quat avatarRigidBodyRotation;
|
||||||
|
getAvatarRigidBodyLocation(avatarRigidBodyPosition, avatarRigidBodyRotation);
|
||||||
|
|
||||||
|
// the offset and rotation between the avatar's rigid body and the palm were determined earlier
|
||||||
|
// in prepareForPhysicsSimulation. At this point, the avatar's rigid body has been moved by bullet
|
||||||
|
// and the data in the Avatar class is stale. This means that the result of get*PalmPosition will
|
||||||
|
// be stale. Instead, determine the current palm position with the current avatar's rigid body
|
||||||
|
// location and the saved offsets.
|
||||||
|
|
||||||
|
// this line is more correct but breaks for the current way avatar data is updated.
|
||||||
|
// palmPosition = avatarRigidBodyPosition + avatarRigidBodyRotation * _palmOffsetFromRigidBody;
|
||||||
|
// instead, use this for now:
|
||||||
|
palmPosition = avatarRigidBodyPosition + _palmOffsetFromRigidBody;
|
||||||
|
|
||||||
|
// the item jitters the least by getting the rotation based on the opinion of Avatar.h rather
|
||||||
|
// than that of the rigid body. leaving this next line here for future reference:
|
||||||
|
// palmRotation = avatarRigidBodyRotation * _palmRotationFromRigidBody;
|
||||||
|
|
||||||
|
if (isRightHand) {
|
||||||
|
palmRotation = holdingAvatar->getRightPalmRotation();
|
||||||
|
} else {
|
||||||
|
palmRotation = holdingAvatar->getLeftPalmRotation();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isRightHand) {
|
if (isRightHand) {
|
||||||
palmPosition = holdingAvatar->getRightPalmPosition();
|
palmPosition = holdingAvatar->getRightPalmPosition();
|
||||||
|
@ -103,21 +187,19 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
|
||||||
if (valid && holdCount > 0) {
|
if (valid && holdCount > 0) {
|
||||||
position /= holdCount;
|
position /= holdCount;
|
||||||
|
|
||||||
bool gotLock = withTryWriteLock([&]{
|
withWriteLock([&]{
|
||||||
_positionalTarget = position;
|
_positionalTarget = position;
|
||||||
_rotationalTarget = rotation;
|
_rotationalTarget = rotation;
|
||||||
_positionalTargetSet = true;
|
_positionalTargetSet = true;
|
||||||
_rotationalTargetSet = true;
|
_rotationalTargetSet = true;
|
||||||
_active = true;
|
_active = true;
|
||||||
});
|
});
|
||||||
if (gotLock) {
|
if (_kinematic) {
|
||||||
if (_kinematic) {
|
doKinematicUpdate(deltaTimeStep);
|
||||||
doKinematicUpdate(deltaTimeStep);
|
} else {
|
||||||
} else {
|
activateBody();
|
||||||
activateBody();
|
forceBodyNonStatic();
|
||||||
forceBodyNonStatic();
|
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
||||||
ObjectActionSpring::updateActionWorker(deltaTimeStep);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,18 +25,21 @@ public:
|
||||||
AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity);
|
AvatarActionHold(const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
virtual ~AvatarActionHold();
|
virtual ~AvatarActionHold();
|
||||||
|
|
||||||
virtual bool updateArguments(QVariantMap arguments);
|
virtual bool updateArguments(QVariantMap arguments) override;
|
||||||
virtual QVariantMap getArguments();
|
virtual QVariantMap getArguments() override;
|
||||||
|
|
||||||
virtual void updateActionWorker(float deltaTimeStep);
|
virtual void updateActionWorker(float deltaTimeStep) override;
|
||||||
|
|
||||||
QByteArray serialize() const;
|
QByteArray serialize() const;
|
||||||
virtual void deserialize(QByteArray serializedArguments);
|
virtual void deserialize(QByteArray serializedArguments) override;
|
||||||
|
|
||||||
virtual bool shouldSuppressLocationEdits() { return _active && !_ownerEntity.expired(); }
|
virtual bool shouldSuppressLocationEdits() override { return _active && !_ownerEntity.expired(); }
|
||||||
|
|
||||||
|
bool getAvatarRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
|
||||||
std::shared_ptr<Avatar> getTarget(glm::quat& rotation, glm::vec3& position);
|
std::shared_ptr<Avatar> getTarget(glm::quat& rotation, glm::vec3& position);
|
||||||
|
|
||||||
|
virtual void prepareForPhysicsSimulation() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void doKinematicUpdate(float deltaTimeStep);
|
void doKinematicUpdate(float deltaTimeStep);
|
||||||
|
|
||||||
|
@ -56,6 +59,10 @@ private:
|
||||||
|
|
||||||
float _previousDeltaTimeStep = 0.0f;
|
float _previousDeltaTimeStep = 0.0f;
|
||||||
glm::vec3 _previousPositionalDelta;
|
glm::vec3 _previousPositionalDelta;
|
||||||
|
|
||||||
|
glm::vec3 _palmOffsetFromRigidBody;
|
||||||
|
// leaving this here for future refernece.
|
||||||
|
// glm::quat _palmRotationFromRigidBody;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_AvatarActionHold_h
|
#endif // hifi_AvatarActionHold_h
|
||||||
|
|
|
@ -509,25 +509,25 @@ glm::vec3 MyAvatar::getRightHandTipPosition() const {
|
||||||
controller::Pose MyAvatar::getLeftHandPose() const {
|
controller::Pose MyAvatar::getLeftHandPose() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
||||||
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
|
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
|
||||||
palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
|
palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getRightHandPose() const {
|
controller::Pose MyAvatar::getRightHandPose() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
||||||
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
|
return palmData.isValid() ? controller::Pose(palmData.getPosition(), palmData.getRotation(),
|
||||||
palmData.getVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
|
palmData.getVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getLeftHandTipPose() const {
|
controller::Pose MyAvatar::getLeftHandTipPose() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
auto palmData = getHandData()->getCopyOfPalmData(HandData::LeftHand);
|
||||||
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
|
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
|
||||||
palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
|
palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
||||||
}
|
}
|
||||||
|
|
||||||
controller::Pose MyAvatar::getRightHandTipPose() const {
|
controller::Pose MyAvatar::getRightHandTipPose() const {
|
||||||
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
auto palmData = getHandData()->getCopyOfPalmData(HandData::RightHand);
|
||||||
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
|
return palmData.isValid() ? controller::Pose(palmData.getTipPosition(), palmData.getRotation(),
|
||||||
palmData.getTipVelocity(), palmData.getRawAngularVelocityAsQuat()) : controller::Pose();
|
palmData.getTipVelocity(), palmData.getRawAngularVelocity()) : controller::Pose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
static const quint64 MSECS_TO_USECS = 1000ULL;
|
static const quint64 MSECS_TO_USECS = 1000ULL;
|
||||||
static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
static const quint64 TOOLTIP_DELAY = 500 * MSECS_TO_USECS;
|
||||||
|
|
||||||
static const float RETICLE_COLOR[] = { 0.0f, 198.0f / 255.0f, 244.0f / 255.0f };
|
|
||||||
static const float reticleSize = TWO_PI / 100.0f;
|
static const float reticleSize = TWO_PI / 100.0f;
|
||||||
|
|
||||||
static const float CURSOR_PIXEL_SIZE = 32.0f;
|
static const float CURSOR_PIXEL_SIZE = 32.0f;
|
||||||
|
|
|
@ -122,5 +122,5 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) {
|
||||||
auto clipNode = std::dynamic_pointer_cast<AnimClip>(_children.front());
|
auto clipNode = std::dynamic_pointer_cast<AnimClip>(_children.front());
|
||||||
assert(clipNode);
|
assert(clipNode);
|
||||||
const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f;
|
const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f;
|
||||||
_phase = fmodf(frame, NUM_FRAMES);
|
_phase = fmodf(frame / NUM_FRAMES, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector<FBXJoint>& joints)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
#define DUMP_FBX_JOINTS
|
||||||
void AnimSkeleton::dump() const {
|
void AnimSkeleton::dump() const {
|
||||||
qCDebug(animation) << "[";
|
qCDebug(animation) << "[";
|
||||||
for (int i = 0; i < getNumJoints(); i++) {
|
for (int i = 0; i < getNumJoints(); i++) {
|
||||||
|
@ -151,21 +152,22 @@ void AnimSkeleton::dump() const {
|
||||||
qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i);
|
qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i);
|
||||||
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
|
qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i);
|
||||||
#ifdef DUMP_FBX_JOINTS
|
#ifdef DUMP_FBX_JOINTS
|
||||||
qCDebug(animation) << " isFree =" << _joints[i].isFree;
|
qCDebug(animation) << " fbxJoint =";
|
||||||
qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage;
|
qCDebug(animation) << " isFree =" << _joints[i].isFree;
|
||||||
qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex;
|
qCDebug(animation) << " freeLineage =" << _joints[i].freeLineage;
|
||||||
qCDebug(animation) << " translation =" << _joints[i].translation;
|
qCDebug(animation) << " parentIndex =" << _joints[i].parentIndex;
|
||||||
qCDebug(animation) << " preTransform =" << _joints[i].preTransform;
|
qCDebug(animation) << " translation =" << _joints[i].translation;
|
||||||
qCDebug(animation) << " preRotation =" << _joints[i].preRotation;
|
qCDebug(animation) << " preTransform =" << _joints[i].preTransform;
|
||||||
qCDebug(animation) << " rotation =" << _joints[i].rotation;
|
qCDebug(animation) << " preRotation =" << _joints[i].preRotation;
|
||||||
qCDebug(animation) << " postRotation =" << _joints[i].postRotation;
|
qCDebug(animation) << " rotation =" << _joints[i].rotation;
|
||||||
qCDebug(animation) << " postTransform =" << _joints[i].postTransform;
|
qCDebug(animation) << " postRotation =" << _joints[i].postRotation;
|
||||||
qCDebug(animation) << " transform =" << _joints[i].transform;
|
qCDebug(animation) << " postTransform =" << _joints[i].postTransform;
|
||||||
qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax;
|
qCDebug(animation) << " transform =" << _joints[i].transform;
|
||||||
qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation;
|
qCDebug(animation) << " rotationMin =" << _joints[i].rotationMin << ", rotationMax =" << _joints[i].rotationMax;
|
||||||
qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation;
|
qCDebug(animation) << " inverseDefaultRotation" << _joints[i].inverseDefaultRotation;
|
||||||
qCDebug(animation) << " bindTransform" << _joints[i].bindTransform;
|
qCDebug(animation) << " inverseBindRotation" << _joints[i].inverseBindRotation;
|
||||||
qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint;
|
qCDebug(animation) << " bindTransform" << _joints[i].bindTransform;
|
||||||
|
qCDebug(animation) << " isSkeletonJoint" << _joints[i].isSkeletonJoint;
|
||||||
#endif
|
#endif
|
||||||
if (getParentIndex(i) >= 0) {
|
if (getParentIndex(i) >= 0) {
|
||||||
qCDebug(animation) << " parent =" << getJointName(getParentIndex(i));
|
qCDebug(animation) << " parent =" << getJointName(getParentIndex(i));
|
||||||
|
|
|
@ -112,7 +112,6 @@ public:
|
||||||
|
|
||||||
void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }
|
void setRawAngularVelocity(const glm::vec3& angularVelocity) { _rawAngularVelocity = angularVelocity; }
|
||||||
const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }
|
const glm::vec3& getRawAngularVelocity() const { return _rawAngularVelocity; }
|
||||||
glm::quat getRawAngularVelocityAsQuat() const { return glm::quat(_rawAngularVelocity); }
|
|
||||||
|
|
||||||
void addToPosition(const glm::vec3& delta);
|
void addToPosition(const glm::vec3& delta);
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
namespace controller {
|
namespace controller {
|
||||||
|
|
||||||
Pose::Pose(const vec3& translation, const quat& rotation,
|
Pose::Pose(const vec3& translation, const quat& rotation,
|
||||||
const vec3& velocity, const quat& angularVelocity) :
|
const vec3& velocity, const vec3& angularVelocity) :
|
||||||
translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity), valid (true) { }
|
translation(translation), rotation(rotation), velocity(velocity), angularVelocity(angularVelocity), valid (true) { }
|
||||||
|
|
||||||
bool Pose::operator==(const Pose& right) const {
|
bool Pose::operator==(const Pose& right) const {
|
||||||
|
@ -35,14 +35,29 @@ namespace controller {
|
||||||
obj.setProperty("translation", vec3toScriptValue(engine, pose.translation));
|
obj.setProperty("translation", vec3toScriptValue(engine, pose.translation));
|
||||||
obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation));
|
obj.setProperty("rotation", quatToScriptValue(engine, pose.rotation));
|
||||||
obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity));
|
obj.setProperty("velocity", vec3toScriptValue(engine, pose.velocity));
|
||||||
obj.setProperty("angularVelocity", quatToScriptValue(engine, pose.angularVelocity));
|
obj.setProperty("angularVelocity", vec3toScriptValue(engine, pose.angularVelocity));
|
||||||
obj.setProperty("valid", pose.valid);
|
obj.setProperty("valid", pose.valid);
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) {
|
void Pose::fromScriptValue(const QScriptValue& object, Pose& pose) {
|
||||||
// nothing for now...
|
auto translation = object.property("translation");
|
||||||
|
auto rotation = object.property("rotation");
|
||||||
|
auto velocity = object.property("velocity");
|
||||||
|
auto angularVelocity = object.property("angularVelocity");
|
||||||
|
if (translation.isValid() &&
|
||||||
|
rotation.isValid() &&
|
||||||
|
velocity.isValid() &&
|
||||||
|
angularVelocity.isValid()) {
|
||||||
|
vec3FromScriptValue(translation, pose.translation);
|
||||||
|
quatFromScriptValue(rotation, pose.rotation);
|
||||||
|
vec3FromScriptValue(velocity, pose.velocity);
|
||||||
|
vec3FromScriptValue(angularVelocity, pose.angularVelocity);
|
||||||
|
pose.valid = true;
|
||||||
|
} else {
|
||||||
|
pose.valid = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,12 @@ namespace controller {
|
||||||
vec3 translation;
|
vec3 translation;
|
||||||
quat rotation;
|
quat rotation;
|
||||||
vec3 velocity;
|
vec3 velocity;
|
||||||
quat angularVelocity;
|
vec3 angularVelocity;
|
||||||
bool valid{ false };
|
bool valid{ false };
|
||||||
|
|
||||||
Pose() {}
|
Pose() {}
|
||||||
Pose(const vec3& translation, const quat& rotation,
|
Pose(const vec3& translation, const quat& rotation,
|
||||||
const vec3& velocity = vec3(), const quat& angularVelocity = quat());
|
const vec3& velocity = vec3(), const vec3& angularVelocity = vec3());
|
||||||
|
|
||||||
Pose(const Pose&) = default;
|
Pose(const Pose&) = default;
|
||||||
Pose& operator = (const Pose&) = default;
|
Pose& operator = (const Pose&) = default;
|
||||||
|
@ -38,7 +38,7 @@ namespace controller {
|
||||||
vec3 getTranslation() const { return translation; }
|
vec3 getTranslation() const { return translation; }
|
||||||
quat getRotation() const { return rotation; }
|
quat getRotation() const { return rotation; }
|
||||||
vec3 getVelocity() const { return velocity; }
|
vec3 getVelocity() const { return velocity; }
|
||||||
quat getAngularVelocity() const { return angularVelocity; }
|
vec3 getAngularVelocity() const { return angularVelocity; }
|
||||||
|
|
||||||
static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event);
|
static QScriptValue toScriptValue(QScriptEngine* engine, const Pose& event);
|
||||||
static void fromScriptValue(const QScriptValue& object, Pose& event);
|
static void fromScriptValue(const QScriptValue& object, Pose& event);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <QCursor>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QVariant>
|
#include <QtCore/QVariant>
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
#include <QtScript/QScriptValue>
|
#include <QtScript/QScriptValue>
|
||||||
|
|
||||||
#include <DependencyManager.h>
|
#include <DependencyManager.h>
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
#include "UserInputMapper.h"
|
#include "UserInputMapper.h"
|
||||||
#include "StandardControls.h"
|
#include "StandardControls.h"
|
||||||
|
@ -87,6 +89,21 @@ namespace controller {
|
||||||
Q_INVOKABLE QObject* parseMapping(const QString& json);
|
Q_INVOKABLE QObject* parseMapping(const QString& json);
|
||||||
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
|
Q_INVOKABLE QObject* loadMapping(const QString& jsonUrl);
|
||||||
|
|
||||||
|
Q_INVOKABLE glm::vec2 getReticlePosition() {
|
||||||
|
return toGlm(QCursor::pos());
|
||||||
|
}
|
||||||
|
Q_INVOKABLE void setReticlePosition(glm::vec2 position) {
|
||||||
|
// NOTE: This is some debugging code we will leave in while debugging various reticle movement strategies,
|
||||||
|
// remove it after we're done
|
||||||
|
const float REASONABLE_CHANGE = 50.0f;
|
||||||
|
glm::vec2 oldPos = toGlm(QCursor::pos());
|
||||||
|
auto distance = glm::distance(oldPos, position);
|
||||||
|
if (distance > REASONABLE_CHANGE) {
|
||||||
|
qDebug() << "Contrller::ScriptingInterface ---- UNREASONABLE CHANGE! distance:" << distance << " oldPos:" << oldPos << " newPos:" << position;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCursor::setPos(position.x, position.y);
|
||||||
|
}
|
||||||
|
|
||||||
//Q_INVOKABLE bool isPrimaryButtonPressed() const;
|
//Q_INVOKABLE bool isPrimaryButtonPressed() const;
|
||||||
//Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const;
|
//Q_INVOKABLE glm::vec2 getPrimaryJoystickPosition() const;
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#include <QtCore/QThread>
|
#include <QtCore/QThread>
|
||||||
|
|
||||||
|
#include <StreamUtils.h>
|
||||||
|
|
||||||
using namespace controller;
|
using namespace controller;
|
||||||
|
|
||||||
float ScriptEndpoint::peek() const {
|
float ScriptEndpoint::peek() const {
|
||||||
|
@ -23,7 +25,16 @@ void ScriptEndpoint::updateValue() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastValueRead = (float)_callable.call().toNumber();
|
QScriptValue result = _callable.call();
|
||||||
|
|
||||||
|
// If the callable ever returns a non-number, we assume it's a pose
|
||||||
|
// and start reporting ourselves as a pose.
|
||||||
|
if (result.isNumber()) {
|
||||||
|
_lastValueRead = (float)_callable.call().toNumber();
|
||||||
|
} else {
|
||||||
|
Pose::fromScriptValue(result, _lastPoseRead);
|
||||||
|
_returnPose = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEndpoint::apply(float value, const Pointer& source) {
|
void ScriptEndpoint::apply(float value, const Pointer& source) {
|
||||||
|
@ -44,3 +55,36 @@ void ScriptEndpoint::internalApply(float value, int sourceID) {
|
||||||
_callable.call(QScriptValue(),
|
_callable.call(QScriptValue(),
|
||||||
QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) }));
|
QScriptValueList({ QScriptValue(value), QScriptValue(sourceID) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pose ScriptEndpoint::peekPose() const {
|
||||||
|
const_cast<ScriptEndpoint*>(this)->updatePose();
|
||||||
|
return _lastPoseRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEndpoint::updatePose() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "updatePose", Qt::QueuedConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QScriptValue result = _callable.call();
|
||||||
|
Pose::fromScriptValue(result, _lastPoseRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEndpoint::apply(const Pose& newPose, const Pointer& source) {
|
||||||
|
if (newPose == _lastPoseWritten) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
internalApply(newPose, source->getInput().getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptEndpoint::internalApply(const Pose& newPose, int sourceID) {
|
||||||
|
_lastPoseWritten = newPose;
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "internalApply", Qt::QueuedConnection,
|
||||||
|
Q_ARG(const Pose&, newPose),
|
||||||
|
Q_ARG(int, sourceID));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_callable.call(QScriptValue(),
|
||||||
|
QScriptValueList({ Pose::toScriptValue(_callable.engine(), newPose), QScriptValue(sourceID) }));
|
||||||
|
}
|
||||||
|
|
|
@ -27,13 +27,26 @@ public:
|
||||||
virtual float peek() const override;
|
virtual float peek() const override;
|
||||||
virtual void apply(float newValue, const Pointer& source) override;
|
virtual void apply(float newValue, const Pointer& source) override;
|
||||||
|
|
||||||
|
|
||||||
|
virtual Pose peekPose() const override;
|
||||||
|
virtual void apply(const Pose& newValue, const Pointer& source) override;
|
||||||
|
|
||||||
|
virtual bool isPose() const override { return _returnPose; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Q_INVOKABLE void updateValue();
|
Q_INVOKABLE void updateValue();
|
||||||
Q_INVOKABLE virtual void internalApply(float newValue, int sourceID);
|
Q_INVOKABLE virtual void internalApply(float newValue, int sourceID);
|
||||||
|
|
||||||
|
Q_INVOKABLE void updatePose();
|
||||||
|
Q_INVOKABLE virtual void internalApply(const Pose& newValue, int sourceID);
|
||||||
private:
|
private:
|
||||||
QScriptValue _callable;
|
QScriptValue _callable;
|
||||||
float _lastValueRead { 0.0f };
|
float _lastValueRead { 0.0f };
|
||||||
float _lastValueWritten { 0.0f };
|
float _lastValueWritten { 0.0f };
|
||||||
|
|
||||||
|
bool _returnPose { false };
|
||||||
|
Pose _lastPoseRead;
|
||||||
|
Pose _lastPoseWritten;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -447,16 +447,14 @@ bool RenderableModelEntityItem::isReadyToComputeShape() {
|
||||||
ShapeType type = getShapeType();
|
ShapeType type = getShapeType();
|
||||||
|
|
||||||
if (type == SHAPE_TYPE_COMPOUND) {
|
if (type == SHAPE_TYPE_COMPOUND) {
|
||||||
if (!_model) {
|
if (!_model || _model->getCollisionURL().isEmpty()) {
|
||||||
EntityTreePointer tree = getTree();
|
EntityTreePointer tree = getTree();
|
||||||
if (tree) {
|
if (tree) {
|
||||||
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
QMetaObject::invokeMethod(tree.get(), "callLoader", Qt::QueuedConnection, Q_ARG(EntityItemID, getID()));
|
||||||
}
|
}
|
||||||
return false; // hmm...
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!_model->getCollisionURL().isEmpty());
|
|
||||||
|
|
||||||
if (_model->getURL().isEmpty()) {
|
if (_model->getURL().isEmpty()) {
|
||||||
// we need a render geometry with a scale to proceed, so give up.
|
// we need a render geometry with a scale to proceed, so give up.
|
||||||
return false;
|
return false;
|
||||||
|
@ -625,3 +623,11 @@ glm::vec3 RenderableModelEntityItem::getAbsoluteJointTranslationInObjectFrame(in
|
||||||
}
|
}
|
||||||
return glm::vec3(0.0f);
|
return glm::vec3(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderableModelEntityItem::locationChanged() {
|
||||||
|
EntityItem::locationChanged();
|
||||||
|
if (_model && _model->isActive()) {
|
||||||
|
_model->setRotation(getRotation());
|
||||||
|
_model->setTranslation(getPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -73,6 +73,7 @@ public:
|
||||||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||||
|
|
||||||
virtual void loader() override;
|
virtual void loader() override;
|
||||||
|
virtual void locationChanged() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void remapTextures();
|
void remapTextures();
|
||||||
|
|
|
@ -82,7 +82,7 @@ void main(void) {
|
||||||
varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age);
|
varColor = interpolate3Vec4(particle.color.start, particle.color.middle, particle.color.finish, age);
|
||||||
|
|
||||||
// anchor point in eye space
|
// anchor point in eye space
|
||||||
float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish , age);
|
float radius = bezierInterpolate(particle.radius.start, particle.radius.middle, particle.radius.finish, age);
|
||||||
vec4 quadPos = radius * UNIT_QUAD[twoTriID];
|
vec4 quadPos = radius * UNIT_QUAD[twoTriID];
|
||||||
|
|
||||||
vec4 anchorPoint;
|
vec4 anchorPoint;
|
||||||
|
|
|
@ -58,6 +58,8 @@ public:
|
||||||
|
|
||||||
virtual bool shouldSuppressLocationEdits() { return false; }
|
virtual bool shouldSuppressLocationEdits() { return false; }
|
||||||
|
|
||||||
|
virtual void prepareForPhysicsSimulation() { }
|
||||||
|
|
||||||
// these look in the arguments map for a named argument. if it's not found or isn't well formed,
|
// these look in the arguments map for a named argument. if it's not found or isn't well formed,
|
||||||
// ok will be set to false (note that it's never set to true -- set it to true before calling these).
|
// ok will be set to false (note that it's never set to true -- set it to true before calling these).
|
||||||
// if required is true, failure to extract an argument will cause a warning to be printed.
|
// if required is true, failure to extract an argument will cause a warning to be printed.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <ByteCountCoding.h>
|
#include <ByteCountCoding.h>
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
|
#include <glm/gtx/transform.hpp>
|
||||||
|
|
||||||
#include "EntitiesLogging.h"
|
#include "EntitiesLogging.h"
|
||||||
#include "EntityItemProperties.h"
|
#include "EntityItemProperties.h"
|
||||||
|
@ -243,13 +244,22 @@ void ModelEntityItem::getAnimationFrame(bool& newFrame,
|
||||||
|
|
||||||
_lastKnownFrameDataRotations.resize(_jointMapping.size());
|
_lastKnownFrameDataRotations.resize(_jointMapping.size());
|
||||||
_lastKnownFrameDataTranslations.resize(_jointMapping.size());
|
_lastKnownFrameDataTranslations.resize(_jointMapping.size());
|
||||||
|
|
||||||
for (int j = 0; j < _jointMapping.size(); j++) {
|
for (int j = 0; j < _jointMapping.size(); j++) {
|
||||||
int index = _jointMapping[j];
|
int index = _jointMapping[j];
|
||||||
if (index != -1 && index < rotations.size()) {
|
if (index >= 0) {
|
||||||
_lastKnownFrameDataRotations[j] = fbxJoints[index].preRotation * rotations[index];
|
glm::mat4 translationMat;
|
||||||
}
|
if (index < translations.size()) {
|
||||||
if (index != -1 && index < translations.size()) {
|
translationMat = glm::translate(translations[index]);
|
||||||
_lastKnownFrameDataTranslations[j] = translations[index];
|
}
|
||||||
|
glm::mat4 rotationMat;
|
||||||
|
if (index < rotations.size()) {
|
||||||
|
rotationMat = glm::mat4_cast(rotations[index]);
|
||||||
|
}
|
||||||
|
glm::mat4 finalMat = (translationMat * fbxJoints[index].preTransform *
|
||||||
|
rotationMat * fbxJoints[index].postTransform);
|
||||||
|
_lastKnownFrameDataTranslations[j] = extractTranslation(finalMat);
|
||||||
|
_lastKnownFrameDataRotations[j] = glmExtractRotation(finalMat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -320,10 +320,13 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) {
|
||||||
void OffscreenQmlSurface::resize(const QSize& newSize) {
|
void OffscreenQmlSurface::resize(const QSize& newSize) {
|
||||||
|
|
||||||
if (!_renderer || !_renderer->_quickWindow) {
|
if (!_renderer || !_renderer->_quickWindow) {
|
||||||
QSize currentSize = _renderer->_quickWindow->geometry().size();
|
return;
|
||||||
if (newSize == currentSize) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
QSize currentSize = _renderer->_quickWindow->geometry().size();
|
||||||
|
if (newSize == currentSize) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize);
|
_qmlEngine->rootContext()->setContextProperty("surfaceSize", newSize);
|
||||||
|
@ -437,7 +440,9 @@ void OffscreenQmlSurface::updateQuick() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_render) {
|
if (_render) {
|
||||||
|
QMutexLocker lock(&(_renderer->_mutex));
|
||||||
_renderer->post(RENDER);
|
_renderer->post(RENDER);
|
||||||
|
_renderer->_cond.wait(&(_renderer->_mutex));
|
||||||
_render = false;
|
_render = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,8 +40,8 @@ public:
|
||||||
void create(QOpenGLContext* context);
|
void create(QOpenGLContext* context);
|
||||||
void resize(const QSize& size);
|
void resize(const QSize& size);
|
||||||
QSize size() const;
|
QSize size() const;
|
||||||
QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
Q_INVOKABLE QObject* load(const QUrl& qmlSource, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {});
|
||||||
QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {
|
Q_INVOKABLE QObject* load(const QString& qmlSourceFile, std::function<void(QQmlContext*, QObject*)> f = [](QQmlContext*, QObject*) {}) {
|
||||||
return load(QUrl(qmlSourceFile), f);
|
return load(QUrl(qmlSourceFile), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,11 @@ void QOpenGLContextWrapper::doneCurrent() {
|
||||||
_context->doneCurrent();
|
_context->doneCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QOpenGLContextWrapper::setShareContext(QOpenGLContext* otherContext) {
|
||||||
|
_context->setShareContext(otherContext);
|
||||||
|
}
|
||||||
|
|
||||||
bool isCurrentContext(QOpenGLContext* context) {
|
bool isCurrentContext(QOpenGLContext* context) {
|
||||||
return QOpenGLContext::currentContext() == context;
|
return QOpenGLContext::currentContext() == context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,12 @@ public:
|
||||||
void swapBuffers(QSurface* surface);
|
void swapBuffers(QSurface* surface);
|
||||||
bool makeCurrent(QSurface* surface);
|
bool makeCurrent(QSurface* surface);
|
||||||
void doneCurrent();
|
void doneCurrent();
|
||||||
|
void setShareContext(QOpenGLContext* otherContext);
|
||||||
|
|
||||||
|
QOpenGLContext* getContext() {
|
||||||
|
return _context;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QOpenGLContext* _context { nullptr };
|
QOpenGLContext* _context { nullptr };
|
||||||
|
|
|
@ -91,7 +91,7 @@ Assignment::Assignment(ReceivedMessage& message) :
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
Assignment::Assignment(const Assignment& otherAssignment) {
|
Assignment::Assignment(const Assignment& otherAssignment) : QObject() {
|
||||||
_uuid = otherAssignment._uuid;
|
_uuid = otherAssignment._uuid;
|
||||||
_command = otherAssignment._command;
|
_command = otherAssignment._command;
|
||||||
_type = otherAssignment._type;
|
_type = otherAssignment._type;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
#include "BulletUtil.h"
|
#include "BulletUtil.h"
|
||||||
#include "PhysicsCollisionGroups.h"
|
#include "PhysicsCollisionGroups.h"
|
||||||
|
#include "ObjectMotionState.h"
|
||||||
|
|
||||||
const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
|
const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f);
|
||||||
const float JUMP_SPEED = 3.5f;
|
const float JUMP_SPEED = 3.5f;
|
||||||
|
@ -379,3 +380,15 @@ void CharacterController::preSimulation() {
|
||||||
void CharacterController::postSimulation() {
|
void CharacterController::postSimulation() {
|
||||||
// postSimulation() exists for symmetry and just in case we need to do something here later
|
// postSimulation() exists for symmetry and just in case we need to do something here later
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CharacterController::getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation) {
|
||||||
|
if (!_rigidBody) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const btTransform& worldTrans = _rigidBody->getCenterOfMassTransform();
|
||||||
|
avatarRigidBodyPosition = bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset();
|
||||||
|
avatarRigidBodyRotation = bulletToGLM(worldTrans.getRotation());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -79,6 +79,8 @@ public:
|
||||||
void setEnabled(bool enabled);
|
void setEnabled(bool enabled);
|
||||||
bool isEnabled() const { return _enabled && _dynamicsWorld; }
|
bool isEnabled() const { return _enabled && _dynamicsWorld; }
|
||||||
|
|
||||||
|
bool getRigidBodyLocation(glm::vec3& avatarRigidBodyPosition, glm::quat& avatarRigidBodyRotation);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateUpAxis(const glm::quat& rotation);
|
void updateUpAxis(const glm::quat& rotation);
|
||||||
|
|
||||||
|
|
|
@ -29,12 +29,12 @@ public:
|
||||||
ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
|
ObjectAction(EntityActionType type, const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
virtual ~ObjectAction();
|
virtual ~ObjectAction();
|
||||||
|
|
||||||
virtual void removeFromSimulation(EntitySimulation* simulation) const;
|
virtual void removeFromSimulation(EntitySimulation* simulation) const override;
|
||||||
virtual EntityItemWeakPointer getOwnerEntity() const { return _ownerEntity; }
|
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) { _ownerEntity = ownerEntity; }
|
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; }
|
||||||
|
|
||||||
virtual bool updateArguments(QVariantMap arguments);
|
virtual bool updateArguments(QVariantMap arguments) override;
|
||||||
virtual QVariantMap getArguments();
|
virtual QVariantMap getArguments() override;
|
||||||
|
|
||||||
// this is called from updateAction and should be overridden by subclasses
|
// this is called from updateAction and should be overridden by subclasses
|
||||||
virtual void updateActionWorker(float deltaTimeStep) = 0;
|
virtual void updateActionWorker(float deltaTimeStep) = 0;
|
||||||
|
@ -43,25 +43,25 @@ public:
|
||||||
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
|
virtual void updateAction(btCollisionWorld* collisionWorld, btScalar deltaTimeStep);
|
||||||
virtual void debugDraw(btIDebugDraw* debugDrawer);
|
virtual void debugDraw(btIDebugDraw* debugDrawer);
|
||||||
|
|
||||||
virtual QByteArray serialize() const = 0;
|
virtual QByteArray serialize() const override = 0;
|
||||||
virtual void deserialize(QByteArray serializedArguments) = 0;
|
virtual void deserialize(QByteArray serializedArguments) override = 0;
|
||||||
|
|
||||||
virtual bool lifetimeIsOver();
|
virtual bool lifetimeIsOver() override;
|
||||||
virtual quint64 getExpires() { return _expires; }
|
virtual quint64 getExpires() override { return _expires; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
quint64 localTimeToServerTime(quint64 timeValue) const;
|
quint64 localTimeToServerTime(quint64 timeValue) const;
|
||||||
quint64 serverTimeToLocalTime(quint64 timeValue) const;
|
quint64 serverTimeToLocalTime(quint64 timeValue) const;
|
||||||
|
|
||||||
virtual btRigidBody* getRigidBody();
|
virtual btRigidBody* getRigidBody();
|
||||||
virtual glm::vec3 getPosition();
|
virtual glm::vec3 getPosition() override;
|
||||||
virtual void setPosition(glm::vec3 position);
|
virtual void setPosition(glm::vec3 position) override;
|
||||||
virtual glm::quat getRotation();
|
virtual glm::quat getRotation() override;
|
||||||
virtual void setRotation(glm::quat rotation);
|
virtual void setRotation(glm::quat rotation) override;
|
||||||
virtual glm::vec3 getLinearVelocity();
|
virtual glm::vec3 getLinearVelocity() override;
|
||||||
virtual void setLinearVelocity(glm::vec3 linearVelocity);
|
virtual void setLinearVelocity(glm::vec3 linearVelocity) override;
|
||||||
virtual glm::vec3 getAngularVelocity();
|
virtual glm::vec3 getAngularVelocity() override;
|
||||||
virtual void setAngularVelocity(glm::vec3 angularVelocity);
|
virtual void setAngularVelocity(glm::vec3 angularVelocity) override;
|
||||||
virtual void activateBody();
|
virtual void activateBody();
|
||||||
virtual void forceBodyNonStatic();
|
virtual void forceBodyNonStatic();
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,13 @@ public:
|
||||||
ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity);
|
ObjectActionOffset(const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
virtual ~ObjectActionOffset();
|
virtual ~ObjectActionOffset();
|
||||||
|
|
||||||
virtual bool updateArguments(QVariantMap arguments);
|
virtual bool updateArguments(QVariantMap arguments) override;
|
||||||
virtual QVariantMap getArguments();
|
virtual QVariantMap getArguments() override;
|
||||||
|
|
||||||
virtual void updateActionWorker(float deltaTimeStep);
|
virtual void updateActionWorker(float deltaTimeStep) override;
|
||||||
|
|
||||||
virtual QByteArray serialize() const;
|
virtual QByteArray serialize() const override;
|
||||||
virtual void deserialize(QByteArray serializedArguments);
|
virtual void deserialize(QByteArray serializedArguments) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const uint16_t offsetVersion;
|
static const uint16_t offsetVersion;
|
||||||
|
|
|
@ -19,13 +19,13 @@ public:
|
||||||
ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity);
|
ObjectActionSpring(const QUuid& id, EntityItemPointer ownerEntity);
|
||||||
virtual ~ObjectActionSpring();
|
virtual ~ObjectActionSpring();
|
||||||
|
|
||||||
virtual bool updateArguments(QVariantMap arguments);
|
virtual bool updateArguments(QVariantMap arguments) override;
|
||||||
virtual QVariantMap getArguments();
|
virtual QVariantMap getArguments() override;
|
||||||
|
|
||||||
virtual void updateActionWorker(float deltaTimeStep);
|
virtual void updateActionWorker(float deltaTimeStep) override;
|
||||||
|
|
||||||
virtual QByteArray serialize() const;
|
virtual QByteArray serialize() const override;
|
||||||
virtual void deserialize(QByteArray serializedArguments);
|
virtual void deserialize(QByteArray serializedArguments) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const uint16_t springVersion;
|
static const uint16_t springVersion;
|
||||||
|
|
|
@ -117,6 +117,7 @@ void PhysicalEntitySimulation::clearEntitiesInternal() {
|
||||||
_pendingRemoves.clear();
|
_pendingRemoves.clear();
|
||||||
_pendingAdds.clear();
|
_pendingAdds.clear();
|
||||||
_pendingChanges.clear();
|
_pendingChanges.clear();
|
||||||
|
_outgoingChanges.clear();
|
||||||
}
|
}
|
||||||
// end EntitySimulation overrides
|
// end EntitySimulation overrides
|
||||||
|
|
||||||
|
|
|
@ -497,3 +497,11 @@ void PhysicsEngine::removeAction(const QUuid actionID) {
|
||||||
_objectActions.remove(actionID);
|
_objectActions.remove(actionID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicsEngine::forEachAction(std::function<void(EntityActionPointer)> actor) {
|
||||||
|
QHashIterator<QUuid, EntityActionPointer> iter(_objectActions);
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
iter.next();
|
||||||
|
actor(iter.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -97,6 +97,7 @@ public:
|
||||||
EntityActionPointer getActionByID(const QUuid& actionID) const;
|
EntityActionPointer getActionByID(const QUuid& actionID) const;
|
||||||
void addAction(EntityActionPointer action);
|
void addAction(EntityActionPointer action);
|
||||||
void removeAction(const QUuid actionID);
|
void removeAction(const QUuid actionID);
|
||||||
|
void forEachAction(std::function<void(EntityActionPointer)> actor);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void removeContacts(ObjectMotionState* motionState);
|
void removeContacts(ObjectMotionState* motionState);
|
||||||
|
|
|
@ -22,6 +22,10 @@ quat Quat::normalize(const glm::quat& q) {
|
||||||
return glm::normalize(q);
|
return glm::normalize(q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quat Quat::conjugate(const glm::quat& q) {
|
||||||
|
return glm::conjugate(q);
|
||||||
|
}
|
||||||
|
|
||||||
glm::quat Quat::rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
glm::quat Quat::rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
|
||||||
return ::rotationBetween(v1, v2);
|
return ::rotationBetween(v1, v2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ class Quat : public QObject {
|
||||||
public slots:
|
public slots:
|
||||||
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
|
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
|
||||||
glm::quat normalize(const glm::quat& q);
|
glm::quat normalize(const glm::quat& q);
|
||||||
|
glm::quat conjugate(const glm::quat& q);
|
||||||
glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up);
|
glm::quat lookAt(const glm::vec3& eye, const glm::vec3& center, const glm::vec3& up);
|
||||||
glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center);
|
glm::quat lookAtSimple(const glm::vec3& eye, const glm::vec3& center);
|
||||||
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
|
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
set(TARGET_NAME ui)
|
set(TARGET_NAME ui)
|
||||||
setup_hifi_library(OpenGL Network Qml Quick Script XmlPatterns)
|
setup_hifi_library(OpenGL Network Qml Quick Script WebChannel WebSockets XmlPatterns)
|
||||||
link_hifi_libraries(shared networking gl)
|
link_hifi_libraries(shared networking gl)
|
||||||
|
|
240
libraries/ui/src/QmlWebWindowClass.cpp
Normal file
240
libraries/ui/src/QmlWebWindowClass.cpp
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2015-12-15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "QmlWebWindowClass.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtScript/QScriptContext>
|
||||||
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
|
|
||||||
|
#include <QtWebSockets/QWebSocketServer>
|
||||||
|
#include <QtWebSockets/QWebSocket>
|
||||||
|
#include <QtWebChannel/QWebChannel>
|
||||||
|
#include <QtCore/QJsonDocument>
|
||||||
|
#include <QtCore/QJsonObject>
|
||||||
|
|
||||||
|
#include <AddressManager.h>
|
||||||
|
#include <DependencyManager.h>
|
||||||
|
|
||||||
|
#include "OffscreenUi.h"
|
||||||
|
|
||||||
|
QWebSocketServer* QmlWebWindowClass::_webChannelServer { nullptr };
|
||||||
|
static QWebChannel webChannel;
|
||||||
|
static const uint16_t WEB_CHANNEL_PORT = 51016;
|
||||||
|
static std::atomic<int> nextWindowId;
|
||||||
|
static const char* const URL_PROPERTY = "source";
|
||||||
|
static const char* const TITLE_PROPERTY = "title";
|
||||||
|
static const QRegExp HIFI_URL_PATTERN { "^hifi://" };
|
||||||
|
|
||||||
|
void QmlScriptEventBridge::emitWebEvent(const QString& data) {
|
||||||
|
QMetaObject::invokeMethod(this, "webEventReceived", Qt::QueuedConnection, Q_ARG(QString, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlScriptEventBridge::emitScriptEvent(const QString& data) {
|
||||||
|
QMetaObject::invokeMethod(this, "scriptEventReceived", Qt::QueuedConnection,
|
||||||
|
Q_ARG(int, _webWindow->getWindowId()), Q_ARG(QString, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
class QmlWebTransport : public QWebChannelAbstractTransport {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
QmlWebTransport(QWebSocket* webSocket) : _webSocket(webSocket) {
|
||||||
|
// Translate from the websocket layer to the webchannel layer
|
||||||
|
connect(webSocket, &QWebSocket::textMessageReceived, [this](const QString& message) {
|
||||||
|
QJsonParseError error;
|
||||||
|
QJsonDocument document = QJsonDocument::fromJson(message.toUtf8(), &error);
|
||||||
|
if (error.error || !document.isObject()) {
|
||||||
|
qWarning() << "Unable to parse incoming JSON message" << message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit messageReceived(document.object(), this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void sendMessage(const QJsonObject &message) override {
|
||||||
|
// Translate from the webchannel layer to the websocket layer
|
||||||
|
_webSocket->sendTextMessage(QJsonDocument(message).toJson(QJsonDocument::Compact));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QWebSocket* const _webSocket;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setupServer() {
|
||||||
|
if (!_webChannelServer) {
|
||||||
|
_webChannelServer = new QWebSocketServer("EventBridge Server", QWebSocketServer::NonSecureMode);
|
||||||
|
if (!_webChannelServer->listen(QHostAddress::LocalHost, WEB_CHANNEL_PORT)) {
|
||||||
|
qFatal("Failed to open web socket server.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject::connect(_webChannelServer, &QWebSocketServer::newConnection, [] {
|
||||||
|
webChannel.connectTo(new QmlWebTransport(_webChannelServer->nextPendingConnection()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method called by Qt scripts to create a new web window in the overlay
|
||||||
|
QScriptValue QmlWebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) {
|
||||||
|
QmlWebWindowClass* retVal { nullptr };
|
||||||
|
const QString title = context->argument(0).toString();
|
||||||
|
QString url = context->argument(1).toString();
|
||||||
|
if (!url.startsWith("http") && !url.startsWith("file://")) {
|
||||||
|
url = QUrl::fromLocalFile(url).toString();
|
||||||
|
}
|
||||||
|
const int width = std::max(100, std::min(1280, context->argument(2).toInt32()));;
|
||||||
|
const int height = std::max(100, std::min(720, context->argument(3).toInt32()));;
|
||||||
|
|
||||||
|
// Build the event bridge and wrapper on the main thread
|
||||||
|
QMetaObject::invokeMethod(DependencyManager::get<OffscreenUi>().data(), "load", Qt::BlockingQueuedConnection,
|
||||||
|
Q_ARG(const QString&, "QmlWebWindow.qml"),
|
||||||
|
Q_ARG(std::function<void(QQmlContext*, QObject*)>, [&](QQmlContext* context, QObject* object) {
|
||||||
|
setupServer();
|
||||||
|
retVal = new QmlWebWindowClass(object);
|
||||||
|
webChannel.registerObject(url.toLower(), retVal);
|
||||||
|
retVal->setTitle(title);
|
||||||
|
retVal->setURL(url);
|
||||||
|
retVal->setSize(width, height);
|
||||||
|
}));
|
||||||
|
connect(engine, &QScriptEngine::destroyed, retVal, &QmlWebWindowClass::deleteLater);
|
||||||
|
return engine->newQObject(retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
QmlWebWindowClass::QmlWebWindowClass(QObject* qmlWindow)
|
||||||
|
: _windowId(++nextWindowId), _qmlWindow(qmlWindow)
|
||||||
|
{
|
||||||
|
qDebug() << "Created window with ID " << _windowId;
|
||||||
|
Q_ASSERT(_qmlWindow);
|
||||||
|
Q_ASSERT(dynamic_cast<const QQuickItem*>(_qmlWindow));
|
||||||
|
QObject::connect(_qmlWindow, SIGNAL(navigating(QString)), this, SLOT(handleNavigation(QString)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::handleNavigation(const QString& url) {
|
||||||
|
DependencyManager::get<AddressManager>()->handleLookupString(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setVisible(bool visible) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setVisible", Qt::AutoConnection, Q_ARG(bool, visible));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto qmlWindow = asQuickItem();
|
||||||
|
if (qmlWindow->isEnabled() != visible) {
|
||||||
|
qmlWindow->setEnabled(visible);
|
||||||
|
emit visibilityChanged(visible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem* QmlWebWindowClass::asQuickItem() const {
|
||||||
|
return dynamic_cast<QQuickItem*>(_qmlWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QmlWebWindowClass::isVisible() const {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
bool result;
|
||||||
|
QMetaObject::invokeMethod(const_cast<QmlWebWindowClass*>(this), "isVisible", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return asQuickItem()->isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
glm::vec2 QmlWebWindowClass::getPosition() const {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
glm::vec2 result;
|
||||||
|
QMetaObject::invokeMethod(const_cast<QmlWebWindowClass*>(this), "getPosition", Qt::BlockingQueuedConnection, Q_RETURN_ARG(glm::vec2, result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return glm::vec2(asQuickItem()->x(), asQuickItem()->y());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setPosition(const glm::vec2& position) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setPosition", Qt::QueuedConnection, Q_ARG(glm::vec2, position));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asQuickItem()->setPosition(QPointF(position.x, position.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setPosition(int x, int y) {
|
||||||
|
setPosition(glm::vec2(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 QmlWebWindowClass::getSize() const {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
glm::vec2 result;
|
||||||
|
QMetaObject::invokeMethod(const_cast<QmlWebWindowClass*>(this), "getSize", Qt::BlockingQueuedConnection, Q_RETURN_ARG(glm::vec2, result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return glm::vec2(asQuickItem()->width(), asQuickItem()->height());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setSize(const glm::vec2& size) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setSize", Qt::QueuedConnection, Q_ARG(glm::vec2, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
asQuickItem()->setSize(QSizeF(size.x, size.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setSize(int width, int height) {
|
||||||
|
setSize(glm::vec2(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QmlWebWindowClass::getURL() const {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QString result;
|
||||||
|
QMetaObject::invokeMethod(const_cast<QmlWebWindowClass*>(this), "getURL", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QString, result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return _qmlWindow->property(URL_PROPERTY).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setURL(const QString& urlString) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setURL", Qt::QueuedConnection, Q_ARG(QString, urlString));
|
||||||
|
}
|
||||||
|
_qmlWindow->setProperty(URL_PROPERTY, urlString);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void QmlWebWindowClass::setTitle(const QString& title) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "setTitle", Qt::QueuedConnection, Q_ARG(QString, title));
|
||||||
|
}
|
||||||
|
|
||||||
|
_qmlWindow->setProperty(TITLE_PROPERTY, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::close() {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
_qmlWindow->setProperty("destroyOnInvisible", true);
|
||||||
|
_qmlWindow->setProperty("visible", false);
|
||||||
|
_qmlWindow->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::hasClosed() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void QmlWebWindowClass::raise() {
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
#include "QmlWebWindowClass.moc"
|
104
libraries/ui/src/QmlWebWindowClass.h
Normal file
104
libraries/ui/src/QmlWebWindowClass.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
// Created by Bradley Austin Davis on 2015-12-15
|
||||||
|
// Copyright 2015 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ui_QmlWebWindowClass_h
|
||||||
|
#define hifi_ui_QmlWebWindowClass_h
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
#include <QtScript/QScriptValue>
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
|
#include <QtWebChannel/QWebChannelAbstractTransport>
|
||||||
|
|
||||||
|
class QScriptEngine;
|
||||||
|
class QScriptContext;
|
||||||
|
class QmlWebWindowClass;
|
||||||
|
class QWebSocketServer;
|
||||||
|
class QWebSocket;
|
||||||
|
|
||||||
|
class QmlScriptEventBridge : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
QmlScriptEventBridge(const QmlWebWindowClass* webWindow) : _webWindow(webWindow) {}
|
||||||
|
|
||||||
|
public slots :
|
||||||
|
void emitWebEvent(const QString& data);
|
||||||
|
void emitScriptEvent(const QString& data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void webEventReceived(const QString& data);
|
||||||
|
void scriptEventReceived(int windowId, const QString& data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QmlWebWindowClass* _webWindow { nullptr };
|
||||||
|
QWebSocket *_socket { nullptr };
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME refactor this class to be a QQuickItem derived type and eliminate the needless wrapping
|
||||||
|
class QmlWebWindowClass : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QObject* eventBridge READ getEventBridge CONSTANT)
|
||||||
|
Q_PROPERTY(int windowId READ getWindowId CONSTANT)
|
||||||
|
Q_PROPERTY(QString url READ getURL CONSTANT)
|
||||||
|
Q_PROPERTY(glm::vec2 position READ getPosition WRITE setPosition)
|
||||||
|
Q_PROPERTY(glm::vec2 size READ getSize WRITE setSize)
|
||||||
|
Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine);
|
||||||
|
QmlWebWindowClass(QObject* qmlWindow);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
bool isVisible() const;
|
||||||
|
void setVisible(bool visible);
|
||||||
|
|
||||||
|
glm::vec2 getPosition() const;
|
||||||
|
void setPosition(const glm::vec2& position);
|
||||||
|
void setPosition(int x, int y);
|
||||||
|
|
||||||
|
glm::vec2 getSize() const;
|
||||||
|
void setSize(const glm::vec2& size);
|
||||||
|
void setSize(int width, int height);
|
||||||
|
|
||||||
|
QString getURL() const;
|
||||||
|
void setURL(const QString& url);
|
||||||
|
|
||||||
|
void setTitle(const QString& title);
|
||||||
|
|
||||||
|
// Ugh.... do not want to do
|
||||||
|
Q_INVOKABLE void raise();
|
||||||
|
Q_INVOKABLE void close();
|
||||||
|
Q_INVOKABLE int getWindowId() const { return _windowId; };
|
||||||
|
Q_INVOKABLE QmlScriptEventBridge* getEventBridge() const { return _eventBridge; };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void visibilityChanged(bool visible); // Tool window
|
||||||
|
void urlChanged();
|
||||||
|
void moved(glm::vec2 position);
|
||||||
|
void resized(QSizeF size);
|
||||||
|
void closed();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void hasClosed();
|
||||||
|
void handleNavigation(const QString& url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void setupServer();
|
||||||
|
static QWebSocketServer* _webChannelServer;
|
||||||
|
|
||||||
|
QQuickItem* asQuickItem() const;
|
||||||
|
QmlScriptEventBridge* const _eventBridge { new QmlScriptEventBridge(this) };
|
||||||
|
|
||||||
|
// FIXME needs to be initialized in the ctor once we have support
|
||||||
|
// for tool window panes in QML
|
||||||
|
const bool _isToolWindow { false };
|
||||||
|
const int _windowId;
|
||||||
|
QObject* const _qmlWindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -191,7 +191,7 @@ void SixenseManager::InputDevice::update(float deltaTime, bool jointsCaptured) {
|
||||||
// Rotation of Palm
|
// Rotation of Palm
|
||||||
glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]);
|
glm::quat rotation(data->rot_quat[3], data->rot_quat[0], data->rot_quat[1], data->rot_quat[2]);
|
||||||
handlePoseEvent(deltaTime, position, rotation, left);
|
handlePoseEvent(deltaTime, position, rotation, left);
|
||||||
rawPoses[i] = controller::Pose(position, rotation, glm::vec3(0), glm::quat());
|
rawPoses[i] = controller::Pose(position, rotation, Vectors::ZERO, Vectors::ZERO);
|
||||||
} else {
|
} else {
|
||||||
_poseStateMap.clear();
|
_poseStateMap.clear();
|
||||||
_collectedSamples.clear();
|
_collectedSamples.clear();
|
||||||
|
@ -457,25 +457,22 @@ void SixenseManager::InputDevice::handlePoseEvent(float deltaTime, glm::vec3 pos
|
||||||
rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
|
rotation = _avatarRotation * postOffset * glm::inverse(sixenseToHand) * rotation * preOffset * sixenseToHand;
|
||||||
|
|
||||||
glm::vec3 velocity(0.0f);
|
glm::vec3 velocity(0.0f);
|
||||||
glm::quat angularVelocity;
|
glm::vec3 angularVelocity(0.0f);
|
||||||
|
|
||||||
if (prevPose.isValid() && deltaTime > std::numeric_limits<float>::epsilon()) {
|
if (prevPose.isValid() && deltaTime > std::numeric_limits<float>::epsilon()) {
|
||||||
|
auto& samples = _collectedSamples[hand];
|
||||||
|
|
||||||
velocity = (position - prevPose.getTranslation()) / deltaTime;
|
velocity = (position - prevPose.getTranslation()) / deltaTime;
|
||||||
|
|
||||||
auto deltaRot = rotation * glm::conjugate(prevPose.getRotation());
|
|
||||||
auto axis = glm::axis(deltaRot);
|
|
||||||
auto angle = glm::angle(deltaRot);
|
|
||||||
angularVelocity = glm::angleAxis(angle / deltaTime, axis);
|
|
||||||
|
|
||||||
// Average
|
|
||||||
auto& samples = _collectedSamples[hand];
|
|
||||||
samples.first.addSample(velocity);
|
samples.first.addSample(velocity);
|
||||||
velocity = samples.first.average;
|
velocity = samples.first.average;
|
||||||
|
|
||||||
// FIXME: // Not using quaternion average yet for angular velocity because it s probably wrong but keep the MovingAverage in place
|
auto deltaRot = glm::normalize(rotation * glm::conjugate(prevPose.getRotation()));
|
||||||
//samples.second.addSample(glm::vec4(angularVelocity.x, angularVelocity.y, angularVelocity.z, angularVelocity.w));
|
auto axis = glm::axis(deltaRot);
|
||||||
//angularVelocity = glm::quat(samples.second.average.w, samples.second.average.x, samples.second.average.y, samples.second.average.z);
|
auto speed = glm::angle(deltaRot) / deltaTime;
|
||||||
|
assert(!glm::isnan(speed));
|
||||||
|
angularVelocity = speed * axis;
|
||||||
|
samples.second.addSample(angularVelocity);
|
||||||
|
angularVelocity = samples.second.average;
|
||||||
} else if (!prevPose.isValid()) {
|
} else if (!prevPose.isValid()) {
|
||||||
_collectedSamples[hand].first.clear();
|
_collectedSamples[hand].first.clear();
|
||||||
_collectedSamples[hand].second.clear();
|
_collectedSamples[hand].second.clear();
|
||||||
|
|
|
@ -54,7 +54,7 @@ private:
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
using SampleAverage = MovingAverage<T, MAX_NUM_AVERAGING_SAMPLES>;
|
using SampleAverage = MovingAverage<T, MAX_NUM_AVERAGING_SAMPLES>;
|
||||||
using Samples = std::pair<SampleAverage<glm::vec3>, SampleAverage<glm::vec4>>;
|
using Samples = std::pair<SampleAverage<glm::vec3>, SampleAverage<glm::vec3>>;
|
||||||
using MovingAverageMap = std::map<int, Samples>;
|
using MovingAverageMap = std::map<int, Samples>;
|
||||||
|
|
||||||
class InputDevice : public controller::InputDevice {
|
class InputDevice : public controller::InputDevice {
|
||||||
|
|
|
@ -2,15 +2,32 @@
|
||||||
set(TARGET_NAME "ui-test")
|
set(TARGET_NAME "ui-test")
|
||||||
|
|
||||||
# This is not a testcase -- just set it up as a regular hifi project
|
# This is not a testcase -- just set it up as a regular hifi project
|
||||||
setup_hifi_project(Widgets OpenGL Network Qml Quick Script)
|
setup_hifi_project(Network OpenGL Qml Quick Script WebChannel WebEngine WebSockets)
|
||||||
|
|
||||||
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(${TARGET_NAME} wsock32.lib opengl32.lib Winmm.lib)
|
target_link_libraries(${TARGET_NAME} wsock32.lib opengl32.lib Winmm.lib)
|
||||||
|
# Issue causes build failure unless we add this directory.
|
||||||
|
# See https://bugreports.qt.io/browse/QTBUG-43351
|
||||||
|
add_paths_to_fixup_libs(${Qt5_DIR}/../../../plugins/qtwebengine)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# link in the shared libraries
|
# link in the shared libraries
|
||||||
link_hifi_libraries(shared networking gl gpu ui)
|
link_hifi_libraries(shared networking gl gpu ui)
|
||||||
|
|
||||||
|
# copy the resources files beside the executable
|
||||||
|
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy_directory
|
||||||
|
"${PROJECT_SOURCE_DIR}/qml"
|
||||||
|
$<TARGET_FILE_DIR:${TARGET_NAME}>/qml
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
target_glew()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set(EXTRA_DEPLOY_OPTIONS "--qmldir ${PROJECT_SOURCE_DIR}/../../interface/resources/qml")
|
||||||
|
endif()
|
||||||
|
|
||||||
package_libraries_for_deployment()
|
package_libraries_for_deployment()
|
||||||
|
|
|
@ -1,41 +1,91 @@
|
||||||
//
|
//
|
||||||
// main.cpp
|
// Created by Bradley Austin Davis on 2015-04-22
|
||||||
// tests/render-utils/src
|
// Copyright 2013-2015 High Fidelity, Inc.
|
||||||
//
|
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "OffscreenUi.h"
|
#include <gl/Config.h>
|
||||||
#include <QWindow>
|
#include <gl/OglplusHelpers.h>
|
||||||
#include <QFile>
|
#include <gl/GLHelpers.h>
|
||||||
#include <QTime>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QElapsedTimer>
|
|
||||||
#include <QOpenGLContext>
|
|
||||||
#include <QOpenGLBuffer>
|
|
||||||
#include <QOpenGLShaderProgram>
|
|
||||||
#include <QResizeEvent>
|
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QOpenGLTexture>
|
|
||||||
#include <QOpenGLVertexArrayObject>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QOpenGLDebugLogger>
|
|
||||||
#include <QOpenGLFunctions>
|
|
||||||
#include <QQmlContext>
|
|
||||||
#include <QtQml/QQmlApplicationEngine>
|
|
||||||
#include <PathUtils.h>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QDir>
|
||||||
|
#include <QtCore/QTime>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtCore/QElapsedTimer>
|
||||||
|
#include <QtCore/QLoggingCategory>
|
||||||
|
#include <QtCore/QThread>
|
||||||
|
#include <QtCore/QUuid>
|
||||||
|
|
||||||
|
#include <QtGui/QWindow>
|
||||||
|
#include <QtGui/QImage>
|
||||||
|
#include <QtGui/QGuiApplication>
|
||||||
|
#include <QtGui/QResizeEvent>
|
||||||
|
#include <QtGui/QScreen>
|
||||||
|
|
||||||
|
#include <gl/QOpenGLContextWrapper.h>
|
||||||
|
|
||||||
|
#include <QtScript/QScriptEngine>
|
||||||
|
|
||||||
|
#include <QtQml/QQmlContext>
|
||||||
|
#include <QtQml/QQmlApplicationEngine>
|
||||||
|
|
||||||
|
#include <GLMHelpers.h>
|
||||||
|
#include <gl/OffscreenGLCanvas.h>
|
||||||
|
#include <OffscreenUi.h>
|
||||||
#include <PathUtils.h>
|
#include <PathUtils.h>
|
||||||
#include <QDir>
|
#include <PathUtils.h>
|
||||||
#include "MessageDialog.h"
|
#include <MessageDialog.h>
|
||||||
#include "VrMenu.h"
|
#include <VrMenu.h>
|
||||||
#include "InfoView.h"
|
#include <InfoView.h>
|
||||||
#include <QDesktopWidget>
|
#include <QmlWebWindowClass.h>
|
||||||
|
#include <RegisteredMetaTypes.h>
|
||||||
|
|
||||||
|
const QString& getResourcesDir() {
|
||||||
|
static QString dir;
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
QDir path(__FILE__);
|
||||||
|
path.cdUp();
|
||||||
|
dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/";
|
||||||
|
qDebug() << "Resources Path: " << dir;
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& getExamplesDir() {
|
||||||
|
static QString dir;
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
QDir path(__FILE__);
|
||||||
|
path.cdUp();
|
||||||
|
dir = path.cleanPath(path.absoluteFilePath("../../../examples/")) + "/";
|
||||||
|
qDebug() << "Resources Path: " << dir;
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& getInterfaceQmlDir() {
|
||||||
|
static QString dir;
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
dir = getResourcesDir() + "qml/";
|
||||||
|
qDebug() << "Qml Path: " << dir;
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& getTestQmlDir() {
|
||||||
|
static QString dir;
|
||||||
|
if (dir.isEmpty()) {
|
||||||
|
QDir path(__FILE__);
|
||||||
|
path.cdUp();
|
||||||
|
dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
|
||||||
|
qDebug() << "Qml Test Path: " << dir;
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RateCounter {
|
class RateCounter {
|
||||||
std::vector<float> times;
|
std::vector<float> times;
|
||||||
|
@ -74,142 +124,394 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class MenuConstants : public QObject{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_ENUMS(Item)
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum Item {
|
|
||||||
RenderLookAtTargets,
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
extern QOpenGLContext* qt_gl_global_share_context();
|
||||||
MenuConstants(QObject* parent = nullptr) : QObject(parent) {
|
|
||||||
|
|
||||||
|
|
||||||
|
static bool hadUncaughtExceptions(QScriptEngine& engine, const QString& fileName) {
|
||||||
|
if (engine.hasUncaughtException()) {
|
||||||
|
const auto backtrace = engine.uncaughtExceptionBacktrace();
|
||||||
|
const auto exception = engine.uncaughtException().toString();
|
||||||
|
const auto line = QString::number(engine.uncaughtExceptionLineNumber());
|
||||||
|
engine.clearExceptions();
|
||||||
|
|
||||||
|
auto message = QString("[UncaughtException] %1 in %2:%3").arg(exception, fileName, line);
|
||||||
|
if (!backtrace.empty()) {
|
||||||
|
static const auto lineSeparator = "\n ";
|
||||||
|
message += QString("\n[Backtrace]%1%2").arg(lineSeparator, backtrace.join(lineSeparator));
|
||||||
|
}
|
||||||
|
qWarning() << qPrintable(message);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0f / 60.0f) * 1000 * 1000) + 0.5f);
|
||||||
|
|
||||||
|
static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine) {
|
||||||
|
QString message = "";
|
||||||
|
for (int i = 0; i < context->argumentCount(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
message += " ";
|
||||||
|
}
|
||||||
|
message += context->argument(i).toString();
|
||||||
|
}
|
||||||
|
qDebug().noquote() << "script:print()<<" << message; // noquote() so that \n is treated as newline
|
||||||
|
|
||||||
|
message = message.replace("\\", "\\\\")
|
||||||
|
.replace("\n", "\\n")
|
||||||
|
.replace("\r", "\\r")
|
||||||
|
.replace("'", "\\'");
|
||||||
|
engine->evaluate("Script.print('" + message + "')");
|
||||||
|
|
||||||
|
return QScriptValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ScriptEngine : public QScriptEngine {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
void loadFile(const QString& scriptPath) {
|
||||||
|
if (_isRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "Loading script from " << scriptPath;
|
||||||
|
_fileNameString = scriptPath;
|
||||||
|
|
||||||
|
QFile file(scriptPath);
|
||||||
|
if (file.exists()) {
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
_scriptContents = file.readAll();
|
||||||
|
} else {
|
||||||
|
qFatal("Missing file ");
|
||||||
|
}
|
||||||
|
runInThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void stop() {
|
||||||
|
if (!_isFinished) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "stop");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_isFinished = true;
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit runningStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE void print(const QString& message) {
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit printedMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QObject* setupTimerWithInterval(const QScriptValue& function, int intervalMS, bool isSingleShot) {
|
||||||
|
// create the timer, add it to the map, and start it
|
||||||
|
QTimer* newTimer = new QTimer(this);
|
||||||
|
newTimer->setSingleShot(isSingleShot);
|
||||||
|
|
||||||
|
connect(newTimer, &QTimer::timeout, this, &ScriptEngine::timerFired);
|
||||||
|
|
||||||
|
// make sure the timer stops when the script does
|
||||||
|
connect(this, &ScriptEngine::scriptEnding, newTimer, &QTimer::stop);
|
||||||
|
|
||||||
|
_timerFunctionMap.insert(newTimer, function);
|
||||||
|
|
||||||
|
newTimer->start(intervalMS);
|
||||||
|
return newTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QObject* setInterval(const QScriptValue& function, int intervalMS) {
|
||||||
|
return setupTimerWithInterval(function, intervalMS, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_INVOKABLE QObject* setTimeout(const QScriptValue& function, int timeoutMS) {
|
||||||
|
return setupTimerWithInterval(function, timeoutMS, true);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
|
||||||
|
void runInThread() {
|
||||||
|
QThread* workerThread = new QThread();
|
||||||
|
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
|
||||||
|
connect(workerThread, &QThread::started, this, &ScriptEngine::run);
|
||||||
|
connect(workerThread, &QThread::finished, this, &ScriptEngine::deleteLater);
|
||||||
|
connect(this, &ScriptEngine::doneRunning, workerThread, &QThread::quit);
|
||||||
|
moveToThread(workerThread);
|
||||||
|
workerThread->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
_isInitialized = true;
|
||||||
|
registerMetaTypes(this);
|
||||||
|
registerGlobalObject("Script", this);
|
||||||
|
qScriptRegisterSequenceMetaType<QVector<QUuid>>(this);
|
||||||
|
qScriptRegisterSequenceMetaType<QVector<QString>>(this);
|
||||||
|
globalObject().setProperty("OverlayWebWindow", newFunction(QmlWebWindowClass::constructor));
|
||||||
|
QScriptValue printConstructorValue = newFunction(debugPrint);
|
||||||
|
globalObject().setProperty("print", printConstructorValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerFired() {
|
||||||
|
QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
|
||||||
|
QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
|
||||||
|
|
||||||
|
if (!callingTimer->isActive()) {
|
||||||
|
// this timer is done, we can kill it
|
||||||
|
_timerFunctionMap.remove(callingTimer);
|
||||||
|
delete callingTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call the associated JS function, if it exists
|
||||||
|
if (timerFunction.isValid()) {
|
||||||
|
timerFunction.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void run() {
|
||||||
|
if (!_isInitialized) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
_isRunning = true;
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit runningStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue result = evaluate(_scriptContents, _fileNameString);
|
||||||
|
QElapsedTimer startTime;
|
||||||
|
startTime.start();
|
||||||
|
|
||||||
|
int thisFrame = 0;
|
||||||
|
|
||||||
|
qint64 lastUpdate = usecTimestampNow();
|
||||||
|
|
||||||
|
while (!_isFinished) {
|
||||||
|
int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec
|
||||||
|
if (usecToSleep > 0) {
|
||||||
|
usleep(usecToSleep);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isFinished) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
if (_isFinished) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 now = usecTimestampNow();
|
||||||
|
float deltaTime = (float)(now - lastUpdate) / (float)USECS_PER_SECOND;
|
||||||
|
if (!_isFinished) {
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit update(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastUpdate = now;
|
||||||
|
|
||||||
|
// Debug and clear exceptions
|
||||||
|
hadUncaughtExceptions(*this, _fileNameString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit scriptEnding();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit finished(_fileNameString, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_isRunning = false;
|
||||||
|
|
||||||
|
if (_wantSignals) {
|
||||||
|
emit runningStateChanged();
|
||||||
|
emit doneRunning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerGlobalObject(const QString& name, QObject* object) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "registerGlobalObject",
|
||||||
|
Q_ARG(const QString&, name),
|
||||||
|
Q_ARG(QObject*, object));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!globalObject().property(name).isValid()) {
|
||||||
|
if (object) {
|
||||||
|
QScriptValue value = newQObject(object);
|
||||||
|
globalObject().setProperty(name, value);
|
||||||
|
} else {
|
||||||
|
globalObject().setProperty(name, QScriptValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerFunction(const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "registerFunction",
|
||||||
|
Q_ARG(const QString&, name),
|
||||||
|
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
|
||||||
|
Q_ARG(int, numArguments));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QScriptValue scriptFun = newFunction(functionSignature, numArguments);
|
||||||
|
globalObject().setProperty(name, scriptFun);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerFunction(const QString& parent, const QString& name, QScriptEngine::FunctionSignature functionSignature, int numArguments) {
|
||||||
|
if (QThread::currentThread() != thread()) {
|
||||||
|
QMetaObject::invokeMethod(this, "registerFunction",
|
||||||
|
Q_ARG(const QString&, name),
|
||||||
|
Q_ARG(QScriptEngine::FunctionSignature, functionSignature),
|
||||||
|
Q_ARG(int, numArguments));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptValue object = globalObject().property(parent);
|
||||||
|
if (object.isValid()) {
|
||||||
|
QScriptValue scriptFun = newFunction(functionSignature, numArguments);
|
||||||
|
object.setProperty(name, scriptFun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void scriptLoaded(const QString& scriptFilename);
|
||||||
|
void errorLoadingScript(const QString& scriptFilename);
|
||||||
|
void update(float deltaTime);
|
||||||
|
void scriptEnding();
|
||||||
|
void finished(const QString& fileNameString, ScriptEngine* engine);
|
||||||
|
void cleanupMenuItem(const QString& menuItemString);
|
||||||
|
void printedMessage(const QString& message);
|
||||||
|
void errorMessage(const QString& message);
|
||||||
|
void runningStateChanged();
|
||||||
|
void evaluationFinished(QScriptValue result, bool isException);
|
||||||
|
void loadScript(const QString& scriptName, bool isUserLoaded);
|
||||||
|
void reloadScript(const QString& scriptName, bool isUserLoaded);
|
||||||
|
void doneRunning();
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _scriptContents;
|
||||||
|
QString _fileNameString;
|
||||||
|
QString _parentURL;
|
||||||
|
bool _isInitialized { false };
|
||||||
|
std::atomic<bool> _isFinished { false };
|
||||||
|
std::atomic<bool> _isRunning { false };
|
||||||
|
bool _wantSignals { true };
|
||||||
|
QHash<QTimer*, QScriptValue> _timerFunctionMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
const QString& getResourcesDir() {
|
|
||||||
static QString dir;
|
|
||||||
if (dir.isEmpty()) {
|
ScriptEngine* loadScript(const QString& scriptFilename) {
|
||||||
QDir path(__FILE__);
|
ScriptEngine* scriptEngine = new ScriptEngine();
|
||||||
path.cdUp();
|
scriptEngine->loadFile(scriptFilename);
|
||||||
dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/";
|
return scriptEngine;
|
||||||
qDebug() << "Resources Path: " << dir;
|
|
||||||
}
|
|
||||||
return dir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString& getQmlDir() {
|
OffscreenGLCanvas* _chromiumShareContext { nullptr };
|
||||||
static QString dir;
|
Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context);
|
||||||
if (dir.isEmpty()) {
|
|
||||||
dir = getResourcesDir() + "qml/";
|
|
||||||
qDebug() << "Qml Path: " << dir;
|
|
||||||
}
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString& getTestQmlDir() {
|
|
||||||
static QString dir;
|
|
||||||
if (dir.isEmpty()) {
|
|
||||||
QDir path(__FILE__);
|
|
||||||
path.cdUp();
|
|
||||||
dir = path.cleanPath(path.absoluteFilePath("../")) + "/";
|
|
||||||
qDebug() << "Qml Test Path: " << dir;
|
|
||||||
}
|
|
||||||
return dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a simple OpenGL window that renders text in various ways
|
// Create a simple OpenGL window that renders text in various ways
|
||||||
class QTestWindow : public QWindow, private QOpenGLFunctions {
|
class QTestWindow : public QWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
QOpenGLContext* _context{ nullptr };
|
QOpenGLContextWrapper* _context{ nullptr };
|
||||||
QSize _size;
|
QSize _size;
|
||||||
bool _altPressed{ false };
|
bool _altPressed{ false };
|
||||||
RateCounter fps;
|
RateCounter fps;
|
||||||
QTimer _timer;
|
QTimer _timer;
|
||||||
int testQmlTexture{ 0 };
|
int testQmlTexture{ 0 };
|
||||||
|
ProgramPtr _program;
|
||||||
|
ShapeWrapperPtr _plane;
|
||||||
|
QScriptEngine* _scriptEngine { nullptr };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QObject* rootMenu;
|
QObject* rootMenu;
|
||||||
|
|
||||||
QTestWindow() {
|
QTestWindow() {
|
||||||
|
_scriptEngine = new ScriptEngine();
|
||||||
_timer.setInterval(1);
|
_timer.setInterval(1);
|
||||||
connect(&_timer, &QTimer::timeout, [=] {
|
QObject::connect(&_timer, &QTimer::timeout, this, &QTestWindow::draw);
|
||||||
draw();
|
|
||||||
});
|
|
||||||
|
|
||||||
DependencyManager::set<OffscreenUi>();
|
_chromiumShareContext = new OffscreenGLCanvas();
|
||||||
setSurfaceType(QSurface::OpenGLSurface);
|
_chromiumShareContext->create();
|
||||||
|
_chromiumShareContext->makeCurrent();
|
||||||
|
qt_gl_set_global_share_context(_chromiumShareContext->getContext());
|
||||||
|
|
||||||
QSurfaceFormat format;
|
{
|
||||||
format.setDepthBufferSize(16);
|
setSurfaceType(QSurface::OpenGLSurface);
|
||||||
format.setStencilBufferSize(8);
|
QSurfaceFormat format = getDefaultOpenGLSurfaceFormat();
|
||||||
format.setVersion(4, 1);
|
setFormat(format);
|
||||||
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
|
_context = new QOpenGLContextWrapper();
|
||||||
format.setOption(QSurfaceFormat::DebugContext);
|
_context->setFormat(format);
|
||||||
|
_context->setShareContext(_chromiumShareContext->getContext());
|
||||||
|
}
|
||||||
|
|
||||||
setFormat(format);
|
|
||||||
|
|
||||||
_context = new QOpenGLContext;
|
|
||||||
_context->setFormat(format);
|
|
||||||
if (!_context->create()) {
|
if (!_context->create()) {
|
||||||
qFatal("Could not create OpenGL context");
|
qFatal("Could not create OpenGL context");
|
||||||
}
|
}
|
||||||
|
|
||||||
show();
|
show();
|
||||||
|
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
initializeOpenGLFunctions();
|
|
||||||
|
|
||||||
{
|
glewExperimental = true;
|
||||||
QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(this);
|
glewInit();
|
||||||
logger->initialize(); // initializes in the current context, i.e. ctx
|
glGetError();
|
||||||
logger->enableMessages();
|
|
||||||
connect(logger, &QOpenGLDebugLogger::messageLogged, this, [&](const QOpenGLDebugMessage & debugMessage) {
|
|
||||||
qDebug() << debugMessage;
|
|
||||||
});
|
|
||||||
// logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << (const char*)this->glGetString(GL_VERSION);
|
using namespace oglplus;
|
||||||
glEnable(GL_BLEND);
|
Context::Enable(Capability::Blend);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
Context::BlendFunc(BlendFunction::SrcAlpha, BlendFunction::OneMinusSrcAlpha);
|
||||||
glClearColor(0.2f, 0.2f, 0.2f, 1);
|
Context::Disable(Capability::DepthTest);
|
||||||
glDisable(GL_DEPTH_TEST);
|
Context::Disable(Capability::CullFace);
|
||||||
|
Context::ClearColor(0.2f, 0.2f, 0.2f, 1);
|
||||||
|
|
||||||
MessageDialog::registerType();
|
MessageDialog::registerType();
|
||||||
VrMenu::registerType();
|
|
||||||
InfoView::registerType();
|
InfoView::registerType();
|
||||||
|
|
||||||
|
auto offscreenUi = DependencyManager::set<OffscreenUi>();
|
||||||
|
{
|
||||||
|
offscreenUi->create(_context->getContext());
|
||||||
|
offscreenUi->setProxyWindow(this);
|
||||||
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) {
|
||||||
offscreenUi->create(_context);
|
testQmlTexture = textureId;
|
||||||
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) {
|
});
|
||||||
testQmlTexture = textureId;
|
|
||||||
});
|
|
||||||
|
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
|
}
|
||||||
|
|
||||||
offscreenUi->setProxyWindow(this);
|
|
||||||
QDesktopWidget* desktop = QApplication::desktop();
|
auto primaryScreen = QGuiApplication::primaryScreen();
|
||||||
QRect rect = desktop->availableGeometry(desktop->screenCount() - 1);
|
auto targetScreen = primaryScreen;
|
||||||
int height = rect.height();
|
auto screens = QGuiApplication::screens();
|
||||||
//rect.setHeight(height / 2);
|
if (screens.size() > 1) {
|
||||||
rect.setY(rect.y() + height / 2);
|
for (auto screen : screens) {
|
||||||
|
if (screen != targetScreen) {
|
||||||
|
targetScreen = screen;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto rect = targetScreen->availableGeometry();
|
||||||
|
rect.setWidth(rect.width() * 0.8f);
|
||||||
|
rect.setHeight(rect.height() * 0.8f);
|
||||||
|
rect.moveTo(QPoint(20, 20));
|
||||||
setGeometry(rect);
|
setGeometry(rect);
|
||||||
// setFramePosition(QPoint(-1000, 0));
|
|
||||||
// resize(QSize(800, 600));
|
|
||||||
|
|
||||||
#ifdef QML_CONTROL_GALLERY
|
#ifdef QML_CONTROL_GALLERY
|
||||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getTestQmlDir()));
|
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getTestQmlDir()));
|
||||||
offscreenUi->load(QUrl("main.qml"));
|
offscreenUi->load(QUrl("main.qml"));
|
||||||
#else
|
#else
|
||||||
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir()));
|
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getInterfaceQmlDir()));
|
||||||
offscreenUi->load(QUrl("TestRoot.qml"));
|
offscreenUi->load(QUrl("TestRoot.qml"));
|
||||||
offscreenUi->load(QUrl("TestMenu.qml"));
|
|
||||||
// Requires a root menu to have been loaded before it can load
|
|
||||||
VrMenu::load();
|
|
||||||
#endif
|
#endif
|
||||||
installEventFilter(offscreenUi.data());
|
installEventFilter(offscreenUi.data());
|
||||||
offscreenUi->resume();
|
offscreenUi->resume();
|
||||||
|
@ -227,16 +529,35 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
auto error = glGetError();
|
||||||
glViewport(0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio());
|
if (error != GL_NO_ERROR) {
|
||||||
|
qDebug() << "GL error in entering draw " << error;
|
||||||
|
}
|
||||||
|
|
||||||
renderQml();
|
using namespace oglplus;
|
||||||
|
Context::Clear().ColorBuffer().DepthBuffer();
|
||||||
|
ivec2 size(_size.width(), _size.height());
|
||||||
|
size *= devicePixelRatio();
|
||||||
|
size = glm::max(size, ivec2(100, 100));
|
||||||
|
Context::Viewport(size.x, size.y);
|
||||||
|
if (!_program) {
|
||||||
|
_program = loadDefaultShader();
|
||||||
|
_plane = loadPlane(_program);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testQmlTexture > 0) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, testQmlTexture);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
}
|
||||||
|
|
||||||
|
_program->Bind();
|
||||||
|
_plane->Use();
|
||||||
|
_plane->Draw();
|
||||||
_context->swapBuffers(this);
|
_context->swapBuffers(this);
|
||||||
glFinish();
|
|
||||||
|
|
||||||
fps.increment();
|
fps.increment();
|
||||||
if (fps.elapsed() >= 2.0f) {
|
if (fps.elapsed() >= 10.0f) {
|
||||||
qDebug() << "FPS: " << fps.rate();
|
qDebug() << "FPS: " << fps.rate();
|
||||||
fps.reset();
|
fps.reset();
|
||||||
}
|
}
|
||||||
|
@ -246,8 +567,6 @@ private:
|
||||||
_context->makeCurrent(this);
|
_context->makeCurrent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderQml();
|
|
||||||
|
|
||||||
void resizeWindow(const QSize & size) {
|
void resizeWindow(const QSize & size) {
|
||||||
_size = size;
|
_size = size;
|
||||||
DependencyManager::get<OffscreenUi>()->resize(_size);
|
DependencyManager::get<OffscreenUi>()->resize(_size);
|
||||||
|
@ -269,11 +588,13 @@ protected:
|
||||||
offscreenUi->load("Browser.qml");
|
offscreenUi->load("Browser.qml");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Qt::Key_L:
|
|
||||||
|
case Qt::Key_J:
|
||||||
if (event->modifiers() & Qt::CTRL) {
|
if (event->modifiers() & Qt::CTRL) {
|
||||||
InfoView::show(getResourcesDir() + "html/interface-welcome.html", true);
|
loadScript(getExamplesDir() + "tests/qmlWebTest.js");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Qt::Key_K:
|
case Qt::Key_K:
|
||||||
if (event->modifiers() & Qt::CTRL) {
|
if (event->modifiers() & Qt::CTRL) {
|
||||||
OffscreenUi::question("Message title", "Message contents", [](QMessageBox::Button b){
|
OffscreenUi::question("Message title", "Message contents", [](QMessageBox::Button b){
|
||||||
|
@ -281,22 +602,9 @@ protected:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Qt::Key_J:
|
|
||||||
if (event->modifiers() & Qt::CTRL) {
|
|
||||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
|
||||||
rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu");
|
|
||||||
QMetaObject::invokeMethod(rootMenu, "popup");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
QWindow::keyPressEvent(event);
|
QWindow::keyPressEvent(event);
|
||||||
}
|
}
|
||||||
QQmlContext* menuContext{ nullptr };
|
|
||||||
void keyReleaseEvent(QKeyEvent *event) override {
|
|
||||||
if (_altPressed && Qt::Key_Alt == event->key()) {
|
|
||||||
VrMenu::toggle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void moveEvent(QMoveEvent* event) override {
|
void moveEvent(QMoveEvent* event) override {
|
||||||
static qreal oldPixelRatio = 0.0;
|
static qreal oldPixelRatio = 0.0;
|
||||||
|
@ -308,40 +616,26 @@ protected:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void QTestWindow::renderQml() {
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
|
||||||
glLoadIdentity();
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
|
||||||
glLoadIdentity();
|
|
||||||
if (testQmlTexture > 0) {
|
|
||||||
glEnable(GL_TEXTURE_2D);
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, testQmlTexture);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
}
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
{
|
|
||||||
glTexCoord2f(0, 0);
|
|
||||||
glVertex2f(-1, -1);
|
|
||||||
glTexCoord2f(0, 1);
|
|
||||||
glVertex2f(-1, 1);
|
|
||||||
glTexCoord2f(1, 1);
|
|
||||||
glVertex2f(1, 1);
|
|
||||||
glTexCoord2f(1, 0);
|
|
||||||
glVertex2f(1, -1);
|
|
||||||
}
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const char * LOG_FILTER_RULES = R"V0G0N(
|
const char * LOG_FILTER_RULES = R"V0G0N(
|
||||||
hifi.offscreen.focus.debug=false
|
hifi.offscreen.focus.debug=false
|
||||||
qt.quick.mouse.debug=false
|
qt.quick.mouse.debug=false
|
||||||
)V0G0N";
|
)V0G0N";
|
||||||
|
|
||||||
|
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||||
|
QString logMessage = message;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
if (!logMessage.isEmpty()) {
|
||||||
|
OutputDebugStringA(logMessage.toLocal8Bit().constData());
|
||||||
|
OutputDebugStringA("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
QApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
|
qInstallMessageHandler(messageHandler);
|
||||||
QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
|
QLoggingCategory::setFilterRules(LOG_FILTER_RULES);
|
||||||
QTestWindow window;
|
QTestWindow window;
|
||||||
app.exec();
|
app.exec();
|
||||||
|
|
Loading…
Reference in a new issue