Tachyon (current)  Current Main Branch
triangle.c
Go to the documentation of this file.
1 /*
2  * triangle.c - This file contains the functions for dealing with triangles.
3  *
4  * (C) Copyright 1994-2022 John E. Stone
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * $Id: triangle.c,v 1.41 2022/02/18 17:55:28 johns Exp $
8  *
9  */
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <math.h>
15 
16 #define TACHYON_INTERNAL 1
17 #include "tachyon.h"
18 #include "vector.h"
19 #include "macros.h"
20 #include "intersect.h"
21 #include "util.h"
22 
23 #define TRIANGLE_PRIVATE
24 #include "triangle.h"
25 
26 static object_methods tri_methods = {
27  (void (*)(const void *, void *))(tri_intersect),
28  (void (*)(const void *, const void *, const void *, void *))(tri_normal),
29  tri_bbox,
30  free
31 };
32 
33 static object_methods stri_methods = {
34  (void (*)(const void *, void *))(tri_intersect),
35  (void (*)(const void *, const void *, const void *, void *))(stri_normal),
36  tri_bbox,
37  free
38 };
39 
40 static object_methods stri_methods_reverse = {
41  (void (*)(const void *, void *))(tri_intersect),
42  (void (*)(const void *, const void *, const void *, void *))(stri_normal_reverse),
43  tri_bbox,
44  free
45 };
46 
47 static object_methods stri_methods_guess = {
48  (void (*)(const void *, void *))(tri_intersect),
49  (void (*)(const void *, const void *, const void *, void *))(stri_normal_guess),
50  tri_bbox,
51  free
52 };
53 
54 object * newtri(void * tex, vector v0, vector v1, vector v2) {
55  tri * t;
56  vector edge1, edge2, edge3;
57 
58  VSub(&v1, &v0, &edge1);
59  VSub(&v2, &v0, &edge2);
60  VSub(&v2, &v1, &edge3);
61 
62  /* check to see if this will be a degenerate triangle before creation */
63  if ((VLength(&edge1) >= EPSILON) &&
64  (VLength(&edge2) >= EPSILON) &&
65  (VLength(&edge3) >= EPSILON)) {
66 
67  t=(tri *) malloc(sizeof(tri));
68 
69  t->nextobj = NULL;
70  t->methods = &tri_methods;
71 
72  t->tex = tex;
73  t->v0 = v0;
74  t->edge1 = edge1;
75  t->edge2 = edge2;
76 
77  return (object *) t;
78  }
79 
80  return NULL; /* was a degenerate triangle */
81 }
82 
83 
84 object * newstri(void * tex, vector v0, vector v1, vector v2,
85  vector n0, vector n1, vector n2) {
86  stri * t;
87  vector edge1, edge2, edge3;
88 
89  VSub(&v1, &v0, &edge1);
90  VSub(&v2, &v0, &edge2);
91  VSub(&v2, &v1, &edge3);
92 
93  /* check to see if this will be a degenerate triangle before creation */
94  if ((VLength(&edge1) >= EPSILON) &&
95  (VLength(&edge2) >= EPSILON) &&
96  (VLength(&edge3) >= EPSILON)) {
97 
98  t=(stri *) malloc(sizeof(stri));
99 
100  t->nextobj = NULL;
101  t->methods = &stri_methods;
102 
103  t->tex = tex;
104  t->v0 = v0;
105  t->edge1 = edge1;
106  t->edge2 = edge2;
107  t->n0 = n0;
108  t->n1 = n1;
109  t->n2 = n2;
110 
111  return (object *) t;
112  }
113 
114  return NULL; /* was a degenerate triangle */
115 }
116 
117 
118 void stri_normal_fixup(object *otri, int mode) {
119  stri *t = (stri *) otri;
120 
121  switch (mode) {
122 /* case RT_NORMAL_FIXUP_GUESS: */
123  case 2:
124  t->methods = &stri_methods_guess;
125  break;
126 
127 /* case RT_NORMAL_FIXUP_FLIP: */
128  case 1:
129  t->methods = &stri_methods_reverse;
130  break;
131 
132 /* case RT_NORMAL_FIXUP_OFF: */
133  case 0:
134  default:
135  t->methods = &stri_methods;
136  break;
137  }
138 }
139 
140 
141 object * newvcstri(void * voidtex, vector v0, vector v1, vector v2,
142  vector n0, vector n1, vector n2,
143  color c0, color c1, color c2) {
144  vcstri * t;
145  vector edge1, edge2, edge3;
146  vcstri_texture * tex = (vcstri_texture *) voidtex;
147 
148  VSub(&v1, &v0, &edge1);
149  VSub(&v2, &v0, &edge2);
150  VSub(&v2, &v1, &edge3);
151 
152  /* check to see if this will be a degenerate triangle before creation */
153  if ((VLength(&edge1) >= EPSILON) &&
154  (VLength(&edge2) >= EPSILON) &&
155  (VLength(&edge3) >= EPSILON)) {
156 
157  t=(vcstri *) malloc(sizeof(vcstri));
158 
159  t->nextobj = NULL;
160  t->methods = &stri_methods;
161 
162  t->v0 = v0;
163  t->edge1 = edge1;
164  t->edge2 = edge2;
165  t->n0 = n0;
166  t->n1 = n1;
167  t->n2 = n2;
168 
169  tex->c0 = c0;
170  tex->c1 = c1;
171  tex->c2 = c2;
172  tex->obj = t; /* XXX hack to let the texture function get c0c1c2 data */
173  tex->texfunc = (color(*)(const void *, const void *, void *))(vcstri_color);
174  t->tex = (texture *) tex;
175 
176  return (object *) t;
177  }
178 
179  return NULL; /* was a degenerate triangle */
180 }
181 
182 
183 void vcstri_normal_fixup(object *otri, int mode) {
184  vcstri *t = (vcstri *) otri;
185 
186  switch (mode) {
187 /* case RT_NORMAL_FIXUP_GUESS: */
188  case 2:
189  t->methods = &stri_methods_guess;
190  break;
191 
192 /* case RT_NORMAL_FIXUP_FLIP: */
193  case 1:
194  t->methods = &stri_methods_reverse;
195  break;
196 
197 /* case RT_NORMAL_FIXUP_OFF: */
198  case 0:
199  default:
200  t->methods = &stri_methods;
201  break;
202  }
203 }
204 
205 
206 #define CROSS(dest,v1,v2) \
207  dest.x=v1.y*v2.z-v1.z*v2.y; \
208  dest.y=v1.z*v2.x-v1.x*v2.z; \
209  dest.z=v1.x*v2.y-v1.y*v2.x;
210 
211 #define DOT(v1,v2) (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z)
212 
213 #define SUB(dest,v1,v2) \
214  dest.x=v1.x-v2.x; \
215  dest.y=v1.y-v2.y; \
216  dest.z=v1.z-v2.z;
217 
218 static int tri_bbox(void * obj, vector * min, vector * max) {
219  tri * t = (tri *) obj;
220  vector v1, v2;
221 
222  VAdd(&t->v0, &t->edge1, &v1);
223  VAdd(&t->v0, &t->edge2, &v2);
224 
225  min->x = MYMIN( t->v0.x , MYMIN( v1.x , v2.x ));
226  min->y = MYMIN( t->v0.y , MYMIN( v1.y , v2.y ));
227  min->z = MYMIN( t->v0.z , MYMIN( v1.z , v2.z ));
228 
229  max->x = MYMAX( t->v0.x , MYMAX( v1.x , v2.x ));
230  max->y = MYMAX( t->v0.y , MYMAX( v1.y , v2.y ));
231  max->z = MYMAX( t->v0.z , MYMAX( v1.z , v2.z ));
232 
233  return 1;
234 }
235 
236 static void tri_intersect(const tri * trn, ray * ry) {
237  vector tvec, pvec, qvec;
238  flt det, inv_det, t, u, v;
239 
240  /* begin calculating determinant - also used to calculate U parameter */
241  CROSS(pvec, ry->d, trn->edge2);
242 
243  /* if determinant is near zero, ray lies in plane of triangle */
244  det = DOT(trn->edge1, pvec);
245 
246 #if 0 /* define TEST_CULL if culling is desired */
247  if (det < EPSILON)
248  return;
249 
250  /* calculate distance from vert0 to ray origin */
251  SUB(tvec, ry->o, trn->v0);
252 
253  /* calculate U parameter and test bounds */
254  u = DOT(tvec, pvec);
255  if (u < 0.0 || u > det)
256  return;
257 
258  /* prepare to test V parameter */
259  CROSS(qvec, tvec, trn->edge1);
260 
261  /* calculate V parameter and test bounds */
262  v = DOT(ry->d, qvec);
263  if (v < 0.0 || u + v > det)
264  return;
265 
266  /* calculate t, scale parameters, ray intersects triangle */
267  t = DOT(trn->edge2, qvec);
268  inv_det = 1.0 / det;
269  t *= inv_det;
270  u *= inv_det;
271  v *= inv_det;
272 #else /* the non-culling branch */
273  if (det > -EPSILON && det < EPSILON)
274  return;
275 
276  inv_det = 1.0 / det;
277 
278  /* calculate distance from vert0 to ray origin */
279  SUB(tvec, ry->o, trn->v0);
280 
281  /* calculate U parameter and test bounds */
282  u = DOT(tvec, pvec) * inv_det;
283  if (u < 0.0 || u > 1.0)
284  return;
285 
286  /* prepare to test V parameter */
287  CROSS(qvec, tvec, trn->edge1);
288 
289  /* calculate V parameter and test bounds */
290  v = DOT(ry->d, qvec) * inv_det;
291  if (v < 0.0 || u + v > 1.0)
292  return;
293 
294  /* calculate t, ray intersects triangle */
295  t = DOT(trn->edge2, qvec) * inv_det;
296 #endif
297 
298  ry->add_intersection(t,(object *) trn, ry);
299 }
300 
301 
302 static void tri_normal(const tri * trn, const vector * hit, const ray * incident, vector * N) {
303  flt invlen;
304 
305  CROSS((*N), trn->edge1, trn->edge2);
306 
307  invlen = 1.0 / SQRT(N->x*N->x + N->y*N->y + N->z*N->z);
308  N->x *= invlen;
309  N->y *= invlen;
310  N->z *= invlen;
311 
312  /* Flip surface normal to point toward the viewer if necessary */
313  if (VDot(N, &(incident->d)) > 0.0) {
314  N->x=-N->x;
315  N->y=-N->y;
316  N->z=-N->z;
317  }
318 }
319 
320 
321 static void stri_normal(const stri * trn, const vector * hit, const ray * incident, vector * N) {
322  flt U, V, W, lensqr, invlen;
323  vector P, tmp, norm;
324 
325  CROSS(norm, trn->edge1, trn->edge2);
326  lensqr = DOT(norm, norm);
327 
328  VSUB((*hit), trn->v0, P);
329 
330  CROSS(tmp, P, trn->edge2);
331  U = DOT(tmp, norm) / lensqr;
332 
333  CROSS(tmp, trn->edge1, P);
334  V = DOT(tmp, norm) / lensqr;
335 
336  W = 1.0 - (U + V);
337 
338  N->x = W*trn->n0.x + U*trn->n1.x + V*trn->n2.x;
339  N->y = W*trn->n0.y + U*trn->n1.y + V*trn->n2.y;
340  N->z = W*trn->n0.z + U*trn->n1.z + V*trn->n2.z;
341 
342  invlen = 1.0 / SQRT(N->x*N->x + N->y*N->y + N->z*N->z);
343  N->x *= invlen;
344  N->y *= invlen;
345  N->z *= invlen;
346 
347  /* Flip surface normal to point toward the viewer if necessary */
348  /* Note: unlike the normal routines for other objects, the code */
349  /* for triangles interpolated surface normals tests the */
350  /* vertex winding order rather than using the resulting */
351  /* interpolated normal. */
352  if (VDot(&norm, &(incident->d)) > 0.0) {
353  N->x=-N->x;
354  N->y=-N->y;
355  N->z=-N->z;
356  }
357 }
358 
359 
360 color vcstri_color(const vector * hit, const texture * tx, const ray * incident) {
361  vcstri_texture * tex = (vcstri_texture *) tx;
362  const vcstri * trn = (vcstri *) tex->obj;
363  flt U, V, W, lensqr;
364  vector P, tmp, norm;
365  color col;
366 
367  CROSS(norm, trn->edge1, trn->edge2);
368  lensqr = DOT(norm, norm);
369 
370  VSUB((*hit), trn->v0, P);
371 
372  CROSS(tmp, P, trn->edge2);
373  U = DOT(tmp, norm) / lensqr;
374 
375  CROSS(tmp, trn->edge1, P);
376  V = DOT(tmp, norm) / lensqr;
377 
378  W = 1.0 - (U + V);
379 
380  col.r = W*tex->c0.r + U*tex->c1.r + V*tex->c2.r;
381  col.g = W*tex->c0.g + U*tex->c1.g + V*tex->c2.g;
382  col.b = W*tex->c0.b + U*tex->c1.b + V*tex->c2.b;
383 
384  return col;
385 }
386 
387 
388 static void stri_normal_reverse(const stri * trn, const vector * hit, const ray * incident, vector * N) {
389  flt U, V, W, lensqr, invlen;
390  vector P, tmp, norm;
391 
392  CROSS(norm, trn->edge1, trn->edge2);
393  lensqr = DOT(norm, norm);
394 
395  VSUB((*hit), trn->v0, P);
396 
397  CROSS(tmp, P, trn->edge2);
398  U = DOT(tmp, norm) / lensqr;
399 
400  CROSS(tmp, trn->edge1, P);
401  V = DOT(tmp, norm) / lensqr;
402 
403  W = 1.0 - (U + V);
404 
405  N->x = W*trn->n0.x + U*trn->n1.x + V*trn->n2.x;
406  N->y = W*trn->n0.y + U*trn->n1.y + V*trn->n2.y;
407  N->z = W*trn->n0.z + U*trn->n1.z + V*trn->n2.z;
408 
409  invlen = 1.0 / SQRT(N->x*N->x + N->y*N->y + N->z*N->z);
410  N->x *= invlen;
411  N->y *= invlen;
412  N->z *= invlen;
413 
414  /* Flip surface normal to point toward the viewer if necessary */
415  /* Note: unlike the normal routines for other objects, the code */
416  /* for triangles interpolated surface normals tests the */
417  /* vertex winding order rather than using the resulting */
418  /* interpolated normal. */
419  /* Note: This version is the reverse of the normal version */
420  if (VDot(&norm, &(incident->d)) < 0.0) {
421  N->x=-N->x;
422  N->y=-N->y;
423  N->z=-N->z;
424  }
425 }
426 
427 
428 static void stri_normal_guess(const stri * trn, const vector * hit, const ray * incident, vector * N) {
429  flt U, V, W, lensqr, invlen;
430  vector P, tmp, norm;
431 
432  CROSS(norm, trn->edge1, trn->edge2);
433  lensqr = DOT(norm, norm);
434 
435  VSUB((*hit), trn->v0, P);
436 
437  CROSS(tmp, P, trn->edge2);
438  U = DOT(tmp, norm) / lensqr;
439 
440  CROSS(tmp, trn->edge1, P);
441  V = DOT(tmp, norm) / lensqr;
442 
443  W = 1.0 - (U + V);
444 
445  N->x = W*trn->n0.x + U*trn->n1.x + V*trn->n2.x;
446  N->y = W*trn->n0.y + U*trn->n1.y + V*trn->n2.y;
447  N->z = W*trn->n0.z + U*trn->n1.z + V*trn->n2.z;
448 
449  invlen = 1.0 / SQRT(N->x*N->x + N->y*N->y + N->z*N->z);
450  N->x *= invlen;
451  N->y *= invlen;
452  N->z *= invlen;
453 
454  /* Flip surface normal to point toward the viewer if necessary */
455  /* XXX NOTE: this is actually incorrect, but will sorta work */
456  /* for surfaces with inconsistent winding order and mean vertex */
457  /* normal directions. This implementation is provided only for */
458  /* cases where the incoming geometry can't be fixed and is */
459  /* randomly mixing winding order and normal direction. */
460  if (VDot(N, &(incident->d)) > 0.0) {
461  N->x=-N->x;
462  N->y=-N->y;
463  N->z=-N->z;
464  }
465 }
static void stri_normal_guess(const stri *trn, const vector *hit, const ray *incident, vector *N)
Definition: triangle.c:428
static object_methods tri_methods
Definition: triangle.c:26
static void stri_normal(const stri *trn, const vector *hit, const ray *incident, vector *N)
Definition: triangle.c:321
void VSub(apivector *a, apivector *b, apivector *c)
#define VSUB(a, b, c)
Definition: macros.h:24
static object_methods stri_methods_guess
Definition: triangle.c:47
static object_methods stri_methods
Definition: triangle.c:33
flt VDot(apivector *a, apivector *b)
static void tri_normal(const tri *trn, const vector *hit, const ray *incident, vector *N)
Definition: triangle.c:302
object * newstri(void *tex, vector v0, vector v1, vector v2, vector n0, vector n1, vector n2)
Definition: triangle.c:84
flt VLength(const vector *a)
Definition: vector.c:30
double flt
generic floating point number, using double
Definition: tachyon.h:47
object * newvcstri(void *voidtex, vector v0, vector v1, vector v2, vector n0, vector n1, vector n2, color c0, color c1, color c2)
Definition: triangle.c:141
static object_methods stri_methods_reverse
Definition: triangle.c:40
color vcstri_color(const vector *hit, const texture *tx, const ray *incident)
Definition: triangle.c:360
void stri_normal_fixup(object *otri, int mode)
Definition: triangle.c:118
Tachyon cross-platform timers, special math function wrappers, and RNGs.
#define MYMIN(a, b)
Definition: macros.h:13
#define SQRT(x)
Definition: util.h:31
static void stri_normal_reverse(const stri *trn, const vector *hit, const ray *incident, vector *N)
Definition: triangle.c:388
static void tri_intersect(const tri *trn, ray *ry)
Definition: triangle.c:236
#define CROSS(dest, v1, v2)
Definition: triangle.c:206
void VAdd(apivector *a, apivector *b, apivector *c)
#define SUB(dest, v1, v2)
Definition: triangle.c:213
Tachyon public API function prototypes and declarations used to drive the ray tracing engine...
static int tri_bbox(void *obj, vector *min, vector *max)
Definition: triangle.c:218
#define MYMAX(a, b)
Definition: macros.h:12
object * newtri(void *tex, vector v0, vector v1, vector v2)
Definition: triangle.c:54
void vcstri_normal_fixup(object *otri, int mode)
Definition: triangle.c:183
#define DOT(v1, v2)
Definition: triangle.c:211