-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcylinder_ring.cpp
154 lines (136 loc) · 5.01 KB
/
cylinder_ring.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "utils.h"
#include "argparser.h"
#include "material.h"
#include "cylinder_ring.h"
#include "mesh.h"
#include "ray.h"
#include "hit.h"
// ====================================================================
// ====================================================================
// HELPER FUNCTIONS FOR RING INTERSECTION
bool IntersectFiniteCylinder(const Ray &r, const Vec3f ¢er, double radius, double height, double &t, Vec3f &normal) {
// assumes cylinder is aligned with the y axis
const Vec3f &ori = r.getOrigin();
const Vec3f &dir = r.getDirection();
// insert explict ray equation into implicit cylinder equation and
// solve for t using the quadratic formula
double A = dir.x()*dir.x() +dir.z()*dir.z();
double B = 2 * (dir.x()*ori.x() + dir.z()*ori.z());
double C = ori.x()*ori.x() + ori.z()*ori.z() - radius*radius;
// if the solutions are imaginary, no intersection!
double radical = B*B - 4*A*C;
if (radical < EPSILON) return false;
// there might be 2 solutions...
radical = sqrt(radical);
double t_m = (-B - radical) / (2*A);
double t_p = (-B + radical) / (2*A);
assert (t_m <= t_p);
Vec3f pt_m = r.pointAtParameter(t_m);
t = t_m;
// select the correct solution, the smallest non-negative solution
if (t_m < EPSILON ||
pt_m.y() > center.y() + height/2.0 ||
pt_m.y() < center.y() - height/2.0) {
t = t_p;
}
Vec3f pt = r.pointAtParameter(t);
if (t < EPSILON ||
pt.y() > center.y() + height/2.0 ||
pt.y() < center.y() - height/2.0) {
return false;
}
// compute the normal at the interesection point
normal = Vec3f(pt.x()-center.x(),0,pt.z()-center.z());
normal.Normalize();
return true;
}
bool IntersectAnnulus(const Ray &r, const Vec3f ¢er, double inner_radius, double outer_radius,
double &t, Vec3f &normal) {
// assume annulus is parallel to the y = 0 plane
const Vec3f &ori = r.getOrigin();
const Vec3f &dir = r.getDirection();
assert (r.getDirection().Length() > 0.9);
t = (center.y()-ori.y()) / dir.y();
// check if intersection is behind the eye
if (t < EPSILON) return false;
// check to see that the interesection is "between" the two circles
Vec3f pt = r.pointAtParameter(t);
double test = sqrt((pt.x()-center.x())*(pt.x()-center.x())+
(pt.z()-center.z())*(pt.z()-center.z()));
if (test < inner_radius || test > outer_radius) return false;
normal = Vec3f(0,1,0);
return true;
}
bool CylinderRing::intersect(const Ray &r, Hit &h) const {
// intersect with the 4 parts of the ring
double outer_t;
Vec3f outer_normal;
bool outer = IntersectFiniteCylinder(r,center,outer_radius,height,outer_t,outer_normal);
double inner_t;
Vec3f inner_normal;
bool inner = IntersectFiniteCylinder(r,center,inner_radius,height,inner_t,inner_normal);
double top_t;
Vec3f top_normal;
bool top = IntersectAnnulus(r,center+Vec3f(0,height/2.0,0),inner_radius,outer_radius,top_t,top_normal);
double bottom_t;
Vec3f bottom_normal;
bool bottom = IntersectAnnulus(r,center-Vec3f(0,height/2.0,0),inner_radius,outer_radius,bottom_t,bottom_normal);
bool answer = false;
// return the closest intersection
if (outer && (outer_t < h.getT())) {
h.set(outer_t,this->getMaterial(),outer_normal);
answer = true;
}
if (inner && (inner_t < h.getT())) {
h.set(inner_t,this->getMaterial(),-inner_normal);
answer = true;
}
if (top && (top_t < h.getT())) {
h.set(top_t,this->getMaterial(),top_normal);
answer = true;
}
if (bottom && (bottom_t < h.getT())) {
h.set(bottom_t,this->getMaterial(),-bottom_normal);
answer = true;
}
return answer;
}
// ====================================================================
// ====================================================================
// helper function to place a grid of points on the cylinderRing
Vec3f ComputeCylinderRingPoint(double s, const Vec3f center, double radius, double height) {
double angle = 2*M_PI*s;
double x = radius*cos(angle);
double z = radius*-sin(angle);
Vec3f answer = center + Vec3f(x,height,z);
return answer;
}
void CylinderRing::addRasterizedFaces(Mesh *m, ArgParser *args) {
int crr = args->cylinder_ring_rasterization;
int i;
int va,vb,vc,vd;
Vertex *a,*b,*c,*d;
int offset = m->numVertices();
// place vertices
for (i = 0; i < crr; i++) {
double s = i / double(crr);
m->addVertex(ComputeCylinderRingPoint(s,center,outer_radius,-height/2.0));
m->addVertex(ComputeCylinderRingPoint(s,center,outer_radius,+height/2.0));
m->addVertex(ComputeCylinderRingPoint(s,center,inner_radius,+height/2.0));
m->addVertex(ComputeCylinderRingPoint(s,center,inner_radius,-height/2.0));
}
// the patches
for (i = 0; i < crr; i++) {
for (int j = 0; j < 4; j++) {
va = 4*i + j;
vb = 4*((i+1)%crr) + j;
vc = 4*((i+1)%crr)+ (j+1)%4;
vd = 4*i + (j+1)%4;
a = m->getVertex(offset + va);
b = m->getVertex(offset + vb);
c = m->getVertex(offset + vc);
d = m->getVertex(offset + vd);
m->addRasterizedPrimitiveFace(a,b,c,d,material);
}
}
}