package pl.vorg.mowa.core.graphics;

import com.framsticks.net.client3D.Log;


/**
 * Zbiór pomocniczych funkcji do stosowania na obiektach 3d
 * @author vorg
 *
 */
public class GeometryUtils {
	public static void generateNormals(Geometry geometry) {
		VertexStream vertexStream = geometry.getVertexStream();
		if (vertexStream == null) {
			Log.getInstance().log("err","GeometryUtils.generateNormals err: vertex stream not found");
			return;
		}
		if (vertexStream.getAttribByName("normal") == null) {
			FloatVertexAttrib normalAttrib = new FloatVertexAttrib("normal");
			vertexStream.addAttrib(normalAttrib);
			float[] normals = new float[((FloatVertexAttrib)vertexStream.getAttribByName("pos")).getBuffer().length];
			normalAttrib.setBuffer(normals);		
		}	
		if (geometry.getPrimitiveType() == PrimitiveType.Triangles) {
			genSmoothTriangleNormals(geometry.getVertexStream());
		}
		if (geometry.getPrimitiveType() == PrimitiveType.Quads) {
			genSmoothQuadNormals(geometry.getVertexStream());
		}
	}
	
	//a jakby tak na poczatku przerobic na tablice vektorow?
	private static void genSmoothTriangleNormals(VertexStream vertexStream) {
		IndexBuffer indexBuffer = vertexStream.getIndexBuffer();
		int[] indices = indexBuffer.getBuffer();
		FloatVertexAttrib posAttrib = (FloatVertexAttrib)vertexStream.getAttribByName("pos");
		float[] positions = posAttrib.getBuffer();
		FloatVertexAttrib normalAttrib = (FloatVertexAttrib)vertexStream.getAttribByName("normal");
		float[] normals = normalAttrib.getBuffer();
		normalAttrib.setBuffer(normals);
		Vec3[] faceNormalList = new Vec3[indices.length/3];
		
		for(int i=0; i<indices.length; i+=3) {
			Vec3 a = new Vec3(positions[indices[i  ]*3], positions[indices[i  ]*3+1], positions[indices[i  ]*3+2]);
			Vec3 b = new Vec3(positions[indices[i+1]*3], positions[indices[i+1]*3+1], positions[indices[i+1]*3+2]);
			Vec3 c = new Vec3(positions[indices[i+2]*3], positions[indices[i+2]*3+1], positions[indices[i+2]*3+2]);
			Vec3 bsa = Vec3.sub(b,a);
			Vec3 bsc = Vec3.sub(b,c);
			Vec3 n = Vec3.cross(bsc, bsa);
			n.normalize();
			faceNormalList[i/3] = n;
		}
		
		//dla kazdego wierzcholka
		for(int i=0; i<positions.length; i+=3) {
			//szukamy faceow ktorych jest elementem
			//i dodajemy normalna tego face'a
			float x = positions[i  ];
			float y = positions[i+1];
			float z = positions[i+2];
			Vec3 vertNormal = new Vec3(); 
			for(int j=0; j<indices.length; j++) {
				if ((x == positions[indices[j]*3]) && (y == positions[indices[j]*3+1]) && (z == positions[indices[j]*3+2])) {
					vertNormal.add(faceNormalList[j/3]);
				}				
			}
			vertNormal.normalize();
			normals[i  ] = vertNormal.getX();
			normals[i+1] = vertNormal.getY();
			normals[i+2] = vertNormal.getZ();
		}
	}
	
	private static void genSmoothQuadNormals(VertexStream vertexStream) {
		IndexBuffer indexBuffer = vertexStream.getIndexBuffer();
		int[] indices = indexBuffer.getBuffer();
		FloatVertexAttrib posAttrib = (FloatVertexAttrib)vertexStream.getAttribByName("pos");
		float[] positions = posAttrib.getBuffer();
		FloatVertexAttrib normalAttrib = (FloatVertexAttrib)vertexStream.getAttribByName("normal");
		float[] normals = new float[positions.length];
		normalAttrib.setBuffer(normals);
		Vec3[] faceNormalList = new Vec3[indices.length/4];
		
		for(int i=0; i<indices.length; i+=4) {
			Vec3 a = new Vec3(positions[indices[i  ]*3], positions[indices[i  ]*3+1], positions[indices[i  ]*3+2]);
			Vec3 b = new Vec3(positions[indices[i+1]*3], positions[indices[i+1]*3+1], positions[indices[i+1]*3+2]);
			Vec3 c = new Vec3(positions[indices[i+2]*3], positions[indices[i+2]*3+1], positions[indices[i+2]*3+2]);
			Vec3 bsa = Vec3.sub(b,a);
			Vec3 bsc = Vec3.sub(b,c);
			Vec3 n = Vec3.cross(bsc, bsa);
			n.normalize();
			faceNormalList[i/4] = n;
		}
		
		//dla kazdego wierzcholka
		for(int i=0; i<positions.length; i+=3) {
			//szukamy faceow ktorych jest elementem
			//i dodajemy normalna tego face'a
			float x = positions[i  ];
			float y = positions[i+1];
			float z = positions[i+2];
			Vec3 vertNormal = new Vec3(); 
			for(int j=0; j<indices.length; j++) {
				if ((x == positions[indices[j]*3]) && (y == positions[indices[j]*3+1]) && (z == positions[indices[j]*3+2])) {
					vertNormal.add(faceNormalList[j/4]);
				}				
			}
			vertNormal.normalize();
			normals[i  ] = vertNormal.getX();
			normals[i+1] = vertNormal.getY();
			normals[i+2] = vertNormal.getZ();
		}
	}
	
	//to w sumie nie jest do konca przetestowane
	public static void generateTangents(Geometry geometry) {
		VertexStream vertexStream = geometry.getVertexStream();
		if (vertexStream == null) {
			Log.getInstance().log("err", "GeometryUtils.generateTangents err: vertex stream not found");
			return;
		}
		if (vertexStream.getAttribByName("tangent") == null) {
			FloatVertexAttrib tangentAttrib = new FloatVertexAttrib("tangent");
			vertexStream.addAttrib(tangentAttrib);
			float[] tangents = new float[((FloatVertexAttrib)vertexStream.getAttribByName("pos")).getBuffer().length];
			tangentAttrib.setBuffer(tangents);
		}	
		if (vertexStream.getAttribByName("normal") == null) {
			FloatVertexAttrib normalAttrib = new FloatVertexAttrib("normal");
			vertexStream.addAttrib(normalAttrib);
			float[] normals = new float[((FloatVertexAttrib)vertexStream.getAttribByName("pos")).getBuffer().length];
			normalAttrib.setBuffer(normals);		
		}	
		if (geometry.getPrimitiveType() == PrimitiveType.Triangles) {
			genTriangleTangents(geometry.getVertexStream());
		}
	}
	
	private static void genTriangleTangents(VertexStream vertexStream) {
		IndexBuffer indexBuffer = vertexStream.getIndexBuffer();
		int[] indices = indexBuffer.getBuffer();
		FloatVertexAttrib posAttrib = (FloatVertexAttrib)vertexStream.getAttribByName("pos");
		float[] positions = posAttrib.getBuffer();
		FloatVertexAttrib texCoord0Attrib = (FloatVertexAttrib)vertexStream.getAttribByName("texCoord0");
		float[] texCoords0 = texCoord0Attrib.getBuffer();
		FloatVertexAttrib tangentAttrib = (FloatVertexAttrib)vertexStream.getAttribByName("tangent");
		float[] tangents = tangentAttrib.getBuffer();
		
		for(int i=0; i<indices.length; i+=3) {
			int i1 = indices[i  ];
			int i2 = indices[i+1];
			int i3 = indices[i+2];
			Vec3 pos1 = new Vec3(positions[i1]*3, positions[i1*3+1], positions[i1*3+2]);
			Vec3 pos2 = new Vec3(positions[i2]*3, positions[i2*3+1], positions[i2*3+2]);
			Vec3 pos3 = new Vec3(positions[i3]*3, positions[i3*3+1], positions[i3*3+2]);
			Vec2 coord1 = new Vec2(texCoords0[i1*2], texCoords0[i1*2+1]);
			Vec2 coord2 = new Vec2(texCoords0[i2*2], texCoords0[i2*2+1]);
			Vec2 coord3 = new Vec2(texCoords0[i3*2], texCoords0[i3*2+1]);			
			Vec3 v0 = Vec3.sub(pos1, pos2);
			Vec3 v1 = Vec3.sub(pos3, pos1);
			Vec3 normal = Vec3.normalized(Vec3.cross(v0, v1));
			
			float dt0 = coord1.getT() - coord2.getT();
			float dt1 = coord3.getT() - coord1.getT();
			Vec3 tangent = Vec3.normalized(Vec3.sub(Vec3.mul(dt1, v0), Vec3.mul(dt0, v1)));
			
			float ds0 = coord1.getS() - coord2.getS();
			float ds1 = coord3.getS() - coord1.getS();
			Vec3 binormal = Vec3.normalized(Vec3.sub(Vec3.mul(ds1, v0), Vec3.mul(ds0, v1)));
			
			Vec3 tangentCross = Vec3.cross(tangent, binormal);
			if (Vec3.dot(tangentCross, normal) < 0.0f) {
				tangent = Vec3.neg(tangent);
				binormal = Vec3.neg(binormal);
			}
			
			tangents[i1*3  ] = tangents[i1*3  ] + tangent.getX();
			tangents[i1*3+1] = tangents[i1*3+1] + tangent.getY();
			tangents[i1*3+2] = tangents[i1*3+2] + tangent.getZ();
			
			tangents[i2*3  ] = tangents[i2*3  ] + tangent.getX();
			tangents[i2*3+1] = tangents[i2*3+1] + tangent.getY();
			tangents[i2*3+2] = tangents[i2*3+2] + tangent.getZ();
			
			tangents[i3*3  ] = tangents[i3*3  ] + tangent.getX();
			tangents[i3*3+1] = tangents[i3*3+1] + tangent.getY();
			tangents[i3*3+2] = tangents[i3*3+2] + tangent.getZ();
		}
		
		for (int j=0; j<tangents.length; j+=3)
		{
			Vec3 tangent = new Vec3(tangents[j  ], tangents[j+1], tangents[j+2]);
			tangent.normalize();
			tangents[j  ] = tangent.getX();
			tangents[j+1] = tangent.getY();
			tangents[j+2] = tangent.getZ();
		}
		
	}
	/*
	void MeshUtils::generateTangents()
	{	
		VertexAttrib* tangentAttrib = m_vertexStream.getAttrib("TANGENT", TYPE_VEC3);
		if (tangentAttrib == NULL) {
			tangentAttrib = m_vertexStream.addAttrib("TANGENT", TYPE_VEC3);
		}
		vec3* tangentBuf = (vec3*)tangentAttrib->getBuffer();

		VertexAttrib* normalAttrib = m_vertexStream.getAttrib("NORMAL", TYPE_VEC3);
		vec3* normalBuf = (vec3*)normalAttrib->getBuffer();	
		
		VertexAttrib* posAttrib = m_vertexStream.getAttrib("POSITION", TYPE_VEC3);
		vec3* posBuf = (vec3*)posAttrib->getBuffer();	
		                                              
		VertexAttrib* tex0Attrib = m_vertexStream.getAttrib("TEXCOORD0", TYPE_VEC2);
		vec2* tex0Buf = (vec2*)tex0Attrib->getBuffer();

		uint32 numVertices = m_vertexStream.getNumVertices();
		uint32 numIndices = m_vertexStream.getNumIndices();
		uint32* indexBuf = m_vertexStream.getIndices();
		
		for(uint32 j=0; j<numIndices; j+=3) { //dla kazdego trojkata
			uint32 i1 = indexBuf[j];
			uint32 i2 = indexBuf[j+1];
			uint32 i3 = indexBuf[j+2];
		
			vec3 pos1 = posBuf[i1];
			vec3 pos2 = posBuf[i2];
			vec3 pos3 = posBuf[i3];
			vec2 coord1 = tex0Buf[i1];
			vec2 coord2 = tex0Buf[i2];
			vec2 coord3 = tex0Buf[i3];
			
			vec3 v0 = pos1 - pos2;
			vec3 v1 = pos3 - pos1;
			
			vec3 normal = cross(v0, v1);
			normal.normalize();
			
			float32 dt0 = coord1.t - coord2.t;
			float32 dt1 = coord3.t - coord1.t;
			vec3 tangent = dt1 * v0 - dt0 * v1;
			tangent.normalize();
			
			float32 ds0 = coord1.s - coord2.s;
			float32 ds1 = coord3.s - coord1.s;
			vec3 binormal = ds1 * v0 - ds0 * v1;
			binormal.normalize();
			
			vec3 tangentCross = cross(tangent, binormal);
			if (dot(tangentCross, normal) < 0.0f) {
				tangent = -tangent;
				binormal = -binormal;
			}
			
			tangentBuf[i1] += tangent;
			tangentBuf[i2] += tangent;
			tangentBuf[i3] += tangent;
		}
		
		vec3 n, t;
		for (uint32 j=0; j<numVertices; j++)
		{
			tangentBuf[j].normalize();
		}
		
		//smooth
		
		vec3 avgTangent;
		for (uint32 j=0; j<numVertices; j++) {		
			for (uint32 i=0; i<numVertices; i++) {
				if (i==j) continue;
				if ((posBuf[i] - posBuf[j]).length() < 0.001) {
					avgTangent = (tangentBuf[i] + tangentBuf[j]);
					avgTangent.normalize();
					tangentBuf[i] = avgTangent;
					tangentBuf[j] = avgTangent;
				}
			}
		}
		
	}*/
}
