// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_zb_buffer #define tools_zb_buffer #include //DBL_MAX #include "polygon" namespace tools { namespace zb { // ZPos, ZZ defined in point. class buffer { typedef double ZReal; static ZReal ZREAL_HUGE() {return DBL_MAX;} public: typedef unsigned int ZPixel; //NOTE : with X11, bits_per_pixel can't be > 32. protected: class writer { public: virtual void write(ZPos,ZPos,ZZ) = 0; public: writer(ZPixel a_pixel):m_pixel(a_pixel){} virtual ~writer(){} public: writer(const writer& a_from):m_pixel(a_from.m_pixel){} writer& operator=(const writer& a_from){ m_pixel = a_from.m_pixel; return *this; } public: ZPixel m_pixel; }; void _write_point(ZPos a_x,ZPos a_y,ZZ a_z,ZPixel a_pixel) { if((a_xm_endX)) return; if((a_ym_endY)) return; ZReal zpoint = (ZReal)a_z; unsigned long offset = a_y * m_zbw + a_x; ZReal* zbuff = m_zbuffer + offset; if(m_depth_test) {if(zpoint<*zbuff) return;} ZPixel* zimage = m_zimage + offset; *zbuff = zpoint; blend(*zimage,a_pixel); } void write_point(ZPos a_x,ZPos a_y,ZZ a_z,unsigned int a_size,ZPixel a_pixel) { if(a_size>=1) { //see zb_action::npix(). ZPos x,y; for(int i=-int(a_size);i<=int(a_size);i++) { x = a_x + i; for(int j=-int(a_size);j<=int(a_size);j++) { y = a_y + j; _write_point(x,y,a_z,a_pixel); } } } else { _write_point(a_x,a_y,a_z,a_pixel); } } public: buffer() :m_depth_test(true) ,m_blend(false) ,m_zbuffer(0) ,m_zimage(0) ,m_zbw(0),m_zbh(0) ,m_begX(0),m_begY(0),m_endX(0),m_endY(0) ,m_scan_pixel(0L) ,m_planeAC(0),m_planeBC(0),m_planeDC(0) {} virtual ~buffer(){ cmem_free(m_zbuffer); cmem_free(m_zimage); m_zbw = 0; m_zbh = 0; m_polygon.clear(); } protected: buffer(const buffer& a_from) :m_depth_test(a_from.m_depth_test) ,m_blend(a_from.m_blend) {} buffer& operator=(const buffer& a_from){ m_depth_test = a_from.m_depth_test; m_blend = a_from.m_blend; return *this; } public: void set_depth_test(bool a_on) {m_depth_test = a_on;} void set_blend(bool a_value) {m_blend = a_value;} bool change_size(unsigned int a_width,unsigned int a_height){ if(!a_width||!a_height) return false; if(m_zbuffer && (m_zbw==a_width) && (m_zbh==a_height) ) return true; if(m_zbuffer){ cmem_free(m_zbuffer); cmem_free(m_zimage); } m_zbw = a_width; m_zbh = a_height; m_zbuffer = cmem_alloc(m_zbw*m_zbh); if(!m_zbuffer){ m_zbw = 0; m_zbh = 0; return false; } m_zimage = cmem_alloc(m_zbw*m_zbh); if(!m_zimage){ cmem_free(m_zbuffer); m_zbw = 0; m_zbh = 0; return false; } set_clip_region(0,0,m_zbw,m_zbh); m_polygon.clear(); return true; } ZPixel* get_color_buffer(unsigned int& a_width,unsigned int& a_height) const { a_width = m_zbw; a_height = m_zbh; return m_zimage; } void clear_color_buffer(ZPixel a_pixel) { // Erase acoording clip region. ZPos row,col; for(row=m_begY;row<=m_endY;row++){ ZPixel* zimage = m_zimage + row * m_zbw + m_begX; for(col=m_begX;col<=m_endX;col++,zimage++) *zimage = a_pixel; } } void clear_depth_buffer() { // Erase acoording clip region. ZPos row,col; for(row=m_begY;row<=m_endY;row++) { ZReal* zbuff = m_zbuffer + row * m_zbw + m_begX; for(col=m_begX;col<=m_endX;col++,zbuff++){ *zbuff = - ZREAL_HUGE(); } } } ZPixel* zimage() {return m_zimage;} bool get_clipped_pixel(ZPos a_x,ZPos a_y,ZPixel& a_pixel) const { if((a_xm_endX)) {a_pixel = 0;return false;} if((a_ym_endY)) {a_pixel = 0;return false;} a_pixel = *(m_zimage + a_y * m_zbw + a_x); return true; } public: void set_clip_region(ZPos a_x,ZPos a_y,unsigned int a_width,unsigned int a_height){ // if a_width or a_height is zero, clip region is empty. m_begX = a_x; m_begY = a_y; m_endX = a_x + a_width - 1; m_endY = a_y + a_height - 1; if(m_begX<0) m_begX = 0; if(m_begY<0) m_begY = 0; if(m_endX>ZPos(m_zbw-1)) m_endX = m_zbw-1; if(m_endY>ZPos(m_zbh-1)) m_endY = m_zbh-1; } void draw_point(const point& a_p,ZPixel a_pixel,unsigned int a_size){ write_point(a_p.x,a_p.y,a_p.z,a_size,a_pixel); } void draw_line(const point& a_beg,const point& a_end,ZPixel a_pixel,unsigned int a_size){ WriteLine(a_beg,a_end,a_size,a_pixel); } void draw_lines(int a_number,const point* a_list,ZPixel a_pixel,unsigned int a_size){ for(int count=1;counta_buffer.m_endY)) return; if(a_end<=a_beg) return; if(a_beg>a_buffer.m_endX) return; if(a_end=(*zbuff)) ){ *zbuff = zpoint; a_buffer.blend(*zimage,a_buffer.m_scan_pixel); } } else { *zbuff = zpoint; a_buffer.blend(*zimage,a_buffer.m_scan_pixel); } zbuff ++; zimage ++; } } typedef void(buffer::*scan_write_point_func)(ZPos,ZPos,ZZ,ZPos,unsigned int,ZPixel); void ScanLine(ZPos a_x,ZPos a_y,ZZ a_z, ZPos a_dx,ZPos a_dy,ZZ a_dz, unsigned int a_size,ZPixel a_pixel, scan_write_point_func a_func){ // Mid point algorithm // assume 0*a_func)(a_x,a_y,a_z,beg,a_size,a_pixel); while(a_x*a_func)(a_x,a_y,a_z,beg,a_size,a_pixel); } } else if(a_dy==a_dx) { (this->*a_func)(a_x,a_y,a_z,beg,a_size,a_pixel); while(a_x*a_func)(a_x,a_y,a_z,beg,a_size,a_pixel); } } else { ZPos d = 2 * a_dy - a_dx; ZPos incrE = 2 * a_dy; ZPos incrNE = 2 * ( a_dy - a_dx); (this->*a_func)(a_x,a_y,a_z,beg,a_size,a_pixel); while(a_x*a_func)(a_x,a_y,a_z,beg,a_size,a_pixel); } } } void WriteLine(const point& a_beg, const point& a_end, unsigned int a_size,ZPixel a_pixel){ ZPos dx = a_end.x - a_beg.x; ZPos dy = a_end.y - a_beg.y; ZZ dz = a_end.z - a_beg.z; // 6 2 // 5 1 // 7 3 // 8 4 if( (dx==0) && (dy==0) ) { write_point(a_beg.x,a_beg.y,a_beg.z,a_size,a_pixel); write_point(a_end.x,a_end.y,a_end.z,a_size,a_pixel); } else if(dx==0) { if(dy>0) ScanLine ( a_beg.y, a_beg.x,a_beg.z, dy, dx, dz, a_size,a_pixel,&buffer::scan_write_point_2); else ScanLine ( a_end.y, a_end.x,a_end.z,-dy, dx,-dz, a_size,a_pixel,&buffer::scan_write_point_2); } else if(dx>0) { if((0<=dy) && (dy<=dx)) /*1*/ ScanLine ( a_beg.x, a_beg.y,a_beg.z, dx, dy, dz, a_size,a_pixel,&buffer::scan_write_point_1); else if(dx