#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; itwopi) 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 }