// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_sg_tex_quadrilateral #define tools_sg_tex_quadrilateral #include "node" #include "mf" #include "render_action" #include "pick_action" #include "bbox_action" #include "event_action" #include "render_manager" #include "gstos" #include "base_tex" #include "../num2s" namespace tools { namespace sg { class tex_quadrilateral : public node, public gstos, public base_tex { TOOLS_NODE_NO_CAST(tex_quadrilateral,tools::sg::tex_quadrilateral,node) public: virtual void* cast(const std::string& a_class) const { {if(void* p = cmp_cast(this,a_class)) return p;} {if(void* p = base_tex::cast(a_class)) return p;} return parent::cast(a_class); } public: sf show_border; mf_vec corners; public: virtual const desc_fields& node_desc_fields() const { TOOLS_FIELD_DESC_NODE_CLASS(tools::sg::tex_quadrilateral) static const desc_fields s_v(parent::node_desc_fields(),6, //WARNING : take care of count. TOOLS_ARG_FIELD_DESC(img), TOOLS_ARG_FIELD_DESC(back_color), TOOLS_ARG_FIELD_DESC(expand), TOOLS_ARG_FIELD_DESC(limit), TOOLS_ARG_FIELD_DESC(show_border), TOOLS_ARG_FIELD_DESC(corners) ); return s_v; } private: void add_fields(){ add_field(&img); add_field(&back_color); add_field(&expand); add_field(&limit); add_field(&show_border); add_field(&corners); } public: virtual void render(render_action& a_action) { //NOTE : we draw border (show_border is true) and background even if // gen_texture() failed. if(touched()) { update_sg(a_action.out()); reset_touched(); } if(m_img.is_empty()) return; if(corners.size()!=4) return; unsigned int _id = get_tex_id(a_action.out(),a_action.render_manager(),m_img,nearest.value()); const state& state = a_action.state(); //image must be 2^n,2^m in size ! // exa : 128x64 f12 xyzs,nms; if(show_border.value()) { _front(xyzs,nms/*,0.01f*/); //have to revisit a_epsil. a_action.color4f(1,0,0,1); a_action.line_width(1); a_action.draw_vertex_array(gl::line_loop(),12,xyzs); //pushes back the filled polygons to avoid z-fighting with lines a_action.set_polygon_offset(true); a_action.color4f(state.m_color); a_action.line_width(state.m_line_width); } //draw a back face pointing toward negative z : {a_action.color4f(back_color.value()); f18 tris,_nms; _tris(tris,_nms); a_action.draw_vertex_normal_array(gl::triangles(),18,tris,_nms); a_action.color4f(state.m_color);} if(_id) { _front(xyzs,nms); float tcs[8]; set_tcs(tcs); a_action.draw_vertex_normal_array_texture(gl::triangle_fan(),12,xyzs,nms,_id,tcs); } a_action.set_polygon_offset(state.m_GL_POLYGON_OFFSET_FILL); } virtual void pick(pick_action& a_action) { if(touched()) { update_sg(a_action.out()); reset_touched(); } if(m_pick_bbox_check_image) {if(m_img.is_empty()) return;} if(corners.size()!=4) return; f12 xyzs,nms; _front(xyzs,nms); a_action.add__primitive(*this,gl::triangle_fan(),12,xyzs,true); } virtual void bbox(bbox_action& a_action) { if(touched()) { update_sg(a_action.out()); reset_touched(); } if(m_pick_bbox_check_image) if(m_img.is_empty()) return; if(corners.size()!=4) return; f12 xyzs,nms; _front(xyzs,nms); a_action.add_points(12,xyzs); } public: virtual bool intersect_value(std::ostream&,intersect_type,const line& a_line,std::string& a_s) const { // a_line is in local world coordinate. float x,y; if(!line_2_img_ndc(a_line,x,y)) {a_s.clear();return false;} return img_ndc_value(x,y,a_s); } public: tex_quadrilateral() :parent() ,base_tex() ,show_border(false) ,corners() ,m_pick_bbox_check_image(true) { add_fields(); corners.add(vec3f(-1,-1,0)); corners.add(vec3f( 1,-1,0)); corners.add(vec3f( 1, 1,0)); corners.add(vec3f(-1, 1,0)); } virtual ~tex_quadrilateral(){} public: tex_quadrilateral(const tex_quadrilateral& a_from) :parent(a_from) ,gstos(a_from) ,base_tex(a_from) ,show_border(a_from.show_border) ,corners(a_from.corners) ,m_pick_bbox_check_image(a_from.m_pick_bbox_check_image) { add_fields(); } tex_quadrilateral& operator=(const tex_quadrilateral& a_from){ parent::operator=(a_from); gstos::operator=(a_from); base_tex::operator=(a_from); if(&a_from==this) return *this; show_border = a_from.show_border; corners = a_from.corners; m_pick_bbox_check_image = a_from.m_pick_bbox_check_image; return *this; } protected: void update_sg(std::ostream& a_out) { plane plane(corners[0],corners[1],corners[3]); m_normal = plane.normal(); clean_gstos(); //must reset for all render_manager. base_tex::_update_sg_(a_out); } protected: bool img_ndc_value(float a_x,float a_y,std::string& a_s) const { const img_byte& _img = img.value(); if(_img.is_empty()) {a_s.clear();return false;} int ix = int(float(_img.width())*a_x); int iy = int(float(_img.height())*a_y); //rgb of pixel : std::vector pixel; if((ix<0)||(iy<0)||!_img.pixel(ix,iy,pixel)) {a_s.clear();return false;} a_s.clear(); for(unsigned int ipix=0;ipix(float(pixel[ipix])/255.0f,a_s)){} } return true; } bool point_2_img_ndc(const vec3f& a_point,float& a_x,float& a_y) const { // a_point is assumed to be in the corners[0,1,3] plane. if(corners.size()!=4) {a_x = 0;a_y = 0;return false;} // In fact, in the below corners[2] is not used. // we assume that : // corners[0] is the bottom-left of image // corners[1] is the bottom-right of image // corners[2] is the top-right of image // corners[3] is the top-left of image vec3f x_axis = corners[1]-corners[0]; float l_01 = x_axis.normalize(); if(l_01==0.0f) {a_x = 0;a_y = 0;return false;} vec3f y_axis = corners[3]-corners[0]; float l_03 = y_axis.normalize(); if(l_03==0.0f) {a_x = 0;a_y = 0;return false;} float alpha = x_axis.dot(y_axis); float alpha_sq = alpha*alpha; if(alpha_sq==1.0f) {a_x = 0;a_y = 0;return false;} vec3f Op = a_point-corners[0]; float px = Op.dot(x_axis); float py = Op.dot(y_axis); float lambda = (px-alpha*py)/(1.0f-alpha_sq); float mu = (py-alpha*px)/(1-alpha_sq); // We must have : Op = lambda*x_axis+mu*y_axis; a_x = lambda/l_01; a_y = mu/l_03; return true; } bool line_2_img_ndc(const line& a_line,float& a_x,float& a_y) const { // a_line is in local world coordinate. if(corners.size()!=4) {a_x = 0;a_y = 0;return false;} // In fact corners[2] is not used, only [0,1,3]. plane plane(corners[0],corners[1],corners[3]); vec3f p; if(!plane.intersect(a_line,p)) {a_x = 0;a_y = 0;return false;} return point_2_img_ndc(p,a_x,a_y); } bool img_ndc_2_point(float a_x,float a_y,vec3f& a_point) const { if(corners.size()!=4) {a_point.set_value(0,0,0);return false;} // In fact, in the below corners[2] is not used. // we assume that : // corners[0] is the bottom-left of image // corners[1] is the bottom-right of image // corners[2] is the top-right of image // corners[3] is the top-left of image vec3f x_axis = corners[1]-corners[0]; float l_01 = x_axis.normalize(); if(l_01==0.0f) {a_point.set_value(0,0,0);return false;} vec3f y_axis = corners[3]-corners[0]; float l_03 = y_axis.normalize(); if(l_03==0.0f) {a_point.set_value(0,0,0);return false;} float alpha = x_axis.dot(y_axis); float lambda = a_x*l_01; float mu = a_y*l_03; float px = lambda+mu*alpha; float py = lambda*alpha+mu; vec3f Op = px*x_axis+py*y_axis; a_point = Op+corners[0]; return true; } typedef float f12[12]; void _front(f12& a_front,f12& a_nms,float a_epsil = 0.0f) { const std::vector& cs = corners.values(); a_front[0] = cs[0].x()-a_epsil; a_front[1] = cs[0].y()-a_epsil; a_front[2] = cs[0].z(); a_front[3] = cs[1].x()+a_epsil; a_front[4] = cs[1].y()-a_epsil; a_front[5] = cs[1].z(); a_front[6] = cs[2].x()+a_epsil; a_front[7] = cs[2].y()+a_epsil; a_front[8] = cs[2].z(); a_front[ 9] = cs[3].x()-a_epsil; a_front[10] = cs[3].y()+a_epsil; a_front[11] = cs[3].z(); a_nms[0] = m_normal.x(); a_nms[1] = m_normal.y(); a_nms[2] = m_normal.z(); a_nms[3] = m_normal.x(); a_nms[4] = m_normal.y(); a_nms[5] = m_normal.z(); a_nms[6] = m_normal.x(); a_nms[7] = m_normal.y(); a_nms[8] = m_normal.z(); a_nms[9] = m_normal.x(); a_nms[10] = m_normal.y(); a_nms[11] = m_normal.z(); } void _back(f12& a_back) { const std::vector& cs = corners.values(); a_back[0] = cs[1].x(); a_back[1] = cs[1].y(); a_back[2] = cs[1].z(); a_back[3] = cs[0].x(); a_back[4] = cs[0].y(); a_back[5] = cs[0].z(); a_back[6] = cs[3].x(); a_back[7] = cs[3].y(); a_back[8] = cs[3].z(); a_back[ 9] = cs[2].x(); a_back[10] = cs[2].y(); a_back[11] = cs[2].z(); } typedef float f18[18]; void _tris(f18& a_tris,f18& a_nms){ f12 back; _back(back); a_tris[0] = back[0]; a_tris[1] = back[1]; a_tris[2] = back[2]; a_tris[3] = back[3]; a_tris[4] = back[4]; a_tris[5] = back[5]; a_tris[6] = back[6]; a_tris[7] = back[7]; a_tris[8] = back[8]; // a_tris[9] = back[6]; a_tris[10] = back[7]; a_tris[11] = back[8]; a_tris[12] = back[9]; a_tris[13] = back[10]; a_tris[14] = back[11]; a_tris[15] = back[0]; a_tris[16] = back[1]; a_tris[17] = back[2]; ///////////////////// back a_nms[0] = -m_normal.x(); a_nms[1] = -m_normal.y(); a_nms[2] = -m_normal.z(); a_nms[3] = -m_normal.x(); a_nms[4] = -m_normal.y(); a_nms[5] = -m_normal.z(); a_nms[6] = -m_normal.x(); a_nms[7] = -m_normal.y(); a_nms[8] = -m_normal.z(); // a_nms[9] = -m_normal.x(); a_nms[10] = -m_normal.y(); a_nms[11] = -m_normal.z(); a_nms[12] = -m_normal.x(); a_nms[13] = -m_normal.y(); a_nms[14] = -m_normal.z(); a_nms[15] = -m_normal.x(); a_nms[16] = -m_normal.y(); a_nms[17] = -m_normal.z(); } protected: vec3f m_normal; bool m_pick_bbox_check_image; //for SDSS_image. }; }} #endif