mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 00:13:53 +02:00
Merge pull request #2815 from ZappoMan/modelserver
More Model Improvements & some other tweaks
This commit is contained in:
commit
538e785208
27 changed files with 359 additions and 103 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <HandData.h>
|
||||
|
||||
#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;}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<ModelItem>& 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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<uint16_t> 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<ModelItem>::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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue