/* Computer Graphics
 *
 * Filename ........ bsp.c
 * Description ..... Functions related to Binary Space-Partitioning trees
 * Date ............ 23.11.2009
 * Created by ...... Paul Melis
 *
 * Student name .... Sander van Veen & Taddeus Kroes
 * Student email ... sandervv@gmail.com & taddeuskroes@hotmail.com
 * Collegekaart .... 6169676 & 6054129
 * Date ............ 01.12.2010
 */

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "bsp.h"

#define DOTPROD_TRESHOLD 1.0e-3

/* Given a list of polygons builds a BSP tree by recursively choosing
 * one polygon that defines a split plane and dividing the other polygons
 * arbitrarily into left and right subsets. This does NOT result in a
 * correct BSP tree.
 *
 * Returns the root node of the BSP tree */

BSPNode*
BuildBSPTreeIncorrect(polys *polygons)
{
    polys   *left_polygons, *right_polygons;
    BSPNode *left_tree, *right_tree;
    BSPNode *root;
    poly    split_poly;

    // Arbitrary initial capacity for the lists, they will grow as
    // needed automatically
    left_polygons = CreatePolylist(4);
    right_polygons = CreatePolylist(4);

    // Pick a polygon which defines the split plane

    split_poly = polygons->items[0];

    // Arbitrarily assign polys to left and right sublists
    // This is where a "real" BSP construction routine would need
    // to do much more work...

    for (int i = 1; i < polygons->length; i++)
    {
        if (i % 2 == 0)
            AddPolyToPolylist(left_polygons, polygons->items[i]);
        else
            AddPolyToPolylist(right_polygons, polygons->items[i]);
    }

    // Recursively subdivide the left and right lists

    left_tree = NULL;
    if (left_polygons->length > 0)
        left_tree = BuildBSPTreeIncorrect(left_polygons);

    right_tree = NULL;
    if (right_polygons->length > 0)
        right_tree = BuildBSPTreeIncorrect(right_polygons);

    // We can free the child polygon lists, as each child polygon
    // will now have been stored in a BSP node

    DestroyPolylist(left_polygons);
    DestroyPolylist(right_polygons);

    // Create tree node and return it

    root = (BSPNode*) malloc(sizeof(BSPNode));

    root->polygon = split_poly;
    root->left_child = left_tree;
    root->right_child = right_tree;

    return root;
}

/* Checks if the line segment p1 -> p2 intersects the
 * plane defined by point plane_p and normal plane_n.
 *
 * Returns 1 if an intersection was found and sets t appropriately
 * (the intersection point will be (1-t)*p1 + t*p2)
 * Returns 0 otherwise. */

int
check_for_intersection(float *t, vec3 plane_p, vec3 plane_n, vec3 p1, vec3 p2)
{
    float   D = v3_dotprod(plane_n, plane_p);
    float   denom = v3_dotprod(plane_n, v3_subtract(p2, p1));

    if (fabs(denom) < 1.0e-6)
    {
        // Line segment parallel to the plane, no intersection possible
        return 0;
    }

    float tt = (D - v3_dotprod(plane_n, p1)) / denom;

    if (tt < 0.0 || tt > 1.0)
    {
        // Although the infinite line through p1 and p2 intersects
        // the plane, the line *segment* p1 -> p2 does not
        return 0;
    }

    // We have an intersection, set t
    *t = tt;

    return 1;
}

/* Triangle 'p' is known to intersect the plane defined
 * by 'plane_p' and 'plane_n'. One of the vertices of 'p'
 * (the one with index 'in_plane') is known to lie in the plane.
 * Array 'side' describes for each vertex of 'p' on which side
 * of the plane it lies (see comment for split_triangle()).
 *
 * Split the triangle along the plane and add the resulting
 * new triangles to the relevant list(s).
 *
 * Make sure to set the correct texture coordinates for each
 * edge intersection point and set the correct triangle normal
 * for new triangles.
 */

void
split_triangle_one_edge_intersection(polys *left_polygons, polys *right_polygons,
    vec3 plane_p, vec3 plane_n, poly p, int *side, int in_plane)
{
	// Indices to the other vertices, vl is the one left of
	// the split plane and vr is the one on the right
	int vl = (in_plane + 1) % 3, vr = (in_plane + 2) % 3, temp;
	float t;
	poly p2 = p;
	
	// Swap vl and vr if vl is on the right of the split plane
	if( side[vl] == 1 )
	{
		temp = vl;
		vl = vr;
		vr = temp;
	}
	
	// Find the intersection
	if( !check_for_intersection(&t, plane_p, plane_n, p.pts[vl], p.pts[vr]) )
	{
		puts("Plane does not go through the vertices");
		exit(1);
	}
	
	// Change one vertex of each triangle to the intersection on the plane,
	// do the same for the texture coordinates of these vertices
	p2.pts[vl] = p.pts[vr] =
		v3_add(v3_multiply(p.pts[vl], 1-t), v3_multiply(p.pts[vr], t));
	p2.tcoord[vl] = p.tcoord[vr] =
		v3_add(v3_multiply(p.tcoord[vl], 1-t), v3_multiply(p.tcoord[vr], t));
	
	// The triangle with vl should be on the left of the plane
	AddPolyToPolylist(left_polygons, p);
	AddPolyToPolylist(right_polygons, p2);
}

/* Triangle 'p' is known to intersect the plane defined
 * by 'plane_p' and 'plane_n'. One of the vertices of 'p'
 * (the one with index 'single') is known to be alone on
 * one side of the plane. Array 'side' describes for each
 * vertex of 'p' on which side of the plane it lies (see comment
 * for split_triangle()).
 *
 * Split the triangle along the plane and add the resulting
 * new triangles to the relevant list(s).
 *
 * Make sure to set the correct texture coordinates for each
 * edge intersection point and set the correct triangle normal
 * for new triangles.
 */

void
split_triangle_two_edge_intersections(polys *left_polygons, polys *right_polygons,
    vec3 plane_p, vec3 plane_n, poly p, int *side, int single)
{
	// Indices to the other vertices
	int v1 = (single + 1) % 3, v2 = (single + 2) % 3;
	float t1, t2;
	poly p2 = p, p3 = p;

	// Find intersection points
	if( !check_for_intersection(&t1, plane_p, plane_n, p.pts[single], p.pts[v1])
	    || !check_for_intersection(&t2, plane_p, plane_n, p.pts[single], p.pts[v2]) )
	{
		puts("Plane does not go through both edges");
		exit(1);
	}
	
	// Change the vertices of the triangles so that there's no overlap and
	// p is on the opposite side of the plane as p2 and p3. Do the same for
	// the texture coordinates
	p.pts[v1] = p3.pts[v1] = p2.pts[single] =
		v3_add(v3_multiply(p.pts[single], 1-t1), v3_multiply(p.pts[v1], t1));
	p.pts[v2] = p3.pts[single] =
		v3_add(v3_multiply(p.pts[single], 1-t2), v3_multiply(p.pts[v2], t2));

	p.tcoord[v1] = p3.tcoord[v1] = p2.tcoord[single] =
		v3_add(v3_multiply(p.tcoord[single], 1-t1), v3_multiply(p.tcoord[v1], t1));
	p.tcoord[v2] = p3.tcoord[single] =
		v3_add(v3_multiply(p.tcoord[single], 1-t2), v3_multiply(p.tcoord[v2], t2));
	
	// As said, p is on the opposite side as p2 and p3
	if( side[single] == -1 )
	{
		AddPolyToPolylist(left_polygons, p);
		AddPolyToPolylist(right_polygons, p2);
		AddPolyToPolylist(right_polygons, p3);
	}
	else
	{
		AddPolyToPolylist(right_polygons, p);
		AddPolyToPolylist(left_polygons, p2);
		AddPolyToPolylist(left_polygons, p3);
	}
}

/* Polygon 'p' (a triangle) is known to straddle the plane defined by
 * point 'plane_p' and normal 'plane_n'. The array 'side' describes
 * for each vertex of p on which side of the plane it lies
 * (-1 = left of, 1 = right of, 0 = on the plane). The length of
 * the 'side' array is equal to p.points.
 *
 * Split the triangle in multiple triangles based on intersections
 * of the triangle's edges with the plane, using the two split functions
 * above. Add the new triangles to the relevant list(s).
 */

void
split_triangle(polys *left_polygons, polys *right_polygons,
    vec3 plane_p, vec3 plane_n, poly p, int *side)
{
	for( int i = 0; i < 3; i++ )
	{
		if( !side[i] )
		{
			// Vertex found on plane, there's one intersection
			split_triangle_one_edge_intersection(left_polygons, right_polygons,
				plane_p, plane_n, p, side, i);
			return;
		}
	}

	// No vertex found on plane, there are two intersections
	split_triangle_two_edge_intersections(left_polygons, right_polygons, plane_p,
		plane_n, p, side, side[0] == side[1] ? 2 : (side[1] == side[2] ? 0 : 1));
}

/*
 * Create a correct BSP-tree for the given list of transparent
 * polygons. Return the root node of the tree.
 */

BSPNode*
BuildBSPTree(polys *polygons)
{
    polys *left_polygons, *right_polygons;
    BSPNode *left_tree, *right_tree;
    BSPNode *root;
    poly split_poly, p;
	float dot;
	vec3 plane_p, plane_n;
	int side[MAX_VERTICES], i, j, left, right;

    // Arbitrary initial capacity for the lists, they will grow as
    // needed automatically
    left_polygons = CreatePolylist(4);
    right_polygons = CreatePolylist(4);

    // Pick a polygon which defines the split plane
    split_poly = polygons->items[0];
	plane_p = split_poly.pts[0];
	plane_n = split_poly.normal[0];

    // Sort the remaining polygons in seperate trees, one left and one right of
	// the split plane. We'll assume that all polygons are triangles
	for( i = 1; i < polygons->length; i++ )
    {
		p = polygons->items[i];
		left = right = 0;

		// Classify vertices
		for( j = 0; j < p.points; j++ )
		{
			dot = v3_dotprod(v3_subtract(p.pts[j], plane_p), plane_n);
			
			if( dot > DOTPROD_TRESHOLD )
			{
				side[j] = -1; // Left
				left++;
			}
			else if( dot < -DOTPROD_TRESHOLD )
			{
				side[j] = 1; // Right
				right++;
			}
			else
				side[j] = 0; // On
		}

		// Classify triangle
		if( !right )
			AddPolyToPolylist(left_polygons, p); // Left
		else if( !left )
			AddPolyToPolylist(right_polygons, p); // Right
		else
			split_triangle(left_polygons, right_polygons, plane_p, plane_n, p, side); // Straddling
    }

    // Recursively subdivide the left and right lists
	left_tree = left_polygons->length ? BuildBSPTree(left_polygons) : NULL;
	right_tree = right_polygons->length ? BuildBSPTree(right_polygons) : NULL;

    // We can free the child polygon lists, as each child polygon
    // will now have been stored in a BSP node
    DestroyPolylist(left_polygons);
    DestroyPolylist(right_polygons);

    // Create tree node and return it
    root = (BSPNode*) malloc(sizeof(BSPNode));

    root->polygon = split_poly;
    root->left_child = left_tree;
    root->right_child = right_tree;

    return root;
}
