10e8bc89b1535a9ed0fa1f695ed798a1b015b86a
[antikythera] / src / gear.cc
1 #include <GL/glew.h>
2 #include "gear.h"
3 #include "meshgen.h"
4 #include "app.h"
5
6 Gear::Gear()
7         : axis(0, 0, 1)
8 {
9         pdist = 0;
10         angle = 0;
11         nteeth = 42;
12         radius = 80;
13         teeth_length = 5;
14         thickness = 5;
15         bevel = 1.5;
16         init_angle = 0;
17         xform_valid = false;
18
19         mesh = 0;
20 }
21
22 Gear::~Gear()
23 {
24         delete mesh;
25 }
26
27 void Gear::set_teeth(int nt, float tooth_pitch)
28 {
29         float circ = tooth_pitch * nt;
30         radius = circ / (2.0 * M_PI);
31         nteeth = nt;
32 }
33
34 void Gear::set_axis(const Vec3 &axis)
35 {
36         this->axis = normalize(axis);
37         xform_valid = false;
38 }
39
40 void Gear::set_position(const Vec3 &pos)
41 {
42         this->pos = pos;
43         xform_valid = false;
44 }
45
46 const Vec3 &Gear::get_position() const
47 {
48         return pos;
49 }
50
51 Vec3 Gear::get_global_position() const
52 {
53         return pos;     // TODO
54 }
55
56 Vec3 Gear::get_planar_position() const
57 {
58         Mat4 inv_xform = transpose(get_dir_matrix());
59         return inv_xform * pos;
60 }
61
62 void Gear::set_angle(float angle)
63 {
64         this->angle = angle;
65         xform_valid = false;
66 }
67
68 float Gear::get_angle() const
69 {
70         return angle;
71 }
72
73 float Gear::get_vis_rotation() const
74 {
75         float fix_crooked_teeth = get_angular_pitch() * 3.0 / 8.0;
76         return fmod(init_angle + fix_crooked_teeth + angle, M_PI * 2.0);
77 }
78
79 const Mat4 &Gear::get_matrix() const
80 {
81         if(!xform_valid) {
82                 calc_matrix();
83                 xform_valid = true;
84         }
85         return xform;
86 }
87
88 const Mat4 &Gear::get_dir_matrix() const
89 {
90         if(!xform_valid) {
91                 calc_matrix();
92                 xform_valid = true;
93         }
94         return dir_xform;
95 }
96
97 float Gear::get_angular_pitch() const
98 {
99         return 2.0 * M_PI / (float)nteeth;
100 }
101
102 void Gear::draw() const
103 {
104         if(!mesh) {
105                 if(!((Gear*)this)->gen_mesh()) {
106                         abort();
107                 }
108         }
109         if(!xform_valid) {
110                 calc_matrix();
111         }
112
113         glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
114
115         glPushMatrix();
116         glMultMatrixf(xform[0]);
117
118         if(opt_gear_wireframe) {
119                 glPolygonOffset(1, 1);
120                 glEnable(GL_POLYGON_OFFSET_FILL);
121         }
122
123         mesh->draw();
124
125         glDisable(GL_LIGHTING);
126
127         if(opt_gear_wireframe) {
128                 glColor3f(0.2, 0.4, 1.0);
129                 mesh->draw_wire();
130         }
131
132         glLineWidth(2.0);
133         glBegin(GL_LINES);
134         glColor3f(0, 0, 1);
135         glVertex3f(0, 0, -10);
136         glVertex3f(0, 0, 10);
137         glEnd();
138
139         glPopMatrix();
140         glPopAttrib();
141 }
142
143 void Gear::draw_wire(float wire_width) const
144 {
145         if(!mesh) {
146                 if(!((Gear*)this)->gen_mesh()) {
147                         abort();
148                 }
149         }
150         if(!xform_valid) {
151                 calc_matrix();
152         }
153
154         glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
155         glLineWidth(wire_width);
156         glDisable(GL_LIGHTING);
157
158         glPushMatrix();
159         glMultMatrixf(xform[0]);
160
161         mesh->draw_wire();
162
163         glPopMatrix();
164         glPopAttrib();
165 }
166
167 static Vec2 rev_pos(float u, float v, void *cls)
168 {
169         Gear *gear = (Gear*)cls;
170
171         float y = ((v - 1.0 / 3.0) / (1.0 / 3.0) - 0.5);
172         if(y < -0.5) y = -0.5;
173         if(y > 0.5) y = 0.5;
174         y *= gear->thickness;
175
176         if(v < 0.001 || v > 0.999) {
177                 return Vec2(0, -y);
178         }
179
180         float nt = (float)gear->nteeth * 4.0;
181
182         int part = (int)(u * nt) % 4;   /* 4 parts to a tooth /#\_ */
183         float t = fmod(u * nt * 4.0, 1.0);
184
185         float offs;
186         switch(part) {
187         case 0:
188                 offs = t;
189                 break;
190         case 1:
191                 offs = 1.0f;
192                 break;
193         case 2:
194                 offs = 1.0f - t;
195                 break;
196         case 3:
197                 offs = 0.0f;
198         }
199
200         float inner_rad = gear->radius - gear->teeth_length;
201         return Vec2(inner_rad + offs * gear->teeth_length, -y);
202 }
203
204 bool Gear::gen_mesh()
205 {
206         mesh = new Mesh;
207         gen_revol(mesh, nteeth * 4, 3, rev_pos, 0, this);
208         mesh->explode();
209         mesh->calc_face_normals();
210
211         Mat4 rot;
212         rot.rotation_x(M_PI / 2.0);
213         mesh->apply_xform(rot, rot);
214
215         mesh->set_vis_vecsize(6.0);
216         return true;
217 }
218
219 void Gear::calc_matrix() const
220 {
221         Vec3 up = Vec3(0, 1, 0);
222         if(1.0 - fabs(dot(up, axis)) < 1e-4) {
223                 up = Vec3(0, 0, -1);
224         }
225         Vec3 right = normalize(cross(up, axis));
226         up = cross(axis, right);
227
228         dir_xform = Mat4(right, up, axis);
229
230         xform = dir_xform;
231         xform.rotate_z(get_vis_rotation());
232         xform.translate(pos);
233 }