Merge branch 'master' of https://github.com/highfidelity/hifi into anchers_apply_rotation_and_scale

This commit is contained in:
Atlante45 2014-05-17 22:08:39 -07:00
commit 2839f5207b
16 changed files with 530 additions and 133 deletions

View file

@ -230,6 +230,9 @@ target_link_libraries(
"${GNUTLS_LIBRARY}"
)
# assume we are using a Qt build without bearer management
add_definitions(-DQT_NO_BEARERMANAGEMENT)
if (APPLE)
# link in required OS X frameworks and include the right GL headers
find_library(AppKit AppKit)

View file

@ -169,6 +169,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_voxelHideShowThread(&_voxels),
_packetsPerSecond(0),
_bytesPerSecond(0),
_nodeBoundsDisplay(this),
_previousScriptLocation(),
_runningScriptsWidget(new RunningScriptsWidget(_window)),
_runningScriptsWidgetWasVisible(false)
@ -2527,6 +2528,9 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) {
// restore default, white specular
glMaterialfv(GL_FRONT, GL_SPECULAR, WHITE_SPECULAR_COLOR);
_nodeBoundsDisplay.draw();
}
bool mirrorMode = (whichCamera.getInterpolatedMode() == CAMERA_MODE_MIRROR);
@ -2760,6 +2764,7 @@ void Application::displayOverlay() {
? 80 : 20;
drawText(_glWidget->width() - 100, _glWidget->height() - timerBottom, 0.30f, 0.0f, 0, frameTimer, WHITE_TEXT);
}
_nodeBoundsDisplay.drawOverlay();
// give external parties a change to hook in
emit renderingOverlay();
@ -3417,8 +3422,9 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript
}
// start the script on a new thread...
ScriptEngine* scriptEngine = new ScriptEngine(QUrl(scriptName), &_controllerScriptingInterface);
_scriptEnginesHash.insert(scriptName, scriptEngine);
QUrl scriptUrl(scriptName);
ScriptEngine* scriptEngine = new ScriptEngine(scriptUrl, &_controllerScriptingInterface);
_scriptEnginesHash.insert(scriptUrl.toString(), scriptEngine);
if (!scriptEngine->hasScript()) {
qDebug() << "Application::loadScript(), script failed to load...";

View file

@ -74,6 +74,7 @@
#include "ui/BandwidthDialog.h"
#include "ui/BandwidthMeter.h"
#include "ui/ModelsBrowser.h"
#include "ui/NodeBounds.h"
#include "ui/OctreeStatsDialog.h"
#include "ui/RearMirrorTools.h"
#include "ui/SnapshotShareDialog.h"
@ -191,6 +192,8 @@ public:
bool isMouseHidden() const { return _mouseHidden; }
const glm::vec3& getMouseRayOrigin() const { return _mouseRayOrigin; }
const glm::vec3& getMouseRayDirection() const { return _mouseRayDirection; }
int getMouseX() const { return _mouseX; }
int getMouseY() const { return _mouseY; }
Faceplus* getFaceplus() { return &_faceplus; }
Faceshift* getFaceshift() { return &_faceshift; }
Visage* getVisage() { return &_visage; }
@ -246,7 +249,7 @@ public:
void computeOffAxisFrustum(float& left, float& right, float& bottom, float& top, float& nearVal,
float& farVal, glm::vec4& nearClipPlane, glm::vec4& farClipPlane) const;
NodeBounds& getNodeBoundsDisplay() { return _nodeBoundsDisplay; }
VoxelShader& getVoxelShader() { return _voxelShader; }
PointShader& getPointShader() { return _pointShader; }
@ -526,6 +529,8 @@ private:
NodeToOctreeSceneStats _octreeServerSceneStats;
QReadWriteLock _octreeSceneStatsLock;
NodeBounds _nodeBoundsDisplay;
std::vector<VoxelFade> _voxelFades;
ControllerScriptingInterface _controllerScriptingInterface;
QPointer<LogDialog> _logDialog;

View file

@ -42,6 +42,7 @@
#include "ui/MetavoxelEditor.h"
#include "ui/ModelsBrowser.h"
#include "ui/LoginDialog.h"
#include "ui/NodeBounds.h"
Menu* Menu::_instance = NULL;
@ -242,6 +243,19 @@ Menu::Menu() :
SLOT(setEnable3DTVMode(bool)));
QMenu* nodeBordersMenu = viewMenu->addMenu("Server Borders");
NodeBounds& nodeBounds = appInstance->getNodeBoundsDisplay();
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersVoxelNodes,
Qt::CTRL | Qt::SHIFT | Qt::Key_1, false,
&nodeBounds, SLOT(setShowVoxelNodes(bool)));
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersModelNodes,
Qt::CTRL | Qt::SHIFT | Qt::Key_2, false,
&nodeBounds, SLOT(setShowModelNodes(bool)));
addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersParticleNodes,
Qt::CTRL | Qt::SHIFT | Qt::Key_3, false,
&nodeBounds, SLOT(setShowParticleNodes(bool)));
QMenu* avatarSizeMenu = viewMenu->addMenu("Avatar Size");
addActionToQMenuAndActionHash(avatarSizeMenu,

View file

@ -367,6 +367,9 @@ namespace MenuOption {
const QString SettingsExport = "Export Settings";
const QString SettingsImport = "Import Settings";
const QString Shadows = "Shadows";
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
const QString ShowBordersModelNodes = "Show Model Nodes";
const QString ShowBordersParticleNodes = "Show Particle Nodes";
const QString ShowIKConstraints = "Show IK Constraints";
const QString Stars = "Stars";
const QString Stats = "Stats";

View file

@ -214,37 +214,37 @@ void Avatar::render(const glm::vec3& cameraPosition, RenderMode renderMode) {
if (Menu::getInstance()->isOptionChecked(MenuOption::Avatars)) {
renderBody(renderMode, glowLevel);
}
if (renderMode != SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes)) {
_skeletonModel.updateShapePositions();
_skeletonModel.renderJointCollisionShapes(0.7f);
}
if (renderMode != SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes)) {
if (shouldRenderHead(cameraPosition, renderMode)) {
getHead()->getFaceModel().updateShapePositions();
if (renderMode != SHADOW_RENDER_MODE) {
bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes);
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
if (renderSkeleton || renderHead || renderBounding) {
updateShapePositions();
}
if (renderSkeleton) {
_skeletonModel.renderJointCollisionShapes(0.7f);
}
if (renderHead && shouldRenderHead(cameraPosition, renderMode)) {
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
}
}
if (renderMode != SHADOW_RENDER_MODE &&
Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes)) {
if (shouldRenderHead(cameraPosition, renderMode)) {
getHead()->getFaceModel().updateShapePositions();
if (renderBounding && shouldRenderHead(cameraPosition, renderMode)) {
getHead()->getFaceModel().renderBoundingCollisionShapes(0.7f);
_skeletonModel.updateShapePositions();
_skeletonModel.renderBoundingCollisionShapes(0.7f);
}
}
// If this is the avatar being looked at, render a little ball above their head
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 };
glPushMatrix();
glColor4fv(LOOK_AT_INDICATOR_COLOR);
glTranslatef(_position.x, _position.y + (getSkeletonHeight() * LOOK_AT_INDICATOR_HEIGHT), _position.z);
glutSolidSphere(LOOK_AT_INDICATOR_RADIUS, 15, 15);
glPopMatrix();
// If this is the avatar being looked at, render a little ball above their head
if (_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 };
glPushMatrix();
glColor4fv(LOOK_AT_INDICATOR_COLOR);
glTranslatef(_position.x, _position.y + (getSkeletonHeight() * LOOK_AT_INDICATOR_HEIGHT), _position.z);
glutSolidSphere(LOOK_AT_INDICATOR_RADIUS, 15, 15);
glPopMatrix();
}
}
// quick check before falling into the code below:
@ -585,6 +585,12 @@ void Avatar::updateShapePositions() {
_skeletonModel.updateShapePositions();
Model& headModel = getHead()->getFaceModel();
headModel.updateShapePositions();
/* KEEP FOR DEBUG: use this in rather than code above to see shapes
* in their default positions where the bounding shape is computed.
_skeletonModel.resetShapePositions();
Model& headModel = getHead()->getFaceModel();
headModel.resetShapePositions();
*/
}
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {

View file

@ -334,13 +334,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
glm::quat shoulderRotation;
getJointRotation(shoulderJointIndex, shoulderRotation, true);
applyRotationDelta(shoulderJointIndex, rotationBetween(shoulderRotation * forwardVector, elbowPosition - shoulderPosition), false);
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
setJointRotation(shoulderJointIndex, shoulderRotation, true);
glm::quat elbowRotation;
getJointRotation(elbowJointIndex, elbowRotation, true);
applyRotationDelta(elbowJointIndex, rotationBetween(elbowRotation * forwardVector, wristPosition - elbowPosition), false);
setJointRotation(elbowJointIndex, rotationBetween(shoulderRotation * forwardVector,
wristPosition - elbowPosition) * shoulderRotation, true);
setJointRotation(jointIndex, rotation, true);
}

View file

@ -39,6 +39,7 @@ Model::Model(QObject* parent) :
_scaledToFit(false),
_snapModelToCenter(false),
_snappedToCenter(false),
_rootIndex(-1),
_shapesAreDirty(true),
_boundingRadius(0.f),
_boundingShape(),
@ -128,7 +129,6 @@ QVector<Model::JointState> Model::createJointStates(const FBXGeometry& geometry)
jointIsSet.fill(false, numJoints);
int numJointsSet = 0;
int lastNumJointsSet = -1;
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset);
while (numJointsSet < numJoints && numJointsSet != lastNumJointsSet) {
lastNumJointsSet = numJointsSet;
for (int i = 0; i < numJoints; ++i) {
@ -139,6 +139,8 @@ QVector<Model::JointState> Model::createJointStates(const FBXGeometry& geometry)
const FBXJoint& joint = geometry.joints[i];
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
_rootIndex = i;
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * glm::scale(_scale) * glm::translate(_offset);
glm::quat combinedRotation = joint.preRotation * state.rotation * joint.postRotation;
state.transform = baseTransform * geometry.offset * glm::translate(state.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
@ -603,66 +605,20 @@ void Model::clearShapes() {
void Model::rebuildShapes() {
clearShapes();
if (!_geometry) {
if (!_geometry || _rootIndex == -1) {
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (geometry.joints.isEmpty()) {
return;
}
int numJoints = geometry.joints.size();
QVector<glm::mat4> transforms;
transforms.fill(glm::mat4(), numJoints);
QVector<glm::quat> combinedRotations;
combinedRotations.fill(glm::quat(), numJoints);
QVector<bool> shapeIsSet;
shapeIsSet.fill(false, numJoints);
int rootIndex = 0;
// We create the shapes with proper dimensions, but we set their transforms later.
float uniformScale = extractUniformScale(_scale);
int numShapesSet = 0;
int lastNumShapesSet = -1;
while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) {
lastNumShapesSet = numShapesSet;
for (int i = 0; i < numJoints; ++i) {
if (shapeIsSet[i]) {
continue;
}
const FBXJoint& joint = geometry.joints[i];
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
rootIndex = i;
glm::mat4 baseTransform = glm::mat4_cast(_rotation) * uniformScale * glm::translate(_offset);
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = baseTransform * geometry.offset * glm::translate(joint.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
combinedRotations[i] = _rotation * combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
} else if (shapeIsSet[parentIndex]) {
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) * joint.preTransform *
glm::mat4_cast(combinedRotation) * joint.postTransform;
combinedRotations[i] = combinedRotations[parentIndex] * combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
}
}
}
// joint shapes
Extents totalExtents;
totalExtents.reset();
for (int i = 0; i < _jointStates.size(); i++) {
const FBXJoint& joint = geometry.joints[i];
glm::vec3 worldPosition = extractTranslation(transforms[i]);
Extents shapeExtents;
shapeExtents.reset();
float radius = uniformScale * joint.boneRadius;
float halfHeight = 0.5f * uniformScale * joint.distanceToParent;
Shape::Type type = joint.shapeType;
@ -672,47 +628,150 @@ void Model::rebuildShapes() {
}
if (type == Shape::CAPSULE_SHAPE) {
CapsuleShape* capsule = new CapsuleShape(radius, halfHeight);
capsule->setPosition(worldPosition);
capsule->setRotation(combinedRotations[i] * joint.shapeRotation);
_jointShapes.push_back(capsule);
// add the two furthest surface points of the capsule
glm::vec3 axis;
capsule->computeNormalizedAxis(axis);
axis = halfHeight * axis + glm::vec3(radius);
shapeExtents.addPoint(worldPosition + axis);
shapeExtents.addPoint(worldPosition - axis);
totalExtents.addExtents(shapeExtents);
} else if (type == Shape::SPHERE_SHAPE) {
SphereShape* sphere = new SphereShape(radius, worldPosition);
SphereShape* sphere = new SphereShape(radius, glm::vec3(0.0f));
_jointShapes.push_back(sphere);
glm::vec3 axis = glm::vec3(radius);
shapeExtents.addPoint(worldPosition + axis);
shapeExtents.addPoint(worldPosition - axis);
totalExtents.addExtents(shapeExtents);
} else {
// this shape type is not handled and the joint shouldn't collide,
// however we must have a shape for each joint,
// so we make a bogus sphere with zero radius.
// TODO: implement collision groups for more control over what collides with what
SphereShape* sphere = new SphereShape(0.f, worldPosition);
SphereShape* sphere = new SphereShape(0.f, glm::vec3(0.0f));
_jointShapes.push_back(sphere);
}
}
// bounding shape
// NOTE: we assume that the longest side of totalExtents is the yAxis
// This method moves the shapes to their default positions in Model frame
// which is where we compute the bounding shape's parameters.
computeBoundingShape(geometry);
// finally sync shapes to joint positions
_shapesAreDirty = true;
updateShapePositions();
}
void Model::computeBoundingShape(const FBXGeometry& geometry) {
// compute default joint transforms and rotations
// (in local frame, ignoring Model translation and rotation)
int numJoints = geometry.joints.size();
QVector<glm::mat4> transforms;
transforms.fill(glm::mat4(), numJoints);
QVector<glm::quat> finalRotations;
finalRotations.fill(glm::quat(), numJoints);
QVector<bool> shapeIsSet;
shapeIsSet.fill(false, numJoints);
int numShapesSet = 0;
int lastNumShapesSet = -1;
glm::vec3 rootOffset(0.0f);
while (numShapesSet < numJoints && numShapesSet != lastNumShapesSet) {
lastNumShapesSet = numShapesSet;
for (int i = 0; i < numJoints; i++) {
const FBXJoint& joint = geometry.joints.at(i);
int parentIndex = joint.parentIndex;
if (parentIndex == -1) {
glm::mat4 baseTransform = glm::scale(_scale) * glm::translate(_offset);
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = baseTransform * geometry.offset * glm::translate(joint.translation)
* joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform;
rootOffset = extractTranslation(transforms[i]);
finalRotations[i] = combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
} else if (shapeIsSet[parentIndex]) {
glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation;
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
* joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform;
finalRotations[i] = finalRotations[parentIndex] * combinedRotation;
++numShapesSet;
shapeIsSet[i] = true;
}
}
}
// sync shapes to joints
_boundingRadius = 0.0f;
float uniformScale = extractUniformScale(_scale);
for (int i = 0; i < _jointShapes.size(); i++) {
const FBXJoint& joint = geometry.joints[i];
glm::vec3 jointToShapeOffset = uniformScale * (finalRotations[i] * joint.shapePosition);
glm::vec3 localPosition = extractTranslation(transforms[i]) + jointToShapeOffset- rootOffset;
Shape* shape = _jointShapes[i];
shape->setPosition(localPosition);
shape->setRotation(finalRotations[i] * joint.shapeRotation);
float distance = glm::length(localPosition) + shape->getBoundingRadius();
if (distance > _boundingRadius) {
_boundingRadius = distance;
}
}
// compute bounding box
Extents totalExtents;
totalExtents.reset();
for (int i = 0; i < _jointShapes.size(); i++) {
Extents shapeExtents;
shapeExtents.reset();
Shape* shape = _jointShapes[i];
glm::vec3 localPosition = shape->getPosition();
int type = shape->getType();
if (type == Shape::CAPSULE_SHAPE) {
// add the two furthest surface points of the capsule
CapsuleShape* capsule = static_cast<CapsuleShape*>(shape);
glm::vec3 axis;
capsule->computeNormalizedAxis(axis);
float radius = capsule->getRadius();
float halfHeight = capsule->getHalfHeight();
axis = halfHeight * axis + glm::vec3(radius);
shapeExtents.addPoint(localPosition + axis);
shapeExtents.addPoint(localPosition - axis);
totalExtents.addExtents(shapeExtents);
} else if (type == Shape::SPHERE_SHAPE) {
float radius = shape->getBoundingRadius();
glm::vec3 axis = glm::vec3(radius);
shapeExtents.addPoint(localPosition + axis);
shapeExtents.addPoint(localPosition - axis);
totalExtents.addExtents(shapeExtents);
}
}
// compute bounding shape parameters
// NOTE: we assume that the longest side of totalExtents is the yAxis...
glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum;
// the radius is half the RMS of the X and Z sides:
// ... and assume the radius is half the RMS of the X and Z sides:
float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
_boundingShape.setRadius(capsuleRadius);
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum);
}
glm::quat inverseRotation = glm::inverse(_rotation);
glm::vec3 rootPosition = extractTranslation(transforms[rootIndex]);
_boundingShapeLocalOffset = inverseRotation * (0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition);
void Model::resetShapePositions() {
// DEBUG method.
// Moves shapes to the joint default locations for debug visibility into
// how the bounding shape is computed.
if (!_geometry || _rootIndex == -1) {
// geometry or joints have not yet been created
return;
}
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (geometry.joints.isEmpty() || _jointShapes.size() != geometry.joints.size()) {
return;
}
// The shapes are moved to their default positions in computeBoundingShape().
computeBoundingShape(geometry);
// Then we move them into world frame for rendering at the Model's location.
for (int i = 0; i < _jointShapes.size(); i++) {
Shape* shape = _jointShapes[i];
shape->setPosition(_translation + _rotation * shape->getPosition());
shape->setRotation(_rotation * shape->getRotation());
}
_boundingShape.setPosition(_translation + _rotation * _boundingShapeLocalOffset);
_boundingShape.setRotation(_rotation);
}
@ -728,17 +787,17 @@ void Model::updateShapePositions() {
// shape position and rotation need to be in world-frame
glm::vec3 jointToShapeOffset = uniformScale * (_jointStates[i].combinedRotation * joint.shapePosition);
glm::vec3 worldPosition = extractTranslation(_jointStates[i].transform) + jointToShapeOffset + _translation;
_jointShapes[i]->setPosition(worldPosition);
_jointShapes[i]->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation);
float distance2 = glm::distance2(worldPosition, _translation);
if (distance2 > _boundingRadius) {
_boundingRadius = distance2;
Shape* shape = _jointShapes[i];
shape->setPosition(worldPosition);
shape->setRotation(_jointStates[i].combinedRotation * joint.shapeRotation);
float distance = glm::distance(worldPosition, _translation) + shape->getBoundingRadius();
if (distance > _boundingRadius) {
_boundingRadius = distance;
}
if (joint.parentIndex == -1) {
rootPosition = worldPosition;
}
}
_boundingRadius = sqrtf(_boundingRadius);
_shapesAreDirty = false;
_boundingShape.setPosition(rootPosition + _rotation * _boundingShapeLocalOffset);
_boundingShape.setRotation(_rotation);

View file

@ -187,6 +187,7 @@ public:
void clearShapes();
void rebuildShapes();
void resetShapePositions();
void updateShapePositions();
void renderJointCollisionShapes(float alpha);
void renderBoundingCollisionShapes(float alpha);
@ -234,6 +235,7 @@ protected:
bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space
bool _snappedToCenter; /// are we currently snapped to center
int _rootIndex;
class JointState {
public:
@ -293,6 +295,8 @@ protected:
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true);
void computeBoundingShape(const FBXGeometry& geometry);
private:
void applyNextGeometry();

View file

@ -0,0 +1,239 @@
//
// NodeBounds.cpp
// interface/src/ui
//
// Created by Ryan Huffman on 05/14/14.
// Copyright 2014 High Fidelity, Inc.
//
// This class draws a border around the different Voxel, Model, and Particle nodes on the current domain,
// and a semi-transparent cube around the currently mouse-overed node.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Application.h"
#include "Util.h"
#include "NodeBounds.h"
NodeBounds::NodeBounds(QObject* parent) :
QObject(parent),
_showVoxelNodes(false),
_showModelNodes(false),
_showParticleNodes(false),
_overlayText() {
}
void NodeBounds::draw() {
if (!(_showVoxelNodes || _showModelNodes || _showParticleNodes)) {
_overlayText[0] = '\0';
return;
}
NodeToJurisdictionMap& voxelServerJurisdictions = Application::getInstance()->getVoxelServerJurisdictions();
NodeToJurisdictionMap& modelServerJurisdictions = Application::getInstance()->getModelServerJurisdictions();
NodeToJurisdictionMap& particleServerJurisdictions = Application::getInstance()->getParticleServerJurisdictions();
NodeToJurisdictionMap* serverJurisdictions;
// Compute ray to find selected nodes later on. We can't use the pre-computed ray in Application because it centers
// itself after the cursor disappears.
Application* application = Application::getInstance();
QGLWidget* glWidget = application->getGLWidget();
float mouseX = application->getMouseX() / (float)glWidget->width();
float mouseY = application->getMouseY() / (float)glWidget->height();
glm::vec3 mouseRayOrigin;
glm::vec3 mouseRayDirection;
application->getViewFrustum()->computePickRay(mouseX, mouseY, mouseRayOrigin, mouseRayDirection);
// Variables to keep track of the selected node and properties to draw the cube later if needed
Node* selectedNode = NULL;
float selectedDistance = FLT_MAX;
bool selectedIsInside = true;
glm::vec3 selectedCenter;
float selectedScale = 0;
NodeList* nodeList = NodeList::getInstance();
foreach (const SharedNodePointer& node, nodeList->getNodeHash()) {
NodeType_t nodeType = node->getType();
if (nodeType == NodeType::VoxelServer && _showVoxelNodes) {
serverJurisdictions = &voxelServerJurisdictions;
} else if (nodeType == NodeType::ModelServer && _showModelNodes) {
serverJurisdictions = &modelServerJurisdictions;
} else if (nodeType == NodeType::ParticleServer && _showParticleNodes) {
serverJurisdictions = &particleServerJurisdictions;
} else {
continue;
}
QUuid nodeUUID = node->getUUID();
if (serverJurisdictions->find(nodeUUID) != serverJurisdictions->end()) {
const JurisdictionMap& map = serverJurisdictions->value(nodeUUID);
unsigned char* rootCode = map.getRootOctalCode();
if (rootCode) {
VoxelPositionSize rootDetails;
voxelDetailsForCode(rootCode, rootDetails);
glm::vec3 location(rootDetails.x, rootDetails.y, rootDetails.z);
location *= (float)TREE_SCALE;
AABox serverBounds(location, rootDetails.s * TREE_SCALE);
glm::vec3 center = serverBounds.getVertex(BOTTOM_RIGHT_NEAR)
+ ((serverBounds.getVertex(TOP_LEFT_FAR) - serverBounds.getVertex(BOTTOM_RIGHT_NEAR)) / 2.0f);
const float VOXEL_NODE_SCALE = 1.00f;
const float MODEL_NODE_SCALE = 0.99f;
const float PARTICLE_NODE_SCALE = 0.98f;
float scaleFactor = rootDetails.s * TREE_SCALE;
// Scale by 0.92 - 1.00 depending on the scale of the node. This allows smaller nodes to scale in
// a bit and not overlap larger nodes.
scaleFactor *= 0.92 + (rootDetails.s * 0.08);
// Scale different node types slightly differently because it's common for them to overlap.
if (nodeType == NodeType::VoxelServer) {
scaleFactor *= VOXEL_NODE_SCALE;
} else if (nodeType == NodeType::ModelServer) {
scaleFactor *= MODEL_NODE_SCALE;
} else {
scaleFactor *= PARTICLE_NODE_SCALE;
}
float red, green, blue;
getColorForNodeType(nodeType, red, green, blue);
drawNodeBorder(center, scaleFactor, red, green, blue);
float distance;
BoxFace face;
bool inside = serverBounds.contains(mouseRayOrigin);
bool colliding = serverBounds.findRayIntersection(mouseRayOrigin, mouseRayDirection, distance, face);
// If the camera is inside a node it will be "selected" if you don't have your cursor over another node
// that you aren't inside.
if (colliding && (!selectedNode || (!inside && (distance < selectedDistance || selectedIsInside)))) {
selectedNode = node.data();
selectedDistance = distance;
selectedIsInside = inside;
selectedCenter = center;
selectedScale = scaleFactor;
}
}
}
}
if (selectedNode) {
glPushMatrix();
glTranslatef(selectedCenter.x, selectedCenter.y, selectedCenter.z);
glScalef(selectedScale, selectedScale, selectedScale);
NodeType_t selectedNodeType = selectedNode->getType();
float red, green, blue;
getColorForNodeType(selectedNode->getType(), red, green, blue);
glColor4f(red, green, blue, 0.2);
glutSolidCube(1.0);
glPopMatrix();
HifiSockAddr addr = selectedNode->getPublicSocket();
QString overlay = QString("%1:%2 %3ms")
.arg(addr.getAddress().toString())
.arg(addr.getPort())
.arg(selectedNode->getPingMs())
.left(MAX_OVERLAY_TEXT_LENGTH);
// Ideally we'd just use a QString, but I ran into weird blinking issues using
// constData() directly, as if the data was being overwritten.
strcpy(_overlayText, overlay.toLocal8Bit().constData());
} else {
_overlayText[0] = '\0';
}
}
void NodeBounds::drawNodeBorder(const glm::vec3& center, float scale, float red, float green, float blue) {
glPushMatrix();
glTranslatef(center.x, center.y, center.z);
glScalef(scale, scale, scale);
glLineWidth(2.5);
glColor3f(red, green, blue);
glBegin(GL_LINES);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f(-0.5, -0.5, -0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f( 0.5, 0.5, -0.5);
glVertex3f(-0.5, 0.5, -0.5);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f( 0.5, 0.5, 0.5);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f( 0.5, 0.5, 0.5);
glVertex3f( 0.5, -0.5, 0.5);
glVertex3f( 0.5, 0.5, 0.5);
glVertex3f( 0.5, 0.5, -0.5);
glVertex3f( 0.5, -0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glVertex3f( 0.5, -0.5, 0.5);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f( 0.5, 0.5, -0.5);
glVertex3f( 0.5, -0.5, -0.5);
glVertex3f(-0.5, 0.5, 0.5);
glVertex3f(-0.5, -0.5, 0.5);
glEnd();
glPopMatrix();
}
void NodeBounds::getColorForNodeType(NodeType_t nodeType, float& red, float& green, float& blue) {
red = nodeType == NodeType::VoxelServer ? 1.0 : 0.0;
green = nodeType == NodeType::ParticleServer ? 1.0 : 0.0;
blue = nodeType == NodeType::ModelServer ? 1.0 : 0.0;
}
void NodeBounds::drawOverlay() {
if (strlen(_overlayText) > 0) {
Application* application = Application::getInstance();
const float TEXT_COLOR[] = { 0.90f, 0.90f, 0.90f };
const float TEXT_SCALE = 0.1f;
const int TEXT_HEIGHT = 10;
const float ROTATION = 0.0f;
const int FONT = 2;
const int PADDING = 10;
const int MOUSE_OFFSET = 10;
const int BACKGROUND_OFFSET_Y = -20;
const int BACKGROUND_BEVEL = 3;
int mouseX = application->getMouseX(),
mouseY = application->getMouseY(),
textWidth = widthText(TEXT_SCALE, 0, _overlayText);
glColor4f(0.4, 0.4, 0.4, 0.6);
renderBevelCornersRect(mouseX + MOUSE_OFFSET, mouseY - TEXT_HEIGHT - PADDING,
textWidth + (2 * PADDING), TEXT_HEIGHT + (2 * PADDING), BACKGROUND_BEVEL);
drawText(mouseX + MOUSE_OFFSET + PADDING, mouseY, TEXT_SCALE, ROTATION, FONT, _overlayText, TEXT_COLOR);
}
}

View file

@ -0,0 +1,50 @@
//
// NodeBounds.h
// interface/src/ui
//
// Created by Ryan Huffman on 05/14/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_NodeBounds_h
#define hifi_NodeBounds_h
#include <QObject>
#include <NodeList.h>
const int MAX_OVERLAY_TEXT_LENGTH = 64;
class NodeBounds : public QObject {
Q_OBJECT
public:
NodeBounds(QObject* parent = NULL);
bool getShowVoxelNodes() { return _showVoxelNodes; }
bool getShowModelNodes() { return _showModelNodes; }
bool getShowParticleNodes() { return _showParticleNodes; }
void draw();
void drawOverlay();
public slots:
void setShowVoxelNodes(bool value) { _showVoxelNodes = value; }
void setShowModelNodes(bool value) { _showModelNodes = value; }
void setShowParticleNodes(bool value) { _showParticleNodes = value; }
protected:
void drawNodeBorder(const glm::vec3& center, float scale, float red, float green, float blue);
void getColorForNodeType(NodeType_t nodeType, float& red, float& green, float& blue);
private:
bool _showVoxelNodes;
bool _showModelNodes;
bool _showParticleNodes;
char _overlayText[MAX_OVERLAY_TEXT_LENGTH + 1];
};
#endif // hifi_NodeBounds_h

View file

@ -714,6 +714,7 @@ void ModelItem::mapJoints(const QStringList& modelJointNames) {
if (!_jointMappingCompleted) {
QStringList animationJointNames = myAnimation->getJointNames();
if (modelJointNames.size() > 0 && animationJointNames.size() > 0) {
_jointMapping.resize(modelJointNames.size());
for (int i = 0; i < modelJointNames.size(); i++) {
@ -729,13 +730,17 @@ QVector<glm::quat> ModelItem::getAnimationFrame() {
if (hasAnimation() && _jointMappingCompleted) {
Animation* myAnimation = getAnimation(_animationURL);
QVector<FBXAnimationFrame> frames = myAnimation->getFrames();
int animationFrameIndex = (int)std::floor(_animationFrameIndex) % frames.size();
QVector<glm::quat> rotations = frames[animationFrameIndex].rotations;
frameData.resize(_jointMapping.size());
for (int j = 0; j < _jointMapping.size(); j++) {
int rotationIndex = _jointMapping[j];
if (rotationIndex != -1 && rotationIndex < rotations.size()) {
frameData[j] = rotations[rotationIndex];
int frameCount = frames.size();
if (frameCount > 0) {
int animationFrameIndex = (int)glm::floor(_animationFrameIndex) % frameCount;
QVector<glm::quat> rotations = frames[animationFrameIndex].rotations;
frameData.resize(_jointMapping.size());
for (int j = 0; j < _jointMapping.size(); j++) {
int rotationIndex = _jointMapping[j];
if (rotationIndex != -1 && rotationIndex < rotations.size()) {
frameData[j] = rotations[rotationIndex];
}
}
}
}

View file

@ -108,6 +108,7 @@ bool FindAndUpdateModelOperator::PostRecursion(OctreeElement* element) {
return !_found; // if we haven't yet found it, keep looking
}
// TODO: improve this to not use multiple recursions
void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& senderNode) {
// First, look for the existing model in the tree..
FindAndUpdateModelOperator theOperator(model);
@ -118,8 +119,13 @@ void ModelTree::storeModel(const ModelItem& model, const SharedNodePointer& send
AABox modelBox = model.getAABox();
ModelTreeElement* element = (ModelTreeElement*)getOrCreateChildElementContaining(model.getAABox());
element->storeModel(model);
// In the case where we stored it, we also need to mark the entire "path" down to the model as
// having changed. Otherwise viewers won't see this change. So we call this recursion now that
// we know it will be found, this find/update will correctly mark the tree as changed.
recurseTreeWithOperator(&theOperator);
}
// what else do we need to do here to get reaveraging to work
_isDirty = true;
}

View file

@ -1389,6 +1389,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
keepDiggingDeeper = (inViewNotLeafCount > 0);
if (continueThisLevel && keepDiggingDeeper) {
// at this point, we need to iterate the children who are in view, even if not colored
// and we need to determine if there's a deeper tree below them that we care about.
//
@ -1433,7 +1434,12 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
//
// This only applies in the view frustum case, in other cases, like file save and copy/past where
// no viewFrustum was requested, we still want to recurse the child tree.
if (!params.viewFrustum || !oneAtBit(childrenColoredBits, originalIndex)) {
//
// NOTE: some octree styles (like models and particles) will store content in parent elements, and child
// elements. In this case, if we stop recursion when we include any data (the colorbits should really be
// called databits), then we wouldn't send the children. So those types of Octree's should tell us to keep
// recursing, by returning TRUE in recurseChildrenWithData().
if (recurseChildrenWithData() || !params.viewFrustum || !oneAtBit(childrenColoredBits, originalIndex)) {
childTreeBytesOut = encodeTreeBitstreamRecursion(childElement, packetData, bag, params,
thisLevel, nodeLocationThisView);
}
@ -1519,16 +1525,6 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element,
} // end keepDiggingDeeper
// At this point all our BitMasks are complete... so let's output them to see how they compare...
/**
printf("This Level's BitMasks: childInTree:");
outputBits(childrenExistInTreeBits, false, true);
printf(" childInPacket:");
outputBits(childrenExistInPacketBits, false, true);
printf(" childrenColored:");
outputBits(childrenColoredBits, false, true);
qDebug("");
**/
// if we were unable to fit this level in our packet, then rewind and add it to the element bag for
// sending later...
if (continueThisLevel) {

View file

@ -209,6 +209,8 @@ public:
virtual bool handlesEditPacketType(PacketType packetType) const { return false; }
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; }
virtual bool recurseChildrenWithData() const { return true; }
virtual void update() { }; // nothing to do by default

View file

@ -55,6 +55,7 @@ public:
virtual bool handlesEditPacketType(PacketType packetType) const;
virtual int processEditPacketData(PacketType packetType, const unsigned char* packetData, int packetLength,
const unsigned char* editData, int maxLength, const SharedNodePointer& node);
virtual bool recurseChildrenWithData() const { return false; }
private:
// helper functions for nudgeSubTree