#define property _property #include #undef property import "ecere" #if defined(__WIN32__) #define TESS_CALLBACK_FUNCTION_PROTOTYPE void (__stdcall*)(void) #else #define TESS_CALLBACK_FUNCTION_PROTOTYPE void * #define CALLBACK #endif void * eC_malloc(unsigned int size) { return new byte[size]; } void * eC_realloc(void * pointer, unsigned int size) { return renew pointer byte[size]; } void eC_free(void * pointer) { delete pointer; } Mutex tessMutex { }; static Array vertices { }; static GLUtesselator * butterburTesselator = null; static double ccw(Pointf a, Pointf b, Pointf c) { return (((double)b.x - (double)a.x) * ((double)c.y - (double)a.y) - ((double)c.x - (double)a.x) * ((double)b.y - (double)a.y)); } static bool selfIntersects(Array points) { int i, j; for(i = 0; i < points.count; i++) { Pointf * ap = &points[i], * aq = &points[(i < points.count-1) ? i + 1 : 0]; for(j = 0; j < points.count; j++) { Pointf * bp = &points[j], * bq = &points[(j < points.count-1) ? j + 1 : 0]; if(ap != bp && ap != bq && aq != bp && aq != bq) { double a = ccw(ap, aq, bp); double b = ccw(ap, aq, bq); double c = ccw(bp, bq, ap); double d = ccw(bp, bq, aq); #define roundoffDouble 0.00000000001 if(Abs(a) < roundoffDouble) a = 0; if(Abs(b) < roundoffDouble) b = 0; if(Abs(c) < roundoffDouble) c = 0; if(Abs(d) < roundoffDouble) d = 0; if(Sgn(a) * Sgn(b) < 0 && Sgn(c) * Sgn(d) < 0) return true; } } } return false; } struct TessPrim { GLIMTKMode type; uint count; uint32 * indices; void OnFree() { delete indices; } }; struct TessPrimData { GLIMTKMode which; Array combineVertices; Array primitives; Array triIndices; Array tmpIndices; Array output; }; class CombineVertex : struct { Pointf position; int id; }; static void CALLBACK tessPrimBegin(GLenum which, TessPrimData tesselatorData) { tesselatorData.which = which == GL_TRIANGLES ? triangles : which == GL_TRIANGLE_STRIP ? triangleStrip : triangleFan; if(which != GL_TRIANGLES) { tesselatorData.tmpIndices.size = 0; if(tesselatorData.primitives.size + 1 > tesselatorData.primitives.minAllocSize) tesselatorData.primitives.minAllocSize += tesselatorData.primitives.minAllocSize / 2; } } static void CALLBACK tessPrimVertex(Pointf * point, TessPrimData tesselatorData) { uint index = 0; uint * indices; if(tesselatorData.which == triangles) { if(tesselatorData.triIndices.size + 1 > tesselatorData.triIndices.minAllocSize) tesselatorData.triIndices.minAllocSize += tesselatorData.triIndices.minAllocSize / 2; indices = tesselatorData.triIndices.array; } else { if(tesselatorData.tmpIndices.size + 1 > tesselatorData.tmpIndices.minAllocSize) tesselatorData.tmpIndices.minAllocSize += tesselatorData.tmpIndices.minAllocSize / 2; indices = tesselatorData.tmpIndices.array; } if(point >= &tesselatorData.output[0] && point < &tesselatorData.output[tesselatorData.output.count]) index = point - &tesselatorData.output[0]; else { CombineVertex vertex = (CombineVertex)point; index = tesselatorData.output.count + vertex.id; } if(tesselatorData.which == triangles) { uint ix = tesselatorData.triIndices.size; tesselatorData.triIndices.size++; indices[ix] = index; } else { uint ix = tesselatorData.tmpIndices.size; tesselatorData.tmpIndices.size++; indices[ix] = index; } } static void CALLBACK tessPrimEnd(TessPrimData tesselatorData) { if(tesselatorData.which != triangles) { TessPrim * prim; uint ix = tesselatorData.primitives.size; tesselatorData.primitives.size++; prim = &tesselatorData.primitives[ix]; prim->type = tesselatorData.which; prim->count = tesselatorData.tmpIndices.count; prim->indices = new uint32[prim->count]; memcpy(prim->indices, &tesselatorData.tmpIndices[0], sizeof(uint) * prim->count); } } static void CALLBACK tessPrimCombine(GLdouble coords[3], Pointf *d[4], GLfloat w[4], Pointf **dataOut, TessPrimData tesselatorData) { CombineVertex vertex { position = { (float)coords[0], (float)coords[1] }; id = tesselatorData.combineVertices.count; }; *dataOut = &vertex.position; tesselatorData.combineVertices.Add(vertex); } void tesselatePolygon(Array area, Array * outputPtr, Array * primitivesPtr) { int i, d; Pointf * destPoints; int totalCount = area.count; int start = 0; Array output { }; Array primitives { minAllocSize = 16 }; Array combineVertices { }; static TessPrimData tesselatorData; tessMutex.Wait(); output.size = totalCount; destPoints = &output[0]; tesselatorData.primitives = primitives; tesselatorData.combineVertices = combineVertices; tesselatorData.tmpIndices = { minAllocSize = 16 }; tesselatorData.triIndices = { minAllocSize = 16 }; tesselatorData.output = output; vertices.size = totalCount; { int n = area.count; d = 0; for(i = 0; i < n; i++, d++) { destPoints[d] = area[i]; vertices[d] = { area[i].x, area[i].y }; } } if(!butterburTesselator) { butterburTesselator = gluNewTess(); gluTessCallback(butterburTesselator, GLU_TESS_BEGIN_DATA, (TESS_CALLBACK_FUNCTION_PROTOTYPE)(void *)tessPrimBegin); gluTessCallback(butterburTesselator, GLU_TESS_END_DATA, (TESS_CALLBACK_FUNCTION_PROTOTYPE)(void *)tessPrimEnd); gluTessCallback(butterburTesselator, GLU_TESS_VERTEX_DATA, (TESS_CALLBACK_FUNCTION_PROTOTYPE)(void *)tessPrimVertex); gluTessCallback(butterburTesselator, GLU_TESS_COMBINE_DATA, (TESS_CALLBACK_FUNCTION_PROTOTYPE)(void *)tessPrimCombine); gluTessProperty(butterburTesselator, GLU_TESS_BOUNDARY_ONLY, GL_FALSE); gluTessProperty(butterburTesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE); } gluTessBeginPolygon(butterburTesselator, &tesselatorData); start = 0; { int n = area.count; if(n && !selfIntersects(area)) { gluTessBeginContour(butterburTesselator); d = start; for(i = 0; i < n; i++, d++) gluTessVertex(butterburTesselator, (double *)&vertices[d], &destPoints[d]); gluTessEndContour(butterburTesselator); } start += n; } gluTessEndPolygon(butterburTesselator); if(combineVertices.count) { int c; int count = output.count; output.size = count + combineVertices.count; for(c = 0; c < combineVertices.count; c++) { CombineVertex vertex = combineVertices[c]; output[count++] = vertex.position; delete vertex; } } delete combineVertices; if(tesselatorData.triIndices && tesselatorData.triIndices.count) { uint ix = primitives.size; TessPrim * prim; primitives.size++; prim = &primitives[ix]; prim->type = triangles; prim->count = tesselatorData.triIndices.count; prim->indices = new uint[prim->count]; memcpy(prim->indices, &tesselatorData.triIndices[0], sizeof(uint) * prim->count); } primitives.minAllocSize = 0; delete tesselatorData.triIndices; delete tesselatorData.tmpIndices; *outputPtr = output; *primitivesPtr = primitives; tessMutex.Release(); }