Browse Source

SceneGraph rework, part 5: bulk transformation computing.

pull/7/head
Vladimír Vondruš 14 years ago
parent
commit
4e53b17f8d
  1. 11
      src/SceneGraph/AbstractObject.h
  2. 22
      src/SceneGraph/Object.h
  3. 124
      src/SceneGraph/Object.hpp
  4. 69
      src/SceneGraph/Test/ObjectTest.cpp
  5. 1
      src/SceneGraph/Test/ObjectTest.h

11
src/SceneGraph/AbstractObject.h

@ -101,6 +101,17 @@ template<std::uint8_t dimensions, class T = GLfloat> class AbstractObject
*/ */
virtual typename DimensionTraits<dimensions, T>::MatrixType absoluteTransformationMatrix() const = 0; virtual typename DimensionTraits<dimensions, T>::MatrixType absoluteTransformationMatrix() const = 0;
/**
* @brief Transformation matrices of given set of objects relative to this object
*
* All transformations are premultiplied with @p initialTransformationMatrix,
* if specified.
* @warning This function cannot check if all objects are of the same
* Object type, use typesafe Object::transformations() when
* possible.
*/
virtual std::vector<typename DimensionTraits<dimensions, T>::MatrixType> transformationMatrices(const std::vector<AbstractObject<dimensions, T>*>& objects, const typename DimensionTraits<dimensions, T>::MatrixType& initialTransformationMatrix = typename DimensionTraits<dimensions, T>::MatrixType()) const = 0;
/*@}*/ /*@}*/
/** /**

22
src/SceneGraph/Object.h

@ -33,7 +33,9 @@ template<class Transformation> class Scene;
#ifndef DOXYGEN_GENERATING_OUTPUT #ifndef DOXYGEN_GENERATING_OUTPUT
namespace Implementation { namespace Implementation {
enum class ObjectFlag: std::uint8_t { enum class ObjectFlag: std::uint8_t {
Dirty = 1 << 0 Dirty = 1 << 0,
Visited = 1 << 1,
Joint = 1 << 2
}; };
typedef Corrade::Containers::EnumSet<ObjectFlag, std::uint8_t> ObjectFlags; typedef Corrade::Containers::EnumSet<ObjectFlag, std::uint8_t> ObjectFlags;
@ -80,7 +82,7 @@ template<class Transformation> class Object: public AbstractObject<Transformatio
* @brief Constructor * @brief Constructor
* @param parent Parent object * @param parent Parent object
*/ */
inline Object(Object<Transformation>* parent = nullptr): flags(Flag::Dirty) { inline Object(Object<Transformation>* parent = nullptr): counter(0xFFFFu), flags(Flag::Dirty) {
setParent(parent); setParent(parent);
} }
@ -186,6 +188,17 @@ template<class Transformation> class Object: public AbstractObject<Transformatio
*/ */
typename Transformation::DataType absoluteTransformation() const; typename Transformation::DataType absoluteTransformation() const;
/**
* @brief Transformations of given group of objects relative to this object
*
* All transformations can be premultiplied with @p initialTransformation,
* if specified.
* @see AbstractObject::transformationMatrices()
*/
/* `objects` passed by copy intentionally (to allow move from
transformationMatrices() and avoid copy in the function itself) */
std::vector<typename Transformation::DataType> transformations(std::vector<Object<Transformation>*> objects, const typename Transformation::DataType& initialTransformation = typename Transformation::DataType()) const;
/*@}*/ /*@}*/
inline bool isDirty() const override { return !!(flags & Flag::Dirty); } inline bool isDirty() const override { return !!(flags & Flag::Dirty); }
@ -196,8 +209,13 @@ template<class Transformation> class Object: public AbstractObject<Transformatio
Object<Transformation>* sceneObject() override; Object<Transformation>* sceneObject() override;
const Object<Transformation>* sceneObject() const override; const Object<Transformation>* sceneObject() const override;
std::vector<typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType> transformationMatrices(const std::vector<AbstractObject<Transformation::Dimensions, typename Transformation::Type>*>& objects, const typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType& initialTransformationMatrix = typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType()) const override;
typename Transformation::DataType computeJointTransformation(const std::vector<Object<Transformation>*>& jointObjects, std::vector<typename Transformation::DataType>& jointTransformations, const std::size_t joint, const typename Transformation::DataType& initialTransformation) const;
typedef Implementation::ObjectFlag Flag; typedef Implementation::ObjectFlag Flag;
typedef Implementation::ObjectFlags Flags; typedef Implementation::ObjectFlags Flags;
std::uint16_t counter;
Flags flags; Flags flags;
}; };

124
src/SceneGraph/Object.hpp

@ -162,6 +162,130 @@ template<class Transformation> void Object<Transformation>::setClean() {
} }
} }
template<class Transformation> std::vector<typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType> Object<Transformation>::transformationMatrices(const std::vector<AbstractObject<Transformation::Dimensions, typename Transformation::Type>*>& objects, const typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType& initialTransformationMatrix) const {
std::vector<Object<Transformation>*> castObjects(objects.size());
for(std::size_t i = 0; i != objects.size(); ++i)
/** @todo Ensure this doesn't crash, somehow */
castObjects[i] = static_cast<Object<Transformation>*>(objects[i]);
std::vector<typename Transformation::DataType> transformations = this->transformations(std::move(castObjects), Transformation::fromMatrix(initialTransformationMatrix));
std::vector<typename DimensionTraits<Transformation::Dimensions, typename Transformation::Type>::MatrixType> transformationMatrices(transformations.size());
for(std::size_t i = 0; i != objects.size(); ++i)
transformationMatrices[i] = Transformation::toMatrix(transformations[i]);
return transformationMatrices;
}
template<class Transformation> std::vector<typename Transformation::DataType> Object<Transformation>::transformations(std::vector<Object<Transformation>*> objects, const typename Transformation::DataType& initialTransformation) const {
/* Remember object count for later */
std::size_t objectCount = objects.size();
/* Create initial list of joints from original objects */
std::vector<Object<Transformation>*> jointObjects(objects.size());
for(std::size_t i = 0; i != jointObjects.size(); ++i) {
jointObjects[i] = static_cast<Object<Transformation>*>(objects[i]);
CORRADE_INTERNAL_ASSERT(jointObjects[i]->counter == 0xFFFFu);
jointObjects[i]->counter = i;
jointObjects[i]->flags |= Flag::Joint;
}
/* Scene object */
const Scene<Transformation>* scene = this->scene();
/* Nearest common ancestor not yet implemented - assert this is done on scene */
CORRADE_ASSERT(scene == this, "SceneGraph::Object::transformationMatrices(): currently implemented only for Scene", {});
/* Mark all objects up the hierarchy as visited */
auto it = objects.begin();
while(!objects.empty()) {
/* Mark the object as visited */
CORRADE_INTERNAL_ASSERT(!((*it)->flags & Flag::Visited));
(*it)->flags |= Flag::Visited;
Object<Transformation>* parent = (*it)->parent();
/* If this is root object, remove from list */
if(!parent) {
CORRADE_ASSERT(*it == scene, "SceneGraph::Object::transformations(): the objects are not part of the same tree", {});
it = objects.erase(it);
/* Parent is an joint or already visited - remove current from list */
} else if(parent->flags & (Flag::Visited|Flag::Joint)) {
it = objects.erase(it);
/* If not already marked as joint, mark it as such and add it to
list of joint objects */
if(!(parent->flags & Flag::Joint)) {
CORRADE_INTERNAL_ASSERT(parent->counter == 0xFFFFu);
parent->counter = jointObjects.size();
parent->flags |= Flag::Joint;
jointObjects.push_back(parent);
}
/* Else go up the hierarchy */
} else *it = parent;
/* Cycle if reached end */
if(it == objects.end()) it = objects.begin();
}
CORRADE_ASSERT(objects.size() < 0xFFFFu, "SceneGraph::Object::transformations(): too large scene", {});
/* Array of absolute transformations in joints */
std::vector<typename Transformation::DataType> jointTransformations(jointObjects.size());
/* Compute transformations for all joints */
for(std::size_t i = 0; i != jointTransformations.size(); ++i)
computeJointTransformation(jointObjects, jointTransformations, i, initialTransformation);
/* All visited marks are now cleaned, clean joint marks and counters */
for(auto i: jointObjects) {
CORRADE_INTERNAL_ASSERT(i->flags & Flag::Joint);
i->flags &= ~Flag::Joint;
i->counter = 0xFFFFu;
}
/* Shrink the array to contain only transformations of requested objects and return */
jointTransformations.resize(objectCount);
return jointTransformations;
}
template<class Transformation> typename Transformation::DataType Object<Transformation>::computeJointTransformation(const std::vector<Object<Transformation>*>& jointObjects, std::vector<typename Transformation::DataType>& jointTransformations, const std::size_t joint, const typename Transformation::DataType& initialTransformation) const {
Object<Transformation>* o = jointObjects[joint];
/* Transformation already computed ("unvisited" by this function before), done */
if(!(o->flags & Flag::Visited)) return jointTransformations[joint];
/* Initialize transformation */
jointTransformations[joint] = o->transformation();
/* Go up until next joint or root */
for(;;) {
/* Clean visited mark */
CORRADE_INTERNAL_ASSERT(o->flags & Flag::Visited);
o->flags &= ~Flag::Visited;
Object<Transformation>* parent = o->parent();
/* Root object, compose transformation with initial, done */
if(!parent) {
CORRADE_INTERNAL_ASSERT(o->isScene());
return (jointTransformations[joint] =
Transformation::compose(initialTransformation, jointTransformations[joint]));
/* Joint object, compose transformation with the joint, done */
} else if(parent->flags & Flag::Joint) {
return (jointTransformations[joint] =
Transformation::compose(computeJointTransformation(jointObjects, jointTransformations, parent->counter, initialTransformation), jointTransformations[joint]));
/* Else compose transformation with parent, go up the hierarchy */
} else {
jointTransformations[joint] = Transformation::compose(parent->transformation(), jointTransformations[joint]);
o = parent;
}
}
}
}} }}
#endif #endif

69
src/SceneGraph/Test/ObjectTest.cpp

@ -34,6 +34,7 @@ ObjectTest::ObjectTest() {
addTests(&ObjectTest::parenting, addTests(&ObjectTest::parenting,
&ObjectTest::scene, &ObjectTest::scene,
&ObjectTest::absoluteTransformation, &ObjectTest::absoluteTransformation,
&ObjectTest::transformations,
&ObjectTest::caching); &ObjectTest::caching);
} }
@ -99,6 +100,74 @@ void ObjectTest::absoluteTransformation() {
CORRADE_COMPARE(o3.absoluteTransformation(), Matrix4::translation({1.0f, 2.0f, 3.0f})); CORRADE_COMPARE(o3.absoluteTransformation(), Matrix4::translation({1.0f, 2.0f, 3.0f}));
} }
void ObjectTest::transformations() {
Scene3D s;
Matrix4 initial = Matrix4::rotationX(deg(90.0f)).inverted();
/* Scene alone */
CORRADE_COMPARE(s.transformations({&s}, initial), vector<Matrix4>{initial});
/* One object */
Object3D first(&s);
first.rotateZ(deg(30.0f));
Object3D second(&first);
second.scale(Vector3(0.5f));
CORRADE_COMPARE(s.transformations({&second}, initial), vector<Matrix4>{
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::scaling(Vector3(0.5f))
});
/* One object and scene */
CORRADE_COMPARE(s.transformations({&second, &s}, initial), (vector<Matrix4>{
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::scaling(Vector3(0.5f)),
initial
}));
/* Two objects with foreign joint */
Object3D third(&first);
third.translate(Vector3::xAxis(5.0f));
CORRADE_COMPARE(s.transformations({&second, &third}, initial), (vector<Matrix4>{
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::scaling(Vector3(0.5f)),
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::translation(Vector3::xAxis(5.0f)),
}));
/* Three objects with joint as one of them */
CORRADE_COMPARE(s.transformations({&second, &third, &first}, initial), (vector<Matrix4>{
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::scaling(Vector3(0.5f)),
initial*Matrix4::rotationZ(deg(30.0f))*Matrix4::translation(Vector3::xAxis(5.0f)),
initial*Matrix4::rotationZ(deg(30.0f)),
}));
{
CORRADE_EXPECT_FAIL("Transformations not relative to scene are not yet implemented.");
/* Transformation relative to another object */
CORRADE_COMPARE(second.transformations({&third}), vector<Matrix4>{
Matrix4::scaling(Vector3(0.5f)).inverted()*Matrix4::translation(Vector3::xAxis(5.0f))
});
/* Transformation relative to another object, not part of any scene (but should work) */
Object3D orphanParent1;
orphanParent1.rotate(deg(31.0f), Vector3(1.0f).normalized());
Object3D orphanParent(&orphanParent1);
Object3D orphan1(&orphanParent);
orphan1.scale(Vector3::xScale(3.0f));
Object3D orphan2(&orphanParent);
orphan2.translate(Vector3::zAxis(5.0f));
CORRADE_COMPARE(orphan1.transformations({&orphan2}), vector<Matrix4>{
Matrix4::scaling(Vector3::xScale(3.0f)).inverted()*Matrix4::translation(Vector3::zAxis(5.0f))
});
}
ostringstream o;
Error::setOutput(&o);
/* Transformation of objects not part of the same scene */
Object3D orphan;
CORRADE_COMPARE(s.transformations({&orphan}), vector<Matrix4>());
CORRADE_COMPARE(o.str(), "SceneGraph::Object::transformations(): the objects are not part of the same tree\n");
}
void ObjectTest::caching() { void ObjectTest::caching() {
Scene3D scene; Scene3D scene;

1
src/SceneGraph/Test/ObjectTest.h

@ -26,6 +26,7 @@ class ObjectTest: public Corrade::TestSuite::Tester<ObjectTest> {
void parenting(); void parenting();
void scene(); void scene();
void absoluteTransformation(); void absoluteTransformation();
void transformations();
void caching(); void caching();
}; };

Loading…
Cancel
Save