Improve transforms for panels and attachables.

Add offsetScale to PanelAttachable for scaling all the contents of a
panel properly.  Also reduce the number of transform operations per
second while rendering a PanelAttachable Overlay.
This commit is contained in:
Zander Otavka 2015-08-06 21:46:36 -07:00
parent 6e8c154c42
commit 0b4be8fca2
10 changed files with 134 additions and 98 deletions

View file

@ -25,8 +25,12 @@ var mainPanel = new OverlayPanel({
offsetRotation: { w: 0, x: 0, y: 1, z: 0 } offsetRotation: { w: 0, x: 0, y: 1, z: 0 }
}); });
print(JSON.stringify(mainPanel.offsetPosition));
print(JSON.stringify(mainPanel.offsetRotation));
var bluePanel = mainPanel.addChild(new OverlayPanel ({ var bluePanel = mainPanel.addChild(new OverlayPanel ({
offsetPosition: { x: 0.1, y: 0.1, z: 0.2 } offsetPosition: { x: 0.1, y: 0.1, z: 0.2 },
offsetScale: 0.5
})); }));
var mainPanelBackground = new Image3DOverlay({ var mainPanelBackground = new Image3DOverlay({
@ -46,10 +50,6 @@ var mainPanelBackground = new Image3DOverlay({
}); });
var bluePanelBackground = mainPanelBackground.clone(); var bluePanelBackground = mainPanelBackground.clone();
bluePanelBackground.dimensions = {
x: 0.3,
y: 0.3
};
mainPanel.addChild(mainPanelBackground); mainPanel.addChild(mainPanelBackground);
bluePanel.addChild(bluePanelBackground); bluePanel.addChild(bluePanelBackground);
@ -117,15 +117,15 @@ var redDot2 = mainPanel.addChild(new Image3DOverlay({
var blueSquare = bluePanel.addChild(new Image3DOverlay({ var blueSquare = bluePanel.addChild(new Image3DOverlay({
url: BLUE_SQUARE_IMAGE_URL, url: BLUE_SQUARE_IMAGE_URL,
dimensions: { dimensions: {
x: 0.1, x: 0.15,
y: 0.1, y: 0.15,
}, },
isFacingAvatar: false, isFacingAvatar: false,
alpha: 1.0, alpha: 1.0,
ignoreRayIntersection: false, ignoreRayIntersection: false,
offsetPosition: { offsetPosition: {
x: 0.055, x: 0.09,
y: -0.055, y: -0.09,
z: 0 z: 0
} }
})); }));
@ -133,23 +133,23 @@ var blueSquare = bluePanel.addChild(new Image3DOverlay({
var blueSquare2 = bluePanel.addChild(new Image3DOverlay({ var blueSquare2 = bluePanel.addChild(new Image3DOverlay({
url: BLUE_SQUARE_IMAGE_URL, url: BLUE_SQUARE_IMAGE_URL,
dimensions: { dimensions: {
x: 0.1, x: 0.15,
y: 0.1, y: 0.15,
}, },
isFacingAvatar: false, isFacingAvatar: false,
alpha: 1.0, alpha: 1.0,
ignoreRayIntersection: false, ignoreRayIntersection: false,
offsetPosition: { offsetPosition: {
x: 0.055, x: 0.09,
y: 0.055, y: 0.09,
z: 0 z: 0
} }
})); }));
var blueSquare3 = blueSquare2.clone(); var blueSquare3 = blueSquare2.clone();
blueSquare3.offsetPosition = { blueSquare3.offsetPosition = {
x: -0.055, x: -0.09,
y: 0.055, y: 0.09,
z: 0 z: 0
}; };

View file

@ -234,7 +234,7 @@
// Supports multiple inheritance of properties. Just `concat` them onto the end of the // Supports multiple inheritance of properties. Just `concat` them onto the end of the
// properties list. // properties list.
var PANEL_ATTACHABLE_FIELDS = ["offsetPosition", "offsetRotation"]; var PANEL_ATTACHABLE_FIELDS = ["offsetPosition", "offsetRotation", "offsetScale"];
Overlay = (function() { Overlay = (function() {
var that = function(type, params) { var that = function(type, params) {
@ -383,8 +383,8 @@
that.prototype.constructor = that; that.prototype.constructor = that;
[ [
"position", "positionBinding", "rotation", "rotationBinding", "position", "positionBinding", "rotation", "rotationBinding", "scale",
"offsetPosition", "offsetRotation", "visible" "offsetPosition", "offsetRotation", "offsetScale", "visible"
].forEach(function(prop) { ].forEach(function(prop) {
Object.defineProperty(that.prototype, prop, { Object.defineProperty(that.prototype, prop, {
get: function() { get: function() {

View file

@ -46,11 +46,13 @@ QScriptValue Billboard3DOverlay::getProperty(const QString &property) {
return Planar3DOverlay::getProperty(property); return Planar3DOverlay::getProperty(property);
} }
void Billboard3DOverlay::applyTransformTo(Transform& transform) { void Billboard3DOverlay::applyTransformTo(Transform& transform, bool force) {
PanelAttachable::applyTransformTo(transform); if (force || usecTimestampNow() > _transformExpiry) {
if (_isFacingAvatar) { PanelAttachable::applyTransformTo(transform, true);
glm::quat rotation = Application::getInstance()->getCamera()->getOrientation(); if (_isFacingAvatar) {
glm::quat rotation = Application::getInstance()->getCamera()->getOrientation();
transform.setRotation(rotation); transform.setRotation(rotation);
}
} }
} }

View file

@ -29,7 +29,7 @@ public:
virtual QScriptValue getProperty(const QString& property); virtual QScriptValue getProperty(const QString& property);
protected: protected:
virtual void applyTransformTo(Transform& transform); virtual void applyTransformTo(Transform& transform, bool force = false);
private: private:
bool _isFacingAvatar; bool _isFacingAvatar;

View file

@ -87,7 +87,7 @@ void Image3DOverlay::render(RenderArgs* args) {
xColor color = getColor(); xColor color = getColor();
float alpha = getAlpha(); float alpha = getAlpha();
applyTransformTo(_transform); applyTransformTo(_transform, true);
Transform transform = _transform; Transform transform = _transform;
transform.postScale(glm::vec3(getDimensions(), 1.0f)); transform.postScale(glm::vec3(getDimensions(), 1.0f));
transform.postRotate(glm::angleAxis(glm::pi<float>(), IDENTITY_UP)); transform.postRotate(glm::angleAxis(glm::pi<float>(), IDENTITY_UP));
@ -170,7 +170,7 @@ bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec
float& distance, BoxFace& face) { float& distance, BoxFace& face) {
if (_texture && _texture->isLoaded()) { if (_texture && _texture->isLoaded()) {
// Make sure position and rotation is updated. // Make sure position and rotation is updated.
applyTransformTo(_transform); applyTransformTo(_transform, true);
// Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale.
bool isNull = _fromImage.isNull(); bool isNull = _fromImage.isNull();

View file

@ -80,6 +80,9 @@ QScriptValue OverlayPanel::getProperty(const QString &property) {
"MyAvatar" : "", "MyAvatar" : "",
_rotationBindEntity)); _rotationBindEntity));
} }
if (property == "scale") {
return vec3toScriptValue(_scriptEngine, getScale());
}
if (property == "visible") { if (property == "visible") {
return getVisible(); return getVisible();
} }
@ -102,7 +105,9 @@ void OverlayPanel::setProperties(const QScriptValue &properties) {
position.property("x").isValid() && position.property("x").isValid() &&
position.property("y").isValid() && position.property("y").isValid() &&
position.property("z").isValid()) { position.property("z").isValid()) {
vec3FromScriptValue(position, _position); glm::vec3 newPosition;
vec3FromScriptValue(position, newPosition);
setPosition(newPosition);
} }
QScriptValue positionBinding = properties.property("positionBinding"); QScriptValue positionBinding = properties.property("positionBinding");
@ -119,7 +124,9 @@ void OverlayPanel::setProperties(const QScriptValue &properties) {
rotation.property("y").isValid() && rotation.property("y").isValid() &&
rotation.property("z").isValid() && rotation.property("z").isValid() &&
rotation.property("w").isValid()) { rotation.property("w").isValid()) {
quatFromScriptValue(rotation, _rotation); glm::quat newRotation;
quatFromScriptValue(rotation, newRotation);
setRotation(newRotation);
} }
QScriptValue rotationBinding = properties.property("rotationBinding"); QScriptValue rotationBinding = properties.property("rotationBinding");
@ -134,38 +141,49 @@ void OverlayPanel::setProperties(const QScriptValue &properties) {
if (visible.isValid()) { if (visible.isValid()) {
setVisible(visible.toVariant().toBool()); setVisible(visible.toVariant().toBool());
} }
}
void OverlayPanel::applyTransformTo(Transform& transform) { QScriptValue scale = properties.property("scale");
if (getParentPanel()) { if (scale.isValid()) {
getParentPanel()->applyTransformTo(transform); if (scale.property("x").isValid() &&
} else { scale.property("y").isValid() &&
transform.setTranslation(getComputedPosition()); scale.property("z").isValid()) {
transform.setRotation(getComputedRotation()); glm::vec3 newScale;
vec3FromScriptValue(scale, newScale);
setScale(newScale);
} else {
setScale(scale.toVariant().toFloat());
}
} }
_position = transform.getTranslation();
_rotation = transform.getRotation();
transform.postTranslate(getOffsetPosition());
transform.postRotate(getOffsetRotation());
} }
glm::vec3 OverlayPanel::getComputedPosition() const { void OverlayPanel::applyTransformTo(Transform& transform, bool force) {
if (force || usecTimestampNow() > _transformExpiry) {
PanelAttachable::applyTransformTo(transform, true);
if (!getParentPanel()) {
updateTransform();
transform.setTranslation(getPosition());
transform.setRotation(getRotation());
transform.setScale(getScale());
transform.postTranslate(getOffsetPosition());
transform.postRotate(getOffsetRotation());
transform.postScale(getOffsetScale());
}
}
}
void OverlayPanel::updateTransform() {
if (_positionBindMyAvatar) { if (_positionBindMyAvatar) {
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition(); setPosition(DependencyManager::get<AvatarManager>()->getMyAvatar()->getPosition());
} else if (!_positionBindEntity.isNull()) { } else if (!_positionBindEntity.isNull()) {
return DependencyManager::get<EntityScriptingInterface>()->getEntityTree()-> setPosition(DependencyManager::get<EntityScriptingInterface>()->getEntityTree()->
findEntityByID(_positionBindEntity)->getPosition(); findEntityByID(_positionBindEntity)->getPosition());
} }
return getPosition();
}
glm::quat OverlayPanel::getComputedRotation() const {
if (_rotationBindMyAvatar) { if (_rotationBindMyAvatar) {
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getOrientation() * setRotation(DependencyManager::get<AvatarManager>()->getMyAvatar()->getOrientation() *
glm::angleAxis(glm::pi<float>(), IDENTITY_UP); glm::angleAxis(glm::pi<float>(), IDENTITY_UP));
} else if (!_rotationBindEntity.isNull()) { } else if (!_rotationBindEntity.isNull()) {
return DependencyManager::get<EntityScriptingInterface>()->getEntityTree()-> setRotation(DependencyManager::get<EntityScriptingInterface>()->getEntityTree()->
findEntityByID(_rotationBindEntity)->getRotation(); findEntityByID(_rotationBindEntity)->getRotation());
} }
return getRotation();
} }

View file

@ -42,13 +42,16 @@ public:
void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; }
// getters // getters
glm::vec3 getPosition() const { return _position; } glm::vec3 getPosition() const { return _transform.getTranslation(); }
glm::quat getRotation() const { return _rotation; } glm::quat getRotation() const { return _transform.getRotation(); }
glm::vec3 getScale() const { return _transform.getScale(); }
bool getVisible() const { return _visible; } bool getVisible() const { return _visible; }
// setters // setters
void setPosition(const glm::vec3& position) { _position = position; } void setPosition(const glm::vec3& position) { _transform.setTranslation(position); }
void setRotation(const glm::quat& rotation) { _rotation = rotation; } void setRotation(const glm::quat& rotation) { _transform.setRotation(rotation); }
void setScale(float scale) { _transform.setScale(scale); }
void setScale(const glm::vec3& scale) { _transform.setScale(scale); }
void setVisible(bool visible) { _visible = visible; } void setVisible(bool visible) { _visible = visible; }
const QList<unsigned int>& getChildren() { return _children; } const QList<unsigned int>& getChildren() { return _children; }
@ -59,14 +62,12 @@ public:
QScriptValue getProperty(const QString& property); QScriptValue getProperty(const QString& property);
void setProperties(const QScriptValue& properties); void setProperties(const QScriptValue& properties);
virtual void applyTransformTo(Transform& transform); virtual void applyTransformTo(Transform& transform, bool force = false);
private: private:
glm::vec3 getComputedPosition() const; void updateTransform();
glm::quat getComputedRotation() const;
glm::vec3 _position = {0, 0, 0}; Transform _transform;
glm::quat _rotation = {1, 0, 0, 0};
bool _positionBindMyAvatar = false; bool _positionBindMyAvatar = false;
QUuid _positionBindEntity; QUuid _positionBindEntity;

View file

@ -23,11 +23,16 @@ bool PanelAttachable::getParentVisible() const {
} }
} }
void PanelAttachable::applyTransformTo(Transform& transform) { void PanelAttachable::applyTransformTo(Transform& transform, bool force) {
if (getParentPanel()) { if (force || usecTimestampNow() > _transformExpiry) {
getParentPanel()->applyTransformTo(transform); const quint64 TRANSFORM_UPDATE_PERIOD = 50000;
transform.postTranslate(getOffsetPosition()); _transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD;
transform.postRotate(getOffsetRotation()); if (getParentPanel()) {
getParentPanel()->applyTransformTo(transform, true);
transform.postTranslate(getOffsetPosition());
transform.postRotate(getOffsetRotation());
transform.postScale(getOffsetScale());
}
} }
} }
@ -38,39 +43,44 @@ QScriptValue PanelAttachable::getProperty(QScriptEngine* scriptEngine, const QSt
if (property == "offsetRotation") { if (property == "offsetRotation") {
return quatToScriptValue(scriptEngine, getOffsetRotation()); return quatToScriptValue(scriptEngine, getOffsetRotation());
} }
if (property == "offsetScale") {
return vec3toScriptValue(scriptEngine, getOffsetScale());
}
return QScriptValue(); return QScriptValue();
} }
void PanelAttachable::setProperties(const QScriptValue &properties) { void PanelAttachable::setProperties(const QScriptValue &properties) {
QScriptValue offsetPosition = properties.property("offsetPosition"); QScriptValue offsetPosition = properties.property("offsetPosition");
if (offsetPosition.isValid()) { if (offsetPosition.isValid() &&
QScriptValue x = offsetPosition.property("x"); offsetPosition.property("x").isValid() &&
QScriptValue y = offsetPosition.property("y"); offsetPosition.property("y").isValid() &&
QScriptValue z = offsetPosition.property("z"); offsetPosition.property("z").isValid()) {
glm::vec3 newPosition;
if (x.isValid() && y.isValid() && z.isValid()) { vec3FromScriptValue(offsetPosition, newPosition);
glm::vec3 newPosition; setOffsetPosition(newPosition);
newPosition.x = x.toVariant().toFloat();
newPosition.y = y.toVariant().toFloat();
newPosition.z = z.toVariant().toFloat();
setOffsetPosition(newPosition);
}
} }
QScriptValue offsetRotation = properties.property("offsetRotation"); QScriptValue offsetRotation = properties.property("offsetRotation");
if (offsetRotation.isValid()) { if (offsetRotation.isValid() &&
QScriptValue x = offsetRotation.property("x"); offsetRotation.property("x").isValid() &&
QScriptValue y = offsetRotation.property("y"); offsetRotation.property("y").isValid() &&
QScriptValue z = offsetRotation.property("z"); offsetRotation.property("z").isValid() &&
QScriptValue w = offsetRotation.property("w"); offsetRotation.property("w").isValid()) {
glm::quat newRotation;
quatFromScriptValue(offsetRotation, newRotation);
setOffsetRotation(newRotation);
}
if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { QScriptValue offsetScale = properties.property("offsetScale");
glm::quat newRotation; if (offsetScale.isValid()) {
newRotation.x = x.toVariant().toFloat(); if (offsetScale.property("x").isValid() &&
newRotation.y = y.toVariant().toFloat(); offsetScale.property("y").isValid() &&
newRotation.z = z.toVariant().toFloat(); offsetScale.property("z").isValid()) {
newRotation.w = w.toVariant().toFloat(); glm::vec3 newScale;
setOffsetRotation(newRotation); vec3FromScriptValue(offsetScale, newScale);
setOffsetScale(newScale);
} else {
setOffsetScale(offsetScale.toVariant().toFloat());
} }
} }
} }

View file

@ -24,25 +24,30 @@ class OverlayPanel;
class PanelAttachable { class PanelAttachable {
public: public:
// getters
std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; } std::shared_ptr<OverlayPanel> getParentPanel() const { return _parentPanel; }
virtual glm::vec3 getOffsetPosition() const { return _offsetPosition; } glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); }
virtual glm::quat getOffsetRotation() const { return _offsetRotation; } glm::quat getOffsetRotation() const { return _offset.getRotation(); }
glm::vec3 getOffsetScale() const { return _offset.getScale(); }
bool getParentVisible() const; bool getParentVisible() const;
// setters
void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; } void setParentPanel(std::shared_ptr<OverlayPanel> panel) { _parentPanel = panel; }
virtual void setOffsetPosition(const glm::vec3& position) { _offsetPosition = position; } void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); }
virtual void setOffsetRotation(const glm::quat& rotation) { _offsetRotation = rotation; } void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); }
void setOffsetScale(float scale) { _offset.setScale(scale); }
void setOffsetScale(const glm::vec3& scale) { _offset.setScale(scale); }
QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property); QScriptValue getProperty(QScriptEngine* scriptEngine, const QString& property);
void setProperties(const QScriptValue& properties); void setProperties(const QScriptValue& properties);
protected: protected:
virtual void applyTransformTo(Transform& transform); virtual void applyTransformTo(Transform& transform, bool force = false);
quint64 _transformExpiry = 0;
private: private:
std::shared_ptr<OverlayPanel> _parentPanel = nullptr; std::shared_ptr<OverlayPanel> _parentPanel = nullptr;
glm::vec3 _offsetPosition = {0, 0, 0}; Transform _offset;
glm::quat _offsetRotation = {1, 0, 0, 0};
}; };
#endif // hifi_PanelAttachable_h #endif // hifi_PanelAttachable_h

View file

@ -86,7 +86,7 @@ void Text3DOverlay::render(RenderArgs* args) {
Q_ASSERT(args->_batch); Q_ASSERT(args->_batch);
auto& batch = *args->_batch; auto& batch = *args->_batch;
applyTransformTo(_transform); applyTransformTo(_transform, true);
batch.setModelTransform(_transform); batch.setModelTransform(_transform);
const float MAX_COLOR = 255.0f; const float MAX_COLOR = 255.0f;
@ -212,6 +212,6 @@ QSizeF Text3DOverlay::textSize(const QString& text) const {
} }
bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance, BoxFace &face) { bool Text3DOverlay::findRayIntersection(const glm::vec3 &origin, const glm::vec3 &direction, float &distance, BoxFace &face) {
applyTransformTo(_transform); applyTransformTo(_transform, true);
return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face); return Billboard3DOverlay::findRayIntersection(origin, direction, distance, face);
} }