/***********************************************************
 * File: main.cpp
 * Author: Keith Schwarz (htiek@cs.stanford.edu)
 *
 * A sample program showing off Copper3D by rendering a spinning
 * tetrahedron.
 */

#include <iostream>
#include "Matrix.h"
#include "Display.h"
#include "Scene.h"
#include "Ptr.h"
using namespace std;
using namespace Copper3D;

/* Utility function to create a 3-vector. */
Vector<3> V3(double x, double y, double z)
{
	Vector<3> result;
	result[0] = x;
	result[1] = y;
	result[2] = z;
	return result;
}

/* The spinning tetrahedron! */
int main() try {
	Ptr<Model> tetra = Model::New("tetra");
	size_t c1   = tetra->addPoint(V3( 1.0, -1.0,  1.0));
	size_t c2   = tetra->addPoint(V3(-1.0, -1.0,  1.0));
	size_t c3   = tetra->addPoint(V3(-1.0, -1.0, -1.0));
	size_t c4   = tetra->addPoint(V3( 1.0, -1.0, -1.0));
	size_t apex = tetra->addPoint(V3( 0.0,  1.0,  0.0));
	
	/* Add triangles for each face. */
	tetra->addTriangle(Triangle(apex, c2, c1));
	tetra->addTriangle(Triangle(apex, c3, c2));
	tetra->addTriangle(Triangle(apex, c4, c3));
	tetra->addTriangle(Triangle(apex, c1, c4));
	tetra->addTriangle(Triangle(c2, c4, c1));
	tetra->addTriangle(Triangle(c2, c3, c4));
	Ptr<Scene> scene = Scene::New();
	scene->setDisplay(Display::New(400, 400));
	scene->addModel(tetra);

	Ptr<SceneElement> tetra0 = SceneElement::New("tetra0", "tetra", V3(0,0,4), Orientation());
	Ptr<SceneElement> tetra1 = SceneElement::New("tetra1", "tetra", V3(3,0,6), Orientation());
	Ptr<SceneElement> tetra2 = SceneElement::New("tetra2", "tetra", V3(-3,0,6), Orientation());

	scene->addElement(tetra0);
	scene->addElement(tetra1);
	scene->addElement(tetra2);

	/* Build our u and r vectors. */
	Vector<3> r;
	Vector<3> u;
	u[0] = 0.0;
	u[1] = 1.0;
	u[2] = 0.0;

	r[0] = 1.0;
	r[1] = 0.0;
	r[2] = 0.0;

	for (int deg = 0; ; deg = (deg + 1) % 360) {
		const double theta = deg * 3.1415926535 / 180;

		/* Build a matrix to rotate a vector 15deg around the X axis.  This looks like this:
		 * |1      0      0   |
		 * |0   cos 15 -sin 15|
		 * |0   sin 15  cos 15|
		 */
		
		Matrix<3, 3> rotateX = Identity<3, double>();
		rotateX[1][1] = cos(theta);
		rotateX[1][2] = -sin(theta);
		rotateX[2][1] = sin(theta);
		rotateX[2][2] = cos(theta);

		/* Build a matrix to rotate a vector 15deg around the y axis.  This looks like this:
		 * |cos 15   0  -sin 15|
		 * |   0     1      1  |
		 * |sin 15   0   cos 15|
		 */
		
		Matrix<3, 3> rotateY = Identity<3, double>();
		rotateY[0][0] = cos(theta);
		rotateY[0][2] = -sin(theta);
		rotateY[2][0] = sin(theta);
		rotateY[2][2] = cos(theta);

		/* Build a matrix to rotate a vector 15deg around the Z axis.  This looks like this:
		 * |cos 15 -sin 15  0|
		 * |sin 15  cos 15  0|
		 * |   0     0      1|
		 
		 */
		
		Matrix<3, 3> rotateZ = Identity<3, double>();
		rotateZ[0][0] = cos(theta);
		rotateZ[0][1] = -sin(theta);
		rotateZ[1][0] = sin(theta);
		rotateZ[1][1] = cos(theta);

		Vector<3> currR = rotateX * r;
		Vector<3> currU = rotateX * u;
		tetra0->setOrientation(Orientation(currR, currU, CrossProduct(currR, currU)));

		currR = rotateY * r;
		currU = rotateY * u;
		tetra1->setOrientation(Orientation(currR, currU, CrossProduct(currR, currU)));

		currR = rotateZ * r;
		currU = rotateZ * u;
		tetra2->setOrientation(Orientation(currR, currU, CrossProduct(currR, currU)));


		scene->render();
	}
}
catch (const exception& e) {
	cerr << "Caught exception: " << e.what() << endl;
}