/* PEX.c ---- outdated ... it needs to be fixed ... */

#include <stdlib.h>
#include <stdio.h>

#include <Xm/Xm.h>
#include <Xm/DrawingA.h>

#include "render.h"
#include "canvas.h"
#include "camera.h"
#include "scene.h"
#include "error.h"
#include "Memory.h"
#include "globals.h"

static float maximpoverarea;

#include "pex.h"
#ifdef DOUBLE_BUFFERING
#include "mbx.h"
#endif /*DOUBLE_BUFFERING*/

extern XVisualInfo		best_visual;
extern XStandardColormap	std_cmap;
static PEXColorApproxEntry	capx_info;
static PEXExtensionInfo 	*pexinfo;

static PEXRenderer 		renderer;
static PEXRendererAttributes 	rattrs;
static Display			*dpy;
static Window			window;

#ifdef DOUBLE_BUFFERING
static Multibuffer		buffers[2], cur_buf;
static int double_buffering = FALSE;
#endif /*DOUBLE_BUFFERING*/

static int pex_available = FALSE,
           renderer_created = FALSE;

typedef struct RENDEROPTIONS {
	char	gouraud_shading,  /* True voor Gouraudshading, False voor flat shading */
		draw_outlines,	  /* True om randjes van facetten te tekenen */
	        showlinks,	  /* True om ook de links te laten zien */
	        backface_culling, /* True om backface_culling te doen */
	        mode;		  /* REFLECTIVITY, RADIOSITY, ... */
	RGB 	outline_color,	  /* kleur waarin randjes van facetten getekend worden */
	        link_color;	  /* kleur waarin links geteknd moeten worden */
} RENDEROPTIONS;

static COLOR ambient_radiance;

static RENDEROPTIONS renderopts;

/* Gouraud shading wordt momenteel nog niet gedaan. */
void RenderSetOptions(char gouraud_shading, 
		      char backface_culling,
		      char draw_outlines, 
		      RGB *outline_color,
		      char showlinks,
		      RGB *link_color)
{
	renderopts.gouraud_shading = gouraud_shading;
	renderopts.backface_culling = backface_culling;
	renderopts.draw_outlines = draw_outlines;
	renderopts.outline_color = *outline_color;
	renderopts.showlinks = showlinks;
	renderopts.link_color = *link_color;
}

void RenderSetGouraudShading(char gouraud_shading)
{
	renderopts.gouraud_shading = gouraud_shading;	
}

void RenderSetBackfaceCulling(char backface_culling)
{
	renderopts.backface_culling = backface_culling;
}

void RenderSetOutlineDrawing(char draw_outlines)
{
	renderopts.draw_outlines = draw_outlines;
}

void RenderSetOutlineColor(RGB *outline_color)
{
	renderopts.outline_color = *outline_color;
}

void RenderShowLinks(char showlinks)
{
	renderopts.showlinks = showlinks;
}

void RenderSetLinkColor(RGB *link_color)
{
	renderopts.link_color = *link_color;
}

void RenderSetMode(char mode)
{
	renderopts.mode = mode;
}

char RenderGetMode(void)
{
	return renderopts.mode;
}

void RenderSetAmbientRadiance(COLOR *ambient)
{
	ambient_radiance = *ambient;
}

/* Maak de PEX renderer klaar voor immediate mode rendering */
static void BeginRendering(void)
{
#ifdef DOUBLE_BUFFERING
	if (!double_buffering)
#endif /*DOUBLE_BUFFERING*/
		PEXBeginRendering(dpy, window, renderer);
#ifdef DOUBLE_BUFFERING
	else
		PEXBeginRendering(dpy, cur_buf, renderer);
#endif /*DOUBLE_BUFFERING*/
}

/* Renderen (voorlopig) beeindigen */
static void EndRendering(void)
{
	PEXEndRendering(dpy, renderer, True /* flush */);
#ifdef DOUBLE_BUFFERING
	/* Display the buffer. */
	XmbufDisplayBuffers( dpy, 1, &cur_buf, 0, 0 );
	XFlush( dpy );
    
	/* Make the undisplayed buffer the drawing buffer. */
	cur_buf = (cur_buf == buffers[0] ? buffers[1] : buffers[0]);
#endif /*DOUBLE_BUFFERING*/
}

static void RenderClearWindow(void)
{
	PEXColor bkg, edge_color;

/* achtergrondskleur instellen */
	bkg.rgb.red = Camera.background.r;
	bkg.rgb.green = Camera.background.g;
	bkg.rgb.blue = Camera.background.b;
	rattrs.background_color.type = PEXColorTypeRGB;
	rattrs.background_color.value = bkg;	

/* Bij de volgende PEXBeginRendering wordt het beeld en de eventuele 
 * Z-buffer gewist. */
	rattrs.clear_image = rattrs.clear_z = True;

	PEXChangeRenderer(dpy, renderer, 
			  PEXRABackgroundColor|PEXRAClearImage|PEXRAClearZ, 
			  &rattrs);
#ifndef DOUBLE_BUFFERING
	XClearWindow(dpy, window);
#endif /*DOUBLE_BUFFERING*/
	BeginRendering();

	PEXSetViewIndex(dpy, renderer, PEXOCRender, VIEW_INDEX);

/* kleur om de randen van facetten te tekenen instellen */
	edge_color.rgb.red = renderopts.outline_color.r;
	edge_color.rgb.green = renderopts.outline_color.g;
	edge_color.rgb.blue = renderopts.outline_color.b;
	PEXSetSurfaceEdgeColor(dpy, renderer, PEXOCRender, PEXColorTypeRGB, &edge_color);

	PEXSetSurfaceEdgeFlag(dpy, renderer, PEXOCRender, renderopts.draw_outlines ? PEXOn : PEXOff);
}

/* bepaal afstand tot front- en backclipping plane */
static void GetNearFar(float *near, float *far)
{
	BOUNDINGBOX bounds;
	VECTOR b[2];
	int i, j, k;
	float z;

	if (!World) {
		*far = 10.; *near = 0.1;	/* zomaar iets */
		return;
	}
	GeomListBounds(World, bounds);

	b[0] = *(VECTOR *)&bounds[MIN_X];
	b[1] = *(VECTOR *)&bounds[MAX_X];

	*far = -HUGE; *near = HUGE;
	for (i=0; i<=1; i++)
		for (j=0; j<=1; j++)
			for (k=0; k<=1; k++) {
				z = (b[i].x - Camera.eyep.x) * Camera.Z.x +
				    (b[j].y - Camera.eyep.y) * Camera.Z.y +	
				    (b[k].z - Camera.eyep.z) * Camera.Z.z;
				if (z > *far) *far = z;
				if (z < *near) *near = z;
			}

	*far += EPSILON; *near -= EPSILON;
	if (*far < EPSILON) *far = Camera.viewdist;
	if (*near < EPSILON) *near = Camera.viewdist/100.;
}

void RenderSetCamera(void)
{
/* View Reference Point: bepaalt samen met de view plane normal het
 * projectievlak in wereldcoordinaten. vup = View UP vector */
	PEXCoord vrp; 
	PEXVector vpn, vup;

/* rechthoek in het projectievlak dat overeenkomt met een window op het 
 * scherm. (0,0) = vrp */
	PEXCoord2D frame[2];	

/* deelvolume in geNormaliseerde Projectie Coordinaten waarop het
 * viewing volume afgebeeld wordt: front-clipping plane komt overeen
 * met zmax, back clipping plane met zmin, frame[0].x met xmin, ... 
 * gebruikt voor clipping */
	PEXNPCSubVolume  viewport;	

/* Projection Reference Point: bij parallelle projectie zijn alle
 * projectoren evenwijdig met deze vector (gegeven in View Reference 
 * Coordinaten, d.i. na transformatie van vrp naar (0,0,0) en vpn naar
 * (0,0,1) ...). Bij perspectief-projectie komen alle projectoren samen in 
 * dit punt. Dit punt komt dan overeen met het oogpunt. Z-coordinaat moet
 * dan de afstand van oogpunt tot referentiepunt in de scene zijn. */
	PEXCoord prp;

/* bevat:
 * - view orientatie matrix: voor transformatie van wereldcoordinaten naar 
 *   View Reference Coordinaten,
 * - view mapping matrix: transformeert View Reference Coordinaten naar
 *   geNormaliseerde Projectie Coordinaten (Z=0 komt overeen met
 *   back clipping vlak, Z=1 met front clipping vlak, X=0 met linker
 *   zijkant van het window ... 
 * - clipping flags 
 * - viewport: deelvolume in geNormaliseerde Projectie Coordinaten 
 *   dat afgebeeld wordt */
	PEXViewEntry 	view_entry;
	
	VECTOR d;
	double view_dist, aspect, back_plane, front_plane, view_plane, w;
	float near, far;

	if (!renderer_created) 
		return;

/* PEX view oriention matrix - zie PEX documentatie */
	vrp.x = Camera.lookp.x;
	vrp.y = Camera.lookp.y;
	vrp.z = Camera.lookp.z;

	VECTORSUBSTRACT(Camera.eyep, Camera.lookp, d);
	view_dist = VECTORNORM(d);
	vpn.x = d.x/view_dist;
	vpn.y = d.y/view_dist;
	vpn.z = d.z/view_dist;

	vup.x = Camera.updir.x;
	vup.y = Camera.updir.y;
	vup.z = Camera.updir.z;

	PEXViewOrientationMatrix(&vrp, &vpn, &vup, view_entry.orientation);

/* PEX view mapping matrix */
	view_plane = 0;
	prp.x = 0; prp.y = 0; prp.z = view_dist;
	
	w = view_dist * tan(Camera.fov * M_PI / 180.);	

	viewport.min.x = 0;  viewport.max.x = 1;
	viewport.min.y = 0;  viewport.max.y = 1;
	viewport.min.z = 0;  viewport.max.z = 1;

/* aspect: verhouding van breedte en hoogte van het window waarin gerenderd wordt */
	aspect = (double)Camera.hres/(double)Camera.vres;

	if (aspect > 1.) {	/* breder dan hoog */
		frame[0].x = -w * aspect; 
		frame[1].x =  w * aspect;
		frame[0].y = -w;
		frame[1].y =  w;
		viewport.max.y /= aspect;
	} else {		/* hoger dan breed */
		viewport.max.x *= aspect;
		frame[0].x = -w;
		frame[1].x =  w;
		frame[0].y = -w / aspect; 
		frame[1].y =  w / aspect;
	}

/* Z-coordinaat wordt in PEX positief gerekend naar het oogpunt toe */
	GetNearFar(&near, &far);
	front_plane = view_dist-near; back_plane = view_dist-far;

	view_entry.clip_flags = PEXClipXY|PEXClipFront;		

	if (PEXViewMappingMatrix(frame, &viewport, 
				 True /* perspectief transformatie */,
				 &prp, view_plane, back_plane, front_plane,
				 view_entry.mapping) != 0) {
		Error(NULL, "Fouten bij het instellen van het aanzicht in PEX");
		return;
	}

	view_entry.clip_limits = viewport;

/* berekende PEX aanzicht instellen als entry 1 in de PEX 
 * renderer aanzichtentabel */
	PEXSetTableEntries(dpy, rattrs.view_table, VIEW_INDEX, 1, PEXLUTView, &view_entry);

	rattrs.npc_subvolume = viewport;
	PEXChangeRenderer(dpy, renderer, PEXRANPCSubVolume, &rattrs);

	RenderClearWindow();
}

static void ora_set_stdcmap_approx(XVisualInfo		*vis_info,
				   XStandardColormap	*cmap_info,
				   PEXColorApproxEntry	*capx_info)
/* book_utils.c 1.4 */
/*
    Copyright 1992, 1993 O'Reilly and Associates, Inc.  Permission to
    use, copy, and modify this program is hereby granted, as long as
    this copyright notice appears in each copy of the program source
    code.
*/
{
	switch ( vis_info->class ) {
	case DirectColor:
	case TrueColor:
	case PseudoColor:
	case StaticColor:
	default:
		capx_info->type = PEXColorSpace;
		capx_info->model = PEXColorApproxRGB;
		capx_info->dither = PEXOn;
		capx_info->base_pixel = cmap_info->base_pixel;
		capx_info->max1 = cmap_info->red_max;
		capx_info->max2 = cmap_info->green_max;
		capx_info->max3 = cmap_info->blue_max;
		capx_info->weight1 = 0; /* not used by PEXColorSpace */
		capx_info->weight2 = 0; /* not used by PEXColorSpace */
		capx_info->weight3 = 0; /* not used by PEXColorSpace */
		capx_info->mult1 = cmap_info->red_mult;
		capx_info->mult2 = cmap_info->green_mult;
		capx_info->mult3 = cmap_info->blue_mult;
		break;
	case GrayScale:
	case StaticGray:
		capx_info->type = PEXColorRange;
		capx_info->model = PEXColorApproxRGB;
		capx_info->dither = PEXOn;
		capx_info->base_pixel = cmap_info->base_pixel;
		capx_info->max1 = cmap_info->red_max;
		capx_info->max2 = 0; /* not used by PEXColorRange */
		capx_info->max3 = 0; /* not used by PEXColorRange */
	/* Give the weights the NTSC intensity coefficients. */
		capx_info->weight1 = 0.299; 
		capx_info->weight2 = 0.587;
		capx_info->weight3 = 0.114;
		capx_info->mult1 = cmap_info->red_mult;
		capx_info->mult2 = 0;
		capx_info->mult3 = 0;
		break;
	}
}

/* Globale PEXlib initializaties */
void InitPex(Widget parent)
{
	char error[PEXErrorStringLength+1];

	dpy = XtDisplay(parent);

	pex_available = FALSE;

/* initializeer PEXlib */
	if (PEXInitialize(dpy, &pexinfo, PEXErrorStringLength, error) != 0) {
		Error(NULL, "Error initializing PEX:\n%s", error);
		return;
	} 

/* Als immediate mode niet gesupporteerd wordt, hebben we pech, want kunnen 
 * we niets met de PEX server extensie doen */
	if ( !(IMMED_MODE_SPT(pexinfo)) ) {
		Error(NULL, "This PEX-server does not support 'immediate mode' rendering.");
		return;
	}

/* PEX kleurbenaderingsinformatie invullen */
	ora_set_stdcmap_approx( &best_visual, &std_cmap, &capx_info );

	pex_available = TRUE;
}

/* stelt de kleur van de ambiente lichtbron in het gegeven aanzicht in */
static void SetAmbient(COLOR *color)
{
}

static void CreateRenderer(Widget canvas)
{
	Display *dpy = XtDisplay(canvas);
	Window	window = XtWindow(canvas);

	int i, j, count;

	int nrEnumTypes=1, enumTypes[1] = {
		PEXETHLHSRMode
	};
	unsigned long *enumTypeCount;
	PEXEnumTypeDesc *enumTypeDesc;

	unsigned long mask=0,  /* masker voor het instellen van renderer attributen */
	              hlhsr;

	PEXLightEntry light;

	if (PEXGetEnumTypeInfo(dpy, window, 	
			       nrEnumTypes, enumTypes,
			       PEXETAll,
			       &enumTypeCount, &enumTypeDesc) != 0) 
	{
		for (i=0, count=0; i<nrEnumTypes; i++) {
			switch (enumTypes[i]) {

/* Hidden Line Hidden Surface Removal Mogelijkheden */
			case PEXETHLHSRMode: {
				hlhsr = 0;

				for (j=0; j<enumTypeCount[i]; j++) {
					PEXEnumTypeIndex idx = enumTypeDesc[count+j].index;

					switch (idx) {
					case PEXHLHSROff:
						break;
					case PEXHLHSRZBuffer: 
						hlhsr |= HLHSR_ZBUFFER; 
						break; 
					case PEXHLHSRPainters: 
						hlhsr |= HLHSR_PAINTERS; 
						break; 
					case PEXHLHSRScanline: 
						hlhsr |= HLHSR_SCANLINE; 
						break; 
					case PEXHLHSRHiddenLineOnly: 
						hlhsr |= HLHSR_HIDDENLINEONLY; 
						break;
					case PEXHLHSRZBufferID: 
						hlhsr |= HLHSR_ZBUFFERID; 
						break; 
					default:
						Error("CreateRenderer", "Unknown HLHSR mode %s (index %d)", enumTypeDesc[count+j].descriptor, idx);
					}
				}

/* kies de beste HLHSR methode */
				mask |= PEXRAHLHSRMode;
				rattrs.hlhsr_mode = PEXHLHSROff;
				if (hlhsr & HLHSR_ZBUFFER) 
					rattrs.hlhsr_mode = PEXHLHSRZBuffer;
				else if (hlhsr & HLHSR_ZBUFFERID) 
					rattrs.hlhsr_mode = PEXHLHSRZBufferID;
				else if (hlhsr & HLHSR_SCANLINE)
					rattrs.hlhsr_mode = PEXHLHSRScanline;
				else if (hlhsr & HLHSR_PAINTERS)
					rattrs.hlhsr_mode = PEXHLHSRPainters;
				else if (hlhsr & HLHSR_HIDDENLINEONLY)
					rattrs.hlhsr_mode = PEXHLHSRHiddenLineOnly;
				break;
			}

			default:
				Fatal(-1, "CreateRenderer", "Impossible EnumType");
			}

			count += enumTypeCount[i];
		}

		PEXFreeEnumInfo(nrEnumTypes, enumTypeCount, enumTypeDesc);
	} else { 
		Error("PexInquireCapabilities", "PEXGetEnumTypeInfo failed");
		return;
	}

/* creeer een aanzichtentabel voor de PEX renderer die dadelijk gecreerd 
 * wordt */
	mask |= PEXRAViewTable;
	rattrs.view_table =
		PEXCreateLookupTable( dpy, window, PEXLUTView );

/* creeer een lichtbronnentabel */
	mask |= PEXRALightTable;
	rattrs.light_table =
		PEXCreateLookupTable( dpy, window, PEXLUTLight );

/* kleurbenaderingstabel creeeren + default entry (index 0) instellen */
	mask |= PEXRAColorApproxTable;    
	rattrs.color_approx_table =
		PEXCreateLookupTable(dpy, window, PEXLUTColorApprox );
	PEXSetTableEntries(dpy, rattrs.color_approx_table, 0, 1,
			   PEXLUTColorApprox, &capx_info);

/* creeer een renderer resource voor gebruik in conjuntie met het widget */
	renderer = PEXCreateRenderer(dpy, window,
					  mask, &rattrs);

/* er is een ambiente lichtbron voorgedefinieerd in PEX. De index van deze
 * ambiente lichtbron is 1 - de default lichtbron tabel index */
	light.type = PEXLightAmbient;
	light.color.type = PEXColorTypeRGB;
	light.color.value.rgb.red = 1.;
	light.color.value.rgb.green = 1.;
	light.color.value.rgb.blue = 1.;

	PEXSetTableEntries(dpy, rattrs.light_table,
			   1, 1, PEXLUTLight, (PEXPointer)&light);

	renderer_created = TRUE;
}

/* enkel bij eerste expose: oninstalleert zichzelf. */
static void FirstExposeCallback(Widget canvas, XtPointer client_data, XtPointer call_data)
{
	Dimension newheight, newwidth;

	window = XtWindow(canvas);

/* creeer PEX Renderer voor gebruik in het canvas window */
	CreateRenderer(canvas);

#ifdef DOUBLE_BUFFERING
/* Create the MBX buffers. */
	if ( ora_create_mbx_buffers( dpy, window, 2, buffers ) ) {
		/* Set the current buffer to buffer 1. */
		cur_buf = buffers[1];
	        double_buffering = TRUE;
	} else {
		Error(NULL, "Can't create MBX buffers for double buffering");
		double_buffering = FALSE;
	}
#endif /*DOUBLE_BUFFERING*/

	XtVaGetValues(canvas,
		      XmNheight, &newheight,
		      XmNwidth, &newwidth,
		      NULL);

	CameraSet(&Camera, &Camera.eyep, &Camera.lookp, &Camera.updir,
		           Camera.fov, newwidth, newheight, &Camera.background);

	XtRemoveCallback(canvas, XmNexposeCallback, FirstExposeCallback, (XtPointer)NULL);
}

static void DestroyCallback(Widget canvas, XtPointer client_data, XtPointer call_data)
{
#ifdef DOUBLE_BUFFERING
	XmbufDestroyBuffers( dpy, window );
#endif /*DOUBLE_BUFFERING*/
}

/* open window for rendering */
Widget RenderCreateWindow(Widget parent)
{
	Widget canvas;
	XColor bkgd_color;

	renderer_created = FALSE;
	InitPex(parent);

/* achtergrondskleur instellen */
	bkgd_color.red = (unsigned short)(Camera.background.r * 65535.);
	bkgd_color.green = (unsigned short)(Camera.background.g * 65535.);
	bkgd_color.blue = (unsigned short)(Camera.background.b * 65535.);
	bkgd_color.flags = DoRed | DoGreen | DoBlue;

/* Determine the pixel value for the background. */
	if ( XAllocColor( XtDisplay(parent), std_cmap.colormap, &bkgd_color ) == 0 )
		/* Colormap is read/write and no more entries left.  So use */
		/* the colormap's base pixel, which will probably be black. */
		bkgd_color.pixel = std_cmap.base_pixel;

/* creeer een widget om in te tekenen */
	canvas = XtVaCreateManagedWidget("canvas",
					 xmDrawingAreaWidgetClass,
					 parent, 
					 XmNunitType,	XmPIXELS,
					 XmNbackground, bkgd_color.pixel,
					 NULL);

/* window wordt zichbaar */
	XtAddCallback(canvas, XmNexposeCallback, FirstExposeCallback, (XtPointer)NULL);

	XtAddCallback(canvas, XmNexposeCallback, CanvasExposeCallback, (XtPointer)NULL);

/* window resizes */
	XtAddCallback(canvas, XmNresizeCallback, CanvasResizeCallback, (XtPointer)NULL);

/* muis bewegingen */
	XtAddEventHandler(canvas, 
			  ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
			  False,	/* geen non-maskable event */
			  (XtEventHandler)CanvasMouseEvent,
			  (XtPointer)NULL);	/* client_data */

/* window wordt vernietigd */
	XtAddCallback(canvas, XmNdestroyCallback, DestroyCallback, (XtPointer)NULL);

	return canvas;
}

void RenderElementOutline(PATCH *patch)
{
	static PEXColor pexcolor;
	static PEXCoord coord[PATCHMAXVERTICES];
	int i;

	pexcolor.rgb.red = renderopts.outline_color.r;
	pexcolor.rgb.green = renderopts.outline_color.g;
	pexcolor.rgb.blue = renderopts.outline_color.b;
	PEXSetLineColor(dpy, renderer, PEXOCRender,
			PEXColorTypeRGB, &pexcolor);

	for (i=0; i<patch->nrvertices; i++) {
		coord[i].x = patch->vertex[i]->point->x;
		coord[i].y = patch->vertex[i]->point->y;
		coord[i].z = patch->vertex[i]->point->z;
	}

	PEXPolyline(dpy, renderer, PEXOCRender,
		    patch->nrvertices, coord);
}

void RenderElementSolid(PATCH *patch)
{
	static PEXCoord coord[PATCHMAXVERTICES];
	int i;

	for (i=0; i<patch->nrvertices; i++) {
		coord[i].x = patch->vertex[i]->point->x;
		coord[i].y = patch->vertex[i]->point->y;
		coord[i].z = patch->vertex[i]->point->z;
	}

	PEXFillArea(dpy, renderer, PEXOCRender,
		    PEXShapeConvex,
		    renderopts.draw_outlines ? False : True, /* ignore edges */
		    patch->nrvertices, coord);
}

void RenderSetColor(PATCH *patch)
{
	PEXColor pexcolor;
	RGB rgb;
	COLOR color;

	switch (renderopts.mode) {
	case RENDER_REFLECTIVITY:
		color = patch->surface->material->Kd;
		ColorToRGB(color, &rgb);
		RGBGammaCorrect(&rgb);
		break;
	case RENDER_ID:
		rgb.r = (float)(patch->id&0x0000ff)/255.;
		rgb.g = (float)((patch->id&0x00ff00)>>8)/255.;
		rgb.b = (float)((patch->id&0xff0000)>>16)/255.;
		break;
	case RENDER_RADIANCE:
		color = patch->basis->GetPower(patch);
		COLORABS(color, color);
		COLORSCALEINVERSE(M_PI*patch->area, color, color);
		if (render_ambient) {
			COLOR a;
			COLORPROD(patch->surface->material->Kd, ambient_radiance, a);
			COLORADD(color, a, color);
		}
		RadianceToRGB(color, &rgb);
		break;
	case RENDER_IMPORTANCE:
		color = patch->basis->GetImportance(patch);
		COLORABS(color, color);
		COLORSCALEINVERSE(patch->area, color, color);
		RadianceToRGB(color, &rgb);
		break;
	case RENDER_UNSHOT_RADIANCE:	
		color = patch->basis->GetUnshotPower(patch);
		COLORABS(color, color);
		COLORSCALEINVERSE(M_PI*patch->area, color, color);
		RadianceToRGB(color, &rgb);
		break;
	case RENDER_UNSHOT_IMPORTANCE:
		color = patch->basis->GetUnshotImportance(patch);
		COLORABS(color, color);
		COLORSCALEINVERSE(patch->area, color, color);
		RadianceToRGB(color, &rgb);
		break;
	default:
		Fatal(3, "RenderSetColor", "Invalid rendering mode");
	}

	pexcolor.rgb.red = rgb.r;
	pexcolor.rgb.green = rgb.g;
	pexcolor.rgb.blue = rgb.b;
	PEXSetSurfaceColor(dpy, renderer, PEXOCRender,
			   PEXColorTypeRGB, &pexcolor);
}

void RenderElementFlat(PATCH *patch)
{
	RenderSetColor(patch);
	RenderElementSolid(patch);
}

void RenderElementGouraud(PATCH *patch)
{
	static PEXFacetData face_data;
	static PEXVertexRGB vertex_data[PATCHMAXVERTICES];
	int i;

	face_data.normal.x = patch->norm.x;
	face_data.normal.y = patch->norm.y;
	face_data.normal.z = patch->norm.z;

	for (i=0; i<patch->nrvertices; i++) {
		vertex_data[i].point.x = patch->vertex[i]->point->x;
		vertex_data[i].point.y = patch->vertex[i]->point->y;
		vertex_data[i].point.z = patch->vertex[i]->point->z;
		vertex_data[i].rgb.red = patch->vertex[i]->color.r;
		vertex_data[i].rgb.green = patch->vertex[i]->color.g;
		vertex_data[i].rgb.blue = patch->vertex[i]->color.b;
	}

	PEXFillAreaWithData(dpy, renderer, PEXOCRender,
			    PEXShapeConvex,
			    renderopts.draw_outlines ? False : True, /* ignore edges */
			    PEXGANormal /*facet attributes*/, PEXGAColor /*vertex attributes*/, PEXColorTypeRGB,
			    &face_data, 
			    patch->nrvertices, (PEXArrayOfVertex) vertex_data);
}

/* wordt ingevuld door RenderScene() */
void (*RenderElement)(PATCH *) = RenderElementFlat;
void (*RenderPatch)(PATCH *) = RenderElementFlat;

void RenderPatchRecursive(PATCH *patch)
{
	if (!patch->subpatches) 
		RenderElement(patch);
	else
		PatchListIterate(patch->subpatches, RenderPatchRecursive);
}

/* switches backfaceculling off before rendering and on after for twosided patches */
static void DoRenderPatch(PATCH *patch)
{
	if (patch->twosided) 
		PEXSetFacetCullingMode(dpy, renderer, PEXOCRender, PEXNone);

	RenderPatch(patch);

	if (patch->twosided)
		PEXSetFacetCullingMode(dpy, renderer, PEXOCRender, 
			        renderopts.backface_culling ? PEXBackFaces : PEXNone);
}

void RenderLink(INTERACTION *PQ)
{
	PATCH *P = PQ->P, *Q = PQ->Q;
	static PEXCoord coord[2];

	coord[0].x = P->midpoint.x;
	coord[0].y = P->midpoint.y;
	coord[0].z = P->midpoint.z;
	coord[1].x = Q->midpoint.x;
	coord[1].y = Q->midpoint.y;
	coord[1].z = Q->midpoint.z;

	PEXPolyline(dpy, renderer, PEXOCRender,
		    2, coord);	
}

void PatchRenderLinks(PATCH *patch)
{
	static PEXColor pexcolor;

	pexcolor.rgb.red = renderopts.link_color.r;
	pexcolor.rgb.green = renderopts.link_color.g;
	pexcolor.rgb.blue = renderopts.link_color.b;
	PEXSetLineColor(dpy, renderer, PEXOCRender,
			PEXColorTypeRGB, &pexcolor);

	InteractionListIterate(patch->interactions, RenderLink);
	PatchListIterate(patch->subpatches, PatchRenderLinks);	
}

static int RenderPatchCompare(PATCH **pP, PATCH **pQ)
{
	PATCH *P = *(pP), *Q = *(pQ);
	VECTOR v;
	float Pdist, Qdist, Pmindist, Pmaxdist, Qmindist, Qmaxdist, d;
	int i;

	VECTORSUBSTRACT(*(P->vertex[0]->point), Camera.eyep, v);
	Pmindist = Pmaxdist = VECTORNORM2(v);
	for (i=1; i<P->nrvertices; i++) {
		VECTORSUBSTRACT(*(P->vertex[i]->point), Camera.eyep, v);
		d = VECTORNORM2(v);
		if (d < Pmindist) Pmindist = d;
		if (d > Pmaxdist) Pmaxdist = d;
	}

	VECTORSUBSTRACT(*(Q->vertex[0]->point), Camera.eyep, v);
	Qmindist = Qmaxdist = VECTORNORM2(v);
	for (i=1; i<Q->nrvertices; i++) {
		VECTORSUBSTRACT(*(Q->vertex[i]->point), Camera.eyep, v);
		d = VECTORNORM2(v);
		if (d < Qmindist) Qmindist = d;
		if (d > Qmaxdist) Qmaxdist = d;
	}

	Pdist = Pmaxdist; Qdist = Qmaxdist;
/*	if (FLOATEQUAL(Pdist, Qdist)) {
		Pdist = Pmaxdist; Qdist = Qmaxdist;
	}
*/
	return (Pdist > Qdist ? -1 : (Pdist < Qdist ? 1 : 0));
}

/* nodig voor importance herschaling */
static void RenderSetImportanceRescaleFactor(void)
{
	PATCHLIST *patches = Patches;
	PATCH *P;
	float impoverarea;
	COLOR imp;

	maximpoverarea = 0;
	while ((P = PatchListNext(&patches))) {
		imp = P->basis->GetImportance(P);
		impoverarea = COLORMAXCOMPONENT(imp)/P->area;
		if (impoverarea > maximpoverarea)
			maximpoverarea = impoverarea;
	}

/* kan gebeuren als geen importance gebruikt wordt en een
 * gebruiker vraagt toch importance te renderen */
	if (maximpoverarea < EPSILON) maximpoverarea = 1.;
	SetRadianceRescaleFactor(maximpoverarea/2.);
}

/* nodig voor radiantie herschaling */
static void RenderSetRadianceRescaleFactor(void)
{
/* radiance_rescale is gedefinieerd in globals.[hc] en wordt
 * berekend in InitRadianceComputation() in radiance.c onmiddelijk
 * na het inladen van een scene */
	SetRadianceRescaleFactor(radiance_rescale);
}

void RenderScene(void)
{
	int nrpatches=0, i;
	PATCH **toplevelpatches=(PATCH **)NULL, *P=(PATCH *)NULL;
	PATCHLIST *patches;

	if (!renderer_created)
		return;

	if (!Patches)
		return;

	CanvasPushMode(CANVASMODE_RENDER);

	if (rattrs.hlhsr_mode == PEXHLHSROff) {
/* Painters algorithm */
		nrpatches = PatchListCount(Patches);
		toplevelpatches = (PATCH **)Alloc(nrpatches * sizeof(PATCH *));
		patches = Patches; i=0;
		while ((P = PatchListNext(&patches))) {
			toplevelpatches[i++] = P;
		}
		qsort((void *)toplevelpatches, nrpatches, sizeof(PATCH *), 
		      (int (*)(const void *, const void *))RenderPatchCompare);
	}

	RenderSetCamera();

	PEXSetFacetCullingMode(dpy, renderer, PEXOCRender, 
			       (renderopts.backface_culling ? PEXBackFaces : PEXNone));

	PEXSetInteriorStyle(dpy, renderer, PEXOCRender, PEXInteriorStyleSolid);

	PEXSetSurfaceInterpMethod(dpy, renderer, PEXOCRender,
				  renderopts.gouraud_shading ? PEXSurfaceInterpColor : PEXSurfaceInterpNone);

	switch (renderopts.mode) {
	case RENDER_REFLECTIVITY:
		PEXSetSurfaceInterpMethod(dpy, renderer, PEXOCRender,
					  PEXSurfaceInterpNone);
		RenderPatch = RenderElementFlat;
		break;
	case RENDER_IMPORTANCE:
	case RENDER_UNSHOT_IMPORTANCE:
		PEXSetSurfaceInterpMethod(dpy, renderer, PEXOCRender,
					  PEXSurfaceInterpNone);
		RenderPatch = RenderElementFlat;
		RenderSetImportanceRescaleFactor();
		break;
	case RENDER_ID:
		PEXSetSurfaceInterpMethod(dpy, renderer, PEXOCRender,
					  PEXSurfaceInterpNone);
		RenderPatch = RenderPatchRecursive;
		RenderElement = RenderElementFlat;
		break;
	case RENDER_UNSHOT_RADIANCE:
		PEXSetSurfaceInterpMethod(dpy, renderer, PEXOCRender,
					  PEXSurfaceInterpNone);
		RenderPatch = RenderPatchRecursive;
		RenderElement = RenderElementFlat;
		RenderSetRadianceRescaleFactor();
		break;
	case RENDER_RADIANCE:
		RenderPatch = RenderPatchRecursive;
		RenderElement = renderopts.gouraud_shading ?
			        RenderElementGouraud :
				RenderElementFlat;
		RenderSetRadianceRescaleFactor();
		break;
	default:
		Fatal(3, NULL, "Invalid rendering mode.\n");
	}

	if (rattrs.hlhsr_mode == PEXHLHSROff) {
		for (i=0; i<nrpatches; i++)
			DoRenderPatch(toplevelpatches[i]);
		Free((char *)toplevelpatches, nrpatches * sizeof(PATCH *));
	} else
		PatchListIterate(Patches, DoRenderPatch); 

	EndRendering();

	CanvasPullMode();
}

void RenderPixels(int x, int y, int n, RGB *rgb)
{
/*
	Error("RenderPixels", "Not implemented");
*/
}

unsigned long *GetIDs(long *x, long *y)
{
	Error("GetIDs", "Not implemented");

	*x = *y = 0;
	return (unsigned long *)NULL;
}

void SaveScreen(FILE *fp)
{
	Error("SaveScreen", "Not implemented");
}
