#include <../source/TAL/objbase.h>

/*
 *	TODO: convert to object !
typedef struct vbuffer_s
{
	libv_object_t base;
	...
}	vbuffer_t, *vbuffer_p;

V_LOCAL char_p vb_object = "vertex buffer";

#define VB_MAGIC 0x56425546 // VBUF
#define GET_VB(handle,caller) \
		CHECK_AND_GET_OBJECT(vbuffer_t,handle,caller,vb_object,VB_MAGIC)
*/

trilist_p	V_GLOBAL
tlistCreate(void)
{
	trilist_p list;
	list=(trilist_t*)memAlloc(sizeof(trilist_t));
	memZero(list,sizeof(trilist_t));
	stackInit(&list->vlist,sizeof(tlist_face_t));
	stackInit(&list->mstack,sizeof(matrix3x4_t));
	stackInit(&list->edge_info,sizeof(tlist_edge_t));
	vpsInit(&list->order);
	mat3x4Unit(list->xform);
	/* initially, mark all edges as boundary */
	list->edge_flag=1;
	list->sorted=false;
	return list;
}

void	V_GLOBAL
tlistClear(trilist_t *list)
{
	if(!list)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"tlistClear(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	stackResize(&list->vlist,0);
	list->num_vert=0; /* reject vertices in partially added triangle */
	/*
	 *	We would not clear edge_info here; regenerating it requires
	 *	lot of CPU time. Instead, we clear it in tlistGenerageEdgeInfo().
	 */
}

void	V_GLOBAL
tlistDestroy(trilist_t *list)
{
	if(!list)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"tlistDestroy(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	stackClear(&list->vlist);
	stackClear(&list->mstack);
	stackClear(&list->edge_info);
	vpsClear(&list->order);
	memFree(list);
}

void	V_GLOBAL
tlistPushMatrix(trilist_t *list)
{
	V_ASSERT(list);
	stackPush(&list->mstack,list->xform);
#ifdef	CHECKED_BUILD
	{
		u32_t depth=stackCount(&list->mstack);
		if(depth > 1024)
			conlPrintf(LOG_WARNING,
				"tlistPushMatrix(): stack depth too large (%d)\n",depth);
	}
#endif	/* CHECKED_BUILD */
}

/*
 *	Transformation functions
 */
void	V_GLOBAL
tlistPopMatrix(trilist_t *list)
{
	V_ASSERT(list);
	if(stackCount(&list->mstack)>0)
		stackPop(&list->mstack,list->xform);
#ifdef	CHECKED_BUILD
	else
		conlPuts(LOG_ERROR,"tlistPopMatrix(): underflow.\n");
#endif	/* CHECKED_BUILD */
	list->sorted=false;
}

void	V_GLOBAL
tlistLoadMatrix(trilist_t *list,matrix3x4_t tos)
{
	V_ASSERT(list && tos);
	mat3x4Copy(list->xform,tos);
	list->sorted=false;
}

void	V_GLOBAL
tlistLoadIdentity(trilist_t *list)
{
	V_ASSERT(list);
	mat3x4Unit(list->xform);
	list->sorted=false;
}

void	V_GLOBAL
tlistMultMatrix(trilist_t *list,matrix3x4_t mult)
{
	matrix3x4_t res;
	V_ASSERT(list && mult);
	mat3x4MatMul(res,list->xform,mult);
	mat3x4Copy(list->xform,res);
	list->sorted=false;
}

void	V_GLOBAL
tlistRotatev(trilist_t *list,vec_t radians,vec3_t axis)
{
	matrix3x4_t rot,res;
	vec3_t au;
	V_ASSERT(list && axis);
	vec3Unit(au,axis);
	mat3x4Rotate(rot,au,radians);
	mat3x4MatMul(res,list->xform,rot);
	mat3x4Copy(list->xform,res);
	list->sorted=false;
}

void	V_GLOBAL
tlistRotate(trilist_t *list,vec_t radians,vec_t ax,vec_t ay,vec_t az)
{
	vec3_t axis;
	vec3Set(axis,ax,ay,az);
	tlistRotatev(list,radians,axis);
}

void	V_GLOBAL
tlistTranslatev(trilist_t *list,vec3_t v)
{
	matrix3x4_t t,res;
	V_ASSERT(list && v);
	mat3x4Translate(t,v);
	mat3x4MatMul(res,list->xform,t);
	mat3x4Copy(list->xform,res);
	list->sorted=false;
}

void	V_GLOBAL
tlistTranslate(trilist_t *list,vec_t x,vec_t y,vec_t z)
{
	vec3_t v;
	vec3Set(v,x,y,z);
	tlistTranslatev(list,v);
}

void	V_GLOBAL
tlistScalev(trilist_t *list,vec3_t scales)
{
	matrix3x4_t s,res;
	V_ASSERT(list && scales);
	mat3x4Scale(s,scales);
	mat3x4MatMul(res,list->xform,s);
	mat3x4Copy(list->xform,res);
	list->sorted=false;
}
	
void	V_GLOBAL
tlistScale(trilist_t *list,vec_t sx,vec_t sy,vec_t sz)
{
	vec3_t scale;
	vec3Set(scale,sx,sy,sz);
	tlistScalev(list,scale);
}

void	V_GLOBAL
tlistVertex3(trilist_t *list,vec_t x,vec_t y,vec_t z)
{
	vec3_t v;
	vec3Set(v,x,y,z);
	tlistVertex3v(list,v);
}

void	V_GLOBAL
tlistVertex3v(trilist_t *list,vec3_t vert)
{
	tlist_face_t *tri;
	vec3_t transformed;
	V_ASSERT(list && vert);
	tri=&list->tri;
	mat3x4VecMul(transformed,list->xform,vert);
	vec3Copy(tri->verts[list->num_vert].vert,transformed);
	vec4Copy(list->tri.verts[list->num_vert].color,list->color);
	vec3Copy(list->tri.verts[list->num_vert].normal,list->normal);
	vec2Copy(list->tri.verts[list->num_vert].texcoord,list->texcoord);
	list->tri.verts[list->num_vert].edge_flag=list->edge_flag;
	list->num_vert++;
	if(list->num_vert==3)
	{
		vec3_t a,b;
		vec3Sub(a,tri->verts[1].vert,tri->verts[0].vert);
		vec3Sub(b,tri->verts[2].vert,tri->verts[0].vert);
		vec3Cross(tri->normal,a,b);
		vec3Unit(tri->normal,tri->normal);
		vec3Add(tri->center,tri->verts[0].vert,tri->verts[1].vert);
		vec3Add(tri->center,tri->center,tri->verts[2].vert);
		vec3Scale(tri->center,tri->center,0.333333333f);
		stackPush(&list->vlist,tri);
		list->sorted=false;
		list->num_vert=0;
	}
}

void	V_GLOBAL
tlistColor4(trilist_t *list,vec_t r,vec_t g,vec_t b,vec_t a)
{
	V_ASSERT(list);
	vec4Set(list->color,r,g,b,a);
}

void	V_GLOBAL
tlistColor4v(trilist_t *list,vec4_t rgba)
{
	V_ASSERT(list && rgba);
	vec4Copy(list->color,rgba);
}

void	V_GLOBAL
tlistNormal3(trilist_t *list,vec_t x,vec_t y,vec_t z)
{
	V_ASSERT(list);
	vec3Set(list->normal,x,y,z);
}

void	V_GLOBAL
tlistNormal3v(trilist_t *list,vec3_t normal)
{
	V_ASSERT(list && normal);
	vec3Copy(list->normal,normal);
}

void	V_GLOBAL
tlistTexCoord2(trilist_t *list,vec_t u,vec_t v)
{
	V_ASSERT(list);
	vec2Set(list->texcoord,u,v);
}

void	V_GLOBAL
tlistTexCoord2v(trilist_t *list,vec2_t texcoord)
{
	V_ASSERT(list && texcoord);
	vec2Copy(list->texcoord,texcoord);
}

void	V_GLOBAL
tlistEdgeFlag(trilist_t *list,u32_t flag)
{
	V_ASSERT(list);
	list->edge_flag=flag;
}

/*
 * Callback for vpsSort()
 */
V_LOCAL	int	V_CALL
compare_faces(void *viewpoint,void *f1,void *f2)
{
	vec_t d1,d2;
	vec3_t v1,v2;
	vec3Sub(v1,((tlist_face_t*)f1)->center,(vec_t*)viewpoint);
	vec3Sub(v2,((tlist_face_t*)f2)->center,(vec_t*)viewpoint);
	d1=vec3Dot(v1,v1);
	d2=vec3Dot(v2,v2);
	if(IsZero(d1-d2))
		return 0;
	else
		return d1 > d2 ? 1 : -1;
}

V_LOCAL	void
tlistSort(trilist_t *list,vec3_t viewpoint)
{
	void **faces;
	u32_t i,count=stackCount(&list->vlist);
	u8_t *base=(u8_t*)stackGetStorage(&list->vlist);
	if(count<2)	/* nothing to sort */
		return;
	/* Regenerate 'order'; allocate memory, if needed: */
	vpsSet(&list->order,count-1,NULL);
	/* Get stack storage and fill it with pointers */
	faces=vpsGetStorage(&list->order);
	for(i=0;i<count;i++,base+=sizeof(tlist_face_t))
		faces[i]=base;
	/* Invoke our thread-safe sorting procedure, passing viewpoint
     * as parameter */
	vpsSort(&list->order,compare_faces,viewpoint);
	vec3Copy(list->last_viewpoint,viewpoint);
	list->sorted=true;
}

void	V_GLOBAL
tlistDrawEx(trilist_t *list,vec3_t viewpoint,vboolean front_to_back,
	int draw_bits)
{
	u32_t i,num_tri;
	tlist_face_t **tris;
	tri_draw_proc_t draw_proc;
	if(!list || !viewpoint)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"tlistDraw(): incorrect parameter(s).\n");
#endif	/* CHECKED_BUILD */
		timeDelay(10);
		return;
	}
	if(!list->sorted || !vec3IsEqual(list->last_viewpoint,viewpoint))
		tlistSort(list,viewpoint);
	tris=(tlist_face_t**)vpsGetStorage(&list->order);
	num_tri=(int)vpsCount(&list->order);
	draw_proc=vglGetTriangleDrawProc(draw_bits);
	glBegin(GL_TRIANGLES);
	if(front_to_back)
	{
		for(i=0;i<num_tri;i++)
			draw_proc(tris[i]);
	}
	else
	{
		if(num_tri)
			for(i=num_tri-1;;i--)
			{
				draw_proc(tris[i]);
				if(!i)
					break;
			}
	}
	glEnd();
}

void	V_GLOBAL
tlistDraw(trilist_t *list,vec3_t viewpoint,vboolean front_to_back)
{
	tlistDrawEx(list,viewpoint,front_to_back,-1);
}

/*
 * This must be extremely fast because it will be called every frame.
 */
void	V_GLOBAL
tlistConcat(trilist_t *dst,trilist_t *src,
	vertex_proc_t vertex_proc,void *param)
{
	u32_t i,j,num_tris,num_edges,src_edge_base,src_tris_base;
	u32_t new_edges[3];
	tlist_face_t *tri, *last;
	tlist_vertex_t vert;
	tlist_vertex_t *p_vert;
	tlist_edge_t *edges;
	if(!dst || !src)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"tlistConcat(%X,%X): incorrect parameter(s).\n",dst,src);
#endif	/* CHECKED_BUILD */
		return;
	}
	/*
		this condition will be always 'false'
	if(!vertex_proc && IsUnitTransform(dst->tos))
		stackConcat(&dst->vlist,&src->vlist);
	else
		...
	*/
	dst->num_vert=0;	/* we start adding triangles */
	num_edges=stackCount(&src->edge_info);
	num_tris=stackCount(&src->vlist);
	src_edge_base=stackCount(&dst->edge_info);
	src_tris_base=stackCount(&dst->vlist);
	/* concat edge_info's (if present in both lists) */
	if(num_edges)
	{
		stackConcat(&dst->edge_info,&src->edge_info);
		edges=(tlist_edge_t*)stackGetStorage(&dst->edge_info)+src_edge_base;
		for(i=0;i<num_edges;i++)
		{
			edges->triangle+=src_tris_base;
			edges++;
		}
	}
	/* concat triangle lists */
	tri=(tlist_face_t*)stackGetStorage(&src->vlist);
	if(vertex_proc)
	{
    	for(i=0;i<num_tris;i++)
    	{
    		for(j=0;j<3;j++)
    		{
    			new_edges[j]=tri->edges[j]+src_edge_base;
    			memCopy(&vert,tri->verts+j,sizeof(tlist_vertex_t));
    			vertex_proc(vert.vert,vert.color,vert.normal,vert.texcoord,
    				&vert.edge_flag,param);
        		vec4Copy(dst->color,vert.color);
        		vec3Copy(dst->normal,vert.normal);
        		vec2Copy(dst->texcoord,vert.texcoord);
        		dst->edge_flag=vert.edge_flag;
    			tlistVertex3v(dst,vert.vert);
    		}
    		tri++;
    		/*	we must do it inside loop because "dst"'s face storage can be
    		 *	realloc()'ed any time */
    		last=(tlist_face_t*)stackGetStorage(&dst->vlist)+src_tris_base;
    		last->edges[0]=new_edges[0];
    		last->edges[1]=new_edges[1];
    		last->edges[2]=new_edges[2];
    		src_tris_base++;
    	}
	}
	else
	{
    	for(i=0;i<num_tris;i++)
    	{
    		for(j=0;j<3;j++)
    		{
    			new_edges[j]=tri->edges[j]+src_edge_base;
    			p_vert=tri->verts+j;
        		vec4Copy(dst->color,p_vert->color);
        		vec3Copy(dst->normal,p_vert->normal);
        		vec2Copy(dst->texcoord,p_vert->texcoord);
        		dst->edge_flag=p_vert->edge_flag;
    			tlistVertex3v(dst,p_vert->vert);
    		}
    		tri++;
    		last=(tlist_face_t*)stackGetStorage(&dst->vlist)+src_tris_base;
    		last->edges[0]=new_edges[0];
    		last->edges[1]=new_edges[1];
    		last->edges[2]=new_edges[2];
    		src_tris_base++;
    	}
	}
	dst->sorted=false;
}

void	V_GLOBAL
tlistProcess(trilist_t *list,vertex_proc_t vertex_proc,void *param)
{
	u32_t i,num_tris;
	tlist_face_t *tri;
	tlist_vertex_t *pv;
	if(!list)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"tlistConcat(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	num_tris=stackCount(&list->vlist);
	tri=(tlist_face_t*)stackGetStorage(&list->vlist);
   	for(i=0;i<num_tris;i++)
   	{
		pv=tri->verts;
   		vertex_proc(pv->vert,pv->color,pv->normal,pv->texcoord,
    		&pv->edge_flag,param);
    	tri++;
    }
	list->sorted=false;
}


void	V_GLOBAL
tlistAddMeshObject(trilist_t *list,scene_t *mesh,u32_t obj_num,
	vertex_proc_t vertex_proc,void *param)
{
	mesh_face_t *face, *f_end;
	mesh_vertex_t *vert;
	vec3_t v;
	vec3_t n;
	vec4_t c;
	vec2_t tx;
	u32_t ef;
	int i;
	if(!list || !mesh || obj_num>=mesh->object_count)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"tlistAddMeshObject(%X,%X,%d,%X,%X): incorrect parameter(s).\n",
			list,mesh,obj_num,vertex_proc,param);
#endif	/* CHECKED_BUILD */
		return;
	}
	face=mesh->faces + mesh->objects[obj_num].startface;
	f_end=face + mesh->objects[obj_num].num_faces;
	if(vertex_proc)
	{
    	for(;face<f_end;face++)
    	{
    		for(i=0;i<3;i++)
    		{
    			vert=(*face)+i;
    			vec3Copy(v,mesh->vertices[vert->vertex]);
    			vec3Copy(n,mesh->normals[vert->normal]);
    			rgbaFromByte(c,mesh->colors[vert->color]);
    			vec2Copy(tx,mesh->texcoords[vert->texcoord]);
    			ef=1;
    			vertex_proc(v,c,n,tx,&ef,param);
    			vec4Copy(list->color,c);
    			vec3Copy(list->normal,n);
    			vec2Copy(list->texcoord,tx);
    			list->edge_flag=ef;
    			tlistVertex3v(list,v);
    		}
    	}
    }
    else
    {
		list->edge_flag=1;
    	for(;face<f_end;face++)
    	{
    		for(i=0;i<3;i++)
    		{
    			vert=(*face)+i;
    			rgbaFromByte(list->color,mesh->colors[vert->color]);
    			vec3Copy(list->normal,mesh->normals[vert->normal]);
    			vec2Copy(list->texcoord,mesh->texcoords[vert->texcoord]);
    			tlistVertex3v(list,mesh->vertices[vert->vertex]);
    		}
    	}
    }
}

/*
 * Returns index of edge in 'edge_info'
 */
u32_t	V_LOCAL
tlistFindEdge(trilist_t *list,tlist_edge_t *edge)
{
	u32_t			i,len;
	tlist_edge_t	*edges;
	vec_p			a1,a2,b1,b2;
	tlist_face_t		*tris;
	edges=(tlist_edge_t*)stackGetStorage(&list->edge_info);
	len=stackCount(&list->edge_info);
	tris=(tlist_face_t*)stackGetStorage(&list->vlist);
	for(i=0;i<len;i++)
	{
		if(edges[i].visible != edge->visible)
			continue;
		a1=tris[edges[i].triangle].verts[edges[i].points[0]].vert;
		a2=tris[edges[i].triangle].verts[edges[i].points[1]].vert;
		b1=tris[edge->triangle].verts[edge->points[0]].vert;
		b2=tris[edge->triangle].verts[edge->points[1]].vert;
		if(vec3IsEqual(a1,b1) && vec3IsEqual(a2,b2))
			break;
		if(vec3IsEqual(a1,b2) && vec3IsEqual(a2,b1))
			break;
	}
	if(i==len)
		stackPush(&list->edge_info,edge);
	return i;
}

/*
 * Updates list->edge_info and all [tris]->edge[0,1,2].
 * Takes VERY long time to execute.
 */
void	V_GLOBAL
tlistGenerateEdgeInfo(trilist_t *list)
{
	u32_t ed,tri,num_tri;
	tlist_face_t *tris;
	tlist_edge_t edge;
	if(!list)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"tlistGenerateEdgeInfo(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	/* calls to realloc() consume less time than iterating though all
	 * vertices of all triangles, so we can memFree() all memory */
	stackClear(&list->edge_info);
	tris=(tlist_face_t*)stackGetStorage(&list->vlist);
	num_tri=stackCount(&list->vlist);
	for(tri=0;tri<num_tri;tri++)
	{
		for(ed=0;ed<3;ed++)
		{
			edge.triangle=tri;
			edge.points[0]=ed;
			edge.points[1]=(ed+1) % 3;
			edge.visible=tris[tri].verts[ed].edge_flag;
			tris[tri].edges[ed]=tlistFindEdge(list,&edge);
		}
	}
}

/*
 * Computes bounding box of all triangles in list.
 * Note: instead of calling this proc it's much faster to calculate
 * bounding box for all objects which will be added to list. This
 * proc isn't too fast.
 */
void	V_GLOBAL
tlistGenerateBBox(trilist_t *list)
{
	tlist_face_t *tri,*end;
	vec_t *mins,*maxs;
	if(!list)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,
			"tlistGenerateBBox(NULL): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	mins=list->mins;
	maxs=list->maxs;
	bboxClear(mins,maxs);
	tri=(tlist_face_t*)stackGetStorage(&list->vlist);
	end=tri+stackCount(&list->vlist);
	while(tri<end)
	{
		bboxAddPoint(mins,maxs,tri->verts[0].vert);
		bboxAddPoint(mins,maxs,tri->verts[1].vert);
		bboxAddPoint(mins,maxs,tri->verts[2].vert);
		tri++;
	}
}

void	V_GLOBAL
tlistDrawWireframeEx(trilist_t *list,int draw_bits)
{
	u32_t e, num_edges;
	tlist_edge_t *edges, *edge;
	tlist_face_t *tris, *tri;
	tlist_vertex_t *v1, *v2;
	vertex_draw_proc_t vp;
	if(!list)
	{
#ifdef	CHECKED_BUILD
		conlPuts(LOG_ERROR,"tlistDrawWireframeEx(): incorrect parameter.\n");
#endif	/* CHECKED_BUILD */
		return;
	}
	edges=(tlist_edge_t*)stackGetStorage(&list->edge_info);
	tris=(tlist_face_t*)stackGetStorage(&list->vlist);
	num_edges=stackCount(&list->edge_info);
	vp=vglGetVertexDrawProc(draw_bits);
	glBegin(GL_LINES);
	for(e=0;e<num_edges;e++)
	{
		if(!edges[e].visible)
			continue;
		edge=edges+e;
		tri=tris+edge->triangle;
		v1=&(tri->verts[edge->points[0]]);
		v2=&(tri->verts[edge->points[1]]);
		vp(v1->vert,v1->color,v1->normal,v1->texcoord);
		vp(v2->vert,v2->color,v2->normal,v2->texcoord);
	}
	glEnd();
}

void	V_GLOBAL
tlistDrawWireframe(trilist_t *list)
{
	tlistDrawWireframeEx(list,-1);
}

/*
 * Currently this proc used to determine which edges of patch are
 * non-boundary.
 * TODO: generate boundary info in patch itself.
 */
u32_t V_LOCAL
AxialVectorXOY(vec3_t v1,vec3_t v2)
{
	vec3_t v;
	vec3Sub(v,v2,v1);
	return v_abs(v[0]) < 1e-3 || v_abs(v[1]) < 1e-3;
}

void	V_GLOBAL
tlistAddPatch(trilist_t *list,tripatch_t *patch,vertex_proc_t vertex_proc,void *param)
{
	int i,j,vert,next;
	vec3_t v;
	vec3_t n;
	vec4_t c;
	vec2_t tx;
	u32_t ef;
	if(!list || !patch)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"tlistAddPatch(%X,%X,%X,%X): incorrect parameter(s).\n",
			list,patch,vertex_proc,param);
#endif	/* CHECKED_BUILD */
		return;
	}
	if(!patch->extinfo)
	{
#ifdef	CHECKED_BUILD
		conlPrintf(LOG_ERROR,
			"tlistAddPatch(%X,%X,%X,%X): patch without geometry info.\n",
			list,patch,vertex_proc,param);
#endif	/* CHECKED_BUILD */
		return;
	}
	if(vertex_proc)
	{
    	for(i=0;i<patch->numtri;i++)
    	{
    		for(j=0;j<3;j++)
    		{
    			vert=patch->tlist[i][j];
    			next=patch->tlist[i][(j+1) % 3];
    			vec3Copy(v,patch->vlist[vert]);
    			vec3Copy(n,patch->nlist[vert]);
    			rgbaFromByte(c,patch->clist[vert]);
    			vec2Copy(tx,patch->uvlist[vert]);
    			ef=AxialVectorXOY(patch->vlist[vert],patch->vlist[next]);
    			vertex_proc(v,c,n,tx,&ef,param);
    			vec4Copy(list->color,c);
    			vec3Copy(list->normal,n);
    			vec2Copy(list->texcoord,tx);
    			list->edge_flag=ef;
    			tlistVertex3v(list,v);
    		}
    	}
    }
    else
    {
    	for(i=0;i<patch->numtri;i++)
    	{
    		for(j=0;j<3;j++)
    		{
    			vert=patch->tlist[i][j];
    			next=patch->tlist[i][(j+1) % 3];
    			rgbaFromByte(list->color,patch->clist[vert]);
    			vec3Copy(list->normal,patch->nlist[vert]);
    			vec2Copy(list->texcoord,patch->uvlist[vert]);
    			list->edge_flag=AxialVectorXOY(patch->vlist[vert],
    				patch->vlist[next]);
    			tlistVertex3v(list,patch->vlist[vert]);
    		}
    	}
    }
}
