342 lines
9.2 KiB
C++
342 lines
9.2 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "core/item.h"
|
|
#include "geometry/bounding_box.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::Core;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
Matrix4 Item::GetMatrixNoPivot()
|
|
{
|
|
Matrix4 p = GetPivot();
|
|
SetPivot(Matrix4::IdentityMatrix());
|
|
Matrix4 m = GetMatrix();
|
|
SetPivot(p);
|
|
return m;
|
|
}
|
|
void Item::ComputeLocalMatrix()
|
|
{
|
|
if (!item_flags.IsSet(ItemFlagLocalMatrixDirty))
|
|
return;
|
|
|
|
if (!item_flags.IsSet(ItemFlagHasTarget) && item_flags.IsSet(ItemFlagRotationMatrixDirty))
|
|
{
|
|
rotation_matrix = Matrix3::FromEuler(rotation.x, rotation.y, rotation.z, rorder);
|
|
item_flags.Remove(ItemFlagRotationMatrixDirty);
|
|
}
|
|
|
|
local_matrix =
|
|
(
|
|
Matrix4::TranslationMatrix(position) *
|
|
Matrix4::FromMatrix3(rotation_matrix) *
|
|
Matrix4::ScaleMatrix(scale)
|
|
)
|
|
*
|
|
pivot_matrix;
|
|
|
|
item_flags.Remove(ItemFlagLocalMatrixDirty);
|
|
}
|
|
void Item::ComputeMatrix()
|
|
{
|
|
bool update_local = item_flags.IsSet(ItemFlagLocalMatrixDirty),
|
|
update_world = item_flags.IsSet(ItemFlagWorldMatrixDirty);
|
|
|
|
if (update_local)
|
|
ComputeLocalMatrix();
|
|
|
|
if (!parent)
|
|
{
|
|
if (update_world)
|
|
matrix = local_matrix;
|
|
}
|
|
else if (update_world)
|
|
{
|
|
matrix = parent->GetMatrix() * local_matrix;
|
|
|
|
if (item_flags.IsSet(ItemFlagInheritPositionOnly))
|
|
{
|
|
Matrix4 m(Matrix4::FromMatrix3(rotation_matrix));
|
|
m.SetRow(3, matrix.GetRow(3));
|
|
matrix = m;
|
|
}
|
|
}
|
|
|
|
/// Handle item target and scale.
|
|
if (update_local || update_world)
|
|
if (item_flags.IsSet(ItemFlagHasTarget))
|
|
{
|
|
matrix.Decompose(NULL, NULL, &rotation_matrix);
|
|
Matrix3 tgtm = Matrix3::FromOrthonormalBasis(target - matrix.GetRow(3));
|
|
matrix = matrix * Matrix4::FromMatrix3(rotation_matrix.Transposed() * tgtm);
|
|
rotation_matrix = tgtm;
|
|
}
|
|
|
|
item_flags.Remove(ItemFlagWorldMatrixDirty);
|
|
}
|
|
void Item::ComputeInverseMatrix()
|
|
{
|
|
if (item_flags.IsSet(ItemFlagInverseWorldMatrixDirty))
|
|
{
|
|
imatrix = GetMatrix().InversedFast();
|
|
item_flags.Remove(ItemFlagInverseWorldMatrixDirty);
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Item::MarkWorldTransformationDirty()
|
|
{
|
|
item_flags.Set(ItemFlagWorldMatrixDirty | ItemFlagInverseWorldMatrixDirty);
|
|
ListForeachPtr(Item *, i, children)
|
|
i->MarkWorldTransformationDirty();
|
|
}
|
|
void Item::MarkTransformationDirty(bool child_only)
|
|
{
|
|
if (!child_only)
|
|
item_flags.Set(ItemFlagLocalMatrixDirty | ItemFlagWorldMatrixDirty | ItemFlagInverseWorldMatrixDirty);
|
|
|
|
ListForeachPtr(Item *, i, children)
|
|
i->MarkWorldTransformationDirty();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Item::SetPosition(const Vector4 &p)
|
|
{
|
|
position = p;
|
|
MarkTransformationDirty();
|
|
}
|
|
const Vector4 &Item::GetPosition() const
|
|
{ return position; }
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Item::SetRotation(const Vector4 &r)
|
|
{
|
|
rotation = r;
|
|
item_flags.Set(ItemFlagRotationMatrixDirty);
|
|
MarkTransformationDirty();
|
|
}
|
|
void Item::SetRotation(const Quaternion &q)
|
|
{
|
|
rotation_matrix = q.AsMatrix3();
|
|
item_flags.Remove(ItemFlagRotationMatrixDirty);
|
|
rotation = rotation_matrix.AsEuler(GetRotationOrder());
|
|
MarkTransformationDirty();
|
|
}
|
|
void Item::SetRotation(const Matrix3 &m)
|
|
{
|
|
rotation_matrix = m;
|
|
item_flags.Remove(ItemFlagRotationMatrixDirty);
|
|
rotation = rotation_matrix.AsEuler(GetRotationOrder());
|
|
MarkTransformationDirty();
|
|
}
|
|
const Vector4 &Item::GetRotation() const
|
|
{ return rotation; }
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Item::SetScale(const Vector4 &s)
|
|
{
|
|
scale = s;
|
|
MarkTransformationDirty();
|
|
}
|
|
const Vector4 &Item::GetScale() const
|
|
{ return scale; }
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Item::SetPivot(const Matrix4 &m)
|
|
{
|
|
pivot_matrix = m;
|
|
MarkTransformationDirty();
|
|
}
|
|
const Matrix4 &Item::GetPivot() const
|
|
{ return pivot_matrix; }
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
const Matrix4 &Item::GetPreviousMatrix() const
|
|
{ return prv_matrix; }
|
|
void Item::SetPreviousMatrix(const Matrix4 &mtx)
|
|
{ prv_matrix = mtx; }
|
|
const Matrix4 &Item::GetLocalMatrix()
|
|
{
|
|
ComputeMatrix();
|
|
return local_matrix;
|
|
}
|
|
const Matrix4 &Item::GetMatrix()
|
|
{
|
|
ComputeMatrix();
|
|
return matrix;
|
|
}
|
|
const Matrix4 &Item::GetInverseMatrix()
|
|
{
|
|
ComputeInverseMatrix();
|
|
return imatrix;
|
|
}
|
|
const Matrix3 &Item::GetRotationMatrix()
|
|
{
|
|
ComputeMatrix();
|
|
return rotation_matrix;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//-------------------------------------------------------------------
|
|
void Item::OffsetWorldPosition(const Vector4 &offset)
|
|
//-------------------------------------------------------------------
|
|
{
|
|
if (parent)
|
|
position += (matrix.GetRow(3) + offset) * parent->imatrix - matrix.GetRow(3) * parent->imatrix;
|
|
else position += offset;
|
|
|
|
MarkTransformationDirty();
|
|
}
|
|
|
|
//------------------------------------------------
|
|
bool Item::IsLinkedTo(Item *item)
|
|
//------------------------------------------------
|
|
{
|
|
for (Item *c = this; c; )
|
|
{
|
|
if (c == item)
|
|
return true;
|
|
c = c->GetParent();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
void Item::ComputeLocalMinMax(MinMax &minmax) const
|
|
//------------------------------------------------------------------
|
|
{
|
|
Vector4 w_position = matrix.GetRow(3);
|
|
minmax.Set(w_position, w_position);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------
|
|
void Item::SnapshotTransformation(const Matrix4 &m, bool local, bool only_matrix)
|
|
//------------------------------------------------------------------------------------------------
|
|
{
|
|
local_matrix = (parent && !local) ? parent->GetInverseMatrix() * m : m;
|
|
item_flags.Remove(ItemFlagLocalMatrixDirty);
|
|
|
|
if (!only_matrix)
|
|
{
|
|
// Remove pivot before extracting transformation components.
|
|
(local_matrix * pivot_matrix.InversedFast()).Decompose(&position, &scale, &rotation_matrix);
|
|
rotation = rotation_matrix.AsEuler(rorder);
|
|
item_flags.Remove(ItemFlagRotationMatrixDirty);
|
|
}
|
|
|
|
MarkWorldTransformationDirty();
|
|
}
|
|
|
|
//--------------------------------------------
|
|
void Item::SetParent(Item *p)
|
|
//--------------------------------------------
|
|
{
|
|
if (p == this)
|
|
__ERRRAW__(__LOG_E__ << "Cannot parent item to itself.\n")
|
|
|
|
// Already linked.
|
|
if (p && p->children.Find(this)) // FIXME why not if (parent == i)???
|
|
return;
|
|
|
|
// Catch and correct deadlock.
|
|
for (Item *c = p; c; c = c->GetParent())
|
|
if (c->GetParent() && (c->GetParent() == this))
|
|
{
|
|
c->SetParent(GetParent()); // Extract 'this' from the parent chain.
|
|
__LOG_W__ << "Deadlock detected, link chain has been corrected.\n";
|
|
break;
|
|
}
|
|
|
|
// Remove from current parent children list.
|
|
if (parent)
|
|
parent->children.Remove(this);
|
|
|
|
// Set parent.
|
|
parent = p;
|
|
|
|
// Insert as a new parent child.
|
|
if (parent)
|
|
parent->children.Add(this);
|
|
|
|
MarkTransformationDirty();
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
void Item::SetTarget(const Vector4 *p)
|
|
//----------------------------------------------------
|
|
{
|
|
if (p)
|
|
{
|
|
item_flags.Set(ItemFlagHasTarget);
|
|
target = *p;
|
|
}
|
|
else
|
|
item_flags.Remove(ItemFlagHasTarget);
|
|
|
|
MarkTransformationDirty();
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
void Item::SetMatrix(const Matrix4 &m)
|
|
//-----------------------------------------------------
|
|
{
|
|
SnapshotTransformation(m, false);
|
|
|
|
matrix = m;
|
|
imatrix = m.InversedFast();
|
|
item_flags.Remove(ItemFlagWorldMatrixDirty | ItemFlagInverseWorldMatrixDirty);
|
|
|
|
MarkTransformationDirty(true);
|
|
}
|
|
|
|
//--------------------------------------------------------------
|
|
void Item::SetRotationOrder(Math::rOrder order)
|
|
//--------------------------------------------------------------
|
|
{
|
|
rorder = order;
|
|
MarkTransformationDirty();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
Item::Item() : position(0, 0, 0), rotation(0, 0, 0), scale(1, 1, 1)
|
|
{
|
|
local_matrix = Matrix4::IdentityMatrix();
|
|
prv_matrix = Matrix4::IdentityMatrix();
|
|
matrix = Matrix4::IdentityMatrix();
|
|
imatrix = Matrix4::IdentityMatrix();
|
|
pivot_matrix = Matrix4::IdentityMatrix();
|
|
|
|
parent = NULL;
|
|
|
|
rorder = Math::rOrder_Default;
|
|
|
|
scale.Set(1,1,1,1);
|
|
|
|
mitem = NULL;
|
|
target.Set(0, 0, 1);
|
|
|
|
opacity = 1;
|
|
|
|
MarkTransformationDirty();
|
|
item_flags.Set(ItemFlagRotationMatrixDirty);
|
|
}
|
|
Item::~Item()
|
|
{
|
|
ListForeachPtr(Item *, child, children)
|
|
child->SetParent(NULL);
|
|
SetParent(NULL);
|
|
}
|
|
//------------------------------------------------------------------------------
|