#include #include #include #include #include #include using namespace std; #include "Heartbeat.h" #include "txs_globals.h" #include "txs_algo.h" NormRGB randomRgbFrom(TiffImage &img) { int numPix = img.getWidth() * img.getHeight(); int i = rand_range(0, numPix); return img.getRGB(i); } double random_pos_unit() { double absval = ((double)rand()) / ((double) RAND_MAX); return absval; } NormRGB randomRgb() { return NormRGB(random_pos_unit(), random_pos_unit(), random_pos_unit()); } /* * generates a random number in [a, b) */ int rand_range(int a, int b) { int range = b - a - 1; double norm = (double)rand() / (double)RAND_MAX; int offset = (int)floor(norm * range); return a + offset; } template void knuth_shuffle(vector &items) { int rand; for(int i = items.size() - 1; i > 0; i--) { rand = rand_range(0, i); T obj = items[i]; items[i] = items[rand]; items[rand] = obj; } } int accumAllFinestPixLocs(vector &out, TxsMesh &mesh) { Heartbeat hb; list faces = mesh.getFaces(); int num = 0; int faceI = 0; list::iterator fit; for(fit = faces.begin(); fit != faces.end(); ++fit) { TxsFace* f = *fit; num += f->accumFinestPixelLocns(out); if(hb.OK_to_beat()) { cout << "accum'ing pixel locns " << (double)faceI/(double)faces.size()*100 << "% done" << endl; } faceI++; } return num; } Point pointForUV(const Point &uvo, const Vector &uvUp, const Vector &uvRight, const UVs &uv) { return Point( uvo(0) + uv.u * uvRight(0) + uv.v * uvUp(0), uvo(1) + uv.u * uvRight(1) + uv.v * uvUp(1), uvo(2) + uv.u * uvRight(2) + uv.v * uvUp(2)); } bool getHitRgb(list &faces, const Point &s, const Vector &d, int level, NormRGB &rgbOut) { //__DBa("begin GHR for point " << s); bool hit = false; list::iterator fit; for(fit = faces.begin(); fit != faces.end(); ++fit) { TxsFace* f = *fit; //__DBa("trying against face " << f); if( f->getRgbForRay(s, d, level, rgbOut)) { // we got one! hit = true; break; } } return hit; } // uhoh..a global variable!! int g_numNhoodRaysMissed = 0; /* * this fills the given image by shooting rays * derived from the given nhood patch * into the given faces. * it will use colors from the given faces at the given level. */ void fillNhoodImage(TiffImage &img, NhoodPatch_s nhood, int level, list &faces) { int px, py; for(px = 0; px < img.getWidth(); px++) { for(py = 0; py < img.getHeight(); py++) { Point hoodPt = nhood.getSquareCenter(px, py); NormRGB hitRgb; bool hit = getHitRgb(faces, hoodPt, nhood.normal, level, hitRgb); if(!hit) { g_numNhoodRaysMissed++; } img.setRGB(px, py, hitRgb); } } } // quick helper NormRGB averageColors(list &clrs) { NormRGB total(0,0,0); list::iterator clr_iter; for(clr_iter = clrs.begin(); clr_iter != clrs.end(); ++clr_iter) { NormRGB clr = *clr_iter; total = total.plus(clr); } TXS_REAL n = (TXS_REAL)clrs.size(); return NormRGB( total.r / n, total.g / n, total.b / n); } /* * Same as fillNhoodImage, except for each pixel, we do super sampling. * instead of just shooting a ray from the square's center, shoot rays from * the corners as well. then, take average of all 5 colors */ void fillNhoodImageSuperSampled(TiffImage &img, NhoodPatch_s nhood, int level, list &faces) { int px, py; for(px = 0; px < img.getWidth(); px++) { for(py = 0; py < img.getHeight(); py++) { // collect clrs from corners and center list clrs; Point center = nhood.getSquareCenter(px, py); NormRGB hitRgb; bool hit = getHitRgb(faces, center, nhood.normal, level, hitRgb); if(!hit) { g_numNhoodRaysMissed++; } else { clrs.push_back(hitRgb); } // corners for(int cx = 0; cx < 2; cx++) for(int cy = 0; cy < 2; cy++) { Point corner = nhood.getSquareCorner(px, py, cx, cy); if(getHitRgb(faces, corner, nhood.normal, level, hitRgb)) { clrs.push_back(hitRgb); } } NormRGB avgClr = averageColors(clrs); img.setRGB(px, py, avgClr); } } } void synthPixelValue(pixelLocn &p, TiffImage &img) { //__DBa("begin SPC for PL " << &p); // all the faces that our nhood patch could possibly need // ie. the incident face, and its immediate nbors TxsFace* face = p.face; // note that this is a reference list &faces = face->getImmediateNbors(); // set up the neighborhood patch NhoodPatch_s nhood; nhood.setTo(p, UNITS_PER_PIXEL, NHOOD_PIXELS); bool someNoHits = false; TiffImage nhoodImg(NHOOD_PIXELS, NHOOD_PIXELS); // fill at the finest level fillNhoodImage(nhoodImg, nhood, face->getPyramid()->finestLevel(), faces); // TEMP DEBUG /* if(someNoHits) { char filename[64]; sprintf(filename, "nhood_debug/%d.tif", rand()); nhoodImg.writeToTiff(filename); } */ // now we have nhood // find best match in sample img // tell it to ignore the middle pixel const int midOfs = (NHOOD_PIXELS - 1) / 2; int mx = -1; int my = -1; img.getBestMatchingRegion(nhoodImg, mx, my, midOfs); // set p's color using the middle pixel //__LOG__("midOfs is " << midOfs << " BMR is " << mx << " " << my); NormRGB bestMatch = img.getRGB(mx + midOfs, my + midOfs); p.setRGB(bestMatch); // TEMP DEBUG // using the phong-shaded normal to determine color.. // do simple lighting /* Vector sunDir(0, 0, -1); double light = max(0.0, -1.0 * sunDir.Dot(gridNorm)); p.setRGB(NormRGB(light, light, light)); */ } /* * the algo without pyramids */ void synthTexturesNaive(TxsMesh &mesh, TiffImage &img) { // front matter.. unsigned int seed = (unsigned int)time(NULL); srand(seed); __LOG__("Rand seed: " << seed); // get all pixel locations and shuffle them vector pixLocs; int numPixLocs = accumAllFinestPixLocs(pixLocs, mesh); __LOG__("Accum'd " << numPixLocs << " pixels. Shuffling.."); knuth_shuffle(pixLocs); __LOG__("shuffled."); // go thru each pixel loc and set it to a // random color from the img vector::iterator pit; for(pit = pixLocs.begin(); pit != pixLocs.end(); ++pit) { pixelLocn loc = *pit; //loc.setRGB(randomRgbFrom(img)); loc.setRGB(randomRgb()); } cout << "Begin synthesis.." << endl; double startTime = clock(); // now do the synth process for each pixel Heartbeat hb(3); int counter = 0; for(pit = pixLocs.begin(); pit != pixLocs.end(); ++pit) { pixelLocn loc = *pit; synthPixelValue(loc, img); if(hb.OK_to_beat()) { cout << "synthesized " << counter << " of " << numPixLocs << " pixels. " << (double)counter/(double)numPixLocs*100 << "% done." << endl; cout << " elapsed seconds: " << (clock() - startTime)/1000 << endl; } counter++; } //----------- // TEMP DEBUG // do it again! __STAT__("doing second pass"); knuth_shuffle(pixLocs); counter = 0; for(pit = pixLocs.begin(); pit != pixLocs.end(); ++pit) { pixelLocn loc = *pit; synthPixelValue(loc, img); if(hb.OK_to_beat()) { cout << "synthesized " << counter << " of " << numPixLocs << " pixels. " << (double)counter/(double)numPixLocs*100 << "% done." << endl; cout << " elapsed seconds: " << (clock() - startTime)/1000 << endl; } counter++; } cout << "DONE synthesis.." << endl; double endTime = clock(); __LOG__("total run time: " << (endTime - startTime)/1000.0 << " seconds for " << numPixLocs << " pixel locations."); __LOG__("number of nhood rays that didn't hit anything: " << g_numNhoodRaysMissed); }