/*
 * $Id: mesh.cpp,v 1.14 2002/07/19 20:08:28 msell Exp $
 *
 *
 * $Log: mesh.cpp,v $
 * Revision 1.14  2002/07/19 20:08:28  msell
 * Linux-porttausta
 *
 * Revision 1.13  2002/07/15 15:22:08  msell
 * Parantelua
 *
 * Revision 1.12  2002/07/14 21:40:43  msell
 * Conflictit pois, liikkumiset (hyppy, kävely, lyönti), uusi areena
 *
 * Revision 1.11  2002/07/14 21:22:39  jkaarlas
 * skybox ja ukkojen säätö
 *
 * Revision 1.10  2002/07/10 22:22:53  msell
 * Cartoon-rendaus
 *
 * Revision 1.9  2002/07/10 22:10:32  jkaarlas
 * skaalatut ja käännellyt ruumiinosat
 *
 * Revision 1.8  2002/07/10 17:13:44  msell
 * Törmäystarkastelun parantelua
 *
 * Revision 1.7  2002/07/07 17:53:21  msell
 * Legoukon alku
 *
 * Revision 1.6  2002/07/07 15:29:07  msell
 * Törmäyksien parantelua
 *
 * Revision 1.5  2002/07/04 21:05:41  msell
 * Se toimii!! =)
 * Törmäystarkistukset siis
 *
 * Revision 1.4  2002/06/30 16:05:04  msell
 * Törmäyksien parantelua, transformaatioita mukana
 *
 * Revision 1.3  2002/06/26 22:30:29  jkaarlas
 * lisätty Vertexiin tekstuurikoordinaatit
 *
 * Revision 1.2  2002/06/24 14:12:15  msell
 * Nyt toimii sphere -> mesh -törmäykset, ihan tosi
 *
 * Revision 1.1  2002/06/20 22:50:12  msell
 * Meshit
 *
 *
 *
 * $Date: 2002/07/19 20:08:28 $
 *
 */

#include "main.h"

#include "mesh.h"
#include "vector.h"
#include "sphere.h"
#include "collision.h"
#include "audio.h"

#include <math.h>

#include "glapi.h"

Vertex::Vertex(void){
	vectorSet(position, 0, 0, 0);
	vectorSet(normal, 0, 0, 0);
}

Vertex::Vertex(float x, float y, float z){
	vectorSet(position, x, y, z);
	vectorSet(normal, x, y, z);
	vectorNormalize(normal);
}

Vertex::Vertex(float x, float y, float z, float nx, float ny, float nz){
	vectorSet(position, x, y, z);
	vectorSet(normal, nx, ny, nz);
}

void Vertex::setTexCoords(float u, float v){
  this->texcoords[0] = u;
  this->texcoords[1] = v;
}

Polygon::Polygon(void){
	vertexcount = 0;
	edgecount = 0;
	smooth = false;
	realsmooth = false;
}



Mesh::Mesh(void){
	vertexcount = 0;
	polygoncount = 0;
	edgecount = 0;
}

Mesh::~Mesh(void){
	delete [] polygons;
	delete [] vertices;
}

void Mesh::createPlanes(void){
	int i;
	for (i = 0; i < polygoncount; i++){
		class Polygon *polygon = &this->polygons[i];
		if (polygon->vertexcount >= 3){
			float v1[3], v2[3];
			vectorSub(v1, polygon->vertices[1]->position,
										polygon->vertices[0]->position);
			vectorSub(v2, polygon->vertices[2]->position,
										polygon->vertices[0]->position);
			vectorCross(polygon->planenormal, v1, v2);
			vectorNormalize(polygon->planenormal);

			polygon->planedistance = -vectorDot(polygon->vertices[0]->position, polygon->planenormal);
		}
	}
}

void Mesh::createVertexnormals(void){
	int i, j, ii;
	bool connect;
	float normal[3];
	for (i = 0; i < vertexcount; i++){
		bool found = false;
		vectorSet(normal, 0, 0, 0);
		for (j = 0; j < polygoncount; j++){
			connect = false;
			class Polygon *polygon = &polygons[j];
			for (ii = 0;ii < polygon->vertexcount; ii++){
				if (polygons[j].vertices[ii] == &(vertices[i])){
					connect = true;
				}
			}
			if (connect){
				vectorAdd(normal, polygon->planenormal);
				found = true;
			}
		}
		if (found){
			vectorNormalize(vertices[i].normal, normal);
		}
	}
	for (j = 0; j < polygoncount; j++){
		class Polygon *polygon = &polygons[j];
		if (!polygon->realsmooth) polygon->smooth = true;
	}
}

void Mesh::createEdges(void){
	int maxedgecount = 0;
	int i;
	for (i = 0; i < polygoncount; i++){
		class Polygon *polygon = &polygons[i];
		maxedgecount += polygon->vertexcount;
	}

	edgecount = 0;
	int j, k;
	Edge *edges = new Edge[maxedgecount];
	for (i = 0; i < polygoncount; i++){
		class Polygon *polygon = &polygons[i];
		polygon->edges = new Edge *[polygon->vertexcount];
		for (j = 1; j <= polygon->vertexcount; j++){
			Vertex *v1 = polygon->vertices[j-1];
			Vertex *v2 = polygon->vertices[j%polygon->vertexcount];

			bool found = false;
			for (k = 0; k < edgecount; k++){
				if (edges[k].v2 == v1 && edges[k].v1 == v2){
					found = true;
					edges[k].p2 = polygon;
					break;
				}
			}

			if (!found){
				edges[edgecount].v1 = v1;
				edges[edgecount].v2 = v2;
				edges[edgecount].p1 = polygon;
				edges[edgecount].p2 = NULL;
				edgecount++;
			}
		}
	}

	this->edges = new Edge[edgecount];
	//printf("%i\n", edgecount);
	for (i = 0; i < edgecount; i++){
		this->edges[i].v1 = edges[i].v1;
		this->edges[i].v2 = edges[i].v2;
		this->edges[i].p1 = edges[i].p1;
		this->edges[i].p2 = edges[i].p2;

		class Polygon *p;
		p = edges[i].p1;
		p->edges[p->edgecount++] = &this->edges[i];
		p = edges[i].p2;
		p->edges[p->edgecount++] = &this->edges[i];
		//printf("%p, %p\n", edges[i].p1, edges[i].p2);
	}
	
	delete[] edges;
}


float Mesh::calculateScale(float targetLength, int axis){

	float min = 0.0;
	float max = 0.0;

	for (int i=0; i < this->vertexcount; i++){
		if (this->vertices->position[axis] > max){
			max = this->vertices->position[axis];
		}
		if (this->vertices->position[axis] < min){
			min = this->vertices->position[axis];
		}
	}
	return fabs(targetLength / (max - min));
}

/* ei toimi kunnolla kaikille objekteille (kädet ok, jalat ja torso ei) */
void Mesh::scale(float targetLength, int axis){
	float newscale = this->calculateScale(targetLength, axis);
	this->scale(newscale);
}

/* ei toimi kunnolla kaikille objekteille (kädet ok, jalat ja torso ei)*/
void Mesh::scale(float scale){
	for (int i=0; i < this->vertexcount; i++){
		this->vertices->position[0] *= scale;
		this->vertices->position[1] *= scale;
		this->vertices->position[2] *= scale;
	}
	this->createVertexnormals();
	this->createPlanes();
}

MeshObject::MeshObject(Mesh *mesh){
	this->mesh = mesh;
	this->appearance = new MeshAppearance(mesh);
	this->geometry = new MeshShape(this);
}



MeshAppearance::MeshAppearance(Mesh *mesh){
	this->mesh = mesh;
}



void MeshAppearance::draw(void){
  //glDisable(GL_CULL_FACE);
  glColor4fv(this->material.getColor());
  this->material.enable();

	int i, j;
	
	for (i = 0; i < mesh->polygoncount; i++){
		class Polygon *polygon = &mesh->polygons[i];
			
		glBegin(GL_TRIANGLE_FAN);

		if (!polygon->smooth) glNormal3fv(polygon->planenormal);

		for (j = 0; j < polygon->vertexcount; j++){
			Vertex *vertex = polygon->vertices[j];

			if (polygon->smooth) glNormal3fv(vertex->normal);
			glVertex3fv(vertex->position);

		}


		glEnd();
	}


	glDisable(GL_DEPTH);
	glDisable(GL_LIGHTING);
  glLineWidth(5.0);
	glBegin(GL_LINES);
	for (i = 0; i < mesh->edgecount; i++){
		glColor3f(0, 0, 0);
		glVertex3fv(mesh->edges[i].v1->position);
		glVertex3fv(mesh->edges[i].v2->position);
	}

	glEnd();
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH);


  this->material.disable();
  //glEnable(GL_CULL_FACE);
}






MeshShape::MeshShape(MeshObject *object) : Shape(object){
	mesh = object->mesh;
}

MeshShape::MeshShape(Object *object, Mesh *mesh) : Shape(object){
	this->mesh = mesh;
}

bool MeshShape::checkCollision(Object *target){
	return target->geometry->checkCollisionPeer(this);
}

float MeshShape::calculateMomentOfInertia(float *rotationvector){
	if (vectorDot(rotationvector, rotationvector) < EPSILON) return 0;
	int i;
	float j = 0;
	for (i = 0; i < mesh->vertexcount; i++){
		float proj[3];
		vectorProject(proj, mesh->vertices[i].position, rotationvector);
		vectorSub(proj, mesh->vertices[i].position, proj);
		//float r = vectorLength(proj);
		float r2 = vectorDot(proj, proj);
		j += r2;
	}
	return j / i;
}

bool MeshShape::checkCollisionPeer(SphereShape *target){
	float position[3] = {0, 0, 0};
	target->object->transformPoint(position, position);
	object->unTransformPoint(position, position);

	float normal[3];
	float contactpoint[3];
	float r = target->getRadius();

	if (checkSphereMeshCollision(position, r, mesh, normal, contactpoint)){
		vectorScale(normal, -1);
		object->transformVector(normal, normal);
		object->transformPoint(contactpoint, contactpoint);

		addCollision(object, target->object, normal, contactpoint);

		//vectorAdd(contactnormal, normal);

		return true;
	}

	return false;
}

//extern Sound *shotsound;

bool MeshShape::checkCollisionPeer(MeshShape *target){
	float normal[3];
	float contactpoint[3];

	bool collided = false;

	int i;

	Mesh *sourcemesh, *targetmesh;
	
	sourcemesh = this->mesh;
	targetmesh = target->mesh;
	for (i = 0; i < sourcemesh->vertexcount; i++){
		Vertex *vertex = &sourcemesh->vertices[i];
		float vertexposition[3];
		object->transformPoint(vertexposition, vertex->position);
		target->object->unTransformPoint(vertexposition, vertexposition);

		if (checkPointMeshCollision(vertexposition, targetmesh, normal, contactpoint)){
			target->object->transformVector(normal, normal);
			target->object->transformPoint(contactpoint, contactpoint);

			if (vectorIsZero(contactpoint)){
				vectorSet(contactpoint, 0, 0, 0);
			}
			addCollision(object, target->object, normal, contactpoint);
			collided = true;
		}
	}

	sourcemesh = target->mesh;
	targetmesh = this->mesh;
	for (i = 0; i < sourcemesh->vertexcount; i++){
		Vertex *vertex = &sourcemesh->vertices[i];
		float vertexposition[3];
		target->object->transformPoint(vertexposition, vertex->position);
		object->unTransformPoint(vertexposition, vertexposition);

		if (checkPointMeshCollision(vertexposition, targetmesh, normal, contactpoint)){
			object->transformVector(normal, normal);
			object->transformPoint(contactpoint, contactpoint);

			addCollision(target->object, object, normal, contactpoint);
			collided = true;
		}
	}

	sourcemesh = this->mesh;
	targetmesh = target->mesh;
	for (i = 0; i < sourcemesh->edgecount; i++){
		Edge *edge = &sourcemesh->edges[i];
		float v1[3], v2[3];
		object->transformPoint(v1, edge->v1->position);
		target->object->unTransformPoint(v1, v1);

		object->transformPoint(v2, edge->v2->position);
		target->object->unTransformPoint(v2, v2);

		if (checkEdgeMeshCollision(v1, v2, targetmesh, normal, contactpoint)){
			target->object->transformVector(normal, normal);
			target->object->transformPoint(contactpoint, contactpoint);

			addCollision(object, target->object, normal, contactpoint);
			collided = true;
		}
	}

	return collided;
}

