
#include <WFX/scene/Spatial.h>

#include <WFX/renderer/Renderer.h>
#include <WFX/math/Sphere.h>

using namespace WFX;
using namespace WFX::math;
using namespace WFX::scene;
using namespace WFX::renderer;

using namespace std;

Spatial::Spatial() {
    LocalRotation = math::Matrix3::IDENTITY;
    LocalTranslation = math::Vector3::ZERO;
    LocalScale = 1.0f;
    WorldRotation = math::Matrix3::IDENTITY;
    WorldTranslation = math::Vector3::ZERO;
    WorldScale = 1.0f;
    Parent = NULL;
    ForceCull = false;
    Dirty = true;
    ModelBound = new Sphere();
    WorldBound = NULL;
}

void Spatial::rotateX(Real r) {
    Matrix3 rot = Matrix3::rotationMatrixX(r);
    setLocalRotation(LocalRotation * rot);
}

void Spatial::rotateY(Real r) {
    Matrix3 rot = Matrix3::rotationMatrixY(r);
    setLocalRotation(LocalRotation * rot);
}

void Spatial::rotateZ(Real r) {
    Matrix3 rot = Matrix3::rotationMatrixZ(r);
    setLocalRotation(LocalRotation * rot);
}

void Spatial::scale(Real r) {
    setLocalScale(LocalScale*r);
    markDirty();
}

void Spatial::translate(math::Vector3& v) {
    setLocalTranslation(LocalTranslation+v);
    markDirty();
}

void Spatial::translate(math::Vector2& v) {
    math::Vector3 v2(v);
    setLocalTranslation(LocalTranslation+v2);
    markDirty();
}

void Spatial::updateDown() {
    //only perform updates if subtree is dirty
    if (!Dirty) return;
    //perform transform updates
    if (Parent) {
        WorldScale = LocalScale * Parent->WorldScale;
        WorldRotation = LocalRotation * Parent->WorldRotation;
        WorldTranslation = Parent->WorldTranslation + 
                            Parent->WorldScale * (Parent->WorldRotation * LocalTranslation);
    } else {
        WorldScale = LocalScale;
        WorldTranslation = LocalTranslation;
        WorldRotation = LocalRotation;
    }
    //update WorldBound
    if (WorldBound) delete WorldBound;
    WorldBound = ModelBound->copy();
    WorldBound->transformBy(WorldRotation,WorldTranslation,WorldScale);
}

void Spatial::onDraw(Renderer* renderer) {
    if (ForceCull) return;
    Camera* cam = renderer->getCamera();
    if (!cam->culled(WorldBound))
        draw(renderer);
}

void Spatial::destroy() {
    if (ModelBound)
        delete ModelBound;
    if (WorldBound)
        delete WorldBound;
}

void Spatial::setModelBound(BoundingVolume* b) {
    if (ModelBound)
        delete ModelBound;
    ModelBound = b;
    markDirty();
}


