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