/* * This file is part of rasdaman community. * * Rasdaman community 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 3 of the License, or * (at your option) any later version. * * Rasdaman community 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 rasdaman community. If not, see . * * Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / rasdaman GmbH. * * For more information please see * or contact Peter Baumann via . / /** * COMMENTS: * * This is called from within cube_render.c to render a line using a voxel * approch. It has been moved here to easily handle different base types * and sizes. */ static void RENDER_CORE_NAME(int line, project_plane_desc *ppd, const render_desc *renderDesc) { graph_env *ge; tex_desc *td; union {uint8 *c; uint16 *s; uint32 *l; float *f; double *d;} texture; union {uint8 *c; uint16 *s; uint32 *l; float *f; double *d;} dest; int minx, maxx, posx; project_plane_intersect *front, *back, *lastFront, *lastBack; real_t front_z, back_z, new_z; real_t nom, den, pos, h; real_t zpro; vertex_fp front_t, back_t, deltaFB_t; real_t front_h, back_h; /*int front_num, back_num;*/ int i, depth; int32 tx, ty, tz, dtx, dty, dtz; int dimy, dimz, dimyz; int pixelsDone; RENDER_CAST_TYPE value; RENDER_CAST_TYPE ptl, pth, wth; /* thresholds */ RENDER_CAST_TYPE wgtAccu, weight; RENDER_CAST_TYPE *voxColour; #ifdef RENDER_FLOAT_TYPE real_t weightScale; #else int weightQuant; #endif #if (TEXEL_BSIZE == 3) uint8 *srcPix; uint32 red, green, blue; #else RENDER_CAST_TYPE pixel; #endif ge = renderDesc->graphEnv; td = renderDesc->texDesc; zpro = (real_t)ge->zpro; /* Kill warnings */ dtx = 0; dty = 0; dtz = 0; dimy = td->dimy; dimz = td->dimz; dimyz = dimy*dimz; /* Horizontal clipping */ minx = (ppd->left_p); if (minx > ge->clipr) return; maxx = (ppd->right_p); if (maxx < ge->clipl) return; if (minx < ge->clipl) minx = ge->clipl; if (maxx > ge->clipr) maxx = ge->clipr; #ifdef RENDER_FLOAT_TYPE ptl = (RENDER_CAST_TYPE)(ppd->pixelThresholdLowF); pth = (RENDER_CAST_TYPE)(ppd->pixelThresholdHighF); wth = (RENDER_CAST_TYPE)(ppd->weightThresholdF); weightScale = (real_t)(1.0 / (1 << (ppd->weightQuantisation))); #else ptl = (RENDER_CAST_TYPE)(ppd->pixelThresholdLow); pth = (RENDER_CAST_TYPE)(ppd->pixelThresholdHigh); wth = (RENDER_CAST_TYPE)(ppd->weightThreshold); weightQuant = ppd->weightQuantisation; #endif voxColour = (RENDER_CAST_TYPE *)(ppd->voxColour); dest.c = (uint8*)(ge->dest) + (ge->midy - line)*(ge->lineadd) + ((ge->midx + minx) * TEXEL_BSIZE); texture.c = (uint8*)(td->data); #if 0 for (back_num = ppd->segs-1; back_num >= 0; back_num--) { if ((ppd->ppi[back_num].left_p <= minx) && (ppd->ppi[back_num].right_p >= minx)) break; } for (front_num = 0; front_num < ppd->segs; front_num++) { if ((ppd->ppi[front_num].left_p <= minx) && (ppd->ppi[front_num].right_p >= minx)) break; } if (back_num == front_num) { printf("%d,%d | %d\n", front_num, back_num, ppd->segs); fflush(stdout); for (i=0; isegs; i++) { printf("\t[%d,%d]\n", ppd->ppi[i].left_p, ppd->ppi[i].right_p); } } #endif front = NULL; back = NULL; lastFront = NULL; lastBack = NULL; front_z = 0.0; back_z = 0.0; pixelsDone = 0; /* main pixel loop */ for (posx=minx; posx <= maxx; posx++) { pos = (real_t)posx; if (front != NULL) if (front->right_p < posx) front = NULL; if (back != NULL) if (back->right_p < posx) back = NULL; #if 1 /* * Quite tricky: you may only use the two segments found for longer if they * remain the same for a few pixels. For instance two segments which are * linked, one being front and one back: at the leftmost pixel the order * is undefined. Therefore make sure the front / back segments are static * over more than 1 pixel (2 seem to be enough). */ if ((front == NULL) || (back == NULL) || (pixelsDone < 2)) { /* Determine front and back segment */ front = NULL; back = NULL; for (i=0; isegs; i++) { if ((ppd->ppi[i].left_p <= posx) && (ppd->ppi[i].right_p >= posx)) { den = (ppd->ppi[i].deltaLR_g.x * zpro) - pos * (ppd->ppi[i].deltaLR_g.z); if (den == 0.0) continue; nom = (ppd->ppi[i].left_g.x * zpro) - pos * (ppd->ppi[i].left_g.z); h = - nom / den; if (h < 0.0) h = 0.0; if (h > 1.0) h = 1.0; new_z = ppd->ppi[i].left_g.z + h * (ppd->ppi[i].deltaLR_g.z); if ((front == NULL) || (new_z <= front_z)) { front_z = new_z; front = ppd->ppi + i; front_h = h; } if ((back == NULL) || (new_z >= back_z)) { back_z = new_z; back = ppd->ppi + i; back_h = h; } } } /* If no front/back segments found: continue loop and try again next time */ if ((front == NULL) || (back == NULL)) continue; if ((lastFront != front) || (lastBack != back)) { lastFront = front; lastBack = back; pixelsDone = 0; } else { pixelsDone++; } } else #else if (back == NULL) { /*printf("new back (%d)\n", back_num); fflush(stdout);*/ if (back_num < 0) continue; back = ppd->ppi + (back_num--); } if (front == NULL) { /*printf("new front (%d)\n", front_num); fflush(stdout);*/ if (front_num >= ppd->segs) continue; front = ppd->ppi + (front_num++); } #endif { den = (front->deltaLR_g.x) * zpro - pos * (front->deltaLR_g.z); if (den == 0.0) continue; nom = (front->left_g.x) * zpro - pos * (front->left_g.z); front_h = - nom / den; if (front_h < 0.0) front_h = 0.0; if (front_h > 1.0) front_h = 1.0; den = (back->deltaLR_g.x) * zpro - pos * (back->deltaLR_g.z); if (den == 0.0) continue; nom = (back->left_g.x) * zpro - pos * (back->left_g.z); back_h = - nom / den; if (back_h < 0.0) back_h = 0.0; if (back_h > 1.0) back_h = 1.0; } /* front texture coordinates */ front_t.x = front->left_t.x + front_h * (front->deltaLR_t.x); front_t.y = front->left_t.y + front_h * (front->deltaLR_t.y); front_t.z = front->left_t.z + front_h * (front->deltaLR_t.z); /* back texture coordinates */ back_t.x = back->left_t.x + back_h * (back->deltaLR_t.x); back_t.y = back->left_t.y + back_h * (back->deltaLR_t.y); back_t.z = back->left_t.z + back_h * (back->deltaLR_t.z); /* Determine frontmost pixel */ deltaFB_t.x = (back_t.x - front_t.x); deltaFB_t.y = (back_t.y - front_t.y); deltaFB_t.z = (back_t.z - front_t.z); /* Step in such a way that you don't move more than one texel in each dimension per step */ h = (deltaFB_t.x < 0) ? -deltaFB_t.x : deltaFB_t.x; depth = (int)h; new_z = (deltaFB_t.y < 0) ? -deltaFB_t.y : deltaFB_t.y; if (new_z > h) {depth = (int)new_z; h = new_z;} new_z = (deltaFB_t.z < 0) ? -deltaFB_t.z : deltaFB_t.z; if (new_z > h) depth = (int)new_z; if (depth == 0) { depth = 1; } else { h = (1< 0) { /*printf("%d : %x,%x,%x : %x,%x,%x\n", depth, tx, ty, tz, dtx, dty, dtz); fflush(stdout);*/ /*if ((tx < 0) || (ty < 0) || (tz < 0) || ((tx >> FIXPOINT_PREC) > renderDesc->tmax.x) || ((ty >> FIXPOINT_PREC) > renderDesc->tmax.y) || ((tz >> FIXPOINT_PREC) > renderDesc->tmax.z)) { printf("Range!\n"); }*/ #if (TEXEL_BSIZE == 3) TEXEL_FETCH; /* Check each rgb component or the total brightness against threshold values? */ #ifdef TEXEL_RGB_BRIGHTNESS value = (srcPix[0] + srcPix[1] + srcPix[2]) / 3; if ((value >= ptl) && (value <= pth)) { #else if (((srcPix[0] >= ptl) && (srcPix[0] <= pth)) || ((srcPix[1] >= ptl) && (srcPix[1] <= pth)) || ((srcPix[2] >= ptl) && (srcPix[2] <= pth))) { value = (srcPix[0] + srcPix[1] + srcPix[2]) / 3; #endif weight = (value + ((1<> weightQuant; red += srcPix[0] * weight; green += srcPix[1] * weight; blue += srcPix[2] * weight; wgtAccu += weight; if ((RENDER_CAST_TYPE)wgtAccu >= wth) break; } #else value = (RENDER_CAST_TYPE)(TEXEL_FETCH); if ((value >= ptl) && (value <= pth)) { #ifdef RENDER_FLOAT_TYPE weight = weightScale * value; #else /* Use weights: bright pixels count more and make the scan terminate much faster than dark ones */ weight = (value + ((1<> weightQuant; #endif #ifdef RENDER_FLOAT_TYPE /* Especially with FP types, value and thus weight may be negative, but weight must be positive! */ if (weight < 0) weight = -weight; #endif pixel += value * weight; wgtAccu += weight; if ((RENDER_CAST_TYPE)wgtAccu >= wth) break; } #endif tx += dtx; ty += dty; tz += dtz; depth--; } /* Pixel transparent? */ #if (TEXEL_BSIZE == 3) if (wgtAccu != 0) { red /= wgtAccu; green /= wgtAccu; blue /= wgtAccu; } # ifdef TEXEL_RGB_BRIGHTNESS if ((red + green + blue < 3*ptl) || (red + green + blue > 3*pth)) # else if (((red < ptl) && (green < ptl) && (blue < ptl)) || ((red > pth) && (green > pth) && (blue > pth))) # endif { dest.c += 3; } #else if (wgtAccu != 0) pixel /= wgtAccu; if ((pixel < ptl) || (pixel > pth)) { # if (TEXEL_BSIZE == 1) dest.c++; # elif (TEXEL_BSIZE == 2) dest.s++; # elif (TEXEL_BSIZE == 4) dest.l++; # else dest.d++; # endif } #endif else { /* Try grey-level gradient shading */ if (ppd->lightsAmbient >= 0) { real_t vx0, vx1, vy0, vy1, vz0, vz1, norm; int offset; /* Position of pixel the ray aborted in */ tx = (tx - dtx) >> FIXPOINT_PREC; ty = (ty - dty) >> FIXPOINT_PREC; tz = (tz - dtz) >> FIXPOINT_PREC; if (tx < 0) tx = 0; if (tx > td->widthx-1) tx = td->widthx-1; if (ty < 0) ty = 0; if (ty > td->widthy-1) ty = td->widthy-1; if (tz < 0) tz = 0; if (tz > td->widthz-1) tz = td->widthz-1; if (ppd->kDesc->region == 0) { real_t center; #if 1 #define RENDER_NORMAL_COORD(t, width, v, delta) \ if (t <= 0) v##0 = center; else v##0 = (center + (real_t) RENDER_TABLE_TYPE [offset - delta])/2; \ if (t >= (width)-1) v##1 = center; else v##1 = (center + (real_t) RENDER_TABLE_TYPE [offset + delta])/2; #else #define RENDER_NORMAL_COORD(t, width, v, delta) \ if (t <= 0) v##0 = 0.5; else {center = (real_t) RENDER_TABLE_TYPE [offset - delta]; v##0 = (1 + ((center >= ptl) && (center <= pth)) ? 1 : 0) / 2;} \ if (t >= (width)-1) v##1 = 0.5; else {center = (real_t) RENDER_TABLE_TYPE [offset + delta]; v##1 = (1 + ((center >= ptl) && (center <= pth)) ? 1 : 0) / 2;} #endif offset = (((tx * dimy) + ty) * dimz) + tz; /*printf("%d,%d,%d,%d,%p\n", tx, ty, tz, offset, RENDER_TABLE_TYPE); fflush(stdout);*/ center = (real_t) RENDER_TABLE_TYPE [offset]; RENDER_NORMAL_COORD(tx, td->widthx, vx, dimyz); RENDER_NORMAL_COORD(ty, td->widthy, vy, dimz); RENDER_NORMAL_COORD(tz, td->widthz, vz, 1); vx1 -= vx0; vy1 -= vy0; vz1 -= vz0; } else { int xl, xh, yl, yh, zl, zh, ix, iy, iz, region, region2; RENDER_CAST_TYPE val; long offbase, koff, koffbase; real_t *kernel; region = ppd->kDesc->region; region2 = 2*region + 1; /* Use mid-point of kernel */ kernel = ppd->kDesc->kernel + ((region * region2) + region)*region2 + region; xl = tx - region; if (xl < 0) xl = 0; xh = tx + region; if (xh > td->widthx-1) xh = td->widthx-1; yl = ty - region; if (yl < 0) yl = 0; yh = ty + region; if (yh > td->widthy-1) yh = td->widthy-1; zl = tz - region; if (zl < 0) zl = 0; zh = tz + region; if (zh > td->widthz-1) zh = td->widthz-1; vx1 = 0.0; vy1 = 0.0; vz1 = 0.0; koffbase = ((xl-tx)*region2 + (yl-ty))*region2; region = region2; region2 *= region2; offbase = (((xl * dimy) + yl) * dimz); for (ix = xl; ix <= xh; ix++, offbase += dimyz, koffbase += region2) { offset = offbase; koff = koffbase; for (iy = yl; iy <= yh; iy++, offset += dimz, koff += region) { for (iz = zl; iz <= zh; iz++) { #if (TEXEL_BSIZE == 3) srcPix = RENDER_TABLE_TYPE + (offset + iz) * TEXEL_BSIZE; val = (RENDER_CAST_TYPE)((srcPix[0] + srcPix[1] + srcPix[2]) / 3); #else val = (RENDER_CAST_TYPE) RENDER_TABLE_TYPE [offset + iz]; #endif #if (!defined(RENDER_RGB_BRIGHTNESS) && (TEXEL_BSIZE == 3)) if (((srcPix[0] >= ptl) || (srcPix[1] >= ptl) || (srcPix[2] >= ptl)) && ((srcPix[0] <= pth) || (srcPix[1] <= pth) || (srcPix[2] <= pth))) #else if ((val >= ptl) && (val <= pth)) #endif { real_t wgtval; /* Fold the data with a weighing kernel */ wgtval = (real_t)(kernel[koff + iz-tz] * val); /* * Whether we use the central values too has no effect on the sums, so * let's no use them for a little extra speed. */ if (ix < tx) vx1 -= wgtval; if (ix > tx) vx1 += wgtval; if (iy < ty) vy1 -= wgtval; if (iy > ty) vy1 += wgtval; if (iz < tz) vz1 -= wgtval; if (iz > tz) vz1 += wgtval; } } } } /* Surface normal n, oriented into the volume now in vx1, vy1, vz1*/ } norm = vx1*vx1 + vy1*vy1 + vz1*vz1; /* calculate vector l from light source to voxel */ vx0 = (real_t)tx - ppd->lights.x; vy0 = (real_t)ty - ppd->lights.y; vz0 = (real_t)tz - ppd->lights.z; /* norm = 1 / (|| n ||^2 * || l ||^2) */ norm *= (vx0*vx0 + vy0*vy0 + vz0*vz0); /* * In case voxColour != NULL it points to a uint32 / float / double value describing the colour * each voxel is assigned. This is good for isosurface rendering, but naturally doesn't make * any sense when shading is disabled. */ if (norm != 0) { real_t lweight, gweight; #if (TEXEL_BSIZE == 3) RENDER_CAST_TYPE addGrey; #endif norm = (real_t)(1/sqrt(norm)); /* norm = n * l = cos(alpha) */ norm = norm * (vx0 * vx1 + vy0 * vy1 + vz0 * vz1); /* Check if the cos is within the angle specified */ if ((lweight = norm - ppd->lightsCos) < 0) lweight = 0; else lweight *= ppd->lightsScale; /* lweight = multiplier for pixel intensities */ lweight = (real_t)(ppd->lightsAmbient + lweight * (1.0 - ppd->lightsAmbient)); if ((gweight = norm - ppd->lightsScintCos) < 0) gweight = 0; else gweight *= ppd->lightsScintScale; #if (TEXEL_BSIZE == 3) if (voxColour != NULL) { red = (*voxColour) & 0xff; green = ((*voxColour) >> 8) & 0xff; blue = ((*voxColour) >> 16) & 0xff; } addGrey = (RENDER_CAST_TYPE)(((red + green + blue) / 3) * gweight); red = (uint32)(lweight * red + addGrey); green = (uint32)(lweight * green + addGrey); blue = (uint32)(lweight * blue + addGrey); /* Limit range */ if (red > 0xff) red = 0xff; if (green > 0xff) green = 0xff; if (blue > 0xff) blue = 0xff; #else if (voxColour != NULL) { pixel = *voxColour; } /* Add a ``white''-component depending on scintillation angle and gain parameter */ pixel = (RENDER_CAST_TYPE)(pixel * (lweight + gweight)); #ifndef RENDER_FLOAT_TYPE /* Limit range for non-floating types */ if (pixel > ((1<<(8*TEXEL_BSIZE))-1)) pixel = ((1<<(8*TEXEL_BSIZE))-1); #endif #endif } } #if (TEXEL_BSIZE == 3) dest.c[0] = (uint8)red; dest.c[1] = (uint8)green; dest.c[2] = (uint8)blue; dest.c += 3; #else # if (TEXEL_BSIZE == 1) *dest.c++ = (uint8)pixel; # elif (TEXEL_BSIZE == 2) *dest.s++ = (uint16)pixel; # elif (TEXEL_BSIZE == 4) # ifdef RENDER_FLOAT_TYPE *dest.f++ = (float)pixel; # else *dest.l++ = (uint32)pixel; # endif # else *dest.d++ = (double)pixel; # endif #endif } } }