working on pivot

This commit is contained in:
HifiExperiments 2020-12-16 21:01:56 -08:00
parent c510a8fa3e
commit 7069e48073
16 changed files with 167 additions and 101 deletions

View file

@ -248,9 +248,9 @@ private: \
* <em>Read-only.</em>
* @property {string} lodStatus - Description of the current LOD.
* <em>Read-only.</em>
* @property {string} numEntityUpdates - The number of entity updates that happened last frame.
* @property {number} numEntityUpdates - The number of entity updates that happened last frame.
* <em>Read-only.</em>
* @property {string} numNeededEntityUpdates - The total number of entity updates scheduled for last frame.
* @property {number} numNeededEntityUpdates - The total number of entity updates scheduled for last frame.
* <em>Read-only.</em>
* @property {string} timingStats - Details of the average time (ms) spent in and number of calls made to different parts of
* the code. Provided only if <code>timingExpanded</code> is <code>true</code>. Only the top 10 items are provided if
@ -547,8 +547,8 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, lodAngle, 0)
STATS_PROPERTY(int, lodTargetFramerate, 0)
STATS_PROPERTY(QString, lodStatus, QString())
STATS_PROPERTY(int, numEntityUpdates, 0)
STATS_PROPERTY(int, numNeededEntityUpdates, 0)
STATS_PROPERTY(quint64, numEntityUpdates, 0)
STATS_PROPERTY(quint64, numNeededEntityUpdates, 0)
STATS_PROPERTY(QString, timingStats, QString())
STATS_PROPERTY(QString, gameUpdateStats, QString())
STATS_PROPERTY(int, serverElements, 0)

View file

@ -156,17 +156,13 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
updateAttitude(_owningAvatar->getWorldOrientation());
setBlendshapeCoefficients(_owningAvatar->getHead()->getSummedBlendshapeCoefficients());
Parent::simulate(deltaTime, fullUpdate);
if (fullUpdate) {
Parent::simulate(deltaTime, fullUpdate);
// let rig compute the model offset
glm::vec3 registrationPoint;
if (_rig.getModelRegistrationPoint(registrationPoint)) {
setOffset(registrationPoint);
}
} else {
Parent::simulate(deltaTime, fullUpdate);
}
// FIXME: This texture loading logic should probably live in Avatar, to mirror RenderableModelEntityItem,

View file

@ -136,8 +136,8 @@ public:
static bool addMaterialToAvatar(const QUuid& avatarID, graphics::MaterialLayer material, const std::string& parentMaterialName);
static bool removeMaterialFromAvatar(const QUuid& avatarID, graphics::MaterialPointer material, const std::string& parentMaterialName);
int getPrevNumEntityUpdates() const { return _prevNumEntityUpdates; }
int getPrevTotalNeededEntityUpdates() const { return _prevTotalNeededEntityUpdates; }
size_t getPrevNumEntityUpdates() const { return _prevNumEntityUpdates; }
size_t getPrevTotalNeededEntityUpdates() const { return _prevTotalNeededEntityUpdates; }
signals:
void enterEntity(const EntityItemID& entityItemID);
@ -253,8 +253,8 @@ private:
ReadWriteLockable _changedEntitiesGuard;
std::unordered_set<EntityItemID> _changedEntities;
int _prevNumEntityUpdates { 0 };
int _prevTotalNeededEntityUpdates { 0 };
size_t _prevNumEntityUpdates { 0 };
size_t _prevTotalNeededEntityUpdates { 0 };
std::unordered_set<EntityRendererPointer> _renderablesToUpdate;
std::unordered_map<EntityItemID, EntityRendererPointer> _entitiesInScene;

View file

@ -149,10 +149,11 @@ void RenderableModelEntityItem::updateModelBounds() {
bool overridingModelTransform = model->isOverridingModelTransformAndOffset();
glm::vec3 scaledDimensions = getScaledDimensions();
glm::vec3 registrationPoint = getRegistrationPoint();
bool needsSimulate = false;
if (!overridingModelTransform &&
(model->getScaleToFitDimensions() != scaledDimensions ||
model->getRegistrationPoint() != registrationPoint ||
!model->getIsScaledToFit() || _needsToRescaleModel)) {
!model->getIsScaledToFit() || _useOriginalPivot == model->getSnapModelToRegistrationPoint())) {
// The machinery for updateModelBounds will give existing models the opportunity to fix their
// translation/rotation/scale/registration. The first two are straightforward, but the latter two
// have guards to make sure they don't happen after they've already been set. Here we reset those guards.
@ -162,9 +163,10 @@ void RenderableModelEntityItem::updateModelBounds() {
// now recalculate the bounds and registration
model->setScaleToFit(true, scaledDimensions);
model->setSnapModelToRegistrationPoint(true, registrationPoint);
model->setSnapModelToRegistrationPoint(!_useOriginalPivot, registrationPoint);
updateRenderItems = true;
model->scaleToFit();
needsSimulate = true;
locationChanged();
_needsToRescaleModel = false;
}
@ -176,7 +178,7 @@ void RenderableModelEntityItem::updateModelBounds() {
updateRenderItems = true;
}
if (_needsInitialSimulation || _needsJointSimulation || isAnimatingSomething()) {
if (_needsInitialSimulation || _needsJointSimulation || needsSimulate || isAnimatingSomething()) {
// NOTE: on isAnimatingSomething() we need to call Model::simulate() which calls Rig::updateRig()
// TODO: there is opportunity to further optimize the isAnimatingSomething() case.
model->simulate(0.0f);
@ -222,6 +224,16 @@ EntityItemProperties RenderableModelEntityItem::getProperties(const EntityProper
return properties;
}
glm::vec3 RenderableModelEntityItem::getPivot() const {
auto model = getModel();
auto raycastOffset = EntityItem::getPivot();
if (!model || !model->isLoaded() || !_useOriginalPivot) {
return raycastOffset;
}
return raycastOffset + model->getOriginalOffset();
}
bool RenderableModelEntityItem::supportsDetailedIntersection() const {
return true;
}
@ -452,7 +464,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + model->getOffset()) - registrationOffset;
}
}
shapeInfo.setParams(type, 0.5f * extents, getCompoundShapeURL());
shapeInfo.setParams(type, 0.5f * extents, getCompoundShapeURL() + model->getSnapModelToRegistrationPoint());
adjustShapeInfoByRegistration(shapeInfo);
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
updateModelBounds();
@ -685,7 +697,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
}
}
shapeInfo.setParams(type, 0.5f * extents.size(), getModelURL());
shapeInfo.setParams(type, 0.5f * extents.size(), getModelURL() + model->getSnapModelToRegistrationPoint());
adjustShapeInfoByRegistration(shapeInfo);
} else {
EntityItem::computeShapeInfo(shapeInfo);

View file

@ -42,7 +42,7 @@ protected:
void setModel(const ModelPointer& model);
ModelPointer getModel() const;
bool _needsInitialSimulation{ true };
bool _needsInitialSimulation { true };
private:
ModelPointer _model;
};
@ -63,6 +63,7 @@ public:
virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
void updateModelBounds();
glm::vec3 getPivot() const override;
virtual bool supportsDetailedIntersection() const override;
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
OctreeElementPointer& element, float& distance,

View file

@ -723,8 +723,10 @@ ShapeType RenderablePolyVoxEntityItem::getShapeType() const {
}
void RenderablePolyVoxEntityItem::setRegistrationPoint(const glm::vec3& value) {
if (value != _registrationPoint) {
_shapeReady = false;
if (value != getRegistrationPoint()) {
withWriteLock([&] {
_shapeReady = false;
});
EntityItem::setRegistrationPoint(value);
startUpdates();
}

View file

@ -1607,8 +1607,13 @@ void EntityItem::recordCreationTime() {
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()) * getScaledDimensions()); // Position to center
glm::vec3 pivot = getPivot();
if (pivot != ENTITY_ITEM_ZERO_VEC3) {
result.postTranslate(pivot);
}
glm::vec3 registrationPoint = getRegistrationPoint();
if (registrationPoint != ENTITY_ITEM_HALF_VEC3) { // If it is not already centered, translate to center
result.postTranslate((ENTITY_ITEM_HALF_VEC3 - registrationPoint) * getScaledDimensions()); // Position to center
}
return result;
}
@ -1624,7 +1629,8 @@ AACube EntityItem::getMaximumAACube(bool& success) const {
// we want to compute the furthestExtent that an entity can extend out from its "position"
// to do this we compute the max of these two vec3s: registration and 1-registration
// and then scale by dimensions
glm::vec3 maxExtents = getScaledDimensions() * glm::max(_registrationPoint, glm::vec3(1.0f) - _registrationPoint);
glm::vec3 offset = getScaledDimensions() * getRegistrationPoint() + getPivot();
glm::vec3 maxExtents = glm::max(offset, glm::vec3(1.0f) - offset);
// there exists a sphere that contains maxExtents for all rotations
float radius = glm::length(maxExtents);
@ -1650,9 +1656,12 @@ AACube EntityItem::getMinimumAACube(bool& success) const {
if (success) {
_recalcMinAACube = false;
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 unrotatedMinRelativeToEntity = - (dimensions * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint);
glm::vec3 registrationPoint = getRegistrationPoint();
glm::vec3 pivot = getPivot();
glm::vec3 unrotatedMinRelativeToEntity = -(dimensions * registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = dimensions * (ENTITY_ITEM_ONE_VEC3 - registrationPoint);
Extents extents = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
extents.shiftBy(pivot);
extents.rotate(getWorldOrientation());
// shift the extents to be relative to the position/registration point
@ -1680,9 +1689,12 @@ AABox EntityItem::getAABox(bool& success) const {
if (success) {
_recalcAABox = false;
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 unrotatedMinRelativeToEntity = - (dimensions * _registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = dimensions * (glm::vec3(1.0f, 1.0f, 1.0f) - _registrationPoint);
glm::vec3 registrationPoint = getRegistrationPoint();
glm::vec3 pivot = getPivot();
glm::vec3 unrotatedMinRelativeToEntity = -(dimensions * registrationPoint);
glm::vec3 unrotatedMaxRelativeToEntity = dimensions * (ENTITY_ITEM_ONE_VEC3 - registrationPoint);
Extents extents = { unrotatedMinRelativeToEntity, unrotatedMaxRelativeToEntity };
extents.shiftBy(pivot);
extents.rotate(getWorldOrientation());
// shift the extents to be relative to the position/registration point
@ -1723,11 +1735,10 @@ float EntityItem::getRadius() const {
}
void EntityItem::adjustShapeInfoByRegistration(ShapeInfo& info) const {
if (_registrationPoint != ENTITY_ITEM_DEFAULT_REGISTRATION_POINT) {
glm::mat4 scale = glm::scale(getScaledDimensions());
glm::mat4 registration = scale * glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint());
glm::vec3 regTransVec = glm::vec3(registration[3]); // extract position component from matrix
info.setOffset(regTransVec);
glm::vec3 registrationPoint = getRegistrationPoint();
if (registrationPoint != ENTITY_ITEM_DEFAULT_REGISTRATION_POINT) {
glm::vec3 registration = (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - registrationPoint) * getScaledDimensions();
info.setOffset(registration);
}
}
@ -1739,7 +1750,7 @@ bool EntityItem::contains(const glm::vec3& point) const {
// anything with shapeType == SPHERE must collide as a bounding sphere in the world-frame regardless of dimensions
// therefore we must do math using an unscaled localPoint relative to sphere center
glm::vec3 dimensions = getScaledDimensions();
glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())));
glm::vec3 localPoint = point - (getWorldPosition() + getWorldOrientation() * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()) + getPivot()));
const float HALF_SQUARED = 0.25f;
return glm::length2(localPoint) < HALF_SQUARED * glm::length2(dimensions);
}
@ -1797,11 +1808,16 @@ float EntityItem::getVolumeEstimate() const {
}
void EntityItem::setRegistrationPoint(const glm::vec3& value) {
if (value != _registrationPoint) {
withWriteLock([&] {
bool changed = false;
withWriteLock([&] {
if (value != _registrationPoint) {
_registrationPoint = glm::clamp(value, glm::vec3(ENTITY_ITEM_MIN_REGISTRATION_POINT),
glm::vec3(ENTITY_ITEM_MAX_REGISTRATION_POINT));
});
changed = true;
}
});
if (changed) {
dimensionsChanged(); // Registration Point affects the bounding box
markDirtyFlags(Simulation::DIRTY_SHAPE);
}
@ -2892,11 +2908,9 @@ QString EntityItem::getCollisionSoundURL() const {
}
glm::vec3 EntityItem::getRegistrationPoint() const {
glm::vec3 result;
withReadLock([&] {
result = _registrationPoint;
return resultWithReadLock<glm::vec3>([&] {
return _registrationPoint;
});
return result;
}
float EntityItem::getAngularDamping() const {

View file

@ -204,6 +204,7 @@ public:
virtual glm::vec3 getScaledDimensions() const;
virtual void setScaledDimensions(const glm::vec3& value);
virtual glm::vec3 getRaycastDimensions() const { return getScaledDimensions(); }
virtual glm::vec3 getPivot() const { return glm::vec3(0.0f); } // pivot offset for positioning, mainly for model entities
glm::vec3 getUnscaledDimensions() const;
virtual void setUnscaledDimensions(const glm::vec3& value);

View file

@ -538,6 +538,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
CHECK_PROPERTY_CHANGE(PROP_GROUP_CULLED, groupCulled);
CHECK_PROPERTY_CHANGE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients);
CHECK_PROPERTY_CHANGE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot);
changedProperties += _animation.getChangedProperties();
// Light
@ -1733,6 +1734,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GROUP_CULLED, groupCulled);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BLENDSHAPE_COEFFICIENTS, blendshapeCoefficients);
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USE_ORIGINAL_PIVOT, useOriginalPivot);
if (!psuedoPropertyFlagsButDesiredEmpty) {
_animation.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
}
@ -2142,6 +2144,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
COPY_PROPERTY_FROM_QSCRIPTVALUE(relayParentJoints, bool, setRelayParentJoints);
COPY_PROPERTY_FROM_QSCRIPTVALUE(groupCulled, bool, setGroupCulled);
COPY_PROPERTY_FROM_QSCRIPTVALUE(blendshapeCoefficients, QString, setBlendshapeCoefficients);
COPY_PROPERTY_FROM_QSCRIPTVALUE(useOriginalPivot, bool, setUseOriginalPivot);
_animation.copyFromScriptValue(object, _defaultSettings);
// Light
@ -2434,6 +2437,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
COPY_PROPERTY_IF_CHANGED(relayParentJoints);
COPY_PROPERTY_IF_CHANGED(groupCulled);
COPY_PROPERTY_IF_CHANGED(blendshapeCoefficients);
COPY_PROPERTY_IF_CHANGED(useOriginalPivot);
_animation.merge(other._animation);
// Light
@ -2790,6 +2794,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
ADD_PROPERTY_TO_MAP(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool);
ADD_PROPERTY_TO_MAP(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool);
ADD_PROPERTY_TO_MAP(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString);
ADD_PROPERTY_TO_MAP(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool);
{ // Animation
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_URL, Animation, animation, URL, url);
ADD_GROUP_PROPERTY_TO_MAP(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation);
@ -4129,6 +4134,7 @@ void EntityItemProperties::markAllChanged() {
_relayParentJointsChanged = true;
_groupCulledChanged = true;
_blendshapeCoefficientsChanged = true;
_useOriginalPivotChanged = true;
_animation.markAllChanged();
// Light
@ -4701,6 +4707,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
if (blendshapeCoefficientsChanged()) {
out += "blendshapeCoefficients";
}
if (useOriginalPivotChanged()) {
out += "useOriginalPivot";
}
getAnimation().listChangedProperties(out);
// Light

View file

@ -302,6 +302,7 @@ public:
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
DEFINE_PROPERTY_REF(PROP_GROUP_CULLED, GroupCulled, groupCulled, bool, false);
DEFINE_PROPERTY_REF(PROP_BLENDSHAPE_COEFFICIENTS, BlendshapeCoefficients, blendshapeCoefficients, QString, "");
DEFINE_PROPERTY_REF(PROP_USE_ORIGINAL_PIVOT, UseOriginalPivot, useOriginalPivot, bool, false);
DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup);
// Light

View file

@ -218,16 +218,17 @@ enum EntityPropertyList {
PROP_RELAY_PARENT_JOINTS = PROP_DERIVED_6,
PROP_GROUP_CULLED = PROP_DERIVED_7,
PROP_BLENDSHAPE_COEFFICIENTS = PROP_DERIVED_8,
PROP_USE_ORIGINAL_PIVOT = PROP_DERIVED_9,
// Animation
PROP_ANIMATION_URL = PROP_DERIVED_9,
PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_10,
PROP_ANIMATION_FPS = PROP_DERIVED_11,
PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_12,
PROP_ANIMATION_PLAYING = PROP_DERIVED_13,
PROP_ANIMATION_LOOP = PROP_DERIVED_14,
PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_15,
PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_16,
PROP_ANIMATION_HOLD = PROP_DERIVED_17,
PROP_ANIMATION_URL = PROP_DERIVED_10,
PROP_ANIMATION_ALLOW_TRANSLATION = PROP_DERIVED_11,
PROP_ANIMATION_FPS = PROP_DERIVED_12,
PROP_ANIMATION_FRAME_INDEX = PROP_DERIVED_13,
PROP_ANIMATION_PLAYING = PROP_DERIVED_14,
PROP_ANIMATION_LOOP = PROP_DERIVED_15,
PROP_ANIMATION_FIRST_FRAME = PROP_DERIVED_16,
PROP_ANIMATION_LAST_FRAME = PROP_DERIVED_17,
PROP_ANIMATION_HOLD = PROP_DERIVED_18,
// Light
PROP_IS_SPOTLIGHT = PROP_DERIVED_0,

View file

@ -205,10 +205,7 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori
// (this is faster and more likely to cull results than the filter check below so we do it first)
bool success;
AABox entityBox = entity->getAABox(success);
if (!success) {
return;
}
if (!entityBox.rayHitsBoundingSphere(origin, direction)) {
if (!success || !entityBox.rayHitsBoundingSphere(origin, direction)) {
return;
}
@ -226,7 +223,7 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori
glm::vec3 dimensions = entity->getRaycastDimensions();
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
glm::vec3 corner = -(dimensions * registrationPoint) + entity->getPivot();
AABox entityFrameBox(corner, dimensions);
@ -244,6 +241,9 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori
// now ask the entity if we actually intersect
if (entity->supportsDetailedIntersection()) {
QVariantMap localExtraInfo;
if (entity->getName().contains("boop")) {
qDebug() << entity->getName() << entity->getPivot() << entityFrameBox;
}
if (entity->findDetailedRayIntersection(origin, direction, element, localDistance,
localFace, localSurfaceNormal, localExtraInfo, searchFilter.isPrecise())) {
if (localDistance < distance) {
@ -277,11 +277,12 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad
bool result = false;
withReadLock([&] {
foreach(EntityItemPointer entity, _entityItems) {
glm::vec3 entityCenter = entity->getWorldPosition();
bool success;
glm::vec3 entityCenter = entity->getCenterPosition(success);
float entityRadius = entity->getRadius();
// don't penetrate yourself
if (entityCenter == center && entityRadius == radius) {
if (!success || (entityCenter == center && entityRadius == radius)) {
return;
}
@ -349,15 +350,12 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3
// (this is faster and more likely to cull results than the filter check below so we do it first)
bool success;
AABox entityBox = entity->getAABox(success);
if (!success) {
return;
}
// Instead of checking parabolaInstersectsBoundingSphere here, we are just going to check if the plane
// defined by the parabola slices the sphere. The solution to parabolaIntersectsBoundingSphere is cubic,
// the solution to which is more computationally expensive than the quadratic AABox::findParabolaIntersection
// below
if (!entityBox.parabolaPlaneIntersectsBoundingSphere(origin, velocity, acceleration, normal)) {
if (!success || !entityBox.parabolaPlaneIntersectsBoundingSphere(origin, velocity, acceleration, normal)) {
return;
}
@ -375,7 +373,7 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3
glm::vec3 dimensions = entity->getRaycastDimensions();
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
glm::vec3 corner = -(dimensions * registrationPoint) + entity->getPivot();
AABox entityFrameBox(corner, dimensions);
@ -445,7 +443,6 @@ void EntityTreeElement::evalEntitiesInSphere(const glm::vec3& position, float ra
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 (success && entityBox.findSpherePenetration(position, radius, penetration)) {
@ -464,10 +461,9 @@ void EntityTreeElement::evalEntitiesInSphere(const glm::vec3& position, float ra
float entityTrueRadius = dimensions.x / 2.0f;
bool success;
if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) {
if (success) {
foundEntities.push_back(entity->getID());
}
glm::vec3 center = entity->getCenterPosition(success);
if (success && findSphereSpherePenetration(position, radius, center, entityTrueRadius, penetration)) {
foundEntities.push_back(entity->getID());
}
} else {
// determine the worldToEntityMatrix that doesn't include scale because
@ -478,7 +474,7 @@ void EntityTreeElement::evalEntitiesInSphere(const glm::vec3& position, float ra
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
glm::vec3 corner = -(dimensions * registrationPoint) + entity->getPivot();
AABox entityFrameBox(corner, dimensions);
@ -499,7 +495,6 @@ void EntityTreeElement::evalEntitiesInSphereWithType(const glm::vec3& position,
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 (success && entityBox.findSpherePenetration(position, radius, penetration)) {
@ -518,10 +513,9 @@ void EntityTreeElement::evalEntitiesInSphereWithType(const glm::vec3& position,
float entityTrueRadius = dimensions.x / 2.0f;
bool success;
if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) {
if (success) {
foundEntities.push_back(entity->getID());
}
glm::vec3 center = entity->getCenterPosition(success);
if (success && findSphereSpherePenetration(position, radius, center, entityTrueRadius, penetration)) {
foundEntities.push_back(entity->getID());
}
} else {
// determine the worldToEntityMatrix that doesn't include scale because
@ -532,7 +526,7 @@ void EntityTreeElement::evalEntitiesInSphereWithType(const glm::vec3& position,
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
glm::vec3 corner = -(dimensions * registrationPoint) + entity->getPivot();
AABox entityFrameBox(corner, dimensions);
@ -575,12 +569,11 @@ void EntityTreeElement::evalEntitiesInSphereWithName(const glm::vec3& position,
// NOTE: entity->getRadius() doesn't return the true radius, it returns the radius of the
// maximum bounding sphere, which is actually larger than our actual radius
float entityTrueRadius = dimensions.x / 2.0f;
bool success;
if (findSphereSpherePenetration(position, radius, entity->getCenterPosition(success), entityTrueRadius, penetration)) {
if (success) {
foundEntities.push_back(entity->getID());
}
glm::vec3 center = entity->getCenterPosition(success);
if (success && findSphereSpherePenetration(position, radius, center, entityTrueRadius, penetration)) {
foundEntities.push_back(entity->getID());
}
} else {
// determine the worldToEntityMatrix that doesn't include scale because
@ -591,7 +584,7 @@ void EntityTreeElement::evalEntitiesInSphereWithName(const glm::vec3& position,
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 registrationPoint = entity->getRegistrationPoint();
glm::vec3 corner = -(dimensions * registrationPoint);
glm::vec3 corner = -(dimensions * registrationPoint) + entity->getPivot();
AABox entityFrameBox(corner, dimensions);
@ -612,6 +605,7 @@ void EntityTreeElement::evalEntitiesInCube(const AACube& cube, PickFilter search
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
@ -642,6 +636,7 @@ void EntityTreeElement::evalEntitiesInBox(const AABox& box, PickFilter searchFil
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
@ -680,7 +675,7 @@ void EntityTreeElement::evalEntitiesInFrustum(const ViewFrustum& frustum, PickFi
});
}
void EntityTreeElement::getEntities(EntityItemFilter& filter, QVector<EntityItemPointer>& foundEntities) {
void EntityTreeElement::getEntities(EntityItemFilter& filter, QVector<EntityItemPointer>& foundEntities) {
forEachEntity([&](EntityItemPointer entity) {
if (filter(entity)) {
foundEntities.push_back(entity);

View file

@ -73,6 +73,7 @@ EntityItemProperties ModelEntityItem::getProperties(const EntityPropertyFlags& d
COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(groupCulled, getGroupCulled);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(blendshapeCoefficients, getBlendshapeCoefficients);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(useOriginalPivot, getUseOriginalPivot);
withReadLock([&] {
_animationProperties.getProperties(properties);
});
@ -96,6 +97,7 @@ bool ModelEntityItem::setSubClassProperties(const EntityItemProperties& properti
SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(groupCulled, setGroupCulled);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(blendshapeCoefficients, setBlendshapeCoefficients);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(useOriginalPivot, setUseOriginalPivot);
withWriteLock([&] {
AnimationPropertyGroup animationProperties = _animationProperties;
@ -130,6 +132,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
READ_ENTITY_PROPERTY(PROP_GROUP_CULLED, bool, setGroupCulled);
READ_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients);
READ_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot);
// grab a local copy of _animationProperties to avoid multiple locks
int bytesFromAnimation;
@ -169,6 +172,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
requestedProperties += PROP_RELAY_PARENT_JOINTS;
requestedProperties += PROP_GROUP_CULLED;
requestedProperties += PROP_BLENDSHAPE_COEFFICIENTS;
requestedProperties += PROP_USE_ORIGINAL_PIVOT;
requestedProperties += _animationProperties.getEntityProperties(params);
return requestedProperties;
@ -198,6 +202,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, getRelayParentJoints());
APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, getGroupCulled());
APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, getBlendshapeCoefficients());
APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, getUseOriginalPivot());
withReadLock([&] {
_animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
@ -251,6 +256,7 @@ void ModelEntityItem::debugDump() const {
qCDebug(entities) << " model URL:" << getModelURL();
qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL();
qCDebug(entities) << " blendshapeCoefficients:" << getBlendshapeCoefficients();
qCDebug(entities) << " useOrigialPivot:" << getUseOriginalPivot();
}
void ModelEntityItem::setShapeType(ShapeType type) {
@ -713,3 +719,16 @@ QVector<float> ModelEntityItem::getBlendshapeCoefficientVector() {
return _blendshapeCoefficientsVector;
});
}
void ModelEntityItem::setUseOriginalPivot(bool value) {
withWriteLock([&] {
_needsRenderUpdate |= _useOriginalPivot != value;
_useOriginalPivot = value;
});
}
bool ModelEntityItem::getUseOriginalPivot() const {
return resultWithReadLock<bool>([&] {
return _useOriginalPivot;
});
}

View file

@ -119,6 +119,9 @@ public:
bool blendshapesChanged() const { return _blendshapesChanged; }
QVector<float> getBlendshapeCoefficientVector();
bool getUseOriginalPivot() const;
void setUseOriginalPivot(bool useOriginalPivot);
private:
void setAnimationSettings(const QString& value); // only called for old bitstream format
bool applyNewAnimationProperties(AnimationPropertyGroup newProperties);
@ -152,6 +155,7 @@ protected:
bool _relayParentJoints;
bool _groupCulled { false };
QVariantMap _blendshapeCoefficientsMap;
bool _useOriginalPivot { false };
ThreadSafeValueCache<QString> _compoundShapeURL;

View file

@ -151,6 +151,7 @@ void Model::setOffset(const glm::vec3& offset) {
// if someone manually sets our offset, then we are no longer snapped to center
_snapModelToRegistrationPoint = false;
_snappedToRegistrationPoint = false;
_needsTransformUpdate = true;
}
void Model::calculateTextureInfo() {
@ -344,6 +345,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
// extents is the entity relative, scaled, centered extents of the entity
glm::mat4 modelToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation);
if (!_snapModelToRegistrationPoint) {
modelToWorldMatrix = modelToWorldMatrix * glm::translate(getOriginalOffset());
}
glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);
Extents modelExtents = getMeshExtents(); // NOTE: unrotated
@ -375,8 +379,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
calculateTriangleSets(hfmModel);
}
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
glm::mat4 meshToWorldMatrix = modelToWorldMatrix * meshToModelMatrix;
glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * (glm::scale(_scale) * glm::translate(_offset));
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
@ -499,6 +502,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co
// extents is the entity relative, scaled, centered extents of the entity
glm::mat4 modelToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation);
if (!_snapModelToRegistrationPoint) {
modelToWorldMatrix = modelToWorldMatrix * glm::translate(getOriginalOffset());
}
glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);
Extents modelExtents = getMeshExtents(); // NOTE: unrotated
@ -531,8 +537,7 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co
calculateTriangleSets(hfmModel);
}
glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset);
glm::mat4 meshToWorldMatrix = modelToWorldMatrix * meshToModelMatrix;
glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * (glm::scale(_scale) * glm::translate(_offset));
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
@ -1182,17 +1187,8 @@ glm::vec3 Model::getNaturalDimensions() const {
}
Extents Model::getMeshExtents() const {
if (!isLoaded()) {
return Extents();
}
const Extents& extents = getHFMModel().meshExtents;
// even though our caller asked for "unscaled" we need to include any fst scaling, translation, and rotation, which
// is captured in the offset matrix
glm::vec3 minimum = glm::vec3(getHFMModel().offset * glm::vec4(extents.minimum, 1.0f));
glm::vec3 maximum = glm::vec3(getHFMModel().offset * glm::vec4(extents.maximum, 1.0f));
Extents scaledExtents = { minimum * _scale, maximum * _scale };
return scaledExtents;
Extents extents = getUnscaledMeshExtents();
return { extents.minimum * _scale, extents.maximum * _scale};
}
Extents Model::getUnscaledMeshExtents() const {
@ -1406,6 +1402,7 @@ void Model::setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, c
_snapModelToRegistrationPoint = snapModelToRegistrationPoint;
_registrationPoint = clampedRegistrationPoint;
_snappedToRegistrationPoint = false; // force re-centering
_needsTransformUpdate = true;
}
}
@ -1417,6 +1414,15 @@ void Model::snapToRegistrationPoint() {
_snappedToRegistrationPoint = true;
}
glm::vec3 Model::getOriginalOffset() const {
Extents modelMeshExtents = getUnscaledMeshExtents();
glm::vec3 dimensions = (modelMeshExtents.maximum - modelMeshExtents.minimum);
glm::vec3 offset = modelMeshExtents.minimum + (dimensions * _registrationPoint);
glm::mat4 transform = glm::scale(_scale) * glm::translate(offset);
return transform[3];
}
void Model::setUseDualQuaternionSkinning(bool value) {
_useDualQuaternionSkinning = value;
}
@ -1424,7 +1430,7 @@ void Model::setUseDualQuaternionSkinning(bool value) {
void Model::simulate(float deltaTime, bool fullUpdate) {
DETAILED_PROFILE_RANGE(simulation_detail, __FUNCTION__);
fullUpdate = updateGeometry() || fullUpdate || (_scaleToFit && !_scaledToFit)
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint);
|| (_snapModelToRegistrationPoint && !_snappedToRegistrationPoint) || _needsTransformUpdate;
if (isLoaded() && fullUpdate) {
onInvalidate();
@ -1437,8 +1443,10 @@ void Model::simulate(float deltaTime, bool fullUpdate) {
snapToRegistrationPoint();
}
// update the world space transforms for all joints
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
glm::mat4 parentTransform = glm::scale(_scale) * (_snapModelToRegistrationPoint ?
glm::translate(_offset) : glm::translate(getNaturalDimensions() * (0.5f - _registrationPoint)));
updateRig(deltaTime, parentTransform);
_needsTransformUpdate = false;
}
}

View file

@ -167,6 +167,7 @@ public:
void setSnapModelToRegistrationPoint(bool snapModelToRegistrationPoint, const glm::vec3& registrationPoint);
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
bool getSnappedToRegistrationPoint() { return _snappedToRegistrationPoint; }
virtual void simulate(float deltaTime, bool fullUpdate = true);
virtual void updateClusterMatrices();
@ -203,6 +204,7 @@ public:
void setOffset(const glm::vec3& offset);
const glm::vec3& getOffset() const { return _offset; }
glm::vec3 getOriginalOffset() const;
void setScaleToFit(bool scaleToFit, float largestDimension = 0.0f, bool forceRescale = false);
void setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale = false);
@ -348,6 +350,7 @@ public:
virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override;
void scaleToFit();
void snapToRegistrationPoint();
bool getUseDualQuaternionSkinning() const { return _useDualQuaternionSkinning; }
void setUseDualQuaternionSkinning(bool value);
@ -409,14 +412,14 @@ protected:
bool _snapModelToRegistrationPoint; /// is the model's offset automatically adjusted to a registration point in model space
bool _snappedToRegistrationPoint; /// are we currently snapped to a registration point
glm::vec3 _registrationPoint = glm::vec3(0.5f); /// the point in model space our center is snapped to
glm::vec3 _registrationPoint { glm::vec3(0.5f) }; /// the point in model space our center is snapped to
bool _needsTransformUpdate { false };
std::vector<MeshState> _meshStates;
virtual void initJointStates();
void setScaleInternal(const glm::vec3& scale);
void snapToRegistrationPoint();
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);