#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"); chirps=new ofSoundPlayer[2]; chirps[0].loadSound("chirp1.wav"); chirps[1].loadSound("chirp2.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/20; turnRate=20; diveRate=0; fieldofview=60; lastTime=ofGetElapsedTimef(); centrePoint=ofVec2f(ofGetWidth()/2,600); //quick and dirty } void bird::setBounds(ofPlane* _bounds) { bounds=_bounds; } void bird::setCentre(ofVec2f _centre) { //in 2D on plane centre=_centre; } bird::~bird() { //dtor } void bird::update(map& players, float angle){ //movement basics float time=ofGetElapsedTimef(); float timeSeg=time-lastTime; lastTime=time; //tending to straighten unless avoiding an edge //turnRate *= 0.995; //create, -->draw a line representing the birds heading //absolute ray pointer //put on ground //do intersection with outline? //ray: can intersect with another ray //so: make a ray for each segment of polygon and intersect, find the nearest? //ray:: intersect returns a ray- shortest line between lines //or: project bird & heading onto ground //get intersection with each line of poly bounds //or maybe just use screen edges- quicker //find the shortest //depending on the angle it makes, decide whether to turn left or right to avoid boundary or get back within it //similar for people - use similar 2d algorithm //deal with elevation seperately //bounds is already an array of 4 planes representing edges of screen- does this work? 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) { 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) < 30) { //this could be based on height 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()/20) { 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()/10) { //below ceiling if (diveRate>-0.5f) { //increase climb rate diveRate-=0.05f; } } else diveRate*=0.9f; } switch (state) { case SCANNING: if (diveRate>0.5f) { model.sequences["flap"].fadeout(0.5); model.sequences["swoop"].start(); state=SWOOPING; int whichsound=(int)ofRandom(1.9999999); chirps[whichsound].play(); } break; case SWOOPING: if (diveRate<0.5f) { model.sequences["swoop"].fadeout(0.5); model.sequences["flap"].start(); state=SCANNING; int whichsound=(int)ofRandom(2.9999999); rattles[whichsound].play(); } break; case ATTACKING: 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 //where does it start //whistles rather than chirps 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()*(1.0f+diveRate))*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(position); 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", diveRate,position.z,state==1?"SCANNING":state==2?"SWOOPING":"ATTACKING"); ofDrawBitmapString(numStr,10,10); } } void bird::setSpeed(float speed){ model.speed=speed; } float bird::getSpeed(){ return model.speed; }