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