diff --git a/core/src/processing/core/PConstants.java b/core/src/processing/core/PConstants.java index c87dafcbf..082794b2f 100644 --- a/core/src/processing/core/PConstants.java +++ b/core/src/processing/core/PConstants.java @@ -309,13 +309,14 @@ public interface PConstants { //static final int SCALE_STROKE_WIDTH = 0; //static final int LIGHTING_AFFECTS_STROKE = 1; - static final int ENABLE_NATIVE_FONTS = 2; - static final int DISABLE_TEXT_SMOOTH = 3; + static final int ENABLE_NATIVE_FONTS = 2; + static final int DISABLE_TEXT_SMOOTH = 3; //static final int DISABLE_SMOOTH_HACK = 4; - static final int DISABLE_DEPTH_TEST = 5; - static final int NO_FLYING_POO = 6; - static final int ENABLE_DEPTH_SORT = 7; - static final int DISABLE_ERROR_REPORT = 8; + static final int DISABLE_DEPTH_TEST = 5; + static final int NO_FLYING_POO = 6; + static final int ENABLE_DEPTH_SORT = 7; + static final int DISABLE_ERROR_REPORT = 8; + static final int ENABLE_ACCURATE_TEXTURES = 9; - static final int HINT_COUNT = 9; + static final int HINT_COUNT = 10; } diff --git a/core/src/processing/core/PGraphics3D.java b/core/src/processing/core/PGraphics3D.java index df19ea23e..9620c9c16 100644 --- a/core/src/processing/core/PGraphics3D.java +++ b/core/src/processing/core/PGraphics3D.java @@ -87,6 +87,17 @@ public class PGraphics3D extends PGraphics { protected PMatrix forwardTransform; protected PMatrix reverseTransform; + // Added by ewjordan for accurate texturing purposes. Screen plane is + // not scaled to pixel-size, so these manually keep track of its size + // from frustum() calls. Sorry to add public vars, is there a way + // to compute these from something publicly available without matrix ops? + // (used once per triangle in PTriangle with ENABLE_ACCURATE_TEXTURES) + protected float leftScreen; + protected float rightScreen; + protected float topScreen; + protected float bottomScreen; + protected float nearPlane; //depth of near clipping plane + // ........................................................ // pos of first vertex of current shape in vertices array @@ -1341,6 +1352,24 @@ public class PGraphics3D extends PGraphics { int tex = triangles[i][TEXTURE_INDEX]; int index = triangles[i][INDEX]; + float shift = 0.49f; + boolean shifted = false; + if (drawing2D() && (a[MZ] == 0)) { + shifted = true; + a[X] += shift; + a[Y] += shift; + a[VX] += shift*a[VW]; + a[VY] += shift*a[VW]; + b[X] += shift; + b[Y] += shift; + b[VX] += shift*b[VW]; + b[VY] += shift*b[VW]; + c[X] += shift; + c[Y] += shift; + c[VX] += shift*b[VW]; + c[VY] += shift*b[VW]; + } + triangle.reset(); // This is only true when not textured. We really should pass SPECULAR @@ -1377,6 +1406,14 @@ public class PGraphics3D extends PGraphics { b[X], b[Y], b[Z], c[X], c[Y], c[Z]); + // Need to pass camera-space coordinates to triangle renderer + // in order to compute true texture coordinates, else skip it + if (hints[ENABLE_ACCURATE_TEXTURES]){ + triangle.setCamVertices(a[VX], a[VY], a[VZ], + b[VX], b[VY], b[VZ], + c[VX], c[VY], c[VZ]); + } + triangle.setIndex(index); triangle.render(); @@ -1401,6 +1438,21 @@ public class PGraphics3D extends PGraphics { raw.vertex(c[X], c[Y]); } } + + if (drawing2D() && shifted){ + a[X] -= shift; + a[Y] -= shift; + a[VX] -= shift*a[VW]; + a[VY] -= shift*a[VW]; + b[X] -= shift; + b[Y] -= shift; + b[VX] -= shift*b[VW]; + b[VY] -= shift*b[VW]; + c[X] -= shift; + c[Y] -= shift; + c[VX] -= shift*b[VW]; + c[VY] -= shift*b[VW]; + } } if (raw != null) { @@ -1409,29 +1461,6 @@ public class PGraphics3D extends PGraphics { } - /* - public void triangleCallback(float x1, float y1, float z1, - float r1, float g1, float b1, float a1, - float u1, float v1, boolean e1, - float x2, float y2, float z2, - float r2, float g2, float b2, float a2, - float u2, float v2, boolean e2, - float x3, float y3, float z3, - float r3, float g3, float b3, float a3, - float u3, float v3, boolean e3, - PImage texture) { - } - - - public void lineCallback(float x1, float y1, float z1, - float r1, float g1, float b1, float a1, - float x2, float y2, float z2, - float r2, float g2, float b2, float a2, - float weight, int cap, int join) { - } - */ - - protected void depth_sort_lines() { } @@ -1448,6 +1477,35 @@ public class PGraphics3D extends PGraphics { float b[] = vertices[lines[i][VERTEX2]]; int index = lines[i][INDEX]; + // 2D hack added by ewjordan 6/13/07 + // Offset coordinates by a little bit if drawing 2D graphics. + // http://dev.processing.org/bugs/show_bug.cgi?id=95 + + // This hack fixes a bug caused by numerical precision issues when + // applying the 3D transformations to coordinates in the screen plane + // that should actually not be altered under said transformations. + // It will not be applied if any transformations other than translations + // are active, nor should it apply in OpenGL mode (PGraphicsOpenGL + // overrides render_lines(), so this should be fine). + // This fix exposes a last-pixel bug in the lineClipCode() function + // of PLine.java, so that fix must remain in place if this one is used. + + // Note: the "true" fix for this bug is to change the pixel coverage + // model so that the threshold for display does not lie on an integer + // boundary. Search "diamond exit rule" for info the OpenGL approach. + + if (drawing2D() && a[MZ] == 0) { + a[X] += 0.01; + a[Y] += 0.01; + a[VX] += 0.01*a[VW]; + a[VY] += 0.01*a[VW]; + b[X] += 0.01; + b[Y] += 0.01; + b[VX] += 0.01*b[VW]; + b[VY] += 0.01*b[VW]; + } + // end 2d-hack + line.reset(); line.setIntensities(a[SR], a[SG], a[SB], a[SA], @@ -1519,19 +1577,21 @@ public class PGraphics3D extends PGraphics { // figure out which dimension is the perpendicular axis boolean foundValidX = false; boolean foundValidY = false; + for (int i = vertex_start; i < vertex_end; i++) { - if (vertices[i][MX] != 0) foundValidX = true; - if (vertices[i][MY] != 0) foundValidY = true; + for (int j = i; j < vertex_end; j++){ + if ( vertices[i][MX] != vertices[j][MX] ) foundValidX = true; + if ( vertices[i][MY] != vertices[j][MY] ) foundValidY = true; + } } - if (foundValidX && !foundValidY) { + + if (foundValidX) { //d1 = MX; // already the case d2 = MZ; - - } else if (!foundValidX && foundValidY) { + } else if (foundValidY) { // ermm.. which is the proper order for cw/ccw here? d1 = MY; d2 = MZ; - } else { // screw it, this polygon is just f-ed up return; @@ -3055,6 +3115,16 @@ public class PGraphics3D extends PGraphics { */ public void frustum(float left, float right, float bottom, float top, float znear, float zfar) { + + //if (hints[ENABLE_ACCURATE_TEXTURES]){ + //These vars are only needed if accurate textures are used, however, + //there is the possibility that accurate texturing will only be turned + //on after the perspective matrix has already been set, so we might as + //well store these no matter what since it's not much overhead. + leftScreen = left; rightScreen = right; bottomScreen = bottom; topScreen = top; + nearPlane = znear; + //} + //System.out.println(projection); projection.set((2*znear)/(right-left), 0, (right+left)/(right-left), 0, 0, (2*znear)/(top-bottom), (top+bottom)/(top-bottom), 0, @@ -3071,6 +3141,41 @@ public class PGraphics3D extends PGraphics { } + /* + * This function checks if the modelview matrix is set up to likely be + * drawing in 2D. It merely checks if the non-translational piece of the + * matrix is unity. If this is to be used, it should be coupled with a + * check that the raw vertex coordinates lie in the z=0 plane. + * Mainly useful for applying sub-pixel shifts to avoid 2d artifacts + * in the screen plane. + * Added by ewjordan 6/13/07 + * + * TODO need to invert the logic here so that we can simply return + * the value, rather than calculating true/false and returning it. + */ + private boolean drawing2D() { + if (modelview.m00 != 1.0f || + modelview.m11 != 1.0f || + modelview.m22 != 1.0f || // check scale + modelview.m01 != 0.0f || + modelview.m02 != 0.0f || // check rotational pieces + modelview.m10 != 0.0f || + modelview.m12 != 0.0f || + modelview.m20 != 0.0f || + modelview.m21 != 0.0f || + !((camera.m23-modelview.m23) <= EPSILON && + (camera.m23-modelview.m23) >= -EPSILON)) { // check for z-translation + // Something about the modelview matrix indicates 3d drawing + // (or rotated 2d, in which case 2d subpixel fixes probably aren't needed) + return false; + } else { + //The matrix is mapping z=0 vertices to the screen plane, + // which means it's likely that 2D drawing is happening. + return true; + } + } + + ////////////////////////////////////////////////////////////// diff --git a/core/src/processing/core/PLine.java b/core/src/processing/core/PLine.java index a5483cbd8..b69780a8a 100644 --- a/core/src/processing/core/PLine.java +++ b/core/src/processing/core/PLine.java @@ -484,8 +484,13 @@ public class PLine implements PConstants int xmax = SCREEN_WIDTH1; int ymax = SCREEN_HEIGHT1; - return ((yi < ymin ? 8 : 0) | (yi > ymax ? 4 : 0) | - (xi < xmin ? 2 : 0) | (xi > xmax ? 1 : 0)); + //return ((yi < ymin ? 8 : 0) | (yi > ymax ? 4 : 0) | + // (xi < xmin ? 2 : 0) | (xi > xmax ? 1 : 0)); + //(int) added by ewjordan 6/13/07 because otherwise we sometimes clip last pixel when it should actually be displayed. + //Currently the min values are okay because values less than 0 should not be rendered; however, bear in mind that + //(int) casts towards zero, so without this clipping, values between -1+eps and +1-eps would all be rendered as 0. + return ((yi < ymin ? 8 : 0) | ((int)yi > ymax ? 4 : 0) | + (xi < xmin ? 2 : 0) | ((int)xi > xmax ? 1 : 0)); } diff --git a/core/src/processing/core/PTriangle.java b/core/src/processing/core/PTriangle.java index 157eacfb9..7142af5b6 100644 --- a/core/src/processing/core/PTriangle.java +++ b/core/src/processing/core/PTriangle.java @@ -56,11 +56,20 @@ public class PTriangle implements PConstants public boolean INTERPOLATE_RGB; public boolean INTERPOLATE_ALPHA; + // the power of 2 that tells how many pixels to interpolate + // for between exactly computed texture coordinates + private static final int DEFAULT_INTERP_POWER = 3; + private static int TEX_INTERP_POWER = DEFAULT_INTERP_POWER; + // Vertex coordinates private float[] x_array; private float[] y_array; private float[] z_array; + private float[] camX; + private float[] camY; + private float[] camZ; + // U,V coordinates private float[] u_array; private float[] v_array; @@ -192,6 +201,23 @@ public class PTriangle implements PConstants /** */ private boolean m_bilinear; + + + //Vectors needed in accurate texture code + //We store them as class members to avoid too much code duplication + private float ax,ay,az; + private float bx,by,bz; + private float cx,cy,cz; + private float nearPlaneWidth; + private float nearPlaneHeight; + private float nearPlaneDepth; + private float xmult; + private float ymult; + private float newax,newbx,newcx; //optimization vars...not pretty, but save a couple mults per pixel + private boolean firstSegment; //are we currently drawing the first piece of the triangle, or have we already done so? + + + public PTriangle(PGraphics3D g) { //SCREEN_WIDTH = g.width; @@ -212,6 +238,10 @@ public class PTriangle implements PConstants g_array = new float[3]; b_array = new float[3]; a_array = new float[3]; + + camX = new float[3]; + camY = new float[3]; + camZ = new float[3]; this.parent = g; reset(); @@ -271,6 +301,28 @@ public class PTriangle implements PConstants z_array[2] = z2; } + /** + * Pass camera-space coordinates for the triangle (needed to render if ENABLE_ACCURATE_TEXTURES is hinted). + */ + public void setCamVertices(float x0, float y0, float z0, + float x1, float y1, float z1, + float x2, float y2, float z2) { + //Generally this will not need to be called manually, currently called if hints[ENABLE_ACCURATE_TEXTURES] + //from PGraphics3D.render_triangles() + + camX[0] = x0; + camX[1] = x1; + camX[2] = x2; + + camY[0] = y0; + camY[1] = y1; + camY[2] = y2; + + camZ[0] = z0; + camZ[1] = z1; + camZ[2] = z2; + } + /** * Sets the UV coordinates of the texture */ @@ -433,6 +485,10 @@ public class PTriangle implements PConstants float y1 = y_array[1]; float y2 = y_array[2]; + // For accurate texture interpolation, need to mark whether + // we've already pre-calculated for the triangle + firstSegment = true; + // do backface culling? if (m_culling) { x0 = x_array[0]; @@ -878,8 +934,159 @@ public class PTriangle implements PConstants drawsegment_gouraud_texture32(xadd2, xadd1, yi1,yi2); } } + } + } + } + + + /* + Accurate texturing code by ewjordan@gmail.com, April 14, 2007 + The getColorFromTexture() function should be inlined and optimized so that most of the heavy lifting + happens outside the per-pixel loop. The unoptimized generic algorithm looks like this (unless noted, + all of these variables are vectors, so the actual code will look messier): + + p = camera space vector where u == 0, v == 0; + m = vector from p to location where u == TEX_WIDTH; + n = vector from p to location where v == TEX_HEIGHT; + + A = p cross n; + B = m cross p; + C = n cross m; + A *= texture.width; + B *= texture.height; + + for (scanlines in triangle){ + float a = S * A; + float b = S * B; + float c = S * C; + for (pixels in scanline){ + int u = a/c; + int v = b/c; + color = texture[v * texture.width + u]; + a += A.x; + b += B.x; + c += C.x; } } + + We don't use this exact algorithm here, however, because of the extra overhead from the divides. + Instead we compute the exact u and v (labelled iu and iv in the code) at the start of each scanline + and we perform a linear interpolation for every linearInterpLength = 1 << TEX_INTERP_POWER pixels. + This means that we only perform the true calculation once in a while, and the rest of the time + the algorithm functions exactly as in the fast inaccurate case, at least in theory. In practice, + even if we set linearInterpLength very high we still incur some speed penalty due to the preprocessing + that must take place per-scanline. A similar method could be applied per scanline to avoid this, but + it would only be worthwhile in the case that we never compute more than one exact calculation per + scanline. If we want something like this, however, it would be best to create another mode of calculation + called "constant-z" interpolation, which could be used for things like floors and ceilings where the + distance from the camera plane never changes over the course of a scanline. We could also add the + vertical analogue for drawing vertical walls. In any case, these are not critical as the current + algorithm runs fairly well, perhaps ~10% slower than the default perspective-less one. + + */ + + /** + * Solve for camera space coordinates of critical texture points and set up per-triangle variables for accurate texturing + */ + private boolean precomputeAccurateTexturing(){ + //Sets all class variables relevant to accurate texture computation + //Should be called once per triangle - checks firstSegment to see if we've already called + + float myFact = 65500.0f; //rescale u/v_array values when inverting matrix and performing other calcs + float myFact2 = 65500.0f; + + //Matrix inversion to find affine transform between (u,v,(1)) -> (x,y,z) + + //OPTIMIZE: There should be a way to avoid the inversion here, which is + //quite expensive (~150 mults). Also it might crap out due to loss of precision depending + //on the scaling of the u/v_arrays. Nothing clever currently happens if the inversion + //fails, since this means the transformation is degenerate - we just pass false back to + //the caller and let it handle the situation. [There is no good solution to this + //case from within this function, since the way the calculation proceeds presumes a non- + //degenerate transformation matrix between camera space and uv space] + + //Obvious optimization: if the vertices are actually at the appropriate texture coordinates + //(e.g. (0,0), (TEX_WIDTH,0), and (0,TEX_HEIGHT)) then we can immediately return the + //right solution without the inversion. This is fairly common, so could speed up + //many cases of drawing. [not implemented] + + //Furthermore, we could cache the p,resultT0,result0T vectors in the triangle's + //basis, since we could then do a look-up and generate the resulting coordinates very simply. + //This would include the above optimization as a special case - we could pre-populate the + //cache with special cases like that and dynamically add more. The idea here is + //that most people simply paste textures onto triangles and move the triangles from + //frame to frame, so any per-triangle-per-frame code is likely wasted effort. + //[not implemented] + + //Note: o0, o1, and o2 vary depending on view angle to triangle, but p, n, and m should not depend on ordering differences + + if(firstSegment){ + + PMatrix myMatrix = new PMatrix( u_array[o0]/myFact, v_array[o0]/myFact2, 1, 0, + u_array[o1]/myFact, v_array[o1]/myFact2, 1, 0, + u_array[o2]/myFact, v_array[o2]/myFact2, 1, 0, + 0, 0, 0, 1); + myMatrix = myMatrix.invert(); //A 3x3 inversion would be more efficient here, given that the fourth r/c are unity + if (myMatrix == null) {return false;} //if the matrix inversion had trouble, let the caller know + float m00, m01, m02, m10, m11, m12, m20, m21, m22; + m00 = myMatrix.m00*camX[o0]+myMatrix.m01*camX[o1]+myMatrix.m02*camX[o2]; + m01 = myMatrix.m10*camX[o0]+myMatrix.m11*camX[o1]+myMatrix.m12*camX[o2]; + m02 = myMatrix.m20*camX[o0]+myMatrix.m21*camX[o1]+myMatrix.m22*camX[o2]; + m10 = myMatrix.m00*camY[o0]+myMatrix.m01*camY[o1]+myMatrix.m02*camY[o2]; + m11 = myMatrix.m10*camY[o0]+myMatrix.m11*camY[o1]+myMatrix.m12*camY[o2]; + m12 = myMatrix.m20*camY[o0]+myMatrix.m21*camY[o1]+myMatrix.m22*camY[o2]; + m20 = -(myMatrix.m00*camZ[o0]+myMatrix.m01*camZ[o1]+myMatrix.m02*camZ[o2]); + m21 = -(myMatrix.m10*camZ[o0]+myMatrix.m11*camZ[o1]+myMatrix.m12*camZ[o2]); + m22 = -(myMatrix.m20*camZ[o0]+myMatrix.m21*camZ[o1]+myMatrix.m22*camZ[o2]); + + float px = m02; + float py = m12; + float pz = m22; + float resultT0x = m00*TEX_WIDTH+m02; //Bugfix: possibly we should use F_TEX_WIDTH/HEIGHT instead? Seems to read off end of array in that case, though... + float resultT0y = m10*TEX_WIDTH+m12; + float resultT0z = m20*TEX_WIDTH+m22; + float result0Tx = m01*TEX_HEIGHT+m02; + float result0Ty = m11*TEX_HEIGHT+m12; + float result0Tz = m21*TEX_HEIGHT+m22; + float mx = resultT0x-m02; + float my = resultT0y-m12; + float mz = resultT0z-m22; + float nx = result0Tx-m02; + float ny = result0Ty-m12; + float nz = result0Tz-m22; + + //avec = p x n + ax = (py*nz-pz*ny)*TEX_WIDTH; //F_TEX_WIDTH/HEIGHT? + ay = (pz*nx-px*nz)*TEX_WIDTH; + az = (px*ny-py*nx)*TEX_WIDTH; + //bvec = m x p + bx = (my*pz-mz*py)*TEX_HEIGHT; + by = (mz*px-mx*pz)*TEX_HEIGHT; + bz = (mx*py-my*px)*TEX_HEIGHT; + //cvec = n x m + cx = ny*mz-nz*my; + cy = nz*mx-nx*mz; + cz = nx*my-ny*mx; + } + + nearPlaneWidth = parent.rightScreen-parent.leftScreen; + nearPlaneHeight = parent.topScreen-parent.bottomScreen; + nearPlaneDepth = parent.nearPlane; + xmult = nearPlaneWidth / SCREEN_WIDTH; //one pixel width in nearPlane coordinates + ymult = nearPlaneHeight / SCREEN_HEIGHT; + newax = ax*xmult;//Extra scalings to map screen plane units to pixel units + newbx = bx*xmult; + newcx = cx*xmult; + return true; + } + + /** + * Set the power of two used for linear interpolation of texture coordinates. + * A true texture coordinate is computed every 2^pwr pixels along a scanline. + */ + static public void setInterpPower(int pwr){ + //Currently must be invoked from P5 as PTriangle.setInterpPower(...) + TEX_INTERP_POWER = pwr; } @@ -1134,8 +1341,10 @@ public class PTriangle implements PConstants /** - * 8-bit alpha texture + * 8-bit plain texture */ + + //THIS IS MESSED UP, NEED TO GRAB ORIGINAL VERSION!!! private void drawsegment_texture8 ( float leftadd, @@ -1143,6 +1352,26 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -1159,6 +1388,8 @@ public class PTriangle implements PConstants if (xstart < 0) xstart = 0; + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -1171,7 +1402,64 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + + for ( ; xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } // try-catch just in case pixel offset it out of range try { @@ -1184,7 +1472,7 @@ public class PTriangle implements PConstants int iui = iu & 0xFFFF; al0 = m_texture[ofs] & 0xFF; int al1 = m_texture[ofs + 1] & 0xFF; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int al2 = m_texture[ofs] & 0xFF; int al3 = m_texture[ofs + 1] & 0xFF; al0 = al0 + (((al1-al0) * iui) >> 16); @@ -1204,13 +1492,15 @@ public class PTriangle implements PConstants } catch (Exception e) { } + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } iz+=izadd; } - + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; - xleft+=leftadd; xrght+=rghtadd; uleft+=uleftadd; @@ -1220,6 +1510,7 @@ public class PTriangle implements PConstants } + /** * 8-bit texutre + alpha */ @@ -1230,6 +1521,26 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -1247,6 +1558,8 @@ public class PTriangle implements PConstants if (xstart < 0) xstart = 0; + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -1260,7 +1573,64 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + + for ( ; xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } // try-catch just in case pixel offset it out of range try { @@ -1273,7 +1643,7 @@ public class PTriangle implements PConstants int iui = iu & 0xFFFF; al0 = m_texture[ofs] & 0xFF; int al1 = m_texture[ofs + 1] & 0xFF; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int al2 = m_texture[ofs] & 0xFF; int al3 = m_texture[ofs + 1] & 0xFF; al0 = al0 + (((al1-al0) * iui) >> 16); @@ -1294,12 +1664,15 @@ public class PTriangle implements PConstants } catch (Exception e) { } + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } iz+=izadd; ia+=iaadd; } - + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; xleft+=leftadd; xrght+=rghtadd; @@ -1310,9 +1683,8 @@ public class PTriangle implements PConstants } } - /** - * Plain 24-bit texutre + * Plain 24-bit texture */ private void drawsegment_texture24 ( @@ -1321,45 +1693,198 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; - float iuf = iuadd; float ivf = ivadd; - while (ytop < ybottom) { + int ypixel = ytop/SCREEN_WIDTH;//ACCTEX + int lastRowStart = m_texture.length - TEX_WIDTH - 2;//If we're past this index, we can't shift down a row w/o throwing an exception +// int exCount = 0;//counter for exceptions caught + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; //bring this local since it will be accessed often + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + + //Interpolation length of 16 tends to look good except at a small angle; 8 looks okay then, except for the + //above issue. When viewing close to flat, as high as 32 is often acceptable. Could add dynamic interpolation + //settings based on triangle angle - currently we just pick a value and leave it (by default I have the + //power set at 3, so every 8 pixels a true coordinate is calculated, which seems a decent compromise). + + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion gave us garbage, revert to normal rendering (something is degenerate) + } + } + + + while (ytop < ybottom) {//scanline loop int xstart = (int) (xleft + PIXEL_CENTER); - if (xstart < 0) - xstart = 0; + if (xstart < 0){ xstart = 0; } + + int xpixel = xstart;//accurate mode int xend = (int) (xrght + PIXEL_CENTER); - if (xend > SCREEN_WIDTH) - xend = SCREEN_WIDTH; - + if (xend > SCREEN_WIDTH){ xend = SCREEN_WIDTH; } float xdiff = (xstart + PIXEL_CENTER) - xleft; int iu = (int) (iuf * xdiff + uleft); int iv = (int) (ivf * xdiff + vleft); float iz = izadd * xdiff + zleft; - xstart+=ytop; xend+=ytop; - for ( ; xstart < xend; xstart++ ) { - // try-catch just in case pixel offset it out of range - try - { + if (accurateMode){ + //off by one (half, I guess) hack, w/o it the first rows are outside the texture - maybe a mistake somewhere? + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az;//OPT - some of this could be brought out of the y-loop since + b = screenx*bx+screeny*by+screenz*bz;//xpixel and ypixel just increment by the same numbers each iteration. + c = screenx*cx+screeny*cy+screenz*cz;//Probably not a big bottleneck, though. + } + + //Figure out whether triangle is going further into the screen or not as we move along scanline + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + + //Set up linear interpolation between calculated texture points + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + //float fdeltaU = 0; float fdeltaV = 0;//vars for floating point interpolating version of algorithm + //float fiu = 0; float fiv = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + + //Bugfix (done): it's a Really Bad Thing to interpolate along a scanline when the triangle goes deeper into the screen, + //because if the angle is severe enough the last control point for interpolation may cross the vanishing + //point. This leads to some pretty nasty artifacts, and ideally we should scan from right to left if the + //triangle is better served that way, or (what we do now) precompute the offset that we'll need so that we end up + //with a control point exactly at the furthest edge of the triangle. + + if (accurateMode&&goingIn){ + //IMPORTANT!!! Results are horrid without this hack! + //If polygon goes into the screen along scan line, we want to match the control point to the furthest point drawn + //since the control points are less meaningful the closer you are to the vanishing point. + //We'll do this by making the first control point lie before the start of the scanline (safe since it's closer to us) + + int rightOffset = (xend-xstart-1)%linearInterpLength; //"off by one" hack...probably means there's a small bug somewhere + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + + //Take step to control point to the left of start pixel + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + + //Now step to right control point + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + + //Get deltas for interpolation + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + //Otherwise the left edge is further, and we pin the first control point to it + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + + for ( ; xstart < xend; xstart++ ) {//pixel loop - keep trim, can execute thousands of times per frame + //boolean drawBlack = false; //used to display control points + if(accurateMode){ + /* //Non-interpolating algorithm - slowest version, calculates exact coordinate for each pixel, + //and casts from float->int + float oneoverc = 65536.0f/c; //a bit faster to pre-divide for next two steps + iu = (int)(a*oneoverc); + iv = (int)(b*oneoverc); + a += newax; + b += newbx; + c += newcx; + */ + + //Float while calculating, int while interpolating + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + //drawBlack = true; + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; //ints are used for interpolation, not actual computation + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ //race through using linear interpolation if we're not at a control point + iu += deltaU; + iv += deltaV; + } + interpCounter++; + + /* //Floating point interpolating version - slower than int thanks to casts during interpolation steps + if (interpCounter == 0) { + interpCounter = linearInterpLength; + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; + oldfv = fv; + fu = (a*oneoverc); + fv = (b*oneoverc); + //oldu = u; oldv = v; + fiu = oldfu; + fiv = oldfv; + fdeltaU = (fu-oldfu)/linearInterpLength; + fdeltaV = (fv-oldfv)/linearInterpLength; + } + else{ + fiu += fdeltaU; + fiv += fdeltaV; + } + interpCounter--; + iu = (int)(fiu); iv = (int)(fiv);*/ + + } + + // try-catch just in case pixel offset is out of range + try{ if (noDepthTest || (iz <= m_zbuffer[xstart])) { m_zbuffer[xstart] = iz; if (m_bilinear) { + //We could (should?) add bounds checking on iu and iv here (keep in mind the 16 bit shift!). + //This would also be the place to add looping texture mode (bounds check == clamped). + //For looping/clamped textures, we'd also need to change PGraphics.textureVertex() to remove + //the texture range check there (it constrains normalized texture coordinates from 0->1). + int ofs = (iv >> 16) * TEX_WIDTH + (iu >> 16); int iui = (iu & 0xFFFF) >> 9; int ivi = (iv & 0xFFFF) >> 9; + //if(ofs < 0) { ofs += TEX_WIDTH; } + //if(ofs > m_texture.length-2) {ofs -= TEX_WIDTH; } + // get texture pixels int pix0 = m_texture[ofs]; int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; //quick hack to thwart exceptions int pix2 = m_texture[ofs]; int pix3 = m_texture[ofs + 1]; @@ -1384,33 +1909,36 @@ public class PTriangle implements PConstants dn = red2 + ((((pix3 & 0xFF) - red2) * iui) >> 7); int blu = up + (((dn-up) * ivi) >> 7); - // m_pixels[xstart] = (red & 0xFF0000) | (grn & 0xFF00) | (blu & 0xFF); - } else { + //if (drawBlack){ m_pixels[xstart] = 0; } + + } else{ m_pixels[xstart] = m_texture[(iv >> 16) * TEX_WIDTH + (iu >> 16)]; } m_stencil[xstart] = p; } - } - catch (Exception e) { - - } + } catch (Exception e) {/*exCount++;*/} + iz+=izadd; + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; - iz+=izadd; - } - + } + } + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; xleft+=leftadd; xrght+=rghtadd; + zleft+=zleftadd; uleft+=uleftadd; vleft+=vleftadd; - zleft+=zleftadd; - } + } +// if (exCount>0) System.out.println(exCount+" exceptions in this segment"); } + /** - * Alpha 24-bit texutre + * Alpha 24-bit texture */ private void drawsegment_texture24_alpha ( @@ -1419,6 +1947,26 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -1432,6 +1980,8 @@ public class PTriangle implements PConstants if (xstart < 0) xstart = 0; + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -1444,9 +1994,66 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + for ( ; xstart < xend; xstart++ ) { - // try-catch just in case pixel offset it out of range + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + try { if (noDepthTest || (iz <= m_zbuffer[xstart])) { @@ -1463,7 +2070,7 @@ public class PTriangle implements PConstants // get texture pixels int pix0 = m_texture[ofs]; int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int pix2 = m_texture[ofs]; int pix3 = m_texture[ofs + 1]; @@ -1510,13 +2117,16 @@ public class PTriangle implements PConstants m_stencil[xstart] = p; } } - catch (Exception e) { - } + catch (Exception e) {} + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } ia+=iaadd; iz+=izadd; } + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; @@ -1539,6 +2149,26 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -1550,6 +2180,8 @@ public class PTriangle implements PConstants int xstart = (int) (xleft + PIXEL_CENTER); if (xstart < 0) xstart = 0; + + int xpixel = xstart;//accurate mode int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) @@ -1563,7 +2195,65 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + + for ( ; xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + // try-catch just in case pixel offset it out of range try { @@ -1578,7 +2268,7 @@ public class PTriangle implements PConstants // get texture pixels int pix0 = m_texture[ofs]; int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int pix2 = m_texture[ofs]; int pix3 = m_texture[ofs + 1]; @@ -1635,10 +2325,14 @@ public class PTriangle implements PConstants } catch (Exception e) { } + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } iz+=izadd; } + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; @@ -1654,7 +2348,7 @@ public class PTriangle implements PConstants } /** - * Alpha 24-bit texutre + * Alpha 32-bit texutre */ private void drawsegment_texture32_alpha ( @@ -1663,6 +2357,25 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; @@ -1677,6 +2390,8 @@ public class PTriangle implements PConstants if (xstart < 0) xstart = 0; + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -1690,7 +2405,66 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + + + for ( ; xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + // try-catch just in case pixel offset it out of range try { @@ -1708,7 +2482,7 @@ public class PTriangle implements PConstants // get texture pixels int pix0 = m_texture[ofs]; int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int pix2 = m_texture[ofs]; int pix3 = m_texture[ofs + 1]; @@ -1765,11 +2539,15 @@ public class PTriangle implements PConstants } catch (Exception e) { } + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } ia+=iaadd; iz+=izadd; } + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; @@ -1795,6 +2573,26 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -1809,6 +2607,9 @@ public class PTriangle implements PConstants int xstart = (int) (xleft + PIXEL_CENTER); if (xstart < 0) xstart = 0; + + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -1824,7 +2625,65 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + + for ( ; xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + try { if (noDepthTest || (iz <= m_zbuffer[xstart])) { @@ -1836,7 +2695,7 @@ public class PTriangle implements PConstants int iui = iu & 0xFFFF; al0 = m_texture[ofs] & 0xFF; int al1 = m_texture[ofs + 1] & 0xFF; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int al2 = m_texture[ofs] & 0xFF; int al3 = m_texture[ofs + 1] & 0xFF; al0 = al0 + (((al1-al0) * iui) >> 16); @@ -1867,14 +2726,17 @@ public class PTriangle implements PConstants } // + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } ir+=iradd; ig+=igadd; ib+=ibadd; iz+=izadd; } - + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; xleft+=leftadd; xrght+=rghtadd; @@ -1899,6 +2761,26 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -1914,6 +2796,9 @@ public class PTriangle implements PConstants int xstart = (int) (xleft + PIXEL_CENTER); if (xstart < 0) xstart = 0; + + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -1930,7 +2815,65 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + + for ( ; xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + try { if (noDepthTest || (iz <= m_zbuffer[xstart])) { @@ -1942,7 +2885,7 @@ public class PTriangle implements PConstants int iui = iu & 0xFFFF; al0 = m_texture[ofs] & 0xFF; int al1 = m_texture[ofs + 1] & 0xFF; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int al2 = m_texture[ofs] & 0xFF; int al3 = m_texture[ofs + 1] & 0xFF; al0 = al0 + (((al1-al0) * iui) >> 16); @@ -1974,15 +2917,18 @@ public class PTriangle implements PConstants } // + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } ir+=iradd; ig+=igadd; ib+=ibadd; ia+=iaadd; iz+=izadd; } - + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; xleft+=leftadd; xrght+=rghtadd; @@ -2007,6 +2953,26 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -2021,6 +2987,9 @@ public class PTriangle implements PConstants int xstart = (int) (xleft + PIXEL_CENTER); if (xstart < 0) xstart = 0; + + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -2036,7 +3005,65 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + for ( ; xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + try { if (noDepthTest || (iz <= m_zbuffer[xstart])) { @@ -2054,7 +3081,7 @@ public class PTriangle implements PConstants // get texture pixels int pix0 = m_texture[ofs]; int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int pix2 = m_texture[ofs]; int pix3 = m_texture[ofs + 1]; @@ -2089,10 +3116,10 @@ public class PTriangle implements PConstants // int r = (ir >> 16); int g = (ig >> 16); - int b = (ib >> 16); + int bb2 = (ib >> 16); //oops, namespace collision with accurate texture vector b...sorry [ewjordan] // - m_pixels[xstart] = ( ((red * r) & 0xFF000000) | ((grn * g) & 0xFF0000) | (blu * b) ) >> 8; + m_pixels[xstart] = ( ((red * r) & 0xFF000000) | ((grn * g) & 0xFF0000) | (blu * bb2) ) >> 8; m_stencil[xstart] = p; } } @@ -2100,13 +3127,17 @@ public class PTriangle implements PConstants } // + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } ir+=iradd; ig+=igadd; ib+=ibadd; iz+=izadd; } + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; xleft+=leftadd; @@ -2131,6 +3162,27 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -2146,6 +3198,9 @@ public class PTriangle implements PConstants int xstart = (int) (xleft + PIXEL_CENTER); if (xstart < 0) xstart = 0; + + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -2162,7 +3217,64 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + for ( ;xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + // get texture pixel color try { @@ -2185,7 +3297,7 @@ public class PTriangle implements PConstants // get texture pixels int pix0 = m_texture[ofs]; int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int pix2 = m_texture[ofs]; int pix3 = m_texture[ofs + 1]; @@ -2236,8 +3348,11 @@ public class PTriangle implements PConstants } // + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } ir+=iradd; ig+=igadd; ib+=ibadd; @@ -2245,6 +3360,8 @@ public class PTriangle implements PConstants iz+=izadd; } + ypixel++;//accurate mode + ytop+=SCREEN_WIDTH; xleft+=leftadd; xrght+=rghtadd; @@ -2269,6 +3386,27 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; //int p = m_index; @@ -2283,6 +3421,9 @@ public class PTriangle implements PConstants int xstart = (int) (xleft + PIXEL_CENTER); if (xstart < 0) xstart = 0; + + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -2297,8 +3438,65 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } + for ( ; xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + try { if (noDepthTest || (iz <= m_zbuffer[xstart])) { @@ -2317,7 +3515,7 @@ public class PTriangle implements PConstants // get texture pixels int pix0 = m_texture[ofs]; int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int pix2 = m_texture[ofs]; int pix3 = m_texture[ofs + 1]; @@ -2376,14 +3574,17 @@ public class PTriangle implements PConstants } // + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } ir+=iradd; ig+=igadd; ib+=ibadd; iz+=izadd; } - + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; xleft+=leftadd; xrght+=rghtadd; @@ -2407,6 +3608,26 @@ public class PTriangle implements PConstants int ytop, int ybottom ) { + //Accurate texture mode added - comments stripped from dupe code, see drawsegment_texture24() for details + int ypixel = ytop; + int lastRowStart = m_texture.length - TEX_WIDTH - 2; + boolean accurateMode = parent.hints[ENABLE_ACCURATE_TEXTURES]; + float screenx = 0; float screeny = 0; float screenz = 0; + float a = 0; float b = 0; float c = 0; + int linearInterpPower = TEX_INTERP_POWER; + int linearInterpLength = 1 << linearInterpPower; + if (accurateMode){ + if(precomputeAccurateTexturing()){ //see if the precomputation goes well, if so finish the setup + newax *= linearInterpLength; + newbx *= linearInterpLength; + newcx *= linearInterpLength; + screenz = nearPlaneDepth; + firstSegment = false; + } else{ + accurateMode = false; //if the matrix inversion screwed up, revert to normal rendering (something is degenerate) + } + } + ytop*=SCREEN_WIDTH; ybottom*=SCREEN_WIDTH; int p = m_index; @@ -2422,6 +3643,9 @@ public class PTriangle implements PConstants int xstart = (int) (xleft + PIXEL_CENTER); if (xstart < 0) xstart = 0; + + int xpixel = xstart;//accurate mode + int xend = (int) (xrght + PIXEL_CENTER); if (xend > SCREEN_WIDTH) xend = SCREEN_WIDTH; @@ -2437,8 +3661,64 @@ public class PTriangle implements PConstants xstart+=ytop; xend+=ytop; + if (accurateMode){ + screenx = xmult*(xpixel+.5f-(SCREEN_WIDTH/2.0f)); + screeny = ymult*(ypixel+.5f-(SCREEN_HEIGHT/2.0f)); + a = screenx*ax+screeny*ay+screenz*az; + b = screenx*bx+screeny*by+screenz*bz; + c = screenx*cx+screeny*cy+screenz*cz; + } + boolean goingIn = ( (newcx > 0) == (c > 0) )?false:true; + int interpCounter = 0; + int deltaU = 0; int deltaV = 0; + float fu = 0; float fv = 0; + float oldfu = 0; float oldfv = 0; + + if (accurateMode&&goingIn){ + int rightOffset = (xend-xstart-1)%linearInterpLength; + int leftOffset = linearInterpLength-rightOffset; + float rightOffset2 = rightOffset / ((float)linearInterpLength); + float leftOffset2 = leftOffset / ((float)linearInterpLength); + interpCounter = leftOffset; + float ao = a-leftOffset2*newax; + float bo = b-leftOffset2*newbx; + float co = c-leftOffset2*newcx; + float oneoverc = 65536.0f/co; + oldfu = (ao*oneoverc); oldfv = (bo*oneoverc); + a += rightOffset2*newax; + b += rightOffset2*newbx; + c += rightOffset2*newcx; + oneoverc = 65536.0f/c; + fu = a*oneoverc; fv = b*oneoverc; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + iu = ( (int)oldfu )+(leftOffset-1)*deltaU; iv = ( (int)oldfv )+(leftOffset-1)*deltaV; //another "off-by-one" hack + } else{ + float preoneoverc = 65536.0f/c; + fu = (a*preoneoverc); + fv = (b*preoneoverc); + } for ( ;xstart < xend; xstart++ ) { + if(accurateMode){ + if (interpCounter == linearInterpLength) interpCounter = 0; + if (interpCounter == 0){ + a += newax; + b += newbx; + c += newcx; + float oneoverc = 65536.0f/c; + oldfu = fu; oldfv = fv; + fu = (a*oneoverc); fv = (b*oneoverc); + iu = (int)oldfu; iv = (int)oldfv; + deltaU = ((int)(fu - oldfu)) >> linearInterpPower; + deltaV = ((int)(fv - oldfv)) >> linearInterpPower; + } else{ + iu += deltaU; + iv += deltaV; + } + interpCounter++; + } + // get texture pixel color try { @@ -2461,7 +3741,7 @@ public class PTriangle implements PConstants // get texture pixels int pix0 = m_texture[ofs]; int pix1 = m_texture[ofs + 1]; - ofs+=TEX_WIDTH; + if (ofs < lastRowStart) ofs+=TEX_WIDTH; int pix2 = m_texture[ofs]; int pix3 = m_texture[ofs + 1]; @@ -2520,15 +3800,18 @@ public class PTriangle implements PConstants } // + xpixel++;//accurate mode + if (!accurateMode){ iu+=iuadd; iv+=ivadd; + } ir+=iradd; ig+=igadd; ib+=ibadd; ia+=iaadd; iz+=izadd; } - + ypixel++;//accurate mode ytop+=SCREEN_WIDTH; xleft+=leftadd; xrght+=rghtadd; diff --git a/core/todo.txt b/core/todo.txt index f68a5f26d..46ba4cdd5 100644 --- a/core/todo.txt +++ b/core/todo.txt @@ -91,6 +91,16 @@ X http://processing.org/discourse/yabb_beta/YaBB.cgi?board=Syntax;action=displ X significant improvement to text and images in opengl X now using mipmaps to interpolate large and small images +ewjordan stuff (changes checked in) +_ rect() changes size as it changes position +_ http://dev.processing.org/bugs/show_bug.cgi?id=95 +_ strange texture warping in P3D +_ http://dev.processing.org/bugs/show_bug.cgi?id=103 +_ lines skip on 200x200 surface because of fixed point rounding error +_ http://dev.processing.org/bugs/show_bug.cgi?id=267 +_ this may be same as 95 +_ Polygons parallel to z-axis not always filling with nonzero x or y +_ http://dev.processing.org/bugs/show_bug.cgi?id=547 _ PGraphics problem with fillColor _ http://dev.processing.org/bugs/show_bug.cgi?id=468 @@ -101,12 +111,6 @@ _ http://dev.processing.org/bugs/show_bug.cgi?id=560 _ replaceAll() not supported by 1.1 _ http://dev.processing.org/bugs/show_bug.cgi?id=561 -ewjordan stuff -_ Polygons parallel to z-axis not always filling with nonzero x or y -_ http://dev.processing.org/bugs/show_bug.cgi?id=547 -_ strange texture warping in P3D -_ http://dev.processing.org/bugs/show_bug.cgi?id=103 - _ loadBytes() doesn't do auto gunzip? but loadStrings and createReader do? _ this might be a good solution for dealing with the differences _ with loadBytes(), they can use gzipInput (or loadBytesGZ?) @@ -777,10 +781,6 @@ CORE / PGraphics3D _ images are losing pixels at the edges _ http://dev.processing.org/bugs/show_bug.cgi?id=102 -_ rect() changes size as it changes position -_ http://dev.processing.org/bugs/show_bug.cgi?id=95 -_ lines skip on 200x200 surface because of fixed point rounding error -_ http://dev.processing.org/bugs/show_bug.cgi?id=267 _ P3D not doing bilinear interpolation in text and images _ because smooth() has to be set (and smooth throws an error in P3D) _ how should this be handled? a hint? allowing smooth()?