refactoring
[laserbrain_demo] / src / machine / gear.cc
diff --git a/src/machine/gear.cc b/src/machine/gear.cc
new file mode 100644 (file)
index 0000000..6a34815
--- /dev/null
@@ -0,0 +1,309 @@
+#include <stdlib.h>
+#include <GL/glew.h>
+#include "gear.h"
+#include "meshgen.h"
+#include "app.h"
+
+Gear::Gear()
+       : axis(0, 0, 1)
+{
+       pdist = 0;
+       angle = 0;
+       teeth_length = 5;
+       thickness = 5;
+       bevel = 1.5;
+       init_angle = 0;
+       xform_valid = false;
+
+       set_teeth(42, 10);
+
+       supergear = 0;
+
+       mesh = 0;
+
+       color = Vec3(0.6, 0.6, 0.6);
+       roughness = 1.0;
+       metallic = false;
+}
+
+Gear::~Gear()
+{
+       delete mesh;
+}
+
+void Gear::attach(Gear *g)
+{
+       if(g->supergear) {
+               if(g->supergear == this) {
+                       return;
+               }
+               g->supergear->detach(g);
+       }
+       g->supergear = this;
+       subgears.push_back(g);
+
+       // make co-axial
+       g->axis = axis;
+       g->pos.x = pos.x;
+       g->pos.y = pos.y;
+}
+
+bool Gear::detach(Gear *g)
+{
+       int nsubgears = (int)subgears.size();
+       for(int i=0; i<nsubgears; i++) {
+               if(subgears[i] == g) {
+                       subgears.erase(subgears.begin() + i);
+                       g->supergear = 0;
+                       return true;
+               }
+       }
+       return false;
+}
+
+Gear *Gear::get_super() const
+{
+       return supergear;
+}
+
+void Gear::set_angular_offset(float offs)
+{
+       init_angle = offs;
+       xform_valid = false;
+}
+
+float Gear::get_angular_offset() const
+{
+       return init_angle;
+}
+
+void Gear::set_teeth(int nt, float tooth_pitch)
+{
+       if(tooth_pitch <= 0) {
+               tooth_pitch = this->tooth_pitch;
+       } else {
+               this->tooth_pitch = tooth_pitch;
+       }
+       float circ = tooth_pitch * nt;
+       radius = circ / (2.0 * M_PI);
+       nteeth = nt;
+}
+
+void Gear::set_axis(const Vec3 &axis)
+{
+       this->axis = normalize(axis);
+       xform_valid = false;
+}
+
+const Vec3 &Gear::get_axis() const
+{
+       return axis;
+}
+
+void Gear::set_position(const Vec3 &pos)
+{
+       if(!supergear) {
+               this->pos = pos;
+       } else {
+               this->pos = supergear->pos;
+               this->pos.z = pos.z;
+       }
+       xform_valid = false;
+
+       for(int i=0; i<(int)subgears.size(); i++) {
+               Vec3 subpos = this->pos;
+               subpos.z = subgears[i]->pos.z;
+               subgears[i]->set_position(subpos);
+       }
+}
+
+const Vec3 &Gear::get_position() const
+{
+       return pos;
+}
+
+Vec3 Gear::get_global_position() const
+{
+       const Mat4 &m = get_matrix();
+       return m * Vec3(0, 0, 0);
+}
+
+void Gear::set_angle(float angle)
+{
+       this->angle = angle;
+       xform_valid = false;
+}
+
+float Gear::get_angle() const
+{
+       return angle;
+}
+
+float Gear::get_vis_rotation() const
+{
+       return fmod(init_angle + angle, M_PI * 2.0);
+}
+
+const Mat4 &Gear::get_matrix() const
+{
+       if(!xform_valid) {
+               calc_matrix();
+               xform_valid = true;
+       }
+       return xform;
+}
+
+const Mat4 &Gear::get_dir_matrix() const
+{
+       if(!xform_valid) {
+               calc_matrix();
+               xform_valid = true;
+       }
+       return dir_xform;
+}
+
+float Gear::get_angular_pitch() const
+{
+       return 2.0 * M_PI / (float)nteeth;
+}
+
+void Gear::draw() const
+{
+       if(!mesh) {
+               if(!((Gear*)this)->gen_mesh()) {
+                       abort();
+               }
+       }
+       if(!xform_valid) {
+               calc_matrix();
+       }
+
+       glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_LIGHTING_BIT);
+
+       glPushMatrix();
+       glMultMatrixf(xform[0]);
+
+       if(opt_gear_wireframe) {
+               glPolygonOffset(1, 1);
+               glEnable(GL_POLYGON_OFFSET_FILL);
+       }
+
+       Vec3 diffuse = metallic ? Vec3(0, 0, 0) : color;
+       float col[] = {diffuse.x, diffuse.y, diffuse.z, 1.0};
+       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
+
+       Vec3 specular = (metallic ? color : Vec3(1, 1, 1)) * (1.0 - roughness);
+       float scol[] = {specular.x, specular.y, specular.z, 1.0};
+       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, scol);
+       glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 1.0 + (1.0 - roughness) * 60.0);
+
+       mesh->draw();
+
+       glDisable(GL_LIGHTING);
+
+       if(opt_gear_wireframe) {
+               glColor3f(0.2, 0.4, 1.0);
+               mesh->draw_wire();
+       }
+
+       glPopMatrix();
+       glPopAttrib();
+}
+
+void Gear::draw_wire(float wire_width) const
+{
+       if(!mesh) {
+               if(!((Gear*)this)->gen_mesh()) {
+                       abort();
+               }
+       }
+       if(!xform_valid) {
+               calc_matrix();
+       }
+
+       glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT);
+       glLineWidth(wire_width);
+       glDisable(GL_LIGHTING);
+
+       glPushMatrix();
+       glMultMatrixf(xform[0]);
+
+       mesh->draw_wire();
+
+       glPopMatrix();
+       glPopAttrib();
+}
+
+static Vec2 rev_pos(float u, float v, void *cls)
+{
+       Gear *gear = (Gear*)cls;
+
+       float y = ((v - 1.0 / 3.0) / (1.0 / 3.0) - 0.5);
+       if(y < -0.5) y = -0.5;
+       if(y > 0.5) y = 0.5;
+       y *= gear->thickness;
+
+       if(v < 0.001 || v > 0.999) {
+               return Vec2(0, -y);
+       }
+
+       float nt = (float)gear->nteeth * 4.0;
+
+       int part = (int)(u * nt) % 4;   /* 4 parts to a tooth /#\_ */
+       float t = fmod(u * nt * 4.0, 1.0);
+
+       float offs;
+       switch(part) {
+       case 0:
+               offs = t;
+               break;
+       case 1:
+               offs = 1.0f;
+               break;
+       case 2:
+               offs = 1.0f - t;
+               break;
+       case 3:
+               offs = 0.0f;
+       }
+
+       float inner_rad = gear->radius - gear->teeth_length;
+       return Vec2(inner_rad + offs * gear->teeth_length, -y);
+}
+
+bool Gear::gen_mesh()
+{
+       mesh = new Mesh;
+       gen_revol(mesh, nteeth * 4, 3, rev_pos, 0, this);
+       mesh->explode();
+       mesh->calc_face_normals();
+
+       float fix_tooth_up = get_angular_pitch() * 3.0 / 8.0;
+
+       Mat4 rot;
+       rot.rotation_x(M_PI / 2.0);
+       rot.rotate_z(fix_tooth_up);
+       mesh->apply_xform(rot, rot);
+
+       mesh->set_vis_vecsize(6.0);
+       return true;
+}
+
+void Gear::calc_matrix() const
+{
+       Vec3 up = Vec3(0, 1, 0);
+       if(1.0 - fabs(dot(up, axis)) < 1e-4) {
+               up = Vec3(0, 0, -1);
+       }
+       Vec3 right = normalize(cross(up, axis));
+       up = cross(axis, right);
+
+       dir_xform = Mat4(right, up, axis);
+
+       xform = dir_xform;
+       xform.rotate_z(get_vis_rotation());
+       xform.translate(pos);
+
+       axel_xform = dir_xform;
+       axel_xform.translate(pos);
+}