From 7d7e0129656959558a563f87ceabfd0d4c399c9b Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 11:28:09 -0700 Subject: [PATCH 01/44] Add XMLHttpRequest --- .../script-engine/src/XMLHttpRequestClass.cpp | 233 ++++++++++++++++++ .../script-engine/src/XMLHttpRequestClass.h | 128 ++++++++++ 2 files changed, 361 insertions(+) create mode 100644 libraries/script-engine/src/XMLHttpRequestClass.cpp create mode 100644 libraries/script-engine/src/XMLHttpRequestClass.h diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp new file mode 100644 index 0000000000..77b03db999 --- /dev/null +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -0,0 +1,233 @@ +// +// XMLHttpRequestClass.cpp +// libraries/script-engine/src/ +// +// Created by Ryan Huffman on 5/2/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// This class is an implementation of the XMLHttpRequest object for scripting use. It provides a near-complete implementation +// of the class described in the Mozilla docs: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include "XMLHttpRequestClass.h" + +XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : + _engine(engine), + _async(true), + _url(), + _method(""), + _responseType(""), + _manager(this), + _request(), + _reply(NULL), + _sendData(NULL), + _rawResponseData(), + _responseData(""), + _onTimeout(QScriptValue::NullValue), + _onReadyStateChange(QScriptValue::NullValue), + _readyState(XMLHttpRequestClass::UNSENT), + _errorCode(QNetworkReply::NoError), + _timeout(0), + _timer(this), + _numRedirects(0) { + + _timer.setSingleShot(true); +} + +XMLHttpRequestClass::~XMLHttpRequestClass() { + if (_reply) { delete _reply; } + if (_sendData) { delete _sendData; } +} + +QScriptValue XMLHttpRequestClass::constructor(QScriptContext* context, QScriptEngine* engine) { + return engine->newQObject(new XMLHttpRequestClass(engine)); +} + +QScriptValue XMLHttpRequestClass::getStatus() const { + if (_reply) { + return QScriptValue(_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()); + } + return QScriptValue(0); +} + +QString XMLHttpRequestClass::getStatusText() const { + if (_reply) { + return _reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + } + return ""; +} + +void XMLHttpRequestClass::abort() { + _abortRequest(); +} + +void XMLHttpRequestClass::setRequestHeader(const QString& name, const QString& value) { + _request.setRawHeader(QByteArray(name.toLatin1()), QByteArray(value.toLatin1())); +} + +void XMLHttpRequestClass::requestMetaDataChanged() { + QVariant redirect = _reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + + // If this is a redirect, abort the current request and start a new one + if (redirect.isValid() && _numRedirects < MAXIMUM_REDIRECTS) { + _numRedirects++; + _abortRequest(); + + QUrl newUrl = _url.resolved(redirect.toUrl().toString()); + _request.setUrl(newUrl); + _doSend(); + } +} + +void XMLHttpRequestClass::requestDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + if (_readyState == OPENED && bytesReceived > 0) { + _setReadyState(HEADERS_RECEIVED); + _setReadyState(LOADING); + } +} + +QScriptValue XMLHttpRequestClass::getAllResponseHeaders() const { + if (_reply) { + QList headerList = _reply->rawHeaderPairs(); + QByteArray headers; + for (int i = 0; i < headerList.size(); i++) { + headers.append(headerList[i].first); + headers.append(": "); + headers.append(headerList[i].second); + headers.append("\n"); + } + return QString(headers.data()); + } + return QScriptValue(""); +} + +QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { + if (_reply && _reply->hasRawHeader(name.toLatin1())) { + return QScriptValue(QString(_reply->rawHeader(name.toLatin1()))); + } + return QScriptValue::NullValue; +} + +void XMLHttpRequestClass::_setReadyState(ReadyState readyState) { + if (readyState != _readyState) { + _readyState = readyState; + if (_onReadyStateChange.isFunction()) { + _onReadyStateChange.call(QScriptValue::NullValue); + } + } +} + +void XMLHttpRequestClass::open(const QString& method, const QString& url, bool async, const QString& username, + const QString& password) { + if (_readyState == UNSENT) { + _async = async; + _url.setUrl(url); + if (!username.isEmpty()) { + _url.setUserName(username); + } + if (!password.isEmpty()) { + _url.setPassword(password); + } + _request.setUrl(_url); + _method = method; + _setReadyState(OPENED); + } +} + +void XMLHttpRequestClass::send() { + send(QString::Null()); +} + +void XMLHttpRequestClass::send(const QString& data) { + if (_readyState == OPENED && !_reply) { + if (!data.isNull()) { + _sendData = new QBuffer(this); + _sendData->setData(data.toUtf8()); + } + + _doSend(); + + if (!_async) { + QEventLoop loop; + connect(this, SIGNAL(requestComplete()), &loop, SLOT(quit())); + loop.exec(); + } + } +} + +void XMLHttpRequestClass::_doSend() { + _reply = _manager.sendCustomRequest(_request, _method.toLatin1(), _sendData); + + _connectToReply(_reply); + + if (_timeout > 0) { + _timer.start(_timeout); + connect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); + } +} + +void XMLHttpRequestClass::requestTimeout() { + if (_onTimeout.isFunction()) { + _onTimeout.call(QScriptValue::NullValue); + } + _abortRequest(); + _errorCode = QNetworkReply::TimeoutError; + _setReadyState(DONE); + emit requestComplete(); +} + +void XMLHttpRequestClass::requestError(QNetworkReply::NetworkError code) { +} + +void XMLHttpRequestClass::requestFinished() { + disconnect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); + + _errorCode = _reply->error(); + if (_errorCode == QNetworkReply::NoError) { + _rawResponseData.append(_reply->readAll()); + + if (_responseType == "json") { + _responseData = _engine->evaluate("(" + QString(_rawResponseData.data()) + ")"); + if (_responseData.isError()) { + _engine->clearExceptions(); + _responseData = QScriptValue::NullValue; + } + } else if (_responseType == "arraybuffer") { + _responseData = QScriptValue(_rawResponseData.data()); + } else { + _responseData = QScriptValue(QString(_rawResponseData.data())); + } + } + _setReadyState(DONE); + emit requestComplete(); +} + +void XMLHttpRequestClass::_abortRequest() { + // Disconnect from signals we don't want to receive any longer. + disconnect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); + if (_reply) { + _disconnectFromReply(_reply); + _reply->abort(); + delete _reply; + _reply = NULL; + } +} + +void XMLHttpRequestClass::_connectToReply(QNetworkReply* reply) { + connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); + connect(reply, SIGNAL(metaDataChanged()), this, SLOT(requestMetaDataChanged())); +} + +void XMLHttpRequestClass::_disconnectFromReply(QNetworkReply* reply) { + disconnect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); + disconnect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); + disconnect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); + disconnect(reply, SIGNAL(metaDataChanged()), this, SLOT(requestMetaDataChanged())); +} diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h new file mode 100644 index 0000000000..b1b33e4cc6 --- /dev/null +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -0,0 +1,128 @@ +// +// XMLHttpRequestClass.h +// libraries/script-engine/src/ +// +// Created by Ryan Huffman on 5/2/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// +// 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_XMLHttpRequestClass_h +#define hifi_XMLHttpRequestClass_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class XMLHttpRequestClass : public QObject { + Q_OBJECT + Q_PROPERTY(QScriptValue response READ getResponse) + Q_PROPERTY(QScriptValue responseText READ getResponseText) + Q_PROPERTY(QString responseType READ getResponseType WRITE setResponseType) + Q_PROPERTY(QScriptValue status READ getStatus) + Q_PROPERTY(QString statusText READ getStatusText) + Q_PROPERTY(QScriptValue readyState READ getReadyState) + Q_PROPERTY(QScriptValue errorCode READ getError) + Q_PROPERTY(int timeout READ getTimeout WRITE setTimeout) + + Q_PROPERTY(int UNSENT READ getUnsent) + Q_PROPERTY(int OPENED READ getOpened) + Q_PROPERTY(int HEADERS_RECEIVED READ getHeadersReceived) + Q_PROPERTY(int LOADING READ getLoading) + Q_PROPERTY(int DONE READ getDone) + + // Callbacks + Q_PROPERTY(QScriptValue ontimeout WRITE setOnTimeout) + Q_PROPERTY(QScriptValue onreadystatechange WRITE setOnReadyStateChange) +public: + XMLHttpRequestClass(QScriptEngine* engine); + ~XMLHttpRequestClass(); + + static const int MAXIMUM_REDIRECTS = 5; + enum ReadyState { + UNSENT = 0, + OPENED, + HEADERS_RECEIVED, + LOADING, + DONE + }; + + int getUnsent() const { return UNSENT; }; + int getOpened() const { return OPENED; }; + int getHeadersReceived() const { return HEADERS_RECEIVED; }; + int getLoading() const { return LOADING; }; + int getDone() const { return DONE; }; + + static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + static QString escapeJavascriptString(const QString& jsString); + + int getTimeout() const { return _timeout; } + void setTimeout(int timeout) { _timeout = timeout; } + QScriptValue getResponse() const { return _responseData; } + QScriptValue getResponseText() const { return QScriptValue(QString(_rawResponseData.data())); } + QString getResponseType() const { return _responseType; } + void setResponseType(const QString& responseType) { _responseType = responseType; } + QScriptValue getReadyState() const { return QScriptValue(_readyState); } + QScriptValue getError() const { return QScriptValue(_errorCode); } + QScriptValue getStatus() const; + QString getStatusText() const; + + void setOnTimeout(QScriptValue function) { _onTimeout = function; } + void setOnReadyStateChange(QScriptValue function) { _onReadyStateChange = function; } + +public slots: + void abort(); + void setRequestHeader(const QString& name, const QString& value); + void open(const QString& method, const QString& url, bool async = true, const QString& username = "", + const QString& password = ""); + void send(); + void send(const QString& data); + QScriptValue getAllResponseHeaders() const; + QScriptValue getResponseHeader(const QString& name) const; + +signals: + void requestComplete(); + +private: + void _setReadyState(ReadyState readyState); + void _doSend(); + void _connectToReply(QNetworkReply* reply); + void _disconnectFromReply(QNetworkReply* reply); + void _abortRequest(); + + QScriptEngine* _engine; + bool _async; + QUrl _url; + QString _method; + QString _responseType; + QNetworkAccessManager _manager; + QNetworkRequest _request; + QNetworkReply* _reply; + QBuffer* _sendData; + QByteArray _rawResponseData; + QScriptValue _responseData; + QScriptValue _onTimeout; + QScriptValue _onReadyStateChange; + ReadyState _readyState; + QNetworkReply::NetworkError _errorCode; + int _timeout; + QTimer _timer; + int _numRedirects; + +private slots: + void requestFinished(); + void requestError(QNetworkReply::NetworkError code); + void requestMetaDataChanged(); + void requestDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void requestTimeout(); +}; + +#endif // hifi_XMLHttpRequestClass_h From 2dda87fe7f4c5d8d8ed78e0ea73c2794d6b26c97 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 11:28:35 -0700 Subject: [PATCH 02/44] Add XMLHttpRequest constructor to ScriptEngine --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 402f1a2885..41ed07cc23 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -34,6 +34,7 @@ #include "MenuItemProperties.h" #include "LocalVoxels.h" #include "ScriptEngine.h" +#include "XMLHttpRequestClass.h" VoxelsScriptingInterface ScriptEngine::_voxelsScriptingInterface; ParticlesScriptingInterface ScriptEngine::_particlesScriptingInterface; @@ -214,6 +215,9 @@ void ScriptEngine::init() { qScriptRegisterSequenceMetaType >(&_engine); qScriptRegisterSequenceMetaType >(&_engine); + QScriptValue xmlHttpRequestConstructorValue = _engine.newFunction(XMLHttpRequestClass::constructor); + _engine.globalObject().setProperty("XMLHttpRequest", xmlHttpRequestConstructorValue); + QScriptValue printConstructorValue = _engine.newFunction(debugPrint); _engine.globalObject().setProperty("print", printConstructorValue); From 39e6e61a6d2026c3c57da3a425566551a6fca982 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 11:31:33 -0700 Subject: [PATCH 03/44] Add XMLHttpRequest tests and examples --- examples/Test.js | 68 +++++++++++++++ examples/streetAreaExample.js | 51 ++++++++++++ examples/testXMLHttpRequest.js | 147 +++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 examples/Test.js create mode 100644 examples/streetAreaExample.js create mode 100644 examples/testXMLHttpRequest.js diff --git a/examples/Test.js b/examples/Test.js new file mode 100644 index 0000000000..c56c7ebdc1 --- /dev/null +++ b/examples/Test.js @@ -0,0 +1,68 @@ +// +// Test.js +// examples +// +// Created by Ryan Huffman on 5/4//14 +// Copyright 2014 High Fidelity, Inc. +// +// This provides very basic unit testing functionality. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +test = function(name, func) { + print("Running test: " + name); + + var unitTest = new UnitTest(name, func); + + try { + unitTest.run(); + print(" Success: " + unitTest.numAssertions + " assertions passed"); + } catch (error) { + print(" Failure: " + error.message); + } +}; + +AssertionException = function(expected, actual, message) { + print("Creating exception"); + this.message = message + "\n: " + actual + " != " + expected; + this.name = 'AssertionException'; +}; + +UnitTest = function(name, func) { + this.numAssertions = 0; + this.func = func; +}; + +UnitTest.prototype.run = function() { + this.func(); +}; + +UnitTest.prototype.assertNotEquals = function(expected, actual, message) { + this.numAssertions++; + if (expected == actual) { + throw new AssertionException(expected, actual, message); + } +}; + +UnitTest.prototype.assertEquals = function(expected, actual, message) { + this.numAssertions++; + if (expected != actual) { + throw new AssertionException(expected, actual, message); + } +}; + +UnitTest.prototype.assertHasProperty = function(property, actual, message) { + this.numAssertions++; + if (actual[property] === undefined) { + throw new AssertionException(property, actual, message); + } +}; + +UnitTest.prototype.assertNull = function(value, message) { + this.numAssertions++; + if (value !== null) { + throw new AssertionException(value, null, message); + } +}; diff --git a/examples/streetAreaExample.js b/examples/streetAreaExample.js new file mode 100644 index 0000000000..5e92753689 --- /dev/null +++ b/examples/streetAreaExample.js @@ -0,0 +1,51 @@ +// +// streetAreaExample.js +// examples +// +// Created by Ryan Huffman on 5/4//14 +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script showing how to load JSON data using XMLHttpRequest. +// +// URL Macro created by Thijs Wenker. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var url = "https://script.google.com/macros/s/AKfycbwIo4lmF-qUwX1Z-9eA_P-g2gse9oFhNcjVyyksGukyDDEFXgU/exec?action=listOwners&domain=alpha.highfidelity.io"; +print("Loading street data from " + url); + +var req = new XMLHttpRequest(); + +// Set response type to "json". This will tell XMLHttpRequest to parse the response data as json, so req.response can be used +// as a regular javascript object +req.responseType = 'json'; + +req.open("GET", url, false); +req.send(); + +if (req.status == 200) { + for (var domain in req.response) { + print("DOMAIN: " + domain); + var locations = req.response[domain]; + var userAreas = []; + for (var i = 0; i < locations.length; i++) { + var loc = locations[i]; + var x1 = loc[1], + y1 = loc[2], + x2 = loc[3], + y2 = loc[4]; + userAreas.push({ + username: loc[0], + area: Math.abs(x2 - x1) * Math.abs(y2 - y1), + }); + } + userAreas.sort(function(a, b) { return a.area > b.area ? -1 : (a.area < b.area ? 1 : 0) }); + for (var i = 0; i < userAreas.length; i++) { + print(userAreas[i].username + ": " + userAreas[i].area + " sq units"); + } + } +} else { + print("Error loading data: " + req.status + " " + req.statusText + ", " + req.errorCode); +} diff --git a/examples/testXMLHttpRequest.js b/examples/testXMLHttpRequest.js new file mode 100644 index 0000000000..6471b2fdeb --- /dev/null +++ b/examples/testXMLHttpRequest.js @@ -0,0 +1,147 @@ +// +// testXMLHttpRequest.js +// examples +// +// Created by Ryan Huffman on 5/4//14 +// Copyright 2014 High Fidelity, Inc. +// +// XMLHttpRequest Tests +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include("Test.js"); + +test("Test default request values", function(finished) { + var req = new XMLHttpRequest(); + + this.assertEquals(req.UNSENT, req.readyState, "readyState should be UNSENT"); + this.assertEquals(0, req.status, "status should be `0` by default"); + this.assertEquals("", req.statusText, "statusText should be empty string by default"); + this.assertEquals("", req.getAllResponseHeaders(), "getAllResponseHeaders() should return empty string by default"); + this.assertEquals("", req.response, "response should be empty string by default"); + this.assertEquals("", req.responseText, "responseText should be empty string by default"); + this.assertEquals("", req.responseType, "responseType should be empty string by default"); + this.assertEquals(0, req.timeout, "timeout should be `0` by default"); + this.assertEquals(0, req.errorCode, "there should be no error by default"); +}); + + +test("Test readyStates", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + var statesVisited = [true, false, false, false, false] + + req.onreadystatechange = function() { + statesVisited[req.readyState] = true; + }; + + req.open("GET", "https://gist.githubusercontent.com/huffman/33cc618fec183d1bccd0/raw/test.json", false); + req.send(); + for (var i = 0; i <= req.DONE; i++) { + this.assertEquals(true, statesVisited[i], i + " should be set"); + } + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); +}); + +test("Test TEXT request", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.open("GET", "https://gist.githubusercontent.com/huffman/33cc618fec183d1bccd0/raw/test.json", false); + req.send(); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(200, req.status, "status should be `200`"); + this.assertEquals(0, req.errorCode); + this.assertEquals("OK", req.statusText, "statusText should be `OK`"); + this.assertNotEquals("", req.getAllResponseHeaders(), "headers should no longer be empty string"); + this.assertNull(req.getResponseHeader('invalidheader'), "invalid header should return `null`"); + this.assertEquals("GitHub.com", req.getResponseHeader('Server'), "Server header should be GitHub.com"); + this.assertEquals('{"id": 1}', req.response); + this.assertEquals('{"id": 1}', req.responseText); +}); + +test("Test JSON request", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.responseType = "json"; + req.open("GET", "https://gist.githubusercontent.com/huffman/33cc618fec183d1bccd0/raw/test.json", false); + req.send(); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(200, req.status, "status should be `200`"); + this.assertEquals(0, req.errorCode); + this.assertEquals("OK", req.statusText, "statusText should be `OK`"); + this.assertNotEquals("", req.getAllResponseHeaders(), "headers should no longer be empty string"); + this.assertNull(req.getResponseHeader('invalidheader'), "invalid header should return `null`"); + this.assertEquals("GitHub.com", req.getResponseHeader('Server'), "Server header should be GitHub.com"); + this.assertHasProperty('id', req.response); + this.assertEquals(1, req.response.id); + this.assertEquals('{"id": 1}', req.responseText); +}); + +test("Test Bad URL", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.open("POST", "hifi://domain/path", false); + req.send(); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertNotEquals(0, req.errorCode); +}); + +test("Test Bad Method Error", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.open("POST", "https://www.google.com", false); + + req.send("randomdata"); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(405, req.status); + this.assertEquals(202, req.errorCode); + + req.open("POST", "https://www.google.com", false) + req.send(); + + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(405, req.status); + this.assertEquals(202, req.errorCode); +}); + +test("Test abort", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + + req.open("POST", "https://www.google.com", true) + req.send(); + req.abort(); + + this.assertEquals(0, req.status); + this.assertEquals(0, req.errorCode); +}); + +test("Test timeout", function() { + var req = new XMLHttpRequest(); + var state = req.readyState; + var timedOut = false; + + req.ontimeout = function() { + timedOut = true; + }; + + req.open("POST", "https://gist.githubusercontent.com/huffman/33cc618fec183d1bccd0/raw/test.json", false) + req.timeout = 1; + req.send(); + + this.assertEquals(true, timedOut, "request should have timed out"); + this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); + this.assertEquals(0, req.status, "status should be `0`"); + this.assertEquals(4, req.errorCode, "4 is the timeout error code for QNetworkReply::NetworkError"); +}); From 5a7b133d2556bae5b9620e67b219c3e692a707a8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 11:38:12 -0700 Subject: [PATCH 04/44] Fix dates in XMLHttpRequest js files --- examples/Test.js | 2 +- examples/streetAreaExample.js | 2 +- examples/testXMLHttpRequest.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/Test.js b/examples/Test.js index c56c7ebdc1..056ec3cbbf 100644 --- a/examples/Test.js +++ b/examples/Test.js @@ -2,7 +2,7 @@ // Test.js // examples // -// Created by Ryan Huffman on 5/4//14 +// Created by Ryan Huffman on 5/4/14 // Copyright 2014 High Fidelity, Inc. // // This provides very basic unit testing functionality. diff --git a/examples/streetAreaExample.js b/examples/streetAreaExample.js index 5e92753689..deb10dd65a 100644 --- a/examples/streetAreaExample.js +++ b/examples/streetAreaExample.js @@ -2,7 +2,7 @@ // streetAreaExample.js // examples // -// Created by Ryan Huffman on 5/4//14 +// Created by Ryan Huffman on 5/4/14 // Copyright 2014 High Fidelity, Inc. // // This is an example script showing how to load JSON data using XMLHttpRequest. diff --git a/examples/testXMLHttpRequest.js b/examples/testXMLHttpRequest.js index 6471b2fdeb..421eb458e4 100644 --- a/examples/testXMLHttpRequest.js +++ b/examples/testXMLHttpRequest.js @@ -2,7 +2,7 @@ // testXMLHttpRequest.js // examples // -// Created by Ryan Huffman on 5/4//14 +// Created by Ryan Huffman on 5/4/14 // Copyright 2014 High Fidelity, Inc. // // XMLHttpRequest Tests From f3305a51e28047feaad47b43bac1ea9735af6200 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 May 2014 13:09:49 -0700 Subject: [PATCH 05/44] Remove XMLHttpRequestClass::escapeJavascriptString --- libraries/script-engine/src/XMLHttpRequestClass.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index b1b33e4cc6..bff88d91a9 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -62,7 +62,6 @@ public: int getDone() const { return DONE; }; static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); - static QString escapeJavascriptString(const QString& jsString); int getTimeout() const { return _timeout; } void setTimeout(int timeout) { _timeout = timeout; } From cd78be7d2500a2a4a00bd19f1c147302969aab50 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 8 May 2014 16:47:18 -0700 Subject: [PATCH 06/44] Some work on mouse tools for editModels.js --- examples/editModels.js | 34 +++++++++++++++++++++++++++++++++- examples/testScript.js | 0 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 examples/testScript.js diff --git a/examples/editModels.js b/examples/editModels.js index ecf398edfa..737b6a85a5 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -33,6 +33,7 @@ var modelURLs = [ "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx", ]; +var tools = []; var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; var numberOfTools = 1; var toolHeight = 50; @@ -44,13 +45,44 @@ var toolsY = (windowDimensions.y - toolsHeight) / 2; var firstModel = Overlays.addOverlay("image", { - x: 0, y: 0, width: toolWidth, height: toolHeight, subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, imageURL: toolIconUrl + "voxel-tool.svg", x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight, visible: true, alpha: 0.9 }); +function Tool(iconURL) { + this.overlay = Overlays.addOverlay("image", { + subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, + imageURL: iconURL, + x: toolsX, + y: toolsY + ((toolHeight + toolVerticalSpacing) * tools.length), + width: toolWidth, + height: toolHeight, + visible: true, + alpha: 0.9 + }); + + + this.cleanup = function() { + Ovelays.deleteOverlay(this.overlay); + } + tools[tools.length] = this; + return tools.length - 1; +} +Tool.ICON_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; +Tool.HEIGHT = 50; +Tool.WIDTH = 50; + +function ToolBar(direction, x, y) { + this.tools = []; + + this.numberOfTools = function() { + return this.tools.length; + } +} +ToolBar.SPACING = 4; + function controller(wichSide) { this.side = wichSide; diff --git a/examples/testScript.js b/examples/testScript.js new file mode 100644 index 0000000000..e69de29bb2 From 106c8bffd842fc28a4c3c32cba8351c15f61f9f3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 9 May 2014 16:08:06 -0700 Subject: [PATCH 07/44] first cut at animations in models --- assignment-client/CMakeLists.txt | 1 + examples/placeModelsWithHands.js | 22 +++ interface/CMakeLists.txt | 1 + interface/src/models/ModelTreeRenderer.cpp | 62 +++++-- interface/src/models/ModelTreeRenderer.h | 6 +- interface/src/renderer/Model.cpp | 21 ++- interface/src/renderer/Model.h | 2 + .../src/AnimationCache.cpp | 4 +- .../src/AnimationCache.h | 3 + .../src/AnimationObject.cpp | 0 .../src/AnimationObject.h | 0 libraries/fbx/src/FBXReader.cpp | 1 + libraries/fbx/src/FBXReader.h | 2 + libraries/models/CMakeLists.txt | 1 + libraries/models/src/ModelItem.cpp | 156 +++++++++++++++++- libraries/models/src/ModelItem.h | 34 +++- libraries/networking/src/PacketHeaders.cpp | 2 + libraries/particles/CMakeLists.txt | 1 + libraries/script-engine/CMakeLists.txt | 1 + libraries/script-engine/src/ScriptEngine.h | 5 +- 20 files changed, 297 insertions(+), 28 deletions(-) rename libraries/{script-engine => animation}/src/AnimationCache.cpp (98%) rename libraries/{script-engine => animation}/src/AnimationCache.h (96%) rename libraries/{script-engine => animation}/src/AnimationObject.cpp (100%) rename libraries/{script-engine => animation}/src/AnimationObject.h (100%) diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index e783001228..6436ffbd75 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -33,6 +33,7 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(embedded-webserver ${TARGET_NAME} "${ROOT_DIR}") diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index e1ac151fe4..f16945472d 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -37,6 +37,7 @@ var radiusMinimum = 0.05; var radiusMaximum = 0.5; var modelURLs = [ + "http://www.fungibleinsight.com/faces/beta.fst", "https://s3-us-west-1.amazonaws.com/highfidelity-public/models/attachments/topHat.fst", "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/Feisar_Ship.FBX", "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/birarda/birarda_head.fbx", @@ -48,6 +49,19 @@ var modelURLs = [ "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx", ]; +var animationURLs = [ + "http://www.fungibleinsight.com/faces/gangnam_style_2.fbx", + "", + "", + "", + "", + "", + "", + "", + "", + "", +]; + var currentModelURL = 1; var numModels = modelURLs.length; @@ -214,10 +228,18 @@ function checkControllerSide(whichSide) { modelRotation: palmRotation, modelURL: modelURLs[currentModelURL] }; + + if (animationURLs[currentModelURL] !== "") { + properties.animationURL = animationURLs[currentModelURL]; + } debugPrint("modelRadius=" +modelRadius); newModel = Models.addModel(properties); + + print("just added model... newModel=" + newModel.creatorTokenID); + print("properties.animationURL=" + properties.animationURL); + if (whichSide == LEFT_PALM) { leftModelAlreadyInHand = true; leftHandModel = newModel; diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0a56109260..4d02415df2 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -127,6 +127,7 @@ link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(avatars ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(audio ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") # find any optional libraries diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index c762182290..e5c55ef066 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -20,11 +20,16 @@ ModelTreeRenderer::ModelTreeRenderer() : } ModelTreeRenderer::~ModelTreeRenderer() { - // delete the models in _modelsItemModels - foreach(Model* model, _modelsItemModels) { + // delete the models in _knownModelsItemModels + foreach(Model* model, _knownModelsItemModels) { delete model; } - _modelsItemModels.clear(); + _knownModelsItemModels.clear(); + + foreach(Model* model, _unknownModelsItemModels) { + delete model; + } + _unknownModelsItemModels.clear(); } void ModelTreeRenderer::init() { @@ -43,17 +48,27 @@ void ModelTreeRenderer::render(RenderMode renderMode) { OctreeRenderer::render(renderMode); } -Model* ModelTreeRenderer::getModel(const QString& url) { +Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { Model* model = NULL; - // if we don't already have this model then create it and initialize it - if (_modelsItemModels.find(url) == _modelsItemModels.end()) { - model = new Model(); - model->init(); - model->setURL(QUrl(url)); - _modelsItemModels[url] = model; + if (modelItem.isKnownID()) { + if (_knownModelsItemModels.find(modelItem.getID()) != _knownModelsItemModels.end()) { + model = _knownModelsItemModels[modelItem.getID()]; + } else { + model = new Model(); + model->init(); + model->setURL(QUrl(modelItem.getModelURL())); + _knownModelsItemModels[modelItem.getID()] = model; + } } else { - model = _modelsItemModels[url]; + if (_unknownModelsItemModels.find(modelItem.getCreatorTokenID()) != _unknownModelsItemModels.end()) { + model = _unknownModelsItemModels[modelItem.getCreatorTokenID()]; + } else { + model = new Model(); + model->init(); + model->setURL(QUrl(modelItem.getModelURL())); + _unknownModelsItemModels[modelItem.getCreatorTokenID()] = model; + } } return model; } @@ -63,7 +78,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // we need to iterate the actual modelItems of the element ModelTreeElement* modelTreeElement = (ModelTreeElement*)element; - const QList& modelItems = modelTreeElement->getModels(); + QList& modelItems = modelTreeElement->getModels(); uint16_t numberOfModels = modelItems.size(); @@ -139,7 +154,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) } for (uint16_t i = 0; i < numberOfModels; i++) { - const ModelItem& modelItem = modelItems[i]; + ModelItem& modelItem = modelItems[i]; // render modelItem aspoints AABox modelBox = modelItem.getAABox(); modelBox.scale(TREE_SCALE); @@ -156,7 +171,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) glPushMatrix(); const float alpha = 1.0f; - Model* model = getModel(modelItem.getModelURL()); + Model* model = getModel(modelItem); model->setScaleToFit(true, radius * 2.0f); model->setSnapModelToCenter(true); @@ -167,6 +182,25 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); + + qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); + qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); + qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); + + // handle animations.. + if (modelItem.hasAnimation()) { + if (!modelItem.jointsMapped()) { + QStringList modelJointNames = model->getJointNames(); + modelItem.mapJoints(modelJointNames); + } + + QVector frameData = modelItem.getAnimationFrame(); + for (int i = 0; i < frameData.size(); i++) { + model->setJointState(i, true, frameData[i]); + } + } + + // make sure to simulate so everything gets set up correctly for rendering model->simulate(0.0f); // TODO: should we allow modelItems to have alpha on their models? diff --git a/interface/src/models/ModelTreeRenderer.h b/interface/src/models/ModelTreeRenderer.h index 7af5bbf317..e0b8d7d0a2 100644 --- a/interface/src/models/ModelTreeRenderer.h +++ b/interface/src/models/ModelTreeRenderer.h @@ -49,9 +49,9 @@ public: virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); protected: - Model* getModel(const QString& url); - - QMap _modelsItemModels; + Model* getModel(const ModelItem& modelItem); + QMap _knownModelsItemModels; + QMap _unknownModelsItemModels; }; #endif // hifi_ModelTreeRenderer_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index f46fd48beb..b2c1665c2f 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -430,7 +430,8 @@ Extents Model::getMeshExtents() const { return Extents(); } const Extents& extents = _geometry->getFBXGeometry().meshExtents; - Extents scaledExtents = { extents.minimum * _scale, extents.maximum * _scale }; + glm::vec3 scale = _scale * _geometry->getFBXGeometry().fstScaled; + Extents scaledExtents = { extents.minimum * scale, extents.maximum * scale }; return scaledExtents; } @@ -438,8 +439,13 @@ Extents Model::getUnscaledMeshExtents() const { if (!isActive()) { return Extents(); } + const Extents& extents = _geometry->getFBXGeometry().meshExtents; - return extents; + + // even though our caller asked for "unscaled" we need to include any fst scaling + float scale = _geometry->getFBXGeometry().fstScaled; + Extents scaledExtents = { extents.minimum * scale, extents.maximum * scale }; + return scaledExtents; } bool Model::getJointState(int index, glm::quat& rotation) const { @@ -573,6 +579,17 @@ bool Model::getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind) return true; } +QStringList Model::getJointNames() const { + if (QThread::currentThread() != thread()) { + QStringList result; + QMetaObject::invokeMethod(const_cast(this), "getJointNames", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QStringList, result)); + return result; + } + return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList(); +} + + void Model::clearShapes() { for (int i = 0; i < _jointShapes.size(); ++i) { delete _jointShapes[i]; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 1a469c8122..b7a42930dc 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -182,6 +182,8 @@ public: bool getJointPosition(int jointIndex, glm::vec3& position) const; bool getJointRotation(int jointIndex, glm::quat& rotation, bool fromBind = false) const; + + QStringList getJointNames() const; void clearShapes(); void rebuildShapes(); diff --git a/libraries/script-engine/src/AnimationCache.cpp b/libraries/animation/src/AnimationCache.cpp similarity index 98% rename from libraries/script-engine/src/AnimationCache.cpp rename to libraries/animation/src/AnimationCache.cpp index 8e1493f075..ce7e4cdf36 100644 --- a/libraries/script-engine/src/AnimationCache.cpp +++ b/libraries/animation/src/AnimationCache.cpp @@ -36,7 +36,8 @@ QSharedPointer AnimationCache::createResource(const QUrl& url, const Q } Animation::Animation(const QUrl& url) : - Resource(url) { + Resource(url), + _isValid(false) { } class AnimationReader : public QRunnable { @@ -93,6 +94,7 @@ QVector Animation::getFrames() const { void Animation::setGeometry(const FBXGeometry& geometry) { _geometry = geometry; finishedLoading(true); + _isValid = true; } void Animation::downloadFinished(QNetworkReply* reply) { diff --git a/libraries/script-engine/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h similarity index 96% rename from libraries/script-engine/src/AnimationCache.h rename to libraries/animation/src/AnimationCache.h index 23183adf10..392443e7b5 100644 --- a/libraries/script-engine/src/AnimationCache.h +++ b/libraries/animation/src/AnimationCache.h @@ -53,6 +53,8 @@ public: Q_INVOKABLE QStringList getJointNames() const; Q_INVOKABLE QVector getFrames() const; + + bool isValid() const { return _isValid; } protected: @@ -63,6 +65,7 @@ protected: private: FBXGeometry _geometry; + bool _isValid; }; #endif // hifi_AnimationCache_h diff --git a/libraries/script-engine/src/AnimationObject.cpp b/libraries/animation/src/AnimationObject.cpp similarity index 100% rename from libraries/script-engine/src/AnimationObject.cpp rename to libraries/animation/src/AnimationObject.cpp diff --git a/libraries/script-engine/src/AnimationObject.h b/libraries/animation/src/AnimationObject.h similarity index 100% rename from libraries/script-engine/src/AnimationObject.h rename to libraries/animation/src/AnimationObject.h diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 1fc03ceb66..f743a383d1 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1385,6 +1385,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) // get offset transform from mapping float offsetScale = mapping.value("scale", 1.0f).toFloat(); + geometry.fstScaled = offsetScale; glm::quat offsetRotation = glm::quat(glm::radians(glm::vec3(mapping.value("rx").toFloat(), mapping.value("ry").toFloat(), mapping.value("rz").toFloat()))); geometry.offset = glm::translate(glm::vec3(mapping.value("tx").toFloat(), mapping.value("ty").toFloat(), diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 51e7380181..567a32c1cb 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -212,6 +212,8 @@ public: Extents bindExtents; Extents meshExtents; + float fstScaled; + QVector animationFrames; QVector attachments; diff --git a/libraries/models/CMakeLists.txt b/libraries/models/CMakeLists.txt index 062352e50c..1e70942872 100644 --- a/libraries/models/CMakeLists.txt +++ b/libraries/models/CMakeLists.txt @@ -28,6 +28,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # for streamable link_hifi_library(metavoxels ${TARGET_NAME} "${ROOT_DIR}") diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index af8500bf25..785183da56 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -85,6 +85,12 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& _shouldDie = false; _modelURL = MODEL_DEFAULT_MODEL_URL; _modelRotation = MODEL_DEFAULT_MODEL_ROTATION; + + // animation related + _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _frameIndex = 0.0f; + _jointMappingCompleted = false; + _lastAnimated = now; setProperties(properties); } @@ -110,6 +116,12 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t _shouldDie = false; _modelURL = MODEL_DEFAULT_MODEL_URL; _modelRotation = MODEL_DEFAULT_MODEL_ROTATION; + + // animation related + _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _frameIndex = 0.0f; + _jointMappingCompleted = false; + _lastAnimated = now; } bool ModelItem::appendModelData(OctreePacketData* packetData) const { @@ -150,6 +162,16 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { if (success) { success = packetData->appendValue(getModelRotation()); } + + // animationURL + if (success) { + uint16_t animationURLLength = _animationURL.size() + 1; // include NULL + success = packetData->appendValue(animationURLLength); + if (success) { + success = packetData->appendRawData((const unsigned char*)qPrintable(_animationURL), animationURLLength); + } + } + return success; } @@ -215,7 +237,7 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(modelURLLength); bytesRead += sizeof(modelURLLength); QString modelURLString((const char*)dataAt); - _modelURL = modelURLString; + setModelURL(modelURLString); dataAt += modelURLLength; bytesRead += modelURLLength; @@ -224,6 +246,19 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += bytes; bytesRead += bytes; + // animationURL + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + bytesRead += sizeof(animationURLLength); + QString animationURLString((const char*)dataAt); + setAnimationURL(animationURLString); + dataAt += animationURLLength; + bytesRead += animationURLLength; + +qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + + //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); } return bytesRead; @@ -346,6 +381,21 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes += bytes; } + // animationURL + if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_URL) == MODEL_PACKET_CONTAINS_ANIMATION_URL)) { + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + processedBytes += sizeof(animationURLLength); + QString tempString((const char*)dataAt); + newModelItem._animationURL = tempString; + dataAt += animationURLLength; + processedBytes += animationURLLength; + +qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); + + } + const bool wantDebugging = false; if (wantDebugging) { qDebug("ModelItem::fromEditPacket()..."); @@ -476,6 +526,21 @@ bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id sizeOut += bytes; } + // animationURL + if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_URL) == MODEL_PACKET_CONTAINS_ANIMATION_URL)) { + uint16_t urlLength = properties.getAnimationURL().size() + 1; + memcpy(copyAt, &urlLength, sizeof(urlLength)); + copyAt += sizeof(urlLength); + sizeOut += sizeof(urlLength); + memcpy(copyAt, qPrintable(properties.getAnimationURL()), urlLength); + copyAt += urlLength; + sizeOut += urlLength; + +qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable(properties.getAnimationURL()); + + } + + bool wantDebugging = false; if (wantDebugging) { qDebug("encodeModelItemEditMessageDetails()...."); @@ -521,6 +586,66 @@ void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssi } } + +QMap ModelItem::_loadedAnimations; // TODO: cleanup?? +AnimationCache ModelItem::_animationCache; + +Animation* ModelItem::getAnimation(const QString& url) { + AnimationPointer animation; + + // if we don't already have this model then create it and initialize it + if (_loadedAnimations.find(url) == _loadedAnimations.end()) { + animation = _animationCache.getAnimation(url); + _loadedAnimations[url] = animation; + } else { + animation = _loadedAnimations[url]; + } + return animation.data(); +} + +void ModelItem::mapJoints(const QStringList& modelJointNames) { + // if we don't have animation, or we're already joint mapped then bail early + if (!hasAnimation() || _jointMappingCompleted) { + return; + } + + Animation* myAnimation = getAnimation(_animationURL); + + if (!_jointMappingCompleted) { + QStringList animationJointNames = myAnimation->getJointNames(); + if (modelJointNames.size() > 0 && animationJointNames.size() > 0) { + _jointMapping.resize(modelJointNames.size()); + for (int i = 0; i < modelJointNames.size(); i++) { + _jointMapping[i] = animationJointNames.indexOf(modelJointNames[i]); + } + _jointMappingCompleted = true; + } + } +} + +QVector ModelItem::getAnimationFrame() { + QVector frameData; + if (hasAnimation() && _jointMappingCompleted) { + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; + Animation* myAnimation = getAnimation(_animationURL); + QVector frames = myAnimation->getFrames(); + int frameIndex = (int)std::floor(_frameIndex) % frames.size(); + QVector rotations = frames[frameIndex].rotations; + frameData.resize(_jointMapping.size()); + for (int j = 0; j < _jointMapping.size(); j++) { + int rotationIndex = _jointMapping[j]; + if (rotationIndex != -1 && rotationIndex < rotations.size()) { + frameData[j] = rotations[rotationIndex]; + } + } + } + return frameData; +} + void ModelItem::update(const quint64& now) { _lastUpdated = now; setShouldDie(getShouldDie()); @@ -547,6 +672,7 @@ ModelItemProperties::ModelItemProperties() : _shouldDie(false), _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), + _animationURL(""), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -558,6 +684,7 @@ ModelItemProperties::ModelItemProperties() : _shouldDieChanged(false), _modelURLChanged(false), _modelRotationChanged(false), + _animationURLChanged(false), _defaultSettings(true) { } @@ -589,6 +716,11 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_MODEL_ROTATION; } + if (_animationURLChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; + } + + return changedBits; } @@ -611,6 +743,7 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const QScriptValue modelRotation = quatToScriptValue(engine, _modelRotation); properties.setProperty("modelRotation", modelRotation); + properties.setProperty("animationURL", _animationURL); if (_idSet) { properties.setProperty("id", _id); @@ -707,6 +840,16 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } + QScriptValue animationURL = object.property("animationURL"); + if (animationURL.isValid()) { + QString newAnimationURL; + newAnimationURL = animationURL.toVariant().toString(); + if (_defaultSettings || newAnimationURL != _animationURL) { + _animationURL = newAnimationURL; + _animationURLChanged = true; + } + } + _lastEdited = usecTimestampNow(); } @@ -741,7 +884,14 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { modelItem.setModelRotation(_modelRotation); somethingChanged = true; } - + + if (_animationURLChanged) { + modelItem.setAnimationURL(_animationURL); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; + } + if (somethingChanged) { bool wantDebug = false; if (wantDebug) { @@ -761,6 +911,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _shouldDie = modelItem.getShouldDie(); _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); + _animationURL = modelItem.getAnimationURL(); _id = modelItem.getID(); _idSet = true; @@ -772,6 +923,7 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _shouldDieChanged = false; _modelURLChanged = false; _modelRotationChanged = false; + _animationURLChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 9edcf482c0..72a12c9b2c 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -39,14 +40,16 @@ const uint32_t UNKNOWN_MODEL_ID = 0xFFFFFFFF; const uint16_t MODEL_PACKET_CONTAINS_RADIUS = 1; const uint16_t MODEL_PACKET_CONTAINS_POSITION = 2; const uint16_t MODEL_PACKET_CONTAINS_COLOR = 4; -const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 512; -const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 1024; -const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 2048; +const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 8; +const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 16; +const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; +const QString MODEL_DEFAULT_ANIMATION_URL(""); /// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model /// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of @@ -69,6 +72,7 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } + const QString& getAnimationURL() const { return _animationURL; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -82,6 +86,7 @@ public: // model related properties void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } + void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -97,6 +102,7 @@ private: QString _modelURL; glm::quat _modelRotation; + QString _animationURL; uint32_t _id; bool _idSet; @@ -109,6 +115,7 @@ private: bool _modelURLChanged; bool _modelRotationChanged; + bool _animationURLChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -178,6 +185,8 @@ public: bool hasModel() const { return !_modelURL.isEmpty(); } const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } + bool hasAnimation() const { return !_animationURL.isEmpty(); } + const QString& getAnimationURL() const { return _animationURL; } ModelItemID getModelItemID() const { return ModelItemID(getID(), getCreatorTokenID(), getID() != UNKNOWN_MODEL_ID); } ModelItemProperties getProperties() const; @@ -196,6 +205,7 @@ public: bool getShouldDie() const { return _shouldDie; } uint32_t getCreatorTokenID() const { return _creatorTokenID; } bool isNewlyCreated() const { return _newlyCreated; } + bool isKnownID() const { return getID() != UNKNOWN_MODEL_ID; } /// set position in domain scale units (0.0 - 1.0) void setPosition(const glm::vec3& value) { _position = value; } @@ -215,6 +225,7 @@ public: // model related properties void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } + void setAnimationURL(const QString& url) { _animationURL = url; } void setProperties(const ModelItemProperties& properties); @@ -239,6 +250,10 @@ public: static uint32_t getNextCreatorTokenID(); static void handleAddModelResponse(const QByteArray& packet); + void mapJoints(const QStringList& modelJointNames); + QVector getAnimationFrame(); + bool jointsMapped() const { return _jointMappingCompleted; }; + protected: glm::vec3 _position; rgbColor _color; @@ -256,10 +271,23 @@ protected: quint64 _lastUpdated; quint64 _lastEdited; + quint64 _lastAnimated; + + QString _animationURL; + float _frameIndex; // we keep this as a float and round to int only when we need the exact index + bool _jointMappingCompleted; + QVector _jointMapping; + // used by the static interfaces for creator token ids static uint32_t _nextCreatorTokenID; static std::map _tokenIDsToIDs; + + + static Animation* getAnimation(const QString& url); + static QMap _loadedAnimations; + static AnimationCache _animationCache; + }; #endif // hifi_ModelItem_h diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 0785b81581..b9eee6e0c9 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -66,6 +66,8 @@ PacketVersion versionForPacketType(PacketType type) { return 1; case PacketTypeOctreeStats: return 1; + case PacketTypeModelData: + return 1; default: return 0; } diff --git a/libraries/particles/CMakeLists.txt b/libraries/particles/CMakeLists.txt index 1cb60756a2..8cd2f30012 100644 --- a/libraries/particles/CMakeLists.txt +++ b/libraries/particles/CMakeLists.txt @@ -25,6 +25,7 @@ link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(octree ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(networking ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # link ZLIB and GnuTLS find_package(ZLIB) diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index ee918ff864..0374ad570c 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -27,6 +27,7 @@ link_hifi_library(voxels ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(particles ${TARGET_NAME} "${ROOT_DIR}") link_hifi_library(models ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(animation ${TARGET_NAME} "${ROOT_DIR}") # link ZLIB find_package(ZLIB) diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 09d41e3e2e..96cc874453 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -18,13 +18,12 @@ #include #include +#include #include -#include - #include #include +#include -#include "AnimationCache.h" #include "AbstractControllerScriptingInterface.h" #include "Quat.h" #include "ScriptUUID.h" From d8488b34efafaf54caa5e3cb1516333cf598b05c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 9 May 2014 16:08:16 -0700 Subject: [PATCH 08/44] first cut at animations in models --- libraries/animation/CMakeLists.txt | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 libraries/animation/CMakeLists.txt diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt new file mode 100644 index 0000000000..36088ba4bd --- /dev/null +++ b/libraries/animation/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 2.8) + +if (WIN32) + cmake_policy (SET CMP0020 NEW) +endif (WIN32) + +set(ROOT_DIR ../..) +set(MACRO_DIR "${ROOT_DIR}/cmake/macros") + +# setup for find modules +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/modules/") + +set(TARGET_NAME animation) + +find_package(Qt5Widgets REQUIRED) + +include(${MACRO_DIR}/SetupHifiLibrary.cmake) +setup_hifi_library(${TARGET_NAME}) + +include(${MACRO_DIR}/IncludeGLM.cmake) +include_glm(${TARGET_NAME} "${ROOT_DIR}") + +include(${MACRO_DIR}/LinkHifiLibrary.cmake) +link_hifi_library(shared ${TARGET_NAME} "${ROOT_DIR}") +link_hifi_library(fbx ${TARGET_NAME} "${ROOT_DIR}") + +# link ZLIB +find_package(ZLIB) +find_package(GnuTLS REQUIRED) + +# add a definition for ssize_t so that windows doesn't bail on gnutls.h +if (WIN32) + add_definitions(-Dssize_t=long) +endif () + +include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${GNUTLS_INCLUDE_DIR}") +target_link_libraries(${TARGET_NAME} "${ZLIB_LIBRARIES}" "${GNUTLS_LIBRARY}" Qt5::Widgets) From 3ffd4cd65dd8675e5561b77d35b423b189c9da73 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 09:13:53 -0700 Subject: [PATCH 09/44] removed some debug --- interface/src/models/ModelTreeRenderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index e5c55ef066..36a22621b2 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -183,9 +183,11 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); + /* qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); + */ // handle animations.. if (modelItem.hasAnimation()) { From 02ca7c75eb1feb084609f47445915f668499fc94 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 11:02:10 -0700 Subject: [PATCH 10/44] first cut at adding version parsing to model items, not really working --- libraries/models/src/ModelItem.cpp | 23 ++++++++++++----------- libraries/models/src/ModelItem.h | 2 ++ libraries/octree/src/Octree.cpp | 6 ++++-- libraries/octree/src/Octree.h | 7 +++++-- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 785183da56..1c735c2075 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -246,18 +246,19 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += bytes; bytesRead += bytes; - // animationURL - uint16_t animationURLLength; - memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); - dataAt += sizeof(animationURLLength); - bytesRead += sizeof(animationURLLength); - QString animationURLString((const char*)dataAt); - setAnimationURL(animationURLString); - dataAt += animationURLLength; - bytesRead += animationURLLength; - -qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + if (args.bitstreamVersion >= VERSION_MODELS_HAVE_ANIMATION) { + // animationURL + uint16_t animationURLLength; + memcpy(&animationURLLength, dataAt, sizeof(animationURLLength)); + dataAt += sizeof(animationURLLength); + bytesRead += sizeof(animationURLLength); + QString animationURLString((const char*)dataAt); + setAnimationURL(animationURLString); + dataAt += animationURLLength; + bytesRead += animationURLLength; + qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + } //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 72a12c9b2c..cafd150e6d 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -51,6 +51,8 @@ const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; const QString MODEL_DEFAULT_ANIMATION_URL(""); +const PacketVersion VERSION_MODELS_HAVE_ANIMATION = 1; + /// A collection of properties of a model item used in the scripting API. Translates between the actual properties of a model /// and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete set of /// model item properties via JavaScript hashes/QScriptValues diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 5b766ecdd7..9f08adb1d9 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1556,6 +1556,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, bool Octree::readFromSVOFile(const char* fileName) { bool fileOk = false; + PacketVersion gotVersion = 0; std::ifstream file(fileName, std::ios::in|std::ios::binary|std::ios::ate); if(file.is_open()) { emit importSize(1.0f, 1.0f, 1.0f); @@ -1587,7 +1588,7 @@ bool Octree::readFromSVOFile(const char* fileName) { dataAt += sizeof(expectedType); dataLength -= sizeof(expectedType); PacketVersion expectedVersion = versionForPacketType(expectedType); - PacketVersion gotVersion = *dataAt; + gotVersion = *dataAt; if (gotVersion == expectedVersion) { dataAt += sizeof(expectedVersion); dataLength -= sizeof(expectedVersion); @@ -1602,7 +1603,8 @@ bool Octree::readFromSVOFile(const char* fileName) { fileOk = true; // assume the file is ok } if (fileOk) { - ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, SharedNodePointer(), wantImportProgress); + ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS, NULL, 0, + SharedNodePointer(), wantImportProgress, gotVersion); readBitstreamToTree(dataAt, dataLength, args); } delete[] entireFile; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 4a17cb3c1d..0b9cc31a2a 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -170,6 +170,7 @@ public: QUuid sourceUUID; SharedNodePointer sourceNode; bool wantImportProgress; + PacketVersion bitstreamVersion; ReadBitstreamToTreeParams( bool includeColor = WANT_COLOR, @@ -177,13 +178,15 @@ public: OctreeElement* destinationElement = NULL, QUuid sourceUUID = QUuid(), SharedNodePointer sourceNode = SharedNodePointer(), - bool wantImportProgress = false) : + bool wantImportProgress = false, + PacketVersion bitstreamVersion = 0) : includeColor(includeColor), includeExistsBits(includeExistsBits), destinationElement(destinationElement), sourceUUID(sourceUUID), sourceNode(sourceNode), - wantImportProgress(wantImportProgress) + wantImportProgress(wantImportProgress), + bitstreamVersion(bitstreamVersion) {} }; From 8fe74d006a96429f606365f14cc82e4bc46a1698 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 11:40:15 -0700 Subject: [PATCH 11/44] correctly handle reading models.svo files with older version --- libraries/models/src/ModelItem.cpp | 3 +++ libraries/models/src/ModelTree.h | 1 + libraries/octree/src/Octree.cpp | 12 +++++++----- libraries/octree/src/Octree.h | 4 ++++ libraries/octree/src/OctreeRenderer.cpp | 3 ++- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 1c735c2075..a818105521 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -188,6 +188,7 @@ int ModelItem::expectedBytes() { } int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) { + int bytesRead = 0; if (bytesLeftToRead >= expectedBytes()) { int clockSkew = args.sourceNode ? args.sourceNode->getClockSkewUsec() : 0; @@ -258,6 +259,8 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT bytesRead += animationURLLength; qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + } else { + qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index ac25cdc003..10ef62c0a0 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -36,6 +36,7 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return true; } virtual PacketType expectedDataPacketType() const { return PacketTypeModelData; } + virtual bool canProcessVersion(PacketVersion thisVersion) const { return true; } // we support all versions virtual bool handlesEditPacketType(PacketType packetType) const; virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 9f08adb1d9..12ccf98c70 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1587,14 +1587,16 @@ bool Octree::readFromSVOFile(const char* fileName) { if (gotType == expectedType) { dataAt += sizeof(expectedType); dataLength -= sizeof(expectedType); - PacketVersion expectedVersion = versionForPacketType(expectedType); gotVersion = *dataAt; - if (gotVersion == expectedVersion) { - dataAt += sizeof(expectedVersion); - dataLength -= sizeof(expectedVersion); + if (canProcessVersion(gotVersion)) { + dataAt += sizeof(gotVersion); + dataLength -= sizeof(gotVersion); fileOk = true; + qDebug("SVO file version match. Expected: %d Got: %d", + versionForPacketType(expectedDataPacketType()), gotVersion); } else { - qDebug("SVO file version mismatch. Expected: %d Got: %d", expectedVersion, gotVersion); + qDebug("SVO file version mismatch. Expected: %d Got: %d", + versionForPacketType(expectedDataPacketType()), gotVersion); } } else { qDebug("SVO file type mismatch. Expected: %c Got: %c", expectedType, gotType); diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 0b9cc31a2a..84212586f8 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -203,6 +203,9 @@ public: // own definition. Implement these to allow your octree based server to support editing virtual bool getWantSVOfileVersions() const { return false; } virtual PacketType expectedDataPacketType() const { return PacketTypeUnknown; } + virtual bool canProcessVersion(PacketVersion thisVersion) const { + return thisVersion == versionForPacketType(expectedDataPacketType()); } + virtual PacketVersion expectedVersion() const { return versionForPacketType(expectedDataPacketType()); } virtual bool handlesEditPacketType(PacketType packetType) const { return false; } virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength, const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; } @@ -306,6 +309,7 @@ public: bool getIsViewing() const { return _isViewing; } void setIsViewing(bool isViewing) { _isViewing = isViewing; } + signals: void importSize(float x, float y, float z); diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index c1ce3cb218..1a85518181 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -64,6 +64,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar unsigned int numBytesPacketHeader = numBytesForPacketHeader(dataByteArray); QUuid sourceUUID = uuidFromPacketHeader(dataByteArray); PacketType expectedType = getExpectedPacketType(); + PacketVersion expectedVersion = _tree->expectedVersion(); // TODO: would be better to read this from the packet! if(command == expectedType) { PerformanceWarning warn(showTimingDetails, "OctreeRenderer::processDatagram expected PacketType", showTimingDetails); @@ -115,7 +116,7 @@ void OctreeRenderer::processDatagram(const QByteArray& dataByteArray, const Shar if (sectionLength) { // ask the VoxelTree to read the bitstream into the tree ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, - sourceUUID, sourceNode); + sourceUUID, sourceNode, false, expectedVersion); _tree->lockForWrite(); OctreePacketData packetData(packetIsCompressed); packetData.loadFinalizedContent(dataAt, sectionLength); From e4d2f075867f7fec8903008ede141682117dcea3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 12:51:45 -0700 Subject: [PATCH 12/44] fixed bug in writing small SVO files --- libraries/models/src/ModelItem.cpp | 3 ++- libraries/models/src/ModelTreeElement.cpp | 2 +- libraries/octree/src/Octree.cpp | 7 +------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index a818105521..aedd1fef47 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -263,7 +263,7 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } - //printf("ModelItem::readModelDataFromBuffer()... "); debugDump(); + //qDebug() << "ModelItem::readModelDataFromBuffer()... "; debugDump(); } return bytesRead; } @@ -417,6 +417,7 @@ void ModelItem::debugDump() const { qDebug(" position:%f,%f,%f", _position.x, _position.y, _position.z); qDebug(" radius:%f", getRadius()); qDebug(" color:%d,%d,%d", _color[0], _color[1], _color[2]); + qDebug() << " modelURL:" << qPrintable(getModelURL()); } bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id, const ModelItemProperties& properties, diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index 5c5d5100cf..4edcdd3e92 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -324,7 +324,7 @@ int ModelTreeElement::readElementDataFromBuffer(const unsigned char* data, int b dataAt += sizeof(numberOfModels); bytesLeftToRead -= (int)sizeof(numberOfModels); bytesRead += sizeof(numberOfModels); - + if (bytesLeftToRead >= (int)(numberOfModels * expectedBytesPerModel)) { for (uint16_t i = 0; i < numberOfModels; i++) { ModelItem tempModel; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 12ccf98c70..128677ff2b 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -335,10 +335,8 @@ void Octree::readBitstreamToTree(const unsigned char * bitstream, unsigned long int octalCodeBytes = bytesRequiredForCodeLength(*bitstreamAt); int theseBytesRead = 0; theseBytesRead += octalCodeBytes; - theseBytesRead += readElementData(bitstreamRootElement, bitstreamAt + octalCodeBytes, bufferSizeBytes - (bytesRead + octalCodeBytes), args); - // skip bitstream to new startPoint bitstreamAt += theseBytesRead; bytesRead += theseBytesRead; @@ -1619,7 +1617,6 @@ bool Octree::readFromSVOFile(const char* fileName) { } void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { - std::ofstream file(fileName, std::ios::out|std::ios::binary); if(file.is_open()) { @@ -1642,13 +1639,12 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { nodeBag.insert(_rootElement); } - static OctreePacketData packetData; + OctreePacketData packetData; int bytesWritten = 0; bool lastPacketWritten = false; while (!nodeBag.isEmpty()) { OctreeElement* subTree = nodeBag.extract(); - lockForRead(); // do tree locking down here so that we have shorter slices and less thread contention EncodeBitstreamParams params(INT_MAX, IGNORE_VIEW_FRUSTUM, WANT_COLOR, NO_EXISTS_BITS); bytesWritten = encodeTreeBitstream(subTree, &packetData, nodeBag, params); @@ -1670,7 +1666,6 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { if (!lastPacketWritten) { file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize()); } - } file.close(); } From 5309c5ad8bd0e7228c4b23a98e76d68d1b4d9ba0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 13:44:44 -0700 Subject: [PATCH 13/44] add more properties to animated models --- examples/placeModelsWithHands.js | 1 + libraries/models/src/ModelItem.cpp | 132 +++++++++++++++++- libraries/models/src/ModelItem.h | 19 ++- .../models/src/ModelsScriptingInterface.h | 14 ++ 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index f16945472d..bd0915da54 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -231,6 +231,7 @@ function checkControllerSide(whichSide) { if (animationURLs[currentModelURL] !== "") { properties.animationURL = animationURLs[currentModelURL]; + properties.isAnimationPlaying = true; } debugPrint("modelRadius=" +modelRadius); diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index aedd1fef47..9dd1173259 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -88,7 +88,9 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _isAnimationPlaying = false; _frameIndex = 0.0f; + _jointMappingCompleted = false; _lastAnimated = now; @@ -119,6 +121,7 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; + _isAnimationPlaying = false; _frameIndex = 0.0f; _jointMappingCompleted = false; _lastAnimated = now; @@ -172,6 +175,16 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { } } + // isAnimationPlaying + if (success) { + success = packetData->appendValue(getIsAnimationPlaying()); + } + + // frameIndex + if (success) { + success = packetData->appendValue(getFrameIndex()); + } + return success; } @@ -259,6 +272,17 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT bytesRead += animationURLLength; qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); + + // isAnimationPlaying + memcpy(&_isAnimationPlaying, dataAt, sizeof(_isAnimationPlaying)); + dataAt += sizeof(_isAnimationPlaying); + bytesRead += sizeof(_isAnimationPlaying); + + // frameIndex + memcpy(&_frameIndex, dataAt, sizeof(_frameIndex)); + dataAt += sizeof(_frameIndex); + bytesRead += sizeof(_frameIndex); + } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } @@ -395,9 +419,25 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& newModelItem._animationURL = tempString; dataAt += animationURLLength; processedBytes += animationURLLength; - qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); + } + // isAnimationPlaying + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { + + memcpy(&newModelItem._isAnimationPlaying, dataAt, sizeof(newModelItem._isAnimationPlaying)); + dataAt += sizeof(newModelItem._isAnimationPlaying); + processedBytes += sizeof(newModelItem._isAnimationPlaying); + } + + // frameIndex + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { + + memcpy(&newModelItem._frameIndex, dataAt, sizeof(newModelItem._frameIndex)); + dataAt += sizeof(newModelItem._frameIndex); + processedBytes += sizeof(newModelItem._frameIndex); } const bool wantDebugging = false; @@ -545,6 +585,30 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable } + // isAnimationPlaying + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { + + bool isAnimationPlaying = properties.getIsAnimationPlaying(); + memcpy(copyAt, &isAnimationPlaying, sizeof(isAnimationPlaying)); + copyAt += sizeof(isAnimationPlaying); + sizeOut += sizeof(isAnimationPlaying); + + +qDebug() << "encodeModelItemEditMessageDetails()... isAnimationPlaying=" << isAnimationPlaying; + } + + // frameIndex + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { + + float frameIndex = properties.getFrameIndex(); + memcpy(copyAt, &frameIndex, sizeof(frameIndex)); + copyAt += sizeof(frameIndex); + sizeOut += sizeof(frameIndex); + +qDebug() << "encodeModelItemEditMessageDetails()... frameIndex=" << frameIndex; + } bool wantDebugging = false; if (wantDebugging) { @@ -631,11 +695,16 @@ void ModelItem::mapJoints(const QStringList& modelJointNames) { QVector ModelItem::getAnimationFrame() { QVector frameData; if (hasAnimation() && _jointMappingCompleted) { - quint64 now = usecTimestampNow(); - float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; - _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; + + // only advance the frame index if we're playing + if (getIsAnimationPlaying()) { + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; + } + Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); int frameIndex = (int)std::floor(_frameIndex) % frames.size(); @@ -678,6 +747,8 @@ ModelItemProperties::ModelItemProperties() : _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), _animationURL(""), + _isAnimationPlaying(false), + _frameIndex(0.0), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -690,6 +761,8 @@ ModelItemProperties::ModelItemProperties() : _modelURLChanged(false), _modelRotationChanged(false), _animationURLChanged(false), + _isAnimationPlayingChanged(false), + _frameIndexChanged(false), _defaultSettings(true) { } @@ -725,6 +798,13 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; } + if (_isAnimationPlayingChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_PLAYING; + } + + if (_frameIndexChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FRAME; + } return changedBits; } @@ -749,6 +829,8 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const properties.setProperty("modelRotation", modelRotation); properties.setProperty("animationURL", _animationURL); + properties.setProperty("isAnimationPlaying", _isAnimationPlaying); + properties.setProperty("frameIndex", _frameIndex); if (_idSet) { properties.setProperty("id", _id); @@ -855,6 +937,26 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } + QScriptValue isAnimationPlaying = object.property("isAnimationPlaying"); + if (isAnimationPlaying.isValid()) { + bool newIsAnimationPlaying; + newIsAnimationPlaying = isAnimationPlaying.toVariant().toBool(); + if (_defaultSettings || newIsAnimationPlaying != _isAnimationPlaying) { + _isAnimationPlaying = newIsAnimationPlaying; + _isAnimationPlayingChanged = true; + } + } + + QScriptValue frameIndex = object.property("frameIndex"); + if (frameIndex.isValid()) { + float newFrameIndex; + newFrameIndex = frameIndex.toVariant().toFloat(); + if (_defaultSettings || newFrameIndex != _frameIndex) { + _frameIndex = newFrameIndex; + _frameIndexChanged = true; + } + } + _lastEdited = usecTimestampNow(); } @@ -897,6 +999,20 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } + if (_isAnimationPlayingChanged) { + modelItem.setIsAnimationPlaying(_isAnimationPlaying); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _isAnimationPlaying=" << _isAnimationPlaying; + } + + if (_frameIndexChanged) { + modelItem.setFrameIndex(_frameIndex); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _frameIndex=" << _frameIndex; + } + if (somethingChanged) { bool wantDebug = false; if (wantDebug) { @@ -917,6 +1033,8 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); _animationURL = modelItem.getAnimationURL(); + _isAnimationPlaying = modelItem.getIsAnimationPlaying(); + _frameIndex = modelItem.getFrameIndex(); _id = modelItem.getID(); _idSet = true; @@ -929,6 +1047,8 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURLChanged = false; _modelRotationChanged = false; _animationURLChanged = false; + _isAnimationPlayingChanged = false; + _frameIndexChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index cafd150e6d..a6c317d122 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -44,6 +44,8 @@ const uint16_t MODEL_PACKET_CONTAINS_SHOULDDIE = 8; const uint16_t MODEL_PACKET_CONTAINS_MODEL_URL = 16; const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_PLAYING = 128; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FRAME = 256; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container @@ -75,6 +77,8 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } const QString& getAnimationURL() const { return _animationURL; } + float getFrameIndex() const { return _frameIndex; } + bool getIsAnimationPlaying() const { return _isAnimationPlaying; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -89,6 +93,8 @@ public: void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } + void setFrameIndex(float value) { _frameIndex = value; _frameIndexChanged = true; } + void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; _isAnimationPlayingChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -105,6 +111,8 @@ private: QString _modelURL; glm::quat _modelRotation; QString _animationURL; + bool _isAnimationPlaying; + float _frameIndex; uint32_t _id; bool _idSet; @@ -118,6 +126,8 @@ private: bool _modelURLChanged; bool _modelRotationChanged; bool _animationURLChanged; + bool _isAnimationPlayingChanged; + bool _frameIndexChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -228,6 +238,8 @@ public: void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } void setAnimationURL(const QString& url) { _animationURL = url; } + void setFrameIndex(float value) { _frameIndex = value; } + void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; } void setProperties(const ModelItemProperties& properties); @@ -254,7 +266,10 @@ public: void mapJoints(const QStringList& modelJointNames); QVector getAnimationFrame(); - bool jointsMapped() const { return _jointMappingCompleted; }; + bool jointsMapped() const { return _jointMappingCompleted; } + + bool getIsAnimationPlaying() const { return _isAnimationPlaying; } + float getFrameIndex() const { return _frameIndex; } protected: glm::vec3 _position; @@ -277,6 +292,8 @@ protected: QString _animationURL; float _frameIndex; // we keep this as a float and round to int only when we need the exact index + bool _isAnimationPlaying; + bool _jointMappingCompleted; QVector _jointMapping; diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index bf8e193f25..f08ec715a9 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -59,6 +59,20 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to models QVector findModels(const glm::vec3& center, float radius) const; + /* + /// pauses the model animation. + ModelItemID pauseModelAnimation(ModelItemID modelID); + + /// plays the model animation. + ModelItemID playModelAnimation(ModelItemID modelID); + + /// gets the current frame of the model animation. + float getModelAnimationFrame(ModelItemID modelID); + + /// gets the current frame of the model animation. + void setModelAnimationFrame(ModelItemID modelID, float frame); + */ + signals: void modelCollisionWithVoxel(const ModelItemID& modelID, const VoxelDetail& voxel, const CollisionInfo& collision); void modelCollisionWithModel(const ModelItemID& idA, const ModelItemID& idB, const CollisionInfo& collision); From 0fc33f575ffece93f0a928ba50523598c884e871 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 12 May 2014 14:07:54 -0700 Subject: [PATCH 14/44] moving frame advancement to update() --- libraries/models/src/ModelItem.cpp | 35 +++++++++++++++-------- libraries/models/src/ModelTreeElement.cpp | 5 ++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 9dd1173259..f9b1092c84 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -283,6 +283,8 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(_frameIndex); bytesRead += sizeof(_frameIndex); + qDebug() << "readModelDataFromBuffer()... _frameIndex=" << _frameIndex; + } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } @@ -695,16 +697,6 @@ void ModelItem::mapJoints(const QStringList& modelJointNames) { QVector ModelItem::getAnimationFrame() { QVector frameData; if (hasAnimation() && _jointMappingCompleted) { - - // only advance the frame index if we're playing - if (getIsAnimationPlaying()) { - quint64 now = usecTimestampNow(); - float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; - _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; - } - Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); int frameIndex = (int)std::floor(_frameIndex) % frames.size(); @@ -720,9 +712,28 @@ QVector ModelItem::getAnimationFrame() { return frameData; } -void ModelItem::update(const quint64& now) { - _lastUpdated = now; +void ModelItem::update(const quint64& updateTime) { + _lastUpdated = updateTime; setShouldDie(getShouldDie()); + +//qDebug() << "ModelItem::update() now=" << now; + + // only advance the frame index if we're playing + if (getIsAnimationPlaying()) { + + quint64 now = usecTimestampNow(); + float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; +qDebug() << "ModelItem::update() now=" << now; +qDebug() << " updateTime=" << updateTime; +qDebug() << " _lastAnimated=" << _lastAnimated; +qDebug() << " deltaTime=" << deltaTime; + + _lastAnimated = now; + const float FRAME_RATE = 10.0f; + _frameIndex += deltaTime * FRAME_RATE; +qDebug() << " _frameIndex=" << _frameIndex; + + } } void ModelItem::copyChangedProperties(const ModelItem& other) { diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index 4edcdd3e92..e8b37c478b 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -102,10 +102,15 @@ bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { } void ModelTreeElement::update(ModelTreeUpdateArgs& args) { +//qDebug() << "ModelTreeElement::update()..."; // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { ModelItem& model = (*modelItr); + + // TODO: this _lastChanged isn't actually changing because we're not marking this element as changed. + // how do we want to handle this??? We really only want to consider an element changed when it is + // edited... not just animated... model.update(_lastChanged); // If the model wants to die, or if it's left our bounding box, then move it From e0486b26549062d0311b706cc0d58465d43883e7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 15:25:25 -0700 Subject: [PATCH 15/44] Starting on PrioVR integration. --- cmake/modules/FindPrioVR.cmake | 42 +++++++++++++++++++ interface/CMakeLists.txt | 9 +++++ interface/external/priovr/readme.txt | 11 +++++ interface/src/Application.cpp | 1 + interface/src/Application.h | 2 + interface/src/devices/PrioVR.cpp | 60 ++++++++++++++++++++++++++++ interface/src/devices/PrioVR.h | 41 +++++++++++++++++++ 7 files changed, 166 insertions(+) create mode 100644 cmake/modules/FindPrioVR.cmake create mode 100644 interface/external/priovr/readme.txt create mode 100644 interface/src/devices/PrioVR.cpp create mode 100644 interface/src/devices/PrioVR.h diff --git a/cmake/modules/FindPrioVR.cmake b/cmake/modules/FindPrioVR.cmake new file mode 100644 index 0000000000..96ee1a7bdc --- /dev/null +++ b/cmake/modules/FindPrioVR.cmake @@ -0,0 +1,42 @@ +# Try to find the PrioVT library +# +# You must provide a PRIOVR_ROOT_DIR which contains lib and include directories +# +# Once done this will define +# +# PRIOVR_FOUND - system found PrioVR +# PRIOVR_INCLUDE_DIRS - the PrioVR include directory +# PRIOVR_LIBRARIES - Link this to use PrioVR +# +# Created on 5/12/2014 by Andrzej Kapolka +# Copyright (c) 2014 High Fidelity +# + +if (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) + # in cache already + set(PRIOVR_FOUND TRUE) +else (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) + find_path(PRIOVR_INCLUDE_DIRS yei_threespace_api.h ${PRIOVR_ROOT_DIR}/include) + + if (WIN32) + find_library(PRIOVR_LIBRARIES ThreeSpace_API.lib ${PRIOVR_ROOT_DIR}) + endif (WIN32) + + if (PRIOVR_INCLUDE_DIRS AND PRIOVR_LIBRARIES) + set(PRIOVR_FOUND TRUE) + endif (PRIOVR_INCLUDE_DIRS AND PRIOVR_LIBRARIES) + + if (PRIOVR_FOUND) + if (NOT PRIOVR_FIND_QUIETLY) + message(STATUS "Found PrioVR... ${PRIOVR_LIBRARIES}") + endif (NOT PRIOVR_FIND_QUIETLY) + else () + if (PRIOVR_FIND_REQUIRED) + message(FATAL_ERROR "Could not find PrioVR") + endif (PRIOVR_FIND_REQUIRED) + endif () + + # show the PRIOVR_INCLUDE_DIRS and PRIOVR_LIBRARIES variables only in the advanced view + mark_as_advanced(PRIOVR_INCLUDE_DIRS PRIOVR_LIBRARIES) + +endif (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 0a56109260..d075fbe9d0 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -15,6 +15,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../cmake set(FACEPLUS_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceplus") set(FACESHIFT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/faceshift") set(LIBOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/oculus") +set(PRIOVR_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/priovr") set(SIXENSE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/Sixense") set(VISAGE_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/visage") @@ -133,6 +134,7 @@ link_hifi_library(script-engine ${TARGET_NAME} "${ROOT_DIR}") find_package(Faceplus) find_package(Faceshift) find_package(LibOVR) +find_package(PrioVR) find_package(Sixense) find_package(Visage) find_package(ZLIB) @@ -183,6 +185,13 @@ if (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) target_link_libraries(${TARGET_NAME} "${LIBOVR_LIBRARIES}") endif (LIBOVR_FOUND AND NOT DISABLE_LIBOVR) +# and with PrioVR library +if (PRIOVR_FOUND AND NOT DISABLE_PRIOVR) + add_definitions(-DHAVE_PRIOVR) + include_directories(SYSTEM "${PRIOVR_INCLUDE_DIRS}") + target_link_libraries(${TARGET_NAME} "${PRIOVR_LIBRARIES}") +endif (PRIOVR_FOUND AND NOT DISABLE_PRIOVR) + # and with qxmpp for chat if (QXMPP_FOUND AND NOT DISABLE_QXMPP) add_definitions(-DHAVE_QXMPP -DQXMPP_STATIC) diff --git a/interface/external/priovr/readme.txt b/interface/external/priovr/readme.txt new file mode 100644 index 0000000000..92a2aff711 --- /dev/null +++ b/interface/external/priovr/readme.txt @@ -0,0 +1,11 @@ + +Instructions for adding the PrioVR driver to Interface +Andrzej Kapolka, May 12, 2014 + +1. Copy the PrioVR sdk folders (include, *.lib) into the interface/external/priovr folder. + This readme.txt should be there as well. + +2. Copy the PrioVR DLLs from the API into your path. + +3. Delete your build directory, run cmake and build, and you should be all set. + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0b59fc3aa2..cc1ea14d2d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1987,6 +1987,7 @@ void Application::update(float deltaTime) { _myAvatar->updateLookAtTargetAvatar(); updateMyAvatarLookAtPosition(); _sixenseManager.update(deltaTime); + _prioVR.update(); updateMyAvatar(deltaTime); // Sample hardware, update view frustum if needed, and send avatar data to mixer/nodes updateThreads(deltaTime); // If running non-threaded, then give the threads some time to process... _avatarManager.updateOtherAvatars(deltaTime); //loop through all the other avatars and simulate them... diff --git a/interface/src/Application.h b/interface/src/Application.h index 67cf7dad44..77f8024b6c 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -58,6 +58,7 @@ #include "avatar/MyAvatar.h" #include "devices/Faceplus.h" #include "devices/Faceshift.h" +#include "devices/PrioVR.h" #include "devices/SixenseManager.h" #include "devices/Visage.h" #include "models/ModelTreeRenderer.h" @@ -441,6 +442,7 @@ private: Visage _visage; SixenseManager _sixenseManager; + PrioVR _prioVR; Camera _myCamera; // My view onto the world Camera _viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp new file mode 100644 index 0000000000..cd28c6ab68 --- /dev/null +++ b/interface/src/devices/PrioVR.cpp @@ -0,0 +1,60 @@ +// +// PrioVR.cpp +// interface/src/devices +// +// Created by Andrzej Kapolka on 5/12/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "PrioVR.h" + +PrioVR::PrioVR() { +#ifdef HAVE_PRIOVR + TSS_ComPort comPort; + if (!tss_getComPorts(&comPort, 1, 0, PVR_FIND_BS)) { + _baseStation = TSS_NO_DEVICE_ID; + return; + } + _baseStation = tss_createTSDeviceStr(comPort.com_port, TSS_TIMESTAMP_SYSTEM); + if (_baseStation == TSS_NO_DEVICE_ID) { + return; + } + for (int i = 0; i < MAX_SENSOR_NODES; i++) { + tss_getSensorFromDongle(_baseStation, i, &_sensorNodes[i]); + if (_sensorNodes[i] == TSS_NO_DEVICE_ID) { + continue; + } + int present; + tss_isPresent(_sensorNodes[i], &present); + if (!present) { + _sensorNodes[i] = TSS_NO_DEVICE_ID; + } + } + tss_startStreaming(_baseStation, NULL); +#endif +} + +PrioVR::~PrioVR() { +#ifdef HAVE_PRIOVR + if (_baseStation != TSS_NO_DEVICE_ID) { + tss_stopStreaming(_baseStation, NULL); + } +#endif +} + +void PrioVR::update() { +#ifdef HAVE_PRIOVR + for (int i = 0; i < MAX_SENSOR_NODES; i++) { + if (_sensorNodes[i] == TSS_NO_DEVICE_ID) { + continue; + } + glm::quat rotation; + if (!tss_getLastStreamData(_sensorNodes[i], (char*)&rotation, sizeof(glm::quat), NULL)) { + qDebug() << i << rotation.x << rotation.y << rotation.z << rotation.w; + } + } +#endif +} diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h new file mode 100644 index 0000000000..97fec2e1ee --- /dev/null +++ b/interface/src/devices/PrioVR.h @@ -0,0 +1,41 @@ +// +// PrioVR.h +// interface/src/devices +// +// Created by Andrzej Kapolka on 5/12/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_PrioVR_h +#define hifi_PrioVR_h + +#include + +#ifdef HAVE_PRIOVR +#include +#endif + +/// Handles interaction with the PrioVR skeleton tracking suit. +class PrioVR : public QObject { + Q_OBJECT + +public: + + PrioVR(); + virtual ~PrioVR(); + + void update(); + +private: +#ifdef HAVE_PRIOVR + TSS_Device_Id _baseStation; + + const int MAX_SENSOR_NODES = 20; + TSS_Device_Id _sensorNodes[MAX_SENSOR_NODES]; +#endif +}; + +#endif // hifi_PrioVR_h From 37700c6ad3a331aac0bd3b8b4c2896512ba0b3e7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 12 May 2014 17:10:19 -0700 Subject: [PATCH 16/44] Fixed prompt size --- examples/editModels.js | 167 ++++++++------- examples/testScript.js | 0 examples/toolBars.js | 200 ++++++++++++++++++ .../scripting/WindowScriptingInterface.cpp | 1 + 4 files changed, 291 insertions(+), 77 deletions(-) delete mode 100644 examples/testScript.js create mode 100644 examples/toolBars.js diff --git a/examples/editModels.js b/examples/editModels.js index 9acbff4aa6..90ce87d259 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -9,7 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +Script.include("toolBars.js"); + var windowDimensions = Controller.getViewportDimensions(); +var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; +var toolHeight = 50; +var toolWidth = 50; var LASER_WIDTH = 4; var LASER_COLOR = { red: 255, green: 0, blue: 0 }; @@ -33,56 +38,7 @@ var modelURLs = [ "http://highfidelity-public.s3-us-west-1.amazonaws.com/meshes/slimer.fbx", ]; -var tools = []; -var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; -var numberOfTools = 1; -var toolHeight = 50; -var toolWidth = 50; -var toolVerticalSpacing = 4; -var toolsHeight = toolHeight * numberOfTools + toolVerticalSpacing * (numberOfTools - 1); -var toolsX = windowDimensions.x - 8 - toolWidth; -var toolsY = (windowDimensions.y - toolsHeight) / 2; - - -var firstModel = Overlays.addOverlay("image", { - subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, - imageURL: toolIconUrl + "voxel-tool.svg", - x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }); -function Tool(iconURL) { - this.overlay = Overlays.addOverlay("image", { - subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight }, - imageURL: iconURL, - x: toolsX, - y: toolsY + ((toolHeight + toolVerticalSpacing) * tools.length), - width: toolWidth, - height: toolHeight, - visible: true, - alpha: 0.9 - }); - - - this.cleanup = function() { - Ovelays.deleteOverlay(this.overlay); - } - tools[tools.length] = this; - return tools.length - 1; -} -Tool.ICON_URL = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/"; -Tool.HEIGHT = 50; -Tool.WIDTH = 50; - -function ToolBar(direction, x, y) { - this.tools = []; - - this.numberOfTools = function() { - return this.tools.length; - } -} -ToolBar.SPACING = 4; - +var toolBar; function controller(wichSide) { this.side = wichSide; @@ -238,6 +194,13 @@ function controller(wichSide) { }); } + this.hideLaser = function() { + Overlays.editOverlay(this.laser, { visible: false }); + Overlays.editOverlay(this.ball, { visible: false }); + Overlays.editOverlay(this.leftRight, { visible: false }); + Overlays.editOverlay(this.topDown, { visible: false }); + } + this.moveModel = function () { if (this.grabbing) { var newPosition = Vec3.sum(this.palmPosition, @@ -377,67 +340,117 @@ function moveModels() { rightController.moveModel(); } +var hydraConnected = false; function checkController(deltaTime) { var numberOfButtons = Controller.getNumberOfButtons(); var numberOfTriggers = Controller.getNumberOfTriggers(); var numberOfSpatialControls = Controller.getNumberOfSpatialControls(); var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers; - - moveOverlays(); // this is expected for hydras - if (!(numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2)) { - //print("no hydra connected?"); - return; // bail if no hydra + if (numberOfButtons==12 && numberOfTriggers == 2 && controllersPerTrigger == 2) { + if (!hydraConnected) { + hydraConnected = true; + } + + leftController.update(); + rightController.update(); + moveModels(); + } else { + if (hydraConnected) { + hydraConnected = false; + + leftController.hideLaser(); + rightController.hideLaser(); + } } - leftController.update(); - rightController.update(); - moveModels(); + moveOverlays(); } function moveOverlays() { - windowDimensions = Controller.getViewportDimensions(); - - toolsX = windowDimensions.x - 8 - toolWidth; - toolsY = (windowDimensions.y - toolsHeight) / 2; - - Overlays.editOverlay(firstModel, { - x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * 0), width: toolWidth, height: toolHeight, + if (typeof(toolBar) === 'undefined') { + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); + // New Model + toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 }); + // Move YZ + toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Move XZ + toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Move XY + toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + } else if (windowDimensions.x == Controller.getViewportDimensions().x && + windowDimensions.y == Controller.getViewportDimensions().y) { + return; + } + + + windowDimensions = Controller.getViewportDimensions(); + var toolsX = windowDimensions.x - 8 - toolBar.width; + var toolsY = (windowDimensions.y - toolBar.height) / 2; + + toolBar.move(toolsX, toolsY); } function mousePressEvent(event) { var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); var url; - - if (clickedOverlay == firstModel) { + var index = toolBar.clicked(clickedOverlay); + if (index == 0) { url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); if (url == null) { - return; } - } else { + return; + } + + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + Models.addModel({ position: position, + radius: radiusDefault, + modelURL: url + }); + } else if (index == -1) { print("Didn't click on anything"); - return; } +} + +function mouseMoveEvent(event) { - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); - Models.addModel({ position: position, - radius: radiusDefault, - modelURL: url - }); } function scriptEnding() { leftController.cleanup(); rightController.cleanup(); - - Overlays.deleteOverlay(firstModel); + toolBar.cleanup(); } Script.scriptEnding.connect(scriptEnding); // register the call back so it fires before each data send Script.update.connect(checkController); Controller.mousePressEvent.connect(mousePressEvent); +Controller.mousePressEvent.connect(mouseMoveEvent); diff --git a/examples/testScript.js b/examples/testScript.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/toolBars.js b/examples/toolBars.js new file mode 100644 index 0000000000..62a01a9a15 --- /dev/null +++ b/examples/toolBars.js @@ -0,0 +1,200 @@ +// +// testScript.js +// examples +// +// Created by ClĂ©ment Brisset on 5/7/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Overlay2D = function(properties, overlay) { // overlay is an optionnal variable + if (!(typeof(properties) === 'undefined')) { + if(typeof(overlay) === 'undefined') { + overlay = Overlays.addOverlay("image", properties); + print("New overlay: " + overlay); + } else { + Overlays.editOverlay(overlay, properties); + } + } + + this.overlay = function() { + return overlay; + } + this.x = function() { + return properties.x; + } + this.y = function() { + return properties.y; + } + this.width = function() { + return properties.width; + } + this.height = function() { + return properties.height; + } + this.alpha = function() { + return properties.alpha; + } + this.visible = function() { + return properties.visible; + } + + + this.move = function(x, y) { + properties.x = x; + properties.y = y; + Overlays.editOverlay(overlay, { x: x, y: y }); + } + this.resize = function(width, height) { + properties.width = width; + properties.height = height; + Overlays.editOverlay(overlay, { width: width, height: height }); + } + this.setAlpha = function(alpha) { + properties.alpha = alpha; + Overlays.editOverlay(overlay, { alpha: alpha }); + } + this.show = function(doShow) { + properties.visible = doShow; + Overlays.editOverlay(overlay, { visible: doShow }); + } + + this.clicked = function(clickedOverlay) { + return (overlay == clickedOverlay ? true : false); + } + + this.cleanup = function() { + print("Cleanup"); + Overlays.deleteOverlay(overlay); + } +} + + +Tool = function(properties, selectable, selected) { // selectable and selected are optional variables. + Overlay2D.call(this, properties); + + if(typeof(selectable)==='undefined') { + selectable = false; + if(typeof(selected)==='undefined') { + selected = false; + + } + } + + this.selectable = function() { + return selectable; + } + + if (this.selectable()) { + this.selected = function() { + return selected; + } + this.select = function(doSelect) { + selected = doSelect; + properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; + Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); + } + this.toggle = function() { + selected = !selected; + properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; + Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); + + return selected; + } + + this.select(selected); + } + + this.baseClicked = this.clicked; + this.clicked = function(clickedOverlay) { + if (this.baseClicked(clickedOverlay)) { + if (selectable) { + this.toggle(); + } + return true; + } + return false; + } +} +Tool.prototype = new Overlay2D; +Tool.IMAGE_HEIGHT = 50; +Tool.IMAGE_WIDTH = 50; + +ToolBar = function(x, y, direction) { + this.tools = []; + this.x = x; + this.y = y; + this.width = 0; + this.height = 0; + + + this.addTool = function(properties, selectable, selected) { + if (direction == ToolBar.HORIZONTAL) { + properties.x = this.x + this.width; + properties.y = this.y; + this.width += properties.width + ToolBar.SPACING; + this.height += Math.max(properties.height, this.height); + } else { + properties.x = this.x; + properties.y = this.y + this.height; + this.width = Math.max(properties.width, this.width); + this.height += properties.height + ToolBar.SPACING; + } + + this.tools[this.tools.length] = new Tool(properties, selectable, selected); + return this.tools.length - 1; + } + + this.move = function(x, y) { + var dx = x - this.x; + var dy = y - this.y; + this.x = x; + this.y = y; + for(var tool in this.tools) { + this.tools[tool].move(this.tools[tool].x() + dx, this.tools[tool].y() + dy); + } + } + + this.setAlpha = function(alpha) { + for(var tool in this.tools) { + this.tools[tool].setAlpha(alpha); + } + } + + this.show = function(doShow) { + for(var tool in this.tools) { + this.tools[tool].show(doShow); + } + } + + this.clicked = function(clickedOverlay) { + for(var tool in this.tools) { + if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay)) { + return tool; + } + } + return -1; + } + + this.numberOfTools = function() { + return this.tools.length; + } + + this.cleanup = function() { + for(var tool in this.tools) { + this.tools[tool].cleanup(); + delete this.tools[tool]; + } + + this.tools = []; + this.x = x; + this.y = y; + this.width = 0; + this.height = 0; + } +} +ToolBar.SPACING = 4; +ToolBar.VERTICAL = 0; +ToolBar.HORIZONTAL = 1; \ No newline at end of file diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 144415d6ee..e10197d488 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -78,6 +78,7 @@ QScriptValue WindowScriptingInterface::showPrompt(const QString& message, const promptDialog.setWindowTitle(""); promptDialog.setLabelText(message); promptDialog.setTextValue(defaultText); + promptDialog.setFixedSize(600, 200); if (promptDialog.exec() == QDialog::Accepted) { return QScriptValue(promptDialog.textValue()); From 52a3c70bf589fffa319a9aec039c15a708e5f990 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 17:11:02 -0700 Subject: [PATCH 17/44] Updates to PrioVR interface. --- cmake/modules/FindPrioVR.cmake | 4 +-- interface/src/devices/PrioVR.cpp | 62 +++++++++++++++----------------- interface/src/devices/PrioVR.h | 15 +++++--- 3 files changed, 41 insertions(+), 40 deletions(-) diff --git a/cmake/modules/FindPrioVR.cmake b/cmake/modules/FindPrioVR.cmake index 96ee1a7bdc..6e78045321 100644 --- a/cmake/modules/FindPrioVR.cmake +++ b/cmake/modules/FindPrioVR.cmake @@ -16,10 +16,10 @@ if (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) # in cache already set(PRIOVR_FOUND TRUE) else (PRIOVR_LIBRARIES AND PRIOVR_INCLUDE_DIRS) - find_path(PRIOVR_INCLUDE_DIRS yei_threespace_api.h ${PRIOVR_ROOT_DIR}/include) + find_path(PRIOVR_INCLUDE_DIRS yei_skeletal_api.h ${PRIOVR_ROOT_DIR}/include) if (WIN32) - find_library(PRIOVR_LIBRARIES ThreeSpace_API.lib ${PRIOVR_ROOT_DIR}) + find_library(PRIOVR_LIBRARIES Skeletal_API.lib ${PRIOVR_ROOT_DIR}/lib) endif (WIN32) if (PRIOVR_INCLUDE_DIRS AND PRIOVR_LIBRARIES) diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index cd28c6ab68..f9f807bdb7 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -9,52 +9,48 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include + #include "PrioVR.h" +const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, 0x0000000C, 0x0000000D, + 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; +const unsigned char AXIS_LIST[] = { 9, 43, 37, 37, 37, 13, 13, 13, 52, 52, 28, 28 }; +const int LIST_LENGTH = sizeof(SERIAL_LIST) / sizeof(SERIAL_LIST[0]); + PrioVR::PrioVR() { #ifdef HAVE_PRIOVR - TSS_ComPort comPort; - if (!tss_getComPorts(&comPort, 1, 0, PVR_FIND_BS)) { - _baseStation = TSS_NO_DEVICE_ID; - return; - } - _baseStation = tss_createTSDeviceStr(comPort.com_port, TSS_TIMESTAMP_SYSTEM); - if (_baseStation == TSS_NO_DEVICE_ID) { - return; - } - for (int i = 0; i < MAX_SENSOR_NODES; i++) { - tss_getSensorFromDongle(_baseStation, i, &_sensorNodes[i]); - if (_sensorNodes[i] == TSS_NO_DEVICE_ID) { - continue; - } - int present; - tss_isPresent(_sensorNodes[i], &present); - if (!present) { - _sensorNodes[i] = TSS_NO_DEVICE_ID; - } - } - tss_startStreaming(_baseStation, NULL); + _jointsDiscovered.resize(LIST_LENGTH); + _skeletalDevice = yei_setUpPrioVRSensors(0x00000000, const_cast(SERIAL_LIST), + const_cast(AXIS_LIST), _jointsDiscovered.data(), LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); + if (!_skeletalDevice) { + return; + } + _jointRotations.resize(LIST_LENGTH); + yei_tareSensors(_skeletalDevice); #endif } PrioVR::~PrioVR() { #ifdef HAVE_PRIOVR - if (_baseStation != TSS_NO_DEVICE_ID) { - tss_stopStreaming(_baseStation, NULL); - } + if (_skeletalDevice) { + yei_stopStreaming(_skeletalDevice); + } #endif } void PrioVR::update() { #ifdef HAVE_PRIOVR - for (int i = 0; i < MAX_SENSOR_NODES; i++) { - if (_sensorNodes[i] == TSS_NO_DEVICE_ID) { - continue; - } - glm::quat rotation; - if (!tss_getLastStreamData(_sensorNodes[i], (char*)&rotation, sizeof(glm::quat), NULL)) { - qDebug() << i << rotation.x << rotation.y << rotation.z << rotation.w; - } - } + if (!_skeletalDevice) { + return; + } + unsigned int timestamp; + yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), + _jointRotations.size() * sizeof(glm::quat), ×tamp); + for (int i = 0; i < _jointsDiscovered.size(); i++) { + if (_jointsDiscovered.at(i)) { + qDebug() << i << _jointRotations.at(i).x << _jointRotations.at(i).y << _jointRotations.at(i).z << _jointRotations.at(i).w; + } + } #endif } diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index 97fec2e1ee..d5bac44a9a 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -13,9 +13,14 @@ #define hifi_PrioVR_h #include +#include + +#include #ifdef HAVE_PRIOVR -#include +extern "C" { +#include +} #endif /// Handles interaction with the PrioVR skeleton tracking suit. @@ -31,11 +36,11 @@ public: private: #ifdef HAVE_PRIOVR - TSS_Device_Id _baseStation; - - const int MAX_SENSOR_NODES = 20; - TSS_Device_Id _sensorNodes[MAX_SENSOR_NODES]; + YEI_Device_Id _skeletalDevice; #endif + + QVector _jointsDiscovered; + QVector _jointRotations; }; #endif // hifi_PrioVR_h From 6c58bec550a04d71d1269dfd10540a0fd3bac19e Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 17:32:06 -0700 Subject: [PATCH 18/44] Untabbed. --- interface/src/devices/PrioVR.cpp | 36 ++++++++++++++++---------------- interface/src/devices/PrioVR.h | 4 ++-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index f9f807bdb7..9b5427be07 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -14,20 +14,20 @@ #include "PrioVR.h" const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, 0x0000000C, 0x0000000D, - 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; + 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; const unsigned char AXIS_LIST[] = { 9, 43, 37, 37, 37, 13, 13, 13, 52, 52, 28, 28 }; const int LIST_LENGTH = sizeof(SERIAL_LIST) / sizeof(SERIAL_LIST[0]); PrioVR::PrioVR() { #ifdef HAVE_PRIOVR - _jointsDiscovered.resize(LIST_LENGTH); + _jointsDiscovered.resize(LIST_LENGTH); _skeletalDevice = yei_setUpPrioVRSensors(0x00000000, const_cast(SERIAL_LIST), - const_cast(AXIS_LIST), _jointsDiscovered.data(), LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); - if (!_skeletalDevice) { - return; - } - _jointRotations.resize(LIST_LENGTH); - yei_tareSensors(_skeletalDevice); + const_cast(AXIS_LIST), _jointsDiscovered.data(), LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); + if (!_skeletalDevice) { + return; + } + _jointRotations.resize(LIST_LENGTH); + yei_tareSensors(_skeletalDevice); #endif } @@ -42,15 +42,15 @@ PrioVR::~PrioVR() { void PrioVR::update() { #ifdef HAVE_PRIOVR if (!_skeletalDevice) { - return; - } - unsigned int timestamp; - yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), - _jointRotations.size() * sizeof(glm::quat), ×tamp); - for (int i = 0; i < _jointsDiscovered.size(); i++) { - if (_jointsDiscovered.at(i)) { - qDebug() << i << _jointRotations.at(i).x << _jointRotations.at(i).y << _jointRotations.at(i).z << _jointRotations.at(i).w; - } - } + return; + } + unsigned int timestamp; + yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), + _jointRotations.size() * sizeof(glm::quat), ×tamp); + for (int i = 0; i < _jointsDiscovered.size(); i++) { + if (_jointsDiscovered.at(i)) { + qDebug() << i << _jointRotations.at(i).x << _jointRotations.at(i).y << _jointRotations.at(i).z << _jointRotations.at(i).w; + } + } #endif } diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index d5bac44a9a..1c26009b33 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -39,8 +39,8 @@ private: YEI_Device_Id _skeletalDevice; #endif - QVector _jointsDiscovered; - QVector _jointRotations; + QVector _jointsDiscovered; + QVector _jointRotations; }; #endif // hifi_PrioVR_h From 4a0b4de1787fddccc1ded9545a33bc1bf051af24 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 12 May 2014 17:55:07 -0700 Subject: [PATCH 19/44] Better instructions. --- interface/external/priovr/readme.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/interface/external/priovr/readme.txt b/interface/external/priovr/readme.txt index 92a2aff711..202a90cf12 100644 --- a/interface/external/priovr/readme.txt +++ b/interface/external/priovr/readme.txt @@ -2,10 +2,15 @@ Instructions for adding the PrioVR driver to Interface Andrzej Kapolka, May 12, 2014 -1. Copy the PrioVR sdk folders (include, *.lib) into the interface/external/priovr folder. - This readme.txt should be there as well. +1. Download and install the YEI drivers from https://www.yeitechnology.com/yei-3-space-sensor-software-suite. If using + Window 8+, follow the workaround instructions at http://forum.yeitechnology.com/viewtopic.php?f=3&t=24. + +2. Get the PrioVR skeleton API, open ts_c_api2_priovr2/visual_studio/ThreeSpace_API_2/ThreeSpace_API_2.sln + in Visual Studio, and build it. -2. Copy the PrioVR DLLs from the API into your path. - -3. Delete your build directory, run cmake and build, and you should be all set. +3. Copy ts_c_api2_priovr2/visual_studio/ThreeSpace_API_2/Skeletal_API/yei_skeletal_api.h to interface/external/priovr/include, + ts_c_api2_priovr2/visual_studio/ThreeSpace_API_2/Debug/Skeletal_API.lib to interface/external/priovr/lib, and + ts_c_api2_priovr2/visual_studio/ThreeSpace_API_2/Debug/*.dll to your path. + +4. Delete your build directory, run cmake and build, and you should be all set. From a56567a3693aa5018d5f27c306a3a285389abe7b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 13 May 2014 09:27:31 -0700 Subject: [PATCH 20/44] add support for adjustable FPS --- examples/placeModelsWithHands.js | 2 +- libraries/models/src/ModelItem.cpp | 228 ++++++++++++------ libraries/models/src/ModelItem.h | 37 +-- .../models/src/ModelsScriptingInterface.h | 5 +- 4 files changed, 176 insertions(+), 96 deletions(-) diff --git a/examples/placeModelsWithHands.js b/examples/placeModelsWithHands.js index bd0915da54..47b4615924 100644 --- a/examples/placeModelsWithHands.js +++ b/examples/placeModelsWithHands.js @@ -231,7 +231,7 @@ function checkControllerSide(whichSide) { if (animationURLs[currentModelURL] !== "") { properties.animationURL = animationURLs[currentModelURL]; - properties.isAnimationPlaying = true; + properties.animationIsPlaying = true; } debugPrint("modelRadius=" +modelRadius); diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index f9b1092c84..474a6a38a4 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -88,8 +88,9 @@ ModelItem::ModelItem(const ModelItemID& modelItemID, const ModelItemProperties& // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; - _isAnimationPlaying = false; - _frameIndex = 0.0f; + _animationIsPlaying = false; + _animationFrameIndex = 0.0f; + _animationFPS = MODEL_DEFAULT_ANIMATION_FPS; _jointMappingCompleted = false; _lastAnimated = now; @@ -121,8 +122,9 @@ void ModelItem::init(glm::vec3 position, float radius, rgbColor color, uint32_t // animation related _animationURL = MODEL_DEFAULT_ANIMATION_URL; - _isAnimationPlaying = false; - _frameIndex = 0.0f; + _animationIsPlaying = false; + _animationFrameIndex = 0.0f; + _animationFPS = MODEL_DEFAULT_ANIMATION_FPS; _jointMappingCompleted = false; _lastAnimated = now; } @@ -175,14 +177,19 @@ bool ModelItem::appendModelData(OctreePacketData* packetData) const { } } - // isAnimationPlaying + // animationIsPlaying if (success) { - success = packetData->appendValue(getIsAnimationPlaying()); + success = packetData->appendValue(getAnimationIsPlaying()); } - // frameIndex + // animationFrameIndex if (success) { - success = packetData->appendValue(getFrameIndex()); + success = packetData->appendValue(getAnimationFrameIndex()); + } + + // animationFPS + if (success) { + success = packetData->appendValue(getAnimationFPS()); } return success; @@ -273,17 +280,24 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); - // isAnimationPlaying - memcpy(&_isAnimationPlaying, dataAt, sizeof(_isAnimationPlaying)); - dataAt += sizeof(_isAnimationPlaying); - bytesRead += sizeof(_isAnimationPlaying); + // animationIsPlaying + memcpy(&_animationIsPlaying, dataAt, sizeof(_animationIsPlaying)); + dataAt += sizeof(_animationIsPlaying); + bytesRead += sizeof(_animationIsPlaying); - // frameIndex - memcpy(&_frameIndex, dataAt, sizeof(_frameIndex)); - dataAt += sizeof(_frameIndex); - bytesRead += sizeof(_frameIndex); + // animationFrameIndex + memcpy(&_animationFrameIndex, dataAt, sizeof(_animationFrameIndex)); + dataAt += sizeof(_animationFrameIndex); + bytesRead += sizeof(_animationFrameIndex); - qDebug() << "readModelDataFromBuffer()... _frameIndex=" << _frameIndex; + qDebug() << "readModelDataFromBuffer()... _animationFrameIndex=" << _animationFrameIndex; + + // animationFPS + memcpy(&_animationFPS, dataAt, sizeof(_animationFPS)); + dataAt += sizeof(_animationFPS); + bytesRead += sizeof(_animationFPS); + + qDebug() << "readModelDataFromBuffer()... _animationFPS=" << _animationFPS; } else { qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; @@ -424,22 +438,31 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); } - // isAnimationPlaying + // animationIsPlaying if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { - memcpy(&newModelItem._isAnimationPlaying, dataAt, sizeof(newModelItem._isAnimationPlaying)); - dataAt += sizeof(newModelItem._isAnimationPlaying); - processedBytes += sizeof(newModelItem._isAnimationPlaying); + memcpy(&newModelItem._animationIsPlaying, dataAt, sizeof(newModelItem._animationIsPlaying)); + dataAt += sizeof(newModelItem._animationIsPlaying); + processedBytes += sizeof(newModelItem._animationIsPlaying); } - // frameIndex + // animationFrameIndex if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { - memcpy(&newModelItem._frameIndex, dataAt, sizeof(newModelItem._frameIndex)); - dataAt += sizeof(newModelItem._frameIndex); - processedBytes += sizeof(newModelItem._frameIndex); + memcpy(&newModelItem._animationFrameIndex, dataAt, sizeof(newModelItem._animationFrameIndex)); + dataAt += sizeof(newModelItem._animationFrameIndex); + processedBytes += sizeof(newModelItem._animationFrameIndex); + } + + // animationFPS + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FPS) == MODEL_PACKET_CONTAINS_ANIMATION_FPS)) { + + memcpy(&newModelItem._animationFPS, dataAt, sizeof(newModelItem._animationFPS)); + dataAt += sizeof(newModelItem._animationFPS); + processedBytes += sizeof(newModelItem._animationFPS); } const bool wantDebugging = false; @@ -587,29 +610,41 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable } - // isAnimationPlaying + // animationIsPlaying if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_PLAYING) == MODEL_PACKET_CONTAINS_ANIMATION_PLAYING)) { - bool isAnimationPlaying = properties.getIsAnimationPlaying(); - memcpy(copyAt, &isAnimationPlaying, sizeof(isAnimationPlaying)); - copyAt += sizeof(isAnimationPlaying); - sizeOut += sizeof(isAnimationPlaying); + bool animationIsPlaying = properties.getAnimationIsPlaying(); + memcpy(copyAt, &animationIsPlaying, sizeof(animationIsPlaying)); + copyAt += sizeof(animationIsPlaying); + sizeOut += sizeof(animationIsPlaying); -qDebug() << "encodeModelItemEditMessageDetails()... isAnimationPlaying=" << isAnimationPlaying; +qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << animationIsPlaying; } - // frameIndex + // animationFrameIndex if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_ANIMATION_FRAME) == MODEL_PACKET_CONTAINS_ANIMATION_FRAME)) { - float frameIndex = properties.getFrameIndex(); - memcpy(copyAt, &frameIndex, sizeof(frameIndex)); - copyAt += sizeof(frameIndex); - sizeOut += sizeof(frameIndex); + float animationFrameIndex = properties.getAnimationFrameIndex(); + memcpy(copyAt, &animationFrameIndex, sizeof(animationFrameIndex)); + copyAt += sizeof(animationFrameIndex); + sizeOut += sizeof(animationFrameIndex); -qDebug() << "encodeModelItemEditMessageDetails()... frameIndex=" << frameIndex; +qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << animationFrameIndex; + } + + // animationFPS + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_ANIMATION_FPS) == MODEL_PACKET_CONTAINS_ANIMATION_FPS)) { + + float animationFPS = properties.getAnimationFPS(); + memcpy(copyAt, &animationFPS, sizeof(animationFPS)); + copyAt += sizeof(animationFPS); + sizeOut += sizeof(animationFPS); + +qDebug() << "encodeModelItemEditMessageDetails()... animationFPS=" << animationFPS; } bool wantDebugging = false; @@ -699,8 +734,8 @@ QVector ModelItem::getAnimationFrame() { if (hasAnimation() && _jointMappingCompleted) { Animation* myAnimation = getAnimation(_animationURL); QVector frames = myAnimation->getFrames(); - int frameIndex = (int)std::floor(_frameIndex) % frames.size(); - QVector rotations = frames[frameIndex].rotations; + int animationFrameIndex = (int)std::floor(_animationFrameIndex) % frames.size(); + QVector rotations = frames[animationFrameIndex].rotations; frameData.resize(_jointMapping.size()); for (int j = 0; j < _jointMapping.size(); j++) { int rotationIndex = _jointMapping[j]; @@ -716,23 +751,29 @@ void ModelItem::update(const quint64& updateTime) { _lastUpdated = updateTime; setShouldDie(getShouldDie()); -//qDebug() << "ModelItem::update() now=" << now; + quint64 now = usecTimestampNow(); // only advance the frame index if we're playing - if (getIsAnimationPlaying()) { + if (getAnimationIsPlaying()) { - quint64 now = usecTimestampNow(); float deltaTime = (float)(now - _lastAnimated) / (float)USECS_PER_SECOND; -qDebug() << "ModelItem::update() now=" << now; -qDebug() << " updateTime=" << updateTime; -qDebug() << " _lastAnimated=" << _lastAnimated; -qDebug() << " deltaTime=" << deltaTime; - + + const bool wantDebugging = false; + if (wantDebugging) { + qDebug() << "ModelItem::update() now=" << now; + qDebug() << " updateTime=" << updateTime; + qDebug() << " _lastAnimated=" << _lastAnimated; + qDebug() << " deltaTime=" << deltaTime; + } _lastAnimated = now; - const float FRAME_RATE = 10.0f; - _frameIndex += deltaTime * FRAME_RATE; -qDebug() << " _frameIndex=" << _frameIndex; + _animationFrameIndex += deltaTime * _animationFPS; + if (wantDebugging) { + qDebug() << " _animationFrameIndex=" << _animationFrameIndex; + } + + } else { + _lastAnimated = now; } } @@ -758,8 +799,9 @@ ModelItemProperties::ModelItemProperties() : _modelURL(""), _modelRotation(MODEL_DEFAULT_MODEL_ROTATION), _animationURL(""), - _isAnimationPlaying(false), - _frameIndex(0.0), + _animationIsPlaying(false), + _animationFrameIndex(0.0), + _animationFPS(MODEL_DEFAULT_ANIMATION_FPS), _id(UNKNOWN_MODEL_ID), _idSet(false), @@ -772,8 +814,9 @@ ModelItemProperties::ModelItemProperties() : _modelURLChanged(false), _modelRotationChanged(false), _animationURLChanged(false), - _isAnimationPlayingChanged(false), - _frameIndexChanged(false), + _animationIsPlayingChanged(false), + _animationFrameIndexChanged(false), + _animationFPSChanged(false), _defaultSettings(true) { } @@ -809,14 +852,18 @@ uint16_t ModelItemProperties::getChangedBits() const { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_URL; } - if (_isAnimationPlayingChanged) { + if (_animationIsPlayingChanged) { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_PLAYING; } - if (_frameIndexChanged) { + if (_animationFrameIndexChanged) { changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FRAME; } + if (_animationFPSChanged) { + changedBits += MODEL_PACKET_CONTAINS_ANIMATION_FPS; + } + return changedBits; } @@ -840,8 +887,9 @@ QScriptValue ModelItemProperties::copyToScriptValue(QScriptEngine* engine) const properties.setProperty("modelRotation", modelRotation); properties.setProperty("animationURL", _animationURL); - properties.setProperty("isAnimationPlaying", _isAnimationPlaying); - properties.setProperty("frameIndex", _frameIndex); + properties.setProperty("animationIsPlaying", _animationIsPlaying); + properties.setProperty("animationFrameIndex", _animationFrameIndex); + properties.setProperty("animationFPS", _animationFPS); if (_idSet) { properties.setProperty("id", _id); @@ -948,23 +996,34 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { } } - QScriptValue isAnimationPlaying = object.property("isAnimationPlaying"); - if (isAnimationPlaying.isValid()) { + QScriptValue animationIsPlaying = object.property("animationIsPlaying"); + if (animationIsPlaying.isValid()) { bool newIsAnimationPlaying; - newIsAnimationPlaying = isAnimationPlaying.toVariant().toBool(); - if (_defaultSettings || newIsAnimationPlaying != _isAnimationPlaying) { - _isAnimationPlaying = newIsAnimationPlaying; - _isAnimationPlayingChanged = true; + newIsAnimationPlaying = animationIsPlaying.toVariant().toBool(); + if (_defaultSettings || newIsAnimationPlaying != _animationIsPlaying) { + _animationIsPlaying = newIsAnimationPlaying; + _animationIsPlayingChanged = true; } } - QScriptValue frameIndex = object.property("frameIndex"); - if (frameIndex.isValid()) { + QScriptValue animationFrameIndex = object.property("animationFrameIndex"); + if (animationFrameIndex.isValid()) { float newFrameIndex; - newFrameIndex = frameIndex.toVariant().toFloat(); - if (_defaultSettings || newFrameIndex != _frameIndex) { - _frameIndex = newFrameIndex; - _frameIndexChanged = true; + newFrameIndex = animationFrameIndex.toVariant().toFloat(); + if (_defaultSettings || newFrameIndex != _animationFrameIndex) { + _animationFrameIndex = newFrameIndex; + _animationFrameIndexChanged = true; + } + } + + QScriptValue animationFPS = object.property("animationFPS"); + if (animationFPS.isValid()) { + float newFPS; + newFPS = animationFPS.toVariant().toFloat(); + if (_defaultSettings || newFPS != _animationFPS) { + _animationFPS = newFPS; + _animationFPSChanged = true; +qDebug() << "ModelItemProperties::copyFromScriptValue()... _animationFPS=" << _animationFPS; } } @@ -1010,18 +1069,25 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } - if (_isAnimationPlayingChanged) { - modelItem.setIsAnimationPlaying(_isAnimationPlaying); + if (_animationIsPlayingChanged) { + modelItem.setAnimationIsPlaying(_animationIsPlaying); somethingChanged = true; -qDebug() << "ModelItemProperties::copyToModelItem()... _isAnimationPlaying=" << _isAnimationPlaying; +qDebug() << "ModelItemProperties::copyToModelItem()... _animationIsPlaying=" << _animationIsPlaying; } - if (_frameIndexChanged) { - modelItem.setFrameIndex(_frameIndex); + if (_animationFrameIndexChanged) { + modelItem.setAnimationFrameIndex(_animationFrameIndex); somethingChanged = true; -qDebug() << "ModelItemProperties::copyToModelItem()... _frameIndex=" << _frameIndex; +qDebug() << "ModelItemProperties::copyToModelItem()... _animationFrameIndex=" << _animationFrameIndex; + } + + if (_animationFPSChanged) { + modelItem.setAnimationFPS(_animationFPS); + somethingChanged = true; + +qDebug() << "ModelItemProperties::copyToModelItem()... _animationFPS=" << _animationFPS; } if (somethingChanged) { @@ -1044,8 +1110,9 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURL = modelItem.getModelURL(); _modelRotation = modelItem.getModelRotation(); _animationURL = modelItem.getAnimationURL(); - _isAnimationPlaying = modelItem.getIsAnimationPlaying(); - _frameIndex = modelItem.getFrameIndex(); + _animationIsPlaying = modelItem.getAnimationIsPlaying(); + _animationFrameIndex = modelItem.getAnimationFrameIndex(); + _animationFPS = modelItem.getAnimationFPS(); _id = modelItem.getID(); _idSet = true; @@ -1058,8 +1125,9 @@ void ModelItemProperties::copyFromModelItem(const ModelItem& modelItem) { _modelURLChanged = false; _modelRotationChanged = false; _animationURLChanged = false; - _isAnimationPlayingChanged = false; - _frameIndexChanged = false; + _animationIsPlayingChanged = false; + _animationFrameIndexChanged = false; + _animationFPSChanged = false; _defaultSettings = false; } diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index a6c317d122..18074fbe13 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -46,12 +46,14 @@ const uint16_t MODEL_PACKET_CONTAINS_MODEL_ROTATION = 32; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_URL = 64; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_PLAYING = 128; const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FRAME = 256; +const uint16_t MODEL_PACKET_CONTAINS_ANIMATION_FPS = 512; const float MODEL_DEFAULT_RADIUS = 0.1f / TREE_SCALE; const float MINIMUM_MODEL_ELEMENT_SIZE = (1.0f / 100000.0f) / TREE_SCALE; // smallest size container const QString MODEL_DEFAULT_MODEL_URL(""); const glm::quat MODEL_DEFAULT_MODEL_ROTATION; const QString MODEL_DEFAULT_ANIMATION_URL(""); +const float MODEL_DEFAULT_ANIMATION_FPS = 30.0f; const PacketVersion VERSION_MODELS_HAVE_ANIMATION = 1; @@ -77,8 +79,9 @@ public: const QString& getModelURL() const { return _modelURL; } const glm::quat& getModelRotation() const { return _modelRotation; } const QString& getAnimationURL() const { return _animationURL; } - float getFrameIndex() const { return _frameIndex; } - bool getIsAnimationPlaying() const { return _isAnimationPlaying; } + float getAnimationFrameIndex() const { return _animationFrameIndex; } + bool getAnimationIsPlaying() const { return _animationIsPlaying; } + float getAnimationFPS() const { return _animationFPS; } quint64 getLastEdited() const { return _lastEdited; } uint16_t getChangedBits() const; @@ -93,8 +96,9 @@ public: void setModelURL(const QString& url) { _modelURL = url; _modelURLChanged = true; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; _modelRotationChanged = true; } void setAnimationURL(const QString& url) { _animationURL = url; _animationURLChanged = true; } - void setFrameIndex(float value) { _frameIndex = value; _frameIndexChanged = true; } - void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; _isAnimationPlayingChanged = true; } + void setAnimationFrameIndex(float value) { _animationFrameIndex = value; _animationFrameIndexChanged = true; } + void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; _animationIsPlayingChanged = true; } + void setAnimationFPS(float value) { _animationFPS = value; _animationFPSChanged = true; } /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } @@ -111,8 +115,9 @@ private: QString _modelURL; glm::quat _modelRotation; QString _animationURL; - bool _isAnimationPlaying; - float _frameIndex; + bool _animationIsPlaying; + float _animationFrameIndex; + float _animationFPS; uint32_t _id; bool _idSet; @@ -126,8 +131,9 @@ private: bool _modelURLChanged; bool _modelRotationChanged; bool _animationURLChanged; - bool _isAnimationPlayingChanged; - bool _frameIndexChanged; + bool _animationIsPlayingChanged; + bool _animationFrameIndexChanged; + bool _animationFPSChanged; bool _defaultSettings; }; Q_DECLARE_METATYPE(ModelItemProperties); @@ -238,8 +244,9 @@ public: void setModelURL(const QString& url) { _modelURL = url; } void setModelRotation(const glm::quat& rotation) { _modelRotation = rotation; } void setAnimationURL(const QString& url) { _animationURL = url; } - void setFrameIndex(float value) { _frameIndex = value; } - void setIsAnimationPlaying(bool value) { _isAnimationPlaying = value; } + void setAnimationFrameIndex(float value) { _animationFrameIndex = value; } + void setAnimationIsPlaying(bool value) { _animationIsPlaying = value; } + void setAnimationFPS(float value) { _animationFPS = value; } void setProperties(const ModelItemProperties& properties); @@ -268,8 +275,9 @@ public: QVector getAnimationFrame(); bool jointsMapped() const { return _jointMappingCompleted; } - bool getIsAnimationPlaying() const { return _isAnimationPlaying; } - float getFrameIndex() const { return _frameIndex; } + bool getAnimationIsPlaying() const { return _animationIsPlaying; } + float getAnimationFrameIndex() const { return _animationFrameIndex; } + float getAnimationFPS() const { return _animationFPS; } protected: glm::vec3 _position; @@ -291,8 +299,9 @@ protected: quint64 _lastAnimated; QString _animationURL; - float _frameIndex; // we keep this as a float and round to int only when we need the exact index - bool _isAnimationPlaying; + float _animationFrameIndex; // we keep this as a float and round to int only when we need the exact index + bool _animationIsPlaying; + float _animationFPS; bool _jointMappingCompleted; QVector _jointMapping; diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index f08ec715a9..8447e5018d 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -65,7 +65,10 @@ public slots: /// plays the model animation. ModelItemID playModelAnimation(ModelItemID modelID); - + */ + + + /* /// gets the current frame of the model animation. float getModelAnimationFrame(ModelItemID modelID); From 0321821b559b94125d9e2d217a478af931a79079 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 13 May 2014 11:38:24 -0700 Subject: [PATCH 21/44] More work on PrioVR integration. --- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/SkeletonModel.cpp | 12 ++--- interface/src/avatar/SkeletonModel.h | 3 +- interface/src/devices/PrioVR.cpp | 35 ++++++++++---- interface/src/devices/PrioVR.h | 5 +- libraries/fbx/src/FBXReader.cpp | 65 +++++++++++++++----------- libraries/fbx/src/FBXReader.h | 9 ++-- 8 files changed, 81 insertions(+), 54 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 36c51dc9fd..e6d6634653 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -111,7 +111,7 @@ void MyAvatar::reset() { void MyAvatar::update(float deltaTime) { Head* head = getHead(); head->relaxLean(deltaTime); - updateFromFaceTracker(deltaTime); + updateFromTrackers(deltaTime); if (Menu::getInstance()->isOptionChecked(MenuOption::MoveWithLean)) { // Faceshift drive is enabled, set the avatar drive based on the head position moveWithLean(); @@ -241,7 +241,7 @@ void MyAvatar::simulate(float deltaTime) { } // Update avatar head rotation with sensor data -void MyAvatar::updateFromFaceTracker(float deltaTime) { +void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index d446c2e895..2df74f23c2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -38,7 +38,7 @@ public: void reset(); void update(float deltaTime); void simulate(float deltaTime); - void updateFromFaceTracker(float deltaTime); + void updateFromTrackers(float deltaTime); void moveWithLean(); void render(const glm::vec3& cameraPosition, RenderMode renderMode = NORMAL_RENDER_MODE); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 8c21a3240f..96510dad25 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -52,15 +52,12 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { } else if (leftPalmIndex == rightPalmIndex) { // right hand only - applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, - hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]); restoreLeftHandPosition(HAND_RESTORATION_RATE); } else { - applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, - hand->getPalms()[leftPalmIndex]); - applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, - hand->getPalms()[rightPalmIndex]); + applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); + applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[rightPalmIndex]); } } @@ -140,8 +137,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position) applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector)); } -void SkeletonModel::applyPalmData(int jointIndex, const QVector& fingerJointIndices, - const QVector& fingertipJointIndices, PalmData& palm) { +void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { if (jointIndex == -1) { return; } diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index ee6b3b9de3..20384829ea 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -39,8 +39,7 @@ protected: void applyHandPosition(int jointIndex, const glm::vec3& position); - void applyPalmData(int jointIndex, const QVector& fingerJointIndices, - const QVector& fingertipJointIndices, PalmData& palm); + void applyPalmData(int jointIndex, PalmData& palm); /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index 9b5427be07..5fe6b56b11 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -11,22 +11,44 @@ #include +#include + #include "PrioVR.h" -const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, 0x0000000C, 0x0000000D, - 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; +const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, + 0x0000000C, 0x0000000D, 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; const unsigned char AXIS_LIST[] = { 9, 43, 37, 37, 37, 13, 13, 13, 52, 52, 28, 28 }; const int LIST_LENGTH = sizeof(SERIAL_LIST) / sizeof(SERIAL_LIST[0]); +const char* JOINT_NAMES[] = { "Head", "Spine", "LeftArm", "LeftForeArm", "LeftHand", "RightArm", + "RightForeArm", "RightHand", "LeftUpLeg", "LeftLeg", "RightUpLeft", "RightLeg" }; + +#ifdef HAVE_PRIOVR +static int indexOfHumanIKJoint(const char* jointName) { + for (int i = 0;; i++) { + QByteArray humanIKJoint = HUMANIK_JOINTS[i]; + if (humanIKJoint.isEmpty()) { + return -1; + } + if (humanIKJoint == jointName) { + return i; + } + } +} +#endif + PrioVR::PrioVR() { #ifdef HAVE_PRIOVR - _jointsDiscovered.resize(LIST_LENGTH); + char jointsDiscovered[LIST_LENGTH]; _skeletalDevice = yei_setUpPrioVRSensors(0x00000000, const_cast(SERIAL_LIST), - const_cast(AXIS_LIST), _jointsDiscovered.data(), LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); + const_cast(AXIS_LIST), jointsDiscovered, LIST_LENGTH, YEI_TIMESTAMP_SYSTEM); if (!_skeletalDevice) { return; } _jointRotations.resize(LIST_LENGTH); + for (int i = 0; i < LIST_LENGTH; i++) { + _humanIKJointIndices.append(jointsDiscovered[i] ? indexOfHumanIKJoint(JOINT_NAMES[i]) : -1); + } yei_tareSensors(_skeletalDevice); #endif } @@ -47,10 +69,5 @@ void PrioVR::update() { unsigned int timestamp; yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), _jointRotations.size() * sizeof(glm::quat), ×tamp); - for (int i = 0; i < _jointsDiscovered.size(); i++) { - if (_jointsDiscovered.at(i)) { - qDebug() << i << _jointRotations.at(i).x << _jointRotations.at(i).y << _jointRotations.at(i).z << _jointRotations.at(i).w; - } - } #endif } diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index 1c26009b33..8bb21f4357 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -31,6 +31,9 @@ public: PrioVR(); virtual ~PrioVR(); + + const QVector& getHumanIKJointIndices() const { return _humanIKJointIndices; } + const QVector& getJointRotations() const { return _jointRotations; } void update(); @@ -39,7 +42,7 @@ private: YEI_Device_Id _skeletalDevice; #endif - QVector _jointsDiscovered; + QVector _humanIKJointIndices; QVector _jointRotations; }; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 1fc03ceb66..8d2ba4001a 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -577,6 +577,25 @@ const char* FACESHIFT_BLENDSHAPES[] = { "" }; +const char* HUMANIK_JOINTS[] = { + "RightHand", + "RightForeArm", + "RightArm", + "Head", + "LeftArm", + "LeftForeArm", + "LeftHand", + "Spine", + "Hips", + "RightUpLeg", + "LeftUpLeg", + "RightLeg", + "LeftLeg", + "RightFoot", + "LeftFoot", + "" +}; + class FBXModel { public: QString name; @@ -1012,10 +1031,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString jointHeadName = processID(getString(joints.value("jointHead", "jointHead"))); QString jointLeftHandName = processID(getString(joints.value("jointLeftHand", "jointLeftHand"))); QString jointRightHandName = processID(getString(joints.value("jointRightHand", "jointRightHand"))); - QVariantList jointLeftFingerNames = joints.values("jointLeftFinger"); - QVariantList jointRightFingerNames = joints.values("jointRightFinger"); - QVariantList jointLeftFingertipNames = joints.values("jointLeftFingertip"); - QVariantList jointRightFingertipNames = joints.values("jointRightFingertip"); QString jointEyeLeftID; QString jointEyeRightID; QString jointNeckID; @@ -1024,10 +1039,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QString jointHeadID; QString jointLeftHandID; QString jointRightHandID; - QVector jointLeftFingerIDs(jointLeftFingerNames.size()); - QVector jointRightFingerIDs(jointRightFingerNames.size()); - QVector jointLeftFingertipIDs(jointLeftFingertipNames.size()); - QVector jointRightFingertipIDs(jointRightFingertipNames.size()); + + QVector humanIKJointNames; + for (int i = 0;; i++) { + QByteArray jointName = HUMANIK_JOINTS[i]; + if (jointName.isEmpty()) { + break; + } + humanIKJointNames.append(processID(getString(joints.value(jointName, jointName)))); + } + QVector humanIKJointIDs(humanIKJointNames.size()); QVariantHash blendshapeMappings = mapping.value("bs").toHash(); QMultiHash blendshapeIndices; @@ -1091,7 +1112,6 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else { name = getID(object.properties); } - int index; if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") { jointEyeLeftID = getID(object.properties); @@ -1115,19 +1135,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (name == jointRightHandName) { jointRightHandID = getID(object.properties); - - } else if ((index = jointLeftFingerNames.indexOf(name)) != -1) { - jointLeftFingerIDs[index] = getID(object.properties); - - } else if ((index = jointRightFingerNames.indexOf(name)) != -1) { - jointRightFingerIDs[index] = getID(object.properties); - - } else if ((index = jointLeftFingertipNames.indexOf(name)) != -1) { - jointLeftFingertipIDs[index] = getID(object.properties); - - } else if ((index = jointRightFingertipNames.indexOf(name)) != -1) { - jointRightFingertipIDs[index] = getID(object.properties); } + int humanIKJointIndex = humanIKJointNames.indexOf(name); + if (humanIKJointIndex != -1) { + humanIKJointIDs[humanIKJointIndex] = getID(object.properties); + } + glm::vec3 translation; // NOTE: the euler angles as supplied by the FBX file are in degrees glm::vec3 rotationOffset; @@ -1513,11 +1526,11 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) geometry.headJointIndex = modelIDs.indexOf(jointHeadID); geometry.leftHandJointIndex = modelIDs.indexOf(jointLeftHandID); geometry.rightHandJointIndex = modelIDs.indexOf(jointRightHandID); - geometry.leftFingerJointIndices = getIndices(jointLeftFingerIDs, modelIDs); - geometry.rightFingerJointIndices = getIndices(jointRightFingerIDs, modelIDs); - geometry.leftFingertipJointIndices = getIndices(jointLeftFingertipIDs, modelIDs); - geometry.rightFingertipJointIndices = getIndices(jointRightFingertipIDs, modelIDs); - + + foreach (const QString& id, humanIKJointIDs) { + geometry.humanIKJointIndices.append(modelIDs.indexOf(id)); + } + // extract the translation component of the neck transform if (geometry.neckJointIndex != -1) { const glm::mat4& transform = geometry.joints.at(geometry.neckJointIndex).transform; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 51e7380181..38251e4065 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -30,6 +30,9 @@ typedef QList FBXNodeList; /// The names of the blendshapes expected by Faceshift, terminated with an empty string. extern const char* FACESHIFT_BLENDSHAPES[]; +/// The names of the joints in the Maya HumanIK rig, terminated with an empty string. +extern const char* HUMANIK_JOINTS[]; + class Extents { public: /// set minimum and maximum to FLT_MAX and -FLT_MAX respectively @@ -199,11 +202,7 @@ public: int leftHandJointIndex; int rightHandJointIndex; - QVector leftFingerJointIndices; - QVector rightFingerJointIndices; - - QVector leftFingertipJointIndices; - QVector rightFingertipJointIndices; + QVector humanIKJointIndices; glm::vec3 palmDirection; From c59358ac96446c6a5e8947f4688db93afe51c64b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 May 2014 14:10:01 -0700 Subject: [PATCH 22/44] First cut at editModels with the mouse --- examples/editModels.js | 273 ++++++++++++++++++++++++++++++++++------- examples/toolBars.js | 39 +++--- 2 files changed, 244 insertions(+), 68 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 90ce87d259..46e8248681 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -368,41 +368,100 @@ function checkController(deltaTime) { moveOverlays(); } +var clickEvent = false; +var newModel; +var modifierType = -1; +var moveYZ; +var moveXZ; +var moveXY; +var yaw; +var pitch; +var roll; +var scale; + +var modelSelected = false; +var selectedModelID; +var selectedModelProperties; + + +function initToolBar() { + toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); + // New Model + newModel = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }); + print("New Model: " + newModel) + + // Move YZ + moveYZ = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Move XZ + moveXZ = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Move XY + moveXY = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + + + // Yaw + yaw = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Pitch + pitch = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + // Roll + roll = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); + + + // Scale + scale = toolBar.addTool({ + imageURL: toolIconUrl + "voxel-tool.svg", + subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, + width: toolWidth, height: toolHeight, + visible: true, + alpha: 0.9 + }, true); +} + function moveOverlays() { if (typeof(toolBar) === 'undefined') { - toolBar = new ToolBar(0, 0, ToolBar.VERTICAL); - // New Model - toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }); - // Move YZ - toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Move XZ - toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Move XY - toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); + initToolBar(); + } else if (windowDimensions.x == Controller.getViewportDimensions().x && windowDimensions.y == Controller.getViewportDimensions().y) { return; @@ -417,27 +476,147 @@ function moveOverlays() { } function mousePressEvent(event) { + modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - var url; - var index = toolBar.clicked(clickedOverlay); - if (index == 0) { - url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); - if (url == null) { - return; - } + + if (clickedOverlay != 0) { + var index = toolBar.clicked(clickedOverlay); - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); - Models.addModel({ position: position, - radius: radiusDefault, - modelURL: url - }); - } else if (index == -1) { - print("Didn't click on anything"); + switch(index) { + case newModel: + var url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); + if (url == null) { + return; + } + + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + Models.addModel({ position: position, + radius: radiusDefault, + modelURL: url + }); + break; + case moveYZ: + print("Selected moveYZ"); + + break; + case moveXZ: + print("Selected moveXZ"); + + break; + case moveXY: + print("Selected moveXY"); + + break; + case yaw: + print("Selected yaw"); + + break; + case pitch: + print("Selected pitch"); + + break; + case roll: + print("Selected roll"); + + break; + case scale: + print("Selected scale"); + + break; + default: + clickEvent = false; + return; + } + clickEvent = true; + + if (modifierType != -1) { + toolBar.tools[modifierType].select(false); + } + modifierType = index; + } else { + var pickRay = Camera.computePickRay(event.x, event.y); + Vec3.print("Looking at: ", pickRay.origin); + var foundModels = Models.findModels(pickray.origin, LASER_LENGTH_FACTOR); + for (var i = 0; i < foundModels.length; i++) { + if (!foundModels[i].isKnownID) { + var identify = Models.identifyModel(foundModels[i]); + if (!identify.isKnownID) { + print("Unknown ID " + identify.id + "(update loop)"); + return; + } + foundModels[i] = identify; + } + + var properties = Models.getModelProperties(foundModels[i]); + print("Checking properties: " + properties.id + " " + properties.isKnownID); + + // P P - Model + // /| A - Palm + // / | d B - unit vector toward tip + // / | X - base of the perpendicular line + // A---X----->B d - distance fom axis + // x x - distance from A + // + // |X-A| = (P-A).B + // X == A + ((P-A).B)B + // d = |P-X| + + var A = pickRay.origin; + var B = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, LASER_LENGTH_FACTOR)); + var P = properties.position; + + var x = Vec3.dot(Vec3.subtract(P, A), B); + var X = Vec3.sum(A, Vec3.multiply(B, x)); + var d = Vec3.length(Vec3.subtract(P, X)); + + if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) { + modelSelected = true; + selectedModelID = foundModels[i]; + selectedModelProperties = properties; + return; + } + } } } function mouseMoveEvent(event) { + if (clickEvent && !modelSelected) { + return; + } + print("Dragging"); + + switch(modifierType) { + case moveYZ: + print("Move " + moveYZ); + break; + case moveXZ: + print("Move " + moveXZ); + + break; + case moveXY: + print("Move " + moveXY); + + break; + case yaw: + print("Move " + yaw); + + break; + case pitch: + print("Move " + pitch); + + break; + case roll: + print("Move " + roll); + + break; + case scale: + print("Move " + scale); + + break; + } + + Model.editModel(selectedModelID, selectedModelProperties); } function scriptEnding() { diff --git a/examples/toolBars.js b/examples/toolBars.js index 62a01a9a15..7f8f09575b 100644 --- a/examples/toolBars.js +++ b/examples/toolBars.js @@ -13,7 +13,6 @@ Overlay2D = function(properties, overlay) { // overlay is an optionnal variable if (!(typeof(properties) === 'undefined')) { if(typeof(overlay) === 'undefined') { overlay = Overlays.addOverlay("image", properties); - print("New overlay: " + overlay); } else { Overlays.editOverlay(overlay, properties); } @@ -87,25 +86,23 @@ Tool = function(properties, selectable, selected) { // selectable and selected a return selectable; } - if (this.selectable()) { - this.selected = function() { - return selected; - } - this.select = function(doSelect) { - selected = doSelect; - properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; - Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); - } - this.toggle = function() { - selected = !selected; - properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; - Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); - - return selected; - } - - this.select(selected); + this.selected = function() { + return selected; } + this.select = function(doSelect) { + selected = doSelect; + properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; + Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); + } + this.toggle = function() { + selected = !selected; + properties.subImage.y = (selected ? 2 : 1) * properties.subImage.height; + Overlays.editOverlay(this.overlay(), { subImage: properties.subImage }); + + return selected; + } + + this.select(selected); this.baseClicked = this.clicked; this.clicked = function(clickedOverlay) { @@ -144,7 +141,7 @@ ToolBar = function(x, y, direction) { } this.tools[this.tools.length] = new Tool(properties, selectable, selected); - return this.tools.length - 1; + return ((this.tools.length) - 1); } this.move = function(x, y) { @@ -172,7 +169,7 @@ ToolBar = function(x, y, direction) { this.clicked = function(clickedOverlay) { for(var tool in this.tools) { if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay)) { - return tool; + return parseInt(tool); } } return -1; From 701bc63bd19b37907ad7272898f6b044ec42948e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 May 2014 14:34:08 -0700 Subject: [PATCH 23/44] Removed old toolbar --- examples/editModels.js | 185 +++++------------------------------------ 1 file changed, 22 insertions(+), 163 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 46e8248681..07b31e77d2 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -18,7 +18,7 @@ var toolWidth = 50; var LASER_WIDTH = 4; var LASER_COLOR = { red: 255, green: 0, blue: 0 }; -var LASER_LENGTH_FACTOR = 1.5; +var LASER_LENGTH_FACTOR = 5; var LEFT = 0; var RIGHT = 1; @@ -368,20 +368,6 @@ function checkController(deltaTime) { moveOverlays(); } -var clickEvent = false; -var newModel; -var modifierType = -1; -var moveYZ; -var moveXZ; -var moveXY; -var yaw; -var pitch; -var roll; -var scale; - -var modelSelected = false; -var selectedModelID; -var selectedModelProperties; function initToolBar() { @@ -394,68 +380,6 @@ function initToolBar() { visible: true, alpha: 0.9 }); - print("New Model: " + newModel) - - // Move YZ - moveYZ = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Move XZ - moveXZ = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Move XY - moveXY = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - - - // Yaw - yaw = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Pitch - pitch = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - // Roll - roll = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); - - - // Scale - scale = toolBar.addTool({ - imageURL: toolIconUrl + "voxel-tool.svg", - subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT }, - width: toolWidth, height: toolHeight, - visible: true, - alpha: 0.9 - }, true); } function moveOverlays() { @@ -475,74 +399,39 @@ function moveOverlays() { toolBar.move(toolsX, toolsY); } + + +var modelSelected = false; +var selectedModelID; +var selectedModelProperties; + function mousePressEvent(event) { - modelSelected = false; + var modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); - if (clickedOverlay != 0) { - var index = toolBar.clicked(clickedOverlay); - - switch(index) { - case newModel: - var url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); - if (url == null) { - return; - } - - var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); - Models.addModel({ position: position, - radius: radiusDefault, - modelURL: url - }); - break; - case moveYZ: - print("Selected moveYZ"); - - break; - case moveXZ: - print("Selected moveXZ"); - - break; - case moveXY: - print("Selected moveXY"); - - break; - case yaw: - print("Selected yaw"); - - break; - case pitch: - print("Selected pitch"); - - break; - case roll: - print("Selected roll"); - - break; - case scale: - print("Selected scale"); - - break; - default: - clickEvent = false; - return; + if (newModel == toolBar.clicked(clickedOverlay)) { + var url = Window.prompt("Model url", modelURLs[Math.floor(Math.random() * modelURLs.length)]); + if (url == null) { + return; } - clickEvent = true; - if (modifierType != -1) { - toolBar.tools[modifierType].select(false); - } - modifierType = index; + var position = Vec3.sum(MyAvatar.position, Vec3.multiply(Quat.getFront(MyAvatar.orientation), SPAWN_DISTANCE)); + Models.addModel({ position: position, + radius: radiusDefault, + modelURL: url + }); + } else { var pickRay = Camera.computePickRay(event.x, event.y); - Vec3.print("Looking at: ", pickRay.origin); + Vec3.print("[Mouse] Looking at: ", pickRay.origin); var foundModels = Models.findModels(pickray.origin, LASER_LENGTH_FACTOR); + print("Num: " + foundModels.length.toString()); for (var i = 0; i < foundModels.length; i++) { if (!foundModels[i].isKnownID) { var identify = Models.identifyModel(foundModels[i]); if (!identify.isKnownID) { print("Unknown ID " + identify.id + "(update loop)"); - return; + continue; } foundModels[i] = identify; } @@ -580,41 +469,11 @@ function mousePressEvent(event) { } function mouseMoveEvent(event) { - if (clickEvent && !modelSelected) { + if (!modelSelected) { return; } - print("Dragging"); - switch(modifierType) { - case moveYZ: - print("Move " + moveYZ); - break; - case moveXZ: - print("Move " + moveXZ); - - break; - case moveXY: - print("Move " + moveXY); - - break; - case yaw: - print("Move " + yaw); - - break; - case pitch: - print("Move " + pitch); - - break; - case roll: - print("Move " + roll); - - break; - case scale: - print("Move " + scale); - - break; - } Model.editModel(selectedModelID, selectedModelProperties); } From eceb600de721500952878e55abf62f0d2c393637 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 13 May 2014 14:44:24 -0700 Subject: [PATCH 24/44] More work on PrioVR integration. --- interface/src/Application.cpp | 5 +++ interface/src/Application.h | 4 +++ interface/src/avatar/MyAvatar.cpp | 47 ++++++++++++++++---------- interface/src/avatar/SkeletonModel.cpp | 20 +++++++++-- interface/src/devices/PrioVR.cpp | 40 +++++++++++++++++++++- interface/src/devices/PrioVR.h | 13 +++++++ 6 files changed, 108 insertions(+), 21 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index deefd10ab0..f9eaf89ea4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2752,6 +2752,9 @@ void Application::displayOverlay() { drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT); } + // give external parties a change to hook in + emit renderingOverlay(); + _overlays.render2D(); glPopMatrix(); @@ -3060,6 +3063,8 @@ void Application::resetSensors() { OculusManager::reset(); } + _prioVR.reset(); + QCursor::setPos(_mouseX, _mouseY); _myAvatar->reset(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 77f8024b6c..985859f4d0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -195,6 +195,7 @@ public: Visage* getVisage() { return &_visage; } FaceTracker* getActiveFaceTracker(); SixenseManager* getSixenseManager() { return &_sixenseManager; } + PrioVR* getPrioVR() { return &_prioVR; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } QUndoStack* getUndoStack() { return &_undoStack; } @@ -268,6 +269,9 @@ signals: /// Fired when we're rendering in-world interface elements; allows external parties to hook in. void renderingInWorldInterface(); + /// Fired when we're rendering the overlay. + void renderingOverlay(); + /// Fired when the import window is closed void importDone(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e6d6634653..8358fecd9d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -244,23 +244,28 @@ void MyAvatar::simulate(float deltaTime) { void MyAvatar::updateFromTrackers(float deltaTime) { glm::vec3 estimatedPosition, estimatedRotation; - FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); - if (tracker) { - estimatedPosition = tracker->getHeadTranslation(); - estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); - - // Rotate the body if the head is turned beyond the screen - if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { - const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f; - const float TRACKER_MIN_YAW_TURN = 15.0f; - const float TRACKER_MAX_YAW_TURN = 50.0f; - if ( (fabs(estimatedRotation.y) > TRACKER_MIN_YAW_TURN) && - (fabs(estimatedRotation.y) < TRACKER_MAX_YAW_TURN) ) { - if (estimatedRotation.y > 0.0f) { - _bodyYawDelta += (estimatedRotation.y - TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; - } else { - _bodyYawDelta += (estimatedRotation.y + TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; - } + if (Application::getInstance()->getPrioVR()->isActive()) { + estimatedRotation = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getHeadRotation())); + + } else { + FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); + if (tracker) { + estimatedPosition = tracker->getHeadTranslation(); + estimatedRotation = glm::degrees(safeEulerAngles(tracker->getHeadRotation())); + } + } + + // Rotate the body if the head is turned beyond the screen + if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { + const float TRACKER_YAW_TURN_SENSITIVITY = 0.5f; + const float TRACKER_MIN_YAW_TURN = 15.0f; + const float TRACKER_MAX_YAW_TURN = 50.0f; + if ( (fabs(estimatedRotation.y) > TRACKER_MIN_YAW_TURN) && + (fabs(estimatedRotation.y) < TRACKER_MAX_YAW_TURN) ) { + if (estimatedRotation.y > 0.0f) { + _bodyYawDelta += (estimatedRotation.y - TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; + } else { + _bodyYawDelta += (estimatedRotation.y + TRACKER_MIN_YAW_TURN) * TRACKER_YAW_TURN_SENSITIVITY; } } } @@ -278,6 +283,14 @@ void MyAvatar::updateFromTrackers(float deltaTime) { head->setDeltaYaw(estimatedRotation.y * magnifyFieldOfView); head->setDeltaRoll(estimatedRotation.z); + // the priovr can give us exact lean + if (Application::getInstance()->getPrioVR()->isActive()) { + glm::vec3 eulers = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getTorsoRotation())); + head->setLeanSideways(eulers.z); + head->setLeanForward(eulers.x); + return; + } + // Update torso lean distance based on accelerometer data const float TORSO_LENGTH = 0.5f; glm::vec3 relativePosition = estimatedPosition - glm::vec3(0.0f, -TORSO_LENGTH, 0.0f); diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 96510dad25..7a515d63dc 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -33,14 +33,28 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { return; // only simulate for own avatar } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + PrioVR* prioVR = Application::getInstance()->getPrioVR(); + if (prioVR->isActive()) { + for (int i = 0; i < prioVR->getJointRotations().size(); i++) { + int humanIKJointIndex = prioVR->getHumanIKJointIndices().at(i); + if (humanIKJointIndex == -1) { + continue; + } + int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex); + if (jointIndex != -1) { + setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true); + } + } + return; + } + // find the left and rightmost active Leap palms int leftPalmIndex, rightPalmIndex; Hand* hand = _owningAvatar->getHand(); hand->getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - const float HAND_RESTORATION_RATE = 0.25f; - - const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const float HAND_RESTORATION_RATE = 0.25f; if (leftPalmIndex == -1) { // no Leap data; set hands from mouse if (_owningAvatar->getHandState() == HAND_STATE_NULL) { diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index 5fe6b56b11..adda3b5cb2 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -13,7 +13,9 @@ #include +#include "Application.h" #include "PrioVR.h" +#include "ui/TextRenderer.h" const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000009, 0x0000000A, 0x0000000C, 0x0000000D, 0x0000000E, 0x00000004, 0x00000005, 0x00000010, 0x00000011 }; @@ -49,7 +51,6 @@ PrioVR::PrioVR() { for (int i = 0; i < LIST_LENGTH; i++) { _humanIKJointIndices.append(jointsDiscovered[i] ? indexOfHumanIKJoint(JOINT_NAMES[i]) : -1); } - yei_tareSensors(_skeletalDevice); #endif } @@ -61,6 +62,16 @@ PrioVR::~PrioVR() { #endif } +glm::quat PrioVR::getHeadRotation() const { + const int HEAD_ROTATION_INDEX = 0; + return _jointRotations.size() > HEAD_ROTATION_INDEX ? _jointRotations.at(HEAD_ROTATION_INDEX) : glm::quat(); +} + +glm::quat PrioVR::getTorsoRotation() const { + const int TORSO_ROTATION_INDEX = 1; + return _jointRotations.size() > TORSO_ROTATION_INDEX ? _jointRotations.at(TORSO_ROTATION_INDEX) : glm::quat(); +} + void PrioVR::update() { #ifdef HAVE_PRIOVR if (!_skeletalDevice) { @@ -71,3 +82,30 @@ void PrioVR::update() { _jointRotations.size() * sizeof(glm::quat), ×tamp); #endif } + +void PrioVR::reset() { +#ifdef HAVE_PRIOVR + if (!_skeletalDevice) { + return; + } + connect(Application::getInstance(), SIGNAL(renderingOverlay()), SLOT(renderCalibrationCountdown())); + _calibrationCountdownStarted = QDateTime::currentDateTime(); +#endif +} + +void PrioVR::renderCalibrationCountdown() { +#ifdef HAVE_PRIOVR + const int COUNTDOWN_SECONDS = 3; + int secondsRemaining = COUNTDOWN_SECONDS - _calibrationCountdownStarted.secsTo(QDateTime::currentDateTime()); + if (secondsRemaining == 0) { + yei_tareSensors(_skeletalDevice); + Application::getInstance()->disconnect(this); + return; + } + static TextRenderer textRenderer(MONO_FONT_FAMILY, 18, QFont::Bold, false, TextRenderer::OUTLINE_EFFECT, 2); + QByteArray text = "Assume T-Pose in " + QByteArray::number(secondsRemaining) + "..."; + textRenderer.draw((Application::getInstance()->getGLWidget()->width() - textRenderer.computeWidth(text.constData())) / 2, + Application::getInstance()->getGLWidget()->height() / 2, + text); +#endif +} diff --git a/interface/src/devices/PrioVR.h b/interface/src/devices/PrioVR.h index 8bb21f4357..9cd7bda5d4 100644 --- a/interface/src/devices/PrioVR.h +++ b/interface/src/devices/PrioVR.h @@ -12,6 +12,7 @@ #ifndef hifi_PrioVR_h #define hifi_PrioVR_h +#include #include #include @@ -32,10 +33,20 @@ public: PrioVR(); virtual ~PrioVR(); + bool isActive() const { return !_jointRotations.isEmpty(); } + + glm::quat getHeadRotation() const; + glm::quat getTorsoRotation() const; + const QVector& getHumanIKJointIndices() const { return _humanIKJointIndices; } const QVector& getJointRotations() const { return _jointRotations; } void update(); + void reset(); + +private slots: + + void renderCalibrationCountdown(); private: #ifdef HAVE_PRIOVR @@ -44,6 +55,8 @@ private: QVector _humanIKJointIndices; QVector _jointRotations; + + QDateTime _calibrationCountdownStarted; }; #endif // hifi_PrioVR_h From 624ae4732f8a11af7aabc9c1abab9282fb751acb Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 13 May 2014 14:58:34 -0700 Subject: [PATCH 25/44] Fixes to PickRay check --- examples/editModels.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 07b31e77d2..017e4ba740 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -406,7 +406,7 @@ var selectedModelID; var selectedModelProperties; function mousePressEvent(event) { - var modelSelected = false; + modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); if (newModel == toolBar.clicked(clickedOverlay)) { @@ -424,8 +424,7 @@ function mousePressEvent(event) { } else { var pickRay = Camera.computePickRay(event.x, event.y); Vec3.print("[Mouse] Looking at: ", pickRay.origin); - var foundModels = Models.findModels(pickray.origin, LASER_LENGTH_FACTOR); - print("Num: " + foundModels.length.toString()); + var foundModels = Models.findModels(pickRay.origin, LASER_LENGTH_FACTOR); for (var i = 0; i < foundModels.length; i++) { if (!foundModels[i].isKnownID) { var identify = Models.identifyModel(foundModels[i]); @@ -451,7 +450,7 @@ function mousePressEvent(event) { // d = |P-X| var A = pickRay.origin; - var B = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, LASER_LENGTH_FACTOR)); + var B = Vec3.normalize(pickRay.direction); var P = properties.position; var x = Vec3.dot(Vec3.subtract(P, A), B); @@ -462,6 +461,7 @@ function mousePressEvent(event) { modelSelected = true; selectedModelID = foundModels[i]; selectedModelProperties = properties; + print("Clicked on " + selectedModelID.id + " " + modelSelected); return; } } @@ -475,7 +475,7 @@ function mouseMoveEvent(event) { print("Dragging"); - Model.editModel(selectedModelID, selectedModelProperties); + //Model.editModel(selectedModelID, selectedModelProperties); } function scriptEnding() { @@ -488,7 +488,7 @@ Script.scriptEnding.connect(scriptEnding); // register the call back so it fires before each data send Script.update.connect(checkController); Controller.mousePressEvent.connect(mousePressEvent); -Controller.mousePressEvent.connect(mouseMoveEvent); +Controller.mouseMoveEvent.connect(mouseMoveEvent); From bca5636f9b424f3024e7186218656a9e70dc7dc9 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 13 May 2014 15:05:00 -0700 Subject: [PATCH 26/44] Looks like "reflection" can be used as a synonym for "specular." --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 8d2ba4001a..ce71e90114 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1365,7 +1365,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (type.contains("bump") || type.contains("normal")) { bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); - } else if (type.contains("specular")) { + } else if (type.contains("specular") || type.contains("reflection")) { specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); } else if (type == "lcl rotation") { From 310f184978b4b396d698e72916b21f3187465ea4 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 09:53:44 -0700 Subject: [PATCH 27/44] Add getters for onTimeout and onReadyStateChange --- libraries/script-engine/src/XMLHttpRequestClass.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index bff88d91a9..e94c67562a 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -40,8 +40,8 @@ class XMLHttpRequestClass : public QObject { Q_PROPERTY(int DONE READ getDone) // Callbacks - Q_PROPERTY(QScriptValue ontimeout WRITE setOnTimeout) - Q_PROPERTY(QScriptValue onreadystatechange WRITE setOnReadyStateChange) + Q_PROPERTY(QScriptValue ontimeout READ getOnTimeout WRITE setOnTimeout) + Q_PROPERTY(QScriptValue onreadystatechange READ getOnReadyStateChange WRITE setOnReadyStateChange) public: XMLHttpRequestClass(QScriptEngine* engine); ~XMLHttpRequestClass(); @@ -74,7 +74,9 @@ public: QScriptValue getStatus() const; QString getStatusText() const; + QScriptValue getOnTimeout() const { return _onTimeout; } void setOnTimeout(QScriptValue function) { _onTimeout = function; } + QScriptValue getOnReadyStateChange() const { return _onReadyStateChange; } void setOnReadyStateChange(QScriptValue function) { _onReadyStateChange = function; } public slots: From 02676848c605643e46789767a489701e5b8286dd Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 09:58:04 -0700 Subject: [PATCH 28/44] Fix order of components in streetAreaExample.js --- examples/streetAreaExample.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/streetAreaExample.js b/examples/streetAreaExample.js index deb10dd65a..b4efd99b70 100644 --- a/examples/streetAreaExample.js +++ b/examples/streetAreaExample.js @@ -33,8 +33,8 @@ if (req.status == 200) { for (var i = 0; i < locations.length; i++) { var loc = locations[i]; var x1 = loc[1], - y1 = loc[2], - x2 = loc[3], + x2 = loc[2], + y1 = loc[3], y2 = loc[4]; userAreas.push({ username: loc[0], From bd56ab911dad4ad92cc1162b602c5e81353d8cba Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 10:05:04 -0700 Subject: [PATCH 29/44] Fix bug with js print() breaking on certain characters The message text needs to be escaped before beign evaluated. --- libraries/script-engine/src/ScriptEngine.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index be97b37b46..7b09916a1a 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -49,7 +49,11 @@ static QScriptValue soundConstructor(QScriptContext* context, QScriptEngine* eng static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ qDebug() << "script:print()<<" << context->argument(0).toString(); - engine->evaluate("Script.print('" + context->argument(0).toString() + "')"); + QString message = context->argument(0).toString() + .replace("\\", "\\\\") + .replace("\n", "\\n") + .replace("'", "\\'"); + engine->evaluate("Script.print('" + message + "')"); return QScriptValue(); } From c16654628a5530f4051703f739eb95741deaf9a7 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 10:08:54 -0700 Subject: [PATCH 30/44] Add carriage return to escaped string in print() --- libraries/script-engine/src/ScriptEngine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 7b09916a1a..3427d0b19d 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -52,6 +52,7 @@ static QScriptValue debugPrint(QScriptContext* context, QScriptEngine* engine){ QString message = context->argument(0).toString() .replace("\\", "\\\\") .replace("\n", "\\n") + .replace("\r", "\\r") .replace("'", "\\'"); engine->evaluate("Script.print('" + message + "')"); return QScriptValue(); From a5c10220e6d62ddcac541893f0209c9be446eb50 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 14 May 2014 10:16:04 -0700 Subject: [PATCH 31/44] Remove _ prefix from private methods in XMLHttpRequest --- .../script-engine/src/XMLHttpRequestClass.cpp | 34 +++++++++---------- .../script-engine/src/XMLHttpRequestClass.h | 10 +++--- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 77b03db999..a81f8950fa 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -63,7 +63,7 @@ QString XMLHttpRequestClass::getStatusText() const { } void XMLHttpRequestClass::abort() { - _abortRequest(); + abortRequest(); } void XMLHttpRequestClass::setRequestHeader(const QString& name, const QString& value) { @@ -76,18 +76,18 @@ void XMLHttpRequestClass::requestMetaDataChanged() { // If this is a redirect, abort the current request and start a new one if (redirect.isValid() && _numRedirects < MAXIMUM_REDIRECTS) { _numRedirects++; - _abortRequest(); + abortRequest(); QUrl newUrl = _url.resolved(redirect.toUrl().toString()); _request.setUrl(newUrl); - _doSend(); + doSend(); } } void XMLHttpRequestClass::requestDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { if (_readyState == OPENED && bytesReceived > 0) { - _setReadyState(HEADERS_RECEIVED); - _setReadyState(LOADING); + setReadyState(HEADERS_RECEIVED); + setReadyState(LOADING); } } @@ -113,7 +113,7 @@ QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { return QScriptValue::NullValue; } -void XMLHttpRequestClass::_setReadyState(ReadyState readyState) { +void XMLHttpRequestClass::setReadyState(ReadyState readyState) { if (readyState != _readyState) { _readyState = readyState; if (_onReadyStateChange.isFunction()) { @@ -135,7 +135,7 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a } _request.setUrl(_url); _method = method; - _setReadyState(OPENED); + setReadyState(OPENED); } } @@ -150,7 +150,7 @@ void XMLHttpRequestClass::send(const QString& data) { _sendData->setData(data.toUtf8()); } - _doSend(); + doSend(); if (!_async) { QEventLoop loop; @@ -160,10 +160,10 @@ void XMLHttpRequestClass::send(const QString& data) { } } -void XMLHttpRequestClass::_doSend() { +void XMLHttpRequestClass::doSend() { _reply = _manager.sendCustomRequest(_request, _method.toLatin1(), _sendData); - _connectToReply(_reply); + connectToReply(_reply); if (_timeout > 0) { _timer.start(_timeout); @@ -175,9 +175,9 @@ void XMLHttpRequestClass::requestTimeout() { if (_onTimeout.isFunction()) { _onTimeout.call(QScriptValue::NullValue); } - _abortRequest(); + abortRequest(); _errorCode = QNetworkReply::TimeoutError; - _setReadyState(DONE); + setReadyState(DONE); emit requestComplete(); } @@ -203,29 +203,29 @@ void XMLHttpRequestClass::requestFinished() { _responseData = QScriptValue(QString(_rawResponseData.data())); } } - _setReadyState(DONE); + setReadyState(DONE); emit requestComplete(); } -void XMLHttpRequestClass::_abortRequest() { +void XMLHttpRequestClass::abortRequest() { // Disconnect from signals we don't want to receive any longer. disconnect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); if (_reply) { - _disconnectFromReply(_reply); + disconnectFromReply(_reply); _reply->abort(); delete _reply; _reply = NULL; } } -void XMLHttpRequestClass::_connectToReply(QNetworkReply* reply) { +void XMLHttpRequestClass::connectToReply(QNetworkReply* reply) { connect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); connect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); connect(reply, SIGNAL(metaDataChanged()), this, SLOT(requestMetaDataChanged())); } -void XMLHttpRequestClass::_disconnectFromReply(QNetworkReply* reply) { +void XMLHttpRequestClass::disconnectFromReply(QNetworkReply* reply) { disconnect(reply, SIGNAL(finished()), this, SLOT(requestFinished())); disconnect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(requestError(QNetworkReply::NetworkError))); disconnect(reply, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(requestDownloadProgress(qint64, qint64))); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index e94c67562a..49a952e638 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -93,11 +93,11 @@ signals: void requestComplete(); private: - void _setReadyState(ReadyState readyState); - void _doSend(); - void _connectToReply(QNetworkReply* reply); - void _disconnectFromReply(QNetworkReply* reply); - void _abortRequest(); + void setReadyState(ReadyState readyState); + void doSend(); + void connectToReply(QNetworkReply* reply); + void disconnectFromReply(QNetworkReply* reply); + void abortRequest(); QScriptEngine* _engine; bool _async; From 5ef71d6a3557715773d2dde964aedb08bb33a158 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 May 2014 10:40:50 -0700 Subject: [PATCH 32/44] Full mouse handling --- examples/editModels.js | 121 +++++++++++++++++- .../models/src/ModelsScriptingInterface.cpp | 1 - 2 files changed, 119 insertions(+), 3 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index 017e4ba740..70a2e178ae 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -404,8 +404,24 @@ function moveOverlays() { var modelSelected = false; var selectedModelID; var selectedModelProperties; +var mouseLastPosition; +var orientation; +var intersection; + + +var SCALE_FACTOR = 200.0; +var TRANSLATION_FACTOR = 100.0; +var ROTATION_FACTOR = 100.0; + +function rayPlaneIntersection(pickRay, point, normal) { + var d = -Vec3.dot(point, normal); + var t = -(Vec3.dot(pickRay.origin, normal) + d) / Vec3.dot(pickRay.direction, normal); + + return Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction, t)); +} function mousePressEvent(event) { + mouseLastPosition = { x: event.x, y: event.y }; modelSelected = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); @@ -461,21 +477,122 @@ function mousePressEvent(event) { modelSelected = true; selectedModelID = foundModels[i]; selectedModelProperties = properties; + + selectedModelProperties.oldRadius = selectedModelProperties.radius; + selectedModelProperties.oldPosition = { + x: selectedModelProperties.position.x, + y: selectedModelProperties.position.y, + z: selectedModelProperties.position.z, + }; + selectedModelProperties.oldRotation = { + x: selectedModelProperties.modelRotation.x, + y: selectedModelProperties.modelRotation.y, + z: selectedModelProperties.modelRotation.z, + w: selectedModelProperties.modelRotation.w, + }; + + + orientation = MyAvatar.orientation; + intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); print("Clicked on " + selectedModelID.id + " " + modelSelected); + return; } } } } +var oldModifier = 0; +var modifier = 0; +var wasShifted = false; function mouseMoveEvent(event) { if (!modelSelected) { return; } - print("Dragging"); + + if (event.isLeftButton) { + if (event.isRightButton) { + modifier = 1; // Scale + } else { + modifier = 2; // Translate + } + } else if (event.isRightButton) { + modifier = 3; // rotate + } else { + modifier = 0; + } + + var pickRay = Camera.computePickRay(event.x, event.y); + if (wasShifted != event.isShifted || modifier != oldModifier) { + selectedModelProperties.oldRadius = selectedModelProperties.radius; + + selectedModelProperties.oldPosition = { + x: selectedModelProperties.position.x, + y: selectedModelProperties.position.y, + z: selectedModelProperties.position.z, + }; + selectedModelProperties.oldRotation = { + x: selectedModelProperties.modelRotation.x, + y: selectedModelProperties.modelRotation.y, + z: selectedModelProperties.modelRotation.z, + w: selectedModelProperties.modelRotation.w, + }; + orientation = MyAvatar.orientation; + intersection = rayPlaneIntersection(pickRay, + selectedModelProperties.oldPosition, + Quat.getFront(orientation)); + + mouseLastPosition = { x: event.x, y: event.y }; + wasShifted = event.isShifted; + oldModifier = modifier; + return; + } - //Model.editModel(selectedModelID, selectedModelProperties); + switch (modifier) { + case 0: + return; + case 1: + // Let's Scale + selectedModelProperties.radius = (selectedModelProperties.oldRadius * + (1.0 + (mouseLastPosition.y - event.y) / SCALE_FACTOR)); + + if (selectedModelProperties.radius < 0.01) { + print("Scale too small ... bailling."); + return; + } + break; + + case 2: + // Let's translate + var newIntersection = rayPlaneIntersection(pickRay, + selectedModelProperties.oldPosition, + Quat.getFront(orientation)); + var vector = Vec3.subtract(newIntersection, intersection) + if (event.isShifted) { + var i = Vec3.dot(vector, Quat.getRight(orientation)); + var j = Vec3.dot(vector, Quat.getUp(orientation)); + vector = Vec3.sum(Vec3.multiply(Quat.getRight(orientation), i), + Vec3.multiply(Quat.getFront(orientation), j)); + } + + selectedModelProperties.position = Vec3.sum(selectedModelProperties.oldPosition, vector); + break; + case 3: + // Let's rotate + var rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: event.x - mouseLastPosition.x, z: 0 }); + if (event.isShifted) { + rotation = Quat.fromVec3Degrees({ x: event.y - mouseLastPosition.y, y: 0, z: mouseLastPosition.x - event.x }); + } + + var newRotation = Quat.multiply(orientation, rotation); + newRotation = Quat.multiply(newRotation, Quat.inverse(orientation)); + + selectedModelProperties.modelRotation = Quat.multiply(newRotation, selectedModelProperties.oldRotation); + break; + } + + Models.editModel(selectedModelID, selectedModelProperties); } function scriptEnding() { diff --git a/libraries/models/src/ModelsScriptingInterface.cpp b/libraries/models/src/ModelsScriptingInterface.cpp index 446b0280a4..7625eef998 100644 --- a/libraries/models/src/ModelsScriptingInterface.cpp +++ b/libraries/models/src/ModelsScriptingInterface.cpp @@ -105,7 +105,6 @@ ModelItemID ModelsScriptingInterface::editModel(ModelItemID modelID, const Model _modelTree->updateModel(modelID, properties); _modelTree->unlock(); } - return modelID; } From 53d7aeab4f1b6a38d4820d1d055725522d0b9ea7 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 14 May 2014 12:02:38 -0700 Subject: [PATCH 33/44] Fixed typo --- examples/toolBars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/toolBars.js b/examples/toolBars.js index 7f8f09575b..88b07276f0 100644 --- a/examples/toolBars.js +++ b/examples/toolBars.js @@ -1,5 +1,5 @@ // -// testScript.js +// toolBars.js // examples // // Created by ClĂ©ment Brisset on 5/7/14. From 0c34d9e065299e35374a38ad58c2e7a88e627337 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 12:59:59 -0700 Subject: [PATCH 34/44] cleanup remove some debug --- interface/src/models/ModelTreeRenderer.cpp | 6 ---- libraries/models/src/ModelItem.cpp | 35 ++----------------- libraries/models/src/ModelTreeElement.cpp | 1 - .../models/src/ModelsScriptingInterface.h | 17 --------- 4 files changed, 2 insertions(+), 57 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 36a22621b2..ae60683745 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -183,12 +183,6 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) // set the position model->setTranslation(position); - /* - qDebug() << "modelItem.getModelURL()=" << modelItem.getModelURL(); - qDebug() << "modelItem.getAnimationURL()=" << modelItem.getAnimationURL(); - qDebug() << "modelItem.hasAnimation()=" << modelItem.hasAnimation(); - */ - // handle animations.. if (modelItem.hasAnimation()) { if (!modelItem.jointsMapped()) { diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 474a6a38a4..7a4f9e3edc 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -278,8 +278,6 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += animationURLLength; bytesRead += animationURLLength; - qDebug() << "readModelDataFromBuffer()... animationURL=" << qPrintable(animationURLString); - // animationIsPlaying memcpy(&_animationIsPlaying, dataAt, sizeof(_animationIsPlaying)); dataAt += sizeof(_animationIsPlaying); @@ -290,26 +288,16 @@ int ModelItem::readModelDataFromBuffer(const unsigned char* data, int bytesLeftT dataAt += sizeof(_animationFrameIndex); bytesRead += sizeof(_animationFrameIndex); - qDebug() << "readModelDataFromBuffer()... _animationFrameIndex=" << _animationFrameIndex; - // animationFPS memcpy(&_animationFPS, dataAt, sizeof(_animationFPS)); dataAt += sizeof(_animationFPS); bytesRead += sizeof(_animationFPS); - - qDebug() << "readModelDataFromBuffer()... _animationFPS=" << _animationFPS; - - } else { - qDebug() << "readModelDataFromBuffer()... this model didn't have animation details"; } - - //qDebug() << "ModelItem::readModelDataFromBuffer()... "; debugDump(); } return bytesRead; } ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& processedBytes, ModelTree* tree, bool& valid) { - ModelItem newModelItem; // id and _lastUpdated will get set here... const unsigned char* dataAt = data; processedBytes = 0; @@ -419,7 +407,8 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& } // modelRotation - if (isNewModelItem || ((packetContainsBits & MODEL_PACKET_CONTAINS_MODEL_ROTATION) == MODEL_PACKET_CONTAINS_MODEL_ROTATION)) { + if (isNewModelItem || ((packetContainsBits & + MODEL_PACKET_CONTAINS_MODEL_ROTATION) == MODEL_PACKET_CONTAINS_MODEL_ROTATION)) { int bytes = unpackOrientationQuatFromBytes(dataAt, newModelItem._modelRotation); dataAt += bytes; processedBytes += bytes; @@ -435,7 +424,6 @@ ModelItem ModelItem::fromEditPacket(const unsigned char* data, int length, int& newModelItem._animationURL = tempString; dataAt += animationURLLength; processedBytes += animationURLLength; -qDebug() << "fromEditPacket()... animationURL=" << qPrintable(tempString); } // animationIsPlaying @@ -605,9 +593,6 @@ bool ModelItem::encodeModelEditMessageDetails(PacketType command, ModelItemID id memcpy(copyAt, qPrintable(properties.getAnimationURL()), urlLength); copyAt += urlLength; sizeOut += urlLength; - -qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable(properties.getAnimationURL()); - } // animationIsPlaying @@ -618,9 +603,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationURL=" << qPrintable memcpy(copyAt, &animationIsPlaying, sizeof(animationIsPlaying)); copyAt += sizeof(animationIsPlaying); sizeOut += sizeof(animationIsPlaying); - - -qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << animationIsPlaying; } // animationFrameIndex @@ -631,8 +613,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationIsPlaying=" << anim memcpy(copyAt, &animationFrameIndex, sizeof(animationFrameIndex)); copyAt += sizeof(animationFrameIndex); sizeOut += sizeof(animationFrameIndex); - -qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << animationFrameIndex; } // animationFPS @@ -643,8 +623,6 @@ qDebug() << "encodeModelItemEditMessageDetails()... animationFrameIndex=" << ani memcpy(copyAt, &animationFPS, sizeof(animationFPS)); copyAt += sizeof(animationFPS); sizeOut += sizeof(animationFPS); - -qDebug() << "encodeModelItemEditMessageDetails()... animationFPS=" << animationFPS; } bool wantDebugging = false; @@ -1023,7 +1001,6 @@ void ModelItemProperties::copyFromScriptValue(const QScriptValue &object) { if (_defaultSettings || newFPS != _animationFPS) { _animationFPS = newFPS; _animationFPSChanged = true; -qDebug() << "ModelItemProperties::copyFromScriptValue()... _animationFPS=" << _animationFPS; } } @@ -1065,29 +1042,21 @@ void ModelItemProperties::copyToModelItem(ModelItem& modelItem) const { if (_animationURLChanged) { modelItem.setAnimationURL(_animationURL); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... modelItem.setAnimationURL(_animationURL)=" << _animationURL; } if (_animationIsPlayingChanged) { modelItem.setAnimationIsPlaying(_animationIsPlaying); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationIsPlaying=" << _animationIsPlaying; } if (_animationFrameIndexChanged) { modelItem.setAnimationFrameIndex(_animationFrameIndex); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationFrameIndex=" << _animationFrameIndex; } if (_animationFPSChanged) { modelItem.setAnimationFPS(_animationFPS); somethingChanged = true; - -qDebug() << "ModelItemProperties::copyToModelItem()... _animationFPS=" << _animationFPS; } if (somethingChanged) { diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index e8b37c478b..c5dce04fe2 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -102,7 +102,6 @@ bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { } void ModelTreeElement::update(ModelTreeUpdateArgs& args) { -//qDebug() << "ModelTreeElement::update()..."; // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { diff --git a/libraries/models/src/ModelsScriptingInterface.h b/libraries/models/src/ModelsScriptingInterface.h index 8447e5018d..bf8e193f25 100644 --- a/libraries/models/src/ModelsScriptingInterface.h +++ b/libraries/models/src/ModelsScriptingInterface.h @@ -59,23 +59,6 @@ public slots: /// this function will not find any models in script engine contexts which don't have access to models QVector findModels(const glm::vec3& center, float radius) const; - /* - /// pauses the model animation. - ModelItemID pauseModelAnimation(ModelItemID modelID); - - /// plays the model animation. - ModelItemID playModelAnimation(ModelItemID modelID); - */ - - - /* - /// gets the current frame of the model animation. - float getModelAnimationFrame(ModelItemID modelID); - - /// gets the current frame of the model animation. - void setModelAnimationFrame(ModelItemID modelID, float frame); - */ - signals: void modelCollisionWithVoxel(const ModelItemID& modelID, const VoxelDetail& voxel, const CollisionInfo& collision); void modelCollisionWithModel(const ModelItemID& idA, const ModelItemID& idB, const CollisionInfo& collision); From 7c44162209ffc816108c5180e428587e7b4fc105 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:00:35 -0700 Subject: [PATCH 35/44] animated model example --- examples/animatedModelExample.js | 131 +++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 examples/animatedModelExample.js diff --git a/examples/animatedModelExample.js b/examples/animatedModelExample.js new file mode 100644 index 0000000000..150ca36bcd --- /dev/null +++ b/examples/animatedModelExample.js @@ -0,0 +1,131 @@ +// +// animatedModelExample.js +// examples +// +// Created by Brad Hefta-Gaub on 12/31/13. +// Copyright 2014 High Fidelity, Inc. +// +// This is an example script that demonstrates creating and editing a model +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var count = 0; +var moveUntil = 6000; +var stopAfter = moveUntil + 100; + +var pitch = 0.0; +var yaw = 0.0; +var roll = 0.0; +var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll) + +var originalProperties = { + position: { x: 10, + y: 0, + z: 0 }, + + radius : 1, + + color: { red: 0, + green: 255, + blue: 0 }, + + modelURL: "http://www.fungibleinsight.com/faces/beta.fst", + modelRotation: rotation, + animationURL: "http://www.fungibleinsight.com/faces/gangnam_style_2.fbx", + animationIsPlaying: true, +}; + +var positionDelta = { x: 0, y: 0, z: 0 }; + + +var modelID = Models.addModel(originalProperties); +print("Models.addModel()... modelID.creatorTokenID = " + modelID.creatorTokenID); + +var isPlaying = true; +var playPauseEveryWhile = 360; +var animationFPS = 30; +var adjustFPSEveryWhile = 120; + +function moveModel(deltaTime) { + +//print("count =" + count); + +print("(count % playPauseEveryWhile)=" + (count % playPauseEveryWhile)); + + if (count % playPauseEveryWhile == 0) { + isPlaying = !isPlaying; + print("isPlaying=" + isPlaying); + } + +print("(count % adjustFPSEveryWhile)=" + (count % adjustFPSEveryWhile)); + + if (count % adjustFPSEveryWhile == 0) { + +print("considering adjusting animationFPS=" + animationFPS); + + if (animationFPS == 30) { + animationFPS = 10; + } else if (animationFPS == 10) { + animationFPS = 60; + } else if (animationFPS == 60) { + animationFPS = 30; + } + print("animationFPS=" + animationFPS); + isPlaying = true; + print("always start playing if we change the FPS -- isPlaying=" + isPlaying); + } + + if (count >= moveUntil) { + + // delete it... + if (count == moveUntil) { + print("calling Models.deleteModel()"); + Models.deleteModel(modelID); + } + + // stop it... + if (count >= stopAfter) { + print("calling Script.stop()"); + Script.stop(); + } + + count++; + return; // break early + } + + count++; + + //print("modelID.creatorTokenID = " + modelID.creatorTokenID); + + if (true) { + var newProperties = { + //position: { + // x: originalProperties.position.x + (count * positionDelta.x), + // y: originalProperties.position.y + (count * positionDelta.y), + // z: originalProperties.position.z + (count * positionDelta.z) + //}, + animationIsPlaying: isPlaying, + animationFPS: animationFPS, + }; + + + //print("modelID = " + modelID); + //print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z); + + Models.editModel(modelID, newProperties); + } +} + + +// register the call back so it fires before each data send +Script.update.connect(moveModel); + + +Script.scriptEnding.connect(function () { + print("cleaning up..."); + print("modelID="+ modelID.creatorTokenID + ", id:" + modelID.id); + Models.deleteModel(modelID); +}); + From 0a8dc78c963d3796041c3d50ee819ec7f2b1dc3f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:08:32 -0700 Subject: [PATCH 36/44] tweaks to animation script example --- examples/animatedModelExample.js | 39 +++++++++++++------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/examples/animatedModelExample.js b/examples/animatedModelExample.js index 150ca36bcd..5199eb419f 100644 --- a/examples/animatedModelExample.js +++ b/examples/animatedModelExample.js @@ -37,9 +37,6 @@ var originalProperties = { animationIsPlaying: true, }; -var positionDelta = { x: 0, y: 0, z: 0 }; - - var modelID = Models.addModel(originalProperties); print("Models.addModel()... modelID.creatorTokenID = " + modelID.creatorTokenID); @@ -47,24 +44,17 @@ var isPlaying = true; var playPauseEveryWhile = 360; var animationFPS = 30; var adjustFPSEveryWhile = 120; +var resetFrameEveryWhile = 600; function moveModel(deltaTime) { - -//print("count =" + count); - -print("(count % playPauseEveryWhile)=" + (count % playPauseEveryWhile)); - + var somethingChanged = false; if (count % playPauseEveryWhile == 0) { isPlaying = !isPlaying; print("isPlaying=" + isPlaying); + somethingChanged = true; } -print("(count % adjustFPSEveryWhile)=" + (count % adjustFPSEveryWhile)); - if (count % adjustFPSEveryWhile == 0) { - -print("considering adjusting animationFPS=" + animationFPS); - if (animationFPS == 30) { animationFPS = 10; } else if (animationFPS == 10) { @@ -75,6 +65,12 @@ print("considering adjusting animationFPS=" + animationFPS); print("animationFPS=" + animationFPS); isPlaying = true; print("always start playing if we change the FPS -- isPlaying=" + isPlaying); + somethingChanged = true; + } + + if (count % resetFrameEveryWhile == 0) { + resetFrame = true; + somethingChanged = true; } if (count >= moveUntil) { @@ -99,20 +95,17 @@ print("considering adjusting animationFPS=" + animationFPS); //print("modelID.creatorTokenID = " + modelID.creatorTokenID); - if (true) { + if (somethingChanged) { var newProperties = { - //position: { - // x: originalProperties.position.x + (count * positionDelta.x), - // y: originalProperties.position.y + (count * positionDelta.y), - // z: originalProperties.position.z + (count * positionDelta.z) - //}, animationIsPlaying: isPlaying, animationFPS: animationFPS, }; - - - //print("modelID = " + modelID); - //print("newProperties.position = " + newProperties.position.x + "," + newProperties.position.y+ "," + newProperties.position.z); + + if (resetFrame) { + print("resetting the frame!"); + newProperties.animationFrameIndex = 0; + resetFrame = false; + } Models.editModel(modelID, newProperties); } From fa20e8ff950e6bd8453b96abe138c558555cadee Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 13:17:10 -0700 Subject: [PATCH 37/44] fix the agent support for scripting models --- assignment-client/src/Agent.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 9b2134ba44..e6c14d06da 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -250,6 +250,12 @@ void Agent::run() { _particleViewer.init(); _scriptEngine.getParticlesScriptingInterface()->setParticleTree(_particleViewer.getTree()); + _scriptEngine.registerGlobalObject("ModelViewer", &_modelViewer); + JurisdictionListener* modelJL = _scriptEngine.getModelsScriptingInterface()->getJurisdictionListener(); + _modelViewer.setJurisdictionListener(modelJL); + _modelViewer.init(); + _scriptEngine.getModelsScriptingInterface()->setModelTree(_modelViewer.getTree()); + _scriptEngine.setScriptContents(scriptContents); _scriptEngine.run(); setFinished(true); From cf230d1cc33ecc92153df953ac18ce789f9608ed Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 14 May 2014 13:36:26 -0700 Subject: [PATCH 38/44] PrioVR tweaks. --- interface/src/avatar/MyAvatar.cpp | 4 +++- interface/src/avatar/SkeletonModel.cpp | 2 +- interface/src/devices/PrioVR.cpp | 10 ++++++++-- interface/src/devices/Visage.cpp | 3 ++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 014d53520e..5ef4b6ecc0 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -239,7 +239,9 @@ void MyAvatar::updateFromTrackers(float deltaTime) { if (Application::getInstance()->getPrioVR()->isActive()) { estimatedRotation = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getHeadRotation())); - + estimatedRotation.x *= -1.0f; + estimatedRotation.z *= -1.0f; + } else { FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); if (tracker) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 5c017cd094..e48ebfa63c 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -200,7 +200,7 @@ void SkeletonModel::updateJointState(int index) { } void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { - if (!_owningAvatar->isMyAvatar()) { + if (!_owningAvatar->isMyAvatar() || Application::getInstance()->getPrioVR()->isActive()) { return; } // get the rotation axes in joint space and use them to adjust the rotation diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index adda3b5cb2..605ace5c9b 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -22,8 +22,8 @@ const unsigned int SERIAL_LIST[] = { 0x00000001, 0x00000000, 0x00000008, 0x00000 const unsigned char AXIS_LIST[] = { 9, 43, 37, 37, 37, 13, 13, 13, 52, 52, 28, 28 }; const int LIST_LENGTH = sizeof(SERIAL_LIST) / sizeof(SERIAL_LIST[0]); -const char* JOINT_NAMES[] = { "Head", "Spine", "LeftArm", "LeftForeArm", "LeftHand", "RightArm", - "RightForeArm", "RightHand", "LeftUpLeg", "LeftLeg", "RightUpLeft", "RightLeg" }; +const char* JOINT_NAMES[] = { "Neck", "Spine", "LeftArm", "LeftForeArm", "LeftHand", "RightArm", + "RightForeArm", "RightHand", "LeftUpLeg", "LeftLeg", "RightUpLeg", "RightLeg" }; #ifdef HAVE_PRIOVR static int indexOfHumanIKJoint(const char* jointName) { @@ -80,6 +80,12 @@ void PrioVR::update() { unsigned int timestamp; yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), _jointRotations.size() * sizeof(glm::quat), ×tamp); + + // convert to our expected coordinate system + for (int i = 0; i < _jointRotations.size(); i++) { + _jointRotations[i].y *= -1.0f; + _jointRotations[i].z *= -1.0f; + } #endif } diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 8173519478..7c8c3ae89e 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -55,7 +55,8 @@ Visage::Visage() : Visage::~Visage() { #ifdef HAVE_VISAGE _tracker->stop(); - delete _tracker; + // deleting the tracker crashes windows; disable for now + //delete _tracker; delete _data; #endif } From 4cbe8a41dba751095b0f58e6bd643fdf29bb8950 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 14 May 2014 13:40:03 -0700 Subject: [PATCH 39/44] Add PrioVR to .gitignore. --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index c296a918af..8d537b993f 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,9 @@ interface/resources/visage/* interface/external/faceplus/* !interface/external/faceplus/readme.txt +# Ignore PrioVR +interface/external/priovr/* +!interface/external/priovr/readme.txt + # Ignore interfaceCache for Linux users interface/interfaceCache/ From 95c05a720dd6fd23799fe95285fdad31bb346a41 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 14 May 2014 13:48:03 -0700 Subject: [PATCH 40/44] Tabs -> spaces. --- interface/src/avatar/MyAvatar.cpp | 4 ++-- interface/src/devices/PrioVR.cpp | 12 ++++++------ interface/src/devices/Visage.cpp | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5ef4b6ecc0..1eac264ae4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -239,8 +239,8 @@ void MyAvatar::updateFromTrackers(float deltaTime) { if (Application::getInstance()->getPrioVR()->isActive()) { estimatedRotation = glm::degrees(safeEulerAngles(Application::getInstance()->getPrioVR()->getHeadRotation())); - estimatedRotation.x *= -1.0f; - estimatedRotation.z *= -1.0f; + estimatedRotation.x *= -1.0f; + estimatedRotation.z *= -1.0f; } else { FaceTracker* tracker = Application::getInstance()->getActiveFaceTracker(); diff --git a/interface/src/devices/PrioVR.cpp b/interface/src/devices/PrioVR.cpp index 605ace5c9b..064e2be4b5 100644 --- a/interface/src/devices/PrioVR.cpp +++ b/interface/src/devices/PrioVR.cpp @@ -80,12 +80,12 @@ void PrioVR::update() { unsigned int timestamp; yei_getLastStreamDataAll(_skeletalDevice, (char*)_jointRotations.data(), _jointRotations.size() * sizeof(glm::quat), ×tamp); - - // convert to our expected coordinate system - for (int i = 0; i < _jointRotations.size(); i++) { - _jointRotations[i].y *= -1.0f; - _jointRotations[i].z *= -1.0f; - } + + // convert to our expected coordinate system + for (int i = 0; i < _jointRotations.size(); i++) { + _jointRotations[i].y *= -1.0f; + _jointRotations[i].z *= -1.0f; + } #endif } diff --git a/interface/src/devices/Visage.cpp b/interface/src/devices/Visage.cpp index 7c8c3ae89e..119d89654a 100644 --- a/interface/src/devices/Visage.cpp +++ b/interface/src/devices/Visage.cpp @@ -56,7 +56,7 @@ Visage::~Visage() { #ifdef HAVE_VISAGE _tracker->stop(); // deleting the tracker crashes windows; disable for now - //delete _tracker; + //delete _tracker; delete _data; #endif } From f6c1d3e635b2e7d26175c25153a06ac148876e2e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 14 May 2014 14:00:31 -0700 Subject: [PATCH 41/44] clean up animations --- libraries/models/src/ModelItem.cpp | 19 ++++++++++++++++++- libraries/models/src/ModelItem.h | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/libraries/models/src/ModelItem.cpp b/libraries/models/src/ModelItem.cpp index 7a4f9e3edc..c04f9a76ae 100644 --- a/libraries/models/src/ModelItem.cpp +++ b/libraries/models/src/ModelItem.cpp @@ -671,9 +671,26 @@ void ModelItem::adjustEditPacketForClockSkew(unsigned char* codeColorBuffer, ssi } -QMap ModelItem::_loadedAnimations; // TODO: cleanup?? +QMap ModelItem::_loadedAnimations; // TODO: improve cleanup by leveraging the AnimationPointer(s) AnimationCache ModelItem::_animationCache; +// This class/instance will cleanup the animations once unloaded. +class ModelAnimationsBookkeeper { +public: + ~ModelAnimationsBookkeeper() { + ModelItem::cleanupLoadedAnimations(); + } +}; + +ModelAnimationsBookkeeper modelAnimationsBookkeeperInstance; + +void ModelItem::cleanupLoadedAnimations() { + foreach(AnimationPointer animation, _loadedAnimations) { + animation.clear(); + } + _loadedAnimations.clear(); +} + Animation* ModelItem::getAnimation(const QString& url) { AnimationPointer animation; diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 18074fbe13..847e58e7c2 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -278,6 +278,8 @@ public: bool getAnimationIsPlaying() const { return _animationIsPlaying; } float getAnimationFrameIndex() const { return _animationFrameIndex; } float getAnimationFPS() const { return _animationFPS; } + + static void cleanupLoadedAnimations(); protected: glm::vec3 _position; From dbfa60ff1b7ffbd6c81ebaabc06be1651143d87d Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 14 May 2014 14:46:48 -0700 Subject: [PATCH 42/44] Fix interface log file location Put in \High Fidelity\Interface\Logs rather than \interface\Logs. --- interface/src/Application.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c23804166c..d20b46c9a9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -170,7 +170,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _packetsPerSecond(0), _bytesPerSecond(0), _previousScriptLocation(), - _logger(new FileLogger(this)), _runningScriptsWidget(new RunningScriptsWidget(_window)), _runningScriptsWidgetWasVisible(false) { @@ -190,6 +189,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : setOrganizationName(applicationInfo.value("organizationName").toString()); setOrganizationDomain(applicationInfo.value("organizationDomain").toString()); + _logger = new FileLogger(this); // After setting organization name in order to get correct directory + QSettings::setDefaultFormat(QSettings::IniFormat); _myAvatar = _avatarManager.getMyAvatar(); From ca2d2c751ce08618095c17842ffce156e311a712 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 15 May 2014 00:34:09 -0700 Subject: [PATCH 43/44] fixing models not rendering sometimes --- interface/src/models/ModelTreeRenderer.cpp | 5 ++++- .../src/particles/ParticleTreeRenderer.cpp | 2 +- libraries/models/src/ModelTree.cpp | 8 +++++--- libraries/models/src/ModelTreeElement.cpp | 17 ++++++++++++----- libraries/models/src/ModelTreeElement.h | 17 +++++++++++++++-- libraries/octree/src/OctreeElement.cpp | 2 +- libraries/octree/src/OctreeElement.h | 4 ++++ libraries/octree/src/OctreeRenderer.cpp | 2 +- libraries/octree/src/OctreeRenderer.h | 5 ++++- 9 files changed, 47 insertions(+), 15 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index ae60683745..0f9da86887 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -74,6 +74,7 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { } void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) { + args->_elementsTouched++; // actually render it here... // we need to iterate the actual modelItems of the element ModelTreeElement* modelTreeElement = (ModelTreeElement*)element; @@ -165,7 +166,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) bool drawAsModel = modelItem.hasModel(); - args->_renderedItems++; + args->_itemsRendered++; if (drawAsModel) { glPushMatrix(); @@ -220,6 +221,8 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) glutSolidSphere(radius, 15, 15); glPopMatrix(); } + } else { + args->_itemsOutOfView++; } } } diff --git a/interface/src/particles/ParticleTreeRenderer.cpp b/interface/src/particles/ParticleTreeRenderer.cpp index 2983093564..38ef9c8516 100644 --- a/interface/src/particles/ParticleTreeRenderer.cpp +++ b/interface/src/particles/ParticleTreeRenderer.cpp @@ -76,7 +76,7 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg bool drawAsModel = particle.hasModel(); - args->_renderedItems++; + args->_itemsRendered++; if (drawAsModel) { glPushMatrix(); diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index 45694b081d..ee980b1185 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -491,11 +491,14 @@ void ModelTree::update() { lockForWrite(); _isDirty = true; - ModelTreeUpdateArgs args = { }; + ModelTreeUpdateArgs args; recurseTreeWithOperation(updateOperation, &args); // now add back any of the particles that moved elements.... int movingModels = args._movingModels.size(); + + qDebug() << "ModelTree::update()... movingModels=" << movingModels; + for (int i = 0; i < movingModels; i++) { bool shouldDie = args._movingModels[i].getShouldDie(); @@ -553,7 +556,7 @@ bool ModelTree::encodeModelsDeletedSince(quint64& sinceTime, unsigned char* outp memcpy(copyAt, &numberOfIds, sizeof(numberOfIds)); copyAt += sizeof(numberOfIds); outputLength += sizeof(numberOfIds); - + // we keep a multi map of model IDs to timestamps, we only want to include the model IDs that have been // deleted since we last sent to this node _recentlyDeletedModelsLock.lockForRead(); @@ -595,7 +598,6 @@ bool ModelTree::encodeModelsDeletedSince(quint64& sinceTime, unsigned char* outp // replace the correct count for ids included memcpy(numberOfIDsAt, &numberOfIds, sizeof(numberOfIds)); - return hasMoreToSend; } diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index c5dce04fe2..2f57818044 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -84,14 +84,17 @@ bool ModelTreeElement::appendElementData(OctreePacketData* packetData, EncodeBit } bool ModelTreeElement::containsModelBounds(const ModelItem& model) const { - return _box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint()); + glm::vec3 clampedMin = glm::clamp(model.getMinimumPoint(), 0.0f, 1.0f); + glm::vec3 clampedMax = glm::clamp(model.getMaximumPoint(), 0.0f, 1.0f); + return _box.contains(clampedMin) && _box.contains(clampedMax); } bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { - if (_box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint())) { - int childForMinimumPoint = getMyChildContainingPoint(model.getMinimumPoint()); - int childForMaximumPoint = getMyChildContainingPoint(model.getMaximumPoint()); - + glm::vec3 clampedMin = glm::clamp(model.getMinimumPoint(), 0.0f, 1.0f); + glm::vec3 clampedMax = glm::clamp(model.getMaximumPoint(), 0.0f, 1.0f); + if (_box.contains(clampedMin) && _box.contains(clampedMax)) { + int childForMinimumPoint = getMyChildContainingPoint(clampedMin); + int childForMaximumPoint = getMyChildContainingPoint(clampedMax); // If I contain both the minimum and maximum point, but two different children of mine // contain those points, then I am the best fit for that model if (childForMinimumPoint != childForMaximumPoint) { @@ -102,10 +105,12 @@ bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { } void ModelTreeElement::update(ModelTreeUpdateArgs& args) { + args._totalElements++; // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { ModelItem& model = (*modelItr); + args._totalItems++; // TODO: this _lastChanged isn't actually changing because we're not marking this element as changed. // how do we want to handle this??? We really only want to consider an element changed when it is @@ -119,6 +124,8 @@ void ModelTreeElement::update(ModelTreeUpdateArgs& args) { // erase this model modelItr = _modelItems->erase(modelItr); + + args._movingItems++; // this element has changed so mark it... markWithChangedTime(); diff --git a/libraries/models/src/ModelTreeElement.h b/libraries/models/src/ModelTreeElement.h index ce9e2dec7e..83b745206f 100644 --- a/libraries/models/src/ModelTreeElement.h +++ b/libraries/models/src/ModelTreeElement.h @@ -23,7 +23,16 @@ class ModelTreeElement; class ModelTreeUpdateArgs { public: + ModelTreeUpdateArgs() : + _totalElements(0), + _totalItems(0), + _movingItems(0) + { } + QList _movingModels; + int _totalElements; + int _totalItems; + int _movingItems; }; class FindAndUpdateModelItemIDArgs { @@ -63,7 +72,11 @@ public: /// Should this element be considered to have content in it. This will be used in collision and ray casting methods. /// By default we assume that only leaves are actual content, but some octrees may have different semantics. - virtual bool hasContent() const { return isLeaf(); } + virtual bool hasContent() const { return hasModels(); } + + /// Should this element be considered to have detailed content in it. Specifically should it be rendered. + /// By default we assume that only leaves have detailed content, but some octrees may have different semantics. + virtual bool hasDetailedContent() const { return hasModels(); } /// Override this to break up large octree elements when an edit operation is performed on a smaller octree element. /// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the @@ -92,7 +105,7 @@ public: const QList& getModels() const { return *_modelItems; } QList& getModels() { return *_modelItems; } - bool hasModels() const { return _modelItems->size() > 0; } + bool hasModels() const { return _modelItems ? _modelItems->size() > 0 : false; } void update(ModelTreeUpdateArgs& args); void setTree(ModelTree* tree) { _myTree = tree; } diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index edba26f2a7..0462a3b53d 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -1213,7 +1213,7 @@ bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float float furthestDistance = furthestDistanceToCamera(*viewFrustum); float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize); bool inChildBoundary = (furthestDistance <= childBoundary); - if (isLeaf() && inChildBoundary) { + if (hasDetailedContent() && inChildBoundary) { shouldRender = true; } else { float boundary = childBoundary * 2.0f; // the boundary is always twice the distance of the child boundary diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 42c9abad46..2485e49797 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -71,6 +71,10 @@ public: /// Should this element be considered to have content in it. This will be used in collision and ray casting methods. /// By default we assume that only leaves are actual content, but some octrees may have different semantics. virtual bool hasContent() const { return isLeaf(); } + + /// Should this element be considered to have detailed content in it. Specifically should it be rendered. + /// By default we assume that only leaves have detailed content, but some octrees may have different semantics. + virtual bool hasDetailedContent() const { return isLeaf(); } /// Override this to break up large octree elements when an edit operation is performed on a smaller octree element. /// For example, if the octrees represent solid cubes and a delete of a smaller octree element is done then the diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 1a85518181..e2aec8c890 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -156,7 +156,7 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) { } void OctreeRenderer::render(RenderMode renderMode) { - RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode }; + RenderArgs args = { this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode, 0, 0, 0 }; if (_tree) { _tree->lockForRead(); _tree->recurseTreeWithOperation(renderOperation, &args); diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 73e26c97f6..18e68e26aa 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -71,12 +71,15 @@ protected: class RenderArgs { public: - int _renderedItems; OctreeRenderer* _renderer; ViewFrustum* _viewFrustum; float _sizeScale; int _boundaryLevelAdjust; OctreeRenderer::RenderMode _renderMode; + + int _elementsTouched; + int _itemsRendered; + int _itemsOutOfView; }; From 0db242bbaa222c7b8ff0e8937eb9de50b717bddf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 15 May 2014 00:46:09 -0700 Subject: [PATCH 44/44] cleanup debug --- libraries/models/src/ModelTree.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index ee980b1185..4e92544f40 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -497,8 +497,6 @@ void ModelTree::update() { // now add back any of the particles that moved elements.... int movingModels = args._movingModels.size(); - qDebug() << "ModelTree::update()... movingModels=" << movingModels; - for (int i = 0; i < movingModels; i++) { bool shouldDie = args._movingModels[i].getShouldDie();