Merge branch 'master' of github.com:highfidelity/hifi into improve-grab-action-reliability

This commit is contained in:
Seth Alves 2016-01-05 11:13:38 -08:00
commit cd82383d33
61 changed files with 946 additions and 382 deletions

View file

@ -11,9 +11,14 @@
#include "AssignmentParentFinder.h"
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID) const {
SpatiallyNestableWeakPointer AssignmentParentFinder::find(QUuid parentID, bool& success) const {
SpatiallyNestableWeakPointer parent;
// search entities
parent = _tree->findEntityByEntityItemID(parentID);
if (parent.expired()) {
success = false;
} else {
success = true;
}
return parent;
}

View file

@ -25,7 +25,7 @@ class AssignmentParentFinder : public SpatialParentFinder {
public:
AssignmentParentFinder(EntityTreePointer tree) : _tree(tree) { }
virtual ~AssignmentParentFinder() { }
virtual SpatiallyNestableWeakPointer find(QUuid parentID) const;
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const;
protected:
EntityTreePointer _tree;

View file

@ -1,6 +1,6 @@
//
// actionInspector.js
// examples
// examples/debugging/
//
// Created by Seth Alves on 2015-9-30.
// Copyright 2015 High Fidelity, Inc.
@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/utils.js");
Script.include("../libraries/utils.js");
var INSPECT_RADIUS = 10;

View file

@ -1,6 +1,6 @@
//
// grabInspector.js
// examples
// examples/debugging/
//
// Created by Seth Alves on 2015-9-30.
// Copyright 2015 High Fidelity, Inc.
@ -9,7 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("libraries/utils.js");
Script.include("../libraries/utils.js");
var INSPECT_RADIUS = 10;
var overlays = {};

View file

@ -0,0 +1,58 @@
//
// grabInspector.js
// examples/debugging/
//
// Created by Seth Alves on 2015-12-19.
// Copyright 2015 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
//
// This script draws an overlay cube around nearby entities to show their queryAABoxes.
Script.include("../libraries/utils.js");
var INSPECT_RADIUS = 10;
var overlays = {};
function updateOverlay(entityID, queryAACube) {
var cubeCenter = {x: queryAACube.x + queryAACube.scale / 2.0,
y: queryAACube.y + queryAACube.scale / 2.0,
z: queryAACube.z + queryAACube.scale / 2.0};
if (entityID in overlays) {
var overlay = overlays[entityID];
Overlays.editOverlay(overlay, {
position: cubeCenter,
size: queryAACube.scale
});
} else {
overlays[entityID] = Overlays.addOverlay("cube", {
position: cubeCenter,
size: queryAACube.scale,
color: { red: 0, green: 0, blue: 255},
alpha: 1,
// borderSize: ...,
solid: false
});
}
}
Script.setInterval(function() {
var nearbyEntities = Entities.findEntities(MyAvatar.position, INSPECT_RADIUS);
for (var entityIndex = 0; entityIndex < nearbyEntities.length; entityIndex++) {
var entityID = nearbyEntities[entityIndex];
var queryAACube = Entities.getEntityProperties(entityID, ["queryAACube"]).queryAACube;
updateOverlay(entityID, queryAACube);
}
}, 100);
function cleanup() {
for (var entityID in overlays) {
Overlays.deleteOverlay(overlays[entityID]);
}
}
Script.scriptEnding.connect(cleanup);

View file

@ -11,7 +11,7 @@
{ "from": "Standard.DR", "to": "Actions.UiNavLateral" },
{ "from": "Standard.LB", "to": "Actions.UiNavGroup","filters": "invert" },
{ "from": "Standard.RB", "to": "Actions.UiNavGroup" },
{ "from": [ "Standard.A", "Standard.X", "Standard.RT", "Standard.LT" ], "to": "Actions.UiNavSelect" },
{ "from": [ "Standard.A", "Standard.X" ], "to": "Actions.UiNavSelect" },
{ "from": [ "Standard.B", "Standard.Y", "Standard.RightPrimaryThumb", "Standard.LeftPrimaryThumb" ], "to": "Actions.UiNavBack" },
{
"from": [ "Standard.RT", "Standard.LT" ],

View file

@ -169,8 +169,10 @@ DialogContainer {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
enabled = false
event.accepted = true
if (enabled) {
enabled = false
event.accepted = true
}
break
case Qt.Key_Enter:
case Qt.Key_Return:

View file

@ -96,6 +96,9 @@ DialogContainer {
}
Keys.onPressed: {
if (!enabled) {
return
}
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:

View file

@ -343,8 +343,10 @@ DialogContainer {
switch (event.key) {
case Qt.Key_Escape:
case Qt.Key_Back:
enabled = false
event.accepted = true
if (enabled) {
enabled = false
event.accepted = true
}
break
case Qt.Key_Enter:
case Qt.Key_Return:

View file

@ -300,6 +300,10 @@ VrDialog {
Keys.onPressed: {
if (!enabled) {
return
}
if (event.modifiers === Qt.ControlModifier)
switch (event.key) {
case Qt.Key_A:

View file

@ -10,7 +10,6 @@ import "styles"
VrDialog {
id: root
objectName: "topLevelWindow"
HifiConstants { id: hifi }
title: "QmlWindow"
resizable: true
@ -43,10 +42,6 @@ VrDialog {
focus: true
property var dialog: root
onLoaded: {
forceActiveFocus()
}
Keys.onPressed: {
console.log("QmlWindow pageLoader keypress")
}

View file

@ -1,11 +1,16 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick 2.5
import QtQuick.Controls 1.4
// This is our primary 'window' object to which all dialogs and controls will
// be childed.
Root {
id: root
objectName: "desktopRoot"
anchors.fill: parent
property var rootMenu: Menu {
objectName: "rootMenu"
}
onParentChanged: {
forceActiveFocus();

View file

@ -1,9 +0,0 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
Item {
Menu {
id: root
objectName: "rootMenu"
}
}

View file

@ -5,6 +5,7 @@ import "../styles"
Item {
id: root
objectName: "topLevelWindow"
HifiConstants { id: hifi }
implicitHeight: contentImplicitHeight + titleBorder.height + hifi.styles.borderWidth
implicitWidth: contentImplicitWidth + hifi.styles.borderWidth * 2

View file

@ -1177,7 +1177,6 @@ void Application::initializeUi() {
offscreenUi->setProxyWindow(_window->windowHandle());
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
offscreenUi->load("Root.qml");
offscreenUi->load("RootMenu.qml");
// FIXME either expose so that dialogs can set this themselves or
// do better detection in the offscreen UI of what has focus
offscreenUi->setNavigationFocused(false);

View file

@ -16,22 +16,31 @@
#include "InterfaceParentFinder.h"
SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID) const {
SpatiallyNestableWeakPointer InterfaceParentFinder::find(QUuid parentID, bool& success) const {
SpatiallyNestableWeakPointer parent;
if (parentID.isNull()) {
success = true;
return parent;
}
// search entities
EntityTreeRenderer* treeRenderer = qApp->getEntities();
EntityTreePointer tree = treeRenderer->getTree();
parent = tree->findEntityByEntityItemID(parentID);
EntityTreePointer tree = treeRenderer ? treeRenderer->getTree() : nullptr;
parent = tree ? tree->findEntityByEntityItemID(parentID) : nullptr;
if (!parent.expired()) {
success = true;
return parent;
}
// search avatars
QSharedPointer<AvatarManager> avatarManager = DependencyManager::get<AvatarManager>();
return avatarManager->getAvatarBySessionID(parentID);
parent = avatarManager->getAvatarBySessionID(parentID);
if (!parent.expired()) {
success = true;
return parent;
}
success = false;
return parent;
}

View file

@ -21,7 +21,7 @@ class InterfaceParentFinder : public SpatialParentFinder {
public:
InterfaceParentFinder() { }
virtual ~InterfaceParentFinder() { }
virtual SpatiallyNestableWeakPointer find(QUuid parentID) const;
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const;
};
#endif // hifi_InterfaceParentFinder_h

View file

@ -1021,6 +1021,9 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) {
if (_moving && _motionState) {
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
}
if (_moving || _hasNewJointRotations || _hasNewJointTranslations) {
locationChanged();
}
endUpdate();
return bytesRead;

View file

@ -159,7 +159,9 @@ public:
AvatarMotionState* getMotionState() { return _motionState; }
using SpatiallyNestable::setPosition;
virtual void setPosition(const glm::vec3& position) override;
using SpatiallyNestable::setOrientation;
virtual void setOrientation(const glm::quat& orientation) override;
public slots:

View file

@ -90,10 +90,18 @@ const QUrl& AvatarData::defaultFullAvatarModelUrl() {
// There are a number of possible strategies for this set of tools through endRender, below.
void AvatarData::nextAttitude(glm::vec3 position, glm::quat orientation) {
avatarLock.lock();
Transform trans = getTransform();
bool success;
Transform trans = getTransform(success);
if (!success) {
qDebug() << "Warning -- AvatarData::nextAttitude failed";
return;
}
trans.setTranslation(position);
trans.setRotation(orientation);
SpatiallyNestable::setTransform(trans);
SpatiallyNestable::setTransform(trans, success);
if (!success) {
qDebug() << "Warning -- AvatarData::nextAttitude failed";
}
avatarLock.unlock();
updateAttitude();
}
@ -208,8 +216,9 @@ QByteArray AvatarData::toByteArray(bool cullSmallChanges, bool sendAll) {
setAtBit(bitItems, IS_EYE_TRACKER_CONNECTED);
}
// referential state
SpatiallyNestablePointer parent = getParentPointer();
if (parent) {
bool success;
SpatiallyNestablePointer parent = getParentPointer(success);
if (parent && success) {
setAtBit(bitItems, HAS_REFERENTIAL);
}
*destinationBuffer++ = bitItems;
@ -1446,7 +1455,11 @@ QJsonObject AvatarData::toJson() const {
}
auto recordingBasis = getRecordingBasis();
Transform avatarTransform = getTransform();
bool success;
Transform avatarTransform = getTransform(success);
if (!success) {
qDebug() << "Warning -- AvatarData::toJson couldn't get avatar transform";
}
avatarTransform.setScale(getTargetScale());
if (recordingBasis) {
root[JSON_AVATAR_BASIS] = Transform::toJson(*recordingBasis);
@ -1635,3 +1648,13 @@ void AvatarData::setPosition(const glm::vec3& position) {
void AvatarData::setOrientation(const glm::quat& orientation) {
SpatiallyNestable::setOrientation(orientation);
}
glm::quat AvatarData::getAbsoluteJointRotationInObjectFrame(int index) const {
assert(false);
return glm::quat();
}
glm::vec3 AvatarData::getAbsoluteJointTranslationInObjectFrame(int index) const {
assert(false);
return glm::vec3();
}

View file

@ -201,7 +201,9 @@ public:
float getBodyRoll() const;
void setBodyRoll(float bodyRoll);
using SpatiallyNestable::setPosition;
virtual void setPosition(const glm::vec3& position) override;
using SpatiallyNestable::setOrientation;
virtual void setOrientation(const glm::quat& orientation) override;
void nextAttitude(glm::vec3 position, glm::quat orientation); // Can be safely called at any time.
@ -354,6 +356,9 @@ public slots:
void setJointMappingsFromNetworkReply();
void setSessionUUID(const QUuid& sessionUUID) { setID(sessionUUID); }
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
protected:
glm::vec3 _handPosition;

View file

@ -767,7 +767,12 @@ void EntityTreeRenderer::playEntityCollisionSound(const QUuid& myNodeID, EntityT
// Shift the pitch down by ln(1 + (size / COLLISION_SIZE_FOR_STANDARD_PITCH)) / ln(2)
const float COLLISION_SIZE_FOR_STANDARD_PITCH = 0.2f;
const float stretchFactor = log(1.0f + (entity->getMinimumAACube().getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
bool success;
auto minAACube = entity->getMinimumAACube(success);
if (!success) {
return;
}
const float stretchFactor = log(1.0f + (minAACube.getLargestDimension() / COLLISION_SIZE_FOR_STANDARD_PITCH)) / log(2);
AudioInjector::playSound(collisionSoundURL, volume, stretchFactor, position);
}

View file

@ -56,14 +56,20 @@ void RenderableBoxEntityItem::render(RenderArgs* args) {
gpu::Batch& batch = *args->_batch;
glm::vec4 cubeColor(toGlm(getXColor()), getLocalRenderAlpha());
bool success;
auto transToCenter = getTransformToCenter(success);
if (!success) {
return;
}
if (_procedural->ready()) {
batch.setModelTransform(getTransformToCenter()); // we want to include the scale as well
batch.setModelTransform(transToCenter); // we want to include the scale as well
_procedural->prepare(batch, getPosition(), getDimensions());
auto color = _procedural->getColor(cubeColor);
batch._glColor4f(color.r, color.g, color.b, color.a);
DependencyManager::get<GeometryCache>()->renderCube(batch);
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCubeInstance(batch, getTransformToCenter(), cubeColor);
DependencyManager::get<DeferredLightingEffect>()->renderSolidCubeInstance(batch, transToCenter, cubeColor);
}
static const auto triCount = DependencyManager::get<GeometryCache>()->getCubeTriangleCount();
args->_details._trianglesRendered += (int)triCount;

View file

@ -28,7 +28,12 @@ namespace render {
template <> const Item::Bound payloadGetBound(const RenderableEntityItemProxy::Pointer& payload) {
if (payload && payload->entity) {
return payload->entity->getAABox();
bool success;
auto result = payload->entity->getAABox(success);
if (!success) {
return render::Item::Bound();
}
return result;
}
return render::Item::Bound();
}

View file

@ -196,7 +196,12 @@ namespace render {
template <> const Item::Bound payloadGetBound(const RenderableModelEntityItemMeta::Pointer& payload) {
if (payload && payload->entity) {
return payload->entity->getAABox();
bool success;
auto result = payload->entity->getAABox(success);
if (!success) {
return render::Item::Bound();
}
return result;
}
return render::Item::Bound();
}
@ -338,9 +343,12 @@ void RenderableModelEntityItem::render(RenderArgs* args) {
} else {
static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
gpu::Batch& batch = *args->_batch;
auto shapeTransform = getTransformToCenter();
batch.setModelTransform(Transform()); // we want to include the scale as well
DependencyManager::get<DeferredLightingEffect>()->renderWireCubeInstance(batch, shapeTransform, greenColor);
bool success;
auto shapeTransform = getTransformToCenter(success);
if (success) {
batch.setModelTransform(Transform()); // we want to include the scale as well
DependencyManager::get<DeferredLightingEffect>()->renderWireCubeInstance(batch, shapeTransform, greenColor);
}
}
}

View file

@ -235,10 +235,15 @@ void RenderableParticleEffectEntityItem::updateRenderItem() {
for (auto& particle : _particles) {
particlePrimitives->emplace_back(particle.position, glm::vec2(particle.lifetime, particle.seed));
}
auto bounds = getAABox();
auto position = getPosition();
auto rotation = getRotation();
bool successb, successp, successr;
auto bounds = getAABox(successb);
auto position = getPosition(successp);
auto rotation = getOrientation(successr);
bool success = successb && successp && successr;
if (!success) {
return;
}
Transform transform;
if (!getEmitterShouldTrail()) {
transform.setTranslation(position);

View file

@ -142,9 +142,11 @@ glm::vec3 RenderablePolyVoxEntityItem::getSurfacePositionAdjustment() const {
glm::mat4 RenderablePolyVoxEntityItem::voxelToLocalMatrix() const {
glm::vec3 scale = getDimensions() / _voxelVolumeSize; // meters / voxel-units
glm::vec3 center = getCenterPosition();
glm::vec3 position = getPosition();
bool success; // TODO -- Does this actually have to happen in world space?
glm::vec3 center = getCenterPosition(success);
glm::vec3 position = getPosition(success);
glm::vec3 positionToCenter = center - position;
positionToCenter -= getDimensions() * Vectors::HALF - getSurfacePositionAdjustment();
glm::mat4 centerToCorner = glm::translate(glm::mat4(), positionToCenter);
glm::mat4 scaled = glm::scale(centerToCorner, scale);
@ -581,7 +583,12 @@ namespace render {
template <> const Item::Bound payloadGetBound(const PolyVoxPayload::Pointer& payload) {
if (payload && payload->_owner) {
auto polyVoxEntity = std::dynamic_pointer_cast<RenderablePolyVoxEntityItem>(payload->_owner);
return polyVoxEntity->getAABox();
bool success;
auto result = polyVoxEntity->getAABox(success);
if (!success) {
return render::Item::Bound();
}
return result;
}
return render::Item::Bound();
}

View file

@ -59,7 +59,11 @@ void RenderableSphereEntityItem::render(RenderArgs* args) {
gpu::Batch& batch = *args->_batch;
glm::vec4 sphereColor(toGlm(getXColor()), getLocalRenderAlpha());
Transform modelTransform = getTransformToCenter();
bool success;
Transform modelTransform = getTransformToCenter(success);
if (!success) {
return;
}
modelTransform.postScale(SPHERE_ENTITY_SCALE);
if (_procedural->ready()) {
batch.setModelTransform(modelTransform); // use a transform with scale, rotation, registration point and translation

View file

@ -44,8 +44,12 @@ void RenderableTextEntityItem::render(RenderArgs* args) {
// Batch render calls
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
Transform transformToTopLeft = getTransformToCenter();
bool success;
Transform transformToTopLeft = getTransformToCenter(success);
if (!success) {
return;
}
if (getFaceCamera()) {
//rotate about vertical to face the camera
glm::vec3 dPosition = args->_viewFrustum->getPosition() - getPosition();

View file

@ -185,7 +185,11 @@ void RenderableWebEntityItem::render(RenderArgs* args) {
Q_ASSERT(args->_batch);
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(getTransformToCenter());
bool success;
batch.setModelTransform(getTransformToCenter(success));
if (!success) {
return;
}
bool textured = false, culled = false, emissive = false;
if (_texture) {
batch._glActiveBindTexture(GL_TEXTURE0, GL_TEXTURE_2D, _texture);

View file

@ -135,7 +135,11 @@ void RenderableZoneEntityItem::render(RenderArgs* args) {
gpu::Batch& batch = *args->_batch;
batch.setModelTransform(Transform());
auto shapeTransform = getTransformToCenter();
bool success;
auto shapeTransform = getTransformToCenter(success);
if (!success) {
break;
}
auto deferredLightingEffect = DependencyManager::get<DeferredLightingEffect>();
if (getShapeType() == SHAPE_TYPE_SPHERE) {
shapeTransform.postScale(SPHERE_ENTITY_SCALE);
@ -190,7 +194,12 @@ namespace render {
template <> const Item::Bound payloadGetBound(const RenderableZoneEntityItemMeta::Pointer& payload) {
if (payload && payload->entity) {
return payload->entity->getAABox();
bool success;
auto result = payload->entity->getAABox(success);
if (!success) {
return render::Item::Bound();
}
return result;
}
return render::Item::Bound();
}

View file

@ -25,7 +25,13 @@ AddEntityOperator::AddEntityOperator(EntityTreePointer tree, EntityItemPointer n
// caller must have verified existence of newEntity
assert(_newEntity);
_newEntityBox = _newEntity->getMaximumAACube().clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE);
bool success;
auto queryCube = _newEntity->getQueryAACube(success);
if (!success) {
_newEntity->markAncestorMissing(true);
}
_newEntityBox = queryCube.clamp((float)(-HALF_TREE_SCALE), (float)HALF_TREE_SCALE);
}
bool AddEntityOperator::preRecursion(OctreeElementPointer element) {

View file

@ -1,83 +0,0 @@
//
// BoundingBoxRelatedProperties.cpp
// libraries/entities/src
//
// Created by Seth Alves on 2015-9-24
// Copyright 2013 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
//
#include "EntityItemProperties.h"
#include "BoundingBoxRelatedProperties.h"
#include "EntityTree.h"
BoundingBoxRelatedProperties::BoundingBoxRelatedProperties(EntityItemPointer entity) :
position(entity->getPosition()),
rotation(entity->getRotation()),
registrationPoint(entity->getRegistrationPoint()),
dimensions(entity->getDimensions()),
parentID(entity->getParentID()) {
}
BoundingBoxRelatedProperties::BoundingBoxRelatedProperties(EntityItemPointer entity,
const EntityItemProperties& propertiesWithUpdates) :
BoundingBoxRelatedProperties(entity) {
if (propertiesWithUpdates.parentIDChanged()) {
parentID = propertiesWithUpdates.getParentID();
}
bool parentFound = false;
if (parentID != UNKNOWN_ENTITY_ID) {
EntityTreePointer tree = entity->getTree();
EntityItemPointer parentZone = tree->findEntityByID(parentID);
if (parentZone) {
parentFound = true;
glm::vec3 localPosition = propertiesWithUpdates.containsPositionChange() ?
propertiesWithUpdates.getPosition() :
entity->getLocalPosition();
glm::quat localRotation = propertiesWithUpdates.rotationChanged() ?
propertiesWithUpdates.getRotation() :
entity->getLocalOrientation();
const Transform parentTransform = parentZone->getTransformToCenter();
Transform parentDescaled(parentTransform.getRotation(), glm::vec3(1.0f), parentTransform.getTranslation());
Transform localTransform(localRotation, glm::vec3(1.0f), localPosition);
Transform result;
Transform::mult(result, parentDescaled, localTransform);
position = result.getTranslation();
rotation = result.getRotation();
}
}
if (!parentFound) {
if (propertiesWithUpdates.containsPositionChange()) {
position = propertiesWithUpdates.getPosition();
}
if (propertiesWithUpdates.rotationChanged()) {
rotation = propertiesWithUpdates.getRotation();
}
}
if (propertiesWithUpdates.registrationPointChanged()) {
registrationPoint = propertiesWithUpdates.getRegistrationPoint();
}
if (propertiesWithUpdates.dimensionsChanged()) {
dimensions = propertiesWithUpdates.getDimensions();
}
}
AACube BoundingBoxRelatedProperties::getMaximumAACube() const {
// see EntityItem::getMaximumAACube for comments which explain the following.
glm::vec3 scaledRegistrationPoint = (dimensions * registrationPoint);
glm::vec3 registrationRemainder = (dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - registrationPoint));
glm::vec3 furthestExtentFromRegistration = glm::max(scaledRegistrationPoint, registrationRemainder);
float radius = glm::length(furthestExtentFromRegistration);
glm::vec3 minimumCorner = position - glm::vec3(radius, radius, radius);
return AACube(minimumCorner, radius * 2.0f);
}

View file

@ -1,30 +0,0 @@
//
// BoundingBoxRelatedProperties.h
// libraries/entities/src
//
// Created by Seth Alves on 2015-9-24
// Copyright 2013 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
//
#include "EntityItem.h"
#ifndef hifi_BoundingBoxRelatedProperties_h
#define hifi_BoundingBoxRelatedProperties_h
class BoundingBoxRelatedProperties {
public:
BoundingBoxRelatedProperties(EntityItemPointer entity);
BoundingBoxRelatedProperties(EntityItemPointer entity, const EntityItemProperties& propertiesWithUpdates);
AACube getMaximumAACube() const;
glm::vec3 position;
glm::quat rotation;
glm::vec3 registrationPoint;
glm::vec3 dimensions;
EntityItemID parentID;
};
#endif // hifi_BoundingBoxRelatedProperties_h

View file

@ -133,6 +133,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
requestedProperties += PROP_ACTION_DATA;
requestedProperties += PROP_PARENT_ID;
requestedProperties += PROP_PARENT_JOINT_INDEX;
requestedProperties += PROP_QUERY_AA_CUBE;
return requestedProperties;
}
@ -269,6 +270,7 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet
APPEND_ENTITY_PROPERTY(PROP_ACTION_DATA, getActionData());
APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, getParentID());
APPEND_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, getParentJointIndex());
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, getQueryAACube());
appendSubclassData(packetData, params, entityTreeElementExtraEncodeData,
requestedProperties,
@ -693,6 +695,8 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID);
READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex);
READ_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, AACube, setQueryAACube);
bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
propertyFlags, overwriteLocalData, somethingChanged);
@ -1047,6 +1051,7 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper
COPY_ENTITY_PROPERTY_TO_PROPERTIES(actionData, getActionData);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentID, getParentID);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(parentJointIndex, getParentJointIndex);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(queryAACube, getQueryAACube);
properties._defaultSettings = false;
@ -1111,6 +1116,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(actionData, setActionData);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentID, setParentID);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(parentJointIndex, setParentJointIndex);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(queryAACube, setQueryAACube);
if (somethingChanged) {
uint64_t now = usecTimestampNow();
@ -1152,8 +1158,8 @@ void EntityItem::recordCreationTime() {
_lastSimulated = now;
}
const Transform EntityItem::getTransformToCenter() const {
Transform result = getTransform();
const Transform EntityItem::getTransformToCenter(bool& success) const {
Transform result = getTransform(success);
if (getRegistrationPoint() != ENTITY_ITEM_HALF_VEC3) { // If it is not already centered, translate to center
result.postTranslate(ENTITY_ITEM_HALF_VEC3 - getRegistrationPoint()); // Position to center
}
@ -1171,28 +1177,31 @@ void EntityItem::setDimensions(const glm::vec3& value) {
/// The maximum bounding cube for the entity, independent of it's rotation.
/// This accounts for the registration point (upon which rotation occurs around).
///
const AACube& EntityItem::getMaximumAACube() const {
AACube EntityItem::getMaximumAACube(bool& success) const {
if (_recalcMaxAACube) {
// * we know that the position is the center of rotation
glm::vec3 centerOfRotation = getPosition(); // also where _registration point is
glm::vec3 centerOfRotation = getPosition(success); // also where _registration point is
if (success) {
// * we know that the registration point is the center of rotation
// * we can calculate the length of the furthest extent from the registration point
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
glm::vec3 registrationPoint = (getDimensions() * getRegistrationPoint());
glm::vec3 registrationRemainder = (getDimensions() * (glm::vec3(1.0f, 1.0f, 1.0f) - getRegistrationPoint()));
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
// * we know that the registration point is the center of rotation
// * we can calculate the length of the furthest extent from the registration point
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
glm::vec3 registrationPoint = (getDimensions() * getRegistrationPoint());
glm::vec3 registrationRemainder = (getDimensions() * (glm::vec3(1.0f, 1.0f, 1.0f) - getRegistrationPoint()));
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
// * we know that if you rotate in any direction you would create a sphere
// that has a radius of the length of furthest extent from registration point
float radius = glm::length(furthestExtentFromRegistration);
// * we know that if you rotate in any direction you would create a sphere
// that has a radius of the length of furthest extent from registration point
float radius = glm::length(furthestExtentFromRegistration);
// * we know that the minimum bounding cube of this maximum possible sphere is
// (center - radius) to (center + radius)
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
// * we know that the minimum bounding cube of this maximum possible sphere is
// (center - radius) to (center + radius)
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
_maxAACube = AACube(minimumCorner, radius * 2.0f);
_recalcMaxAACube = false;
_maxAACube = AACube(minimumCorner, radius * 2.0f);
_recalcMaxAACube = false;
}
} else {
success = true;
}
return _maxAACube;
}
@ -1200,7 +1209,7 @@ const AACube& EntityItem::getMaximumAACube() const {
/// The minimum bounding cube for the entity accounting for it's rotation.
/// This accounts for the registration point (upon which rotation occurs around).
///
const AACube& EntityItem::getMinimumAACube() const {
AACube EntityItem::getMinimumAACube(bool& success) const {
if (_recalcMinAACube) {
// _position represents the position of the registration point.
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
@ -1208,25 +1217,30 @@ const AACube& EntityItem::getMinimumAACube() const {
glm::vec3 unrotatedMinRelativeToEntity = - (getDimensions() * getRegistrationPoint());
glm::vec3 unrotatedMaxRelativeToEntity = getDimensions() * registrationRemainder;
Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
Extents rotatedExtentsRelativeToRegistrationPoint =
unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
// shift the extents to be relative to the position/registration point
rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition());
rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition(success));
// the cube that best encompasses extents is...
AABox box(rotatedExtentsRelativeToRegistrationPoint);
glm::vec3 centerOfBox = box.calcCenter();
float longestSide = box.getLargestDimension();
float halfLongestSide = longestSide / 2.0f;
glm::vec3 cornerOfCube = centerOfBox - glm::vec3(halfLongestSide, halfLongestSide, halfLongestSide);
if (success) {
// the cube that best encompasses extents is...
AABox box(rotatedExtentsRelativeToRegistrationPoint);
glm::vec3 centerOfBox = box.calcCenter();
float longestSide = box.getLargestDimension();
float halfLongestSide = longestSide / 2.0f;
glm::vec3 cornerOfCube = centerOfBox - glm::vec3(halfLongestSide, halfLongestSide, halfLongestSide);
_minAACube = AACube(cornerOfCube, longestSide);
_recalcMinAACube = false;
_minAACube = AACube(cornerOfCube, longestSide);
_recalcMinAACube = false;
}
} else {
success = true;
}
return _minAACube;
}
const AABox& EntityItem::getAABox() const {
AABox EntityItem::getAABox(bool& success) const {
if (_recalcAABox) {
// _position represents the position of the registration point.
glm::vec3 registrationRemainder = glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint;
@ -1234,17 +1248,37 @@ const AABox& EntityItem::getAABox() const {
glm::vec3 unrotatedMinRelativeToEntity = - (getDimensions() * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = getDimensions() * registrationRemainder;
Extents unrotatedExtentsRelativeToRegistrationPoint = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
Extents rotatedExtentsRelativeToRegistrationPoint = unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
Extents rotatedExtentsRelativeToRegistrationPoint =
unrotatedExtentsRelativeToRegistrationPoint.getRotated(getRotation());
// shift the extents to be relative to the position/registration point
rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition());
rotatedExtentsRelativeToRegistrationPoint.shiftBy(getPosition(success));
_cachedAABox = AABox(rotatedExtentsRelativeToRegistrationPoint);
_recalcAABox = false;
if (success) {
_cachedAABox = AABox(rotatedExtentsRelativeToRegistrationPoint);
_recalcAABox = false;
}
} else {
success = true;
}
return _cachedAABox;
}
AACube EntityItem::getQueryAACube(bool& success) const {
AACube result = SpatiallyNestable::getQueryAACube(success);
if (success) {
return result;
}
// this is for when we've loaded an older json file that didn't have queryAACube properties.
result = getMaximumAACube(success);
if (success) {
_queryAACube = result;
_queryAACubeSet = true;
}
return result;
}
// NOTE: This should only be used in cases of old bitstreams which only contain radius data
// 0,0,0 --> maxDimension,maxDimension,maxDimension
// ... has a corner to corner distance of glm::length(maxDimension,maxDimension,maxDimension)
@ -1274,7 +1308,9 @@ float EntityItem::getRadius() const {
bool EntityItem::contains(const glm::vec3& point) const {
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
return getAABox().contains(point);
bool success;
bool result = getAABox(success).contains(point);
return result && success;
} else {
ShapeInfo info;
info.setParams(getShapeType(), glm::vec3(0.5f));
@ -1817,6 +1853,14 @@ QList<EntityActionPointer> EntityItem::getActionsOfType(EntityActionType typeToG
return result;
}
glm::quat EntityItem::getAbsoluteJointRotationInObjectFrame(int index) const {
return glm::quat();
}
glm::vec3 EntityItem::getAbsoluteJointTranslationInObjectFrame(int index) const {
return glm::vec3(0.0f);
}
void EntityItem::locationChanged() {
requiresRecalcBoxes();
SpatiallyNestable::locationChanged(); // tell all the children, also

View file

@ -161,11 +161,10 @@ public:
// attributes applicable to all entity types
EntityTypes::EntityType getType() const { return _type; }
inline glm::vec3 getCenterPosition() const { return getTransformToCenter().getTranslation(); }
inline glm::vec3 getCenterPosition(bool& success) const { return getTransformToCenter(success).getTranslation(); }
void setCenterPosition(const glm::vec3& position);
const Transform getTransformToCenter() const;
void setTranformToCenter(const Transform& transform);
const Transform getTransformToCenter(bool& success) const;
inline void requiresRecalcBoxes() { _recalcAABox = true; _recalcMinAACube = true; _recalcMaxAACube = true; }
@ -232,9 +231,12 @@ public:
quint64 getExpiry() const;
// position, size, and bounds related helpers
const AACube& getMaximumAACube() const;
const AACube& getMinimumAACube() const;
const AABox& getAABox() const; /// axis aligned bounding box in world-frame (meters)
virtual AACube getMaximumAACube(bool& success) const override;
AACube getMinimumAACube(bool& success) const;
AABox getAABox(bool& success) const; /// axis aligned bounding box in world-frame (meters)
using SpatiallyNestable::getQueryAACube;
virtual AACube getQueryAACube(bool& success) const override;
const QString& getScript() const { return _script; }
void setScript(const QString& value) { _script = value; }
@ -379,8 +381,8 @@ public:
QList<EntityActionPointer> getActionsOfType(EntityActionType typeToGet);
// these are in the frame of this object
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override { return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override { return glm::vec3(0.0f); }
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
virtual void loader() {} // called indirectly when urls for geometry are updated

View file

@ -262,6 +262,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID);
CHECK_PROPERTY_CHANGE(PROP_PARENT_ID, parentID);
CHECK_PROPERTY_CHANGE(PROP_PARENT_JOINT_INDEX, parentJointIndex);
CHECK_PROPERTY_CHANGE(PROP_QUERY_AA_CUBE, queryAACube);
changedProperties += _animation.getChangedProperties();
changedProperties += _keyLight.getChangedProperties();
@ -473,10 +474,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_ID, parentID);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_JOINT_INDEX, parentJointIndex);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_QUERY_AA_CUBE, queryAACube);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ROTATION, localRotation);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_QUERY_AA_CUBE, queryAACube);
// FIXME - I don't think these properties are supported any more
//COPY_PROPERTY_TO_QSCRIPTVALUE(glowLevel);
//COPY_PROPERTY_TO_QSCRIPTVALUE(localRenderAlpha);
@ -601,6 +605,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(parentID, QUuid, setParentID);
COPY_PROPERTY_FROM_QSCRIPTVALUE(parentJointIndex, quint16, setParentJointIndex);
COPY_PROPERTY_FROM_QSCRIPTVALUE(queryAACube, AACube, setQueryAACube);
COPY_PROPERTY_FROM_QSCRIPTVALUE(localPosition, glmVec3, setLocalPosition);
COPY_PROPERTY_FROM_QSCRIPTVALUE(localRotation, glmQuat, setLocalRotation);
@ -922,6 +927,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_DESCRIPTION, properties.getDescription());
APPEND_ENTITY_PROPERTY(PROP_PARENT_ID, properties.getParentID());
APPEND_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, properties.getParentJointIndex());
APPEND_ENTITY_PROPERTY(PROP_QUERY_AA_CUBE, properties.getQueryAACube());
if (properties.getType() == EntityTypes::Web) {
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
@ -1208,6 +1214,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DESCRIPTION, QString, setDescription);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_ID, QUuid, setParentID);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_QUERY_AA_CUBE, AACube, setQueryAACube);
if (properties.getType() == EntityTypes::Web) {
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
@ -1460,32 +1467,8 @@ void EntityItemProperties::markAllChanged() {
_parentIDChanged = true;
_parentJointIndexChanged = true;
}
/// The maximum bounding cube for the entity, independent of it's rotation.
/// This accounts for the registration point (upon which rotation occurs around).
///
AACube EntityItemProperties::getMaximumAACube() const {
// * we know that the position is the center of rotation
glm::vec3 centerOfRotation = _position; // also where _registration point is
// * we know that the registration point is the center of rotation
// * we can calculate the length of the furthest extent from the registration point
// as the dimensions * max (registrationPoint, (1.0,1.0,1.0) - registrationPoint)
glm::vec3 registrationPoint = (_dimensions * _registrationPoint);
glm::vec3 registrationRemainder = (_dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint));
glm::vec3 furthestExtentFromRegistration = glm::max(registrationPoint, registrationRemainder);
// * we know that if you rotate in any direction you would create a sphere
// that has a radius of the length of furthest extent from registration point
float radius = glm::length(furthestExtentFromRegistration);
// * we know that the minimum bounding cube of this maximum possible sphere is
// (center - radius) to (center + radius)
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
float diameter = radius * 2.0f;
return AACube(minimumCorner, diameter);
_queryAACubeChanged = true;
}
// The minimum bounding box for the entity.
@ -1785,6 +1768,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (parentJointIndexChanged()) {
out += "parentJointIndex";
}
if (queryAACubeChanged()) {
out += "queryAACube";
}
getAnimation().listChangedProperties(out);
getKeyLight().listChangedProperties(out);
@ -1798,3 +1784,7 @@ QList<QString> EntityItemProperties::listChangedProperties() {
bool EntityItemProperties::parentDependentPropertyChanged() const {
return localPositionChanged() || positionChanged() || localRotationChanged() || rotationChanged();
}
bool EntityItemProperties::parentRelatedPropertyChanged() const {
return parentDependentPropertyChanged() || parentIDChanged() || parentJointIndexChanged();
}

View file

@ -84,8 +84,8 @@ public:
EntityPropertyFlags getChangedProperties() const;
bool parentDependentPropertyChanged() const; // was there a changed in a property that requires parent info to interpret?
bool parentRelatedPropertyChanged() const; // parentDependentPropertyChanged or parentID or parentJointIndex
AACube getMaximumAACube() const;
AABox getAABox() const;
void debugDump() const;
@ -192,7 +192,8 @@ public:
DEFINE_PROPERTY_REF(PROP_Y_P_NEIGHBOR_ID, YPNeighborID, yPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_Z_P_NEIGHBOR_ID, ZPNeighborID, zPNeighborID, EntityItemID, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_PARENT_ID, ParentID, parentID, QUuid, UNKNOWN_ENTITY_ID);
DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, 0);
DEFINE_PROPERTY_REF(PROP_PARENT_JOINT_INDEX, ParentJointIndex, parentJointIndex, quint16, -1);
DEFINE_PROPERTY_REF(PROP_QUERY_AA_CUBE, QueryAACube, queryAACube, AACube, AACube());
// these are used when bouncing location data into and out of scripts
DEFINE_PROPERTY_REF(PROP_LOCAL_POSITION, LocalPosition, localPosition, glmVec3, ENTITY_ITEM_ZERO_VEC3);
@ -397,6 +398,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParentID, parentID, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ParentJointIndex, parentJointIndex, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, QueryAACube, queryAACube, "");
properties.getAnimation().debugDump();
properties.getAtmosphere().debugDump();

View file

@ -122,6 +122,9 @@ inline QScriptValue convertScriptValue(QScriptEngine* e, const QByteArray& v) {
inline QScriptValue convertScriptValue(QScriptEngine* e, const EntityItemID& v) { return QScriptValue(QUuid(v).toString()); }
inline QScriptValue convertScriptValue(QScriptEngine* e, const AACube& v) { return aaCubeToScriptValue(e, v); }
#define COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(X,G,g,P,p) \
if ((desiredProperties.isEmpty() || desiredProperties.getHasProperty(X)) && \
@ -225,6 +228,13 @@ inline glmVec3 glmVec3_convertFromScriptValue(const QScriptValue& v, bool& isVal
return glm::vec3(0);
}
inline AACube AACube_convertFromScriptValue(const QScriptValue& v, bool& isValid) {
isValid = true;
AACube result;
aaCubeFromScriptValue(v, result);
return result;
}
inline qVectorFloat qVectorFloat_convertFromScriptValue(const QScriptValue& v, bool& isValid) {
isValid = true;
return qVectorFloatFromScriptValue(v);

View file

@ -157,6 +157,8 @@ enum EntityPropertyList {
PROP_LOCAL_POSITION, // only used to convert values to and from scripts
PROP_LOCAL_ROTATION, // only used to convert values to and from scripts
PROP_QUERY_AA_CUBE, // how the EntityTree considers the size and position on an entity
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties to end of list just ABOVE this line
PROP_AFTER_LAST_ITEM,

View file

@ -72,12 +72,15 @@ EntityItemProperties convertLocationToScriptSemantics(const EntityItemProperties
scriptSideProperties.setLocalPosition(entitySideProperties.getPosition());
scriptSideProperties.setLocalRotation(entitySideProperties.getRotation());
bool success;
glm::vec3 worldPosition = SpatiallyNestable::localToWorld(entitySideProperties.getPosition(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex());
entitySideProperties.getParentJointIndex(),
success);
glm::quat worldRotation = SpatiallyNestable::localToWorld(entitySideProperties.getRotation(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex());
entitySideProperties.getParentJointIndex(),
success);
scriptSideProperties.setPosition(worldPosition);
scriptSideProperties.setRotation(worldRotation);
@ -89,13 +92,15 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti
// convert position and rotation properties from world-space to local, unless localPosition and localRotation
// are set. If they are set, they overwrite position and rotation.
EntityItemProperties entitySideProperties = scriptSideProperties;
bool success;
if (scriptSideProperties.localPositionChanged()) {
entitySideProperties.setPosition(scriptSideProperties.getLocalPosition());
} else if (scriptSideProperties.positionChanged()) {
glm::vec3 localPosition = SpatiallyNestable::worldToLocal(entitySideProperties.getPosition(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex());
entitySideProperties.getParentJointIndex(),
success);
entitySideProperties.setPosition(localPosition);
}
@ -104,7 +109,8 @@ EntityItemProperties convertLocationFromScriptSemantics(const EntityItemProperti
} else if (scriptSideProperties.rotationChanged()) {
glm::quat localRotation = SpatiallyNestable::worldToLocal(entitySideProperties.getRotation(),
entitySideProperties.getParentID(),
entitySideProperties.getParentJointIndex());
entitySideProperties.getParentJointIndex(),
success);
entitySideProperties.setRotation(localRotation);
}
@ -128,6 +134,10 @@ QUuid EntityScriptingInterface::addEntity(const EntityItemProperties& properties
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
propertiesWithSimID.setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
if (propertiesWithSimID.parentRelatedPropertyChanged()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
propertiesWithSimID.setQueryAACube(entity->getQueryAACube());
}
// and make note of it now, so we can act on it right away.
entity->setSimulationOwner(myNodeID, SCRIPT_EDIT_SIMULATION_PRIORITY);
@ -193,16 +203,15 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit
QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& scriptSideProperties) {
EntityItemProperties properties = scriptSideProperties;
EntityItemID entityID(id);
// If we have a local entity tree set, then also update it.
if (!_entityTree) {
queueEntityMessage(PacketType::EntityEdit, entityID, properties);
return id;
}
// If we have a local entity tree set, then also update it.
bool updatedEntity = false;
_entityTree->withWriteLock([&] {
if (scriptSideProperties.parentDependentPropertyChanged() ||
scriptSideProperties.parentIDChanged() || scriptSideProperties.parentJointIndexChanged()) {
if (scriptSideProperties.parentRelatedPropertyChanged()) {
// All of parentID, parentJointIndex, position, rotation are needed to make sense of any of them.
// If any of these changed, pull any missing properties from the entity.
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(entityID);
@ -265,7 +274,24 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
entity->flagForOwnership();
}
}
if (properties.parentRelatedPropertyChanged() && entity->computePuffedQueryAACube()) {
properties.setQueryAACube(entity->getQueryAACube());
}
entity->setLastBroadcast(usecTimestampNow());
// if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server
// if they've changed.
entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
if (descendant->getNestableType() == NestableType::Entity) {
if (descendant->computePuffedQueryAACube()) {
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
EntityItemProperties newQueryCubeProperties;
newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());
queueEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties);
entityDescendant->setLastBroadcast(usecTimestampNow());
}
}
});
}
});
queueEntityMessage(PacketType::EntityEdit, entityID, properties);

View file

@ -119,8 +119,9 @@ void EntitySimulation::sortEntitiesThatMoved() {
while (itemItr != _entitiesToSort.end()) {
EntityItemPointer entity = *itemItr;
// check to see if this movement has sent the entity outside of the domain.
AACube newCube = entity->getMaximumAACube();
if (!domainBounds.touches(newCube)) {
bool success;
AACube newCube = entity->getQueryAACube(success);
if (success && !domainBounds.touches(newCube)) {
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
_entitiesToDelete.insert(entity);
_mortalEntities.remove(entity);
@ -200,8 +201,9 @@ void EntitySimulation::changeEntity(EntityItemPointer entity) {
uint32_t dirtyFlags = entity->getDirtyFlags();
if (dirtyFlags & Simulation::DIRTY_POSITION) {
AACube domainBounds(glm::vec3((float)-HALF_TREE_SCALE), (float)TREE_SCALE);
AACube newCube = entity->getMaximumAACube();
if (!domainBounds.touches(newCube)) {
bool success;
AACube newCube = entity->getQueryAACube(success);
if (success && !domainBounds.touches(newCube)) {
qCDebug(entities) << "Entity " << entity->getEntityItemID() << " moved out of domain bounds.";
_entitiesToDelete.insert(entity);
_mortalEntities.remove(entity);

View file

@ -89,6 +89,9 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
_isDirty = true;
maybeNotifyNewCollisionSoundURL("", entity->getCollisionSoundURL());
emit addingEntity(entity->getEntityItemID());
// find and hook up any entities with this entity as a (previously) missing parent
fixupMissingParents();
}
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
@ -142,8 +145,12 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
EntityItemProperties tempProperties;
tempProperties.setLocked(wantsLocked);
BoundingBoxRelatedProperties newBBRelProperties(entity, tempProperties);
UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, newBBRelProperties);
bool success;
AACube queryCube = entity->getQueryAACube(success);
if (!success) {
qCDebug(entities) << "Warning -- failed to get query-cube for" << entity->getID();
}
UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, queryCube);
recurseTreeWithOperator(&theOperator);
entity->setProperties(tempProperties);
_isDirty = true;
@ -211,8 +218,7 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
QString collisionSoundURLBefore = entity->getCollisionSoundURL();
uint32_t preFlags = entity->getDirtyFlags();
BoundingBoxRelatedProperties newBBRelProperties(entity, properties);
UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, newBBRelProperties);
UpdateEntityOperator theOperator(getThisPointer(), containingElement, entity, properties.getQueryAACube());
recurseTreeWithOperator(&theOperator);
entity->setProperties(properties);
@ -229,14 +235,19 @@ bool EntityTree::updateEntityWithElement(EntityItemPointer entity, const EntityI
if (!childEntity) {
continue;
}
BoundingBoxRelatedProperties newChildBBRelProperties(childEntity);
EntityTreeElementPointer containingElement = childEntity->getElement();
if (!containingElement) {
continue;
}
UpdateEntityOperator theChildOperator(getThisPointer(),
containingElement,
childEntity, newChildBBRelProperties);
bool success;
AACube queryCube = childEntity->getQueryAACube(success);
if (!success) {
_missingParent.append(childEntity);
continue;
}
UpdateEntityOperator theChildOperator(getThisPointer(), containingElement, childEntity, queryCube);
recurseTreeWithOperator(&theChildOperator);
foreach (SpatiallyNestablePointer childChild, childEntity->getChildren()) {
if (childChild && childChild->getNestableType() == NestableType::Entity) {
@ -316,6 +327,11 @@ EntityItemPointer EntityTree::addEntity(const EntityItemID& entityID, const Enti
// Recurse the tree and store the entity in the correct tree element
AddEntityOperator theOperator(getThisPointer(), result);
recurseTreeWithOperator(&theOperator);
if (result->getAncestorMissing()) {
// we added the entity, but didn't know about all its ancestors, so it went into the wrong place.
// add it to a list of entities needing to be fixed once their parents are known.
_missingParent.append(result);
}
postAddEntity(result);
}
@ -757,6 +773,14 @@ void EntityTree::fixupTerseEditLogging(EntityItemProperties& properties, QList<Q
changedProperties[index] = QString("parentJointIndex:") + QString::number((int)value);
}
}
if (properties.parentIDChanged()) {
int index = changedProperties.indexOf("parentID");
if (index >= 0) {
QUuid value = properties.getParentID();
changedProperties[index] = QString("parentID:") + value.toString();
}
}
}
int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength,
@ -918,7 +942,38 @@ void EntityTree::entityChanged(EntityItemPointer entity) {
}
}
void EntityTree::fixupMissingParents() {
MovingEntitiesOperator moveOperator(getThisPointer());
QMutableVectorIterator<EntityItemWeakPointer> iter(_missingParent);
while (iter.hasNext()) {
EntityItemWeakPointer entityWP = iter.next();
EntityItemPointer entity = entityWP.lock();
if (entity) {
bool success;
AACube newCube = entity->getQueryAACube(success);
if (success) {
// this entity's parent (or ancestry) was previously not fully known, and now is. Update its
// location in the EntityTree.
moveOperator.addEntityToMoveList(entity, newCube);
iter.remove();
entity->markAncestorMissing(false);
}
} else {
// entity was deleted before we found its parent.
iter.remove();
}
}
if (moveOperator.hasMovingEntities()) {
PerformanceTimer perfTimer("recurseTreeWithOperator");
recurseTreeWithOperator(&moveOperator);
}
}
void EntityTree::update() {
fixupMissingParents();
if (_simulation) {
withWriteLock([&] {
_simulation->updateEntities();

View file

@ -307,6 +307,9 @@ protected:
quint64 _totalEditDeltas = 0;
quint64 _maxEditDelta = 0;
quint64 _treeResetTime = 0;
void fixupMissingParents();
QVector<EntityItemWeakPointer> _missingParent;
};
#endif // hifi_EntityTree_h

View file

@ -302,8 +302,9 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
// simulation changing what's visible. consider the case where the entity contains an angular velocity
// the entity may not be in view and then in view a frame later, let the client side handle it's view
// frustum culling on rendering.
AACube entityCube = entity->getMaximumAACube();
if (params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) {
bool success;
AACube entityCube = entity->getQueryAACube(success);
if (!success || params.viewFrustum->cubeInFrustum(entityCube) == ViewFrustum::OUTSIDE) {
includeThisEntity = false; // out of view, don't include it
}
}
@ -413,19 +414,29 @@ OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData
}
bool EntityTreeElement::containsEntityBounds(EntityItemPointer entity) const {
return containsBounds(entity->getMaximumAACube());
bool success;
auto queryCube = entity->getQueryAACube(success);
if (!success) {
return false;
}
return containsBounds(queryCube);
}
bool EntityTreeElement::bestFitEntityBounds(EntityItemPointer entity) const {
return bestFitBounds(entity->getMaximumAACube());
bool success;
auto queryCube = entity->getQueryAACube(success);
if (!success) {
return false;
}
return bestFitBounds(queryCube);
}
bool EntityTreeElement::containsBounds(const EntityItemProperties& properties) const {
return containsBounds(properties.getMaximumAACube());
return containsBounds(properties.getQueryAACube());
}
bool EntityTreeElement::bestFitBounds(const EntityItemProperties& properties) const {
return bestFitBounds(properties.getMaximumAACube());
return bestFitBounds(properties.getQueryAACube());
}
bool EntityTreeElement::containsBounds(const AACube& bounds) const {
@ -526,7 +537,12 @@ bool EntityTreeElement::findDetailedRayIntersection(const glm::vec3& origin, con
return;
}
AABox entityBox = entity->getAABox();
bool success;
AABox entityBox = entity->getAABox(success);
if (!success) {
return;
}
float localDistance;
BoxFace localFace;
glm::vec3 localSurfaceNormal;
@ -631,11 +647,12 @@ EntityItemPointer EntityTreeElement::getClosestEntity(glm::vec3 position) const
void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searchRadius, QVector<EntityItemPointer>& foundEntities) const {
forEachEntity([&](EntityItemPointer entity) {
AABox entityBox = entity->getAABox();
bool success;
AABox entityBox = entity->getAABox(success);
// if the sphere doesn't intersect with our world frame AABox, we don't need to consider the more complex case
glm::vec3 penetration;
if (entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) {
if (success && entityBox.findSpherePenetration(searchPosition, searchRadius, penetration)) {
glm::vec3 dimensions = entity->getDimensions();
@ -651,9 +668,12 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
// maximum bounding sphere, which is actually larger than our actual radius
float entityTrueRadius = dimensions.x / 2.0f;
if (findSphereSpherePenetration(searchPosition, searchRadius,
entity->getCenterPosition(), entityTrueRadius, penetration)) {
foundEntities.push_back(entity);
bool success;
if (findSphereSpherePenetration(searchPosition, searchRadius,
entity->getCenterPosition(success), entityTrueRadius, penetration)) {
if (success) {
foundEntities.push_back(entity);
}
}
} else {
// determine the worldToEntityMatrix that doesn't include scale because
@ -679,7 +699,8 @@ void EntityTreeElement::getEntities(const glm::vec3& searchPosition, float searc
void EntityTreeElement::getEntities(const AACube& cube, QVector<EntityItemPointer>& foundEntities) {
forEachEntity([&](EntityItemPointer entity) {
AABox entityBox = entity->getAABox();
bool success;
AABox entityBox = entity->getAABox(success);
// FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better
// FIXME - consider allowing the entity to determine penetration so that
// entities could presumably dull actuall hull testing if they wanted to
@ -693,10 +714,10 @@ void EntityTreeElement::getEntities(const AACube& cube, QVector<EntityItemPointe
// test the triangles of the face against the box?
// if translated search face triangle intersect target box
// add to result
//
//
// If the entities AABox touches the search cube then consider it to be found
if (entityBox.touches(cube)) {
if (success && entityBox.touches(cube)) {
foundEntities.push_back(entity);
}
});
@ -704,7 +725,8 @@ void EntityTreeElement::getEntities(const AACube& cube, QVector<EntityItemPointe
void EntityTreeElement::getEntities(const AABox& box, QVector<EntityItemPointer>& foundEntities) {
forEachEntity([&](EntityItemPointer entity) {
AABox entityBox = entity->getAABox();
bool success;
AABox entityBox = entity->getAABox(success);
// FIXME - handle entity->getShapeType() == SHAPE_TYPE_SPHERE case better
// FIXME - consider allowing the entity to determine penetration so that
// entities could presumably dull actuall hull testing if they wanted to
@ -718,10 +740,10 @@ void EntityTreeElement::getEntities(const AABox& box, QVector<EntityItemPointer>
// test the triangles of the face against the box?
// if translated search face triangle intersect target box
// add to result
//
//
// If the entities AABox touches the search cube then consider it to be found
if (entityBox.touches(box)) {
if (success && entityBox.touches(box)) {
foundEntities.push_back(entity);
}
});
@ -940,7 +962,11 @@ bool EntityTreeElement::pruneChildren() {
void EntityTreeElement::expandExtentsToContents(Extents& extents) {
withReadLock([&] {
foreach(EntityItemPointer entity, _entityItems) {
extents.add(entity->getAABox());
bool success;
AABox aaBox = entity->getAABox(success);
if (success) {
extents.add(aaBox);
}
}
});
}

View file

@ -110,7 +110,11 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons
// then translate back to work coordinates
glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f));
distance = glm::distance(origin, hitAt);
surfaceNormal = glm::normalize(hitAt - getCenterPosition());
bool success;
surfaceNormal = glm::normalize(hitAt - getCenterPosition(success));
if (!success) {
return false;
}
return true;
}
return false;

View file

@ -14,12 +14,11 @@
UpdateEntityOperator::UpdateEntityOperator(EntityTreePointer tree,
EntityTreeElementPointer containingElement,
EntityItemPointer existingEntity,
const BoundingBoxRelatedProperties& newProperties) :
const AACube newQueryAACube) :
_tree(tree),
_existingEntity(existingEntity),
_containingElement(containingElement),
_containingElementCube(containingElement->getAACube()),
_newProperties(newProperties),
_entityItemID(existingEntity->getEntityItemID()),
_foundOld(false),
_foundNew(false),
@ -41,13 +40,13 @@ UpdateEntityOperator::UpdateEntityOperator(EntityTreePointer tree,
// entity into the the element, or do we want to use the entities "relaxed" bounds
// which can handle all potential rotations?
// the getMaximumAACube is the relaxed form.
_oldEntityCube = _existingEntity->getMaximumAACube();
_oldEntityCube = _existingEntity->getQueryAACube();
_oldEntityBox = _oldEntityCube.clamp((float)-HALF_TREE_SCALE, (float)HALF_TREE_SCALE); // clamp to domain bounds
// If our new properties don't have bounds details (no change to position, etc) or if this containing element would
// be the best fit for our new properties, then just do the new portion of the store pass, since the change path will
// be the same for both parts of the update
bool oldElementBestFit = _containingElement->bestFitBounds(newProperties.getMaximumAACube());
bool oldElementBestFit = _containingElement->bestFitBounds(newQueryAACube);
// For some reason we've seen a case where the original containing element isn't a best fit for the old properties
// in this case we want to move it, even if the properties haven't changed.

View file

@ -12,7 +12,6 @@
#ifndef hifi_UpdateEntityOperator_h
#define hifi_UpdateEntityOperator_h
#include "BoundingBoxRelatedProperties.h"
#include "EntitiesLogging.h"
#include "EntityItem.h"
#include "EntityItemProperties.h"
@ -22,7 +21,7 @@
class UpdateEntityOperator : public RecurseOctreeOperator {
public:
UpdateEntityOperator(EntityTreePointer tree, EntityTreeElementPointer containingElement,
EntityItemPointer existingEntity, const BoundingBoxRelatedProperties& newProperties);
EntityItemPointer existingEntity, const AACube newQueryAACube);
~UpdateEntityOperator();
@ -34,7 +33,6 @@ private:
EntityItemPointer _existingEntity;
EntityTreeElementPointer _containingElement;
AACube _containingElementCube; // we temporarily store our cube here in case we need to delete the containing element
BoundingBoxRelatedProperties _newProperties;
EntityItemID _entityItemID;
bool _foundOld;
bool _foundNew;

View file

@ -41,7 +41,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityAdd:
case PacketType::EntityEdit:
case PacketType::EntityData:
return VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP;
return VERSION_ENTITITES_HAVE_QUERY_BOX;
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SoftAttachmentSupport);

View file

@ -161,7 +161,8 @@ const PacketVersion VERSION_ENTITIES_KEYLIGHT_PROPERTIES_GROUP_BIS = 48;
const PacketVersion VERSION_ENTITIES_PARTICLES_ADDITIVE_BLENDING = 49;
const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50;
const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51;
const PacketVersion VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52;
const PacketVersion VERSION_ENTITITES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52;
const PacketVersion VERSION_ENTITITES_HAVE_QUERY_BOX = 53;
enum class AvatarMixerPacketVersion : PacketVersion {
TranslationSupport = 17,

View file

@ -23,6 +23,11 @@ AtomicUIntStat OctreePacketData::_totalBytesOfValues { 0 };
AtomicUIntStat OctreePacketData::_totalBytesOfPositions { 0 };
AtomicUIntStat OctreePacketData::_totalBytesOfRawData { 0 };
struct aaCubeData {
glm::vec3 corner;
float scale;
};
OctreePacketData::OctreePacketData(bool enableCompression, int targetSize) {
changeSettings(enableCompression, targetSize); // does reset...
}
@ -461,6 +466,17 @@ bool OctreePacketData::appendValue(const QByteArray& bytes) {
return success;
}
bool OctreePacketData::appendValue(const AACube& aaCube) {
aaCubeData cube { aaCube.getCorner(), aaCube.getScale() };
const unsigned char* data = (const unsigned char*)&cube;
int length = sizeof(aaCubeData);
bool success = append(data, length);
if (success) {
_bytesOfValues += length;
_totalBytesOfValues += length;
}
return success;
}
bool OctreePacketData::appendPosition(const glm::vec3& value) {
const unsigned char* data = (const unsigned char*)&value;
@ -638,3 +654,10 @@ int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, QByteA
result = value;
return sizeof(length) + length;
}
int OctreePacketData::unpackDataFromBytes(const unsigned char* dataBytes, AACube& result) {
aaCubeData cube;
memcpy(&cube, dataBytes, sizeof(aaCubeData));
result = AACube(cube.corner, cube.scale);
return sizeof(aaCubeData);
}

View file

@ -187,6 +187,9 @@ public:
/// appends a QByteArray value to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(const QByteArray& bytes);
/// appends an AACube value to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendValue(const AACube& aaCube);
/// appends a position to the end of the stream, may fail if new data stream is too long to fit in packet
bool appendPosition(const glm::vec3& value);
@ -253,6 +256,7 @@ public:
static int unpackDataFromBytes(const unsigned char* dataBytes, QVector<glm::vec3>& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, QVector<float>& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, QByteArray& result);
static int unpackDataFromBytes(const unsigned char* dataBytes, AACube& result);
private:
/// appends raw bytes, might fail if byte would cause packet to be too large

View file

@ -374,6 +374,10 @@ bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep, const QUuid& s
return true;
}
if (_entity->queryAABoxNeedsUpdate()) {
return true;
}
if (_entity->getSimulatorID() != sessionID) {
// we don't own the simulation, but maybe we should...
if (_outgoingPriority != NO_PRORITY) {
@ -466,6 +470,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
properties.setActionData(_serverActionData);
}
if (properties.parentRelatedPropertyChanged() && _entity->computePuffedQueryAACube()) {
// due to parenting, the server may not know where something is in world-space, so include the bounding cube.
properties.setQueryAACube(_entity->getQueryAACube());
}
// set the LastEdited of the properties but NOT the entity itself
quint64 now = usecTimestampNow();
properties.setLastEdited(now);
@ -502,6 +511,20 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, const Q
entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, id, properties);
_entity->setLastBroadcast(usecTimestampNow());
// if we've moved an entity with children, check/update the queryAACube of all descendents and tell the server
// if they've changed.
_entity->forEachDescendant([&](SpatiallyNestablePointer descendant) {
if (descendant->getNestableType() == NestableType::Entity) {
EntityItemPointer entityDescendant = std::static_pointer_cast<EntityItem>(descendant);
if (descendant->computePuffedQueryAACube()) {
EntityItemProperties newQueryCubeProperties;
newQueryCubeProperties.setQueryAACube(descendant->getQueryAACube());
entityPacketSender->queueEditEntityMessage(PacketType::EntityEdit, descendant->getID(), newQueryCubeProperties);
entityDescendant->setLastBroadcast(usecTimestampNow());
}
}
});
_lastStep = step;
}

View file

@ -95,6 +95,9 @@ const float METERS_PER_MILLIMETER = 0.01f;
void Model::setScaleInternal(const glm::vec3& scale) {
if (glm::distance(_scale, scale) > METERS_PER_MILLIMETER) {
_scale = scale;
if (_scale.x == 0.0f || _scale.y == 0.0f || _scale.z == 0.0f) {
assert(false);
}
initJointTransforms();
}
}

View file

@ -465,3 +465,16 @@ AABox AACube::clamp(float min, float max) const {
return temp.clamp(min, max);
}
AACube& AACube::operator += (const glm::vec3& point) {
glm::vec3 oldMaximumPoint = getMaximumPoint();
_corner = glm::vec3(glm::min(_corner.x, point.x),
glm::min(_corner.y, point.y),
glm::min(_corner.z, point.z));
glm::vec3 scaleOld = oldMaximumPoint - _corner;
glm::vec3 scalePoint = point - _corner;
_scale = glm::max(_scale, scalePoint.x, scalePoint.y, scalePoint.z);
_scale = glm::max(_scale, scaleOld.x, scaleOld.y, scaleOld.z);
return (*this);
}

View file

@ -64,6 +64,8 @@ public:
AABox clamp(const glm::vec3& min, const glm::vec3& max) const;
AABox clamp(float min, float max) const;
AACube& operator += (const glm::vec3& point);
private:
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;

View file

@ -42,7 +42,7 @@ void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, collisionToScriptValue, collisionFromScriptValue);
qScriptRegisterMetaType(engine, quuidToScriptValue, quuidFromScriptValue);
qScriptRegisterMetaType(engine, qSizeFToScriptValue, qSizeFFromScriptValue);
qScriptRegisterMetaType(engine, aaCubeToScriptValue, aaCubeFromScriptValue);
}
QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) {
@ -238,6 +238,26 @@ QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) {
return object;
}
QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube) {
QScriptValue obj = engine->newObject();
const glm::vec3& corner = aaCube.getCorner();
obj.setProperty("x", corner.x);
obj.setProperty("y", corner.y);
obj.setProperty("z", corner.z);
obj.setProperty("scale", aaCube.getScale());
return obj;
}
void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube) {
glm::vec3 corner;
corner.x = object.property("x").toVariant().toFloat();
corner.y = object.property("y").toVariant().toFloat();
corner.z = object.property("z").toVariant().toFloat();
float scale = object.property("scale").toVariant().toFloat();
aaCube.setBox(corner, scale);
}
void qColorFromScriptValue(const QScriptValue& object, QColor& color) {
if (object.isNumber()) {
color.setRgb(object.toUInt32());

View file

@ -18,6 +18,7 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include "AACube.h"
#include "SharedUtil.h"
class QColor;
@ -30,6 +31,7 @@ Q_DECLARE_METATYPE(glm::quat)
Q_DECLARE_METATYPE(xColor)
Q_DECLARE_METATYPE(QVector<glm::vec3>)
Q_DECLARE_METATYPE(QVector<float>)
Q_DECLARE_METATYPE(AACube)
void registerMetaTypes(QScriptEngine* engine);
@ -67,6 +69,9 @@ QVector<float> qVectorFloatFromScriptValue(const QScriptValue& array);
QVector<QUuid> qVectorQUuidFromScriptValue(const QScriptValue& array);
QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube);
void aaCubeFromScriptValue(const QScriptValue &object, AACube& aaCube);
class PickRay {
public:
PickRay() : origin(0.0f), direction(0.0f) { }

View file

@ -31,7 +31,7 @@ public:
SpatialParentFinder() { }
virtual ~SpatialParentFinder() { }
virtual SpatiallyNestableWeakPointer find(QUuid parentID) const = 0;
virtual SpatiallyNestableWeakPointer find(QUuid parentID, bool& success) const = 0;
};
#endif // hifi_SpatialParentFinder_h

View file

@ -14,6 +14,7 @@
#include "DependencyManager.h"
#include "SpatiallyNestable.h"
const float defaultAACubeSize = 1.0f;
SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) :
_nestableType(nestableType),
@ -24,21 +25,25 @@ SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) :
_transform.setRotation(glm::quat());
}
Transform SpatiallyNestable::getParentTransform() const {
Transform SpatiallyNestable::getParentTransform(bool& success) const {
Transform result;
SpatiallyNestablePointer parent = getParentPointer();
SpatiallyNestablePointer parent = getParentPointer(success);
if (!success) {
return result;
}
if (parent) {
Transform parentTransform = parent->getTransform(_parentJointIndex);
result = parentTransform.setScale(1.0f);
Transform parentTransform = parent->getTransform(_parentJointIndex, success);
result = parentTransform.setScale(1.0f); // TODO: scaling
}
return result;
}
SpatiallyNestablePointer SpatiallyNestable::getParentPointer() const {
SpatiallyNestablePointer SpatiallyNestable::getParentPointer(bool& success) const {
SpatiallyNestablePointer parent = _parent.lock();
if (!parent && _parentID.isNull()) {
// no parent
success = true;
return nullptr;
}
@ -48,6 +53,7 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer() const {
parent->beParentOfChild(getThisPointer());
_parentKnowsMe = true;
}
success = true;
return parent;
}
@ -63,9 +69,14 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer() const {
// we have a _parentID but no parent pointer, or our parent pointer was to the wrong thing
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
if (!parentFinder) {
success = false;
return nullptr;
}
_parent = parentFinder->find(_parentID);
_parent = parentFinder->find(_parentID, success);
if (!success) {
return nullptr;
}
parent = _parent.lock();
if (parent) {
parent->beParentOfChild(thisPointer);
@ -73,7 +84,9 @@ SpatiallyNestablePointer SpatiallyNestable::getParentPointer() const {
}
if (parent || _parentID.isNull()) {
thisPointer->parentChanged();
success = true;
} else {
success = false;
}
return parent;
@ -96,131 +109,255 @@ void SpatiallyNestable::setParentID(const QUuid& parentID) {
_parentID = parentID;
_parentKnowsMe = false;
}
parentChanged();
}
glm::vec3 SpatiallyNestable::worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex) {
void SpatiallyNestable::setParentJointIndex(quint16 parentJointIndex) {
_parentJointIndex = parentJointIndex;
}
glm::vec3 SpatiallyNestable::worldToLocal(const glm::vec3& position,
const QUuid& parentID, int parentJointIndex,
bool& success) {
Transform result;
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
Transform parentTransform;
if (parentFinder) {
auto parentWP = parentFinder->find(parentID);
auto parent = parentWP.lock();
if (parent) {
parentTransform = parent->getTransform(parentJointIndex);
parentTransform.setScale(1.0f);
}
if (!parentFinder) {
success = false;
return glm::vec3(0.0f);
}
Transform parentTransform;
auto parentWP = parentFinder->find(parentID, success);
if (!success) {
return glm::vec3(0.0f);
}
auto parent = parentWP.lock();
if (!parentID.isNull() && !parent) {
success = false;
return glm::vec3(0.0f);
}
if (parent) {
parentTransform = parent->getTransform(parentJointIndex, success);
if (!success) {
return glm::vec3(0.0f);
}
parentTransform.setScale(1.0f); // TODO: scale
}
success = true;
Transform positionTransform;
positionTransform.setTranslation(position);
Transform myWorldTransform;
Transform::mult(myWorldTransform, parentTransform, positionTransform);
myWorldTransform.setTranslation(position);
Transform result;
Transform::inverseMult(result, parentTransform, myWorldTransform);
return result.getTranslation();
}
glm::quat SpatiallyNestable::worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex) {
glm::quat SpatiallyNestable::worldToLocal(const glm::quat& orientation,
const QUuid& parentID, int parentJointIndex,
bool& success) {
Transform result;
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
Transform parentTransform;
if (parentFinder) {
auto parentWP = parentFinder->find(parentID);
auto parent = parentWP.lock();
if (parent) {
parentTransform = parent->getTransform(parentJointIndex);
parentTransform.setScale(1.0f);
}
if (!parentFinder) {
success = false;
return glm::quat();
}
Transform parentTransform;
auto parentWP = parentFinder->find(parentID, success);
if (!success) {
return glm::quat();
}
auto parent = parentWP.lock();
if (!parentID.isNull() && !parent) {
success = false;
return glm::quat();
}
if (parent) {
parentTransform = parent->getTransform(parentJointIndex, success);
if (!success) {
return glm::quat();
}
parentTransform.setScale(1.0f); // TODO: scale
}
success = true;
Transform orientationTransform;
orientationTransform.setRotation(orientation);
Transform myWorldTransform;
Transform::mult(myWorldTransform, parentTransform, orientationTransform);
myWorldTransform.setRotation(orientation);
Transform result;
Transform::inverseMult(result, parentTransform, myWorldTransform);
return result.getRotation();
}
glm::vec3 SpatiallyNestable::localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex) {
glm::vec3 SpatiallyNestable::localToWorld(const glm::vec3& position,
const QUuid& parentID, int parentJointIndex,
bool& success) {
Transform result;
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
Transform parentTransform;
if (parentFinder) {
auto parentWP = parentFinder->find(parentID);
auto parent = parentWP.lock();
if (parent) {
parentTransform = parent->getTransform(parentJointIndex);
parentTransform.setScale(1.0f);
}
if (!parentFinder) {
success = false;
return glm::vec3(0.0f);
}
Transform parentTransform;
auto parentWP = parentFinder->find(parentID, success);
if (!success) {
return glm::vec3(0.0f);
}
auto parent = parentWP.lock();
if (!parentID.isNull() && !parent) {
success = false;
return glm::vec3(0.0f);
}
if (parent) {
parentTransform = parent->getTransform(parentJointIndex, success);
if (!success) {
return glm::vec3(0.0f);
}
parentTransform.setScale(1.0f); // TODO: scale
}
success = true;
Transform positionTransform;
positionTransform.setTranslation(position);
Transform result;
Transform::mult(result, parentTransform, positionTransform);
return result.getTranslation();
}
glm::quat SpatiallyNestable::localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex) {
glm::quat SpatiallyNestable::localToWorld(const glm::quat& orientation,
const QUuid& parentID, int parentJointIndex,
bool& success) {
Transform result;
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
Transform parentTransform;
if (parentFinder) {
auto parentWP = parentFinder->find(parentID);
auto parent = parentWP.lock();
if (parent) {
parentTransform = parent->getTransform(parentJointIndex);
parentTransform.setScale(1.0f);
}
if (!parentFinder) {
success = false;
return glm::quat();
}
Transform parentTransform;
auto parentWP = parentFinder->find(parentID, success);
if (!success) {
return glm::quat();
}
auto parent = parentWP.lock();
if (!parentID.isNull() && !parent) {
success = false;
return glm::quat();
}
if (parent) {
parentTransform = parent->getTransform(parentJointIndex, success);
if (!success) {
return glm::quat();
}
parentTransform.setScale(1.0f);
}
success = true;
Transform orientationTransform;
orientationTransform.setRotation(orientation);
Transform result;
Transform::mult(result, parentTransform, orientationTransform);
return result.getRotation();
}
glm::vec3 SpatiallyNestable::getPosition(bool& success) const {
return getTransform(success).getTranslation();
}
glm::vec3 SpatiallyNestable::getPosition() const {
return getTransform().getTranslation();
bool success;
auto result = getPosition(success);
#ifdef WANT_DEBUG
if (!success) {
qDebug() << "Warning -- getPosition failed" << getID();
}
#endif
return result;
}
glm::vec3 SpatiallyNestable::getPosition(int jointIndex) const {
return getTransform(jointIndex).getTranslation();
glm::vec3 SpatiallyNestable::getPosition(int jointIndex, bool& success) const {
return getTransform(jointIndex, success).getTranslation();
}
void SpatiallyNestable::setPosition(const glm::vec3& position) {
Transform parentTransform = getParentTransform();
void SpatiallyNestable::setPosition(const glm::vec3& position, bool& success) {
Transform parentTransform = getParentTransform(success);
Transform myWorldTransform;
_transformLock.withWriteLock([&] {
Transform::mult(myWorldTransform, parentTransform, _transform);
myWorldTransform.setTranslation(position);
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
});
locationChanged();
if (success) {
locationChanged();
} else {
qDebug() << "setPosition failed for" << getID();
}
}
void SpatiallyNestable::setPosition(const glm::vec3& position) {
bool success;
setPosition(position, success);
#ifdef WANT_DEBUG
if (!success) {
qDebug() << "Warning -- setPosition failed" << getID();
}
#endif
}
glm::quat SpatiallyNestable::getOrientation(bool& success) const {
return getTransform(success).getRotation();
}
glm::quat SpatiallyNestable::getOrientation() const {
return getTransform().getRotation();
bool success;
auto result = getOrientation(success);
#ifdef WANT_DEBUG
if (!success) {
qDebug() << "Warning -- getOrientation failed" << getID();
}
#endif
return result;
}
glm::quat SpatiallyNestable::getOrientation(int jointIndex) const {
return getTransform(jointIndex).getRotation();
glm::quat SpatiallyNestable::getOrientation(int jointIndex, bool& success) const {
return getTransform(jointIndex, success).getRotation();
}
void SpatiallyNestable::setOrientation(const glm::quat& orientation) {
Transform parentTransform = getParentTransform();
void SpatiallyNestable::setOrientation(const glm::quat& orientation, bool& success) {
Transform parentTransform = getParentTransform(success);
Transform myWorldTransform;
_transformLock.withWriteLock([&] {
Transform::mult(myWorldTransform, parentTransform, _transform);
myWorldTransform.setRotation(orientation);
Transform::inverseMult(_transform, parentTransform, myWorldTransform);
});
locationChanged();
if (success) {
locationChanged();
}
}
const Transform SpatiallyNestable::getTransform() const {
void SpatiallyNestable::setOrientation(const glm::quat& orientation) {
bool success;
setOrientation(orientation, success);
#ifdef WANT_DEBUG
if (!success) {
qDebug() << "Warning -- setOrientation failed" << getID();
}
#endif
}
const Transform SpatiallyNestable::getTransform(bool& success) const {
// return a world-space transform for this object's location
Transform parentTransform = getParentTransform();
Transform parentTransform = getParentTransform(success);
Transform result;
_transformLock.withReadLock([&] {
Transform::mult(result, parentTransform, _transform);
@ -228,25 +365,34 @@ const Transform SpatiallyNestable::getTransform() const {
return result;
}
const Transform SpatiallyNestable::getTransform(int jointIndex) const {
const Transform SpatiallyNestable::getTransform(int jointIndex, bool& success) const {
// this returns the world-space transform for this object. It finds its parent's transform (which may
// cause this object's parent to query its parent, etc) and multiplies this object's local transform onto it.
Transform worldTransform = getTransform();
Transform jointInObjectFrame = getAbsoluteJointTransformInObjectFrame(jointIndex);
Transform jointInWorldFrame;
Transform worldTransform = getTransform(success);
if (!success) {
return jointInWorldFrame;
}
Transform jointInObjectFrame = getAbsoluteJointTransformInObjectFrame(jointIndex);
Transform::mult(jointInWorldFrame, worldTransform, jointInObjectFrame);
success = true;
return jointInWorldFrame;
}
void SpatiallyNestable::setTransform(const Transform& transform) {
Transform parentTransform = getParentTransform();
void SpatiallyNestable::setTransform(const Transform& transform, bool& success) {
Transform parentTransform = getParentTransform(success);
_transformLock.withWriteLock([&] {
Transform::inverseMult(_transform, parentTransform, transform);
});
locationChanged();
if (success) {
locationChanged();
}
}
glm::vec3 SpatiallyNestable::getScale() const {
// TODO: scale
glm::vec3 result;
_transformLock.withReadLock([&] {
result = _transform.getScale();
@ -255,11 +401,12 @@ glm::vec3 SpatiallyNestable::getScale() const {
}
glm::vec3 SpatiallyNestable::getScale(int jointIndex) const {
// XXX ... something with joints
// TODO: scale
return getScale();
}
void SpatiallyNestable::setScale(const glm::vec3& scale) {
// TODO: scale
_transformLock.withWriteLock([&] {
_transform.setScale(scale);
});
@ -312,6 +459,7 @@ void SpatiallyNestable::setLocalOrientation(const glm::quat& orientation) {
}
glm::vec3 SpatiallyNestable::getLocalScale() const {
// TODO: scale
glm::vec3 result;
_transformLock.withReadLock([&] {
result = _transform.getScale();
@ -320,6 +468,7 @@ glm::vec3 SpatiallyNestable::getLocalScale() const {
}
void SpatiallyNestable::setLocalScale(const glm::vec3& scale) {
// TODO: scale
_transformLock.withWriteLock([&] {
_transform.setScale(scale);
});
@ -380,3 +529,83 @@ void SpatiallyNestable::locationChanged() {
object->locationChanged();
});
}
AACube SpatiallyNestable::getMaximumAACube(bool& success) const {
return AACube(getPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize);
}
void SpatiallyNestable::setQueryAACube(const AACube& queryAACube) {
_queryAACube = queryAACube;
if (queryAACube.getScale() > 0.0f) {
_queryAACubeSet = true;
}
}
bool SpatiallyNestable::queryAABoxNeedsUpdate() const {
bool success;
AACube currentAACube = getMaximumAACube(success);
if (!success) {
qDebug() << "can't getMaximumAACube for" << getID();
return false;
}
// make sure children are still in their boxes, also.
bool childNeedsUpdate = false;
getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) {
if (!childNeedsUpdate && descendant->queryAABoxNeedsUpdate()) {
childNeedsUpdate = true;
}
});
if (childNeedsUpdate) {
return true;
}
if (_queryAACubeSet && _queryAACube.contains(currentAACube)) {
return false;
}
return true;
}
bool SpatiallyNestable::computePuffedQueryAACube() {
if (!queryAABoxNeedsUpdate()) {
return false;
}
bool success;
AACube currentAACube = getMaximumAACube(success);
// make an AACube with edges thrice as long and centered on the object
_queryAACube = AACube(currentAACube.getCorner() - glm::vec3(currentAACube.getScale()), currentAACube.getScale() * 3.0f);
_queryAACubeSet = true;
getThisPointer()->forEachDescendant([&](SpatiallyNestablePointer descendant) {
bool success;
AACube descendantAACube = descendant->getQueryAACube(success);
if (success) {
if (_queryAACube.contains(descendantAACube)) {
return;
}
_queryAACube += descendantAACube.getMinimumPoint();
_queryAACube += descendantAACube.getMaximumPoint();
}
});
return true;
}
AACube SpatiallyNestable::getQueryAACube(bool& success) const {
if (_queryAACubeSet) {
success = true;
return _queryAACube;
}
success = false;
return AACube(getPosition(success) - glm::vec3(defaultAACubeSize / 2.0f), defaultAACubeSize);
}
AACube SpatiallyNestable::getQueryAACube() const {
bool success;
auto result = getQueryAACube(success);
if (!success) {
qDebug() << "getQueryAACube failed for" << getID();
}
return result;
}

View file

@ -15,6 +15,7 @@
#include <QUuid>
#include "Transform.h"
#include "AACube.h"
#include "SpatialParentFinder.h"
#include "shared/ReadWriteLockable.h"
@ -38,37 +39,49 @@ public:
virtual const QUuid& getID() const { return _id; }
virtual void setID(const QUuid& id) { _id = id; }
virtual const QUuid getParentID() const { return _parentID; }
virtual QUuid getParentID() const { return _parentID; }
virtual void setParentID(const QUuid& parentID);
virtual quint16 getParentJointIndex() const { return _parentJointIndex; }
virtual void setParentJointIndex(quint16 parentJointIndex) { _parentJointIndex = parentJointIndex; }
virtual void setParentJointIndex(quint16 parentJointIndex);
static glm::vec3 worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex);
static glm::quat worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex);
static glm::vec3 worldToLocal(const glm::vec3& position, const QUuid& parentID, int parentJointIndex, bool& success);
static glm::quat worldToLocal(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex, bool& success);
static glm::vec3 localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex);
static glm::quat localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex);
static glm::vec3 localToWorld(const glm::vec3& position, const QUuid& parentID, int parentJointIndex, bool& success);
static glm::quat localToWorld(const glm::quat& orientation, const QUuid& parentID, int parentJointIndex, bool& success);
// world frame
virtual const Transform getTransform() const;
virtual void setTransform(const Transform& transform);
virtual const Transform getTransform(bool& success) const;
virtual void setTransform(const Transform& transform, bool& success);
virtual Transform getParentTransform() const;
virtual Transform getParentTransform(bool& success) const;
virtual glm::vec3 getPosition(bool& success) const;
virtual glm::vec3 getPosition() const;
virtual void setPosition(const glm::vec3& position, bool& success);
virtual void setPosition(const glm::vec3& position);
virtual glm::quat getOrientation(bool& success) const;
virtual glm::quat getOrientation() const;
virtual glm::quat getOrientation(int jointIndex) const;
virtual glm::quat getOrientation(int jointIndex, bool& success) const;
virtual void setOrientation(const glm::quat& orientation, bool& success);
virtual void setOrientation(const glm::quat& orientation);
virtual AACube getMaximumAACube(bool& success) const;
virtual bool computePuffedQueryAACube();
virtual void setQueryAACube(const AACube& queryAACube);
virtual bool queryAABoxNeedsUpdate() const;
virtual AACube getQueryAACube(bool& success) const;
virtual AACube getQueryAACube() const;
virtual glm::vec3 getScale() const;
virtual void setScale(const glm::vec3& scale);
// get world-frame values for a specific joint
virtual const Transform getTransform(int jointIndex) const;
virtual glm::vec3 getPosition(int jointIndex) const;
virtual const Transform getTransform(int jointIndex, bool& success) const;
virtual glm::vec3 getPosition(int jointIndex, bool& success) const;
virtual glm::vec3 getScale(int jointIndex) const;
// object's parent's frame
@ -89,17 +102,23 @@ public:
// this object's frame
virtual const Transform getAbsoluteJointTransformInObjectFrame(int jointIndex) const;
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const { assert(false); return glm::quat(); }
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const { assert(false); return glm::vec3(); }
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const = 0;
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const = 0;
SpatiallyNestablePointer getThisPointer() const;
void markAncestorMissing(bool value) { _missingAncestor = value; }
bool getAncestorMissing() { return _missingAncestor; }
void forEachChild(std::function<void(SpatiallyNestablePointer)> actor);
void forEachDescendant(std::function<void(SpatiallyNestablePointer)> actor);
protected:
const NestableType _nestableType; // EntityItem or an AvatarData
QUuid _id;
QUuid _parentID; // what is this thing's transform relative to?
quint16 _parentJointIndex { 0 }; // which joint of the parent is this relative to?
SpatiallyNestablePointer getParentPointer() const;
SpatiallyNestablePointer getParentPointer(bool& success) const;
mutable SpatiallyNestableWeakPointer _parent;
virtual void beParentOfChild(SpatiallyNestablePointer newChild) const;
@ -108,12 +127,14 @@ protected:
mutable ReadWriteLockable _childrenLock;
mutable QHash<QUuid, SpatiallyNestableWeakPointer> _children;
virtual void parentChanged() {} // called when parent pointer is updated
virtual void locationChanged(); // called when a this object's location has changed
virtual void dimensionsChanged() {} // called when a this object's dimensions have changed
void forEachChild(std::function<void(SpatiallyNestablePointer)> actor);
void forEachDescendant(std::function<void(SpatiallyNestablePointer)> actor);
// _queryAACube is used to decide where something lives in the octree
mutable AACube _queryAACube;
mutable bool _queryAACubeSet { false };
bool _missingAncestor { false };
private:
mutable ReadWriteLockable _transformLock;