Lots of code cleanup, removed Panda code, all grizzly, cleaning up mapping
[SugarCubes.git] / _Grizzly.pde
1 /**
2 * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
3 *
4 * //\\ //\\ //\\ //\\
5 * ///\\\ ///\\\ ///\\\ ///\\\
6 * \\\/// \\\/// \\\/// \\\///
7 * \\// \\// \\// \\//H
8 *
9 * EXPERTS ONLY!! EXPERTS ONLY!!
10 *
11 * If you are an artist, you may ignore this file! It just sets
12 * up the framework to run the patterns. Should not need modification
13 * for general animation work.
14 */
15
16 GrizzlyOutput[] buildGrizzlies() throws SocketException, UnknownHostException {
17 return new GrizzlyOutput[] {
18 new GrizzlyOutput(lx, "192.168.88.100", 6, 5, 6, 7, 7, 8, 1, 2, 4, 3, 11, 10, 9, 9, 12, 13),
19 new GrizzlyOutput(lx, "192.168.88.101", 25, 23, 24, 43, 45, 44, 1, 1, 1, 1, 1, 41, 42, 21, 20, 22),
20 new GrizzlyOutput(lx, "192.168.88.104", 26, 28, 27, 19, 18, 17, 1, 1, 18, 19, 15, 16, 14, 29, 30, 31),
21 new GrizzlyOutput(lx, "192.168.88.105", 1, 1, 1, 39, 38, 40, 34, 35, 33, 32, 37, 37, 1, 1, 1, 1),
22 };
23 }
24
25 class GrizzlyOutput extends LXDatagramOutput {
26
27 public final String ipAddress;
28
29 private int frameNumber = 0;
30
31 GrizzlyOutput(LX lx, String ipAddress, int ... cubeIndices) throws UnknownHostException, SocketException {
32 super(lx);
33 this.ipAddress = ipAddress;
34 int channelNum = 0;
35 for (int rawCubeIndex : cubeIndices) {
36 if (rawCubeIndex > 0) {
37 Cube cube = model.getCubeByRawIndex(rawCubeIndex);
38 addDatagram(new GrizzlyDatagram(this, channelNum, cube).setAddress(ipAddress));
39 }
40 ++channelNum;
41 }
42 this.enabled.setValue(false);
43 }
44
45 protected void beforeSend(int[] colors) {
46 ++frameNumber;
47 }
48
49 public int getFrameNumber() {
50 return this.frameNumber;
51 }
52 }
53
54 static class GrizzlyDatagram extends LXDatagram {
55
56 private static byte[] oscString(String s) {
57 int len = s.length();
58 int padding = (4 - ((len + 1) % 4)) % 4;
59 byte[] bytes = new byte[len + 1 + padding];
60 System.arraycopy(s.getBytes(), 0, bytes, 0, len);
61 for (int i = len; i < bytes.length; ++i) {
62 bytes[i] = 0;
63 }
64 return bytes;
65 }
66
67 private static int oscIntCopy(int i, byte[] buffer, int pos) {
68 buffer[pos] = (byte) ((i >> 24) & 0xff);
69 buffer[pos + 1] = (byte) ((i >> 16) & 0xff);
70 buffer[pos + 2] = (byte) ((i >> 8) & 0xff);
71 buffer[pos + 3] = (byte) (i & 0xff);
72 return 4;
73 }
74
75 private final static int[] STRIP_ORDERING = new int[] { 9, 8, 11, 5, 4, 7, 6, 10, 14, 2, 1, 0, 3, 13, 12, 15 };
76
77 private static int[] cubePointIndices(Cube cube) {
78 int[] pointIndices = new int[Cube.POINTS_PER_CUBE - 2];
79 int pi = 0;
80 for (int stripIndex : STRIP_ORDERING) {
81 Strip strip = cube.strips.get(stripIndex);
82 int stripLen = ((stripIndex == 9) || (stripIndex == 15)) ? 15 : 16;
83 for (int i = stripLen-1; i >= 0; --i) {
84 pointIndices[pi++] = strip.points.get(i).index;
85 }
86 }
87 return pointIndices;
88 }
89
90 private final static byte[] OSC_ADDRESS = oscString("/shady/pointbuffer");
91 private final static byte[] OSC_TYPETAG = oscString(",iiiiibi");
92
93 private final static int HEADER_LENGTH = OSC_ADDRESS.length + OSC_TYPETAG.length + 24;
94 private final static int FOOTER_LENGTH = 4;
95
96 private final static int OSC_PORT = 779;
97
98 private GrizzlyOutput output;
99
100 private int[] pointIndices;
101
102 private final int frameNumberPos;
103
104 private final int dataPos;
105
106 public GrizzlyDatagram(GrizzlyOutput output, int channelNum, Cube cube) {
107 this(output, channelNum, cubePointIndices(cube));
108 }
109
110 public GrizzlyDatagram(GrizzlyOutput output, int channelNum, int[] pointIndices) {
111 super(HEADER_LENGTH + 4*pointIndices.length + FOOTER_LENGTH);
112 setPort(OSC_PORT);
113
114 this.output = output;
115 this.pointIndices = pointIndices;
116 int dataLength = 4*pointIndices.length;
117
118 int pos = 0;
119
120 // OSC address
121 System.arraycopy(OSC_ADDRESS, 0, this.buffer, pos, OSC_ADDRESS.length);
122 pos += OSC_ADDRESS.length;
123
124 // OSC typetag
125 System.arraycopy(OSC_TYPETAG, 0, this.buffer, pos, OSC_TYPETAG.length);
126 pos += OSC_TYPETAG.length;
127 this.frameNumberPos = pos;
128 pos += oscIntCopy(0, this.buffer, pos); // placeholder for frame number
129 pos += oscIntCopy(0xDEADBEEF, this.buffer, pos);
130 pos += oscIntCopy(channelNum, this.buffer, pos);
131 pos += oscIntCopy(0xFEEDBEEF, this.buffer, pos);
132 pos += oscIntCopy(dataLength, this.buffer, pos);
133 pos += oscIntCopy(dataLength, this.buffer, pos);
134 this.dataPos = pos;
135
136 // end header
137 oscIntCopy(0xBEFFFFEB, this.buffer, this.buffer.length - 4);
138 }
139
140 void onSend(int[] colors) {
141 oscIntCopy(this.output.getFrameNumber(), this.buffer, frameNumberPos);
142 int dataIndex = this.dataPos;
143 for (int index : this.pointIndices) {
144 color c = (index >= 0) ? colors[index] : 0;
145 this.buffer[dataIndex] = (byte) 0; // unused, alpha
146 this.buffer[dataIndex + 1] = (byte) ((c >> 16) & 0xff); // r
147 this.buffer[dataIndex + 2] = (byte) ((c >> 8) & 0xff); // g
148 this.buffer[dataIndex + 3] = (byte) (c & 0xff); // b
149 dataIndex += 4;
150 }
151 }
152 }