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