package NET.worlds.scape; import java.io.IOException; public class VelocityBehavior extends SwitchableBehavior implements FrameHandler, BumpHandler { private RollingAttribute attr; private long lastTime; public float linearVel; public float linearDamp; protected Point3 dir; public float angularVel; public float angularDamp; public Point3 axis; private boolean bumplock = false; private static Object classCookie = new Object(); public VelocityBehavior( Point3Temp linearVectorPerSecond, float linearDampingPerSecond, Point3Temp angularAxis, float angularDegPerSecond, float angularDampingPerSecond ) { this.dir = new Point3(); this.linearVel = 0.0F; this.linearDamp = linearDampingPerSecond; this.addVelocity(linearVectorPerSecond); this.axis = new Point3(angularAxis); this.axis.normalize(); this.angularVel = angularDegPerSecond; this.angularDamp = angularDampingPerSecond; } public VelocityBehavior() { this(Point3Temp.make(0.0F, 0.0F, 0.0F)); } public VelocityBehavior(Point3Temp vel) { this(vel, 0.0F, Point3Temp.make(0.0F, 0.0F, 1.0F), 0.0F, 0.0F); } public void setDir(Point3Temp d) { double len = Math.sqrt(d.x * d.x + d.y * d.y + d.z * d.z); if (len == 0.0) { this.dir.x = 0.0F; this.dir.y = 0.0F; this.dir.z = 0.0F; } else { this.dir.x = (float)(d.x / len); this.dir.y = (float)(d.y / len); this.dir.z = (float)(d.z / len); } } public VelocityBehavior addVelocity(Point3Temp vector) { this.dir.times(this.linearVel).plus(vector); float len = this.dir.length(); if (len > 0.0F) { this.dir.dividedBy(len); } this.linearVel = len; return this; } public void setNotifyAttribute(RollingAttribute attr) { this.attr = attr; } @Override public boolean handle(FrameEvent e) { this.bumplock = false; WObject o = e.receiver; float dT = (float)(e.time - this.lastTime) / 1000.0F; if (this.lastTime == 0L) { this.lastTime = e.time; return true; } else { this.lastTime = e.time; if (this.linearVel != 0.0F) { Point3Temp dist = Point3Temp.make(this.dir).times(dT * this.linearVel); o.moveThrough(dist); if (this.linearDamp != 0.0F) { this.linearVel = (float)(this.linearVel * Math.exp(-this.linearDamp * dT)); if (Math.abs(this.linearVel) < this.linearDamp * 16.0F) { this.linearVel = 0.0F; if (this.attr != null) { this.attr.notifyStopped(); } } } } if (this.angularVel != 0.0F) { o.spin(this.axis.x, this.axis.y, this.axis.z, this.angularVel * dT); if (this.angularDamp != 0.0F) { this.angularVel = (float)(this.angularVel * Math.exp(-this.angularDamp * dT)); if (Math.abs(this.angularVel) < this.angularDamp * 0.6F) { this.angularVel = 0.0F; } } } return true; } } @Override public boolean handle(BumpEventTemp b) { if (this.bumplock) { return true; } else { if (this.attr != null) { this.attr.handle(b); } else { this.processBumpEvent(b); } this.bumplock = true; return true; } } public void processBumpEvent(BumpEventTemp b) { b.postBumpPath.minus(b.postBumpPath); if (this.dir.x != 0.0F || this.dir.y != 0.0F || this.dir.z != 0.0F) { WObject bumper = b.receiver == b.target ? (WObject)b.source : b.target; Point3Temp norm; if (!(bumper instanceof Camera) && !(bumper instanceof Hologram)) { norm = Point3Temp.make(b.bumpNormal); } else { float orientation = (float)((360.0F - bumper.getYaw() + 90.0F) * Math.PI / 180.0); norm = Point3Temp.make((float)Math.cos(orientation), (float)Math.sin(orientation), 0.0F); } float lb = (float)Math.sqrt(norm.x * norm.x + norm.y * norm.y + norm.z * norm.z); norm.x /= lb; norm.y /= lb; norm.z /= lb; float projection = Math.abs(this.dir.x * norm.x + this.dir.y * norm.y + this.dir.z * norm.z); this.dir.x = this.dir.x + norm.x * 2.0F * projection; this.dir.y = this.dir.y + norm.y * 2.0F * projection; this.dir.z = this.dir.z + norm.z * 2.0F * projection; double n = Math.sqrt(this.dir.x * this.dir.x + this.dir.y * this.dir.y + this.dir.z * this.dir.z); if (n != 0.0) { this.dir.x = (float)(this.dir.x / n); this.dir.y = (float)(this.dir.y / n); this.dir.z = (float)(this.dir.z / n); } } } @Override public Object properties(int index, int offset, int mode, Object value) throws NoSuchPropertyException { Object ret = null; switch (index - offset) { case 0: if (mode == 0) { ret = FloatPropertyEditor.make(new Property(this, index, "Linear Velocity")); } else if (mode == 1) { ret = new Float(this.linearVel); } else if (mode == 2) { this.linearVel = (Float)value; } break; case 1: if (mode == 0) { ret = FloatPropertyEditor.make(new Property(this, index, "Linear Damping")); } else if (mode == 1) { ret = new Float(this.linearDamp); } else if (mode == 2) { this.linearDamp = (Float)value; } break; case 2: if (mode == 0) { ret = Point3PropertyEditor.make(new Property(this, index, "Direction")); } else if (mode == 1) { ret = new Point3(this.dir); } else if (mode == 2) { this.dir = (Point3)value; } break; case 3: if (mode == 0) { ret = FloatPropertyEditor.make(new Property(this, index, "Angular Velocity")); } else if (mode == 1) { ret = new Float(this.angularVel); } else if (mode == 2) { this.angularVel = (Float)value; } break; case 4: if (mode == 0) { ret = FloatPropertyEditor.make(new Property(this, index, "Angular Damping")); } else if (mode == 1) { ret = new Float(this.angularDamp); } else if (mode == 2) { this.angularDamp = (Float)value; } break; case 5: if (mode == 0) { ret = Point3PropertyEditor.make(new Property(this, index, "Axis")); } else if (mode == 1) { ret = new Point3(this.axis); } else if (mode == 2) { this.axis = (Point3)value; } break; default: ret = super.properties(index, offset + 6, mode, value); } return ret; } @Override public String toString() { return super.toString() + "[lin. velocity " + this.linearVel + ", lin. damp " + this.linearDamp + ", direction " + this.dir + ", ang. vel " + this.angularVel + ", ang. damp " + this.angularDamp + ", axis " + this.axis + "]"; } @Override public void saveState(Saver s) throws IOException { s.saveVersion(0, classCookie); super.saveState(s); s.saveFloat(this.linearVel); s.saveFloat(this.linearDamp); s.save(this.dir); s.saveFloat(this.angularVel); s.saveFloat(this.angularDamp); s.save(this.axis); } @Override public void restoreState(Restorer r) throws IOException, TooNewException { switch (r.restoreVersion(classCookie)) { case 0: super.restoreState(r); this.linearVel = r.restoreFloat(); this.linearDamp = r.restoreFloat(); this.dir = (Point3)r.restore(); this.angularVel = r.restoreFloat(); this.angularDamp = r.restoreFloat(); this.axis = (Point3)r.restore(); return; default: throw new TooNewException(); } } }