// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_sg_zb_action #define tools_sg_zb_action #include "zb_manager" #include "render_action" #include "primitive_visitor" #include "../glprims" #include "../zb/buffer" #include "../colorfs" #include "../lina/plane" #include "../mathf" #include "../lina/vec3d" //ZZ=double namespace tools { namespace sg { class zb_action : public render_action { TOOLS_ACTION(zb_action,tools::sg::zb_action,render_action) private: zb_action& get_me() {return *this;} public: virtual void draw_vertex_array(gl::mode_t a_mode,size_t a_floatn,const float* a_xyzs){ m_pv.add_primitive(a_mode,a_floatn,a_xyzs); } virtual void draw_vertex_array_xy(gl::mode_t a_mode,size_t a_floatn,const float* a_xys){ m_pv.add_primitive_xy(a_mode,a_floatn,a_xys); } virtual void draw_vertex_color_array(gl::mode_t a_mode,size_t a_floatn, const float* a_xyzs,const float* a_rgbas){ m_pv.add_primitive_rgba(a_mode,a_floatn,a_xyzs,a_rgbas); } virtual void draw_vertex_normal_array(gl::mode_t a_mode,size_t a_floatn, const float* a_xyzs,const float* a_nms){ m_pv.add_primitive_normal(a_mode,a_floatn,a_xyzs,a_nms); } virtual void draw_vertex_color_normal_array(gl::mode_t a_mode,size_t a_floatn, const float* a_xyzs,const float* a_rgbas,const float* a_nms){ // We expect a_nms of size : 3*(a_floatn/3) // (then one normal per 3D point). m_pv.add_primitive_normal_rgba(a_mode,a_floatn,a_xyzs,a_nms,a_rgbas); } virtual void clear_color(float a_r,float a_g,float a_b,float a_a){ zb::buffer::ZPixel pix; zb::buffer::rgba2pix(a_r,a_g,a_b,a_a,pix); m_zb.clear_color_buffer(pix); } virtual void color4f(float a_r,float a_g,float a_b,float a_a){ m_rgba.set_value(a_r,a_g,a_b,a_a); } virtual void line_width(float a_v){m_line_width = a_v;} virtual void point_size(float a_v) {m_point_size = a_v;} virtual void set_polygon_offset(bool a_v) {m_POLYGON_OFFSET_FILL = a_v;} virtual void normal(float a_x,float a_y,float a_z) { m_normal.set_value(a_x,a_y,a_z); } virtual void set_winding(winding_type a_v) { m_ccw = (a_v==winding_ccw?true:false); } virtual void set_shade_model(shade_type) {} virtual void set_depth_test(bool a_on) {m_DEPTH_TEST = a_on;} virtual void set_cull_face(bool a_on) {m_CULL_FACE = a_on;} virtual void set_point_smooth(bool a_on) {m_POINT_SMOOTH = a_on;} virtual void set_line_smooth(bool a_on) {m_LINE_SMOOTH = a_on;} virtual void load_proj_matrix(const mat4f& a_mtx) { m_proj = a_mtx; } virtual void load_model_matrix(const mat4f& a_mtx) { m_model = a_mtx; set_normal_matrix(); } virtual unsigned int max_lights() {return 1000;} virtual void enable_light(unsigned int, float a_dx,float a_dy,float a_dz, float a_r,float a_g,float a_b,float a_a, float a_ar,float a_ag,float a_ab,float a_aa){ m_light_color.set_value(a_r,a_g,a_b,a_a); m_light_ambient.set_value(a_ar,a_ag,a_ab,a_aa); m_light_direction.set_value(a_dx,a_dy,a_dz); m_light_direction.normalize(); m_light_on = true; } virtual void set_lighting(bool a_value) {m_light_on = a_value;} virtual void set_blend(bool a_value) {m_blend = a_value;} virtual void restore_state(unsigned int /*a_ret_num_light*/) { const sg::state& _state = state(); m_proj = _state.m_proj; m_model = _state.m_model; set_normal_matrix(); m_rgba = _state.m_color; m_normal = _state.m_normal; m_ccw = (_state.m_winding==winding_ccw?true:false); m_POLYGON_OFFSET_FILL = _state.m_GL_POLYGON_OFFSET_FILL; m_CULL_FACE = _state.m_GL_CULL_FACE; m_POINT_SMOOTH = _state.m_GL_POINT_SMOOTH; m_LINE_SMOOTH = _state.m_GL_LINE_SMOOTH; m_line_width = _state.m_line_width; m_point_size = _state.m_point_size; m_light_on = _state.m_GL_LIGHTING; m_DEPTH_TEST = _state.m_GL_DEPTH_TEST; m_blend = _state.m_GL_BLEND; /* if(_state.m_GL_TEXTURE_2D) ::glEnable(GL_TEXTURE_2D); else ::glDisable(GL_TEXTURE_2D); // The "return of separator" state had ret_num_light. // The restored state has m_light. // We have to glDisable lights with index in [m_light,ret_num_light-1] for(unsigned int index=_state.m_light;index& a_buffer) { a_buffer.clear(); if(!m_ww || !m_wh) return false; size_t sz = 3 * m_ww * m_wh; a_buffer.resize(sz); uchar* pos = vec_data(a_buffer); zb::buffer::ZPixel pix; uchar* _pix = 0; for(unsigned int row=0;row& a_buffer) { a_buffer.clear(); if(!m_ww || !m_wh) return false; size_t sz = 4 * m_ww * m_wh; a_buffer.resize(sz); if(a_top_to_bottom) { ::memcpy(vec_data(a_buffer),m_zb.zimage(),sz); return true; } size_t stride = m_ww*4; uchar* zpos = ((uchar*)m_zb.zimage())+sz-stride; uchar* pos = vec_data(a_buffer); for(unsigned int row=0;row& a_buffer) { a_buffer.clear(); if(!m_ww || !m_wh) return false; if(!a_factor) return false; size_t bpp = 4; a_buffer.resize(a_factor* m_ww * a_factor * m_wh * bpp); uchar* zbuffer = (uchar*)m_zb.zimage(); uchar* abuffer = vec_data(a_buffer); size_t zstride = m_ww*bpp; size_t astride = a_factor*m_ww*bpp; size_t i,j,ar,ac,ipix; uchar* zpos;uchar* apos; for(j=0;j& a_buffer) { a_buffer.clear(); if(!m_ww || !m_wh) return false; size_t sz = 4 * m_ww * m_wh; a_buffer.resize(sz); if(a_top_to_bottom) { uchar* pos = vec_data(a_buffer); uchar* zpos = ((uchar*)m_zb.zimage()); for(size_t count=0;countm_zb.get_clipped_pixel(a_col,rzb->wh()-1-a_row,pix)){ rzb->out() << "tools::sg;:zb_action::get_rgb: can't get zbuffer pixel" << std::endl; a_r = 1; a_g = 0; a_b = 0; return false; } float a; zb::buffer::pix2rgba(pix,a_r,a_g,a_b,a); return true; } protected: void set_normal_matrix() { mat4f tmp(m_model); tmp.no_translate(); if(!tmp.invert(m_normal_matrix)) { m_out << "tools::sg::zb_action::set_normal_matrix : can't invert model matrix." << std::endl; } m_normal_matrix.transpose(); } bool project_point(float& a_x,float& a_y,float& a_z,float& a_w) { a_w = 1; m_model.mul_4f_opt(a_x,a_y,a_z,a_w,m_tmp); m_proj.mul_4f_opt(a_x,a_y,a_z,a_w,m_tmp); if(a_w==0) return false; a_x /= a_w; a_y /= a_w; a_z /= a_w; return true; } // bool project_normal(float& a_x,float& a_y,float& a_z) { // m_model.mul_dir_3f(a_x,a_y,a_z); // m_proj.mul_dir_3f(a_x,a_y,a_z); // return true; // } class primvis : public primitive_visitor { protected: virtual bool project(float& a_x,float& a_y,float& a_z,float& a_w) { return m_this.project_point(a_x,a_y,a_z,a_w); } virtual bool add_point(float a_x,float a_y,float a_z,float) { return _add_point(a_x,a_y,a_z,m_this.m_rgba.r(),m_this.m_rgba.g(),m_this.m_rgba.b(),m_this.m_rgba.a()); } virtual bool add_point(float a_x,float a_y,float a_z,float, float a_r,float a_g,float a_b,float a_a) { return _add_point(a_x,a_y,a_z,a_r,a_g,a_b,a_a); } virtual bool add_line(float a_bx,float a_by,float a_bz,float, float a_ex,float a_ey,float a_ez,float) { m_this.m_vp_mtx.mul_3f_opt(a_bx,a_by,a_bz,m_tmp); m_this.m_vp_mtx.mul_3f_opt(a_ex,a_ey,a_ez,m_tmp); a_bz *= -1; a_ez *= -1; zb::point beg; zinit(beg,a_bx,a_by,a_bz); zb::point end; zinit(end,a_ex,a_ey,a_ez); m_this.m_zb.set_depth_test(m_this.m_DEPTH_TEST); m_this.m_zb.set_blend(m_this.m_blend); zb::buffer::ZPixel pix; color2pix(m_this.m_rgba,pix); m_this.m_zb.draw_line(beg,end,pix,npix(m_this.m_line_width)); return true; } virtual bool add_line(float a_bx,float a_by,float a_bz,float, float a_br,float a_bg,float a_bb,float a_ba, float a_ex,float a_ey,float a_ez,float, float,float,float,float) { m_this.m_vp_mtx.mul_3f_opt(a_bx,a_by,a_bz,m_tmp); m_this.m_vp_mtx.mul_3f_opt(a_ex,a_ey,a_ez,m_tmp); a_bz *= -1; a_ez *= -1; zb::point beg; zinit(beg,a_bx,a_by,a_bz); zb::point end; zinit(end,a_ex,a_ey,a_ez); m_this.m_zb.set_depth_test(m_this.m_DEPTH_TEST); m_this.m_zb.set_blend(m_this.m_blend); // interpolate color with beg,end ? zb::buffer::ZPixel pix; zb::buffer::rgba2pix(a_br,a_bg,a_bb,a_ba,pix); m_this.m_zb.draw_line(beg,end,pix,npix(m_this.m_line_width)); return true; } virtual bool add_triangle(float a_p1x,float a_p1y,float a_p1z,float a_p1w, float a_p2x,float a_p2y,float a_p2z,float a_p2w, float a_p3x,float a_p3y,float a_p3z,float a_p3w){ return _add_triangle(a_p1x,a_p1y,a_p1z,a_p1w, m_this.m_normal.x(), m_this.m_normal.y(), m_this.m_normal.z(), a_p2x,a_p2y,a_p2z,a_p2w, m_this.m_normal.x(), m_this.m_normal.y(), m_this.m_normal.z(), a_p3x,a_p3y,a_p3z,a_p3w, m_this.m_normal.x(), m_this.m_normal.y(), m_this.m_normal.z(), m_this.m_rgba); } virtual bool add_triangle( float a_p1x,float a_p1y,float a_p1z,float a_p1w, float a_r1,float a_g1,float a_b1,float a_a1, float a_p2x,float a_p2y,float a_p2z,float a_p2w, float a_r2,float a_g2,float a_b2,float a_a2, float a_p3x,float a_p3y,float a_p3z,float a_p3w, float a_r3,float a_g3,float a_b3,float a_a3){ float r = (a_r1+a_r2+a_r3)/3.0f; float g = (a_g1+a_g2+a_g3)/3.0f; float b = (a_b1+a_b2+a_b3)/3.0f; float a = (a_a1+a_a2+a_a3)/3.0f; colorf col(r,g,b,a); return _add_triangle(a_p1x,a_p1y,a_p1z,a_p1w, m_this.m_normal.x(), m_this.m_normal.y(), m_this.m_normal.z(), a_p2x,a_p2y,a_p2z,a_p2w, m_this.m_normal.x(), m_this.m_normal.y(), m_this.m_normal.z(), a_p3x,a_p3y,a_p3z,a_p3w, m_this.m_normal.x(), m_this.m_normal.y(), m_this.m_normal.z(), col); } virtual bool project_normal(float&,float&,float&) { //return m_this.project_normal(a_x,a_y,a_z); return true; } virtual bool add_point_normal(float a_x,float a_y,float a_z,float a_w, float /*a_nx*/,float /*a_ny*/,float /*a_nz*/) { add_point(a_x,a_y,a_z,a_w); return true; } virtual bool add_point_normal(float a_x,float a_y,float a_z,float a_w, float /*a_nx*/,float /*a_ny*/,float /*a_nz*/, float a_r,float a_g,float a_b,float a_a) { add_point(a_x,a_y,a_z,a_w,a_r,a_g,a_b,a_a); return true; } virtual bool add_line_normal(float a_bx,float a_by,float a_bz,float a_bw, float /*a_bnx*/,float /*a_bny*/,float /*a_bnz*/, float a_ex,float a_ey,float a_ez,float a_ew, float /*a_enx*/,float /*a_eny*/,float /*a_enz*/) { add_line(a_bx,a_by,a_bz,a_bw, a_ex,a_ey,a_ez,a_ew); return true; } virtual bool add_line_normal(float a_bx,float a_by,float a_bz,float a_bw, float /*a_bnx*/,float /*a_bny*/,float /*a_bnz*/, float a_br,float a_bg,float a_bb,float a_ba, float a_ex,float a_ey,float a_ez,float a_ew, float /*a_enx*/,float /*a_eny*/,float /*a_enz*/, float a_er,float a_eg,float a_eb,float a_ea){ add_line(a_bx,a_by,a_bz,a_bw, a_br,a_bg,a_bb,a_ba, a_ex,a_ey,a_ez,a_ew, a_er,a_eg,a_eb,a_ea); return true; } virtual bool add_triangle_normal( float a_p1x,float a_p1y,float a_p1z,float a_p1w, float a_n1x,float a_n1y,float a_n1z, float a_p2x,float a_p2y,float a_p2z,float a_p2w, float a_n2x,float a_n2y,float a_n2z, float a_p3x,float a_p3y,float a_p3z,float a_p3w, float a_n3x,float a_n3y,float a_n3z) { return _add_triangle(a_p1x,a_p1y,a_p1z,a_p1w, a_n1x,a_n1y,a_n1z, a_p2x,a_p2y,a_p2z,a_p2w, a_n2x,a_n2y,a_n2z, a_p3x,a_p3y,a_p3z,a_p3w, a_n3x,a_n3y,a_n3z, m_this.m_rgba); return true; } virtual bool add_triangle_normal( float a_p1x,float a_p1y,float a_p1z,float a_p1w, float a_n1x,float a_n1y,float a_n1z, float a_r1,float a_g1,float a_b1,float a_a1, float a_p2x,float a_p2y,float a_p2z,float a_p2w, float a_n2x,float a_n2y,float a_n2z, float a_r2,float a_g2,float a_b2,float a_a2, float a_p3x,float a_p3y,float a_p3z,float a_p3w, float a_n3x,float a_n3y,float a_n3z, float a_r3,float a_g3,float a_b3,float a_a3){ float r = (a_r1+a_r2+a_r3)/3.0f; float g = (a_g1+a_g2+a_g3)/3.0f; float b = (a_b1+a_b2+a_b3)/3.0f; float a = (a_a1+a_a2+a_a3)/3.0f; colorf col(r,g,b,a); return _add_triangle(a_p1x,a_p1y,a_p1z,a_p1w, a_n1x,a_n1y,a_n1z, a_p2x,a_p2y,a_p2z,a_p2w, a_n2x,a_n2y,a_n2z, a_p3x,a_p3y,a_p3z,a_p3w, a_n3x,a_n3y,a_n3z, col); return true; } public: primvis(zb_action& a_zb):m_this(a_zb){} virtual ~primvis(){} public: primvis(const primvis& a_from) :primitive_visitor(a_from) ,m_this(a_from.m_this) {} primvis& operator=(const primvis& a_from){ primitive_visitor::operator=(a_from); return *this; } protected: static void zinit(zb::point& a_p,float a_x,float a_y,float a_z) { a_p.x = fround(a_x); //float -> int a_p.y = fround(a_y); //float -> int a_p.z = (zb::ZZ)a_z; //float -> double } unsigned int npix(float a_size) { // 0 -> 0 // 1 -> 0 // 2 -> 1 3x3 // 3 -> 1 3x3 // 4 -> 2 5x5 // 5 -> 2 5x5 // 6 -> 3 7x7 unsigned int num = (unsigned int)a_size; unsigned int num_2 = num/2; if(2*num_2==num) {num++;num_2 = num/2;} return num_2; } bool _add_point(float a_x,float a_y,float a_z,float a_r,float a_g,float a_b,float a_a){ m_this.m_zb.set_depth_test(m_this.m_DEPTH_TEST); m_this.m_zb.set_blend(m_this.m_blend); m_this.m_vp_mtx.mul_3f_opt(a_x,a_y,a_z,m_tmp); a_z *= -1; zb::point p; zinit(p,a_x,a_y,a_z); zb::buffer::ZPixel pix; zb::buffer::rgba2pix(a_r,a_g,a_b,a_a,pix); m_this.m_zb.draw_point(p,pix,npix(m_this.m_point_size)); return true; } bool _add_triangle(float a_p1x,float a_p1y,float a_p1z,float /*a_p1w*/, float a_n1x,float a_n1y,float a_n1z, float a_p2x,float a_p2y,float a_p2z,float /*a_p2w*/, float a_n2x,float a_n2y,float a_n2z, float a_p3x,float a_p3y,float a_p3z,float /*a_p3w*/, float a_n3x,float a_n3y,float a_n3z, const colorf& a_color){ float p1x = a_p1x;float p1y = a_p1y;float p1z = a_p1z;//float p1w = a_p1w; float p2x = a_p2x;float p2y = a_p2y;float p2z = a_p2z;//float p2w = a_p2w; float p3x = a_p3x;float p3y = a_p3y;float p3z = a_p3z;//float p3w = a_p3w; m_this.m_vp_mtx.mul_3f_opt(p1x,p1y,p1z,m_tmp); m_this.m_vp_mtx.mul_3f_opt(p2x,p2y,p2z,m_tmp); m_this.m_vp_mtx.mul_3f_opt(p3x,p3y,p3z,m_tmp); p1z *= -1; p2z *= -1; p3z *= -1; if(m_this.m_POLYGON_OFFSET_FILL){ //note : gopaw pawex9,14,15,21 with "lego" (drawing cubes) are sensitive to the below epsil. // zs are in [-1,1] float epsil = 1e-4f; p1z -= epsil; p2z -= epsil; p3z -= epsil; } typedef zb::ZZ ZZ; //double plane pn( vec3(p1x,p1y,p1z), vec3(p2x,p2y,p2z), vec3(p3x,p3y,p3z) ); if(!pn.is_valid()) return true; // norm[0]*x+norm[1]*y+norm[2]*z = dist // A*x+B*y+C*z+D = 0 ZZ C = pn.normal()[2]; if(m_this.m_CULL_FACE){ if(m_this.m_ccw) { if(C<=0) return true; } else { if(C>=0) return true; } } ZZ A = pn.normal()[0]; ZZ B = pn.normal()[1]; ZZ D = -pn.distance_from_origin(); //ZZ zmn = mn(mn(p1z,p2z),p3z); //ZZ zmx = mx(mx(p1z,p2z),p3z); zb::point list[3]; zinit(list[0],p1x,p1y,p1z); zinit(list[1],p2x,p2y,p2z); zinit(list[2],p3x,p3y,p3z); m_this.m_zb.set_depth_test(m_this.m_DEPTH_TEST); m_this.m_zb.set_blend(m_this.m_blend); colorf frag_color = a_color; if(m_this.m_light_on) { // same logic as toolx/wasm/webgl.js: float nx = (a_n1x+a_n2x+a_n3x)/3.0f; float ny = (a_n1y+a_n2y+a_n3y)/3.0f; float nz = (a_n1z+a_n2z+a_n3z)/3.0f; m_this.m_normal_matrix.mul_dir_3f_opt(nx,ny,nz,m_tmp); vec3f _normal(nx,ny,nz);_normal.normalize(); float _dot = _normal.dot(m_this.m_light_direction); if(_dot<0.0f) { _dot *= -1.0f; colorf _tmp = m_this.m_light_color; _tmp *= _dot; // _tmp *= 1.4f; //to have same intensity as GL on desktops. _tmp += m_this.m_light_ambient; frag_color *= _tmp; } else { frag_color *= m_this.m_light_ambient; } frag_color.clamp(); frag_color.set_a(a_color.a()); } zb::buffer::ZPixel pix; color2pix(frag_color,pix); m_this.m_zb.draw_polygon(3,list,A,B,C,D,pix); return true; } protected: zb_action& m_this; private: //optimize: float m_tmp[3]; }; protected: zb_manager& m_mgr; mat4f m_vp_mtx; zb::buffer m_zb; primvis m_pv; colorf m_light_color; colorf m_light_ambient; vec3f m_light_direction; vec3f m_normal; // to be restored in restore_state() : mat4f m_proj; mat4f m_model; mat4f m_normal_matrix; colorf m_rgba; bool m_ccw; bool m_POLYGON_OFFSET_FILL; bool m_CULL_FACE; bool m_POINT_SMOOTH; bool m_LINE_SMOOTH; float m_line_width; float m_point_size; bool m_light_on; bool m_DEPTH_TEST; bool m_blend; private: //optimize: float m_tmp[4]; }; }} #endif