diff --git a/examples/editModels.js b/examples/editModels.js index 3f1863fef2..8e3503b9b2 100644 --- a/examples/editModels.js +++ b/examples/editModels.js @@ -2807,6 +2807,7 @@ function mouseReleaseEvent(event) { // exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that // added it. var modelMenuAddedDelete = false; +var originalLightsArePickable = Entities.getLightsArePickable(); function setupModelMenus() { print("setupModelMenus()"); // adj our menuitems @@ -2824,15 +2825,18 @@ function setupModelMenus() { Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L", + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L", afterItem: "Paste Models", isCheckable: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S", - afterItem: "Allow Select Large Models", isCheckable: true }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S", + afterItem: "Allow Selecting of Large Models", isCheckable: true }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L", + afterItem: "Allow Selecting of Small Models", isCheckable: true }); Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" }); + Entities.setLightsArePickable(false); } @@ -2846,8 +2850,9 @@ function cleanupModelMenus() { Menu.removeMenuItem("Edit", "Model List..."); Menu.removeMenuItem("Edit", "Paste Models"); - Menu.removeMenuItem("Edit", "Allow Select Large Models"); - Menu.removeMenuItem("Edit", "Allow Select Small Models"); + Menu.removeMenuItem("Edit", "Allow Selecting of Large Models"); + Menu.removeMenuItem("Edit", "Allow Selecting of Small Models"); + Menu.removeMenuItem("Edit", "Allow Selecting of Lights"); Menu.removeSeparator("File", "Models"); Menu.removeMenuItem("File", "Export Models"); @@ -2865,6 +2870,7 @@ function scriptEnding() { if (exportMenu) { exportMenu.close(); } + Entities.setLightsArePickable(originalLightsArePickable); } Script.scriptEnding.connect(scriptEnding); @@ -2890,10 +2896,12 @@ function showPropertiesForm(editModelID) { function handeMenuEvent(menuItem) { print("menuItemEvent() in JS... menuItem=" + menuItem); - if (menuItem == "Allow Select Small Models") { - allowSmallModels = Menu.isOptionChecked("Allow Select Small Models"); - } else if (menuItem == "Allow Select Large Models") { - allowLargeModels = Menu.isOptionChecked("Allow Select Large Models"); + if (menuItem == "Allow Selecting of Small Models") { + allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); + } else if (menuItem == "Allow Selecting of Large Models") { + allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models"); + } else if (menuItem == "Allow Selecting of Lights") { + Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights")); } else if (menuItem == "Delete") { if (leftController.grabbing) { print(" Delete Entity.... leftController.entityID="+ leftController.entityID); diff --git a/examples/newEditEntities.js b/examples/newEditEntities.js index a95c542311..ef1be8fef9 100644 --- a/examples/newEditEntities.js +++ b/examples/newEditEntities.js @@ -640,6 +640,7 @@ Controller.mouseReleaseEvent.connect(mouseReleaseEvent); // exists. If it doesn't they add it. If it does they don't. They also only delete the menu item if they were the one that // added it. var modelMenuAddedDelete = false; +var originalLightsArePickable = Entities.getLightsArePickable(); function setupModelMenus() { print("setupModelMenus()"); // adj our menuitems @@ -657,10 +658,12 @@ function setupModelMenus() { Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Edit Properties..." }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Large Models", shortcutKey: "CTRL+META+L", + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L", afterItem: "Paste Models", isCheckable: true, isChecked: true }); - Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Select Small Models", shortcutKey: "CTRL+META+S", - afterItem: "Allow Select Large Models", isCheckable: true, isChecked: true }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S", + afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true }); + Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L", + afterItem: "Allow Selecting of Small Models", isCheckable: true }); Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); @@ -669,6 +672,8 @@ function setupModelMenus() { Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED, isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); + + Entities.setLightsArePickable(false); } setupModelMenus(); // do this when first running our script. @@ -683,8 +688,9 @@ function cleanupModelMenus() { Menu.removeMenuItem("Edit", "Model List..."); Menu.removeMenuItem("Edit", "Paste Models"); - Menu.removeMenuItem("Edit", "Allow Select Large Models"); - Menu.removeMenuItem("Edit", "Allow Select Small Models"); + Menu.removeMenuItem("Edit", "Allow Selecting of Large Models"); + Menu.removeMenuItem("Edit", "Allow Selecting of Small Models"); + Menu.removeMenuItem("Edit", "Allow Selecting of Lights"); Menu.removeSeparator("File", "Models"); Menu.removeMenuItem("File", "Export Models"); @@ -708,6 +714,7 @@ Script.scriptEnding.connect(function() { if (exportMenu) { exportMenu.close(); } + Entities.setLightsArePickable(originalLightsArePickable); }); // Do some stuff regularly, like check for placement of various overlays @@ -718,10 +725,12 @@ Script.update.connect(function (deltaTime) { }); function handeMenuEvent(menuItem) { - if (menuItem == "Allow Select Small Models") { - allowSmallModels = Menu.isOptionChecked("Allow Select Small Models"); - } else if (menuItem == "Allow Select Large Models") { - allowLargeModels = Menu.isOptionChecked("Allow Select Large Models"); + if (menuItem == "Allow Selecting of Small Models") { + allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models"); + } else if (menuItem == "Allow Selecting of Large Models") { + allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models"); + } else if (menuItem == "Allow Selecting of Lights") { + Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights")); } else if (menuItem == "Delete") { if (SelectionManager.hasSelection()) { print(" Delete Entities"); diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9ab87fdc30..85b21b073f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -896,6 +896,7 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Greater: case Qt::Key_Comma: case Qt::Key_Period: + case Qt::Key_QuoteDbl: Menu::getInstance()->handleViewFrustumOffsetKeyModifier(event->key()); break; case Qt::Key_L: @@ -2089,12 +2090,6 @@ void Application::updateMouseRay() { _mouseRayDirection -= 2.0f * (_viewFrustum.getDirection() * glm::dot(_viewFrustum.getDirection(), _mouseRayDirection) + _viewFrustum.getRight() * glm::dot(_viewFrustum.getRight(), _mouseRayDirection)); } - - // tell my avatar if the mouse is being pressed... - _myAvatar->setMousePressed(_mousePressed); - - // tell my avatar the posiion and direction of the ray projected ino the world based on the mouse position - _myAvatar->setMouseRay(_mouseRayOrigin, _mouseRayDirection); } void Application::updateFaceshift() { @@ -2915,7 +2910,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly, RenderAr // transform by eye offset // load the view frustum - loadViewFrustum(whichCamera, _displayViewFrustum); + loadViewFrustum(whichCamera, _viewFrustum); // flip x if in mirror mode (also requires reversing winding order for backface culling) if (whichCamera.getMode() == CAMERA_MODE_MIRROR) { @@ -3189,7 +3184,7 @@ void Application::computeOffAxisFrustum(float& left, float& right, float& bottom float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const { // allow 3DTV/Oculus to override parameters from camera - _displayViewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); + _viewFrustum.computeOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); if (OculusManager::isConnected()) { OculusManager::overrideOffAxisFrustum(left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); diff --git a/interface/src/Application.h b/interface/src/Application.h index e432f7fdf0..b737141b40 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -196,7 +196,6 @@ public: const AudioReflector* getAudioReflector() const { return &_audioReflector; } Camera* getCamera() { return &_myCamera; } ViewFrustum* getViewFrustum() { return &_viewFrustum; } - ViewFrustum* getDisplayViewFrustum() { return &_displayViewFrustum; } ViewFrustum* getShadowViewFrustum() { return &_shadowViewFrustum; } VoxelImporter* getVoxelImporter() { return &_voxelImporter; } VoxelSystem* getVoxels() { return &_voxels; } @@ -518,7 +517,6 @@ private: ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc. ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels) - ViewFrustum _displayViewFrustum; ViewFrustum _shadowViewFrustum; quint64 _lastQueriedTime; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 6cfb3db35a..ae12b5ff26 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -835,6 +835,14 @@ void Menu::handleViewFrustumOffsetKeyModifier(int key) { const float VIEW_FRUSTUM_OFFSET_UP_DELTA = 0.05f; switch (key) { + case Qt::Key_QuoteDbl: + _viewFrustumOffset.yaw = 0.0f; + _viewFrustumOffset.pitch = 0.0f; + _viewFrustumOffset.roll = 0.0f; + _viewFrustumOffset.up = 0.0f; + _viewFrustumOffset.distance = 0.0f; + break; + case Qt::Key_BracketLeft: _viewFrustumOffset.yaw -= VIEW_FRUSTUM_OFFSET_DELTA; break; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index b98fea8eca..996b92e22d 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -192,7 +192,7 @@ static const float EIGHT_BIT_MAXIMUM_RECIPROCAL = 1.0f / EIGHT_BIT_MAXIMUM; void MetavoxelSystem::render() { // update the frustum - ViewFrustum* viewFrustum = Application::getInstance()->getDisplayViewFrustum(); + ViewFrustum* viewFrustum = Application::getInstance()->getViewFrustum(); _frustum.set(viewFrustum->getFarTopLeft(), viewFrustum->getFarTopRight(), viewFrustum->getFarBottomLeft(), viewFrustum->getFarBottomRight(), viewFrustum->getNearTopLeft(), viewFrustum->getNearTopRight(), viewFrustum->getNearBottomLeft(), viewFrustum->getNearBottomRight()); @@ -1896,7 +1896,7 @@ private: SpannerRenderVisitor::SpannerRenderVisitor(const MetavoxelLOD& lod) : SpannerVisitor(QVector() << AttributeRegistry::getInstance()->getSpannersAttribute(), QVector(), QVector(), lod, - encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), + encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())), _containmentDepth(INT_MAX) { } @@ -1932,7 +1932,7 @@ private: BufferRenderVisitor::BufferRenderVisitor(const AttributePointer& attribute) : MetavoxelVisitor(QVector() << attribute), - _order(encodeOrder(Application::getInstance()->getDisplayViewFrustum()->getDirection())), + _order(encodeOrder(Application::getInstance()->getViewFrustum()->getDirection())), _containmentDepth(INT_MAX) { } diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index 03243c1a83..c680c75056 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -66,8 +66,6 @@ Avatar::Avatar() : _leanScale(0.5f), _scale(1.0f), _worldUpDirection(DEFAULT_UP_DIRECTION), - _mouseRayOrigin(0.0f, 0.0f, 0.0f), - _mouseRayDirection(0.0f, 0.0f, 0.0f), _moving(false), _collisionGroups(0), _initialized(false), @@ -250,11 +248,6 @@ void Avatar::measureMotionDerivatives(float deltaTime) { _lastOrientation = getOrientation(); } -void Avatar::setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction) { - _mouseRayOrigin = origin; - _mouseRayDirection = direction; -} - enum TextRendererType { CHAT, DISPLAYNAME diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 2d1a44403f..88ab3b12ca 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -85,7 +85,6 @@ public: //setters void setDisplayingLookatVectors(bool displayingLookatVectors) { getHead()->setRenderLookatVectors(displayingLookatVectors); } - void setMouseRay(const glm::vec3 &origin, const glm::vec3 &direction); void setIsLookAtTarget(const bool isLookAtTarget) { _isLookAtTarget = isLookAtTarget; } bool getIsLookAtTarget() const { return _isLookAtTarget; } //getters @@ -213,8 +212,6 @@ protected: float _leanScale; float _scale; glm::vec3 _worldUpDirection; - glm::vec3 _mouseRayOrigin; - glm::vec3 _mouseRayDirection; float _stringLength; bool _moving; ///< set when position is changing diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index ba5ba8141d..25916d761c 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -87,7 +87,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (!shouldKillAvatar(sharedAvatar)) { // this avatar's mixer is still around, go ahead and simulate it avatar->simulate(deltaTime); - avatar->setMouseRay(mouseOrigin, mouseDirection); ++avatarIterator; } else { // the mixer that owned this avatar is gone, give it to the vector of fades and kill it diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9f3309ece2..32053ea076 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -67,7 +67,6 @@ const int SCRIPTED_MOTOR_WORLD_FRAME = 2; MyAvatar::MyAvatar() : Avatar(), - _mousePressed(false), _turningKeyPressTime(0.0f), _gravity(0.0f, 0.0f, 0.0f), _distanceToNearestAvatar(std::numeric_limits::max()), diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 37b93e2f04..12ad4474c9 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -55,15 +55,12 @@ public: void renderHeadMouse(int screenWidth, int screenHeight) const; // setters - void setMousePressed(bool mousePressed) { _mousePressed = mousePressed; } void setLeanScale(float scale) { _leanScale = scale; } void setLocalGravity(glm::vec3 gravity); void setShouldRenderLocally(bool shouldRender) { _shouldRender = shouldRender; } // getters float getLeanScale() const { return _leanScale; } - const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; } - const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; } glm::vec3 getGravity() const { return _gravity; } glm::vec3 getDefaultEyePosition() const; bool getShouldRenderLocally() const { return _shouldRender; } @@ -203,7 +200,6 @@ protected: virtual void renderAttachments(RenderMode renderMode); private: - bool _mousePressed; float _turningKeyPressTime; glm::vec3 _gravity; float _distanceToNearestAvatar; // How close is the nearest avatar? diff --git a/interface/src/entities/RenderableLightEntityItem.cpp b/interface/src/entities/RenderableLightEntityItem.cpp index e3e8f61e58..91b2d35106 100644 --- a/interface/src/entities/RenderableLightEntityItem.cpp +++ b/interface/src/entities/RenderableLightEntityItem.cpp @@ -90,3 +90,14 @@ void RenderableLightEntityItem::render(RenderArgs* args) { glPopMatrix(); } }; + +bool RenderableLightEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject) const { + + // TODO: this isn't really correct because we don't know if we actually live in the main tree of the applications's + // EntityTreeRenderer. But we probably do. Technically we could be on the clipboard and someone might be trying to + // use the ray intersection API there. Anyway... if you ever try to do ray intersection testing off of trees other + // than the main tree of the main entity renderer, then you'll need to fix this mechanism. + return Application::getInstance()->getEntities()->getTree()->getLightsArePickable(); +} diff --git a/interface/src/entities/RenderableLightEntityItem.h b/interface/src/entities/RenderableLightEntityItem.h index cecd9b761e..40fa31a4ce 100644 --- a/interface/src/entities/RenderableLightEntityItem.h +++ b/interface/src/entities/RenderableLightEntityItem.h @@ -34,6 +34,10 @@ public: { } virtual void render(RenderArgs* args); + virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject) const; }; diff --git a/interface/src/renderer/DeferredLightingEffect.cpp b/interface/src/renderer/DeferredLightingEffect.cpp index be4e457131..63d874cda7 100644 --- a/interface/src/renderer/DeferredLightingEffect.cpp +++ b/interface/src/renderer/DeferredLightingEffect.cpp @@ -232,8 +232,8 @@ void DeferredLightingEffect::render() { // enlarge the scales slightly to account for tesselation const float SCALE_EXPANSION = 0.05f; - const glm::vec3& eyePoint = Application::getInstance()->getDisplayViewFrustum()->getPosition(); - float nearRadius = glm::distance(eyePoint, Application::getInstance()->getDisplayViewFrustum()->getNearTopLeft()); + const glm::vec3& eyePoint = Application::getInstance()->getViewFrustum()->getPosition(); + float nearRadius = glm::distance(eyePoint, Application::getInstance()->getViewFrustum()->getNearTopLeft()); if (!_pointLights.isEmpty()) { _pointLight.bind(); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index c1b1f243ef..7dbcaed8fc 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -134,6 +134,11 @@ public: virtual SimulationState computeSimulationState() const; virtual void debugDump() const; + + virtual bool supportsDetailedRayIntersection() const { return false; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject) const { return true; } // attributes applicable to all entity types EntityTypes::EntityType getType() const { return _type; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 29c4a8b19a..575a6c1a78 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -221,6 +221,19 @@ RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorke return result; } +void EntityScriptingInterface::setLightsArePickable(bool value) { + if (_entityTree) { + _entityTree->setLightsArePickable(value); + } +} + +bool EntityScriptingInterface::getLightsArePickable() const { + if (_entityTree) { + return _entityTree->getLightsArePickable(); + } + return false; +} + RayToEntityIntersectionResult::RayToEntityIntersectionResult() : intersects(false), diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 2150fa51da..da0c6c9f1a 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -96,6 +96,8 @@ public slots: /// order to return an accurate result Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray); + Q_INVOKABLE void setLightsArePickable(bool value); + Q_INVOKABLE bool getLightsArePickable() const; Q_INVOKABLE void dumpTree() const; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 6ac3c31939..d3d9e2da53 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -21,6 +21,7 @@ EntityTree::EntityTree(bool shouldReaverage) : Octree(shouldReaverage), _simulation(NULL) { _rootElement = createNewElement(); + _lightsArePickable = true; // assume they are by default } EntityTree::~EntityTree() { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 5dccfd7709..eeb0182042 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -144,6 +144,8 @@ public: void emitEntityScriptChanging(const EntityItemID& entityItemID); + bool getLightsArePickable() const { return _lightsArePickable; } + void setLightsArePickable(bool value) { _lightsArePickable = value; } void setSimulation(EntitySimulation* simulation); signals: @@ -169,6 +171,8 @@ private: EntityItemFBXService* _fbxService; QHash _entityToElementMap; + + bool _lightsArePickable; EntitySimulation* _simulation; }; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index f0eeb40ede..e18f79276e 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -511,10 +511,28 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con // and testing intersection there. if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, localFace)) { if (localDistance < distance) { - distance = localDistance; - face = localFace; - *intersectedObject = (void*)entity; - somethingIntersected = true; + // now ask the entity if we actually intersect + if (entity->supportsDetailedRayIntersection()) { + + if (entity->findDetailedRayIntersection(origin, direction, keepSearching, element, localDistance, + localFace, intersectedObject)) { + + if (localDistance < distance) { + distance = localDistance; + face = localFace; + *intersectedObject = (void*)entity; + somethingIntersected = true; + } + } + } else { + // if the entity type doesn't support a detailed intersection, then just return the non-AABox results + if (localDistance < distance) { + distance = localDistance; + face = localFace; + *intersectedObject = (void*)entity; + somethingIntersected = true; + } + } } } } diff --git a/libraries/entities/src/SphereEntityItem.cpp b/libraries/entities/src/SphereEntityItem.cpp index f5b8eb27e9..12fdd7a8c4 100644 --- a/libraries/entities/src/SphereEntityItem.cpp +++ b/libraries/entities/src/SphereEntityItem.cpp @@ -93,3 +93,26 @@ void SphereEntityItem::recalculateCollisionShape() { float largestDiameter = glm::max(dimensionsInMeters.x, dimensionsInMeters.y, dimensionsInMeters.z); _sphereShape.setRadius(largestDiameter / 2.0f); } + +bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject) const { + + // NOTE: origin and direction are in tree units. But our _sphereShape is in meters, so we need to + // do a little math to make these match each other. + RayIntersectionInfo rayInfo; + rayInfo._rayStart = origin * (float)TREE_SCALE; + rayInfo._rayDirection = direction; + + // TODO: Note this is really doing ray intersections against a sphere, which is fine except in cases + // where our dimensions actually make us an ellipsoid. But we'll live with this for now until we + // get a more full fledged physics library + if (_sphereShape.findRayIntersection(rayInfo)) { + distance = rayInfo._hitDistance / (float)TREE_SCALE; + return true; + } + return false; +} + + + diff --git a/libraries/entities/src/SphereEntityItem.h b/libraries/entities/src/SphereEntityItem.h index 21cb58223b..bb4f41726c 100644 --- a/libraries/entities/src/SphereEntityItem.h +++ b/libraries/entities/src/SphereEntityItem.h @@ -56,6 +56,11 @@ public: // TODO: implement proper contains for 3D ellipsoid //virtual bool contains(const glm::vec3& point) const; + virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject) const; + protected: virtual void recalculateCollisionShape(); diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp index 491240c178..17ef33ee1c 100644 --- a/libraries/entities/src/TextEntityItem.cpp +++ b/libraries/entities/src/TextEntityItem.cpp @@ -10,9 +10,12 @@ // +#include + #include #include +#include #include "EntityTree.h" #include "EntityTreeElement.h" @@ -110,4 +113,48 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, appendValue, getLineHeight()); APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, appendColor, getTextColor()); APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_COLOR, appendColor, getBackgroundColor()); -} \ No newline at end of file +} + + +bool TextEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject) const { + + RayIntersectionInfo rayInfo; + rayInfo._rayStart = origin; + rayInfo._rayDirection = direction; + rayInfo._rayLength = std::numeric_limits::max(); + + PlaneShape plane; + + const glm::vec3 UNROTATED_NORMAL(0.0f, 0.0f, -1.0f); + glm::vec3 normal = _rotation * UNROTATED_NORMAL; + plane.setNormal(normal); + plane.setPoint(_position); // the position is definitely a point on our plane + + bool intersects = plane.findRayIntersection(rayInfo); + + if (intersects) { + glm::vec3 hitAt = origin + (direction * rayInfo._hitDistance); + // now we know the point the ray hit our plane + + glm::mat4 rotation = glm::mat4_cast(getRotation()); + glm::mat4 translation = glm::translate(getPosition()); + glm::mat4 entityToWorldMatrix = translation * rotation; + glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); + + glm::vec3 dimensions = getDimensions(); + glm::vec3 registrationPoint = getRegistrationPoint(); + glm::vec3 corner = -(dimensions * registrationPoint); + AABox entityFrameBox(corner, dimensions); + + glm::vec3 entityFrameHitAt = glm::vec3(worldToEntityMatrix * glm::vec4(hitAt, 1.0f)); + + intersects = entityFrameBox.contains(entityFrameHitAt); + } + + if (intersects) { + distance = rayInfo._hitDistance; + } + return intersects; +} diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h index 019d230c36..a3d323aefd 100644 --- a/libraries/entities/src/TextEntityItem.h +++ b/libraries/entities/src/TextEntityItem.h @@ -41,6 +41,11 @@ public: ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData); + virtual bool supportsDetailedRayIntersection() const { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, + void** intersectedObject) const; + static const QString DEFAULT_TEXT; void setText(const QString& value) { _text = value; } const QString& getText() const { return _text; }