// Copyright (C) 1996 Keith Whitwell. // This file may only be copied under the terms of the GNU Library General // Public License - see the file COPYING in the lib3d distribution. #include #include #include #include class Viewport32Bpp : public Viewport { public: void flatTriangleZb( PipelineData *const[], Colour colour ); void flatPolygonZb(uint nr, PipelineData *const[], Colour colour ); Colour getColour( uint r, uint g, uint b ) ; void smoothPolygonZb(uint, SmoothPipelineData *const[], const ColourRamp& ); void smoothTriangleZb(SmoothPipelineData *const[], const ColourRamp& ); void drawColourSpace(); const char *getName() const { return "Viewport32Bpp"; } protected: Viewport *clone( Device *device ); ~Viewport32Bpp(); Viewport32Bpp( Exemplar e ) : Viewport( e, 50 ) {} Viewport32Bpp( Device * ); protected: static Viewport32Bpp *exemplar; }; Viewport32Bpp *Viewport32Bpp::exemplar = new Viewport32Bpp( Exemplar() ); Viewport32Bpp::Viewport32Bpp( Device *device ) : Viewport( device, 1, 1 ) { } Viewport32Bpp::~Viewport32Bpp() { } Viewport * Viewport32Bpp::clone( Device *device ) { uint depth = device->getDepth(); if (depth == 24) { // too strict? debug() << "Creating new viewport for sparse 32 bit device." << endlog; return new Viewport32Bpp( device ); } else { debug() << "Viewport32Bpp not suitable for " << depth << " bit device." << endlog; return 0; } } Colour Viewport32Bpp::getColour( uint r, uint g, uint b ) { // For a 0888 bit layout. Other 32Bpp viewports should need only // override this function. return ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff); } #define EDGEMAX 1024 extern int ex[2*EDGEMAX]; extern uint ez[EDGEMAX]; void Viewport32Bpp::flatPolygonZb(uint nr, PipelineData *const vertex[], Colour colour) { if (nr == 3) { Viewport32Bpp::flatTriangleZb( vertex, colour ); return; } uint i; int x1 = int(vertex[nr-1]->device.v[X]); uint z1 = uint(vertex[nr-1]->device.v[Z]); int y1 = int(vertex[nr-1]->device.v[Y]); int xmin = x1; int xmax = x1; int ymin = y1; int ymax = y1; // Need to find ymid in advance for ( int k = nr-2 ; k >= 0; k-- ) { int y = int(vertex[k]->device.v[Y]); if (y < ymin) ymin = y; if (y > ymax) ymax = y; } int ymid = (ymin + ymax) >> 1; int zmid = 0; // z value of rhs of polygon at ymid. for ( i = 0 ; i < nr; i++ ) { int x0 = x1; int y0 = y1; uint z0 = z1; y1 = int(vertex[i]->device.v[Y]); x1 = int(vertex[i]->device.v[X]); z1 = uint(vertex[i]->device.v[Z]); if (x1 < xmin) xmin = x1; if (x1 > xmax) xmax = x1; if_debug { debug() << "Vertex "<= ymid && y1 <= ymid ) { // Includes the middle scanline of the polygon. zmid = int(z0) + ((ymid - y0)*(int(z1) - int(z0))) / dy; } // We always want to work down the screen, so reverse the // direction of upwards edges. These should always be // right-hand edges, because we have forced a clockwise // traversal of the vertices. dy = -dy; ptr = ex + y1*2 + 1; dx = x0 - x1; x = x1; } else { // Left edge. We also want to interpolate the z coordinates. // Embedding this loop here is probably a little confusing // for the compiler. We may get faster assembly if we // perform separate traversals of the vertices. uint *zptr = ez + y0; uint z = z0 + zBuffer->getGenerationMask(); int dz = z1 - z0; if (dz == 0) { for (int k = 0 ; k <= dy ; k++) { *zptr++ = z; } } else { qa = dz / dy; dz -= qa * dy; if (dz < 0) { dz = -dz; qb = qa - 1; } else { qb = qa + 1; } int a = dz+dz; int d = a-dy; int b = d-dy; for (int k=0 ; k<=dy ; k++) { *zptr++ = z; if (d<0) { d += a; z += qa; } else { d += b; z += qb; } } } // Set up for x interpolation. ptr = ex + y0*2; dx = x1 - x0; x = x0; } qa = dx / dy; dx -= qa * dy; if (dx < 0) { dx = -dx; qb = qa - 1; } else { qb = qa + 1; } int a = dx+dx; int d = a-dy; int b = d-dy; for (int k = 0; k <= dy ; k++) { *ptr = x; ptr += 2; if (d<0) { d += a; x += qa; } else { d += b; x += qb; } } } zBuffer->setDirty( xmin, ymin, xmax, ymax ); device->setDirty( xmin, ymin, xmax, ymax ); zmid += zBuffer->getGenerationMask(); int xspan = (ex[(ymid<<1)+1] - ex[ymid<<1]); if (xspan <= 0) return; int zSlope = (zmid - int(ez[ymid])) / xspan; if_debug { debug() << "Colour: " << colour << endlog; debug() << "ymid: " << ymid << " zmid: " << ez[ymid] << "-" << zmid << " xmid: " << ex[(ymid<<1)] << "-" << ex[(ymid<<1)+1] << " zslope: " << zSlope << endlog; } uchar *cptr = device->getBuffer() + ymin * device->getRowWidth(); uint *zptr = zBuffer->getBuffer() + ymin * zBuffer->getWidth(); int *exptr = ex + ymin * 2; for ( int y = ymin ; y < ymax ; y++ ) { int xmin = *exptr++; int i = (*exptr++ - xmin) - 1; uint z; if (i >= 0) { uint *cp = ((uint *)cptr) + xmin; uint *zp = zptr + xmin; z = ez[y]; while ( i > 0 ) { if (z < zp[0]) { zp[0] = uint(z); cp[0] = colour; } z += zSlope; if (z < zp[1]) { zp[1] = uint(z); cp[1] = colour; } z += zSlope; zp+=2; cp+=2; i -= 2; } if (i == 0) { if (z < *zp) { *zp = uint(z); cp[0] = colour; } } } cptr += device->getRowWidth(); zptr += zBuffer->getWidth(); } } void Viewport32Bpp::flatTriangleZb(PipelineData *const vertex[], Colour colour ) { int tx[3]; // type = 0 -- elbow is on lhs. // = 1 -- elbow is on rhs. // tx[0] = topmost vertex // tx[1] = elbow // tx[2] = bottommost vertex int y0 = vertex[0]->device.v[Y]; int y1 = vertex[1]->device.v[Y]; int y2 = vertex[2]->device.v[Y]; int type; if (y0 > y1) { if (y1 > y2) { type = 1; tx[0] = 2; tx[1] = 1; tx[2] = 0; } else { if (y0 > y2) { type = 0; tx[0] = 1; tx[1] = 2; tx[2] = 0; } else { type = 1; tx[0] = 1; tx[1] = 0; tx[2] = 2; } } } else { if (y2 > y1) { type = 0; tx[0] = 0; tx[1] = 1; tx[2] = 2; } else { if (y0 > y2) { type = 0; tx[0] = 2; tx[1] = 0; tx[2] = 1; } else { type = 1; tx[0] = 0; tx[1] = 2; tx[2] = 1; } } } if_debug { debug() << "In y order: " << endlog; debug() << "\t" << vertex[tx[0]]->device << endlog; debug() << "\t" << vertex[tx[1]]->device << endlog; debug() << "\t" << vertex[tx[2]]->device << endlog; debug() << "type: " << type << endlog; } int dy = vertex[tx[2]]->device.v[Y] - vertex[tx[0]]->device.v[Y]; if (dy == 0) { if_debug { debug() << "Zero height triangle" << endlog; } return; } uint *zptr = ez + vertex[tx[0]]->device.v[Y]; uint z = vertex[tx[0]]->device.v[Z] + zBuffer->getGenerationMask(); int dz = vertex[tx[2]]->device.v[Z] - vertex[tx[0]]->device.v[Z]; int zSlope = dz / dy; int *xptr = ex + (vertex[tx[0]]->device.v[Y] * 2); int x = vertex[tx[0]]->device.v[X] * 256; int dx = vertex[tx[2]]->device.v[0] - vertex[tx[0]]->device.v[0]; int xSlope = (dx * 256) / dy; int k = dy+1; if (dx != 0) { do { *xptr = x >> 8; xptr += 2; x += xSlope; *zptr++ = z; z += zSlope; } while (--k); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; *zptr++ = z; z += zSlope; } while (--k); } xptr = ex + (vertex[tx[0]]->device.v[Y] * 2) + 1; dy = vertex[tx[1]]->device.v[Y] - vertex[tx[0]]->device.v[Y]; if (dy != 0) { x = vertex[tx[0]]->device.v[X] * 256; dx = vertex[tx[1]]->device.v[X] - vertex[tx[0]]->device.v[X]; xSlope = (dx * 256) / dy; if (dx != 0) { do { *xptr = x >> 8; xptr += 2; x += xSlope; } while (--dy); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; } while (--dy); } } dy = vertex[tx[2]]->device.v[Y] - vertex[tx[1]]->device.v[Y]; if (dy != 0) { x = vertex[tx[1]]->device.v[X] * 256; dx = vertex[tx[2]]->device.v[X] - vertex[tx[1]]->device.v[X]; xSlope = (dx * 256) / dy; if (dx != 0) { do { *xptr = x >> 8; xptr += 2; x += xSlope; } while (--dy); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; } while (--dy); } } else { *xptr = x >> 8; } uint zmid = uint(vertex[tx[1]]->device.v[Z]) + zBuffer->getGenerationMask(); int ymin = vertex[tx[0]]->device.v[Y]; int ymid = vertex[tx[1]]->device.v[Y]; int ymax = vertex[tx[2]]->device.v[Y]; uint *cptr = ((uint *)device->getBuffer()) + ymin * device->getWidth(); zptr = (uint *)(zBuffer->getBuffer()) + ymin * zBuffer->getWidth(); xptr = ex + ymin * 2; if (type == 1) { int xspan = (ex[(ymid<<1)+1] - ex[ymid<<1]); if (xspan <= 0) { if_debug { if (xspan != 0) debug() << "degenerate triangle, y:" <= 0) { uint c = colour; uint *cp = cptr + xmin; uint *zp = zptr + xmin; z = ez[y]; while ( i > 0 ) { if (z < zp[0]) { zp[0] = uint(z); cp[0] = c; } z += zSlope; if (z < zp[1]) { zp[1] = uint(z); cp[1] = c; } z += zSlope; zp+=2; cp+=2; i -= 2; } if (i == 0) { if (z < *zp) { *zp = uint(z); cp[0] = c; } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } else { int xspan = (ex[(ymid<<1)] - ex[(ymid<<1)+1]); if (xspan <= 0) { if_debug { if (xspan != 0) debug() << "degenerate triangle, y:" <= 0) { uint *cp = cptr + xmax -1; uint *zp = zptr + xmax -1; z = ez[y]; while ( i > 0 ) { if (z < zp[0]) { zp[0] = uint(z); cp[0] = colour; } z += zSlope; if (z < *(zp-1)) { *(zp-1) = uint(z); *(cp-1) = colour; } z += zSlope; zp -=2; cp -=2; i -= 2; } if (i == 0) { if (z < *zp) { *zp = uint(z); cp[0] = colour; } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } return; } /* * Displays a portion of the colour space and a grey scale. * * Assumes a window larger than (300 x 256). */ void Viewport32Bpp::drawColourSpace() { uint i; uint *dptr = (uint *)(device->getBuffer()); int wid = device->getWidth(); for (i = 0 ; i < 65536; i++) { uint idx =( (((i)&31) ) // horizontal spans + (((i>>5)&31) * (wid)) // arranged vertically + (((i>>10)&3) * (wid * 40)) // in tiles + (((i>>12) * 40))); // in 4 columns dptr[idx] = i; } for (i = 0 ; i < min(uint(256),getHeight()) ; i+=1) { uint idx = getColour( i, i, i ); for (int y = 0 ; y < 1 ; y++) { for (int x = 0 ; x < 32 ; x++ ) { dptr[200+x+((i+y)*wid)] = idx; } } } device->setDirty(0,0,device->getWidth(),device->getHeight()); } extern int ei[]; void Viewport32Bpp::smoothPolygonZb(uint nr, SmoothPipelineData * const vertex[], const ColourRamp &ramp ) { if (nr==3) { Viewport32Bpp::smoothTriangleZb(vertex, ramp); return; } int x1 = vertex[nr-1]->device.v[X]; int y1 = vertex[nr-1]->device.v[Y]; uint z1 = uint(vertex[nr-1]->device.v[Z]); int i1 = vertex[nr-1]->intensity; int ymin = y1; int ymax = y1; // Need to find ymid in advance for ( int k = nr-2 ; k >= 0; k-- ) { int y = int(vertex[k]->device.v[Y]); if (y < ymin) ymin = y; if (y > ymax) ymax = y; } if (ymin == ymax) return; int ymid = (ymin + ymax) >> 1; int zmid = 0; // z value of rhs of polygon at ymid. int imid = 0; for (uint v = 0 ; v < nr; v++ ) { int x0 = x1; int y0 = y1; uint z0 = z1; int i0 = i1; y1 = vertex[v]->device.v[Y]; x1 = vertex[v]->device.v[X]; z1 = uint(vertex[v]->device.v[Z]); i1 = vertex[v]->intensity; if_debug { debug() << "vertex " << v << ":" << vertex[v]->device << " intensity: " << i1 << endlog; } int dy = y1 - y0; if (dy == 0) continue; if (dy < 0) { if ( y0 == ymid ) { zmid = z0; imid = i0; } else if ( y0 > ymid && y1 <= ymid ) { // Includes the middle scanline of the polygon. zmid = int(z0) + ((ymid - y0)*(int(z1) - int(z0))) / dy; imid = i0 + ((ymid - y0)*(i1 - i0)) / dy; } dy = -dy; int *xptr = ex + y1*2 + 1; int x = x1 * 256; int dx = (x0 * 256) - x; int xSlope = dx / dy; int k = dy+1; do { *xptr = x>>8; xptr += 2; x += xSlope; } while(--k); } else { // Left edge. We also want to interpolate z and intensity. uint *zptr = ez + y0; uint z = z0 + zBuffer->getGenerationMask(); int dz = z1 - z0; int zSlope = dz / dy; int k = dy+1; int *xptr = ex + y0*2 ; int x = x0 * 256; int dx = (x1 * 256) - x; int xSlope = dx / dy; int i = i0; int di = i1 - i0; int iSlope = di / dy; int *iptr = ei + y0; do { *xptr = x>>8; xptr += 2; x += xSlope; *zptr = z; zptr++; z += zSlope; *iptr = i; i += iSlope; iptr++; } while (--k); } } zmid += zBuffer->getGenerationMask(); int xspan = (ex[(ymid<<1)+1] - ex[ymid<<1]); if (xspan <= 0) return; int zSlope = (zmid - int(ez[ymid])) / xspan; int iSlope = (imid - ei[ymid]) / xspan; uint *cptr = ((uint *)device->getBuffer()) + ymin * device->getWidth(); uint *zptr = zBuffer->getBuffer() + ymin * zBuffer->getWidth(); int *xptr = ex + ymin * 2; const uint *dptr = (uint *)ramp.getRamp(); for ( int y = ymin ; y < ymax ; y++ ) { int xmin = *xptr++; int wid = (*xptr++ - xmin) - 1; if (wid >= 0) { int i = ei[y]; uint *cp = cptr + xmin; uint *zp = zptr + xmin; uint z = ez[y]; if ((wid & 1) == 0) { zp--; cp--; goto mid100; } do { if (z < zp[0]) { zp[0] = uint(z); cp[0] = uint(dptr[(i>>8)]); } z += zSlope; i += iSlope; mid100: if (z < zp[1]) { zp[1] = uint(z); cp[1] = uint(dptr[(i>>8)]); } z += zSlope; i += iSlope; zp+=2; cp+=2; wid -= 2; } while (wid >= 0); } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } void Viewport32Bpp::smoothTriangleZb(SmoothPipelineData * const vertex[], const ColourRamp &ramp ) { int tx[3]; int y0 = vertex[0]->device.v[Y]; int y1 = vertex[1]->device.v[Y]; int y2 = vertex[2]->device.v[Y]; int type; if (y0 > y1) { if (y1 > y2) { type = 1; tx[0] = 2; tx[1] = 1; tx[2] = 0; } else { if (y0 > y2) { type = 0; tx[0] = 1; tx[1] = 2; tx[2] = 0; } else { type = 1; tx[0] = 1; tx[1] = 0; tx[2] = 2; } } } else { if (y2 > y1) { type = 0; tx[0] = 0; tx[1] = 1; tx[2] = 2; } else { if (y0 > y2) { type = 0; tx[0] = 2; tx[1] = 0; tx[2] = 1; } else { type = 1; tx[0] = 0; tx[1] = 2; tx[2] = 1; } } } int dy = vertex[tx[2]]->device.v[Y] - vertex[tx[0]]->device.v[Y]; if (dy == 0) return; uint *zptr = ez + vertex[tx[0]]->device.v[Y]; uint z = vertex[tx[0]]->device.v[Z] + zBuffer->getGenerationMask(); int dz = vertex[tx[2]]->device.v[Z] - vertex[tx[0]]->device.v[Z]; int zSlope = dz / dy; int *iptr = ei + vertex[tx[0]]->device.v[Y]; int i = vertex[tx[0]]->intensity; int di = vertex[tx[2]]->intensity - i; int iSlope = di / dy; int k = dy+1; int *xptr = ex + (vertex[tx[0]]->device.v[Y] * 2); int x = vertex[tx[0]]->device.v[X] * 256; int dx = vertex[tx[2]]->device.v[0] - vertex[tx[0]]->device.v[0]; if (dx != 0) { int xSlope = (dx * 256) / dy; do { *xptr = x >> 8; xptr += 2; x += xSlope; *zptr++ = z; z += zSlope; *iptr = i; i += iSlope; iptr++; } while (--k); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; *zptr++ = z; z += zSlope; *iptr = i; i += iSlope; iptr++; } while (--k); } xptr = ex + (vertex[tx[0]]->device.v[Y] * 2) + 1; dy = vertex[tx[1]]->device.v[Y] - vertex[tx[0]]->device.v[Y]; if (dy != 0) { x = vertex[tx[0]]->device.v[X] * 256; dx = vertex[tx[1]]->device.v[X] - vertex[tx[0]]->device.v[X]; if (dx != 0) { int xSlope = (dx * 256) / dy; do { *xptr = x >> 8; xptr += 2; x += xSlope; } while (--dy); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; } while (--dy); } } dy = vertex[tx[2]]->device.v[Y] - vertex[tx[1]]->device.v[Y]; if (dy != 0) { x = vertex[tx[1]]->device.v[X] * 256; dx = vertex[tx[2]]->device.v[X] - vertex[tx[1]]->device.v[X]; if (dx != 0) { int xSlope = (dx * 256) / dy; do { *xptr = x >> 8; xptr += 2; x += xSlope; } while (--dy); } else { int xx = x >> 8; do { *xptr = xx; xptr += 2; } while (--dy); } } else { *xptr = x >> 8; } uint zmid = uint(vertex[tx[1]]->device.v[Z]) + zBuffer->getGenerationMask(); int imid = vertex[tx[1]]->intensity; int ymin = vertex[tx[0]]->device.v[Y]; int ymid = vertex[tx[1]]->device.v[Y]; int ymax = vertex[tx[2]]->device.v[Y]; uint *cptr = ((uint *)device->getBuffer()) + ymin * device->getWidth(); const uint *dptr = (uint *)ramp.getRamp(); zptr = (uint *)(zBuffer->getBuffer()) + ymin * zBuffer->getWidth(); xptr = ex + ymin * 2; if (type == 1) { int xspan = (ex[(ymid<<1)+1] - ex[ymid<<1]); if (xspan <= 0) return; int zSlope = (int(zmid) - int(ez[ymid])) / xspan; int iSlope = (imid - ei[ymid]) / xspan; for ( int y = ymin ; y < ymax ; y++ ) { int xmin = *xptr++; int wid = (*xptr++ - xmin) - 1; if (wid >= 0) { int i = ei[y]; uint *cp = cptr + xmin; uint *zp = zptr + xmin; uint z = ez[y]; while (wid > 0) { if (z < zp[0]) { zp[0] = ulong(z); cp[0] = uint(dptr[(i>>8)]); } z += zSlope; i += iSlope; if (z < zp[1]) { zp[1] = ulong(z); cp[1] = uint(dptr[(i>>8)]); } zp +=2; cp +=2; z += zSlope; i += iSlope; wid -= 2; } if (wid == 0) { if (z < *zp) { *zp = ulong(z); cp[0] = uint(dptr[(i>>8)]); } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } else { int xspan = (ex[(ymid<<1)] - ex[(ymid<<1)+1]); if (xspan <= 0) return; int zSlope = (int(zmid) - int(ez[ymid])) / xspan; int iSlope = (imid - ei[ymid]) / xspan; for ( int y = ymin ; y < ymax ; y++ ) { int xmax = *xptr++; int xmin = *xptr++; --xmax; int wid = xmax - xmin; if (wid >= 0) { int i = ei[y]; uint *cp = cptr + xmax; uint *zp = zptr + xmax; uint z = ez[y]; while ( wid > 0 ) { if (z < zp[0]) { zp[0] = ulong(z); cp[0] = uint(dptr[(i>>8)]); } z += zSlope; i += iSlope; if (z < *(zp-1)) { *(zp-1) = ulong(z); *(cp-1) = uint(dptr[(i>>8)]); } z += zSlope; i += iSlope; zp -=2; cp -=2; wid -= 2; } if (wid == 0) { if (z < *zp) { *zp = ulong(z); cp[0] = uint(dptr[(i>>8)]); } } } cptr += device->getWidth(); zptr += zBuffer->getWidth(); } } return; }