// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_sg_base_camera #define tools_sg_base_camera #include "node" #include "sf_vec3f" #include "sf_vec4f" #include "sf_rotf" #include "render_action" #include "pick_action" #include "event_action" #include "visible_action" #include "enums" #include "../mathf" //astro namespace tools { namespace sg { class base_camera : public node { TOOLS_HEADER(base_camera,tools::sg::base_camera,node) public: sf znear; sf zfar; sf_vec3f position; //Camera orientation specified as a rotation value from the default //orientation where the camera is pointing along the negative z-axis, //with "up" along the positive y-axis. sf_rotf orientation; //for viewers : sf dx; sf da; sf ds; sf focal; public: virtual const std::vector& node_fields() const { TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::base_camera) static std::vector s_v; if(s_v.empty()) { s_v = parent::node_fields(); TOOLS_ADD_FIELD_DESC(znear) TOOLS_ADD_FIELD_DESC(zfar) TOOLS_ADD_FIELD_DESC(position) TOOLS_ADD_FIELD_DESC(orientation) TOOLS_ADD_FIELD_DESC(dx) TOOLS_ADD_FIELD_DESC(da) TOOLS_ADD_FIELD_DESC(ds) TOOLS_ADD_FIELD_DESC(focal) } return s_v; } private: void add_fields(){ add_field(&znear); add_field(&zfar); add_field(&position); add_field(&orientation); add_field(&dx); add_field(&da); add_field(&ds); add_field(&focal); } public: virtual float near_height() const = 0; virtual void zoom(float) = 0; virtual camera_type type() const = 0; virtual void get_lrbt(unsigned int,unsigned int, float&,float&,float&,float&) = 0; public: virtual void render(render_action& a_action) { _mult_matrix(a_action); set_state(a_action); a_action.load_proj_matrix(a_action.projection_matrix()); a_action.load_model_matrix(a_action.model_matrix()); } virtual void pick(pick_action& a_action) { _mult_matrix(a_action); set_state(a_action); } virtual void event(event_action& a_action){ _mult_matrix(a_action); set_state(a_action); } virtual void get_matrix(get_matrix_action& a_action){ _mult_matrix(a_action); set_state(a_action); } virtual void is_visible(visible_action& a_action){ _mult_matrix(a_action); set_state(a_action); } protected: base_camera() :parent() ,znear(1) ,zfar(10) ,position(vec3f(0,0,1)) ,orientation(rotf(vec3f(0,0,1),0)) //quat = vec4f(0,0,0,1) ,dx(0.01f) ,da(0.017f) //one degree. ,ds(0.99f) ,focal(1) { #ifdef TOOLS_MEM mem::increment(s_class().c_str()); #endif add_fields(); } public: virtual ~base_camera(){ #ifdef TOOLS_MEM mem::decrement(s_class().c_str()); #endif } protected: base_camera(const base_camera& a_from) :parent(a_from) ,znear(a_from.znear) ,zfar(a_from.zfar) ,position(a_from.position) ,orientation(a_from.orientation) ,dx(a_from.dx) ,da(a_from.da) ,ds(a_from.ds) ,focal(a_from.focal) { #ifdef TOOLS_MEM mem::increment(s_class().c_str()); #endif add_fields(); } base_camera& operator=(const base_camera& a_from){ parent::operator=(a_from); znear = a_from.znear; zfar = a_from.zfar; position = a_from.position; orientation = a_from.orientation; dx = a_from.dx; da = a_from.da; ds = a_from.ds; focal = a_from.focal; m_lrbt.set_value(0,0,0,0); return *this; } protected: //operators: bool operator==(const base_camera& a_from) const{ if(znear!=a_from.znear) return false; if(zfar!=a_from.zfar) return false; if(position!=a_from.position) return false; if(orientation!=a_from.orientation) return false; //we do not test dx,da,ds. return true; } //bool operator!=(const base_camera& a_from) const { // return !operator==(a_from); //} public: void direction(vec3f& a_dir) const { orientation.value().mul_vec(vec3f(0,0,-1),a_dir); } void rotate_around_direction(float a_delta) { //used in exlib/examples/iOS/sg. vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); //_MSC_VER : does not compile : orientation = rotf(dir,a_delta) * orientation; orientation.value(rotf(dir,a_delta) * orientation.value()); } void rotate_around_up(float a_delta){ vec3f up; orientation.value().mul_vec(vec3f(0,1,0),up); //orientation.value(rotf(up,a_delta) * orientation.value()); // must be the below so that rot-cam works for exlib/cbk/[pyrfits,astro,cfitsio] astro setup. // (astro setup change camera orientation). orientation.value(orientation.value() * rotf(up,a_delta)); } void rotate_around_y_at_focal(float a_delta){ //from coin SoGuiExaminerViewerP::rotYWheelMotion. vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); vec3f focalpoint = position.value() + focal * dir; orientation.value(rotf(vec3f(0,1,0),a_delta) * orientation.value()); orientation.value().mul_vec(vec3f(0,0,-1),dir); position = focalpoint - focal * dir; } void rotate_around_x_at_focal(float a_delta){ //from coin SoGuiExaminerViewerP::rotXWheelMotion. vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); vec3f focalpoint = position.value() + focal * dir; orientation.value(rotf(vec3f(1,0,0),a_delta) * orientation.value()); orientation.value().mul_vec(vec3f(0,0,-1),dir); position = focalpoint - focal * dir; } void rotate_to_dir(const vec3f& a_dir) { //rotate around up so that a_dir is in (dir,up) plane //NOTE : it is the invert of orientation which is used // in projection matrix. {vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); vec3f up; orientation.value().mul_vec(vec3f(0,1,0),up); vec3f side;dir.cross(up,side); vec3f v = side * (side.dot(a_dir)) + dir * (dir.dot(a_dir)); if(v.normalize()) orientation.value(orientation.value()*rotf(dir,v));} //rotate around dir^up so that a_dir matches dir. {vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); orientation.value(orientation.value()*rotf(dir,a_dir));} /* //check that dir is on a_dir : {vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); float cos_angle; //it should be 1 if(!dir.cos_angle(a_dir,cos_angle)) { ::printf("debug : can't get angle\n"); return; } ::printf("debug : cos_angle %g\n",cos_angle);} */ } void pane_to(float a_x,float a_y,float a_z){ //translate in view plane so that (a_x,a_y,a_z) is on direction. vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); vec3f up; orientation.value().mul_vec(vec3f(0,1,0),up); vec3f side;dir.cross(up,side); vec3f d(a_x,a_y,a_z); d.subtract(position.value()); vec3f pos = position.value() + side * (side.dot(d)) + up * (up.dot(d)); position.value(pos); } void translate_along_side(float a_delta){ vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); vec3f up; orientation.value().mul_vec(vec3f(0,1,0),up); vec3f side;dir.cross(up,side); vec3f pos = position.value() + side * a_delta; position.value(pos); } void translate_along_up(float a_delta){ vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); vec3f up; orientation.value().mul_vec(vec3f(0,1,0),up); vec3f pos = position.value() + up * a_delta; position.value(pos); } void translate_along_dir(float a_delta){ vec3f dir; orientation.value().mul_vec(vec3f(0,0,-1),dir); vec3f pos = position.value() + dir * a_delta; position.value(pos); } //NOTE : print is a Python keyword. void dump(std::ostream& a_out) { a_out << " znear " << znear.value() << std::endl; a_out << " zfar " << zfar.value() << std::endl; vec3f& pos = position.value(); a_out << " pos " << pos[0] << " " << pos[1] << " " << pos[2] << std::endl; //FIXME : dump orientation. } bool is_type_ortho() const {return type()==camera_ortho?true:false;} bool height_at_focal(float& a_h) const { if(is_type_ortho()) { a_h = near_height(); } else { if(!znear.value()) {a_h = near_height();return false;} a_h = focal.value()*near_height()/znear.value(); } return true; } void astro_orientation(float a_ra,float a_dec/*,const vec3f& a_center*/) { // a_ra, a_dec are in decimal degrees. // Camera default point toward -z with up along +y and +x at right. // Arrange so that camera points toward x with up along +z : rotf r(vec3f::s_y(),-fhalf_pi()); r *= rotf(vec3f::s_x(),fhalf_pi()); // Now -y is at right. // Then rotate it so that it points toward given (ra,dec) by keeping up upward +z direction. r *= rotf(vec3f::s_y(),-a_dec*fdeg2rad()); r *= rotf(vec3f::s_z(),a_ra*fdeg2rad()); orientation = r; /* position = a_center*0.99f; znear = 0.1f; zfar = 200.0f; focal = (a_center-position).length(); */ /* position = vec3f(0,0,0); znear = 1.0f; zfar = 2000.0f; //2*sky_radius. focal = a_center.length(); da = 0.017f/100; //1/100 of a degree */ } protected: void update_sg(std::ostream& a_out) { {const vec4f& v = m_lrbt.value(); float l = v[0]; float r = v[1]; float b = v[2]; float t = v[3]; float n = znear.value(); float f = zfar.value(); if(is_type_ortho()) { m_proj.set_ortho(l,r,b,t,n,f); } else { m_proj.set_frustum(l,r,b,t,n,f); }} if(orientation.value().quat()!=id_orientation()) //OPTIMIZATION {rotf rinv; if(orientation.value().inverse(rinv)) { mat4f mtx; rinv.value(mtx); m_proj.mul_mtx(mtx,m_tmp); } else { a_out << "base_camera::update_sg :" << " get orientation inverse failed." << std::endl; }} m_proj.mul_translate(-position.value()[0], -position.value()[1], -position.value()[2]); } void _mult_matrix(matrix_action& a_action) { float l,r,b,t; get_lrbt(a_action.ww(),a_action.wh(),l,r,b,t); m_lrbt.set_value(l,r,b,t); if(touched()||m_lrbt.touched()) { update_sg(a_action.out()); reset_touched(); m_lrbt.reset_touched(); } a_action.projection_matrix().mul_mtx(m_proj,m_tmp); } void set_state(matrix_action& a_action) { state& _state = a_action.state(); _state.m_camera_ortho = is_type_ortho(); _state.m_camera_znear = znear; _state.m_camera_zfar = zfar; _state.m_camera_position = position.value(); _state.m_camera_orientation = orientation.value(); //_state.m_camera_near_height = near_height(); _state.m_camera_lrbt = m_lrbt.value(); _state.m_proj = a_action.projection_matrix(); } static const vec4& id_orientation() { static const vec4 s_v(0,0,0,1,false); return s_v; } protected: //OPTIMIZATION : sf_vec4f m_lrbt; mat4f m_proj; float m_tmp[16]; }; }} #endif