/** * this sketch illustrates how we created the connection between a creature in the world * and an interface on top */ import gestalt.Gestalt; import gestalt.context.GLContext; import gestalt.extension.quadline.QuadBezierCurve; import gestalt.p5.GestaltPlugIn; import gestalt.shape.AbstractDrawable; import gestalt.shape.Disk; import gestalt.shape.DrawableFactory; import gestalt.texture.bitmap.IntegerBitmap; import mathematik.Vector3f; import werkzeug.interpolation.InterpolateConstant; import werkzeug.interpolation.InterpolateExponential; import werkzeug.interpolation.InterpolateSinus; import werkzeug.interpolation.Interpolator; import verhalten.Engine; import verhalten.Seek; import verhalten.Wander; import processing.opengl.*; public class UmbilicalCord extends PApplet implements Gestalt { private GestaltPlugIn gestalt; private float _myDepth = 1000; private Disk _myEndCap; private UmbilicalCordBezier _myCord; public void setup() { /* setup p5 */ size(640, 480, OPENGL); framerate(50); rectMode(CENTER); noStroke(); gestalt = new GestaltPlugIn(this); /* create umbilical cord */ int myCordTexture = gestalt.texturemanager().create(); PImage myCordImage = loadImage("data/line.png"); IntegerBitmap myCordBitmap = new IntegerBitmap(myCordImage.pixels, myCordImage.width, myCordImage.height, Gestalt.TEXTURE_MEDIA_MOVIE); gestalt.texturemanager().load(myCordTexture, myCordBitmap); _myCord = new UmbilicalCordBezier(gestalt.drawablefactory(), myCordTexture); gestalt.bin(BIN_3D).add(_myCord); /* disk texture */ int myDiskTexture = gestalt.texturemanager().create(); PImage myDiskImage = loadImage("data/disk.png"); IntegerBitmap myDiskBitmap = new IntegerBitmap(myDiskImage.pixels, myDiskImage.width, myDiskImage.height, Gestalt.TEXTURE_MEDIA_MOVIE); gestalt.texturemanager().load(myDiskTexture, myDiskBitmap); /* create a disk */ _myEndCap = gestalt.drawablefactory().disk(); _myEndCap.scale().set(width / 20 + 10, width / 20 + 10); _myEndCap.material().getColor().set(1); _myEndCap.material().setTexture(myDiskTexture); _myEndCap.material().depthtest = false; _myEndCap.material().blendmode = MATERIAL_BLEND_INVERS_MULTIPLY; gestalt.bin(BIN_3D).add(_myEndCap); } public void draw() { /* clear screen */ background(50); /* loop umbilcal cord */ _myCord.setStartpoint(new Vector3f(width / 2, height / 2, _myDepth)); _myCord.setEndpoint(new Vector3f(mouseX, mouseY, 0)); _myCord.setDirection(4.0f * (float) mouseY / height); _myCord.loop(1 / 50f); /* cap */ _myEndCap.transform().translation.set(mouseX, mouseY); Vector3f myToTarget = new Vector3f(80, 120, _myDepth); myToTarget.sub(new Vector3f(_myEndCap.transform().translation)); _myEndCap.transform().rotation.setZRotation(atan2(myToTarget.y, myToTarget.x)); } class UmbilicalCordBezier extends AbstractDrawable { private QuadBezierCurve[] _myCordLine; private final QuadBezierCurve _myReferenceCordLine; private Seek[] _myBezierLineArrivalBehavior; private Wander[] _myBezierLineWanderBehavior; private Engine[] _myBezierLineEngine; private float _myAbsoluteTime; private float[] myDirectionWeigth = new float[] {0.5f, 0.5f}; private Vector3f[] myDirections = new Vector3f[] {new Vector3f(), new Vector3f()}; private static final int ARRIVAL_DIRECTION = 0; private static final int WANDER_DIRECTION = 1; private InterpolateSinus _myInterpolatorKernel; private final float _myStartWidth; private final float _myEndWidth; private float _myDirection; private Vector3f _myBegin; private Vector3f _myEnd; public UmbilicalCordBezier(DrawableFactory theDrawablefactory, int myCordTexture) { /* interpolator kernel */ _myStartWidth = 10 + 20; _myEndWidth = width / 20 + 20; _myInterpolatorKernel = new InterpolateSinus(0, 0, 2f, 1f); Interpolator myInterpolator = new Interpolator(_myStartWidth, _myEndWidth, _myInterpolatorKernel); /* create bezier line */ final float myEndAlpha = 0.25f; final float myBeginAlpha = 0.1f; _myCordLine = new QuadBezierCurve[8]; _myBezierLineArrivalBehavior = new Seek[_myCordLine.length]; _myBezierLineWanderBehavior = new Wander[_myCordLine.length]; _myBezierLineEngine = new Engine[_myCordLine.length]; for (int i = 0; i < _myCordLine.length; ++i) { _myCordLine[i] = theDrawablefactory.extensions().quadbeziercurve(); _myCordLine[i].upvector().set(0, 0, 1); _myCordLine[i].setResolution(20); _myCordLine[i].linewidth = 5; _myCordLine[i].begincolor.set(1, 1, 1, 0); _myCordLine[i].endcolor.set(1, 1, 1, 1); _myCordLine[i].material().blendmode = Gestalt.MATERIAL_BLEND_INVERS_MULTIPLY; _myCordLine[i].material().depthtest = false; _myCordLine[i].material().setTexture(myCordTexture); _myCordLine[i].setColorRedInterpolator(new Interpolator(1, 1, new InterpolateConstant(1))); _myCordLine[i].setColorGreenInterpolator(new Interpolator(1, 1, new InterpolateConstant(1))); _myCordLine[i].setColorBlueInterpolator(new Interpolator(1, 1, new InterpolateConstant(1))); _myCordLine[i].setColorAlphaInterpolator(new Interpolator(myBeginAlpha, myEndAlpha, new InterpolateExponential(0.5f))); _myCordLine[i].setLineWidthInterpolator(myInterpolator); /* controlpoint behavior */ _myBezierLineEngine[i] = new Engine(); _myBezierLineEngine[i].setMaximumForce(400.0f); _myBezierLineEngine[i].setMaximumSpeed(700.0f); _myBezierLineWanderBehavior[i] = new Wander(); _myBezierLineWanderBehavior[i].setRadius(40); _myBezierLineWanderBehavior[i].setSteeringStrength(300); _myBezierLineArrivalBehavior[i] = new Seek(); _myBezierLineArrivalBehavior[i].setPoint(_myCordLine[i].end); _myBezierLineArrivalBehavior[i].direction(); /* connect endpoint to engine */ _myCordLine[i].endcontrol = _myBezierLineEngine[i].position(); } /* reference shape */ _myReferenceCordLine = _myCordLine[0]; _myBegin = new Vector3f(); _myEnd = new Vector3f(); } /* -> umbilical cord */ public void loop(float theDeltaTime) { /* modulate umbilical cord */ _myAbsoluteTime += theDeltaTime; for (int i = 0; i < _myCordLine.length; ++i) { final float myPercentage = 0.5f * (float) i / (float) (_myCordLine.length - 1); _myInterpolatorKernel.setXOffset(_myAbsoluteTime * _myDirection + myPercentage); /* beginning and end point */ _myCordLine[i].begin.set(_myBegin); _myCordLine[i].end.set(_myEnd); /* begin control point */ _myCordLine[i].begincontrol.x = _myCordLine[i].begin.x; _myCordLine[i].begincontrol.y = _myCordLine[i].begin.y; _myCordLine[i].begincontrol.z = _myBegin.z + (_myEnd.z - _myBegin.z) / 2; /* end control point */ _myBezierLineArrivalBehavior[i].get(_myBezierLineEngine[i], myDirections[ARRIVAL_DIRECTION]); _myBezierLineWanderBehavior[i].get(_myBezierLineEngine[i], theDeltaTime, myDirections[WANDER_DIRECTION]); _myBezierLineEngine[i].apply(theDeltaTime, myDirections, myDirectionWeigth); _myCordLine[i].endcontrol.z = _myCordLine[i].begincontrol.z; /* update bezierline */ _myCordLine[i].update(); } } public void setStartpoint(Vector3f thePoint) { _myBegin.set(thePoint); } public void setEndpoint(Vector3f thePoint) { _myEnd.set(thePoint); } public void setDirection(float theDirection) { _myDirection = theDirection; } /* -> drawable */ public void draw(GLContext theRenderContext) { for (int i = 0; i < _myCordLine.length; ++i) { _myCordLine[i].draw(theRenderContext); } } public boolean isActive() { return _myReferenceCordLine.isActive(); } public float getSortValue() { return _myReferenceCordLine.getSortValue(); } public void setSortValue(float theSortValue) { _myReferenceCordLine.setSortValue(theSortValue); } public float[] getSortData() { return _myReferenceCordLine.getSortData(); } public boolean isSortable() { return _myReferenceCordLine.isSortable(); } } public static void main(String[] args) { PApplet.main(new String[] { "UmbilicalCord"} ); } }