////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// mark@danks.org
//
// Implementation file
//
//    Copyright (c) 1997-1998 Mark Danks.
//    For information on usage and redistribution, and for a DISCLAIMER OF ALL
//    WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////

#include "camera.h"

#include <GL/glu.h>

#include "Base/GemMan.h"

CPPEXTERN_NEW(camera)

/////////////////////////////////////////////////////////
//
// camera
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
camera :: camera()
{
    // create the inlets
    inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("ft1"));
    inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("list"), gensym("rotatelist"));
    inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("ft2"));
    inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("list"), gensym("translatelist"));
    inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("list"), gensym("looklist"));

    resetValues();
}

/////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
camera :: ~camera()
{ }

/////////////////////////////////////////////////////////
// resetMess
//
/////////////////////////////////////////////////////////
void camera :: resetMess()
{
    resetValues();
    render(NULL);
    setModified();
}

/////////////////////////////////////////////////////////
// resetValues
//
/////////////////////////////////////////////////////////
void camera :: resetValues()
{
    m_angle = 0.0;
    m_spin[0] = 1.0;
    m_spin[1] = m_spin[2] = 0.0;
    m_trans = 4.0;
    m_pos[0] = m_pos[1] = 0.0;
    m_pos[2] = 1.0;
    m_look[0] = 0.0;
    m_look[1] = 1.0;
    m_look[2] = 0.0;
}

/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
void camera :: render(GemState *)
{
    // make sure everything is ok
    if ( !GemMan::windowExists() ) return;
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 20.0);
    gluLookAt(m_pos[0] * m_trans, m_pos[1] * m_trans, m_pos[2] * m_trans,
		 0.0, 0.0, 0.0, m_look[0], m_look[1], m_look[2]);
    glRotatef(m_angle, m_spin[0], m_spin[1], m_spin[2]);
    
    glMatrixMode(GL_MODELVIEW);
}

/////////////////////////////////////////////////////////
// printMess
//
/////////////////////////////////////////////////////////
void camera :: printMess()
{
    post("rot: %f", m_angle);
    post("rot vect: %f %f %f", m_spin[0], m_spin[1], m_spin[2]);
    post("trans: %f", m_trans);
    post("trans vect: %f %f %f", m_pos[0], m_pos[1], m_pos[2]);
}

/////////////////////////////////////////////////////////
// rotAngleMess
//
/////////////////////////////////////////////////////////
void camera :: rotAngleMess(float angle)
{
    m_angle = angle;
    setModified();
}

/////////////////////////////////////////////////////////
// rotVectorMess
//
/////////////////////////////////////////////////////////
void camera :: rotVectorMess(float x, float y, float z)
{
    m_spin[0] = x;
    m_spin[1] = y;
    m_spin[2] = z;
    setModified();
}

/////////////////////////////////////////////////////////
// transAmountMess
//
/////////////////////////////////////////////////////////
void camera :: transAmountMess(float amount)
{
    m_trans = amount;
    setModified();
}

/////////////////////////////////////////////////////////
// transVectorMess
//
/////////////////////////////////////////////////////////
void camera :: transVectorMess(float x, float y, float z)
{
    m_pos[0] = x;
    m_pos[1] = y;
    m_pos[2] = z;
    setModified();
}

/////////////////////////////////////////////////////////
// lookVectorMess
//
/////////////////////////////////////////////////////////
void camera :: lookVectorMess(float x, float y, float z)
{
    m_look[0] = x;
    m_look[1] = y;
    m_look[2] = z;
    setModified();
}

/////////////////////////////////////////////////////////
// static member functions
//
/////////////////////////////////////////////////////////
void camera :: obj_setupCallback(t_class *classPtr)
{
    class_addmethod(classPtr, (t_method)&camera::rotAngleMessCallback,
    	    gensym("ft1"), A_FLOAT, A_NULL);
    class_addmethod(classPtr, (t_method)&camera::rotVectorMessCallback,
    	    gensym("rotatelist"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);

    class_addmethod(classPtr, (t_method)&camera::transAmountMessCallback,
    	    gensym("ft2"), A_FLOAT, A_NULL);
    class_addmethod(classPtr, (t_method)&camera::transVectorMessCallback,
    	    gensym("translatelist"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(classPtr, (t_method)&camera::lookVectorMessCallback,
    	    gensym("looklist"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);

    class_addmethod(classPtr, (t_method)&camera::resetMessCallback,
    	    gensym("reset"), A_NULL);
    class_addmethod(classPtr, (t_method)&camera::printMessCallback,
    	    gensym("print"), A_NULL);
}
void camera :: rotAngleMessCallback(void *data, t_floatarg angle)
{
    GetMyClass(data)->rotAngleMess((float)angle);
}
void camera :: rotVectorMessCallback(void *data, t_floatarg x, t_floatarg y, t_floatarg z)
{
    GetMyClass(data)->rotVectorMess((float)x, (float)y, (float)z);
}
void camera :: transAmountMessCallback(void *data, t_floatarg amount)
{
    GetMyClass(data)->transAmountMess((float)amount);
}
void camera :: transVectorMessCallback(void *data, t_floatarg x, t_floatarg y, t_floatarg z)
{
    GetMyClass(data)->transVectorMess((float)x, (float)y, (float)z);
}
void camera :: lookVectorMessCallback(void *data, t_floatarg x, t_floatarg y, t_floatarg z)
{
    GetMyClass(data)->lookVectorMess((float)x, (float)y, (float)z);
}
void camera :: resetMessCallback(void *data)
{
    GetMyClass(data)->printMess();
}
void camera :: printMessCallback(void *data)
{
    GetMyClass(data)->resetMess();
}
