From 7fc66392cce00da8f70fd6a1c2048e5275392a08 Mon Sep 17 00:00:00 2001 From: stojce Date: Sat, 8 Feb 2014 12:04:55 +0100 Subject: [PATCH 01/70] Interface OS X hifi URL scheme --- cmake/modules/MacOSXBundleInfo.plist.in | 47 +++++++++++++++++++++++++ interface/CMakeLists.txt | 4 +++ 2 files changed, 51 insertions(+) create mode 100644 cmake/modules/MacOSXBundleInfo.plist.in diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in new file mode 100644 index 0000000000..1682b6c022 --- /dev/null +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + CFBundleURLTypes + + + CFBundleURLName + ${MACOSX_BUNDLE_BUNDLE_NAME} URL + CFBundleURLSchemes + + hifi + + + + + diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 6af6ed478d..846df5e493 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -90,6 +90,10 @@ qt5_wrap_ui(QT_UI_HEADERS ${QT_UI_FILES}) set(INTERFACE_SRCS ${INTERFACE_SRCS} ${QT_UI_HEADERS}) if (APPLE) + + # configure CMake to use a custom Info.plist + SET_TARGET_PROPERTIES( ${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in ) + set(MACOSX_BUNDLE_BUNDLE_NAME Interface) # set how the icon shows up in the Info.plist file SET(MACOSX_BUNDLE_ICON_FILE interface.icns) From b05967a5149ccff2a723a46581214861fcb54e8a Mon Sep 17 00:00:00 2001 From: stojce Date: Sun, 9 Feb 2014 17:47:46 +0100 Subject: [PATCH 02/70] Custom URL handler --- interface/src/Application.cpp | 17 +++++++ interface/src/Application.h | 4 +- interface/src/Menu.cpp | 84 +++++++++++++++-------------------- interface/src/Menu.h | 1 + 4 files changed, 57 insertions(+), 49 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3f32c6be09..9ba7699714 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -101,6 +101,8 @@ const QString SKIP_FILENAME = QStandardPaths::writableLocation(QStandardPaths::D const int STATS_PELS_PER_LINE = 20; +const QString CUSTOM_URL_SCHEME = "hifi:"; + void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { if (message.size() > 0) { QString messageWithNewLine = message + "\n"; @@ -680,6 +682,21 @@ void Application::controlledBroadcastToNodes(const QByteArray& packet, const Nod } } +bool Application::event(QEvent* event) { + + // handle custom URL + if (event->type() == QEvent::FileOpen) { + QFileOpenEvent* fileEvent = static_cast(event); + if (!fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME)) { + QString destination = fileEvent->url().toLocalFile().remove(QRegExp(CUSTOM_URL_SCHEME + "|/")); + Menu::getInstance()->goToDestination(destination); + } + + return false; + } + return QApplication::event(event); +} + void Application::keyPressEvent(QKeyEvent* event) { _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts diff --git a/interface/src/Application.h b/interface/src/Application.h index 94b5601797..c4550e2f0b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -127,7 +127,9 @@ public: void touchUpdateEvent(QTouchEvent* event); void wheelEvent(QWheelEvent* event); - + + bool event(QEvent* event); + void makeVoxel(glm::vec3 position, float scale, unsigned char red, diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6640df3468..a447c6703a 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -913,6 +913,39 @@ void Menu::goToDomain() { sendFakeEnterEvent(); } +bool Menu::goToDestination(QString destination) { + + QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts); + + const int NUMBER_OF_COORDINATE_ITEMS = 3; + const int X_ITEM = 0; + const int Y_ITEM = 1; + const int Z_ITEM = 2; + if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { + + double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble(); + double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble(); + double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble(); + + glm::vec3 newAvatarPos(x, y, z); + + MyAvatar* myAvatar = Application::getInstance()->getAvatar(); + glm::vec3 avatarPos = myAvatar->getPosition(); + if (newAvatarPos != avatarPos) { + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + MyAvatar::sendKillAvatar(); + + qDebug("Going To Location: %f, %f, %f...", x, y, z); + myAvatar->setPosition(newAvatarPos); + } + + return true; + } + + // no coordinates were parsed + return false; +} + void Menu::goTo() { QInputDialog gotoDialog(Application::getInstance()->getWindow()); @@ -928,31 +961,8 @@ void Menu::goTo() { destination = gotoDialog.textValue(); - QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts); - - const int NUMBER_OF_COORDINATE_ITEMS = 3; - const int X_ITEM = 0; - const int Y_ITEM = 1; - const int Z_ITEM = 2; - if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { - - double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble(); - double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble(); - double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble(); - - glm::vec3 newAvatarPos(x, y, z); - - MyAvatar* myAvatar = Application::getInstance()->getAvatar(); - glm::vec3 avatarPos = myAvatar->getPosition(); - if (newAvatarPos != avatarPos) { - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - MyAvatar::sendKillAvatar(); - - qDebug("Going To Location: %f, %f, %f...", x, y, z); - myAvatar->setPosition(newAvatarPos); - } - - } else { + // go to coordinate destination or to Username + if (!goToDestination(destination)) { // there's a username entered by the user, make a request to the data-server DataServerClient::getValuesForKeysAndUserString( QStringList() @@ -983,29 +993,7 @@ void Menu::goToLocation() { int dialogReturn = coordinateDialog.exec(); if (dialogReturn == QDialog::Accepted && !coordinateDialog.textValue().isEmpty()) { - QByteArray newCoordinates; - - QString delimiterPattern(","); - QStringList coordinateItems = coordinateDialog.textValue().split(delimiterPattern); - - const int NUMBER_OF_COORDINATE_ITEMS = 3; - const int X_ITEM = 0; - const int Y_ITEM = 1; - const int Z_ITEM = 2; - if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { - double x = coordinateItems[X_ITEM].toDouble(); - double y = coordinateItems[Y_ITEM].toDouble(); - double z = coordinateItems[Z_ITEM].toDouble(); - glm::vec3 newAvatarPos(x, y, z); - - if (newAvatarPos != avatarPos) { - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - MyAvatar::sendKillAvatar(); - - qDebug("Going To Location: %f, %f, %f...", x, y, z); - myAvatar->setPosition(newAvatarPos); - } - } + goToDestination(coordinateDialog.textValue()); } sendFakeEnterEvent(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index fcd2d74940..4e835146e6 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -84,6 +84,7 @@ public: const char* member = NULL, QAction::MenuRole role = QAction::NoRole); virtual void removeAction(QMenu* menu, const QString& actionName); + bool goToDestination(QString destination); public slots: void bandwidthDetails(); From 6e31f1692437095894dd4edc301cb158e467e01c Mon Sep 17 00:00:00 2001 From: stojce Date: Tue, 11 Feb 2014 19:25:03 +0100 Subject: [PATCH 03/70] fix for Firefox --- interface/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 846df5e493..8e96006828 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -95,6 +95,8 @@ if (APPLE) SET_TARGET_PROPERTIES( ${this_target} PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in ) set(MACOSX_BUNDLE_BUNDLE_NAME Interface) + set(MACOSX_BUNDLE_GUI_IDENTIFIER io.highfidelity.Interface) + # set how the icon shows up in the Info.plist file SET(MACOSX_BUNDLE_ICON_FILE interface.icns) From b6d77ec63767c17e59572b816c97e37e56c10224 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Feb 2014 14:20:52 -0800 Subject: [PATCH 04/70] Switched from loading FBX and FST simultaneously to loading FST first, then using its filename and texdir properties (as Faceshift does) to locate the corresponding model. Also fixed a bug with the fallback models (they were invisible after second failure). --- .../body.fbx} | Bin .../meshes/{ => defaultAvatar}/body.jpg | Bin .../head.fbx} | Bin .../meshes/{ => defaultAvatar}/tail.jpg | Bin .../meshes/{ => defaultAvatar}/visor.png | Bin .../resources/meshes/defaultAvatar_body.fst | 2 + .../resources/meshes/defaultAvatar_head.fst | 4 +- interface/src/avatar/Avatar.cpp | 4 +- interface/src/renderer/FBXReader.cpp | 16 +- interface/src/renderer/FBXReader.h | 5 +- interface/src/renderer/GeometryCache.cpp | 325 +++++++++--------- interface/src/renderer/GeometryCache.h | 24 +- interface/src/renderer/Model.cpp | 8 +- 13 files changed, 191 insertions(+), 197 deletions(-) rename interface/resources/meshes/{defaultAvatar_body.fbx => defaultAvatar/body.fbx} (100%) rename interface/resources/meshes/{ => defaultAvatar}/body.jpg (100%) rename interface/resources/meshes/{defaultAvatar_head.fbx => defaultAvatar/head.fbx} (100%) rename interface/resources/meshes/{ => defaultAvatar}/tail.jpg (100%) rename interface/resources/meshes/{ => defaultAvatar}/visor.png (100%) diff --git a/interface/resources/meshes/defaultAvatar_body.fbx b/interface/resources/meshes/defaultAvatar/body.fbx similarity index 100% rename from interface/resources/meshes/defaultAvatar_body.fbx rename to interface/resources/meshes/defaultAvatar/body.fbx diff --git a/interface/resources/meshes/body.jpg b/interface/resources/meshes/defaultAvatar/body.jpg similarity index 100% rename from interface/resources/meshes/body.jpg rename to interface/resources/meshes/defaultAvatar/body.jpg diff --git a/interface/resources/meshes/defaultAvatar_head.fbx b/interface/resources/meshes/defaultAvatar/head.fbx similarity index 100% rename from interface/resources/meshes/defaultAvatar_head.fbx rename to interface/resources/meshes/defaultAvatar/head.fbx diff --git a/interface/resources/meshes/tail.jpg b/interface/resources/meshes/defaultAvatar/tail.jpg similarity index 100% rename from interface/resources/meshes/tail.jpg rename to interface/resources/meshes/defaultAvatar/tail.jpg diff --git a/interface/resources/meshes/visor.png b/interface/resources/meshes/defaultAvatar/visor.png similarity index 100% rename from interface/resources/meshes/visor.png rename to interface/resources/meshes/defaultAvatar/visor.png diff --git a/interface/resources/meshes/defaultAvatar_body.fst b/interface/resources/meshes/defaultAvatar_body.fst index 3e8fa3ef45..5874c206db 100644 --- a/interface/resources/meshes/defaultAvatar_body.fst +++ b/interface/resources/meshes/defaultAvatar_body.fst @@ -1,3 +1,5 @@ +filename=defaultAvatar/body.fbx +texdir=defaultAvatar scale=130 joint = jointRoot = jointRoot joint = jointLean = jointSpine diff --git a/interface/resources/meshes/defaultAvatar_head.fst b/interface/resources/meshes/defaultAvatar_head.fst index 1352652efc..34cf44f0e4 100644 --- a/interface/resources/meshes/defaultAvatar_head.fst +++ b/interface/resources/meshes/defaultAvatar_head.fst @@ -1,7 +1,7 @@ # faceshift target mapping file name= defaultAvatar_head -filename=../../../Avatars/Jelly/jellyrob_blue.fbx -texdir=../../../Avatars/Jelly +filename=defaultAvatar/head.fbx +texdir=defaultAvatar scale=80 rx=0 ry=0 diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 761ed59db9..c56830de9f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -345,13 +345,13 @@ bool Avatar::findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, floa void Avatar::setFaceModelURL(const QUrl &faceModelURL) { AvatarData::setFaceModelURL(faceModelURL); - const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fbx"); + const QUrl DEFAULT_FACE_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_head.fst"); _head.getFaceModel().setURL(_faceModelURL, DEFAULT_FACE_MODEL_URL); } void Avatar::setSkeletonModelURL(const QUrl &skeletonModelURL) { AvatarData::setSkeletonModelURL(skeletonModelURL); - const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fbx"); + const QUrl DEFAULT_SKELETON_MODEL_URL = QUrl::fromLocalFile("resources/meshes/defaultAvatar_body.fst"); _skeletonModel.setURL(_skeletonModelURL, DEFAULT_SKELETON_MODEL_URL); } diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index b4e7c4abc5..a0d5f03ae9 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1568,14 +1568,16 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) return geometry; } -FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping) { - QBuffer modelBuffer(const_cast(&model)); - modelBuffer.open(QIODevice::ReadOnly); +QVariantHash readMapping(const QByteArray& data) { + QBuffer buffer(const_cast(&data)); + buffer.open(QIODevice::ReadOnly); + return parseMapping(&buffer); +} - QBuffer mappingBuffer(const_cast(&mapping)); - mappingBuffer.open(QIODevice::ReadOnly); - - return extractFBXGeometry(parseFBX(&modelBuffer), parseMapping(&mappingBuffer)); +FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping) { + QBuffer buffer(const_cast(&model)); + buffer.open(QIODevice::ReadOnly); + return extractFBXGeometry(parseFBX(&buffer), mapping); } bool addMeshVoxelsOperation(OctreeElement* element, void* extraData) { diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index d700439460..5e2b77035f 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -163,9 +163,12 @@ public: QVector attachments; }; +/// Reads an FST mapping from the supplied data. +QVariantHash readMapping(const QByteArray& data); + /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing -FBXGeometry readFBX(const QByteArray& model, const QByteArray& mapping); +FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping); /// Reads SVO geometry from the supplied model data. FBXGeometry readSVO(const QByteArray& model); diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 3526fa5050..dfe6949438 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -7,11 +7,7 @@ #include -// include this before QOpenGLBuffer, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - #include -#include #include #include "Application.h" @@ -304,40 +300,23 @@ QSharedPointer GeometryCache::getGeometry(const QUrl& url, cons } NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback) : - _modelRequest(url), - _modelReply(NULL), - _mappingReply(NULL), + _request(url), + _reply(NULL), + _textureBase(url), _fallback(fallback), - _attempts(0) -{ + _attempts(0) { + if (!url.isValid()) { return; } - _modelRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - makeModelRequest(); - - QUrl mappingURL = url; - QString path = url.path(); - mappingURL.setPath(path.left(path.lastIndexOf('.')) + ".fst"); - QNetworkRequest mappingRequest(mappingURL); - mappingRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); - _mappingReply = Application::getInstance()->getNetworkAccessManager()->get(mappingRequest); - - connect(_mappingReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(maybeReadModelWithMapping())); - connect(_mappingReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleMappingReplyError())); + _request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + makeRequest(); } NetworkGeometry::~NetworkGeometry() { - if (_modelReply != NULL) { - delete _modelReply; + if (_reply != NULL) { + delete _reply; } - if (_mappingReply != NULL) { - delete _mappingReply; - } - foreach (const NetworkMesh& mesh, _meshes) { - glDeleteBuffers(1, &mesh.indexBufferID); - glDeleteBuffers(1, &mesh.vertexBufferID); - } } glm::vec4 NetworkGeometry::computeAverageColor() const { @@ -364,20 +343,155 @@ glm::vec4 NetworkGeometry::computeAverageColor() const { return (totalTriangles == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / totalTriangles; } -void NetworkGeometry::makeModelRequest() { - _modelReply = Application::getInstance()->getNetworkAccessManager()->get(_modelRequest); +void NetworkGeometry::makeRequest() { + _reply = Application::getInstance()->getNetworkAccessManager()->get(_request); - connect(_modelReply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(maybeReadModelWithMapping())); - connect(_modelReply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleModelReplyError())); + connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); + connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError())); } -void NetworkGeometry::handleModelReplyError() { - QDebug debug = qDebug() << _modelReply->errorString(); +void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + if (!_reply->isFinished()) { + return; + } - QNetworkReply::NetworkError error = _modelReply->error(); - _modelReply->disconnect(this); - _modelReply->deleteLater(); - _modelReply = NULL; + QUrl url = _reply->url(); + QByteArray data = _reply->readAll(); + _reply->disconnect(this); + _reply->deleteLater(); + _reply = NULL; + + if (url.path().toLower().endsWith(".fst")) { + // it's a mapping file; parse it and get the mesh filename + _mapping = readMapping(data); + QString filename = _mapping.value("filename").toString(); + if (filename.isNull()) { + qDebug() << "Mapping file " << url << " has no filename."; + maybeLoadFallback(); + } else { + QString texdir = _mapping.value("texdir").toString(); + if (!texdir.isNull()) { + if (!texdir.endsWith('/')) { + texdir += '/'; + } + _textureBase = url.resolved(texdir); + } + _request.setUrl(url.resolved(filename)); + makeRequest(); + } + return; + } + + try { + _geometry = url.path().toLower().endsWith(".svo") ? readSVO(data) : readFBX(data, _mapping); + + } catch (const QString& error) { + qDebug() << "Error reading " << url << ": " << error; + maybeLoadFallback(); + return; + } + + foreach (const FBXMesh& mesh, _geometry.meshes) { + NetworkMesh networkMesh = { QOpenGLBuffer(QOpenGLBuffer::IndexBuffer), QOpenGLBuffer(QOpenGLBuffer::VertexBuffer) }; + + int totalIndices = 0; + foreach (const FBXMeshPart& part, mesh.parts) { + NetworkMeshPart networkPart; + if (!part.diffuseFilename.isEmpty()) { + networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture( + _textureBase.resolved(QUrl(part.diffuseFilename)), false, mesh.isEye); + } + if (!part.normalFilename.isEmpty()) { + networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture( + _textureBase.resolved(QUrl(part.normalFilename)), true); + } + networkMesh.parts.append(networkPart); + + totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); + } + + networkMesh.indexBuffer.create(); + networkMesh.indexBuffer.bind(); + networkMesh.indexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + networkMesh.indexBuffer.allocate(totalIndices * sizeof(int)); + int offset = 0; + foreach (const FBXMeshPart& part, mesh.parts) { + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.quadIndices.size() * sizeof(int), + part.quadIndices.constData()); + offset += part.quadIndices.size() * sizeof(int); + glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.triangleIndices.size() * sizeof(int), + part.triangleIndices.constData()); + offset += part.triangleIndices.size() * sizeof(int); + } + networkMesh.indexBuffer.release(); + + networkMesh.vertexBuffer.create(); + networkMesh.vertexBuffer.bind(); + networkMesh.vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw); + + // if we don't need to do any blending or springing, then the positions/normals can be static + if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { + int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); + int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); + int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + + networkMesh.vertexBuffer.allocate(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + networkMesh.vertexBuffer.write(0, mesh.vertices.constData(), mesh.vertices.size() * sizeof(glm::vec3)); + networkMesh.vertexBuffer.write(normalsOffset, mesh.normals.constData(), mesh.normals.size() * sizeof(glm::vec3)); + networkMesh.vertexBuffer.write(tangentsOffset, mesh.tangents.constData(), + mesh.tangents.size() * sizeof(glm::vec3)); + networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3)); + networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(), + mesh.texCoords.size() * sizeof(glm::vec2)); + networkMesh.vertexBuffer.write(clusterIndicesOffset, mesh.clusterIndices.constData(), + mesh.clusterIndices.size() * sizeof(glm::vec4)); + networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(), + mesh.clusterWeights.size() * sizeof(glm::vec4)); + + // if there's no springiness, then the cluster indices/weights can be static + } else if (mesh.springiness == 0.0f) { + int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); + int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); + networkMesh.vertexBuffer.allocate(clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4)); + networkMesh.vertexBuffer.write(0, mesh.tangents.constData(), mesh.tangents.size() * sizeof(glm::vec3)); + networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3)); + networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(), + mesh.texCoords.size() * sizeof(glm::vec2)); + networkMesh.vertexBuffer.write(clusterIndicesOffset, mesh.clusterIndices.constData(), + mesh.clusterIndices.size() * sizeof(glm::vec4)); + networkMesh.vertexBuffer.write(clusterWeightsOffset, mesh.clusterWeights.constData(), + mesh.clusterWeights.size() * sizeof(glm::vec4)); + + } else { + int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); + int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); + networkMesh.vertexBuffer.allocate(texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2)); + networkMesh.vertexBuffer.write(0, mesh.tangents.constData(), mesh.tangents.size() * sizeof(glm::vec3)); + networkMesh.vertexBuffer.write(colorsOffset, mesh.colors.constData(), mesh.colors.size() * sizeof(glm::vec3)); + networkMesh.vertexBuffer.write(texCoordsOffset, mesh.texCoords.constData(), + mesh.texCoords.size() * sizeof(glm::vec2)); + } + + networkMesh.vertexBuffer.release(); + + _meshes.append(networkMesh); + } + + emit loaded(); +} + +void NetworkGeometry::handleReplyError() { + QDebug debug = qDebug() << _reply->errorString(); + + QNetworkReply::NetworkError error = _reply->error(); + _reply->disconnect(this); + _reply->deleteLater(); + _reply = NULL; // retry for certain types of failures switch (error) { @@ -394,7 +508,7 @@ void NetworkGeometry::handleModelReplyError() { const int MAX_ATTEMPTS = 8; const int BASE_DELAY_MS = 1000; if (++_attempts < MAX_ATTEMPTS) { - QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeModelRequest())); + QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest())); debug << " -- retrying..."; return; } @@ -407,135 +521,6 @@ void NetworkGeometry::handleModelReplyError() { } -void NetworkGeometry::handleMappingReplyError() { - _mappingReply->disconnect(this); - _mappingReply->deleteLater(); - _mappingReply = NULL; - - maybeReadModelWithMapping(); -} - -void NetworkGeometry::maybeReadModelWithMapping() { - if (_modelReply == NULL || !_modelReply->isFinished() || (_mappingReply != NULL && !_mappingReply->isFinished())) { - return; - } - - QUrl url = _modelReply->url(); - QByteArray model = _modelReply->readAll(); - _modelReply->disconnect(this); - _modelReply->deleteLater(); - _modelReply = NULL; - - QByteArray mapping; - if (_mappingReply != NULL) { - mapping = _mappingReply->readAll(); - _mappingReply->disconnect(this); - _mappingReply->deleteLater(); - _mappingReply = NULL; - } - - try { - _geometry = url.path().toLower().endsWith(".svo") ? readSVO(model) : readFBX(model, mapping); - - } catch (const QString& error) { - qDebug() << "Error reading " << url << ": " << error; - maybeLoadFallback(); - return; - } - - foreach (const FBXMesh& mesh, _geometry.meshes) { - NetworkMesh networkMesh; - - int totalIndices = 0; - foreach (const FBXMeshPart& part, mesh.parts) { - NetworkMeshPart networkPart; - QString basePath = url.path(); - basePath = basePath.left(basePath.lastIndexOf('/') + 1); - if (!part.diffuseFilename.isEmpty()) { - url.setPath(basePath + part.diffuseFilename); - networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(url, false, mesh.isEye); - } - if (!part.normalFilename.isEmpty()) { - url.setPath(basePath + part.normalFilename); - networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture(url, true); - } - networkMesh.parts.append(networkPart); - - totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); - } - - glGenBuffers(1, &networkMesh.indexBufferID); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalIndices * sizeof(int), NULL, GL_STATIC_DRAW); - int offset = 0; - foreach (const FBXMeshPart& part, mesh.parts) { - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.quadIndices.size() * sizeof(int), - part.quadIndices.constData()); - offset += part.quadIndices.size() * sizeof(int); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, offset, part.triangleIndices.size() * sizeof(int), - part.triangleIndices.constData()); - offset += part.triangleIndices.size() * sizeof(int); - } - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - glGenBuffers(1, &networkMesh.vertexBufferID); - glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); - - // if we don't need to do any blending or springing, then the positions/normals can be static - if (mesh.blendshapes.isEmpty() && mesh.springiness == 0.0f) { - int normalsOffset = mesh.vertices.size() * sizeof(glm::vec3); - int tangentsOffset = normalsOffset + mesh.normals.size() * sizeof(glm::vec3); - int colorsOffset = tangentsOffset + mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4), - NULL, GL_STATIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.constData()); - glBufferSubData(GL_ARRAY_BUFFER, normalsOffset, mesh.normals.size() * sizeof(glm::vec3), mesh.normals.constData()); - glBufferSubData(GL_ARRAY_BUFFER, tangentsOffset, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData()); - glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); - glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), - mesh.texCoords.constData()); - glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4), - mesh.clusterIndices.constData()); - glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, mesh.clusterWeights.size() * sizeof(glm::vec4), - mesh.clusterWeights.constData()); - - // if there's no springiness, then the cluster indices/weights can be static - } else if (mesh.springiness == 0.0f) { - int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - int clusterIndicesOffset = texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2); - int clusterWeightsOffset = clusterIndicesOffset + mesh.clusterIndices.size() * sizeof(glm::vec4); - glBufferData(GL_ARRAY_BUFFER, clusterWeightsOffset + mesh.clusterWeights.size() * sizeof(glm::vec4), - NULL, GL_STATIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData()); - glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); - glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), mesh.texCoords.constData()); - glBufferSubData(GL_ARRAY_BUFFER, clusterIndicesOffset, mesh.clusterIndices.size() * sizeof(glm::vec4), - mesh.clusterIndices.constData()); - glBufferSubData(GL_ARRAY_BUFFER, clusterWeightsOffset, mesh.clusterWeights.size() * sizeof(glm::vec4), - mesh.clusterWeights.constData()); - - } else { - int colorsOffset = mesh.tangents.size() * sizeof(glm::vec3); - int texCoordsOffset = colorsOffset + mesh.colors.size() * sizeof(glm::vec3); - glBufferData(GL_ARRAY_BUFFER, texCoordsOffset + mesh.texCoords.size() * sizeof(glm::vec2), NULL, GL_STATIC_DRAW); - glBufferSubData(GL_ARRAY_BUFFER, 0, mesh.tangents.size() * sizeof(glm::vec3), mesh.tangents.constData()); - glBufferSubData(GL_ARRAY_BUFFER, colorsOffset, mesh.colors.size() * sizeof(glm::vec3), mesh.colors.constData()); - glBufferSubData(GL_ARRAY_BUFFER, texCoordsOffset, mesh.texCoords.size() * sizeof(glm::vec2), - mesh.texCoords.constData()); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - _meshes.append(networkMesh); - } - - emit loaded(); -} - void NetworkGeometry::loadFallback() { _geometry = _fallback->_geometry; _meshes = _fallback->_meshes; diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 618796e907..0587831721 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -9,17 +9,19 @@ #ifndef __interface__GeometryCache__ #define __interface__GeometryCache__ +// include this before QOpenGLBuffer, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + #include #include #include +#include #include #include #include "FBXReader.h" -#include "InterfaceConfig.h" class QNetworkReply; -class QOpenGLBuffer; class NetworkGeometry; class NetworkMesh; @@ -76,19 +78,19 @@ signals: private slots: - void makeModelRequest(); - void handleModelReplyError(); - void handleMappingReplyError(); - void maybeReadModelWithMapping(); + void makeRequest(); + void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void handleReplyError(); void loadFallback(); private: void maybeLoadFallback(); - QNetworkRequest _modelRequest; - QNetworkReply* _modelReply; - QNetworkReply* _mappingReply; + QNetworkRequest _request; + QNetworkReply* _reply; + QVariantHash _mapping; + QUrl _textureBase; QSharedPointer _fallback; int _attempts; @@ -110,8 +112,8 @@ public: class NetworkMesh { public: - GLuint indexBufferID; - GLuint vertexBufferID; + QOpenGLBuffer indexBuffer; + QOpenGLBuffer vertexBuffer; QVector parts; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index b14ed1036d..095429f201 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -768,13 +768,13 @@ void Model::renderMeshes(float alpha, bool translucent) { (networkMesh.getTranslucentPartCount() == networkMesh.parts.size())) { continue; } - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, networkMesh.indexBufferID); - + const_cast(networkMesh.indexBuffer).bind(); + const FBXMesh& mesh = geometry.meshes.at(i); int vertexCount = mesh.vertices.size(); - glBindBuffer(GL_ARRAY_BUFFER, networkMesh.vertexBufferID); - + const_cast(networkMesh.vertexBuffer).bind(); + ProgramObject* program = &_program; ProgramObject* skinProgram = &_skinProgram; SkinLocations* skinLocations = &_skinLocations; From 6c4ecb024654aadf9764a8cf427d9f5c3b00c675 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 11 Feb 2014 14:38:31 -0800 Subject: [PATCH 05/70] Splitting hand collisions between other avatars and ourself. --- interface/src/avatar/Hand.cpp | 149 ++++++++++++++++-------------- interface/src/avatar/Hand.h | 3 + interface/src/avatar/MyAvatar.cpp | 3 + 3 files changed, 88 insertions(+), 67 deletions(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 4dac42a02e..b62e88289f 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -167,91 +167,106 @@ void Hand::simulate(float deltaTime, bool isMine) { } void Hand::updateCollisions() { - // use position to obtain the left and right palm indices - int leftPalmIndex, rightPalmIndex; - getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - + collideAgainstOtherAvatars(); + collideAgainstOurself(); +} + +void Hand::collideAgainstOtherAvatars() { + if (!Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) { + return; + } ModelCollisionList collisions; - // check for collisions + float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale(); for (size_t i = 0; i < getNumPalms(); i++) { PalmData& palm = getPalms()[i]; if (!palm.isActive()) { continue; } - float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale(); glm::vec3 totalPenetration; - - if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) { - // check other avatars - foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { - Avatar* avatar = static_cast(avatarPointer.data()); - if (avatar == _owningAvatar) { - // don't collid with our own hands - continue; - } - if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { - // Check for palm collisions - glm::vec3 myPalmPosition = palm.getPosition(); - float palmCollisionDistance = 0.1f; - bool wasColliding = palm.getIsCollidingWithPalm(); - palm.setIsCollidingWithPalm(false); - // If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound - for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) { - PalmData& otherPalm = avatar->getHand().getPalms()[j]; - if (!otherPalm.isActive()) { - continue; - } - glm::vec3 otherPalmPosition = otherPalm.getPosition(); - if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) { - palm.setIsCollidingWithPalm(true); - if (!wasColliding) { - const float PALM_COLLIDE_VOLUME = 1.f; - const float PALM_COLLIDE_FREQUENCY = 1000.f; - const float PALM_COLLIDE_DURATION_MAX = 0.75f; - const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f; - Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME, - PALM_COLLIDE_FREQUENCY, - PALM_COLLIDE_DURATION_MAX, - PALM_COLLIDE_DECAY_PER_SAMPLE); - // If the other person's palm is in motion, move mine downward to show I was hit - const float MIN_VELOCITY_FOR_SLAP = 0.05f; - if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) { - // add slapback here - } + // check other avatars + foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { + Avatar* avatar = static_cast(avatarPointer.data()); + if (avatar == _owningAvatar) { + // don't collid with our own hands + continue; + } + collisions.clear(); + if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { + // Check for palm collisions + glm::vec3 myPalmPosition = palm.getPosition(); + float palmCollisionDistance = 0.1f; + bool wasColliding = palm.getIsCollidingWithPalm(); + palm.setIsCollidingWithPalm(false); + // If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound + for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) { + PalmData& otherPalm = avatar->getHand().getPalms()[j]; + if (!otherPalm.isActive()) { + continue; + } + glm::vec3 otherPalmPosition = otherPalm.getPosition(); + if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) { + palm.setIsCollidingWithPalm(true); + if (!wasColliding) { + const float PALM_COLLIDE_VOLUME = 1.f; + const float PALM_COLLIDE_FREQUENCY = 1000.f; + const float PALM_COLLIDE_DURATION_MAX = 0.75f; + const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f; + Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME, + PALM_COLLIDE_FREQUENCY, + PALM_COLLIDE_DURATION_MAX, + PALM_COLLIDE_DECAY_PER_SAMPLE); + // If the other person's palm is in motion, move mine downward to show I was hit + const float MIN_VELOCITY_FOR_SLAP = 0.05f; + if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) { + // add slapback here } } } } - if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) { - for (int j = 0; j < collisions.size(); ++j) { - // we don't resolve penetrations that would poke the other avatar - if (!avatar->isPokeable(collisions[j])) { - totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration); - } + } + if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) { + for (int j = 0; j < collisions.size(); ++j) { + // we don't resolve penetrations that would poke the other avatar + if (!avatar->isPokeable(collisions[j])) { + totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration); } } } } - - if (Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) { - // and the current avatar (ignoring everything below the parent of the parent of the last free joint) - collisions.clear(); - const Model& skeletonModel = _owningAvatar->getSkeletonModel(); - int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( - skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : - (i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); - if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) { - for (int j = 0; j < collisions.size(); ++j) { - totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration); - } + // resolve penetration + palm.addToPosition(-totalPenetration); + } +} + +void Hand::collideAgainstOurself() { + if (!Menu::getInstance()->isOptionChecked(MenuOption::HandsCollideWithSelf)) { + return; + } + + ModelCollisionList collisions; + int leftPalmIndex, rightPalmIndex; + getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); + float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale(); + + for (size_t i = 0; i < getNumPalms(); i++) { + PalmData& palm = getPalms()[i]; + if (!palm.isActive()) { + continue; + } + glm::vec3 totalPenetration; + // and the current avatar (ignoring everything below the parent of the parent of the last free joint) + collisions.clear(); + const Model& skeletonModel = _owningAvatar->getSkeletonModel(); + int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( + skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : + (i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); + if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) { + for (int j = 0; j < collisions.size(); ++j) { + totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration); } } - - // un-penetrate + // resolve penetration palm.addToPosition(-totalPenetration); - - // we recycle the collisions container, so we clear it for the next loop - collisions.clear(); } } diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 3c8ec2d562..5a8e3d0e6f 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -97,6 +97,9 @@ private: void renderLeapFingerTrails(); void updateCollisions(); + void collideAgainstOtherAvatars(); + void collideAgainstOurself(); + void calculateGeometry(); void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5a4512419d..31437182bc 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1052,6 +1052,9 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { setPosition(_position - 0.5f * penetration); glm::vec3 pushOut = 0.5f * penetration; } + + // collide their hands against our movable limbs + } } } From 3c535e6c9c28b7e19cddffa308ac516c37974b29 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 11 Feb 2014 14:38:44 -0800 Subject: [PATCH 06/70] These need to be FST files, too. --- libraries/avatars/src/AvatarData.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 08604a95f1..6492d9b7ad 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -52,8 +52,8 @@ static const float MIN_AVATAR_SCALE = .005f; const float MAX_AUDIO_LOUDNESS = 1000.0; // close enough for mouth animation -const QUrl DEFAULT_HEAD_MODEL_URL = QUrl("http://public.highfidelity.io/meshes/defaultAvatar_head.fbx"); -const QUrl DEFAULT_BODY_MODEL_URL = QUrl("http://public.highfidelity.io/meshes/defaultAvatar_body.fbx"); +const QUrl DEFAULT_HEAD_MODEL_URL = QUrl("http://public.highfidelity.io/meshes/defaultAvatar_head.fst"); +const QUrl DEFAULT_BODY_MODEL_URL = QUrl("http://public.highfidelity.io/meshes/defaultAvatar_body.fst"); enum KeyState { NO_KEY_DOWN = 0, From 68584c654b0612f1c7ff47c6326c61b973bfb2ce Mon Sep 17 00:00:00 2001 From: stojce Date: Wed, 12 Feb 2014 02:12:52 +0100 Subject: [PATCH 07/70] Allow drag-and-drop of snapshots into Interface --- interface/src/Application.cpp | 34 +++++++++- interface/src/Application.h | 3 +- interface/src/GLCanvas.cpp | 19 +++++- interface/src/GLCanvas.h | 3 + interface/src/Menu.cpp | 107 ++++++++++++++---------------- interface/src/Menu.h | 4 +- interface/src/ui/Snapshot.cpp | 63 ++++++++++++++++-- interface/src/ui/Snapshot.h | 27 ++++++-- libraries/shared/src/NodeList.cpp | 3 +- 9 files changed, 190 insertions(+), 73 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 160d6b7c2c..0bb5d03eaa 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include @@ -197,7 +199,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : connect(audioThread, SIGNAL(started()), &_audio, SLOT(start())); audioThread->start(); - + connect(nodeList, SIGNAL(domainChanged(const QString&)), SLOT(domainChanged(const QString&))); connect(nodeList, &NodeList::nodeAdded, this, &Application::nodeAdded); connect(nodeList, &NodeList::nodeKilled, this, &Application::nodeKilled); @@ -1378,6 +1380,32 @@ void Application::wheelEvent(QWheelEvent* event) { } } +void Application::dropEvent(QDropEvent *event) { + QString snapshotPath; + const QMimeData *mimeData = event->mimeData(); + foreach (QUrl url, mimeData->urls()) { + if (url.url().toLower().endsWith("jpg")) { + snapshotPath = url.url().remove("file://"); + break; + } + } + + SnapshotMetaData* snapshotData = Snapshot::parseSnapshotData(snapshotPath); + if (snapshotData != NULL) { + if (!snapshotData->getDomain().isEmpty()) { + Menu::getInstance()->goToDomain(snapshotData->getDomain()); + } + + _myAvatar->setPosition(snapshotData->getLocation()); + _myAvatar->setOrientation(snapshotData->getOrientation()); + } else { + QMessageBox msgBox; + msgBox.setText("No location details were found in this JPG, try dragging in an authentic Hifi snapshot."); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.exec(); + } +} + void Application::sendPingPackets() { QByteArray pingPacket = NodeList::getInstance()->constructPingPacket(); controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer @@ -3794,7 +3822,7 @@ void Application::updateWindowTitle(){ QString title = QString() + _profile.getUsername() + " " + nodeList->getSessionUUID().toString() + " @ " + nodeList->getDomainHostname() + buildVersion; - + qDebug("Application title set to: %s", title.toStdString().c_str()); _window->setWindowTitle(title); } @@ -4170,6 +4198,6 @@ void Application::takeSnapshot() { player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath())); player->play(); - Snapshot::saveSnapshot(_glWidget, _profile.getUsername(), _myAvatar->getPosition()); + Snapshot::saveSnapshot(_glWidget, &_profile, _myAvatar); } diff --git a/interface/src/Application.h b/interface/src/Application.h index ff6e08758b..9508c0c9a5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -127,7 +127,8 @@ public: void touchUpdateEvent(QTouchEvent* event); void wheelEvent(QWheelEvent* event); - + void dropEvent(QDropEvent *event); + void makeVoxel(glm::vec3 position, float scale, unsigned char red, diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index cd6f49383e..cfff3b8696 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -9,6 +9,8 @@ #include "Application.h" #include "GLCanvas.h" +#include +#include GLCanvas::GLCanvas() : QGLWidget(QGLFormat(QGL::NoDepthBuffer, QGL::NoStencilBuffer)) { } @@ -16,6 +18,7 @@ GLCanvas::GLCanvas() : QGLWidget(QGLFormat(QGL::NoDepthBuffer, QGL::NoStencilBuf void GLCanvas::initializeGL() { Application::getInstance()->initializeGL(); setAttribute(Qt::WA_AcceptTouchEvents); + setAcceptDrops(true); } void GLCanvas::paintGL() { @@ -67,4 +70,18 @@ bool GLCanvas::event(QEvent* event) { void GLCanvas::wheelEvent(QWheelEvent* event) { Application::getInstance()->wheelEvent(event); -} \ No newline at end of file +} + +void GLCanvas::dragEnterEvent(QDragEnterEvent* event) { + const QMimeData *mimeData = event->mimeData(); + foreach (QUrl url, mimeData->urls()) { + if (url.url().toLower().endsWith("jpg")) { + event->acceptProposedAction(); + break; + } + } +} + +void GLCanvas::dropEvent(QDropEvent* event) { + Application::getInstance()->dropEvent(event); +} diff --git a/interface/src/GLCanvas.h b/interface/src/GLCanvas.h index ad181f4456..0f0cb5c7d0 100644 --- a/interface/src/GLCanvas.h +++ b/interface/src/GLCanvas.h @@ -31,6 +31,9 @@ protected: virtual bool event(QEvent* event); virtual void wheelEvent(QWheelEvent* event); + + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dropEvent(QDropEvent* event); }; #endif /* defined(__hifi__GLCanvas__) */ diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7eb5807c6f..a73506243d 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -109,7 +109,7 @@ Menu::Menu() : MenuOption::GoToDomain, Qt::CTRL | Qt::Key_D, this, - SLOT(goToDomain())); + SLOT(goToDomainDialog())); addActionToQMenuAndActionHash(fileMenu, MenuOption::GoToLocation, Qt::CTRL | Qt::SHIFT | Qt::Key_L, @@ -889,7 +889,18 @@ void Menu::editPreferences() { sendFakeEnterEvent(); } -void Menu::goToDomain() { +void Menu::goToDomain(const QString newDomain) { + if (NodeList::getInstance()->getDomainHostname() != newDomain) { + + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + Application::getInstance()->getAvatar()->sendKillAvatar(); + + // give our nodeList the new domain-server hostname + NodeList::getInstance()->setDomainHostname(newDomain); + } +} + +void Menu::goToDomainDialog() { QString currentDomainHostname = NodeList::getInstance()->getDomainHostname(); @@ -913,17 +924,46 @@ void Menu::goToDomain() { // the user input a new hostname, use that newHostname = domainDialog.textValue(); } - - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - Application::getInstance()->getAvatar()->sendKillAvatar(); - - // give our nodeList the new domain-server hostname - NodeList::getInstance()->setDomainHostname(domainDialog.textValue()); + + goToDomain(newHostname); } sendFakeEnterEvent(); } +bool Menu::goToDestination(QString destination) { + + QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts); + + const int NUMBER_OF_COORDINATE_ITEMS = 3; + const int X_ITEM = 0; + const int Y_ITEM = 1; + const int Z_ITEM = 2; + if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { + + double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble(); + double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble(); + double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble(); + + glm::vec3 newAvatarPos(x, y, z); + + MyAvatar* myAvatar = Application::getInstance()->getAvatar(); + glm::vec3 avatarPos = myAvatar->getPosition(); + if (newAvatarPos != avatarPos) { + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + MyAvatar::sendKillAvatar(); + + qDebug("Going To Location: %f, %f, %f...", x, y, z); + myAvatar->setPosition(newAvatarPos); + } + + return true; + } + + // no coordinates were parsed + return false; +} + void Menu::goTo() { QInputDialog gotoDialog(Application::getInstance()->getWindow()); @@ -939,31 +979,8 @@ void Menu::goTo() { destination = gotoDialog.textValue(); - QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts); - - const int NUMBER_OF_COORDINATE_ITEMS = 3; - const int X_ITEM = 0; - const int Y_ITEM = 1; - const int Z_ITEM = 2; - if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { - - double x = replaceLastOccurrence('-', '.', coordinateItems[X_ITEM].trimmed()).toDouble(); - double y = replaceLastOccurrence('-', '.', coordinateItems[Y_ITEM].trimmed()).toDouble(); - double z = replaceLastOccurrence('-', '.', coordinateItems[Z_ITEM].trimmed()).toDouble(); - - glm::vec3 newAvatarPos(x, y, z); - - MyAvatar* myAvatar = Application::getInstance()->getAvatar(); - glm::vec3 avatarPos = myAvatar->getPosition(); - if (newAvatarPos != avatarPos) { - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - MyAvatar::sendKillAvatar(); - - qDebug("Going To Location: %f, %f, %f...", x, y, z); - myAvatar->setPosition(newAvatarPos); - } - - } else { + // go to coordinate destination or to Username + if (!goToDestination(destination)) { // there's a username entered by the user, make a request to the data-server DataServerClient::getValuesForKeysAndUserString( QStringList() @@ -994,29 +1011,7 @@ void Menu::goToLocation() { int dialogReturn = coordinateDialog.exec(); if (dialogReturn == QDialog::Accepted && !coordinateDialog.textValue().isEmpty()) { - QByteArray newCoordinates; - - QString delimiterPattern(","); - QStringList coordinateItems = coordinateDialog.textValue().split(delimiterPattern); - - const int NUMBER_OF_COORDINATE_ITEMS = 3; - const int X_ITEM = 0; - const int Y_ITEM = 1; - const int Z_ITEM = 2; - if (coordinateItems.size() == NUMBER_OF_COORDINATE_ITEMS) { - double x = coordinateItems[X_ITEM].toDouble(); - double y = coordinateItems[Y_ITEM].toDouble(); - double z = coordinateItems[Z_ITEM].toDouble(); - glm::vec3 newAvatarPos(x, y, z); - - if (newAvatarPos != avatarPos) { - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - MyAvatar::sendKillAvatar(); - - qDebug("Going To Location: %f, %f, %f...", x, y, z); - myAvatar->setPosition(newAvatarPos); - } - } + goToDestination(coordinateDialog.textValue()); } sendFakeEnterEvent(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 742b9fc66f..780face29a 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -84,6 +84,8 @@ public: const char* member = NULL, QAction::MenuRole role = QAction::NoRole); virtual void removeAction(QMenu* menu, const QString& actionName); + bool goToDestination(QString destination); + void goToDomain(const QString newDomain); public slots: void bandwidthDetails(); @@ -100,7 +102,7 @@ private slots: void aboutApp(); void login(); void editPreferences(); - void goToDomain(); + void goToDomainDialog(); void goToLocation(); void bandwidthDetailsClosed(); void voxelStatsDetailsClosed(); diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index f0fef33cee..33d2087594 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -14,6 +14,8 @@ #include #include +#include + // filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg // %1 <= username, %2 <= date and time, %3 <= current location const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2@%3.jpg"; @@ -21,18 +23,69 @@ const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2@%3.jpg"; const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss"; const QString SNAPSHOTS_DIRECTORY = "Snapshots"; -void Snapshot::saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location) { - QImage shot = widget->grabFrameBuffer(); +const QString LOCATION_X = "location-x"; +const QString LOCATION_Y = "location-y"; +const QString LOCATION_Z = "location-z"; +const QString ORIENTATION_X = "orientation-x"; +const QString ORIENTATION_Y = "orientation-y"; +const QString ORIENTATION_Z = "orientation-z"; +const QString ORIENTATION_W = "orientation-w"; + +const QString DOMAIN_KEY = "domain"; + + +SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { + + if (!QFile(snapshotPath).exists()) { + return NULL; + } + + QImage shot(snapshotPath); + + // no location data stored + if (shot.text(LOCATION_X).isEmpty() || shot.text(LOCATION_Y).isEmpty() || shot.text(LOCATION_Z).isEmpty()) { + return NULL; + } + + SnapshotMetaData* data = new SnapshotMetaData(); + data->setLocation(glm::vec3(shot.text(LOCATION_X).toFloat(), + shot.text(LOCATION_Y).toFloat(), + shot.text(LOCATION_Z).toFloat())); + + data->setOrientation(glm::quat(shot.text(ORIENTATION_X).toFloat(), + shot.text(ORIENTATION_Y).toFloat(), + shot.text(ORIENTATION_Z).toFloat(), + shot.text(ORIENTATION_W).toFloat())); + + data->setDomain(shot.text(DOMAIN_KEY)); + + return data; +} + +void Snapshot::saveSnapshot(QGLWidget* widget, Profile* profile, MyAvatar* avatar) { + QImage shot = widget->grabFrameBuffer(); + + glm::vec3 location = avatar->getPosition(); + glm::quat orientation = avatar->getHead().getOrientation(); + // add metadata - shot.setText("location-x", QString::number(location.x)); - shot.setText("location-y", QString::number(location.y)); - shot.setText("location-z", QString::number(location.z)); + shot.setText(LOCATION_X, QString::number(location.x)); + shot.setText(LOCATION_Y, QString::number(location.y)); + shot.setText(LOCATION_Z, QString::number(location.z)); + + shot.setText(ORIENTATION_X, QString::number(orientation.x)); + shot.setText(ORIENTATION_Y, QString::number(orientation.y)); + shot.setText(ORIENTATION_Z, QString::number(orientation.z)); + shot.setText(ORIENTATION_W, QString::number(orientation.w)); + + shot.setText(DOMAIN_KEY, profile->getLastDomain()); QString formattedLocation = QString("%1_%2_%3").arg(location.x).arg(location.y).arg(location.z); // replace decimal . with '-' formattedLocation.replace('.', '-'); + QString username = profile->getUsername(); // normalize username, replace all non alphanumeric with '-' username.replace(QRegExp("[^A-Za-z0-9_]"), "-"); diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 26315678f9..05a2fc3147 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -13,15 +13,32 @@ #include #include -#include +#include "avatar/MyAvatar.h" +#include "avatar/Profile.h" + +class SnapshotMetaData { +public: + + QString getDomain() { return _domain; } + void setDomain(QString domain) { _domain = domain; } + + glm::vec3 getLocation() { return _location; } + void setLocation(glm::vec3 location) { _location = location; } + + glm::quat getOrientation() { return _orientation; } + void setOrientation(glm::quat orientation) { _orientation = orientation; } + +private: + QString _domain; + glm::vec3 _location; + glm::quat _orientation;; +}; class Snapshot { public: - static void saveSnapshot(QGLWidget* widget, QString username, glm::vec3 location); - -private: - QString _username; + static void saveSnapshot(QGLWidget* widget, Profile* profile, MyAvatar* avatar); + static SnapshotMetaData* parseSnapshotData(QString snapshotPath); }; #endif /* defined(__hifi__Snapshot__) */ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index c0bc7f0010..8dd3857198 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -807,7 +807,8 @@ void NodeList::loadData(QSettings *settings) { } else { _domainHostname = DEFAULT_DOMAIN_HOSTNAME; } - + + emit domainChanged(_domainHostname); settings->endGroup(); } From 8e3e7c1537fcde32970069e3b7898783e8e19f33 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 11 Feb 2014 17:18:24 -0800 Subject: [PATCH 08/70] first experimental cut at overlay image --- interface/src/Application.cpp | 6 +++ interface/src/Application.h | 4 ++ interface/src/ui/ImageOverlay.cpp | 69 +++++++++++++++++++++++++++++ interface/src/ui/ImageOverlay.h | 73 +++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 interface/src/ui/ImageOverlay.cpp create mode 100644 interface/src/ui/ImageOverlay.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 004b71074b..aca67ffe5a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1870,6 +1870,9 @@ void Application::init() { _audio.init(_glWidget); + _testOverlayA.init(_glWidget, QString("./resources/images/hifi-interface-tools.svg"), QRect(100,100,62,40), QRect(0,0,62,40)); + _testOverlayB.init(_glWidget, QString("./resources/images/hifi-interface-tools.svg"), QRect(170,100,62,40), QRect(0,80,62,40)); + _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings); connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); @@ -2997,6 +3000,9 @@ void Application::displayOverlay() { _pieMenu.render(); } + _testOverlayA.render(); // + _testOverlayB.render(); // + glPopMatrix(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index ff6e08758b..4cecc1f7d6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -71,6 +71,7 @@ #include "FileLogger.h" #include "ParticleTreeRenderer.h" #include "ControllerScriptingInterface.h" +#include "ui/ImageOverlay.h" class QAction; @@ -490,6 +491,9 @@ private: void takeSnapshot(); TouchEvent _lastTouchEvent; + + ImageOverlay _testOverlayA; + ImageOverlay _testOverlayB; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp new file mode 100644 index 0000000000..33c139089d --- /dev/null +++ b/interface/src/ui/ImageOverlay.cpp @@ -0,0 +1,69 @@ +#include "ImageOverlay.h" + +#include +#include +#include +#include + +ImageOverlay::ImageOverlay() : + _parent(NULL), + _textureID(0) +{ +} + +void ImageOverlay::init(QGLWidget* parent, const QString& filename, const QRect& drawAt, const QRect& fromImage) { + _parent = parent; + qDebug() << "ImageOverlay::init()... filename=" << filename; + _bounds = drawAt; + _fromImage = fromImage; + _textureImage = QImage(filename); + _textureID = _parent->bindTexture(_textureImage); +} + + +ImageOverlay::~ImageOverlay() { + if (_parent && _textureID) { + // do we need to call this? + //_parent->deleteTexture(_textureID); + } +} + + +void ImageOverlay::render() { +qDebug() << "ImageOverlay::render _textureID=" << _textureID << "_bounds=" << _bounds; + + + bool renderImage = false; + if (renderImage) { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, _textureID); + } + glColor4f(1.0f, 0.0f, 0.0f, 0.7f); // ??? + + float imageWidth = _textureImage.width(); + float imageHeight = _textureImage.height(); + float x = _fromImage.x() / imageWidth; + float y = _fromImage.y() / imageHeight; + float w = _fromImage.width() / imageWidth; // ?? is this what we want? not sure + float h = _fromImage.height() / imageHeight; + + qDebug() << "ImageOverlay::render x=" << x << "y=" << y << "w="< Date: Wed, 12 Feb 2014 08:29:22 -0800 Subject: [PATCH 09/70] Moving hand-avatar collision trigger calls into MyAvatar Also renaming some methods in Model to be more descriptive. --- interface/src/avatar/Avatar.cpp | 9 ++- interface/src/avatar/Avatar.h | 5 +- interface/src/avatar/Hand.cpp | 101 +++++++++++++++--------------- interface/src/avatar/Hand.h | 7 +-- interface/src/avatar/MyAvatar.cpp | 9 ++- interface/src/renderer/Model.cpp | 7 +-- interface/src/renderer/Model.h | 8 +-- 7 files changed, 71 insertions(+), 75 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 5b2a142ac2..487f72a1e5 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -445,20 +445,19 @@ float Avatar::getHeight() const { return extents.maximum.y - extents.minimum.y; } -bool Avatar::isPokeable(ModelCollisionInfo& collision) const { +bool Avatar::collisionWouldMoveAvatar(ModelCollisionInfo& collision) const { // ATM only the Skeleton is pokeable // TODO: make poke affect head if (collision._model == &_skeletonModel && collision._jointIndex != -1) { - return _skeletonModel.isPokeable(collision); + return _skeletonModel.collisionHitsMoveableJoint(collision); } return false; } -bool Avatar::poke(ModelCollisionInfo& collision) { +void Avatar::applyCollision(ModelCollisionInfo& collision) { if (collision._model == &_skeletonModel && collision._jointIndex != -1) { - return _skeletonModel.poke(collision); + _skeletonModel.applyCollision(collision); } - return false; } float Avatar::getPelvisFloatingHeight() const { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 7e8a1d8f64..2fc26a36b5 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -129,11 +129,10 @@ public: float getHeight() const; /// \return true if we expect the avatar would move as a result of the collision - bool isPokeable(ModelCollisionInfo& collision) const; + bool collisionWouldMoveAvatar(ModelCollisionInfo& collision) const; /// \param collision a data structure for storing info about collisions against Models - /// \return true if the collision affects the Avatar models - bool poke(ModelCollisionInfo& collision); + void applyCollision(ModelCollisionInfo& collision); public slots: void updateCollisionFlags(); diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index b62e88289f..7e47b0a0d5 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -84,7 +84,6 @@ void Hand::simulate(float deltaTime, bool isMine) { if (isMine) { _buckyBalls.simulate(deltaTime); - updateCollisions(); } calculateGeometry(); @@ -166,16 +165,11 @@ void Hand::simulate(float deltaTime, bool isMine) { } } -void Hand::updateCollisions() { - collideAgainstOtherAvatars(); - collideAgainstOurself(); -} - -void Hand::collideAgainstOtherAvatars() { - if (!Menu::getInstance()->isOptionChecked(MenuOption::CollideWithAvatars)) { +void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { + if (!avatar || avatar == _owningAvatar) { + // don't collide with our own hands (that is done elsewhere) return; } - ModelCollisionList collisions; float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale(); for (size_t i = 0; i < getNumPalms(); i++) { PalmData& palm = getPalms()[i]; @@ -183,58 +177,61 @@ void Hand::collideAgainstOtherAvatars() { continue; } glm::vec3 totalPenetration; - // check other avatars - foreach (const AvatarSharedPointer& avatarPointer, Application::getInstance()->getAvatarManager().getAvatarHash()) { - Avatar* avatar = static_cast(avatarPointer.data()); - if (avatar == _owningAvatar) { - // don't collid with our own hands - continue; - } - collisions.clear(); - if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { - // Check for palm collisions - glm::vec3 myPalmPosition = palm.getPosition(); - float palmCollisionDistance = 0.1f; - bool wasColliding = palm.getIsCollidingWithPalm(); - palm.setIsCollidingWithPalm(false); - // If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound - for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) { - PalmData& otherPalm = avatar->getHand().getPalms()[j]; - if (!otherPalm.isActive()) { - continue; - } - glm::vec3 otherPalmPosition = otherPalm.getPosition(); - if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) { - palm.setIsCollidingWithPalm(true); - if (!wasColliding) { - const float PALM_COLLIDE_VOLUME = 1.f; - const float PALM_COLLIDE_FREQUENCY = 1000.f; - const float PALM_COLLIDE_DURATION_MAX = 0.75f; - const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f; - Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME, - PALM_COLLIDE_FREQUENCY, - PALM_COLLIDE_DURATION_MAX, - PALM_COLLIDE_DECAY_PER_SAMPLE); - // If the other person's palm is in motion, move mine downward to show I was hit - const float MIN_VELOCITY_FOR_SLAP = 0.05f; - if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) { - // add slapback here - } + ModelCollisionList collisions; + if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { + // Check for palm collisions + glm::vec3 myPalmPosition = palm.getPosition(); + float palmCollisionDistance = 0.1f; + bool wasColliding = palm.getIsCollidingWithPalm(); + palm.setIsCollidingWithPalm(false); + // If 'Play Slaps' is enabled, look for palm-to-palm collisions and make sound + for (size_t j = 0; j < avatar->getHand().getNumPalms(); j++) { + PalmData& otherPalm = avatar->getHand().getPalms()[j]; + if (!otherPalm.isActive()) { + continue; + } + glm::vec3 otherPalmPosition = otherPalm.getPosition(); + if (glm::length(otherPalmPosition - myPalmPosition) < palmCollisionDistance) { + palm.setIsCollidingWithPalm(true); + if (!wasColliding) { + const float PALM_COLLIDE_VOLUME = 1.f; + const float PALM_COLLIDE_FREQUENCY = 1000.f; + const float PALM_COLLIDE_DURATION_MAX = 0.75f; + const float PALM_COLLIDE_DECAY_PER_SAMPLE = 0.01f; + Application::getInstance()->getAudio()->startDrumSound(PALM_COLLIDE_VOLUME, + PALM_COLLIDE_FREQUENCY, + PALM_COLLIDE_DURATION_MAX, + PALM_COLLIDE_DECAY_PER_SAMPLE); + // If the other person's palm is in motion, move mine downward to show I was hit + const float MIN_VELOCITY_FOR_SLAP = 0.05f; + if (glm::length(otherPalm.getVelocity()) > MIN_VELOCITY_FOR_SLAP) { + // add slapback here } } } } - if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) { - for (int j = 0; j < collisions.size(); ++j) { - // we don't resolve penetrations that would poke the other avatar - if (!avatar->isPokeable(collisions[j])) { + } + if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) { + for (int j = 0; j < collisions.size(); ++j) { + if (isMyHand) { + if (!avatar->collisionWouldMoveAvatar(collisions[j])) { + // we resolve the hand from collision when it belongs to MyAvatar AND the other Avatar is + // not expected to respond to the collision (hand hit unmovable part of their Avatar) totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration); } + } else { + // when this !isMyHand then avatar is MyAvatar and we apply the collision + // which might not do anything (hand hit unmovable part of MyAvatar) however + // we don't resolve the hand's penetration (we expect their simulation + // to do the right thing). + avatar->applyCollision(collisions[j]); } } } - // resolve penetration - palm.addToPosition(-totalPenetration); + if (isMyHand) { + // resolve penetration + palm.addToPosition(-totalPenetration); + } } } diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 5a8e3d0e6f..9413d024c3 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -67,6 +67,9 @@ public: glm::vec3 getAndResetGrabDeltaVelocity(); glm::quat getAndResetGrabRotation(); + void collideAgainstAvatar(Avatar* avatar, bool isMyHand); + void collideAgainstOurself(); + private: // disallow copies of the Hand, copy of owning Avatar is disallowed too Hand(const Hand&); @@ -96,10 +99,6 @@ private: void renderLeapHands(bool isMine); void renderLeapFingerTrails(); - void updateCollisions(); - void collideAgainstOtherAvatars(); - void collideAgainstOurself(); - void calculateGeometry(); void handleVoxelCollision(PalmData* palm, const glm::vec3& fingerTipPosition, VoxelTreeElement* voxel, float deltaTime); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 31437182bc..e0d9db43aa 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -344,6 +344,7 @@ void MyAvatar::simulate(float deltaTime) { _position += _velocity * deltaTime; // update avatar skeleton and simulate hand and head + _hand.collideAgainstOurself(); _hand.simulate(deltaTime, true); _skeletonModel.simulate(deltaTime); _head.setBodyRotation(glm::vec3(_bodyPitch, _bodyYaw, _bodyRoll)); @@ -1050,11 +1051,13 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { avatar->getPosition(), theirCapsuleRadius, theirCapsuleHeight, penetration)) { // move the avatar out by half the penetration setPosition(_position - 0.5f * penetration); - glm::vec3 pushOut = 0.5f * penetration; } - // collide their hands against our movable limbs - + // collide our hands against them + _hand.collideAgainstAvatar(avatar, true); + + // collide their hands against us + avatar->getHand().collideAgainstAvatar(this, false); } } } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index de48be8694..f899e76ef4 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -722,7 +722,7 @@ void Model::renderCollisionProxies(float alpha) { glPopMatrix(); } -bool Model::isPokeable(ModelCollisionInfo& collision) const { +bool Model::collisionHitsMoveableJoint(ModelCollisionInfo& collision) const { // the joint is pokable by a collision if it exists and is free to move const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._jointIndex]; if (joint.parentIndex == -1 || @@ -736,7 +736,7 @@ bool Model::isPokeable(ModelCollisionInfo& collision) const { return !freeLineage.isEmpty(); } -bool Model::poke(ModelCollisionInfo& collision) { +void Model::applyCollision(ModelCollisionInfo& collision) { // This needs work. At the moment it can wiggle joints that are free to move (such as arms) // but unmovable joints (such as torso) cannot be influenced at all. glm::vec3 jointPosition(0.f); @@ -760,11 +760,10 @@ bool Model::poke(ModelCollisionInfo& collision) { getJointPosition(jointIndex, end); glm::vec3 newEnd = start + glm::angleAxis(glm::degrees(angle), axis) * (end - start); // try to move it - return setJointPosition(jointIndex, newEnd, -1, true); + setJointPosition(jointIndex, newEnd, -1, true); } } } - return false; } void Model::deleteGeometry() { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 003cdfe3e5..bab25bed7a 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -167,12 +167,12 @@ public: void renderCollisionProxies(float alpha); - /// \return true if the collision would move the model - bool isPokeable(ModelCollisionInfo& collision) const; + /// \return true if the collision is against a moveable joint + bool collisionHitsMoveableJoint(ModelCollisionInfo& collision) const; /// \param collisionInfo info about the collision - /// \return true if collision affects the Model - bool poke(ModelCollisionInfo& collisionInfo); + /// Use the collisionInfo to affect the model + void applyCollision(ModelCollisionInfo& collisionInfo); protected: From 0244ead5dfc09b9ab7e5bdc11bc91fa0d55379c3 Mon Sep 17 00:00:00 2001 From: stojce Date: Wed, 12 Feb 2014 21:06:30 +0100 Subject: [PATCH 10/70] fixed glm::quat initialization --- interface/src/ui/Snapshot.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 33d2087594..bd4de19c86 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -53,10 +53,10 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { shot.text(LOCATION_Y).toFloat(), shot.text(LOCATION_Z).toFloat())); - data->setOrientation(glm::quat(shot.text(ORIENTATION_X).toFloat(), + data->setOrientation(glm::quat(shot.text(ORIENTATION_W).toFloat(), + shot.text(ORIENTATION_X).toFloat(), shot.text(ORIENTATION_Y).toFloat(), - shot.text(ORIENTATION_Z).toFloat(), - shot.text(ORIENTATION_W).toFloat())); + shot.text(ORIENTATION_Z).toFloat())); data->setDomain(shot.text(DOMAIN_KEY)); From d6b71f19a8dd075d45195ddfc05c617c7f40f8e4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Feb 2014 18:00:09 -0800 Subject: [PATCH 11/70] fix voxel texture to work better with larger TREE_SCALE --- interface/resources/shaders/perlin_modulate.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/shaders/perlin_modulate.frag b/interface/resources/shaders/perlin_modulate.frag index eea0da3671..8ead57c238 100644 --- a/interface/resources/shaders/perlin_modulate.frag +++ b/interface/resources/shaders/perlin_modulate.frag @@ -12,7 +12,7 @@ uniform sampler2D permutationNormalTexture; // the noise frequency -const float frequency = 1024.0; +const float frequency = 65536.0; // looks better with current TREE_SCALE, was 1024 when TREE_SCALE was either 512 or 128 // the noise amplitude const float amplitude = 0.1; From 20a6f4eea95e826a23d74ab17d275ac0d52f930d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Feb 2014 21:22:21 -0800 Subject: [PATCH 12/70] first cut at auto-LOD adjustment --- interface/src/Application.cpp | 7 +++- interface/src/Menu.cpp | 31 +++++++++++++-- interface/src/Menu.h | 14 +++++++ interface/src/VoxelSystem.cpp | 61 ++++++++++++++++++++--------- interface/src/VoxelSystem.h | 14 ++++--- interface/src/ui/LodToolsDialog.cpp | 6 +++ interface/src/ui/LodToolsDialog.h | 1 + libraries/shared/src/PerfStat.h | 2 + 8 files changed, 108 insertions(+), 28 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 004b71074b..29d838e10b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2720,7 +2720,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels..."); if (!Menu::getInstance()->isOptionChecked(MenuOption::DontRenderVoxels)) { - _voxels.render(Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures)); + _voxels.render(); + + // double check that our LOD doesn't need to be auto-adjusted + Menu::getInstance()->autoAdjustLOD(_fps); } } @@ -2811,7 +2814,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { _mouseVoxel.s, _mouseVoxel.s); - _sharedVoxelSystem.render(true); + _sharedVoxelSystem.render(); glPopMatrix(); } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7eb5807c6f..89277c794c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -70,7 +70,8 @@ Menu::Menu() : _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), _boundaryLevelAdjust(0), - _maxVoxelPacketsPerSecond(DEFAULT_MAX_VOXEL_PPS) + _maxVoxelPacketsPerSecond(DEFAULT_MAX_VOXEL_PPS), + _lastAdjust(usecTimestampNow()) { Application *appInstance = Application::getInstance(); @@ -1096,14 +1097,38 @@ void Menu::voxelStatsDetailsClosed() { } } +void Menu::autoAdjustLOD(float currentFPS) { + bool changed = false; + quint64 now = usecTimestampNow(); + quint64 elapsed = now - _lastAdjust; + + if (elapsed > ADJUST_LOD_DOWN_DELAY && currentFPS < ADJUST_LOD_DOWN_FPS && _voxelSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) { + _voxelSizeScale *= ADJUST_LOD_DOWN_BY; + changed = true; + _lastAdjust = now; + qDebug() << "adjusting LOD down... currentFPS=" << currentFPS << "_voxelSizeScale=" << _voxelSizeScale; + } + + if (elapsed > ADJUST_LOD_UP_DELAY && currentFPS > ADJUST_LOD_UP_FPS && _voxelSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) { + _voxelSizeScale *= ADJUST_LOD_UP_BY; + changed = true; + _lastAdjust = now; + qDebug() << "adjusting LOD up... currentFPS=" << currentFPS << "_voxelSizeScale=" << _voxelSizeScale; + } + + if (changed) { + if (_lodToolsDialog) { + _lodToolsDialog->reloadSliders(); + } + } +} + void Menu::setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; - Application::getInstance()->getVoxels()->redrawInViewVoxels(); } void Menu::setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; - Application::getInstance()->getVoxels()->redrawInViewVoxels(); } void Menu::lodTools() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 742b9fc66f..fd37856b26 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -16,6 +16,18 @@ #include +const float ADJUST_LOD_DOWN_FPS = 40.0; +const float ADJUST_LOD_UP_FPS = 55.0; + +const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 5; +const quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2; + +const float ADJUST_LOD_DOWN_BY = 0.9f; +const float ADJUST_LOD_UP_BY = 1.1f; + +const float ADJUST_LOD_MIN_SIZE_SCALE = TREE_SCALE * 1.0f; +const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE; + enum FrustumDrawMode { FRUSTUM_DRAW_MODE_ALL, FRUSTUM_DRAW_MODE_VECTORS, @@ -68,6 +80,7 @@ public: void handleViewFrustumOffsetKeyModifier(int key); // User Tweakable LOD Items + void autoAdjustLOD(float currentFPS); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } void setBoundaryLevelAdjust(int boundaryLevelAdjust); @@ -154,6 +167,7 @@ private: int _maxVoxelPacketsPerSecond; QMenu* _activeScriptsMenu; QString replaceLastOccurrence(QChar search, QChar replace, QString string); + quint64 _lastAdjust; }; namespace MenuOption { diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 1ffc6c3e5e..a4e47138d1 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -99,6 +99,9 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _culledOnce = false; _inhideOutOfView = false; + + _lastKnownVoxelSizeScale = DEFAULT_OCTREE_SIZE_SCALE; + _lastKnownBoundaryLevelAdjust = 0; } void VoxelSystem::elementDeleted(OctreeElement* element) { @@ -249,6 +252,9 @@ VoxelSystem::~VoxelSystem() { delete _tree; } + +// This is called by the main application thread on both the initialization of the application and when +// the preferences dialog box is called/saved void VoxelSystem::setMaxVoxels(int maxVoxels) { if (maxVoxels == _maxVoxels) { return; @@ -267,6 +273,8 @@ void VoxelSystem::setMaxVoxels(int maxVoxels) { } } +// This is called by the main application thread on both the initialization of the application and when +// the use voxel shader menu item is chosen void VoxelSystem::setUseVoxelShader(bool useVoxelShader) { if (_useVoxelShader == useVoxelShader) { return; @@ -330,7 +338,7 @@ void VoxelSystem::setVoxelsAsPoints(bool voxelsAsPoints) { void VoxelSystem::cleanupVoxelMemory() { if (_initialized) { - _bufferWriteLock.lock(); + _readArraysLock.lockForWrite(); _initialized = false; // no longer initialized if (_useVoxelShader) { // these are used when in VoxelShader mode. @@ -368,7 +376,7 @@ void VoxelSystem::cleanupVoxelMemory() { delete[] _writeVoxelDirtyArray; delete[] _readVoxelDirtyArray; _writeVoxelDirtyArray = _readVoxelDirtyArray = NULL; - _bufferWriteLock.unlock(); + _readArraysLock.unlock(); } } @@ -401,7 +409,7 @@ void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndice } void VoxelSystem::initVoxelMemory() { - _bufferWriteLock.lock(); + _readArraysLock.lockForWrite(); _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them @@ -516,7 +524,7 @@ void VoxelSystem::initVoxelMemory() { _initialized = true; - _bufferWriteLock.unlock(); + _readArraysLock.unlock(); } void VoxelSystem::writeToSVOFile(const char* filename, VoxelTreeElement* element) const { @@ -646,7 +654,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { } _inSetupNewVoxelsForDrawing = true; - + bool didWriteFullVBO = _writeRenderFullVBO; if (_tree->isDirty()) { static char buffer[64] = { 0 }; @@ -673,7 +681,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { } // lock on the buffer write lock so we can't modify the data when the GPU is reading it - _bufferWriteLock.lock(); + _readArraysLock.lockForWrite(); if (_voxelsUpdated) { _voxelsDirty=true; @@ -682,7 +690,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated copyWrittenDataToReadArrays(didWriteFullVBO); - _bufferWriteLock.unlock(); + _readArraysLock.unlock(); quint64 end = usecTimestampNow(); int elapsedmsec = (end - start) / 1000; @@ -713,8 +721,8 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { // lock on the buffer write lock so we can't modify the data when the GPU is reading it { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "setupNewVoxelsForDrawingSingleNode()... _bufferWriteLock.lock();" ); - _bufferWriteLock.lock(); + "setupNewVoxelsForDrawingSingleNode()... _readArraysLock.lockForWrite();" ); + _readArraysLock.lockForWrite(); } _voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty @@ -725,7 +733,7 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { // after... _voxelsUpdated = 0; - _bufferWriteLock.unlock(); + _readArraysLock.unlock(); quint64 end = usecTimestampNow(); int elapsedmsec = (end - start) / 1000; @@ -733,8 +741,12 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; } -void VoxelSystem::checkForCulling() { +void VoxelSystem::recreateVoxelGeometryInView() { + // this is a temporary solution... we need a full implementation that actually does a full redraw into clean VBOs + hideOutOfView(true); +} +void VoxelSystem::checkForCulling() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()"); quint64 start = usecTimestampNow(); @@ -762,7 +774,20 @@ void VoxelSystem::checkForCulling() { _hasRecentlyChanged = false; } - hideOutOfView(forceFullFrustum); + // This would be a good place to do a special processing pass, for example, switching the LOD of the scene + bool fullRedraw = (_lastKnownVoxelSizeScale != Menu::getInstance()->getVoxelSizeScale() || + _lastKnownBoundaryLevelAdjust != Menu::getInstance()->getBoundaryLevelAdjust()); + + // track that these values + _lastKnownVoxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); + _lastKnownBoundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); + + if (fullRedraw) { + // this will remove all old geometry and recreate the correct geometry for all in view voxels + recreateVoxelGeometryInView(); + } else { + hideOutOfView(forceFullFrustum); + } if (forceFullFrustum) { quint64 endViewCulling = usecTimestampNow(); @@ -1197,7 +1222,8 @@ void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex seg } } -void VoxelSystem::render(bool texture) { +void VoxelSystem::render() { + bool texture = Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "render()"); @@ -1404,11 +1430,7 @@ void VoxelSystem::killLocalVoxels() { setupNewVoxelsForDrawing(); } -void VoxelSystem::redrawInViewVoxels() { - hideOutOfView(true); -} - - +// only called on main thread bool VoxelSystem::clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData) { _nodeCount++; VoxelTreeElement* voxel = (VoxelTreeElement*)element; @@ -1416,12 +1438,15 @@ bool VoxelSystem::clearAllNodesBufferIndexOperation(OctreeElement* element, void return true; } +// only called on main thread, and also always followed by a call to cleanupVoxelMemory() +// you shouldn't be calling this on any other thread or without also cleaning up voxel memory void VoxelSystem::clearAllNodesBufferIndex() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "VoxelSystem::clearAllNodesBufferIndex()"); _nodeCount = 0; _tree->lockForRead(); // we won't change the tree so it's ok to treat this as a read _tree->recurseTreeWithOperation(clearAllNodesBufferIndexOperation); + clearFreeBufferIndexes(); // this should be called too _tree->unlock(); if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { qDebug("clearing buffer index of %d nodes", _nodeCount); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index d1404668bf..c2f60d6699 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -53,7 +53,7 @@ public: virtual void init(); void simulate(float deltaTime) { } - void render(bool texture); + void render(); void changeTree(VoxelTree* newTree); VoxelTree* getTree() const { return _tree; } @@ -79,7 +79,6 @@ public: unsigned long getVoxelMemoryUsageGPU(); void killLocalVoxels(); - void redrawInViewVoxels(); virtual void removeOutOfView(); virtual void hideOutOfView(bool forceFullFrustum = false); @@ -151,6 +150,7 @@ protected: static const bool DONT_BAIL_EARLY; // by default we will bail early, if you want to force not bailing, then use this void setupNewVoxelsForDrawingSingleNode(bool allowBailEarly = true); void checkForCulling(); + void recreateVoxelGeometryInView(); glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const; @@ -211,6 +211,10 @@ private: GLfloat* _readVerticesArray; GLubyte* _readColorsArray; + + QReadWriteLock _readArraysLock; + + GLfloat* _writeVerticesArray; GLubyte* _writeColorsArray; bool* _writeVoxelDirtyArray; @@ -253,9 +257,6 @@ private: GLuint _vboIndicesFront; GLuint _vboIndicesBack; - QMutex _bufferWriteLock; - QMutex _treeLock; - ViewFrustum _lastKnownViewFrustum; ViewFrustum _lastStableViewFrustum; ViewFrustum* _viewFrustum; @@ -299,6 +300,9 @@ private: bool _useFastVoxelPipeline; bool _inhideOutOfView; + + float _lastKnownVoxelSizeScale; + int _lastKnownBoundaryLevelAdjust; }; #endif diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index 788f7e5561..4cf4a29bf1 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -121,6 +121,12 @@ LodToolsDialog::~LodToolsDialog() { delete _boundaryLevelAdjust; } +void LodToolsDialog::reloadSliders() { + _lodSize->setValue(Menu::getInstance()->getVoxelSizeScale() / TREE_SCALE); + _boundaryLevelAdjust->setValue(Menu::getInstance()->getBoundaryLevelAdjust()); + _feedback->setText(getFeedbackText()); +} + void LodToolsDialog::sizeScaleValueChanged(int value) { float realValue = value * TREE_SCALE; Menu::getInstance()->setVoxelSizeScale(realValue); diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index ee14196188..ee96cffd7e 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -28,6 +28,7 @@ public slots: void sizeScaleValueChanged(int value); void boundaryLevelValueChanged(int value); void resetClicked(bool checked); + void reloadSliders(); protected: diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index fffb095021..a7a10b97b8 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -45,6 +45,8 @@ public: _alwaysDisplay(alwaysDisplay), _runningTotal(runningTotal), _totalCalls(totalCalls) { } + + quint64 elapsed() const { return (usecTimestampNow() - _start); }; ~PerformanceWarning(); From 3a8aa0c47e4f9556d9401be67cf551b41b8e147f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 13 Feb 2014 10:02:16 -0800 Subject: [PATCH 13/70] Removing HeadData::findSpherePenetration() as unused cruft. --- libraries/avatars/src/HeadData.cpp | 7 ------- libraries/avatars/src/HeadData.h | 7 ------- 2 files changed, 14 deletions(-) diff --git a/libraries/avatars/src/HeadData.cpp b/libraries/avatars/src/HeadData.cpp index f863d6b592..62e8276bd3 100644 --- a/libraries/avatars/src/HeadData.cpp +++ b/libraries/avatars/src/HeadData.cpp @@ -45,10 +45,3 @@ void HeadData::addLean(float sideways, float forwards) { _leanForward += forwards; } -bool HeadData::findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration) const { - // we would like to update this to determine collisions/penetrations with the Avatar's head sphere... - // but right now it does not appear as if the HeadData has a position and radius. - // this is a placeholder for now. - return false; -} - diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index fde684bbf1..0f096059c0 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -58,13 +58,6 @@ public: void setLookAtPosition(const glm::vec3& lookAtPosition) { _lookAtPosition = lookAtPosition; } friend class AvatarData; - - /// Checks for penetration between the described sphere and the hand. - /// \param penetratorCenter the center of the penetration test sphere - /// \param penetratorRadius the radius of the penetration test sphere - /// \param penetration[out] the vector in which to store the penetration - /// \return whether or not the sphere penetrated - bool findSpherePenetration(const glm::vec3& penetratorCenter, float penetratorRadius, glm::vec3& penetration) const; protected: float _yaw; From e793c207f9fd15f2ad70266a154b51e7d9348ab1 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 13 Feb 2014 10:03:14 -0800 Subject: [PATCH 14/70] Minor whitespace removal. --- interface/src/avatar/MyAvatar.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e0d9db43aa..560f1a6fd4 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -837,7 +837,6 @@ void MyAvatar::updateThrust(float deltaTime) { } } } - } // Update speed brake status @@ -903,7 +902,6 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) { } } - void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) { const float VOXEL_ELASTICITY = 0.4f; const float VOXEL_DAMPING = 0.0f; From 9523ea7d03a3b402a860b294b0b2883251f9818f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 13 Feb 2014 10:03:39 -0800 Subject: [PATCH 15/70] Only do slaps for MyAvatar. --- interface/src/avatar/Hand.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 7e47b0a0d5..5749da5f14 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -178,7 +178,7 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { } glm::vec3 totalPenetration; ModelCollisionList collisions; - if (Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { + if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { // Check for palm collisions glm::vec3 myPalmPosition = palm.getPosition(); float palmCollisionDistance = 0.1f; @@ -220,10 +220,10 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration); } } else { - // when this !isMyHand then avatar is MyAvatar and we apply the collision + // when !isMyHand then avatar is MyAvatar and we apply the collision // which might not do anything (hand hit unmovable part of MyAvatar) however - // we don't resolve the hand's penetration (we expect their simulation - // to do the right thing). + // we don't resolve the hand's penetration because we expect the remote + // simulation to do the right thing. avatar->applyCollision(collisions[j]); } } From 312bc7521f5d13a698773c99351b0a2aebbc234a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 13 Feb 2014 10:04:58 -0800 Subject: [PATCH 16/70] Making arm restoration rate independent of FPS and easier to tune. --- interface/src/avatar/SkeletonModel.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index ac08c52b49..ff4de8c41e 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -36,23 +36,24 @@ void SkeletonModel::simulate(float deltaTime) { HandData& hand = _owningAvatar->getHand(); hand.getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); - const float HAND_RESTORATION_RATE = 0.25f; + const float HAND_RESTORATION_PERIOD = 1.f; // seconds + float handRestorePercent = glm::clamp(deltaTime / HAND_RESTORATION_PERIOD, 0.f, 1.f); const FBXGeometry& geometry = _geometry->getFBXGeometry(); if (leftPalmIndex == -1) { // no Leap data; set hands from mouse if (_owningAvatar->getHandState() == HAND_STATE_NULL) { - restoreRightHandPosition(HAND_RESTORATION_RATE); + restoreRightHandPosition(handRestorePercent); } else { applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition()); } - restoreLeftHandPosition(HAND_RESTORATION_RATE); + restoreLeftHandPosition(handRestorePercent); } else if (leftPalmIndex == rightPalmIndex) { // right hand only applyPalmData(geometry.rightHandJointIndex, geometry.rightFingerJointIndices, geometry.rightFingertipJointIndices, hand.getPalms()[leftPalmIndex]); - restoreLeftHandPosition(HAND_RESTORATION_RATE); + restoreLeftHandPosition(handRestorePercent); } else { applyPalmData(geometry.leftHandJointIndex, geometry.leftFingerJointIndices, geometry.leftFingertipJointIndices, From 6d75efd2b01d56fe8d236de1985fc9de824927d6 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 10:46:28 -0800 Subject: [PATCH 17/70] tweaks to default menus for alpha, make Gravity false so we don't fall into abyss --- interface/src/Menu.cpp | 8 ++++---- interface/src/Menu.h | 2 +- interface/src/avatar/Hand.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7eb5807c6f..2e026f216f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -161,7 +161,7 @@ Menu::Menu() : #endif addDisabledActionAndSeparator(editMenu, "Physics"); - addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, true); + addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::Gravity, Qt::SHIFT | Qt::Key_G, false); addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly); @@ -339,14 +339,14 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::CollisionProxies); - addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, true); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::FaceshiftTCP, 0, false, appInstance->getFaceshift(), SLOT(setTCPEnabled(bool))); - addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, true); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); @@ -356,7 +356,7 @@ Menu::Menu() : true, appInstance->getSixenseManager(), SLOT(setFilter(bool))); - addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayLeapHands, 0, true); + addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::VoxelDrumming, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::PlaySlaps, 0, false); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 742b9fc66f..2f0c65413c 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -183,7 +183,7 @@ namespace MenuOption { const QString DisableDeltaSending = "Disable Delta Sending"; const QString DisableLowRes = "Disable Lower Resolution While Moving"; const QString DisplayFrustum = "Display Frustum"; - const QString DisplayLeapHands = "Display Leap Hands"; + const QString DisplayHands = "Display Hands"; const QString DisplayHandTargets = "Display Hand Targets"; const QString FilterSixense = "Smooth Sixense Movement"; const QString DontRenderVoxels = "Don't call _voxels.render()"; diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index 8edb455a75..1239e38818 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -304,7 +304,7 @@ void Hand::render(bool isMine) { } } - if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayLeapHands)) { + if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) { renderLeapHands(isMine); } From 156fa6dc5aaa57465c585b6b6cda7dfc0a98c9a3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 11:03:35 -0800 Subject: [PATCH 18/70] more tweaks, removed old clouds --- interface/src/Application.cpp | 14 ------ interface/src/Application.h | 4 -- interface/src/Cloud.cpp | 85 ----------------------------------- interface/src/Cloud.h | 32 ------------- interface/src/Menu.cpp | 17 +++---- interface/src/Menu.h | 1 - 6 files changed, 5 insertions(+), 148 deletions(-) delete mode 100644 interface/src/Cloud.cpp delete mode 100644 interface/src/Cloud.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 004b71074b..f51360cca1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2146,15 +2146,6 @@ void Application::updateThreads(float deltaTime) { } } -void Application::updateParticles(float deltaTime) { - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); - PerformanceWarning warn(showWarnings, "Application::updateParticles()"); - - if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) { - _cloud.simulate(deltaTime); - } -} - void Application::updateMetavoxels(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::updateMetavoxels()"); @@ -2276,7 +2267,6 @@ void Application::update(float deltaTime) { 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... - updateParticles(deltaTime); // Simulate particle cloud movements updateMetavoxels(deltaTime); // update metavoxels updateCamera(deltaTime); // handle various camera tweaks like off axis projection updateDialogs(deltaTime); // update various stats dialogs if present @@ -2711,10 +2701,6 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // disable specular lighting for ground and voxels glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR); - // Draw Cloud Particles - if (Menu::getInstance()->isOptionChecked(MenuOption::ParticleCloud)) { - _cloud.render(); - } // Draw voxels if (Menu::getInstance()->isOptionChecked(MenuOption::Voxels)) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), diff --git a/interface/src/Application.h b/interface/src/Application.h index ff6e08758b..c5aafc4e9d 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -32,7 +32,6 @@ #include "BandwidthMeter.h" #include "Camera.h" -#include "Cloud.h" #include "DatagramProcessor.h" #include "Environment.h" #include "GLCanvas.h" @@ -284,7 +283,6 @@ private: void updateSixense(float deltaTime); void updateSerialDevices(float deltaTime); void updateThreads(float deltaTime); - void updateParticles(float deltaTime); void updateMetavoxels(float deltaTime); void updateCamera(float deltaTime); void updateDialogs(float deltaTime); @@ -351,8 +349,6 @@ private: Stars _stars; - Cloud _cloud; - VoxelSystem _voxels; VoxelTree _clipboard; // if I copy/paste VoxelImporter* _voxelImporter; diff --git a/interface/src/Cloud.cpp b/interface/src/Cloud.cpp deleted file mode 100644 index a89ee04810..0000000000 --- a/interface/src/Cloud.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// -// Cloud.cpp -// interface -// -// Created by Philip Rosedale on 11/17/12. -// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. -// - -#include -#include -#include "Cloud.h" -#include "Util.h" -#include "Field.h" - -const int NUM_PARTICLES = 100000; -const float FIELD_COUPLE = 0.001f; -const bool RENDER_FIELD = false; - -Cloud::Cloud() { - glm::vec3 box = glm::vec3(PARTICLE_WORLD_SIZE); - _bounds = box; - _count = NUM_PARTICLES; - _particles = new Particle[_count]; - _field = new Field(PARTICLE_WORLD_SIZE, FIELD_COUPLE); - - for (unsigned int i = 0; i < _count; i++) { - _particles[i].position = randVector() * box; - const float INIT_VEL_SCALE = 0.03f; - _particles[i].velocity = randVector() * ((float)PARTICLE_WORLD_SIZE * INIT_VEL_SCALE); - _particles[i].color = randVector(); - } -} - -void Cloud::render() { - if (RENDER_FIELD) { - _field->render(); - } - - glPointSize(3.0f); - glDisable(GL_TEXTURE_2D); - glEnable(GL_POINT_SMOOTH); - - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glVertexPointer(3, GL_FLOAT, 3 * sizeof(glm::vec3), &_particles[0].position); - glColorPointer(3, GL_FLOAT, 3 * sizeof(glm::vec3), &_particles[0].color); - glDrawArrays(GL_POINTS, 0, NUM_PARTICLES); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - -} - -void Cloud::simulate (float deltaTime) { - unsigned int i; - _field->simulate(deltaTime); - for (i = 0; i < _count; ++i) { - - // Update position - _particles[i].position += _particles[i].velocity * deltaTime; - - // Decay Velocity (Drag) - const float CONSTANT_DAMPING = 0.15f; - _particles[i].velocity *= (1.f - CONSTANT_DAMPING * deltaTime); - - // Interact with Field - _field->interact(deltaTime, _particles[i].position, _particles[i].velocity); - - // Update color to velocity - _particles[i].color = (glm::normalize(_particles[i].velocity) * 0.5f) + 0.5f; - - // Bounce at bounds - if ((_particles[i].position.x > _bounds.x) || (_particles[i].position.x < 0.f)) { - _particles[i].position.x = glm::clamp(_particles[i].position.x, 0.f, _bounds.x); - _particles[i].velocity.x *= -1.f; - } - if ((_particles[i].position.y > _bounds.y) || (_particles[i].position.y < 0.f)) { - _particles[i].position.y = glm::clamp(_particles[i].position.y, 0.f, _bounds.y); - _particles[i].velocity.y *= -1.f; - } - if ((_particles[i].position.z > _bounds.z) || (_particles[i].position.z < 0.f)) { - _particles[i].position.z = glm::clamp(_particles[i].position.z, 0.f, _bounds.z); - _particles[i].velocity.z *= -1.f; - } - } - } diff --git a/interface/src/Cloud.h b/interface/src/Cloud.h deleted file mode 100644 index fcf414b62e..0000000000 --- a/interface/src/Cloud.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Cloud.h -// interface -// -// Created by Philip Rosedale on 11/17/12. -// Copyright (c) 2012 High Fidelity, Inc. All rights reserved. -// - -#ifndef __interface__Cloud__ -#define __interface__Cloud__ - -#include "Field.h" - -#define PARTICLE_WORLD_SIZE 256.0 - -class Cloud { -public: - Cloud(); - void simulate(float deltaTime); - void render(); - -private: - struct Particle { - glm::vec3 position, velocity, color; - }* _particles; - - unsigned int _count; - glm::vec3 _bounds; - Field* _field; -}; - -#endif diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2e026f216f..4a3733f760 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -238,9 +238,9 @@ Menu::Menu() : SLOT(setFullscreen(bool))); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true, appInstance,SLOT(cameraMenuChanged())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H,false, - appInstance,SLOT(cameraMenuChanged())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false, + appInstance, SLOT(cameraMenuChanged())); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, false, @@ -266,14 +266,8 @@ Menu::Menu() : appInstance->getAvatar(), SLOT(resetSize())); - addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::OffAxisProjection, - 0, - true); - addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::TurnWithHead, - 0, - true); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::MoveWithLean, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HeadMouse, 0, false); @@ -298,7 +292,6 @@ Menu::Menu() : appInstance->getGlowEffect(), SLOT(cycleRenderMode())); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::ParticleCloud, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, false); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 2f0c65413c..19e9fbf49f 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -223,7 +223,6 @@ namespace MenuOption { const QString KillLocalVoxels = "Kill Local Voxels"; const QString GoHome = "Go Home"; const QString Gravity = "Use Gravity"; - const QString ParticleCloud = "Particle Cloud"; const QString LodTools = "LOD Tools"; const QString Log = "Log"; const QString Login = "Login"; From 08c8f47cd4cb2b2b9fa7cf2776ea64cd126c133a Mon Sep 17 00:00:00 2001 From: stojce Date: Thu, 13 Feb 2014 20:05:59 +0100 Subject: [PATCH 19/70] added SNAPSHOT_EXTENSION constant --- interface/src/Application.cpp | 2 +- interface/src/Application.h | 2 ++ interface/src/GLCanvas.cpp | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eaaea9c5be..4ede4a9405 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1384,7 +1384,7 @@ void Application::dropEvent(QDropEvent *event) { QString snapshotPath; const QMimeData *mimeData = event->mimeData(); foreach (QUrl url, mimeData->urls()) { - if (url.url().toLower().endsWith("jpg")) { + if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) { snapshotPath = url.url().remove("file://"); break; } diff --git a/interface/src/Application.h b/interface/src/Application.h index 9508c0c9a5..54a804ab72 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -93,6 +93,8 @@ static const float NODE_KILLED_RED = 1.0f; static const float NODE_KILLED_GREEN = 0.0f; static const float NODE_KILLED_BLUE = 0.0f; +static const QString SNAPSHOT_EXTENSION = ".jpg"; + class Application : public QApplication { Q_OBJECT diff --git a/interface/src/GLCanvas.cpp b/interface/src/GLCanvas.cpp index cfff3b8696..7bc79e56d8 100644 --- a/interface/src/GLCanvas.cpp +++ b/interface/src/GLCanvas.cpp @@ -75,7 +75,7 @@ void GLCanvas::wheelEvent(QWheelEvent* event) { void GLCanvas::dragEnterEvent(QDragEnterEvent* event) { const QMimeData *mimeData = event->mimeData(); foreach (QUrl url, mimeData->urls()) { - if (url.url().toLower().endsWith("jpg")) { + if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) { event->acceptProposedAction(); break; } From d9b50359a6818434ea7a6c22a3bb1642efb6a7f3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 13 Feb 2014 12:21:28 -0800 Subject: [PATCH 20/70] Hand-face collisions now work (sorta). --- interface/src/avatar/Avatar.cpp | 25 ++++++++++++++++++++++--- interface/src/avatar/Head.cpp | 28 ++++++++++++++++++++++++++++ interface/src/avatar/Head.h | 2 ++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 487f72a1e5..f36c03cba6 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -162,6 +162,7 @@ void Avatar::render(bool forceRenderHead) { // render body if (Menu::getInstance()->isOptionChecked(MenuOption::CollisionProxies)) { _skeletonModel.renderCollisionProxies(1.f); + //_head.getFaceModel().renderCollisionProxies(0.5f); } if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { @@ -276,11 +277,14 @@ bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penet bool didPenetrate = false; glm::vec3 skeletonPenetration; ModelCollisionInfo collisionInfo; + /* Temporarily disabling collisions against the skeleton because the collision proxies up + * near the neck are bad and prevent the hand from hitting the face. if (_skeletonModel.findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo, 1.0f, skeletonSkipIndex)) { collisionInfo._model = &_skeletonModel; collisions.push_back(collisionInfo); didPenetrate = true; } + */ if (_head.getFaceModel().findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo)) { collisionInfo._model = &(_head.getFaceModel()); collisions.push_back(collisionInfo); @@ -448,16 +452,31 @@ float Avatar::getHeight() const { bool Avatar::collisionWouldMoveAvatar(ModelCollisionInfo& collision) const { // ATM only the Skeleton is pokeable // TODO: make poke affect head + if (!collision._model) { + return false; + } if (collision._model == &_skeletonModel && collision._jointIndex != -1) { - return _skeletonModel.collisionHitsMoveableJoint(collision); + // collision response of skeleton is temporarily disabled + return false; + //return _skeletonModel.collisionHitsMoveableJoint(collision); + } + if (collision._model == &(_head.getFaceModel())) { + return true; } return false; } void Avatar::applyCollision(ModelCollisionInfo& collision) { - if (collision._model == &_skeletonModel && collision._jointIndex != -1) { - _skeletonModel.applyCollision(collision); + if (!collision._model) { + return; } + if (collision._model == &(_head.getFaceModel())) { + _head.applyCollision(collision); + } + // TODO: make skeleton respond to collisions + //if (collision._model == &_skeletonModel && collision._jointIndex != -1) { + // _skeletonModel.applyCollision(collision); + //} } float Avatar::getPelvisFloatingHeight() const { diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index e5d4724bb5..bb88530aa7 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -219,6 +219,34 @@ float Head::getTweakedRoll() const { return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } +void Head::applyCollision(ModelCollisionInfo& collisionInfo) { + // HACK: the collision proxies for the FaceModel are bad. As a temporary workaround + // we collide against a hard coded collision proxy. + // TODO: get a better collision proxy here. + const float HEAD_RADIUS = 0.15f; + const glm::vec3 HEAD_CENTER = _position; + + // collide the contactPoint against the collision proxy to obtain a new penetration + // NOTE: that penetration is in opposite direction (points the way out for the point, not the sphere) + glm::vec3 penetration; + if (findPointSpherePenetration(collisionInfo._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) { + // compute lean angles + Avatar* owningAvatar = static_cast(_owningAvatar); + glm::quat bodyRotation = owningAvatar->getOrientation(); + glm::vec3 neckPosition; + if (owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { + glm::vec3 xAxis = bodyRotation * glm::vec3(1.f, 0.f, 0.f); + glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); + float neckLength = glm::length(_position - neckPosition); + if (neckLength > 0.f) { + float forward = glm::dot(collisionInfo._penetration, zAxis) / neckLength; + float sideways = - glm::dot(collisionInfo._penetration, xAxis) / neckLength; + addLean(sideways, forward); + } + } + } +} + void Head::renderLookatVectors(glm::vec3 leftEyePosition, glm::vec3 rightEyePosition, glm::vec3 lookatPosition) { Application::getInstance()->getGlowEffect()->begin(); diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index 19f9efd8e6..eae8223903 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -79,6 +79,8 @@ public: float getTweakedPitch() const; float getTweakedYaw() const; float getTweakedRoll() const; + + void applyCollision(ModelCollisionInfo& collisionInfo); private: // disallow copies of the Head, copy of owning Avatar is disallowed too From b75de42802ca858c9e9ce7c398fc2aba3ec2b931 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 13 Feb 2014 12:22:40 -0800 Subject: [PATCH 21/70] Making the lean recovery use more tuneable parameters. --- interface/src/avatar/MyAvatar.cpp | 33 +++++++++---------------------- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 560f1a6fd4..8a4d734072 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -112,23 +112,7 @@ void MyAvatar::updateTransmitter(float deltaTime) { void MyAvatar::update(float deltaTime) { updateTransmitter(deltaTime); - // TODO: resurrect touch interactions between avatars - //// rotate body yaw for yaw received from multitouch - //setOrientation(getOrientation() * glm::quat(glm::vec3(0, _yawFromTouch, 0))); - //_yawFromTouch = 0.f; - // - //// apply pitch from touch - //_head.setPitch(_head.getPitch() + _pitchFromTouch); - //_pitchFromTouch = 0.0f; - // - //float TOUCH_YAW_SCALE = -0.25f; - //float TOUCH_PITCH_SCALE = -12.5f; - //float FIXED_TOUCH_TIMESTEP = 0.016f; - //_yawFromTouch += ((_touchAvgX - _lastTouchAvgX) * TOUCH_YAW_SCALE * FIXED_TOUCH_TIMESTEP); - //_pitchFromTouch += ((_touchAvgY - _lastTouchAvgY) * TOUCH_PITCH_SCALE * FIXED_TOUCH_TIMESTEP); - - // Update my avatar's state from gyros - updateFromGyros(Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)); + updateFromGyros(deltaTime); // Update head mouse from faceshift if active Faceshift* faceshift = Application::getInstance()->getFaceshift(); @@ -364,7 +348,7 @@ void MyAvatar::simulate(float deltaTime) { const float MAX_PITCH = 90.0f; // Update avatar head rotation with sensor data -void MyAvatar::updateFromGyros(bool turnWithHead) { +void MyAvatar::updateFromGyros(float deltaTime) { Faceshift* faceshift = Application::getInstance()->getFaceshift(); glm::vec3 estimatedPosition, estimatedRotation; @@ -372,7 +356,7 @@ void MyAvatar::updateFromGyros(bool turnWithHead) { estimatedPosition = faceshift->getHeadTranslation(); estimatedRotation = safeEulerAngles(faceshift->getHeadRotation()); // Rotate the body if the head is turned beyond the screen - if (turnWithHead) { + if (Menu::getInstance()->isOptionChecked(MenuOption::TurnWithHead)) { const float FACESHIFT_YAW_TURN_SENSITIVITY = 0.5f; const float FACESHIFT_MIN_YAW_TURN = 15.f; const float FACESHIFT_MAX_YAW_TURN = 50.f; @@ -387,11 +371,12 @@ void MyAvatar::updateFromGyros(bool turnWithHead) { } } else { // restore rotation, lean to neutral positions - const float RESTORE_RATE = 0.05f; - _head.setYaw(glm::mix(_head.getYaw(), 0.0f, RESTORE_RATE)); - _head.setRoll(glm::mix(_head.getRoll(), 0.0f, RESTORE_RATE)); - _head.setLeanSideways(glm::mix(_head.getLeanSideways(), 0.0f, RESTORE_RATE)); - _head.setLeanForward(glm::mix(_head.getLeanForward(), 0.0f, RESTORE_RATE)); + const float RESTORE_PERIOD = 1.f; // seconds + float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f); + _head.setYaw(glm::mix(_head.getYaw(), 0.0f, restorePercentage)); + _head.setRoll(glm::mix(_head.getRoll(), 0.0f, restorePercentage)); + _head.setLeanSideways(glm::mix(_head.getLeanSideways(), 0.0f, restorePercentage)); + _head.setLeanForward(glm::mix(_head.getLeanForward(), 0.0f, restorePercentage)); return; } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index b912f6b0a7..0130cc9ca2 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -34,7 +34,7 @@ public: void reset(); void update(float deltaTime); void simulate(float deltaTime); - void updateFromGyros(bool turnWithHead); + void updateFromGyros(float deltaTime); void updateTransmitter(float deltaTime); void render(bool forceRenderHead); From ee4733d0bd8ce8801e5ea7622b62ea0a198f4b95 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 13 Feb 2014 12:41:23 -0800 Subject: [PATCH 22/70] Fixing a comment. --- libraries/avatars/src/AvatarHashMap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 72ada7d421..82485691c5 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -2,7 +2,7 @@ // AvatarHashMap.cpp // hifi // -// Created by Stephen AndrewMeadows on 1/28/2014. +// Created by AndrewMeadows on 1/28/2014. // Copyright (c) 2014 HighFidelity, Inc. All rights reserved. // From cac67d7baa0669f46864cce742b47e3099d6f4fd Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 13:06:45 -0800 Subject: [PATCH 23/70] add proper readWriteLocks around the VBO arrays to preven random triangles --- interface/src/VoxelSystem.cpp | 160 ++++++++++++++++++++++++++++++---- interface/src/VoxelSystem.h | 2 + 2 files changed, 144 insertions(+), 18 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index a4e47138d1..851c24c5ce 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -57,9 +57,12 @@ GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 }; VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) : NodeData(), - _treeScale(treeScale), - _maxVoxels(maxVoxels), - _initialized(false) { + _treeScale(treeScale), + _maxVoxels(maxVoxels), + _initialized(false), + _writeArraysLock(QReadWriteLock::Recursive), + _readArraysLock(QReadWriteLock::Recursive) + { _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; _writeRenderFullVBO = true; @@ -124,8 +127,16 @@ void VoxelSystem::setDisableFastVoxelPipeline(bool disableFastVoxelPipeline) { void VoxelSystem::elementUpdated(OctreeElement* element) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; +//qDebug() << "VoxelSystem::elementUpdated()..."; + // If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail.. if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { +/* +qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! " + << "_writeRenderFullVBO="<< _writeRenderFullVBO + << "_inSetupNewVoxelsForDrawing="<< _inSetupNewVoxelsForDrawing + << "_useFastVoxelPipeline="<< _useFastVoxelPipeline; +*/ return; } @@ -136,6 +147,8 @@ void VoxelSystem::elementUpdated(OctreeElement* element) { int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); shouldRender = voxel->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust); +//qDebug() << "VoxelSystem::elementUpdated()... recalcing should render!!"; + if (voxel->getShouldRender() != shouldRender) { voxel->setShouldRender(shouldRender); } @@ -163,11 +176,13 @@ void VoxelSystem::elementUpdated(OctreeElement* element) { const bool REUSE_INDEX = true; const bool DONT_FORCE_REDRAW = false; +//qDebug() << "VoxelSystem::elementUpdated()... calling updateNodeInArrays()!!!"; updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW); _voxelsUpdated++; voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. +//qDebug() << "VoxelSystem::elementUpdated()... calling setupNewVoxelsForDrawingSingleNode()!!!"; setupNewVoxelsForDrawingSingleNode(); } } @@ -409,7 +424,8 @@ void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndice } void VoxelSystem::initVoxelMemory() { - _readArraysLock.lockForWrite(); + //_readArraysLock.lockForWrite(); + //_writeArraysLock.lockForWrite(); _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them @@ -524,7 +540,8 @@ void VoxelSystem::initVoxelMemory() { _initialized = true; - _readArraysLock.unlock(); + //_writeArraysLock.unlock(); + //_readArraysLock.unlock(); } void VoxelSystem::writeToSVOFile(const char* filename, VoxelTreeElement* element) const { @@ -741,9 +758,90 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; } + + +class recreateVoxelGeometryInViewArgs { +public: + VoxelSystem* thisVoxelSystem; + ViewFrustum thisViewFrustum; + unsigned long nodesScanned; + float voxelSizeScale; + int boundaryLevelAdjust; + + recreateVoxelGeometryInViewArgs(VoxelSystem* voxelSystem) : + thisVoxelSystem(voxelSystem), + thisViewFrustum(*voxelSystem->getViewFrustum()), + nodesScanned(0), + voxelSizeScale(Menu::getInstance()->getVoxelSizeScale()), + boundaryLevelAdjust(Menu::getInstance()->getBoundaryLevelAdjust()) + { + } +}; + +// The goal of this operation is to remove any old references to old geometry, and if the voxel +// should be visible, create new geometry for it. +bool VoxelSystem::recreateVoxelGeometryInViewOperation(OctreeElement* element, void* extraData) { + VoxelTreeElement* voxel = (VoxelTreeElement*)element; + recreateVoxelGeometryInViewArgs* args = (recreateVoxelGeometryInViewArgs*)extraData; + + args->nodesScanned++; + + // reset the old geometry... + // note: this doesn't "mark the voxel as changed", so it only releases the old buffer index thereby forgetting the + // old geometry + voxel->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); + + bool shouldRender = voxel->calculateShouldRender(&args->thisViewFrustum, args->voxelSizeScale, args->boundaryLevelAdjust); + bool inView = voxel->isInView(args->thisViewFrustum); + voxel->setShouldRender(inView && shouldRender); + + if (shouldRender) { + bool falseColorize = false; + if (falseColorize) { + voxel->setFalseColor(0,0,255); // false colorize + } + // These are both needed to force redraw... + voxel->setDirtyBit(); + qDebug() << "recreateVoxelGeometryInViewOperation()... calling voxel->markWithChangedTime()"; + voxel->markWithChangedTime(); // this will notifyUpdateHooks, which will result in our geometry being created + } + + return true; // keep recursing! +} + + +// TODO: does cleanupRemovedVoxels() ever get called? +// TODO: other than cleanupRemovedVoxels() is there anyplace we attempt to detect too many abandoned slots??? void VoxelSystem::recreateVoxelGeometryInView() { - // this is a temporary solution... we need a full implementation that actually does a full redraw into clean VBOs - hideOutOfView(true); + +qDebug() << "recreateVoxelGeometryInView()..."; + + recreateVoxelGeometryInViewArgs args(this); + _writeArraysLock.lockForWrite(); // don't let anyone read or write our write arrays until we're done + _tree->lockForRead(); // don't let anyone change our tree structure until we're run + + // reset our write arrays bookkeeping to think we've got no voxels in it + clearFreeBufferIndexes(); // does this do everything we need? + /** + // clear out all our abandoned indexes - they are all available + // TODO: is it possible freeBufferIndex() will get called??? + _voxelsInWriteArrays = 0; + _freeIndexLock.lock(); + _freeIndexes.clear(); + _freeIndexLock.unlock(); + **/ + + //_voxelsUpdated = 0; // ???? + //_writeRenderFullVBO = true; + + // do we need to reset out _writeVoxelDirtyArray arrays?? + memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); + + + + _tree->recurseTreeWithOperation(recreateVoxelGeometryInViewOperation,(void*)&args); + _tree->unlock(); + _writeArraysLock.unlock(); } void VoxelSystem::checkForCulling() { @@ -784,7 +882,8 @@ void VoxelSystem::checkForCulling() { if (fullRedraw) { // this will remove all old geometry and recreate the correct geometry for all in view voxels - recreateVoxelGeometryInView(); + //recreateVoxelGeometryInView(); + hideOutOfView(forceFullFrustum); } else { hideOutOfView(forceFullFrustum); } @@ -905,12 +1004,26 @@ void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "copyWrittenDataToReadArrays()"); - if (_voxelsDirty && _voxelsUpdated) { - if (fullVBOs) { - copyWrittenDataToReadArraysFullVBOs(); + // attempt to get the writeArraysLock for reading and the readArraysLock for writing + // so we can copy from the write to the read... if we fail, that's ok, we'll get it the next + // time around, the only side effect is the VBOs won't be updated this frame + const int WAIT_FOR_LOCK_IN_MS = 5; + if (_readArraysLock.tryLockForWrite(WAIT_FOR_LOCK_IN_MS)) { + if (_writeArraysLock.tryLockForRead(WAIT_FOR_LOCK_IN_MS)) { + if (_voxelsDirty && _voxelsUpdated) { + if (fullVBOs) { + copyWrittenDataToReadArraysFullVBOs(); + } else { + copyWrittenDataToReadArraysPartialVBOs(); + } + } + _writeArraysLock.unlock(); } else { - copyWrittenDataToReadArraysPartialVBOs(); + qDebug() << "couldn't get _writeArraysLock.LockForRead()..."; } + _readArraysLock.unlock(); + } else { + qDebug() << "couldn't get _readArraysLock.LockForWrite()..."; } } @@ -1105,7 +1218,8 @@ void VoxelSystem::changeTree(VoxelTree* newTree) { connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); - setupNewVoxelsForDrawing(); + // TODO: hmmmmm????? + //setupNewVoxelsForDrawing(); } void VoxelSystem::updateFullVBOs() { @@ -1166,17 +1280,27 @@ void VoxelSystem::updateVBOs() { // would like to include _callsToTreesToArrays PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); if (_voxelsDirty) { - if (_readRenderFullVBO) { - updateFullVBOs(); + + // attempt to lock the read arrays, to for copying from them to the actual GPU VBOs. + // if we fail to get the lock, that's ok, our VBOs will update on the next frame... + const int WAIT_FOR_LOCK_IN_MS = 5; + if (_readArraysLock.tryLockForRead(WAIT_FOR_LOCK_IN_MS)) { + if (_readRenderFullVBO) { + updateFullVBOs(); + } else { + updatePartialVBOs(); + } + _voxelsDirty = false; + _readRenderFullVBO = false; + _readArraysLock.unlock(); } else { - updatePartialVBOs(); + qDebug() << "updateVBOs().... couldn't get _readArraysLock.tryLockForRead()"; } - _voxelsDirty = false; - _readRenderFullVBO = false; } _callsToTreesToArrays = 0; // clear it } +// this should only be called on the main application thread during render void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) { bool showWarning = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarning, "updateVBOSegment()"); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index c2f60d6699..121a7f86c4 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -194,6 +194,7 @@ private: static bool showAllSubTreeOperation(OctreeElement* element, void* extraData); static bool showAllLocalVoxelsOperation(OctreeElement* element, void* extraData); static bool getVoxelEnclosingOperation(OctreeElement* element, void* extraData); + static bool recreateVoxelGeometryInViewOperation(OctreeElement* element, void* extraData); int updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, bool forceDraw); int forceRemoveNodeFromArrays(VoxelTreeElement* node); @@ -212,6 +213,7 @@ private: GLfloat* _readVerticesArray; GLubyte* _readColorsArray; + QReadWriteLock _writeArraysLock; QReadWriteLock _readArraysLock; From 3aba29b0154d97e0ad886f3f8eff5180e8626a63 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 13:26:31 -0800 Subject: [PATCH 24/70] first working cut at recreateVoxelGeometryInView() --- interface/src/VoxelSystem.cpp | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 851c24c5ce..37bb2b5db1 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -794,16 +794,9 @@ bool VoxelSystem::recreateVoxelGeometryInViewOperation(OctreeElement* element, v bool shouldRender = voxel->calculateShouldRender(&args->thisViewFrustum, args->voxelSizeScale, args->boundaryLevelAdjust); bool inView = voxel->isInView(args->thisViewFrustum); voxel->setShouldRender(inView && shouldRender); - - if (shouldRender) { - bool falseColorize = false; - if (falseColorize) { - voxel->setFalseColor(0,0,255); // false colorize - } - // These are both needed to force redraw... - voxel->setDirtyBit(); - qDebug() << "recreateVoxelGeometryInViewOperation()... calling voxel->markWithChangedTime()"; - voxel->markWithChangedTime(); // this will notifyUpdateHooks, which will result in our geometry being created + if (shouldRender && inView) { + // recreate the geometry + args->thisVoxelSystem->updateNodeInArrays(voxel, false, true); // DONT_REUSE_INDEX, FORCE_REDRAW } return true; // keep recursing! @@ -814,30 +807,17 @@ bool VoxelSystem::recreateVoxelGeometryInViewOperation(OctreeElement* element, v // TODO: other than cleanupRemovedVoxels() is there anyplace we attempt to detect too many abandoned slots??? void VoxelSystem::recreateVoxelGeometryInView() { -qDebug() << "recreateVoxelGeometryInView()..."; + qDebug() << "recreateVoxelGeometryInView()..."; recreateVoxelGeometryInViewArgs args(this); _writeArraysLock.lockForWrite(); // don't let anyone read or write our write arrays until we're done _tree->lockForRead(); // don't let anyone change our tree structure until we're run // reset our write arrays bookkeeping to think we've got no voxels in it - clearFreeBufferIndexes(); // does this do everything we need? - /** - // clear out all our abandoned indexes - they are all available - // TODO: is it possible freeBufferIndex() will get called??? - _voxelsInWriteArrays = 0; - _freeIndexLock.lock(); - _freeIndexes.clear(); - _freeIndexLock.unlock(); - **/ - - //_voxelsUpdated = 0; // ???? - //_writeRenderFullVBO = true; + clearFreeBufferIndexes(); // do we need to reset out _writeVoxelDirtyArray arrays?? memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); - - _tree->recurseTreeWithOperation(recreateVoxelGeometryInViewOperation,(void*)&args); _tree->unlock(); @@ -882,8 +862,7 @@ void VoxelSystem::checkForCulling() { if (fullRedraw) { // this will remove all old geometry and recreate the correct geometry for all in view voxels - //recreateVoxelGeometryInView(); - hideOutOfView(forceFullFrustum); + recreateVoxelGeometryInView(); } else { hideOutOfView(forceFullFrustum); } From 6a39290bf576f19f771cc2e0de666038863ba121 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Feb 2014 13:37:29 -0800 Subject: [PATCH 25/70] Basic LOD switching based on distance to camera. --- interface/src/avatar/FaceModel.cpp | 3 -- interface/src/avatar/SkeletonModel.cpp | 4 -- interface/src/renderer/GeometryCache.cpp | 53 +++++++++++++----------- interface/src/renderer/GeometryCache.h | 20 +++++---- interface/src/renderer/Model.cpp | 12 +++++- interface/src/renderer/Model.h | 1 + 6 files changed, 54 insertions(+), 39 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index b041f5bc2d..b368024337 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -19,9 +19,6 @@ FaceModel::FaceModel(Head* owningHead) : } void FaceModel::simulate(float deltaTime) { - if (!isActive()) { - return; - } Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); glm::vec3 neckPosition; if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index ac08c52b49..5ae17c3923 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -20,10 +20,6 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) : } void SkeletonModel::simulate(float deltaTime) { - if (!isActive()) { - return; - } - setTranslation(_owningAvatar->getPosition()); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f)); const float MODEL_SCALE = 0.0006f; diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index dfe6949438..0688166ca3 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -294,17 +294,21 @@ QSharedPointer GeometryCache::getGeometry(const QUrl& url, cons if (geometry.isNull()) { geometry = QSharedPointer(new NetworkGeometry(url, fallback.isValid() ? getGeometry(fallback) : QSharedPointer())); + geometry->setLODParent(geometry); _networkGeometry.insert(url, geometry); } return geometry; } -NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback) : +NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, + const QVariantHash& mapping, const QUrl& textureBase) : _request(url), _reply(NULL), - _textureBase(url), + _mapping(mapping), + _textureBase(textureBase.isValid() ? textureBase : url), _fallback(fallback), - _attempts(0) { + _attempts(0), + _failedToLoad(false) { if (!url.isValid()) { return; @@ -319,6 +323,17 @@ NetworkGeometry::~NetworkGeometry() { } } +QSharedPointer NetworkGeometry::getLODOrFallback(float distance) const { + if (_lodParent.data() != this) { + return _lodParent.data()->getLODOrFallback(distance); + } + if (_failedToLoad && _fallback) { + return _fallback; + } + QMap >::const_iterator it = _lods.upperBound(distance); + return (it == _lods.constBegin()) ? _lodParent.toStrongRef() : *(it - 1); +} + glm::vec4 NetworkGeometry::computeAverageColor() const { glm::vec4 totalColor; int totalTriangles = 0; @@ -367,7 +382,8 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT QString filename = _mapping.value("filename").toString(); if (filename.isNull()) { qDebug() << "Mapping file " << url << " has no filename."; - maybeLoadFallback(); + _failedToLoad = true; + } else { QString texdir = _mapping.value("texdir").toString(); if (!texdir.isNull()) { @@ -376,6 +392,13 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT } _textureBase = url.resolved(texdir); } + QVariantHash lods = _mapping.value("lod").toHash(); + for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) { + QSharedPointer geometry(new NetworkGeometry(url.resolved(it.key()), + QSharedPointer(), _mapping, _textureBase)); + geometry->setLODParent(_lodParent); + _lods.insert(it.value().toFloat(), geometry); + } _request.setUrl(url.resolved(filename)); makeRequest(); } @@ -387,7 +410,7 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT } catch (const QString& error) { qDebug() << "Error reading " << url << ": " << error; - maybeLoadFallback(); + _failedToLoad = true; return; } @@ -481,8 +504,6 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT _meshes.append(networkMesh); } - - emit loaded(); } void NetworkGeometry::handleReplyError() { @@ -515,28 +536,12 @@ void NetworkGeometry::handleReplyError() { // fall through to final failure } default: - maybeLoadFallback(); + _failedToLoad = true; break; } } -void NetworkGeometry::loadFallback() { - _geometry = _fallback->_geometry; - _meshes = _fallback->_meshes; - emit loaded(); -} - -void NetworkGeometry::maybeLoadFallback() { - if (_fallback) { - if (_fallback->isLoaded()) { - loadFallback(); - } else { - connect(_fallback.data(), SIGNAL(loaded()), SLOT(loadFallback())); - } - } -} - bool NetworkMeshPart::isTranslucent() const { return diffuseTexture && diffuseTexture->isTranslucent(); } diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 0587831721..a3f9c93ca6 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -13,6 +13,7 @@ #include "InterfaceConfig.h" #include +#include #include #include #include @@ -61,41 +62,46 @@ class NetworkGeometry : public QObject { public: - NetworkGeometry(const QUrl& url, const QSharedPointer& fallback); + NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, + const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl()); ~NetworkGeometry(); bool isLoaded() const { return !_geometry.joints.isEmpty(); } + /// Returns a pointer to the geometry appropriate for the specified distance. + QSharedPointer getLODOrFallback(float distance) const; + const FBXGeometry& getFBXGeometry() const { return _geometry; } const QVector& getMeshes() const { return _meshes; } /// Returns the average color of all meshes in the geometry. glm::vec4 computeAverageColor() const; -signals: - - void loaded(); - private slots: void makeRequest(); void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void handleReplyError(); - void loadFallback(); private: - void maybeLoadFallback(); + friend class GeometryCache; + + void setLODParent(const QWeakPointer& lodParent) { _lodParent = lodParent; } QNetworkRequest _request; QNetworkReply* _reply; QVariantHash _mapping; QUrl _textureBase; QSharedPointer _fallback; + bool _failedToLoad; int _attempts; + QMap > _lods; FBXGeometry _geometry; QVector _meshes; + + QWeakPointer _lodParent; }; /// The state associated with a single mesh part. diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 18654a6efc..869e89539e 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -90,6 +90,16 @@ void Model::reset() { } void Model::simulate(float deltaTime) { + // update our LOD + if (_geometry) { + QSharedPointer geometry = _geometry->getLODOrFallback(glm::distance(_translation, + Application::getInstance()->getCamera()->getPosition())); + if (_geometry != geometry) { + deleteGeometry(); + _dilatedTextures.clear(); + _geometry = geometry; + } + } if (!isActive()) { return; } @@ -410,7 +420,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback) { deleteGeometry(); _dilatedTextures.clear(); - _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback); + _baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback); } glm::vec4 Model::computeAverageColor() const { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 003cdfe3e5..64ca789570 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -176,6 +176,7 @@ public: protected: + QSharedPointer _baseGeometry; QSharedPointer _geometry; glm::vec3 _translation; From ab4164a6dfeea9888ee8468aa6b008983188ef1c Mon Sep 17 00:00:00 2001 From: stojce Date: Thu, 13 Feb 2014 22:44:10 +0100 Subject: [PATCH 26/70] #19505 - Add domain & orientation parsing to hifi:// protocol handler --- interface/src/Application.cpp | 21 +++++++++++++-- interface/src/Menu.cpp | 50 ++++++++++++++++++++++++++++++----- interface/src/Menu.h | 2 ++ 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index eeb9d4a73a..57f66e68b2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -687,8 +687,25 @@ bool Application::event(QEvent* event) { if (event->type() == QEvent::FileOpen) { QFileOpenEvent* fileEvent = static_cast(event); if (!fileEvent->url().isEmpty() && fileEvent->url().toLocalFile().startsWith(CUSTOM_URL_SCHEME)) { - QString destination = fileEvent->url().toLocalFile().remove(QRegExp(CUSTOM_URL_SCHEME + "|/")); - Menu::getInstance()->goToDestination(destination); + QString destination = fileEvent->url().toLocalFile().remove(CUSTOM_URL_SCHEME); + QStringList urlParts = destination.split('/', QString::SkipEmptyParts); + + if (urlParts.count() > 1) { + // if url has 2 or more parts, the first one is domain name + Menu::getInstance()->goToDomain(urlParts[0]); + + // location coordinates + Menu::getInstance()->goToDestination(urlParts[1]); + if (urlParts.count() > 2) { + + // location orientation + Menu::getInstance()->goToOrientation(urlParts[2]); + } + } else if (urlParts.count() == 1) { + + // location coordinates + Menu::getInstance()->goToDestination(urlParts[0]); + } } return false; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9854a8b97f..e064c4bd20 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -889,6 +889,17 @@ void Menu::editPreferences() { sendFakeEnterEvent(); } +void Menu::goToDomain(const QString newDomain) { + if (NodeList::getInstance()->getDomainHostname() != newDomain) { + + // send a node kill request, indicating to other clients that they should play the "disappeared" effect + Application::getInstance()->getAvatar()->sendKillAvatar(); + + // give our nodeList the new domain-server hostname + NodeList::getInstance()->setDomainHostname(newDomain); + } +} + void Menu::goToDomain() { QString currentDomainHostname = NodeList::getInstance()->getDomainHostname(); @@ -913,17 +924,44 @@ void Menu::goToDomain() { // the user input a new hostname, use that newHostname = domainDialog.textValue(); } - - // send a node kill request, indicating to other clients that they should play the "disappeared" effect - Application::getInstance()->getAvatar()->sendKillAvatar(); - - // give our nodeList the new domain-server hostname - NodeList::getInstance()->setDomainHostname(domainDialog.textValue()); + + goToDomain(newHostname); } sendFakeEnterEvent(); } +void Menu::goToOrientation(QString orientation) { + + if (orientation.isEmpty()) { + return; + } + + QStringList orientationItems = orientation.split(QRegExp("_|,"), QString::SkipEmptyParts); + + const int NUMBER_OF_ORIENTATION_ITEMS = 4; + const int W_ITEM = 0; + const int X_ITEM = 1; + const int Y_ITEM = 2; + const int Z_ITEM = 3; + + if (orientationItems.size() == NUMBER_OF_ORIENTATION_ITEMS) { + + double w = replaceLastOccurrence('-', '.', orientationItems[W_ITEM].trimmed()).toDouble(); + double x = replaceLastOccurrence('-', '.', orientationItems[X_ITEM].trimmed()).toDouble(); + double y = replaceLastOccurrence('-', '.', orientationItems[Y_ITEM].trimmed()).toDouble(); + double z = replaceLastOccurrence('-', '.', orientationItems[Z_ITEM].trimmed()).toDouble(); + + glm::quat newAvatarOrientation(w, x, y, z); + + MyAvatar* myAvatar = Application::getInstance()->getAvatar(); + glm::quat avatarOrientation = myAvatar->getOrientation(); + if (newAvatarOrientation != avatarOrientation) { + myAvatar->setOrientation(newAvatarOrientation); + } + } +} + bool Menu::goToDestination(QString destination) { QStringList coordinateItems = destination.split(QRegExp("_|,"), QString::SkipEmptyParts); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9ccc5466e8..378c022ae0 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -85,6 +85,8 @@ public: QAction::MenuRole role = QAction::NoRole); virtual void removeAction(QMenu* menu, const QString& actionName); bool goToDestination(QString destination); + void goToOrientation(QString orientation); + void goToDomain(const QString newDomain); public slots: void bandwidthDetails(); From 50d864901e742ba3cb7e0b338855474dbf13af10 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 13 Feb 2014 14:02:25 -0800 Subject: [PATCH 27/70] Improved comment about limitation of collision check. --- interface/src/renderer/Model.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index ce11ee3d71..7686b1ac7f 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -477,7 +477,10 @@ bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetra if (findSphereCapsuleConePenetration(relativeCenter, penetratorRadius, start, end, startRadius, endRadius, bonePenetration)) { totalPenetration = addPenetrations(totalPenetration, bonePenetration); - // TODO: Andrew to try to keep the joint furthest toward the root + // BUG: we currently overwrite the jointIndex with the last one found + // which can cause incorrect collisions when colliding against more than + // one joint. + // TODO: fix this. jointIndex = i; } outerContinue: ; From ff01470850a3f2b0c19d13358ce6208f4b2a4962 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Feb 2014 14:25:01 -0800 Subject: [PATCH 28/70] Wait until each LOD level is actually requested before we start loading; before a level is loaded, try to use the closest already-loaded level. --- interface/src/renderer/GeometryCache.cpp | 37 ++++++++++++++++++++++-- interface/src/renderer/GeometryCache.h | 2 ++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 0688166ca3..28280395e4 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -308,13 +308,18 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer NetworkGeometry::getLODOrFallback(float distance return _fallback; } QMap >::const_iterator it = _lods.upperBound(distance); - return (it == _lods.constBegin()) ? _lodParent.toStrongRef() : *(it - 1); + QSharedPointer lod = (it == _lods.constBegin()) ? _lodParent.toStrongRef() : *(it - 1); + if (lod->isLoaded()) { + return lod; + } + // if the ideal LOD isn't loaded, we need to make sure it's started to load, and possibly return the closest loaded one + if (!lod->_startedLoading) { + lod->makeRequest(); + } + float closestDistance = FLT_MAX; + if (isLoaded()) { + lod = _lodParent; + closestDistance = distance; + } + for (it = _lods.constBegin(); it != _lods.constEnd(); it++) { + float distanceToLOD = glm::abs(distance - it.key()); + if (it.value()->isLoaded() && distanceToLOD < closestDistance) { + lod = it.value(); + closestDistance = distanceToLOD; + } + } + return lod; } glm::vec4 NetworkGeometry::computeAverageColor() const { @@ -359,6 +384,7 @@ glm::vec4 NetworkGeometry::computeAverageColor() const { } void NetworkGeometry::makeRequest() { + _startedLoading = true; _reply = Application::getInstance()->getNetworkAccessManager()->get(_request); connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64))); @@ -400,7 +426,12 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT _lods.insert(it.value().toFloat(), geometry); } _request.setUrl(url.resolved(filename)); - makeRequest(); + + // make the request immediately only if we have no LODs to switch between + _startedLoading = false; + if (_lods.isEmpty()) { + makeRequest(); + } } return; } diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index a3f9c93ca6..cd1972f413 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -66,6 +66,7 @@ public: const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl()); ~NetworkGeometry(); + /// Checks whether the geometry is fulled loaded. bool isLoaded() const { return !_geometry.joints.isEmpty(); } /// Returns a pointer to the geometry appropriate for the specified distance. @@ -94,6 +95,7 @@ private: QVariantHash _mapping; QUrl _textureBase; QSharedPointer _fallback; + bool _startedLoading; bool _failedToLoad; int _attempts; From caab3afb69c65f7703ef9e90f76ccd7bd303b534 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 14:26:30 -0800 Subject: [PATCH 29/70] add menu item for auto adjust LOD --- interface/src/Application.cpp | 5 ++++- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fd4c52d9f2..ab511b62e0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2709,7 +2709,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { _voxels.render(); // double check that our LOD doesn't need to be auto-adjusted - Menu::getInstance()->autoAdjustLOD(_fps); + // only adjust if our option is set + if (Menu::getInstance()->isOptionChecked(MenuOption::AutoAdjustLOD)) { + Menu::getInstance()->autoAdjustLOD(_fps); + } } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 63717fdbb5..3eecdd1e96 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -317,6 +317,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AutoAdjustLOD); addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); QMenu* voxelProtoOptionsMenu = voxelOptionsMenu->addMenu("Voxel Server Protocol Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 130ce19475..fd9582229f 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -175,6 +175,7 @@ namespace MenuOption { const QString AmbientOcclusion = "Ambient Occlusion"; const QString Avatars = "Avatars"; const QString Atmosphere = "Atmosphere"; + const QString AutoAdjustLOD = "Automatically Adjust LOD"; const QString AutomaticallyAuditTree = "Automatically Audit Tree Stats"; const QString Bandwidth = "Bandwidth Display"; const QString BandwidthDetails = "Bandwidth Details"; From 4fb5e6842590feee2960211c448cc5367d03788a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 14:33:23 -0800 Subject: [PATCH 30/70] removed some debug --- interface/src/VoxelSystem.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 37bb2b5db1..9cacd02b18 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -127,16 +127,9 @@ void VoxelSystem::setDisableFastVoxelPipeline(bool disableFastVoxelPipeline) { void VoxelSystem::elementUpdated(OctreeElement* element) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; -//qDebug() << "VoxelSystem::elementUpdated()..."; // If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail.. if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { -/* -qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! " - << "_writeRenderFullVBO="<< _writeRenderFullVBO - << "_inSetupNewVoxelsForDrawing="<< _inSetupNewVoxelsForDrawing - << "_useFastVoxelPipeline="<< _useFastVoxelPipeline; -*/ return; } @@ -147,8 +140,6 @@ qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! " int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); shouldRender = voxel->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust); -//qDebug() << "VoxelSystem::elementUpdated()... recalcing should render!!"; - if (voxel->getShouldRender() != shouldRender) { voxel->setShouldRender(shouldRender); } @@ -176,13 +167,11 @@ qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! " const bool REUSE_INDEX = true; const bool DONT_FORCE_REDRAW = false; -//qDebug() << "VoxelSystem::elementUpdated()... calling updateNodeInArrays()!!!"; updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW); _voxelsUpdated++; voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. -//qDebug() << "VoxelSystem::elementUpdated()... calling setupNewVoxelsForDrawingSingleNode()!!!"; setupNewVoxelsForDrawingSingleNode(); } } From ed7fa6e311a368b3dbe5bf2e70f8aa7ea33d51b1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 14:39:10 -0800 Subject: [PATCH 31/70] added back setupNewVoxelsForDrawing() on changeTree() --- interface/src/VoxelSystem.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 9cacd02b18..632c5e056e 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1186,8 +1186,7 @@ void VoxelSystem::changeTree(VoxelTree* newTree) { connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); - // TODO: hmmmmm????? - //setupNewVoxelsForDrawing(); + setupNewVoxelsForDrawing(); } void VoxelSystem::updateFullVBOs() { From 601c155303692956c5d1e6c94b7df4d3e337b584 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 14:46:59 -0800 Subject: [PATCH 32/70] correctly lock arrays on init --- interface/src/VoxelSystem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 632c5e056e..a3352f36e7 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -413,8 +413,8 @@ void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndice } void VoxelSystem::initVoxelMemory() { - //_readArraysLock.lockForWrite(); - //_writeArraysLock.lockForWrite(); + _readArraysLock.lockForWrite(); + _writeArraysLock.lockForWrite(); _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them @@ -529,8 +529,8 @@ void VoxelSystem::initVoxelMemory() { _initialized = true; - //_writeArraysLock.unlock(); - //_readArraysLock.unlock(); + _writeArraysLock.unlock(); + _readArraysLock.unlock(); } void VoxelSystem::writeToSVOFile(const char* filename, VoxelTreeElement* element) const { From 078b15c02d1aeee7df0854e192dba815b9860e59 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Feb 2014 16:20:43 -0800 Subject: [PATCH 33/70] Add some hysteresis on the LOD switching to prevent rapid switching back and forth. --- interface/src/renderer/GeometryCache.cpp | 21 ++++++++++++++++++--- interface/src/renderer/GeometryCache.h | 6 +++++- interface/src/renderer/Model.cpp | 3 ++- interface/src/renderer/Model.h | 4 +++- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 28280395e4..c02f69b8fb 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -328,16 +328,30 @@ NetworkGeometry::~NetworkGeometry() { } } -QSharedPointer NetworkGeometry::getLODOrFallback(float distance) const { +QSharedPointer NetworkGeometry::getLODOrFallback(float distance, float& hysteresis) const { if (_lodParent.data() != this) { - return _lodParent.data()->getLODOrFallback(distance); + return _lodParent.data()->getLODOrFallback(distance, hysteresis); } if (_failedToLoad && _fallback) { return _fallback; } + QSharedPointer lod = _lodParent; + float lodDistance = 0.0f; QMap >::const_iterator it = _lods.upperBound(distance); - QSharedPointer lod = (it == _lods.constBegin()) ? _lodParent.toStrongRef() : *(it - 1); + if (it != _lods.constBegin()) { + it = it - 1; + lod = it.value(); + lodDistance = it.key(); + } + if (hysteresis != NO_HYSTERESIS && hysteresis != lodDistance) { + // if we previously selected a different distance, make sure we've moved far enough to justify switching + const float HYSTERESIS_PROPORTION = 0.1f; + if (glm::abs(distance - qMax(hysteresis, lodDistance)) / fabsf(hysteresis - lodDistance) < HYSTERESIS_PROPORTION) { + return getLODOrFallback(hysteresis, hysteresis); + } + } if (lod->isLoaded()) { + hysteresis = lodDistance; return lod; } // if the ideal LOD isn't loaded, we need to make sure it's started to load, and possibly return the closest loaded one @@ -356,6 +370,7 @@ QSharedPointer NetworkGeometry::getLODOrFallback(float distance closestDistance = distanceToLOD; } } + hysteresis = NO_HYSTERESIS; return lod; } diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index cd1972f413..110e2eec4c 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -62,6 +62,9 @@ class NetworkGeometry : public QObject { public: + /// A hysteresis value indicating that we have no state memory. + static const float NO_HYSTERESIS = -1.0f; + NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl()); ~NetworkGeometry(); @@ -70,7 +73,8 @@ public: bool isLoaded() const { return !_geometry.joints.isEmpty(); } /// Returns a pointer to the geometry appropriate for the specified distance. - QSharedPointer getLODOrFallback(float distance) const; + /// \param hysteresis a hysteresis parameter that prevents rapid model switching + QSharedPointer getLODOrFallback(float distance, float& hysteresis) const; const FBXGeometry& getFBXGeometry() const { return _geometry; } const QVector& getMeshes() const { return _meshes; } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 869e89539e..5ea8ff33b4 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -93,7 +93,7 @@ void Model::simulate(float deltaTime) { // update our LOD if (_geometry) { QSharedPointer geometry = _geometry->getLODOrFallback(glm::distance(_translation, - Application::getInstance()->getCamera()->getPosition())); + glm::vec3() /* Application::getInstance()->getCamera()->getPosition() */), _lodHysteresis); if (_geometry != geometry) { deleteGeometry(); _dilatedTextures.clear(); @@ -419,6 +419,7 @@ void Model::setURL(const QUrl& url, const QUrl& fallback) { // delete our local geometry and custom textures deleteGeometry(); _dilatedTextures.clear(); + _lodHysteresis = NetworkGeometry::NO_HYSTERESIS; _baseGeometry = _geometry = Application::getInstance()->getGeometryCache()->getGeometry(url, fallback); } diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 64ca789570..74283043cf 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -176,7 +176,6 @@ public: protected: - QSharedPointer _baseGeometry; QSharedPointer _geometry; glm::vec3 _translation; @@ -237,6 +236,9 @@ private: void deleteGeometry(); void renderMeshes(float alpha, bool translucent); + QSharedPointer _baseGeometry; + float _lodHysteresis; + float _pupilDilation; std::vector _blendshapeCoefficients; From 5f38c328d58f6fa6f91454e17d7a93d73def86a6 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Feb 2014 16:25:04 -0800 Subject: [PATCH 34/70] Removed debugging code. --- interface/src/renderer/Model.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5ea8ff33b4..d368de409a 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -93,7 +93,7 @@ void Model::simulate(float deltaTime) { // update our LOD if (_geometry) { QSharedPointer geometry = _geometry->getLODOrFallback(glm::distance(_translation, - glm::vec3() /* Application::getInstance()->getCamera()->getPosition() */), _lodHysteresis); + Application::getInstance()->getCamera()->getPosition()), _lodHysteresis); if (_geometry != geometry) { deleteGeometry(); _dilatedTextures.clear(); From b08d4527490c888cbc84c8d9032fad18c14aa59c Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Feb 2014 16:49:42 -0800 Subject: [PATCH 35/70] Fix for head appearing at wrong position for a single frame. --- interface/src/avatar/FaceModel.cpp | 4 ++++ interface/src/avatar/SkeletonModel.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index b368024337..c9d2565cee 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -19,6 +19,10 @@ FaceModel::FaceModel(Head* owningHead) : } void FaceModel::simulate(float deltaTime) { + if (!isActive()) { + Model::simulate(deltaTime); + return; + } Avatar* owningAvatar = static_cast(_owningHead->_owningAvatar); glm::vec3 neckPosition; if (!owningAvatar->getSkeletonModel().getNeckPosition(neckPosition)) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 5ae17c3923..c6788c34f7 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -20,6 +20,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) : } void SkeletonModel::simulate(float deltaTime) { + if (!isActive()) { + Model::simulate(deltaTime); + return; + } setTranslation(_owningAvatar->getPosition()); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(180.0f, 0.0f, 1.0f, 0.0f)); const float MODEL_SCALE = 0.0006f; From 1afd3ab712aab3e673dcdd2162ee58d2dd175deb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 13 Feb 2014 17:07:57 -0800 Subject: [PATCH 36/70] Fixes for OS X warnings. --- interface/src/avatar/MyAvatar.cpp | 1 - interface/src/renderer/GeometryCache.cpp | 6 ++++-- interface/src/renderer/GeometryCache.h | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0e2625bf0f..de2459d9ee 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1002,7 +1002,6 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { avatar->getPosition(), theirCapsuleRadius, theirCapsuleHeight, penetration)) { // move the avatar out by half the penetration setPosition(_position - 0.5f * penetration); - glm::vec3 pushOut = 0.5f * penetration; } } } diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index c02f69b8fb..78cc657018 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -300,6 +300,8 @@ QSharedPointer GeometryCache::getGeometry(const QUrl& url, cons return geometry; } +const float NetworkGeometry::NO_HYSTERESIS = -1.0f; + NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, const QVariantHash& mapping, const QUrl& textureBase) : _request(url), @@ -307,9 +309,9 @@ NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer& fallback, const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl()); From 9d841ce918f505a82a2f83f68d471f5ec78df268 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 18:19:20 -0800 Subject: [PATCH 37/70] add Stop All Scripts and Reload All Scripts --- interface/src/Application.cpp | 24 ++++++++++++++++++++++++ interface/src/Application.h | 2 ++ interface/src/Menu.cpp | 2 ++ interface/src/Menu.h | 6 ++++-- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f51360cca1..80c2dc7feb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3984,6 +3984,30 @@ void Application::saveScripts() { settings->endArray(); } +void Application::stopAllScripts() { + // stops all current running scripts + QList scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions(); + foreach (QAction* scriptAction, scriptActions) { + scriptAction->activate(QAction::Trigger); + qDebug() << "stopping script..." << scriptAction->text(); + } +} + +void Application::reloadAllScripts() { + // reloads all current running scripts + QList scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions(); + foreach (QAction* scriptAction, scriptActions) { + scriptAction->activate(QAction::Trigger); + qDebug() << "stopping script..." << scriptAction->text(); + } + QStringList reloadList = _activeScripts; + _activeScripts.clear(); + foreach (QString scriptName, reloadList){ + qDebug() << "reloading script..." << scriptName; + loadScript(scriptName); + } +} + void Application::removeScriptName(const QString& fileNameString) { _activeScripts.removeOne(fileNameString); } diff --git a/interface/src/Application.h b/interface/src/Application.h index c5aafc4e9d..3153150457 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -231,6 +231,8 @@ public slots: void loadDialog(); void toggleLogDialog(); void initAvatarAndViewFrustum(); + void stopAllScripts(); + void reloadAllScripts(); private slots: void timer(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 4a3733f760..f16a276653 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -93,6 +93,8 @@ Menu::Menu() : addDisabledActionAndSeparator(fileMenu, "Scripts"); addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, appInstance, SLOT(loadDialog())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, appInstance, SLOT(stopAllScripts())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, 0, appInstance, SLOT(reloadAllScripts())); _activeScriptsMenu = fileMenu->addMenu("Running Scripts"); addDisabledActionAndSeparator(fileMenu, "Voxels"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 19e9fbf49f..31b19a64c6 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -243,8 +243,10 @@ namespace MenuOption { const QString PasteVoxels = "Paste"; const QString PasteToVoxel = "Paste to Voxel..."; const QString PipelineWarnings = "Show Render Pipeline Warnings"; + const QString PlaySlaps = "Play Slaps"; const QString Preferences = "Preferences..."; const QString RandomizeVoxelColors = "Randomize Voxel TRUE Colors"; + const QString ReloadAllScripts = "Reload All Scripts"; const QString ResetAvatarSize = "Reset Avatar Size"; const QString ResetSwatchColors = "Reset Swatch Colors"; const QString RunTimingTests = "Run Timing Tests"; @@ -253,11 +255,10 @@ namespace MenuOption { const QString SettingsExport = "Export Settings"; const QString ShowAllLocalVoxels = "Show All Local Voxels"; const QString ShowTrueColors = "Show TRUE Colors"; - const QString VoxelDrumming = "Voxel Drumming"; - const QString PlaySlaps = "Play Slaps"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; const QString Stars = "Stars"; const QString Stats = "Stats"; + const QString StopAllScripts = "Stop All Scripts"; const QString TestPing = "Test Ping"; const QString TreeStats = "Calculate Tree Stats"; const QString TransmitterDrive = "Transmitter Drive"; @@ -268,6 +269,7 @@ namespace MenuOption { const QString VoxelAddMode = "Add Voxel Mode"; const QString VoxelColorMode = "Color Voxel Mode"; const QString VoxelDeleteMode = "Delete Voxel Mode"; + const QString VoxelDrumming = "Voxel Drumming"; const QString VoxelGetColorMode = "Get Color Mode"; const QString VoxelMode = "Cycle Voxel Mode"; const QString VoxelPaintColor = "Voxel Paint Color"; From dbd6cb71c9aadc491fa667becf566be2a6fc3d06 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 20:55:16 -0800 Subject: [PATCH 38/70] fixed crash on shutdown with multiple scripts --- libraries/octree/src/OctreeScriptingInterface.cpp | 13 +++++++++++-- libraries/octree/src/OctreeScriptingInterface.h | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/octree/src/OctreeScriptingInterface.cpp b/libraries/octree/src/OctreeScriptingInterface.cpp index 553ab961df..89bf5ceb62 100644 --- a/libraries/octree/src/OctreeScriptingInterface.cpp +++ b/libraries/octree/src/OctreeScriptingInterface.cpp @@ -11,13 +11,19 @@ #include "OctreeScriptingInterface.h" OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packetSender, - JurisdictionListener* jurisdictionListener) + JurisdictionListener* jurisdictionListener) : + _packetSender(NULL), + _jurisdictionListener(NULL), + _managedPacketSender(false), + _managedJurisdictionListener(false), + _initialized(false) { setPacketSender(packetSender); setJurisdictionListener(jurisdictionListener); } OctreeScriptingInterface::~OctreeScriptingInterface() { +qDebug() << "OctreeScriptingInterface::~OctreeScriptingInterface() this=" << this; cleanupManagedObjects(); } @@ -45,6 +51,9 @@ void OctreeScriptingInterface::setJurisdictionListener(JurisdictionListener* jur } void OctreeScriptingInterface::init() { + if (_initialized) { + return; + } if (_jurisdictionListener) { _managedJurisdictionListener = false; } else { @@ -64,5 +73,5 @@ void OctreeScriptingInterface::init() { if (QCoreApplication::instance()) { connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(cleanupManagedObjects())); } - + _initialized = true; } diff --git a/libraries/octree/src/OctreeScriptingInterface.h b/libraries/octree/src/OctreeScriptingInterface.h index 34eddd8bed..3c832cbae8 100644 --- a/libraries/octree/src/OctreeScriptingInterface.h +++ b/libraries/octree/src/OctreeScriptingInterface.h @@ -93,6 +93,7 @@ protected: JurisdictionListener* _jurisdictionListener; bool _managedPacketSender; bool _managedJurisdictionListener; + bool _initialized; }; #endif /* defined(__hifi__OctreeScriptingInterface__) */ From cd137b2b12bca1c450fb9eb23db4326e670cfdcf Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 21:00:20 -0800 Subject: [PATCH 39/70] tweak to reload scripts --- interface/src/Application.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 80c2dc7feb..96ace9076a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3991,16 +3991,18 @@ void Application::stopAllScripts() { scriptAction->activate(QAction::Trigger); qDebug() << "stopping script..." << scriptAction->text(); } + _activeScripts.clear(); } void Application::reloadAllScripts() { + // remember all the current scripts so we can reload them + QStringList reloadList = _activeScripts; // reloads all current running scripts QList scriptActions = Menu::getInstance()->getActiveScriptsMenu()->actions(); foreach (QAction* scriptAction, scriptActions) { scriptAction->activate(QAction::Trigger); qDebug() << "stopping script..." << scriptAction->text(); } - QStringList reloadList = _activeScripts; _activeScripts.clear(); foreach (QString scriptName, reloadList){ qDebug() << "reloading script..." << scriptName; From 58e773340abc7645105fa368ffc9d1df9193ff0d Mon Sep 17 00:00:00 2001 From: gaitat Date: Fri, 14 Feb 2014 09:40:59 -0500 Subject: [PATCH 40/70] Worklist Job #19503 Exposing the average audio intensity to javascript through the MyAvatar object. --- examples/audioBall.js | 78 ++++++++++++++++++++++++++++++ examples/audioBallLifetime.js | 62 ++++++++++++++++++++++++ interface/src/Audio.h | 2 + interface/src/avatar/MyAvatar.cpp | 6 ++- libraries/avatars/src/AvatarData.h | 8 ++- libraries/avatars/src/HeadData.h | 11 +++-- 6 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 examples/audioBall.js create mode 100644 examples/audioBallLifetime.js diff --git a/examples/audioBall.js b/examples/audioBall.js new file mode 100644 index 0000000000..b2761a3dd3 --- /dev/null +++ b/examples/audioBall.js @@ -0,0 +1,78 @@ +// +// audioBall.js +// hifi +// +// Created by Athanasios Gaitatzes on 2/10/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This script creates a particle in front of the user that stays in front of +// the user's avatar as they move, and animates it's radius and color +// in response to the audio intensity. +// + +// add two vectors +function vPlus(a, b) { + var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; + return rval; +} + +// multiply scalar with vector +function vsMult(s, v) { + var rval = { x: s * v.x, y: s * v.y, z: s * v.z }; + return rval; +} + +var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); +var FACTOR = 0.20; + +var countParticles = 0; // the first time around we want to create the particle and thereafter to modify it. +var particleID; + +function updateParticle() +{ + // the particle should be placed in front of the user's avatar + var avatarFront = Quat.getFront(MyAvatar.orientation); + + // move particle three units in front of the avatar + var particlePosition = vPlus(MyAvatar.position, vsMult (3, avatarFront)); + + // play a sound at the location of the particle + var options = new AudioInjectionOptions(); + options.position = particlePosition; + options.volume = 0.75; + Audio.playSound(sound, options); + + var audioCardAverageLoudness = MyAvatar.audioCardAverageLoudness * FACTOR; + + if (countParticles < 1) + { + var particleProperies = { + position: particlePosition // the particle should stay in front of the user's avatar as he moves + , color: { red: 0, green: 255, blue: 0 } + , radius: audioCardAverageLoudness + , velocity: { x: 0.0, y: 0.0, z: 0.0 } + , gravity: { x: 0.0, y: 0.0, z: 0.0 } + , damping: 0.0 + } + + particleID = Particles.addParticle (particleProperies); + countParticles++; + } + else + { + // animates the particles radius and color in response to the changing audio intensity + var newProperties = { + position: particlePosition // the particle should stay in front of the user's avatar as he moves + , color: { red: 0, green: 255 * audioCardAverageLoudness, blue: 0 } + , radius: audioCardAverageLoudness + }; + + Particles.editParticle (particleID, newProperties); + } +} + +// register the call back so it fires before each data send +Script.willSendVisualDataCallback.connect(updateParticle); + +// register our scriptEnding callback +Script.scriptEnding.connect(function scriptEnding() {}); diff --git a/examples/audioBallLifetime.js b/examples/audioBallLifetime.js new file mode 100644 index 0000000000..7df4a0cdad --- /dev/null +++ b/examples/audioBallLifetime.js @@ -0,0 +1,62 @@ +// +// audioBall.js +// hifi +// +// Created by Athanasios Gaitatzes on 2/10/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This script creates a particle in front of the user that stays in front of +// the user's avatar as they move, and animates it's radius and color +// in response to the audio intensity. +// + +// add two vectors +function vPlus(a, b) { + var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; + return rval; +} + +// multiply scalar with vector +function vsMult(s, v) { + var rval = { x: s * v.x, y: s * v.y, z: s * v.z }; + return rval; +} + +var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); +var FACTOR = 0.20; + +function addParticle() +{ + // the particle should be placed in front of the user's avatar + var avatarFront = Quat.getFront(MyAvatar.orientation); + + // move particle three units in front of the avatar + var particlePosition = vPlus(MyAvatar.position, vsMult (3, avatarFront)); + + // play a sound at the location of the particle + var options = new AudioInjectionOptions(); + options.position = particlePosition; + options.volume = 0.25; + Audio.playSound(sound, options); + + var audioCardAverageLoudness = MyAvatar.audioCardAverageLoudness * FACTOR; + + // animates the particles radius and color in response to the changing audio intensity + var particleProperies = { + position: particlePosition // the particle should stay in front of the user's avatar as he moves + , color: { red: 0, green: 255 * audioCardAverageLoudness, blue: 0 } + , radius: audioCardAverageLoudness + , velocity: { x: 0.0, y: 0.0, z: 0.0 } + , gravity: { x: 0.0, y: 0.0, z: 0.0 } + , damping: 0.0 + , lifetime: 0.05 + } + + Particles.addParticle (particleProperies); +} + +// register the call back so it fires before each data send +Script.willSendVisualDataCallback.connect(addParticle); + +// register our scriptEnding callback +Script.scriptEnding.connect(function scriptEnding() {}); diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 88e2731006..a356d93425 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -47,6 +47,8 @@ public: float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _averageInputLoudness, 0.f); } + float getAudioCardAverageInputLoudness() const { return _averageInputLoudness; } // saki + void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; } void setLastAcceleration(const glm::vec3 lastAcceleration) { _lastAcceleration = lastAcceleration; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 5628740770..f4570dca45 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -157,7 +157,9 @@ void MyAvatar::update(float deltaTime) { } // Get audio loudness data from audio input device - _head.setAudioLoudness(Application::getInstance()->getAudio()->getLastInputLoudness()); + Audio *audio = Application::getInstance()->getAudio(); + _head.setAudioLoudness(audio->getLastInputLoudness()); + _head.setAudioCardAverageLoudness(audio->getAudioCardAverageInputLoudness()); // saki if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) { setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition())); @@ -639,7 +641,7 @@ void MyAvatar::loadData(QSettings* settings) { _position.y = loadSetting(settings, "position_y", 0.0f); _position.z = loadSetting(settings, "position_z", 0.0f); - _head.setPupilDilation(settings->value("pupilDilation", 0.0f).toFloat()); + _head.setPupilDilation(loadSetting(settings, "pupilDilation", 0.0f)); _leanScale = loadSetting(settings, "leanScale", 0.05f); _targetScale = loadSetting(settings, "scale", 1.0f); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 46d92c0f2e..65f2a318e7 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -75,7 +75,9 @@ class AvatarData : public NodeData { Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) - + + Q_PROPERTY(float audioCardAverageLoudness READ getAudioCardAverageLoudness WRITE setAudioCardAverageLoudness) // saki + Q_PROPERTY(QUrl faceModelURL READ getFaceModelURL WRITE setFaceModelURL) Q_PROPERTY(QUrl skeletonModelURL READ getSkeletonModelURL WRITE setSkeletonModelURL) public: @@ -106,6 +108,10 @@ public: float getHeadPitch() const { return _headData->getPitch(); } void setHeadPitch(float value) { _headData->setPitch(value); }; + // access to Head().set/getAverageLoudness + float getAudioCardAverageLoudness() const { return _headData->getAudioCardAverageLoudness(); } // saki + void setAudioCardAverageLoudness(float value) { _headData->setAudioCardAverageLoudness(value); }; // saki + // Scale float getTargetScale() const { return _targetScale; } void setTargetScale(float targetScale) { _targetScale = targetScale; } diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index fde684bbf1..477a790f93 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -41,9 +41,13 @@ public: float getRoll() const { return _roll; } void setRoll(float roll) { _roll = glm::clamp(roll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } - - void setAudioLoudness(float audioLoudness) { _audioLoudness = audioLoudness; } - + + float getAudioLoudness() const { return _audioLoudness; } + void setAudioLoudness(float audioLoudness) { _audioLoudness = audioLoudness; } + + float getAudioCardAverageLoudness() const { return _audioCardAverageLoudness; } // saki + void setAudioCardAverageLoudness(float audioCardAverageLoudness) { _audioCardAverageLoudness = audioCardAverageLoudness; } // saki + const std::vector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } float getPupilDilation() const { return _pupilDilation; } @@ -79,6 +83,7 @@ protected: float _rightEyeBlink; float _averageLoudness; float _browAudioLift; + float _audioCardAverageLoudness; // saki std::vector _blendshapeCoefficients; float _pupilDilation; AvatarData* _owningAvatar; From 76142c92f248f4e6e8320a738825b1ed4f08afe0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Feb 2014 13:04:17 -0800 Subject: [PATCH 41/70] Adjust offset when skipping parts that don't match the translucency setting. Closes #2001. --- interface/src/renderer/Model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index d5a329c4ac..9029d63bd7 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -915,11 +915,11 @@ void Model::renderMeshes(float alpha, bool translucent) { qint64 offset = 0; for (int j = 0; j < networkMesh.parts.size(); j++) { const NetworkMeshPart& networkPart = networkMesh.parts.at(j); + const FBXMeshPart& part = mesh.parts.at(j); if (networkPart.isTranslucent() != translucent) { + offset += (part.quadIndices.size() + part.triangleIndices.size()) * sizeof(int); continue; } - const FBXMeshPart& part = mesh.parts.at(j); - // apply material properties glm::vec4 diffuse = glm::vec4(part.diffuseColor, alpha); glm::vec4 specular = glm::vec4(part.specularColor, alpha); From af0d3957510525bb851155788065529b5e7b0fcc Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 14 Feb 2014 13:11:16 -0800 Subject: [PATCH 42/70] Added sanity check for zero vertices. --- interface/src/renderer/Model.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 9029d63bd7..8c00842ea2 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -806,6 +806,10 @@ void Model::renderMeshes(float alpha, bool translucent) { const FBXMesh& mesh = geometry.meshes.at(i); int vertexCount = mesh.vertices.size(); + if (vertexCount == 0) { + // sanity check + continue; + } const_cast(networkMesh.vertexBuffer).bind(); From 2eac9c293f073fe64b7f8ace93e7c5d26fd870e4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 14 Feb 2014 15:37:47 -0800 Subject: [PATCH 43/70] first working version of scriptable Overlays --- examples/overlaysExample.js | 35 +++++ interface/src/Application.cpp | 13 +- interface/src/Application.h | 5 +- interface/src/ui/ImageOverlay.cpp | 136 +++++++++++++++--- interface/src/ui/ImageOverlay.h | 38 +++-- interface/src/ui/Overlays.cpp | 66 +++++++++ interface/src/ui/Overlays.h | 58 ++++++++ .../octree/src/OctreeScriptingInterface.cpp | 1 - 8 files changed, 314 insertions(+), 38 deletions(-) create mode 100644 examples/overlaysExample.js create mode 100644 interface/src/ui/Overlays.cpp create mode 100644 interface/src/ui/Overlays.h diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js new file mode 100644 index 0000000000..d297e2525d --- /dev/null +++ b/examples/overlaysExample.js @@ -0,0 +1,35 @@ +// +// overlaysExample.js +// hifi +// +// Created by Brad Hefta-Gaub on 2/14/14. +// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. +// +// This is an example script that demonstrates use of the Overlays class +// +// + + /* + _testOverlayA.init(_glWidget, QString("https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg"), + QRect(100,100,62,40), QRect(0,0,62,40)); + xColor red = { 255, 0, 0 }; + _testOverlayA.setBackgroundColor(red); + _testOverlayB.init(_glWidget, QString("https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg"), + QRect(170,100,62,40), QRect(0,80,62,40)); + */ + +var toolA = Overlays.addOverlay({ + x: 100, + y: 100, + width: 62, + height: 40, + subImage: { x: 0, y: 0, width: 62, height: 40 }, + imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg", + backgroundColor: { red: 255, green: 0, blue: 255}, + alpha: 1.0 + }); + +function scriptEnding() { + Overlays.deleteOverlay(toolA); +} +Script.scriptEnding.connect(scriptEnding); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e95f8b5f7f..4da0a35279 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -289,6 +289,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) : _sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense)); checkVersion(); + + _overlays.init(_glWidget); // do this before scripts load + // do this as late as possible so that all required subsystems are inialized loadScripts(); @@ -1870,14 +1873,13 @@ void Application::init() { _audio.init(_glWidget); - _testOverlayA.init(_glWidget, QString("./resources/images/hifi-interface-tools.svg"), QRect(100,100,62,40), QRect(0,0,62,40)); - _testOverlayB.init(_glWidget, QString("./resources/images/hifi-interface-tools.svg"), QRect(170,100,62,40), QRect(0,80,62,40)); - _rearMirrorTools = new RearMirrorTools(_glWidget, _mirrorViewRect, _settings); connect(_rearMirrorTools, SIGNAL(closeView()), SLOT(closeMirrorView())); connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); + + } void Application::closeMirrorView() { @@ -2986,8 +2988,7 @@ void Application::displayOverlay() { _pieMenu.render(); } - _testOverlayA.render(); // - _testOverlayB.render(); // + _overlays.render(); glPopMatrix(); } @@ -4066,6 +4067,8 @@ void Application::loadScript(const QString& fileNameString) { scriptEngine->registerGlobalObject("Camera", cameraScriptable); connect(scriptEngine, SIGNAL(finished(const QString&)), cameraScriptable, SLOT(deleteLater())); + scriptEngine->registerGlobalObject("Overlays", &_overlays); + QThread* workerThread = new QThread(this); // when the worker thread is started, call our engine's run.. diff --git a/interface/src/Application.h b/interface/src/Application.h index c8541aee29..b2a66d0bf8 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -70,7 +70,7 @@ #include "FileLogger.h" #include "ParticleTreeRenderer.h" #include "ControllerScriptingInterface.h" -#include "ui/ImageOverlay.h" +#include "ui/Overlays.h" class QAction; @@ -490,8 +490,7 @@ private: TouchEvent _lastTouchEvent; - ImageOverlay _testOverlayA; - ImageOverlay _testOverlayB; + Overlays _overlays; }; #endif /* defined(__interface__Application__) */ diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp index 33c139089d..b85f05f557 100644 --- a/interface/src/ui/ImageOverlay.cpp +++ b/interface/src/ui/ImageOverlay.cpp @@ -1,3 +1,11 @@ +// +// ImageOverlay.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + + #include "ImageOverlay.h" #include @@ -7,17 +15,24 @@ ImageOverlay::ImageOverlay() : _parent(NULL), - _textureID(0) + _textureID(0), + _alpha(DEFAULT_ALPHA), + _backgroundColor(DEFAULT_BACKGROUND_COLOR), + _renderImage(false), + _textureBound(false) { -} +} -void ImageOverlay::init(QGLWidget* parent, const QString& filename, const QRect& drawAt, const QRect& fromImage) { +void ImageOverlay::init(QGLWidget* parent) { + qDebug() << "ImageOverlay::init() parent=" << parent; _parent = parent; - qDebug() << "ImageOverlay::init()... filename=" << filename; + + /* + qDebug() << "ImageOverlay::init()... url=" << url; _bounds = drawAt; _fromImage = fromImage; - _textureImage = QImage(filename); - _textureID = _parent->bindTexture(_textureImage); + setImageURL(url); + */ } @@ -28,17 +43,43 @@ ImageOverlay::~ImageOverlay() { } } +void ImageOverlay::setImageURL(const QUrl& url) { + // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? + QNetworkAccessManager* manager = new QNetworkAccessManager(this); + connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); + manager->get(QNetworkRequest(url)); +} + +void ImageOverlay::replyFinished(QNetworkReply* reply) { + qDebug() << "ImageOverlay::replyFinished() reply=" << reply; + // replace our byte array with the downloaded data + QByteArray rawData = reply->readAll(); + _textureImage.loadFromData(rawData); + _renderImage = true; + + // TODO: handle setting image multiple times, how do we manage releasing the bound texture + qDebug() << "ImageOverlay::replyFinished() about to call _parent->bindTexture(_textureImage)... _parent" << _parent; + + + qDebug() << "ImageOverlay::replyFinished _textureID=" << _textureID + << "_textureImage.width()=" << _textureImage.width() + << "_textureImage.height()=" << _textureImage.height(); +} void ImageOverlay::render() { -qDebug() << "ImageOverlay::render _textureID=" << _textureID << "_bounds=" << _bounds; + //qDebug() << "ImageOverlay::render _textureID=" << _textureID << "_bounds=" << _bounds; + + if (_renderImage && !_textureBound) { + _textureID = _parent->bindTexture(_textureImage); + _textureBound = true; + } - - bool renderImage = false; - if (renderImage) { + if (_renderImage) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, _textureID); } - glColor4f(1.0f, 0.0f, 0.0f, 0.7f); // ??? + const float MAX_COLOR = 255; + glColor4f((_backgroundColor.red / MAX_COLOR), (_backgroundColor.green / MAX_COLOR), (_backgroundColor.blue / MAX_COLOR), _alpha); float imageWidth = _textureImage.width(); float imageHeight = _textureImage.height(); @@ -47,23 +88,84 @@ qDebug() << "ImageOverlay::render _textureID=" << _textureID << "_bounds=" << _b float w = _fromImage.width() / imageWidth; // ?? is this what we want? not sure float h = _fromImage.height() / imageHeight; - qDebug() << "ImageOverlay::render x=" << x << "y=" << y << "w="< #include +#include +#include +#include #include +#include #include #include #include #include "InterfaceConfig.h" -//#include "Util.h" +const xColor DEFAULT_BACKGROUND_COLOR = { 255, 255, 255 }; +const float DEFAULT_ALPHA = 0.7f; class ImageOverlay : QObject { Q_OBJECT @@ -34,9 +38,10 @@ class ImageOverlay : QObject { public: ImageOverlay(); ~ImageOverlay(); - void init(QGLWidget* parent, const QString& filename, const QRect& drawAt, const QRect& fromImage); + void init(QGLWidget* parent); void render(); +public slots: // getters int getX() const { return _bounds.x(); } int getY() const { return _bounds.y(); } @@ -47,19 +52,25 @@ public: const xColor& getBackgroundColor() const { return _backgroundColor; } float getAlpha() const { return _alpha; } const QUrl& getImageURL() const { return _imageURL; } + QScriptValue getProperties(); // setters - void setX(int x) { } - void setY(int y) { } - void setWidth(int width) { } - void setHeight(int height) { } - void setBounds(const QRect& bounds) { } - void setClipFromSource(const QRect& bounds) { } - void setBackgroundColor(const xColor& color) { } - void setAlpha(float) { } - void setImageURL(const QUrl& ) { } + void setX(int x) { _bounds.setX(x); } + void setY(int y) { _bounds.setY(y); } + void setWidth(int width) { _bounds.setWidth(width); } + void setHeight(int height) { _bounds.setHeight(height); } + void setBounds(const QRect& bounds) { _bounds = bounds; } + void setClipFromSource(const QRect& bounds) { _fromImage = bounds; } + void setBackgroundColor(const xColor& color) { _backgroundColor = color; } + void setAlpha(float alpha) { _alpha = alpha; } + void setImageURL(const QUrl& url); + void setProperties(const QScriptValue& properties); + +private slots: + void replyFinished(QNetworkReply* reply); // we actually want to hide this... private: + QUrl _imageURL; QGLWidget* _parent; QImage _textureImage; @@ -68,6 +79,9 @@ private: QRect _fromImage; // where from in the image to sample float _alpha; xColor _backgroundColor; + bool _renderImage; + bool _textureBound; }; + #endif /* defined(__interface__ImageOverlay__) */ diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp new file mode 100644 index 0000000000..438e94ad60 --- /dev/null +++ b/interface/src/ui/Overlays.cpp @@ -0,0 +1,66 @@ +// +// Overlays.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + + +#include "Overlays.h" + +unsigned int Overlays::_nextOverlayID = 0; + +Overlays::Overlays() { +} + +Overlays::~Overlays() { +} + +void Overlays::init(QGLWidget* parent) { + qDebug() << "Overlays::init() parent=" << parent; + _parent = parent; +} + +void Overlays::render() { + foreach(ImageOverlay* thisOverlay, _imageOverlays) { + thisOverlay->render(); + } +} + +// TODO: make multi-threaded safe +unsigned int Overlays::addOverlay(const QScriptValue& properties) { + unsigned int thisID = _nextOverlayID; + _nextOverlayID++; + ImageOverlay* thisOverlay = new ImageOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + _imageOverlays[thisID] = thisOverlay; + return thisID; +} + +QScriptValue Overlays::getOverlayProperties(unsigned int id) { + if (!_imageOverlays.contains(id)) { + return QScriptValue(); + } + ImageOverlay* thisOverlay = _imageOverlays[id]; + return thisOverlay->getProperties(); +} + +// TODO: make multi-threaded safe +bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { + if (!_imageOverlays.contains(id)) { + return false; + } + ImageOverlay* thisOverlay = _imageOverlays[id]; + thisOverlay->setProperties(properties); + return true; +} + +// TODO: make multi-threaded safe +void Overlays::deleteOverlay(unsigned int id) { + if (_imageOverlays.contains(id)) { + _imageOverlays.erase(_imageOverlays.find(id)); + + } +} + diff --git a/interface/src/ui/Overlays.h b/interface/src/ui/Overlays.h new file mode 100644 index 0000000000..8b9f6eb8be --- /dev/null +++ b/interface/src/ui/Overlays.h @@ -0,0 +1,58 @@ +// +// Overlays.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Overlays__ +#define __interface__Overlays__ + +/** +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "InterfaceConfig.h" +**/ + +#include + +#include "ImageOverlay.h" + +class Overlays : public QObject { + Q_OBJECT +public: + Overlays(); + ~Overlays(); + void init(QGLWidget* parent); + void render(); + +public slots: + /// adds an overlay with the specific properties + unsigned int addOverlay(const QScriptValue& properties); + + /// gets the current overlay properties for a specific overlay + QScriptValue getOverlayProperties(unsigned int id); + + /// edits an overlay updating only the included properties, will return the identified OverlayID in case of + /// successful edit, if the input id is for an unknown overlay this function will have no effect + bool editOverlay(unsigned int id, const QScriptValue& properties); + + /// deletes a particle + void deleteOverlay(unsigned int id); + +private: + QMap _imageOverlays; + static unsigned int _nextOverlayID; + QGLWidget* _parent; +}; + + +#endif /* defined(__interface__Overlays__) */ diff --git a/libraries/octree/src/OctreeScriptingInterface.cpp b/libraries/octree/src/OctreeScriptingInterface.cpp index 89bf5ceb62..1ed82564b6 100644 --- a/libraries/octree/src/OctreeScriptingInterface.cpp +++ b/libraries/octree/src/OctreeScriptingInterface.cpp @@ -23,7 +23,6 @@ OctreeScriptingInterface::OctreeScriptingInterface(OctreeEditPacketSender* packe } OctreeScriptingInterface::~OctreeScriptingInterface() { -qDebug() << "OctreeScriptingInterface::~OctreeScriptingInterface() this=" << this; cleanupManagedObjects(); } From b7352335f23e2c61b4e3b7854f786136ca93f60a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 14 Feb 2014 16:31:53 -0800 Subject: [PATCH 44/70] Adding collisions options to menu at a second location. And adding CollideWithParticle option which will be working soon... --- interface/src/Menu.cpp | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 4a3733f760..6e2cf17c88 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -166,14 +166,19 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly); - QMenu* collisionsOptionsMenu = editMenu->addMenu("Collision Options"); - - QObject* avatar = appInstance->getAvatar(); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithEnvironment, 0, false, avatar, SLOT(updateCollisionFlags())); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithAvatars, 0, false, avatar, SLOT(updateCollisionFlags())); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithVoxels, 0, false, avatar, SLOT(updateCollisionFlags())); - // TODO: make this option work - //addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithParticles, 0, false, avatar, SLOT(updateCollisionFlags())); + { + // add collision options to the general edit menu + QMenu* collisionsOptionsMenu = editMenu->addMenu("Collision Options"); + QObject* avatar = appInstance->getAvatar(); + addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithEnvironment, + 0, false, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithAvatars, + 0, true, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithVoxels, + 0, false, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithParticles, + 0, true, avatar, SLOT(updateCollisionFlags())); + } QMenu* toolsMenu = addMenu("Tools"); @@ -341,6 +346,20 @@ Menu::Menu() : SLOT(setTCPEnabled(bool))); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false); + { + // add collision options to the avatar menu + QMenu* collisionsOptionsMenu = avatarOptionsMenu->addMenu("Collision Options"); + QObject* avatar = appInstance->getAvatar(); + addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithEnvironment, + 0, false, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithAvatars, + 0, true, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithVoxels, + 0, false, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithParticles, + 0, true, avatar, SLOT(updateCollisionFlags())); + } + QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, @@ -515,6 +534,11 @@ void Menu::loadSettings(QSettings* settings) { Application::getInstance()->getProfile()->loadData(settings); Application::getInstance()->updateWindowTitle(); NodeList::getInstance()->loadData(settings); + + // MyAvatar caches some menu options, so we have to update them whenever we load settings. + // TODO: cache more settings in MyAvatar that are checked with very high frequency. + MyAvatar* myAvatar = Application::getInstance()->getAvatar(); + myAvatar->updateCollisionFlags(); } void Menu::saveSettings(QSettings* settings) { From 335141049cfb2d582b886f3ebbdac7d4bcb71d75 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 14 Feb 2014 16:33:19 -0800 Subject: [PATCH 45/70] Cleaning up collision check API's and re-enabling collisions with particles. --- interface/src/avatar/Avatar.cpp | 112 +++++++++--------- interface/src/avatar/Avatar.h | 34 ++---- interface/src/avatar/Hand.cpp | 32 +++-- interface/src/avatar/Head.cpp | 8 +- interface/src/avatar/Head.h | 2 +- interface/src/avatar/MyAvatar.cpp | 4 +- interface/src/renderer/Model.cpp | 66 ++++++----- interface/src/renderer/Model.h | 23 ++-- libraries/avatars/src/AvatarData.h | 8 +- .../particles/src/ParticleCollisionSystem.cpp | 76 +++++++----- .../particles/src/ParticleCollisionSystem.h | 1 + libraries/shared/src/CollisionInfo.cpp | 42 +++++++ libraries/shared/src/CollisionInfo.h | 76 ++++++++++-- 13 files changed, 298 insertions(+), 186 deletions(-) create mode 100644 libraries/shared/src/CollisionInfo.cpp diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index f36c03cba6..74a3838dc1 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -273,27 +273,19 @@ bool Avatar::findRayIntersection(const glm::vec3& origin, const glm::vec3& direc } bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, - ModelCollisionList& collisions, int skeletonSkipIndex) { - bool didPenetrate = false; - glm::vec3 skeletonPenetration; - ModelCollisionInfo collisionInfo; - /* Temporarily disabling collisions against the skeleton because the collision proxies up - * near the neck are bad and prevent the hand from hitting the face. - if (_skeletonModel.findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo, 1.0f, skeletonSkipIndex)) { - collisionInfo._model = &_skeletonModel; - collisions.push_back(collisionInfo); - didPenetrate = true; - } - */ - if (_head.getFaceModel().findSphereCollision(penetratorCenter, penetratorRadius, collisionInfo)) { - collisionInfo._model = &(_head.getFaceModel()); - collisions.push_back(collisionInfo); - didPenetrate = true; - } - return didPenetrate; + CollisionList& collisions, int skeletonSkipIndex) { + // Temporarily disabling collisions against the skeleton because the collision proxies up + // near the neck are bad and prevent the hand from hitting the face. + //return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions, 1.0f, skeletonSkipIndex); + return _head.getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions); } -bool Avatar::findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) { +bool Avatar::findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { + if (_collisionFlags & COLLISION_GROUP_PARTICLES) { + return false; + } + bool collided = false; + // first do the hand collisions const HandData* handData = getHandData(); if (handData) { for (int i = 0; i < NUM_HANDS; i++) { @@ -311,41 +303,55 @@ bool Avatar::findSphereCollisionWithHands(const glm::vec3& sphereCenter, float s break; } } + + int jointIndex = -1; glm::vec3 handPosition; if (i == 0) { _skeletonModel.getLeftHandPosition(handPosition); + jointIndex = _skeletonModel.getLeftHandJointIndex(); } else { _skeletonModel.getRightHandPosition(handPosition); + jointIndex = _skeletonModel.getRightHandJointIndex(); } glm::vec3 diskCenter = handPosition + HAND_PADDLE_OFFSET * fingerAxis; glm::vec3 diskNormal = palm->getNormal(); - float diskThickness = 0.08f; + const float DISK_THICKNESS = 0.08f; // collide against the disk - if (findSphereDiskPenetration(sphereCenter, sphereRadius, - diskCenter, HAND_PADDLE_RADIUS, diskThickness, diskNormal, - collision._penetration)) { - collision._addedVelocity = palm->getVelocity(); - return true; + glm::vec3 penetration; + if (findSphereDiskPenetration(particleCenter, particleRadius, + diskCenter, HAND_PADDLE_RADIUS, DISK_THICKNESS, diskNormal, + penetration)) { + CollisionInfo* collision = collisions.getNewCollision(); + if (collision) { + collision->_type = PADDLE_HAND_COLLISION; + collision->_flags = jointIndex; + collision->_penetration = penetration; + collision->_addedVelocity = palm->getVelocity(); + collided = true; + } else { + // collisions are full, so we might as well bail now + return collided; + } } } } } - return false; -} - -/* adebug TODO: make this work again -bool Avatar::findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) { - int jointIndex = _skeletonModel.findSphereCollision(sphereCenter, sphereRadius, collision._penetration); - if (jointIndex != -1) { - collision._penetration /= (float)(TREE_SCALE); - collision._addedVelocity = getVelocity(); - return true; + // then collide against the models + int preNumCollisions = collisions.size(); + if (_skeletonModel.findSphereCollisions(particleCenter, particleRadius, collisions)) { + // the Model doesn't have velocity info, so we have to set it for each new collision + int postNumCollisions = collisions.size(); + for (int i = preNumCollisions; i < postNumCollisions; ++i) { + CollisionInfo* collision = collisions.getCollision(i); + collision->_penetration /= (float)(TREE_SCALE); + collision->_addedVelocity = getVelocity(); + } + collided = true; } - return false; + return collided; } -*/ void Avatar::setFaceModelURL(const QUrl &faceModelURL) { AvatarData::setFaceModelURL(faceModelURL); @@ -430,9 +436,9 @@ void Avatar::updateCollisionFlags() { if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithVoxels)) { _collisionFlags |= COLLISION_GROUP_VOXELS; } - //if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) { - // _collisionFlags |= COLLISION_GROUP_PARTICLES; - //} + if (Menu::getInstance()->isOptionChecked(MenuOption::CollideWithParticles)) { + _collisionFlags |= COLLISION_GROUP_PARTICLES; + } } void Avatar::setScale(float scale) { @@ -449,34 +455,34 @@ float Avatar::getHeight() const { return extents.maximum.y - extents.minimum.y; } -bool Avatar::collisionWouldMoveAvatar(ModelCollisionInfo& collision) const { - // ATM only the Skeleton is pokeable - // TODO: make poke affect head - if (!collision._model) { +bool Avatar::collisionWouldMoveAvatar(CollisionInfo& collision) const { + if (!collision._data || collision._type != MODEL_COLLISION) { return false; } - if (collision._model == &_skeletonModel && collision._jointIndex != -1) { + Model* model = static_cast(collision._data); + int jointIndex = collision._flags; + + if (model == &(_skeletonModel) && jointIndex != -1) { // collision response of skeleton is temporarily disabled return false; //return _skeletonModel.collisionHitsMoveableJoint(collision); } - if (collision._model == &(_head.getFaceModel())) { + if (model == &(_head.getFaceModel())) { + // ATM we always handle MODEL_COLLISIONS against the face. return true; } return false; } -void Avatar::applyCollision(ModelCollisionInfo& collision) { - if (!collision._model) { +void Avatar::applyCollision(CollisionInfo& collision) { + if (!collision._data || collision._type != MODEL_COLLISION) { return; } - if (collision._model == &(_head.getFaceModel())) { + // TODO: make skeleton also respond to collisions + Model* model = static_cast(collision._data); + if (model == &(_head.getFaceModel())) { _head.applyCollision(collision); } - // TODO: make skeleton respond to collisions - //if (collision._model == &_skeletonModel && collision._jointIndex != -1) { - // _skeletonModel.applyCollision(collision); - //} } float Avatar::getPelvisFloatingHeight() const { diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 2fc26a36b5..cc1168ca88 100755 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -57,8 +57,6 @@ enum ScreenTintLayer { NUM_SCREEN_TINT_LAYERS }; -typedef QVector ModelCollisionList; - // Where one's own Avatar begins in the world (will be overwritten if avatar data file is found) // this is basically in the center of the ground plane. Slightly adjusted. This was asked for by // Grayson as he's building a street around here for demo dinner 2 @@ -97,26 +95,19 @@ public: /// Checks for penetration between the described sphere and the avatar. /// \param penetratorCenter the center of the penetration test sphere /// \param penetratorRadius the radius of the penetration test sphere - /// \param collisions[out] a list of collisions + /// \param collisions[out] a list to which collisions get appended /// \param skeletonSkipIndex if not -1, the index of a joint to skip (along with its descendents) in the skeleton model /// \return whether or not the sphere penetrated bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, - ModelCollisionList& collisions, int skeletonSkipIndex = -1); + CollisionList& collisions, int skeletonSkipIndex = -1); - /// Checks for collision between the a sphere and the avatar's (paddle) hands. - /// \param collisionCenter the center of the penetration test sphere - /// \param collisionRadius the radius of the penetration test sphere - /// \param collision[out] the details of the collision point - /// \return whether or not the sphere collided - bool findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision); + /// Checks for collision between the a spherical particle and the avatar (including paddle hands) + /// \param collisionCenter the center of particle's bounding sphere + /// \param collisionRadius the radius of particle's bounding sphere + /// \param collisions[out] a list to which collisions get appended + /// \return whether or not the particle collided + bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions); - /// Checks for collision between the a sphere and the avatar's skeleton (including hand capsules). - /// \param collisionCenter the center of the penetration test sphere - /// \param collisionRadius the radius of the penetration test sphere - /// \param collision[out] the details of the collision point - /// \return whether or not the sphere collided - //bool findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision); - virtual bool isMyAvatar() { return false; } virtual void setFaceModelURL(const QUrl& faceModelURL); @@ -126,13 +117,13 @@ public: static void renderJointConnectingCone(glm::vec3 position1, glm::vec3 position2, float radius1, float radius2); - float getHeight() const; - /// \return true if we expect the avatar would move as a result of the collision - bool collisionWouldMoveAvatar(ModelCollisionInfo& collision) const; + bool collisionWouldMoveAvatar(CollisionInfo& collision) const; /// \param collision a data structure for storing info about collisions against Models - void applyCollision(ModelCollisionInfo& collision); + void applyCollision(CollisionInfo& collision); + + float getBoundingRadius() const { return 0.5f * getHeight(); } public slots: void updateCollisionFlags(); @@ -164,6 +155,7 @@ protected: glm::quat computeRotationFromBodyToWorldUp(float proportion = 1.0f) const; void setScale(float scale); + float getHeight() const; float getPelvisFloatingHeight() const; float getPelvisToHeadLength() const; diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index af66c08bf4..7c9905557e 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -125,6 +125,10 @@ void Hand::simulate(float deltaTime, bool isMine) { } } +// We create a static CollisionList that is recycled for each collision test. +const float MAX_COLLISIONS_PER_AVATAR = 32; +static CollisionList handCollisions(MAX_COLLISIONS_PER_AVATAR); + void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { if (!avatar || avatar == _owningAvatar) { // don't collide with our own hands (that is done elsewhere) @@ -137,7 +141,6 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { continue; } glm::vec3 totalPenetration; - ModelCollisionList collisions; if (isMyHand && Menu::getInstance()->isOptionChecked(MenuOption::PlaySlaps)) { // Check for palm collisions glm::vec3 myPalmPosition = palm.getPosition(); @@ -171,20 +174,22 @@ void Hand::collideAgainstAvatar(Avatar* avatar, bool isMyHand) { } } } - if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions)) { - for (int j = 0; j < collisions.size(); ++j) { + handCollisions.clear(); + if (avatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions)) { + for (int j = 0; j < handCollisions.size(); ++j) { + CollisionInfo* collision = handCollisions.getCollision(j); if (isMyHand) { - if (!avatar->collisionWouldMoveAvatar(collisions[j])) { + if (!avatar->collisionWouldMoveAvatar(*collision)) { // we resolve the hand from collision when it belongs to MyAvatar AND the other Avatar is // not expected to respond to the collision (hand hit unmovable part of their Avatar) - totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration); + totalPenetration = addPenetrations(totalPenetration, collision->_penetration); } } else { // when !isMyHand then avatar is MyAvatar and we apply the collision // which might not do anything (hand hit unmovable part of MyAvatar) however // we don't resolve the hand's penetration because we expect the remote // simulation to do the right thing. - avatar->applyCollision(collisions[j]); + avatar->applyCollision(*collision); } } } @@ -200,7 +205,6 @@ void Hand::collideAgainstOurself() { return; } - ModelCollisionList collisions; int leftPalmIndex, rightPalmIndex; getLeftRightPalmIndices(leftPalmIndex, rightPalmIndex); float scaledPalmRadius = PALM_COLLISION_RADIUS * _owningAvatar->getScale(); @@ -210,16 +214,18 @@ void Hand::collideAgainstOurself() { if (!palm.isActive()) { continue; } - glm::vec3 totalPenetration; - // and the current avatar (ignoring everything below the parent of the parent of the last free joint) - collisions.clear(); const Model& skeletonModel = _owningAvatar->getSkeletonModel(); + // ignoring everything below the parent of the parent of the last free joint int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : (i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); - if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, collisions, skipIndex)) { - for (int j = 0; j < collisions.size(); ++j) { - totalPenetration = addPenetrations(totalPenetration, collisions[j]._penetration); + + handCollisions.clear(); + glm::vec3 totalPenetration; + if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions, skipIndex)) { + for (int j = 0; j < handCollisions.size(); ++j) { + CollisionInfo* collision = handCollisions.getCollision(j); + totalPenetration = addPenetrations(totalPenetration, collision->_penetration); } } // resolve penetration diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index bb88530aa7..ddb0660364 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -219,7 +219,7 @@ float Head::getTweakedRoll() const { return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); } -void Head::applyCollision(ModelCollisionInfo& collisionInfo) { +void Head::applyCollision(CollisionInfo& collision) { // HACK: the collision proxies for the FaceModel are bad. As a temporary workaround // we collide against a hard coded collision proxy. // TODO: get a better collision proxy here. @@ -229,7 +229,7 @@ void Head::applyCollision(ModelCollisionInfo& collisionInfo) { // collide the contactPoint against the collision proxy to obtain a new penetration // NOTE: that penetration is in opposite direction (points the way out for the point, not the sphere) glm::vec3 penetration; - if (findPointSpherePenetration(collisionInfo._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) { + if (findPointSpherePenetration(collision._contactPoint, HEAD_CENTER, HEAD_RADIUS, penetration)) { // compute lean angles Avatar* owningAvatar = static_cast(_owningAvatar); glm::quat bodyRotation = owningAvatar->getOrientation(); @@ -239,8 +239,8 @@ void Head::applyCollision(ModelCollisionInfo& collisionInfo) { glm::vec3 zAxis = bodyRotation * glm::vec3(0.f, 0.f, 1.f); float neckLength = glm::length(_position - neckPosition); if (neckLength > 0.f) { - float forward = glm::dot(collisionInfo._penetration, zAxis) / neckLength; - float sideways = - glm::dot(collisionInfo._penetration, xAxis) / neckLength; + float forward = glm::dot(collision._penetration, zAxis) / neckLength; + float sideways = - glm::dot(collision._penetration, xAxis) / neckLength; addLean(sideways, forward); } } diff --git a/interface/src/avatar/Head.h b/interface/src/avatar/Head.h index eae8223903..c88e654d95 100644 --- a/interface/src/avatar/Head.h +++ b/interface/src/avatar/Head.h @@ -80,7 +80,7 @@ public: float getTweakedYaw() const; float getTweakedRoll() const; - void applyCollision(ModelCollisionInfo& collisionInfo); + void applyCollision(CollisionInfo& collisionInfo); private: // disallow copies of the Head, copy of owning Avatar is disallowed too diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 85c0c2b35a..6673abe88b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -955,7 +955,7 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { // no need to compute a bunch of stuff if we have one or fewer avatars return; } - float myBoundingRadius = 0.5f * getHeight(); + float myBoundingRadius = getBoundingRadius(); // HACK: body-body collision uses two coaxial capsules with axes parallel to y-axis // TODO: make the collision work without assuming avatar orientation @@ -975,7 +975,7 @@ void MyAvatar::updateCollisionWithAvatars(float deltaTime) { if (_distanceToNearestAvatar > distance) { _distanceToNearestAvatar = distance; } - float theirBoundingRadius = 0.5f * avatar->getHeight(); + float theirBoundingRadius = avatar->getBoundingRadius(); if (distance < myBoundingRadius + theirBoundingRadius) { Extents theirStaticExtents = _skeletonModel.getStaticExtents(); glm::vec3 staticScale = theirStaticExtents.maximum - theirStaticExtents.minimum; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 7686b1ac7f..97cee8a05e 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -446,9 +446,9 @@ bool Model::findRayIntersection(const glm::vec3& origin, const glm::vec3& direct return false; } -bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetratorRadius, - ModelCollisionInfo& collisionInfo, float boneScale, int skipIndex) const { - int jointIndex = -1; +bool Model::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, + CollisionList& collisions, float boneScale, int skipIndex) const { + bool collided = false; const glm::vec3 relativeCenter = penetratorCenter - _translation; const FBXGeometry& geometry = _geometry->getFBXGeometry(); glm::vec3 totalPenetration; @@ -477,22 +477,22 @@ bool Model::findSphereCollision(const glm::vec3& penetratorCenter, float penetra if (findSphereCapsuleConePenetration(relativeCenter, penetratorRadius, start, end, startRadius, endRadius, bonePenetration)) { totalPenetration = addPenetrations(totalPenetration, bonePenetration); - // BUG: we currently overwrite the jointIndex with the last one found - // which can cause incorrect collisions when colliding against more than - // one joint. - // TODO: fix this. - jointIndex = i; + CollisionInfo* collision = collisions.getNewCollision(); + if (collision) { + collision->_type = MODEL_COLLISION; + collision->_data = (void*)(this); + collision->_flags = i; + collision->_contactPoint = penetratorCenter + penetratorRadius * glm::normalize(totalPenetration); + collision->_penetration = totalPenetration; + collided = true; + } else { + // collisions are full, so we might as well break + break; + } } outerContinue: ; } - if (jointIndex != -1) { - // don't store collisionInfo._model at this stage, let the outer context do that - collisionInfo._penetration = totalPenetration; - collisionInfo._jointIndex = jointIndex; - collisionInfo._contactPoint = penetratorCenter + penetratorRadius * glm::normalize(totalPenetration); - return true; - } - return false; + return collided; } void Model::updateJointState(int index) { @@ -725,24 +725,30 @@ void Model::renderCollisionProxies(float alpha) { glPopMatrix(); } -bool Model::collisionHitsMoveableJoint(ModelCollisionInfo& collision) const { - // the joint is pokable by a collision if it exists and is free to move - const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._jointIndex]; - if (joint.parentIndex == -1 || _jointStates.isEmpty()) { - return false; +bool Model::collisionHitsMoveableJoint(CollisionInfo& collision) const { + if (collision._type == MODEL_COLLISION) { + // the joint is pokable by a collision if it exists and is free to move + const FBXJoint& joint = _geometry->getFBXGeometry().joints[collision._flags]; + if (joint.parentIndex == -1 || _jointStates.isEmpty()) { + return false; + } + // an empty freeLineage means the joint can't move + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + int jointIndex = collision._flags; + const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; + return !freeLineage.isEmpty(); } - // an empty freeLineage means the joint can't move - const FBXGeometry& geometry = _geometry->getFBXGeometry(); - const QVector& freeLineage = geometry.joints.at(collision._jointIndex).freeLineage; - return !freeLineage.isEmpty(); + return false; } -void Model::applyCollision(ModelCollisionInfo& collision) { - // This needs work. At the moment it can wiggle joints that are free to move (such as arms) - // but unmovable joints (such as torso) cannot be influenced at all. +void Model::applyCollision(CollisionInfo& collision) { + if (collision._type != MODEL_COLLISION) { + return; + } + glm::vec3 jointPosition(0.f); - if (getJointPosition(collision._jointIndex, jointPosition)) { - int jointIndex = collision._jointIndex; + int jointIndex = collision._flags; + if (getJointPosition(jointIndex, jointPosition)) { const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex]; if (joint.parentIndex != -1) { // compute the approximate distance (travel) that the joint needs to move diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index bab25bed7a..6f61d6e204 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -17,16 +17,6 @@ #include "ProgramObject.h" #include "TextureCache.h" -class Model; - -// TODO: Andrew to move this into its own file -class ModelCollisionInfo : public CollisionInfo { -public: - ModelCollisionInfo() : CollisionInfo(), _model(NULL), _jointIndex(-1) {} - Model* _model; - int _jointIndex; -}; - /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject { Q_OBJECT @@ -162,17 +152,18 @@ public: bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const; - bool findSphereCollision(const glm::vec3& penetratorCenter, float penetratorRadius, - ModelCollisionInfo& collision, float boneScale = 1.0f, int skipIndex = -1) const; + bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, + CollisionList& collisions, float boneScale = 1.0f, int skipIndex = -1) const; void renderCollisionProxies(float alpha); + /// \param collision details about the collisions /// \return true if the collision is against a moveable joint - bool collisionHitsMoveableJoint(ModelCollisionInfo& collision) const; + bool collisionHitsMoveableJoint(CollisionInfo& collision) const; - /// \param collisionInfo info about the collision - /// Use the collisionInfo to affect the model - void applyCollision(ModelCollisionInfo& collisionInfo); + /// \param collision details about the collision + /// Use the collision to affect the model + void applyCollision(CollisionInfo& collision); protected: diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 08604a95f1..be171ed145 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -135,14 +135,10 @@ public: virtual const glm::vec3& getVelocity() const { return vec3Zero; } - virtual bool findSphereCollisionWithHands(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) { + virtual bool findParticleCollisions(const glm::vec3& particleCenter, float particleRadius, CollisionList& collisions) { return false; } - virtual bool findSphereCollisionWithSkeleton(const glm::vec3& sphereCenter, float sphereRadius, CollisionInfo& collision) { - return false; - } - bool hasIdentityChangedAfterParsing(const QByteArray& packet); QByteArray identityByteArray(); @@ -150,6 +146,8 @@ public: const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); + + virtual float getBoundingRadius() const { return 1.f; } protected: glm::vec3 _position; diff --git a/libraries/particles/src/ParticleCollisionSystem.cpp b/libraries/particles/src/ParticleCollisionSystem.cpp index bb0260c2bf..2d272a8f1f 100644 --- a/libraries/particles/src/ParticleCollisionSystem.cpp +++ b/libraries/particles/src/ParticleCollisionSystem.cpp @@ -19,9 +19,11 @@ #include "ParticleEditPacketSender.h" #include "ParticleTree.h" +const int MAX_COLLISIONS_PER_PARTICLE = 16; + ParticleCollisionSystem::ParticleCollisionSystem(ParticleEditPacketSender* packetSender, ParticleTree* particles, VoxelTree* voxels, AbstractAudioInterface* audio, - AvatarHashMap* avatars) { + AvatarHashMap* avatars) : _collisions(MAX_COLLISIONS_PER_PARTICLE) { init(packetSender, particles, voxels, audio, avatars); } @@ -181,39 +183,53 @@ void ParticleCollisionSystem::updateCollisionWithAvatars(Particle* particle) { const float COLLISION_FREQUENCY = 0.5f; glm::vec3 penetration; + _collisions.clear(); foreach (const AvatarSharedPointer& avatarPointer, _avatars->getAvatarHash()) { AvatarData* avatar = avatarPointer.data(); - CollisionInfo collisionInfo; - collisionInfo._damping = DAMPING; - collisionInfo._elasticity = ELASTICITY; - if (avatar->findSphereCollisionWithHands(center, radius, collisionInfo)) { - // TODO: Andrew to resurrect particles-vs-avatar body collisions - //avatar->findSphereCollisionWithSkeleton(center, radius, collisionInfo)) { - collisionInfo._addedVelocity /= (float)(TREE_SCALE); - glm::vec3 relativeVelocity = collisionInfo._addedVelocity - particle->getVelocity(); - if (glm::dot(relativeVelocity, collisionInfo._penetration) < 0.f) { - // only collide when particle and collision point are moving toward each other - // (doing this prevents some "collision snagging" when particle penetrates the object) - // HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against the avatar. - // NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle. - // TODO: make this less hacky when we have more per-collision details - float elasticity = ELASTICITY; - float attenuationFactor = glm::length(collisionInfo._addedVelocity) / HALTING_SPEED; - float damping = DAMPING; - if (attenuationFactor < 1.f) { - collisionInfo._addedVelocity *= attenuationFactor; - elasticity *= attenuationFactor; - // NOTE: the math below keeps the damping piecewise continuous, - // while ramping it up to 1.0 when attenuationFactor = 0 - damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING); + // use a very generous bounding radius since the arms can stretch + float totalRadius = 2.f * avatar->getBoundingRadius() + radius; + glm::vec3 relativePosition = center - avatar->getPosition(); + if (glm::dot(relativePosition, relativePosition) > (totalRadius * totalRadius)) { + continue; + } + + if (avatar->findParticleCollisions(center, radius, _collisions)) { + int numCollisions = _collisions.size(); + for (int i = 0; i < numCollisions; ++i) { + CollisionInfo* collision = _collisions.getCollision(i); + collision->_damping = DAMPING; + collision->_elasticity = ELASTICITY; + + collision->_addedVelocity /= (float)(TREE_SCALE); + glm::vec3 relativeVelocity = collision->_addedVelocity - particle->getVelocity(); + + if (glm::dot(relativeVelocity, collision->_penetration) <= 0.f) { + // only collide when particle and collision point are moving toward each other + // (doing this prevents some "collision snagging" when particle penetrates the object) + + // HACK BEGIN: to allow paddle hands to "hold" particles we attenuate soft collisions against them. + if (collision->_type == PADDLE_HAND_COLLISION) { + // NOTE: the physics are wrong (particles cannot roll) but it IS possible to catch a slow moving particle. + // TODO: make this less hacky when we have more per-collision details + float elasticity = ELASTICITY; + float attenuationFactor = glm::length(collision->_addedVelocity) / HALTING_SPEED; + float damping = DAMPING; + if (attenuationFactor < 1.f) { + collision->_addedVelocity *= attenuationFactor; + elasticity *= attenuationFactor; + // NOTE: the math below keeps the damping piecewise continuous, + // while ramping it up to 1 when attenuationFactor = 0 + damping = DAMPING + (1.f - attenuationFactor) * (1.f - DAMPING); + } + } + // HACK END + + updateCollisionSound(particle, collision->_penetration, COLLISION_FREQUENCY); + collision->_penetration /= (float)(TREE_SCALE); + particle->applyHardCollision(*collision); + queueParticlePropertiesUpdate(particle); } - // HACK END - - updateCollisionSound(particle, collisionInfo._penetration, COLLISION_FREQUENCY); - collisionInfo._penetration /= (float)(TREE_SCALE); - particle->applyHardCollision(collisionInfo); - queueParticlePropertiesUpdate(particle); } } } diff --git a/libraries/particles/src/ParticleCollisionSystem.h b/libraries/particles/src/ParticleCollisionSystem.h index c525d3ddfc..3bff843743 100644 --- a/libraries/particles/src/ParticleCollisionSystem.h +++ b/libraries/particles/src/ParticleCollisionSystem.h @@ -66,6 +66,7 @@ private: VoxelTree* _voxels; AbstractAudioInterface* _audio; AvatarHashMap* _avatars; + CollisionList _collisions; }; #endif /* defined(__hifi__ParticleCollisionSystem__) */ diff --git a/libraries/shared/src/CollisionInfo.cpp b/libraries/shared/src/CollisionInfo.cpp new file mode 100644 index 0000000000..5d74d591c6 --- /dev/null +++ b/libraries/shared/src/CollisionInfo.cpp @@ -0,0 +1,42 @@ +// +// CollisionInfo.cpp +// hifi +// +// Created by Andrew Meadows on 2014.02.14 +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#include "CollisionInfo.h" + +CollisionList::CollisionList(int maxSize) : + _maxSize(maxSize), + _size(0) { + _collisions.resize(_maxSize); +} + +CollisionInfo* CollisionList::getNewCollision() { + // return pointer to existing CollisionInfo, or NULL of list is full + return (_size < _maxSize) ? &(_collisions[++_size]) : NULL; +} + +CollisionInfo* CollisionList::getCollision(int index) { + return (index > -1 && index < _size) ? &(_collisions[index]) : NULL; +} + +void CollisionList::clear() { + for (int i = 0; i < _size; ++i) { + // we only clear the important stuff + CollisionInfo& collision = _collisions[i]; + collision._type = BASE_COLLISION; + collision._data = NULL; // CollisionInfo does not own whatever this points to. + collision._flags = 0; + // we rely on the consumer to properly overwrite these fields when the collision is "created" + //collision._damping; + //collision._elasticity; + //collision._contactPoint; + //collision._penetration; + //collision._addedVelocity; + } + _size = 0; +} + diff --git a/libraries/shared/src/CollisionInfo.h b/libraries/shared/src/CollisionInfo.h index 1fa95cd83a..acd127435c 100644 --- a/libraries/shared/src/CollisionInfo.h +++ b/libraries/shared/src/CollisionInfo.h @@ -11,15 +11,42 @@ #include -const uint32_t COLLISION_GROUP_ENVIRONMENT = 1U << 0; -const uint32_t COLLISION_GROUP_AVATARS = 1U << 1; -const uint32_t COLLISION_GROUP_VOXELS = 1U << 2; -const uint32_t COLLISION_GROUP_PARTICLES = 1U << 3; +#include + +enum CollisionType { + BASE_COLLISION = 0, + PADDLE_HAND_COLLISION, + MODEL_COLLISION, +}; + +const quint32 COLLISION_GROUP_ENVIRONMENT = 1U << 0; +const quint32 COLLISION_GROUP_AVATARS = 1U << 1; +const quint32 COLLISION_GROUP_VOXELS = 1U << 2; +const quint32 COLLISION_GROUP_PARTICLES = 1U << 3; + +// CollisionInfo contains details about the collision between two things: BodyA and BodyB. +// The assumption is that the context that analyzes the collision knows about BodyA but +// does not necessarily know about BodyB. Hence the data storred in the CollisionInfo +// is expected to be relative to BodyA (for example the penetration points from A into B). class CollisionInfo { public: CollisionInfo() - : _damping(0.f), + : _type(0), + _data(NULL), + _flags(0), + _damping(0.f), + _elasticity(1.f), + _contactPoint(0.f), + _penetration(0.f), + _addedVelocity(0.f) { + } + + CollisionInfo(qint32 type) + : _type(type), + _data(NULL), + _flags(0), + _damping(0.f), _elasticity(1.f), _contactPoint(0.f), _penetration(0.f), @@ -28,13 +55,40 @@ public: ~CollisionInfo() {} - //glm::vec3 _normal; - float _damping; - float _elasticity; - glm::vec3 _contactPoint; // world-frame point on bodyA that is deepest into bodyB - glm::vec3 _penetration; // depth that bodyA penetrates into bodyB - glm::vec3 _addedVelocity; + qint32 _type; // type of Collision (will determine what is supposed to be in _data and _flags) + void* _data; // pointer to user supplied data + quint32 _flags; // 32 bits for whatever + + float _damping; // range [0,1] of friction coeficient + float _elasticity; // range [0,1] of energy conservation + glm::vec3 _contactPoint; // world-frame point on BodyA that is deepest into BodyB + glm::vec3 _penetration; // depth that BodyA penetrates into BodyB + glm::vec3 _addedVelocity; // velocity of BodyB }; +// CollisionList is intended to be a recycled container. Fill the CollisionInfo's, +// use them, and then clear them for the next frame or context. + +class CollisionList { +public: + CollisionList(int maxSize); + + /// \return pointer to next collision. NULL if list is full. + CollisionInfo* getNewCollision(); + + /// \return pointer to collision by index. NULL if index out of bounds. + CollisionInfo* getCollision(int index); + + /// \return number of valid collisions + int size() const { return _size; } + + /// Clear valid collisions. + void clear(); + +private: + int _maxSize; + int _size; + QVector _collisions; +}; #endif /* defined(__hifi__CollisionInfo__) */ From fcd44f58176580db7506f5229ee3c40b819ac0e9 Mon Sep 17 00:00:00 2001 From: gaitat Date: Sat, 15 Feb 2014 08:16:05 -0500 Subject: [PATCH 46/70] Fix for Worklist Job #19503 --- examples/audioBall.js | 11 ++++++----- examples/audioBallLifetime.js | 9 +++++---- interface/src/Audio.h | 3 +-- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/avatars/src/AvatarData.h | 9 ++++++--- libraries/avatars/src/HeadData.h | 6 +++--- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/examples/audioBall.js b/examples/audioBall.js index b2761a3dd3..cfa08ef3a2 100644 --- a/examples/audioBall.js +++ b/examples/audioBall.js @@ -23,7 +23,7 @@ function vsMult(s, v) { } var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); -var FACTOR = 0.20; +var FACTOR = 0.75; var countParticles = 0; // the first time around we want to create the particle and thereafter to modify it. var particleID; @@ -42,14 +42,15 @@ function updateParticle() options.volume = 0.75; Audio.playSound(sound, options); - var audioCardAverageLoudness = MyAvatar.audioCardAverageLoudness * FACTOR; + var audioAverageLoudness = MyAvatar.audioAverageLoudness * FACTOR; + //print ("Audio Loudness = " + MyAvatar.audioLoudness + " -- Audio Average Loudness = " + MyAvatar.audioAverageLoudness); if (countParticles < 1) { var particleProperies = { position: particlePosition // the particle should stay in front of the user's avatar as he moves , color: { red: 0, green: 255, blue: 0 } - , radius: audioCardAverageLoudness + , radius: audioAverageLoudness , velocity: { x: 0.0, y: 0.0, z: 0.0 } , gravity: { x: 0.0, y: 0.0, z: 0.0 } , damping: 0.0 @@ -63,8 +64,8 @@ function updateParticle() // animates the particles radius and color in response to the changing audio intensity var newProperties = { position: particlePosition // the particle should stay in front of the user's avatar as he moves - , color: { red: 0, green: 255 * audioCardAverageLoudness, blue: 0 } - , radius: audioCardAverageLoudness + , color: { red: 0, green: 255 * audioAverageLoudness, blue: 0 } + , radius: audioAverageLoudness }; Particles.editParticle (particleID, newProperties); diff --git a/examples/audioBallLifetime.js b/examples/audioBallLifetime.js index 7df4a0cdad..da15ec7421 100644 --- a/examples/audioBallLifetime.js +++ b/examples/audioBallLifetime.js @@ -23,7 +23,7 @@ function vsMult(s, v) { } var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); -var FACTOR = 0.20; +var FACTOR = 0.75; function addParticle() { @@ -39,13 +39,14 @@ function addParticle() options.volume = 0.25; Audio.playSound(sound, options); - var audioCardAverageLoudness = MyAvatar.audioCardAverageLoudness * FACTOR; + var audioAverageLoudness = MyAvatar.audioAverageLoudness * FACTOR; + //print ("Audio Loudness = " + MyAvatar.audioLoudness + " -- Audio Average Loudness = " + MyAvatar.audioAverageLoudness); // animates the particles radius and color in response to the changing audio intensity var particleProperies = { position: particlePosition // the particle should stay in front of the user's avatar as he moves - , color: { red: 0, green: 255 * audioCardAverageLoudness, blue: 0 } - , radius: audioCardAverageLoudness + , color: { red: 0, green: 255 * audioAverageLoudness, blue: 0 } + , radius: audioAverageLoudness , velocity: { x: 0.0, y: 0.0, z: 0.0 } , gravity: { x: 0.0, y: 0.0, z: 0.0 } , damping: 0.0 diff --git a/interface/src/Audio.h b/interface/src/Audio.h index 3b54a02f4e..fd77cfb3b8 100644 --- a/interface/src/Audio.h +++ b/interface/src/Audio.h @@ -46,8 +46,7 @@ public: void render(int screenWidth, int screenHeight); float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.f); } - - float getAudioCardAverageInputLoudness() const { return _averageInputLoudness; } // saki + float getAudioAverageInputLoudness() const { return _lastInputLoudness; } void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 971d93ced8..b38d970011 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -143,7 +143,7 @@ void MyAvatar::update(float deltaTime) { // Get audio loudness data from audio input device Audio *audio = Application::getInstance()->getAudio(); _head.setAudioLoudness(audio->getLastInputLoudness()); - _head.setAudioCardAverageLoudness(audio->getAudioCardAverageInputLoudness()); // saki + _head.setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); if (Menu::getInstance()->isOptionChecked(MenuOption::Gravity)) { setGravity(Application::getInstance()->getEnvironment()->getGravity(getPosition())); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a141929d44..796d60abd9 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -77,7 +77,8 @@ class AvatarData : public NodeData { Q_PROPERTY(glm::quat orientation READ getOrientation WRITE setOrientation) Q_PROPERTY(float headPitch READ getHeadPitch WRITE setHeadPitch) - Q_PROPERTY(float audioCardAverageLoudness READ getAudioCardAverageLoudness WRITE setAudioCardAverageLoudness) // saki + Q_PROPERTY(float audioLoudness READ getAudioLoudness WRITE setAudioLoudness) + Q_PROPERTY(float audioAverageLoudness READ getAudioAverageLoudness WRITE setAudioAverageLoudness) Q_PROPERTY(QUrl faceModelURL READ getFaceModelURL WRITE setFaceModelURL) Q_PROPERTY(QUrl skeletonModelURL READ getSkeletonModelURL WRITE setSkeletonModelURL) @@ -110,8 +111,10 @@ public: void setHeadPitch(float value) { _headData->setPitch(value); }; // access to Head().set/getAverageLoudness - float getAudioCardAverageLoudness() const { return _headData->getAudioCardAverageLoudness(); } // saki - void setAudioCardAverageLoudness(float value) { _headData->setAudioCardAverageLoudness(value); }; // saki + float getAudioLoudness() const { return _headData->getAudioLoudness(); } + void setAudioLoudness(float value) { _headData->setAudioLoudness(value); } + float getAudioAverageLoudness() const { return _headData->getAudioAverageLoudness(); } + void setAudioAverageLoudness(float value) { _headData->setAudioAverageLoudness(value); } // Scale float getTargetScale() const { return _targetScale; } diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index 5c4ac4c067..5b95d84d7b 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -45,8 +45,8 @@ public: float getAudioLoudness() const { return _audioLoudness; } void setAudioLoudness(float audioLoudness) { _audioLoudness = audioLoudness; } - float getAudioCardAverageLoudness() const { return _audioCardAverageLoudness; } // saki - void setAudioCardAverageLoudness(float audioCardAverageLoudness) { _audioCardAverageLoudness = audioCardAverageLoudness; } // saki + float getAudioAverageLoudness() const { return _audioAverageLoudness; } + void setAudioAverageLoudness(float audioAverageLoudness) { _audioAverageLoudness = audioAverageLoudness; } const std::vector& getBlendshapeCoefficients() const { return _blendshapeCoefficients; } @@ -76,7 +76,7 @@ protected: float _rightEyeBlink; float _averageLoudness; float _browAudioLift; - float _audioCardAverageLoudness; // saki + float _audioAverageLoudness; std::vector _blendshapeCoefficients; float _pupilDilation; AvatarData* _owningAvatar; From f6adce255d355d0914c3604553ae8bb77a46b2ef Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 15 Feb 2014 16:26:34 -0800 Subject: [PATCH 47/70] more work on image overlays --- examples/overlaysExample.js | 125 +++++++++++++++++++++++++++++- interface/src/ui/ImageOverlay.cpp | 106 ++++++++++++++++--------- interface/src/ui/ImageOverlay.h | 22 +++--- 3 files changed, 202 insertions(+), 51 deletions(-) diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index d297e2525d..36e88ea43c 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -18,6 +18,61 @@ QRect(170,100,62,40), QRect(0,80,62,40)); */ +var swatch1 = Overlays.addOverlay({ + x: 100, + y: 200, + width: 31, + height: 54, + subImage: { x: 39, y: 0, width: 30, height: 54 }, + imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", + backgroundColor: { red: 255, green: 0, blue: 0}, + alpha: 1 + }); + +var swatch2 = Overlays.addOverlay({ + x: 130, + y: 200, + width: 31, + height: 54, + subImage: { x: 39, y: 55, width: 30, height: 54 }, + imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", + backgroundColor: { red: 0, green: 255, blue: 0}, + alpha: 1.0 + }); + +var swatch3 = Overlays.addOverlay({ + x: 160, + y: 200, + width: 31, + height: 54, + subImage: { x: 39, y: 0, width: 30, height: 54 }, + imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", + backgroundColor: { red: 0, green: 0, blue: 255}, + alpha: 1.0 + }); + +var swatch4 = Overlays.addOverlay({ + x: 190, + y: 200, + width: 31, + height: 54, + subImage: { x: 39, y: 0, width: 30, height: 54 }, + imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", + backgroundColor: { red: 0, green: 255, blue: 255}, + alpha: 1.0 + }); + +var swatch5 = Overlays.addOverlay({ + x: 220, + y: 200, + width: 31, + height: 54, + subImage: { x: 39, y: 0, width: 30, height: 54 }, + imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", + backgroundColor: { red: 255, green: 255, blue: 0}, + alpha: 1.0 + }); + var toolA = Overlays.addOverlay({ x: 100, y: 100, @@ -25,11 +80,77 @@ var toolA = Overlays.addOverlay({ height: 40, subImage: { x: 0, y: 0, width: 62, height: 40 }, imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg", - backgroundColor: { red: 255, green: 0, blue: 255}, - alpha: 1.0 + backgroundColor: { red: 255, green: 255, blue: 255}, + alpha: 0.7, + visible: false }); +var slider = Overlays.addOverlay({ + // alternate form of expressing bounds + bounds: { x: 100, y: 300, width: 158, height: 35}, + imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/slider.png", + backgroundColor: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + +var thumb = Overlays.addOverlay({ + x: 130, + y: 309, + width: 18, + height: 17, + imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/thumb.png", + backgroundColor: { red: 255, green: 255, blue: 255}, + alpha: 1 + }); + + +// 270x 109 +// 109... 109/2 = 54,1,54 +// 270... 39 to 66 = 28 x 9 swatches with +// unselected: +// 38,0,28,54 +// selected: +// 38,55,28,54 +//http://highfidelity-public.s3-us-west-1.amazonaws.com/images/swatches.svg + +// 123456789*123456789*123456789* +// 0123456789*123456789*123456789* + function scriptEnding() { Overlays.deleteOverlay(toolA); + //Overlays.deleteOverlay(toolB); + //Overlays.deleteOverlay(toolC); + Overlays.deleteOverlay(swatch1); + Overlays.deleteOverlay(swatch2); + Overlays.deleteOverlay(swatch3); + Overlays.deleteOverlay(swatch4); + Overlays.deleteOverlay(swatch5); + Overlays.deleteOverlay(thumb); + Overlays.deleteOverlay(slider); } Script.scriptEnding.connect(scriptEnding); + + +var toolAVisible = false; +var minX = 130; +var maxX = minX + 65; +var moveX = (minX + maxX)/2; +var dX = 1; +function update() { + moveX = moveX + dX; + if (moveX > maxX) { + dX = -1; + } + if (moveX < minX) { + dX = 1; + if (toolAVisible) { + toolAVisible = false; + } else { + toolAVisible = true; + } + Overlays.editOverlay(toolA, { visible: toolAVisible } ); + } + Overlays.editOverlay(thumb, { x: moveX } ); + +} +Script.willSendVisualDataCallback.connect(update); diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp index b85f05f557..7024cff533 100644 --- a/interface/src/ui/ImageOverlay.cpp +++ b/interface/src/ui/ImageOverlay.cpp @@ -18,21 +18,16 @@ ImageOverlay::ImageOverlay() : _textureID(0), _alpha(DEFAULT_ALPHA), _backgroundColor(DEFAULT_BACKGROUND_COLOR), + _visible(true), _renderImage(false), - _textureBound(false) + _textureBound(false), + _wantClipFromImage(false) { } void ImageOverlay::init(QGLWidget* parent) { qDebug() << "ImageOverlay::init() parent=" << parent; _parent = parent; - - /* - qDebug() << "ImageOverlay::init()... url=" << url; - _bounds = drawAt; - _fromImage = fromImage; - setImageURL(url); - */ } @@ -43,6 +38,7 @@ ImageOverlay::~ImageOverlay() { } } +// TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? QNetworkAccessManager* manager = new QNetworkAccessManager(this); @@ -51,24 +47,18 @@ void ImageOverlay::setImageURL(const QUrl& url) { } void ImageOverlay::replyFinished(QNetworkReply* reply) { - qDebug() << "ImageOverlay::replyFinished() reply=" << reply; + // replace our byte array with the downloaded data QByteArray rawData = reply->readAll(); _textureImage.loadFromData(rawData); _renderImage = true; - // TODO: handle setting image multiple times, how do we manage releasing the bound texture - qDebug() << "ImageOverlay::replyFinished() about to call _parent->bindTexture(_textureImage)... _parent" << _parent; - - - qDebug() << "ImageOverlay::replyFinished _textureID=" << _textureID - << "_textureImage.width()=" << _textureImage.width() - << "_textureImage.height()=" << _textureImage.height(); } void ImageOverlay::render() { - //qDebug() << "ImageOverlay::render _textureID=" << _textureID << "_bounds=" << _bounds; - + if (!_visible) { + return; // do nothing if we're not visible + } if (_renderImage && !_textureBound) { _textureID = _parent->bindTexture(_textureImage); _textureBound = true; @@ -79,17 +69,25 @@ void ImageOverlay::render() { glBindTexture(GL_TEXTURE_2D, _textureID); } const float MAX_COLOR = 255; - glColor4f((_backgroundColor.red / MAX_COLOR), (_backgroundColor.green / MAX_COLOR), (_backgroundColor.blue / MAX_COLOR), _alpha); + glColor4f(_backgroundColor.red / MAX_COLOR, _backgroundColor.green / MAX_COLOR, _backgroundColor.blue / MAX_COLOR, _alpha); float imageWidth = _textureImage.width(); float imageHeight = _textureImage.height(); - float x = _fromImage.x() / imageWidth; - float y = _fromImage.y() / imageHeight; - float w = _fromImage.width() / imageWidth; // ?? is this what we want? not sure - float h = _fromImage.height() / imageHeight; - - //qDebug() << "ImageOverlay::render x=" << x << "y=" << y << "w="< Date: Sat, 15 Feb 2014 16:31:09 -0800 Subject: [PATCH 48/70] removed getProperties() which was not implemented --- interface/src/ui/ImageOverlay.cpp | 5 ----- interface/src/ui/ImageOverlay.h | 1 - interface/src/ui/Overlays.cpp | 10 ---------- interface/src/ui/Overlays.h | 3 --- 4 files changed, 19 deletions(-) diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp index 7024cff533..6f5a0e0669 100644 --- a/interface/src/ui/ImageOverlay.cpp +++ b/interface/src/ui/ImageOverlay.cpp @@ -114,11 +114,6 @@ void ImageOverlay::render() { } } -// TODO: handle only setting the included values... -QScriptValue ImageOverlay::getProperties() { - return QScriptValue(); -} - // TODO: handle only setting the included values... void ImageOverlay::setProperties(const QScriptValue& properties) { //qDebug() << "ImageOverlay::setProperties()... properties=" << &properties; diff --git a/interface/src/ui/ImageOverlay.h b/interface/src/ui/ImageOverlay.h index 84838954b6..cad38b1071 100644 --- a/interface/src/ui/ImageOverlay.h +++ b/interface/src/ui/ImageOverlay.h @@ -45,7 +45,6 @@ public: const xColor& getBackgroundColor() const { return _backgroundColor; } float getAlpha() const { return _alpha; } const QUrl& getImageURL() const { return _imageURL; } - QScriptValue getProperties(); // setters void setVisible(bool visible) { _visible = visible; } diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index 438e94ad60..d5de389efc 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -17,7 +17,6 @@ Overlays::~Overlays() { } void Overlays::init(QGLWidget* parent) { - qDebug() << "Overlays::init() parent=" << parent; _parent = parent; } @@ -38,14 +37,6 @@ unsigned int Overlays::addOverlay(const QScriptValue& properties) { return thisID; } -QScriptValue Overlays::getOverlayProperties(unsigned int id) { - if (!_imageOverlays.contains(id)) { - return QScriptValue(); - } - ImageOverlay* thisOverlay = _imageOverlays[id]; - return thisOverlay->getProperties(); -} - // TODO: make multi-threaded safe bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { if (!_imageOverlays.contains(id)) { @@ -60,7 +51,6 @@ bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { void Overlays::deleteOverlay(unsigned int id) { if (_imageOverlays.contains(id)) { _imageOverlays.erase(_imageOverlays.find(id)); - } } diff --git a/interface/src/ui/Overlays.h b/interface/src/ui/Overlays.h index 8b9f6eb8be..4e4abe3157 100644 --- a/interface/src/ui/Overlays.h +++ b/interface/src/ui/Overlays.h @@ -38,9 +38,6 @@ public slots: /// adds an overlay with the specific properties unsigned int addOverlay(const QScriptValue& properties); - /// gets the current overlay properties for a specific overlay - QScriptValue getOverlayProperties(unsigned int id); - /// edits an overlay updating only the included properties, will return the identified OverlayID in case of /// successful edit, if the input id is for an unknown overlay this function will have no effect bool editOverlay(unsigned int id, const QScriptValue& properties); From f7f695d145f9e64d57e9adf6cf1f1e9521a99fc3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 15 Feb 2014 17:45:42 -0800 Subject: [PATCH 49/70] add support for detecting clicked on overlays --- examples/overlaysExample.js | 155 +++++++++++++++--------------- interface/src/ui/ImageOverlay.cpp | 9 ++ interface/src/ui/Overlays.cpp | 17 +++- interface/src/ui/Overlays.h | 3 + 4 files changed, 107 insertions(+), 77 deletions(-) diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index 36e88ea43c..c1835f8957 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -9,69 +9,40 @@ // // - /* - _testOverlayA.init(_glWidget, QString("https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg"), - QRect(100,100,62,40), QRect(0,0,62,40)); - xColor red = { 255, 0, 0 }; - _testOverlayA.setBackgroundColor(red); - _testOverlayB.init(_glWidget, QString("https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg"), - QRect(170,100,62,40), QRect(0,80,62,40)); - */ +var swatchColors = new Array(); +swatchColors[0] = { red: 255, green: 0, blue: 0}; +swatchColors[1] = { red: 0, green: 255, blue: 0}; +swatchColors[2] = { red: 0, green: 0, blue: 255}; +swatchColors[3] = { red: 255, green: 255, blue: 0}; +swatchColors[4] = { red: 255, green: 0, blue: 255}; +swatchColors[5] = { red: 0, green: 255, blue: 255}; +swatchColors[6] = { red: 128, green: 128, blue: 128}; +swatchColors[7] = { red: 128, green: 0, blue: 0}; +swatchColors[8] = { red: 0, green: 240, blue: 240}; -var swatch1 = Overlays.addOverlay({ - x: 100, +var swatches = new Array(); +var numberOfSwatches = 9; +var swatchesX = 100; +var swatchesY = 200; +var selectedSwatch = 0; +for (s = 0; s < numberOfSwatches; s++) { + var imageFromX = 12 + (s * 27); + var imageFromY = 0; + if (s == selectedSwatch) { + imageFromY = 55; + } + + swatches[s] = Overlays.addOverlay({ + x: 100 + (30 * s), y: 200, width: 31, height: 54, - subImage: { x: 39, y: 0, width: 30, height: 54 }, + subImage: { x: imageFromX, y: imageFromY, width: 30, height: 54 }, imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", - backgroundColor: { red: 255, green: 0, blue: 0}, + backgroundColor: swatchColors[s], alpha: 1 }); - -var swatch2 = Overlays.addOverlay({ - x: 130, - y: 200, - width: 31, - height: 54, - subImage: { x: 39, y: 55, width: 30, height: 54 }, - imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", - backgroundColor: { red: 0, green: 255, blue: 0}, - alpha: 1.0 - }); - -var swatch3 = Overlays.addOverlay({ - x: 160, - y: 200, - width: 31, - height: 54, - subImage: { x: 39, y: 0, width: 30, height: 54 }, - imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", - backgroundColor: { red: 0, green: 0, blue: 255}, - alpha: 1.0 - }); - -var swatch4 = Overlays.addOverlay({ - x: 190, - y: 200, - width: 31, - height: 54, - subImage: { x: 39, y: 0, width: 30, height: 54 }, - imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", - backgroundColor: { red: 0, green: 255, blue: 255}, - alpha: 1.0 - }); - -var swatch5 = Overlays.addOverlay({ - x: 220, - y: 200, - width: 31, - height: 54, - subImage: { x: 39, y: 0, width: 30, height: 54 }, - imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", - backgroundColor: { red: 255, green: 255, blue: 0}, - alpha: 1.0 - }); +} var toolA = Overlays.addOverlay({ x: 100, @@ -93,8 +64,11 @@ var slider = Overlays.addOverlay({ alpha: 1 }); +var minThumbX = 130; +var maxThumbX = minThumbX + 65; +var thumbX = (minThumbX + maxThumbX) / 2; var thumb = Overlays.addOverlay({ - x: 130, + x: thumbX, y: 309, width: 18, height: 17, @@ -118,13 +92,9 @@ var thumb = Overlays.addOverlay({ function scriptEnding() { Overlays.deleteOverlay(toolA); - //Overlays.deleteOverlay(toolB); - //Overlays.deleteOverlay(toolC); - Overlays.deleteOverlay(swatch1); - Overlays.deleteOverlay(swatch2); - Overlays.deleteOverlay(swatch3); - Overlays.deleteOverlay(swatch4); - Overlays.deleteOverlay(swatch5); + for (s = 0; s < numberOfSwatches; s++) { + Overlays.deleteOverlay(swatches[s]); + } Overlays.deleteOverlay(thumb); Overlays.deleteOverlay(slider); } @@ -132,17 +102,10 @@ Script.scriptEnding.connect(scriptEnding); var toolAVisible = false; -var minX = 130; -var maxX = minX + 65; -var moveX = (minX + maxX)/2; -var dX = 1; +var count = 0; function update() { - moveX = moveX + dX; - if (moveX > maxX) { - dX = -1; - } - if (moveX < minX) { - dX = 1; + count++; + if (count % 60 == 0) { if (toolAVisible) { toolAVisible = false; } else { @@ -150,7 +113,47 @@ function update() { } Overlays.editOverlay(toolA, { visible: toolAVisible } ); } - Overlays.editOverlay(thumb, { x: moveX } ); - } Script.willSendVisualDataCallback.connect(update); + + +var movingSlider = false; +var thumbClickOffsetX = 0; +function mouseMoveEvent(event) { + if (movingSlider) { + newThumbX = event.x - thumbClickOffsetX; + if (newThumbX < minThumbX) { + newThumbX = minThumbX; + } + if (newThumbX > maxThumbX) { + newThumbX = maxThumbX; + } + Overlays.editOverlay(thumb, { x: newThumbX } ); + } +} +function mousePressEvent(event) { + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + if (clickedOverlay == thumb) { + movingSlider = true; + thumbClickOffsetX = event.x - thumbX; + } else { + for (s = 0; s < numberOfSwatches; s++) { + if (clickedOverlay == swatches[s]) { + Overlays.editOverlay(swatches[selectedSwatch], { subImage: { y: 0 } } ); + Overlays.editOverlay(swatches[s], { subImage: { y: 55 } } ); + selectedSwatch = s; + } + } + } +} + +function mouseReleaseEvent(event) { + if (movingSlider) { + movingSlider = false; + } +} + +Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mousePressEvent.connect(mousePressEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); + diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp index 6f5a0e0669..5849fdae7d 100644 --- a/interface/src/ui/ImageOverlay.cpp +++ b/interface/src/ui/ImageOverlay.cpp @@ -154,18 +154,27 @@ void ImageOverlay::setProperties(const QScriptValue& properties) { } QScriptValue subImageBounds = properties.property("subImage"); if (subImageBounds.isValid()) { + QRect oldSubImageRect = _fromImage; QRect subImageRect = _fromImage; if (subImageBounds.property("x").isValid()) { subImageRect.setX(subImageBounds.property("x").toVariant().toInt()); + } else { + subImageRect.setX(oldSubImageRect.x()); } if (subImageBounds.property("y").isValid()) { subImageRect.setY(subImageBounds.property("y").toVariant().toInt()); + } else { + subImageRect.setY(oldSubImageRect.y()); } if (subImageBounds.property("width").isValid()) { subImageRect.setWidth(subImageBounds.property("width").toVariant().toInt()); + } else { + subImageRect.setWidth(oldSubImageRect.width()); } if (subImageBounds.property("height").isValid()) { subImageRect.setHeight(subImageBounds.property("height").toVariant().toInt()); + } else { + subImageRect.setHeight(oldSubImageRect.height()); } setClipFromSource(subImageRect); } diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index d5de389efc..fef2e94a85 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -8,7 +8,7 @@ #include "Overlays.h" -unsigned int Overlays::_nextOverlayID = 0; +unsigned int Overlays::_nextOverlayID = 1; Overlays::Overlays() { } @@ -54,3 +54,18 @@ void Overlays::deleteOverlay(unsigned int id) { } } +unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { + QMapIterator i(_imageOverlays); + i.toBack(); + while (i.hasPrevious()) { + i.previous(); + unsigned int thisID = i.key(); + ImageOverlay* thisOverlay = i.value(); + if (thisOverlay->getBounds().contains(point.x, point.y, false)) { + return thisID; + } + } + return 0; // not found +} + + diff --git a/interface/src/ui/Overlays.h b/interface/src/ui/Overlays.h index 4e4abe3157..a1ced3979d 100644 --- a/interface/src/ui/Overlays.h +++ b/interface/src/ui/Overlays.h @@ -45,6 +45,9 @@ public slots: /// deletes a particle void deleteOverlay(unsigned int id); + /// returns the top most overlay at the screen point, or 0 if not overlay at that point + unsigned int getOverlayAtPoint(const glm::vec2& point); + private: QMap _imageOverlays; static unsigned int _nextOverlayID; From 37bb85fca626311d347267985ff0106278fff71a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 15 Feb 2014 19:15:30 -0800 Subject: [PATCH 50/70] tweaks to overlays --- examples/overlaysExample.js | 8 ++++---- interface/src/ui/ImageOverlay.cpp | 1 - interface/src/ui/Overlays.cpp | 18 +++++++++++------- interface/src/ui/Overlays.h | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index c1835f8957..778de0421e 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -32,7 +32,7 @@ for (s = 0; s < numberOfSwatches; s++) { imageFromY = 55; } - swatches[s] = Overlays.addOverlay({ + swatches[s] = Overlays.addOverlay("image", { x: 100 + (30 * s), y: 200, width: 31, @@ -44,7 +44,7 @@ for (s = 0; s < numberOfSwatches; s++) { }); } -var toolA = Overlays.addOverlay({ +var toolA = Overlays.addOverlay("image", { x: 100, y: 100, width: 62, @@ -56,7 +56,7 @@ var toolA = Overlays.addOverlay({ visible: false }); -var slider = Overlays.addOverlay({ +var slider = Overlays.addOverlay("image", { // alternate form of expressing bounds bounds: { x: 100, y: 300, width: 158, height: 35}, imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/slider.png", @@ -67,7 +67,7 @@ var slider = Overlays.addOverlay({ var minThumbX = 130; var maxThumbX = minThumbX + 65; var thumbX = (minThumbX + maxThumbX) / 2; -var thumb = Overlays.addOverlay({ +var thumb = Overlays.addOverlay("image", { x: thumbX, y: 309, width: 18, diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp index 5849fdae7d..5d7d7efcc0 100644 --- a/interface/src/ui/ImageOverlay.cpp +++ b/interface/src/ui/ImageOverlay.cpp @@ -202,7 +202,6 @@ void ImageOverlay::setProperties(const QScriptValue& properties) { if (properties.property("visible").isValid()) { setVisible(properties.property("visible").toVariant().toBool()); - qDebug() << "setting visible to " << getVisible(); } } diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index fef2e94a85..5c19dc5529 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -27,13 +27,17 @@ void Overlays::render() { } // TODO: make multi-threaded safe -unsigned int Overlays::addOverlay(const QScriptValue& properties) { - unsigned int thisID = _nextOverlayID; - _nextOverlayID++; - ImageOverlay* thisOverlay = new ImageOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - _imageOverlays[thisID] = thisOverlay; +unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { + unsigned int thisID = 0; + + if (type == "image") { + thisID = _nextOverlayID; + _nextOverlayID++; + ImageOverlay* thisOverlay = new ImageOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + _imageOverlays[thisID] = thisOverlay; + } return thisID; } diff --git a/interface/src/ui/Overlays.h b/interface/src/ui/Overlays.h index a1ced3979d..ed686dc024 100644 --- a/interface/src/ui/Overlays.h +++ b/interface/src/ui/Overlays.h @@ -36,7 +36,7 @@ public: public slots: /// adds an overlay with the specific properties - unsigned int addOverlay(const QScriptValue& properties); + unsigned int addOverlay(const QString& type, const QScriptValue& properties); /// edits an overlay updating only the included properties, will return the identified OverlayID in case of /// successful edit, if the input id is for an unknown overlay this function will have no effect From 0a9f9a7c7a77ad151aca953ccc380281105cbede Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 15 Feb 2014 19:20:08 -0800 Subject: [PATCH 51/70] fix windows build --- interface/src/ui/ImageOverlay.h | 5 +++-- interface/src/ui/Overlays.h | 14 -------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/interface/src/ui/ImageOverlay.h b/interface/src/ui/ImageOverlay.h index cad38b1071..de2dcb7693 100644 --- a/interface/src/ui/ImageOverlay.h +++ b/interface/src/ui/ImageOverlay.h @@ -8,6 +8,9 @@ #ifndef __interface__ImageOverlay__ #define __interface__ImageOverlay__ +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + #include #include #include @@ -19,8 +22,6 @@ #include -#include "InterfaceConfig.h" - const xColor DEFAULT_BACKGROUND_COLOR = { 255, 255, 255 }; const float DEFAULT_ALPHA = 0.7f; diff --git a/interface/src/ui/Overlays.h b/interface/src/ui/Overlays.h index ed686dc024..84e4338a72 100644 --- a/interface/src/ui/Overlays.h +++ b/interface/src/ui/Overlays.h @@ -8,20 +8,6 @@ #ifndef __interface__Overlays__ #define __interface__Overlays__ -/** -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "InterfaceConfig.h" -**/ - #include #include "ImageOverlay.h" From e30cf33a03885e902fc7d3dfd52a7cda5baa101e Mon Sep 17 00:00:00 2001 From: gaitat Date: Sat, 15 Feb 2014 23:04:58 -0500 Subject: [PATCH 52/70] Fix for Worklist Job #19503 Formatting errors --- examples/audioBall.js | 27 ++++++--------------------- examples/audioBallLifetime.js | 17 ++--------------- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/avatars/src/HeadData.h | 2 +- 4 files changed, 10 insertions(+), 38 deletions(-) diff --git a/examples/audioBall.js b/examples/audioBall.js index cfa08ef3a2..a0d9423526 100644 --- a/examples/audioBall.js +++ b/examples/audioBall.js @@ -10,31 +10,18 @@ // in response to the audio intensity. // -// add two vectors -function vPlus(a, b) { - var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; - return rval; -} - -// multiply scalar with vector -function vsMult(s, v) { - var rval = { x: s * v.x, y: s * v.y, z: s * v.z }; - return rval; -} - var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); var FACTOR = 0.75; var countParticles = 0; // the first time around we want to create the particle and thereafter to modify it. var particleID; -function updateParticle() -{ +function updateParticle() { // the particle should be placed in front of the user's avatar - var avatarFront = Quat.getFront(MyAvatar.orientation); + var avatarFront = Quat.getFront(MyAvatar.orientation); // move particle three units in front of the avatar - var particlePosition = vPlus(MyAvatar.position, vsMult (3, avatarFront)); + var particlePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(avatarFront, 3)); // play a sound at the location of the particle var options = new AudioInjectionOptions(); @@ -42,11 +29,10 @@ function updateParticle() options.volume = 0.75; Audio.playSound(sound, options); - var audioAverageLoudness = MyAvatar.audioAverageLoudness * FACTOR; + var audioAverageLoudness = MyAvatar.audioAverageLoudness * FACTOR; //print ("Audio Loudness = " + MyAvatar.audioLoudness + " -- Audio Average Loudness = " + MyAvatar.audioAverageLoudness); - if (countParticles < 1) - { + if (countParticles < 1) { var particleProperies = { position: particlePosition // the particle should stay in front of the user's avatar as he moves , color: { red: 0, green: 255, blue: 0 } @@ -59,8 +45,7 @@ function updateParticle() particleID = Particles.addParticle (particleProperies); countParticles++; } - else - { + else { // animates the particles radius and color in response to the changing audio intensity var newProperties = { position: particlePosition // the particle should stay in front of the user's avatar as he moves diff --git a/examples/audioBallLifetime.js b/examples/audioBallLifetime.js index da15ec7421..a7b8a0a749 100644 --- a/examples/audioBallLifetime.js +++ b/examples/audioBallLifetime.js @@ -10,28 +10,15 @@ // in response to the audio intensity. // -// add two vectors -function vPlus(a, b) { - var rval = { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; - return rval; -} - -// multiply scalar with vector -function vsMult(s, v) { - var rval = { x: s * v.x, y: s * v.y, z: s * v.z }; - return rval; -} - var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); var FACTOR = 0.75; -function addParticle() -{ +function addParticle() { // the particle should be placed in front of the user's avatar var avatarFront = Quat.getFront(MyAvatar.orientation); // move particle three units in front of the avatar - var particlePosition = vPlus(MyAvatar.position, vsMult (3, avatarFront)); + var particlePosition = Vec3.sum(MyAvatar.position, Vec3.multiply (avatarFront, 3)); // play a sound at the location of the particle var options = new AudioInjectionOptions(); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b38d970011..a16162112e 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -141,7 +141,7 @@ void MyAvatar::update(float deltaTime) { } // Get audio loudness data from audio input device - Audio *audio = Application::getInstance()->getAudio(); + Audio* audio = Application::getInstance()->getAudio(); _head.setAudioLoudness(audio->getLastInputLoudness()); _head.setAudioAverageLoudness(audio->getAudioAverageInputLoudness()); diff --git a/libraries/avatars/src/HeadData.h b/libraries/avatars/src/HeadData.h index 5b95d84d7b..04d5fe5b46 100644 --- a/libraries/avatars/src/HeadData.h +++ b/libraries/avatars/src/HeadData.h @@ -76,7 +76,7 @@ protected: float _rightEyeBlink; float _averageLoudness; float _browAudioLift; - float _audioAverageLoudness; + float _audioAverageLoudness; std::vector _blendshapeCoefficients; float _pupilDilation; AvatarData* _owningAvatar; From ef11865d2490356158811d879d010a2c9dd5d31b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 15 Feb 2014 21:13:44 -0800 Subject: [PATCH 53/70] implement text overlay support --- examples/overlaysExample.js | 21 ++++++++ interface/src/Util.h | 9 ---- interface/src/ui/ImageOverlay.cpp | 69 +----------------------- interface/src/ui/ImageOverlay.h | 32 ++--------- interface/src/ui/Overlay.cpp | 88 +++++++++++++++++++++++++++++++ interface/src/ui/Overlay.h | 64 ++++++++++++++++++++++ interface/src/ui/Overlays.cpp | 29 ++++++---- interface/src/ui/Overlays.h | 4 +- interface/src/ui/TextOverlay.cpp | 80 ++++++++++++++++++++++++++++ interface/src/ui/TextOverlay.h | 58 ++++++++++++++++++++ interface/src/ui/TextRenderer.cpp | 34 +++++++++--- interface/src/ui/TextRenderer.h | 16 +++++- 12 files changed, 382 insertions(+), 122 deletions(-) create mode 100644 interface/src/ui/Overlay.cpp create mode 100644 interface/src/ui/Overlay.h create mode 100644 interface/src/ui/TextOverlay.cpp create mode 100644 interface/src/ui/TextOverlay.h diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index 778de0421e..79cd65df68 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -44,6 +44,19 @@ for (s = 0; s < numberOfSwatches; s++) { }); } +var text = Overlays.addOverlay("text", { + x: 200, + y: 100, + width: 150, + height: 50, + backgroundColor: { red: 0, green: 0, blue: 0}, + textColor: { red: 255, green: 0, blue: 0}, + topMargin: 4, + leftMargin: 4, + alpha: 0.7, + text: "Here is some text.\nAnd a second line." + }); + var toolA = Overlays.addOverlay("image", { x: 100, y: 100, @@ -97,6 +110,7 @@ function scriptEnding() { } Overlays.deleteOverlay(thumb); Overlays.deleteOverlay(slider); + Overlays.deleteOverlay(text); } Script.scriptEnding.connect(scriptEnding); @@ -132,10 +146,14 @@ function mouseMoveEvent(event) { } } function mousePressEvent(event) { + var clickedText = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); if (clickedOverlay == thumb) { movingSlider = true; thumbClickOffsetX = event.x - thumbX; + } else if (clickedOverlay == text) { + Overlays.editOverlay(text, { text: "you clicked here:\n " + event.x + "," + event.y } ); + clickedText = true; } else { for (s = 0; s < numberOfSwatches; s++) { if (clickedOverlay == swatches[s]) { @@ -145,6 +163,9 @@ function mousePressEvent(event) { } } } + if (!clickedText) { + Overlays.editOverlay(text, { text: "you didn't click here" } ); + } } function mouseReleaseEvent(event) { diff --git a/interface/src/Util.h b/interface/src/Util.h index 09d1fa0484..0c762ccd79 100644 --- a/interface/src/Util.h +++ b/interface/src/Util.h @@ -19,15 +19,6 @@ #include #include -// the standard sans serif font family -#define SANS_FONT_FAMILY "Helvetica" - -// the standard mono font family -#define MONO_FONT_FAMILY "Courier" - -// the Inconsolata font family -#define INCONSOLATA_FONT_FAMILY "Inconsolata" - void eulerToOrthonormals(glm::vec3 * angles, glm::vec3 * fwd, glm::vec3 * left, glm::vec3 * up); float azimuth_to(glm::vec3 head_pos, glm::vec3 source_pos); diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp index 5d7d7efcc0..d36099cffc 100644 --- a/interface/src/ui/ImageOverlay.cpp +++ b/interface/src/ui/ImageOverlay.cpp @@ -14,23 +14,13 @@ #include ImageOverlay::ImageOverlay() : - _parent(NULL), _textureID(0), - _alpha(DEFAULT_ALPHA), - _backgroundColor(DEFAULT_BACKGROUND_COLOR), - _visible(true), _renderImage(false), _textureBound(false), _wantClipFromImage(false) { } -void ImageOverlay::init(QGLWidget* parent) { - qDebug() << "ImageOverlay::init() parent=" << parent; - _parent = parent; -} - - ImageOverlay::~ImageOverlay() { if (_parent && _textureID) { // do we need to call this? @@ -114,44 +104,9 @@ void ImageOverlay::render() { } } -// TODO: handle only setting the included values... void ImageOverlay::setProperties(const QScriptValue& properties) { - //qDebug() << "ImageOverlay::setProperties()... properties=" << &properties; - QScriptValue bounds = properties.property("bounds"); - if (bounds.isValid()) { - QRect boundsRect; - boundsRect.setX(bounds.property("x").toVariant().toInt()); - boundsRect.setY(bounds.property("y").toVariant().toInt()); - boundsRect.setWidth(bounds.property("width").toVariant().toInt()); - boundsRect.setHeight(bounds.property("height").toVariant().toInt()); - setBounds(boundsRect); - } else { - QRect oldBounds = getBounds(); - QRect newBounds = oldBounds; - - if (properties.property("x").isValid()) { - newBounds.setX(properties.property("x").toVariant().toInt()); - } else { - newBounds.setX(oldBounds.x()); - } - if (properties.property("y").isValid()) { - newBounds.setY(properties.property("y").toVariant().toInt()); - } else { - newBounds.setY(oldBounds.y()); - } - if (properties.property("width").isValid()) { - newBounds.setWidth(properties.property("width").toVariant().toInt()); - } else { - newBounds.setWidth(oldBounds.width()); - } - if (properties.property("height").isValid()) { - newBounds.setHeight(properties.property("height").toVariant().toInt()); - } else { - newBounds.setHeight(oldBounds.height()); - } - setBounds(newBounds); - //qDebug() << "set bounds to " << getBounds(); - } + Overlay::setProperties(properties); + QScriptValue subImageBounds = properties.property("subImage"); if (subImageBounds.isValid()) { QRect oldSubImageRect = _fromImage; @@ -183,26 +138,6 @@ void ImageOverlay::setProperties(const QScriptValue& properties) { if (imageURL.isValid()) { setImageURL(imageURL.toVariant().toString()); } - - QScriptValue color = properties.property("backgroundColor"); - if (color.isValid()) { - QScriptValue red = color.property("red"); - QScriptValue green = color.property("green"); - QScriptValue blue = color.property("blue"); - if (red.isValid() && green.isValid() && blue.isValid()) { - _backgroundColor.red = red.toVariant().toInt(); - _backgroundColor.green = green.toVariant().toInt(); - _backgroundColor.blue = blue.toVariant().toInt(); - } - } - - if (properties.property("alpha").isValid()) { - setAlpha(properties.property("alpha").toVariant().toFloat()); - } - - if (properties.property("visible").isValid()) { - setVisible(properties.property("visible").toVariant().toBool()); - } } diff --git a/interface/src/ui/ImageOverlay.h b/interface/src/ui/ImageOverlay.h index de2dcb7693..be7d8cf5d8 100644 --- a/interface/src/ui/ImageOverlay.h +++ b/interface/src/ui/ImageOverlay.h @@ -22,43 +22,24 @@ #include -const xColor DEFAULT_BACKGROUND_COLOR = { 255, 255, 255 }; -const float DEFAULT_ALPHA = 0.7f; +#include "Overlay.h" -class ImageOverlay : QObject { +class ImageOverlay : public Overlay { Q_OBJECT public: ImageOverlay(); ~ImageOverlay(); - void init(QGLWidget* parent); - void render(); + virtual void render(); -//public slots: // getters - bool getVisible() const { return _visible; } - int getX() const { return _bounds.x(); } - int getY() const { return _bounds.y(); } - int getWidth() const { return _bounds.width(); } - int getHeight() const { return _bounds.height(); } - const QRect& getBounds() const { return _bounds; } const QRect& getClipFromSource() const { return _fromImage; } - const xColor& getBackgroundColor() const { return _backgroundColor; } - float getAlpha() const { return _alpha; } const QUrl& getImageURL() const { return _imageURL; } // setters - void setVisible(bool visible) { _visible = visible; } - void setX(int x) { _bounds.setX(x); } - void setY(int y) { _bounds.setY(y); } - void setWidth(int width) { _bounds.setWidth(width); } - void setHeight(int height) { _bounds.setHeight(height); } - void setBounds(const QRect& bounds) { _bounds = bounds; } void setClipFromSource(const QRect& bounds) { _fromImage = bounds; _wantClipFromImage = true; } - void setBackgroundColor(const xColor& color) { _backgroundColor = color; } - void setAlpha(float alpha) { _alpha = alpha; } void setImageURL(const QUrl& url); - void setProperties(const QScriptValue& properties); + virtual void setProperties(const QScriptValue& properties); private slots: void replyFinished(QNetworkReply* reply); // we actually want to hide this... @@ -66,14 +47,9 @@ private slots: private: QUrl _imageURL; - QGLWidget* _parent; QImage _textureImage; GLuint _textureID; - QRect _bounds; // where on the screen to draw QRect _fromImage; // where from in the image to sample - float _alpha; - xColor _backgroundColor; - bool _visible; // should the overlay be drawn at all bool _renderImage; // is there an image associated with this overlay, or is it just a colored rectangle bool _textureBound; // has the texture been bound bool _wantClipFromImage; diff --git a/interface/src/ui/Overlay.cpp b/interface/src/ui/Overlay.cpp new file mode 100644 index 0000000000..fa54844477 --- /dev/null +++ b/interface/src/ui/Overlay.cpp @@ -0,0 +1,88 @@ +// +// Overlay.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + + +#include "Overlay.h" + +#include +#include +#include +#include + +Overlay::Overlay() : + _parent(NULL), + _alpha(DEFAULT_ALPHA), + _backgroundColor(DEFAULT_BACKGROUND_COLOR), + _visible(true) +{ +} + +void Overlay::init(QGLWidget* parent) { + _parent = parent; +} + + +Overlay::~Overlay() { +} + +void Overlay::setProperties(const QScriptValue& properties) { + QScriptValue bounds = properties.property("bounds"); + if (bounds.isValid()) { + QRect boundsRect; + boundsRect.setX(bounds.property("x").toVariant().toInt()); + boundsRect.setY(bounds.property("y").toVariant().toInt()); + boundsRect.setWidth(bounds.property("width").toVariant().toInt()); + boundsRect.setHeight(bounds.property("height").toVariant().toInt()); + setBounds(boundsRect); + } else { + QRect oldBounds = getBounds(); + QRect newBounds = oldBounds; + + if (properties.property("x").isValid()) { + newBounds.setX(properties.property("x").toVariant().toInt()); + } else { + newBounds.setX(oldBounds.x()); + } + if (properties.property("y").isValid()) { + newBounds.setY(properties.property("y").toVariant().toInt()); + } else { + newBounds.setY(oldBounds.y()); + } + if (properties.property("width").isValid()) { + newBounds.setWidth(properties.property("width").toVariant().toInt()); + } else { + newBounds.setWidth(oldBounds.width()); + } + if (properties.property("height").isValid()) { + newBounds.setHeight(properties.property("height").toVariant().toInt()); + } else { + newBounds.setHeight(oldBounds.height()); + } + setBounds(newBounds); + //qDebug() << "set bounds to " << getBounds(); + } + + QScriptValue color = properties.property("backgroundColor"); + if (color.isValid()) { + QScriptValue red = color.property("red"); + QScriptValue green = color.property("green"); + QScriptValue blue = color.property("blue"); + if (red.isValid() && green.isValid() && blue.isValid()) { + _backgroundColor.red = red.toVariant().toInt(); + _backgroundColor.green = green.toVariant().toInt(); + _backgroundColor.blue = blue.toVariant().toInt(); + } + } + + if (properties.property("alpha").isValid()) { + setAlpha(properties.property("alpha").toVariant().toFloat()); + } + + if (properties.property("visible").isValid()) { + setVisible(properties.property("visible").toVariant().toBool()); + } +} diff --git a/interface/src/ui/Overlay.h b/interface/src/ui/Overlay.h new file mode 100644 index 0000000000..ce63fdaba5 --- /dev/null +++ b/interface/src/ui/Overlay.h @@ -0,0 +1,64 @@ +// +// Overlay.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Overlay__ +#define __interface__Overlay__ + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include + +#include // for xColor + +const xColor DEFAULT_BACKGROUND_COLOR = { 255, 255, 255 }; +const float DEFAULT_ALPHA = 0.7f; + +class Overlay : public QObject { + Q_OBJECT + +public: + Overlay(); + ~Overlay(); + void init(QGLWidget* parent); + virtual void render() = 0; + + // getters + bool getVisible() const { return _visible; } + int getX() const { return _bounds.x(); } + int getY() const { return _bounds.y(); } + int getWidth() const { return _bounds.width(); } + int getHeight() const { return _bounds.height(); } + const QRect& getBounds() const { return _bounds; } + const xColor& getBackgroundColor() const { return _backgroundColor; } + float getAlpha() const { return _alpha; } + + // setters + void setVisible(bool visible) { _visible = visible; } + void setX(int x) { _bounds.setX(x); } + void setY(int y) { _bounds.setY(y); } + void setWidth(int width) { _bounds.setWidth(width); } + void setHeight(int height) { _bounds.setHeight(height); } + void setBounds(const QRect& bounds) { _bounds = bounds; } + void setBackgroundColor(const xColor& color) { _backgroundColor = color; } + void setAlpha(float alpha) { _alpha = alpha; } + + virtual void setProperties(const QScriptValue& properties); + +protected: + QGLWidget* _parent; + QRect _bounds; // where on the screen to draw + float _alpha; + xColor _backgroundColor; + bool _visible; // should the overlay be drawn at all +}; + + +#endif /* defined(__interface__Overlay__) */ diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index 5c19dc5529..2f31e7e8c7 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -7,6 +7,9 @@ #include "Overlays.h" +#include "ImageOverlay.h" +#include "TextOverlay.h" + unsigned int Overlays::_nextOverlayID = 1; @@ -21,7 +24,7 @@ void Overlays::init(QGLWidget* parent) { } void Overlays::render() { - foreach(ImageOverlay* thisOverlay, _imageOverlays) { + foreach(Overlay* thisOverlay, _overlays) { thisOverlay->render(); } } @@ -36,36 +39,44 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope ImageOverlay* thisOverlay = new ImageOverlay(); thisOverlay->init(_parent); thisOverlay->setProperties(properties); - _imageOverlays[thisID] = thisOverlay; + _overlays[thisID] = thisOverlay; + } else if (type == "text") { + thisID = _nextOverlayID; + _nextOverlayID++; + TextOverlay* thisOverlay = new TextOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + _overlays[thisID] = thisOverlay; } + return thisID; } // TODO: make multi-threaded safe bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { - if (!_imageOverlays.contains(id)) { + if (!_overlays.contains(id)) { return false; } - ImageOverlay* thisOverlay = _imageOverlays[id]; + Overlay* thisOverlay = _overlays[id]; thisOverlay->setProperties(properties); return true; } // TODO: make multi-threaded safe void Overlays::deleteOverlay(unsigned int id) { - if (_imageOverlays.contains(id)) { - _imageOverlays.erase(_imageOverlays.find(id)); + if (_overlays.contains(id)) { + _overlays.erase(_overlays.find(id)); } } unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { - QMapIterator i(_imageOverlays); + QMapIterator i(_overlays); i.toBack(); while (i.hasPrevious()) { i.previous(); unsigned int thisID = i.key(); - ImageOverlay* thisOverlay = i.value(); - if (thisOverlay->getBounds().contains(point.x, point.y, false)) { + Overlay* thisOverlay = i.value(); + if (thisOverlay->getVisible() && thisOverlay->getBounds().contains(point.x, point.y, false)) { return thisID; } } diff --git a/interface/src/ui/Overlays.h b/interface/src/ui/Overlays.h index 84e4338a72..e39949d2c9 100644 --- a/interface/src/ui/Overlays.h +++ b/interface/src/ui/Overlays.h @@ -10,7 +10,7 @@ #include -#include "ImageOverlay.h" +#include "Overlay.h" class Overlays : public QObject { Q_OBJECT @@ -35,7 +35,7 @@ public slots: unsigned int getOverlayAtPoint(const glm::vec2& point); private: - QMap _imageOverlays; + QMap _overlays; static unsigned int _nextOverlayID; QGLWidget* _parent; }; diff --git a/interface/src/ui/TextOverlay.cpp b/interface/src/ui/TextOverlay.cpp new file mode 100644 index 0000000000..aec9f641c5 --- /dev/null +++ b/interface/src/ui/TextOverlay.cpp @@ -0,0 +1,80 @@ +// +// TextOverlay.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + + +#include +#include + +#include "TextOverlay.h" +#include "TextRenderer.h" + +TextOverlay::TextOverlay() : + _leftMargin(DEFAULT_MARGIN), + _topMargin(DEFAULT_MARGIN) +{ +} + +TextOverlay::~TextOverlay() { +} + +void TextOverlay::render() { + if (!_visible) { + return; // do nothing if we're not visible + } + + const float MAX_COLOR = 255; + glColor4f(_backgroundColor.red / MAX_COLOR, _backgroundColor.green / MAX_COLOR, _backgroundColor.blue / MAX_COLOR, _alpha); + + glBegin(GL_QUADS); + glVertex2f(_bounds.left(), _bounds.top()); + glVertex2f(_bounds.right(), _bounds.top()); + glVertex2f(_bounds.right(), _bounds.bottom()); + glVertex2f(_bounds.left(), _bounds.bottom()); + glEnd(); + + //TextRenderer(const char* family, int pointSize = -1, int weight = -1, bool italic = false, + // EffectType effect = NO_EFFECT, int effectThickness = 1); + + TextRenderer textRenderer(SANS_FONT_FAMILY, 11, 50); + const int leftAdjust = -1; // required to make text render relative to left edge of bounds + const int topAdjust = -2; // required to make text render relative to top edge of bounds + int x = _bounds.left() + _leftMargin + leftAdjust; + int y = _bounds.top() + _topMargin + topAdjust; + + glColor3f(1.0f, 1.0f, 1.0f); + QStringList lines = _text.split("\n"); + int lineOffset = 0; + foreach(QString thisLine, lines) { + if (lineOffset == 0) { + lineOffset = textRenderer.calculateHeight(qPrintable(thisLine)); + } + lineOffset += textRenderer.draw(x, y + lineOffset, qPrintable(thisLine)); + + const int lineGap = 2; + lineOffset += lineGap; + } + +} + +void TextOverlay::setProperties(const QScriptValue& properties) { + Overlay::setProperties(properties); + + QScriptValue text = properties.property("text"); + if (text.isValid()) { + setText(text.toVariant().toString()); + } + + if (properties.property("leftMargin").isValid()) { + setLeftMargin(properties.property("leftMargin").toVariant().toInt()); + } + + if (properties.property("topMargin").isValid()) { + setTopMargin(properties.property("topMargin").toVariant().toInt()); + } +} + + diff --git a/interface/src/ui/TextOverlay.h b/interface/src/ui/TextOverlay.h new file mode 100644 index 0000000000..323116eccf --- /dev/null +++ b/interface/src/ui/TextOverlay.h @@ -0,0 +1,58 @@ +// +// TextOverlay.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__TextOverlay__ +#define __interface__TextOverlay__ + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Overlay.h" + +const int DEFAULT_MARGIN = 10; + +class TextOverlay : public Overlay { + Q_OBJECT + +public: + TextOverlay(); + ~TextOverlay(); + virtual void render(); + + // getters + const QString& getText() const { return _text; } + int getLeftMargin() const { return _leftMargin; } + int getTopMargin() const { return _topMargin; } + + // setters + void setText(const QString& text) { _text = text; } + void setLeftMargin(int margin) { _leftMargin = margin; } + void setTopMargin(int margin) { _topMargin = margin; } + + virtual void setProperties(const QScriptValue& properties); + +private: + + QString _text; + int _leftMargin; + int _topMargin; + +}; + + +#endif /* defined(__interface__TextOverlay__) */ diff --git a/interface/src/ui/TextRenderer.cpp b/interface/src/ui/TextRenderer.cpp index 65056799e2..cacd730fd6 100644 --- a/interface/src/ui/TextRenderer.cpp +++ b/interface/src/ui/TextRenderer.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "InterfaceConfig.h" #include "TextRenderer.h" @@ -30,10 +32,25 @@ TextRenderer::~TextRenderer() { glDeleteTextures(_allTextureIDs.size(), _allTextureIDs.constData()); } -void TextRenderer::draw(int x, int y, const char* str) { +int TextRenderer::calculateHeight(const char* str) { + int maxHeight = 0; + for (const char* ch = str; *ch != 0; ch++) { + const Glyph& glyph = getGlyph(*ch); + if (glyph.textureID() == 0) { + continue; + } + + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } + } + return maxHeight; +} +int TextRenderer::draw(int x, int y, const char* str) { glEnable(GL_TEXTURE_2D); + int maxHeight = 0; for (const char* ch = str; *ch != 0; ch++) { const Glyph& glyph = getGlyph(*ch); if (glyph.textureID() == 0) { @@ -41,19 +58,23 @@ void TextRenderer::draw(int x, int y, const char* str) { continue; } + if (glyph.bounds().height() > maxHeight) { + maxHeight = glyph.bounds().height(); + } + glBindTexture(GL_TEXTURE_2D, glyph.textureID()); - + int left = x + glyph.bounds().x(); int right = x + glyph.bounds().x() + glyph.bounds().width(); int bottom = y + glyph.bounds().y(); int top = y + glyph.bounds().y() + glyph.bounds().height(); - + float scale = 1.0 / IMAGE_SIZE; float ls = glyph.location().x() * scale; float rs = (glyph.location().x() + glyph.bounds().width()) * scale; float bt = glyph.location().y() * scale; float tt = (glyph.location().y() + glyph.bounds().height()) * scale; - + glBegin(GL_QUADS); glTexCoord2f(ls, bt); glVertex2f(left, bottom); @@ -64,12 +85,13 @@ void TextRenderer::draw(int x, int y, const char* str) { glTexCoord2f(ls, tt); glVertex2f(left, top); glEnd(); - + x += glyph.width(); } - glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); + + return maxHeight; } int TextRenderer::computeWidth(char ch) diff --git a/interface/src/ui/TextRenderer.h b/interface/src/ui/TextRenderer.h index ff484066d8..d6c24c1ce8 100644 --- a/interface/src/ui/TextRenderer.h +++ b/interface/src/ui/TextRenderer.h @@ -20,6 +20,16 @@ // a special "character" that renders as a solid block const char SOLID_BLOCK_CHAR = 127; +// the standard sans serif font family +#define SANS_FONT_FAMILY "Helvetica" + +// the standard mono font family +#define MONO_FONT_FAMILY "Courier" + +// the Inconsolata font family +#define INCONSOLATA_FONT_FAMILY "Inconsolata" + + class Glyph; class TextRenderer { @@ -33,7 +43,11 @@ public: const QFontMetrics& metrics() const { return _metrics; } - void draw(int x, int y, const char* str); + // returns the height of the tallest character + int calculateHeight(const char* str); + + // also returns the height of the tallest character + int draw(int x, int y, const char* str); int computeWidth(char ch); int computeWidth(const char* str); From cb1c659e2e5ef8324546aebead94e6a675378907 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 15 Feb 2014 21:25:20 -0800 Subject: [PATCH 54/70] fix windows --- interface/src/ui/ImageOverlay.cpp | 12 +++++++----- interface/src/ui/Overlay.cpp | 7 +++++-- interface/src/ui/TextOverlay.cpp | 2 ++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp index d36099cffc..d048e7a27e 100644 --- a/interface/src/ui/ImageOverlay.cpp +++ b/interface/src/ui/ImageOverlay.cpp @@ -5,14 +5,16 @@ // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include #include "ImageOverlay.h" -#include -#include -#include -#include - ImageOverlay::ImageOverlay() : _textureID(0), _renderImage(false), diff --git a/interface/src/ui/Overlay.cpp b/interface/src/ui/Overlay.cpp index fa54844477..4660fd6ada 100644 --- a/interface/src/ui/Overlay.cpp +++ b/interface/src/ui/Overlay.cpp @@ -5,14 +5,17 @@ // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // - -#include "Overlay.h" +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" #include #include #include #include +#include "Overlay.h" + + Overlay::Overlay() : _parent(NULL), _alpha(DEFAULT_ALPHA), diff --git a/interface/src/ui/TextOverlay.cpp b/interface/src/ui/TextOverlay.cpp index aec9f641c5..51b57c2f3f 100644 --- a/interface/src/ui/TextOverlay.cpp +++ b/interface/src/ui/TextOverlay.cpp @@ -5,6 +5,8 @@ // Copyright (c) 2014 High Fidelity, Inc. All rights reserved. // +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" #include #include From 5abf908874cfcecf08d8c86801fc7b6055475585 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 16 Feb 2014 11:36:06 -0800 Subject: [PATCH 55/70] added 3D cube overlay support --- examples/overlaysExample.js | 66 ++++++++++++++------ interface/src/Application.cpp | 5 +- interface/src/ui/Cube3DOverlay.cpp | 98 ++++++++++++++++++++++++++++++ interface/src/ui/Cube3DOverlay.h | 57 +++++++++++++++++ interface/src/ui/ImageOverlay.cpp | 4 +- interface/src/ui/ImageOverlay.h | 3 +- interface/src/ui/Overlay.cpp | 10 +-- interface/src/ui/Overlay.h | 6 +- interface/src/ui/Overlay2D.cpp | 63 +++++++++++++++++++ interface/src/ui/Overlay2D.h | 51 ++++++++++++++++ interface/src/ui/Overlays.cpp | 67 ++++++++++++++------ interface/src/ui/Overlays.h | 6 +- interface/src/ui/TextOverlay.cpp | 4 +- interface/src/ui/TextOverlay.h | 3 +- 14 files changed, 389 insertions(+), 54 deletions(-) create mode 100644 interface/src/ui/Cube3DOverlay.cpp create mode 100644 interface/src/ui/Cube3DOverlay.h create mode 100644 interface/src/ui/Overlay2D.cpp create mode 100644 interface/src/ui/Overlay2D.h diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index 79cd65df68..501f9a248e 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -39,7 +39,7 @@ for (s = 0; s < numberOfSwatches; s++) { height: 54, subImage: { x: imageFromX, y: imageFromY, width: 30, height: 54 }, imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/testing-swatches.svg", - backgroundColor: swatchColors[s], + color: swatchColors[s], alpha: 1 }); } @@ -49,11 +49,10 @@ var text = Overlays.addOverlay("text", { y: 100, width: 150, height: 50, - backgroundColor: { red: 0, green: 0, blue: 0}, + color: { red: 0, green: 0, blue: 0}, textColor: { red: 255, green: 0, blue: 0}, topMargin: 4, leftMargin: 4, - alpha: 0.7, text: "Here is some text.\nAnd a second line." }); @@ -64,8 +63,7 @@ var toolA = Overlays.addOverlay("image", { height: 40, subImage: { x: 0, y: 0, width: 62, height: 40 }, imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg", - backgroundColor: { red: 255, green: 255, blue: 255}, - alpha: 0.7, + color: { red: 255, green: 255, blue: 255}, visible: false }); @@ -73,7 +71,7 @@ var slider = Overlays.addOverlay("image", { // alternate form of expressing bounds bounds: { x: 100, y: 300, width: 158, height: 35}, imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/slider.png", - backgroundColor: { red: 255, green: 255, blue: 255}, + color: { red: 255, green: 255, blue: 255}, alpha: 1 }); @@ -86,22 +84,39 @@ var thumb = Overlays.addOverlay("image", { width: 18, height: 17, imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/thumb.png", - backgroundColor: { red: 255, green: 255, blue: 255}, + color: { red: 255, green: 255, blue: 255}, alpha: 1 }); - -// 270x 109 -// 109... 109/2 = 54,1,54 -// 270... 39 to 66 = 28 x 9 swatches with -// unselected: -// 38,0,28,54 -// selected: -// 38,55,28,54 -//http://highfidelity-public.s3-us-west-1.amazonaws.com/images/swatches.svg -// 123456789*123456789*123456789* -// 0123456789*123456789*123456789* +// our 3D cube that moves around... +var cubePosition = { x: 2, y: 0, z: 2 }; +var cubeSize = 5; +var cubeMove = 0.1; +var minCubeX = 1; +var maxCubeX = 20; +var solidCubePosition = { x: 0, y: 5, z: 0 }; +var solidCubeSize = 2; +var minSolidCubeX = 0; +var maxSolidCubeX = 10; +var solidCubeMove = 0.05; + +var cube = Overlays.addOverlay("cube", { + position: cubePosition, + size: cubeSize, + color: { red: 255, green: 0, blue: 0}, + alpha: 1, + solid: false + }); + +var solidCube = Overlays.addOverlay("cube", { + position: solidCubePosition, + size: solidCubeSize, + color: { red: 0, green: 255, blue: 0}, + alpha: 1, + solid: true + }); + function scriptEnding() { Overlays.deleteOverlay(toolA); @@ -127,6 +142,21 @@ function update() { } Overlays.editOverlay(toolA, { visible: toolAVisible } ); } + + // move our 3D cube + cubePosition.x += cubeMove; + cubePosition.z += cubeMove; + if (cubePosition.x > maxCubeX || cubePosition.x < minCubeX) { + cubeMove = cubeMove * -1; + } + Overlays.editOverlay(cube, { position: cubePosition } ); + + solidCubePosition.x += solidCubeMove; + solidCubePosition.z += solidCubeMove; + if (solidCubePosition.x > maxSolidCubeX || solidCubePosition.x < minSolidCubeX) { + solidCubeMove = solidCubeMove * -1; + } + Overlays.editOverlay(solidCube, { position: solidCubePosition } ); } Script.willSendVisualDataCallback.connect(update); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4da0a35279..521757abab 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2842,6 +2842,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // give external parties a change to hook in emit renderingInWorldInterface(); + + // render JS/scriptable overlays + _overlays.render3D(); } } @@ -2988,7 +2991,7 @@ void Application::displayOverlay() { _pieMenu.render(); } - _overlays.render(); + _overlays.render2D(); glPopMatrix(); } diff --git a/interface/src/ui/Cube3DOverlay.cpp b/interface/src/ui/Cube3DOverlay.cpp new file mode 100644 index 0000000000..dda1f64295 --- /dev/null +++ b/interface/src/ui/Cube3DOverlay.cpp @@ -0,0 +1,98 @@ +// +// Cube3DOverlay.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include + +#include "Cube3DOverlay.h" +#include "TextRenderer.h" + +const glm::vec3 DEFAULT_POSITION = glm::vec3(0.0f, 0.0f, 0.0f); +const float DEFAULT_SIZE = 1.0f; +const float DEFAULT_LINE_WIDTH = 1.0f; +const bool DEFAULT_isSolid = false; + +Cube3DOverlay::Cube3DOverlay() : + _position(DEFAULT_POSITION), + _size(DEFAULT_SIZE), + _lineWidth(DEFAULT_LINE_WIDTH), + _isSolid(DEFAULT_isSolid) +{ +} + +Cube3DOverlay::~Cube3DOverlay() { +} + +void Cube3DOverlay::render() { + if (!_visible) { + return; // do nothing if we're not visible + } + + const float MAX_COLOR = 255; + glColor4f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, _alpha); + + + glDisable(GL_LIGHTING); + glPushMatrix(); + glTranslatef(_position.x + _size * 0.5f, + _position.y + _size * 0.5f, + _position.z + _size * 0.5f); + glLineWidth(_lineWidth); + if (_isSolid) { + glutSolidCube(_size); + } else { + glutWireCube(_size); + } + glPopMatrix(); + +} + +void Cube3DOverlay::setProperties(const QScriptValue& properties) { + Overlay::setProperties(properties); + + QScriptValue position = properties.property("position"); + if (position.isValid()) { + QScriptValue x = position.property("x"); + QScriptValue y = position.property("y"); + QScriptValue z = position.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setPosition(newPosition); + } + } + + if (properties.property("size").isValid()) { + setSize(properties.property("size").toVariant().toFloat()); + } + + if (properties.property("lineWidth").isValid()) { + setLineWidth(properties.property("lineWidth").toVariant().toFloat()); + } + + if (properties.property("isSolid").isValid()) { + setIsSolid(properties.property("isSolid").toVariant().toBool()); + } + if (properties.property("isWire").isValid()) { + setIsSolid(!properties.property("isWire").toVariant().toBool()); + } + if (properties.property("solid").isValid()) { + setIsSolid(properties.property("solid").toVariant().toBool()); + } + if (properties.property("wire").isValid()) { + setIsSolid(!properties.property("wire").toVariant().toBool()); + } + + +} + + diff --git a/interface/src/ui/Cube3DOverlay.h b/interface/src/ui/Cube3DOverlay.h new file mode 100644 index 0000000000..ad6ba92d02 --- /dev/null +++ b/interface/src/ui/Cube3DOverlay.h @@ -0,0 +1,57 @@ +// +// Cube3DOverlay.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Cube3DOverlay__ +#define __interface__Cube3DOverlay__ + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Overlay.h" + +class Cube3DOverlay : public Overlay { + Q_OBJECT + +public: + Cube3DOverlay(); + ~Cube3DOverlay(); + virtual void render(); + + // getters + const glm::vec3& getPosition() const { return _position; } + float getSize() const { return _size; } + float getLineWidth() const { return _lineWidth; } + bool getIsSolid() const { return _isSolid; } + + // setters + void setPosition(const glm::vec3& position) { _position = position; } + void setSize(float size) { _size = size; } + void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } + void setIsSolid(bool isSolid) { _isSolid = isSolid; } + + virtual void setProperties(const QScriptValue& properties); + +private: + glm::vec3 _position; + float _size; + float _lineWidth; + bool _isSolid; +}; + + +#endif /* defined(__interface__Cube3DOverlay__) */ diff --git a/interface/src/ui/ImageOverlay.cpp b/interface/src/ui/ImageOverlay.cpp index d048e7a27e..178383749b 100644 --- a/interface/src/ui/ImageOverlay.cpp +++ b/interface/src/ui/ImageOverlay.cpp @@ -61,7 +61,7 @@ void ImageOverlay::render() { glBindTexture(GL_TEXTURE_2D, _textureID); } const float MAX_COLOR = 255; - glColor4f(_backgroundColor.red / MAX_COLOR, _backgroundColor.green / MAX_COLOR, _backgroundColor.blue / MAX_COLOR, _alpha); + glColor4f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, _alpha); float imageWidth = _textureImage.width(); float imageHeight = _textureImage.height(); @@ -107,7 +107,7 @@ void ImageOverlay::render() { } void ImageOverlay::setProperties(const QScriptValue& properties) { - Overlay::setProperties(properties); + Overlay2D::setProperties(properties); QScriptValue subImageBounds = properties.property("subImage"); if (subImageBounds.isValid()) { diff --git a/interface/src/ui/ImageOverlay.h b/interface/src/ui/ImageOverlay.h index be7d8cf5d8..77cac3b3c6 100644 --- a/interface/src/ui/ImageOverlay.h +++ b/interface/src/ui/ImageOverlay.h @@ -23,8 +23,9 @@ #include #include "Overlay.h" +#include "Overlay2D.h" -class ImageOverlay : public Overlay { +class ImageOverlay : public Overlay2D { Q_OBJECT public: diff --git a/interface/src/ui/Overlay.cpp b/interface/src/ui/Overlay.cpp index 4660fd6ada..c6b3902fd6 100644 --- a/interface/src/ui/Overlay.cpp +++ b/interface/src/ui/Overlay.cpp @@ -19,7 +19,7 @@ Overlay::Overlay() : _parent(NULL), _alpha(DEFAULT_ALPHA), - _backgroundColor(DEFAULT_BACKGROUND_COLOR), + _color(DEFAULT_BACKGROUND_COLOR), _visible(true) { } @@ -69,15 +69,15 @@ void Overlay::setProperties(const QScriptValue& properties) { //qDebug() << "set bounds to " << getBounds(); } - QScriptValue color = properties.property("backgroundColor"); + QScriptValue color = properties.property("color"); if (color.isValid()) { QScriptValue red = color.property("red"); QScriptValue green = color.property("green"); QScriptValue blue = color.property("blue"); if (red.isValid() && green.isValid() && blue.isValid()) { - _backgroundColor.red = red.toVariant().toInt(); - _backgroundColor.green = green.toVariant().toInt(); - _backgroundColor.blue = blue.toVariant().toInt(); + _color.red = red.toVariant().toInt(); + _color.green = green.toVariant().toInt(); + _color.blue = blue.toVariant().toInt(); } } diff --git a/interface/src/ui/Overlay.h b/interface/src/ui/Overlay.h index ce63fdaba5..4f1cdc9edb 100644 --- a/interface/src/ui/Overlay.h +++ b/interface/src/ui/Overlay.h @@ -37,7 +37,7 @@ public: int getWidth() const { return _bounds.width(); } int getHeight() const { return _bounds.height(); } const QRect& getBounds() const { return _bounds; } - const xColor& getBackgroundColor() const { return _backgroundColor; } + const xColor& getColor() const { return _color; } float getAlpha() const { return _alpha; } // setters @@ -47,7 +47,7 @@ public: void setWidth(int width) { _bounds.setWidth(width); } void setHeight(int height) { _bounds.setHeight(height); } void setBounds(const QRect& bounds) { _bounds = bounds; } - void setBackgroundColor(const xColor& color) { _backgroundColor = color; } + void setColor(const xColor& color) { _color = color; } void setAlpha(float alpha) { _alpha = alpha; } virtual void setProperties(const QScriptValue& properties); @@ -56,7 +56,7 @@ protected: QGLWidget* _parent; QRect _bounds; // where on the screen to draw float _alpha; - xColor _backgroundColor; + xColor _color; bool _visible; // should the overlay be drawn at all }; diff --git a/interface/src/ui/Overlay2D.cpp b/interface/src/ui/Overlay2D.cpp new file mode 100644 index 0000000000..0c459811c4 --- /dev/null +++ b/interface/src/ui/Overlay2D.cpp @@ -0,0 +1,63 @@ +// +// Overlay2D.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include + +#include "Overlay2D.h" + + +Overlay2D::Overlay2D() { +} + +Overlay2D::~Overlay2D() { +} + +void Overlay2D::setProperties(const QScriptValue& properties) { + Overlay::setProperties(properties); + + QScriptValue bounds = properties.property("bounds"); + if (bounds.isValid()) { + QRect boundsRect; + boundsRect.setX(bounds.property("x").toVariant().toInt()); + boundsRect.setY(bounds.property("y").toVariant().toInt()); + boundsRect.setWidth(bounds.property("width").toVariant().toInt()); + boundsRect.setHeight(bounds.property("height").toVariant().toInt()); + setBounds(boundsRect); + } else { + QRect oldBounds = getBounds(); + QRect newBounds = oldBounds; + + if (properties.property("x").isValid()) { + newBounds.setX(properties.property("x").toVariant().toInt()); + } else { + newBounds.setX(oldBounds.x()); + } + if (properties.property("y").isValid()) { + newBounds.setY(properties.property("y").toVariant().toInt()); + } else { + newBounds.setY(oldBounds.y()); + } + if (properties.property("width").isValid()) { + newBounds.setWidth(properties.property("width").toVariant().toInt()); + } else { + newBounds.setWidth(oldBounds.width()); + } + if (properties.property("height").isValid()) { + newBounds.setHeight(properties.property("height").toVariant().toInt()); + } else { + newBounds.setHeight(oldBounds.height()); + } + setBounds(newBounds); + //qDebug() << "set bounds to " << getBounds(); + } +} diff --git a/interface/src/ui/Overlay2D.h b/interface/src/ui/Overlay2D.h new file mode 100644 index 0000000000..3da8f8bca4 --- /dev/null +++ b/interface/src/ui/Overlay2D.h @@ -0,0 +1,51 @@ +// +// Overlay2D.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Overlay2D__ +#define __interface__Overlay2D__ + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include + +#include // for xColor + +#include "Overlay.h" + +class Overlay2D : public Overlay { + Q_OBJECT + +public: + Overlay2D(); + ~Overlay2D(); + + // getters + int getX() const { return _bounds.x(); } + int getY() const { return _bounds.y(); } + int getWidth() const { return _bounds.width(); } + int getHeight() const { return _bounds.height(); } + const QRect& getBounds() const { return _bounds; } + + // setters + void setX(int x) { _bounds.setX(x); } + void setY(int y) { _bounds.setY(y); } + void setWidth(int width) { _bounds.setWidth(width); } + void setHeight(int height) { _bounds.setHeight(height); } + void setBounds(const QRect& bounds) { _bounds = bounds; } + + virtual void setProperties(const QScriptValue& properties); + +protected: + QRect _bounds; // where on the screen to draw +}; + + +#endif /* defined(__interface__Overlay2D__) */ diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index 2f31e7e8c7..b731d46b16 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -6,8 +6,9 @@ // -#include "Overlays.h" +#include "Cube3DOverlay.h" #include "ImageOverlay.h" +#include "Overlays.h" #include "TextOverlay.h" @@ -23,8 +24,14 @@ void Overlays::init(QGLWidget* parent) { _parent = parent; } -void Overlays::render() { - foreach(Overlay* thisOverlay, _overlays) { +void Overlays::render2D() { + foreach(Overlay* thisOverlay, _overlays2D) { + thisOverlay->render(); + } +} + +void Overlays::render3D() { + foreach(Overlay* thisOverlay, _overlays3D) { thisOverlay->render(); } } @@ -32,21 +39,36 @@ void Overlays::render() { // TODO: make multi-threaded safe unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { unsigned int thisID = 0; + bool created = false; + bool is3D = false; + Overlay* thisOverlay = NULL; if (type == "image") { - thisID = _nextOverlayID; - _nextOverlayID++; - ImageOverlay* thisOverlay = new ImageOverlay(); + thisOverlay = new ImageOverlay(); thisOverlay->init(_parent); thisOverlay->setProperties(properties); - _overlays[thisID] = thisOverlay; + created = true; } else if (type == "text") { - thisID = _nextOverlayID; - _nextOverlayID++; - TextOverlay* thisOverlay = new TextOverlay(); + thisOverlay = new TextOverlay(); thisOverlay->init(_parent); thisOverlay->setProperties(properties); - _overlays[thisID] = thisOverlay; + created = true; + } else if (type == "cube") { + thisOverlay = new Cube3DOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + created = true; + is3D = true; + } + + if (created) { + thisID = _nextOverlayID; + _nextOverlayID++; + if (is3D) { + _overlays3D[thisID] = thisOverlay; + } else { + _overlays2D[thisID] = thisOverlay; + } } return thisID; @@ -54,23 +76,30 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope // TODO: make multi-threaded safe bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { - if (!_overlays.contains(id)) { - return false; + Overlay* thisOverlay = NULL; + if (_overlays2D.contains(id)) { + thisOverlay = _overlays2D[id]; + } else if (_overlays3D.contains(id)) { + thisOverlay = _overlays3D[id]; } - Overlay* thisOverlay = _overlays[id]; - thisOverlay->setProperties(properties); - return true; + if (thisOverlay) { + thisOverlay->setProperties(properties); + return true; + } + return false; } // TODO: make multi-threaded safe void Overlays::deleteOverlay(unsigned int id) { - if (_overlays.contains(id)) { - _overlays.erase(_overlays.find(id)); + if (_overlays2D.contains(id)) { + _overlays2D.erase(_overlays2D.find(id)); + } else if (_overlays3D.contains(id)) { + _overlays3D.erase(_overlays3D.find(id)); } } unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { - QMapIterator i(_overlays); + QMapIterator i(_overlays2D); i.toBack(); while (i.hasPrevious()) { i.previous(); diff --git a/interface/src/ui/Overlays.h b/interface/src/ui/Overlays.h index e39949d2c9..cfd84fd44b 100644 --- a/interface/src/ui/Overlays.h +++ b/interface/src/ui/Overlays.h @@ -18,7 +18,8 @@ public: Overlays(); ~Overlays(); void init(QGLWidget* parent); - void render(); + void render3D(); + void render2D(); public slots: /// adds an overlay with the specific properties @@ -35,7 +36,8 @@ public slots: unsigned int getOverlayAtPoint(const glm::vec2& point); private: - QMap _overlays; + QMap _overlays2D; + QMap _overlays3D; static unsigned int _nextOverlayID; QGLWidget* _parent; }; diff --git a/interface/src/ui/TextOverlay.cpp b/interface/src/ui/TextOverlay.cpp index 51b57c2f3f..edaec6849a 100644 --- a/interface/src/ui/TextOverlay.cpp +++ b/interface/src/ui/TextOverlay.cpp @@ -29,7 +29,7 @@ void TextOverlay::render() { } const float MAX_COLOR = 255; - glColor4f(_backgroundColor.red / MAX_COLOR, _backgroundColor.green / MAX_COLOR, _backgroundColor.blue / MAX_COLOR, _alpha); + glColor4f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, _alpha); glBegin(GL_QUADS); glVertex2f(_bounds.left(), _bounds.top()); @@ -63,7 +63,7 @@ void TextOverlay::render() { } void TextOverlay::setProperties(const QScriptValue& properties) { - Overlay::setProperties(properties); + Overlay2D::setProperties(properties); QScriptValue text = properties.property("text"); if (text.isValid()) { diff --git a/interface/src/ui/TextOverlay.h b/interface/src/ui/TextOverlay.h index 323116eccf..d565aeb70d 100644 --- a/interface/src/ui/TextOverlay.h +++ b/interface/src/ui/TextOverlay.h @@ -23,10 +23,11 @@ #include #include "Overlay.h" +#include "Overlay2D.h" const int DEFAULT_MARGIN = 10; -class TextOverlay : public Overlay { +class TextOverlay : public Overlay2D { Q_OBJECT public: From 03d3a535f827a31e4080a8b4fe85ffefe902401b Mon Sep 17 00:00:00 2001 From: gaitat Date: Sun, 16 Feb 2014 14:50:50 -0500 Subject: [PATCH 56/70] Fix for Worklist Job #19503 Added random sound trigger to the examples --- examples/audioBall.js | 14 +++++++++----- examples/audioBallLifetime.js | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/examples/audioBall.js b/examples/audioBall.js index a0d9423526..676b9118b3 100644 --- a/examples/audioBall.js +++ b/examples/audioBall.js @@ -11,6 +11,8 @@ // var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); +var CHANCE_OF_PLAYING_SOUND = 0.01; + var FACTOR = 0.75; var countParticles = 0; // the first time around we want to create the particle and thereafter to modify it. @@ -23,11 +25,13 @@ function updateParticle() { // move particle three units in front of the avatar var particlePosition = Vec3.sum(MyAvatar.position, Vec3.multiply(avatarFront, 3)); - // play a sound at the location of the particle - var options = new AudioInjectionOptions(); - options.position = particlePosition; - options.volume = 0.75; - Audio.playSound(sound, options); + if (Math.random() < CHANCE_OF_PLAYING_SOUND) { + // play a sound at the location of the particle + var options = new AudioInjectionOptions(); + options.position = particlePosition; + options.volume = 0.75; + Audio.playSound(sound, options); + } var audioAverageLoudness = MyAvatar.audioAverageLoudness * FACTOR; //print ("Audio Loudness = " + MyAvatar.audioLoudness + " -- Audio Average Loudness = " + MyAvatar.audioAverageLoudness); diff --git a/examples/audioBallLifetime.js b/examples/audioBallLifetime.js index a7b8a0a749..affb75f04d 100644 --- a/examples/audioBallLifetime.js +++ b/examples/audioBallLifetime.js @@ -11,6 +11,8 @@ // var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); +var CHANCE_OF_PLAYING_SOUND = 0.01; + var FACTOR = 0.75; function addParticle() { @@ -20,11 +22,13 @@ function addParticle() { // move particle three units in front of the avatar var particlePosition = Vec3.sum(MyAvatar.position, Vec3.multiply (avatarFront, 3)); - // play a sound at the location of the particle - var options = new AudioInjectionOptions(); - options.position = particlePosition; - options.volume = 0.25; - Audio.playSound(sound, options); + if (Math.random() < CHANCE_OF_PLAYING_SOUND) { + // play a sound at the location of the particle + var options = new AudioInjectionOptions(); + options.position = particlePosition; + options.volume = 0.25; + Audio.playSound(sound, options); + } var audioAverageLoudness = MyAvatar.audioAverageLoudness * FACTOR; //print ("Audio Loudness = " + MyAvatar.audioLoudness + " -- Audio Average Loudness = " + MyAvatar.audioAverageLoudness); From 7c350b3acbda71c7dcb39cb7ae3e6c6c074971da Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 16 Feb 2014 12:30:15 -0800 Subject: [PATCH 57/70] add 3D sphere overlay support --- examples/overlaysExample.js | 22 +++++++++ interface/src/ui/Base3DOverlay.cpp | 70 ++++++++++++++++++++++++++++ interface/src/ui/Base3DOverlay.h | 56 ++++++++++++++++++++++ interface/src/ui/Cube3DOverlay.cpp | 56 +--------------------- interface/src/ui/Cube3DOverlay.h | 24 +--------- interface/src/ui/Overlays.cpp | 7 +++ interface/src/ui/Sphere3DOverlay.cpp | 45 ++++++++++++++++++ interface/src/ui/Sphere3DOverlay.h | 37 +++++++++++++++ 8 files changed, 240 insertions(+), 77 deletions(-) create mode 100644 interface/src/ui/Base3DOverlay.cpp create mode 100644 interface/src/ui/Base3DOverlay.h create mode 100644 interface/src/ui/Sphere3DOverlay.cpp create mode 100644 interface/src/ui/Sphere3DOverlay.h diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index 501f9a248e..01eff71188 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -117,6 +117,20 @@ var solidCube = Overlays.addOverlay("cube", { solid: true }); +var spherePosition = { x: 5, y: 5, z: 5 }; +var sphereSize = 1; +var minSphereSize = 0.5; +var maxSphereSize = 10; +var sphereSizeChange = 0.05; + +var sphere = Overlays.addOverlay("sphere", { + position: spherePosition, + size: sphereSize, + color: { red: 0, green: 0, blue: 255}, + alpha: 1, + solid: false + }); + function scriptEnding() { Overlays.deleteOverlay(toolA); @@ -151,12 +165,20 @@ function update() { } Overlays.editOverlay(cube, { position: cubePosition } ); + // move our solid 3D cube solidCubePosition.x += solidCubeMove; solidCubePosition.z += solidCubeMove; if (solidCubePosition.x > maxSolidCubeX || solidCubePosition.x < minSolidCubeX) { solidCubeMove = solidCubeMove * -1; } Overlays.editOverlay(solidCube, { position: solidCubePosition } ); + + // adjust our 3D sphere + sphereSize += sphereSizeChange; + if (sphereSize > maxSphereSize || sphereSize < minSphereSize) { + sphereSizeChange = sphereSizeChange * -1; + } + Overlays.editOverlay(sphere, { size: sphereSize, solid: (sphereSizeChange < 0) } ); } Script.willSendVisualDataCallback.connect(update); diff --git a/interface/src/ui/Base3DOverlay.cpp b/interface/src/ui/Base3DOverlay.cpp new file mode 100644 index 0000000000..23b02a6ac6 --- /dev/null +++ b/interface/src/ui/Base3DOverlay.cpp @@ -0,0 +1,70 @@ +// +// Base3DOverlay.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include + +#include "Base3DOverlay.h" +#include "TextRenderer.h" + +const glm::vec3 DEFAULT_POSITION = glm::vec3(0.0f, 0.0f, 0.0f); +const float DEFAULT_SIZE = 1.0f; +const float DEFAULT_LINE_WIDTH = 1.0f; +const bool DEFAULT_isSolid = false; + +Base3DOverlay::Base3DOverlay() : + _position(DEFAULT_POSITION), + _size(DEFAULT_SIZE), + _lineWidth(DEFAULT_LINE_WIDTH), + _isSolid(DEFAULT_isSolid) +{ +} + +Base3DOverlay::~Base3DOverlay() { +} + +void Base3DOverlay::setProperties(const QScriptValue& properties) { + Overlay::setProperties(properties); + + QScriptValue position = properties.property("position"); + if (position.isValid()) { + QScriptValue x = position.property("x"); + QScriptValue y = position.property("y"); + QScriptValue z = position.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newPosition; + newPosition.x = x.toVariant().toFloat(); + newPosition.y = y.toVariant().toFloat(); + newPosition.z = z.toVariant().toFloat(); + setPosition(newPosition); + } + } + + if (properties.property("size").isValid()) { + setSize(properties.property("size").toVariant().toFloat()); + } + + if (properties.property("lineWidth").isValid()) { + setLineWidth(properties.property("lineWidth").toVariant().toFloat()); + } + + if (properties.property("isSolid").isValid()) { + setIsSolid(properties.property("isSolid").toVariant().toBool()); + } + if (properties.property("isWire").isValid()) { + setIsSolid(!properties.property("isWire").toVariant().toBool()); + } + if (properties.property("solid").isValid()) { + setIsSolid(properties.property("solid").toVariant().toBool()); + } + if (properties.property("wire").isValid()) { + setIsSolid(!properties.property("wire").toVariant().toBool()); + } +} diff --git a/interface/src/ui/Base3DOverlay.h b/interface/src/ui/Base3DOverlay.h new file mode 100644 index 0000000000..264ca74326 --- /dev/null +++ b/interface/src/ui/Base3DOverlay.h @@ -0,0 +1,56 @@ +// +// Base3DOverlay.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Base3DOverlay__ +#define __interface__Base3DOverlay__ + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Overlay.h" + +class Base3DOverlay : public Overlay { + Q_OBJECT + +public: + Base3DOverlay(); + ~Base3DOverlay(); + + // getters + const glm::vec3& getPosition() const { return _position; } + float getSize() const { return _size; } + float getLineWidth() const { return _lineWidth; } + bool getIsSolid() const { return _isSolid; } + + // setters + void setPosition(const glm::vec3& position) { _position = position; } + void setSize(float size) { _size = size; } + void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } + void setIsSolid(bool isSolid) { _isSolid = isSolid; } + + virtual void setProperties(const QScriptValue& properties); + +protected: + glm::vec3 _position; + float _size; + float _lineWidth; + bool _isSolid; +}; + + +#endif /* defined(__interface__Base3DOverlay__) */ diff --git a/interface/src/ui/Cube3DOverlay.cpp b/interface/src/ui/Cube3DOverlay.cpp index dda1f64295..992a18e451 100644 --- a/interface/src/ui/Cube3DOverlay.cpp +++ b/interface/src/ui/Cube3DOverlay.cpp @@ -12,19 +12,8 @@ #include #include "Cube3DOverlay.h" -#include "TextRenderer.h" -const glm::vec3 DEFAULT_POSITION = glm::vec3(0.0f, 0.0f, 0.0f); -const float DEFAULT_SIZE = 1.0f; -const float DEFAULT_LINE_WIDTH = 1.0f; -const bool DEFAULT_isSolid = false; - -Cube3DOverlay::Cube3DOverlay() : - _position(DEFAULT_POSITION), - _size(DEFAULT_SIZE), - _lineWidth(DEFAULT_LINE_WIDTH), - _isSolid(DEFAULT_isSolid) -{ +Cube3DOverlay::Cube3DOverlay() { } Cube3DOverlay::~Cube3DOverlay() { @@ -53,46 +42,3 @@ void Cube3DOverlay::render() { glPopMatrix(); } - -void Cube3DOverlay::setProperties(const QScriptValue& properties) { - Overlay::setProperties(properties); - - QScriptValue position = properties.property("position"); - if (position.isValid()) { - QScriptValue x = position.property("x"); - QScriptValue y = position.property("y"); - QScriptValue z = position.property("z"); - if (x.isValid() && y.isValid() && z.isValid()) { - glm::vec3 newPosition; - newPosition.x = x.toVariant().toFloat(); - newPosition.y = y.toVariant().toFloat(); - newPosition.z = z.toVariant().toFloat(); - setPosition(newPosition); - } - } - - if (properties.property("size").isValid()) { - setSize(properties.property("size").toVariant().toFloat()); - } - - if (properties.property("lineWidth").isValid()) { - setLineWidth(properties.property("lineWidth").toVariant().toFloat()); - } - - if (properties.property("isSolid").isValid()) { - setIsSolid(properties.property("isSolid").toVariant().toBool()); - } - if (properties.property("isWire").isValid()) { - setIsSolid(!properties.property("isWire").toVariant().toBool()); - } - if (properties.property("solid").isValid()) { - setIsSolid(properties.property("solid").toVariant().toBool()); - } - if (properties.property("wire").isValid()) { - setIsSolid(!properties.property("wire").toVariant().toBool()); - } - - -} - - diff --git a/interface/src/ui/Cube3DOverlay.h b/interface/src/ui/Cube3DOverlay.h index ad6ba92d02..0033b9c43f 100644 --- a/interface/src/ui/Cube3DOverlay.h +++ b/interface/src/ui/Cube3DOverlay.h @@ -22,35 +22,15 @@ #include -#include "Overlay.h" +#include "Base3DOverlay.h" -class Cube3DOverlay : public Overlay { +class Cube3DOverlay : public Base3DOverlay { Q_OBJECT public: Cube3DOverlay(); ~Cube3DOverlay(); virtual void render(); - - // getters - const glm::vec3& getPosition() const { return _position; } - float getSize() const { return _size; } - float getLineWidth() const { return _lineWidth; } - bool getIsSolid() const { return _isSolid; } - - // setters - void setPosition(const glm::vec3& position) { _position = position; } - void setSize(float size) { _size = size; } - void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } - void setIsSolid(bool isSolid) { _isSolid = isSolid; } - - virtual void setProperties(const QScriptValue& properties); - -private: - glm::vec3 _position; - float _size; - float _lineWidth; - bool _isSolid; }; diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index b731d46b16..902e850fc6 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -9,6 +9,7 @@ #include "Cube3DOverlay.h" #include "ImageOverlay.h" #include "Overlays.h" +#include "Sphere3DOverlay.h" #include "TextOverlay.h" @@ -59,6 +60,12 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay->setProperties(properties); created = true; is3D = true; + } else if (type == "sphere") { + thisOverlay = new Sphere3DOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + created = true; + is3D = true; } if (created) { diff --git a/interface/src/ui/Sphere3DOverlay.cpp b/interface/src/ui/Sphere3DOverlay.cpp new file mode 100644 index 0000000000..7fded5bedb --- /dev/null +++ b/interface/src/ui/Sphere3DOverlay.cpp @@ -0,0 +1,45 @@ +// +// Sphere3DOverlay.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include + +#include "Sphere3DOverlay.h" + +Sphere3DOverlay::Sphere3DOverlay() { +} + +Sphere3DOverlay::~Sphere3DOverlay() { +} + +void Sphere3DOverlay::render() { + if (!_visible) { + return; // do nothing if we're not visible + } + + const float MAX_COLOR = 255; + glColor4f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, _alpha); + + + glDisable(GL_LIGHTING); + glPushMatrix(); + glTranslatef(_position.x + _size * 0.5f, + _position.y + _size * 0.5f, + _position.z + _size * 0.5f); + glLineWidth(_lineWidth); + const int slices = 15; + if (_isSolid) { + glutSolidSphere(_size, slices, slices); + } else { + glutWireSphere(_size, slices, slices); + } + glPopMatrix(); + +} diff --git a/interface/src/ui/Sphere3DOverlay.h b/interface/src/ui/Sphere3DOverlay.h new file mode 100644 index 0000000000..03210866e8 --- /dev/null +++ b/interface/src/ui/Sphere3DOverlay.h @@ -0,0 +1,37 @@ +// +// Sphere3DOverlay.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Sphere3DOverlay__ +#define __interface__Sphere3DOverlay__ + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Base3DOverlay.h" + +class Sphere3DOverlay : public Base3DOverlay { + Q_OBJECT + +public: + Sphere3DOverlay(); + ~Sphere3DOverlay(); + virtual void render(); +}; + + +#endif /* defined(__interface__Sphere3DOverlay__) */ From 6b410253d4bb1f66c6ece7d177695b91c6989183 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 16 Feb 2014 12:42:26 -0800 Subject: [PATCH 58/70] update comments in example --- examples/overlaysExample.js | 54 ++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index 01eff71188..217d3f40f5 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -9,6 +9,11 @@ // // + +// The "Swatches" example of this script will create 9 different image overlays, that use the color feature to +// display different colors as color swatches. The overlays can be clicked on, to change the "selectedSwatch" variable +// and update the image used for the overlay so that it appears to have a selected indicator. +// These are our colors... var swatchColors = new Array(); swatchColors[0] = { red: 255, green: 0, blue: 0}; swatchColors[1] = { red: 0, green: 255, blue: 0}; @@ -20,11 +25,17 @@ swatchColors[6] = { red: 128, green: 128, blue: 128}; swatchColors[7] = { red: 128, green: 0, blue: 0}; swatchColors[8] = { red: 0, green: 240, blue: 240}; -var swatches = new Array(); -var numberOfSwatches = 9; +// The location of the placement of these overlays var swatchesX = 100; var swatchesY = 200; + +// These will be our "overlay IDs" +var swatches = new Array(); +var numberOfSwatches = 9; var selectedSwatch = 0; + +// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image +// location so that it displays the "selected" marker for (s = 0; s < numberOfSwatches; s++) { var imageFromX = 12 + (s * 27); var imageFromY = 0; @@ -44,6 +55,7 @@ for (s = 0; s < numberOfSwatches; s++) { }); } +// This will create a text overlay that when you click on it, the text will change var text = Overlays.addOverlay("text", { x: 200, y: 100, @@ -56,6 +68,7 @@ var text = Overlays.addOverlay("text", { text: "Here is some text.\nAnd a second line." }); +// This will create an image overlay, which starts out as invisible var toolA = Overlays.addOverlay("image", { x: 100, y: 100, @@ -67,6 +80,8 @@ var toolA = Overlays.addOverlay("image", { visible: false }); +// This will create a couple of image overlays that make a "slider", we will demonstrate how to trap mouse messages to +// move the slider var slider = Overlays.addOverlay("image", { // alternate form of expressing bounds bounds: { x: 100, y: 300, width: 158, height: 35}, @@ -75,6 +90,7 @@ var slider = Overlays.addOverlay("image", { alpha: 1 }); +// This is the thumb of our slider var minThumbX = 130; var maxThumbX = minThumbX + 65; var thumbX = (minThumbX + maxThumbX) / 2; @@ -89,17 +105,13 @@ var thumb = Overlays.addOverlay("image", { }); +// We will also demonstrate some 3D overlays. We will create a couple of cubes, spheres, and lines // our 3D cube that moves around... var cubePosition = { x: 2, y: 0, z: 2 }; var cubeSize = 5; var cubeMove = 0.1; var minCubeX = 1; var maxCubeX = 20; -var solidCubePosition = { x: 0, y: 5, z: 0 }; -var solidCubeSize = 2; -var minSolidCubeX = 0; -var maxSolidCubeX = 10; -var solidCubeMove = 0.05; var cube = Overlays.addOverlay("cube", { position: cubePosition, @@ -109,6 +121,11 @@ var cube = Overlays.addOverlay("cube", { solid: false }); +var solidCubePosition = { x: 0, y: 5, z: 0 }; +var solidCubeSize = 2; +var minSolidCubeX = 0; +var maxSolidCubeX = 10; +var solidCubeMove = 0.05; var solidCube = Overlays.addOverlay("cube", { position: solidCubePosition, size: solidCubeSize, @@ -132,6 +149,7 @@ var sphere = Overlays.addOverlay("sphere", { }); +// When our script shuts down, we should clean up all of our overlays function scriptEnding() { Overlays.deleteOverlay(toolA); for (s = 0; s < numberOfSwatches; s++) { @@ -140,14 +158,21 @@ function scriptEnding() { Overlays.deleteOverlay(thumb); Overlays.deleteOverlay(slider); Overlays.deleteOverlay(text); + Overlays.deleteOverlay(cube); + Overlays.deleteOverlay(solidCube); + Overlays.deleteOverlay(sphere); } Script.scriptEnding.connect(scriptEnding); var toolAVisible = false; var count = 0; + +// Our update() function is called at approximately 60fps, and we will use it to animate our various overlays function update() { count++; + + // every second or so, toggle the visibility our our blinking tool if (count % 60 == 0) { if (toolAVisible) { toolAVisible = false; @@ -183,6 +208,7 @@ function update() { Script.willSendVisualDataCallback.connect(update); +// The slider is handled in the mouse event callbacks. var movingSlider = false; var thumbClickOffsetX = 0; function mouseMoveEvent(event) { @@ -197,16 +223,24 @@ function mouseMoveEvent(event) { Overlays.editOverlay(thumb, { x: newThumbX } ); } } + +// we also handle click detection in our mousePressEvent() function mousePressEvent(event) { var clickedText = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + // If the user clicked on the thumb, handle the slider logic if (clickedOverlay == thumb) { movingSlider = true; thumbClickOffsetX = event.x - thumbX; - } else if (clickedOverlay == text) { + + } else if (clickedOverlay == text) { // if the user clicked on the text, update text with where you clicked + Overlays.editOverlay(text, { text: "you clicked here:\n " + event.x + "," + event.y } ); clickedText = true; - } else { + + } else { // if the user clicked on one of the color swatches, update the selectedSwatch + for (s = 0; s < numberOfSwatches; s++) { if (clickedOverlay == swatches[s]) { Overlays.editOverlay(swatches[selectedSwatch], { subImage: { y: 0 } } ); @@ -215,7 +249,7 @@ function mousePressEvent(event) { } } } - if (!clickedText) { + if (!clickedText) { // if you didn't click on the text, then update the text accordningly Overlays.editOverlay(text, { text: "you didn't click here" } ); } } From 78f4df912d84571a9c611ddf2504f18795e4ec84 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 16 Feb 2014 13:25:49 -0800 Subject: [PATCH 59/70] added 3D line overlay support --- examples/overlaysExample.js | 13 ++++++ interface/src/ui/Base3DOverlay.cpp | 35 ++++++---------- interface/src/ui/Base3DOverlay.h | 20 ---------- interface/src/ui/Cube3DOverlay.h | 18 +-------- interface/src/ui/Line3DOverlay.cpp | 60 ++++++++++++++++++++++++++++ interface/src/ui/Line3DOverlay.h | 34 ++++++++++++++++ interface/src/ui/Overlays.cpp | 7 ++++ interface/src/ui/Sphere3DOverlay.h | 18 +-------- interface/src/ui/Volume3DOverlay.cpp | 47 ++++++++++++++++++++++ interface/src/ui/Volume3DOverlay.h | 42 +++++++++++++++++++ 10 files changed, 220 insertions(+), 74 deletions(-) create mode 100644 interface/src/ui/Line3DOverlay.cpp create mode 100644 interface/src/ui/Line3DOverlay.h create mode 100644 interface/src/ui/Volume3DOverlay.cpp create mode 100644 interface/src/ui/Volume3DOverlay.h diff --git a/examples/overlaysExample.js b/examples/overlaysExample.js index 217d3f40f5..b57f82e7e9 100644 --- a/examples/overlaysExample.js +++ b/examples/overlaysExample.js @@ -148,6 +148,14 @@ var sphere = Overlays.addOverlay("sphere", { solid: false }); +var line3d = Overlays.addOverlay("line3d", { + position: { x: 0, y: 0, z:0 }, + end: { x: 10, y: 10, z:10 }, + color: { red: 0, green: 255, blue: 255}, + alpha: 1, + lineWidth: 5 + }); + // When our script shuts down, we should clean up all of our overlays function scriptEnding() { @@ -161,6 +169,7 @@ function scriptEnding() { Overlays.deleteOverlay(cube); Overlays.deleteOverlay(solidCube); Overlays.deleteOverlay(sphere); + Overlays.deleteOverlay(line3d); } Script.scriptEnding.connect(scriptEnding); @@ -204,6 +213,10 @@ function update() { sphereSizeChange = sphereSizeChange * -1; } Overlays.editOverlay(sphere, { size: sphereSize, solid: (sphereSizeChange < 0) } ); + + + // update our 3D line to go from origin to our avatar's position + Overlays.editOverlay(line3d, { end: MyAvatar.position } ); } Script.willSendVisualDataCallback.connect(update); diff --git a/interface/src/ui/Base3DOverlay.cpp b/interface/src/ui/Base3DOverlay.cpp index 23b02a6ac6..67e7ea25f2 100644 --- a/interface/src/ui/Base3DOverlay.cpp +++ b/interface/src/ui/Base3DOverlay.cpp @@ -15,15 +15,11 @@ #include "TextRenderer.h" const glm::vec3 DEFAULT_POSITION = glm::vec3(0.0f, 0.0f, 0.0f); -const float DEFAULT_SIZE = 1.0f; const float DEFAULT_LINE_WIDTH = 1.0f; -const bool DEFAULT_isSolid = false; Base3DOverlay::Base3DOverlay() : _position(DEFAULT_POSITION), - _size(DEFAULT_SIZE), - _lineWidth(DEFAULT_LINE_WIDTH), - _isSolid(DEFAULT_isSolid) + _lineWidth(DEFAULT_LINE_WIDTH) { } @@ -34,6 +30,18 @@ void Base3DOverlay::setProperties(const QScriptValue& properties) { Overlay::setProperties(properties); QScriptValue position = properties.property("position"); + + // if "position" property was not there, check to see if they included aliases: start, point, p1 + if (!position.isValid()) { + position = properties.property("start"); + if (!position.isValid()) { + position = properties.property("p1"); + if (!position.isValid()) { + position = properties.property("point"); + } + } + } + if (position.isValid()) { QScriptValue x = position.property("x"); QScriptValue y = position.property("y"); @@ -47,24 +55,7 @@ void Base3DOverlay::setProperties(const QScriptValue& properties) { } } - if (properties.property("size").isValid()) { - setSize(properties.property("size").toVariant().toFloat()); - } - if (properties.property("lineWidth").isValid()) { setLineWidth(properties.property("lineWidth").toVariant().toFloat()); } - - if (properties.property("isSolid").isValid()) { - setIsSolid(properties.property("isSolid").toVariant().toBool()); - } - if (properties.property("isWire").isValid()) { - setIsSolid(!properties.property("isWire").toVariant().toBool()); - } - if (properties.property("solid").isValid()) { - setIsSolid(properties.property("solid").toVariant().toBool()); - } - if (properties.property("wire").isValid()) { - setIsSolid(!properties.property("wire").toVariant().toBool()); - } } diff --git a/interface/src/ui/Base3DOverlay.h b/interface/src/ui/Base3DOverlay.h index 264ca74326..286193393c 100644 --- a/interface/src/ui/Base3DOverlay.h +++ b/interface/src/ui/Base3DOverlay.h @@ -8,20 +8,6 @@ #ifndef __interface__Base3DOverlay__ #define __interface__Base3DOverlay__ -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - #include "Overlay.h" class Base3DOverlay : public Overlay { @@ -33,23 +19,17 @@ public: // getters const glm::vec3& getPosition() const { return _position; } - float getSize() const { return _size; } float getLineWidth() const { return _lineWidth; } - bool getIsSolid() const { return _isSolid; } // setters void setPosition(const glm::vec3& position) { _position = position; } - void setSize(float size) { _size = size; } void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } - void setIsSolid(bool isSolid) { _isSolid = isSolid; } virtual void setProperties(const QScriptValue& properties); protected: glm::vec3 _position; - float _size; float _lineWidth; - bool _isSolid; }; diff --git a/interface/src/ui/Cube3DOverlay.h b/interface/src/ui/Cube3DOverlay.h index 0033b9c43f..a1705d47d0 100644 --- a/interface/src/ui/Cube3DOverlay.h +++ b/interface/src/ui/Cube3DOverlay.h @@ -8,23 +8,9 @@ #ifndef __interface__Cube3DOverlay__ #define __interface__Cube3DOverlay__ -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" +#include "Volume3DOverlay.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "Base3DOverlay.h" - -class Cube3DOverlay : public Base3DOverlay { +class Cube3DOverlay : public Volume3DOverlay { Q_OBJECT public: diff --git a/interface/src/ui/Line3DOverlay.cpp b/interface/src/ui/Line3DOverlay.cpp new file mode 100644 index 0000000000..c357233329 --- /dev/null +++ b/interface/src/ui/Line3DOverlay.cpp @@ -0,0 +1,60 @@ +// +// Line3DOverlay.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include "Line3DOverlay.h" + + +Line3DOverlay::Line3DOverlay() { +} + +Line3DOverlay::~Line3DOverlay() { +} + +void Line3DOverlay::render() { + if (!_visible) { + return; // do nothing if we're not visible + } + + const float MAX_COLOR = 255; + glDisable(GL_LIGHTING); + glLineWidth(_lineWidth); + glColor4f(_color.red / MAX_COLOR, _color.green / MAX_COLOR, _color.blue / MAX_COLOR, _alpha); + + glBegin(GL_LINES); + glVertex3f(_position.x, _position.y, _position.z); + glVertex3f(_end.x, _end.y, _end.z); + glEnd(); + glEnable(GL_LIGHTING); +} + +void Line3DOverlay::setProperties(const QScriptValue& properties) { + Base3DOverlay::setProperties(properties); + + QScriptValue end = properties.property("end"); + // if "end" property was not there, check to see if they included aliases: endPoint, or p2 + if (!end.isValid()) { + end = properties.property("endPoint"); + if (!end.isValid()) { + end = properties.property("p2"); + } + } + if (end.isValid()) { + QScriptValue x = end.property("x"); + QScriptValue y = end.property("y"); + QScriptValue z = end.property("z"); + if (x.isValid() && y.isValid() && z.isValid()) { + glm::vec3 newEnd; + newEnd.x = x.toVariant().toFloat(); + newEnd.y = y.toVariant().toFloat(); + newEnd.z = z.toVariant().toFloat(); + setEnd(newEnd); + } + } +} diff --git a/interface/src/ui/Line3DOverlay.h b/interface/src/ui/Line3DOverlay.h new file mode 100644 index 0000000000..d52b639d59 --- /dev/null +++ b/interface/src/ui/Line3DOverlay.h @@ -0,0 +1,34 @@ +// +// Line3DOverlay.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Line3DOverlay__ +#define __interface__Line3DOverlay__ + +#include "Base3DOverlay.h" + +class Line3DOverlay : public Base3DOverlay { + Q_OBJECT + +public: + Line3DOverlay(); + ~Line3DOverlay(); + virtual void render(); + + // getters + const glm::vec3& getEnd() const { return _end; } + + // setters + void setEnd(const glm::vec3& end) { _end = end; } + + virtual void setProperties(const QScriptValue& properties); + +protected: + glm::vec3 _end; +}; + + +#endif /* defined(__interface__Line3DOverlay__) */ diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index 902e850fc6..453367ca61 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -8,6 +8,7 @@ #include "Cube3DOverlay.h" #include "ImageOverlay.h" +#include "Line3DOverlay.h" #include "Overlays.h" #include "Sphere3DOverlay.h" #include "TextOverlay.h" @@ -66,6 +67,12 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay->setProperties(properties); created = true; is3D = true; + } else if (type == "line3d") { + thisOverlay = new Line3DOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + created = true; + is3D = true; } if (created) { diff --git a/interface/src/ui/Sphere3DOverlay.h b/interface/src/ui/Sphere3DOverlay.h index 03210866e8..58ed0d7776 100644 --- a/interface/src/ui/Sphere3DOverlay.h +++ b/interface/src/ui/Sphere3DOverlay.h @@ -8,23 +8,9 @@ #ifndef __interface__Sphere3DOverlay__ #define __interface__Sphere3DOverlay__ -// include this before QGLWidget, which includes an earlier version of OpenGL -#include "InterfaceConfig.h" +#include "Volume3DOverlay.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "Base3DOverlay.h" - -class Sphere3DOverlay : public Base3DOverlay { +class Sphere3DOverlay : public Volume3DOverlay { Q_OBJECT public: diff --git a/interface/src/ui/Volume3DOverlay.cpp b/interface/src/ui/Volume3DOverlay.cpp new file mode 100644 index 0000000000..dbc1582cc5 --- /dev/null +++ b/interface/src/ui/Volume3DOverlay.cpp @@ -0,0 +1,47 @@ +// +// Volume3DOverlay.cpp +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include + +#include "Volume3DOverlay.h" + +const float DEFAULT_SIZE = 1.0f; +const bool DEFAULT_IS_SOLID = false; + +Volume3DOverlay::Volume3DOverlay() : + _size(DEFAULT_SIZE), + _isSolid(DEFAULT_IS_SOLID) +{ +} + +Volume3DOverlay::~Volume3DOverlay() { +} + +void Volume3DOverlay::setProperties(const QScriptValue& properties) { + Base3DOverlay::setProperties(properties); + + if (properties.property("size").isValid()) { + setSize(properties.property("size").toVariant().toFloat()); + } + + if (properties.property("isSolid").isValid()) { + setIsSolid(properties.property("isSolid").toVariant().toBool()); + } + if (properties.property("isWire").isValid()) { + setIsSolid(!properties.property("isWire").toVariant().toBool()); + } + if (properties.property("solid").isValid()) { + setIsSolid(properties.property("solid").toVariant().toBool()); + } + if (properties.property("wire").isValid()) { + setIsSolid(!properties.property("wire").toVariant().toBool()); + } +} diff --git a/interface/src/ui/Volume3DOverlay.h b/interface/src/ui/Volume3DOverlay.h new file mode 100644 index 0000000000..8badbf2c33 --- /dev/null +++ b/interface/src/ui/Volume3DOverlay.h @@ -0,0 +1,42 @@ +// +// Volume3DOverlay.h +// interface +// +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +#ifndef __interface__Volume3DOverlay__ +#define __interface__Volume3DOverlay__ + +// include this before QGLWidget, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include +#include + +#include "Base3DOverlay.h" + +class Volume3DOverlay : public Base3DOverlay { + Q_OBJECT + +public: + Volume3DOverlay(); + ~Volume3DOverlay(); + + // getters + float getSize() const { return _size; } + bool getIsSolid() const { return _isSolid; } + + // setters + void setSize(float size) { _size = size; } + void setIsSolid(bool isSolid) { _isSolid = isSolid; } + + virtual void setProperties(const QScriptValue& properties); + +protected: + float _size; + bool _isSolid; +}; + + +#endif /* defined(__interface__Volume3DOverlay__) */ From d101f19e40d98c2f4c7f9f212e2048bc86b23a11 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sun, 16 Feb 2014 13:39:01 -0800 Subject: [PATCH 60/70] removed bounds properties from Overlay so it only lives in Overlay2D --- interface/src/ui/Overlay.cpp | 36 ----------------------------------- interface/src/ui/Overlay.h | 11 ----------- interface/src/ui/Overlays.cpp | 2 +- 3 files changed, 1 insertion(+), 48 deletions(-) diff --git a/interface/src/ui/Overlay.cpp b/interface/src/ui/Overlay.cpp index c6b3902fd6..40da2253f4 100644 --- a/interface/src/ui/Overlay.cpp +++ b/interface/src/ui/Overlay.cpp @@ -33,42 +33,6 @@ Overlay::~Overlay() { } void Overlay::setProperties(const QScriptValue& properties) { - QScriptValue bounds = properties.property("bounds"); - if (bounds.isValid()) { - QRect boundsRect; - boundsRect.setX(bounds.property("x").toVariant().toInt()); - boundsRect.setY(bounds.property("y").toVariant().toInt()); - boundsRect.setWidth(bounds.property("width").toVariant().toInt()); - boundsRect.setHeight(bounds.property("height").toVariant().toInt()); - setBounds(boundsRect); - } else { - QRect oldBounds = getBounds(); - QRect newBounds = oldBounds; - - if (properties.property("x").isValid()) { - newBounds.setX(properties.property("x").toVariant().toInt()); - } else { - newBounds.setX(oldBounds.x()); - } - if (properties.property("y").isValid()) { - newBounds.setY(properties.property("y").toVariant().toInt()); - } else { - newBounds.setY(oldBounds.y()); - } - if (properties.property("width").isValid()) { - newBounds.setWidth(properties.property("width").toVariant().toInt()); - } else { - newBounds.setWidth(oldBounds.width()); - } - if (properties.property("height").isValid()) { - newBounds.setHeight(properties.property("height").toVariant().toInt()); - } else { - newBounds.setHeight(oldBounds.height()); - } - setBounds(newBounds); - //qDebug() << "set bounds to " << getBounds(); - } - QScriptValue color = properties.property("color"); if (color.isValid()) { QScriptValue red = color.property("red"); diff --git a/interface/src/ui/Overlay.h b/interface/src/ui/Overlay.h index 4f1cdc9edb..df898ec741 100644 --- a/interface/src/ui/Overlay.h +++ b/interface/src/ui/Overlay.h @@ -32,21 +32,11 @@ public: // getters bool getVisible() const { return _visible; } - int getX() const { return _bounds.x(); } - int getY() const { return _bounds.y(); } - int getWidth() const { return _bounds.width(); } - int getHeight() const { return _bounds.height(); } - const QRect& getBounds() const { return _bounds; } const xColor& getColor() const { return _color; } float getAlpha() const { return _alpha; } // setters void setVisible(bool visible) { _visible = visible; } - void setX(int x) { _bounds.setX(x); } - void setY(int y) { _bounds.setY(y); } - void setWidth(int width) { _bounds.setWidth(width); } - void setHeight(int height) { _bounds.setHeight(height); } - void setBounds(const QRect& bounds) { _bounds = bounds; } void setColor(const xColor& color) { _color = color; } void setAlpha(float alpha) { _alpha = alpha; } @@ -54,7 +44,6 @@ public: protected: QGLWidget* _parent; - QRect _bounds; // where on the screen to draw float _alpha; xColor _color; bool _visible; // should the overlay be drawn at all diff --git a/interface/src/ui/Overlays.cpp b/interface/src/ui/Overlays.cpp index 453367ca61..c35c4fc5ec 100644 --- a/interface/src/ui/Overlays.cpp +++ b/interface/src/ui/Overlays.cpp @@ -118,7 +118,7 @@ unsigned int Overlays::getOverlayAtPoint(const glm::vec2& point) { while (i.hasPrevious()) { i.previous(); unsigned int thisID = i.key(); - Overlay* thisOverlay = i.value(); + Overlay2D* thisOverlay = static_cast(i.value()); if (thisOverlay->getVisible() && thisOverlay->getBounds().contains(point.x, point.y, false)) { return thisID; } From e4ef09e06d91528233b0f96e1d7bcc42d6d4b293 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 17 Feb 2014 09:12:38 -0800 Subject: [PATCH 61/70] Helper method for adding avatar collision submenu --- interface/src/Menu.cpp | 45 +++++++++++++++++------------------------- interface/src/Menu.h | 2 ++ 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index beb8369f44..697b21cd8c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -168,19 +168,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ClickToFly); - { - // add collision options to the general edit menu - QMenu* collisionsOptionsMenu = editMenu->addMenu("Collision Options"); - QObject* avatar = appInstance->getAvatar(); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithEnvironment, - 0, false, avatar, SLOT(updateCollisionFlags())); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithAvatars, - 0, true, avatar, SLOT(updateCollisionFlags())); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithVoxels, - 0, false, avatar, SLOT(updateCollisionFlags())); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithParticles, - 0, true, avatar, SLOT(updateCollisionFlags())); - } + addAvatarCollisionSubMenu(editMenu); QMenu* toolsMenu = addMenu("Tools"); @@ -348,19 +336,7 @@ Menu::Menu() : SLOT(setTCPEnabled(bool))); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false); - { - // add collision options to the avatar menu - QMenu* collisionsOptionsMenu = avatarOptionsMenu->addMenu("Collision Options"); - QObject* avatar = appInstance->getAvatar(); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithEnvironment, - 0, false, avatar, SLOT(updateCollisionFlags())); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithAvatars, - 0, true, avatar, SLOT(updateCollisionFlags())); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithVoxels, - 0, false, avatar, SLOT(updateCollisionFlags())); - addCheckableActionToQMenuAndActionHash(collisionsOptionsMenu, MenuOption::CollideWithParticles, - 0, true, avatar, SLOT(updateCollisionFlags())); - } + addAvatarCollisionSubMenu(avatarOptionsMenu); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); @@ -1230,6 +1206,22 @@ void Menu::updateFrustumRenderModeAction() { } } +void Menu::addAvatarCollisionSubMenu(QMenu* overMenu) { + // add avatar collisions subMenu to overMenu + QMenu* subMenu = overMenu->addMenu("Collision Options"); + + Application* appInstance = Application::getInstance(); + QObject* avatar = appInstance->getAvatar(); + addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithEnvironment, + 0, false, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithAvatars, + 0, true, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithVoxels, + 0, false, avatar, SLOT(updateCollisionFlags())); + addCheckableActionToQMenuAndActionHash(subMenu, MenuOption::CollideWithParticles, + 0, true, avatar, SLOT(updateCollisionFlags())); +} + QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string) { int lastIndex; lastIndex = string.lastIndexOf(search); @@ -1240,4 +1232,3 @@ QString Menu::replaceLastOccurrence(QChar search, QChar replace, QString string) return string; } - diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 84f5325e8f..1ac5341ed9 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -139,6 +139,8 @@ private: void updateFrustumRenderModeAction(); + void addAvatarCollisionSubMenu(QMenu* overMenu); + QHash _actionHash; int _audioJitterBufferSamples; /// number of extra samples to wait before starting audio playback BandwidthDialog* _bandwidthDialog; From b28a18dab04f842f3a23d349c02307baacb55484 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 17 Feb 2014 09:17:34 -0800 Subject: [PATCH 62/70] Remove warning about null-op sprintf() in g++ --- libraries/octree/src/OctreeSceneStats.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/octree/src/OctreeSceneStats.cpp b/libraries/octree/src/OctreeSceneStats.cpp index 59287e3c5c..8a5a731cff 100644 --- a/libraries/octree/src/OctreeSceneStats.cpp +++ b/libraries/octree/src/OctreeSceneStats.cpp @@ -791,7 +791,6 @@ const char* OctreeSceneStats::getItemValue(Item item) { break; } default: - sprintf(_itemValueBuffer, ""); break; } return _itemValueBuffer; From 9e7597a6ada5fc945d06d02d4f1cc8f9b51ccce0 Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 17 Feb 2014 19:06:55 +0100 Subject: [PATCH 63/70] bad merge fix --- interface/src/Menu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 81cb5fd8d9..7ee13e0cb1 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -117,7 +117,6 @@ private slots: void login(); void editPreferences(); void goToDomainDialog(); - void goToDomain(); void goToLocation(); void bandwidthDetailsClosed(); void voxelStatsDetailsClosed(); From 2ba742675755394df0c350a224a26b33da2ecc97 Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 17 Feb 2014 19:25:53 +0100 Subject: [PATCH 64/70] windows build fix --- interface/src/ui/Snapshot.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index bd4de19c86..d0a38eec4e 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -14,8 +14,6 @@ #include #include -#include - // filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg // %1 <= username, %2 <= date and time, %3 <= current location const QString FILENAME_PATH_FORMAT = "hifi-snap-by-%1-on-%2@%3.jpg"; From 677e6f5ce9372e1c2f88daa9d48e1dda6c0f0126 Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 17 Feb 2014 20:19:35 +0100 Subject: [PATCH 65/70] windows build fix (2) --- interface/src/ui/Snapshot.h | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 05a2fc3147..5ee8fb4f8d 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -13,6 +13,7 @@ #include #include +#include #include "avatar/MyAvatar.h" #include "avatar/Profile.h" From 0df0c23c327f1a486b01a95af8974e3fe92b398a Mon Sep 17 00:00:00 2001 From: stojce Date: Mon, 17 Feb 2014 21:14:28 +0100 Subject: [PATCH 66/70] Windows build fix (3) --- interface/src/ui/Snapshot.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 5ee8fb4f8d..57a388020d 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -9,6 +9,8 @@ #ifndef __hifi__Snapshot__ #define __hifi__Snapshot__ +#include "InterfaceConfig.h" + #include #include #include From 6c2f3b687e7188b8ae31ff91a9a1e7410667fb89 Mon Sep 17 00:00:00 2001 From: stojce Date: Tue, 18 Feb 2014 01:50:55 +0100 Subject: [PATCH 67/70] removed unnecessary references --- interface/src/ui/Snapshot.cpp | 1 - interface/src/ui/Snapshot.h | 1 - 2 files changed, 2 deletions(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index d0a38eec4e..8cfc759308 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -12,7 +12,6 @@ #include #include -#include // filename format: hifi-snap-by-%username%-on-%date%_%time%_@-%location%.jpg // %1 <= username, %2 <= date and time, %3 <= current location diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 57a388020d..14c59552ec 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -15,7 +15,6 @@ #include #include -#include #include "avatar/MyAvatar.h" #include "avatar/Profile.h" From d36ced9b92884b7e4aa383cd36d3f6a3bcbed18e Mon Sep 17 00:00:00 2001 From: stojce Date: Tue, 18 Feb 2014 07:33:03 +0100 Subject: [PATCH 68/70] fixed misplaced #endif --- interface/src/devices/Transmitter.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/interface/src/devices/Transmitter.h b/interface/src/devices/Transmitter.h index 1fa392a280..12f2b302f7 100644 --- a/interface/src/devices/Transmitter.h +++ b/interface/src/devices/Transmitter.h @@ -45,5 +45,6 @@ private: TouchState _touchState; timeval* _lastReceivedPacket; -#endif /* defined(__hifi__Transmitter__) */ }; + +#endif /* defined(__hifi__Transmitter__) */ From 7621d70c2453a73f4f5997b602ca16aa1ee381dd Mon Sep 17 00:00:00 2001 From: stojce Date: Tue, 18 Feb 2014 08:11:59 +0100 Subject: [PATCH 69/70] use base class - replaced MyAvatar with Avatar --- interface/src/ui/Snapshot.cpp | 2 +- interface/src/ui/Snapshot.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 8cfc759308..e16b0c570d 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -60,7 +60,7 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { return data; } -void Snapshot::saveSnapshot(QGLWidget* widget, Profile* profile, MyAvatar* avatar) { +void Snapshot::saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar) { QImage shot = widget->grabFrameBuffer(); glm::vec3 location = avatar->getPosition(); diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index 14c59552ec..13c6945349 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -15,7 +15,7 @@ #include #include -#include "avatar/MyAvatar.h" +#include "avatar/Avatar.h" #include "avatar/Profile.h" class SnapshotMetaData { @@ -39,7 +39,7 @@ private: class Snapshot { public: - static void saveSnapshot(QGLWidget* widget, Profile* profile, MyAvatar* avatar); + static void saveSnapshot(QGLWidget* widget, Profile* profile, Avatar* avatar); static SnapshotMetaData* parseSnapshotData(QString snapshotPath); }; From b8673db9aa8557188a4b907596efa0dead4ae26c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 18 Feb 2014 09:58:14 -0800 Subject: [PATCH 70/70] remove unneeded example --- examples/audioBallLifetime.js | 54 ----------------------------------- 1 file changed, 54 deletions(-) delete mode 100644 examples/audioBallLifetime.js diff --git a/examples/audioBallLifetime.js b/examples/audioBallLifetime.js deleted file mode 100644 index affb75f04d..0000000000 --- a/examples/audioBallLifetime.js +++ /dev/null @@ -1,54 +0,0 @@ -// -// audioBall.js -// hifi -// -// Created by Athanasios Gaitatzes on 2/10/14. -// Copyright (c) 2014 HighFidelity, Inc. All rights reserved. -// -// This script creates a particle in front of the user that stays in front of -// the user's avatar as they move, and animates it's radius and color -// in response to the audio intensity. -// - -var sound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-public/sounds/Animals/mexicanWhipoorwill.raw"); -var CHANCE_OF_PLAYING_SOUND = 0.01; - -var FACTOR = 0.75; - -function addParticle() { - // the particle should be placed in front of the user's avatar - var avatarFront = Quat.getFront(MyAvatar.orientation); - - // move particle three units in front of the avatar - var particlePosition = Vec3.sum(MyAvatar.position, Vec3.multiply (avatarFront, 3)); - - if (Math.random() < CHANCE_OF_PLAYING_SOUND) { - // play a sound at the location of the particle - var options = new AudioInjectionOptions(); - options.position = particlePosition; - options.volume = 0.25; - Audio.playSound(sound, options); - } - - var audioAverageLoudness = MyAvatar.audioAverageLoudness * FACTOR; - //print ("Audio Loudness = " + MyAvatar.audioLoudness + " -- Audio Average Loudness = " + MyAvatar.audioAverageLoudness); - - // animates the particles radius and color in response to the changing audio intensity - var particleProperies = { - position: particlePosition // the particle should stay in front of the user's avatar as he moves - , color: { red: 0, green: 255 * audioAverageLoudness, blue: 0 } - , radius: audioAverageLoudness - , velocity: { x: 0.0, y: 0.0, z: 0.0 } - , gravity: { x: 0.0, y: 0.0, z: 0.0 } - , damping: 0.0 - , lifetime: 0.05 - } - - Particles.addParticle (particleProperies); -} - -// register the call back so it fires before each data send -Script.willSendVisualDataCallback.connect(addParticle); - -// register our scriptEnding callback -Script.scriptEnding.connect(function scriptEnding() {});