From 621674d6e9fb0024645cd0020c6afb51e4b4a7e9 Mon Sep 17 00:00:00 2001 From: Tim Redfern Date: Tue, 21 Aug 2012 15:44:11 +0100 Subject: had enough --- gaunt01/src/main.cpp | 16 + gaunt01/src/testApp.cpp | 1263 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1279 insertions(+) create mode 100644 gaunt01/src/main.cpp create mode 100644 gaunt01/src/testApp.cpp diff --git a/gaunt01/src/main.cpp b/gaunt01/src/main.cpp new file mode 100644 index 0000000..1f3ac12 --- /dev/null +++ b/gaunt01/src/main.cpp @@ -0,0 +1,16 @@ +#include "ofMain.h" +#include "testApp.h" +#include "ofAppGlutWindow.h" + +//======================================================================== +int main( ){ + + ofAppGlutWindow window; + ofSetupOpenGL(&window, 1024,768, OF_FULLSCREEN ); // <-------- setup the GL context + printf("%ix%i on screen %ix%i\n",ofGetWidth(),ofGetHeight(),ofGetScreenWidth(),ofGetScreenHeight()); + // this kicks off the running of my app + // can be OF_WINDOW or OF_FULLSCREEN + // pass in width and height too: + ofRunApp( new testApp()); + +} diff --git a/gaunt01/src/testApp.cpp b/gaunt01/src/testApp.cpp new file mode 100644 index 0000000..beefce0 --- /dev/null +++ b/gaunt01/src/testApp.cpp @@ -0,0 +1,1263 @@ +#include "testApp.h" + +//-------------------------------------------------------------- +//units ~ 10cm +// +/* +Can use a floating point image or array to accumulate the screen and generate averaged background? + +Is this too much work for every frame? Should it be put in a seperate thread? + +*/ + +void testApp::setup(){ + + printf("setup: %ix%i on screen %ix%i\n",ofGetWidth(),ofGetHeight(),ofGetScreenWidth(),ofGetScreenHeight()); + + int windowMode = ofGetWindowMode(); + if(windowMode == OF_FULLSCREEN){ + this->windowWidth = ofGetScreenWidth(); + this->windowHeight = ofGetScreenHeight(); + } + else if(windowMode == OF_WINDOW){ + this->windowWidth = ofGetWidth(); + this->windowHeight = ofGetHeight(); + } + + mirror=true; + + bLearnBakground = true; + cam_angle=0; + threshold = 80; + + loadSettings("settings.xml"); + + vidGrabber.setVerbose(true); + if (vidGrabber.initGrabber(640,480)) { + hasCamera=true; + useCamera=true; + } + else + { + hasCamera=false; + useCamera=false; + vidPlayer.loadMovie(testmovie); //footage/ camera needs to be the same res as opencv planes and output + vidPlayer.setLoopState(OF_LOOP_NORMAL); + vidPlayer.play(); + } + + /* + accumImg.allocate(640,480); + + bgImg.allocate(640,480); + bgImg.setUseTexture(true); + + + + grayImage.allocate(640,480); + grayBg.allocate(640,480); + */ + + colorImg.allocate(640,480); + colorImg.setUseTexture(true); + currentFrame.allocate(CAM_WIDTH_FG, CAM_HEIGHT_FG); + background.allocate(CAM_WIDTH_FG, CAM_HEIGHT_FG); + background.setUseTexture(true); + + grayFrame.allocate(CAM_WIDTH_FG, CAM_HEIGHT_FG); + grayBg.allocate(CAM_WIDTH_FG, CAM_HEIGHT_FG); + grayDiff.allocate(CAM_WIDTH_FG, CAM_HEIGHT_FG); + + mogoutput.allocate(CAM_WIDTH_FG, CAM_HEIGHT_FG); + + learningRate = 0.01f; + bFirstFrame=true; + + diffchannel=chan_V; + hsvback = cvCreateImage(cvGetSize(currentFrame.getCvImage()), currentFrame.getCvImage()->depth, currentFrame.getCvImage()->nChannels); + //backchan = cvCreateImage(cvGetSize(currentFrame.getCvImage()), 8, 1); + + removeShadows=false; + shadowThreshold=10; + //////////////////////////// + + blobsManager.normalizePercentage = 0.7; + blobsManager.giveLowestPossibleIDs = false; + blobsManager.maxUndetectedTime = 500; + blobsManager.minDetectedTime = 500; + blobsManager.debugDrawCandidates = true; + + ofVec3f centre=ofVec3f(windowWidth/2,0,0); + ofVec3f normal=ofVec3f(0,0,-1); + ray=ofRay(); + plane=ofPlane(centre,normal); + plane.color=ofColor(255,255,255); + + projector=ofProjector(2.0f, ofVec2f(0.0f, 0.0f),windowWidth,windowHeight); //1.535f + projector.setPosition(windowWidth/2,windowHeight/2,-windowWidth); + projector.lookAt(ofVec3f(windowWidth/2,windowHeight/2,0),ofVec3f(0, -1, 0)); + + cam=ofCamera(); + cam.setPosition(windowWidth/2,windowHeight/2,-windowWidth); + cam.lookAt(ofVec3f(windowWidth/2,windowHeight/2,0),ofVec3f(0, -1, 0)); + cam.setFov(41.1); //39.85); //53.13); + cam.cacheMatrices(); //stop error messages - changed API? + + testpts=new ofVec3f[4]; + + bounds=new ofPlane[4]; + + //trapDoor=trapdoor(screen2plane(ofVec2f(windowWidth,0)),screen2plane(ofVec2f(windowWidth,windowHeight)),35); + trapdoorSize=35; + trapdoorSlotSize=50; + + + trapdoorTime=10.0; //time per trapdoor; + + + + mode=PLAY; + + drawStats=false; + + bgnum=1000; + firstframe=true; + + light.setPosition(windowWidth,0,windowHeight); + light.enable(); + + drawingborder=false; + + billboards=new ofImage[4]; + billboards[0].loadImage("GUI_title.png"); + billboards[1].loadImage("GUI_objective.png"); + billboards[2].loadImage("GUI_gotya.png"); + billboards[3].loadImage("GUI_instructions.png"); + + for (int i=0;i<4;i++) { + billboards[i].setAnchorPercent(0.5,0.5); + } + scaleFactor=ofVec2f(windowWidth/1280.0f,windowHeight/768.0f); + + gameState=TITLES; //PLAYING; //TITLES; // + + segTimes[TITLES]=4.0; + segTimes[EXPLAIN]=5.0; + segTimes[PLAYING]=60.0; + segTimes[GOTCHA]=4.0; + + gameStart=ofGetElapsedTimef(); + + sounds=new ofSoundPlayer[1]; + sounds[0].loadSound("arp5.mp3"); //game start + + + doorsounds=new ofSoundPlayer[4]; + doorsounds[0].loadSound("creeky door short1.wav"); + doorsounds[1].loadSound("creeky door short2.wav"); + doorsounds[2].loadSound("creeky door short3.wav"); + doorsounds[3].loadSound("voice falling down hole.wav"); + + cam.begin(); + + + //mog=cv::BackgroundSubtractorMOG(100,10,.1,.1); + + + cam.end(); + +} + +ofVec2f testApp::screen2plane(ofVec2f screenpos){ + ofVec3f p; + ray=projector.castPixel(screenpos.x,screenpos.y); + bool hit = plane.intersect(ray,p); + return ofVec2f(p.x,pow(pow(p.y,2)+pow(p.z,2),0.5f)); +} +ofVec3f testApp::plane2world(ofVec2f planepos){ + return ofVec3f(planepos.x,planepos.y,0); +} +bool testApp::rectsCross(ofRectangle rect1,ofRectangle rect2) { + bool overlap=true; //must overlap in x and y + if (rect1.x corners=trapdoors[num].getCorners(); + ofPolyline pol2; + for (int i=0;i corners=trapdoors[doornumber].getCorners(); + ofVec2f screenCorners[4]; + + for (int i=0;i trapdoors; +// float trapdoorSize; +// float trapdoorSlotSize; +// int numtrapdoorSlots; + +//create all trapdoors at once, deactivated. +//shuffle them +//at each timeout, pick the next door to activate (check if within bounds) and rebuild ground with holes +//on update, check all active doors against all players +//when a falling in sequence is over, start again + + trapdoors.clear(); + + l=ofVec2f(windowWidth/2,19*windowHeight/20); + r=projector.castPixel(l.x,l.y); + plane.intersect(r,p); + float closestY=p.rotated(cam_angle,ofVec3f(-1,0,0)).y; + numtrapdoorSlots=closestY/trapdoorSlotSize; + + //get middle position in the slot on the ground visible at front of screen + ofVec3f rp=ofVec3f(windowWidth/2,closestY-(0.5*trapdoorSlotSize),0); + //translate to the screen + ofVec3f sp=cam.worldToScreen(rp.rotated(cam_angle,ofVec3f(1,0,0))); + + //printf("front slot: %f,%f to %f,%f,%f\n",rp.x,rp.y,sp.x,sp.y,sp.z); + + //get point at left of this line + //project back on ground + ofVec2f gb=screen2plane(ofVec2f(windowWidth/8,sp.y)); + r=projector.castPixel(sp.x,sp.y); + plane.intersect(r,p); + float range=(((windowWidth/2)-p.x)*2); + + for (int i=0;i corners=trapdoors[0].getCorners(); + ofVec2f screenCorners[4]; + + for (int i=0;iwidth,tmp->height,tmp->nChannels,tmp->depth); + + + //get correct channel into backchan + + + + vector chans; + + //to remove shadows, need hsv of foreground and background + if (diffchannel>chan_B||removeShadows) cvtColor(outmat, hsvback, CV_BGR2HSV); + switch (diffchannel) { + case chan_R: + split(outmat,chans); + chans[0].copyTo(backchan); + break; + case chan_G: + split(outmat,chans); + chans[1].copyTo(backchan); + break; + case chan_B: + split(outmat,chans); + chans[2].copyTo(backchan); + break; + case chan_H: + split(hsvback,chans); + chans[0].copyTo(backchan); + break; + case chan_S: + split(hsvback,chans); + chans[1].copyTo(backchan); + break; + case chan_V: + split(hsvback,chans); + chans[2].copyTo(backchan); + break; + } + + + tmp = new IplImage(backchan); + grayBg = tmp; + + //} + //first, optionally remove shadows from FG + //possibly use 1/4 screen res? + + //to remove shadows, need hsv of foreground and background + if (diffchannel>chan_B||removeShadows) cvtColor(img, hsvfront, CV_BGR2HSV); + + cv::Mat outimg; + + if (removeShadows) { + vector slicesFront, slicesBack; + cv::Mat valFront, valBack, satFront, satBack; + + // split image to H,S and V images + split(hsvfront, slicesFront); + split(hsvback, slicesBack); + + slicesFront[2].copyTo(valFront); // get the value channel + slicesFront[1].copyTo(satFront); // get the sat channel + + slicesBack[2].copyTo(valBack); // get the value channel + slicesBack[1].copyTo(satBack); // get the sat channel + + int x,y; + for(x=0; x(y,x)[0] > satBack.at(y,x)[0]-shadowThreshold) && + (satFront.at(y,x)[0] < satBack.at(y,x)[0]+shadowThreshold)); + + if(sat && (valFront.at(y,x)[0] < valBack.at(y,x)[0])) { + hsvfront.at(y,x)[0]= hsvback.at(y,x)[0]; + hsvfront.at(y,x)[1]= hsvback.at(y,x)[1]; + hsvfront.at(y,x)[2]= hsvback.at(y,x)[2]; + } + + } + } + + //convert back into RGB if necessary + + if (diffchannel chans; + split(outimg,chans); + + switch (diffchannel) { + case chan_R: + chans[0].copyTo(frontchan); + break; + case chan_G: + chans[1].copyTo(frontchan); + break; + case chan_B: + chans[2].copyTo(frontchan); + break; + case chan_H: + chans[0].copyTo(frontchan); + break; + case chan_S: + chans[1].copyTo(frontchan); + break; + case chan_V: + chans[2].copyTo(frontchan); + break; + } + + //IplImage* tmp = new IplImage(outmat); + tmp = new IplImage(frontchan); + grayFrame = tmp; + + grayDiff.clear(); + grayDiff.allocate(640,480); + + // take the abs value of the difference between background and incoming and then threshold: + grayDiff.absDiff(grayBg, grayFrame); + grayDiff.threshold(threshold); + //grayDiff.adaptiveThreshold( threshold,20,true,false); //int blockSize, int offset=0,bool invert=false, bool gauss=false); + //grayDiff.erode_3x3(); + //grayDiff.resize(windowWidth,windowHeight); + + /* + + //MOG + + mog(img, outmat, mogf); + + + + + + // Complement the image + //cv::threshold(outmat, output, threshold, 255, cv::THRESH_BINARY_INV); + IplImage* tmp1 = new IplImage(outmat); + //printf("tmp: %ix%i channels: %i depth:%i\n",tmp->width,tmp->height,tmp->nChannels,tmp->depth); + //printf("grayDiff: %ix%i channels: %i depth:%i\n",grayDiff.getCvImage()->width,grayDiff.getCvImage()->height,grayDiff.getCvImage()->nChannels,grayDiff.getCvImage()->depth); + grayDiff=tmp1; //copy to ofx + + */ + + + + if (mode==CHECKACCUM) colorImg=grayDiff; + + grayDiff.resize(windowWidth,windowHeight); //wasteful?? + + + + contourFinder.findContours(grayDiff, 500, (640*480)/3, 20,false); // find holes + + blobsManager.update(contourFinder.blobs); + //check players against blob ids - bland do ray casting, update players + //ids are always in order + //players can be a map vs ID + //check if a key exists in a map - map::count + //do we purge them or just stop drawing them? is it a problem inheriting tesselator re memory? + //if we keep them we can have a 'loserboard' + set ids; + for(int i=0;i::iterator it; + for (it=players.begin();it!=players.end();it++) { + if (ids.find(it->first)==ids.end()||(border.size()>3&&OutsidePolygon(border,ofPoint(it->second.getWorldPosition().x,it->second.getWorldPosition().y)))) it->second.active=false; + else it->second.active=true; + } + } + for (int i=0;iEXPLAIN) Bird.update(players,cam_angle); + +} + +//-------------------------------------------------------------- +void testApp::draw(){ + + glDisable(GL_LIGHTING); + ofBackground(0,0,0); + cam.begin(); + glDisable(GL_DEPTH_TEST); + ofSetHexColor(0xffffff); + glDisable(GL_BLEND); + + if (gameStatetrapdoorTime) { + if (trapdoors.size()>trapdoorCounter+1) { + trapdoorCounter++; + trapdoorTimer=ofGetElapsedTimef(); + tessGround(trapdoorCounter); + } + //else updatePlane(); + } + + ofSetHexColor(0xffffff); + ofPushMatrix(); + ofRotate(cam_angle,1,0,0); + //trapDoor.draw(); + for (int i=0;i<=trapdoorCounter;i++) { + trapdoors[i].draw(); + } + ofPopMatrix(); + +//should be in front with holes being recreated for activated trapdoors + ofSetHexColor(0xffffff); + if (mode==CALIBRATE) bindTexture(colorImg); + else bindTexture(background); + ground.draw(); + if (mode==CALIBRATE) unbindTexture(colorImg); + else unbindTexture(background); + + ofPushMatrix(); + ofRotate(cam_angle,1,0,0); + Bird.drawShadow(); + ofPopMatrix(); + + glDisable(GL_DEPTH_TEST); + ofSetHexColor(0xffffff); + ofPushMatrix(); + ofRotate(cam_angle,1,0,0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + for (int i=0;i<=trapdoorCounter;i++) { + trapdoors[i].drawSplash(cam_angle); + + } + + glDisable(GL_BLEND); + ofPopMatrix(); + + if (mode!=CHECKACCUM) { + glDisable(GL_DEPTH_TEST); + ofSetHexColor(0xffffff); + bindTexture(colorImg); //colorImg.getTextureReference().bind(); + map::iterator it; + for(int i=0;isegTimes[gameState]) { + gameState++; + gameStart=ofGetElapsedTimef(); + gameTime=0.0f; + updatePlane(); + } + break; + case PLAYING: + if (gameTime>segTimes[gameState]) { + gameState=TITLES; + sounds[0].play(); + gameStart=ofGetElapsedTimef(); + gameTime=0.0f; + trapdoorTimer=ofGetElapsedTimef(); + } + break; + case GOTCHA: + if (gameTime>segTimes[gameState]) { + gameState=PLAYING; + gameStart=ofGetElapsedTimef(); + gameTime=0.0f; + updatePlane(); //for new trapdoors + } + break; + } + + float segElapsed=pow(gameTime/segTimes[gameState],2); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + switch(gameState) { + case TITLES: + billboards[0].draw(windowWidth/6 + ,scaleFactor.y*((-billboards[0].height/2)+(billboards[0].height*pow(sin(segElapsed*PI),0.4))) + ,billboards[0].width*scaleFactor.x + ,billboards[0].height*scaleFactor.y); + break; + /* + case CREDIT: + billboards[1].draw(windowWidth/6 + ,windowHeight+(scaleFactor.y*((billboards[1].height/2)-(billboards[1].height*pow(sin(segElapsed*PI),0.4)))) + ,billboards[1].width*scaleFactor.x + ,billboards[1].height*scaleFactor.y); + break; + */ + case EXPLAIN: + billboards[1].draw(windowWidth/2 + ,scaleFactor.y*((-billboards[1].height/2)+(billboards[1].height*pow(sin(segElapsed*PI),0.4))) + ,billboards[1].width*scaleFactor.x + ,billboards[1].height*scaleFactor.y); + break; + case PLAYING: + break; + case GOTCHA: + billboards[2].draw(windowWidth/2 + ,scaleFactor.y*((-billboards[2].height/2)+(billboards[2].height*pow(sin(segElapsed*PI),0.4))) + ,billboards[2].width*scaleFactor.x + ,billboards[2].height*scaleFactor.y); + break; + } + glDisable(GL_BLEND); + + + switch(mode) { + case PLAY: + + + + break; + case CALIBRATE: + case CHECKACCUM: + + ofSetHexColor(0xffffff); + ofPushMatrix(); + ofRotate(cam_angle,1,0,0); + for (float i=0;i<=windowWidth;i+=windowWidth/10) { + glBegin(GL_LINES); + glVertex3f(i,0,0); + glVertex3f(i,windowWidth,0); + glEnd(); + glBegin(GL_LINES); + glVertex3f(0,i,0); + glVertex3f(windowWidth,i,0); + glEnd(); + } + + ofVec2f pp=screen2plane(pos); + //ofSphere(pp.x,pp.y,0,5); + ofPopMatrix(); + + ofPushMatrix(); + ofRotate(cam_angle,1,0,0); + for (int i=0;i1) { + ofSetHexColor(0x00ff00); + ofPushMatrix(); + ofRotate(cam_angle,1,0,0); + for (int i=0;i 255) threshold = 255; + break; + case '-': + threshold --; + if (threshold < 0) threshold = 0; + break; + case 'a': + cam_angle+=1; + updatePlane(); + break; + case 'z': + case 'Z': + cam_angle-=1; + updatePlane(); + break; + case 'q': + case 'Q': + drawStats=!drawStats; + break; + case 'm': + case 'M': + mirror=!mirror; + break; + case 's': + case 'S': + saveSettings("settings.xml"); + break; + case 'i': + case 'I': + drawInstructions=!drawInstructions; + break; + case '9': + mode=PLAY; + break; + case '0': + mode=CALIBRATE; + break; + case '8': + mode=CHECKACCUM; + break; + + case '1': + diffchannel = chan_R; + break; + case '2': + diffchannel = chan_G; + break; + case '3': + diffchannel = chan_B; + break; + case '4': + diffchannel = chan_H; + break; + case '5': + diffchannel = chan_S; + break; + case '6': + diffchannel = chan_V; + break; + + + case 'r': + case 'R': + removeShadows=!removeShadows; + printf(removeShadows?"removing shadows\n":"not removing shadows\n"); + break; + +/* + case '1': + if (Bird.currentseq!="hover") { + //mesh.sequences["trans_flaphover"].stopAt(0.3); + //mesh.sequences["trans_flaphover"].start(); + Bird.model.sequences[Bird.currentseq].fadeout(0.5); + Bird.model.sequences["hover"].fadein(0.5); + Bird.currentseq="hover"; + } + break; + case '2': + if (Bird.currentseq!="flap") { + //mesh.sequences["trans_hoverflap"].stopAt(0.3); + //mesh.sequences["trans_hoverflap"].start(); + Bird.model.sequences[Bird.currentseq].fadeout(0.5); + Bird.model.sequences["flap"].fadein(0.5); + Bird.currentseq="flap"; + } + break; + case '3': + if (Bird.currentseq!="swoop") { + //mesh.sequences["trans_hoverflap"].stopAt(0.3); + //mesh.sequences["trans_hoverflap"].start(); + Bird.model.sequences[Bird.currentseq].fadeout(0.25); + Bird.model.sequences["swoop_trans"].fadein(0.25); + Bird.model.sequences["swoop_trans"].stopTime=ofGetElapsedTimef()+1.0; + Bird.model.sequences["swoop"].startAt(1.0); + Bird.currentseq="swoop"; + } + break; + case '4': + if (Bird.currentseq!="attack") { + //mesh.sequences["trans_hoverflap"].stopAt(0.3); + //mesh.sequences["trans_hoverflap"].start(); + Bird.model.sequences[Bird.currentseq].fadeout(0.2); + Bird.model.sequences["attack_trans"].fadein(0.2); + Bird.model.sequences["attack_trans"].stopTime=ofGetElapsedTimef()+0.6; + Bird.model.sequences["attack"].startAt(0.6); + Bird.currentseq="attack"; + } + break; + */ + /* + case 'y': + light.setPosition(light.getX(),light.getY()-100,light.getZ()); + printf("light at %f,%f,%f\n",light.getX(),light.getY(),light.getZ()); + break; + case 'n': + light.setPosition(light.getX(),light.getY()+100,light.getZ()); + printf("light at %f,%f,%f\n",light.getX(),light.getY(),light.getZ()); + break; + case 'g': + light.setPosition(light.getX()-100,light.getY(),light.getZ()); + printf("light at %f,%f,%f\n",light.getX(),light.getY(),light.getZ()); + break; + case 'j': + light.setPosition(light.getX()+100,light.getY(),light.getZ()); + printf("light at %f,%f,%f\n",light.getX(),light.getY(),light.getZ()); + break; + case 'u': + light.setPosition(light.getX(),light.getY(),light.getZ()+100); + printf("light at %f,%f,%f\n",light.getX(),light.getY(),light.getZ()); + break; + case 'b': + light.setPosition(light.getX(),light.getY(),light.getZ()-100); + printf("light at %f,%f,%f\n",light.getX(),light.getY(),light.getZ()); + break; + */ + case 'b': + if (!drawingborder) { + border.clear(); + drawingborder=true; + } + else drawingborder=false; + break; + case ' ': + ofSaveFrame(); + printf("[%8.2f] saved an image\n",ofGetElapsedTimef()); + break; + /* + case '>': + gameState=(gameState+1)%4; + gameStart=ofGetElapsedTimef(); + break; + + */ + } +} + +//-------------------------------------------------------------- +void testApp::keyReleased(int key){ + +} + +//-------------------------------------------------------------- +void testApp::mouseMoved(int x, int y ){ + +} + +//-------------------------------------------------------------- +void testApp::mouseDragged(int x, int y, int button){ + +} + +//-------------------------------------------------------------- +void testApp::mousePressed(int x, int y, int button){ + +} + +//-------------------------------------------------------------- +void testApp::mouseReleased(int x, int y, int button){ + pos=ofVec2f(x,y); + ofVec2f sp=screen2plane(pos); + if (drawingborder) { + border.push_back(sp); + } + printf("ray:%i,%i hit plane:%f,%f,%f\n",x,y,sp.x,sp.y); +} + +//-------------------------------------------------------------- +void testApp::windowResized(int w, int h){ + +} + +//-------------------------------------------------------------- +void testApp::gotMessage(ofMessage msg){ + +} + +//-------------------------------------------------------------- +void testApp::dragEvent(ofDragInfo dragInfo){ + +} + +void testApp::loadSettings(string filename){ + if( !XML.loadFile(filename) ){ + printf("unable to load %s check data/ folder\n",filename.c_str()); + }else{ + cam_angle=ofToInt(XML.getAttribute("gauntlet","cam_angle","none",0)); + threshold=ofToInt(XML.getAttribute("gauntlet","threshold","none",0)); + diffchannel=ofToInt(XML.getAttribute("gauntlet","keyChannel","none",0)); + learningRate=ofToFloat(XML.getAttribute("gauntlet","learningRate","none",0)); + removeShadows=ofToInt(XML.getAttribute("gauntlet","remove_shadows","none",0))==1; + testmovie=XML.getAttribute("gauntlet","testmovie","camoutput.move",0); + if(XML.pushTag("bounds")) { + for (int i=0;i