- added subgear linkage
[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         supergear = 0;
20
21         mesh = 0;
22 }
23
24 Gear::~Gear()
25 {
26         delete mesh;
27 }
28
29 void Gear::attach(Gear *g)
30 {
31         if(g->supergear) {
32                 if(g->supergear == this) {
33                         return;
34                 }
35                 g->supergear->detach(g);
36         }
37         g->supergear = this;
38         subgears.push_back(g);
39
40         // make co-axial
41         g->axis = axis;
42         g->pos.x = pos.x;
43         g->pos.y = pos.y;
44 }
45
46 bool Gear::detach(Gear *g)
47 {
48         int nsubgears = (int)subgears.size();
49         for(int i=0; i<nsubgears; i++) {
50                 if(subgears[i] == g) {
51                         subgears.erase(subgears.begin() + i);
52                         g->supergear = 0;
53                         return true;
54                 }
55         }
56         return false;
57 }
58
59 Gear *Gear::get_super() const
60 {
61         return supergear;
62 }
63
64 void Gear::set_angular_offset(float offs)
65 {
66         init_angle = offs;
67         xform_valid = false;
68 }
69
70 float Gear::get_angular_offset() const
71 {
72         return init_angle;
73 }
74
75 void Gear::set_teeth(int nt, float tooth_pitch)
76 {
77         float circ = tooth_pitch * nt;
78         radius = circ / (2.0 * M_PI);
79         nteeth = nt;
80 }
81
82 void Gear::set_axis(const Vec3 &axis)
83 {
84         this->axis = normalize(axis);
85         xform_valid = false;
86 }
87
88 const Vec3 &Gear::get_axis() const
89 {
90         return axis;
91 }
92
93 void Gear::set_position(const Vec3 &pos)
94 {
95         if(!supergear) {
96                 this->pos = pos;
97                 xform_valid = false;
98         } else {
99                 if(fabs(this->pos.z - pos.z) > 1e-5) {
100                         this->pos.z = pos.z;
101                         xform_valid = false;
102                 }
103         }
104 }
105
106 const Vec3 &Gear::get_position() const
107 {
108         return pos;
109 }
110
111 Vec3 Gear::get_global_position() const
112 {
113         const Mat4 &m = get_matrix();
114         return m * Vec3(0, 0, 0);
115 }
116
117 void Gear::set_angle(float angle)
118 {
119         this->angle = angle;
120         xform_valid = false;
121 }
122
123 float Gear::get_angle() const
124 {
125         return angle;
126 }
127
128 float Gear::get_vis_rotation() const
129 {
130         return fmod(init_angle + angle, M_PI * 2.0);
131 }
132
133 const Mat4 &Gear::get_matrix() const
134 {
135         if(!xform_valid) {
136                 calc_matrix();
137                 xform_valid = true;
138         }
139         return xform;
140 }
141
142 const Mat4 &Gear::get_dir_matrix() const
143 {
144         if(!xform_valid) {
145                 calc_matrix();
146                 xform_valid = true;
147         }
148         return dir_xform;
149 }
150
151 float Gear::get_angular_pitch() const
152 {
153         return 2.0 * M_PI / (float)nteeth;
154 }
155
156 void Gear::draw() const
157 {
158         if(!mesh) {
159                 if(!((Gear*)this)->gen_mesh()) {
160                         abort();
161                 }
162         }
163         if(!xform_valid) {
164                 calc_matrix();
165         }
166
167         glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
168
169         glPushMatrix();
170         glMultMatrixf(xform[0]);
171
172         if(opt_gear_wireframe) {
173                 glPolygonOffset(1, 1);
174                 glEnable(GL_POLYGON_OFFSET_FILL);
175         }
176
177         mesh->draw();
178
179         glDisable(GL_LIGHTING);
180
181         if(opt_gear_wireframe) {
182                 glColor3f(0.2, 0.4, 1.0);
183                 mesh->draw_wire();
184         }
185
186         glLineWidth(2.0);
187         glBegin(GL_LINES);
188         glColor3f(0, 0, 1);
189         glVertex3f(0, 0, -10);
190         glVertex3f(0, 0, 10);
191         glEnd();
192
193         glPopMatrix();
194         glPopAttrib();
195 }
196
197 void Gear::draw_wire(float wire_width) const
198 {
199         if(!mesh) {
200                 if(!((Gear*)this)->gen_mesh()) {
201                         abort();
202                 }
203         }
204         if(!xform_valid) {
205                 calc_matrix();
206         }
207
208         glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
209         glLineWidth(wire_width);
210         glDisable(GL_LIGHTING);
211
212         glPushMatrix();
213         glMultMatrixf(xform[0]);
214
215         mesh->draw_wire();
216
217         glPopMatrix();
218         glPopAttrib();
219 }
220
221 static Vec2 rev_pos(float u, float v, void *cls)
222 {
223         Gear *gear = (Gear*)cls;
224
225         float y = ((v - 1.0 / 3.0) / (1.0 / 3.0) - 0.5);
226         if(y < -0.5) y = -0.5;
227         if(y > 0.5) y = 0.5;
228         y *= gear->thickness;
229
230         if(v < 0.001 || v > 0.999) {
231                 return Vec2(0, -y);
232         }
233
234         float nt = (float)gear->nteeth * 4.0;
235
236         int part = (int)(u * nt) % 4;   /* 4 parts to a tooth /#\_ */
237         float t = fmod(u * nt * 4.0, 1.0);
238
239         float offs;
240         switch(part) {
241         case 0:
242                 offs = t;
243                 break;
244         case 1:
245                 offs = 1.0f;
246                 break;
247         case 2:
248                 offs = 1.0f - t;
249                 break;
250         case 3:
251                 offs = 0.0f;
252         }
253
254         float inner_rad = gear->radius - gear->teeth_length;
255         return Vec2(inner_rad + offs * gear->teeth_length, -y);
256 }
257
258 bool Gear::gen_mesh()
259 {
260         mesh = new Mesh;
261         gen_revol(mesh, nteeth * 4, 3, rev_pos, 0, this);
262         mesh->explode();
263         mesh->calc_face_normals();
264
265         float fix_tooth_up = get_angular_pitch() * 3.0 / 8.0;
266
267         Mat4 rot;
268         rot.rotation_x(M_PI / 2.0);
269         rot.rotate_z(fix_tooth_up);
270         mesh->apply_xform(rot, rot);
271
272         mesh->set_vis_vecsize(6.0);
273         return true;
274 }
275
276 void Gear::calc_matrix() const
277 {
278         Vec3 up = Vec3(0, 1, 0);
279         if(1.0 - fabs(dot(up, axis)) < 1e-4) {
280                 up = Vec3(0, 0, -1);
281         }
282         Vec3 right = normalize(cross(up, axis));
283         up = cross(axis, right);
284
285         dir_xform = Mat4(right, up, axis);
286
287         xform = dir_xform;
288         xform.rotate_z(get_vis_rotation());
289         xform.translate(pos);
290
291         axel_xform = dir_xform;
292         axel_xform.translate(pos);
293 }