/************************************************************************ Arcball rotation control. See the original article "Arcball Rotation Control" by Ken Shoemake in "Graphics Gems IV", Academic Press, 1994. for more details. $Id: arcball.cxx 427 2004-09-27 04:45:31Z garland $ ************************************************************************/ #include #include #include namespace gfx { // Converts a unit quaternion to two points on the unit sphere static void quat_to_sphere(const Quat& q, Vec3& from, Vec3& to) { const Vec3& v = q.vector(); double s = sqrt(v[0]*v[0] + v[1]*v[1]); if( s==0.0 ) from = Vec3(0.0, 1.0, 0.0); else from = Vec3(-v[1]/s, v[0]/s, 0.0); to[0] = q.scalar()*from[0] - v[2]*from[1]; to[1] = q.scalar()*from[1] + v[2]*from[2]; to[2] = v[0]*from[1] - v[1]*from[0]; if(q.scalar() < 0.0) from = -from; } // Converts to points on unit sphere into a unit quaternion static Quat quat_from_sphere(const Vec3& from, const Vec3& to) { Vec3 v; v[0] = from[1]*to[2] - from[2]*to[1]; v[1] = from[2]*to[0] - from[0]*to[2]; v[2] = from[0]*to[1] - from[1]*to[0]; double s = from*to; return Quat(v, s); } Vec3 Arcball::proj_to_sphere(const Vec2& mouse) { Vec2 p = (mouse - ball_ctr) / ball_radius; double mag = p*p; if( mag > 1.0 ) { double s = sqrt(mag); return Vec3(p[0]/s, p[1]/s, 0.0); } else { return Vec3(p[0], p[1], sqrt(1-mag)); } } void Arcball::update() { // constrain v_from & v_to to axes here, if necessary if( is_dragging ) { q_drag = quat_from_sphere(v_from, v_to); q_now = q_drag * q_down; } } Arcball::Arcball() { ball_ctr = Vec2(0, 0); ball_radius = 1.0; q_now = Quat::ident(); q_down = Quat::ident(); q_drag = Quat::ident(); is_dragging = false; } bool Arcball::mouse_down(int *where, int which) { float vp[4]; glGetFloatv(GL_VIEWPORT, vp); float W=vp[2], H=vp[3]; if( which==1 ) { is_dragging = true; Vec2 v( (2.0 * where[0] - W)/W, (H - 2.0 * where[1])/H ); v_from = proj_to_sphere(v); v_to = v_from; } return true; } bool Arcball::mouse_up(int *where, int which) { is_dragging = false; q_down = q_now; q_drag = Quat::ident(); return false; } bool Arcball::mouse_drag(int *where, int *last, int which) { float vp[4]; glGetFloatv(GL_VIEWPORT, vp); float W=vp[2], H=vp[3]; float diam = 2*radius; if( which==1 ) { Vec2 v( (2.0 * where[0] - W)/W, (H - 2.0 * where[1])/H ); v_to = proj_to_sphere(v); } else if( which==2 ) { trans[0] += diam * (where[0] - last[0]) / W; trans[1] += diam * (last[1] - where[1]) / H; } else if( which==3 ) { trans[2] += 0.02*diam*(where[1] - last[1]); } else return false; return true; } void Arcball::apply_transform() { update(); curquat = conjugate(q_now); Baseball::apply_transform(); } void Arcball::update_animation() { } void Arcball::get_transform(Vec3 & c, Vec3 &t, Quat & q) { c = ctr; t = trans; q = q_now; } void Arcball::set_transform(const Vec3 & c, const Vec3 &t, const Quat & q) { ctr = c; trans = t; q_now = q; q_down = q; q_drag = q; } void Arcball::write(std::ostream& out) { out << "arcball "; out << ball_ctr << " " << ball_radius << " "; out << q_now << " " << q_down << " " << q_drag << std::endl; Baseball::write(out); } void Arcball::read(std::istream& in) { std::string name; in >> name; in >> ball_ctr >> ball_radius; in >> q_now >> q_down >> q_drag; Baseball::read(in); } } // namespace gfx