mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 09:33:49 +02:00
Merge pull request #951 from HifiExperiments/pivot
Model Entities: useOriginalPivot Property
This commit is contained in:
commit
b826743212
22 changed files with 229 additions and 112 deletions
|
@ -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)
|
||||
|
|
|
@ -33,6 +33,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
|||
{
|
||||
// SkeletonModels, and by extention Avatars, use Dual Quaternion skinning.
|
||||
_useDualQuaternionSkinning = true;
|
||||
_forceOffset = true;
|
||||
|
||||
// Avatars all cast shadow
|
||||
setCanCastShadow(true);
|
||||
|
@ -156,17 +157,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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -149,10 +149,12 @@ 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->getRegistrationPoint() != registrationPoint ||
|
||||
!model->getIsScaledToFit() || _needsToRescaleModel ||
|
||||
_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 +164,9 @@ 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;
|
||||
_needsToRescaleModel = false;
|
||||
}
|
||||
|
||||
|
@ -176,10 +178,11 @@ 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);
|
||||
locationChanged();
|
||||
_needsInitialSimulation = false;
|
||||
_needsJointSimulation = false;
|
||||
updateRenderItems = true;
|
||||
|
@ -219,6 +222,16 @@ EntityItemProperties RenderableModelEntityItem::getProperties(const EntityProper
|
|||
return properties;
|
||||
}
|
||||
|
||||
glm::vec3 RenderableModelEntityItem::getPivot() const {
|
||||
auto model = getModel();
|
||||
auto pivot = EntityItem::getPivot();
|
||||
if (!model || !model->isLoaded() || !_useOriginalPivot) {
|
||||
return pivot;
|
||||
}
|
||||
|
||||
return pivot + model->getOriginalOffset();
|
||||
}
|
||||
|
||||
bool RenderableModelEntityItem::supportsDetailedIntersection() const {
|
||||
return true;
|
||||
}
|
||||
|
@ -443,14 +456,15 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
// multiply each point by scale before handing the point-set off to the physics engine.
|
||||
// also determine the extents of the collision model.
|
||||
glm::vec3 registrationOffset = dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint());
|
||||
glm::vec3 offset = model->getSnapModelToRegistrationPoint() ? model->getOffset() : glm::vec3(0.0f);
|
||||
for (int32_t i = 0; i < pointCollection.size(); i++) {
|
||||
for (int32_t j = 0; j < pointCollection[i].size(); j++) {
|
||||
// back compensate for registration so we can apply that offset to the shapeInfo later
|
||||
pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + model->getOffset()) - registrationOffset;
|
||||
pointCollection[i][j] = scaleToFit * (pointCollection[i][j] + offset) - registrationOffset;
|
||||
}
|
||||
}
|
||||
shapeInfo.setParams(type, 0.5f * extents, getCompoundShapeURL());
|
||||
adjustShapeInfoByRegistration(shapeInfo);
|
||||
shapeInfo.setParams(type, 0.5f * extents, getCompoundShapeURL() + model->getSnapModelToRegistrationPoint());
|
||||
adjustShapeInfoByRegistration(shapeInfo, model->getSnapModelToRegistrationPoint());
|
||||
} else if (type >= SHAPE_TYPE_SIMPLE_HULL && type <= SHAPE_TYPE_STATIC_MESH) {
|
||||
updateModelBounds();
|
||||
model->updateGeometry();
|
||||
|
@ -682,8 +696,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
}
|
||||
}
|
||||
|
||||
shapeInfo.setParams(type, 0.5f * extents.size(), getModelURL());
|
||||
adjustShapeInfoByRegistration(shapeInfo);
|
||||
shapeInfo.setParams(type, 0.5f * extents.size(), getModelURL() + model->getSnapModelToRegistrationPoint());
|
||||
adjustShapeInfoByRegistration(shapeInfo, model->getSnapModelToRegistrationPoint());
|
||||
} else {
|
||||
EntityItem::computeShapeInfo(shapeInfo);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -1623,15 +1628,16 @@ AACube EntityItem::getMaximumAACube(bool& success) const {
|
|||
_recalcMaxAACube = false;
|
||||
// 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);
|
||||
// and then scale by dimensions and add the absolute value of the pivot
|
||||
glm::vec3 registrationPoint = getRegistrationPoint();
|
||||
glm::vec3 maxExtents = getScaledDimensions() * glm::max(registrationPoint, glm::vec3(1.0f) - registrationPoint);
|
||||
|
||||
// there exists a sphere that contains maxExtents for all rotations
|
||||
float radius = glm::length(maxExtents);
|
||||
|
||||
// put a cube around the sphere
|
||||
// TODO? replace _maxAACube with _boundingSphereRadius
|
||||
glm::vec3 minimumCorner = centerOfRotation - glm::vec3(radius, radius, radius);
|
||||
glm::vec3 minimumCorner = (centerOfRotation + getWorldOrientation() * getPivot()) - glm::vec3(radius, radius, radius);
|
||||
_maxAACube = AACube(minimumCorner, radius * 2.0f);
|
||||
}
|
||||
} else {
|
||||
|
@ -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
|
||||
|
@ -1722,12 +1734,22 @@ float EntityItem::getRadius() const {
|
|||
return 0.5f * glm::length(getScaledDimensions());
|
||||
}
|
||||
|
||||
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);
|
||||
void EntityItem::adjustShapeInfoByRegistration(ShapeInfo& info, bool includePivot) const {
|
||||
glm::vec3 offset;
|
||||
glm::vec3 registrationPoint = getRegistrationPoint();
|
||||
if (registrationPoint != ENTITY_ITEM_DEFAULT_REGISTRATION_POINT) {
|
||||
offset += (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - registrationPoint) * getScaledDimensions();
|
||||
}
|
||||
|
||||
if (includePivot) {
|
||||
glm::vec3 pivot = getPivot();
|
||||
if (pivot != ENTITY_ITEM_ZERO_VEC3) {
|
||||
offset += pivot;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset != ENTITY_ITEM_ZERO_VEC3) {
|
||||
info.setOffset(offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1739,7 +1761,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 +1819,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 +2919,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 {
|
||||
|
|
|
@ -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);
|
||||
|
@ -403,7 +404,7 @@ public:
|
|||
// TODO: get rid of users of getRadius()...
|
||||
float getRadius() const;
|
||||
|
||||
virtual void adjustShapeInfoByRegistration(ShapeInfo& info) const;
|
||||
virtual void adjustShapeInfoByRegistration(ShapeInfo& info, bool includePivot = true) const;
|
||||
virtual bool contains(const glm::vec3& point) const;
|
||||
|
||||
virtual bool isReadyToComputeShape() const { return !isDead(); }
|
||||
|
|
|
@ -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
|
||||
|
@ -1002,6 +1003,9 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {string} blendshapeCoefficients - A JSON string of a map of blendshape names to values. Only stores set values.
|
||||
* When editing this property, only coefficients that you are editing will change; it will not explicitly reset other
|
||||
* coefficients.
|
||||
* @property {boolean} useOriginalPivot=false - If <code>false</code>, the model will be centered based on its content,
|
||||
* ignoring any offset in the model itself. If <code>true</code>, the model will respect its original offset. Currently,
|
||||
* only pivots relative to <code>{x: 0, y: 0, z: 0}</code> are supported.
|
||||
* @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the
|
||||
* model's original textures. Use a texture name from the <code>originalTextures</code> property to override that texture.
|
||||
* Only the texture names and URLs to be overridden need be specified; original textures are used where there are no
|
||||
|
@ -1733,6 +1737,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 +2147,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 +2440,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 +2797,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);
|
||||
|
@ -3231,6 +3239,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, properties.getRelayParentJoints());
|
||||
APPEND_ENTITY_PROPERTY(PROP_GROUP_CULLED, properties.getGroupCulled());
|
||||
APPEND_ENTITY_PROPERTY(PROP_BLENDSHAPE_COEFFICIENTS, properties.getBlendshapeCoefficients());
|
||||
APPEND_ENTITY_PROPERTY(PROP_USE_ORIGINAL_PIVOT, properties.getUseOriginalPivot());
|
||||
|
||||
_staticAnimation.setProperties(properties);
|
||||
_staticAnimation.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
|
@ -3720,6 +3729,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GROUP_CULLED, bool, setGroupCulled);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLENDSHAPE_COEFFICIENTS, QString, setBlendshapeCoefficients);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_USE_ORIGINAL_PIVOT, bool, setUseOriginalPivot);
|
||||
|
||||
properties.getAnimation().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
}
|
||||
|
@ -4129,6 +4139,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_relayParentJointsChanged = true;
|
||||
_groupCulledChanged = true;
|
||||
_blendshapeCoefficientsChanged = true;
|
||||
_useOriginalPivotChanged = true;
|
||||
_animation.markAllChanged();
|
||||
|
||||
// Light
|
||||
|
@ -4701,6 +4712,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (blendshapeCoefficientsChanged()) {
|
||||
out += "blendshapeCoefficients";
|
||||
}
|
||||
if (useOriginalPivotChanged()) {
|
||||
out += "useOriginalPivot";
|
||||
}
|
||||
getAnimation().listChangedProperties(out);
|
||||
|
||||
// Light
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -277,11 +274,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 +347,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 +370,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 +440,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 +458,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 +471,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 +492,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 +510,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 +523,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 +566,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 +581,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 +602,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 +633,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 +672,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);
|
||||
|
|
|
@ -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,25 @@ QVector<float> ModelEntityItem::getBlendshapeCoefficientVector() {
|
|||
return _blendshapeCoefficientsVector;
|
||||
});
|
||||
}
|
||||
|
||||
void ModelEntityItem::setUseOriginalPivot(bool value) {
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_useOriginalPivot != value) {
|
||||
_needsRenderUpdate = true;
|
||||
_useOriginalPivot = value;
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
markDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
locationChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool ModelEntityItem::getUseOriginalPivot() const {
|
||||
return resultWithReadLock<bool>([&] {
|
||||
return _useOriginalPivot;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ bool HFMModel::convexHullContains(const glm::vec3& point) const {
|
|||
auto checkEachPrimitive = [=](HFMMesh& mesh, QVector<int> indices, int primitiveSize) -> bool {
|
||||
// Check whether the point is "behind" all the primitives.
|
||||
// But first must transform from model-frame into mesh-frame
|
||||
glm::vec3 transformedPoint = glm::vec3(glm::inverse(mesh.modelTransform) * glm::vec4(point, 1.0f));
|
||||
glm::vec3 transformedPoint = glm::vec3(glm::inverse(offset * mesh.modelTransform) * glm::vec4(point, 1.0f));
|
||||
int verticesSize = mesh.vertices.size();
|
||||
for (int j = 0;
|
||||
j < indices.size() - 2; // -2 in case the vertices aren't the right size -- we access j + 2 below
|
||||
|
|
|
@ -283,6 +283,7 @@ enum class EntityVersion : PacketVersion {
|
|||
ZoneOcclusion,
|
||||
ModelBlendshapes,
|
||||
TransparentWeb,
|
||||
UseOriginalPivot,
|
||||
|
||||
// Add new versions above here
|
||||
NUM_PACKET_TYPE,
|
||||
|
|
|
@ -343,7 +343,11 @@ 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);
|
||||
glm::mat4 transRot = createMatFromQuatAndPos(_rotation, _translation);
|
||||
glm::mat4 modelToWorldMatrix = transRot;
|
||||
if (!_snapModelToRegistrationPoint) {
|
||||
modelToWorldMatrix = modelToWorldMatrix * glm::translate(getOriginalOffset());
|
||||
}
|
||||
glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);
|
||||
|
||||
Extents modelExtents = getMeshExtents(); // NOTE: unrotated
|
||||
|
@ -375,8 +379,12 @@ 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 = transRot;
|
||||
if (_snapModelToRegistrationPoint || _forceOffset) {
|
||||
meshToWorldMatrix = meshToWorldMatrix * (glm::scale(_scale) * glm::translate(_offset));
|
||||
} else {
|
||||
meshToWorldMatrix = meshToWorldMatrix * (glm::scale(_scale) * glm::translate(getNaturalDimensions() * (0.5f - _registrationPoint)));
|
||||
}
|
||||
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
|
||||
|
||||
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
|
||||
|
@ -498,7 +506,11 @@ 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);
|
||||
glm::mat4 transRot = createMatFromQuatAndPos(_rotation, _translation);
|
||||
glm::mat4 modelToWorldMatrix = transRot;
|
||||
if (!_snapModelToRegistrationPoint) {
|
||||
modelToWorldMatrix = modelToWorldMatrix * glm::translate(getOriginalOffset());
|
||||
}
|
||||
glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix);
|
||||
|
||||
Extents modelExtents = getMeshExtents(); // NOTE: unrotated
|
||||
|
@ -531,8 +543,12 @@ 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 = transRot;
|
||||
if (_snapModelToRegistrationPoint || _forceOffset) {
|
||||
meshToWorldMatrix = meshToWorldMatrix * (glm::scale(_scale) * glm::translate(_offset));
|
||||
} else {
|
||||
meshToWorldMatrix = meshToWorldMatrix * (glm::scale(_scale) * glm::translate(getNaturalDimensions() * (0.5f - _registrationPoint)));
|
||||
}
|
||||
glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix);
|
||||
|
||||
glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f));
|
||||
|
@ -1182,17 +1198,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 {
|
||||
|
@ -1417,6 +1424,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 + (0.5f * dimensions);
|
||||
glm::mat4 transform = glm::scale(_scale) * glm::translate(offset);
|
||||
return transform[3];
|
||||
}
|
||||
|
||||
|
||||
void Model::setUseDualQuaternionSkinning(bool value) {
|
||||
_useDualQuaternionSkinning = value;
|
||||
}
|
||||
|
@ -1437,7 +1453,8 @@ 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 || _forceOffset) ?
|
||||
glm::translate(_offset) : glm::translate(getNaturalDimensions() * (0.5f - _registrationPoint)));
|
||||
updateRig(deltaTime, parentTransform);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 _forceOffset { false };
|
||||
|
||||
std::vector<MeshState> _meshStates;
|
||||
|
||||
virtual void initJointStates();
|
||||
|
||||
void setScaleInternal(const glm::vec3& scale);
|
||||
void snapToRegistrationPoint();
|
||||
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
||||
|
||||
|
|
|
@ -601,6 +601,9 @@
|
|||
"groupCulled": {
|
||||
"tooltip": "If false, individual pieces of the entity may be culled by the render engine. If true, either the entire entity will be culled, or it won't at all."
|
||||
},
|
||||
"useOriginalPivot": {
|
||||
"tooltip": "If false, the model will be centered based on its content, ignoring any offset in the model itself. If true, the model will respect its original offset."
|
||||
},
|
||||
"webColor": {
|
||||
"tooltip": "The tint of the web entity."
|
||||
},
|
||||
|
|
|
@ -741,6 +741,7 @@ var toolBar = (function () {
|
|||
grabbable: result.grabbable
|
||||
},
|
||||
dynamic: dynamic,
|
||||
useOriginalPivot: result.useOriginalPivot
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -614,6 +614,11 @@ const GROUPS = [
|
|||
propertyID: "compoundShapeURL",
|
||||
hideIfCertified: true,
|
||||
},
|
||||
{
|
||||
label: "Use Original Pivot",
|
||||
type: "bool",
|
||||
propertyID: "useOriginalPivot",
|
||||
},
|
||||
{
|
||||
label: "Animation",
|
||||
type: "string",
|
||||
|
|
|
@ -120,6 +120,12 @@ Rectangle {
|
|||
height: 600
|
||||
spacing: 10
|
||||
|
||||
CheckBox {
|
||||
id: useOriginalPivot
|
||||
text: qsTr("Use Original Pivot")
|
||||
checked: true
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: grabbable
|
||||
text: qsTr("Grabbable")
|
||||
|
@ -219,7 +225,8 @@ Rectangle {
|
|||
url: modelURL.text,
|
||||
dynamic: dynamic.checked,
|
||||
collisionShapeIndex: collisionType.currentIndex,
|
||||
grabbable: grabbable.checked
|
||||
grabbable: grabbable.checked,
|
||||
useOriginalPivot: useOriginalPivot.checked
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue