hidden wireframe works, change v3 api a little bit

This commit is contained in:
Trammell hudson 2018-03-03 14:14:06 -05:00
parent a356ac6490
commit 8ae1fbf612
Failed to extract signature
12 changed files with 637 additions and 380 deletions

View File

@ -13,7 +13,7 @@ struct _camera_t
{ {
float near; float near;
float far; float far;
float r[4][4]; m44_t r;
}; };
@ -53,7 +53,7 @@ camera_setup(
// and the "up" normal // and the "up" normal
v3_t v = v3_norm(v3_cross(w, u)); v3_t v = v3_norm(v3_cross(w, u));
float cam[4][4] = { m44_t cam = {{
#if 0 #if 0
{ u.p[0], v.p[0], w.p[0], 0 }, { u.p[0], v.p[0], w.p[0], 0 },
{ u.p[1], v.p[1], w.p[1], 0 }, { u.p[1], v.p[1], w.p[1], 0 },
@ -65,49 +65,51 @@ camera_setup(
{ w.p[0], w.p[1], w.p[2], -v3_dot(w,eye) }, { w.p[0], w.p[1], w.p[2], -v3_dot(w,eye) },
{ 0, 0, 0, 1 }, { 0, 0, 0, 1 },
#endif #endif
}; }};
fprintf(stderr, "Camera:\n"); fprintf(stderr, "Camera:\n");
for(int i = 0 ; i < 4 ; i++) for(int i = 0 ; i < 4 ; i++)
{ {
for(int j = 0 ; j < 4 ; j++) for(int j = 0 ; j < 4 ; j++)
fprintf(stderr, " %+5.3f", cam[i][j]); fprintf(stderr, " %+5.3f", cam.m[i][j]);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
// now compute the perspective projection matrix // now compute the perspective projection matrix
if(0) {
float s = 1.0 / tan(fov * M_PI / 180 / 2); float s = 1.0 / tan(fov * M_PI / 180 / 2);
c->near = 4.0; c->near = 1;
c->far = 200; c->far = 2;
float f1 = - c->far / (c->far - c->near); float f1 = - c->far / (c->far - c->near);
float f2 = - c->far * c->near / (c->far - c->near); float f2 = - c->far * c->near / (c->far - c->near);
float pers[4][4] = { m44_t pers = {{
{ s, 0, 0, 0 }, { s, 0, 0, 0 },
{ 0, s, 0, 0 }, { 0, s, 0, 0 },
{ 0, 0, f1, -1 }, { 0, 0, f2, -1 },
{ 0, 0, f2, 0 }, { 0, 0, f1, 0 },
}; }};
fprintf(stderr, "Perspective:\n"); fprintf(stderr, "Perspective:\n");
for(int i = 0 ; i < 4 ; i++) for(int i = 0 ; i < 4 ; i++)
{ {
for(int j = 0 ; j < 4 ; j++) for(int j = 0 ; j < 4 ; j++)
fprintf(stderr, " %+5.3f", pers[i][j]); fprintf(stderr, " %+5.3f", pers.m[i][j]);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
// and apply it to the camera matrix to generate transform // and apply it to the camera matrix to generate transform
for(int i = 0 ; i < 4 ; i++) m44_mult(&c->r, &cam, &pers);
{ } else {
for(int j = 0 ; j < 4 ; j++) // no perspective
{ m44_t pers = {{
float d = 0; { 1, 0, 0, 0 },
for(int k = 0 ; k < 4 ; k++) { 0, 1, 0, 0 },
d += pers[i][k] * cam[k][j]; { 0, 0, 1, 0 },
c->r[i][j] = d; { 0, 0, 0, 1 },
} }};
// and apply it to the camera matrix to generate transform
m44_mult(&c->r, &cam, &pers);
} }
@ -115,9 +117,10 @@ camera_setup(
for(int i = 0 ; i < 4 ; i++) for(int i = 0 ; i < 4 ; i++)
{ {
for(int j = 0 ; j < 4 ; j++) for(int j = 0 ; j < 4 ; j++)
fprintf(stderr, " %+5.3f", c->r[i][j]); fprintf(stderr, " %+5.3f", c->r.m[i][j]);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
} }
} }
@ -133,32 +136,29 @@ camera_project(
v3_t * const v_out v3_t * const v_out
) )
{ {
float v[4] = { v_in->p[0], v_in->p[1], v_in->p[2], 1 }; v4_t v = {{ v_in->p[0], v_in->p[1], v_in->p[2], 1 }};
float p[4] = { 0, 0, 0, 0}; v4_t p = m44_multv(&c->r, &v);
for (int i = 0 ; i < 4 ; i++) p.p[2] *= -1;
for (int j = 0 ; j < 4 ; j++)
p[i] += c->r[i][j] * v[j];
// what if p->p[4] == 0? // what if p->p[4] == 0?
// pz < 0 == The point is behind us; do not display? // pz < 0 == The point is behind us; do not display?
//if (p[2] < c->near || p[2] > c->far) //if (p[2] < c->near || p[2] > c->far)
if (p[2] < 0) if (p.p[2] <= 0)
return 0; return 0;
for (int i = 0 ; i < 3 ; i++) // shrink by the distance
p[i] /= p[3]; p.p[0] *= 1000 / p.p[2];
p.p[1] *= 1000 / p.p[2];
if(0) fprintf(stderr, "%.2f %.2f %.2f -> %.2f %.2f %.2f %.2f\n", //p[2] /= 1000;
v[0], v[1], v[2],
p[0], p[1], p[2], p[3]
);
// Transform to screen coordinate frame, // Transform to screen coordinate frame,
// and return it to the caller // and return it to the caller
v_out->p[0] = p[0]; v_out->p[0] = p.p[0];
v_out->p[1] = p[1]; v_out->p[1] = p.p[1];
v_out->p[2] = p[2]; v_out->p[2] = p.p[2];
v3_print(*v_out);
return 1; return 1;
} }

View File

@ -18,6 +18,7 @@ static int debug = 0;
#include "v3.h" #include "v3.h"
#include "tri.h" #include "tri.h"
#include "camera.h" #include "camera.h"
#include "svg.h"
static const char usage[] = static const char usage[] =
@ -77,25 +78,6 @@ stl_face_t;
void
svg_line(
const char * color,
const float * p1,
const float * p2,
float thick
)
{
// invert the sense of y
printf("<line x1=\"%fpx\" y1=\"%fpx\" x2=\"%fpx\" y2=\"%fpx\" stroke=\"%s\" stroke-width=\"%.1fpx\"/>\n",
p1[0],
-p1[1],
p2[0],
-p2[1],
color,
thick
);
}
static inline int static inline int
v2_eq( v2_eq(
@ -302,11 +284,6 @@ int main(
behind++; behind++;
goto reject_early; goto reject_early;
} }
// scale to the image size
s[j].p[0] *= width;
s[j].p[1] *= width;
s[j].p[2] *= width;
} }
if(debug >= 2) if(debug >= 2)
@ -326,7 +303,7 @@ int main(
tri_t * const tri = tri_new(s, stl->p); tri_t * const tri = tri_new(s, stl->p);
// reject this face if any of the vertices are behind us // reject this face if any of the vertices are behind us
if (tri->min[2] < 0) if (tri->min.p[2] < 0)
{ {
behind++; behind++;
goto reject; goto reject;
@ -342,6 +319,7 @@ int main(
} }
// if it has any off-screen coords, reject it // if it has any off-screen coords, reject it
/*
if (!onscreen(&tri->p[0], width, height) if (!onscreen(&tri->p[0], width, height)
|| !onscreen(&tri->p[1], width, height) || !onscreen(&tri->p[1], width, height)
|| !onscreen(&tri->p[2], width, height)) || !onscreen(&tri->p[2], width, height))
@ -350,6 +328,7 @@ tri_print(tri);
offscreen++; offscreen++;
goto reject; goto reject;
} }
*/
// prune the small triangles in the screen space // prune the small triangles in the screen space
if (tri_area_2d(tri) < prune) if (tri_area_2d(tri) < prune)
@ -378,6 +357,8 @@ reject:
reject_early: reject_early:
continue; continue;
} }
for(tri_t * t = zlist ; t ; t = t->next)
tri_print(t);
if (debug) if (debug)
fprintf(stderr, "Retained %d triangles, rejected %d behind, %d offscreen, %d backface, %d small\n", retained, behind, offscreen, backface, small_area); fprintf(stderr, "Retained %d triangles, rejected %d behind, %d offscreen, %d backface, %d small\n", retained, behind, offscreen, backface, small_area);
@ -385,6 +366,7 @@ reject_early:
// drop any triangles that are totally occluded by another // drop any triangles that are totally occluded by another
// triangle. this reduces the amount of work for later // triangle. this reduces the amount of work for later
rejected = 0; rejected = 0;
#if 0
for(tri_t * t = zlist ; t ; t = t->next) for(tri_t * t = zlist ; t ; t = t->next)
{ {
tri_t * t2_next; tri_t * t2_next;
@ -405,6 +387,7 @@ reject_early:
} }
if (debug) if (debug)
fprintf(stderr, "Rejected %d fully occluded triangles\n", rejected); fprintf(stderr, "Rejected %d fully occluded triangles\n", rejected);
#endif
// generate a list of segments, dropping any coplanar ones // generate a list of segments, dropping any coplanar ones
@ -469,7 +452,7 @@ reject_early:
seg_t * s = slist; seg_t * s = slist;
slist = s->next; slist = s->next;
tri_seg_intersect(zlist, s, &slist_visible); tri_seg_hidden(zlist, s, &slist_visible);
} }
} else { } else {
// don't do any intersection tests // don't do any intersection tests

12
seg.h
View File

@ -34,15 +34,17 @@ seg_print(
const seg_t * const s const seg_t * const s
) )
{ {
fprintf(stderr, "%.0f,%.0f -> %.0f,%.0f (was %.0f,%.0f -> %.0f,%.0f)\n", fprintf(stderr, "%+5.1f,%+5.1f,%5.1f -> %+5.1f,%+5.1f,%5.1f\n",
s->p[0].p[0], s->p[0].p[0],
s->p[0].p[1], s->p[0].p[1],
s->p[0].p[2],
s->p[1].p[0], s->p[1].p[0],
s->p[1].p[1], s->p[1].p[1],
s->src[0].p[0], s->p[1].p[2]
s->src[0].p[1], //s->src[0].p[0],
s->src[1].p[0], //s->src[0].p[1],
s->src[1].p[1] //s->src[1].p[0],
//s->src[1].p[1]
); );
} }

View File

@ -38,7 +38,7 @@ stl_vertex_find(
{ {
stl_vertex_t * const v = &vertices[x]; stl_vertex_t * const v = &vertices[x];
if (v3_eq(&v->p, p)) if (v3_eq(v->p, *p))
return v; return v;
} }
@ -95,11 +95,11 @@ stl_angle(
for (int i = 0 ; i < 3 ; i++) for (int i = 0 ; i < 3 ; i++)
{ {
x4 = f2->vertex[i]->p; x4 = f2->vertex[i]->p;
if (v3_eq(&x1, &x4)) if (v3_eq(x1, x4))
continue; continue;
if (v3_eq(&x2, &x4)) if (v3_eq(x2, x4))
continue; continue;
if (v3_eq(&x3, &x4)) if (v3_eq(x3, x4))
continue; continue;
break; break;
} }

41
svg.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef _svg_h_
#define _svg_h_
static inline void
svg_line(
const char * color,
const float * p1,
const float * p2,
float thick
)
{
// invert the sense of y
printf("<line x1=\"%fpx\" y1=\"%fpx\" x2=\"%fpx\" y2=\"%fpx\" stroke=\"%s\" stroke-width=\"%.1fpx\"/>\n",
p1[0],
-p1[1],
p2[0],
-p2[1],
color,
thick
);
}
static inline void
svg_circle(
const char * color,
const float p1,
const float p2,
float radius
)
{
// invert the sense of y
printf("<circle x=\"%fpx\" y=\"%fpx\" radius=\"%fpx\" stroke=\"%s\" stroke-width=\"%.1fpx\"/>\n",
p1,
-p2,
radius,
color,
1.0
);
}
#endif

View File

@ -1 +1,7 @@
cube([10,20,30], center=true); render() difference()
{
cube([10,20,25], center=true);
cube([20,15,20], center=true);
}
translate([3,2,4]) cube([25,8,8], center=true);

BIN
test1.stl

Binary file not shown.

679
tri.c
View File

@ -34,11 +34,8 @@ tri_new(
// compute the bounding box for the triangle in camera space // compute the bounding box for the triangle in camera space
for(int j = 0 ; j < 3 ; j++) t->min = v3_min(v3_min(t->p[0], t->p[1]), t->p[2]);
{ t->max = v3_max(v3_max(t->p[0], t->p[1]), t->p[2]);
t->min[j] = min(min(t->p[0].p[j], t->p[1].p[j]), t->p[2].p[j]);
t->max[j] = max(max(t->p[0].p[j], t->p[1].p[j]), t->p[2].p[j]);
}
return t; return t;
} }
@ -59,7 +56,7 @@ tri_insert(
// check to see if our new triangle is closer than // check to see if our new triangle is closer than
// the current triangle // the current triangle
if(iter->min[2] > t->min[2]) if(iter->min.p[2] > t->min.p[2])
break; break;
zlist = &(iter->next); zlist = &(iter->next);
@ -110,7 +107,7 @@ tri_print(
const tri_t * const t const tri_t * const t
) )
{ {
fprintf(stderr, "%.0f,%.0f,%.0f %.0f,%.0f,%.0f %.0f,%.0f,%.0f norm %.3f,%.3f,%.3f\n", fprintf(stderr, "{{{%+5.1f,%+5.1f,%5.1f }},{{%+5.1f,%+5.1f,%5.1f }},{{%+5.1f,%+5.1f,%5.1f }}}\n", // norm %.3f %.3f %.3f\n",
t->p[0].p[0], t->p[0].p[0],
t->p[0].p[1], t->p[0].p[1],
t->p[0].p[2], t->p[0].p[2],
@ -119,10 +116,10 @@ tri_print(
t->p[1].p[2], t->p[1].p[2],
t->p[2].p[0], t->p[2].p[0],
t->p[2].p[1], t->p[2].p[1],
t->p[2].p[2], t->p[2].p[2]
t->normal.p[0], //t->normal.p[0],
t->normal.p[1], //t->normal.p[1],
t->normal.p[2] //t->normal.p[2]
); );
} }
@ -149,7 +146,7 @@ tri_coplanar(
{ {
for(int j = 0 ; j < 3 ; j++) for(int j = 0 ; j < 3 ; j++)
{ {
if (!v3_eq(&t0->p[i], &t1->p[j])) if (!v3_eq(t0->p[i], t1->p[j]))
continue; continue;
matches |= 1 << i; matches |= 1 << i;
break; break;
@ -162,10 +159,14 @@ tri_coplanar(
case 0x6: return 1; case 0x6: return 1;
case 0x5: return 2; case 0x5: return 2;
case 0x7: case 0x7:
// these are likely small triangles that can be ignored
if (tri_debug > 3)
{
fprintf(stderr, "uh, three points match?\n"); fprintf(stderr, "uh, three points match?\n");
tri_print(t0); tri_print(t0);
tri_print(t1); tri_print(t1);
return -1; }
return 0;
default: default:
// no shared edge // no shared edge
return -1; return -1;
@ -212,6 +213,60 @@ tri_find_z(
} }
/*
* Find the barycentric coordinates for point of an XY coordinate in a triangle.
*
* p can be written as a combination of t01 and t02,
* p - t0 = a * (t1 - t0) + b * (t2 - t0)
* setting t0 to 0, this becomes:
* p = a * t1 + b * t2
* which is two equations with two unknowns
*
* The x and y coordinates are based on the two sides of the triangle
* and the z coordinate is the screen coordinate z in the triangle.
*/
v3_t
tri_bary_coord(
const tri_t * const t,
const v3_t * const p
)
{
const float t1x = t->p[1].p[0] - t->p[0].p[0];
const float t1y = t->p[1].p[1] - t->p[0].p[1];
const float t1z = t->p[1].p[2] - t->p[0].p[2];
const float t2x = t->p[2].p[0] - t->p[0].p[0];
const float t2y = t->p[2].p[1] - t->p[0].p[1];
const float t2z = t->p[2].p[2] - t->p[0].p[2];
const float px = p->p[0] - t->p[0].p[0];
const float py = p->p[1] - t->p[0].p[1];
float a = (px * t2y - py * t2x) / (t1x * t2y - t2x * t1y);
float b = (px * t1y - py * t1x) / (t2x * t1y - t1x * t2y);
v3_t v = {{
a,
b,
t->p[0].p[2] + a * t1z + b * t2z,
}};
//v.p[2] = 1.0 - v.p[0] - v.p[1];
return v;
}
// Returns true if the bary centry point is inside the triangle
// which means that a and b are non-negative and sum to less than 1
int
tri_bary_inside(
const v3_t p
)
{
const float a = p.p[0];
const float b = p.p[1];
return 0 <= a && 0 <= b && a + b <= 1;
}
/** Compute the points of intersection for two segments in 2d, and z points. /** Compute the points of intersection for two segments in 2d, and z points.
* *
* This is a specialized ray intersection algorithm for the * This is a specialized ray intersection algorithm for the
@ -235,18 +290,22 @@ hidden_intersect(
const float p0_x = p0->p[0]; const float p0_x = p0->p[0];
const float p0_y = p0->p[1]; const float p0_y = p0->p[1];
const float p0_z = p0->p[2]; const float p0_z = p0->p[2];
const float p1_x = p1->p[0]; const float p1_x = p1->p[0];
const float p1_y = p1->p[1]; const float p1_y = p1->p[1];
const float p1_z = p1->p[2]; const float p1_z = p1->p[2];
const float p2_x = p2->p[0]; const float p2_x = p2->p[0];
const float p2_y = p2->p[1]; const float p2_y = p2->p[1];
const float p2_z = p2->p[2]; const float p2_z = p2->p[2];
const float p3_x = p3->p[0]; const float p3_x = p3->p[0];
const float p3_y = p3->p[1]; const float p3_y = p3->p[1];
const float p3_z = p3->p[2]; const float p3_z = p3->p[2];
const float s1_x = p1_x - p0_x; const float s1_x = p1_x - p0_x;
const float s1_y = p1_y - p0_y; const float s1_y = p1_y - p0_y;
const float s2_x = p3_x - p2_x; const float s2_x = p3_x - p2_x;
const float s2_y = p3_y - p2_y; const float s2_y = p3_y - p2_y;
@ -279,15 +338,12 @@ if(0) fprintf(stderr, "collision: %.0f,%.0f,%.0f->%.0f,%.0f,%.0f %.0f,%.0f,%.0f-
r1 r1
); );
const float ix = p0_x + (r0 * s1_x);
const float iy = p0_y + (r0 * s1_y);
// compute the z intercept for each on the two different coordinates // compute the z intercept for each on the two different coordinates
if(l0_int) if(l0_int)
{ {
*l0_int = (v3_t){{ *l0_int = (v3_t){{
ix, p0_x + r0 * s1_x,
iy, p0_y + r0 * s1_y,
p0_z + r0 * (p1_z - p0_z) p0_z + r0 * (p1_z - p0_z)
}}; }};
} }
@ -295,8 +351,8 @@ if(0) fprintf(stderr, "collision: %.0f,%.0f,%.0f->%.0f,%.0f,%.0f %.0f,%.0f,%.0f-
if(l1_int) if(l1_int)
{ {
*l1_int = (v3_t){{ *l1_int = (v3_t){{
ix, p2_x + r1 * s2_x,
iy, p2_y + r1 * s2_y,
p2_z + r1 * (p3_z - p2_z) p2_z + r1 * (p3_z - p2_z)
}}; }};
} }
@ -305,270 +361,7 @@ if(0) fprintf(stderr, "collision: %.0f,%.0f,%.0f->%.0f,%.0f,%.0f %.0f,%.0f,%.0f-
} }
/*
* Recursive algorithm:
* Given a line segment and a list of triangles,
* find if the line segment crosses any triangle.
* If it crosses a triangle the segment will be shortened
* and an additional one might be created.
* Recusively try intersecting the new segment (starting at the same triangle)
* and then continue trying the shortened segment.
*/
void
tri_seg_intersect(
const tri_t * zlist,
seg_t * s,
seg_t ** slist_visible
)
{
const float p0z = s->p[0].p[2];
const float p1z = s->p[1].p[2];
const float seg_max_z = max(p0z, p1z);
// avoid processing empty segments
const float seg_len = v3_len(&s->p[0], &s->p[1]);
if (seg_len < EPS)
return;
static int recursive;
recursive++;
//fprintf(stderr, "%d: processing segment ", recursive); seg_print(s);
fprintf(stderr, "--- recursive %d\n", recursive);
seg_print(s);
for( const tri_t * t = zlist ; t ; t = t->next )
{
// if the segment is closer than the triangle,
// then we no longer have to check any further into
// the zlist (it is sorted by depth).
if (seg_max_z <= t->min[2])
break;
#if 0 #if 0
// make sure that we're not comparing to our own triangle
// or one that shares an edge with us (which might be in
// a different order)
if (v2_eq(s->src[0].p, t->p[0].p, 0.0005)
&& v2_eq(s->src[1].p, t->p[1].p, 0.0005))
continue;
if (v2_eq(s->src[0].p, t->p[1].p, 0.0005)
&& v2_eq(s->src[1].p, t->p[2].p, 0.0005))
continue;
if (v2_eq(s->src[0].p, t->p[2].p, 0.0005)
&& v2_eq(s->src[1].p, t->p[0].p, 0.0005))
continue;
if (v2_eq(s->src[0].p, t->p[1].p, 0.0005)
&& v2_eq(s->src[1].p, t->p[0].p, 0.0005))
continue;
if (v2_eq(s->src[0].p, t->p[2].p, 0.0005)
&& v2_eq(s->src[1].p, t->p[1].p, 0.0005))
continue;
if (v2_eq(s->src[0].p, t->p[0].p, 0.0005)
&& v2_eq(s->src[1].p, t->p[2].p, 0.0005))
continue;
#endif
if (tri_debug >= 2)
tri_print(t);
/*
// if the segment is co-linear to any of the
// triangle edges, include it
for(int i = 0 ; i < 3 ; i++)
{
if (parallel(
&s->p[0], &s->p[1],
&t->p[i], &t->p[(i+1)%3]
))
goto next_segment;
}
*/
float z0, z1;
int inside0 = tri_find_z(t, &s->p[0], &z0);
int inside1 = tri_find_z(t, &s->p[1], &z1);
if (tri_debug >= 2 && (inside0 || inside1))
{
fprintf(stderr, "inside %d %d\n", inside0, inside1);
}
// if both are inside but the segment is infront of the
// triangle, then we retain the segment.
// otherwies we discard the segment
if (inside0 && inside1)
{
if (s->p[0].p[2] <= z0
&& s->p[1].p[2] <= z1)
continue;
if (tri_debug >= 2)
fprintf(stderr, "BOTH INSIDE\n");
recursive--;
return;
}
// split the segment for each intersection with the
// triangle segments and add it to the work queue.
int intersections = 0;
v3_t is[3] = {}; // 3d point of segment intercept
v3_t it[3] = {}; // 3d point of triangle intercept
for(int j = 0 ; j < 3 ; j++)
{
float ratio = hidden_intersect(
&s->p[0], &s->p[1],
&t->p[j], &t->p[(j+1)%3],
&is[intersections],
&it[intersections]
);
if (ratio < 0)
continue;
if (tri_debug >= 2)
fprintf(stderr, "%d ratio=%.2f\n", j, ratio);
intersections++;
}
// if none of them intersect, we keep looking
if (intersections == 0)
continue;
if (tri_debug >= 2)
fprintf(stderr, "%d intersections\n", intersections);
if (intersections == 3)
{
// this likely means that the triangle is very, very
// small, so let's just ignore this triangle
if (tri_debug >= 2)
fprintf(stderr, "Three intersections\n");
continue;
}
if (intersections == 2)
{
// figure out how far it is to each of the intersections
const float d00 = v3_len(&s->p[0], &is[0]);
const float d01 = v3_len(&s->p[0], &is[1]);
const float d10 = v3_len(&s->p[1], &is[0]);
const float d11 = v3_len(&s->p[1], &is[1]);
if (tri_debug >= 2)
fprintf(stderr, "Two intersections\n");
// discard segments that have two interesections that match
// the segment exactly (distance from segment ends to
// intersection point close enough to zero).
if (d00 < EPS && d11 < EPS)
{
recursive--;
return;
}
if (d01 < EPS && d10 < EPS)
{
recursive--;
return;
}
// if the segment intersection is closer than the triangle,
// then we do nothing. degenerate cases are not handled
if (d00 <= d01
&& is[0].p[2] <= it[0].p[2]
&& is[1].p[2] <= it[1].p[2])
continue;
if (d00 > d01
&& is[1].p[2] <= it[0].p[2]
&& is[0].p[2] <= it[1].p[2])
continue;
// segment is behind the triangle,
// we have to create a new segment
// and shorten the existing segment
// find the two intersections that we have
// update the src field
// we need to create a new segment
seg_t * news;
if (d00 < d01)
{
// split from p0 to ix0
news = seg_new(s->p[0], is[0]);
news->src[0] = s->src[0];
news->src[1] = s->src[1];
s->p[0] = is[1];
} else {
// split from p0 to ix1
news = seg_new(s->p[0], is[1]);
news->src[0] = s->src[0];
news->src[1] = s->src[1];
s->p[0] = is[0];
}
// recursively start splitting the new segment
// starting at the next triangle down the z-depth
tri_seg_intersect(zlist->next, news, slist_visible);
// continue splitting our current segment
continue;
}
if (intersections == 1)
{
// if there is an intersection, but the segment intercept
// is closer than the triangle intercept, then no problem.
// we do not bother with degenerate cases of intersecting
// triangles
if (is[0].p[2] <= it[0].p[2]
&& is[1].p[2] <= it[0].p[2])
{
//svg_line("#00FF00", s->p[0].p, s->p[1].p, 10);
continue;
}
if (inside0)
{
// shorten it on the 0 side
s->p[0] = is[0];
// huh? shouldn't we process this one?
return;
continue;
} else
if (inside1)
{
// shorten it on the 1 side
s->p[1] = is[0];
// huh? shouldn't we process this one?
return;
continue;
} else {
// both outside, but an intersection?
// split at that point and hope for the best
seg_t * const news = seg_new(s->p[0], is[0]);
news->src[0] = s->src[0];
news->src[1] = s->src[1];
s->p[0] = is[0];
tri_seg_intersect(zlist->next, news, slist_visible);
// continue splitting our current segment
continue;
}
}
next_segment:
continue;
}
// if we've reached here the segment is visible
// and should be added to the visible list
s->next = *slist_visible;
*slist_visible = s;
recursive--;
}
/* /*
* Fast check to see if t2 is entire occluded by t. * Fast check to see if t2 is entire occluded by t.
@ -612,3 +405,323 @@ tri_behind(
// they are all on the same side // they are all on the same side
return 0; return 0;
} }
#endif
/*
tri_no_intersection, // nothing changed
tri_infront, // segment is in front of the triangle
tri_hidden, // segment is completely occluded
tri_clipped, // segment is partially occluded on one end
tri_split, // segment is partially occluded in the middle
*/
tri_intersect_t
tri_seg_intersect(
const tri_t * t,
seg_t * s,
seg_t ** new_seg // only if tri_split
)
{
// avoid processing nearly empty segments
const float seg_len = v3_len(&s->p[0], &s->p[1]);
if (seg_len < EPS)
return tri_hidden;
const v3_t p_max = v3_max(s->p[0], s->p[1]);
const v3_t p_min = v3_min(s->p[0], s->p[1]);
// if the segment is closer than the triangle,
// then we no longer have to check any further into
// the zlist (it is sorted by depth).
if (p_max.p[2] <= t->min.p[2])
return tri_infront;
// check for four quadrant outside the bounding box
// of the triangle min/max, which would have no chance
// of intersecting with the triangle
if (p_min.p[0] < t->min.p[0]
&& p_max.p[0] < t->min.p[0])
return tri_no_intersection;
if (p_min.p[1] < t->min.p[1]
&& p_max.p[1] < t->min.p[1])
return tri_no_intersection;
if (p_min.p[0] > t->max.p[0]
&& p_max.p[0] > t->max.p[0])
return tri_no_intersection;
if (p_min.p[1] > t->max.p[1]
&& p_max.p[1] > t->max.p[1])
return tri_no_intersection;
// there is a possibility that this line crosses the triangle
// compute the coordinates in triangle space
const v3_t tp0 = tri_bary_coord(t, &s->p[0]);
const v3_t tp1 = tri_bary_coord(t, &s->p[1]);
// if both are inside and not both on the same edge of
// the triangle, then the segment is totally hidden.
if (tri_bary_inside(tp0) && tri_bary_inside(tp1))
{
// if the segment z is closer than the triangle z
// then the segment is in front of the triangle
if (s->p[0].p[2] < tp0.p[2] && s->p[1].p[2] < tp1.p[2])
return tri_no_intersection;
// if the barycentric coord is 0 for the same edge
// for both points, then it is on the original line
if (tp0.p[0] < EPS && tp1.p[0] < EPS)
return tri_no_intersection;
if (tp0.p[1] < EPS && tp1.p[1] < EPS)
return tri_no_intersection;
// compute the third barycentric coordinate and check
float c0 = 1.0 - tp0.p[0] - tp0.p[1];
float c1 = 1.0 - tp1.p[0] - tp1.p[1];
if (c0 < EPS && c1 < EPS)
return tri_no_intersection;
// it is not on an edge and not infront of the triangle
// so the segment is totally occluded
return tri_hidden;
}
// find the intersection point for each of the three
// sides of the triangle
v3_t is[3] = {}; // 3d point of segment intercept
v3_t it[3] = {}; // 3d point of triangle intercept
float ratio[3] = {}; // length along the line
int intersections = 0;
for(int j = 0 ; j < 3 ; j++)
{
ratio[intersections] = hidden_intersect(
&s->p[0], &s->p[1],
&t->p[j], &t->p[(j+1)%3],
&is[intersections],
&it[intersections]
);
if (ratio[intersections] < 0)
continue;
// if the segment intersection is closer than the
// triangle intersection, this does not count as
// an intersection and we can ignore it.
if (is[intersections].p[2] < it[intersections].p[2])
continue;
if (tri_debug >= 2)
{
fprintf(stderr, "%d ratio=%.2f %+6.1f", j, ratio[intersections], it[intersections].p[2]);
v3_print(is[intersections]);
}
intersections++;
}
// check for duplicate intersections, which happens if
// the lines go through at precisely the corners
// this might mean that we hit exactly at one
// point and two of the points are the same
if (intersections == 3)
{
if (v3_eq(is[0], is[2]))
intersections--;
else
if (v3_eq(is[1], is[2]))
intersections--;
else
if (v3_eq(is[0], is[1]))
{
intersections--;
is[1] = is[2];
it[1] = it[2];
}
}
if (intersections == 2 && v3_eq(is[0], is[1]))
intersections--;
// no intersections? there is nothing to do
if (intersections == 0)
return tri_no_intersection;
// three intersections? maybe a very small triangle
if (intersections == 3)
{
fprintf(stderr, "THREE INTERSECTIONS?\n");
fprintf(stderr, "is0="); v3_print(is[0]);
fprintf(stderr, "is1="); v3_print(is[1]);
fprintf(stderr, "is2="); v3_print(is[2]);
svg_line("#0000FF", s->p[0].p, s->p[1].p, 8);
return tri_no_intersection;
}
if (intersections == 1)
{
if (tri_bary_inside(tp0))
{
// if the intercept point on the segment is
// closer than the intercept point on the triangle edge,
// then there is no occlusion
if (is[0].p[2] <= it[0].p[2])
return tri_no_intersection;
// clipped from intersection to p1
s->p[0] = is[0];
return tri_clipped;
}
if (tri_bary_inside(tp1))
{
// if the intercept point on the segment is
// closer than the intercept point on the triangle edge,
// then there is no occlusion
if (is[0].p[2] < it[0].p[2])
return tri_no_intersection;
// clipped from p0 to intersection
s->p[1] = is[0];
return tri_clipped;
}
// something isn't right. maybe we have a small triangle?
fprintf(stderr, "ONE INTERSECTION?");
/*
svg_line("#FFFF00", s->p[0].p, s->p[1].p, 20);
svg_line("#000080", t->p[0].p, t->p[1].p, 8);
svg_line("#000080", t->p[1].p, t->p[2].p, 8);
svg_line("#000080", t->p[2].p, t->p[0].p, 8);
*/
seg_print(s);
v3_print(tp0);
v3_print(tp1);
tri_print(t);
*new_seg = seg_new(is[0], s->p[1]);
s->p[1] = is[0];
return tri_split;
}
// two intersections: find the one that is closer to p0
// modify the existing segment and create a new segment
const float d00 = v3_mag2(v3_sub(is[0], s->p[0]));
const float d01 = v3_mag2(v3_sub(is[1], s->p[0]));
const float d10 = v3_mag2(v3_sub(is[0], s->p[1]));
const float d11 = v3_mag2(v3_sub(is[1], s->p[1]));
// if any of the intersections points are zero from an
// end point on the segment, then skip that part
if (tri_debug > 4)
{
seg_print(s);
tri_print(t);
fprintf(stderr, "d: %f %f %f %f\n", d00, d01, d10, d11);
}
if (d00 < EPS && d11 < EPS)
return tri_hidden;
if (d01 < EPS && d10 < EPS)
return tri_hidden;
if (d00 < EPS)
{
s->p[0] = is[1];
return tri_clipped;
} else
if (d01 < EPS)
{
s->p[0] = is[0];
return tri_clipped;
} else
if (d10 < EPS)
{
s->p[1] = is[1];
return tri_clipped;
} else
if (d11 < EPS)
{
s->p[1] = is[0];
return tri_clipped;
}
// neither end points match, so create a new segment
// that excludes the space covered by the triangle.
// determine which is closer to point is[0]
if (d00 < d01)
{
// p0 is closer to is0, so new segment is is1 to p1
*new_seg = seg_new(is[1], s->p[1]);
s->p[1] = is[0];
} else {
// p0 is closer to is1, so new segment is is0 to p1
*new_seg = seg_new(is[0], s->p[1]);
s->p[1] = is[1];
}
fprintf(stderr, "SPLIT: ");
seg_print(*new_seg);
return tri_split;
}
int
tri_seg_hidden(
const tri_t * zlist,
seg_t * s,
seg_t ** slist_visible
)
{
int count = 0;
fprintf(stderr, "TEST: ");
seg_print(s);
for( const tri_t * t = zlist ; t ; t = t->next )
{
seg_t * new_seg = NULL;
//tri_print(t);
tri_intersect_t type = tri_seg_intersect(t, s, &new_seg);
//fprintf(stderr, "rc=%d\n", type);
// if there is no intersection or if the segment has
// been clipped on one side, keep looking
if (type == tri_no_intersection)
continue;
if (type == tri_clipped)
{
//fprintf(stderr, "CLIP: ");
seg_print(s);
continue;
}
// if this segment is infront of this triangle then we can
// stop searching
if (type == tri_infront)
break;
// if this segment is totally occluded, we're done
if (type == tri_hidden)
return count;
// if this line has been split into two, process the
// new segment starting at the next triangle since it
// has already intersected this one
if (type == tri_split)
{
static int recursive;
if (tri_debug > 4) fprintf(stderr, "RECURSIVE %d\n", recursive++);
int new_count = tri_seg_hidden(t->next, new_seg, slist_visible);
if (tri_debug > 4) fprintf(stderr, "END %d: %d segments\n", --recursive, new_count);
if (tri_debug > 4) fprintf(stderr, "CLIP: ");
if (tri_debug > 4) seg_print(s);
count += new_count;
continue;
}
fprintf(stderr, "unknown type %d\n", type);
return -1;
}
// we've reached the end and it is still visible
s->next = *slist_visible;
*slist_visible = s;
return ++count;
}

40
tri.h
View File

@ -6,6 +6,7 @@
#include "v3.h" #include "v3.h"
#include "seg.h" #include "seg.h"
#include "svg.h"
extern int tri_debug; extern int tri_debug;
@ -15,8 +16,8 @@ struct _tri_t
v3_t p[3]; // camera space v3_t p[3]; // camera space
v3_t normal; // camera space v3_t normal; // camera space
v3_t normal_xyz; // original xyz space v3_t normal_xyz; // original xyz space
float min[3]; // camera space v3_t min; // camera space
float max[3]; // camera space v3_t max; // camera space
tri_t * next; tri_t * next;
tri_t ** prev; tri_t ** prev;
}; };
@ -108,17 +109,18 @@ hidden_intersect(
/* /*
* Recursive algorithm:
* Given a line segment and a list of triangles, * Given a line segment and a list of triangles,
* find if the line segment crosses any triangle. * find if the line segment crosses any triangle.
* If it crosses a triangle the segment will be shortened * If it crosses a triangle the segment will be shortened
* and an additional one might be created. * and an additional one might be created.
* Recusively try intersecting the new segment (starting at the same triangle) * Recusively try intersecting the new segment (starting at the same triangle)
* and then continue trying the shortened segment. * and then continue trying the shortened segment.
*
* Line segments will be added to the visible list.
* Returns the number of new elements created
*/ */
int
void tri_seg_hidden(
tri_seg_intersect(
const tri_t * zlist, const tri_t * zlist,
seg_t * s, seg_t * s,
seg_t ** slist_visible seg_t ** slist_visible
@ -134,4 +136,30 @@ tri_behind(
const tri_t * const t2 const tri_t * const t2
); );
/*
* There are four possible line/triangle intersections.
*/
typedef enum {
tri_no_intersection, // nothing changed
tri_infront, // segment is in front of the triangle
tri_hidden, // segment is completely occluded
tri_clipped, // segment is partially occluded on one end
tri_split, // segment is partially occluded in the middle
} tri_intersect_t;
tri_intersect_t
tri_seg_intersect(
const tri_t * tri,
seg_t * s,
seg_t ** new_seg // only if tri_split
);
v3_t
tri_bary_coord(
const tri_t * const t,
const v3_t * const p
);
#endif #endif

View File

@ -92,7 +92,7 @@ edge_eq2(
const v3_t * const v10 = &t1->p[e1]; const v3_t * const v10 = &t1->p[e1];
const v3_t * const v11 = &t1->p[(e1+1) % 3]; const v3_t * const v11 = &t1->p[(e1+1) % 3];
if (v3_eq(v00, v11) && v3_eq(v01, v10)) if (v3_eq(*v00, *v11) && v3_eq(*v01, *v10))
return 1; return 1;
return 0; return 0;
@ -613,11 +613,11 @@ coplanar_check(
for (int i = 0 ; i < 3 ; i++) for (int i = 0 ; i < 3 ; i++)
{ {
x4 = f2->p[i]; x4 = f2->p[i];
if (v3_eq(&x1, &x4)) if (v3_eq(x1, x4))
continue; continue;
if (v3_eq(&x2, &x4)) if (v3_eq(x2, x4))
continue; continue;
if (v3_eq(&x3, &x4)) if (v3_eq(x3, x4))
continue; continue;
break; break;
} }

96
v3.h
View File

@ -5,9 +5,10 @@
#define _papercraft_v3_h_ #define _papercraft_v3_h_
#include <math.h> #include <math.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#define EPS 0.001 #define EPS 0.00001
#ifndef M_PI #ifndef M_PI
#define M_PI 3.1415926535897932384 #define M_PI 3.1415926535897932384
@ -52,13 +53,13 @@ typedef struct
static inline int static inline int
v3_eq( v3_eq(
const v3_t * v1, const v3_t v1,
const v3_t * v2 const v3_t v2
) )
{ {
float dx = v1->p[0] - v2->p[0]; float dx = v1.p[0] - v2.p[0];
float dy = v1->p[1] - v2->p[1]; float dy = v1.p[1] - v2.p[1];
float dz = v1->p[2] - v2->p[2]; float dz = v1.p[2] - v2.p[2];
if (-EPS < dx && dx < EPS if (-EPS < dx && dx < EPS
&& -EPS < dy && dy < EPS && -EPS < dy && dy < EPS
@ -209,6 +210,36 @@ v3_cross(
} }
static inline v3_t
v3_min(
const v3_t a,
const v3_t b
)
{
v3_t c = { {
min(a.p[0], b.p[0]),
min(a.p[1], b.p[1]),
min(a.p[2], b.p[2]),
}};
return c;
}
static inline v3_t
v3_max(
const v3_t a,
const v3_t b
)
{
v3_t c = { {
max(a.p[0], b.p[0]),
max(a.p[1], b.p[1]),
max(a.p[2], b.p[2]),
}};
return c;
}
// Compute the length of a line in screen space, ignoring Z // Compute the length of a line in screen space, ignoring Z
static inline float static inline float
v3_dist_2d( v3_dist_2d(
@ -223,4 +254,57 @@ v3_dist_2d(
} }
static inline void
v3_print(const v3_t p)
{
fprintf(stderr, "%+6.1f %+6.1f %+6.1f\n",
p.p[0],
p.p[1],
p.p[2]
);
}
typedef struct {
float p[4];
} v4_t;
typedef struct {
float m[4][4];
} m44_t;
static inline void
m44_mult(
m44_t * r,
const m44_t * a,
const m44_t * b
)
{
for(int i = 0 ; i < 4 ; i++)
{
for(int j = 0 ; j < 4 ; j++)
{
float d = 0;
for(int k = 0 ; k < 4 ; k++)
d += a->m[i][k] * b->m[k][j];
r->m[i][j] = d;
}
}
}
static inline v4_t
m44_multv(
const m44_t * const m,
const v4_t * const v
)
{
v4_t p = {};
for (int i = 0 ; i < 4 ; i++)
for (int j = 0 ; j < 4 ; j++)
p.p[i] += m->m[i][j] * v->p[j];
return p;
}
#endif #endif

View File

@ -62,7 +62,7 @@ coplanar_check(
for (int i = 0 ; i < 3 ; i++) for (int i = 0 ; i < 3 ; i++)
{ {
for (int j = 0 ; j < 3 ; j++) for (int j = 0 ; j < 3 ; j++)
if (v3_eq(&f1->p[i], &f2->p[j])) if (v3_eq(f1->p[i], f2->p[j]))
match[i] = 1; match[i] = 1;
} }
@ -132,7 +132,7 @@ coplanar_check(
return mask; return mask;
#else #else
// if the normals are close enough, then it is coplanner // if the normals are close enough, then it is coplanner
if (v3_eq(&f1->normal, &f2->normal)) if (v3_eq(f1->normal, f2->normal))
return mask; return mask;
else else
return 0; return 0;
@ -184,7 +184,7 @@ stl_vertex_find(
{ {
stl_vertex_t * const v = vertices[x]; stl_vertex_t * const v = vertices[x];
if (v3_eq(&v->p, p)) if (v3_eq(v->p, *p))
return v; return v;
} }