#include "plugin.h"
// Triangle-stripdetection made simple. By Joost Bloemen, Copyright Vimana BV
// If you can improve the algorithm, be sure to let me know you can reach me
// at research@3dtop.com

struct BMF_SURFACE{
    USHORT p0, p1, p2;
};

struct Hit{
    USHORT hits;
    USHORT t[3];
    UCHAR mask[3];
    BMF_SURFACE p[3];
    BOOL Stripped;
}* HitList;

// INPUT:
//ilist is the list with indices of the original triangles, will be broken down to
//	  only the triangles that can 't be stripped, at the end.
//length is the number of triangles in ilist.
// OUTPUT:
//nr-strips is the number of strips we detected, they have a minimum of two triangles
//strips_length contains an array[nr_strips] of strip-lengths, strip1, strip2 strip3 etc...
//nr_indices is the total number of vertice-indices we have in the array "stripindex":
//stripindex is a long list of strip1-indices, strip2-indices, strip3-indices etc...
//return-value is the new number of seperate triangles in ilist.
USHORT DetectStrips(BMF_SURFACE * ilist, USHORT length,
		    USHORT * nr_strips, USHORT * strip_length,
		    USHORT * nr_indices, USHORT * stripindex)
{
    USHORT lastp0,lastp1,lastp2;
    USHORT n,nr,trynr;
    USHORT p0,p1,p2;
    USHORT i,k,next,p;
    UCHAR mask;
    BMF_SURFACE * temp, * tempEnd;
    struct Hit * tempHitList;
    // First part makes a hitlist per triangle, how many sides(hits) a triangle has shared,
    // the indexes of the hits, up to 3 masks that define wich points the triangles each share
    // and the points of each shared triangle.
    HitList=(struct Hit *)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,length*sizeof(struct Hit));
    tempHitList=HitList;
    for(n=0;n<length;n++,tempHitList++)
	{
	    p0=ilist[n].p0;
	    p1=ilist[n].p1;
	    p2=ilist[n].p2;
	    temp=ilist;
	    tempEnd=ilist+length;
	    while(temp<tempEnd)
		{USHORT p;
		// The following piece of code accounts for 99% of the time when converting large models
		nr=2;                               // count-down, we only want the ones with two points the same
		mask=0;
		if ((p=temp->p0)==p0){mask|=0x11;nr--;}
		else {if (p==p1){mask|=0x12;nr--;}
		else {if (p==p2){mask|=0x14;nr--;}}
		}
		if ((p=temp->p1)==p0){mask|=0x21;nr--;}
		else {if (p==p1){mask|=0x22;nr--;}
		else {if (p==p2){mask|=0x24;nr--;}}
		}
		if ((p=temp->p2)==p0){mask|=0x41;nr--;}
		else {if (p==p1){mask|=0x42;nr--;}
		else {if (p==p2){mask|=0x44;nr--;}}
		}

		// That's it
		if(!nr)																	//this doesn't happen very often
		    {USHORT nrs=tempHitList->hits;
		    //Rotate and save points so that p0 and p1 point to points that are the same.
		    // to check later, if we can make a strip of this
		    switch (mask>>4){
		    case 5:
			tempHitList->p[nrs].p1=temp->p0;
			tempHitList->p[nrs].p0=temp->p2;
			tempHitList->p[nrs].p2=temp->p1;
			break;

		    case 6:
			tempHitList->p[nrs].p0=temp->p1;
			tempHitList->p[nrs].p1=temp->p2;
			tempHitList->p[nrs].p2=temp->p0;
			break;

		    case 3:
			tempHitList->p[nrs].p0=temp->p0;
			tempHitList->p[nrs].p1=temp->p1;
			tempHitList->p[nrs].p2=temp->p2;
			break;
		    }
		    tempHitList->t[nrs]=temp-ilist;         // (temp-ilist) is actually the triangle-number 
		    tempHitList->mask[nrs]=mask;            // mask is for later
		    if(++tempHitList->hits==3)temp=tempEnd;		// break while-loop if we have 3 hits
		    }
		temp++;
		}
	}
    // Next:
    // Start with trying to make a strip of all triangles with 1 hit as a starting point,
    // then 2 , finally 3.
    // That's all.
    for(trynr=1;trynr<=3;trynr++)
	{
	    for(p=0;p<length;p++)
		{
		    if(HitList[p].hits==trynr&&!HitList[p].Stripped)
			{
			    n=p;                              		// n is first triangle of possible strip
			    i=0;                                  // i is triangle-counter of this possible strip
			    k=10;                                 // found a matching triangle
			    while(k>=10)                          // while found a triangle
				{for (k=0;k<trynr;k++)    					// try all possible triangles
				    {next=HitList[n].t[k];						// possible triangle
				    if(!HitList[next].Stripped)				// not included yet ?
					{if(!i)							// if testing with first triangle, it must be rotated so that
					    // points that are the same as next triangle are p1 and p2
					    {switch (HitList[n].mask[k]&0x0f){
					    case 6:
						lastp0=ilist[p].p0;         // lastp0-p2 is first triangle of strip
						lastp1=ilist[p].p1;         // is a local triangle that defines the last
						lastp2=ilist[p].p2;					// triangle added to the strip
						break;

					    case 3:
						lastp1=ilist[p].p0;
						lastp2=ilist[p].p1;
						lastp0=ilist[p].p2;
						break;

					    case 5:
						lastp0=ilist[p].p1;
						lastp1=ilist[p].p2;
						lastp2=ilist[p].p0;
						break;
					    }
					    stripindex[*nr_indices]=lastp0;				 //save
					    stripindex[(*nr_indices)+1]=lastp1;
					    stripindex[(*nr_indices)+2]=lastp2;
					    }
					if(i&1)                                  // odd or even, makes a difference, see OpenGL
					    {if (HitList[n].p[k].p0==lastp0&&HitList[n].p[k].p1==lastp2)    //new one fits ?
						{lastp0=HitList[n].p[k].p0;          // update last triangle used
						lastp1=HitList[n].p[k].p1;
						lastp2=HitList[n].p[k].p2;
						HitList[next].Stripped=TRUE;				 //this one is done
						stripindex[(*nr_indices)++]=lastp2;  //save, p2 defines this triangle
						n=next;                //use this one as next source
						k=10;									// break for-loop with k, found one
						i++;
						}	}
					else
					    {if (HitList[n].p[k].p0==lastp2&&HitList[n].p[k].p1==lastp1)    //new one fits ?
						{lastp0=HitList[n].p[k].p0;          // update last triangle used
						lastp1=HitList[n].p[k].p1;
						lastp2=HitList[n].p[k].p2;
						HitList[next].Stripped=TRUE;				 //this one is done
						if(!i++)
						    {
							(*nr_indices)+=3;								 //first triangle had 3 indices and keep it
							HitList[n].Stripped=TRUE;          //and is done also
						    }
						stripindex[(*nr_indices)++]=lastp2;  // save, p2 defines this triangle
						n=next;								//use this one as next source
						k=10;									// break for-loop with k, found one
						}	}	}	}	}
			    i++;														//actual number of triangles in strip is one more
			    if(i>1)strip_length[(*nr_strips)++]=i+2;		 //actual strip-length (in indices) is traingles +2
			}	}	}
    // Done.
    // Now keep all triangles that aren't stripped in original triangle-list and return
    // remaining number of triangles in original triangle-list.
    i=0;
    for(p=0;p<length;p++)
	{if(!HitList[p].Stripped)
	    {
		ilist[i++]=ilist[p];
	    }
	}
    GlobalFree(HitList);
    return(i);
}