// (c) Janne V. Kujala #ifndef VOB_VOBS_IRREGU_HXX #define VOB_VOBS_IRREGU_HXX #ifndef VOB_DEFINED #define VOB_DEFINED(x) #endif #include #include #include namespace Vob { namespace Vobs { PREDBGVAR(dbg_irregularquad); const int IRREGU_SHIFTS = 0x0001; const int IRREGU_CS2_TO_SCREEN = 0x0002; /** * coords1: paper => window * coords2: tearaway => paper (assumed to be affine) * The rectangular paper from which the tearaway is torn away * is [x0,y0]..[x1,y1] in paper coordinates (as in PaperQuad) and * the tearaway part is [-1,1]x[-1,1] in tearaway coordinates. * border and ripple_period are relative to the ripple amplitude and period in * paper coordinates */ class IrregularQuad { public: enum { NTrans = 2 }; float x0, y0, x1, y1; float border; float freq; int flags; DisplayListID setup; float dicefactor; template void params(F &f) { float ripple_period; f(x0, y0, x1, y1, border, ripple_period, flags, setup, dicefactor); freq = 1.0 / ripple_period; } template inline void vert(const Coords& coords1, const Coords& coords2, ZPt q) const { ZPt p = coords2.transform(q); float xrelp = (p.x - x0)/ (x1-x0); float yrelp = (p.y - y0)/ (y1-y0); glMultiTexCoord2f(1, .25 + .5 * xrelp, // the center texels of 4x4 are colored .25 + .5 * yrelp ); coords1.vertex(p); DBG(dbg_irregularquad) << "Vert: " << q< &pts, ZPt v1) const { if (v1.x < x0) v1.x = x0; if (v1.x > x1) v1.x = x1; if (v1.y < y0) v1.y = y0; if (v1.y > y1) v1.y = y1; pts.push_back(v1); } // Do vert(pts, v) for any crossings and then vert(pts, v1) void vert(vector &pts, ZPt v0, ZPt v1) const { ZPt v; if ((v0.x - x0) * (v1.x - x0) < 0) v = lerp(v0, v1, (x0 - v0.x) / (v1.x - v0.x)), v.x = x0; else if ((v0.x - x1) * (v1.x - x1) < 0) v = lerp(v0, v1, (x1 - v0.x) / (v1.x - v0.x)), v.x = x1; else if ((v0.y - y0) * (v1.y - y0) < 0) v = lerp(v0, v1, (y0 - v0.y) / (v1.y - v0.y)), v.y = y0; else if ((v0.y - y1) * (v1.y - y1) < 0) v = lerp(v0, v1, (y1 - v0.y) / (v1.y - v0.y)), v.y = y1; else { return vert(pts, v1); } vert(pts, v0, v); vert(pts, v, v1); } // Draw a polygon as a trinagle fan from the centroid template void drawStarPoly(const Coords& coords1, vector& p) const { float A = 0, cx = 0, cy = 0; p.push_back(p[0]); for (unsigned i = 0; i < p.size() - 1; i++) { float t = p[i].x * p[i+1].y - p[i+1].x * p[i].y; A += t; cx += (p[i].x + p[i+1].x) * t; cy += (p[i].y + p[i+1].y) * t; } if (A != 0) { cx /= 3 * A; cy /= 3 * A; } else { cx = 0.5 * (x0 + x1); cy = 0.5 * (y0 + y1); } glBegin(GL_TRIANGLE_FAN); coords1.vertex(ZPt(cx, cy, p[0].z)); for (unsigned i = 0; i < p.size(); i++) coords1.vertex(p[i]); glEnd(); } template void render(const T &coords1, const T &coords2) const { DBG(dbg_irregularquad) << "Irregular quad\\n"; glCallList(setup); GLERR; // Normalize tearaway part unit vectors in paper coords to get border widths // Note: we assume that coords2 is an affine transform so that the border (ripple) width // is translation invariant const T &cs2inv = coords2.getInverse(); ZVec xvec = (coords2.transform(ZPt(1,0,0)) - coords2.transform(ZPt(0,0,0))).normalized(); ZVec yvec = (coords2.transform(ZPt(0,1,0)) - coords2.transform(ZPt(0,0,0))).normalized(); //cout << (coords2.transform(ZPt(1,0,0)) - coords2.transform(ZPt(0,0,0))) // << (coords2.transform(ZPt(0,1,0)) - coords2.transform(ZPt(0,0,0))) << "\\n"; float bx = border * (cs2inv.transform(ZPt(0,0,0) + xvec) - cs2inv.transform(ZPt(0,0,0))).length(); float by = border * (cs2inv.transform(ZPt(0,0,0) + yvec) - cs2inv.transform(ZPt(0,0,0))).length(); //cout << bx << "," << by << "\\n"; float x0 = 1; float x1 = 1 - bx; float x2 = 1 - 2 * bx; float mx0 = 0; float mx1 = 0 + bx; float mx2 = 0 + 2 * bx; float y0 = 1; float y1 = 1 - by; float y2 = 1 - 2 * by; float my0 = 0; float my1 = 0 + by; float my2 = 0 + 2 * by; float w = .5; // scale of last two vertices of sides float wtbl[] = { 1, 1, 1, 1, w, w, w, w }; ZPt ctr = coords2.transform(ZPt(0,0,0)); double r1 = (coords2.transform(ZPt(1,1,0)) - ctr).length(); double r2 = (coords2.transform(ZPt(1,0,0)) - ctr).length(); double r3 = (coords2.transform(ZPt(0,1,0)) - ctr).length(); double r4 = (coords2.transform(ZPt(-1,0,0)) - ctr).length(); double r5 = (coords2.transform(ZPt(0,-1,0)) - ctr).length(); double r = r1; if(r < r2) r = r2; if(r < r3) r = r3; if(r < r4) r = r4; if(r < r5) r = r5; double nonl = dicefactor * coords1.nonlinearity(ctr, r); int diceb = (int)fabs(2 * border * sqrt(2) * nonl) + 1; int dicex = (int)fabs(2 * border * (1 / bx - 2) * nonl) + 1; int dicey = (int)fabs(2 * border * (1 / by - 2) * nonl) + 1; if(diceb > 20) diceb = 20; if(dicex > 60) dicex = 60; if(dicey > 60) dicey = 60; // Dicing number for each of the primitives int dice[] = { dicey, dicey, dicex, dicex, diceb, diceb, diceb, diceb }; ZPt sides[][4] = { // The sides of the rectangle: { ZPt(mx0,my2,0), ZPt(mx0,+y2,0), ZPt(mx1,+y2,0), ZPt(mx1,my2,0) }, { ZPt(+x0,my2,0), ZPt(+x0,+y2,0), ZPt(+x1,+y2,0), ZPt(+x1,my2,0) }, { ZPt(mx2,my0,0), ZPt(+x2,my0,0), ZPt(+x2,my1,0), ZPt(mx2,my1,0) }, { ZPt(mx2,+y0,0), ZPt(+x2,+y0,0), ZPt(+x2,+y1,0), ZPt(mx2,+y1,0) }, // Corners: { ZPt(+x2,+y0,0), ZPt(+x0,+y2,0), ZPt(+x1,+y2,0), ZPt(+x2,+y1,0) }, { ZPt(+x2,my0,0), ZPt(+x0,my2,0), ZPt(+x1,my2,0), ZPt(+x2,my1,0) }, { ZPt(mx2,+y0,0), ZPt(mx0,+y2,0), ZPt(mx1,+y2,0), ZPt(mx2,+y1,0) }, { ZPt(mx2,my0,0), ZPt(mx0,my2,0), ZPt(mx1,my2,0), ZPt(mx2,my1,0) } }; /* The distance (as a fraction of border) from the square where the 1D texture slice is taken. * Smaller number lowers the frequency of the corner pieces */ float texf = 0.5; DBG(dbg_irregularquad) << "Dice: " << dicex << " " << dicey << " " << diceb << "\\n"; for (int pass = 0; pass < ((flags & IRREGU_SHIFTS) ? 4 : 1); pass++) { if (flags & IRREGU_SHIFTS) { glPushMatrix(); glTranslatef((pass & 1) ? -2 : 2, (pass & 2) ? -2 : 2, 0); } glEnable(GL_REGISTER_COMBINERS_NV); glEnable(GL_TEXTURE_2D); for (unsigned i = 0; i < sizeof(sides)/sizeof(sides[0]); i++) { ZVec tex0 = freq * (coords2.transform(lerp(sides[i][3], sides[i][0], texf)) - ZPt(0,0,0)); ZVec tex1 = freq * (coords2.transform(lerp(sides[i][2], sides[i][1], texf)) - ZPt(0,0,0)); float w = wtbl[i]; glBegin(GL_QUAD_STRIP); for (int d = 0; d <= dice[i]; d++) { float t = (float)d / dice[i]; ZVec tex = lerp(tex0, tex1, t); ZPt pt0 = lerp(sides[i][0], sides[i][1], t); ZPt pt1 = lerp(sides[i][3], sides[i][2], t); glSecondaryColor3fEXT(0, 0, 0); glTexCoord2f(tex.x, tex.y); vert(coords1, coords2, pt0); glSecondaryColor3fEXT(1, 1, 1); glTexCoord4f(tex.x * w, tex.y * w, 0, w); vert(coords1, coords2, pt1); } glEnd(); GLERR; } glDisable(GL_REGISTER_COMBINERS_NV); glDisable(GL_TEXTURE_2D); ZPt poly[] = { ZPt(+x1,+y2,0), ZPt(+x2,+y1,0), ZPt(mx2,+y1,0), ZPt(mx1,+y2,0), ZPt(mx1,my2,0), ZPt(mx2,my1,0), ZPt(+x2,my1,0), ZPt(+x1,my2,0), ZPt(+x1,+y2,0) }; int dice[] = { diceb, dicex, diceb, dicey, diceb, dicex, diceb, dicey }; ZPt poly2[9]; for (int i = 0; i < 9; i++) poly2[i] = coords2.transform(poly[i]); // Disable the effect of TEXTURE1 as the clipping is done explicitly glMultiTexCoord2f(1, .5, .5); static vector pts; pts.clear(); pts.reserve(int(1.5 * (4*diceb + 2*dicex + 2*dicey))); ZPt prev = poly2[0]; for (int i = 0; i < 8; i++) { for (int d = 0; d < dice[i]; d++) { float t = (float)d / dice[i]; ZPt v = lerp(poly2[i], poly2[i+1], t); vert(pts, prev, v); prev = v; } } drawStarPoly(coords1, pts); if (flags & IRREGU_SHIFTS) glPopMatrix(); } glPopAttrib(); DBG(dbg_irregularquad) << "IrregularQuad done\\n"; } }; VOB_DEFINED(IrregularQuad); } } #endif