/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * Contributor(s): 2004-2006, Blender Foundation, full recode
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

/** \file blender/render/intern/source/renderdatabase.c
 *  \ingroup render
 */


/*
 * Storage, retrieval and query of render specific data.
 *
 * All data from a Blender scene is converted by the renderconverter/
 * into a special format that is used by the render module to make
 * images out of. These functions interface to the render-specific
 * database.  
 *
 * The blo{ha/ve/vl} arrays store pointers to blocks of 256 data
 * entries each.
 *
 * The index of an entry is >>8 (the highest 24 * bits), to find an
 * offset in a 256-entry block.
 *
 * - If the 256-entry block entry has an entry in the
 * vertnodes/vlaknodes/bloha array of the current block, the i-th entry in
 * that block is allocated to this entry.
 *
 * - If the entry has no block allocated for it yet, memory is
 * allocated.
 *
 * The pointer to the correct entry is returned. Memory is guaranteed
 * to exist (as long as the malloc does not break). Since guarded
 * allocation is used, memory _must_ be available. Otherwise, an
 * exit(0) would occur.
 * 
 */

#include <limits.h>
#include <math.h>
#include <string.h>

#include "MEM_guardedalloc.h"


#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BLI_memarena.h"

#include "DNA_material_types.h" 
#include "DNA_mesh_types.h" 
#include "DNA_meshdata_types.h" 
#include "DNA_texture_types.h" 

#include "BKE_customdata.h"
#include "BKE_texture.h" 
#include "BKE_DerivedMesh.h"

#include "RE_render_ext.h"	/* externtex */

#include "rayobject.h"
#include "renderpipeline.h"
#include "render_types.h"
#include "renderdatabase.h"
#include "texture.h"
#include "strand.h"
#include "zbuf.h"

/* ------------------------------------------------------------------------- */

/* More dynamic allocation of options for render vertices and faces, so we don't
 * have to reserve this space inside vertices.
 * Important; vertices and faces, should have been created already (to get tables
 * checked) that's a reason why the calls demand VertRen/VlakRen * as arg, not
 * the index */

/* NOTE! the hardcoded table size 256 is used still in code for going quickly over vertices/faces */
#define RE_STRESS_ELEMS		1
#define RE_RAD_ELEMS		4
#define RE_STRAND_ELEMS		1
#define RE_TANGENT_ELEMS	3
#define RE_WINSPEED_ELEMS	4
#define RE_MTFACE_ELEMS		1
#define RE_MCOL_ELEMS		4
#define RE_UV_ELEMS			2
#define RE_VLAK_ORIGINDEX_ELEMS	1
#define RE_VERT_ORIGINDEX_ELEMS	1
#define RE_SURFNOR_ELEMS	3
#define RE_RADFACE_ELEMS	1
#define RE_SIMPLIFY_ELEMS	2
#define RE_FACE_ELEMS		1
#define RE_NMAP_TANGENT_ELEMS	16

float *RE_vertren_get_stress(ObjectRen *obr, VertRen *ver, int verify)
{
	float *stress;
	int nr= ver->index>>8;
	
	stress= obr->vertnodes[nr].stress;
	if (stress==NULL) {
		if (verify) 
			stress= obr->vertnodes[nr].stress= MEM_mallocN(256*RE_STRESS_ELEMS*sizeof(float), "stress table");
		else
			return NULL;
	}
	return stress + (ver->index & 255)*RE_STRESS_ELEMS;
}

/* this one callocs! */
float *RE_vertren_get_rad(ObjectRen *obr, VertRen *ver, int verify)
{
	float *rad;
	int nr= ver->index>>8;
	
	rad= obr->vertnodes[nr].rad;
	if (rad==NULL) {
		if (verify) 
			rad= obr->vertnodes[nr].rad= MEM_callocN(256*RE_RAD_ELEMS*sizeof(float), "rad table");
		else
			return NULL;
	}
	return rad + (ver->index & 255)*RE_RAD_ELEMS;
}

float *RE_vertren_get_strand(ObjectRen *obr, VertRen *ver, int verify)
{
	float *strand;
	int nr= ver->index>>8;
	
	strand= obr->vertnodes[nr].strand;
	if (strand==NULL) {
		if (verify) 
			strand= obr->vertnodes[nr].strand= MEM_mallocN(256*RE_STRAND_ELEMS*sizeof(float), "strand table");
		else
			return NULL;
	}
	return strand + (ver->index & 255)*RE_STRAND_ELEMS;
}

/* needs calloc */
float *RE_vertren_get_tangent(ObjectRen *obr, VertRen *ver, int verify)
{
	float *tangent;
	int nr= ver->index>>8;
	
	tangent= obr->vertnodes[nr].tangent;
	if (tangent==NULL) {
		if (verify) 
			tangent= obr->vertnodes[nr].tangent= MEM_callocN(256*RE_TANGENT_ELEMS*sizeof(float), "tangent table");
		else
			return NULL;
	}
	return tangent + (ver->index & 255)*RE_TANGENT_ELEMS;
}

/* needs calloc! not all renderverts have them */
/* also winspeed is exception, it is stored per instance */
float *RE_vertren_get_winspeed(ObjectInstanceRen *obi, VertRen *ver, int verify)
{
	float *winspeed;
	int totvector;
	
	winspeed= obi->vectors;
	if (winspeed==NULL) {
		if (verify) {
			totvector= obi->obr->totvert + obi->obr->totstrand;
			winspeed= obi->vectors= MEM_callocN(totvector*RE_WINSPEED_ELEMS*sizeof(float), "winspeed table");
		}
		else
			return NULL;
	}
	return winspeed + ver->index*RE_WINSPEED_ELEMS;
}

int *RE_vertren_get_origindex(ObjectRen *obr, VertRen *ver, int verify)
{
	int *origindex;
	int nr= ver->index>>8;

	origindex= obr->vertnodes[nr].origindex;
	if (origindex==NULL) {
		if (verify)
			origindex= obr->vertnodes[nr].origindex= MEM_mallocN(256*RE_VERT_ORIGINDEX_ELEMS*sizeof(int), "origindex table");
		else
			return NULL;
	}
	return origindex + (ver->index & 255)*RE_VERT_ORIGINDEX_ELEMS;
}

VertRen *RE_vertren_copy(ObjectRen *obr, VertRen *ver)
{
	VertRen *v1= RE_findOrAddVert(obr, obr->totvert++);
	float *fp1, *fp2;
	int *int1, *int2;
	int index= v1->index;
	
	*v1= *ver;
	v1->index= index;

	fp1= RE_vertren_get_stress(obr, ver, 0);
	if (fp1) {
		fp2= RE_vertren_get_stress(obr, v1, 1);
		memcpy(fp2, fp1, RE_STRESS_ELEMS*sizeof(float));
	}
	fp1= RE_vertren_get_rad(obr, ver, 0);
	if (fp1) {
		fp2= RE_vertren_get_rad(obr, v1, 1);
		memcpy(fp2, fp1, RE_RAD_ELEMS*sizeof(float));
	}
	fp1= RE_vertren_get_strand(obr, ver, 0);
	if (fp1) {
		fp2= RE_vertren_get_strand(obr, v1, 1);
		memcpy(fp2, fp1, RE_STRAND_ELEMS*sizeof(float));
	}
	fp1= RE_vertren_get_tangent(obr, ver, 0);
	if (fp1) {
		fp2= RE_vertren_get_tangent(obr, v1, 1);
		memcpy(fp2, fp1, RE_TANGENT_ELEMS*sizeof(float));
	}
	int1= RE_vertren_get_origindex(obr, ver, 0);
	if (int1) {
		int2= RE_vertren_get_origindex(obr, v1, 1);
		memcpy(int2, int1, RE_VERT_ORIGINDEX_ELEMS*sizeof(int));
	}
	return v1;
}

VertRen *RE_findOrAddVert(ObjectRen *obr, int nr)
{
	VertTableNode *temp;
	VertRen *v;
	int a;

	if (nr<0) {
		printf("error in findOrAddVert: %d\n", nr);
		return NULL;
	}
	a= nr>>8;
	
	if (a>=obr->vertnodeslen-1) {  /* Need to allocate more columns..., and keep last element NULL for free loop */
		temp= obr->vertnodes;
		
		obr->vertnodes= MEM_mallocN(sizeof(VertTableNode)*(obr->vertnodeslen+TABLEINITSIZE), "vertnodes");
		if (temp) memcpy(obr->vertnodes, temp, obr->vertnodeslen*sizeof(VertTableNode));
		memset(obr->vertnodes+obr->vertnodeslen, 0, TABLEINITSIZE*sizeof(VertTableNode));
		
		obr->vertnodeslen+=TABLEINITSIZE; 
		if (temp) MEM_freeN(temp);
	}
	
	v= obr->vertnodes[a].vert;
	if (v==NULL) {
		int i;
		
		v= (VertRen *)MEM_callocN(256*sizeof(VertRen), "findOrAddVert");
		obr->vertnodes[a].vert= v;
		
		for (i= (nr & 0xFFFFFF00), a=0; a<256; a++, i++) {
			v[a].index= i;
		}
	}
	v+= (nr & 255);
	return v;
}

/* ------------------------------------------------------------------------ */

MTFace *RE_vlakren_get_tface(ObjectRen *obr, VlakRen *vlr, int n, char **name, int verify)
{
	VlakTableNode *node;
	int nr= vlr->index>>8, vlakindex= (vlr->index&255);
	int index= (n<<8) + vlakindex;

	node= &obr->vlaknodes[nr];

	if (verify) {
		if (n>=node->totmtface) {
			MTFace *mtface= node->mtface;
			int size= (n+1)*256;

			node->mtface= MEM_callocN(size*sizeof(MTFace), "Vlak mtface");

			if (mtface) {
				size= node->totmtface*256;
				memcpy(node->mtface, mtface, size*sizeof(MTFace));
				MEM_freeN(mtface);
			}

			node->totmtface= n+1;
		}
	}
	else {
		if (n>=node->totmtface)
			return NULL;

		if (name) *name= obr->mtface[n];
	}

	return node->mtface + index;
}

MCol *RE_vlakren_get_mcol(ObjectRen *obr, VlakRen *vlr, int n, char **name, int verify)
{
	VlakTableNode *node;
	int nr= vlr->index>>8, vlakindex= (vlr->index&255);
	int index= (n<<8) + vlakindex;

	node= &obr->vlaknodes[nr];

	if (verify) {
		if (n>=node->totmcol) {
			MCol *mcol= node->mcol;
			int size= (n+1)*256;

			node->mcol= MEM_callocN(size*sizeof(MCol)*RE_MCOL_ELEMS, "Vlak mcol");

			if (mcol) {
				size= node->totmcol*256;
				memcpy(node->mcol, mcol, size*sizeof(MCol)*RE_MCOL_ELEMS);
				MEM_freeN(mcol);
			}

			node->totmcol= n+1;
		}
	}
	else {
		if (n>=node->totmcol)
			return NULL;

		if (name) *name= obr->mcol[n];
	}

	return node->mcol + index*RE_MCOL_ELEMS;
}

int *RE_vlakren_get_origindex(ObjectRen *obr, VlakRen *vlak, int verify)
{
	int *origindex;
	int nr= vlak->index>>8;

	origindex= obr->vlaknodes[nr].origindex;
	if (origindex==NULL) {
		if (verify)
			origindex= obr->vlaknodes[nr].origindex= MEM_callocN(256*RE_VLAK_ORIGINDEX_ELEMS*sizeof(int), "origindex table");
		else
			return NULL;
	}
	return origindex + (vlak->index & 255)*RE_VLAK_ORIGINDEX_ELEMS;
}

float *RE_vlakren_get_surfnor(ObjectRen *obr, VlakRen *vlak, int verify)
{
	float *surfnor;
	int nr= vlak->index>>8;
	
	surfnor= obr->vlaknodes[nr].surfnor;
	if (surfnor==NULL) {
		if (verify) 
			surfnor= obr->vlaknodes[nr].surfnor= MEM_callocN(256*RE_SURFNOR_ELEMS*sizeof(float), "surfnor table");
		else
			return NULL;
	}
	return surfnor + (vlak->index & 255)*RE_SURFNOR_ELEMS;
}

float *RE_vlakren_get_nmap_tangent(ObjectRen *obr, VlakRen *vlak, int verify)
{
	float *tangent;
	int nr= vlak->index>>8;

	tangent= obr->vlaknodes[nr].tangent;
	if (tangent==NULL) {
		if (verify) 
			tangent= obr->vlaknodes[nr].tangent= MEM_callocN(256*RE_NMAP_TANGENT_ELEMS*sizeof(float), "tangent table");
		else
			return NULL;
	}
	return tangent + (vlak->index & 255)*RE_NMAP_TANGENT_ELEMS;
}

RadFace **RE_vlakren_get_radface(ObjectRen *obr, VlakRen *vlak, int verify)
{
	RadFace **radface;
	int nr= vlak->index>>8;
	
	radface= obr->vlaknodes[nr].radface;
	if (radface==NULL) {
		if (verify) 
			radface = obr->vlaknodes[nr].radface= MEM_callocN(256 * RE_RADFACE_ELEMS * sizeof(void *), "radface table");
		else
			return NULL;
	}
	return radface + (vlak->index & 255)*RE_RADFACE_ELEMS;
}

VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr)
{
	VlakRen *vlr1 = RE_findOrAddVlak(obr, obr->totvlak++);
	MTFace *mtface, *mtface1;
	MCol *mcol, *mcol1;
	float *surfnor, *surfnor1, *tangent, *tangent1;
	int *origindex, *origindex1;
	RadFace **radface, **radface1;
	int i, index = vlr1->index;
	char *name;

	*vlr1= *vlr;
	vlr1->index= index;

	for (i=0; (mtface=RE_vlakren_get_tface(obr, vlr, i, &name, 0)) != NULL; i++) {
		mtface1= RE_vlakren_get_tface(obr, vlr1, i, &name, 1);
		memcpy(mtface1, mtface, sizeof(MTFace)*RE_MTFACE_ELEMS);
	}

	for (i=0; (mcol=RE_vlakren_get_mcol(obr, vlr, i, &name, 0)) != NULL; i++) {
		mcol1= RE_vlakren_get_mcol(obr, vlr1, i, &name, 1);
		memcpy(mcol1, mcol, sizeof(MCol)*RE_MCOL_ELEMS);
	}

	origindex= RE_vlakren_get_origindex(obr, vlr, 0);
	if (origindex) {
		origindex1= RE_vlakren_get_origindex(obr, vlr1, 1);
		/* Just an int, but memcpy for consistency. */
		memcpy(origindex1, origindex, sizeof(int)*RE_VLAK_ORIGINDEX_ELEMS);
	}

	surfnor= RE_vlakren_get_surfnor(obr, vlr, 0);
	if (surfnor) {
		surfnor1= RE_vlakren_get_surfnor(obr, vlr1, 1);
		copy_v3_v3(surfnor1, surfnor);
	}

	tangent= RE_vlakren_get_nmap_tangent(obr, vlr, 0);
	if (tangent) {
		tangent1= RE_vlakren_get_nmap_tangent(obr, vlr1, 1);
		memcpy(tangent1, tangent, sizeof(float)*RE_NMAP_TANGENT_ELEMS);
	}

	radface= RE_vlakren_get_radface(obr, vlr, 0);
	if (radface) {
		radface1= RE_vlakren_get_radface(obr, vlr1, 1);
		*radface1= *radface;
	}

	return vlr1;
}

void RE_vlakren_get_normal(Render *UNUSED(re), ObjectInstanceRen *obi, VlakRen *vlr, float r_nor[3])
{
	float (*nmat)[3]= obi->nmat;

	if (obi->flag & R_TRANSFORMED) {
		mul_v3_m3v3(r_nor, nmat, vlr->n);
		normalize_v3(r_nor);
	}
	else {
		copy_v3_v3(r_nor, vlr->n);
	}
}

void RE_set_customdata_names(ObjectRen *obr, CustomData *data)
{
	/* CustomData layer names are stored per object here, because the
	 * DerivedMesh which stores the layers is freed */
	
	CustomDataLayer *layer;
	int numtf = 0, numcol = 0, i, mtfn, mcn;

	if (CustomData_has_layer(data, CD_MTFACE)) {
		numtf= CustomData_number_of_layers(data, CD_MTFACE);
		obr->mtface= MEM_callocN(sizeof(*obr->mtface)*numtf, "mtfacenames");
	}

	if (CustomData_has_layer(data, CD_MCOL)) {
		numcol= CustomData_number_of_layers(data, CD_MCOL);
		obr->mcol= MEM_callocN(sizeof(*obr->mcol)*numcol, "mcolnames");
	}

	for (i=0, mtfn=0, mcn=0; i < data->totlayer; i++) {
		layer= &data->layers[i];

		if (layer->type == CD_MTFACE) {
			BLI_strncpy(obr->mtface[mtfn++], layer->name, sizeof(layer->name));
			obr->actmtface= CLAMPIS(layer->active_rnd, 0, numtf);
			obr->bakemtface= layer->active;
		}
		else if (layer->type == CD_MCOL) {
			BLI_strncpy(obr->mcol[mcn++], layer->name, sizeof(layer->name));
			obr->actmcol= CLAMPIS(layer->active_rnd, 0, numcol);
		}
	}
}

VlakRen *RE_findOrAddVlak(ObjectRen *obr, int nr)
{
	VlakTableNode *temp;
	VlakRen *v;
	int a;

	if (nr<0) {
		printf("error in findOrAddVlak: %d\n", nr);
		return obr->vlaknodes[0].vlak;
	}
	a= nr>>8;
	
	if (a>=obr->vlaknodeslen-1) {  /* Need to allocate more columns..., and keep last element NULL for free loop */
		temp= obr->vlaknodes;
		
		obr->vlaknodes= MEM_mallocN(sizeof(VlakTableNode)*(obr->vlaknodeslen+TABLEINITSIZE), "vlaknodes");
		if (temp) memcpy(obr->vlaknodes, temp, obr->vlaknodeslen*sizeof(VlakTableNode));
		memset(obr->vlaknodes+obr->vlaknodeslen, 0, TABLEINITSIZE*sizeof(VlakTableNode));

		obr->vlaknodeslen+=TABLEINITSIZE;  /*Does this really need to be power of 2?*/
		if (temp) MEM_freeN(temp);
	}

	v= obr->vlaknodes[a].vlak;
	
	if (v==NULL) {
		int i;

		v= (VlakRen *)MEM_callocN(256*sizeof(VlakRen), "findOrAddVlak");
		obr->vlaknodes[a].vlak= v;

		for (i= (nr & 0xFFFFFF00), a=0; a<256; a++, i++)
			v[a].index= i;
	}
	v+= (nr & 255);
	return v;
}

/* ------------------------------------------------------------------------ */

float *RE_strandren_get_surfnor(ObjectRen *obr, StrandRen *strand, int verify)
{
	float *surfnor;
	int nr= strand->index>>8;
	
	surfnor= obr->strandnodes[nr].surfnor;
	if (surfnor==NULL) {
		if (verify) 
			surfnor= obr->strandnodes[nr].surfnor= MEM_callocN(256*RE_SURFNOR_ELEMS*sizeof(float), "surfnor strand table");
		else
			return NULL;
	}
	return surfnor + (strand->index & 255)*RE_SURFNOR_ELEMS;
}

float *RE_strandren_get_uv(ObjectRen *obr, StrandRen *strand, int n, char **name, int verify)
{
	StrandTableNode *node;
	int nr= strand->index>>8, strandindex= (strand->index&255);
	int index= (n<<8) + strandindex;

	node= &obr->strandnodes[nr];

	if (verify) {
		if (n>=node->totuv) {
			float *uv= node->uv;
			int size= (n+1)*256;

			node->uv= MEM_callocN(size*sizeof(float)*RE_UV_ELEMS, "strand uv table");

			if (uv) {
				size= node->totuv*256;
				memcpy(node->uv, uv, size*sizeof(float)*RE_UV_ELEMS);
				MEM_freeN(uv);
			}

			node->totuv= n+1;
		}
	}
	else {
		if (n>=node->totuv)
			return NULL;

		if (name) *name= obr->mtface[n];
	}

	return node->uv + index*RE_UV_ELEMS;
}

MCol *RE_strandren_get_mcol(ObjectRen *obr, StrandRen *strand, int n, char **name, int verify)
{
	StrandTableNode *node;
	int nr= strand->index>>8, strandindex= (strand->index&255);
	int index= (n<<8) + strandindex;

	node= &obr->strandnodes[nr];

	if (verify) {
		if (n>=node->totmcol) {
			MCol *mcol= node->mcol;
			int size= (n+1)*256;

			node->mcol= MEM_callocN(size*sizeof(MCol)*RE_MCOL_ELEMS, "strand mcol table");

			if (mcol) {
				size= node->totmcol*256;
				memcpy(node->mcol, mcol, size*sizeof(MCol)*RE_MCOL_ELEMS);
				MEM_freeN(mcol);
			}

			node->totmcol= n+1;
		}
	}
	else {
		if (n>=node->totmcol)
			return NULL;

		if (name) *name= obr->mcol[n];
	}

	return node->mcol + index*RE_MCOL_ELEMS;
}

float *RE_strandren_get_simplify(struct ObjectRen *obr, struct StrandRen *strand, int verify)
{
	float *simplify;
	int nr= strand->index>>8;
	
	simplify= obr->strandnodes[nr].simplify;
	if (simplify==NULL) {
		if (verify) 
			simplify= obr->strandnodes[nr].simplify= MEM_callocN(256*RE_SIMPLIFY_ELEMS*sizeof(float), "simplify strand table");
		else
			return NULL;
	}
	return simplify + (strand->index & 255)*RE_SIMPLIFY_ELEMS;
}

int *RE_strandren_get_face(ObjectRen *obr, StrandRen *strand, int verify)
{
	int *face;
	int nr= strand->index>>8;
	
	face= obr->strandnodes[nr].face;
	if (face==NULL) {
		if (verify) 
			face= obr->strandnodes[nr].face= MEM_callocN(256*RE_FACE_ELEMS*sizeof(int), "face strand table");
		else
			return NULL;
	}
	return face + (strand->index & 255)*RE_FACE_ELEMS;
}

/* winspeed is exception, it is stored per instance */
float *RE_strandren_get_winspeed(ObjectInstanceRen *obi, StrandRen *strand, int verify)
{
	float *winspeed;
	int totvector;
	
	winspeed= obi->vectors;
	if (winspeed==NULL) {
		if (verify) {
			totvector= obi->obr->totvert + obi->obr->totstrand;
			winspeed= obi->vectors= MEM_callocN(totvector*RE_WINSPEED_ELEMS*sizeof(float), "winspeed strand table");
		}
		else
			return NULL;
	}
	return winspeed + (obi->obr->totvert + strand->index)*RE_WINSPEED_ELEMS;
}

StrandRen *RE_findOrAddStrand(ObjectRen *obr, int nr)
{
	StrandTableNode *temp;
	StrandRen *v;
	int a;

	if (nr<0) {
		printf("error in findOrAddStrand: %d\n", nr);
		return obr->strandnodes[0].strand;
	}
	a= nr>>8;
	
	if (a>=obr->strandnodeslen-1) {  /* Need to allocate more columns..., and keep last element NULL for free loop */
		temp= obr->strandnodes;
		
		obr->strandnodes= MEM_mallocN(sizeof(StrandTableNode)*(obr->strandnodeslen+TABLEINITSIZE), "strandnodes");
		if (temp) memcpy(obr->strandnodes, temp, obr->strandnodeslen*sizeof(StrandTableNode));
		memset(obr->strandnodes+obr->strandnodeslen, 0, TABLEINITSIZE*sizeof(StrandTableNode));

		obr->strandnodeslen+=TABLEINITSIZE;  /*Does this really need to be power of 2?*/
		if (temp) MEM_freeN(temp);
	}

	v= obr->strandnodes[a].strand;
	
	if (v==NULL) {
		int i;

		v= (StrandRen *)MEM_callocN(256*sizeof(StrandRen), "findOrAddStrand");
		obr->strandnodes[a].strand= v;

		for (i= (nr & 0xFFFFFF00), a=0; a<256; a++, i++)
			v[a].index= i;
	}
	v+= (nr & 255);
	return v;
}

StrandBuffer *RE_addStrandBuffer(ObjectRen *obr, int totvert)
{
	StrandBuffer *strandbuf;

	strandbuf= MEM_callocN(sizeof(StrandBuffer), "StrandBuffer");
	strandbuf->vert= MEM_callocN(sizeof(StrandVert)*totvert, "StrandVert");
	strandbuf->totvert= totvert;
	strandbuf->obr= obr;

	obr->strandbuf= strandbuf;

	return strandbuf;
}

/* ------------------------------------------------------------------------ */

ObjectRen *RE_addRenderObject(Render *re, Object *ob, Object *par, int index, int psysindex, int lay)
{
	ObjectRen *obr= MEM_callocN(sizeof(ObjectRen), "object render struct");
	
	BLI_addtail(&re->objecttable, obr);
	obr->ob= ob;
	obr->par= par;
	obr->index= index;
	obr->psysindex= psysindex;
	obr->lay= lay;

	return obr;
}

void free_renderdata_vertnodes(VertTableNode *vertnodes)
{
	int a;
	
	if (vertnodes==NULL) return;
	
	for (a=0; vertnodes[a].vert; a++) {
		MEM_freeN(vertnodes[a].vert);
		
		if (vertnodes[a].rad)
			MEM_freeN(vertnodes[a].rad);
		if (vertnodes[a].strand)
			MEM_freeN(vertnodes[a].strand);
		if (vertnodes[a].tangent)
			MEM_freeN(vertnodes[a].tangent);
		if (vertnodes[a].stress)
			MEM_freeN(vertnodes[a].stress);
		if (vertnodes[a].winspeed)
			MEM_freeN(vertnodes[a].winspeed);
		if (vertnodes[a].origindex)
			MEM_freeN(vertnodes[a].origindex);
	}
	
	MEM_freeN(vertnodes);
}

void free_renderdata_vlaknodes(VlakTableNode *vlaknodes)
{
	int a;
	
	if (vlaknodes==NULL) return;
	
	for (a=0; vlaknodes[a].vlak; a++) {
		MEM_freeN(vlaknodes[a].vlak);
		
		if (vlaknodes[a].mtface)
			MEM_freeN(vlaknodes[a].mtface);
		if (vlaknodes[a].mcol)
			MEM_freeN(vlaknodes[a].mcol);
		if (vlaknodes[a].origindex)
			MEM_freeN(vlaknodes[a].origindex);
		if (vlaknodes[a].surfnor)
			MEM_freeN(vlaknodes[a].surfnor);
		if (vlaknodes[a].tangent)
			MEM_freeN(vlaknodes[a].tangent);
		if (vlaknodes[a].radface)
			MEM_freeN(vlaknodes[a].radface);
	}
	
	MEM_freeN(vlaknodes);
}

static void free_renderdata_strandnodes(StrandTableNode *strandnodes)
{
	int a;
	
	if (strandnodes==NULL) return;
	
	for (a=0; strandnodes[a].strand; a++) {
		MEM_freeN(strandnodes[a].strand);
		
		if (strandnodes[a].uv)
			MEM_freeN(strandnodes[a].uv);
		if (strandnodes[a].mcol)
			MEM_freeN(strandnodes[a].mcol);
		if (strandnodes[a].winspeed)
			MEM_freeN(strandnodes[a].winspeed);
		if (strandnodes[a].surfnor)
			MEM_freeN(strandnodes[a].surfnor);
		if (strandnodes[a].simplify)
			MEM_freeN(strandnodes[a].simplify);
		if (strandnodes[a].face)
			MEM_freeN(strandnodes[a].face);
	}
	
	MEM_freeN(strandnodes);
}

void free_renderdata_tables(Render *re)
{
	ObjectInstanceRen *obi;
	ObjectRen *obr;
	StrandBuffer *strandbuf;
	int a=0;
	
	for (obr=re->objecttable.first; obr; obr=obr->next) {
		if (obr->vertnodes) {
			free_renderdata_vertnodes(obr->vertnodes);
			obr->vertnodes= NULL;
			obr->vertnodeslen= 0;
		}

		if (obr->vlaknodes) {
			free_renderdata_vlaknodes(obr->vlaknodes);
			obr->vlaknodes= NULL;
			obr->vlaknodeslen= 0;
			obr->totvlak= 0;
		}

		if (obr->bloha) {
			for (a=0; obr->bloha[a]; a++)
				MEM_freeN(obr->bloha[a]);

			MEM_freeN(obr->bloha);
			obr->bloha= NULL;
			obr->blohalen= 0;
		}

		if (obr->strandnodes) {
			free_renderdata_strandnodes(obr->strandnodes);
			obr->strandnodes= NULL;
			obr->strandnodeslen= 0;
		}

		strandbuf= obr->strandbuf;
		if (strandbuf) {
			if (strandbuf->vert) MEM_freeN(strandbuf->vert);
			if (strandbuf->bound) MEM_freeN(strandbuf->bound);
			MEM_freeN(strandbuf);
		}

		if (obr->mtface)
			MEM_freeN(obr->mtface);

		if (obr->mcol)
			MEM_freeN(obr->mcol);
			
		if (obr->rayfaces) {
			MEM_freeN(obr->rayfaces);
			obr->rayfaces = NULL;
		}

		if (obr->rayprimitives) {
			MEM_freeN(obr->rayprimitives);
			obr->rayprimitives = NULL;
		}

		if (obr->raytree) {
			RE_rayobject_free(obr->raytree);
			obr->raytree = NULL;
		}
	}

	if (re->objectinstance) {
		for (obi=re->instancetable.first; obi; obi=obi->next) {
			if (obi->vectors)
				MEM_freeN(obi->vectors);

			if (obi->raytree)
				RE_rayobject_free(obi->raytree);
		}

		MEM_freeN(re->objectinstance);
		re->objectinstance= NULL;
		re->totinstance= 0;
		re->instancetable.first= re->instancetable.last= NULL;
	}

	if (re->sortedhalos) {
		MEM_freeN(re->sortedhalos);
		re->sortedhalos= NULL;
	}

	BLI_freelistN(&re->customdata_names);
	BLI_freelistN(&re->objecttable);
	BLI_freelistN(&re->instancetable);
}

/* ------------------------------------------------------------------------ */

HaloRen *RE_findOrAddHalo(ObjectRen *obr, int nr)
{
	HaloRen *h, **temp;
	int a;

	if (nr<0) {
		printf("error in findOrAddHalo: %d\n", nr);
		return NULL;
	}
	a= nr>>8;
	
	if (a>=obr->blohalen-1) {  /* Need to allocate more columns..., and keep last element NULL for free loop */
		//printf("Allocating %i more halo groups.  %i total.\n", 
		//	TABLEINITSIZE, obr->blohalen+TABLEINITSIZE );
		temp=obr->bloha;
		
		obr->bloha = (HaloRen**)MEM_callocN(sizeof(void *) * (obr->blohalen + TABLEINITSIZE), "Bloha");
		if (temp) memcpy(obr->bloha, temp, obr->blohalen*sizeof(void *));
		memset(&(obr->bloha[obr->blohalen]), 0, TABLEINITSIZE * sizeof(void *));
		obr->blohalen+=TABLEINITSIZE;  /*Does this really need to be power of 2?*/
		if (temp) MEM_freeN(temp);
	}
	
	h= obr->bloha[a];
	if (h==NULL) {
		h= (HaloRen *)MEM_callocN(256*sizeof(HaloRen), "findOrAdHalo");
		obr->bloha[a]= h;
	}
	h+= (nr & 255);
	return h;
}

/* ------------------------------------------------------------------------- */

HaloRen *RE_inithalo(Render *re, ObjectRen *obr, Material *ma,
                     const float vec[3], const float vec1[3],
                     const float *orco, float hasize, float vectsize, int seed)
{
	HaloRen *har;
	MTex *mtex;
	float tin, tr, tg, tb, ta;
	float xn, yn, zn, texvec[3], hoco[4], hoco1[4];

	if (hasize==0.0f) return NULL;

	projectverto(vec, re->winmat, hoco);
	if (hoco[3]==0.0f) return NULL;
	if (vec1) {
		projectverto(vec1, re->winmat, hoco1);
		if (hoco1[3]==0.0f) return NULL;
	}

	har= RE_findOrAddHalo(obr, obr->tothalo++);
	copy_v3_v3(har->co, vec);
	har->hasize= hasize;

	/* actual projectvert is done in function project_renderdata() because of parts/border/pano */
	/* we do it here for sorting of halos */
	zn= hoco[3];
	har->xs= 0.5f*re->winx*(hoco[0]/zn);
	har->ys= 0.5f*re->winy*(hoco[1]/zn);
	har->zs= 0x7FFFFF*(hoco[2]/zn);
	
	har->zBufDist = 0x7FFFFFFF*(hoco[2]/zn); 
	
	/* halovect */
	if (vec1) {

		har->type |= HA_VECT;

		xn=  har->xs - 0.5f*re->winx*(hoco1[0]/hoco1[3]);
		yn=  har->ys - 0.5f*re->winy*(hoco1[1]/hoco1[3]);
		if (xn==0.0f || (xn==0.0f && yn==0.0f)) zn= 0.0f;
		else zn= atan2(yn, xn);

		har->sin= sin(zn);
		har->cos= cos(zn);
		zn= len_v3v3(vec1, vec);

		har->hasize= vectsize*zn + (1.0f-vectsize)*hasize;
		
		sub_v3_v3v3(har->no, vec, vec1);
		normalize_v3(har->no);
	}

	if (ma->mode & MA_HALO_XALPHA) har->type |= HA_XALPHA;

	har->alfa= ma->alpha;
	har->r= ma->r;
	har->g= ma->g;
	har->b= ma->b;
	har->add= (255.0f*ma->add);
	har->mat= ma;
	har->hard= ma->har;
	har->seed= seed % 256;

	if (ma->mode & MA_STAR) har->starpoints= ma->starc;
	if (ma->mode & MA_HALO_LINES) har->linec= ma->linec;
	if (ma->mode & MA_HALO_RINGS) har->ringc= ma->ringc;
	if (ma->mode & MA_HALO_FLARE) har->flarec= ma->flarec;


	if (ma->mtex[0]) {

		if (ma->mode & MA_HALOTEX) {
			har->tex = 1;
		}
		else if (har->mat->septex & (1 << 0)) {
			/* only 1 level textures */
		}
		else {
			mtex= ma->mtex[0];
			copy_v3_v3(texvec, vec);

			if (mtex->texco & TEXCO_NORM) {
				;
			}
			else if (mtex->texco & TEXCO_OBJECT) {
				/* texvec[0]+= imatbase->ivec[0]; */
				/* texvec[1]+= imatbase->ivec[1]; */
				/* texvec[2]+= imatbase->ivec[2]; */
				/* mul_m3_v3(imatbase->imat, texvec); */
			}
			else {
				if (orco) {
					copy_v3_v3(texvec, orco);
				}
			}

			externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta, 0, re->pool);

			yn= tin*mtex->colfac;
			//zn= tin*mtex->alphafac;

			if (mtex->mapto & MAP_COL) {
				zn= 1.0f-yn;
				har->r= (yn*tr+ zn*ma->r);
				har->g= (yn*tg+ zn*ma->g);
				har->b= (yn*tb+ zn*ma->b);
			}
			if (mtex->texco & TEXCO_UV) {
				har->alfa= tin;
			}
			if (mtex->mapto & MAP_ALPHA)
				har->alfa= tin;
		}
	}

	har->pool = re->pool;

	return har;
}

HaloRen *RE_inithalo_particle(Render *re, ObjectRen *obr, DerivedMesh *dm, Material *ma,
                              const float vec[3], const float vec1[3],
                              const float *orco, const float *uvco, float hasize, float vectsize, int seed, const float pa_co[3])
{
	HaloRen *har;
	MTex *mtex;
	float tin, tr, tg, tb, ta;
	float xn, yn, zn, texvec[3], hoco[4], hoco1[4], in[3], tex[3], out[3];
	int i, hasrgb;

	if (hasize==0.0f) return NULL;

	projectverto(vec, re->winmat, hoco);
	if (hoco[3]==0.0f) return NULL;
	if (vec1) {
		projectverto(vec1, re->winmat, hoco1);
		if (hoco1[3]==0.0f) return NULL;
	}

	har= RE_findOrAddHalo(obr, obr->tothalo++);
	copy_v3_v3(har->co, vec);
	har->hasize= hasize;

	/* actual projectvert is done in function project_renderdata() because of parts/border/pano */
	/* we do it here for sorting of halos */
	zn= hoco[3];
	har->xs= 0.5f*re->winx*(hoco[0]/zn);
	har->ys= 0.5f*re->winy*(hoco[1]/zn);
	har->zs= 0x7FFFFF*(hoco[2]/zn);
	
	har->zBufDist = 0x7FFFFFFF*(hoco[2]/zn); 
	
	/* halovect */
	if (vec1) {

		har->type |= HA_VECT;

		xn=  har->xs - 0.5f*re->winx*(hoco1[0]/hoco1[3]);
		yn=  har->ys - 0.5f*re->winy*(hoco1[1]/hoco1[3]);
		if (xn==0.0f || (xn==0.0f && yn==0.0f)) zn= 0.0;
		else zn= atan2(yn, xn);

		har->sin= sin(zn);
		har->cos= cos(zn);
		zn= len_v3v3(vec1, vec)*0.5f;

		har->hasize= vectsize*zn + (1.0f-vectsize)*hasize;
		
		sub_v3_v3v3(har->no, vec, vec1);
		normalize_v3(har->no);
	}

	if (ma->mode & MA_HALO_XALPHA) har->type |= HA_XALPHA;

	har->alfa= ma->alpha;
	har->r= ma->r;
	har->g= ma->g;
	har->b= ma->b;
	har->add= (255.0f*ma->add);
	har->mat= ma;
	har->hard= ma->har;
	har->seed= seed % 256;

	if (ma->mode & MA_STAR) har->starpoints= ma->starc;
	if (ma->mode & MA_HALO_LINES) har->linec= ma->linec;
	if (ma->mode & MA_HALO_RINGS) har->ringc= ma->ringc;
	if (ma->mode & MA_HALO_FLARE) har->flarec= ma->flarec;

	if ((ma->mode & MA_HALOTEX) && ma->mtex[0])
		har->tex= 1;
	
	for (i=0; i<MAX_MTEX; i++)
		if (ma->mtex[i] && (ma->septex & (1<<i))==0) {
			mtex= ma->mtex[i];
			copy_v3_v3(texvec, vec);

			if (mtex->texco & TEXCO_NORM) {
				;
			}
			else if (mtex->texco & TEXCO_OBJECT) {
				if (mtex->object)
					mul_m4_v3(mtex->object->imat_ren, texvec);
			}
			else if (mtex->texco & TEXCO_GLOB) {
				copy_v3_v3(texvec, vec);
			}
			else if (mtex->texco & TEXCO_UV && uvco) {
				int uv_index=CustomData_get_named_layer_index(&dm->faceData, CD_MTFACE, mtex->uvname);
				if (uv_index<0)
					uv_index=CustomData_get_active_layer_index(&dm->faceData, CD_MTFACE);

				uv_index-=CustomData_get_layer_index(&dm->faceData, CD_MTFACE);

				texvec[0]=2.0f*uvco[2*uv_index]-1.0f;
				texvec[1]=2.0f*uvco[2*uv_index+1]-1.0f;
				texvec[2]=0.0f;
			}
			else if (mtex->texco & TEXCO_PARTICLE) {
				/* particle coordinates in range [0, 1] */
				texvec[0] = 2.f * pa_co[0] - 1.f;
				texvec[1] = 2.f * pa_co[1] - 1.f;
				texvec[2] = pa_co[2];
			}
			else if (orco) {
				copy_v3_v3(texvec, orco);
			}

			hasrgb = externtex(mtex, texvec, &tin, &tr, &tg, &tb, &ta, 0, re->pool);

			//yn= tin*mtex->colfac;
			//zn= tin*mtex->alphafac;
			if (mtex->mapto & MAP_COL) {
				tex[0]=tr;
				tex[1]=tg;
				tex[2]=tb;
				out[0]=har->r;
				out[1]=har->g;
				out[2]=har->b;

				texture_rgb_blend(in, tex, out, tin, mtex->colfac, mtex->blendtype);
			//	zn= 1.0-yn;
				//har->r= (yn*tr+ zn*ma->r);
				//har->g= (yn*tg+ zn*ma->g);
				//har->b= (yn*tb+ zn*ma->b);
				har->r= in[0];
				har->g= in[1];
				har->b= in[2];
			}

			/* alpha returned, so let's use it instead of intensity */
			if (hasrgb)
				tin = ta;

			if (mtex->mapto & MAP_ALPHA)
				har->alfa = texture_value_blend(mtex->def_var, har->alfa, tin, mtex->alphafac, mtex->blendtype);
			if (mtex->mapto & MAP_HAR)
				har->hard = 1.0f+126.0f*texture_value_blend(mtex->def_var, ((float)har->hard)/127.0f, tin, mtex->hardfac, mtex->blendtype);
			if (mtex->mapto & MAP_RAYMIRR)
				har->hasize = 100.0f*texture_value_blend(mtex->def_var, har->hasize/100.0f, tin, mtex->raymirrfac, mtex->blendtype);
			if (mtex->mapto & MAP_TRANSLU) {
				float add = texture_value_blend(mtex->def_var, (float)har->add/255.0f, tin, mtex->translfac, mtex->blendtype);
				CLAMP(add, 0.f, 1.f);
				har->add = 255.0f*add;
			}
			/* now what on earth is this good for?? */
			//if (mtex->texco & 16) {
			//	har->alfa= tin;
			//}
		}

	har->pool = re->pool;

	return har;
}

/* -------------------------- operations on entire database ----------------------- */

/* ugly function for halos in panorama */
static int panotestclip(Render *re, int do_pano, float v[4])
{
	/* part size (ensure we run RE_parts_clamp first) */
	BLI_assert(re->partx == min_ii(re->r.tilex, re->rectx));
	BLI_assert(re->party == min_ii(re->r.tiley, re->recty));

	if (do_pano == FALSE) {
		return testclip(v);
	}
	else {
		/* to be used for halos en infos */
		float abs4;
		short c = 0;

		int xparts = (re->rectx + re->partx - 1) / re->partx;

		abs4= fabsf(v[3]);

		if (v[2]< -abs4) c=16;		/* this used to be " if (v[2]<0) ", see clippz() */
		else if (v[2]> abs4) c+= 32;

		if ( v[1]>abs4) c+=4;
		else if ( v[1]< -abs4) c+=8;

		abs4*= xparts;
		if ( v[0]>abs4) c+=2;
		else if ( v[0]< -abs4) c+=1;

		return c;
	}
}

/**
 * This adds the hcs coordinates to vertices. It iterates over all
 * vertices, halos and faces. After the conversion, we clip in hcs.
 *
 * Elsewhere, all primites are converted to vertices.
 * Called in
 * - envmapping (envmap.c)
 * - shadow buffering (shadbuf.c)
 */

void project_renderdata(Render *re,
                        void (*projectfunc)(const float *, float mat[4][4], float *),
                        int do_pano, float xoffs, int UNUSED(do_buckets))
{
	ObjectRen *obr;
	HaloRen *har = NULL;
	float zn, vec[3], hoco[4];
	int a;

	if (do_pano) {
		float panophi= xoffs;
		
		re->panosi= sin(panophi);
		re->panoco= cos(panophi);
	}

	for (obr=re->objecttable.first; obr; obr=obr->next) {
		/* calculate view coordinates (and zbuffer value) */
		for (a=0; a<obr->tothalo; a++) {
			if ((a & 255)==0) har= obr->bloha[a>>8];
			else har++;

			if (do_pano) {
				vec[0]= re->panoco*har->co[0] + re->panosi*har->co[2];
				vec[1]= har->co[1];
				vec[2]= -re->panosi*har->co[0] + re->panoco*har->co[2];
			}
			else {
				copy_v3_v3(vec, har->co);
			}

			projectfunc(vec, re->winmat, hoco);
			
			/* we clip halos less critical, but not for the Z */
			hoco[0]*= 0.5f;
			hoco[1]*= 0.5f;
			
			if ( panotestclip(re, do_pano, hoco) ) {
				har->miny= har->maxy= -10000;	/* that way render clips it */
			}
			else if (hoco[3]<0.0f) {
				har->miny= har->maxy= -10000;	/* render clips it */
			}
			else { /* do the projection...*/
				/* bring back hocos */
				hoco[0]*= 2.0f;
				hoco[1]*= 2.0f;
				
				zn= hoco[3];
				har->xs= 0.5f*re->winx*(1.0f+hoco[0]/zn); /* the 0.5 negates the previous 2...*/
				har->ys= 0.5f*re->winy*(1.0f+hoco[1]/zn);
			
				/* this should be the zbuffer coordinate */
				har->zs= 0x7FFFFF*(hoco[2]/zn);
				/* taking this from the face clip functions? seems ok... */
				har->zBufDist = 0x7FFFFFFF*(hoco[2]/zn);
				
				vec[0]+= har->hasize;
				projectfunc(vec, re->winmat, hoco);
				vec[0]-= har->hasize;
				zn= hoco[3];
				har->rad= fabsf(har->xs- 0.5f*re->winx*(1.0f+hoco[0]/zn));
			
				/* this clip is not really OK, to prevent stars to become too large */
				if (har->type & HA_ONLYSKY) {
					if (har->rad>3.0f) har->rad= 3.0f;
				}
			
				har->radsq= har->rad*har->rad;
			
				har->miny= har->ys - har->rad/re->ycor;
				har->maxy= har->ys + har->rad/re->ycor;
			
				/* the Zd value is still not really correct for pano */
			
				vec[2] -= har->hasize;  /* z negative, otherwise it's clipped */
				projectfunc(vec, re->winmat, hoco);
				zn = hoco[3];
				zn = fabsf((float)har->zs - 0x7FFFFF * (hoco[2] / zn));
				har->zd = CLAMPIS(zn, 0, INT_MAX);
			
			}
			
		}
	}
}

/* ------------------------------------------------------------------------- */

ObjectInstanceRen *RE_addRenderInstance(Render *re, ObjectRen *obr, Object *ob, Object *par, int index, int psysindex, float mat[4][4], int lay)
{
	ObjectInstanceRen *obi;
	float mat3[3][3];

	obi= MEM_callocN(sizeof(ObjectInstanceRen), "ObjectInstanceRen");
	obi->obr= obr;
	obi->ob= ob;
	obi->par= par;
	obi->index= index;
	obi->psysindex= psysindex;
	obi->lay= lay;

	if (mat) {
		copy_m4_m4(obi->mat, mat);
		copy_m3_m4(mat3, mat);
		invert_m3_m3(obi->nmat, mat3);
		transpose_m3(obi->nmat);
		obi->flag |= R_DUPLI_TRANSFORMED;
	}

	BLI_addtail(&re->instancetable, obi);

	return obi;
}

void RE_makeRenderInstances(Render *re)
{
	ObjectInstanceRen *obi, *oldobi;
	ListBase newlist;
	int tot;

	/* convert list of object instances to an array for index based lookup */
	tot= BLI_countlist(&re->instancetable);
	re->objectinstance= MEM_callocN(sizeof(ObjectInstanceRen)*tot, "ObjectInstance");
	re->totinstance= tot;
	newlist.first= newlist.last= NULL;

	obi= re->objectinstance;
	for (oldobi=re->instancetable.first; oldobi; oldobi=oldobi->next) {
		*obi= *oldobi;

		if (obi->obr) {
			obi->prev= obi->next= NULL;
			BLI_addtail(&newlist, obi);
			obi++;
		}
		else
			re->totinstance--;
	}

	BLI_freelistN(&re->instancetable);
	re->instancetable= newlist;
}

int clip_render_object(float boundbox[2][3], float bounds[4], float winmat[4][4])
{
	float mat[4][4], vec[4];
	int a, fl, flag = -1;

	copy_m4_m4(mat, winmat);

	for (a=0; a < 8; a++) {
		vec[0]= (a & 1)? boundbox[0][0]: boundbox[1][0];
		vec[1]= (a & 2)? boundbox[0][1]: boundbox[1][1];
		vec[2]= (a & 4)? boundbox[0][2]: boundbox[1][2];
		vec[3]= 1.0;
		mul_m4_v4(mat, vec);

		fl = 0;
		if (bounds) {
			if      (vec[0] < bounds[0] * vec[3]) fl |= 1;
			else if (vec[0] > bounds[1] * vec[3]) fl |= 2;
			
			if      (vec[1] > bounds[3] * vec[3]) fl |= 4;
			else if (vec[1] < bounds[2] * vec[3]) fl |= 8;
		}
		else {
			if      (vec[0] < -vec[3]) fl |= 1;
			else if (vec[0] >  vec[3]) fl |= 2;
			
			if      (vec[1] >  vec[3]) fl |= 4;
			else if (vec[1] < -vec[3]) fl |= 8;
		}
		if      (vec[2] < -vec[3]) fl |= 16;
		else if (vec[2] >  vec[3]) fl |= 32;

		flag &= fl;
		if (flag == 0) {
			return 0;
		}
	}

	return flag;
}

