/* patch.c */ #include #include "patch.h" #include "pools.h" #include "error.h" #include "polygon.h" #include "radiance.h" #include "vertex.h" #include "patch_flags.h" #include "QMC/nied31.h" #ifdef NOPOOLS #define NEWPATCH() (PATCH *)Alloc(sizeof(PATCH)) #define DISPOSEPATCH(ptr) Free((char *)ptr, sizeof(PATCH)) #else static POOL *patchPool = (POOL *)NULL; #define NEWPATCH() (PATCH *)NewPoolCell(sizeof(PATCH), 0, "patches", &patchPool) #define DISPOSEPATCH(ptr) Dispose((char *)ptr, &patchPool) #endif /* a static counter which is increased every time a PATCH is created in * order to make a unique PATCH id. */ static int patchid = 1; int nrelements; /* This routine returns the ID number the next patch would get */ int PatchGetNextID(void) { return patchid; } /* With this routine, the ID nummer is set that the next patch will get. * Note that patch ID 0 is reserved. The smallest patch ID number should be 1. */ void PatchSetNextID(int id) { patchid = id; } /* adds the patch to the list of patches that share the vertex */ void PatchConnectVertex(PATCH *patch, VERTEX *vertex) { vertex->patches = PatchListAdd(vertex->patches, patch); } /* adds the patch to the list of patches sharing each vertex */ void PatchConnectVertices(PATCH *patch) { int i; for (i=0; inrvertices; i++) PatchConnectVertex(patch, patch->vertex[i]); } /* returns a pointer to the normal vector if everything is OK. NULL pointer if * degenerate polygon. */ VECTOR *PatchNormal(PATCH *patch, VECTOR *normal) { double norm; DVECTOR prev, cur; int i; VECTORSET(*normal,0,0,0); VECTORSUBTRACT(*patch->vertex[patch->nrvertices-1]->point, *patch->vertex[0]->point,cur); for(i=0; inrvertices; i++) { prev = cur; VECTORSUBTRACT(*patch->vertex[i]->point,*patch->vertex[0]->point,cur); normal->x += (prev.y - cur.y) * (prev.z + cur.z); normal->y += (prev.z - cur.z) * (prev.x + cur.x); normal->z += (prev.x - cur.x) * (prev.y + cur.y); } if ((norm = VECTORNORM(*normal)) < EPSILON) { Warning("PatchNormal", "degenerate patch (id %d)", patch->id); return (VECTOR *)NULL; } VECTORSCALEINVERSE(norm, *normal, *normal); return normal; } /* We also compute the jacobian J(u,v) of the coordinate mapping from the unit * square [0,1]^2 or the standard triangle (0,0),(1,0),(0,1) to the patch. * * - for triangles: J(u,v) = the area of the triangle * - for quadrilaterals: J(u,v) = A + B.u + C.v * the area of the patch = A + (B+C)/2 * for paralellograms holds that B=C=0 * the coefficients A,B,C are only explicitely stored if B and C are nonzero. * * The normal of the patch should have been computed before calling this routine. */ float PatchArea(PATCH *patch) { POINT *p1, *p2, *p3, *p4; DVECTOR d1, d2, d3, d4, cp1, cp2, cp3; double a, b, c; /* we need to compute the area and jacobian explicitely */ switch (patch->nrvertices) { case 3: /* jacobian J(u,v) for the mapping of the triangle * (0,0),(0,1),(1,0) to a triangular patch is constant and equal * to the area of the triangle ... so there is no need to store * any coefficients explicitely */ patch->jacobian = (JACOBIAN *)NULL; p1 = patch->vertex[0]->point; p2 = patch->vertex[1]->point; p3 = patch->vertex[2]->point; VECTORSUBTRACT(*p2, *p1, d1); VECTORSUBTRACT(*p3, *p2, d2); VECTORCROSSPRODUCT(d1, d2, cp1); patch->area = 0.5 * VECTORNORM(cp1); break; case 4: p1 = patch->vertex[0]->point; p2 = patch->vertex[1]->point; p3 = patch->vertex[2]->point; p4 = patch->vertex[3]->point; VECTORSUBTRACT(*p2, *p1, d1); VECTORSUBTRACT(*p3, *p2, d2); VECTORSUBTRACT(*p3, *p4, d3); VECTORSUBTRACT(*p4, *p1, d4); VECTORCROSSPRODUCT(d1, d4, cp1); VECTORCROSSPRODUCT(d1, d3, cp2); VECTORCROSSPRODUCT(d2, d4, cp3); a = VECTORDOTPRODUCT(cp1, patch->normal); b = VECTORDOTPRODUCT(cp2, patch->normal); c = VECTORDOTPRODUCT(cp3, patch->normal); patch->area = a + 0.5 * (b + c); if (patch->area < 0.) { /* may happen if the normal direction and a = -a; * vertex order are not consistent. */ b = -b; c = -c; patch->area = -patch->area; } /* b and c are zero for parallellograms. In that case, the area is equal to * a, so we don't need to store the coefficients. */ if (fabs(b)/patch->area < EPSILON && fabs(c)/patch->area < EPSILON) patch->jacobian = (JACOBIAN *)NULL; else patch->jacobian = JacobianCreate(a, b, c); break; default: Fatal(2, "PatchArea", "Can only handle triangular and quadrilateral patches.\n"); patch->jacobian = (JACOBIAN *)NULL; patch->area = 0.0; } if (patch->area < EPSILON*EPSILON) { fprintf(stderr, "Warning: very small patch id %d area = %g\n", patch->id, patch->area); } return patch->area; } /* Computes the midpoint of the patch, stores the result in p and * returns a pointer to p. */ POINT *PatchMidpoint(PATCH *patch, POINT *p) { int i; VECTORSET(*p, 0, 0, 0); for (i=0; inrvertices; i++) VECTORSUM(*p, *(patch->vertex[i]->point), *p); VECTORSCALEINVERSE((float)patch->nrvertices, *p, *p); return p; } /* Computes a certain "width" for the plane, e.g. for coplanarity testing. */ float PatchTolerance(PATCH *patch) { int i; double tolerance; /* fill in the vertices in the plane equation + take into account the vertex * position tolerance */ tolerance = 0.; for (i=0; inrvertices; i++) { POINT *p = patch->vertex[i]->point; double e = fabs(VECTORDOTPRODUCT(patch->normal, *p) + patch->plane_constant) + VECTORTOLERANCE(*p); if (e > tolerance) tolerance = e; } return tolerance; } /* edge flags */ #define U_DOMINANT 0x04 #define REVERSE_DIST 0x08 /* computes some ray tracing constants for the patch */ static void PatchInitRayTracing(PATCH *patch) { #ifndef LOWMEM_INTERSECT int i, j; DVEC2D p[4], m; float *s = patch->eslope, *d = patch->edist; unsigned char *flags = patch->eflags; VECTORPROJECT(m, patch->midpoint, patch->index); for (i=0; inrvertices; i++) { VECTORPROJECT(p[i], *patch->vertex[i]->point, patch->index); p[i].u -= m.u; p[i].v -= m.v; } for (i=0, j=1; inrvertices; i++, j=(j+1)%patch->nrvertices) { double a, b, ss; a = p[j].v - p[i].v; b = p[i].u - p[j].u; flags[i] = 0; if (fabs(a) > fabs(b)) { flags[i] |= U_DOMINANT; if (fabs(a) < patch->tolerance) { /* degenerate edge -> make sure inside test always succeeds. */ s[i] = 0.; d[i] = HUGE; } else { s[i] = ss = b/a; d[i] = (p[i].u + ss * p[i].v + p[j].u + ss * p[j].v) * 0.5; } } else { if (fabs(b) < patch->tolerance) { /* degenerate edge -> make sure inside test always succeeds. */ s[i] = 0.; d[i] = HUGE; } else { s[i] = ss = a/b; d[i] = (ss * p[i].u + p[i].v + ss * p[j].u + p[j].v) * 0.5; } } if (fabs(d[i]) < patch->tolerance) /* patches with their midpoint on an edge are undesired, malformed, patches */ d[i] = -HUGE; else /* positive should indicate "further away from midpoint" */ if (d[i] < 0.) flags[i] |= REVERSE_DIST; } #endif /*LOWMEM_INTERSECT not defined*/ } int IsPatchVirtual(PATCH *patch) { return(patch->nrvertices == 0); } /* Creates a VIRTUAL patch with no vertices */ PATCH *PatchCreateVirtual(void) { PATCH *patch; patch = NEWPATCH(); nrelements++; patch->twin = (PATCH *)NULL; patch->id = patchid; patchid++; patch->surface = (SURFACE *)NULL; patch->nrvertices = 0; patch->vertex[0] = (VERTEX *)NULL; patch->vertex[1] = (VERTEX *)NULL; patch->vertex[2] = (VERTEX *)NULL; patch->vertex[3] = (VERTEX *)NULL; patch->brep_data = (BREP_FACE *)NULL; patch->bounds = (float *)NULL; VECTORSET(patch->normal, 0.0, 0.0, 0.0); patch->jacobian = (JACOBIAN *)NULL; patch->area = 0.0; VECTORSET(patch->midpoint, 0.0, 0.0, 0.0);; patch->plane_constant = 0.0; patch->tolerance = 0.0; patch->index = 0; patch->direct_potential = 0.0; RGBSET(patch->color, 0.0, 0.0, 0.0); patch->omit = TRUE; patch->flags = 0; return patch; } /* Creates a patch structure for a patch with given vertices. */ PATCH *PatchCreate(int nrvertices, VERTEX *v1, VERTEX *v2, VERTEX *v3, VERTEX *v4) { PATCH *patch; /* this may occur after an error parsing the input file, and some other routine * will already complain */ if (!v1 || !v2 || !v3 || (nrvertices==4 && !v4)) return (PATCH *)NULL; /* it's sad but it's true */ if (nrvertices != 3 && nrvertices != 4) { Error("PatchCreate", "Can only handle quadrilateral or triagular patches"); return (PATCH *)NULL; } patch = NEWPATCH(); nrelements++; patch->twin = (PATCH *)NULL; patch->id = patchid; patchid++; patch->surface = (SURFACE *)NULL; patch->nrvertices = nrvertices; patch->vertex[0] = v1; patch->vertex[1] = v2; patch->vertex[2] = v3; patch->vertex[3] = v4; patch->brep_data = (BREP_FACE *)NULL; /* a bounding box will be computed the first time it is needed. */ patch->bounds = (float *)NULL; /* compute normal */ if (!PatchNormal(patch, &patch->normal)) { DISPOSEPATCH(patch); nrelements--; return (PATCH *)NULL; } /* also computes the jacobian */ patch->area = PatchArea(patch); /* patch midpoint */ PatchMidpoint(patch, &patch->midpoint); /* plane constant */ patch->plane_constant = -VECTORDOTPRODUCT(patch->normal, patch->midpoint); /* plane tolerance */ patch->tolerance = PatchTolerance(patch); /* dominant part of normal */ patch->index = VectorDominantCoord(&patch->normal); /* ray tracing constants */ PatchInitRayTracing(patch); /* tell the vertices that there's a new PATCH using them */ PatchConnectVertices(patch); patch->direct_potential = 0.0; RGBSET(patch->color, 0.0, 0.0, 0.0); patch->omit = FALSE; patch->flags = 0; /* other flags */ /* if we are doing radiance computations, create radiance data for the patch */ patch->radiance_data = (Radiance && Radiance->CreatePatchData) ? Radiance->CreatePatchData(patch) : (void *)NULL; return patch; } /* disposes the memory allocated for the patch, does not remove * the pointers to the patch in the VERTEXes of the patch. */ void PatchDestroy(PATCH *patch) { if (Radiance && patch->radiance_data) if (Radiance->DestroyPatchData) Radiance->DestroyPatchData(patch); if (patch->bounds) BoundsDestroy(patch->bounds); if (patch->jacobian) JacobianDestroy(patch->jacobian); if (patch->brep_data) /* also destroys all contours, and edges if not used in * other faces as well. Not the vertices: these are * destroyed when destroying the corresponding VERTEX. */ BrepDestroyFace(patch->brep_data); if (patch->twin) patch->twin->twin = (PATCH *)NULL; DISPOSEPATCH(patch); nrelements--; } /* computes a bounding box for the patch. fills it in in 'bounds' and returns * a pointer to 'bounds'. */ float *PatchBounds(PATCH *patch, float *bounds) { int i; if (!patch->bounds) { patch->bounds = BoundsCreate(); BoundsInit(patch->bounds); for (i=0; inrvertices; i++) BoundsEnlargePoint(patch->bounds, patch->vertex[i]->point); } BoundsCopy(patch->bounds, bounds); return bounds; } static int nrofsamples(PATCH *patch) { int nrsamples = 1; if (BsdfIsTextured(patch->surface->material->bsdf)) { if (patch->vertex[0]->texCoord == patch->vertex[1]->texCoord && patch->vertex[0]->texCoord == patch->vertex[2]->texCoord && (patch->nrvertices == 3 || patch->vertex[0]->texCoord == patch->vertex[3]->texCoord) && patch->vertex[0]->texCoord != NULL) /* all vertices have same texture coordinates (important special case) */ nrsamples = 1; else nrsamples = 100; } return nrsamples; } COLOR PatchAverageNormalAlbedo(PATCH *patch, BSDFFLAGS components) { int i, nrsamples; COLOR albedo; HITREC hit; InitHit(&hit, patch, NULL, &patch->midpoint, &patch->normal, patch->surface->material, 0.); nrsamples = nrofsamples(patch); COLORCLEAR(albedo); for (i=0; isurface->material->bsdf, &hit, &patch->normal, components); COLORADD(albedo, sample, albedo); } COLORSCALEINVERSE((float)nrsamples, albedo, albedo); return albedo; } COLOR PatchAverageEmittance(PATCH *patch, XXDFFLAGS components) { int i, nrsamples; COLOR emittance; HITREC hit; InitHit(&hit, patch, NULL, &patch->midpoint, &patch->normal, patch->surface->material, 0.); nrsamples = nrofsamples(patch); COLORCLEAR(emittance); for (i=0; isurface->material->edf, &hit, components); COLORADD(emittance, sample, emittance); } COLORSCALEINVERSE((float)nrsamples, emittance, emittance); return emittance; } void PatchPrintID(FILE *out, PATCH *patch) { fprintf(out, "%d ", patch->id); } void PatchPrint(FILE *out, PATCH *patch) { int i; COLOR Rd, Ed; fprintf(out, "Patch id %d:\n", patch->id); fprintf(out, "%d vertices:\n", patch->nrvertices); for (i=0; inrvertices; i++) VertexPrint(out, patch->vertex[i]); fprintf(out, "\n"); fprintf(out, "midpoint = "); VectorPrint(out, patch->midpoint); fprintf(out, ", normal = "); VectorPrint(out, patch->normal); fprintf(out, ", plane constant = %g, tolerance = %g\narea = %g, ", patch->plane_constant, patch->tolerance, patch->area); if (patch->jacobian) fprintf(out, "Jacobian: %g %+g*u %+g*v \n", patch->jacobian->A, patch->jacobian->B, patch->jacobian->C); else fprintf(out, "No explicitely stored jacobian\n"); fprintf(out, "sided = %d, material = '%s'\n", patch->surface->material->sided, patch->surface->material->name ? patch->surface->material->name : "(default)"); COLORCLEAR(Rd); if (patch->surface->material->bsdf) Rd = PatchAverageNormalAlbedo(patch, BRDF_DIFFUSE_COMPONENT); COLORCLEAR(Ed); if (patch->surface->material->edf) { Ed = PatchAverageEmittance(patch, DIFFUSE_COMPONENT); } fprintf(out, ", reflectance = "); ColorPrint(out, Rd); fprintf(out, ", self-emitted luminosity = %g\n", ColorLuminance(Ed)); fprintf(out, "color: "); RGBPrint(out, patch->color); fprintf(out, "\n"); fprintf(out, "directly received potential: %g\n", patch->direct_potential); fprintf(out, "flags: %s\n", PATCH_IS_VISIBLE(patch) ? "PATCH_IS_VISIBLE" : ""); if (Radiance) { if (patch->radiance_data && Radiance->PrintPatchData) { fprintf(out, "Radiance data:\n"); Radiance->PrintPatchData(out, patch); } else fprintf(out, "No radiance data\n"); } #ifndef LOWMEM_INTERSECT fprintf(out, "RayTracing data:\n"); for (i=0; inrvertices; i++) { fprintf(stderr, "edge %d: slope = %g, dist = %g, flags = %02x\n", i, patch->eslope[i], patch->edist[i], patch->eflags[i]); } #endif } /* Specify up to MAX_EXCLUDED_PATCHES patches not to test for intersections with. * Used to avoid selfintersections when raytracing. First argument is number of * patches to include. Next arguments are pointers to the patches to exclude. * Call with first parameter == 0 to clear the list. */ #define MAX_EXCLUDED_PATCHES 4 static PATCH *excludedPatches[MAX_EXCLUDED_PATCHES] = {NULL, NULL, NULL, NULL}; void PatchDontIntersect(int n, ...) { va_list ap; int i; if (n>MAX_EXCLUDED_PATCHES) { Fatal(-1, "PatchDontIntersect", "Too many patches to exclude from intersection tests (maximum is %d)", MAX_EXCLUDED_PATCHES); return; } va_start(ap, n); for (i=0; inrvertices; i++) { if (!patch->vertex[i]->normal) break; } return i>=patch->nrvertices; } static VECTOR GetInterpolatedNormalAtUV(PATCH *patch, double u, double v) { VECTOR normal, *v1, *v2, *v3, *v4; v1 = patch->vertex[0]->normal; v2 = patch->vertex[1]->normal; v3 = patch->vertex[2]->normal; switch (patch->nrvertices) { case 3: PINT(*v1, *v2, *v3, u, v, normal); break; case 4: v4 = patch->vertex[3]->normal; PINQ(*v1, *v2, *v3, *v4, u, v, normal); break; default: Fatal(-1, "PatchNormalAtUV", "Invalid number of vertices %d", patch->nrvertices); } VECTORNORMALIZE(normal); return normal; } /* computes interpolated (= shading) normal at the point with given parameters * on the patch */ VECTOR PatchInterpolatedNormalAtUV(PATCH *patch, double u, double v) { if (!AllVerticesHaveANormal(patch)) return patch->normal; return GetInterpolatedNormalAtUV(patch, u, v); } /* computes interpolated (= shading) normal at the given point on the patch */ VECTOR PatchInterpolatedNormalAtPoint(PATCH *patch, POINT *point) { double u, v; if (!AllVerticesHaveANormal(patch)) return patch->normal; PatchUV(patch, point, &u, &v); return GetInterpolatedNormalAtUV(patch, u, v); } /* computes shading frame at the given uv on the patch */ void PatchInterpolatedFrameAtUV(PATCH *patch, double u, double v, VECTOR *X, VECTOR *Y, VECTOR *Z) { *Z = PatchInterpolatedNormalAtUV(patch, u, v); /* fprintf(stderr, "*Z %g %g %g, index %i\n", Z->x, Z->y, Z->z, patch->index); */ if (X && Y) { double zz = sqrt(1 - Z->z*Z->z); if(zz < EPSILON) { VECTORSET(*X, 1., 0., 0.); } else { VECTORSET(*X, Z->y/zz, -Z->x/zz, 0.); } VECTORCROSSPRODUCT(*Z, *X, *Y); /* *Y = (*Z) ^ (*X); */ } #ifdef NEVER if(!VectorFrame(Z, patch->index, X, Y)) { /* The vertex normals deviate too much from the geometric * normal, and the shading normal has become perpendicular * to the geometric normal */ static int already_printed = FALSE; if(!already_printed) { Warning("PatchInterpolatedFrameAtUV", "Large differences in vertex normals detected"); already_printed = TRUE; } VectorFrame(Z, (patch->index + 1) % 3, X, Y); } #endif } /* computes shading frame at the given point on the patch */ void PatchInterpolatedFrameAtPoint(PATCH *patch, POINT *point, VECTOR *X, VECTOR *Y, VECTOR *Z) { double u, v; if (!AllVerticesHaveANormal(patch)) u=v=0.; else PatchUV(patch, point, &u, &v); PatchInterpolatedFrameAtUV(patch, u, v, X, Y, Z); } VECTOR PatchTextureCoordAtUV(PATCH *patch, double u, double v) { VECTOR *t0, *t1, *t2, *t3; VECTOR texCoord; VECTORSET(texCoord, 0., 0., 0.); t0 = patch->vertex[0]->texCoord; t1 = patch->vertex[1]->texCoord; t2 = patch->vertex[2]->texCoord; switch (patch->nrvertices) { case 3: if (!t0 || !t1 || !t2) { VECTORSET(texCoord, u, v, 0.); } else { PINT(*t0, *t1, *t2, u, v, texCoord); } break; case 4: t3 = patch->vertex[3]->texCoord; if (!t0 || !t1 || !t2 || !t3) { VECTORSET(texCoord, u, v, 0.); } else { PINQ(*t0, *t1, *t2, *t3, u, v, texCoord); } break; default: Fatal(-1, "PatchTextureCoordAtUV", "Invalid nr of vertices %d", patch->nrvertices); } return texCoord; } #ifdef IDEBUG int idebug = 0; #endif void PatchIntersectStartDebug(void) { #ifdef IDEBUG idebug = TRUE; #endif } void PatchIntersectEndDebug(void) { #ifdef IDEBUG idebug = FALSE; #endif } /* Returns (u,v) coordinates of the point in the triangle. */ static void OldTriangleUV(PATCH *poly, VECTOR *point, DVEC2D *uv) { static PATCH *cached_poly; DVECTOR d0, d1, d2, cu, cv; if (!poly) poly = cached_poly; /* These operations should be reduced to 2D by projecting onto the most * parallel plane */ VECTORSUBTRACT(*poly->vertex[0]->point, *point, d0); VECTORSUBTRACT(*poly->vertex[1]->point, *point, d1); VECTORSUBTRACT(*poly->vertex[2]->point, *point, d2); VECTORCROSSPRODUCT(d2, d0, cu); VECTORCROSSPRODUCT(d0, d1, cv); uv->u = 0.5 * VECTORNORM(cu) / poly->area; uv->v = 0.5 * VECTORNORM(cv) / poly->area; } /* returns (u,v) parameters for the point in the polygon * ref: Eric Haines, Essential Ray-tracing algorithms, in A. Glassner, * Introduction to Ray-Tracing, p 59. * * If the symbol QUADUV_CLIPBOUNDS is defined, the resulting coordinates will be * clipped to lay within the unit interval. In that case, this routine shall only * be used for points that are known to lay inside the quadrilateral (you can use * PointInPatch() to test this.) */ #define QUADUV_CLIPBOUNDS #define DISTANCE_TO_UNIT_INTERVAL(x) ((x < 0.) ? -x : ((x > 1.) ? (x-1.) : 0.)) #define CLIP_TO_UNIT_INTERVAL(x) ((x < EPSILON) ? EPSILON : ((x > (1.-EPSILON)) ? (1.-EPSILON) : x)) static void OldQuadUV(PATCH *poly, VECTOR *point, DVEC2D *uv) { static double Du0, Du1, Du2, Dux, Duy, Dv0, Dv1, Dv2, Dvx, Dvy; static DVECTOR Na, Nb, Nc, Qux, Quy, Qvx, Qvy; static PATCH *lastpoly = (PATCH *)NULL; DVECTOR Pa, Pb, Pc, Pd; /* use the values computed last time if poly==NULL, this way we can do * some caching. Storing this data with (toplevel) patches would be more * efficient ... but would take quite some memory. */ if (!poly) poly = lastpoly; if (!poly->jacobian) { static VEC2D v[4]; static double du2, dv2; VEC2D p; if (1 || poly != lastpoly) { switch (poly->index) { case XNORMAL: v[0].u = poly->vertex[0]->point->y; v[0].v = poly->vertex[0]->point->z; v[1].u = poly->vertex[1]->point->y; v[1].v = poly->vertex[1]->point->z; v[3].u = poly->vertex[3]->point->y; v[3].v = poly->vertex[3]->point->z; break; case YNORMAL: v[0].u = poly->vertex[0]->point->x; v[0].v = poly->vertex[0]->point->z; v[1].u = poly->vertex[1]->point->x; v[1].v = poly->vertex[1]->point->z; v[3].u = poly->vertex[3]->point->x; v[3].v = poly->vertex[3]->point->z; break; case ZNORMAL: v[0].u = poly->vertex[0]->point->x; v[0].v = poly->vertex[0]->point->y; v[1].u = poly->vertex[1]->point->x; v[1].v = poly->vertex[1]->point->y; v[3].u = poly->vertex[3]->point->x; v[3].v = poly->vertex[3]->point->y; break; } /* printf(" (0) v[0].u = %f\n", v[0].u); */ v[1].u -= v[0].u; v[3].u -= v[0].u; v[1].v -= v[0].v; v[3].v -= v[0].v; du2 = (v[1].v * v[3].u - v[1].u * v[3].v); /*dv2 = (v[3].v * v[1].u - v[3].u * v[1].v);*/ dv2 = -du2; if (du2 < EPSILON && du2 > -EPSILON) printf("du2 == 0 !\n"); if (dv2 < EPSILON && dv2 > -EPSILON) printf("dv2 == 0 !\n"); lastpoly = poly; } /* printf(" (1) v[0].u = %f\n", v[0].u); */ VECTORPROJECT(p, *point, poly->index); /* printf(" (2) v[0].u = %f id=%ld\n", v[0].u, lastpoly->id); */ p.u -= v[0].u; p.v -= v[0].v; uv->u = uv->v = 0.0; if (du2 > EPSILON || du2 < -EPSILON) uv->u = (p.v * v[3].u - p.u * v[3].v) / du2; if (dv2 > EPSILON || dv2 < -EPSILON) uv->v = (p.v * v[1].u - p.u * v[1].v) / dv2; #ifdef QUADUV_CLIPBOUNDS uv->u = CLIP_TO_UNIT_INTERVAL(uv->u); uv->v = CLIP_TO_UNIT_INTERVAL(uv->v); #endif return; } /* These operations should be reduced to 2D by projecting onto the most * parallel plane */ if (poly != lastpoly) { VECTORCOPY(*poly->vertex[0]->point, Pd); VECTORSUBTRACT(*poly->vertex[1]->point, *poly->vertex[0]->point, Pb); VECTORSUBTRACT(*poly->vertex[3]->point, *poly->vertex[0]->point, Pc); VECTORSUBTRACT(*poly->vertex[2]->point, *poly->vertex[3]->point, Pa); VECTORSUBTRACT(Pa, Pb, Pa); VECTORCROSSPRODUCT(Pa, poly->normal, Na); VECTORCROSSPRODUCT(Pb, poly->normal, Nb); VECTORCROSSPRODUCT(Pc, poly->normal, Nc); Du0 = VECTORDOTPRODUCT(Nc, Pd); Du1 = VECTORDOTPRODUCT(Na, Pd) + VECTORDOTPRODUCT(Nc, Pb); Du2 = VECTORDOTPRODUCT(Na, Pb); if (fabs(Du2) > EPSILON) { /* 'u' axes are not parallel */ VECTORSCALEINVERSE(2.*Du2, Na, Qux); Dux = - Du1 / (2.*Du2); VECTORSCALEINVERSE(-Du2, Nc, Quy); Duy = Du0 / Du2; } Dv0 = VECTORDOTPRODUCT(Nb, Pd); Dv1 = VECTORDOTPRODUCT(Na, Pd) + VECTORDOTPRODUCT(Nb, Pc); Dv2 = VECTORDOTPRODUCT(Na, Pc); if (fabs(Dv2) > EPSILON) { /* 'v' axes are not parallel */ VECTORSCALEINVERSE(2.*Dv2, Na, Qvx); Dvx = - Dv1 / (2.*Dv2); VECTORSCALEINVERSE(-Dv2, Nb, Qvy); Dvy = Dv0 / Dv2; } lastpoly = poly; } if (fabs(Du2) < EPSILON) { /* 'u' axes are parallel */ double B, C; B = Du1 - VECTORDOTPRODUCT(Na, *point); if (fabs(B) < EPSILON) uv->u = 0.0; /* vertex of a triangle */ else { C = VECTORDOTPRODUCT(Nc, *point) - Du0; uv->u = C/B; } } else { /* 'u' axes are not parallel */ double Ka, Kb, D; Ka = Dux + VECTORDOTPRODUCT(Qux, *point); Kb = Duy + VECTORDOTPRODUCT(Quy, *point); D = sqrt(Ka*Ka - Kb); uv->u = (Ka > D) ? (Ka - D) : (Ka + D); /* take positive solution */ if (uv->u > 1.) { /* 'u' out of range */ double u1 = Ka - D, u2 = Ka + D; double d1 = DISTANCE_TO_UNIT_INTERVAL(u1); double d2 = DISTANCE_TO_UNIT_INTERVAL(u2); uv->u = (d1 < d2) ? u1 : u2; /* choose closest to polygon */ } } if (fabs(Dv2) < EPSILON) { /* 'v' axes are parallel */ double B, C; B = Dv1 - VECTORDOTPRODUCT(Na, *point); if (fabs(B) < EPSILON) uv->v = 0.0; /* vertex of a triangle */ else { C = VECTORDOTPRODUCT(Nb, *point) - Dv0; uv->v = C/B; } } else { /* 'v' axes are not parallel */ double Ka, Kb, D; Ka = Dvx + VECTORDOTPRODUCT(Qvx, *point); Kb = Dvy + VECTORDOTPRODUCT(Qvy, *point); D = sqrt(Ka*Ka - Kb); uv->v = (Ka > D) ? (Ka - D) : (Ka + D); /* take positive solution */ if (uv->v > 1.) { /* 'v' out of range */ double v1 = Ka - D, v2 = Ka + D; double d1 = DISTANCE_TO_UNIT_INTERVAL(v1); double d2 = DISTANCE_TO_UNIT_INTERVAL(v2); uv->v = (d1 < d2) ? v1 : v2; /* choose closest to polygon */ } } #ifdef QUADUV_CLIPBOUNDS uv->u = CLIP_TO_UNIT_INTERVAL(uv->u); uv->v = CLIP_TO_UNIT_INTERVAL(uv->v); #endif } /* Now comes same as above, but more efficient .... - PhB Oct 2000 */ #define REAL double /* yes, float or double makes a difference!!! */ #define V2Set(V, a, b) { (V).u = (a) ; (V).v = (b) ; } #define V2Sub(p, q, r) { (r).u = (p).u - (q).u; (r).v = (p).v - (q).v; } #define V2Add(p, q, r) { (r).u = (p).u + (q).u; (r).v = (p).v + (q).v; } #define V2Negate(p) { (p).u = -(p).u; (p).v = -(p).v; } #define DETERMINANT(A,B) (((REAL)(A).u * (REAL)(B).v - (REAL)(A).v * (REAL)(B).u)) #define ABS(A) ((A)<0. ? -(A) : (A)) /* Returns (u,v) coordinates of the point in the triangle. */ /* Didier Badouel, Graphics Gems I, p390 */ static int TriangleUV(PATCH *patch, VECTOR *point, DVEC2D *uv) { static PATCH *cachedpatch = (PATCH *)NULL; double u0, v0; REAL alpha=-1., beta=1.; VERTEX **v; DVEC2D p0, p1, p2; if (!patch) patch = cachedpatch; cachedpatch = patch; /* project to 2D */ v = patch->vertex; switch (patch->index) { case XNORMAL: u0 = (*v)->point->y; v0 = (*v)->point->z; V2Set(p0, point->y - u0, point->z - v0); v++; V2Set(p1, (*v)->point->y - u0, (*v)->point->z - v0); v++; V2Set(p2, (*v)->point->y - u0, (*v)->point->z - v0); break; case YNORMAL: u0 = (*v)->point->x; v0 = (*v)->point->z; V2Set(p0, point->x - u0, point->z - v0); v++; V2Set(p1, (*v)->point->x - u0, (*v)->point->z - v0); v++; V2Set(p2, (*v)->point->x - u0, (*v)->point->z - v0); break; case ZNORMAL: u0 = (*v)->point->x; v0 = (*v)->point->y; V2Set(p0, point->x - u0, point->y - v0); v++; V2Set(p1, (*v)->point->x - u0, (*v)->point->y - v0); v++; V2Set(p2, (*v)->point->x - u0, (*v)->point->y - v0); break; } if (p1.u < -EPSILON || p1.u > EPSILON) { /* p1.u non zero */ beta = (p0.v*p1.u - p0.u*p1.v) / (p2.v*p1.u - p2.u*p1.v); if (beta >= 0. && beta <= 1.) alpha = (p0.u - beta * p2.u) / p1.u; else { #ifdef IDEBUG if (idebug) { fprintf(stderr, "%s %d: point-in-polygon test fails (alpha=%f, beta=%f)\n", __FILE__, __LINE__, alpha, beta); } #endif return FALSE; } } else { beta = p0.u / p2.u; if (beta >= 0. && beta <= 1.) alpha = (p0.v - beta * p2.v) / p1.v; else { #ifdef IDEBUG if (idebug) { fprintf(stderr, "%s %d: point-in-polygon test fails (alpha=%f, beta=%f)\n", __FILE__, __LINE__, alpha, beta); } #endif return FALSE; } } uv->u = alpha; uv->v = beta; if (alpha < 0. || (alpha + beta) > 1.) { #ifdef IDEBUG if (idebug) { fprintf(stderr, "%s %d: point-in-polygon test fails (alpha=%f, beta=%f)\n", __FILE__, __LINE__, alpha, beta); } #endif return FALSE; } return TRUE; } /* Christophe Schlick and Gilles Subrenat (15 May 1994) * "Ray Intersection of Tessellated Surfaces : Quadrangles versus Triangles" * in Graphics Gems V (edited by A. Paeth), Academic Press */ static int QuadUV(PATCH *patch, VECTOR *point, DVEC2D *uv) { static PATCH *cachedpatch = (PATCH *)NULL; VERTEX **p; DVEC2D A, B, C, D; /* Projected vertices */ DVEC2D M; /* Projected intersection point */ DVEC2D AB, BC, CD, AD, AM, AE; /* AE = DC - AB = DA - CB */ REAL u=-1., v=-1.; /* Parametric coordinates */ REAL a, b, c, SqrtDelta; /* for the quadratic equation */ DVEC2D Vector; /* temporary 2D-vector */ int IsInside = FALSE; if (!patch) patch = cachedpatch; cachedpatch = patch; /* ** Projection on the plane that is most parallel to the facet */ p = patch->vertex; switch (patch->index) { case XNORMAL: V2Set(A, (*p)->point->y, (*p)->point->z); p++; V2Set(B, (*p)->point->y, (*p)->point->z); p++; V2Set(C, (*p)->point->y, (*p)->point->z); p++; V2Set(D, (*p)->point->y, (*p)->point->z); V2Set(M, point->y, point->z); break; case YNORMAL: V2Set(A, (*p)->point->x, (*p)->point->z); p++; V2Set(B, (*p)->point->x, (*p)->point->z); p++; V2Set(C, (*p)->point->x, (*p)->point->z); p++; V2Set(D, (*p)->point->x, (*p)->point->z); V2Set(M, point->x, point->z); break; case ZNORMAL: V2Set(A, (*p)->point->x, (*p)->point->y); p++; V2Set(B, (*p)->point->x, (*p)->point->y); p++; V2Set(C, (*p)->point->x, (*p)->point->y); p++; V2Set(D, (*p)->point->x, (*p)->point->y); V2Set(M, point->x, point->y); break; } V2Sub (B, A, AB); V2Sub (C, B, BC); V2Sub (D, C, CD); V2Sub (D, A, AD); V2Add (CD, AB, AE); V2Negate (AE); V2Sub (M, A, AM); if (fabs(DETERMINANT(AB, CD)) < EPSILON) /* case AB CD */ { V2Sub (AB, CD, Vector); v = DETERMINANT(AM, Vector) / DETERMINANT(AD, Vector); if ((v >= 0.0) && (v <= 1.0)) { b = DETERMINANT(AB, AD) - DETERMINANT(AM, AE); c = DETERMINANT (AM, AD); u = ABS(b) < EPSILON ? -1 : c/b; IsInside = ((u >= 0.0) && (u <= 1.0)); } #ifdef IDEBUG if (idebug && !IsInside) { fprintf(stderr, "%s %d: point-in-polygon test fails (u=%f, v=%f)\n", __FILE__, __LINE__, u, v); } #endif } else if (fabs(DETERMINANT(BC, AD)) < EPSILON) /* case AD BC */ { V2Add (AD, BC, Vector); u = DETERMINANT(AM, Vector) / DETERMINANT(AB, Vector); if ((u >= 0.0) && (u <= 1.0)) { b = DETERMINANT(AD, AB) - DETERMINANT(AM, AE); c = DETERMINANT (AM, AB); v = ABS(b) < EPSILON ? -1 : c/b; IsInside = ((v >= 0.0) && (v <= 1.0)); } #ifdef IDEBUG if (idebug && !IsInside) { fprintf(stderr, "%s %d: point-in-polygon test fails (u=%f, v=%f)\n", __FILE__, __LINE__, u, v); } #endif } else /* general case */ { a = DETERMINANT(AB, AE); c = - DETERMINANT (AM,AD); b = DETERMINANT(AB, AD) - DETERMINANT(AM, AE); a = -0.5/a; b *= a; c *= (a + a); SqrtDelta = b*b + c; if (SqrtDelta >= 0.0) { SqrtDelta = sqrt(SqrtDelta); u = b - SqrtDelta; if ((u < 0.0) || (u > 1.0)) /* to choose u between 0 and 1 */ u = b + SqrtDelta; if ((u >= 0.0) && (u <= 1.0)) { v = AD.u + u * AE.u; if (ABS(v) < EPSILON) v = (AM.v - u * AB.v) / (AD.v + u * AE.v); else v = (AM.u - u * AB.u) / v; IsInside = ((v >= 0.0) && (v <= 1.0)); } #ifdef IDEBUG if (idebug && !IsInside) { fprintf(stderr, "%s %d: point-in-polygon test fails (u=%f, v=%f)\n", __FILE__, __LINE__, u, v); } #endif } else { u = v = -1.; #ifdef IDEBUG if (idebug && !IsInside) { fprintf(stderr, "%s %d: point-in-polygon test fails (u=%f, v=%f)\n", __FILE__, __LINE__, u, v); } #endif } } uv->u = u; uv->v = v; #ifdef QUADUV_CLIPBOUNDS uv->u = CLIP_TO_UNIT_INTERVAL(uv->u); uv->v = CLIP_TO_UNIT_INTERVAL(uv->v); #endif return IsInside; } #ifndef LOWMEM_INTERSECT /* returns TRUE if the point is inside the patch and FALSE if not. The point * is supposed to lay in the plane of the patch. */ static int FastPointInPatch(POINT *point, PATCH *patch) { float u=0., v=0., t; float *s = patch->eslope, *d = patch->edist; unsigned char *flags = patch->eflags; switch (patch->index) { case XNORMAL: u = point->y - patch->midpoint.y; v = point->z - patch->midpoint.z; break; case YNORMAL: u = point->x - patch->midpoint.x; v = point->z - patch->midpoint.z; break; case ZNORMAL: u = point->x - patch->midpoint.x; v = point->y - patch->midpoint.y; break; } t = ((*flags) & U_DOMINANT) ? (u + (*s) * v) : ((*s) * u + v); if (((*flags) & REVERSE_DIST) ? (t < (*d)) : (t > (*d))) return FALSE; t = ((*++flags) & U_DOMINANT) ? (u + (*++s) * v) : ((*++s) * u + v); if (((*flags) & REVERSE_DIST) ? (t < (*++d)) : (t > (*++d))) return FALSE; t = ((*++flags) & U_DOMINANT) ? (u + (*++s) * v) : ((*++s) * u + v); if (((*flags) & REVERSE_DIST) ? (t < (*++d)) : (t > (*++d))) return FALSE; if (patch->nrvertices <= 3) return TRUE; t = ((*++flags) & U_DOMINANT) ? (u + (*++s) * v) : ((*++s) * u + v); if (((*flags) & REVERSE_DIST) ? (t < (*++d)) : (t > (*++d))) return FALSE; return TRUE; } #endif /*LOWMEM_INTERSECT not defined*/ #ifdef CHECK_INTERSECT /* compares fast and low-memory ray-polygon intersection tests */ static int HitInPatch(HITREC *hit, PATCH *patch) { int inside, oldinside, problem = FALSE; DVEC2D olduv; if (patch->nrvertices == 3) { inside = TriangleUV(patch, &hit->point, &hit->uv); #ifdef NEVER if (inside) { OldTriangleUV(patch, &hit->point, &olduv); if (!FLOATEQUAL(hit->uv.u, olduv.u, 1e-4) || !FLOATEQUAL(hit->uv.v, olduv.v, 1e-4)) { fprintf(stderr, "%s %d: WARNING: hit->uv.u = %f != olduv.u = %f, hit->uv.v = %f != olduv.v = %f\n", __FILE__, __LINE__, hit->uv.u, olduv.u, hit->uv.v, olduv.v); problem = TRUE; } } #endif } else { inside = QuadUV(patch, &hit->point, &hit->uv); #ifdef NEVER if (inside) { OldQuadUV(patch, &hit->point, &olduv); if (!FLOATEQUAL(hit->uv.u, olduv.u, 1e-4) || !FLOATEQUAL(hit->uv.v, olduv.v, 1e-4)) { fprintf(stderr, "%s %d: WARNING: hit->uv.u = %f != olduv.u = %f, hit->uv.v = %f != olduv.v = %f\n", __FILE__, __LINE__, hit->uv.u, olduv.u, hit->uv.v, olduv.v); problem = TRUE; } } #endif } oldinside = FastPointInPatch(&hit->point, patch); if (inside != oldinside) { fprintf(stderr, "%s %d: WARNING: inside = %d != oldinside =%d\n", __FILE__, __LINE__, inside, oldinside); problem = TRUE; } if (problem) { VECTOR newpoint, oldpoint; fprintf(stderr, "point = "); VectorPrint(stderr, hit->point); fprintf(stderr, "\n"); fprintf(stderr, "uv = %f,%f\n", hit->uv.u, hit->uv.v); PatchPoint(patch, hit->uv.u, hit->uv.v, &newpoint); fprintf(stderr, "new point = "); VectorPrint(stderr, newpoint); fprintf(stderr, "\n"); /* PatchPoint(patch, olduv.u, olduv.v, &oldpoint); fprintf(stderr, "old point = "); VectorPrint(stderr, oldpoint); fprintf(stderr, "\n"); */ fprintf(stderr, "patch id = %d ", patch->id); fprintf(stderr, "\n"); } return inside; } #else /* CHECK_INTERSECT not defined */ #ifndef LOWMEM_INTERSECT static int HitInPatch(HITREC *hit, PATCH *patch) { return FastPointInPatch(&hit->point, patch); } #else /* LOWMEM_INTERSECT defined: use Badouels and Schlicks method from * graphics gems: slower, but consumes less storage and computes * (u,v) parameters as a side result */ static int HitInPatch(HITREC *hit, PATCH *patch) { hit->flags |= HIT_UV; /* uv parameters computed as a side result */ return (patch->nrvertices == 3) ? TriangleUV(patch, &hit->point, &hit->uv) : QuadUV(patch, &hit->point, &hit->uv); } #endif /* LOWMEM_INTERSECT */ #endif /* CHECK_INTERSECT */ /* ray-patch intersection test, for computing formfactors, creating raycast * images ... Returns NULL if the RAY doesn't hit the patch. Fills in the * 'the_hit' hit record if there is a new hit and returns a pointer to it. * Fills in the distance to the patch in maxdist if the patch * is hit. Intersections closer than mindist or further than *maxdist are * ignored. The hitflags determine what information to return about an * intersection and whether or not front/back facing patches are to be * considered and are described in ray.h. */ HITREC *PatchIntersect(PATCH *patch, RAY *ray, float mindist, float *maxdist, int hitflags, HITREC *hitstore) { HITREC hit; float dist; #ifdef IDEBUG if (idebug) { fprintf(stderr, "=====> PatchIntersect:\n"); fprintf(stderr, "%s %d: mindist = %f, *maxdist = %f, patch = ", __FILE__, __LINE__, mindist, *maxdist); PatchPrint(stderr, patch); } #endif if (IsExcludedPatch(patch)) { #ifdef IDEBUG if (idebug) fprintf(stderr, "%s %d: excluded patch -> no intersection\n", __FILE__, __LINE__); #endif return (HITREC *)NULL; } if ((dist = VECTORDOTPRODUCT(patch->normal, ray->dir)) > EPSILON) { /* backfacing patch */ if (!(hitflags & HIT_BACK)) { #ifdef IDEBUG if (idebug) fprintf(stderr, "%s %d: wrong side -> no intersection\n", __FILE__, __LINE__); #endif return (HITREC *)NULL; } else hit.flags = HIT_BACK; } else if (dist < -EPSILON){ /* frontfacing patch */ if (!(hitflags & HIT_FRONT)) { #ifdef IDEBUG if (idebug) fprintf(stderr, "%s %d: wrong side -> no intersection\n", __FILE__, __LINE__); #endif return (HITREC *)NULL; } else hit.flags = HIT_FRONT; } else { /* ray is parallel with the plane of the patch */ #ifdef IDEBUG if (idebug) fprintf(stderr, "%s %d: parallel ray -> no intersection\n", __FILE__, __LINE__); #endif return (HITREC *)NULL; } dist = - (VECTORDOTPRODUCT(patch->normal, ray->pos) + patch->plane_constant) / dist; #ifdef IDEBUG if (idebug) fprintf(stderr, "%s %d: dist = %f\n", __FILE__, __LINE__, dist); #endif if (dist > *maxdist || dist < mindist) { /* intersection too far or too close */ #ifdef IDEBUG if (idebug) fprintf(stderr, "%s %d: too far or too close -> no intersection\n", __FILE__, __LINE__); #endif return (HITREC *)NULL; } /* intersection point of ray with plane of patch */ hit.dist = dist; VECTORADDSCALED(ray->pos, dist, ray->dir, hit.point); #ifdef IDEBUG if (idebug) { fprintf(stderr, "%s %d: intersection point = ", __FILE__, __LINE__); fprintf(stderr, "%f %f %f", hit.point.x, hit.point.y, hit.point.z); fprintf(stderr, "\n"); } #endif /* test whether it lays inside or outside the patch */ if (HitInPatch(&hit, patch)) { hit.geom = (GEOM *)NULL; /* we don't know it */ hit.patch = patch; hit.material = patch->surface->material; hit.gnormal = patch->normal; hit.flags |= HIT_PATCH | HIT_POINT | HIT_MATERIAL | HIT_GNORMAL | HIT_DIST; if (hitflags & HIT_UV) { if (!(hit.flags & HIT_UV)) { PatchUV(hit.patch, &hit.point, &hit.uv.u, &hit.uv.v); hit.flags &= HIT_UV; } } *hitstore = hit; *maxdist = dist; #ifdef IDEBUG if (idebug) { fprintf(stderr, "%s %d: -> intersection, new maxdist = %f\n", __FILE__, __LINE__, *maxdist); } #endif return hitstore; } #ifdef IDEBUG if (idebug) fprintf(stderr, "%s %d: point-in-polygon test fails -> no intersection\n", __FILE__, __LINE__); #endif return (HITREC *)NULL; } /* Converts bilinear coordinates into uniform (area preserving) * coordinates for a polygon with given jacobian. Uniform coordinates * are such that the area left of the line of points with u-coordinate 'u', * will be u.A and the area under the line of points with v-coordinate 'v' * will be v.A. */ void BilinearToUniform(PATCH *patch, double *u, double *v) { double a=patch->jacobian->A, b=patch->jacobian->B, c=patch->jacobian->C; *u = ((a + 0.5*c) + 0.5*b*(*u)) * (*u) / patch->area; *v = ((a + 0.5*b) + 0.5*c*(*v)) * (*v) / patch->area; } /* Looks for solution of the quadratic equation A.u^2 + B.u + C = 0 * in the interval [0,1]. There must be exactly one such solution. * Returns TRUE if one such solution is found, or FALSE if the equation * has no real solutions, both solutions are in the interval [0,1] or * none of them is. In case of problems, a best guess solution * is returned. Problems seem to be due to numerical inaccuracy. */ static int SolveQuadraticUnitInterval(double A, double B, double C, double *x) { double D = B*B - 4.*A*C, x1, x2; #define TOLERANCE 1e-5 if (A-TOLERANCE) { /* degenerate case, solve B*x + C = 0 */ x1 = -1.; x2 = -C / B; } else { if (D < -TOLERANCE*TOLERANCE) { *x = -B / (2. * A); Error(NULL, "Bilinear->Uniform mapping has negative discriminant D = %g.\nTaking 0 as discriminant and %g as solution.", D, *x); return FALSE; } D = D > TOLERANCE*TOLERANCE ? sqrt(D) : 0.; A = 1. / (2. * A); x1 = (-B + D) * A; x2 = (-B - D) * A; if (x1 > -TOLERANCE && x1 < 1.+TOLERANCE) { *x = x1; if (x2 > -TOLERANCE && x2 < 1.+TOLERANCE) { /* Error(NULL, "Bilinear->Uniform mapping ambiguous: x1=%g, x2=%g, taking %g as solution", x1, x2, x1); */ return FALSE; } else return TRUE; } } if (x2 > -TOLERANCE && x2 < 1.+TOLERANCE) { *x = x2; return TRUE; } else { double d; /* may happen due to numerical errors */ /* chose the root closest to [0,1] */ if (x1>1.) d = x1-1.; else /*x1<0.*/ d = -x1; *x = x1; if (x2>1.) { if (x2-1. < d) *x = x2; } else { if (0.-x2 < d) *x = x2; } /* clip it to [0,1] */ if (*x < 0.) *x=0.; if (*x > 1.) *x=1.; /* Error(NULL, "Bilinear->Uniform mapping has no valid solution: x1=%g, x2=%g, taking %g as solution", x1, x2, *x); */ return FALSE; } /* return TRUE; */ } /* Converts uniform to bilinear coordinates. */ void UniformToBilinear(PATCH *patch, double *u, double *v) { double a=patch->jacobian->A, b=patch->jacobian->B, c=patch->jacobian->C, A, B, C; A = 0.5*b/patch->area; B = (a+0.5*c)/patch->area; C = -(*u); if (!SolveQuadraticUnitInterval(A, B, C, u)) { /* Error(NULL, "Tried to solve %g*u^2 + %g*u = %g for patch %d", A, B, -C, patch->id); fprintf(stderr, "Jacobian: %g + %g*u + %g*v\n", a, b, c); */ } A = 0.5*c/patch->area; B = (a+0.5*b)/patch->area; C = -(*v); if (!SolveQuadraticUnitInterval(A, B, C, v)) { /* Error(NULL, "Tried to solve %g*v^2 + %g*v = %g for patch %d", A, B, -C, patch->id); fprintf(stderr, "Jacobian: %g + %g*u + %g*v\n", a, b, c); */ } } /* Given the parameter (u,v) of a point on the patch, this routine * computes the 3D space coordinates of the same point, using barycentric * mapping for a triangle and bilinear mapping for a quadrilateral. * u and v should be numbers in the range [0,1]. If u+v>1 for a triangle, * (1-u) and (1-v) are used instead. */ VECTOR *PatchPoint(PATCH *patch, double u, double v, VECTOR *point) { VECTOR *v1, *v2, *v3, *v4; if(IsPatchVirtual(patch)) return(NULL); v1 = patch->vertex[0]->point; v2 = patch->vertex[1]->point; v3 = patch->vertex[2]->point; if (patch->nrvertices == 3) { if (u+v > 1.) { u=1.-u; v=1.-v; /* Warning("PatchPoint", "(u,v) outside unit triangle"); */ } PINT(*v1, *v2, *v3, u, v, *point); } else if (patch->nrvertices == 4) { v4 = patch->vertex[3]->point; PINQ(*v1, *v2, *v3, *v4, u, v, *point); } else Fatal(4, "PatchPoint", "Can only handle triangular or quadrilateral patches"); return point; } /* Like above, except that always a uniform mapping is used (one that * preserves area, with this mapping you'll have more points in "stretched" * regions of an irregular quadrilateral, irregular quadrilaterals are the * only onces for which this routine will yield other points than the above * routine). */ VECTOR *PatchUniformPoint(PATCH *patch, double u, double v, VECTOR *point) { if (patch->jacobian) UniformToBilinear(patch, &u, &v); return PatchPoint(patch, u, v, point); } /* computes (u,v) parameters of the point on the patch (barycentric or bilinear * parametrisation). Returns TRUE if the point is inside the patch and FALSE if * not. * WARNING: The (u,v) coordinates are correctly computed only for points inside * the patch. For points outside, they can be garbage!!! */ int PatchUV(PATCH *poly, VECTOR *point, double *u, double *v) { static PATCH *cached; PATCH *thepoly; DVEC2D uv; int inside = FALSE; thepoly = poly ? poly : cached; cached = thepoly; switch (thepoly->nrvertices) { case 3: inside = TriangleUV(thepoly, point, &uv); break; case 4: inside = QuadUV(thepoly, point, &uv); break; default: Fatal(3, "PatchUV", "Can only handle triangular or quadrilateral patches"); } *u = uv.u; *v = uv.v; return inside; } /* Like above, but returns uniform coordinates. */ int PatchUniformUV(PATCH *poly, VECTOR *point, double *u, double *v) { int inside = PatchUV(poly, point, u, v); if (poly->jacobian) BilinearToUniform(poly, u, v); return inside; } #ifdef RECORD_MONITORED void PatchCloseAllRecordFiles(PATCH *patch) { int i; for(i = 0; i < MAX_RECORD_FILES; i++) { if(patch->monitor_file[i] != NULL) { fclose(patch->monitor_file[i]); patch->monitor_file[i] = NULL; } } } #endif