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