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