This is old C++ code originally intended to be a playground for 3D math.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

568 lines
20 KiB

/**
* \file rasterize.cpp
*
* \brief Implementation des Software-Rasterizers
*
* Für eine detailierte Beschreibung von Rasterizer und
* Software-Rasterizer schauen Sie bitte unter \ref rasterize.h
*
* \author Georg Steffers <georg@steffers.org> [gs]
*
* \date 04.12.2003
*
* \version ..2002 [gs]: erste funktionierende Implementation
* \version 04.12.2003 [gs]: beginn der Dokumentation via doxygen
* \version 16.12.2003 [gs]: <ul><li>
* Linienclipping gefixed. Die alte
* Version versuchte Linien zu zeichnen
* die sich nach dem Clipping in
* Area 1001, 1010, 0101, 0110, also den
* Ecken rechtoben, linksoben,
* rechtsunten und linksunten neben dem
* darstellbaren Bereich befanden.
* </li><li>
* Außerdum mussten nach jedem Clipping
* die Masken aktualisiert werden, damit
* der Algoritmus abbricht wenn die
* Linie gezeichnet werden kann.
* Ansosnten führte weiters clipping zu
* fehlerhaften Ergebnissen.
* </li><li>
* draw_polygon_flat ruft jetzt den in
* polygon.h neu geschriebene
* Sutherland-Hodgen Clipper auf und
* zeichnet nur das geclippte Polygon
* das dieser Zurückliefert.
* </li></ul>
* \version 19.12.2003 [gs]: <ul><li>
* draw_polyeder_wire neu geschrieben. (Der
* alte Code existiert noch auskommentiert.
* </li><li>
* draw_polygon_wire geschrieben. Es macht was man
* sich denkt, es zeichnte ein Polygon alse linien.
* </li></ul>
*
* \todo (siehe \ref draw_polygon_flat)
*/
/*
* Copyright (C)2003 Georg Steffers
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
using namespace std;
#include <cstdlib>
#include <climits>
#include <cstring>
#include <values.h>
#include <iostream>
#include "geometry/polygon.h"
#include "rasterize.h"
#define CLIP_MASK_LEFT (0x1<<0)
#define CLIP_MASK_RIGHT (0x1<<1)
#define CLIP_MASK_BOTTOM (0x1<<2)
#define CLIP_MASK_TOP (0x1<<3)
rasterizer::rasterizer(unsigned xs, unsigned ys, canvas_imp* ci) {
x_size=xs;
y_size=ys;
sinfo=ci;
zbuf=new (double*)[xs];
for(unsigned i=0; i<xs; i++) {
zbuf[i]=new double[ys];
for(unsigned j=0; j<ys; zbuf[i][j++]=MAXDOUBLE);
}
unsigned sp[]={0,1,2,3};
vertex v[]={vertex(0,y_size,0,1),
vertex(0,0,0,1),
vertex(x_size,0,0,1),
vertex(x_size,y_size,0,1)};
screen_p_vl=vertex_list(v, 4);
screen_p=polygon((unsigned*)sp, 4, &screen_p_vl);
}
unsigned char* sw_rasterizer::adr_of_point(unsigned x, unsigned y) const {
return sinfo->p_screenbuf + (x+(y*x_size))*sinfo->bytes_per_pixel;
}
void sw_rasterizer::draw_point_at_paddress(unsigned char** p,
unsigned long col) const {
unsigned long mask = 0xFF;
char shift = 0;
// farbytes in Little-Endian order in den Puffer
for(int i=0; i<sinfo->bytes_per_pixel; i++) {
**p = (col & mask) >> shift;
(*p)++;
mask <<= 8;
shift += 8;
}
}
void sw_rasterizer::clear_buffer(void) const {
memset(sinfo->p_screenbuf, 0x00,
(size_t)x_size*y_size*sinfo->bytes_per_pixel);
for(unsigned i=0; i<x_size; i++) {
for(unsigned j=0; j<y_size; zbuf[i][j++]=MAXDOUBLE);
}
}
void sw_rasterizer::draw_point(unsigned x, unsigned y,
unsigned long col) const {
unsigned char *p = adr_of_point(x, y);
draw_point_at_paddress(&p, col);
}
void sw_rasterizer::line(double ax, double ay, double bx, double by,
unsigned long col) const {
/* Line-Clipping (nach Cohen-Sutherland) */
char clip_mask_a=0;
char clip_mask_b=0;
unsigned x_size=this->x_size-1;
unsigned y_size=this->y_size-1;
if(ax<0) clip_mask_a |= CLIP_MASK_LEFT;
if(ax>x_size) clip_mask_a |= CLIP_MASK_RIGHT;
if(ay>y_size) clip_mask_a |= CLIP_MASK_BOTTOM;
if(ay<0) clip_mask_a |= CLIP_MASK_TOP;
if(bx<0) clip_mask_b |= CLIP_MASK_LEFT;
if(bx>x_size) clip_mask_b |= CLIP_MASK_RIGHT;
if(by>y_size) clip_mask_b |= CLIP_MASK_BOTTOM;
if(by<0) clip_mask_b |= CLIP_MASK_TOP;
if((clip_mask_a & clip_mask_b) != 0)
return;
double dx=bx-ax;
double dy=by-ay;
double xinc=dx/dy;
double yinc=dy/dx;
if(clip_mask_a != 0) {
if((clip_mask_a&CLIP_MASK_LEFT)!=0) {
ay+=(0-ax)*yinc;
ax=0;
}
else if(clip_mask_a & CLIP_MASK_RIGHT) {
ay+=(x_size-ax)*yinc;
ax=x_size;
}
clip_mask_a=0;
if(ax<0) clip_mask_a |= CLIP_MASK_LEFT;
if(ax>x_size) clip_mask_a |= CLIP_MASK_RIGHT;
if(ay>y_size) clip_mask_a |= CLIP_MASK_BOTTOM;
if(ay<0) clip_mask_a |= CLIP_MASK_TOP;
if(clip_mask_a & CLIP_MASK_TOP) {
ax+=(0-ay)*xinc;
ay=0;
}
else if(clip_mask_a & CLIP_MASK_BOTTOM) {
ax+=(y_size-ay)*xinc;
ay=y_size;
}
}
if(clip_mask_b != 0) {
if(clip_mask_b & CLIP_MASK_LEFT) {
by+=(0-bx)*yinc;
bx=0;
}
else if(clip_mask_b & CLIP_MASK_RIGHT) {
by+=(x_size-bx)*yinc;
bx=x_size;
}
clip_mask_b=0;
if(bx<0) clip_mask_b |= CLIP_MASK_LEFT;
if(bx>x_size) clip_mask_b |= CLIP_MASK_RIGHT;
if(by>y_size) clip_mask_b |= CLIP_MASK_BOTTOM;
if(by<0) clip_mask_b |= CLIP_MASK_TOP;
if(clip_mask_b & CLIP_MASK_TOP) {
bx+=(0-by)*xinc;
by=0;
}
else if(clip_mask_b & CLIP_MASK_BOTTOM) {
bx+=(y_size-by)*xinc;
by=y_size;
}
}
clip_mask_a=0;
if(ax<0) clip_mask_a |= CLIP_MASK_LEFT;
if(ax>x_size) clip_mask_a |= CLIP_MASK_RIGHT;
if(ay>y_size) clip_mask_a |= CLIP_MASK_BOTTOM;
if(ay<0) clip_mask_a |= CLIP_MASK_TOP;
clip_mask_b=0;
if(bx<0) clip_mask_b |= CLIP_MASK_LEFT;
if(bx>x_size) clip_mask_b |= CLIP_MASK_RIGHT;
if(by>y_size) clip_mask_b |= CLIP_MASK_BOTTOM;
if(by<0) clip_mask_b |= CLIP_MASK_TOP;
if((clip_mask_a | clip_mask_b) == (CLIP_MASK_LEFT | CLIP_MASK_TOP) ||
(clip_mask_a | clip_mask_b) == (CLIP_MASK_LEFT | CLIP_MASK_BOTTOM) ||
(clip_mask_a | clip_mask_b) == (CLIP_MASK_RIGHT | CLIP_MASK_TOP) ||
(clip_mask_a | clip_mask_b) == (CLIP_MASK_RIGHT | CLIP_MASK_BOTTOM))
return;
/* ----------- */
/* Linie nach Bresenham */
int Ax=(int)ax;
int Bx=(int)bx;
int Ay=(int)ay;
int By=(int)by;
int dX = abs(Bx-Ax);
int dY = abs(By-Ay);
int Xincr=(Ax > Bx)?-1:1;
int Yincr=(Ay > By)?-1:1;
if (dX >= dY) {
int dPr = dY<<1;
int dPru = dPr - (dX<<1);
int P = dPr - dX;
for (; dX>=0; dX--) {
draw_point(Ax, Ay, col);
if (P > 0) {
Ax+=Xincr;
Ay+=Yincr;
P+=dPru;
}
else {
Ax+=Xincr;
P+=dPr;
}
}
}
else {
int dPr = dX<<1;
int dPru = dPr - (dY<<1);
int P = dPr - dY;
for (; dY>=0; dY--) {
draw_point(Ax, Ay, col);
if (P > 0) {
Ax+=Xincr;
Ay+=Yincr;
P+=dPru;
}
else {
Ay+=Yincr;
P+=dPr;
}
}
}
/*------------------*/
}
void sw_rasterizer::draw_polygon_wire(const polygon& p,
unsigned long col) const {
for(unsigned i=0; i<p.card(); i++) {
line((int)p[i][X], (int)p[i][Y],
(int)p[(i+1)%p.card()][X], (int)p[(i+1)%p.card()][Y], col);
}
}
void sw_rasterizer::draw_polyeder_wire(const polyeder& p,
unsigned long col) const {
/* for(unsigned i=0; i<p.card(); i++) {
int oldx, oldy;
int firstx, firsty;
oldx=oldy=firstx=firsty=0;
for(unsigned j=0; j<p[i].card(); j++) {
if(oldx)
line(oldx, oldy, (int)p[i][j][X], (int)p[i][j][Y], col);
oldx=(int)p[i][j][X], oldy=(int)p[i][j][Y];
if(j==0)
firstx=(int)p[i][j][X], firsty=(int)p[i][j][Y];
}
line(oldx, oldy, firstx, firsty, col);
}*/
for(unsigned i=0; i<p.card(); i++) {
for(unsigned j=0; j<p[i].card(); j++) {
line((int)p[i][j][X], (int)p[i][j][Y],
(int)p[i][(j+1)%p.card()][X],
(int)p[i][(j+1)%p.card()][Y], col);
}
}
}
/**
* \todo Ein funktionierendes z-buffering und texturemapping usw.
* Aktuell ist ein Prototyp-Code f&uuml;r einen z-Buffer drin,
* aber der funktioniert aus irgendeinem Grund nicht richtig.
*/
void sw_rasterizer::draw_polygon_flat(const polygon& p,
unsigned long col) const {
int top=0, bottom=0, o_top=0;
int start_left, end_left;
int start_right, end_right;
/* Clipping..... */
/* Sutherland-Hodgman */
polygon clipped=p.clip_2d(screen_p);
// Sind nach dem Clipping weniger als 3 Vertices übrig, so ist das
// Polygon nicht mehr im sichtbaren Bereich und darf nicht gezeichnet
// werden.
if(clipped.card()<3)
return;
// oberes und unterstes Vertex finden...
// außerdem den oberen Vertex fuer die erste rechte und linke Kante
for(unsigned i=1; i<clipped.card(); i++) {
end_left=end_right=top=(clipped[i][Y]<clipped[top][Y])?i:top;
bottom=(clipped[i][Y]>clipped[bottom][Y])?i:bottom;
}
for(unsigned i=1; i<p.card(); i++) {
o_top=(p[i][Y]<p[o_top][Y])?i:o_top;
}
// Startpositionen
double act_y=clipped[top][Y];
int act_ceily=(int)ceil(act_y);
double act_x_l, act_x_r;
double act_z_l, act_z_r;
// !!! Das erste mal wenn act_z ermittelt wird muss ich die Verchiebung
// auf die Integer Koordinaten (x,y) mit berechnen, da der gezeichnete Punkt
// nicht exact dem geometrischen entspricht.
// calculate gradients (!!!NICHT VOM GECLIPPTEN!!!)
// nur anwendbar über Werte die über das gesamte Polygon (screenspace)
// linear interpolierbar sind, also nicht gourard-shading, da diese
// Werte nur entlang einer Kante linier sind.
// !!! Ich brauche eine Methode, ein Dreieck zu finden, das einen Punkt
// oben, den zweiten als linke Seite und den dritten als rechte Seite bildet
// dazu muss ich die x,y Werte der Puntke analysieren. Die Lage der
// Punkte ist wichtig, damit die Vorzeichen der Gradienten stimmen, bzw.
// überhaupt erstmal welche berechnet werden können, es gibt Punkt
// kombinationen für die die Gradientenberechnung kein Ergebnis liefert.
// Das ermittelte Dreieck lässt sich dann allerdings zur ermittlung aller
// Gradienten gebrauchen.
// den obersten Vertex raussuchen, dann den davor und den danach.
// !!!ACHTUNG!!! Bei allen Berechnungen lasse ich im moment einen Fall
// ganz außer acht und zwar den, wenn das Dreiech zu einer Linie
// entartet.
unsigned gr_v0=o_top;
unsigned gr_v1=(o_top+1)%p.card();
unsigned gr_v2=(o_top-1<0)?o_top-1+p.card():o_top-1;
// der kleinere X soll gr_v1 sein wenn sein Y größer ist.
if((p[gr_v1][X]>p[gr_v2][X] && p[gr_v1][Y]>=p[gr_v2][Y]) ||
(p[gr_v1][X]<p[gr_v2][X] && p[gr_v1][Y]<p[gr_v2][Y]))
swap<unsigned>(gr_v1, gr_v2);
double dx21=p[gr_v2][X]-p[gr_v1][X];
double dx10=p[gr_v1][X]-p[gr_v0][X];
double dy21=p[gr_v2][Y]-p[gr_v1][Y];
double dy01=p[gr_v0][Y]-p[gr_v1][Y];
double d_z21=p[gr_v2][Z]-p[gr_v1][Z];
double d_z10=p[gr_v1][Z]-p[gr_v0][Z];
double dx01=p[gr_v0][X]-p[gr_v1][X];
double dy10=p[gr_v1][Y]-p[gr_v0][Y];
double v3_x=p[gr_v0][X];
double v4_y=p[gr_v0][Y];
double v3_y=((dy21*dx01) / dx21) + p[gr_v1][Y];
double v3_z=((d_z21*dx01) / dx21) + p[gr_v1][Z];
double v4_x=((dx21*dy01) / dy21) + p[gr_v1][X];
double v4_z=((d_z21*dy01) / dy21) + p[gr_v1][Z];
// zuallererst müsste ich mal überprüfen ob alle drei Punkte in
// einer Linie liegen, also dx01/dy01 == dx02/dy02 ist...da ich aber
// noch nicht weiss wie ich diesen Fall behandeln soll ignorier ich
// ihn im moment und nehme die entstehenden Fehler in kauf.
double d_zdx_denominator=0;
double d_zdy_denominator=0;
double d_zdx, d_zdy;
// zuerst dc/dx ermitteln. (einfach wenn y1==y2)
// zu den Bezeichnern: _x == 1/x, _? == 1/?
if(p[gr_v0][Y] == p[gr_v1][Y])
d_zdx=(p[gr_v1][Z]-p[gr_v0][Z]) / (p[gr_v1][X]-p[gr_v0][X]);
else if(p[gr_v0][Y] == p[gr_v2][Y])
d_zdx=(p[gr_v2][Z]-p[gr_v0][Z]) / (p[gr_v2][X]-p[gr_v0][X]);
else if(p[gr_v1][Y] == p[gr_v2][Y])
d_zdx=(p[gr_v2][Z]-p[gr_v1][Z]) / (p[gr_v2][X]-p[gr_v1][X]);
else {
d_zdx_denominator=(dx21*dy01)+(dx10*dy21);
d_zdx=((d_z21*dy01)+(d_z10*dy21))/d_zdx_denominator;
}
// jetzt dc/dy...(einfach falls es eine Sekrechte gibt.)
if(p[gr_v0][X] == p[gr_v1][X])
d_zdy=(p[gr_v1][Z]-p[gr_v0][Z]) / p[gr_v1][Y]-p[gr_v0][Y];
else if(p[gr_v0][X] == p[gr_v2][X])
d_zdy=(p[gr_v2][Z]-p[gr_v0][Z]) / p[gr_v2][Y]-p[gr_v0][Y];
else if(p[gr_v1][X] == p[gr_v2][X])
d_zdy=(p[gr_v2][Z]-p[gr_v1][Z]) / p[gr_v2][Y]-p[gr_v1][Y];
else {
d_zdy_denominator=d_zdx_denominator!=0?-d_zdx_denominator:
(dy21*dx01)+(dy10*dx21);
d_zdy=((d_z21*dx01)+(d_z10*dx21))/d_zdy_denominator;
}
/* if(!strncmp(p.get_id(), "DEBUG", 29)) {
cout << "---Polygon: " << &p << "----------\n";
for(unsigned i=0; i<p.card(); i++) {
cout << "p[" << i << "][X]: " << p[i][X] << ", ";
cout << "p[" << i << "][Y]: " << p[i][Y] << ", ";
cout << "p[" << i << "][1/Z]: " << p[i][Z] << ", ";
cout << "p[" << i << "][Z]: " << 1/p[i][Z] << "\n";
}
for(unsigned i=0; i<clipped.card(); i++) {
cout << "clipped[" << i << "][X]: " << clipped[i][X] << ", ";
cout << "clipped[" << i << "][Y]: " << clipped[i][Y] << ", ";
cout << "clipped[" << i << "][1/Z]: " << clipped[i][Z] << ", ";
cout << "clipped[" << i << "][Z]: " << 1/clipped[i][Z] << "\n";
}
cout << "gr_v0: " << gr_v0 << " | ";
cout << "gr_v0[X]: " << p[gr_v0][X] << " | ";
cout << "gr_v0[Y]: " << p[gr_v0][Y] << " | ";
cout << "gr_v0[Z]: " << p[gr_v0][Z] << "\n";
cout << "gr_v1: " << gr_v1 << " | ";
cout << "gr_v1[X]: " << p[gr_v1][X] << " | ";
cout << "gr_v1[Y]: " << p[gr_v1][Y] << " | ";
cout << "gr_v1[Z]: " << p[gr_v1][Z] << "\n";
cout << "gr_v2: " << gr_v2 << " | ";
cout << "gr_v2[X]: " << p[gr_v2][X] << " | ";
cout << "gr_v2[Y]: " << p[gr_v2][Y] << " | ";
cout << "gr_v2[Z]: " << p[gr_v2][Z] << "\n";
cout << "d_zdx: " << d_zdx << "\n";
cout << "d_zdy: " << d_zdy << "\n";
cout << "-------------------------------\n";
} */
//if(!strncmp(p.get_id(), "DEBUG", 29)) {
// solange zeichnen, bis der unterste y erreicht ist
while(act_ceily < y_size) {
double x_inc_l, x_inc_r, z_inc_l, z_inc_r;
// naechste linke und rechte Kante finden deren hoehe>0 und
// das linke und rechte x-Increment ermitteln.
if((int)ceil(clipped[end_left][Y]) == act_ceily) {
do {
start_left=end_left;
if(start_left == bottom) {
return;
}
end_left=(end_left-1<0)?end_left-1+clipped.card():end_left-1;
} while((int)ceil(clipped[end_left][Y]) == act_ceily);
act_y=clipped[start_left][Y];
act_ceily=(int)ceil(act_y);
x_inc_l=(clipped[end_left][X]-clipped[start_left][X])/
(clipped[end_left][Y]-clipped[start_left][Y]);
z_inc_l=x_inc_l*d_zdx;
act_x_l=clipped[start_left][X];
// Subpixelkorrektur
act_x_l+=((double)act_ceily-act_y)*x_inc_l;
act_z_l=clipped[start_left][Z];
act_z_l+=((double)act_ceily-act_y)*z_inc_l;
}
if((int)ceil(clipped[end_right][Y]) == act_ceily) {
do {
start_right=end_right;
if(start_right == bottom) {
return;
}
end_right=(end_right+1)%clipped.card();
} while((int)ceil(clipped[end_right][Y]) == act_ceily);
act_y=clipped[start_right][Y];
act_ceily=(int)ceil(act_y);
x_inc_r=(clipped[end_right][X]-clipped[start_right][X])/
(clipped[end_right][Y]-clipped[start_right][Y]);
z_inc_r=x_inc_r*d_zdx;
act_x_r=clipped[start_right][X];
// Subpixelkorrektur
act_x_r+=((double)act_ceily-act_y)*x_inc_r;
act_z_r=clipped[start_right][Z];
act_z_r+=((double)act_ceily-act_y)*z_inc_r;
}
// bis zu welchem der beiden Vertexe (links o. rechts) muss ich?
unsigned togo=((int)ceil(clipped[end_left][Y])-act_ceily <=
(int)ceil(clipped[end_right][Y])-act_ceily)?
end_left:end_right;
for(; act_ceily < (int)ceil(clipped[togo][Y]); act_ceily++) {
unsigned char* adr=adr_of_point((act_x_l<act_x_r)?
(int)ceil(act_x_l):
(int)ceil(act_x_r),
act_ceily);
unsigned len=abs((int)ceil(act_x_r)-(int)ceil(act_x_l));
int _X=(act_x_l<act_x_r)?(int)ceil(act_x_l):(int)ceil(act_x_r);
//double act_z=(act_x_l<act_x_r)?act_z_l:act_z_r;
double act_row_z=(act_x_l<act_x_r)?act_z_l:act_z_r;
for(unsigned i=0; i<len; i++) {
double _Z=1/act_row_z;
//if(zbuf[_X+i][act_ceily] > _Z) {
draw_point_at_paddress(&adr, col);
zbuf[_X+i][act_ceily]=_Z;
//}
// if(!strncmp(p.get_id(), "DEBUG", 29)) {
// printf("%03ld,%03ld,%10.7f ", _X+i, act_ceily, act_row_z);
// }
act_row_z+=d_zdx;
}
act_x_l+=x_inc_l;
act_x_r+=x_inc_r;
act_z_l+=z_inc_l;
act_z_l+=d_zdy;
act_z_r+=z_inc_r;
act_z_r+=d_zdy;
// if(!strncmp(p.get_id(), "DEBUG", 29)) {
// cout << "\n";
// }
}
}
//}
}