diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e4adb38cd4..fb414311ef 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2373,8 +2373,8 @@ void Application::updateShadowMap() { updateUntranslatedViewMatrix(); _avatarManager.renderAvatars(Avatar::SHADOW_RENDER_MODE); - _particles.render(); - _models.render(); + _particles.render(OctreeRenderer::SHADOW_RENDER_MODE); + _models.render(OctreeRenderer::SHADOW_RENDER_MODE); glPopMatrix(); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 0587e979f6..ad2b220d55 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -292,7 +292,6 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Metavoxels, 0, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::BuckyBalls, 0, false); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Particles, 0, true); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Models, 0, true); addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); QMenu* voxelOptionsMenu = developerMenu->addMenu("Voxel Options"); @@ -309,6 +308,12 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DisableAutoAdjustLOD); + QMenu* modelOptionsMenu = developerMenu->addMenu("Model Options"); + addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::Models, 0, true); + addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelBounds, 0, false); + addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelElementProxy, 0, false); + addCheckableActionToQMenuAndActionHash(modelOptionsMenu, MenuOption::DisplayModelElementChildProxies, 0, false); + QMenu* avatarOptionsMenu = developerMenu->addMenu("Avatar Options"); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Avatars, 0, true); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d1097929f7..f1d35fcb81 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -304,6 +304,9 @@ namespace MenuOption { const QString DisplayFrustum = "Display Frustum"; const QString DisplayHands = "Display Hands"; const QString DisplayHandTargets = "Display Hand Targets"; + const QString DisplayModelBounds = "Display Model Bounds"; + const QString DisplayModelElementProxy = "Display Model Element Bounds"; + const QString DisplayModelElementChildProxies = "Display Model Element Children"; const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoServerAudio = "Echo Server Audio"; @@ -338,6 +341,7 @@ namespace MenuOption { const QString Metavoxels = "Metavoxels"; const QString Mirror = "Mirror"; const QString Models = "Models"; + const QString ModelOptions = "Model Options"; const QString MoveWithLean = "Move with Lean"; const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index a084ee0003..bc2bbb6712 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -215,17 +215,20 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) { renderBody(renderMode, glowLevel); } - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { + if (renderMode != SHADOW_RENDER_MODE && + Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { _skeletonModel.updateShapePositions(); _skeletonModel.renderJointCollisionShapes(0.7f); } - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes)) { + if (renderMode != SHADOW_RENDER_MODE && + Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes)) { if (shouldRenderHead(cameraPosition, renderMode)) { getHead()->getFaceModel().updateShapePositions(); getHead()->getFaceModel().renderJointCollisionShapes(0.7f); } } - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) { + if (renderMode != SHADOW_RENDER_MODE && + Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) { if (shouldRenderHead(cameraPosition, renderMode)) { getHead()->getFaceModel().updateShapePositions(); getHead()->getFaceModel().renderBoundingCollisionShapes(0.7f); @@ -234,7 +237,7 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { } } // If this is the avatar being looked at, render a little ball above their head - if (_isLookAtTarget) { + if (renderMode != SHADOW_RENDER_MODE &&_isLookAtTarget) { const float LOOK_AT_INDICATOR_RADIUS = 0.03f; const float LOOK_AT_INDICATOR_HEIGHT = 0.60f; const float LOOK_AT_INDICATOR_COLOR[] = { 0.8f, 0.0f, 0.0f, 0.5f }; @@ -340,7 +343,8 @@ 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; + Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + { Glower glower(glowLevel); @@ -351,7 +355,7 @@ void Avatar::renderBody(RenderMode renderMode, float glowLevel) { } _skeletonModel.render(1.0f, modelRenderMode); renderAttachments(modelRenderMode); - getHand()->render(false); + getHand()->render(false, modelRenderMode); } getHead()->render(1.0f, modelRenderMode); } diff --git a/interface/src/avatar/Hand.cpp b/interface/src/avatar/Hand.cpp index c925e452b2..320a8477c1 100644 --- a/interface/src/avatar/Hand.cpp +++ b/interface/src/avatar/Hand.cpp @@ -192,11 +192,11 @@ void Hand::calculateGeometry() { } } -void Hand::render(bool isMine) { - +void Hand::render(bool isMine, Model::RenderMode renderMode) { _renderAlpha = 1.0; - if (Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { + if (renderMode != Model::SHADOW_RENDER_MODE && + Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) { // draw a green sphere at hand joint location, which is actually near the wrist) for (size_t i = 0; i < getNumPalms(); i++) { PalmData& palm = getPalms()[i]; @@ -212,7 +212,7 @@ void Hand::render(bool isMine) { } } - if (Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) { + if (renderMode != Model::SHADOW_RENDER_MODE && Menu::getInstance()->isOptionChecked(MenuOption::DisplayHands)) { renderLeapHands(isMine); } diff --git a/interface/src/avatar/Hand.h b/interface/src/avatar/Hand.h index 65a7dcb74a..757a74db29 100755 --- a/interface/src/avatar/Hand.h +++ b/interface/src/avatar/Hand.h @@ -24,6 +24,7 @@ #include #include "InterfaceConfig.h" +#include "renderer/Model.h" #include "world.h" @@ -52,7 +53,7 @@ public: void init(); void reset(); void simulate(float deltaTime, bool isMine); - void render(bool isMine); + void render(bool isMine, Model::RenderMode renderMode = Model::DEFAULT_RENDER_MODE); // getters const glm::vec3& getLeapFingerTipBallPosition (int ball) const { return _leapFingerTipBalls [ball].position;} diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 19aebba25c..9cb8ea3d9c 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -182,7 +182,7 @@ void Head::relaxLean(float deltaTime) { } void Head::render(float alpha, Model::RenderMode mode) { - if (_faceModel.render(alpha, mode) && _renderLookatVectors) { + if (_faceModel.render(alpha, mode) && _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 34d256626b..1655a17f08 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -337,7 +337,9 @@ void MyAvatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) { return; // exit early } Avatar::render(cameraPosition, renderMode); - if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints)) { + + // don't display IK constraints in shadow mode + if (Menu::getInstance()->isOptionChecked(MenuOption::ShowIKConstraints) && renderMode != SHADOW_RENDER_MODE) { _skeletonModel.renderIKConstraints(); } } @@ -586,7 +588,7 @@ void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { if (shouldRenderHead(Application::getInstance()->getCamera()->getPosition(), renderMode)) { getHead()->render(1.0f, modelRenderMode); } - getHand()->render(true); + getHand()->render(true, modelRenderMode); } const float RENDER_HEAD_CUTOFF_DISTANCE = 0.50f; diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 9546c7d1c4..c762182290 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -39,8 +39,8 @@ void ModelTreeRenderer::update() { } } -void ModelTreeRenderer::render() { - OctreeRenderer::render(); +void ModelTreeRenderer::render(RenderMode renderMode) { + OctreeRenderer::render(renderMode); } Model* ModelTreeRenderer::getModel(const QString& url) { @@ -66,54 +66,130 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) const QList& modelItems = modelTreeElement->getModels(); uint16_t numberOfModels = modelItems.size(); + + bool isShadowMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE; + + bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds); + bool displayElementProxy = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementProxy); + bool displayElementChildProxies = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelElementChildProxies); + + + if (!isShadowMode && displayElementProxy && numberOfModels > 0) { + glm::vec3 elementCenter = modelTreeElement->getAABox().calcCenter() * (float)TREE_SCALE; + float elementSize = modelTreeElement->getScale() * (float)TREE_SCALE; + glColor3f(1.0f, 0.0f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x, elementCenter.y, elementCenter.z); + glutWireCube(elementSize); + glPopMatrix(); + + if (displayElementChildProxies) { + // draw the children + float halfSize = elementSize / 2.0f; + float quarterSize = elementSize / 4.0f; + glColor3f(1.0f, 1.0f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(1.0f, 0.0f, 1.0f); + glPushMatrix(); + glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z - quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.0f, 1.0f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.0f, 0.0f, 1.0f); + glPushMatrix(); + glTranslatef(elementCenter.x - quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(1.0f, 1.0f, 1.0f); + glPushMatrix(); + glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.0f, 0.5f, 0.5f); + glPushMatrix(); + glTranslatef(elementCenter.x - quarterSize, elementCenter.y + quarterSize, elementCenter.z + quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.5f, 0.0f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x + quarterSize, elementCenter.y - quarterSize, elementCenter.z + quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + + glColor3f(0.0f, 0.5f, 0.0f); + glPushMatrix(); + glTranslatef(elementCenter.x + quarterSize, elementCenter.y + quarterSize, elementCenter.z - quarterSize); + glutWireCube(halfSize); + glPopMatrix(); + } + + } for (uint16_t i = 0; i < numberOfModels; i++) { const ModelItem& modelItem = modelItems[i]; // render modelItem aspoints - glm::vec3 position = modelItem.getPosition() * (float)TREE_SCALE; - glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); - float radius = modelItem.getRadius() * (float)TREE_SCALE; - //glm::vec3 center = position + glm::vec3(radius, radius, radius); // center it around the position + AABox modelBox = modelItem.getAABox(); + modelBox.scale(TREE_SCALE); + if (args->_viewFrustum->boxInFrustum(modelBox) != ViewFrustum::OUTSIDE) { + glm::vec3 position = modelItem.getPosition() * (float)TREE_SCALE; + float radius = modelItem.getRadius() * (float)TREE_SCALE; + float size = modelItem.getSize() * (float)TREE_SCALE; - bool drawAsModel = modelItem.hasModel(); + bool drawAsModel = modelItem.hasModel(); - args->_renderedItems++; + args->_renderedItems++; - if (drawAsModel) { - glPushMatrix(); - const float alpha = 1.0f; + if (drawAsModel) { + glPushMatrix(); + const float alpha = 1.0f; - Model* model = getModel(modelItem.getModelURL()); + Model* model = getModel(modelItem.getModelURL()); - model->setScaleToFit(true, radius * 2.0f); - model->setSnapModelToCenter(true); + model->setScaleToFit(true, radius * 2.0f); + model->setSnapModelToCenter(true); - // set the rotation - glm::quat rotation = modelItem.getModelRotation(); - model->setRotation(rotation); + // set the rotation + glm::quat rotation = modelItem.getModelRotation(); + model->setRotation(rotation); - // set the position - model->setTranslation(position); + // set the position + model->setTranslation(position); + model->simulate(0.0f); - model->simulate(0.0f); + // TODO: should we allow modelItems to have alpha on their models? + Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE + ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + model->render(alpha, modelRenderMode); + if (!isShadowMode && displayModelBounds) { + glColor3f(0.0f, 1.0f, 0.0f); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glutWireCube(size); + glPopMatrix(); + } - model->render(alpha); // TODO: should we allow modelItems to have alpha on their models? - - const bool wantDebugSphere = false; - if (wantDebugSphere) { - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glutWireSphere(radius, 15, 15); - glPopMatrix(); - } - - glPopMatrix(); - } else { - glPushMatrix(); - glTranslatef(position.x, position.y, position.z); - glutSolidSphere(radius, 15, 15); - glPopMatrix(); + glPopMatrix(); + } else { + glColor3ub(modelItem.getColor()[RED_INDEX],modelItem.getColor()[GREEN_INDEX],modelItem.getColor()[BLUE_INDEX]); + glPushMatrix(); + glTranslatef(position.x, position.y, position.z); + glutSolidSphere(radius, 15, 15); + glPopMatrix(); + } } } } diff --git a/interface/src/models/ModelTreeRenderer.h b/interface/src/models/ModelTreeRenderer.h index 5ed4720391..7af5bbf317 100644 --- a/interface/src/models/ModelTreeRenderer.h +++ b/interface/src/models/ModelTreeRenderer.h @@ -46,7 +46,7 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); virtual void init(); - virtual void render(); + virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); protected: Model* getModel(const QString& url); diff --git a/interface/src/particles/ParticleTreeRenderer.cpp b/interface/src/particles/ParticleTreeRenderer.cpp index aa498082d9..2983093564 100644 --- a/interface/src/particles/ParticleTreeRenderer.cpp +++ b/interface/src/particles/ParticleTreeRenderer.cpp @@ -39,8 +39,8 @@ void ParticleTreeRenderer::update() { } } -void ParticleTreeRenderer::render() { - OctreeRenderer::render(); +void ParticleTreeRenderer::render(RenderMode renderMode) { + OctreeRenderer::render(renderMode); } Model* ParticleTreeRenderer::getModel(const QString& url) { @@ -102,7 +102,11 @@ void ParticleTreeRenderer::renderElement(OctreeElement* element, RenderArgs* arg model->setScale(scale * MODEL_SCALE * radius * modelScale); model->simulate(0.0f); - model->render(alpha); // TODO: should we allow particles to have alpha on their models? + + // TODO: should we allow particles to have alpha on their models? + Model::RenderMode modelRenderMode = args->_renderMode == OctreeRenderer::SHADOW_RENDER_MODE + ? Model::SHADOW_RENDER_MODE : Model::DEFAULT_RENDER_MODE; + model->render(alpha, modelRenderMode); const bool wantDebugSphere = false; if (wantDebugSphere) { diff --git a/interface/src/particles/ParticleTreeRenderer.h b/interface/src/particles/ParticleTreeRenderer.h index ea52df3932..ccb8bfbdf3 100644 --- a/interface/src/particles/ParticleTreeRenderer.h +++ b/interface/src/particles/ParticleTreeRenderer.h @@ -43,7 +43,7 @@ public: void processEraseMessage(const QByteArray& dataByteArray, const SharedNodePointer& sourceNode); virtual void init(); - virtual void render(); + virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); protected: Model* getModel(const QString& url); diff --git a/libraries/models/src/ModelItem.h b/libraries/models/src/ModelItem.h index 76a78122ff..9edcf482c0 100644 --- a/libraries/models/src/ModelItem.h +++ b/libraries/models/src/ModelItem.h @@ -85,6 +85,9 @@ public: /// used by ModelScriptingInterface to return ModelItemProperties for unknown models void setIsUnknownID() { _id = UNKNOWN_MODEL_ID; _idSet = true; } + + glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); } + glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); } private: glm::vec3 _position; @@ -156,11 +159,20 @@ public: /// get position in domain scale units (0.0 - 1.0) const glm::vec3& getPosition() const { return _position; } + glm::vec3 getMinimumPoint() const { return _position - glm::vec3(_radius, _radius, _radius); } + glm::vec3 getMaximumPoint() const { return _position + glm::vec3(_radius, _radius, _radius); } + const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } /// get radius in domain scale units (0.0 - 1.0) float getRadius() const { return _radius; } + + /// get maximum dimension in domain scale units (0.0 - 1.0) + float getSize() const { return _radius * 2.0f; } + + /// get maximum dimension in domain scale units (0.0 - 1.0) + AABox getAABox() const { return AABox(getMinimumPoint(), getSize()); } // model related properties bool hasModel() const { return !_modelURL.isEmpty(); } diff --git a/libraries/models/src/ModelTree.cpp b/libraries/models/src/ModelTree.cpp index cef38a9422..45694b081d 100644 --- a/libraries/models/src/ModelTree.cpp +++ b/libraries/models/src/ModelTree.cpp @@ -113,13 +113,10 @@ void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& send FindAndUpdateModelOperator theOperator(model); recurseTreeWithOperator(&theOperator); - // if we didn't find it in the tree, then store it... if (!theOperator.wasFound()) { - glm::vec3 position = model.getPosition(); - float size = std::max(MINIMUM_MODEL_ELEMENT_SIZE, model.getRadius()); - - ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementAt(position.x, position.y, position.z, size); + AABox modelBox = model.getAABox(); + ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementContaining(model.getAABox()); element->storeModel(model); } // what else do we need to do here to get reaveraging to work @@ -494,12 +491,29 @@ void ModelTree::update() { lockForWrite(); _isDirty = true; - // TODO: we don't need to update models yet, but when we do, for example - // when we add animation support, we will revisit this code. - //ModelTreeUpdateArgs args = { }; - //recurseTreeWithOperation(updateOperation, &args); + ModelTreeUpdateArgs args = { }; + recurseTreeWithOperation(updateOperation, &args); - // Now is a reasonable time to prune the tree... + // now add back any of the particles that moved elements.... + int movingModels = args._movingModels.size(); + for (int i = 0; i < movingModels; i++) { + bool shouldDie = args._movingModels[i].getShouldDie(); + + // if the particle is still inside our total bounds, then re-add it + AABox treeBounds = getRoot()->getAABox(); + + if (!shouldDie && treeBounds.contains(args._movingModels[i].getPosition())) { + storeModel(args._movingModels[i]); + } else { + uint32_t modelItemID = args._movingModels[i].getID(); + quint64 deletedAt = usecTimestampNow(); + _recentlyDeletedModelsLock.lockForWrite(); + _recentlyDeletedModelItemIDs.insert(deletedAt, modelItemID); + _recentlyDeletedModelsLock.unlock(); + } + } + + // prune the tree... recurseTreeWithOperation(pruneOperation, NULL); unlock(); } diff --git a/libraries/models/src/ModelTreeElement.cpp b/libraries/models/src/ModelTreeElement.cpp index 0327d8a0c4..5c5d5100cf 100644 --- a/libraries/models/src/ModelTreeElement.cpp +++ b/libraries/models/src/ModelTreeElement.cpp @@ -47,15 +47,32 @@ ModelTreeElement* ModelTreeElement::addChildAtIndex(int index) { } -bool ModelTreeElement::appendElementData(OctreePacketData* packetData) const { +bool ModelTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { bool success = true; // assume the best... - // write our models out... - uint16_t numberOfModels = _modelItems->size(); + // write our models out... first determine which of the models are in view based on our params + uint16_t numberOfModels = 0; + QVector indexesOfModelsToInclude; + + for (uint16_t i = 0; i < _modelItems->size(); i++) { + if (params.viewFrustum) { + const ModelItem& model = (*_modelItems)[i]; + AABox modelBox = model.getAABox(); + modelBox.scale(TREE_SCALE); + if (params.viewFrustum->boxInFrustum(modelBox) != ViewFrustum::OUTSIDE) { + indexesOfModelsToInclude << i; + numberOfModels++; + } + } else { + indexesOfModelsToInclude << i; + numberOfModels++; + } + } + success = packetData->appendValue(numberOfModels); if (success) { - for (uint16_t i = 0; i < numberOfModels; i++) { + foreach (uint16_t i, indexesOfModelsToInclude) { const ModelItem& model = (*_modelItems)[i]; success = model.appendModelData(packetData); if (!success) { @@ -66,10 +83,25 @@ bool ModelTreeElement::appendElementData(OctreePacketData* packetData) const { return success; } -void ModelTreeElement::update(ModelTreeUpdateArgs& args) { - markWithChangedTime(); - // TODO: early exit when _modelItems is empty +bool ModelTreeElement::containsModelBounds(const ModelItem& model) const { + return _box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint()); +} +bool ModelTreeElement::bestFitModelBounds(const ModelItem& model) const { + if (_box.contains(model.getMinimumPoint()) && _box.contains(model.getMaximumPoint())) { + int childForMinimumPoint = getMyChildContainingPoint(model.getMinimumPoint()); + int childForMaximumPoint = getMyChildContainingPoint(model.getMaximumPoint()); + + // If I contain both the minimum and maximum point, but two different children of mine + // contain those points, then I am the best fit for that model + if (childForMinimumPoint != childForMaximumPoint) { + return true; + } + } + return false; +} + +void ModelTreeElement::update(ModelTreeUpdateArgs& args) { // update our contained models QList::iterator modelItr = _modelItems->begin(); while(modelItr != _modelItems->end()) { @@ -78,19 +110,18 @@ void ModelTreeElement::update(ModelTreeUpdateArgs& args) { // If the model wants to die, or if it's left our bounding box, then move it // into the arguments moving models. These will be added back or deleted completely - if (model.getShouldDie() || !_box.contains(model.getPosition())) { + if (model.getShouldDie() || !bestFitModelBounds(model)) { args._movingModels.push_back(model); // erase this model modelItr = _modelItems->erase(modelItr); + + // this element has changed so mark it... + markWithChangedTime(); } else { ++modelItr; } } - // TODO: if _modelItems is empty after while loop consider freeing memory in _modelItems if - // internal array is too big (QList internal array does not decrease size except in dtor and - // assignment operator). Otherwise _modelItems could become a "resource leak" for large - // roaming piles of models. } bool ModelTreeElement::findSpherePenetration(const glm::vec3& center, float radius, @@ -136,6 +167,7 @@ bool ModelTreeElement::updateModel(const ModelItem& model) { (localOlder ? "OLDER" : "NEWER"), difference, debug::valueOf(model.isNewlyCreated()) ); } + thisModel.copyChangedProperties(model); markWithChangedTime(); } else { diff --git a/libraries/models/src/ModelTreeElement.h b/libraries/models/src/ModelTreeElement.h index ce03d50065..ce9e2dec7e 100644 --- a/libraries/models/src/ModelTreeElement.h +++ b/libraries/models/src/ModelTreeElement.h @@ -74,7 +74,7 @@ public: virtual bool requiresSplit() const { return false; } /// Override to serialize the state of this element. This is used for persistance and for transmission across the network. - virtual bool appendElementData(OctreePacketData* packetData) const; + virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const; /// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading /// from the network. @@ -118,6 +118,9 @@ public: bool removeModelWithID(uint32_t id); + bool containsModelBounds(const ModelItem& model) const; + bool bestFitModelBounds(const ModelItem& model) const; + protected: virtual void init(unsigned char * octalCode); diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index d308d007cd..5b766ecdd7 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -578,6 +578,10 @@ OctreeElement* Octree::getOrCreateChildElementAt(float x, float y, float z, floa return getRoot()->getOrCreateChildElementAt(x, y, z, s); } +OctreeElement* Octree::getOrCreateChildElementContaining(const AABox& box) { + return getRoot()->getOrCreateChildElementContaining(box); +} + // combines the ray cast arguments into a single object class RayArgs { @@ -1001,7 +1005,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // Keep track of how deep we've encoded. currentEncodeLevel++; - params.maxLevelReached = std::max(currentEncodeLevel,params.maxLevelReached); + params.maxLevelReached = std::max(currentEncodeLevel, params.maxLevelReached); // If we've reached our max Search Level, then stop searching. if (currentEncodeLevel >= params.maxEncodeLevel) { @@ -1342,7 +1346,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, OctreeElement* childElement = element->getChildAtIndex(i); if (childElement) { int bytesBeforeChild = packetData->getUncompressedSize(); - continueThisLevel = childElement->appendElementData(packetData); + continueThisLevel = childElement->appendElementData(packetData, params); int bytesAfterChild = packetData->getUncompressedSize(); if (!continueThisLevel) { diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 6e0693dc23..4a17cb3c1d 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -227,6 +227,7 @@ public: OctreeElement* getOctreeEnclosingElementAt(float x, float y, float z, float s) const; OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s); + OctreeElement* getOrCreateChildElementContaining(const AABox& box); void recurseTreeWithOperation(RecurseOctreeOperation operation, void* extraData = NULL); void recurseTreeWithPostOperation(RecurseOctreeOperation operation, void* extraData = NULL); diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index d54f7aa94b..edba26f2a7 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -1379,3 +1379,90 @@ OctreeElement* OctreeElement::getOrCreateChildElementAt(float x, float y, float // Now that we have the child to recurse down, let it answer the original question... return child->getOrCreateChildElementAt(x, y, z, s); } + + +OctreeElement* OctreeElement::getOrCreateChildElementContaining(const AABox& box) { + OctreeElement* child = NULL; + + float ourScale = getScale(); + float boxScale = box.getScale(); + + if(boxScale > ourScale) { + qDebug("UNEXPECTED -- OctreeElement::getOrCreateChildElementContaining() " + "boxScale=[%f] > ourScale=[%f] ", boxScale, ourScale); + } + + // Determine which of our children the minimum and maximum corners of the box live in... + glm::vec3 boxCornerMinimum = box.getCorner(); + glm::vec3 boxCornerMaximum = box.calcTopFarLeft(); + + int childIndexBoxMinimum = getMyChildContainingPoint(boxCornerMinimum); + int childIndexBoxMaximum = getMyChildContainingPoint(boxCornerMaximum); + + // If the minimum and maximum corners of the box are in two different children's boxes, then we are the containing element + if (childIndexBoxMinimum != childIndexBoxMaximum) { + return this; + } + + // otherwise, they are the same and that child should be considered as the correct element + int childIndex = childIndexBoxMinimum; // both the same... + + // Now, check if we have a child at that location + child = getChildAtIndex(childIndex); + if (!child) { + child = addChildAtIndex(childIndex); + } + + // Now that we have the child to recurse down, let it answer the original question... + return child->getOrCreateChildElementContaining(box); +} + +int OctreeElement::getMyChildContainingPoint(const glm::vec3& point) const { + glm::vec3 ourCenter = _box.calcCenter(); + int childIndex = CHILD_UNKNOWN; + // left half + if (point.x > ourCenter.x) { + if (point.y > ourCenter.y) { + // top left + if (point.z > ourCenter.z) { + // top left far + childIndex = CHILD_TOP_LEFT_FAR; + } else { + // top left near + childIndex = CHILD_TOP_LEFT_NEAR; + } + } else { + // bottom left + if (point.z > ourCenter.z) { + // bottom left far + childIndex = CHILD_BOTTOM_LEFT_FAR; + } else { + // bottom left near + childIndex = CHILD_BOTTOM_LEFT_NEAR; + } + } + } else { + // right half + if (point.y > ourCenter.y) { + // top right + if (point.z > ourCenter.z) { + // top right far + childIndex = CHILD_TOP_RIGHT_FAR; + } else { + // top right near + childIndex = CHILD_TOP_RIGHT_NEAR; + } + } else { + // bottom right + if (point.z > ourCenter.z) { + // bottom right far + childIndex = CHILD_BOTTOM_RIGHT_FAR; + } else { + // bottom right near + childIndex = CHILD_BOTTOM_RIGHT_NEAR; + } + } + } + return childIndex; +} + diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index c5eec1c9e2..42c9abad46 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -23,14 +23,14 @@ #include "AABox.h" #include "ViewFrustum.h" #include "OctreeConstants.h" -//#include "Octree.h" +class EncodeBitstreamParams; class Octree; class OctreeElement; class OctreeElementDeleteHook; class OctreePacketData; -class VoxelSystem; class ReadBitstreamToTreeParams; +class VoxelSystem; // Callers who want delete hook callbacks should implement this class class OctreeElementDeleteHook { @@ -81,7 +81,7 @@ public: virtual bool requiresSplit() const { return false; } /// Override to serialize the state of this element. This is used for persistance and for transmission across the network. - virtual bool appendElementData(OctreePacketData* packetData) const { return true; } + virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { return true; } /// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading /// from the network. @@ -217,6 +217,8 @@ public: OctreeElement* getOrCreateChildElementAt(float x, float y, float z, float s); + OctreeElement* getOrCreateChildElementContaining(const AABox& box); + int getMyChildContainingPoint(const glm::vec3& point) const; protected: diff --git a/libraries/octree/src/OctreeHeadlessViewer.h b/libraries/octree/src/OctreeHeadlessViewer.h index ebabf1dbad..3509713d50 100644 --- a/libraries/octree/src/OctreeHeadlessViewer.h +++ b/libraries/octree/src/OctreeHeadlessViewer.h @@ -33,7 +33,7 @@ public: virtual void renderElement(OctreeElement* element, RenderArgs* args) { /* swallow these */ }; virtual void init(); - virtual void render() { /* swallow these */ }; + virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE) { /* swallow these */ }; void setJurisdictionListener(JurisdictionListener* jurisdictionListener) { _jurisdictionListener = jurisdictionListener; } diff --git a/libraries/octree/src/OctreeRenderer.cpp b/libraries/octree/src/OctreeRenderer.cpp index 5c5da2250f..c1ce3cb218 100644 --- a/libraries/octree/src/OctreeRenderer.cpp +++ b/libraries/octree/src/OctreeRenderer.cpp @@ -154,8 +154,8 @@ bool OctreeRenderer::renderOperation(OctreeElement* element, void* extraData) { return false; } -void OctreeRenderer::render() { - RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust() }; +void OctreeRenderer::render(RenderMode renderMode) { + RenderArgs args = { 0, this, _viewFrustum, getSizeScale(), getBoundaryLevelAdjust(), renderMode }; if (_tree) { _tree->lockForRead(); _tree->recurseTreeWithOperation(renderOperation, &args); diff --git a/libraries/octree/src/OctreeRenderer.h b/libraries/octree/src/OctreeRenderer.h index 652f9d0399..73e26c97f6 100644 --- a/libraries/octree/src/OctreeRenderer.h +++ b/libraries/octree/src/OctreeRenderer.h @@ -25,15 +25,7 @@ #include "ViewFrustum.h" class OctreeRenderer; - -class RenderArgs { -public: - int _renderedItems; - OctreeRenderer* _renderer; - ViewFrustum* _viewFrustum; - float _sizeScale; - int _boundaryLevelAdjust; -}; +class RenderArgs; // Generic client side Octree renderer class. @@ -59,8 +51,10 @@ public: /// initialize and GPU/rendering related resources virtual void init(); + enum RenderMode { DEFAULT_RENDER_MODE, SHADOW_RENDER_MODE, DIFFUSE_RENDER_MODE, NORMAL_RENDER_MODE }; + /// render the content of the octree - virtual void render(); + virtual void render(RenderMode renderMode = DEFAULT_RENDER_MODE); ViewFrustum* getViewFrustum() const { return _viewFrustum; } void setViewFrustum(ViewFrustum* viewFrustum) { _viewFrustum = viewFrustum; } @@ -75,4 +69,15 @@ protected: ViewFrustum* _viewFrustum; }; +class RenderArgs { +public: + int _renderedItems; + OctreeRenderer* _renderer; + ViewFrustum* _viewFrustum; + float _sizeScale; + int _boundaryLevelAdjust; + OctreeRenderer::RenderMode _renderMode; +}; + + #endif // hifi_OctreeRenderer_h diff --git a/libraries/particles/src/ParticleTreeElement.cpp b/libraries/particles/src/ParticleTreeElement.cpp index d28ccf2f5e..b6e59eb0ab 100644 --- a/libraries/particles/src/ParticleTreeElement.cpp +++ b/libraries/particles/src/ParticleTreeElement.cpp @@ -47,7 +47,7 @@ ParticleTreeElement* ParticleTreeElement::addChildAtIndex(int index) { } -bool ParticleTreeElement::appendElementData(OctreePacketData* packetData) const { +bool ParticleTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { bool success = true; // assume the best... // write our particles out... diff --git a/libraries/particles/src/ParticleTreeElement.h b/libraries/particles/src/ParticleTreeElement.h index 59f80d588a..4381cdd777 100644 --- a/libraries/particles/src/ParticleTreeElement.h +++ b/libraries/particles/src/ParticleTreeElement.h @@ -76,7 +76,7 @@ public: virtual bool requiresSplit() const { return false; } /// Override to serialize the state of this element. This is used for persistance and for transmission across the network. - virtual bool appendElementData(OctreePacketData* packetData) const; + virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const; /// Override to deserialize the state of this element. This is used for loading from a persisted file or from reading /// from the network. diff --git a/libraries/voxels/src/VoxelTreeElement.cpp b/libraries/voxels/src/VoxelTreeElement.cpp index 2582980816..f72e628b74 100644 --- a/libraries/voxels/src/VoxelTreeElement.cpp +++ b/libraries/voxels/src/VoxelTreeElement.cpp @@ -65,7 +65,7 @@ void VoxelTreeElement::splitChildren() { } } -bool VoxelTreeElement::appendElementData(OctreePacketData* packetData) const { +bool VoxelTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { return packetData->appendColor(getColor()); } diff --git a/libraries/voxels/src/VoxelTreeElement.h b/libraries/voxels/src/VoxelTreeElement.h index 8733987df4..788a728f6f 100644 --- a/libraries/voxels/src/VoxelTreeElement.h +++ b/libraries/voxels/src/VoxelTreeElement.h @@ -43,7 +43,7 @@ public: virtual bool hasContent() const { return isColored(); } virtual void splitChildren(); virtual bool requiresSplit() const; - virtual bool appendElementData(OctreePacketData* packetData) const; + virtual bool appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const; virtual int readElementDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args); virtual void calculateAverageFromChildren(); virtual bool collapseChildren();