package cgp.render; import cgp.basics.Shape3D; import cgp.basics.Face3D; import cgp.basics.Vertex3D; import cgp.basics.LightParams; import cgp.math.Point2f; import cgp.math.Point4f; import java.util.Enumeration; /** * Ein Renderer, der * - BFC durchfuehrt, * - die uebrigen Flaechen am Frustum clippt * - mit Texturen darstellt. * * Diese Version beruecksichtigt, dass es keinen Unterschied zwischen * Flat und CurvedFace bzw. -Vertex mehr gibt. * * Jetzt wird ein BFCuller benutzt. * * @author Olaf Mueller * @version V1.0 22.07.2002 */ public class TextureMapper implements Renderer { public static final float EPS = -0.001f; public WCClipper clipper; public BFCuller bfculler; public View view; public Lighting lights = null; public CGCanvas cgc = null; int [] colors; // Dreieck zum Shaden // DC-Koordinaten Point4f[] dc = {new Point4f(), new Point4f(), new Point4f()}; // Texturkoordinaten Point2f[] tc = {new Point2f(), new Point2f(), new Point2f()}; // Vertices mit allen Infos Vertex3D[] triangle = {new Vertex3D(null, 0, null, new Point2f()), new Vertex3D(null, 0, null, new Point2f()), new Vertex3D(null, 0, null, new Point2f())}; public TextureMapper() { clipper = new WCClipper(); bfculler = new BFCuller(); colors = new int[256]; for (int i=0; i<256; i++) { colors[i] = 255<<24; colors[i] |= i<<16; colors[i] |= i<<8; colors[i] |= i; } } /** * Zentrale Methode; loest das Rendern der Szene aus. Die uebergebene View * liefert alle Informationen, die der Renderer braucht, um die Szene * darzustellen. * Die Uebergabe der View ermoeglicht den Einsatz derselben * Renderer-Instanz in mehreren Views. */ public void renderScene(View view) { Scene scene = view.scene; lights = scene.lights; Camera cam = view.cam; Shape3D shape; // aktueller Koerper Face3D face; // aktuelle Flaeche view.cgc.clearBuffer(); // neues Frame vorbereiten view.cgc.clearZBuffer(); // neues Frame vorbereiten Enumeration e = scene.getVisibleShapes(cam); // Liste sichtbarer Koerper while(e.hasMoreElements()) { // Liste durchlaufen shape = (Shape3D)e.nextElement(); // naechsten Koerper holen if (shape == cam) continue; for(int j=0; j<shape.count; j++) { // Alle Flaechen face = shape.faces[j]; renderFace(face, view); // Flat or Curved } // Fuer alle Flaechen } // Fuer alle Koerper view.cgc.refresh(); // Frame auf den Schirm } public void renderFace(Face3D f, View view) { Camera cam = view.cam; cgc = view.cgc; LightParams lp = new LightParams(); Vertex3D[] knoten; // sichtbare Punkte int kc=0; // Anzahl sichtb. Punkte f = bfculler.bfc(f, cam); // Flaeche holen u. bfc if(f != null) { knoten = clipper.clip(f, cam); // Flaeche clippen if((knoten != null) && (knoten.length >2)){// Flaeche z.T. sichtbar? Vertex3D tmptri; kc = knoten.length; float tmpw; // gemaess der Anleitung von Hecker. Allerdings scheint es noch // ein paar Probleme zu geben... // Alle Informationen fuer den ersten Vertex uebertragen // Geometriekoordinaten projizieren view.WC2DC.multiply(knoten[0].wc, triangle[0].wc); tmpw = 1f/triangle[0].wc.w; // homogene Koordinate merken triangle[0].wc.homogenize(); // teile durch homogene Koord. triangle[0].wc.w = tmpw; // homogene Koordinate retten // Texturkoordinaten projizieren triangle[0].tc.u = knoten[0].tc.u*tmpw; triangle[0].tc.v = knoten[0].tc.v*tmpw; // Normalen retten triangle[0].wc_normal = knoten[0].wc_normal; // Flaeche setzen triangle[0].face = f; // Ab hier: zweiter Vertex view.WC2DC.multiply(knoten[1].wc, triangle[2].wc); tmpw = 1f/triangle[2].wc.w; // homogene Koordinate merken triangle[2].wc.homogenize(); // teile durch homogene Koord. triangle[2].wc.w = tmpw; // homogene Koordinate retten // Texturkoordinaten projizieren triangle[2].tc.u = knoten[1].tc.u*tmpw; triangle[2].tc.v = knoten[1].tc.v*tmpw; // Normale retten triangle[2].wc_normal = knoten[1].wc_normal; // Flaeche setzen triangle[2].face = f; for(int k=2; k<kc; k++) { // Fuer alle Punkte tmptri = triangle[1]; // Fuer ersten Punkt triangle[1] = triangle[2]; triangle[2] = tmptri; view.WC2DC.multiply(knoten[k].wc, triangle[2].wc); tmpw = 1f/triangle[2].wc.w; // homogene Koordinate merken triangle[2].wc.homogenize(); // teile durch homogene Koord. triangle[2].wc.w = tmpw; // homogene Koordinate retten triangle[2].tc.u = knoten[k].tc.u*tmpw; triangle[2].tc.v = knoten[k].tc.v*tmpw; // Normale retten triangle[2].wc_normal = knoten[k].wc_normal; // Flaeche setzen triangle[2].face = f; shade(triangle); } // fuer alle Punkte dieser Flaeche } // Flaeche nicht entartet und mind. z.T. sichtbar } // Flaeche nicht abgewandt } // Faerbt das Dreieck in p mit der Textur von f private void shade(Vertex3D[] triangle) { Vertex3D va = triangle[0]; Vertex3D vb = triangle[1]; Vertex3D vc = triangle[2]; Vertex3D vA, vB, vC; Point4f pa = triangle[0].wc; Point4f pb = triangle[1].wc; Point4f pc = triangle[2].wc; Point4f pA, pB, pC; // pA.y <= pB.y <= pC.y Point2f ta = triangle[0].tc; Point2f tb = triangle[1].tc; Point2f tc = triangle[2].tc; Point2f tA, tB, tC; float sxAB, sxAC, sxBC; // Steigungen in Dimension x float szAB, szAC, szBC; // Steigungen in Dimension z float suAB, suAC, suBC; // Steigungen in u-Dimension float svAB, svAC, svBC; // Steigungen in u-Dimension float swAB, swAC, swBC; // Steigungen in 1/w-Dimension float onedy; int y, yEnd; float yPreStep; if (va.wc.y <= vb.wc.y) { vA = va; vB = vb; } else { vA = vb; vB = va; } if (vc.wc.y >= vB.wc.y) { vC = vc; } else { vC = vB; if (vc.wc.y >= vA.wc.y) { vB = vc; } else { vB = vA; vA = vc; } } // Sortierung abgeschlossen //Jetzt folgt der Test auf negative z-Koordinaten if(((vA.wc.z <0) || (vB.wc.z<0)) || (vC.wc.z<0)) { System.out.println("shade:Negative z-Koordinate im Dreieck:"); System.out.println("vA="+vA+"; vB="+vB+"; vC="+vC); } // Jetzt kommt die Berechnung der Gradienten // Wir haben in dieser Implementation 5 Parameter: // x, z, w, u und v. // Diese liegen bereits in der Form // x/w, z/w, 1/w, u/w und v/w vor. Gradients gradAC = new Gradients(5); Gradients gradAB = new Gradients(5); Gradients gradBC = new Gradients(5); if (vA.wc.y != vC.wc.y) { // Kante AC nicht horizontal -> Steigungen berechnen onedy = 1f/(vC.wc.y - vA.wc.y); sxAC = (vC.wc.x - vA.wc.x) * onedy; szAC = (vC.wc.z - vA.wc.z) * onedy; swAC = (vC.wc.w - vA.wc.w) * onedy; suAC = (vC.tc.u - vA.tc.u) * onedy; svAC = (vC.tc.v - vA.tc.v) * onedy; } else { // Steigungen werden nicht benoetigt - ausser fuer den Compiler ;-) sxAC = 0f; szAC = 0f; swAC = 0f; suAC = 0f; svAC = 0f; } // Y=Scanline beginnt beim obertsen Punkt (A) y = ceil(vA.wc.y); yPreStep = y - vA.wc.y; float xAC = vA.wc.x + yPreStep*sxAC; float zAC = vA.wc.z + yPreStep*szAC; float wAC = vA.wc.w + yPreStep*swAC; float uAC = vA.tc.u + yPreStep*suAC; float vAC = vA.tc.v + yPreStep*svAC; yEnd = ceil(vB.wc.y); if (vA.wc.y == vB.wc.y) { // Kante AB horizontal } else { onedy = 1f/(vB.wc.y - vA.wc.y); sxAB = (vB.wc.x - vA.wc.x) * onedy; // Steigungen berechnen szAB = (vB.wc.z - vA.wc.z) * onedy; swAB = (vB.wc.w - vA.wc.w) * onedy; suAB = (vB.tc.u - vA.tc.u) * onedy; svAB = (vB.tc.v - vA.tc.v) * onedy; // laeuft von A nach B ... float xAB = vA.wc.x + yPreStep*sxAB; // in x/w-Dimension float zAB = vA.wc.z + yPreStep*szAB; // in z/w-Dimension float wAB = vA.wc.w + yPreStep*swAB; // in 1/w-Dimension float uAB = vA.tc.u + yPreStep*suAB; // in u/w-Dimension float vAB = vA.tc.v + yPreStep*svAB; // in v/w-Dimension for (; y < yEnd; y++) { putScanline(y, xAC, xAB, zAC, zAB, uAC, uAB, vAC, vAB, wAC, wAB, vA.face); // Schritt entlang Kante AC in x- und z-Richtung machen xAC += sxAC; zAC += szAC; // Schritt entlang Kante AC in u- und v-Richtung machen uAC += suAC; vAC += svAC; // Schritt entlang der 1/w Dimension wAC += swAC; // Schritt entlang Kante AB in x- und z-Richtung machen xAB += sxAB; zAB += szAB; // Schritt entlang Kante AB in u- und v-Richtung machen uAB += suAB; vAB += svAB; // Schritt entlang der Kante AB in 1/w-Richtung wAB += swAB; } } if (vB.wc.y == vC.wc.y) { // Kante BC horizontal } else { onedy = 1f/(vC.wc.y - vB.wc.y); sxBC = (vC.wc.x - vB.wc.x) * onedy; // Steigungen berechnen szBC = (vC.wc.z - vB.wc.z) * onedy; swBC = (vC.wc.w - vB.wc.w) * onedy; suBC = (vC.tc.u - vB.tc.u) * onedy; svBC = (vC.tc.v - vB.tc.v) * onedy; yPreStep = yEnd - vB.wc.y; yEnd = ceil(vC.wc.y); // Wir laufen von B nach C (xAC laeuft weiter von A nach C) float xBC = vB.wc.x + yPreStep*sxBC; float zBC = vB.wc.z + yPreStep*szBC; float wBC = vB.wc.w + yPreStep*swBC; float uBC = vB.tc.u + yPreStep*suBC; float vBC = vB.tc.v + yPreStep*svBC; for (; y < yEnd; y++) { putScanline(y, xBC, xAC, zBC, zAC, uBC, uAC, vBC, vAC, wBC, wAC, vA.face); // Schritt entlang Kante BC in x- und z-Richtung machen xBC += sxBC; zBC += szBC; // Schritt in u- und v-Richtung entlang der Kante BC uBC += suBC; vBC += svBC; // Schritt in Richtung der 1/w-Dimension wBC += swBC; // Schritt entlang Kante AC in x- und z-Richtung machen xAC += sxAC; zAC += szAC; // Schritt in u- und v-Richtung entlang der Kante AC uAC += suAC; vAC += svAC; // Schritt in Richtung der 1/w-Dimension wAC += swAC; } } } private void putScanline(int y, // Berechnet waagerechte Linie in Hoehe y float x1, float x2, // x-Komponenten float z1, float z2, // mit Z-Komponenten z1, z2 float u1, float u2, // u-Komponenten float v1, float v2, // v-Komponenten float w1, float w2, // 1/w Komponenten Face3D f) { // Flaeche mit Textur int width = f.tex.getUSize()-1; // Texturgroesse besorgen int height = f.tex.getVSize()-1; // Texturgroesse besorgen //int width = f.tex.getUSize(); // Texturgroesse besorgen //int height = f.tex.getVSize(); // Texturgroesse besorgen if (x1 == x2) { // Linie ist Punkt int x = (int)x1; cgc.setPixelWithZ(x, y, z1, f.tex.getTexel(f.uMode.getUInt(u1/w1,f.tex), f.vMode.getVInt(v1/w1,f.tex))); } else { float xl, xr; float zl, zr; float ul, ur; float vl, vr; float onewl, onewr; if (x1 < x2) { xl = x1; xr = x2; zl = z1; zr = z2; ul = u1; ur = u2; vl = v1; vr = v2; onewl = w1; onewr = w2; } else { xl = x2; xr = x1; zl = z2; zr = z1; ul = u2; ur = u1; vl = v2; vr = v1; onewl = w2; onewr = w1; } float onedx = 1f / (xr - xl); float sz = (zr - zl) * onedx; // Steigung berechnen float su = (ur - ul) * onedx; // Steigung berechnen float sv = (vr - vl) * onedx; // Steigung berechnen float sonew = (onewr - onewl) * onedx; // Steigung in 1/w-Richtung int x = ceil(xl); float xPreStep = x - xl; int xEnd = ceil(xr); float z = zl + sz*xPreStep; // x laeuft von Links nach Rechts float u = ul + su*xPreStep; float v = vl + sv*xPreStep; float onew = onewl + sonew*xPreStep; float w = 1/onew; // Aus 1/w wieder w machen float uNow = u*w; float vNow = v*w; for (; x < xEnd; x++) { cgc.setPixelWithZ(x, y, z, f.tex.getTexel(f.uMode.getUInt(uNow,f.tex), f.vMode.getVInt(vNow,f.tex))); z += sz; u += su; v += sv; onew += sonew; w = 1/onew; // Aus 1/w wieder w machen uNow = u*w; vNow = v*w; } } } /** * Liefert den naechstgroesseren Ganzahlwert bezogen auf x; ausser x ist * bereits eine Ganzzahl. */ private static int ceil(float x) { int ganz = (int)x; return x-(float)ganz == 0f ? ganz : x<0 ? ganz : ganz+1; } }