/*
 *                            COPYRIGHT
 *
 *  PCB, interactive printed circuit board design
 *  Copyright (C) 2009-2011 PCB Contributers (See ChangeLog for details)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>

/* The Linux OpenGL ABI 1.0 spec requires that we define
 * GL_GLEXT_PROTOTYPES before including gl.h or glx.h for extensions
 * in order to get prototypes:
 *   http://www.opengl.org/registry/ABI/
 */
#define GL_GLEXT_PROTOTYPES 1
#ifdef HAVE_OPENGL_GL_H
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif

#ifdef HAVE_OPENGL_GLU_H
#include <OpenGL/glu.h>
#else
#include <GL/glu.h>
#endif

#include "action_helper.h"
#include "crosshair.h"
#include "data.h"
#include "error.h"
#include "global.h"
#include "mymem.h"
#include "clip.h"

#include "hid.h"
#include "hidgl.h"
#include "rtree.h"



triangle_buffer buffer;
float global_depth = 0;

void hidgl_init_triangle_array(triangle_buffer * buffer)
{
	buffer->triangle_count = 0;
	buffer->coord_comp_count = 0;
}

void hidgl_flush_triangles(triangle_buffer * buffer)
{
	if (buffer->triangle_count == 0)
		return;

	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, buffer->triangle_array);
	glDrawArrays(GL_TRIANGLES, 0, buffer->triangle_count * 3);
	glDisableClientState(GL_VERTEX_ARRAY);

	buffer->triangle_count = 0;
	buffer->coord_comp_count = 0;
}

void hidgl_ensure_triangle_space(triangle_buffer * buffer, int count)
{
	if (count > TRIANGLE_ARRAY_SIZE) {
		fprintf(stderr, "Not enough space in vertex buffer\n");
		fprintf(stderr, "Requested %i triangles, %i available\n", count, TRIANGLE_ARRAY_SIZE);
		exit(1);
	}
	if (count > TRIANGLE_ARRAY_SIZE - buffer->triangle_count)
		hidgl_flush_triangles(buffer);
}

void hidgl_set_depth(float depth)
{
	global_depth = depth;
}

void hidgl_draw_grid(BoxType * drawn_area)
{
	static GLfloat *points = 0;
	static int npoints = 0;
	Coord x1, y1, x2, y2, n, i;
	double x, y;

	if (!Settings.DrawGrid)
		return;

	x1 = GridFit(MAX(0, drawn_area->X1), PCB->Grid, PCB->GridOffsetX);
	y1 = GridFit(MAX(0, drawn_area->Y1), PCB->Grid, PCB->GridOffsetY);
	x2 = GridFit(MIN(PCB->MaxWidth, drawn_area->X2), PCB->Grid, PCB->GridOffsetX);
	y2 = GridFit(MIN(PCB->MaxHeight, drawn_area->Y2), PCB->Grid, PCB->GridOffsetY);

	if (x1 > x2) {
		Coord tmp = x1;
		x1 = x2;
		x2 = tmp;
	}

	if (y1 > y2) {
		Coord tmp = y1;
		y1 = y2;
		y2 = tmp;
	}

	n = (int) ((x2 - x1) / PCB->Grid + 0.5) + 1;
	if (n > npoints) {
		npoints = n + 10;
		points = realloc(points, npoints * 3 * sizeof(GLfloat));
	}

	glEnableClientState(GL_VERTEX_ARRAY);
	glVertexPointer(3, GL_FLOAT, 0, points);

	n = 0;
	for (x = x1; x <= x2; x += PCB->Grid) {
		points[3 * n + 0] = x;
		points[3 * n + 2] = global_depth;
		n++;
	}
	for (y = y1; y <= y2; y += PCB->Grid) {
		for (i = 0; i < n; i++)
			points[3 * i + 1] = y;
		glDrawArrays(GL_POINTS, 0, n);
	}

	glDisableClientState(GL_VERTEX_ARRAY);
}

#define MAX_PIXELS_ARC_TO_CHORD 0.5
#define MIN_SLICES 6
int calc_slices(float pix_radius, float sweep_angle)
{
	float slices;

	if (pix_radius <= MAX_PIXELS_ARC_TO_CHORD)
		return MIN_SLICES;

	slices = sweep_angle / acosf(1 - MAX_PIXELS_ARC_TO_CHORD / pix_radius) / 2.;
	return (int) ceilf(slices);
}

#define MIN_TRIANGLES_PER_CAP 3
#define MAX_TRIANGLES_PER_CAP 90
static void draw_cap(Coord width, Coord x, Coord y, Angle angle, double scale)
{
	float last_capx, last_capy;
	float capx, capy;
	float radius = width / 2.;
	int slices = calc_slices(radius / scale, M_PI);
	int i;

	if (slices < MIN_TRIANGLES_PER_CAP)
		slices = MIN_TRIANGLES_PER_CAP;

	if (slices > MAX_TRIANGLES_PER_CAP)
		slices = MAX_TRIANGLES_PER_CAP;

	hidgl_ensure_triangle_space(&buffer, slices);

	last_capx = radius * cosf(angle * M_PI / 180.) + x;
	last_capy = -radius * sinf(angle * M_PI / 180.) + y;
	for (i = 0; i < slices; i++) {
		capx = radius * cosf(angle * M_PI / 180. + ((float) (i + 1)) * M_PI / (float) slices) + x;
		capy = -radius * sinf(angle * M_PI / 180. + ((float) (i + 1)) * M_PI / (float) slices) + y;
		hidgl_add_triangle(&buffer, last_capx, last_capy, capx, capy, x, y);
		last_capx = capx;
		last_capy = capy;
	}
}

void hidgl_draw_line(int cap, Coord width, Coord x1, Coord y1, Coord x2, Coord y2, double scale)
{
	double angle;
	float deltax, deltay, length;
	float wdx, wdy;
	int circular_caps = 0;
	int hairline = 0;

	if (width == 0.0)
		hairline = 1;

	if (width < scale)
		width = scale;

	deltax = x2 - x1;
	deltay = y2 - y1;

	length = sqrt(deltax * deltax + deltay * deltay);

	if (length == 0) {
		/* Assume the orientation of the line is horizontal */
		angle = 0;
		wdx = -width / 2.;
		wdy = 0;
		length = 1.;
		deltax = 1.;
		deltay = 0.;
	}
	else {
		wdy = deltax * width / 2. / length;
		wdx = -deltay * width / 2. / length;

		if (deltay == 0.)
			angle = (deltax < 0) ? 270. : 90.;
		else
			angle = 180. / M_PI * atanl(deltax / deltay);

		if (deltay < 0)
			angle += 180.;
	}

	switch (cap) {
	case Trace_Cap:
	case Round_Cap:
		circular_caps = 1;
		break;

	case Square_Cap:
	case Beveled_Cap:
		x1 -= deltax * width / 2. / length;
		y1 -= deltay * width / 2. / length;
		x2 += deltax * width / 2. / length;
		y2 += deltay * width / 2. / length;
		break;
	}

	hidgl_ensure_triangle_space(&buffer, 2);
	hidgl_add_triangle(&buffer, x1 - wdx, y1 - wdy, x2 - wdx, y2 - wdy, x2 + wdx, y2 + wdy);
	hidgl_add_triangle(&buffer, x1 - wdx, y1 - wdy, x2 + wdx, y2 + wdy, x1 + wdx, y1 + wdy);

	/* Don't bother capping hairlines */
	if (circular_caps && !hairline) {
		draw_cap(width, x1, y1, angle, scale);
		draw_cap(width, x2, y2, angle + 180., scale);
	}
}

#define MIN_SLICES_PER_ARC 6
#define MAX_SLICES_PER_ARC 360
void hidgl_draw_arc(Coord width, Coord x, Coord y, Coord rx, Coord ry, Angle start_angle, Angle delta_angle, double scale)
{
	float last_inner_x, last_inner_y;
	float last_outer_x, last_outer_y;
	float inner_x, inner_y;
	float outer_x, outer_y;
	float inner_r;
	float outer_r;
	float cos_ang, sin_ang;
	float start_angle_rad;
	float delta_angle_rad;
	float angle_incr_rad;
	int slices;
	int i;
	int hairline = 0;

	if (width == 0.0)
		hairline = 1;

	if (width < scale)
		width = scale;

	inner_r = rx - width / 2.;
	outer_r = rx + width / 2.;

	if (delta_angle < 0) {
		start_angle += delta_angle;
		delta_angle = -delta_angle;
	}

	start_angle_rad = start_angle * M_PI / 180.;
	delta_angle_rad = delta_angle * M_PI / 180.;

	slices = calc_slices((rx + width / 2.) / scale, delta_angle_rad);

	if (slices < MIN_SLICES_PER_ARC)
		slices = MIN_SLICES_PER_ARC;

	if (slices > MAX_SLICES_PER_ARC)
		slices = MAX_SLICES_PER_ARC;

	hidgl_ensure_triangle_space(&buffer, 2 * slices);

	angle_incr_rad = delta_angle_rad / (float) slices;

	cos_ang = cosf(start_angle_rad);
	sin_ang = sinf(start_angle_rad);
	last_inner_x = -inner_r * cos_ang + x;
	last_inner_y = inner_r * sin_ang + y;
	last_outer_x = -outer_r * cos_ang + x;
	last_outer_y = outer_r * sin_ang + y;
	for (i = 1; i <= slices; i++) {
		cos_ang = cosf(start_angle_rad + ((float) (i)) * angle_incr_rad);
		sin_ang = sinf(start_angle_rad + ((float) (i)) * angle_incr_rad);
		inner_x = -inner_r * cos_ang + x;
		inner_y = inner_r * sin_ang + y;
		outer_x = -outer_r * cos_ang + x;
		outer_y = outer_r * sin_ang + y;
		hidgl_add_triangle(&buffer, last_inner_x, last_inner_y, last_outer_x, last_outer_y, outer_x, outer_y);
		hidgl_add_triangle(&buffer, last_inner_x, last_inner_y, inner_x, inner_y, outer_x, outer_y);
		last_inner_x = inner_x;
		last_inner_y = inner_y;
		last_outer_x = outer_x;
		last_outer_y = outer_y;
	}

	/* Don't bother capping hairlines */
	if (hairline)
		return;

	draw_cap(width, x + rx * -cosf(start_angle_rad), y + rx * sinf(start_angle_rad), start_angle, scale);
	draw_cap(width, x + rx * -cosf(start_angle_rad + delta_angle_rad),
					 y + rx * sinf(start_angle_rad + delta_angle_rad), start_angle + delta_angle + 180., scale);
}

void hidgl_draw_rect(Coord x1, Coord y1, Coord x2, Coord y2)
{
	glBegin(GL_LINE_LOOP);
	glVertex3f(x1, y1, global_depth);
	glVertex3f(x1, y2, global_depth);
	glVertex3f(x2, y2, global_depth);
	glVertex3f(x2, y1, global_depth);
	glEnd();
}


void hidgl_fill_circle(Coord vx, Coord vy, Coord vr, double scale)
{
#define MIN_TRIANGLES_PER_CIRCLE 6
#define MAX_TRIANGLES_PER_CIRCLE 360
	float last_x, last_y;
	float radius = vr;
	int slices;
	int i;

	slices = calc_slices(vr / scale, 2 * M_PI);

	if (slices < MIN_TRIANGLES_PER_CIRCLE)
		slices = MIN_TRIANGLES_PER_CIRCLE;

	if (slices > MAX_TRIANGLES_PER_CIRCLE)
		slices = MAX_TRIANGLES_PER_CIRCLE;

	hidgl_ensure_triangle_space(&buffer, slices);

	last_x = vx + vr;
	last_y = vy;

	for (i = 0; i < slices; i++) {
		float x, y;
		x = radius * cosf(((float) (i + 1)) * 2. * M_PI / (float) slices) + vx;
		y = radius * sinf(((float) (i + 1)) * 2. * M_PI / (float) slices) + vy;
		hidgl_add_triangle(&buffer, vx, vy, last_x, last_y, x, y);
		last_x = x;
		last_y = y;
	}
}

#define MAX_COMBINED_MALLOCS 2500
static void *combined_to_free[MAX_COMBINED_MALLOCS];
static int combined_num_to_free = 0;

static GLenum tessVertexType;
static int stashed_vertices;
static int triangle_comp_idx;


static void myError(GLenum errno)
{
	printf("gluTess error: %s\n", gluErrorString(errno));
}

static void myFreeCombined()
{
	while (combined_num_to_free)
		free(combined_to_free[--combined_num_to_free]);
}

static void myCombine(GLdouble coords[3], void *vertex_data[4], GLfloat weight[4], void **dataOut)
{
#define MAX_COMBINED_VERTICES 2500
	static GLdouble combined_vertices[3 * MAX_COMBINED_VERTICES];
	static int num_combined_vertices = 0;

	GLdouble *new_vertex;

	if (num_combined_vertices < MAX_COMBINED_VERTICES) {
		new_vertex = &combined_vertices[3 * num_combined_vertices];
		num_combined_vertices++;
	}
	else {
		new_vertex = malloc(3 * sizeof(GLdouble));

		if (combined_num_to_free < MAX_COMBINED_MALLOCS)
			combined_to_free[combined_num_to_free++] = new_vertex;
		else
			printf("myCombine leaking %lu bytes of memory\n", 3 * sizeof(GLdouble));
	}

	new_vertex[0] = coords[0];
	new_vertex[1] = coords[1];
	new_vertex[2] = coords[2];

	*dataOut = new_vertex;
}

static void myBegin(GLenum type)
{
	tessVertexType = type;
	stashed_vertices = 0;
	triangle_comp_idx = 0;
}

static double global_scale;

static void myVertex(GLdouble * vertex_data)
{
	static GLfloat triangle_vertices[2 * 3];

	if (tessVertexType == GL_TRIANGLE_STRIP || tessVertexType == GL_TRIANGLE_FAN) {
		if (stashed_vertices < 2) {
			triangle_vertices[triangle_comp_idx++] = vertex_data[0];
			triangle_vertices[triangle_comp_idx++] = vertex_data[1];
			stashed_vertices++;
		}
		else {
			hidgl_ensure_triangle_space(&buffer, 1);
			hidgl_add_triangle(&buffer,
												 triangle_vertices[0], triangle_vertices[1],
												 triangle_vertices[2], triangle_vertices[3], vertex_data[0], vertex_data[1]);

			if (tessVertexType == GL_TRIANGLE_STRIP) {
				/* STRIP saves the last two vertices for re-use in the next triangle */
				triangle_vertices[0] = triangle_vertices[2];
				triangle_vertices[1] = triangle_vertices[3];
			}
			/* Both FAN and STRIP save the last vertex for re-use in the next triangle */
			triangle_vertices[2] = vertex_data[0];
			triangle_vertices[3] = vertex_data[1];
		}
	}
	else if (tessVertexType == GL_TRIANGLES) {
		triangle_vertices[triangle_comp_idx++] = vertex_data[0];
		triangle_vertices[triangle_comp_idx++] = vertex_data[1];
		stashed_vertices++;
		if (stashed_vertices == 3) {
			hidgl_ensure_triangle_space(&buffer, 1);
			hidgl_add_triangle(&buffer,
												 triangle_vertices[0], triangle_vertices[1],
												 triangle_vertices[2], triangle_vertices[3], triangle_vertices[4], triangle_vertices[5]);
			triangle_comp_idx = 0;
			stashed_vertices = 0;
		}
	}
	else
		printf("Vertex received with unknown type\n");
}

void hidgl_fill_polygon(int n_coords, Coord * x, Coord * y)
{
	int i;
	GLUtesselator *tobj;
	GLdouble *vertices;

	assert(n_coords > 0);

	vertices = malloc(sizeof(GLdouble) * n_coords * 3);

	tobj = gluNewTess();
	gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr) myBegin);
	gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr) myVertex);
	gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr) myCombine);
	gluTessCallback(tobj, GLU_TESS_ERROR, (_GLUfuncptr) myError);

	gluTessBeginPolygon(tobj, NULL);
	gluTessBeginContour(tobj);

	for (i = 0; i < n_coords; i++) {
		vertices[0 + i * 3] = x[i];
		vertices[1 + i * 3] = y[i];
		vertices[2 + i * 3] = 0.;
		gluTessVertex(tobj, &vertices[i * 3], &vertices[i * 3]);
	}

	gluTessEndContour(tobj);
	gluTessEndPolygon(tobj);
	gluDeleteTess(tobj);

	myFreeCombined();
	free(vertices);
}

void tesselate_contour(GLUtesselator * tobj, PLINE * contour, GLdouble * vertices, double scale)
{
	VNODE *vn = &contour->head;
	int offset = 0;

	/* If the contour is round, and hidgl_fill_circle would use
	 * less slices than we have vertices to draw it, then call
	 * hidgl_fill_circle to draw this contour.
	 */
	if (contour->is_round) {
		double slices = calc_slices(contour->radius / scale, 2 * M_PI);
		if (slices < contour->Count) {
			hidgl_fill_circle(contour->cx, contour->cy, contour->radius, scale);
			return;
		}
	}

	gluTessBeginPolygon(tobj, NULL);
	gluTessBeginContour(tobj);
	do {
		vertices[0 + offset] = vn->point[0];
		vertices[1 + offset] = vn->point[1];
		vertices[2 + offset] = 0.;
		gluTessVertex(tobj, &vertices[offset], &vertices[offset]);
		offset += 3;
	} while ((vn = vn->next) != &contour->head);
	gluTessEndContour(tobj);
	gluTessEndPolygon(tobj);
}

struct do_hole_info {
	GLUtesselator *tobj;
	GLdouble *vertices;
	double scale;
};

static r_dir_t do_hole(const BoxType * b, void *cl)
{
	struct do_hole_info *info = cl;
	PLINE *curc = (PLINE *) b;

	/* Ignore the outer contour - we draw it first explicitly */
	if (curc->Flags.orient == PLF_DIR) {
		return R_DIR_NOT_FOUND;
	}

	tesselate_contour(info->tobj, curc, info->vertices, info->scale);
	return R_DIR_FOUND_CONTINUE;
}

static GLint stencil_bits;
static int dirty_bits = 0;
static int assigned_bits = 0;

/* FIXME: JUST DRAWS THE FIRST PIECE.. TODO: SUPPORT FOR FULLPOLY POLYGONS */
void hidgl_fill_pcb_polygon(PolygonType * poly, const BoxType * clip_box, double scale)
{
	int vertex_count = 0;
	PLINE *contour;
	struct do_hole_info info;
	int stencil_bit;

	info.scale = scale;
	global_scale = scale;

	if (poly->Clipped == NULL) {
		fprintf(stderr, "hidgl_fill_pcb_polygon: poly->Clipped == NULL\n");
		return;
	}

	stencil_bit = hidgl_assign_clear_stencil_bit();
	if (!stencil_bit) {
		printf("hidgl_fill_pcb_polygon: No free stencil bits, aborting polygon\n");
		return;
	}

	/* Flush out any existing geoemtry to be rendered */
	hidgl_flush_triangles(&buffer);

	/* Walk the polygon structure, counting vertices */
	/* This gives an upper bound on the amount of storage required */
	for (contour = poly->Clipped->contours; contour != NULL; contour = contour->next)
		vertex_count = MAX(vertex_count, contour->Count);

	info.vertices = malloc(sizeof(GLdouble) * vertex_count * 3);
	info.tobj = gluNewTess();
	gluTessCallback(info.tobj, GLU_TESS_BEGIN, (_GLUfuncptr) myBegin);
	gluTessCallback(info.tobj, GLU_TESS_VERTEX, (_GLUfuncptr) myVertex);
	gluTessCallback(info.tobj, GLU_TESS_COMBINE, (_GLUfuncptr) myCombine);
	gluTessCallback(info.tobj, GLU_TESS_ERROR, (_GLUfuncptr) myError);

	glPushAttrib(GL_STENCIL_BUFFER_BIT);	/* Save the write mask etc.. for final restore */
	glEnable(GL_STENCIL_TEST);
	glPushAttrib(GL_STENCIL_BUFFER_BIT |	/* Resave the stencil write-mask etc.., and */
							 GL_COLOR_BUFFER_BIT);	/* the colour buffer write mask etc.. for part way restore */
	glStencilMask(stencil_bit);		/* Only write to our stencil bit */
	glStencilFunc(GL_ALWAYS, stencil_bit, stencil_bit);	/* Always pass stencil test, ref value is our bit */
	glColorMask(0, 0, 0, 0);			/* Disable writting in color buffer */

	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);	/* Stencil pass => replace stencil value */

	/* Drawing operations now set our reference bit in the stencil buffer */

	r_search(poly->Clipped->contour_tree, clip_box, NULL, do_hole, &info, NULL);
	hidgl_flush_triangles(&buffer);

	glPopAttrib();								/* Restore the colour and stencil buffer write-mask etc.. */

	glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT);	/* This allows us to toggle the bit on any subcompositing bitplane */
	/* If the stencil test has passed, we know that bit is 0, so we're */
	/* effectively just setting it to 1. */

	glStencilFunc(GL_GEQUAL, 0, assigned_bits);	/* Pass stencil test if all assigned bits clear, */
	/* reference is all assigned bits so we set */
	/* any bits permitted by the stencil writemask */

	/* Drawing operations as masked to areas where the stencil buffer is '0' */

	/* Draw the polygon outer */
	tesselate_contour(info.tobj, poly->Clipped->contours, info.vertices, scale);
	hidgl_flush_triangles(&buffer);

	/* Unassign our stencil buffer bit */
	hidgl_return_stencil_bit(stencil_bit);

	glPopAttrib();								/* Restore the stencil buffer write-mask etc.. */

	gluDeleteTess(info.tobj);
	myFreeCombined();
	free(info.vertices);
}

void hidgl_fill_rect(Coord x1, Coord y1, Coord x2, Coord y2)
{
	hidgl_ensure_triangle_space(&buffer, 2);
	hidgl_add_triangle(&buffer, x1, y1, x1, y2, x2, y2);
	hidgl_add_triangle(&buffer, x2, y1, x2, y2, x1, y1);
}

void hidgl_init(void)
{
	glGetIntegerv(GL_STENCIL_BITS, &stencil_bits);

	if (stencil_bits == 0) {
		printf("No stencil bits available.\n" "Cannot mask polygon holes or subcomposite layers\n");
		/* TODO: Flag this to the HID so it can revert to the dicer? */
	}
	else if (stencil_bits == 1) {
		printf("Only one stencil bitplane avilable\n" "Cannot use stencil buffer to sub-composite layers.\n");
		/* Do we need to disable that somewhere? */
	}
}

int hidgl_stencil_bits(void)
{
	return stencil_bits;
}

static void hidgl_clean_unassigned_stencil(void)
{
	glPushAttrib(GL_STENCIL_BUFFER_BIT);
	glStencilMask(~assigned_bits);
	glClearStencil(0);
	glClear(GL_STENCIL_BUFFER_BIT);
	glPopAttrib();
}

int hidgl_assign_clear_stencil_bit(void)
{
	int stencil_bitmask = (1 << stencil_bits) - 1;
	int test;
	int first_dirty = 0;

	if (assigned_bits == stencil_bitmask) {
		printf("No more stencil bits available, total of %i already assigned\n", stencil_bits);
		return 0;
	}

	/* Look for a bitplane we don't have to clear */
	for (test = 1; test & stencil_bitmask; test <<= 1) {
		if (!(test & dirty_bits)) {
			assigned_bits |= test;
			dirty_bits |= test;
			return test;
		}
		else if (!first_dirty && !(test & assigned_bits)) {
			first_dirty = test;
		}
	}

	/* Didn't find any non dirty planes. Clear those dirty ones which aren't in use */
	hidgl_clean_unassigned_stencil();
	assigned_bits |= first_dirty;
	dirty_bits = assigned_bits;

	return first_dirty;
}

void hidgl_return_stencil_bit(int bit)
{
	assigned_bits &= ~bit;
}

void hidgl_reset_stencil_usage(void)
{
	assigned_bits = 0;
	dirty_bits = 0;
}
