452 lines
13 KiB
Plaintext
452 lines
13 KiB
Plaintext
// Copyright (C) 2010, Guy Barrand. All rights reserved.
|
|
// See the file tools.license for terms.
|
|
|
|
#ifndef tools_zb_buffer
|
|
#define tools_zb_buffer
|
|
|
|
#include <cfloat> //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_x<m_begX) || (a_x>m_endX)) return;
|
|
if((a_y<m_begY) || (a_y>m_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_zmin(0),m_zmax(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)
|
|
//,m_zboundPrec(10)
|
|
{}
|
|
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;}
|
|
//bool depth_test() const {return m_depth_test;}
|
|
|
|
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);
|
|
}
|
|
|
|
//printf ("debug:ZBufferChangeSize:%d %d\n",a_width,a_height);
|
|
m_zbw = a_width;
|
|
m_zbh = a_height;
|
|
m_zbuffer = cmem_alloc<ZReal>(m_zbw*m_zbh);
|
|
if(!m_zbuffer){
|
|
m_zbw = 0;
|
|
m_zbh = 0;
|
|
return false;
|
|
}
|
|
|
|
m_zimage = cmem_alloc<ZPixel>(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;
|
|
//printf("debug:ZBufferClearDepthBuffer: %g.\n",a_depth);
|
|
|
|
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_x<m_begX) || (a_x>m_endX)) {a_pixel = 0;return false;}
|
|
if((a_y<m_begY) || (a_y>m_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;count<a_number;count++) {
|
|
WriteLine(a_list[count-1],a_list[count],a_size,a_pixel);
|
|
}
|
|
}
|
|
|
|
void draw_segments(int a_number,const point* a_list,ZPixel a_pixel,unsigned int a_size){
|
|
int segment_number = a_number/2;
|
|
for(int count=0;count<segment_number;count++) {
|
|
WriteLine(a_list[2*count],a_list[2*count+1],a_size,a_pixel);
|
|
}
|
|
}
|
|
void draw_markers(int a_number,const point* a_list,ZPixel a_pixel,unsigned int a_size){
|
|
for(int count=0;count<a_number;count++){
|
|
const point& p = a_list[count];
|
|
write_point(p.x,p.y,p.z,a_size,a_pixel);
|
|
}
|
|
}
|
|
|
|
void draw_polygon(int a_number,const point* a_list,
|
|
ZZ a_A,ZZ a_B,ZZ a_C,ZZ a_D,
|
|
//ZZ a_zmin,ZZ a_zmax,
|
|
ZPixel a_pixel){
|
|
// Assume a_list is closed.
|
|
if(a_number<3) return;
|
|
if(a_C==0) return; //polygone seen from edge
|
|
//if(m_zboundPrec<0) m_zboundPrec = 0;
|
|
|
|
m_scan_pixel = a_pixel;
|
|
m_planeAC = a_A/a_C;
|
|
m_planeBC = a_B/a_C;
|
|
m_planeDC = a_D/a_C;
|
|
|
|
//if this polygon A is quite perpandicular to screen and close
|
|
//to an other B than |dz| then some pixel of A could overwrite
|
|
//pixel of B. Your have then to give a lower m_zboundPrec
|
|
|
|
//ZZ dz = m_zboundPrec * (a_zmax - a_zmin)/100.;
|
|
//m_zmin = (ZReal)(a_zmin - dz);
|
|
//m_zmax = (ZReal)(a_zmax + dz);
|
|
|
|
m_polygon.scan(a_number,a_list,0,WriteScanLine,this);
|
|
|
|
}
|
|
|
|
typedef unsigned char uchar;
|
|
static void rgba2pix(float a_r,float a_g,float a_b,float a_a,ZPixel& a_pix) {
|
|
uchar* _px = (uchar*)&a_pix;
|
|
*_px = (uchar)(255.0F * a_r);_px++;
|
|
*_px = (uchar)(255.0F * a_g);_px++;
|
|
*_px = (uchar)(255.0F * a_b);_px++;
|
|
*_px = (uchar)(255.0F * a_a);_px++;
|
|
}
|
|
static void pix2rgba(const ZPixel& a_pix,float& a_r,float& a_g,float& a_b,float& a_a) {
|
|
uchar* _px = (uchar*)&a_pix;
|
|
a_r = (*_px)/255.0f;_px++;
|
|
a_g = (*_px)/255.0f;_px++;
|
|
a_b = (*_px)/255.0f;_px++;
|
|
a_a = (*_px)/255.0f;_px++;
|
|
}
|
|
protected:
|
|
void scan_write_point_1(ZPos a_x,ZPos a_y,ZZ a_z,ZPos /*a_beg*/,unsigned int a_size,ZPixel a_pixel) {
|
|
write_point(a_x,a_y,a_z,a_size,a_pixel);
|
|
}
|
|
void scan_write_point_2(ZPos a_x,ZPos a_y,ZZ a_z,ZPos /*a_beg*/,unsigned int a_size,ZPixel a_pixel) {
|
|
write_point(a_y,a_x,a_z,a_size,a_pixel);
|
|
}
|
|
void scan_write_point_3(ZPos a_x,ZPos a_y,ZZ a_z,ZPos a_beg,unsigned int a_size,ZPixel a_pixel) {
|
|
write_point(a_x,2*a_beg-a_y,a_z,a_size,a_pixel);
|
|
}
|
|
void scan_write_point_4(ZPos a_x,ZPos a_y,ZZ a_z,ZPos a_beg,unsigned int a_size,ZPixel a_pixel) {
|
|
write_point(2*a_beg-a_y,a_x,a_z,a_size,a_pixel);
|
|
}
|
|
|
|
void blend(ZPixel& a_pix,const ZPixel& a_new) {
|
|
if(!m_blend) {
|
|
a_pix = a_new;
|
|
return;
|
|
}
|
|
float _or,_og,_ob,_oa;
|
|
pix2rgba(a_pix,_or,_og,_ob,_oa);
|
|
float nr,ng,nb,na;
|
|
pix2rgba(a_new,nr,ng,nb,na);
|
|
if((0.0f<=na)&&(na<1.0f)) {
|
|
// same as glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA):
|
|
float one_minus_na = 1.0f-na;
|
|
float pr = nr*na+_or*one_minus_na;
|
|
float pg = ng*na+_og*one_minus_na;
|
|
float pb = nb*na+_ob*one_minus_na;
|
|
float pa = 1;
|
|
rgba2pix(pr,pg,pb,pa,a_pix);
|
|
} else {
|
|
a_pix = a_new;
|
|
}
|
|
}
|
|
|
|
static void WriteScanLine(void* a_tag,int a_beg,int a_end,int a_y){
|
|
buffer& a_buffer = *((buffer*)a_tag);
|
|
|
|
if((a_y<a_buffer.m_begY) || (a_y>a_buffer.m_endY)) return;
|
|
if(a_end<=a_beg) return;
|
|
|
|
if(a_beg>a_buffer.m_endX) return;
|
|
if(a_end<a_buffer.m_begX) return;
|
|
|
|
// border clip :
|
|
int beg = mx(a_beg,(int)a_buffer.m_begX);
|
|
int end = mn(a_end,(int)a_buffer.m_endX);
|
|
|
|
unsigned long offset = a_y * a_buffer.m_zbw + beg;
|
|
ZReal* zbuff = a_buffer.m_zbuffer + offset;
|
|
ZPixel* zimage = a_buffer.m_zimage + offset;
|
|
|
|
ZReal zpoint;
|
|
for(int x=beg;x<=end;x++){
|
|
zpoint =
|
|
(ZReal)(- a_buffer.m_planeDC
|
|
- a_buffer.m_planeAC * x
|
|
- a_buffer.m_planeBC * a_y);
|
|
if(a_buffer.m_depth_test) {
|
|
if((zpoint>=(*zbuff))
|
|
// &&(zpoint>=a_buffer.m_zmin) //for plane quite perpandicular to screen.
|
|
// &&(zpoint<=a_buffer.m_zmax)
|
|
){
|
|
*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<dx 0<=dy<=dx
|
|
|
|
ZPos end = a_x + a_dx;
|
|
ZPos beg = a_y;
|
|
ZZ incz = a_dz/(ZZ)a_dx;
|
|
if(a_dy==0) {
|
|
(this->*a_func)(a_x,a_y,a_z,beg,a_size,a_pixel);
|
|
while(a_x<end){
|
|
a_x++;
|
|
a_z += incz;
|
|
(this->*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<end){
|
|
a_x++;
|
|
a_y++;
|
|
a_z += incz;
|
|
(this->*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<end){
|
|
if(d<=0){
|
|
d += incrE;
|
|
a_x++;
|
|
}else{
|
|
d += incrNE;
|
|
a_x++;
|
|
a_y++;
|
|
}
|
|
a_z += incz;
|
|
(this->*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<dy) /*2*/
|
|
ScanLine ( a_beg.y, a_beg.x,a_beg.z, dy, dx, dz, a_size,a_pixel,&buffer::scan_write_point_2);
|
|
else if((-dx<=dy) && (dy<0) ) /*3*/
|
|
ScanLine ( a_beg.x, a_beg.y,a_beg.z, dx,-dy, dz, a_size,a_pixel,&buffer::scan_write_point_3);
|
|
else if(dy<-dx) /*4*/
|
|
ScanLine ( a_end.y, a_end.x,a_end.z,-dy, dx,-dz, a_size,a_pixel,&buffer::scan_write_point_4);
|
|
|
|
} else { //dx<0
|
|
if((0<=dy) && (dy<=-dx)) /*5*/
|
|
ScanLine ( a_end.x, a_end.y,a_end.z,-dx, dy,-dz, a_size,a_pixel,&buffer::scan_write_point_3);
|
|
else if(-dx<dy) /*6*/
|
|
ScanLine ( a_beg.y, a_beg.x,a_beg.z, dy,-dx, dz, a_size,a_pixel,&buffer::scan_write_point_4);
|
|
else if((dx<=dy) && (dy<0) ) /*7*/
|
|
ScanLine ( a_end.x, a_end.y,a_end.z,-dx,-dy,-dz, a_size,a_pixel,&buffer::scan_write_point_1);
|
|
else if(dy<dx) /*8*/
|
|
ScanLine ( a_end.y, a_end.x,a_end.z,-dy,-dx,-dz, a_size,a_pixel,&buffer::scan_write_point_2);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
protected:
|
|
bool m_depth_test;
|
|
bool m_blend;
|
|
ZReal* m_zbuffer;
|
|
//ZReal m_zmin,m_zmax;
|
|
|
|
ZPixel* m_zimage;
|
|
|
|
unsigned int m_zbw,m_zbh;
|
|
ZPos m_begX,m_begY;
|
|
ZPos m_endX,m_endY; //could be <0
|
|
|
|
ZPixel m_scan_pixel;
|
|
ZZ m_planeAC;
|
|
ZZ m_planeBC;
|
|
ZZ m_planeDC;
|
|
//int m_zboundPrec;
|
|
polygon m_polygon;
|
|
};
|
|
|
|
}}
|
|
|
|
#endif
|