/*<html><pre>*/
#include <../source/TAL/objbase.h>

int		V_LOCAL
calc_patch_size(int numvert,int numtri,vboolean fullsize)
{
	if(fullsize)
		return sizeof(tripatch_t)+
			numvert * (sizeof(vec3_t) + sizeof(color4b_t) + sizeof(vec3_t) + sizeof(vec2_t)) +
      		numtri * (sizeof(vec3_t) + sizeof(itriple_t));
  	else
    	return sizeof(tripatch_t)+
      		numvert*sizeof(vec3_t)+
      		numtri*sizeof(itriple_t);
}

tripatch_p	V_GLOBAL
patAlloc(int numvert,int numtri,vboolean fullsize)
{
	int size;
	tripatch_t *p;
	size=calc_patch_size(numvert,numtri,fullsize);
	p=(tripatch_p)memAlloc(size);
	memZero(p,size);
	p->numvert=numvert;
	p->numtri=numtri;
	p->extinfo=fullsize;
	if(fullsize)
	{
		p->vlist=(vec3_t*)&p[1];
		p->clist=(color4b_t*)(p->vlist+numvert);
		p->nlist=(vec3_t*)(p->clist+numvert);
		p->fnlist=p->nlist+numvert;
		p->uvlist=(vec2_t*)(p->fnlist+numtri);
		p->tlist=(itriple_t*)(p->uvlist+numvert);
	}
	else
	{
		p->vlist=(vec3_t*)&p[1];
		p->tlist=(itriple_t*)(p->vlist+numvert);
	}
	return p;
}

tripatch_p	V_GLOBAL
patDuplicate(tripatch_p ref)
{
	int size;
	tripatch_p p;
	size=calc_patch_size(ref->numvert,ref->numtri,ref->extinfo);
	p=(tripatch_p)memAlloc(size);
	memCopy(p,ref,size);
	return p;
}

void V_GLOBAL
patFree(tripatch_p patch)
{
	memFree(patch);
}

tripatch_p V_GLOBAL
patCreateRectangular(vec_t l,vec_t t,vec_t r,vec_t b,
	int hsplit,int vsplit,vboolean fullsize)
{
	tripatch_t *pat;
	int i,j,count,ofs;
	vec3_t p,d;
	if(vsplit<1 || hsplit<1)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,"patCreateRectangular(): wrong spits.\n");
#endif	/* CHECKED_BUILD */
    	return NULL;
	}
	pat=patAlloc((hsplit+1)*(vsplit+1),hsplit*vsplit*2,fullsize);
	pat->hsplit=hsplit;
	pat->vsplit=vsplit;
	vec3Set(pat->base[0],l,t,0);
	vec3Set(pat->base[1],r,t,0);
	vec3Set(pat->base[2],l,b,0);
	vec3Set(pat->base[3],r,b,0);
	vec3Set(pat->basenormal,0,0,1);
	vec3Set(p,l,t,0);
	vec3Set(d,r-l,b-t,0);
	d[0]/=(vec_t)hsplit;
	d[1]/=(vec_t)vsplit;
	count=0;
    /* create vertices */
    for(i=0;i<=vsplit;i++)
    {
        for(j=0;j<=hsplit;j++)
        {
            vec3Copy(pat->vlist[count++],p);
            p[0]+=d[0];
        }
        p[0]=l;
        p[1]+=d[1];
    }
    /* create face list */
    count=0;
    ofs=0;
    for(i=0;i<vsplit;i++)
    {
        for(j=0;j<hsplit;j++)
        {
            pat->tlist[count][0]=ofs+j;
            pat->tlist[count][1]=ofs+j+1;
            pat->tlist[count][2]=ofs+j+hsplit+2;
            count++;
            pat->tlist[count][0]=ofs+j;
            pat->tlist[count][1]=ofs+j+hsplit+2;
            pat->tlist[count][2]=ofs+j+hsplit+1;
            count++;
        }
        ofs+=hsplit+1;
    }
    return pat;
}

tripatch_p	V_GLOBAL
patCreateTriangular(vec3_t a,vec3_t b,vec3_t c,int split,int extinfo)
{
    tripatch_t *pat;
    int i,j,count,unopt_limit,ofs;
    vec3_t dab,dac,p,pac;
    vec3_t ab,ac;
    int numvert, numtri;
    if(split<1)
    {
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,"patCreateTriangular(): wrong spit.\n");
#endif	/* CHECKED_BUILD */
      return NULL;
    }
    numvert = (split*split+split)/2 + split + 1;
    numtri = split*split;
    pat=patAlloc(numvert,numtri,extinfo);
    pat->hsplit=split;
    pat->vsplit=0;
    vec3Sub(ab,b,a);
    vec3Sub(ac,c,a);
    vec3Copy(pat->base[0],a);
    vec3Copy(pat->base[1],b);
    vec3Copy(pat->base[2],c);
    vec3Zero(pat->base[3]);
    vec3Cross(pat->basenormal,ab,ac);
    vec3Unit(pat->basenormal,pat->basenormal);
    vec3Scale(dab,ab,(vec_t)1.0/(vec_t)split);
    vec3Scale(dac,ac,(vec_t)1.0/(vec_t)split);
    /* generate vertices */
    vec3Copy(p,a);
    count=0;
    /* TODO: reverse generation order */
    for(i=0;i<=split;i++)
    {
        unopt_limit=split-i;
        vec3Copy(pac,p);
        for(j=0;j<=unopt_limit;j++)
        {
			vec3Copy(pat->vlist[count++],p);
			vec3Add(p,p,dab);
        }
        vec3Add(p,pac,dac);
    }
  	/*	generate face list
  	 *	first row - all triangles "below" the base line
  	 */
    for(count=0;count<split;count++)
    {
		pat->tlist[count][0]=count;
		pat->tlist[count][1]=count+1;
		pat->tlist[count][2]=count+1+split;
    }
  	/*	rest rows - tris both "above" and "below" relative to the base line
  	 */
    ofs=split+1;
    for(i=1;i<split;i++)
    {
        unopt_limit=split-i;
        for(j=0;j<unopt_limit;j++)
        {
            pat->tlist[count][0]=ofs+j;
            pat->tlist[count][1]=ofs+j-unopt_limit-1;
            pat->tlist[count][2]=ofs+j+1;
            count++;
            pat->tlist[count][0]=ofs+j;
            pat->tlist[count][1]=ofs+j+1;
            pat->tlist[count][2]=ofs+j+unopt_limit+1;
            count++;
        }
        ofs+=unopt_limit+1;
    }
    return pat;
}

void V_GLOBAL
patDefaultGeometry(tripatch_p patch)
{
    int i;
    vec3_t ab,ac,cross;
    vec3_t e1,e2;
    vec_t e1_abs;
    matrix3_t rot,scale,xform;
    vec3_t uvw;
    vec3_t dist;
    int j,usenext;
    int *used;
    vec3_t sum;
    if(!patch || !patch->extinfo)
    {
#ifdef	CHECKED_BUILD
  		conlPrintf(LOG_ERROR,"patDefaultGeometry(%X (%X)): wrong argument.\n",
  			patch, patch->extinfo);
#endif	/* CHECKED_BUILD */
		return;
    }
    /*
     *	Set all vertex colors to semi-transparent gray ( [128, 128, 128, 128] )
     */
    memFill(patch->clist,0x80,patch->numvert * sizeof(color4b_t));
    /*
     *	Calculate face normals.
     */
	for(i=0;i<patch->numtri;i++)
    {
		vec3Sub(ab,patch->vlist[patch->tlist[i][1]],patch->vlist[patch->tlist[i][0]]);
		vec3Sub(ac,patch->vlist[patch->tlist[i][2]],patch->vlist[patch->tlist[i][0]]);
		vec3Cross(cross,ab,ac);
		vec3Unit(patch->fnlist[i],cross);
    }
    /*
     *	Calculate vertex normals: the topology is not fixed, so we can't
     *	simple obtain adjacent triangles. Here we (dumb) iterate throw all
     *	triangles in patch.
	 */
	used=(int*)memAlloc(patch->numtri*sizeof(int));
    for(i=0;i<patch->numvert;i++)
    {
		memZero32(used,patch->numtri*sizeof(int)>>2);
		usenext=0;
		/* iterate throw all triangles */
		for(j=0;j<patch->numtri;j++)
		{
			/* assume face are not degenerate (all vertices must be different) */
			if(patch->tlist[j][0]==i || patch->tlist[j][1]==i || patch->tlist[j][2]==i)
				used[usenext++]=j;
		}
		/* Average all normals */
		if(!usenext)
		{
			/*
			 *	Isolated vertex: broken topology ? Anyway, we must do something
			 *	'intelligent' to avoid future zero-divisions.
			 */
			if(!vec3IsZero(patch->vlist[i]))
			{
				vec3Unit(patch->nlist[i],patch->vlist[i]);
				vec3Negate(patch->nlist[i],patch->nlist[i]);
			}
			else
				vec3Set(patch->nlist[i],0,0,1);
			continue;
		}
      	vec3Copy(sum,patch->fnlist[used[0]]);
		for(j=1;j<usenext;j++)
			vec3Add(sum,sum,patch->fnlist[used[j]]);
		vec3Scale(patch->nlist[i],sum,(vec_t)1/(vec_t)usenext);
	}
	memFree(used);
	/*
	 *	Apply simple planar wrap, scale texture so it can completely
	 *	enclose patch.
	 */
	vec3Sub(e1,patch->base[1],patch->base[0]);
	mat3Zero(scale);
    e1_abs=vec3Abs(e1);
    if(IsZero(e1_abs))
    {
#ifdef	CHECKED_BUILD
		conlPuts(LOG_WARNING,"patDefaultGeometry(): can't map planar UV:"
			" zero length of the base edge.\n");
#endif	/* CHECKED_BUILD */
		return;
    }
	scale[0][0]=scale[1][1]=scale[2][2]=(vec_t)1.0/e1_abs;
    vec3Scale(e1,e1,scale[0][0]);
    vec3Cross(e2,patch->basenormal,e1);
    mat3SetRows(rot,e1,e2,patch->basenormal);
    mat3MatMul(xform,scale,rot);
    for(i=0;i<patch->numvert;i++)
    {
		vec3Sub(dist,patch->vlist[i],patch->base[0]);
		mat3VecMul(uvw,xform,dist);
		vec2Set(patch->uvlist[i],uvw[0],uvw[1]);
		/*
		vec2Set(patch->uvlist[i],uvw[0]*settings.u_tile,uvw[1]*settings.v_tile);
		*/
	}
}

/*
 *	Replaces X, Y or Z components of vertices by values in given 'mat'.
 *	Topology remains unchanged.
 */
void	V_GLOBAL
patSetGeometry(tripatch_t *pat,matrix_t *mat,u32_t ind)
{
	int i,j,w,h,count;
	if(!pat || !mat || ind>2)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"patSetGeometry(%X,%X,%u): incorrect parameter(s).\n",
			pat,mat,ind);
#endif	/* CHECKED_BUILD */
		return;
	}
	w=pat->hsplit+1;
	h=pat->vsplit+1;
	if(w!=(int)mat->cols || h!=(int)mat->rows)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,"patSetGeometry(%X,%X,%u): matrix expected "
			"to be [%d X %d], found [%u X %u].\n",pat,mat,ind,
			w,h,mat->cols,mat->rows);
#endif	/* CHECKED_BUILD */
		return;
	}
	count=0;
    for(i=0;i<h;i++)
		for(j=0;j<w;j++)
			pat->vlist[count++][ind]=(vec_t)mat->cell[i][j];
}

/*
 *	Draws unculled wireframe via OpenGL.
 */
void	V_GLOBAL
patDrawWireframeEx(tripatch_t *pat,int draw_bits)
{
	int i,j,ul,uls;
	vec3_t 		*verts=pat->vlist;
	color4b_t	*colors=pat->clist;
	vec2_t		*texcoords=pat->uvlist;
	vec3_t		*normals=pat->nlist;
	vec4_t		rgba;
	vertex_draw_proc_t vp=vglGetVertexDrawProc(draw_bits);
	uls=0;
	if(!pat)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"patDrawWireframe(NULL): incorect parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	if(!pat->extinfo)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"patDrawWireframe(): patch without "
			"all geometry information.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
    for(j=0;j<=pat->vsplit;j++)
    {
		ul=uls;
		glBegin(GL_LINE_STRIP);
		for(i=0;i<=pat->hsplit;i++)
        {
        	rgbaFromByte(rgba,colors[ul]);
        	vp(verts[ul],rgba,normals[ul],texcoords[ul]);
          	ul++;
		}
      	glEnd();
      	uls+=pat->hsplit+1;
	}
	uls=0;
    for(j=0;j<=pat->hsplit;j++)
    {
    	ul=uls;
    	glBegin(GL_LINE_STRIP);
        for(i=0;i<=pat->vsplit;i++)
        {
        	rgbaFromByte(rgba,colors[ul]);
        	vp(verts[ul],rgba,normals[ul],texcoords[ul]);
			ul+=pat->hsplit+1;
        }
      	glEnd();
		uls++;
	}
}

void	V_GLOBAL
patDrawWireframe(tripatch_t *pat)
{
	patDrawWireframeEx(pat,-1);
}

/*</pre></html>*/
/*EOF*/
