Merge branch 'master' of https://github.com/highfidelity/hifi into QtImageCompare

This commit is contained in:
Nissim Hadar 2018-01-10 18:22:06 -08:00
commit 8661d8a20d
15 changed files with 919 additions and 91 deletions

View file

@ -0,0 +1,642 @@
//
// ScriptAPI.qml
//
// Created by Luis Cuenca on 12/18/2017
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import "styles-uit"
import "controls-uit" as HifiControls
Item {
id: root
width: parent.width
height: parent.height
property var hideQtMethods: true
property var maxUpdateValues: 20
property var maxReloadValues: 200
property var apiMembers: []
property var membersWithValues: []
property var isReloading: false
property var evaluatingIdx: -1
property Component scrollSlider
property Component keyboard
Rectangle {
color: "white"
width: parent.width
height: parent.height
}
FontLoader { id: ralewayRegular; source: pathToFonts + "fonts/Raleway-Regular.ttf"; }
Timer {
id: updateList
interval: 200
repeat: false
running: false
onTriggered: {
scrollSlider.y = 0;
list.contentY = 0;
}
}
Row {
id: topBar
anchors.left: parent.left
anchors.leftMargin: 8
width: parent.width
height: 50
HifiControls.GlyphButton {
id: search
enabled: true
glyph: hifi.glyphs.search
color: hifi.colors.text
size: 48
width: 50
height: 50
onClicked: {
addListElements(searchBar.text);
focus = true;
}
}
HifiControls.GlyphButton {
id: back;
enabled: true;
glyph: hifi.glyphs.backward
color: hifi.colors.text
size: 48
width: 30
height: 50
anchors.margins: 2
onClicked: {
var text = searchBar.text;
var chain = text.split(".");
if (chain.length > 2) {
var result = chain[0]+".";
for (var i = 1; i < chain.length-2; i++) {
result += chain[i] + ".";
}
result += chain[chain.length-2];
searchBar.text = result;
} else {
searchBar.text = (chain.length > 1) ? chain[0] : "";
}
if (chain.length > 1) {
addListElements(searchBar.text);
} else {
addListElements();
}
focus = true;
}
}
TextField {
id: searchBar
focus: true
font.pixelSize: 16
width: 2*(parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3
height: parent.height
font.family: ralewayRegular.name
placeholderText: "Search"
onAccepted: {
console.log("Enter Pressed");
search.clicked();
}
onActiveFocusChanged: {
if (activeFocus && HMD.mounted) {
keyboard.raised = true;
} else {
keyboard.raised = false;
}
}
}
HifiControls.Button {
id: addMember;
enabled: true;
text: "+"
width: 50
height: 50
anchors.margins: 2
onClicked: {
addNewMember();
updateList.start();
focus = true;
}
}
HifiControls.Button {
id: evaluate;
enabled: true;
text: "Eval"
width: 50
height: 50
anchors.margins: 2
onClicked: {
evaluateMember();
focus = true;
}
}
TextField {
id: valueBar
focus: true
font.pixelSize: 16
width: (parent.width-back.width-search.width-reload.width-update.width-evaluate.width-addMember.width-16)/3
height: parent.height
font.family: ralewayRegular.name
placeholderText: "Value"
textColor: "#4466DD"
anchors.margins: 2
}
HifiControls.GlyphButton {
id: reload;
enabled: false;
glyph: hifi.glyphs.reload
color: hifi.colors.text
size: 48
width: 50
height: 50
anchors.margins: 2
onClicked: {
reloadListValues();
focus = true;
}
}
HifiControls.GlyphButton {
id: update;
enabled: false;
glyph: hifi.glyphs.playback_play
size: 48
width: 50
height: 50
anchors.margins: 2
onClicked: {
if (isReloading) {
update.glyph = hifi.glyphs.playback_play
isReloading = false;
stopReload();
} else {
update.glyph = hifi.glyphs.stop_square
isReloading = true;
startReload();
}
focus = true;
}
}
}
ListModel {
id: memberModel
}
Component {
id: memberDelegate
Row {
id: memberRow
property var isMainKey: apiType === "class";
spacing: 10
Rectangle {
width: isMainKey ? 20 : 40;
height: parent.height
}
RalewayRegular {
text: apiMember
size: !isMainKey ? 16 : 22
MouseArea {
width: list.width
height: parent.height
onClicked: {
searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember;
valueBar.text = !apiValue ? "" : apiValue;
list.currentIndex = index;
evaluatingIdx = index;
}
onDoubleClicked: {
if (apiType === "class") {
addListElements(apiMember+".");
} else {
isolateElement(evaluatingIdx);
}
}
}
}
RalewayRegular {
text: apiType
size: 14
color: hifi.colors.baseGrayHighlight
}
RalewayRegular {
text: !apiValue ? "" : apiValue;
size: 16
color: "#4466DD"
}
}
}
Rectangle {
id: membersBackground
anchors {
left: parent.left; right: parent.right; top: topBar.bottom; bottom: parent.bottom;
margins: hifi.dimensions.contentMargin.x
bottomMargin: hifi.dimensions.contentSpacing.y + 40
}
color: "white"
radius: 4
ListView {
id: list
anchors {
top: parent.top
left: parent.left
right: scrollBar.left
bottom: parent.bottom
margins: 4
}
clip: true
cacheBuffer: 4000
model: memberModel
delegate: memberDelegate
highlightMoveDuration: 0
highlight: Rectangle {
anchors {
left: parent ? parent.left : undefined
right: parent ? parent.right : undefined
leftMargin: hifi.dimensions.borderWidth
rightMargin: hifi.dimensions.borderWidth
}
color: "#BBDDFF"
}
onMovementStarted: {
scrollSlider.manual = true;
}
onMovementEnded: {
if (list.contentHeight > list.height) {
var range = list.contentY/(list.contentHeight-list.height);
range = range > 1 ? 1 : range;
var idx = Math.round((list.count-1)*range);
scrollSlider.positionSlider(idx);
}
scrollSlider.manual = false;
returnToBounds()
}
}
Rectangle {
id: scrollBar
property bool scrolling: list.contentHeight > list.height
anchors {
top: parent.top
right: parent.right
bottom: parent.bottom
topMargin: 4
bottomMargin: 4
}
width: scrolling ? 18 : 0
radius: 4
color: hifi.colors.baseGrayShadow
MouseArea {
anchors.fill: parent
onClicked: {
var index = scrollSlider.y * (list.count - 1) / (scrollBar.height - scrollSlider.height);
index = Math.round(index);
var scrollAmount = Math.round(list.count/10);
index = index + (mouse.y <= scrollSlider.y ? -scrollAmount : scrollAmount);
if (index < 0) {
index = 0;
}
if (index > list.count - 1) {
index = list.count - 1;
}
scrollSlider.positionSlider(index);
}
}
Rectangle {
id: scrollSlider
property var manual: false
function positionSlider(index){
y = index*(scrollBar.height - scrollSlider.height)/(list.count - 1);
}
anchors {
right: parent.right
rightMargin: 3
}
width: 12
height: (list.height / list.contentHeight) * list.height
radius: width / 4
color: "white"
visible: scrollBar.scrolling;
onYChanged: {
var index = y * (list.count - 1) / (scrollBar.height - scrollSlider.height);
index = Math.round(index);
if (!manual) {
list.positionViewAtIndex(index, ListView.Visible);
}
}
MouseArea {
anchors.fill: parent
drag.target: scrollSlider
drag.axis: Drag.YAxis
drag.minimumY: 0
drag.maximumY: scrollBar.height - scrollSlider.height
}
}
}
}
HifiControls.GlyphButton {
id: clipboard;
enabled: true;
glyph: hifi.glyphs.scriptNew
size: 38
width: 50
height: 50
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: 2
anchors.leftMargin: 8
onClicked: {
var buffer = "";
for (var i = 0; i < memberModel.count; i++) {
var datarow = memberModel.get(i);
buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue;
}
Window.copyToClipboard(buffer);
focus = true;
}
}
HifiControls.Button {
id: debug;
enabled: true;
text: "Debug Script"
width: 120
height: 50
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 2
anchors.rightMargin: 8
onClicked: {
sendToScript({type: "selectScript"});
}
}
HifiControls.CheckBox {
id: hideQt
boxSize: 25
boxRadius: 3
checked: true
anchors.left: clipboard.right
anchors.leftMargin: 8
anchors.verticalCenter: clipboard.verticalCenter
anchors.margins: 2
onClicked: {
hideQtMethods = checked;
addListElements();
}
}
HifiControls.Label {
id: hideLabel
anchors.left: hideQt.right
anchors.verticalCenter: clipboard.verticalCenter
anchors.margins: 2
font.pixelSize: 15
text: "Hide Qt Methods"
}
HifiControls.Keyboard {
id: keyboard;
raised: false;
anchors {
bottom: parent.bottom;
left: parent.left;
right: parent.right;
}
Keys.onPressed: {
console.log(event.nativeScanCode);
if (event.key == Qt.Key_Left) {
keyboard.raised = false;
}
}
}
function addNewMember() {
apiMembers.push({member: searchBar.text, type: "user", value: valueBar.text, hasValue: true});
var data = {'memberIndex': apiMembers.length-1, 'apiMember': searchBar.text, 'apiType':"user", 'apiValue': valueBar.text};
memberModel.insert(0, data);
computeMembersWithValues();
}
function evaluateMember() {
sendToScript({type: "evaluateMember", data:{member: searchBar.text, index: evaluatingIdx}});
}
function getValuesToRefresh() {
var valuesToRefresh = [];
for (var i = 0; i < membersWithValues.length; i++) {
var index = membersWithValues[i];
var row = memberModel.get(index);
valuesToRefresh.push({index: index, member: (row.apiType == "function()") ? row.apiMember+"()" : row.apiMember, value: row.apiValue});
}
return valuesToRefresh;
}
function reloadListValues(){
var valuesToRefresh = getValuesToRefresh();
sendToScript({type: "refreshValues", data:valuesToRefresh});
}
function startReload(){
var valuesToRefresh = getValuesToRefresh();
sendToScript({type: "startRefreshValues", data:valuesToRefresh});
}
function stopReload(){
sendToScript({type: "stopRefreshValues"});
}
function refreshValues(data) {
var buffer = "";
for (var i = 0; i < data.length; i++) {
var row = memberModel.get(data[i].index);
row.apiValue = data[i].value;
apiMembers[row.memberIndex].value = data[i].value;
memberModel.set(data[i].index, row);
buffer += "\n" + apiMembers[row.memberIndex].member + " : " + data[i].value;
}
print(buffer);
}
function fromScript(message) {
if (message.type === "methods") {
apiMembers = message.data;
if (ScriptDiscoveryService.debugScriptUrl != "") {
addListElements("GlobalDebugger");
if (memberModel.count == 0) {
addListElements();
}
} else {
addListElements();
}
} else if (message.type === "debugMethods") {
addListElements("GlobalDebugger");
} else if (message.type === "refreshValues") {
refreshValues(message.data);
} else if (message.type === "evaluateMember") {
valueBar.text = message.data.value;
var selrow = memberModel.get(message.data.index);
if (selrow.apiMember === searchBar.text || selrow.apiMember === searchBar.text + "()") {
selrow.apiValue = message.data.value;
apiMembers[selrow.memberIndex].value = message.data.value;
apiMembers[selrow.memberIndex].hasValue = true;
memberModel.set(message.data.index, selrow);
}
} else if (message.type === "selectScript") {
if (message.data.length > 0) {
ScriptDiscoveryService.debugScriptUrl = message.data;
}
}
}
function getFilterPairs(filter){
var filteredArray = [];
var filterChain;
filterChain = filter.split(" ");
for (var i = 0; i < filterChain.length; i++) {
filterChain[i] = filterChain[i].toUpperCase();
}
var matchPairs = [];
for (var i = 0; i < apiMembers.length; i++) {
if (filterChain != undefined) {
var found = 0;
var memberComp = apiMembers[i].member.toUpperCase();
for (var j = 0; j < filterChain.length; j++) {
found += memberComp.indexOf(filterChain[j]) >= 0 ? 1 : 0;
}
if (found === 0) {
continue;
}
matchPairs.push({index: i, found: found, member: apiMembers[i].member});
}
}
matchPairs.sort(function(a, b){
if(a.found > b.found) return -1;
if(a.found < b.found) return 1;
if(a.member > b.member) return 1;
if(a.member < b.member) return -1;
return 0;
});
return matchPairs;
}
function isolateElement(index) {
var oldElement = memberModel.get(index);
var newElement = {memberIndex: oldElement.memberIndex, apiMember: oldElement.apiMember, apiType: oldElement.apiType, apiValue: oldElement.apiValue};
membersWithValues = apiMembers[oldElement.memberIndex].hasValue ? [0] : [];
memberModel.remove(0, memberModel.count);
memberModel.append(newElement);
}
function computeMembersWithValues() {
membersWithValues = [];
for (var i = 0; i < memberModel.count; i++) {
var idx = memberModel.get(i).memberIndex;
if (apiMembers[idx].hasValue) {
membersWithValues.push(i);
}
}
update.enabled = membersWithValues.length <= maxUpdateValues;
reload.enabled = membersWithValues.length <= maxReloadValues;
}
function addListElements(filter) {
valueBar.text = "";
memberModel.remove(0, memberModel.count);
var filteredArray = (filter != undefined) ? [] : apiMembers;
var matchPairs;
if (filter != undefined) {
matchPairs = getFilterPairs(filter);
for (var i = 0; i < matchPairs.length; i++) {
if (matchPairs[i].found < matchPairs[0].found) {
break;
}
var idx = matchPairs[i].index;
filteredArray.push(apiMembers[idx]);
}
}
for (var i = 0; i < filteredArray.length; i++) {
var data = {'memberIndex': matchPairs ? matchPairs[i].index : i,
'apiMember': filteredArray[i].member,
'apiType': filteredArray[i].type,
'apiValue': filteredArray[i].value};
if (hideQtMethods) {
var chain = data.apiMember.split(".");
var method = chain[chain.length-1];
if (method != "destroyed" &&
method != "objectName" &&
method != "objectNameChanged") {
memberModel.append(data);
}
} else {
memberModel.append(data);
}
}
computeMembersWithValues();
if (isReloading) {
update.glyph = hifi.glyphs.playback_play
isReloading = false;
stopReload();
}
if (memberModel.count > 0) {
scrollSlider.y = 0;
list.contentY = 0;
}
}
signal sendToScript(var message);
}

View file

@ -45,18 +45,6 @@ Item {
}
}
onClicked: {
mouse.accepted = true;
Tablet.playSound(TabletEnums.ButtonClick);
webEntity.synthesizeKeyPress(glyph);
webEntity.synthesizeKeyPress(glyph, mirrorText);
if (toggle) {
toggled = !toggled;
}
}
onDoubleClicked: {
mouse.accepted = true;
}
@ -94,6 +82,14 @@ Item {
onReleased: {
if (containsMouse) {
Tablet.playSound(TabletEnums.ButtonClick);
webEntity.synthesizeKeyPress(glyph);
webEntity.synthesizeKeyPress(glyph, mirrorText);
if (toggle) {
toggled = !toggled;
}
keyItem.state = "mouseOver";
} else {
if (toggled) {

View file

@ -70,7 +70,15 @@ ModalWindow {
signal selectedFile(var file);
signal canceled();
signal selected(int button);
function click(button) {
clickedButton = button;
selected(button);
destroy();
}
property int clickedButton: OriginalDialogs.StandardButton.NoButton;
Component.onCompleted: {
console.log("Helper " + helper + " drives " + drives);
@ -628,7 +636,10 @@ ModalWindow {
case Qt.Key_Backtab:
event.accepted = false;
break;
case Qt.Key_Escape:
event.accepted = true;
root.click(OriginalDialogs.StandardButton.Cancel);
break;
default:
if (addToPrefix(event)) {
event.accepted = true
@ -793,7 +804,11 @@ ModalWindow {
case Qt.Key_Home:
event.accepted = d.navigateHome();
break;
}
case Qt.Key_Escape:
event.accepted = true;
root.click(OriginalDialogs.StandardButton.Cancel);
break;
}
}
}

View file

@ -62,7 +62,8 @@ StackView {
var callback = rpcCalls[message.id];
if (!callback) {
console.log('No callback for message fromScript', JSON.stringify(message));
// FIXME: We often recieve very long messages here, the logging of which is drastically slowing down the main thread
//console.log('No callback for message fromScript', JSON.stringify(message));
return;
}
delete rpcCalls[message.id];

View file

@ -2444,7 +2444,7 @@ void Application::initializeUi() {
offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2);
}
void Application::updateCamera(RenderArgs& renderArgs) {
void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
PROFILE_RANGE(render, __FUNCTION__);
PerformanceTimer perfTimer("updateCamera");
@ -2459,6 +2459,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
// Using the latter will cause the camera to wobble with idle animations,
// or with changes from the face tracker
if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) {
_thirdPersonHMDCameraBoomValid= false;
if (isHMDMode()) {
mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
_myCamera.setPosition(extractTranslation(camMat));
@ -2471,12 +2472,25 @@ void Application::updateCamera(RenderArgs& renderArgs) {
}
else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
if (isHMDMode()) {
auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix();
_myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat)));
_myCamera.setPosition(extractTranslation(hmdWorldMat) +
myAvatar->getWorldOrientation() * boomOffset);
if (!_thirdPersonHMDCameraBoomValid) {
const glm::vec3 CAMERA_OFFSET = glm::vec3(0.0f, 0.0f, 0.7f);
_thirdPersonHMDCameraBoom = cancelOutRollAndPitch(myAvatar->getHMDSensorOrientation()) * CAMERA_OFFSET;
_thirdPersonHMDCameraBoomValid = true;
}
glm::mat4 thirdPersonCameraSensorToWorldMatrix = myAvatar->getSensorToWorldMatrix();
const glm::vec3 cameraPos = myAvatar->getHMDSensorPosition() + _thirdPersonHMDCameraBoom * myAvatar->getBoomLength();
glm::mat4 sensorCameraMat = createMatFromQuatAndPos(myAvatar->getHMDSensorOrientation(), cameraPos);
glm::mat4 worldCameraMat = thirdPersonCameraSensorToWorldMatrix * sensorCameraMat;
_myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat)));
_myCamera.setPosition(extractTranslation(worldCameraMat));
}
else {
_thirdPersonHMDCameraBoomValid = false;
_myCamera.setOrientation(myAvatar->getHead()->getOrientation());
if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) {
_myCamera.setPosition(myAvatar->getDefaultEyePosition()
@ -2489,6 +2503,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
}
}
else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) {
_thirdPersonHMDCameraBoomValid= false;
if (isHMDMode()) {
auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f));
@ -2523,6 +2538,7 @@ void Application::updateCamera(RenderArgs& renderArgs) {
renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE;
}
else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) {
_thirdPersonHMDCameraBoomValid= false;
EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer();
if (cameraEntity != nullptr) {
if (isHMDMode()) {
@ -5099,7 +5115,8 @@ void Application::update(float deltaTime) {
_postUpdateLambdas.clear();
}
editRenderArgs([this](AppRenderArgs& appRenderArgs) {
editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) {
PerformanceTimer perfTimer("editRenderArgs");
appRenderArgs._headPose= getHMDSensorPose();
@ -5146,7 +5163,7 @@ void Application::update(float deltaTime) {
resizeGL();
}
this->updateCamera(appRenderArgs._renderArgs);
this->updateCamera(appRenderArgs._renderArgs, deltaTime);
appRenderArgs._eyeToWorld = _myCamera.getTransform();
appRenderArgs._isStereo = false;

View file

@ -146,7 +146,7 @@ public:
void initializeGL();
void initializeUi();
void updateCamera(RenderArgs& renderArgs);
void updateCamera(RenderArgs& renderArgs, float deltaTime);
void paintGL();
void resizeGL();
@ -695,6 +695,9 @@ private:
void startHMDStandBySession();
void endHMDSession();
glm::vec3 _thirdPersonHMDCameraBoom { 0.0f, 0.0f, -1.0f };
bool _thirdPersonHMDCameraBoomValid { true };
QUrl _avatarOverrideUrl;
bool _saveAvatarOverrideUrl { false };
QObject* _renderEventHandler{ nullptr };

View file

@ -758,6 +758,14 @@ Menu::Menu() {
// Developer > Stats
addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats);
// Developer > API Debugger
action = addActionToQMenuAndActionHash(developerMenu, "API Debugger");
connect(action, &QAction::triggered, [] {
auto scriptEngines = DependencyManager::get<ScriptEngines>();
QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation();
defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js");
scriptEngines->loadScript(defaultScriptsLoc.toString());
});
#if 0 /// -------------- REMOVED FOR NOW --------------
addDisabledActionAndSeparator(navigateMenu, "History");

View file

@ -2799,14 +2799,9 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) {
}
bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
auto cameraMode = qApp->getCamera().getMode();
if (cameraMode == CAMERA_MODE_THIRD_PERSON) {
return false;
} else {
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
}
const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees
glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix);
return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD;
}
bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {

View file

@ -34,7 +34,6 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle
}
static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) {
glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix());
// check for pinned hips.
@ -106,7 +105,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (avatarHeadPose.isValid()) {
AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation());
params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose;
params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = true;
params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled;
} else {
// even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and
// down in desktop mode.
@ -114,7 +113,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
// postMult 180 is necessary to convert head from -z forward to z forward.
glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180;
params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f));
params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = false;
params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0;
}
//
@ -135,10 +134,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (controllerPose.isValid()) {
AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation());
params.primaryControllerPoses[pair.second] = avatarToRigPose * pose;
params.primaryControllerActiveFlags[pair.second] = true;
params.primaryControllerFlags[pair.second] = (uint8_t)Rig::ControllerFlags::Enabled;
} else {
params.primaryControllerPoses[pair.second] = AnimPose::identity;
params.primaryControllerActiveFlags[pair.second] = false;
params.primaryControllerFlags[pair.second] = 0;
}
}
@ -166,15 +165,15 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
if (controllerPose.isValid()) {
AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation());
params.secondaryControllerPoses[pair.second] = avatarToRigPose * pose;
params.secondaryControllerActiveFlags[pair.second] = true;
params.secondaryControllerFlags[pair.second] = (uint8_t)Rig::ControllerFlags::Enabled;
} else {
params.secondaryControllerPoses[pair.second] = AnimPose::identity;
params.secondaryControllerActiveFlags[pair.second] = false;
params.secondaryControllerFlags[pair.second] = 0;
}
}
// if hips are not under direct control, estimate the hips position.
if (avatarHeadPose.isValid() && !params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips]) {
if (avatarHeadPose.isValid() && !(params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] & (uint8_t)Rig::ControllerFlags::Enabled)) {
bool isFlying = (myAvatar->getCharacterController()->getState() == CharacterController::State::Hover || myAvatar->getCharacterController()->computeCollisionGroup() == BULLET_COLLISION_GROUP_COLLISIONLESS);
if (!_prevHipsValid) {
@ -200,7 +199,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix());
params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips;
params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips] = true;
params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated;
} else {
_prevHipsValid = false;

View file

@ -182,10 +182,6 @@ bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) {
return offscreenUi->isPointOnDesktopWindow(point);
}
glm::vec2 WindowScriptingInterface::getDeviceSize() const {
return qApp->getDeviceSize();
}
/// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and
/// might be in same thread as a script that sets the reticle to invisible
void WindowScriptingInterface::ensureReticleVisible() const {
@ -395,11 +391,15 @@ QString WindowScriptingInterface::checkVersion() {
}
int WindowScriptingInterface::getInnerWidth() {
return qApp->getWindow()->geometry().width();
return qApp->getDeviceSize().x;
}
int WindowScriptingInterface::getInnerHeight() {
return qApp->getWindow()->geometry().height();
return qApp->getDeviceSize().y;
}
glm::vec2 WindowScriptingInterface::getDeviceSize() const {
return qApp->getDeviceSize();
}
int WindowScriptingInterface::getX() {

View file

@ -1,5 +1,5 @@
//
// WindowScriptingInterface.cpp
// WindowScriptingInterface.h
// interface/src/scripting
//
// Created by Ryan Huffman on 4/29/14.
@ -54,6 +54,7 @@ class WindowScriptingInterface : public QObject, public Dependency {
Q_PROPERTY(int innerHeight READ getInnerHeight)
Q_PROPERTY(int x READ getX)
Q_PROPERTY(int y READ getY)
public:
WindowScriptingInterface();
~WindowScriptingInterface();

View file

@ -1265,7 +1265,8 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin
return position;
}
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
bool leftArmEnabled, bool rightArmEnabled, float dt,
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) {
@ -1279,7 +1280,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
glm::vec3 handPosition = leftHandPose.trans();
glm::quat handRotation = leftHandPose.rot();
if (!hipsEnabled) {
if (!hipsEnabled || hipsEstimated) {
// prevent the hand IK targets from intersecting the torso
handPosition = deflectHandFromTorso(handPosition, hipsShapeInfo, spineShapeInfo, spine1ShapeInfo, spine2ShapeInfo);
}
@ -1326,7 +1327,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab
glm::vec3 handPosition = rightHandPose.trans();
glm::quat handRotation = rightHandPose.rot();
if (!hipsEnabled) {
if (!hipsEnabled || hipsEstimated) {
// prevent the hand IK targets from intersecting the torso
handPosition = deflectHandFromTorso(handPosition, hipsShapeInfo, spineShapeInfo, spine1ShapeInfo, spine2ShapeInfo);
}
@ -1550,20 +1551,20 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
_animVars.set("isTalking", params.isTalking);
_animVars.set("notIsTalking", !params.isTalking);
bool headEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Head];
bool leftHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftHand];
bool rightHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightHand];
bool hipsEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Hips];
bool leftFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftFoot];
bool rightFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightFoot];
bool spine2Enabled = params.primaryControllerActiveFlags[PrimaryControllerType_Spine2];
bool leftArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_LeftArm];
bool rightArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_RightArm];
bool headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled;
bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled;
bool rightHandEnabled = params.primaryControllerFlags[PrimaryControllerType_RightHand] & (uint8_t)ControllerFlags::Enabled;
bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled;
bool hipsEstimated = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated;
bool leftFootEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftFoot] & (uint8_t)ControllerFlags::Enabled;
bool rightFootEnabled = params.primaryControllerFlags[PrimaryControllerType_RightFoot] & (uint8_t)ControllerFlags::Enabled;
bool spine2Enabled = params.primaryControllerFlags[PrimaryControllerType_Spine2] & (uint8_t)ControllerFlags::Enabled;
bool leftArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_LeftArm] & (uint8_t)ControllerFlags::Enabled;
bool rightArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_RightArm] & (uint8_t)ControllerFlags::Enabled;
updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]);
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, leftArmEnabled, rightArmEnabled, dt,
updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, dt,
params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand],
params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo);
@ -1623,7 +1624,7 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo
for (int i = 0; i < (int)NumSecondaryControllerTypes; i++) {
int index = indexOfJoint(secondaryControllerJointNames[i]);
if (index >= 0) {
if (params.secondaryControllerActiveFlags[i]) {
if (params.secondaryControllerFlags[i] & (uint8_t)ControllerFlags::Enabled) {
ikNode->setSecondaryTargetInRigFrame(index, params.secondaryControllerPoses[i]);
} else {
ikNode->clearSecondaryTarget(index);

View file

@ -69,11 +69,16 @@ public:
NumSecondaryControllerTypes
};
enum class ControllerFlags : uint8_t {
Enabled = 0x01,
Estimated = 0x02
};
struct ControllerParameters {
AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space
bool primaryControllerActiveFlags[NumPrimaryControllerTypes];
uint8_t primaryControllerFlags[NumPrimaryControllerTypes];
AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space
bool secondaryControllerActiveFlags[NumSecondaryControllerTypes];
uint8_t secondaryControllerFlags[NumSecondaryControllerTypes];
bool isTalking;
FBXJointShapeInfo hipsShapeInfo;
FBXJointShapeInfo spineShapeInfo;
@ -251,7 +256,8 @@ protected:
void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut);
void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix);
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt,
void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated,
bool leftArmEnabled, bool rightArmEnabled, float dt,
const AnimPose& leftHandPose, const AnimPose& rightHandPose,
const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo,
const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo);

View file

@ -31,6 +31,7 @@ class ScriptEngines : public QObject, public Dependency {
Q_PROPERTY(ScriptsModel* scriptsModel READ scriptsModel CONSTANT)
Q_PROPERTY(ScriptsModelFilter* scriptsModelFilter READ scriptsModelFilter CONSTANT)
Q_PROPERTY(QString debugScriptUrl READ getDebugScriptUrl WRITE setDebugScriptUrl)
public:
using ScriptInitializer = std::function<void(ScriptEnginePointer)>;
@ -41,6 +42,9 @@ public:
void loadScripts();
void saveScripts();
QString getDebugScriptUrl() { return _debugScriptUrl; };
void setDebugScriptUrl(const QString& url) { _debugScriptUrl = url; };
QString getScriptsLocation() const;
void loadDefaultScripts();
void setScriptsLocation(const QString& scriptsLocation);
@ -117,6 +121,7 @@ protected:
std::atomic<bool> _isStopped { false };
std::atomic<bool> _isReloading { false };
bool _defaultScriptsLocationOverridden { false };
QString _debugScriptUrl;
};
QUrl normalizeScriptURL(const QUrl& rawScriptURL);

View file

@ -9,34 +9,173 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var array = [];
function listKeys(string, object) {
if (string === "listKeys" || string === "array" || string === "buffer" || string === "i") {
return;
(function(){
var array = [];
var mainKeys = Object.keys(this);
var qml = Script.resourcesPath() + '/qml/CurrentAPI.qml';
var needsUpdate = false;
var updateTime = 20;
var updateData = [];
var deltaTime = 0;
var maxUpdatingMethods = 20;
var scriptPath = "";
if (ScriptDiscoveryService.debugScriptUrl != "") {
Script.include(ScriptDiscoveryService.debugScriptUrl);
}
if (typeof(object) !== "object" || object === null) {
array.push(string + " " + typeof(object));
return;
}
var keys = Object.keys(object);
for (var i = 0; i < keys.length; ++i) {
if (string === "") {
listKeys(keys[i], object[keys[i]]);
} else if (keys[i] !== "parent") {
listKeys(string + "." + keys[i], object[keys[i]]);
var window = new OverlayWindow({
title: 'API Debugger',
source: qml,
width: 1200,
height: 500
});
window.closed.connect(function () {
Script.stop();
});
function addMainKeys(){
var keys = Object.keys(this);
for (var i = 0; i < keys.length; i++) {
array.push({member:keys[i] , type: "class"});
}
}
}
listKeys("", this);
array.sort();
function memberHasValue(member, type) {
if (type === "function()") {
if (member.indexOf(".has") < 0 && member.indexOf(".is") < 0 && member.indexOf(".get") < 0) {
return false;
}
if (member.indexOf("indow") < 0) {
return true;
}
} else if (type === "boolean" || type === "string" || type === "number" || type === "user") {
return true;
}
return false;
}
var buffer = "\n======= JS API list =======";
for (var i = 0; i < array.length; ++i) {
buffer += "\n" + array[i];
}
buffer += "\n========= API END =========\n";
function listKeys(string, object) {
if (string === "listKeys" || string === "array" || string === "buffer" || string === "i") {
return;
}
if (typeof(object) !== "object" || object === null) {
var type = typeof(object);
if (type === "function") {
chain = string.split("(");
if (chain.length > 1) {
string = chain[0];
type = "function(" + chain[1];
} else {
type = "function()";
}
}
var value = "";
var hasValue = false;
if (memberHasValue(string, type)){
var evalstring = type === "function()" ? string+"()" : string;
try{
value = "" + eval(evalstring);
hasValue = true;
} catch(e) {
value = "Error evaluating";
}
}
array.push({member:string , type: type, value: value, hasValue: hasValue});
return;
}
var keys = Object.keys(object);
for (var i = 0; i < keys.length; ++i) {
if (string === "") {
listKeys(keys[i], object[keys[i]]);
} else if (keys[i] !== "parent") {
listKeys(string + "." + keys[i], object[keys[i]]);
}
}
}
function findMethods(addon, object, addkeys) {
array = [];
var string = addkeys ? "" : addon+".";
listKeys(string, object);
if (addkeys) {
addMainKeys();
}
array.sort(function(a, b){
if(a.member < b.member) return -1;
if(a.member > b.member) return 1;
return 0;
});
};
findMethods("", this, true);
window.sendToQml({type:"methods", data:array});
window.fromQml.connect(function(message){
if (message.type == "refreshValues") {
updateData = message.data;
updateValues();
} else if (message.type == "startRefreshValues") {
updateData = message.data;
if (updateData.length > maxUpdatingMethods) {
updateData = message.data;
updateValues();
} else {
needsUpdate = true;
deltaTime = updateTime;
}
} else if (message.type == "stopRefreshValues") {
needsUpdate = false;
deltaTime = 0;
} else if (message.type == "evaluateMember") {
var value = ""
try {
value = "" + eval(message.data.member);
} catch(e) {
value = "Error evaluating"
}
window.sendToQml({type:"evaluateMember", data:{value:value, index:message.data.index}});
} else if (message.type == "selectScript") {
scriptPath = Window.browse("Select script to debug", "*.js", "JS files(*.js)");
if (scriptPath) {
ScriptDiscoveryService.stopScript(Paths.defaultScripts + "/developer/utilities/tools/currentAPI.js", true);
}
}
});
function updateValues() {
for (var i = 0; i < updateData.length; i++) {
try {
updateData[i].value = "" + eval(updateData[i].member);
} catch(e) {
updateData[i].value = "Error evaluating"
}
}
window.sendToQml({type: "refreshValues", data: updateData});
}
Script.update.connect(function(){
deltaTime++;
if (deltaTime > updateTime) {
deltaTime = 0;
if (needsUpdate) {
updateValues();
}
}
});
Script.scriptEnding.connect(function(){
if (!scriptPath || scriptPath.length == 0) {
ScriptDiscoveryService.debugScriptUrl = "";
} else {
ScriptDiscoveryService.debugScriptUrl = scriptPath;
}
console.log("done running");
window.close();
});
}());
print(buffer);