VRML: Tag Chaser


#VRML V2.0 utf8

PROTO TagChaser [
	         field SFVec3f position 0 0 0
		 field SFVec3f goal 0 0 0
		 field SFFloat speed 1
		 field MFVec3f obstacles [ ]
		 field SFInt32 index 0
		 field SFVec3f direction 0 0 1
		 field SFVec3f scale 1 1 1
		 field SFFloat turnLimit 1   # radians per second
		 field SFBool it FALSE
		 field SFInt32 itIndex -1
		 eventIn SFVec3f set_goal
		 eventIn MFVec3f obstacles_changed
		 eventIn SFBool set_it
		 eventIn SFInt32 set_itIndex
		 eventOut MFFloat report_position
		 eventOut SFInt32 itIndex_changed
		]
{
   Group {
      children [
	 DEF MOVEME Transform {
	    translation IS position
	    children Transform {
	       translation 0 0 -.25
	       rotation 1 0 0  1.57 
	       scale IS scale
	       children Shape {
		  appearance Appearance { 
		     material DEF MAT Material {} }
		  geometry Cone {}
	       }
	    }
	 }

	 DEF HEART TimeSensor { loop TRUE }

	 # update direction and position when receive "heartbeats"
	 DEF SCRIPT Script {
	    eventIn SFTime beat
	    eventIn SFVec3f set_goal IS set_goal
	    eventIn MFVec3f obstacles_changed IS obstacles_changed
	    eventIn SFInt32 set_itIndex IS set_itIndex
	    eventOut SFVec3f position_changed
	    eventOut SFRotation rotation_changed  # update own rotation
	    eventOut MFFloat report_position IS report_position
	    eventOut SFColor color
	    eventOut SFInt32 itIndex_changed IS itIndex_changed
	    field SFInt32 index IS index
	    field SFVec3f direction IS direction
	    field SFVec3f position IS position
	    field SFVec3f goal IS goal
	    field SFFloat speed IS speed
	    field SFTime lastBeat 0
	    field MFVec3f obstacles IS obstacles
	    field SFFloat repulse 10
	    field SFFloat attract 1
	    field SFFloat power 2
	    field SFFloat rotation 0
	    field SFFloat turnLimit IS turnLimit  # max radians per second
	    field SFFloat changeAngle 0  # desired amount to rotate by
	    field SFFloat twopi 6.2832  # so don't keep recalculating
	    field SFBool first TRUE
	    field SFBool it IS it
	    field SFInt32 itIndex IS itIndex
	    field SFInt32 prevItIndex -1
	    url "vrmlscript:

function set_itIndex(val) { 
   prevItIndex = itIndex;
   itIndex = val; 
   if(itIndex != index) { 
      it = FALSE; 
      color = new SFColor(1, 1, 1);
   }
   else { 
      it = TRUE;  
      color = new SFColor(1, 0, 0);
   }
}

// clamp val between min and max numbers
function clamp(val, min, max) {
   if(val<min) return min;
   else if(val > max) return max;
   else return val;
}

function repulseForce(i) {
   v = position.subtract(obstacles[i]);
   if(it && (v.length() < .75)) {
      itIndex_changed = i;
   }
   m = 1;
   if(i!=itIndex) {
      m = repulse * 1/Math.pow(v.length(), power);
   }
   else {
      m = repulse * 1/v.length();
   }
   return v.normalize().multiply(m);
}

// Every so often, a script node's eventsProcessed function is called
// after a few events have been received.  Complicated calculations should
// go here.  In this case, calculating the new direction we should be
// travelling.
function eventsProcessed() {

   force = new SFVec3f(0,0,0);
   if(!it) {
      // compute repulsive force away from obstacles
      // double repulsive force away from 'it'
      rforce = new SFVec3f(0,0,0);
      for(i=0; i twopi) rotation = rotation - twopi;
   r = new SFRotation(0, 1, 0, rotation);
   rotation_changed = r;
   x = Math.round(direction.x * 10) / 10;
   y = Math.round(direction.y * 10) / 10;
   z = Math.round(direction.z * 10) / 10;
   direction = r.multVec(new SFVec3f(0, 0, 1));
}

function set_goal(val) {
   goal = val;
}

function beat(val) {
   if(first) { // initialize lastBeat on first heartbeat
      first = FALSE;
   }
   else {
      timeElapsed = val - lastBeat;
      calcDirection(timeElapsed);
      move = new SFVec3f(0, 0, 0);
      if(!it) {
	 move = direction.multiply(speed*timeElapsed);
      }
      else {
	 move = direction.multiply(speed*timeElapsed*1.25);
      }
      position = position.add(move);
      position_changed = position;
      report_position = new MFFloat(index, position.x, position.y, position.z);
   }
   lastBeat = val;
}

function initialize() {
   if(it) {
      color = new SFColor(1, 0, 0);
   }
}

function obstacles_changed(val) {
   obstacles = val;
}

"
	 }
	]
   }

   ROUTE HEART.time TO SCRIPT.beat
   ROUTE SCRIPT.position_changed TO MOVEME.translation
   ROUTE SCRIPT.rotation_changed TO MOVEME.rotation
   ROUTE SCRIPT.color TO MAT.diffuseColor
}


mrl