diff --git a/.classpath b/.classpath
index 21de87f..8c2bcd3 100644
--- a/.classpath
+++ b/.classpath
@@ -10,5 +10,6 @@
+
diff --git a/src/animata/Animata.java b/src/animata/Animata.java
new file mode 100644
index 0000000..36ca7ff
--- /dev/null
+++ b/src/animata/Animata.java
@@ -0,0 +1,72 @@
+package animata;
+
+import oscP5.OscMessage;
+import oscP5.OscP5;
+import processing.core.PApplet;
+import netP5.NetAddress;
+import netP5.NetInfo;
+
+public class Animata {
+
+ public static NetAddress net;
+ public static OscP5 oscP5;
+
+ public static void startOSC(PApplet applet) {
+ net = new NetAddress(NetInfo.getHostAddress(), 7110);
+ oscP5 = new OscP5(applet, 12000);
+ PApplet.println("Sending Animata stuff to " + net.address());
+ }
+
+
+ private static boolean checkInitialised() {
+ if (net == null) {
+ //PApplet.println("need to call init() with valid settings on Animata");
+ return false;
+ }
+ return true;
+ }
+
+ public static void zoomCamera(Float delta) {
+ if (!checkInitialised()) return;
+ OscMessage message = new OscMessage("/cameradeltazoom");
+ message.add(delta);
+ oscP5.send(message, net);
+ System.out.println("message:" + message + " to " + net);
+ }
+
+ public static void panLayer(Float deltaX) {
+ if (!checkInitialised()) return;
+ OscMessage message = new OscMessage("/cameradeltapan");
+ message.add(deltaX);
+ message.add(0.0f);
+ oscP5.send(message, net);
+ System.out.println("message:" + message + " to " + net);
+ }
+
+ public static void setBone(String name, Float n) {
+ if (!checkInitialised()) return;
+ OscMessage message = new OscMessage("/anibone");
+ message.add(name);
+ message.add((float) n);
+ PApplet.println("trying to send to " + net.address() + " = " + n + " to bone " + name);
+ oscP5.send(message, net);
+ }
+
+ public static void setAlpha(String layer, float value) {
+ if(!checkInitialised()) return;
+ OscMessage message = new OscMessage("/layeralpha");
+ message.add(layer);
+ message.add(value);
+ oscP5.send(message, net);
+ }
+
+ public static void setBoneTempo(String bone, Float tempo) {
+ if(!checkInitialised()) return;
+ OscMessage message = new OscMessage("/bonetempo");
+ message.add(bone);
+ message.add((float) tempo);
+ PApplet.println("trying to send to " + net.address() + " = " + tempo + " to bone " + bone);
+ oscP5.send(message, net);
+ }
+
+}
diff --git a/src/animata/AnimataPlayback.java b/src/animata/AnimataPlayback.java
index b1b942e..43d200d 100644
--- a/src/animata/AnimataPlayback.java
+++ b/src/animata/AnimataPlayback.java
@@ -33,9 +33,14 @@ public void panXBy(float delta) {
x += delta;
targetX += delta;
}
+ public void panYBy(float delta) {
+ y += delta;
+ targetY += delta;
+ }
}
public static final float timeDivision = 42f;
public static float gravity = 0;
+ private static boolean debug;
private PApplet applet;
private Layer root;
private LayerView layersView;
@@ -52,6 +57,7 @@ public AnimataPlayback(PApplet applet){
}
private void setup(PApplet applet) {
this.applet = applet;
+ Animator.init(applet);
camera = new Camera(applet);
this.applet.hint(PApplet.ENABLE_OPENGL_2X_SMOOTH);
root = new Layer();
@@ -106,5 +112,15 @@ public void zoomCamera(float delta) {
public void panCameraX(float delta) {
camera.panXBy(delta);
}
+ public void panCameraY(float delta){
+ camera.panYBy(delta);
+ }
+ public void debug() {
+ debug = true;
+
+ }
+ public static boolean debugging() {
+ return debug;
+ }
}
\ No newline at end of file
diff --git a/src/animata/Animator.java b/src/animata/Animator.java
new file mode 100644
index 0000000..6be94ad
--- /dev/null
+++ b/src/animata/Animator.java
@@ -0,0 +1,89 @@
+package animata;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import processing.core.PApplet;
+
+public class Animator extends Observable implements Observer {
+
+
+ private static Engine engine;
+ float targetValue;
+ float initialValue;
+ float difference;
+ float frameIncrement;
+ public float currentValue = 0;
+ float frame = 0;
+ boolean isComplete = false;
+
+ public Animator(float startValue, Observer target) {
+ engine.addObserver(this);
+ initialValue = currentValue = startValue;
+ addObserver(target);
+ }
+
+ public void set(float value, int _frames) {
+ targetValue = value;
+ initialValue = currentValue;
+ difference = (value - initialValue);
+ frameIncrement = 1 / (float) _frames;
+ isComplete = false;
+ frame = 0;
+ frame += frameIncrement;
+ }
+
+ public float getValue() {
+ return currentValue;
+ }
+
+ public void update(Observable o, Object arg) {
+ // Just gonna assume it's always nextFrame.
+ if (frame >= 1 || frame == 0) return;
+ frame += frameIncrement;
+ currentValue = initialValue + (difference * easeOutQuad(frame));
+ changed();
+ if (frame >= 1) complete();
+ }
+
+ void changed() {
+ setChanged();
+ notifyObservers(NEXT_FRAME);
+ }
+
+ public boolean isComplete() {
+ return isComplete;
+ }
+
+ public void complete() {
+ currentValue = targetValue;
+ isComplete = true;
+ notifyObservers("complete");
+ }
+
+ public void stop() {
+ // engine.deleteObserver(this);
+ }
+
+ float easeOutQuad(float t) {
+ return -1 * (t /= 1) * (t - 2);
+ }
+
+ public static void init(PApplet applet) {
+ engine = new Engine(applet);
+
+ };
+ public static final String NEXT_FRAME = "nextFrame";
+ public static class Engine extends Observable{
+
+ public Engine(PApplet applet) {
+ applet.registerDraw(this);
+ }
+ public void draw() {
+ setChanged();
+ notifyObservers(NEXT_FRAME);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/animata/ControlFactory.java b/src/animata/ControlFactory.java
index 3ae53aa..81bfdd6 100644
--- a/src/animata/ControlFactory.java
+++ b/src/animata/ControlFactory.java
@@ -2,8 +2,11 @@
import processing.xml.XMLElement;
import rwmidi.MidiInput;
+import animata.controls.BoneTempoKeyRanges;
+import animata.controls.BoneTempoKeys;
import animata.controls.Control;
import animata.controls.NoteBone;
+import animata.controls.NoteRangeBone;
public class ControlFactory {
@@ -12,9 +15,9 @@ public static Control createControl(XMLElement element, MidiInput in) {
if(name.equals("notebone")) return new NoteBone(element, in);
// if(name.equals("faderbone")) return new FaderBone(element);
// if(name.equals("freqbone")) return new FreqBone(element);
-// if(name.equals("noterangebone")) return new NoteRangeBone(element);
-// if(name.equals("bonetempokeys")) return new BoneTempoKeys(element);
-// if(name.equals("bonetempokeyranges")) return new BoneTempoKeyRanges(element);
+ if(name.equals("noterangebone")) return new NoteRangeBone(element,in);
+ if(name.equals("bonetempokeys")) return new BoneTempoKeys(element,in);
+ if(name.equals("bonetempokeyranges")) return new BoneTempoKeyRanges(element, in);
return new Control(element, in);
diff --git a/src/animata/Controller.java b/src/animata/Controller.java
index 4e7ee00..a08395f 100644
--- a/src/animata/Controller.java
+++ b/src/animata/Controller.java
@@ -43,6 +43,8 @@ public boolean setBoneTempo(String name, Float tempo) {
for (Bone bone : bones) {
bone.setTempo(tempo);
}
+ // TODO: extract interface for Animata calls
+ Animata.setBoneTempo(name, tempo);
return true;
}
@@ -51,6 +53,8 @@ public boolean animateBone(String name, float scale) {
for (Bone bone : bones) {
bone.setScale(scale);
}
+ // TODO: extract interface for Animata calls
+ Animata.setBone(name, scale);
return true;
}
diff --git a/src/animata/LayerView.java b/src/animata/LayerView.java
index 3e8fe7d..2e1b30f 100644
--- a/src/animata/LayerView.java
+++ b/src/animata/LayerView.java
@@ -4,6 +4,8 @@
import processing.core.PApplet;
import animata.model.Layer;
+import animata.model.Skeleton.Bone;
+import animata.model.Skeleton.Joint;
public class LayerView extends ViewBase {
@@ -30,21 +32,32 @@ public void draw() {
applet.pushMatrix();
doTransformation();
applet.pushMatrix();
- drawMesh();
+ if(mesh!= null) mesh.draw();
+ if(AnimataPlayback.debugging()) drawDebugStuff();
drawChildLayers();
applet.popMatrix();
applet.popMatrix();
}
+ private void drawDebugStuff() {
+ if(layer.skeleton == null) return;
+ for(Joint joint : layer.skeleton.joints){
+ applet.fill(0xFFFFFF00);
+ applet.ellipse(joint.x, joint.y, 5, 5);
+ }
+ applet.stroke(0x99FF00FF);
+ applet.strokeWeight(5);
+ for(Bone bone : layer.skeleton.bones){
+ applet.line(bone.j1.x, bone.j1.y, bone.j0.x, bone.j0.y);
+ }
+ }
+
private void drawChildLayers() {
for (LayerView layerView : layers) {
layerView.draw();
}
}
- private void drawMesh() {
- if(mesh!= null) mesh.draw();
- }
// this is like the calcTransformationMatrix method from the original, but called during draw
private void doTransformation() {
diff --git a/src/animata/MeshView.java b/src/animata/MeshView.java
index 1996630..27f8639 100644
--- a/src/animata/MeshView.java
+++ b/src/animata/MeshView.java
@@ -16,6 +16,7 @@ public MeshView(PApplet applet, Layer layer) {
this.layer = layer;
}
public void draw() {
+ applet.noStroke();
drawFaces(layer.mesh.faces);
// applet.textFont(font);
// applet.fill(0);
diff --git a/src/animata/controls/BoneTempoKeyRanges.java b/src/animata/controls/BoneTempoKeyRanges.java
new file mode 100644
index 0000000..3ae6999
--- /dev/null
+++ b/src/animata/controls/BoneTempoKeyRanges.java
@@ -0,0 +1,39 @@
+package animata.controls;
+
+import processing.xml.XMLElement;
+import rwmidi.MidiInput;
+import animata.NoteParser;
+import animata.NoteParser.BadNoteFormatException;
+
+public class BoneTempoKeyRanges extends Control {
+ private Integer low;
+ private Integer high;
+ private int bonecount;
+ private float tempo;
+ private BoneTempoKeys[] ranges;
+ private String boneRoot;
+ public BoneTempoKeyRanges(XMLElement element, MidiInput in) {
+ super(element, in);
+ try {
+ low = NoteParser.getNote(element.getStringAttribute("low", "1"));
+ high = NoteParser.getNote(element.getStringAttribute("high", "100"));
+ } catch (BadNoteFormatException e) {
+ System.out.println(e.getMessage());
+ }
+ channel = element.getIntAttribute("channel", 16) - 1;
+ boneRoot = element.getStringAttribute("bone");
+ tempo = element.getFloatAttribute("tempo", 1);
+ bonecount = element.getIntAttribute("bonecount",1);
+ addRanges();
+ }
+
+ private void addRanges() {
+ ranges = new BoneTempoKeys[bonecount];
+ float step = (((float)high)-((float)low))/((float)bonecount);
+ for (int i = 0; i < bonecount; i++) {
+ int rangeLow = low + (int)(i*step);
+ System.out.println("added range low=" + rangeLow + " step was " + step);
+ ranges[i] = new BoneTempoKeys(in, rangeLow, rangeLow + (int)step, boneRoot+i, tempo, channel);
+ }
+ }
+}
diff --git a/src/animata/controls/BoneTempoKeys.java b/src/animata/controls/BoneTempoKeys.java
new file mode 100644
index 0000000..1445c6a
--- /dev/null
+++ b/src/animata/controls/BoneTempoKeys.java
@@ -0,0 +1,67 @@
+package animata.controls;
+
+import processing.xml.XMLElement;
+import rwmidi.MidiInput;
+import rwmidi.Note;
+import animata.Controller;
+import animata.NoteParser;
+import animata.NoteParser.BadNoteFormatException;
+
+public class BoneTempoKeys extends Control {
+
+ private Float tempo;
+ private String bone;
+ private int keysPressed = 0;
+ private int low;
+ private int high;
+
+ public BoneTempoKeys(MidiInput in, int low, int high, String bone, float tempo, int channel) {
+ super(channel, in);
+ this.low = low;
+ this.high = high;
+ this.bone = bone;
+ this.tempo = tempo;
+ }
+
+ public BoneTempoKeys(XMLElement element, MidiInput in) {
+ super(element, in);
+ try {
+ low = NoteParser.getNote(element.getStringAttribute("low", "1"));
+ high = NoteParser.getNote(element.getStringAttribute("high", "100"));
+ } catch (BadNoteFormatException e) {
+ System.out.println(e.getMessage());
+ }
+ bone = element.getStringAttribute("bone");
+ tempo = element.getFloatAttribute("tempo");
+ Controller.getInstance().setBoneTempo(bone, 0f);
+ }
+
+ public void noteOnReceived(Note n) {
+ if(n.getChannel()!= channel) return;
+ int pitch = n.getPitch();
+ if (pitch < low) return;
+ if (pitch > high) return;
+ float trigger = 0f;
+ Float newTempo = 1f;
+ keysPressed++;
+ newTempo = tempo;
+
+ Controller.getInstance().setBoneTempo(bone, newTempo);
+ // used for piano
+ Controller.getInstance().animateBone("trigger" + bone, trigger);
+ }
+
+ public void noteOffReceived(Note n) {
+ if(n.getChannel()!= channel) return;
+ int pitch = n.getPitch();
+ if (pitch < low) return;
+ if (pitch > high) return;
+ float trigger = 1f;
+ keysPressed--;
+ float newTempo = 1f;
+ if (keysPressed == 0) newTempo = 0f;
+ Controller.getInstance().setBoneTempo(bone, newTempo);
+ // used for piano
+ Controller.getInstance().animateBone("trigger" + bone, trigger);
+ }
+}
diff --git a/src/animata/controls/Control.java b/src/animata/controls/Control.java
index 5bc5a3a..4b674b8 100644
--- a/src/animata/controls/Control.java
+++ b/src/animata/controls/Control.java
@@ -8,9 +8,18 @@ public class Control {
protected int channel;
protected final Controller controller = Controller.getInstance();
+ protected MidiInput in;
public Control(XMLElement element, MidiInput in) {
+ init(element.getIntAttribute("channel", element.getParent().getIntAttribute("channel")) - 1,in);
+ }
+ public Control(int channel, MidiInput in){
+ init(channel, in);
+ }
+ private void init(int channel, MidiInput in) {
if (in == null) System.out.println("error, MIDI Input Device not supplied!");
- channel = element.getIntAttribute("channel", element.getParent().getIntAttribute("channel")) - 1;
+ this.channel = channel;
+ this.in = in;
+ in.plug(this);
}
}
diff --git a/src/animata/controls/NoteBone.java b/src/animata/controls/NoteBone.java
index 03e3e52..4d63b24 100644
--- a/src/animata/controls/NoteBone.java
+++ b/src/animata/controls/NoteBone.java
@@ -1,18 +1,23 @@
package animata.controls;
+import java.util.Observable;
+import java.util.Observer;
+
import processing.xml.XMLElement;
import rwmidi.MidiInput;
import rwmidi.Note;
+import animata.Animator;
import animata.NoteParser;
import animata.NoteParser.BadNoteFormatException;
-public class NoteBone extends Control {
+public class NoteBone extends Control implements Observer {
private String bone;
private Integer note;
private float on;
private float off;
+ private Animator animator;
public NoteBone(XMLElement element, MidiInput in) {
super(element, in);
@@ -24,19 +29,21 @@ public NoteBone(XMLElement element, MidiInput in) {
} catch (BadNoteFormatException e) {
e.printStackTrace();
}
-
- in.plug(this);
+ animator = new animata.Animator(off,this);
System.out.println("Created notebone for bone " + bone + " note=" +note);
}
public void noteOnReceived(Note n){
if(n.getChannel() != channel) return;
if(n.getPitch() != note) return;
- controller.animateBone(bone, on);
+ animator.set(on,3);
}
public void noteOffReceived(Note n){
if(n.getChannel() != channel) return;
if(n.getPitch() != note) return;
- controller.animateBone(bone, off);
+ animator.set(off,10);
+ }
+ public void update(Observable o, Object arg) {
+ controller.animateBone(bone, animator.currentValue);
}
}
diff --git a/src/animata/controls/NoteRangeBone.java b/src/animata/controls/NoteRangeBone.java
new file mode 100644
index 0000000..e21981f
--- /dev/null
+++ b/src/animata/controls/NoteRangeBone.java
@@ -0,0 +1,36 @@
+package animata.controls;
+
+import animata.Controller;
+import animata.NoteParser;
+import animata.NoteParser.BadNoteFormatException;
+import processing.xml.XMLElement;
+import rwmidi.MidiInput;
+import rwmidi.Note;
+
+public class NoteRangeBone extends Control {
+
+ private String bone;
+ private float range;
+ private int low;
+ private int high;
+
+ public NoteRangeBone(XMLElement element, MidiInput in) {
+ super(element, in);
+ try {
+ low = NoteParser.getNote(element.getStringAttribute("low", "1"));
+ high = NoteParser.getNote(element.getStringAttribute("high", "100"));
+ } catch (BadNoteFormatException e) {
+ System.out.println(e.getMessage());
+ }
+ bone = element.getStringAttribute("bone");
+ range = (float) high - low;
+ }
+ public void noteOnReceived(Note n){
+ if(n.getChannel() != channel) return;
+ int pitch = n.getPitch();
+ if(pitch < low ) return;
+ if(pitch > high) return;
+ float length = 1f - ((float)((pitch - low)) / range);
+ Controller.getInstance().animateBone(bone, length);
+ }
+}
diff --git a/src/animata/model/Skeleton.java b/src/animata/model/Skeleton.java
index e17c035..d5f94c7 100644
--- a/src/animata/model/Skeleton.java
+++ b/src/animata/model/Skeleton.java
@@ -46,7 +46,7 @@ private void setInitialConditions() {
dst = vd;
- float vdnorm = vd / (bone.attachRadiusMult * bone.dOrig * .5f);
+ float vdnorm = vd / (bone.radius * bone.size * .5f);
if (vdnorm >= 1)
{
@@ -74,23 +74,18 @@ private void assignAttributes(XMLElement element) {
public class Bone {
- private static final float BONE_DEFAULT_DAMP = 0.5f;
- private Joint j0;
- private Joint j1;
- private float stiffness;
+ public Joint j0;
+ public Joint j1;
private float scale;
private float maxScale;
private float minScale;
public Float tempo;
- private float radius;
- private boolean selected;
- private float size;
private float time;
private AttachedVertex[] attachedVertices;
private String name;
- private float damp;
- public float dOrig;
- private float attachRadiusMult;
+ private float stiffness;
+ public float size;
+ private float radius;
private float falloff;
public Bone(XMLElement element) {
@@ -102,17 +97,6 @@ public Bone(XMLElement element) {
}
private void setInitialConditions() {
- damp = BONE_DEFAULT_DAMP;
-
- float x0 = j0.x;
- float y0 = j0.y;
- float x1 = j1.x;
- float y1 = j1.y;
-
- dOrig = PApplet.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
-
-
- attachRadiusMult = 1.0f;
falloff = 1.0f;
}
@@ -128,7 +112,6 @@ private void assignAttributes(XMLElement element) {
tempo = element.getFloatAttribute("tempo");
time = element.getFloatAttribute("time");
size = element.getFloatAttribute("size");
- selected = element.getIntAttribute("selected") == 1;
radius = element.getFloatAttribute("radius");
}
@@ -148,23 +131,15 @@ public void simulate() {
time += tempo / AnimataPlayback.timeDivision; // FIXME
animateScale(0.5f + PApplet.sin(time) * 0.5f);
}
-
- float x0 = j0.x;
- float y0 = j0.y;
- float x1 = j1.x;
- float y1 = j1.y;
-
- float dx = (x1 - x0);
- float dy = (y1 - y0);
+ float dx = (j1.x - j0.x);
+ float dy = (j1.y - j0.y);
float dCurrent = PApplet.sqrt(dx*dx + dy*dy);
-// if (dCurrent > FLT_EPSILON)
-// {
- dx /= dCurrent;
- dy /= dCurrent;
-// }
+ dx /= dCurrent;
+ dy /= dCurrent;
+
- float m = ((dOrig * scale) - dCurrent) * damp;
+ float m = ((size * scale) - dCurrent) * stiffness;
if (!j0.fixed )
{
@@ -234,11 +209,11 @@ public void setScale(Float value) {
public class Joint {
- private float x;
- private float y;
- private boolean fixed;
+ public float x;
+ public float y;
+ public boolean fixed;
private boolean selected;
- private String name;
+ public String name;
public Joint(XMLElement element) {
name = element.getStringAttribute("name","");
@@ -256,8 +231,8 @@ public void simulate() {
}
- private Joint[] joints;
- private Bone[] bones;
+ public Joint[] joints;
+ public Bone[] bones;
private final Mesh mesh;
private static ArrayList allBones = new ArrayList();
diff --git a/src/test/TestScene.java b/src/test/TestScene.java
new file mode 100644
index 0000000..7d28365
--- /dev/null
+++ b/src/test/TestScene.java
@@ -0,0 +1,41 @@
+package test;
+
+import microkontrol.MicroKontrol;
+import processing.core.PApplet;
+import animata.Animata;
+import animata.AnimataPlayback;
+
+public class TestScene extends PApplet {
+ private AnimataPlayback playback;
+ private MicroKontrol mk;
+
+ public void setup() {
+ size(950, 614, OPENGL);
+ Animata.startOSC(this);
+ playback = new AnimataPlayback(this);
+
+ playback.loadSet("set.xml");
+ playback.debug();
+ MicroKontrol.init(this);
+ mk = MicroKontrol.getInstance();
+ }
+
+ public void draw() {
+ if (keyPressed) {
+ if (keyCode == DOWN) playback.panCameraY(10);
+ if (keyCode == UP) playback.panCameraY(-10);
+ }
+ playback.panCameraX(mk.joystick.getX() * 30);
+ playback.zoomCamera(mk.joystick.getY() * 30);
+ background(255);
+
+ playback.draw();
+
+ }
+
+ public static void main(String[] args) {
+ PApplet.main(new String[] { "--bgcolor=#c0c0c0", "TestScene" });
+
+ }
+
+}