
////////////////////////////////////////////////////////
//
// 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 "partatom.h"

#include "Base/matrix.h"

#include "papi.h"

CPPEXTERN_NEW(partatom)

/////////////////////////////////////////////////////////
//
// partatom
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
partatom :: partatom()
		  : m_orbit(3.0f), m_gravity(1)
{
	const float m_size = .08f;
	m_particle = pGenParticleGroups(1, 250);
	m_dList = glGenLists(1);
	m_but[0] = m_but[1] = m_but[2] = m_but[3] = m_but[4] = m_but[5] = 0;

    static GLfloat v[4][3] =
    {
	{-1.0f, -1.0f,   1.0f}, { 1.0f, -1.0f, 1.0f},
	{ 0.0f, -1.0f,  -1.0f}, { 0.0f,  1.0f, 0.0f}
    };
	if (m_dList)
	{
		float norm[3];
		glNewList(m_dList, GL_COMPILE);
			glBegin(GL_TRIANGLE_STRIP);
				Matrix::generateNormal(&v[0][0], &v[1][0], &v[2][0], &norm[0]);
				glNormal3fv(&norm[0]);
				glVertex3f(m_size * v[0][0], m_size * v[0][1], m_size * v[0][2]);
				glVertex3f(m_size * v[1][0], m_size * v[1][1], m_size * v[1][2]);
				glVertex3f(m_size * v[2][0], m_size * v[2][1], m_size * v[2][2]);

				Matrix::generateNormal(&v[1][0], &v[2][0], &v[3][0], &norm[0]);
				glNormal3fv(&norm[0]);
				glVertex3f(m_size * v[1][0], m_size * v[1][1], m_size * v[1][2]);
				glVertex3f(m_size * v[2][0], m_size * v[2][1], m_size * v[2][2]);
				glVertex3f(m_size * v[3][0], m_size * v[3][1], m_size * v[3][2]);

				Matrix::generateNormal(&v[2][0], &v[3][0], &v[0][0], &norm[0]);
				glNormal3fv(&norm[0]);
				glVertex3f(m_size * v[2][0], m_size * v[2][1], m_size * v[2][2]);
				glVertex3f(m_size * v[3][0], m_size * v[3][1], m_size * v[3][2]);
				glVertex3f(m_size * v[0][0], m_size * v[0][1], m_size * v[0][2]);

				Matrix::generateNormal(&v[3][0], &v[0][0], &v[1][0], &norm[0]);
				glNormal3fv(&norm[0]);
				glVertex3f(m_size * v[3][0], m_size * v[3][1], m_size * v[3][2]);
				glVertex3f(m_size * v[0][0], m_size * v[0][1], m_size * v[0][2]);
				glVertex3f(m_size * v[1][0], m_size * v[1][1], m_size * v[1][2]);
			glEnd();
		glEndList();
	}
    // create the rotation inlet
    inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("list"), gensym("rotlist"));
    // create the motion inlet
    inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("list"), gensym("motlist"));
}

/////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
partatom :: ~partatom()
{
	if (m_particle < 0)
		pDeleteParticleGroups(m_particle, 1);
	if (m_dList)
		glDeleteLists(m_dList, 1);
}

/////////////////////////////////////////////////////////
// rotationMess
//
/////////////////////////////////////////////////////////
void partatom :: rotationMess(float x, float y, float z)
{
	m_rotMatrix.rotateX(x);
	m_rotMatrix.rotateY(y);
	m_rotMatrix.rotateZ(z);
}

/////////////////////////////////////////////////////////
// motionMess
//
/////////////////////////////////////////////////////////
void partatom :: motionMess(int but1, int but2, int but3, int but4, int but5, int but6)
{
	if (but1 != m_but[0])
	{
		m_but[0] = but1;
		if (m_but[0])
			m_gravity = 0;
	}
	if (but2 != m_but[1])
	{
		m_but[1] = but2;
		if (m_but[1])
			m_gravity = 1;
	}
	if (but3 != m_but[2])
	{
		m_but[2] = but3;
		if (m_but[2])
			m_orbit += 0.5;
	}
	if (but4 != m_but[3])
	{
		m_but[3] = but4;
		if (m_but[3])
			m_orbit -= 0.5;
	}
	if (but5 != m_but[4])
	{
		m_but[4] = but5;
	}
	if (but6 != m_but[5])
	{
		m_but[5] = but6;
	}

	if (m_orbit < 0.5f)
		m_orbit = 0.5f;
	else if (m_orbit > 4.0f)
		m_orbit = 4.0f;
}

/////////////////////////////////////////////////////////
// reset
//
/////////////////////////////////////////////////////////
void partatom :: reset()
{
	m_rotMatrix.identity();
}

/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
void partatom :: render(GemState *state)
{
	if (m_particle < 0)
		return;

	const float col1[] = { 0.5f, 0.5f, 0.5f };
	const float col2[] = { 1.0f, 1.0f, 1.0f };

	pCurrentGroup(m_particle);

	pVelocityD(PDSphere, 0, 0, 0, 0.3f);

	float dst1[3], dst2[3];
	m_rotMatrix.transform(col1[0], col1[1], col1[2], &dst1[0], &dst1[1], &dst1[2]);
	m_rotMatrix.transform(col2[0], col2[1], col2[2], &dst2[0], &dst2[1], &dst2[2]);
	pColorD(1.0, PDBox, dst1[0], dst1[1], dst1[2], dst2[0], dst2[1], dst2[2]);

	pSize(1.9);
	
	pCopyVertexB(false, true);

	pSource(300, PDSphere, 0, 0, 0, m_orbit);
	
	// Orbit about the origin.
	if (m_gravity)
		pOrbitPoint(0, 0, 0, 0.1f);
	else
		pOrbitPoint(0, 0, 0, -0.1f);
	
	// Keep orbits from being too eccentric.
	pSink(true, PDSphere, 0.0, 0.0, 0.0, m_orbit - 0.5f);
	pSink(false, PDSphere, 0.0, 0.0, 0.0, m_orbit + 0.5f);
	
	pKillOld(10.f);

	pMove();

	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	pDrawGroupl(m_dList);
	
	glDisable(GL_CULL_FACE);
}

/////////////////////////////////////////////////////////
// static member functions
//
/////////////////////////////////////////////////////////
void partatom :: obj_setupCallback(t_class *classPtr)
{
    class_addmethod(classPtr, (t_method)&partatom::rotationMessCallback,
    	    gensym("rotlist"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(classPtr, (t_method)&partatom::motionMessCallback,
    	    gensym("motlist"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(classPtr, (t_method)&partatom::resetCallback,
    	    gensym("reset"), A_NULL);
}
void partatom :: rotationMessCallback(void *data, t_floatarg x, t_floatarg y, t_floatarg z)
{
    GetMyClass(data)->rotationMess((float)x, (float)y, (float)z);
}
void partatom :: motionMessCallback(void *data, t_floatarg but1, t_floatarg but2, t_floatarg but3, t_floatarg but4, t_floatarg but5, t_floatarg but6)
{
    GetMyClass(data)->motionMess((int)but1, (int)but2, (int)but3, (int)but4, (int)but5, (int)but6);
}
void partatom :: resetCallback(void *data)
{
	GetMyClass(data)->reset();
}
