From b35134e325aceb22adc6ec0836dc9e29057f38d2 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Sat, 17 May 2014 20:38:43 +0530 Subject: [PATCH 1/8] Fix the chat widget's visibility when not logged in --- interface/src/Menu.cpp | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index bff08d5221..c79d4618e5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -34,6 +34,7 @@ #include #include "Application.h" +#include "AccountManager.h" #include "Menu.h" #include "scripting/MenuScriptingInterface.h" #include "Util.h" @@ -193,10 +194,10 @@ Menu::Menu() : QAction::PreferencesRole); addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, this, SLOT(editAttachments())); - + addDisabledActionAndSeparator(editMenu, "Physics"); QObject* avatar = appInstance->getAvatar(); - addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false, + addCheckableActionToQMenuAndActionHash(editMenu, MenuOption::ObeyEnvironmentalGravity, Qt::SHIFT | Qt::Key_G, false, avatar, SLOT(updateMotionBehaviorsFromMenu())); @@ -1013,24 +1014,24 @@ void Menu::multipleDestinationsDecision(const QJsonObject& userData, const QJson void Menu::muteEnvironment() { int headerSize = numBytesForPacketHeaderGivenPacketType(PacketTypeMuteEnvironment); int packetSize = headerSize + sizeof(glm::vec3) + sizeof(float); - + glm::vec3 position = Application::getInstance()->getAvatar()->getPosition(); - + char* packet = (char*)malloc(packetSize); populatePacketHeader(packet, PacketTypeMuteEnvironment); memcpy(packet + headerSize, &position, sizeof(glm::vec3)); memcpy(packet + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float)); - + QByteArray mutePacket(packet, packetSize); - + // grab our audio mixer from the NodeList, if it exists SharedNodePointer audioMixer = NodeList::getInstance()->soloNodeOfType(NodeType::AudioMixer); - + if (audioMixer) { // send off this mute packet NodeList::getInstance()->writeDatagram(mutePacket, audioMixer); } - + free(packet); } @@ -1188,19 +1189,22 @@ void Menu::showScriptEditor() { } void Menu::showChat() { - QMainWindow* mainWindow = Application::getInstance()->getWindow(); - if (!_chatWindow) { - _chatWindow = new ChatWindow(mainWindow); - } - if (_chatWindow->isHidden()) { - _chatWindow->show(); + if (AccountManager::getInstance().isLoggedIn()) { + QMainWindow* mainWindow = Application::getInstance()->getWindow(); + if (!_chatWindow) { + _chatWindow = new ChatWindow(mainWindow); + } + + if (_chatWindow->isHidden()) { + _chatWindow->show(); + } } } void Menu::toggleChat() { #ifdef HAVE_QXMPP _chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected()); - if (!_chatAction->isEnabled() && _chatWindow) { + if (!_chatAction->isEnabled() && _chatWindow && AccountManager::getInstance().isLoggedIn()) { if (_chatWindow->isHidden()) { _chatWindow->show(); } else { From ad49b5c2e15846ab4d5178e1cff88e81c8783beb Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Thu, 22 May 2014 16:53:07 +0530 Subject: [PATCH 2/8] Show OS notification when user is not logged in --- interface/src/Application.cpp | 109 +++++++++++++++++----------------- interface/src/Application.h | 6 +- interface/src/Menu.cpp | 3 + 3 files changed, 64 insertions(+), 54 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 17443b3cb0..79015185c6 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -172,11 +172,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _previousScriptLocation(), _nodeBoundsDisplay(this), _runningScriptsWidget(new RunningScriptsWidget(_window)), - _runningScriptsWidgetWasVisible(false) + _runningScriptsWidgetWasVisible(false), + _trayIcon(new QSystemTrayIcon(_window)) { // init GnuTLS for DTLS with domain-servers DTLSClientSession::globalInit(); - + // read the ApplicationInfo.ini file for Name/Version/Domain information QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat); @@ -238,10 +239,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : connect(&nodeList->getDomainHandler(), SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); - + // update our location every 5 seconds in the data-server, assuming that we are authenticated with one const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5.0f * 1000.0f; - + QTimer* locationUpdateTimer = new QTimer(this); connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer); locationUpdateTimer->start(DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS); @@ -337,7 +338,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // when -url in command line, teleport to location urlGoTo(argc, constArgv); - + // For now we're going to set the PPS for outbound packets to be super high, this is // probably not the right long term solution. But for now, we're going to do this to // allow you to move a particle around in your hand @@ -364,27 +365,29 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // clear the scripts, and set out script to our default scripts clearScriptsBeforeRunning(); loadScript("http://public.highfidelity.io/scripts/defaultScripts.js"); - + QMutexLocker locker(&_settingsMutex); _settings->setValue("firstRun",QVariant(false)); } else { // do this as late as possible so that all required subsystems are inialized loadScripts(); - + QMutexLocker locker(&_settingsMutex); _previousScriptLocation = _settings->value("LastScriptLocation", QVariant("")).toString(); } - + connect(_window, &MainWindow::windowGeometryChanged, _runningScriptsWidget, &RunningScriptsWidget::setBoundary); - - //When -url in command line, teleport to location - urlGoTo(argc, constArgv); - + + //When -url in command line, teleport to location + urlGoTo(argc, constArgv); + // call the OAuthWebviewHandler static getter so that its instance lives in our thread OAuthWebViewHandler::getInstance(); // make sure the High Fidelity root CA is in our list of trusted certs OAuthWebViewHandler::addHighFidelityRootCAToSSLConfig(); + + _trayIcon->show(); } Application::~Application() { @@ -393,11 +396,11 @@ Application::~Application() { // make sure we don't call the idle timer any more delete idleTimer; - + _sharedVoxelSystem.changeTree(new VoxelTree); - + saveSettings(); - + delete _voxelImporter; // let the avatar mixer know we're out @@ -431,14 +434,14 @@ Application::~Application() { delete _glWidget; AccountManager::getInstance().destroy(); - + DTLSClientSession::globalDeinit(); } void Application::saveSettings() { Menu::getInstance()->saveSettings(); _rearMirrorTools->saveSettings(_settings); - + if (_voxelImporter) { _voxelImporter->saveSettings(_settings); } @@ -520,7 +523,7 @@ void Application::initializeGL() { _voxelHideShowThread.initialize(_enableProcessVoxelsThread); _particleEditSender.initialize(_enableProcessVoxelsThread); _modelEditSender.initialize(_enableProcessVoxelsThread); - + if (_enableProcessVoxelsThread) { qDebug("Voxel parsing thread created."); } @@ -585,7 +588,7 @@ void Application::paintGL() { _myCamera.setDistance(MIRROR_FULLSCREEN_DISTANCE * _myAvatar->getScale() * _scaleMirror); _myCamera.setTargetPosition(_myAvatar->getPosition() + glm::vec3(0, headHeight + (_raiseMirror * _myAvatar->getScale()), 0)); _myCamera.setTargetRotation(_myAvatar->getWorldAlignedOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f))); - + // if the head would intersect the near clip plane, we must push the camera out glm::vec3 relativePosition = glm::inverse(_myCamera.getTargetRotation()) * (eyePosition - _myCamera.getTargetPosition()); @@ -594,7 +597,7 @@ void Application::paintGL() { pushback = relativePosition.z + pushbackRadius - _myCamera.getDistance(); pushbackFocalLength = _myCamera.getDistance(); } - + // handle pushback, if any if (pushbackFocalLength > 0.0f) { const float PUSHBACK_DECAY = 0.5f; @@ -1279,7 +1282,7 @@ void Application::dropEvent(QDropEvent *event) { void Application::sendPingPackets() { QByteArray pingPacket = NodeList::getInstance()->constructPingPacket(); - controlledBroadcastToNodes(pingPacket, NodeSet() + controlledBroadcastToNodes(pingPacket, NodeSet() << NodeType::VoxelServer << NodeType::ParticleServer << NodeType::ModelServer << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::MetavoxelServer); @@ -1290,7 +1293,7 @@ void Application::timer() { if (Menu::getInstance()->isOptionChecked(MenuOption::TestPing)) { sendPingPackets(); } - + float diffTime = (float)_timerStart.nsecsElapsed() / 1000000000.0f; _fps = (float)_frameCount / diffTime; @@ -1694,7 +1697,7 @@ void Application::init() { connect(_rearMirrorTools, SIGNAL(restoreView()), SLOT(restoreMirrorView())); connect(_rearMirrorTools, SIGNAL(shrinkView()), SLOT(shrinkMirrorView())); connect(_rearMirrorTools, SIGNAL(resetView()), SLOT(resetSensors())); - + // set up our audio reflector _audioReflector.setMyAvatar(getAvatar()); _audioReflector.setVoxels(_voxels.getTree()); @@ -1703,7 +1706,7 @@ void Application::init() { connect(getAudio(), &Audio::processInboundAudio, &_audioReflector, &AudioReflector::processInboundAudio,Qt::DirectConnection); connect(getAudio(), &Audio::processLocalAudio, &_audioReflector, &AudioReflector::processLocalAudio,Qt::DirectConnection); - connect(getAudio(), &Audio::preProcessOriginalInboundAudio, &_audioReflector, + connect(getAudio(), &Audio::preProcessOriginalInboundAudio, &_audioReflector, &AudioReflector::preProcessOriginalInboundAudio,Qt::DirectConnection); // save settings when avatar changes @@ -1818,7 +1821,7 @@ void Application::updateMyAvatarLookAtPosition() { PerformanceWarning warn(showWarnings, "Application::updateMyAvatarLookAtPosition()"); FaceTracker* tracker = getActiveFaceTracker(); - + bool isLookingAtSomeone = false; glm::vec3 lookAtSpot; if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { @@ -1853,7 +1856,7 @@ void Application::updateMyAvatarLookAtPosition() { glm::distance(_mouseRayOrigin, _myAvatar->getHead()->calculateAverageEyePosition())); lookAtSpot = _mouseRayOrigin + _mouseRayDirection * qMax(minEyeDistance, distance); */ - + } // // Deflect the eyes a bit to match the detected Gaze from 3D camera if active @@ -1873,7 +1876,7 @@ void Application::updateMyAvatarLookAtPosition() { eyePitch * pitchSign * deflection, eyeYaw * deflection, 0.0f))) * glm::inverse(_myCamera.getRotation()) * (lookAtSpot - origin); } - + _myAvatar->getHead()->setLookAtPosition(lookAtSpot); } @@ -1925,7 +1928,7 @@ void Application::updateCamera(float deltaTime) { PerformanceWarning warn(showWarnings, "Application::updateCamera()"); if (!OculusManager::isConnected() && !TV3DManager::isConnected() && - Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) { + Menu::getInstance()->isOptionChecked(MenuOption::OffAxisProjection)) { FaceTracker* tracker = getActiveFaceTracker(); if (tracker) { const float EYE_OFFSET_SCALE = 0.025f; @@ -2481,7 +2484,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { // disable specular lighting for ground and voxels glMaterialfv(GL_FRONT, GL_SPECULAR, NO_SPECULAR_COLOR); - + // draw the audio reflector overlay _audioReflector.render(); @@ -2649,7 +2652,7 @@ void Application::displayOverlay() { const float LOG2_LOUDNESS_FLOOR = 11.f; float audioLevel = 0.f; float loudness = _audio.getLastInputLoudness() + 1.f; - + _trailingAudioLoudness = AUDIO_METER_AVERAGING * _trailingAudioLoudness + (1.f - AUDIO_METER_AVERAGING) * loudness; float log2loudness = log(_trailingAudioLoudness) / LOG2; @@ -2662,7 +2665,7 @@ void Application::displayOverlay() { audioLevel = AUDIO_METER_SCALE_WIDTH; } bool isClipping = ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)); - + if ((_audio.getTimeSinceLastClip() > 0.f) && (_audio.getTimeSinceLastClip() < CLIPPING_INDICATOR_TIME)) { const float MAX_MAGNITUDE = 0.7f; float magnitude = MAX_MAGNITUDE * (1 - _audio.getTimeSinceLastClip() / CLIPPING_INDICATOR_TIME); @@ -2768,7 +2771,7 @@ void Application::displayOverlay() { // give external parties a change to hook in emit renderingOverlay(); - + _overlays.render2D(); glPopMatrix(); @@ -2844,7 +2847,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { // save absolute translations glm::vec3 absoluteSkeletonTranslation = _myAvatar->getSkeletonModel().getTranslation(); glm::vec3 absoluteFaceTranslation = _myAvatar->getHead()->getFaceModel().getTranslation(); - + // get the eye positions relative to the neck and use them to set the face translation glm::vec3 leftEyePosition, rightEyePosition; _myAvatar->getHead()->getFaceModel().setTranslation(glm::vec3()); @@ -3110,7 +3113,7 @@ void Application::uploadModel(ModelType modelType) { thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit())); thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); uploader->connect(thread, SIGNAL(started()), SLOT(send())); - + thread->start(); } @@ -3127,28 +3130,28 @@ void Application::updateWindowTitle(){ } void Application::updateLocationInServer() { - + AccountManager& accountManager = AccountManager::getInstance(); - + if (accountManager.isLoggedIn()) { - + static QJsonObject lastLocationObject; - + // construct a QJsonObject given the user's current address information QJsonObject updatedLocationObject; - + QJsonObject addressObject; addressObject.insert("position", QString(createByteArray(_myAvatar->getPosition()))); addressObject.insert("orientation", QString(createByteArray(glm::degrees(safeEulerAngles(_myAvatar->getOrientation()))))); addressObject.insert("domain", NodeList::getInstance()->getDomainHandler().getHostname()); - + updatedLocationObject.insert("address", addressObject); - + if (updatedLocationObject != lastLocationObject) { - + accountManager.authenticatedRequest("/api/v1/users/address", QNetworkAccessManager::PutOperation, JSONCallbackParameters(), QJsonDocument(updatedLocationObject).toJson()); - + lastLocationObject = updatedLocationObject; } } @@ -3173,7 +3176,7 @@ void Application::domainChanged(const QString& domainHostname) { // reset the voxels renderer _voxels.killLocalVoxels(); - + // reset the auth URL for OAuth web view handler OAuthWebViewHandler::getInstance().clearLastAuthorizationURL(); } @@ -3394,7 +3397,7 @@ void Application::loadScripts() { loadScript(string); } } - + QMutexLocker locker(&_settingsMutex); _settings->endArray(); } @@ -3655,7 +3658,7 @@ void Application::parseVersionXml() { QObject* sender = QObject::sender(); QXmlStreamReader xml(qobject_cast(sender)); - + while (!xml.atEnd() && !xml.hasError()) { if (xml.tokenType() == QXmlStreamReader::StartElement && xml.name() == operatingSystem) { while (!(xml.tokenType() == QXmlStreamReader::EndElement && xml.name() == operatingSystem)) { @@ -3672,7 +3675,7 @@ void Application::parseVersionXml() { } xml.readNext(); } - + if (!shouldSkipVersion(latestVersion) && applicationVersion() != latestVersion) { new UpdateDialog(_glWidget, releaseNotes, latestVersion, downloadUrl); } @@ -3724,24 +3727,24 @@ void Application::urlGoTo(int argc, const char * constArgv[]) { } else if (urlParts.count() > 1) { // if url has 2 or more parts, the first one is domain name QString domain = urlParts[0]; - + // second part is either a destination coordinate or // a place name QString destination = urlParts[1]; - + // any third part is an avatar orientation. QString orientation = urlParts.count() > 2 ? urlParts[2] : QString(); - + Menu::goToDomain(domain); - + // goto either @user, #place, or x-xx,y-yy,z-zz // style co-ordinate. Menu::goTo(destination); - + if (!orientation.isEmpty()) { // location orientation Menu::goToOrientation(orientation); } - } + } } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 5460093cbd..36076218b1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -203,6 +204,7 @@ public: JoystickManager* getJoystickManager() { return &_joystickManager; } BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; } QUndoStack* getUndoStack() { return &_undoStack; } + QSystemTrayIcon* getTrayIcon() { return _trayIcon; } /// if you need to access the application settings, use lockSettings()/unlockSettings() QSettings* lockSettings() { _settingsMutex.lock(); return _settings; } @@ -466,7 +468,7 @@ private: glm::mat4 _untranslatedViewMatrix; glm::vec3 _viewMatrixTranslation; glm::mat4 _projectionMatrix; - + float _scaleMirror; float _rotateMirror; float _raiseMirror; @@ -553,6 +555,8 @@ private: RunningScriptsWidget* _runningScriptsWidget; QHash _scriptEnginesHash; bool _runningScriptsWidgetWasVisible; + + QSystemTrayIcon* _trayIcon; }; #endif // hifi_Application_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c79d4618e5..8021816db9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1198,6 +1199,8 @@ void Menu::showChat() { if (_chatWindow->isHidden()) { _chatWindow->show(); } + } else { + Application::getInstance()->getTrayIcon()->showMessage("Interface", "You need to login to be able to chat with others on this domain."); } } From c4b17963e2d78c2ce9c6002d7f76aa77392a1a38 Mon Sep 17 00:00:00 2001 From: Mohammed Nafees Date: Thu, 22 May 2014 16:55:24 +0530 Subject: [PATCH 3/8] Remove unnecessory header file inclusion in Menu.cpp --- interface/src/Menu.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8021816db9..c40058f89b 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include From 70ab33b65b95f3cc93fd880ac6033dfdf8cecfc1 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 27 May 2014 10:54:18 -0700 Subject: [PATCH 4/8] Some tricks to avoid shadow artifacts: render back faces to shadow buffer, use polygon offset. --- interface/src/Application.cpp | 5 +++++ interface/src/renderer/Model.cpp | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b5d9d8939c..cad6c26a00 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2325,10 +2325,15 @@ void Application::updateShadowMap() { // store view matrix without translation, which we'll use for precision-sensitive objects updateUntranslatedViewMatrix(); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(1.1f, 4.0f); + _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); _particles.render(OctreeRenderer::SHADOW_RENDER_MODE); _models.render(OctreeRenderer::SHADOW_RENDER_MODE); + glDisable(GL_POLYGON_OFFSET_FILL); + glPopMatrix(); glMatrixMode(GL_PROJECTION); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5337e845a1..c4cc312091 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -498,6 +498,9 @@ bool Model::render(float alpha, RenderMode mode) { glDisable(GL_CULL_FACE); } else { glEnable(GL_CULL_FACE); + if (mode == SHADOW_RENDER_MODE) { + glCullFace(GL_FRONT); + } } // render opaque meshes with alpha testing @@ -515,6 +518,10 @@ bool Model::render(float alpha, RenderMode mode) { glDisable(GL_CULL_FACE); + if (mode == SHADOW_RENDER_MODE) { + glCullFace(GL_BACK); + } + // deactivate vertex arrays after drawing glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); From edaa251e5f4b274b31bca21daa437adaa8ad6ec4 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 27 May 2014 11:43:34 -0700 Subject: [PATCH 5/8] Provide option to disable avatar shadowing. --- interface/src/Application.cpp | 2 +- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + interface/src/avatar/Avatar.cpp | 7 ++++--- interface/src/avatar/Head.cpp | 3 ++- interface/src/avatar/MyAvatar.cpp | 5 +++-- interface/src/renderer/Model.cpp | 10 +++++----- interface/src/renderer/Model.h | 4 ++-- 8 files changed, 19 insertions(+), 14 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cad6c26a00..1addb7bafd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2326,7 +2326,7 @@ void Application::updateShadowMap() { updateUntranslatedViewMatrix(); glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(1.1f, 4.0f); + glPolygonOffset(1.1f, 4.0f); // magic numbers courtesy http://www.eecs.berkeley.edu/~ravir/6160/papers/shadowmaps.ppt _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); _particles.render(OctreeRenderer::SHADOW_RENDER_MODE); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 856e1efae7..68e3b484d3 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -332,6 +332,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AllowOculusCameraModeChange, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); + addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::AvatarsReceiveShadows, 0, true); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderSkeletonCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderHeadCollisionShapes); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::RenderBoundingCollisionShapes); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 279d2151c9..70f4f62ce4 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -293,6 +293,7 @@ namespace MenuOption { const QString AudioSpatialProcessingDontDistanceAttenuate = "Don't calculate distance attenuation"; const QString AudioSpatialProcessingAlternateDistanceAttenuate = "Alternate distance attenuation"; const QString Avatars = "Avatars"; + const QString AvatarsReceiveShadows = "Avatars Receive Shadows"; const QString Bandwidth = "Bandwidth Display"; const QString BandwidthDetails = "Bandwidth Details"; const QString BuckyBalls = "Bucky Balls"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index aff15f4e0b..306dc0194e 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -347,7 +347,6 @@ glm::quat Avatar::computeRotationFromBodyToWorldUp(float proportion) const { void Avatar::renderBody(RenderMode renderMode, float glowLevel) { Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; - { Glower glower(glowLevel); @@ -356,7 +355,8 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { renderBillboard(); return; } - _skeletonModel.render(1.0f, modelRenderMode); + + _skeletonModel.render(1.0f, modelRenderMode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows)); renderAttachments(renderMode); getHand()->render(false, modelRenderMode); } @@ -390,8 +390,9 @@ void Avatar::simulateAttachments(float deltaTime) { void Avatar::renderAttachments(RenderMode renderMode) { Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + bool receiveShadows = Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows); foreach (Model* model, _attachmentModels) { - model->render(1.0f, modelRenderMode); + model->render(1.0f, modelRenderMode, receiveShadows); } } diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 2d0599b31f..8382843c1e 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -176,7 +176,8 @@ void Head::relaxLean(float deltaTime) { } void Head::render(float alpha, Model::RenderMode mode) { - if (_faceModel.render(alpha, mode) && _renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) { + if (_faceModel.render(alpha, mode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows)) && + _renderLookatVectors && mode != Model::SHADOW_RENDER_MODE) { renderLookatVectors(_leftEyePosition, _rightEyePosition, _lookAtPosition); } } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a598c55aaa..f47880056c 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -773,7 +773,7 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { // Render the body's voxels and head Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; - _skeletonModel.render(1.0f, modelRenderMode); + _skeletonModel.render(1.0f, modelRenderMode, Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows)); renderAttachments(renderMode); // Render head so long as the camera isn't inside it @@ -1695,10 +1695,11 @@ void MyAvatar::renderAttachments(RenderMode renderMode) { QString headJointName = (geometry.headJointIndex == -1) ? QString() : geometry.joints.at(geometry.headJointIndex).name; Model::RenderMode modelRenderMode = (renderMode == SHADOW_RENDER_MODE) ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + bool receiveShadows = Menu::getInstance()->isOptionChecked(MenuOption::AvatarsReceiveShadows); for (int i = 0; i < _attachmentData.size(); i++) { const QString& jointName = _attachmentData.at(i).jointName; if (jointName != headJointName && jointName != "Head") { - _attachmentModels.at(i)->render(1.0f, modelRenderMode); + _attachmentModels.at(i)->render(1.0f, modelRenderMode, receiveShadows); } } } diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index c4cc312091..db2f6e0664 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -470,7 +470,7 @@ bool Model::updateGeometry() { return needFullUpdate; } -bool Model::render(float alpha, RenderMode mode) { +bool Model::render(float alpha, RenderMode mode, bool receiveShadows) { // render the attachments foreach (Model* attachment, _attachments) { attachment->render(alpha, mode); @@ -508,13 +508,14 @@ bool Model::render(float alpha, RenderMode mode) { glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5f * alpha); - renderMeshes(alpha, mode, false); + receiveShadows &= Menu::getInstance()->isOptionChecked(MenuOption::Shadows); + renderMeshes(alpha, mode, false, receiveShadows); glDisable(GL_ALPHA_TEST); // render translucent meshes afterwards - renderMeshes(alpha, mode, true); + renderMeshes(alpha, mode, true, receiveShadows); glDisable(GL_CULL_FACE); @@ -1596,11 +1597,10 @@ void Model::deleteGeometry() { } } -void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) { +void Model::renderMeshes(float alpha, RenderMode mode, bool translucent, bool receiveShadows) { const FBXGeometry& geometry = _geometry->getFBXGeometry(); const QVector& networkMeshes = _geometry->getMeshes(); - bool receiveShadows = Menu::getInstance()->isOptionChecked(MenuOption::Shadows); if (receiveShadows) { glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]); glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 5b1f6402d3..69ea700b49 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -79,7 +79,7 @@ public: enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE }; - bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE); + bool render(float alpha = 1.0f, RenderMode mode = DEFAULT_RENDER_MODE, bool receiveShadows = true); /// Sets the URL of the model to render. /// \param fallback the URL of a fallback model to render if the requested model fails to load @@ -315,7 +315,7 @@ private: void applyNextGeometry(); void deleteGeometry(); - void renderMeshes(float alpha, RenderMode mode, bool translucent); + void renderMeshes(float alpha, RenderMode mode, bool translucent, bool receiveShadows); QVector createJointStates(const FBXGeometry& geometry); QSharedPointer _baseGeometry; ///< reference required to prevent collection of base From e22d239d4d4041b32f05f323c5b991e809736d40 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 27 May 2014 12:30:45 -0700 Subject: [PATCH 6/8] What the heck; let's try using some multisampling on the shadow maps. --- interface/resources/shaders/model_shadow_map.frag | 9 ++++++++- interface/resources/shaders/model_shadow_normal_map.frag | 9 ++++++++- .../shaders/model_shadow_normal_specular_map.frag | 9 ++++++++- .../resources/shaders/model_shadow_specular_map.frag | 9 ++++++++- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/interface/resources/shaders/model_shadow_map.frag b/interface/resources/shaders/model_shadow_map.frag index 4a2bb312dc..aa1df03b95 100644 --- a/interface/resources/shaders/model_shadow_map.frag +++ b/interface/resources/shaders/model_shadow_map.frag @@ -17,6 +17,9 @@ uniform sampler2D diffuseMap; // the shadow texture uniform sampler2DShadow shadowMap; +// the inverse of the size of the shadow map +const float shadowScale = 1.0 / 2048.0; + // the interpolated position varying vec4 position; @@ -27,7 +30,11 @@ void main(void) { // compute the base color based on OpenGL lighting model vec4 normalizedNormal = normalize(normal); float diffuse = dot(normalizedNormal, gl_LightSource[0].position); - float facingLight = step(0.0, diffuse) * shadow2D(shadowMap, gl_TexCoord[1].stp).r; + float facingLight = step(0.0, diffuse) * 0.25 * + (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); diff --git a/interface/resources/shaders/model_shadow_normal_map.frag b/interface/resources/shaders/model_shadow_normal_map.frag index d61b123fba..3461c1b5f3 100644 --- a/interface/resources/shaders/model_shadow_normal_map.frag +++ b/interface/resources/shaders/model_shadow_normal_map.frag @@ -20,6 +20,9 @@ uniform sampler2D normalMap; // the shadow texture uniform sampler2DShadow shadowMap; +// the inverse of the size of the shadow map +const float shadowScale = 1.0 / 2048.0; + // the interpolated position varying vec4 interpolatedPosition; @@ -39,7 +42,11 @@ void main(void) { vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); float diffuse = dot(viewNormal, gl_LightSource[0].position); - float facingLight = step(0.0, diffuse) * shadow2D(shadowMap, gl_TexCoord[1].stp).r; + float facingLight = step(0.0, diffuse) * 0.25 * + (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); diff --git a/interface/resources/shaders/model_shadow_normal_specular_map.frag b/interface/resources/shaders/model_shadow_normal_specular_map.frag index 9865056479..273d197fca 100644 --- a/interface/resources/shaders/model_shadow_normal_specular_map.frag +++ b/interface/resources/shaders/model_shadow_normal_specular_map.frag @@ -23,6 +23,9 @@ uniform sampler2D specularMap; // the shadow texture uniform sampler2DShadow shadowMap; +// the inverse of the size of the shadow map +const float shadowScale = 1.0 / 2048.0; + // the interpolated position varying vec4 interpolatedPosition; @@ -42,7 +45,11 @@ void main(void) { vec4 viewNormal = vec4(normalizedTangent * localNormal.x + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); float diffuse = dot(viewNormal, gl_LightSource[0].position); - float facingLight = step(0.0, diffuse) * shadow2D(shadowMap, gl_TexCoord[1].stp).r; + float facingLight = step(0.0, diffuse) * 0.25 * + (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); diff --git a/interface/resources/shaders/model_shadow_specular_map.frag b/interface/resources/shaders/model_shadow_specular_map.frag index 4be0f1636d..77cff1e04e 100644 --- a/interface/resources/shaders/model_shadow_specular_map.frag +++ b/interface/resources/shaders/model_shadow_specular_map.frag @@ -20,6 +20,9 @@ uniform sampler2D specularMap; // the shadow texture uniform sampler2DShadow shadowMap; +// the inverse of the size of the shadow map +const float shadowScale = 1.0 / 2048.0; + // the interpolated position in view space varying vec4 position; @@ -30,7 +33,11 @@ void main(void) { // compute the base color based on OpenGL lighting model vec4 normalizedNormal = normalize(normal); float diffuse = dot(normalizedNormal, gl_LightSource[0].position); - float facingLight = step(0.0, diffuse) * shadow2D(shadowMap, gl_TexCoord[1].stp).r; + float facingLight = step(0.0, diffuse) * 0.25 * + (shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r + + shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r); vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight)); From 8ec14568fe72bbf3468004f38a9b2614484a10a1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 27 May 2014 13:39:32 -0700 Subject: [PATCH 7/8] support ray picking against the AABB for the rotated model extents --- interface/src/models/ModelTreeRenderer.cpp | 55 ++++++++-------------- interface/src/models/ModelTreeRenderer.h | 5 +- libraries/fbx/src/FBXReader.cpp | 37 +++++++++++++++ libraries/fbx/src/FBXReader.h | 5 +- libraries/models/src/ModelTree.h | 11 +++++ libraries/models/src/ModelTreeElement.cpp | 37 ++++++++++++++- 6 files changed, 111 insertions(+), 39 deletions(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 6d7c61dca1..8ffd61043a 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -11,6 +11,8 @@ #include +#include + #include "InterfaceConfig.h" #include "Menu.h" #include "ModelTreeRenderer.h" @@ -34,8 +36,13 @@ ModelTreeRenderer::~ModelTreeRenderer() { void ModelTreeRenderer::init() { OctreeRenderer::init(); + static_cast(_tree)->setFBXService(this); } +void ModelTreeRenderer::setTree(Octree* newTree) { + OctreeRenderer::setTree(newTree); + static_cast(_tree)->setFBXService(this); +} void ModelTreeRenderer::update() { if (_tree) { @@ -48,6 +55,16 @@ void ModelTreeRenderer::render(RenderMode renderMode) { OctreeRenderer::render(renderMode); } +const FBXGeometry* ModelTreeRenderer::getGeometryForModel(const ModelItem& modelItem) { + const FBXGeometry* result = NULL; + Model* model = getModel(modelItem); + if (model) { + result = &model->getGeometry()->getFBXGeometry(); + + } + return result; +} + Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { Model* model = NULL; @@ -73,42 +90,6 @@ Model* ModelTreeRenderer::getModel(const ModelItem& modelItem) { return model; } -void calculateRotatedExtents(Extents& extents, const glm::quat& rotation) { - glm::vec3 bottomLeftNear(extents.minimum.x, extents.minimum.y, extents.minimum.z); - glm::vec3 bottomRightNear(extents.maximum.x, extents.minimum.y, extents.minimum.z); - glm::vec3 bottomLeftFar(extents.minimum.x, extents.minimum.y, extents.maximum.z); - glm::vec3 bottomRightFar(extents.maximum.x, extents.minimum.y, extents.maximum.z); - glm::vec3 topLeftNear(extents.minimum.x, extents.maximum.y, extents.minimum.z); - glm::vec3 topRightNear(extents.maximum.x, extents.maximum.y, extents.minimum.z); - glm::vec3 topLeftFar(extents.minimum.x, extents.maximum.y, extents.maximum.z); - glm::vec3 topRightFar(extents.maximum.x, extents.maximum.y, extents.maximum.z); - - glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear; - glm::vec3 bottomRightNearRotated = rotation * bottomRightNear; - glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar; - glm::vec3 bottomRightFarRotated = rotation * bottomRightFar; - glm::vec3 topLeftNearRotated = rotation * topLeftNear; - glm::vec3 topRightNearRotated = rotation * topRightNear; - glm::vec3 topLeftFarRotated = rotation * topLeftFar; - glm::vec3 topRightFarRotated = rotation * topRightFar; - - extents.minimum = glm::min(bottomLeftNearRotated, - glm::min(bottomRightNearRotated, - glm::min(bottomLeftFarRotated, - glm::min(bottomRightFarRotated, - glm::min(topLeftNearRotated, - glm::min(topRightNearRotated, - glm::min(topLeftFarRotated,topRightFarRotated))))))); - - extents.maximum = glm::max(bottomLeftNearRotated, - glm::max(bottomRightNearRotated, - glm::max(bottomLeftFarRotated, - glm::max(bottomRightFarRotated, - glm::max(topLeftNearRotated, - glm::max(topRightNearRotated, - glm::max(topLeftFarRotated,topRightFarRotated))))))); -} - void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) { args->_elementsTouched++; // actually render it here... @@ -242,6 +223,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) model->render(alpha, modelRenderMode); if (!isShadowMode && displayModelBounds) { + glm::vec3 unRotatedMinimum = model->getUnscaledMeshExtents().minimum; glm::vec3 unRotatedMaximum = model->getUnscaledMeshExtents().maximum; glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum; @@ -279,6 +261,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) glutWireCube(1.0); glPopMatrix(); + } glPopMatrix(); diff --git a/interface/src/models/ModelTreeRenderer.h b/interface/src/models/ModelTreeRenderer.h index e0b8d7d0a2..2d418b1a25 100644 --- a/interface/src/models/ModelTreeRenderer.h +++ b/interface/src/models/ModelTreeRenderer.h @@ -26,7 +26,7 @@ #include "renderer/Model.h" // Generic client side Octree renderer class. -class ModelTreeRenderer : public OctreeRenderer { +class ModelTreeRenderer : public OctreeRenderer, public ModelItemFBXService { public: ModelTreeRenderer(); virtual ~ModelTreeRenderer(); @@ -38,6 +38,7 @@ public: virtual void renderElement(OctreeElement* element, RenderArgs* args); virtual float getSizeScale() const; virtual int getBoundaryLevelAdjust() const; + virtual void setTree(Octree* newTree); void update(); @@ -48,6 +49,8 @@ public: virtual void init(); virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); + virtual const FBXGeometry* getGeometryForModel(const ModelItem& modelItem); + protected: Model* getModel(const ModelItem& modelItem); QMap _knownModelsItemModels; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 713870dafb..60412cf0ce 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -2052,3 +2052,40 @@ FBXGeometry readSVO(const QByteArray& model) { return geometry; } + +void calculateRotatedExtents(Extents& extents, const glm::quat& rotation) { + glm::vec3 bottomLeftNear(extents.minimum.x, extents.minimum.y, extents.minimum.z); + glm::vec3 bottomRightNear(extents.maximum.x, extents.minimum.y, extents.minimum.z); + glm::vec3 bottomLeftFar(extents.minimum.x, extents.minimum.y, extents.maximum.z); + glm::vec3 bottomRightFar(extents.maximum.x, extents.minimum.y, extents.maximum.z); + glm::vec3 topLeftNear(extents.minimum.x, extents.maximum.y, extents.minimum.z); + glm::vec3 topRightNear(extents.maximum.x, extents.maximum.y, extents.minimum.z); + glm::vec3 topLeftFar(extents.minimum.x, extents.maximum.y, extents.maximum.z); + glm::vec3 topRightFar(extents.maximum.x, extents.maximum.y, extents.maximum.z); + + glm::vec3 bottomLeftNearRotated = rotation * bottomLeftNear; + glm::vec3 bottomRightNearRotated = rotation * bottomRightNear; + glm::vec3 bottomLeftFarRotated = rotation * bottomLeftFar; + glm::vec3 bottomRightFarRotated = rotation * bottomRightFar; + glm::vec3 topLeftNearRotated = rotation * topLeftNear; + glm::vec3 topRightNearRotated = rotation * topRightNear; + glm::vec3 topLeftFarRotated = rotation * topLeftFar; + glm::vec3 topRightFarRotated = rotation * topRightFar; + + extents.minimum = glm::min(bottomLeftNearRotated, + glm::min(bottomRightNearRotated, + glm::min(bottomLeftFarRotated, + glm::min(bottomRightFarRotated, + glm::min(topLeftNearRotated, + glm::min(topRightNearRotated, + glm::min(topLeftFarRotated,topRightFarRotated))))))); + + extents.maximum = glm::max(bottomLeftNearRotated, + glm::max(bottomRightNearRotated, + glm::max(bottomLeftFarRotated, + glm::max(bottomRightFarRotated, + glm::max(topLeftNearRotated, + glm::max(topRightNearRotated, + glm::max(topLeftFarRotated,topRightFarRotated))))))); +} + diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 8073ab36a9..4c93f3dc5e 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -51,7 +51,8 @@ public: bool containsPoint(const glm::vec3& point) const; /// \return whether or not the extents are empty - bool isEmpty() { return minimum == maximum; } + bool isEmpty() const { return minimum == maximum; } + bool isValid() const { return !((minimum == glm::vec3(FLT_MAX)) && (maximum == glm::vec3(-FLT_MAX))); } glm::vec3 minimum; glm::vec3 maximum; @@ -238,4 +239,6 @@ FBXGeometry readFBX(const QByteArray& model, const QVariantHash& mapping); /// Reads SVO geometry from the supplied model data. FBXGeometry readSVO(const QByteArray& model); +void calculateRotatedExtents(Extents& extents, const glm::quat& rotation); + #endif // hifi_FBXReader_h diff --git a/libraries/models/src/ModelTree.h b/libraries/models/src/ModelTree.h index 596ca9074d..c450f04e2d 100644 --- a/libraries/models/src/ModelTree.h +++ b/libraries/models/src/ModelTree.h @@ -20,6 +20,11 @@ public: virtual void modelCreated(const ModelItem& newModel, const SharedNodePointer& senderNode) = 0; }; +class ModelItemFBXService { +public: + virtual const FBXGeometry* getGeometryForModel(const ModelItem& modelItem) = 0; +}; + class ModelTree : public Octree { Q_OBJECT public: @@ -74,6 +79,11 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); void handleAddModelResponse(const QByteArray& packet); + + void setFBXService(ModelItemFBXService* service) { _fbxService = service; } + const FBXGeometry* getGeometryForModel(const ModelItem& modelItem) { + return _fbxService ? _fbxService->getGeometryForModel(modelItem) : NULL; + } private: @@ -96,6 +106,7 @@ private: QReadWriteLock _recentlyDeletedModelsLock; QMultiMap _recentlyDeletedModelItemIDs; + ModelItemFBXService* _fbxService; }; #endif // hifi_ModelTree_h diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index dcb3253ef4..dad592f838 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -9,6 +9,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include "ModelTree.h" @@ -158,7 +159,41 @@ bool ModelTreeElement::findDetailedRayIntersection(const glm::vec3& origin, cons // if the ray doesn't intersect with our cube, we can stop searching! if (modelCube.findRayIntersection(origin, direction, localDistance, localFace)) { - if (localDistance < distance) { + const FBXGeometry* fbxGeometry = _myTree->getGeometryForModel(model); + if (fbxGeometry && fbxGeometry->meshExtents.isValid()) { + Extents extents = fbxGeometry->meshExtents; + + // NOTE: these extents are model space, so we need to scale and center them accordingly + // size is our "target size in world space" + // we need to set our model scale so that the extents of the mesh, fit in a cube that size... + float maxDimension = glm::distance(extents.maximum, extents.minimum); + float scale = model.getSize() / maxDimension; + + glm::vec3 halfDimensions = (extents.maximum - extents.minimum) * 0.5f; + glm::vec3 offset = -extents.minimum - halfDimensions; + + extents.minimum += offset; + extents.maximum += offset; + + extents.minimum *= scale; + extents.maximum *= scale; + + calculateRotatedExtents(extents, model.getModelRotation()); + + extents.minimum += model.getPosition(); + extents.maximum += model.getPosition(); + + AABox rotatedExtentsBox(extents.minimum, (extents.maximum - extents.minimum)); + + if (rotatedExtentsBox.findRayIntersection(origin, direction, localDistance, localFace)) { + if (localDistance < distance) { + distance = localDistance; + face = localFace; + *intersectedObject = (void*)(&model); + somethingIntersected = true; + } + } + } else if (localDistance < distance) { distance = localDistance; face = localFace; *intersectedObject = (void*)(&model); From 285bd0d2f24e90887b270efa51851d0091ba2053 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 27 May 2014 15:50:39 -0700 Subject: [PATCH 8/8] Switched to findRayIntersection --- examples/editModels.js | 223 ++++++++++++++++++++++------------------- 1 file changed, 120 insertions(+), 103 deletions(-) diff --git a/examples/editModels.js b/examples/editModels.js index d53b854003..30d1e4edf4 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -188,7 +188,7 @@ function controller(wichSide) { var X = Vec3.sum(A, Vec3.multiply(B, x)); var d = Vec3.length(Vec3.subtract(P, X)); - if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) { + if (0 < x && x < LASER_LENGTH_FACTOR) { return { valid: true, x: x, y: y, z: z }; } return { valid: false }; @@ -293,39 +293,43 @@ function controller(wichSide) { if (this.pressing) { Vec3.print("Looking at: ", this.palmPosition); - var foundModels = Models.findModels(this.palmPosition, LASER_LENGTH_FACTOR); + var pickRay = { origin: this.palmPosition, + direction: Vec3.normalize(Vec3.subtract(this.tipPosition, this.palmPosition)) }; + var foundIntersection = Models.findRayIntersection(pickRay); - for (var i = 0; i < foundModels.length; i++) { - - if (!foundModels[i].isKnownID) { - var identify = Models.identifyModel(foundModels[i]); - if (!identify.isKnownID) { - print("Unknown ID " + identify.id + "(update loop)"); - return; - } - foundModels[i] = identify; + if(!foundIntersection.accurate) { + return; + } + var foundModel = foundIntersection.modelID; + + if (!foundModel.isKnownID) { + var identify = Models.identifyModel(foundModel); + if (!identify.isKnownID) { + print("Unknown ID " + identify.id + " (update loop " + foundModel.id + ")"); + continue; } - - var properties = Models.getModelProperties(foundModels[i]); - print("foundModels["+i+"].modelURL=" + properties.modelURL); - - if (isLocked(properties)) { - print("Model locked " + properties.id); - } else { - print("Checking properties: " + properties.id + " " + properties.isKnownID); - var check = this.checkModel(properties); - if (check.valid) { - this.grab(foundModels[i], properties); - this.x = check.x; - this.y = check.y; - this.z = check.z; - return; - } + foundModel = identify; + } + + var properties = Models.getModelProperties(foundModel); + print("foundModel.modelURL=" + properties.modelURL); + + if (isLocked(properties)) { + print("Model locked " + properties.id); + } else { + print("Checking properties: " + properties.id + " " + properties.isKnownID); + var check = this.checkModel(properties); + if (check.valid) { + this.grab(foundModel, properties); + this.x = check.x; + this.y = check.y; + this.z = check.z; + return; } } } } - + this.cleanup = function () { Overlays.deleteOverlay(this.laser); Overlays.deleteOverlay(this.ball); @@ -478,93 +482,97 @@ function mousePressEvent(event) { } else { var pickRay = Camera.computePickRay(event.x, event.y); Vec3.print("[Mouse] Looking at: ", pickRay.origin); - var foundModels = Models.findModels(pickRay.origin, LASER_LENGTH_FACTOR); - var closest = -1.0; - for (var i = 0; i < foundModels.length; i++) { - if (!foundModels[i].isKnownID) { - var identify = Models.identifyModel(foundModels[i]); - if (!identify.isKnownID) { - print("Unknown ID " + identify.id + "(update loop)"); - continue; - } - foundModels[i] = identify; - } - - var properties = Models.getModelProperties(foundModels[i]); - if (isLocked(properties)) { - print("Model locked " + properties.id); - } else { - print("Checking properties: " + properties.id + " " + properties.isKnownID); - // P P - Model - // /| A - Palm - // / | d B - unit vector toward tip - // / | X - base of the perpendicular line - // A---X----->B d - distance fom axis - // x x - distance from A - // - // |X-A| = (P-A).B - // X == A + ((P-A).B)B - // d = |P-X| - - var A = pickRay.origin; - var B = Vec3.normalize(pickRay.direction); - var P = properties.position; - - var x = Vec3.dot(Vec3.subtract(P, A), B); - var X = Vec3.sum(A, Vec3.multiply(B, x)); - var d = Vec3.length(Vec3.subtract(P, X)); - - if (d < properties.radius && 0 < x && x < LASER_LENGTH_FACTOR) { - if (closest < 0.0) { - closest = x; - } - - if (x <= closest) { - modelSelected = true; - selectedModelID = foundModels[i]; - selectedModelProperties = properties; - - orientation = MyAvatar.orientation; - intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); - } - } + var foundIntersection = Models.findRayIntersection(pickRay); + + if(!foundIntersection.accurate) { + return; + } + var foundModel = foundIntersection.modelID; + + if (!foundModel.isKnownID) { + var identify = Models.identifyModel(foundModel); + if (!identify.isKnownID) { + print("Unknown ID " + identify.id + " (update loop " + foundModel.id + ")"); + continue; } + foundModel = identify; } - if (modelSelected) { - selectedModelProperties.oldRadius = selectedModelProperties.radius; - selectedModelProperties.oldPosition = { - x: selectedModelProperties.position.x, - y: selectedModelProperties.position.y, - z: selectedModelProperties.position.z, - }; - selectedModelProperties.oldRotation = { - x: selectedModelProperties.modelRotation.x, - y: selectedModelProperties.modelRotation.y, - z: selectedModelProperties.modelRotation.z, - w: selectedModelProperties.modelRotation.w, - }; + var properties = Models.getModelProperties(foundModel); + if (isLocked(properties)) { + print("Model locked " + properties.id); + } else { + print("Checking properties: " + properties.id + " " + properties.isKnownID); + // P P - Model + // /| A - Palm + // / | d B - unit vector toward tip + // / | X - base of the perpendicular line + // A---X----->B d - distance fom axis + // x x - distance from A + // + // |X-A| = (P-A).B + // X == A + ((P-A).B)B + // d = |P-X| - selectedModelProperties.glowLevel = 0.1; - Models.editModel(selectedModelID, { glowLevel: selectedModelProperties.glowLevel}); + var A = pickRay.origin; + var B = Vec3.normalize(pickRay.direction); + var P = properties.position; - print("Clicked on " + selectedModelID.id + " " + modelSelected); + var x = Vec3.dot(Vec3.subtract(P, A), B); + var X = Vec3.sum(A, Vec3.multiply(B, x)); + var d = Vec3.length(Vec3.subtract(P, X)); + + if (0 < x && x < LASER_LENGTH_FACTOR) { + modelSelected = true; + selectedModelID = foundModel; + selectedModelProperties = properties; + + orientation = MyAvatar.orientation; + intersection = rayPlaneIntersection(pickRay, P, Quat.getFront(orientation)); + } } } + + if (modelSelected) { + selectedModelProperties.oldRadius = selectedModelProperties.radius; + selectedModelProperties.oldPosition = { + x: selectedModelProperties.position.x, + y: selectedModelProperties.position.y, + z: selectedModelProperties.position.z, + }; + selectedModelProperties.oldRotation = { + x: selectedModelProperties.modelRotation.x, + y: selectedModelProperties.modelRotation.y, + z: selectedModelProperties.modelRotation.z, + w: selectedModelProperties.modelRotation.w, + }; + selectedModelProperties.glowLevel = 0.0; + + print("Clicked on " + selectedModelID.id + " " + modelSelected); + } } -Controller.mouseReleaseEvent.connect(function() { - if (modelSelected) { - Models.editModel(selectedModelID, { glowLevel: 0.0 }); - modelSelected = false; - } - }); - +var glowedModelID = { id: -1, isKnownID: false }; var oldModifier = 0; var modifier = 0; var wasShifted = false; function mouseMoveEvent(event) { + var pickRay = Camera.computePickRay(event.x, event.y); + if (!modelSelected) { + var modelIntersection = Models.findRayIntersection(pickRay); + if (modelIntersection.accurate) { + if(glowedModelID.isKnownID && glowedModelID.id != modelIntersection.modelID.id) { + Models.editModel(glowedModelID, { glowLevel: 0.0 }); + glowedModelID.id = -1; + glowedModelID.isKnownID = false; + } + + if (modelIntersection.modelID.isKnownID) { + Models.editModel(modelIntersection.modelID, { glowLevel: 0.25 }); + glowedModelID = modelIntersection.modelID; + } + } return; } @@ -579,8 +587,7 @@ function mouseMoveEvent(event) { } else { modifier = 0; } - - var pickRay = Camera.computePickRay(event.x, event.y); + pickRay = Camera.computePickRay(event.x, event.y); if (wasShifted != event.isShifted || modifier != oldModifier) { selectedModelProperties.oldRadius = selectedModelProperties.radius; @@ -653,6 +660,15 @@ function mouseMoveEvent(event) { Models.editModel(selectedModelID, selectedModelProperties); } +function mouseReleaseEvent(event) { + modelSelected = false; + + glowedModelID.id = -1; + glowedModelID.isKnownID = false; +} + + + function setupModelMenus() { // add our menuitems Menu.addMenuItem({ menuName: "Edit", menuItemName: "Models", isSeparator: true, beforeItem: "Physics" }); @@ -677,6 +693,7 @@ Script.scriptEnding.connect(scriptEnding); Script.update.connect(checkController); Controller.mousePressEvent.connect(mousePressEvent); Controller.mouseMoveEvent.connect(mouseMoveEvent); +Controller.mouseReleaseEvent.connect(mouseReleaseEvent); setupModelMenus(); Menu.menuItemEvent.connect(function(menuItem){