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