#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
}