/****************************************************************************** * Poly_Pts.c - polygonal data point and other filtering tools. * ******************************************************************************* * (C) Gershon Elber, Technion, Israel Institute of Technology * ******************************************************************************* * Written by Gershon Elber, Feb 1997. * ******************************************************************************/ #include #include #include "irit_sm.h" #include "iritprsr.h" #include "allocate.h" #include "geom_loc.h" #include "bool_lib.h" #define VERTEX_COPY(VDest, VSrc) { PT_COPY(VDest -> Coord, VSrc -> Coord); \ PT_COPY(VDest -> Normal, VSrc -> Normal); \ VDest -> Tags = VSrc -> Tags; \ VDest -> Attr = \ IP_ATTR_COPY_ATTRS(VSrc -> Attr); } #define COPY_RGB_ATTR(VDest, VSrc) { int RTmp, GTmp, BTmp; \ if (AttrGetRGBColor(VSrc -> Attr, &RTmp, >mp, &BTmp)) \ AttrSetRGBColor(&VDest -> Attr, RTmp, GTmp, BTmp); } #define COPY_UV_ATTR(VDest, VSrc) { float *UV; \ if ((UV = AttrGetUVAttrib(VSrc -> Attr, "uvvals")) != NULL) \ AttrSetUVAttrib(&VDest -> Attr, "uvvals", UV[0], UV[1]); } #define TEST_CLOSEST_DIST(V1, V2, IsStart1, IsStart2) { \ RealType DstSqr; \ if ((DstSqr = PT_PT_DIST_SQR(V1 -> Coord, \ V2 -> Coord)) < MinSqr) { \ MinSqr = DstSqr; \ *Start1 = IsStart1; \ *Start2 = IsStart2; \ } \ } typedef struct GMPlPlDistStruct { IPPolygonStruct *Pl; int Idx, MinIdx, MinStart1, MinStart2; RealType MinDistSqr; } GMPlPlDistStruct; static RealType GetPlPlDist(IPPolygonStruct *Pl1, IPPolygonStruct *Pl2, int *Start1, int *Start2); #if defined(ultrix) && defined(mips) static int ComparePlPlDist(VoidPtr PlPlDist1, VoidPtr PlPlDist2); #else static int ComparePlPlDist(const VoidPtr PReal1, const VoidPtr PReal2); #endif /* ultrix && mips (no const support) */ static int CmpTwoVertices(IPVertexStruct *V1, IPVertexStruct *V2, RealType Eps); static void UpdateAdjPolys(IPVertexStruct *V1, IPPolygonStruct *Pl1, IPVertexStruct *V2, IPPolygonStruct *Pl2); static void InsertVertexToSplitList(IPVertexStruct *V, RealType *Pos, RealType t); static IPVertexStruct *GetAdjVertex(IPVertexStruct *V, IPPolygonStruct *Pl, IPPolygonStruct *PAdj); /***************************************************************************** * DESCRIPTION: M * Creates a new polygonal objects out of given one, that contains only M * polygons of upto n vertices. None convex polygons are split to convex one M * so the result will contain convex data only. M * * * PARAMETERS: M * PolyObj: Polygonal object to split into up to n-gons. M * n: Maximal number of vertices. M * * * RETURN VALUE: M * IPObjectStruct *: A polygonal object containing polygons with upto n M * vertices, representing the same model as PolyObj. M * * * SEE ALSO: M * ConvexPolyObjectN, GMConvertPolysToTriangles M * * * KEYWORDS: M * GMConvertPolysToNGons M *****************************************************************************/ IPObjectStruct *GMConvertPolysToNGons(IPObjectStruct *PolyObj, int n) { IPPolygonStruct *Pl; int IsCirc = IPSetPolyListCirc(FALSE); IPSetPolyListCirc(IsCirc); /* Restore state, now that we know it. */ n = MAX(n, 3); /* No less than a triangle! */ PolyObj = GMConvexPolyObjectN(PolyObj); for (Pl = PolyObj -> U.Pl; Pl != NULL; Pl = Pl -> Pnext) { IPVertexStruct *V, *VHead = Pl -> PVertex; int j, m, Len = IPVrtxListLen(VHead); for (j = 3, V = VHead; j < Len; j++, V = V -> Pnext) { if (!GMCoplanar4Pts(V -> Coord, V -> Pnext -> Coord, V -> Pnext -> Pnext -> Coord, V -> Pnext -> Pnext -> Pnext -> Coord)) break; } if (j < Len) /* Non planar data - split a triangle out of it. */ m = 3; else m = n; if (Len > m) { int i; IPVertexStruct *VRest, *VPrev, *VLast = IPGetLastVrtx(VHead); IPPolygonStruct *PlNew; /* Find the splitting point. */ for (i = 1, VRest = VHead, VPrev = NULL; i < m; i++, VPrev = VRest, VRest = VRest -> Pnext); /* Isolate the first polygon out of the original polygon. */ VPrev -> Pnext = IPAllocVertex2(IsCirc ? VHead : NULL); VERTEX_COPY(VPrev -> Pnext, VRest); IP_SET_INTERNAL_VRTX(VPrev -> Pnext); /* Put the rest in a new polygon to be treated soon. */ PlNew = IPAllocPolygon(0, VRest, Pl -> Pnext); PLANE_COPY(PlNew -> Plane, Pl -> Plane); IP_SET_PLANE_POLY(PlNew); Pl -> Pnext = PlNew; PlNew -> Attr = IP_ATTR_COPY_ATTRS(Pl -> Attr); VLast -> Pnext = IPAllocVertex2(IsCirc ? VRest : NULL); VERTEX_COPY(VLast -> Pnext, VHead); IP_SET_INTERNAL_VRTX(VLast -> Pnext); } } return PolyObj; } /***************************************************************************** * DESCRIPTION: M * Creates a new polygonal objects out of given one, that contains only M * triangles. None convex polygons are split to convex one which, in turn, M * converted to triangles. Collinear points are purged away. M * * * PARAMETERS: M * PolyObj: Polygonal object to split into triangles. M * * * RETURN VALUE: M * IPObjectStruct *: A polygonal object containing only triangles M * representing the same model as PolyObj. M * * * SEE ALSO: M * ConvexPolyObjectN, GMConvertPolysToNGons, GMLimitTrianglesEdgeLen M * GMConvertPolysToTriangles2 M * * * KEYWORDS: M * GMConvertPolysToTriangles M *****************************************************************************/ IPObjectStruct *GMConvertPolysToTriangles(IPObjectStruct *PolyObj) { IPPolygonStruct *Pl; int IsCirc = IPSetPolyListCirc(FALSE); IPSetPolyListCirc(IsCirc); /* Restore state, now that we know it. */ PolyObj = GMConvexPolyObjectN(PolyObj); for (Pl = PolyObj -> U.Pl; Pl != NULL; ) { IPPolygonStruct *PlNext = Pl -> Pnext; IPVertexStruct *VHead = Pl -> PVertex; int Len = IPVrtxListLen(VHead), FreeVHead = FALSE; if (Len > 3) { /* Split into several triangles. */ int VLastTags; IPVertexStruct *VLast, *VRest = VHead -> Pnext -> Pnext -> Pnext; IPPolygonStruct *PlNew = NULL; /* Isolate the first triangle out of the original polygon. */ VHead -> Pnext -> Pnext -> Pnext = IsCirc ? VHead : NULL; VLast = VHead -> Pnext -> Pnext; VLastTags = VLast -> Tags; IP_SET_INTERNAL_VRTX(VLast); if (GMCollinear3Pts(VHead -> Coord, VHead -> Pnext -> Coord, VHead -> Pnext -> Pnext -> Coord)) { FreeVHead = TRUE; Pl -> PVertex = NULL; } /* Construct triangles out of the rest of the points. */ while (VRest != NULL && VRest != VHead) { IPVertexStruct *VRestNext = VRest -> Pnext, *V3 = IPAllocVertex2(NULL), *V2 = IPAllocVertex2(V3), *V1 = IPAllocVertex2(V2); VERTEX_COPY(V1, VHead); VERTEX_COPY(V2, VLast); VERTEX_COPY(V3, VRest); if (IsCirc) V3 -> Pnext = V1; IP_SET_INTERNAL_VRTX(V1); V2 -> Tags = VLastTags; if (VRest -> Pnext != NULL && VRest -> Pnext != VHead) IP_SET_INTERNAL_VRTX(V3); else V3 -> Tags = VRest -> Tags; if (GMCollinear3Pts(V1 -> Coord, V2 -> Coord, V3 -> Coord)) { IPFreeVertex(V1); IPFreeVertex(V2); } else { if (Pl -> PVertex == NULL) { Pl -> PVertex = V1; } else { PlNew = IPAllocPolygon(0, V1, PlNew); PLANE_COPY(PlNew -> Plane, Pl -> Plane); IP_SET_PLANE_POLY(PlNew); PlNew -> Attr = IP_ATTR_COPY_ATTRS(Pl -> Attr); } } VLast = V3; VLastTags = VRest -> Tags; IPFreeVertex(VRest); VRest = VRestNext; } if (PlNew != NULL) { Pl -> Pnext = PlNew; IPGetLastPoly(PlNew) -> Pnext = PlNext; } if (FreeVHead) IPFreeVertexList(VHead); } Pl = PlNext; } /* Purge empty polygons. */ for (Pl = PolyObj -> U.Pl; Pl != NULL && Pl -> PVertex == NULL; ) { PolyObj -> U.Pl = Pl -> Pnext; IPFreePolygon(Pl); Pl = PolyObj -> U.Pl; } if ((Pl = PolyObj -> U.Pl) != NULL) { while (Pl -> Pnext != NULL) { if (Pl -> Pnext -> PVertex == NULL) { IPPolygonStruct *PlTmp = Pl -> Pnext; Pl -> Pnext = PlTmp -> Pnext; IPFreePolygon(PlTmp); } else Pl = Pl -> Pnext; } } /* Update the plane equation. */ for (Pl = PolyObj -> U.Pl; Pl != NULL; Pl = Pl -> Pnext) { VectorType OldPlNrml; VEC_COPY(OldPlNrml, Pl -> Plane); if (IPUpdatePolyPlane(Pl)) { if (DOT_PROD(OldPlNrml, Pl -> Plane) < 0.0) PLANE_SCALE(Pl -> Plane, -1.0); } } return PolyObj; } /***************************************************************************** * DESCRIPTION: M * Creates a new polygonal objects out of given one, that contains only M * triangles. None convex polygons are split to convex one which, in turn, M * converted to triangles. Collinear points are used and split at. M * * * PARAMETERS: M * PolyObj: Polygonal object to split into triangles. M * * * RETURN VALUE: M * IPObjectStruct *: A polygonal object containing only triangles M * representing the same model as PolyObj. M * * * SEE ALSO: M * ConvexPolyObjectN, GMConvertPolysToNGons, GMLimitTrianglesEdgeLen M * GMConvertPolysToTriangles M * * * KEYWORDS: M * GMConvertPolysToTriangles2 M *****************************************************************************/ IPObjectStruct *GMConvertPolysToTriangles2(IPObjectStruct *PolyObj) { IPPolygonStruct *Pl; int IsCirc = IPSetPolyListCirc(FALSE); IPSetPolyListCirc(IsCirc); /* Restore state, now that we know it. */ PolyObj = GMConvexPolyObjectN(PolyObj); for (Pl = PolyObj -> U.Pl; Pl != NULL; ) { IPVertexStruct *VHead = Pl -> PVertex, *V = VHead; int WasSplit = FALSE; if (IPVrtxListLen(VHead) <= 3) { Pl = Pl -> Pnext; continue; } do { IPVertexStruct *VNext = V -> Pnext == NULL ? VHead : V -> Pnext, *VNextNext = VNext -> Pnext == NULL ? VHead : VNext -> Pnext; if (!GMCollinear3Pts(V -> Coord, VNext -> Coord, VNextNext -> Coord)) { /* Found a non colinear corner to cut. */ IPVertexStruct *V3 = IPAllocVertex2(NULL), *V2 = VNext, *V1 = IPAllocVertex2(V2); /* Build the new triangle. */ V2 -> Pnext = V3; if (IsCirc) V3 -> Pnext = V1; VERTEX_COPY(V1, V); VERTEX_COPY(V3, VNextNext); IP_SET_INTERNAL_VRTX(V3); Pl -> Pnext = IPAllocPolygon(0, V1, Pl -> Pnext); PLANE_COPY(Pl -> Pnext -> Plane, Pl -> Plane); IP_SET_PLANE_POLY(Pl -> Pnext); Pl -> Pnext -> Attr = IP_ATTR_COPY_ATTRS(Pl -> Attr); /* Update the remaining portion. */ V -> Pnext = VNextNext; IP_SET_INTERNAL_VRTX(V); WasSplit = TRUE; /* Make sure the head vertex is not in the clipped side. */ if (VNext == VHead) VHead = Pl -> PVertex = V; break; } V = VNext; } while (V != NULL && V != VHead); if (!WasSplit) Pl = Pl -> Pnext; } /* Purge empty polygons. */ for (Pl = PolyObj -> U.Pl; Pl != NULL && Pl -> PVertex == NULL; ) { PolyObj -> U.Pl = Pl -> Pnext; IPFreePolygon(Pl); Pl = PolyObj -> U.Pl; } if ((Pl = PolyObj -> U.Pl) != NULL) { while (Pl -> Pnext != NULL) { if (Pl -> Pnext -> PVertex == NULL) { IPPolygonStruct *PlTmp = Pl -> Pnext; Pl -> Pnext = PlTmp -> Pnext; IPFreePolygon(PlTmp); } else Pl = Pl -> Pnext; } } return PolyObj; } /***************************************************************************** * DESCRIPTION: * * Computes the distance between two polylines. * * * * PARAMETERS: * * Pl1, Pl2: To compute the minimal distance between the end points of. * * Start1, Start2: TRUE if start of polyline, FALSE if end of polyline. * * * * RETURN VALUE: * * RealType: Minimal distance squared computed. * *****************************************************************************/ static RealType GetPlPlDist(IPPolygonStruct *Pl1, IPPolygonStruct *Pl2, int *Start1, int *Start2) { RealType MinSqr = IRIT_INFNTY; IPVertexStruct *V1Start = Pl1 -> PVertex, *V1End = IPGetLastVrtx(V1Start), *V2Start = Pl2 -> PVertex, *V2End = IPGetLastVrtx(V2Start); if (V1Start == V1End) { if (V2Start == V2End) { TEST_CLOSEST_DIST(V1Start, V2Start, 1, 1); } else { TEST_CLOSEST_DIST(V1Start, V2Start, 1, 1); TEST_CLOSEST_DIST(V1Start, V2End, 1, 0); } } else { if (V2Start == V2End) { TEST_CLOSEST_DIST(V1Start, V2Start, 1, 1); TEST_CLOSEST_DIST(V1End, V2Start, 0, 1); } else { TEST_CLOSEST_DIST(V1Start, V2Start, 1, 1); TEST_CLOSEST_DIST(V1End, V2Start, 0, 1); TEST_CLOSEST_DIST(V1Start, V2End, 1, 0); TEST_CLOSEST_DIST(V1End, V2End, 0, 0); } } return MinSqr; } /***************************************************************************** * DESCRIPTION: * * A comparison function to examine if the given two vertices are the same. * * * * PARAMETERS: * * V1, V2: The two vertices to compare. * * Eps: Epslion of similarity to merge points at. * * * * RETURN VALUE: * * int: TRUE if identical, FALSE if differ. * *****************************************************************************/ static int CmpTwoVertices(IPVertexStruct *V1, IPVertexStruct *V2, RealType Eps) { return PT_APX_EQ_EPS(V1 -> Coord, V2 -> Coord, Eps); } /***************************************************************************** * DESCRIPTION: M * Merges seperated polylines into longer ones, in place, as possible. M * Given a list of polylines, matches end points and merged as possible M * polylines with common end points, in place. M * * * PARAMETERS: M * Polys: Polylines to merge, in place. M * Eps: Epslion of similarity to merge points at. M * VrtxCmpFunc: Vertices comparison functions, NULL for coord cmp. M * * * RETURN VALUE: M * IPPolygonStruct *: Merged as possible polylines. M * * * KEYWORDS: M * GMMergePolylines, merge, polyline M *****************************************************************************/ IPPolygonStruct *GMMergePolylines(IPPolygonStruct *Polys, RealType Eps, GMMergePolyVrtxCmpFuncType VrtxCmpFunc) { IPPolygonStruct *Pl, *Pl2, *Pl2Prev; if (VrtxCmpFunc == NULL) VrtxCmpFunc = CmpTwoVertices; for (Pl = Polys; Pl != NULL; ) { int HasChanged = FALSE; IPVertexStruct *V1 = Pl -> PVertex, *V2 = IPGetLastVrtx(V1); for (Pl2Prev = Pl, Pl2 = Pl -> Pnext; Pl2 != NULL && !HasChanged; ) { IPVertexStruct *V, *Pl2V1 = Pl2 -> PVertex, *Pl2V2 = IPGetLastVrtx(Pl2V1); int FoundMatch = TRUE; if (VrtxCmpFunc(V1, Pl2V1, Eps)) { Pl -> PVertex = IPReverseVrtxList2(Pl -> PVertex); V = IPGetLastVrtx(Pl -> PVertex); V -> Pnext = Pl2 -> PVertex -> Pnext; } else if (VrtxCmpFunc(V1, Pl2V2, Eps)) { Pl -> PVertex = IPReverseVrtxList2(Pl -> PVertex); Pl2 -> PVertex = IPReverseVrtxList2(Pl2 -> PVertex); V = IPGetLastVrtx(Pl -> PVertex); V -> Pnext = Pl2 -> PVertex -> Pnext; } else if (VrtxCmpFunc(V2, Pl2V1, Eps)) { V2 -> Pnext = Pl2V1 -> Pnext; } else if (VrtxCmpFunc(V2, Pl2V2, Eps)) { Pl2 -> PVertex = IPReverseVrtxList2(Pl2 -> PVertex); V2 -> Pnext = Pl2 -> PVertex -> Pnext; } else FoundMatch = FALSE; if (FoundMatch) { Pl2Prev -> Pnext = Pl2 -> Pnext; Pl2 -> PVertex -> Pnext = NULL; IPFreePolygon(Pl2); Pl2 = Pl2Prev -> Pnext; HasChanged = TRUE; } else { Pl2Prev = Pl2; Pl2 = Pl2 -> Pnext; } } if (!HasChanged) Pl = Pl -> Pnext; } return Polys; } /***************************************************************************** * DESCRIPTION: * * Routine to compare two GMPlPlDistStruct for sorting purposes. * * * * PARAMETERS: * * PlPlDist1, PlPlDist2: Two pointers to PlPlDist structs. * * * * RETURN VALUE: * * int: >0, 0, or <0 as the relation between the two distances (squared). * *****************************************************************************/ #if defined(ultrix) && defined(mips) static int ComparePlPlDist(VoidPtr PlPlDist1, VoidPtr PlPlDist2) #else static int ComparePlPlDist(const VoidPtr PlPlDist1, const VoidPtr PlPlDist2) #endif /* ultrix && mips (no const support) */ { RealType Diff = ((GMPlPlDistStruct *) PlPlDist1) -> MinDistSqr - ((GMPlPlDistStruct *) PlPlDist2) -> MinDistSqr; return SIGN(Diff); } /***************************************************************************** * DESCRIPTION: M * Connect the list of points into polylines by connecting the closest M * point pairs, until the distances between adjacent points/polylines is more M * than MaxTol. Points are assumed to be in E3. M * * * PARAMETERS: M * PtsList: Point list to connect into polylines. M * MaxTol: Maximum distance allowed to connect to points. M * * * RETURN VALUE: M * IPPolygonStruct *: Connected polylines, upto MaxTol tolerance. M * * * KEYWORDS: M * GMMatchPointListIntoPolylines M *****************************************************************************/ IPPolygonStruct *GMMatchPointListIntoPolylines(IPObjectStruct *PtsList, RealType MaxTol) { int i, LastN, n; RealType MaxTolSqr = SQR(MaxTol); IPPolygonStruct *PllList; IPObjectStruct *PObj; PtsList = IPCopyObject(NULL, PtsList, FALSE); IPCoercePtsListTo(PtsList, CAGD_PT_E3_TYPE); /* Convert the list object to polyline linked list with one vertex in */ /* each polyline so we can start and match-connect them. */ for (PllList = NULL, i = 0; (PObj = IPListObjectGet(PtsList, i++)) != NULL; ) { IPVertexStruct *V; CagdRType *Coords = PObj -> U.CtlPt.Coords; PllList = IPAllocPolygon(0, V = IPAllocVertex2(NULL), PllList); CagdCoerceToE3(V -> Coord, &Coords, -1, PObj -> U.CtlPt.PtType); } IPFreeObject(PtsList); n = IPPolyListLen(PllList); do { int j, *InvIdxMap = (int *) IritMalloc(sizeof(int) * n); GMPlPlDistStruct *Plls = (GMPlPlDistStruct *) IritMalloc(sizeof(GMPlPlDistStruct) * n); LastN = n; # ifdef DEBUG { IRIT_SET_IF_DEBUG_ON_PARAMETER(_DebugPtsToPlsSteps, FALSE) fprintf(stderr, IRIT_EXP_STR("Num of Polylines = %5d\n"), n); } # endif /* DEBUG */ /* Make the list into an array. */ for (i = 0; i < n; i++) LIST_POP(Plls[i].Pl, PllList); /* Compute minimal distance between all points/polylines. */ for (i = 0; i < n; i++) { RealType MinDistSqr = IRIT_INFNTY; int MinStart1 = -1, MinStart2 = -1, MinIdx = -1; for (j = i + 1; j < n; j++) { int Start1, Start2; RealType DistSqr = GetPlPlDist(Plls[i].Pl, Plls[j].Pl, &Start1, &Start2); if (DistSqr < MinDistSqr) { MinIdx = j; MinStart1 = Start1; MinStart2 = Start2; MinDistSqr = DistSqr; } } Plls[i].Idx = i; Plls[i].MinIdx = MinIdx; Plls[i].MinStart1 = MinStart1; Plls[i].MinStart2 = MinStart2; Plls[i].MinDistSqr = MinDistSqr; } /* Sort the array based on MinDistSqr slot and build an inverse map. */ qsort(Plls, n - 1, sizeof(GMPlPlDistStruct), ComparePlPlDist); for (i = 0; i < n; i++) InvIdxMap[Plls[i].Idx] = i; /* Merge all polyline we can (no merged polyline will be remerged). */ for (i = 0, PllList = NULL; i < n - 1 && Plls[i].MinDistSqr < MaxTolSqr; i++) { j = InvIdxMap[Plls[i].MinIdx]; if (Plls[i].Pl != NULL && Plls[j].Pl != NULL) { /* Merge plln index i with plln index j, j > i. */ if (Plls[i].MinStart1) Plls[i].Pl -> PVertex = IPReverseVrtxList2(Plls[i].Pl -> PVertex); if (!Plls[i].MinStart2) Plls[j].Pl -> PVertex = IPReverseVrtxList2(Plls[j].Pl -> PVertex); IPGetLastVrtx(Plls[i].Pl -> PVertex) -> Pnext = Plls[j].Pl -> PVertex; Plls[j].Pl -> PVertex = NULL; IPFreePolygon(Plls[j].Pl); LIST_PUSH(Plls[i].Pl, PllList); Plls[j].Pl = Plls[i].Pl = NULL; } } /* Regroup into a linked list the rest of the polylines. */ for (i = 0; i < n; i++) if (Plls[i].Pl != NULL) LIST_PUSH(Plls[i].Pl, PllList); IritFree(Plls); IritFree(InvIdxMap); n = IPPolyListLen(PllList); } while (n < LastN); return PllList; } /***************************************************************************** * DESCRIPTION: M * Regularize a polygonal model by eliminating all T junction in the M * polygonal mesh. M * * * PARAMETERS: M * PObj: A polygonal object to regularize. M * * * RETURN VALUE: M * IPObjectStruct *: Regularized object. M * * * KEYWORDS: M * GMRegularizePolyModel M *****************************************************************************/ IPObjectStruct *GMRegularizePolyModel(IPObjectStruct *PObj) { int TrisOnly = TRUE; IPPolygonStruct *Pl; if (!IP_IS_POLY_OBJ(PObj) || !IP_IS_POLYGON_OBJ(PObj)) return NULL; PObj = IPCopyObject(NULL, PObj, FALSE); IP_SET_POLYGON_OBJ(PObj); BoolGenAdjacencies(PObj); /* Find all edges in polygons to split according to adj. edges' sizes. */ for (Pl = PObj -> U.Pl; Pl != NULL; Pl = Pl -> Pnext) { IPVertexStruct *VAdj, *V = Pl -> PVertex; if (IPVrtxListLen(V) != 3) /* Do we have triangles only in input? */ TrisOnly = FALSE; do { if (V -> PAdj != NULL && (VAdj = GetAdjVertex(V, Pl, V -> PAdj)) != NULL) { if ((PT_APX_EQ(V -> Coord, VAdj -> Coord) && PT_APX_EQ(V -> Pnext -> Coord, VAdj -> Pnext -> Coord)) || (PT_APX_EQ(V -> Coord, VAdj -> Pnext -> Coord) && PT_APX_EQ(V -> Pnext -> Coord, VAdj -> Coord))) { /* This edge is identical on both sides - regular! */ V -> PAdj = VAdj -> PAdj = NULL; /* Clear adjacency. */ } else { /* Need to split this edge - update this adj. info. */ UpdateAdjPolys(V, Pl, VAdj, V -> PAdj); V -> PAdj = VAdj -> PAdj = NULL; /* Clear adjacency. */ } } V = V -> Pnext; } while (V != NULL && V != Pl -> PVertex); } /* Insert the new vertices into the edges that require splitting. */ for (Pl = PObj -> U.Pl; Pl != NULL; Pl = Pl -> Pnext) { IPVertexStruct *VSList, *VNext, *VMid, *V = Pl -> PVertex; do { VNext = V -> Pnext; if ((VSList = AttrGetPtrAttrib(V -> Attr, "_vslist")) != NULL) { IPGetLastVrtx(VSList) -> Pnext = VNext; for (VMid = VSList; VMid != VNext; VMid = VMid -> Pnext) { GMInterpVrtxNrmlBetweenTwo(VMid, V, V -> Pnext); GMInterpVrtxRGBBetweenTwo(VMid, V, V -> Pnext); GMInterpVrtxUVBetweenTwo(VMid, V, V -> Pnext); } V -> Pnext = VSList; AttrFreeOneAttribute(&V -> Attr, "_vslist"); } V = VNext; } while (V != NULL && V != Pl -> PVertex); } /* Split polygons that has colinear adj. edges due to vertex insertions. */ Pl = GMSplitPolysAtCollinearVertices(PObj -> U.Pl); IPFreePolygonList(PObj -> U.Pl); PObj -> U.Pl = Pl; if (TrisOnly) { IPObjectStruct *PRet = GMConvertPolysToTriangles(PObj); IPFreeObject(PObj); AttrSetObjectIntAttrib(PRet, "regular", TRUE); return PRet; } else { AttrSetObjectIntAttrib(PObj, "regular", TRUE); return PObj; } } /***************************************************************************** * DESCRIPTION: * * Update the two edges that are colinear with vertices to be splitted at * * as/if necessary. Compute length along the edges' direction and insert * * into vertex split list if inside the edge. * * * * PARAMETERS: * * V1: First vertex in edge (V1, V1 -> Pnext) of Pl1. * * Pl1: First polygon holding V1. * * V2: First vertex in edge (V2, V2 -> Pnext) of Pl2. * * Pl2: First polygon holding V2. * * * * RETURN VALUE: * * void * *****************************************************************************/ static void UpdateAdjPolys(IPVertexStruct *V1, IPPolygonStruct *Pl1, IPVertexStruct *V2, IPPolygonStruct *Pl2) { RealType Edge1Len, Edge2Len, t, *Pt1 = V1 -> Coord, *Pt1Next = V1 -> Pnext -> Coord, *Pt2 = V2 -> Coord, *Pt2Next = V2 -> Pnext -> Coord; VectorType Edge1Dir, Edge2Dir, Vec; /* Do the first, V2 inside V1, direction. */ VEC_SUB(Edge1Dir, Pt1Next, Pt1); Edge1Len = VEC_LENGTH(Edge1Dir); VEC_SUB(Vec, Pt2, Pt1); t = VEC_LENGTH(Vec) / Edge1Len; if (DOT_PROD(Vec, Edge1Dir) < 0.0) t = -t; if (0.0 < t && !APX_EQ(0.0, t) && 1.0 > t && !APX_EQ(1.0, t)) InsertVertexToSplitList(V1, Pt2, t); VEC_SUB(Vec, Pt2Next, Pt1); t = VEC_LENGTH(Vec) / Edge1Len; if (DOT_PROD(Vec, Edge1Dir) < 0.0) t = -t; if (0.0 < t && !APX_EQ(0.0, t) && 1.0 > t && !APX_EQ(1.0, t)) InsertVertexToSplitList(V1, Pt2Next, t); /* Do the second, V1 inside V2, direction. */ VEC_SUB(Edge2Dir, Pt2Next, Pt2); Edge2Len = VEC_LENGTH(Edge2Dir); VEC_SUB(Vec, Pt1, Pt2); t = VEC_LENGTH(Vec) / Edge2Len; if (DOT_PROD(Vec, Edge2Dir) < 0.0) t = -t; if (0.0 < t && !APX_EQ(0.0, t) && 1.0 > t && !APX_EQ(1.0, t)) InsertVertexToSplitList(V2, Pt1, t); VEC_SUB(Vec, Pt1Next, Pt2); t = VEC_LENGTH(Vec) / Edge2Len; if (DOT_PROD(Vec, Edge2Dir) < 0.0) t = -t; if (0.0 < t && !APX_EQ(0.0, t) && 1.0 > t && !APX_EQ(1.0, t)) InsertVertexToSplitList(V2, Pt1Next, t); } /***************************************************************************** * DESCRIPTION: * * Insert a new vertex to split the edge (V, V -> Pnext) into two at, to * * the split vertex list. * * * * PARAMETERS: * * V: Pointer on the edge (V, V -> Pnext) that Pos is a middle position * * to split at. * * Pos: New location inside (V, V -> Pnext) to split at. * * t: location along the edge where Pos is. * * (t = 0 is V, t = 1 is V -> Pnext). * * * * RETURN VALUE: * * void * *****************************************************************************/ static void InsertVertexToSplitList(IPVertexStruct *V, RealType *Pos, RealType t) { IPVertexStruct *VSList = AttrGetPtrAttrib(V -> Attr, "_vslist"), *NewV = IPAllocVertex(V -> Tags, NULL, NULL); PT_COPY(NewV -> Coord, Pos); VEC_BLEND(NewV -> Normal, V -> Pnext -> Normal, V -> Normal, t); AttrSetRealAttrib(&NewV -> Attr, "_vst", t); /* Insert the new vertex into VSList at the proper order. */ if (VSList == NULL) { VSList = NewV; } else { RealType Tl; IPVertexStruct *VSHead, *VSPrev = NULL; /* Find the proper location to insert NewV into VSList. */ VSHead = VSList; do { Tl = AttrGetRealAttrib(VSHead -> Attr, "_vst"); if (APX_EQ(Tl, t)) { /* Drop this vertex - it is already in. */ IPFreeVertex(NewV); return; } if (Tl > t) break; VSPrev = VSHead; VSHead = VSHead -> Pnext; } while (VSHead != NULL); if (VSPrev == NULL) { NewV -> Pnext = VSList; VSList = NewV; } else { NewV -> Pnext = VSPrev -> Pnext; VSPrev -> Pnext = NewV; } } AttrSetPtrAttrib(&V -> Attr, "_vslist", VSList); } /***************************************************************************** * DESCRIPTION: M * Splits the given polygons in vertices that connect two adjacent colinear M * edges. M * Polygons are assumed convex other than this colinearity conditions. M * * * PARAMETERS: M * Pls: List of polygons to split at colinear edges. M * * * RETURN VALUE: M * IPPolygonStruct *: New list of polygons with no colinear adjacent edges. M * * * KEYWORDS: M * GMSplitPolysAtCollinearVertices M *****************************************************************************/ IPPolygonStruct *GMSplitPolysAtCollinearVertices(IPPolygonStruct *Pls) { IPPolygonStruct *Pl, *PlNext, *PlPrev = NULL; Pls = IPCopyPolygonList(Pls); for (Pl = Pls; Pl != NULL; ) { int WasSplit = FALSE; IPVertexStruct *VNext, *V = Pl -> PVertex; do { VNext = V -> Pnext; if (GMCollinear3Pts(V -> Coord, VNext -> Coord, VNext -> Pnext -> Coord)) { /* Split polygon into two at VNext and chain second poly in. */ if ((PlNext = GMSplitPolyInPlaceAtVertex(Pl, VNext)) == NULL) { /* Remove Pl from polygon list - a degenerated polygon. */ if (PlPrev == NULL) { Pls = Pls -> Pnext; IPFreePolygon(Pl); Pl = Pls; } else { PlPrev -> Pnext = Pl -> Pnext; IPFreePolygon(Pl); Pl = PlPrev -> Pnext; } } else { PlNext -> Pnext = Pl -> Pnext; Pl -> Pnext = PlNext; } WasSplit = TRUE; break; } V = VNext; } while (V != NULL && V != Pl -> PVertex); if (!WasSplit) { PlPrev = Pl; Pl = Pl -> Pnext; } } return Pls; } /***************************************************************************** * DESCRIPTION: M * Splits the given convex polygon, in place, into two, returning second M * half of the polygon while updating Pl to hold the first half. M * Polygon is split so that VHead is on border between the two polygons. M * * * PARAMETERS: M * Pl: Convex polygon to split into two. M * VHead: Vertex to split Pl at. M * * * RETURN VALUE: M * IPPolygonStruct *: The second half of the splitted polygon (first half M * is returned, in place, in Pl). M * This function returns a NULL if split failed due to M * the fact that the polygon degenerated into a line. M * Pl is not affected if NULL is returned. M * * * SEE ALSO: M * GMSplitPolyInPlaceAt2Vertices M * * * KEYWORDS: M * GMSplitPolyInPlaceAtVertex M *****************************************************************************/ IPPolygonStruct *GMSplitPolyInPlaceAtVertex(IPPolygonStruct *Pl, IPVertexStruct *VHead) { IPVertexStruct *VHead2, *V2, *VTmp, *VNext = VHead -> Pnext, *V = VNext -> Pnext; IPPolygonStruct *PlRet; do { if (V -> Pnext != VHead && !GMCollinear3Pts(VHead -> Coord, VNext -> Coord, V -> Coord)) { /* The edge (VHead, V) is our splitting edge. */ VHead2 = IPAllocVertex(VHead -> Tags, NULL, VHead -> Pnext); PT_COPY(VHead2 -> Coord, VHead -> Coord); VEC_COPY(VHead2 -> Normal, VHead -> Normal); VHead2 -> Attr = IP_ATTR_COPY_ATTRS(VHead -> Attr); V2 = IPAllocVertex(V -> Tags, NULL, V -> Pnext); PT_COPY(V2 -> Coord, V -> Coord); VEC_COPY(V2 -> Normal, V -> Normal); V2 -> Attr = IP_ATTR_COPY_ATTRS(V -> Attr); for (VTmp = V2; VTmp -> Pnext != VHead; VTmp = VTmp -> Pnext); VTmp -> Pnext = VHead2; V -> Pnext = VHead; IP_SET_INTERNAL_VRTX(V); Pl -> PVertex = V; VHead2 -> Pnext = V2; IP_SET_INTERNAL_VRTX(VHead2); PlRet = IPAllocPolygon(Pl -> Tags, V2, NULL); PLANE_COPY(PlRet -> Plane, Pl -> Plane); IP_SET_PLANE_POLY(PlRet); PlRet -> Attr = IP_ATTR_COPY_ATTRS(Pl -> Attr); IP_RST_BBOX_POLY(Pl); IP_RST_BBOX_POLY(PlRet); return PlRet; } V = V -> Pnext; } while (V != NULL && V -> Pnext != VHead); return NULL; /* This polygon has degenerated into a line. */ } /***************************************************************************** * DESCRIPTION: M * Splits the given convex polygon, in place, into two, returning second M * half of the polygon while updating Pl to hold the first half. M * * * PARAMETERS: M * Pl: Convex polygon to split into two. M * V1: First Vertex to split Pl at. M * V2: Second Vertex to split Pl at. M * * * RETURN VALUE: M * IPPolygonStruct *: The second half of the splitted polygon (first half M * is returned, in place, in Pl). M * This function returns a NULL if split failed due to M * the fact that the polygon degenerated into a line. M * Pl is not affected if NULL is returned. M * The second polygon is added as next to the first M * polygon. M * * * SEE ALSO: M * GMSplitPolyInPlaceAtVertex M * * * KEYWORDS: M * GMSplitPolyInPlaceAt2Vertices M *****************************************************************************/ IPPolygonStruct *GMSplitPolyInPlaceAt2Vertices(IPPolygonStruct *Pl, IPVertexStruct *V1, IPVertexStruct *V2) { int IPIsVertexListCirc = IPGetLastVrtx(Pl -> PVertex) -> Pnext != NULL; IPPolygonStruct *PlRet; IPVertexStruct *V11, *V21; /* Make list temporary circular, even if not. */ if (!IPIsVertexListCirc) IPGetLastVrtx(Pl -> PVertex) -> Pnext = Pl -> PVertex; if (PT_APX_EQ(V1 -> Coord, V2 -> Coord) || PT_APX_EQ(V1 -> Coord, V2 -> Pnext -> Coord) || PT_APX_EQ(V1 -> Pnext -> Coord, V2 -> Coord)) return NULL; /* Duplicate the two vertices we are splitting at. */ V11 = IPAllocVertex(V1 -> Tags, NULL, V1 -> Pnext); PT_COPY(V11 -> Coord, V1 -> Coord); VEC_COPY(V11 -> Normal, V1 -> Normal); V11 -> Attr = IP_ATTR_COPY_ATTRS(V1 -> Attr); V21 = IPAllocVertex(V2 -> Tags, NULL, V2 -> Pnext); PT_COPY(V21 -> Coord, V2 -> Coord); VEC_COPY(V21 -> Normal, V2 -> Normal); V21 -> Attr = IP_ATTR_COPY_ATTRS(V2 -> Attr); /* Make the two new links of the two pieces and create the second poly. */ V1 -> Pnext = V21; V2 -> Pnext = V11; IP_SET_INTERNAL_VRTX(V2); IP_SET_INTERNAL_VRTX(V1); PlRet = IPAllocPolygon(Pl -> Tags, V2, NULL); PLANE_COPY(PlRet -> Plane, Pl -> Plane); IP_SET_PLANE_POLY(PlRet); PlRet -> Attr = IP_ATTR_COPY_ATTRS(Pl -> Attr); IP_RST_BBOX_POLY(Pl); IP_RST_BBOX_POLY(PlRet); /* Make vertices' lists NULL terminated if input was NULL terminated. */ if (!IPIsVertexListCirc) { IPGetLastVrtx(Pl -> PVertex) -> Pnext = NULL; IPGetLastVrtx(PlRet -> PVertex) -> Pnext = NULL; } PlRet -> Pnext = Pl -> Pnext; Pl -> Pnext = PlRet; return PlRet; } /***************************************************************************** * DESCRIPTION: * * Find out the adjacent edge to edge (V, V -> Pnext) in PAdj. * * * * PARAMETERS: * * V: Of edge (V, V -> Pnext) to find adjacent edge on PAdj. * * Pl: Polygon containing vertex V and edge (V, V -> Pnext). * * PAdj: The adjacent polygon to look in. * * * * RETURN VALUE: * * IPVertexStruct *: The vertex the leads to the adjacent edge in PAdj. * *****************************************************************************/ static IPVertexStruct *GetAdjVertex(IPVertexStruct *V, IPPolygonStruct *Pl, IPPolygonStruct *PAdj) { IPVertexStruct *VTmp; VectorType Dir, UDir; /* Lets see if we have the mutual pointer - a full adjacency. */ VTmp = PAdj -> PVertex; do { if (VTmp -> PAdj == Pl) return VTmp; VTmp = VTmp -> Pnext; } while (VTmp != NULL && VTmp != PAdj -> PVertex); /* Can still be a partial adjacency. Check it now */ VEC_SUB(Dir, V -> Pnext -> Coord, V -> Coord); VEC_COPY(UDir, Dir); VEC_NORMALIZE(UDir); VTmp = PAdj -> PVertex; do { PointType Pt1, Pt2; GMPointFromPointLine(VTmp -> Coord, V -> Coord, Dir, Pt1); GMPointFromPointLine(VTmp -> Pnext -> Coord, V -> Coord, Dir, Pt2); if (PT_PT_DIST_SQR(VTmp -> Coord, Pt1) < SQR(IRIT_EPS) && PT_PT_DIST_SQR(VTmp -> Pnext -> Coord, Pt2) < SQR(IRIT_EPS)) { RealType t1, t2; VectorType Vec; /* This edge is colinear with our edge - make sure these two */ /* edge share a common ground. */ VEC_SUB(Vec, Pt1, V -> Coord); t1 = DOT_PROD(Vec, UDir); VEC_SUB(Vec, Pt2, V -> Coord); t2 = DOT_PROD(Vec, UDir); if (t1 > t2) SWAP(RealType, t1, t2); if (t1 < 1.0 || t2 > 0.) return VTmp; } VTmp = VTmp -> Pnext; } while (VTmp != NULL && VTmp != PAdj -> PVertex); return NULL; } /***************************************************************************** * DESCRIPTION: M * Splits all triangles that has edge length larger than MaxLen. The M * output will have no edge in no triangle with length larger than Maxlen. M * * * PARAMETERS: M * Pls: List of triangles. M * MaxLen: Maximum allowed length of an edge of a triangle. M * * * RETURN VALUE: M * IPPolygonStruct *: Splitted polygons with edges smaller/equal to MaxLen. M * * * SEE ALSO: M * ConvexPolyObjectN, GMConvertPolysToNGons, GMConvertPolysToTriangles M * * * KEYWORDS: M * GMLimitTrianglesEdgeLen M *****************************************************************************/ IPPolygonStruct *GMLimitTrianglesEdgeLen(IPPolygonStruct *Pls, RealType MaxLen) { RealType MaxLenSqr = SQR(MaxLen); IPPolygonStruct *Pl; Pls = IPCopyPolygonList(Pls); for (Pl = Pls; Pl != NULL; ) { RealType D12, D23, D31; IPVertexStruct *V2, *V3, *Vl2, *V1 = Pl -> PVertex; if (IPVrtxListLen(V1) != 3) { GEOM_FATAL_ERROR(GEOM_ERR_TRIANGLES_ONLY); return NULL; } V2 = V1 -> Pnext; V3 = V2 -> Pnext; D12 = PT_PT_DIST_SQR(V1 -> Coord, V2 -> Coord); D23 = PT_PT_DIST_SQR(V2 -> Coord, V3 -> Coord); D31 = PT_PT_DIST_SQR(V3 -> Coord, V1 -> Coord); /* If we have an edge too long - duplicate the polygon as its next */ /* and keep the two parts in Pl and pl -> Pnext. */ if (D12 > MaxLenSqr || D23 > MaxLenSqr || D31 > MaxLenSqr) { int DoNormals; Pl -> Pnext = IPAllocPolygon(Pl -> Tags, IPCopyVertexList(V1), Pl -> Pnext); PLANE_COPY(Pl -> Pnext -> Plane, Pl -> Plane); IP_SET_PLANE_POLY(Pl -> Pnext); Pl -> Pnext -> Attr = IP_ATTR_COPY_ATTRS(Pl -> Attr); IP_RST_BBOX_POLY(Pl -> Pnext); Vl2 = Pl -> Pnext -> PVertex; if (IP_HAS_NORMAL_VRTX(V1) && IP_HAS_NORMAL_VRTX(V2) && IP_HAS_NORMAL_VRTX(V3)) { DoNormals = TRUE; IP_SET_NORMAL_VRTX(Vl2); IP_SET_NORMAL_VRTX(Vl2 -> Pnext); IP_SET_NORMAL_VRTX(Vl2 -> Pnext -> Pnext); } else { DoNormals = FALSE; IP_RST_NORMAL_VRTX(Vl2); IP_RST_NORMAL_VRTX(Vl2 -> Pnext); IP_RST_NORMAL_VRTX(Vl2 -> Pnext -> Pnext); } if (D12 >= D23 && D12 >= D31) { PT_BLEND(Vl2 -> Coord, V1 -> Coord, V2 -> Coord, 0.5); GMInterpVrtxRGBBetweenTwo(Vl2, V1, V2); COPY_RGB_ATTR(V2, Vl2) GMInterpVrtxUVBetweenTwo(Vl2, V1, V2); COPY_UV_ATTR(V2, Vl2); if (DoNormals) { GMInterpVrtxNrmlBetweenTwo(Vl2, V1, V2); VEC_COPY(V2 -> Normal, Vl2 -> Normal); } PT_COPY(V2 -> Coord, Vl2 -> Coord); IP_SET_INTERNAL_VRTX(V2); IP_SET_INTERNAL_VRTX(Vl2 -> Pnext -> Pnext); } else if (D23 >= D12 && D23 >= D31) { PT_BLEND(Vl2 -> Pnext -> Coord, V2 -> Coord, V3 -> Coord, 0.5); GMInterpVrtxRGBBetweenTwo(Vl2 -> Pnext, V2, V3); COPY_RGB_ATTR(V3, Vl2 -> Pnext) GMInterpVrtxUVBetweenTwo(Vl2 -> Pnext, V2, V3); COPY_UV_ATTR(V3, Vl2 -> Pnext); if (DoNormals) { GMInterpVrtxNrmlBetweenTwo(Vl2 -> Pnext, V2, V3); VEC_COPY(V3 -> Normal, Vl2 -> Pnext -> Normal); } PT_COPY(V3 -> Coord, Vl2 -> Pnext -> Coord); IP_SET_INTERNAL_VRTX(V3); IP_SET_INTERNAL_VRTX(Vl2); } else { PT_BLEND(Vl2 -> Coord, V3 -> Coord, V1 -> Coord, 0.5); GMInterpVrtxRGBBetweenTwo(Vl2, V1, V3); COPY_RGB_ATTR(V3, Vl2) GMInterpVrtxUVBetweenTwo(Vl2, V1, V3); COPY_UV_ATTR(V3, Vl2) if (DoNormals) { GMInterpVrtxNrmlBetweenTwo(Vl2, V1, V3); VEC_COPY(V3 -> Normal, Vl2 -> Normal); } PT_COPY(V3 -> Coord, Vl2 -> Coord); IP_SET_INTERNAL_VRTX(V2); IP_SET_INTERNAL_VRTX(Vl2); } } else { Pl = Pl -> Pnext; /* This polygon is small enough - skip it. */ } } return Pls; } /***************************************************************************** * DESCRIPTION: M * Generates UV values for polygonal geometry, based on the XY(Z) M * coordinates of the geometry. Will NOT overwrite existing "uvvals", if any. M * * * PARAMETERS: M * PObj: A polygonal object to update UV vals for. M * UTextureRepeat, VTextureRepeat, WTextureRepeat: Repeat texture factors. M * HasXYZScale: If TRUE, WTextureRepeat is also valid - use XYZ coords. M * * * RETURN VALUE: M * void M * * * KEYWORDS: M * GMGenUVValsForPolys M *****************************************************************************/ void GMGenUVValsForPolys(IPObjectStruct *PObj, RealType UTextureRepeat, RealType VTextureRepeat, RealType WTextureRepeat, int HasXYZScale) { int i, IgnoreAxis; float *OldUV; GMBBBboxStruct BBox = *GMBBComputeBboxObject(PObj); IPPolygonStruct *Pl; if (HasXYZScale) { /* For each polygon, use the best out of the three XYZ coordinates. */ for (Pl = PObj -> U.Pl; Pl != NULL; Pl = Pl -> Pnext) { int UseAxis[3]; RealType UV[2]; VectorType TextScl; IPVertexStruct *V = Pl -> PVertex, *V2 = IPGetLastVrtx(V); GMBBBboxStruct *PlBBox = GMBBComputeOnePolyBbox(Pl); UseAxis[0] = UseAxis[1] = UseAxis[2] = 0; do { int SameX = APX_EQ(V -> Coord[0], V2 -> Coord[0]), SameY = APX_EQ(V -> Coord[1], V2 -> Coord[1]), SameZ = APX_EQ(V -> Coord[2], V2 -> Coord[2]); /* We must use the specified axes if other two are same. */ UseAxis[0] += SameY && SameZ; UseAxis[1] += SameX && SameZ; UseAxis[2] += SameX && SameY; V2 = V; V = V -> Pnext; } while (V != NULL && V != Pl -> PVertex); for (i = 0; i < 3; i++) { if (UseAxis[i] == 0) { IgnoreAxis = i; break; } } /* Try to find the minimal bbox size that is also not used. */ if (IgnoreAxis >= 0) { for (i = IgnoreAxis + 1; i < 3; i++) { if (UseAxis[i] == 0 && PlBBox -> Max[i] - PlBBox -> Min[i] < PlBBox -> Max[IgnoreAxis] - PlBBox -> Min[IgnoreAxis]) IgnoreAxis = i; } } else { for (i = 0; i < 3; i++) { if (PlBBox -> Max[i] - PlBBox -> Min[i] < PlBBox -> Max[IgnoreAxis] - PlBBox -> Min[IgnoreAxis]) IgnoreAxis = i; } } switch (IgnoreAxis) { case 0: TextScl[1] = VTextureRepeat / (BBox.Max[1] - BBox.Min[1]); TextScl[2] = WTextureRepeat / (BBox.Max[2] - BBox.Min[2]); break; case 1: TextScl[0] = UTextureRepeat / (BBox.Max[0] - BBox.Min[0]); TextScl[2] = WTextureRepeat / (BBox.Max[2] - BBox.Min[2]); break; case 2: TextScl[0] = UTextureRepeat / (BBox.Max[0] - BBox.Min[0]); TextScl[1] = VTextureRepeat / (BBox.Max[1] - BBox.Min[1]); break; } /* And set all vertices of the polygon with UV. */ V = Pl -> PVertex; do { switch (IgnoreAxis) { case 0: UV[0] = (V -> Coord[1] - BBox.Min[1]) * TextScl[1]; UV[1] = (V -> Coord[2] - BBox.Min[2]) * TextScl[2]; break; case 1: UV[0] = (V -> Coord[0] - BBox.Min[0]) * TextScl[0]; UV[1] = (V -> Coord[2] - BBox.Min[2]) * TextScl[2]; break; case 2: UV[0] = (V -> Coord[0] - BBox.Min[0]) * TextScl[0]; UV[1] = (V -> Coord[1] - BBox.Min[1]) * TextScl[1]; break; } if ((OldUV = AttrGetUVAttrib(V -> Attr, "uvvals")) != NULL) { /* Use old values if has them but appl the scale. */ UV[0] = OldUV[0] * UTextureRepeat; UV[1] = OldUV[1] * VTextureRepeat; } AttrSetUVAttrib(&V -> Attr, "uvvals", UV[0], UV[1]); V = V -> Pnext; } while (V != NULL && V != Pl -> PVertex); } } else { /* Globally find best two axes to employ to compute the UV values. */ IgnoreAxis = 0; for (i = 1; i < 3; i++) { if (BBox.Max[i] - BBox.Min[i] < BBox.Max[IgnoreAxis] - BBox.Min[IgnoreAxis]) IgnoreAxis = i; } /* Keep the difference in Max slot, and scale with repeat vals. */ for (i = 0; i < 3; i++) BBox.Max[i] -= BBox.Min[i]; switch (IgnoreAxis) { case 0: BBox.Max[1] = UTextureRepeat / BBox.Max[1]; BBox.Max[2] = VTextureRepeat / BBox.Max[2]; break; case 1: BBox.Max[0] = UTextureRepeat / BBox.Max[0]; BBox.Max[2] = VTextureRepeat / BBox.Max[2]; break; case 2: BBox.Max[0] = UTextureRepeat / BBox.Max[0]; BBox.Max[1] = VTextureRepeat / BBox.Max[1]; break; } /* And set all vertices of all polygons with UV. */ for (Pl = PObj -> U.Pl; Pl != NULL; Pl = Pl -> Pnext) { IPVertexStruct *V; RealType UV[2]; V = Pl -> PVertex; do { switch (IgnoreAxis) { case 0: UV[0] = (V -> Coord[1] - BBox.Min[1]) * BBox.Max[1]; UV[1] = (V -> Coord[2] - BBox.Min[2]) * BBox.Max[2]; break; case 1: UV[0] = (V -> Coord[0] - BBox.Min[0]) * BBox.Max[0]; UV[1] = (V -> Coord[2] - BBox.Min[2]) * BBox.Max[2]; break; case 2: UV[0] = (V -> Coord[0] - BBox.Min[0]) * BBox.Max[0]; UV[1] = (V -> Coord[1] - BBox.Min[1]) * BBox.Max[1]; break; } if ((OldUV = AttrGetUVAttrib(V -> Attr, "uvvals")) != NULL) { /* Use old values if has them but appl the scale. */ UV[0] = OldUV[0] * UTextureRepeat; UV[1] = OldUV[1] * VTextureRepeat; } AttrSetUVAttrib(&V -> Attr, "uvvals", UV[0], UV[1]); V = V -> Pnext; } while (V != NULL && V != Pl -> PVertex); } } }