/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "common/textconsole.h"
#include "tetraedge/tetraedge.h"
#include "tetraedge/game/application.h"
#include "tetraedge/te/te_layout.h"
#include "tetraedge/te/te_i_3d_object2.h"
namespace Tetraedge {
TeLayout::TeLayout() : Te3DObject2(), _autoz(true), _needZUpdate(true), _updatingZ(false),
_needZSizeUpdate(true), _updatingZSize(false), _sizeChanged(true), _updatingSize(false),
_positionChanged(true), _updatingPosition(false), _worldMatrixChanged(true),
_updatingWorldMatrix(false), _drawMode(TeILayout::DrawMode0),
_sizeType(CoordinatesType::ABSOLUTE), _userSize(1.0f, 1.0f, 1.0f),
_anchor(0.5f, 0.5f, 0.5f), _ratio(1.0f), _safeAreaRatio(1.333333f),
_ratioMode(RATIO_MODE_NONE), _positionType(CoordinatesType::RELATIVE_TO_PARENT)
{
_userPosition = _position = TeVector3f32(0.5f, 0.5f, 0.5f);
_size = TeVector3f32(1.0f, 1.0f, 1.0f);
_onChildSizeChangedCallback.reset(
new TeCallback0Param(this, &TeLayout::onChildSizeChanged));
_onParentSizeChangedCallback.reset(
new TeCallback0Param(this, &TeLayout::onParentSizeChanged));
_onParentWorldTransformationMatrixChangedCallback.reset(
new TeCallback0Param(this, &TeLayout::onParentWorldTransformationMatrixChanged));
updateSize();
updateMesh();
}
TeLayout::~TeLayout() {
if (parent() && _onParentSizeChangedCallback) {
parent()->onSizeChanged().remove(_onParentSizeChangedCallback);
_onParentSizeChangedCallback.reset();
parent()->onWorldTransformationMatrixChanged().remove(_onParentWorldTransformationMatrixChangedCallback);
_onParentWorldTransformationMatrixChangedCallback.reset();
}
if (_onChildSizeChangedCallback) {
for (auto &child : childList()) {
child->onSizeChanged().remove(_onChildSizeChangedCallback);
}
}
}
void TeLayout::addChild(Te3DObject2 *child) {
Te3DObject2::addChild(child);
if (_onChildSizeChangedCallback) {
child->onSizeChanged().push_back(_onChildSizeChangedCallback);
}
_needZSizeUpdate = true;
_needZUpdate = true;
updateZSize();
updateZ();
}
void TeLayout::addChildBefore(Te3DObject2 *child, const Te3DObject2 *ref) {
Te3DObject2::addChildBefore(child, ref);
if (_onChildSizeChangedCallback) {
child->onSizeChanged().push_back(_onChildSizeChangedCallback);
}
_needZSizeUpdate = true;
_needZUpdate = true;
updateZSize();
updateZ();
}
void TeLayout::removeChild(Te3DObject2 *child) {
if (_onChildSizeChangedCallback) {
child->onSizeChanged().remove(_onChildSizeChangedCallback);
}
Te3DObject2::removeChild(child);
_needZSizeUpdate = true;
_needZUpdate = true;
updateZSize();
updateZ();
}
const TeVector3f32 &TeLayout::anchor() {
return _anchor;
}
void TeLayout::disableAutoZ() {
_autoz = false;
}
void TeLayout::enableAutoZ() {
_autoz = true;
}
bool TeLayout::isAutoZEnabled() {
return _autoz;
}
void TeLayout::draw() {
if (visible() && worldVisible()) {
// Ensure world transform is up-to-date.
worldTransformationMatrix();
for (auto &child : childList()) {
child->draw();
}
}
}
static const float EPSILON = 1.192093e-07f;
bool TeLayout::isMouseIn(const TeVector2s32 &mouseloc) {
const TeVector3f32 transformedPos = transformMousePosition(mouseloc);
const TeVector3f32 halfSize = size() / 2.0;
return ( (transformedPos.x() >= -halfSize.x() - EPSILON)
&& (transformedPos.x() < halfSize.x() + EPSILON)
&& (transformedPos.y() >= -halfSize.y() - EPSILON)
&& (transformedPos.y() < halfSize.y() + EPSILON));
}
TeILayout::DrawMode TeLayout::mode() {
return _drawMode;
}
bool TeLayout::onChildSizeChanged() {
_needZSizeUpdate = true;
_needZUpdate = true;
updateSize();
if (!_updatingZSize)
updateZSize();
return false;
}
bool TeLayout::onParentSizeChanged() {
_sizeChanged = true;
_positionChanged = true;
_worldMatrixChanged = true;
return false;
}
bool TeLayout::onParentWorldTransformationMatrixChanged() {
_worldMatrixChanged = true;
return false;
}
TeVector3f32 TeLayout::position() {
updateZ();
updatePosition();
return _position;
}
TeLayout::CoordinatesType TeLayout::positionType() const {
return _positionType;
}
float TeLayout::ratio() const {
return _ratio;
}
TeLayout::RatioMode TeLayout::ratioMode() const {
return _ratioMode;
}
float TeLayout::safeAreaRatio() const {
return _safeAreaRatio;
}
void TeLayout::setAnchor(const TeVector3f32 &anchor) {
if (_anchor != anchor) {
_anchor = anchor;
_positionChanged = true;
_worldMatrixChanged = true;
updatePosition();
}
}
void TeLayout::setMode(DrawMode mode) {
_drawMode = mode;
}
void TeLayout::setParent(Te3DObject2 *parent) {
assert(parent != this);
Te3DObject2 *oldParent = Te3DObject2::parent();
if (oldParent) {
if (_onParentSizeChangedCallback)
oldParent->onSizeChanged().remove(_onParentSizeChangedCallback);
if (_onParentWorldTransformationMatrixChangedCallback)
oldParent->onWorldTransformationMatrixChanged().remove(_onParentWorldTransformationMatrixChangedCallback);
}
//
TeLayout &mainWindowLayout = g_engine->getApplication()->getMainWindow();
mainWindowLayout.onSizeChanged().remove(_onMainWindowChangedCallback);
Te3DObject2::setParent(parent);
if (parent) {
if (_onParentSizeChangedCallback)
parent->onSizeChanged().push_back(_onParentSizeChangedCallback);
if (_onParentWorldTransformationMatrixChangedCallback)
parent->onWorldTransformationMatrixChanged().push_back(_onParentWorldTransformationMatrixChangedCallback);
if (_onMainWindowChangedCallback)
mainWindowLayout.onSizeChanged().push_back(_onMainWindowChangedCallback);
}
_needZUpdate = true;
_sizeChanged = true;
_positionChanged = true;
_worldMatrixChanged = true;
updateSize();
}
void TeLayout::setPosition(const TeVector3f32 &pos) {
TeVector3f32 pos3d(pos.x(), pos.y(), _userPosition.z());
if (_userPosition != pos3d) {
_userPosition.x() = pos.x();
_userPosition.y() = pos.y();
_positionChanged = true;
_worldMatrixChanged = true;
}
}
void TeLayout::setPositionType(CoordinatesType newtype) {
if (_positionType != newtype) {
_positionType = newtype;
_positionChanged = true;
_worldMatrixChanged = true;
}
}
void TeLayout::setRatio(float val) {
if (_ratio != val) {
_ratio = val;
_sizeChanged = true;
_worldMatrixChanged = true;
}
}
void TeLayout::setRatioMode(RatioMode mode) {
if (_ratioMode != mode) {
_ratioMode = mode;
_sizeChanged = true;
_worldMatrixChanged = true;
}
}
void TeLayout::setRotation(const TeQuaternion &rot) {
if (rot != _rotation) {
Te3DObject2::setRotation(rot);
_worldMatrixChanged = true;
}
}
void TeLayout::setSafeAreaRatio(float ratio) {
if (_safeAreaRatio != ratio) {
_safeAreaRatio = ratio;
_sizeChanged = true;
_worldMatrixChanged = true;
}
}
void TeLayout::setScale(const TeVector3f32 &scale) {
Te3DObject2::setScale(scale);
_worldMatrixChanged = true;
}
void TeLayout::setSize(const TeVector3f32 &size) {
const TeVector3f32 size3d(size.x(), size.y(), _userSize.z());
if (_userSize != size3d) {
_userSize.x() = size.x();
_userSize.y() = size.y();
_sizeChanged = true;
_positionChanged = true;
_worldMatrixChanged = true;
}
}
void TeLayout::setSizeType(CoordinatesType coordtype) {
assert(coordtype == RELATIVE_TO_PARENT || coordtype == ABSOLUTE);
if (_sizeType != coordtype) {
_sizeType = coordtype;
_sizeChanged = true;
_worldMatrixChanged = true;
}
}
void TeLayout::setZPosition(float zpos) {
if (_userPosition.z() != zpos) {
_userPosition.z() = zpos;
_positionChanged = true;
_worldMatrixChanged = true;
}
}
TeVector3f32 TeLayout::size() {
updateSize();
updateZSize();
return _size;
}
TeLayout::CoordinatesType TeLayout::sizeType() const {
return _sizeType;
}
TeVector3f32 TeLayout::transformMousePosition(const TeVector2s32 &mousepos) {
TeVector3f32 relativeMouse(mousepos);
relativeMouse.x() -= g_system->getWidth() / 2;
relativeMouse.y() -= g_system->getHeight() / 2;
TeMatrix4x4 transform = worldTransformationMatrix();
transform.inverse();
TeVector3f32 transformpos = transform * relativeMouse;
//debug("transformMousePosition: %d %d -> %f %f", mousepos._x, mousepos._y, transformpos.x(), transformpos.y());
return transformpos;
}
void TeLayout::updatePosition() {
if (!_positionChanged)
return;
_positionChanged = false;
_updatingPosition = true;
TeVector3f32 oldpos = _position;
Te3DObject2 *parentObj = parent();
static const TeVector3f32 midPoint(0.5f, 0.5f, 0.5f);
if (_positionType == RELATIVE_TO_PARENT && parentObj) {
const TeVector3f32 offsetUserPos = _userPosition - midPoint;
const TeVector3f32 parentSize(parentObj->xSize(), parentObj->ySize(), 0.0);
const TeVector3f32 offsetAnchor = midPoint - _anchor;
const TeVector3f32 thisSize(xSize(), ySize(), 0.0);
const TeVector3f32 newpos = (offsetUserPos * parentSize) + (offsetAnchor * thisSize);
_position = newpos;
} else if (_positionType == RELATIVE_TO_PARENT && !parentObj) {
// Not in original, but no parent -> set midpoint.
const TeVector3f32 offsetAnchor = midPoint - _anchor;
const TeVector3f32 thisSize(xSize(), ySize(), 0.0);
_position = offsetAnchor * thisSize;
} else if (_positionType == ABSOLUTE) {
_position = _userPosition;
}
_position.z() = _userPosition.z();
_worldMatrixChanged = true;
_updatingPosition = false;
if (_position != oldpos) {
onPositionChanged().call();
}
}
void TeLayout::updateSize() {
if (!_sizeChanged)
return;
_sizeChanged = false;
_updatingSize = true;
const TeVector3f32 oldSize = _size;
if (_sizeType == ABSOLUTE) {
TeVector3f32 newSize = _userSize;
_size.x() = abs(newSize.x());
_size.y() = abs(newSize.y());
// don't set Z val.
} else if (_sizeType == RELATIVE_TO_PARENT) {
Te3DObject2 *parentObj = parent();
if (parentObj) {
const TeVector3f32 parentSize(parentObj->xSize(), parentObj->ySize(), 0.0);
TeVector3f32 newSize = _userSize * parentSize;
if (newSize.x() > 0.0f && newSize.y() > 0.0f && _ratio > 0.0f && _safeAreaRatio > 0.0f) {
float newSizeRatio = newSize.x() / newSize.y();
if (_ratioMode == RATIO_MODE_PAN_SCAN) {
if (_safeAreaRatio <= newSizeRatio) {
newSize.x() = _ratio * newSize.y();
} else {
newSize.x() =
(1.0f - (_safeAreaRatio - newSizeRatio) / _safeAreaRatio) *
_ratio * newSize.y();
}
} else if (_ratioMode == RATIO_MODE_LETTERBOX) {
if (_ratio < newSizeRatio)
newSize.x() = _ratio * newSize.y();
else
newSize.y() = newSize.x() / _ratio;
}
}
_size.x() = newSize.x();
_size.y() = newSize.y();
} else {
_size.x() = 0.0f;
_size.y() = 0.0f;
}
}
_updatingSize = false;
// TODO: check this, is it the right flag to set?
_positionChanged = true;
updateMesh();
if (_size != oldSize) {
onSizeChanged().call();
}
}
void TeLayout::updateWorldMatrix() {
if (!_worldMatrixChanged)
return;
_worldMatrixChanged = false;
_updatingWorldMatrix = true;
const TeMatrix4x4 oldMatrix = _worldMatrixCache;
_worldMatrixCache = Te3DObject2::worldTransformationMatrix();
_updatingWorldMatrix = false;
if (_worldMatrixCache != oldMatrix) {
onWorldTransformationMatrixChanged().call();
}
}
void TeLayout::updateZ() {
if (!_needZUpdate || !_autoz)
return;
_needZUpdate = false;
_updatingZ = true;
float ztotal = 0.1f;
for (auto &child : childList()) {
child->setZPosition(ztotal);
ztotal += child->zSize();
}
_updatingZ = false;
}
void TeLayout::updateZSize() {
if (!_needZSizeUpdate)
return;
_needZSizeUpdate = false;
_updatingZSize = true;
const TeVector3f32 oldSize = _size;
_size.z() = 0.1f;
for (auto &child : childList()) {
_size.z() += child->zSize();
}
_positionChanged = true;
_updatingZSize = false;
if (_size != oldSize) {
onSizeChanged().call();
}
}
TeVector3f32 TeLayout::userPosition() const {
return _userPosition;
}
TeVector3f32 TeLayout::userSize() {
updateZ();
return _userSize;
}
TeVector3f32 TeLayout::worldPosition() {
if (!parent()) {
return position();
} else {
return parent()->worldPosition() + position();
}
}
TeMatrix4x4 TeLayout::worldTransformationMatrix() {
updateZ();
updatePosition();
updateWorldMatrix();
return _worldMatrixCache;
}
bool TeLayout::worldVisible() {
bool visible = Te3DObject2::visible();
if (visible && parent()) {
return parent()->worldVisible();
}
return visible;
}
float TeLayout::xSize() {
updateSize();
return _size.x();
}
float TeLayout::ySize() {
updateSize();
return _size.y();
}
float TeLayout::zSize() {
updateZSize();
return _size.z();
}
} // end namespace Tetraedge