#include "bird.h" /* keep track of the high-level properties of the bird here activate and control sequences of targets in the morphmesh finally drawAnimated() */ bird::bird() { if (model.loadMesh("Bird-poses.xml")) printf("mesh loaded with %i vertices, %i face indices, %i targets\n",model.getNumVertices(),model.getNumIndices(),model.getNumTargets()); else printf("mesh XML file not parsed\n"); if (model.loadSeqs("Bird-anim.xml")) printf("animation loaded with %i sequences\n",model.getNumSequences()); else printf("animation XML file not parsed\n"); model.sequences["swoop"].start(); //how to track state/ bring animation in and out state=SWOOPING; aggressives=new ofSoundPlayer[2]; aggressives[0].loadSound("aggressive1.wav"); aggressives[1].loadSound("aggressive2.wav"); whistles=new ofSoundPlayer[2]; whistles[0].loadSound("whistle1.wav"); whistles[1].loadSound("whistle2.wav"); rattles=new ofSoundPlayer[3]; rattles[0].loadSound("rattle1.wav"); rattles[1].loadSound("rattle2.wav"); rattles[2].loadSound("rattle3.wav"); //emit a rattle when the bird becomes vsisible and when it switches back to scanning //emit a chirp when it switches to swooping //emit an aggressive noise when it attacks texture.loadImage("TextureBird.jpg"); //starting pos position=ofVec3f(ofRandom(ofGetWidth()/4,(3*ofGetWidth())/4),ofRandom(ofGetHeight()/4,(3*ofGetHeight())/4),-ofGetHeight()/10); heading=-90; direction=ofVec3f(0,-1,0); //director for a heading of 0, level velocity=1.0f/10; turnRate=20; diveRate=0; fieldofview=60; lastTime=ofGetElapsedTimef(); } void bird::setBounds(ofPlane* _bounds) { bounds=_bounds; } void bird::setCentre(ofVec2f _centre) { //in 2D on plane centre=_centre; position=ofVec3f(centre.x,centre.y,-ofGetHeight()/10); printf("bird init: %4.2f,%4.2f,%4.2f\n",position.x,position.y,position.z); } bird::~bird() { //dtor } void bird::update(map& players, float angle){ //movement basics float time=ofGetElapsedTimef(); float timeSeg=time-lastTime; lastTime=time; //this is causing the bird to go off the screen at the start.. maybe not a problem.. pointer=ofRay(position.rotated(angle,ofVec3f(1,0,0)),-direction.rotated(heading,ofVec3f(0,0,-1)).rotated(angle,ofVec3f(1,0,0))*1000.0f,false); //intersect with bounds and find shortest distance to edge of world // // /* float shortest=1000000.0f; vector pts; int shnum=-1; int bdnum=0; for (int i=0;i<4;i++) { ofVec3f p; if (bounds[i].intersect(pointer,p)) { pts.push_back(p); if (position.rotated(angle,ofVec3f(1,0,0)).distance(p)-1) { edgepoint=pts[shnum]; edgelength=shortest; ofVec3f bv=bounds[bdnum].getNormal().rotated(angle,ofVec3f(-1,0,0)); edgeangle=((atan2(bv.y,bv.x)*RAD_TO_DEG)+90)+heading; while (edgeangle>180) edgeangle -=360; while (edgeangle<-180) edgeangle +=360; } else { printf("error: no bird bounds intersection\n"); } */ //avoiding edges is a bit of a nightmare //turnRate=(turnRate*.99)+(0.05f*max(0.0f,1.0f-(pow(edgelength*.0100f,2.0f)))*max(0.0f,90.0f-abs(edgeangle))*sign(edgeangle)); //decide whether we are running out of space and if so, which way to turn# //turning tendency is more acute when we are more perpendicular, and closer, to the edge //mayeb work out how many frames left before we crash //what about a force attracting to the middle of the screen? float radius=centre.distance(ofVec2f(position.x,position.y)); //angle from centre to bird position edgeangle=(180.0f+(90.0f-(atan2(position.y-centre.y,position.x-centre.x)*RAD_TO_DEG)))-heading; while (edgeangle>180.0f) edgeangle -=360.0f; while (edgeangle<-180.0f) edgeangle +=360.0f; turnRate=(turnRate*.90)+(pow(0.01f*radius,1)*edgeangle*0.01f); //bird cruising while avoiding edges //bird floowing people while staying on screen //bird changing height //morph targets //make a list of players within view and decide which one is the best target //1sr draw lines and visualise ofRay relpointer=ofRay(position,-direction.rotated(heading,ofVec3f(0,0,-1))*1000.0f,false); playang.clear(); playdist.clear(); playpos.clear(); playhead.clear(); playdip.clear(); float nearest=1000000.0f; int nearnum=-1; int in=0; // // DEBUGGING feature // bool trackPlayers=true; map::iterator it; for (it=players.begin();it!=players.end();it++) { if (it->second.active&& !it->second.isCaught) { float headif=(atan2(it->second.getWorldPosition().x-position.x,it->second.getWorldPosition().y-position.y)*RAD_TO_DEG)-heading; while (headif>180) headif=headif-360; while (headif <-180) headif=headif+360; if (abs(headif) < 120) { //was 30 playhead.push_back(headif); ofVec3f p=it->second.getWorldPosition()-position; playang.push_back(p.angle(-direction.rotated(heading,ofVec3f(0,0,-1)))); playdist.push_back(p.length()); playpos.push_back(it->second.getWorldPosition()); //maybe all of this is unnecessary if we are only following 1 - just save it if nearer if (p.length() < nearest) { nearest=p.length(); if (trackPlayers) nearnum=in; } in++; } } } //dive behaviour if (nearnum>-1) { //influence direction turnRate+=playhead[nearnum]*.1; //check if relatively high if (position.z<-ofGetHeight()/15) { if (diveRate<2.0f) { //increase dive rate diveRate+=0.1f; } } else { //low enough if (diveRate>0.0f) { diveRate*=0.9f; } } } else { if (position.z>-ofGetHeight()/5) { //below ceiling if (diveRate>-0.5f) { //increase climb rate diveRate-=0.05f; } } else diveRate*=0.9f; } float neardist=ofGetWidth()/12.0f; if ((state==SCANNING||state==SWOOPING)&&nearnum>-1) { if (playdist[nearnum]0.1f) velocity /=1.01; if (diveRate>0.5f) { model.sequences["flap"].fadeout(0.5); model.sequences["swoop"].start(); state=SWOOPING; int whichsound=(int)ofRandom(2.9999999); rattles[whichsound].play(); } break; case SWOOPING: if (velocity<0.1f) velocity *=1.1; if (velocity>0.25f) velocity /=1.02; if (diveRate<0.5f) { model.sequences["swoop"].fadeout(0.5); model.sequences["flap"].start(); state=SCANNING; int whichsound=(int)ofRandom(1.9999999); whistles[whichsound].play(); } break; case ATTACKING: if (nearnum==-1||playdist[nearnum]>neardist) { //player escaped model.sequences["attack"].fadeout(0.5); model.sequences["flap"].start(); state=SCANNING; int whichsound=(int)ofRandom(1.9999999); whistles[whichsound].play(); } if (nearnum>-1) { velocity*=(19+(playdist[nearnum]/neardist))*.05; //slow down } break; } if (diveRate<0.0f) setSpeed(1.0f-diveRate); else setSpeed(1.0f); //if (sign(turnRate)) turnRate=min(2.0f,turnRate); //else turnRate=max(-2.0f,turnRate); //if high and target is in sight, swoop to gain speed //if low and no narget is near, climb to gain a view //for now, when the bird hits the target they both blink and the board comes down? //initially, get the bird to connect with players- //integrate new keying //interface - update info - save data //do the documentation //go back to the bird - watch out for dead players (from trap doors) - bird is going out of play - just retrieve it for now? //flap faster while climbing + swoop //bird noises //increase angle of view and allow more extreme turn angles heading=heading+(turnRate*timeSeg); while (heading>180) heading=heading-360; while (heading <-180) heading=heading+360; position-=direction.rotated(heading,ofVec3f(0,0,-1))*(velocity*ofGetHeight())*timeSeg; //.rotate(heading,ofVec3f(0,1,0)) position +=ofVec3f(0,0,5)*diveRate*timeSeg; } void bird::draw(){ glEnable(GL_DEPTH_TEST); ofPushMatrix(); ofTranslate(position); ofRotate(90,0,0,1); ofRotate(90,-1,0,0); ofRotate(heading+90,0,1,0); ofSetHexColor(0xffffff); ofScale(.25,.25,.25); bindTexture(texture); model.drawAnimated(); unbindTexture(texture); ofPopMatrix(); glDisable(GL_DEPTH_TEST); } void bird::drawShadow(){ ofPushMatrix(); ofTranslate(ofVec3f(position.x,position.y,0)); ofRotate(90,0,0,1); ofRotate(90,-1,0,0); ofRotate(heading+90,0,1,0); ofSetHexColor(0x303030); //ofTranslate(0,(-ofGetHeight()/4)+5,0); ofScale(.15,0,.15); model.drawAnimated(); ofPopMatrix(); } void bird::drawDebug(){ /* if (leaving) ofSetHexColor(0xff0000); else ofSetHexColor(0xff00ff); ofLine(pointer.s,pointer.s+1000*pointer.t); ofSphere(edgepoint,2.0f); */ if (DEBUG) { ofSetHexColor(0xff00ff); char numStr[100]; sprintf(numStr, "dive: %4.2f\nheight: %4.2f\n%s\n%4.2f,%4.2f,%4.2f", diveRate,position.z,state==1?"SCANNING":state==2?"SWOOPING":"ATTACKING",position.x,position.y,position.z); ofDrawBitmapString(numStr,10,10); } } void bird::setSpeed(float speed){ model.speed=speed; } float bird::getSpeed(){ return model.speed; }