/**
 * $Id:$
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * The contents of this file may be used under the terms of either the GNU
 * General Public License Version 2 or later (the "GPL", see
 * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or
 * later (the "BL", see http://www.blender.org/BL/ ) which has to be
 * bought from the Blender Foundation to become active, in which case the
 * above mentioned GPL option does not apply.
 *
 * The Original Code is Copyright (C) 2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

/* *************************************** 




    preproces.c	nov/dec 1992
				may 1999
	
	- collect from meshes
	- countglobaldata()
	- makeGlobalElemArray()
	
  *************************************** */

#include "radio.h"

int VecCompare(float *v1, float *v2, float limit)
{
	if( fabs(v1[0]-v2[0])<limit )
		if( fabs(v1[1]-v2[1])<limit )
			if( fabs(v1[2]-v2[2])<limit ) return 1;
	return 0;
}


void splitconnected()
{
	/* voor zover de videoscapefile nog gedeelde vertices leverde, worden de vlakken getest
	 * op normaal en kleur. Doe dit door voor elke vertex een normaal en een kleur te onthouden.
	 */
	RPatch *rp;
	RNode *rn;
	VeNoCo *vnc, *next, *vnc1;
	int a;
	
	/* test of gesplit moet worden */
	
	rp= RG.patchbase.first;
	while(rp) {
		rn= rp->first;
		if((rp->f1 & RAD_NO_SPLIT)==0) {
			for(a=0; a<rp->type; a++) {

				if(a==0) vnc= (VeNoCo *)rn->v1;
				else if(a==1) vnc= (VeNoCo *)rn->v2;
				else if(a==2) vnc= (VeNoCo *)rn->v3;
				else vnc= (VeNoCo *)rn->v4;

				if(vnc->flag==0) {
					vnc->n= (float *)rp->norm;
					vnc->col= (float *)rp->ref;
					vnc->flag= 1;
				}
				else {	/* mag vlak deze vertex gebruiken voor gouraud? */
					vnc1= vnc;
					while(vnc1) {
						if(VecCompare(vnc1->n, rp->norm, 0.01)) {
							if(VecCompare(vnc1->col, rp->ref, 0.01)) {
								break;
							}
						}
						vnc= vnc1;
						vnc1= vnc1->next;
					}
					if(vnc1==0) {
						vnc1= mallocN(sizeof(VeNoCo), "splitconn");
						vnc1->next= 0;
						vnc1->v= mallocVert();
						vnc->next= vnc1;
						VECCOPY(vnc1->v, vnc->v);
						vnc1->n= (float *)rp->norm;
						vnc1->col= (float *)rp->ref;
					}
					if(a==0) rn->v1= (float *)vnc1;
					else if(a==1) rn->v2= (float *)vnc1;
					else if(a==2) rn->v3= (float *)vnc1;
					else rn->v4= (float *)vnc1;
				}
			}
		}
		rp= rp->next;
	}
		/* de vertexpointers van nodes aanpassen */
	
	rp= RG.patchbase.first;
	while(rp) {
		rn= rp->first;
		rn->v1= ((VeNoCo *)(rn->v1))->v;
		rn->v2= ((VeNoCo *)(rn->v2))->v;
		rn->v3= ((VeNoCo *)(rn->v3))->v;
		if(rp->type==4) rn->v4= ((VeNoCo *)(rn->v4))->v;

		rp= rp->next;
	}
	
	
	/* het hele zaakje vrijgeven */
	vnc= RG.verts;
	for(a=0; a<RG.totvert; a++) {
		vnc1= vnc->next;
		while(vnc1) {
			next= vnc1->next;
			freeN(vnc1);
			vnc1= next;
		}
		vnc++;
	}
	freeN(RG.verts);
	RG.verts= 0;
}

int vergedge(const void *v1,const void *v2)
{
	int *e1, *e2;
	
	e1= (int *)v1;
	e2= (int *)v2;

	if( e1[0] > e2[0] ) return 1;
	else if( e1[0] < e2[0] ) return -1;
	else if( e1[1] > e2[1] ) return 1;
	else if( e1[1] < e2[1] ) return -1;

	return 0;
}


void addedge(float *v1, float *v2, EdSort *es)
{
	if( ((long)v1)<((long)v2) ) {
		es->v1= v1;
		es->v2= v2;
	}
	else {
		es->v2= v1;
		es->v1= v2;
	}
}

static void setedge(RNode *node, RNode *nb, int nr, int nrb)
{
	switch(nr) {
		case 1:
			node->ed1= nb;
			break;
		case 2:
			node->ed2= nb;
			break;
		case 3:
			node->ed3= nb;
			break;
		case 4:
			node->ed4= nb;
			break;
	}
	switch(nrb) {
		case 1:
			nb->ed1= node;
			break;
		case 2:
			nb->ed2= node;
			break;
		case 3:
			nb->ed3= node;
			break;
		case 4:
			nb->ed4= node;
			break;
	}
}

void setedgepointers()
{
	/* edge-array maken en sorteren */
	/* paren edges staan bij elkaar: pointers invullen in nodes */
	EdSort *es, *esblock;
	RPatch *rp;
	RNode *rn;
	int tot= 0;
	
	rp= RG.patchbase.first;
	while(rp) {
		tot+= rp->type;
		rp= rp->next;
	}
	
	if(tot==0) return;
	
	es=esblock= mallocN(tot*sizeof(EdSort), "setedgepointers");
	rp= RG.patchbase.first;
	while(rp) {
		rn= rp->first;
		addedge(rn->v1, rn->v2, es);
		es->nr= 1;
		es->node= rn;
		es++;		
		addedge(rn->v2, rn->v3, es);
		es->nr= 2;
		es->node= rn;
		es++;		
		if(rp->type==3) {
			addedge(rn->v3, rn->v1, es);
			es->nr= 3;
			es->node= rn;
			es++;
		}
		else {
			addedge(rn->v3, rn->v4, es);
			es->nr= 3;
			es->node= rn;
			es++;					
			addedge(rn->v4, rn->v1, es);
			es->nr= 4;
			es->node= rn;
			es++;
		}
		rp= rp->next;
	}
	
	qsort(esblock,tot,sizeof(EdSort),vergedge);

	es= esblock;
	while(tot>0) {
		if( es->v1== (es+1)->v1 ) {
			if( es->v2== (es+1)->v2 ) {
				setedge(es->node, (es+1)->node, es->nr, (es+1)->nr);
				tot--;
				es++;
			}
		}
		es++;
		tot--;
	}

	freeN(esblock);
}

void rad_collect_meshes()
{
	extern Material defmaterial;
	Base *base;
	Object *ob;
	Mesh *me;
	MVert *mvert;
	MFace *mface;
	Material *ma;
	RPatch *rp;
	RNode *rn;
	VeNoCo *vnc, **nodevert;
	float *vd, *v1, *v2, *v3, *v4;
	int a, b, offs, index;
	
	if(G.obedit) {
		error("Unable to perform function in EditMode");
		return;
	}

	set_radglobal();

	freeAllRad();

	start_fastmalloc("Radiosity");
					
	/* count the number of verts */
	RG.totvert= 0;
	RG.totface= 0;
	base= FIRSTBASE;
	while(base) {
		if(TESTBASE(base)) {
			if(base->object->type==OB_MESH) {
				base->flag |= OB_RADIO;
				me= base->object->data;
				RG.totvert+= me->totvert;
			}
		}
		base= base->next;
	}
	if(RG.totvert==0) {
		error("No vertices");
		return;
	}
	vnc= RG.verts= callocN(RG.totvert*sizeof(VeNoCo), "readvideoscape1");

	RG.min[0]= RG.min[1]= RG.min[2]= 1.0e20;
	RG.max[0]= RG.max[1]= RG.max[2]= -1.0e20;

	base= FIRSTBASE;
	while(base) {
		if(TESTBASE(base)) {
			if(base->object->type==OB_MESH) {
				me= base->object->data;
				mvert= me->mvert;
				for(a=0; a<me->totvert; a++, mvert++) {
					vd= mallocVert();
					VECCOPY(vd, mvert->co);
					Mat4MulVecfl(base->object->obmat, vd);
					
					vnc->v= vd;
					for(b=0; b<3; b++) {
						RG.min[b]= MIN2(RG.min[b], vd[b]);
						RG.max[b]= MAX2(RG.max[b], vd[b]);
					}
					vnc++;
				}
			}
		}
		base= base->next;
	}

	RG.cent[0]= (RG.min[0]+ RG.max[0])/2;
	RG.cent[1]= (RG.min[1]+ RG.max[1])/2;
	RG.cent[2]= (RG.min[2]+ RG.max[2])/2;
	RG.size[0]= (RG.max[0]- RG.min[0]);
	RG.size[1]= (RG.max[1]- RG.min[1]);
	RG.size[2]= (RG.max[2]- RG.min[2]);
	RG.maxsize= MAX3(RG.size[0],RG.size[1],RG.size[2]);

	/* make patches */

	RG.totelem= 0;
	RG.totpatch= 0;
	RG.totlamp= 0;
	offs= 0;

	base= FIRSTBASE;
	while(base) {
		if(TESTBASE(base)) {
			if(base->object->type==OB_MESH) {
				ob= base->object;
				me= ob->data;
				mface= me->mface;
				
				index= -1;

				for(a=0; a<me->totface; a++, mface++) {
					if(mface->v3) {
						
						rp= callocPatch();
						addtail(&(RG.patchbase), rp);
						rp->from= ob;
						
						if(mface->v4) rp->type= 4;
						else rp->type= 3;
						
						rp->first= rn= callocNode();
						
						if(mface->flag & ME_SMOOTH) rp->f1= RAD_NO_SPLIT;
						
						/* temporal: we store the venoco in the node */
						rn->v1= (float *)(RG.verts+mface->v1+offs);
						v1= (RG.verts+mface->v1+offs)->v;
						rn->v2= (float *)(RG.verts+mface->v2+offs);
						v2= (RG.verts+mface->v2+offs)->v;
						rn->v3= (float *)(RG.verts+mface->v3+offs);
						v3= (RG.verts+mface->v3+offs)->v;

						if(mface->v4) {
							rn->v4= (float *)(RG.verts+mface->v4+offs);
							v4= (RG.verts+mface->v4+offs)->v;
						}			
						rn->par= rp;
						rn->f= RAD_PATCH;	/* deze node is Patch */
						rn->type= rp->type;

						CalcNormFloat(v1, v2, v3, rp->norm);
						if(rn->type==4) rp->area= AreaQ3Dfl(v1, v2, v3, v4);
						else rp->area= AreaT3Dfl(v1, v2, v3);

						rn->area= rp->area;

						/* kleur en emit */
						if(mface->mat_nr != index) {
							index= mface->mat_nr;
							ma= give_current_material(ob, index+1);
							if(ma==0) ma= &defmaterial;
						}
						rp->ref[0]= ma->r;
						rp->ref[1]= ma->g;
						rp->ref[2]= ma->b;

						if(ma->emit) RG.totlamp++;
	
						rp->emit[0]= rp->emit[1]= rp->emit[2]= ma->emit;
						rp->emit[0]*= rp->ref[0];
						rp->emit[1]*= rp->ref[1];
						rp->emit[2]*= rp->ref[2];

						nodevert= (VeNoCo **)&(rn->v1);
						for(b=0; b<rp->type; b++) {
							rp->cent[0]+= (*nodevert)->v[0];
							rp->cent[1]+= (*nodevert)->v[1];
							rp->cent[2]+= (*nodevert)->v[2];
							nodevert++;
						}
						rp->cent[0]/= (float)rp->type;
						rp->cent[1]/= (float)rp->type;
						rp->cent[2]/= (float)rp->type;

						RG.totelem++;
						RG.totpatch++;
					}
				}
				offs+= me->totvert;
			}
		}
		base= base->next;
	}
	
	splitconnected();
	setedgepointers();

	makeGlobalElemArray();
	pseudoAmb();
	rad_setlimits();
}

void setparelem(RNode *rn, RPatch *par)
{
	
	if(rn->down1) {
		setparelem(rn->down1, par);
		setparelem(rn->down2, par);
	}
	else {
		rn->par= par;
	}
}

void countelem(RNode *rn)
{

	if(rn->down1) {
		countelem(rn->down1);
		countelem(rn->down2);
	}
	else RG.totelem++;
}

void countglobaldata()
{
	/* telt aantal elements en patches*/
	RPatch *rp;

	RG.totelem= RG.totpatch= 0;

	rp= RG.patchbase.first;
	while(rp) {
		RG.totpatch++;
		countelem(rp->first);
		rp= rp->next;
	}
}

void addelem(RNode ***el, RNode *rn, RPatch *rp)
{
	if(rn->down1) {
		addelem(el, rn->down1, rp);
		addelem(el, rn->down2, rp);
	}
	else {
		rn->par= rp;
		**el= rn;
		(*el)++;
	}
}

void makeGlobalElemArray()
{
	/* always called when # of elements change */
	RPatch *rp;
	RNode **el;

	countglobaldata();

	if(RG.elem) freeN(RG.elem);
	if(RG.totelem) {
		el= RG.elem= mallocN(sizeof(void *)*RG.totelem, "makeGlobalElemArray");
	}
	else {
		RG.elem= 0;
		return;
	}

	/* recursief elements toevoegen */
	rp= RG.patchbase.first;
	while(rp) {
		addelem(&el, rp->first, rp);
		rp= rp->next;
	}

	/* formfactor array */
	if(RG.formfactors) freeN(RG.formfactors);
	if(RG.totelem)
		RG.formfactors= mallocN(sizeof(float)*RG.totelem, "formfactors");
	else
		RG.formfactors= 0;
}

void splitpatch(RPatch *old)		/* bij overflow gedurende shoot */
{
	RNode *rn;
	float **fpp;
	RPatch *rp;
	int a;
	
	rn= old->first;
	if(rn->down1==0) return;
	rn= rn->down1;

	old->unshot[0]/=2.0;
	old->unshot[1]/=2.0;
	old->unshot[2]/=2.0;
	setnodeflags(old->first, 2, 0);

	rp= mallocPatch();
	*rp= *old;
	addhead(&RG.patchbase, rp);
	rp->first= rn;
	rp->area= rn->area;
	rp->cent[0]= rp->cent[1]= rp->cent[2]= 0.0;
	fpp= &(rn->v1);
	for(a=0; a<rp->type; a++) {
		rp->cent[0]+= (*fpp)[0];
		rp->cent[1]+= (*fpp)[1];
		rp->cent[2]+= (*fpp)[2];
		fpp++;
	}
	rp->cent[0]/=(float)rp->type;
	rp->cent[1]/=(float)rp->type;
	rp->cent[2]/=(float)rp->type;
		
	setparelem(rn, rp);

	rn= old->first->down2;

	rp= mallocPatch();
	*rp= *old;
	addhead(&RG.patchbase, rp);
	rp->first= rn;
	rp->area= rn->area;
	rp->cent[0]= rp->cent[1]= rp->cent[2]= 0.0;
	fpp= &(rn->v1);
	for(a=0; a<rp->type; a++) {
		rp->cent[0]+= (*fpp)[0];
		rp->cent[1]+= (*fpp)[1];
		rp->cent[2]+= (*fpp)[2];
		fpp++;
	}
	rp->cent[0]/=(float)rp->type;
	rp->cent[1]/=(float)rp->type;
	rp->cent[2]/=(float)rp->type;
	
	setparelem(rn, rp);

	remlink(&RG.patchbase, old);
	freePatch(old);
}


void addpatch(RPatch *old, RNode *rn)
{
	float **fpp;
	RPatch *rp;
	int a;
	
	if(rn->down1) {
		addpatch(old, rn->down1);
		addpatch(old, rn->down2);
	}
	else {
		rp= mallocPatch();
		*rp= *old;
		addhead(&RG.patchbase, rp);
		rp->first= rn;
		
		rp->area= rn->area;
		rp->cent[0]= rp->cent[1]= rp->cent[2]= 0.0;
		fpp= &(rn->v1);
		for(a=0; a<rp->type; a++) {
			rp->cent[0]+= (*fpp)[0];
			rp->cent[1]+= (*fpp)[1];
			rp->cent[2]+= (*fpp)[2];
			fpp++;
		}
		rp->cent[0]/=(float)rp->type;
		rp->cent[1]/=(float)rp->type;
		rp->cent[2]/=(float)rp->type;
		
		rn->par= rp;
	}
}

void converttopatches()
{
	/* loopt patcheslijst af, als node gesubdivided: nieuwe patch */
	RPatch *rp, *next;
	
	rp= RG.patchbase.first;
	while(rp) {
		next= rp->next;
		if(rp->first->down1) {
			addpatch(rp, rp->first);
			remlink(&RG.patchbase, rp);
			freePatch(rp);
		}
		rp= next;
	}
	
}

void subdiv_elements()
{
	RNode **el, *rn;
	int a, toobig= 1;

	rad_init_energy();
	
	/* eerst maxsize elements */
	
	while(toobig) {
		toobig= 0;
		
		el= RG.elem;
		for(a=RG.totelem; a>0; a--, el++) {
			rn= *el;
			if( rn->totrad[0]==0.0 && rn->totrad[1]==0.0 && rn->totrad[2]==0.0) {
				if(rn->area>RG.elemmin) {
					subdivideNode(rn, 0);
					if(rn->down1 ) {
						toobig= 1;
						if(rn->down1->area>RG.elemmin)
							subdivideNode( rn->down1, 0);
						if(rn->down2->area>RG.elemmin)
							subdivideNode( rn->down2, 0);
					}
				}
			}
		}
		if(toobig) makeGlobalElemArray();
	}

	el= RG.elem;
	for(a=RG.totelem; a>0; a--, el++) {
		rn= *el;
		if( rn->totrad[0]==0.0 && rn->totrad[1]==0.0 && rn->totrad[2]==0.0) {
			subdivideNode(rn, 0);
			if( rn->down1 ) {
				subdivideNode( rn->down1, 0);
				subdivideNode( rn->down2, 0);
			}
		}
	}
	makeGlobalElemArray();
}

void subdividelamps()
{
	RPatch *rp, *next;
	
	rp= RG.patchbase.first;
	while(rp) {
		next= rp->next;
		if(rp->emit[0]!=0.0 || rp->emit[1]!=0.0 || rp->emit[2]!=0.0) {
			subdivideNode( rp->first, 0);
			if(rp->first->down1) {
				subdivideNode(rp->first->down1, 0);
				subdivideNode(rp->first->down2, 0);
			}
			
			addpatch(rp, rp->first);
			remlink(&RG.patchbase, rp);
			freePatch(rp);
		}
		rp= next;
	}
	
}

void maxsizePatches()
{
	RPatch *rp;
	int toobig= 1;
	
	while(toobig) {
		toobig= 0;
		rp= RG.patchbase.first;
		while(rp) {
			if(rp->area>RG.patchmax) {
				subdivideNode( rp->first, 0);
				if(rp->first->down1) toobig= 1;
			}
			rp= rp->next;
		}
		
		if(toobig) converttopatches();
	}
	
	/* aantal lampen tellen */
	rp= RG.patchbase.first;
	RG.totlamp= 0;
	while(rp) {
		if(rp->emit[0]!=0.0 || rp->emit[1]!=0.0 || rp->emit[2]!=0.0) {
			RG.totlamp++;
		}
		rp= rp->next;
	}
	makeGlobalElemArray();
}