summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile25
-rw-r--r--bin/shader_minifier.exebin0 -> 2002944 bytes
-rw-r--r--src/base.h35
-rw-r--r--src/config.json41
-rw-r--r--src/device.c522
-rw-r--r--src/device.h54
-rw-r--r--src/shader.frag185
-rw-r--r--src/sync.h55
-rw-r--r--src/tiny.c216
-rw-r--r--src/track.c121
-rw-r--r--src/track.h47
11 files changed, 1301 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6c2b310
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,25 @@
+.PHONY: clean
+DEBUG=false
+CFLAGS := -fno-plt -O1 -std=gnu11 -nostartfiles -Wall -Wextra
+CFLAGS += -fno-stack-protector -fno-stack-check -fno-unwind-tables -fno-asynchronous-unwind-tables -fomit-frame-pointer
+CFLAGS += -no-pie -fno-pic -fno-PIE -fno-PIC -march=core2 -ffunction-sections -fdata-sections
+
+OBJS = track.o device.o tiny.o
+LIBS = `pkg-config --cflags gtk+-3.0` -lGL -lgtk-3 -lgdk-3 -lgobject-2.0
+
+all: tiny.elf tiny
+
+clean:
+ rm -f src/shader.h tiny.elf tiny $(OBJS)
+
+src/shader.h: src/shader.frag Makefile
+ mono ./bin/shader_minifier.exe --preserve-externals src/shader.frag -o ./src/shader.h
+
+%.o: src/%.c
+ gcc -c $< $(LIBS) $(CFLAGS)
+
+tiny.elf: src/shader.h $(OBJS)
+ gcc -o $@ $< $(LIBS) $(CFLAGS) $(OBJS)
+
+tiny: tiny.elf
+ strip -o $@ $< && upx --best $@
diff --git a/bin/shader_minifier.exe b/bin/shader_minifier.exe
new file mode 100644
index 0000000..71d36b7
--- /dev/null
+++ b/bin/shader_minifier.exe
Binary files differ
diff --git a/src/base.h b/src/base.h
new file mode 100644
index 0000000..44683c9
--- /dev/null
+++ b/src/base.h
@@ -0,0 +1,35 @@
+#ifndef SYNC_BASE_H
+#define SYNC_BASE_H
+
+#ifdef _MSC_VER
+ #define _CRT_SECURE_NO_WARNINGS 1
+ #define _CRT_NONSTDC_NO_DEPRECATE 1
+#endif
+
+#include <stddef.h>
+
+/* configure inline keyword */
+#if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)) && !defined(__cplusplus)
+ #if defined(_MSC_VER) || defined(__GNUC__) || defined(__SASC)
+ #define inline __inline
+ #else
+ /* compiler does not support inline */
+ #define inline
+ #endif
+#endif
+
+/* configure lacking CRT features */
+#ifdef _MSC_VER
+ #if _MSC_VER < 1900
+ #define snprintf _snprintf
+ #endif
+ /* int is 32-bit for both x86 and x64 */
+ typedef unsigned int uint32_t;
+ #define UINT32_MAX UINT_MAX
+#elif defined(__GNUC__)
+ #include <stdint.h>
+#elif defined(M68000)
+ typedef unsigned int uint32_t;
+#endif
+
+#endif /* SYNC_BASE_H */
diff --git a/src/config.json b/src/config.json
new file mode 100644
index 0000000..30ae9b9
--- /dev/null
+++ b/src/config.json
@@ -0,0 +1,41 @@
+{
+ "window":{ // default window size / state, if there's a setup dialog, it will override it
+ "width":1920,
+ "height":1080,
+ "fullscreen":true
+ },
+ "font":{ // all paths in the file are also relative to the binary, but again, can be absolute paths if that's more convenient
+ "file":"Input-Regular_(InputMono-Medium).ttf",
+ "size":16
+ },
+ "rendering":{
+ "fftSmoothFactor": 0.9, // 0.0 means there's no smoothing at all, 1.0 means the FFT is completely smoothed flat
+ "fftAmplification": 1.0, // 1.0 means no change, larger values will result in brighter/stronger bands, smaller values in darker/weaker ones
+ },
+ "textures":{ // the keys below will become the shader variable names
+ "texChecker":"textures/checker.png",
+ "texNoise":"textures/noise.png",
+ "texTex1":"textures/tex1.jpg"
+ },
+ "gui":{
+ "outputHeight": 200,
+ "opacity": 192, // 255 means the editor occludes the effect completely, 0 means the editor is fully transparent
+ "texturePreviewWidth": 64,
+ "spacesForTabs": false,
+ "tabSize": 8,
+ "visibleWhitespace": true,
+ "autoIndent": "smart", // can be "none", "preserve" or "smart"
+ },
+ "midi":{ // the keys below will become the shader variable names, the values are the CC numbers
+ "fMidiKnob": 16, // e.g. this would be CC#16, i.e. by default the leftmost knob on a nanoKONTROL 2
+ },
+ // this section is if you want to enable NDI streaming; otherwise just ignore it
+ "ndi":{
+ "enabled": true,
+ "connectionString": "<ndi_product something=\"123\"/>", // metadata sent to the receiver; completely optional
+ "identifier": "hello!", // additional string to the device name; helps source discovery/identification in the receiver if there are multiple sources on the network
+ "frameRate": 60.0, // frames per second
+ "progressive": true, // progressive or interleaved?
+ },
+ "postExitCmd":"copy_to_dropbox.bat" // this command gets ran when you quit Bonzomatic, and the shader filename gets passed to it as first parameter. Use this to take regular backups.
+}
diff --git a/src/device.c b/src/device.c
new file mode 100644
index 0000000..879d2a0
--- /dev/null
+++ b/src/device.c
@@ -0,0 +1,522 @@
+#include "device.h"
+#include "track.h"
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+static int find_track(struct sync_device *d, const char *name)
+{
+ int i;
+ for (i = 0; i < (int)d->num_tracks; ++i)
+ if (!strcmp(name, d->tracks[i]->name))
+ return i;
+ return -1; /* not found */
+}
+
+static const char *path_encode(const char *path)
+{
+ static char temp[FILENAME_MAX];
+ int i, pos = 0;
+ int path_len = (int)strlen(path);
+ for (i = 0; i < path_len; ++i) {
+ int ch = path[i];
+ if (isalnum(ch) || ch == '.' || ch == '_') {
+ if (pos >= sizeof(temp) - 1)
+ break;
+
+ temp[pos++] = (char)ch;
+ } else {
+ if (pos >= sizeof(temp) - 3)
+ break;
+
+ temp[pos++] = '-';
+ temp[pos++] = "0123456789ABCDEF"[(ch >> 4) & 0xF];
+ temp[pos++] = "0123456789ABCDEF"[ch & 0xF];
+ }
+ }
+
+ temp[pos] = '\0';
+ return temp;
+}
+
+static const char *sync_track_path(const char *base, const char *name)
+{
+ static char temp[FILENAME_MAX];
+ strncpy(temp, base, sizeof(temp) - 1);
+ temp[sizeof(temp) - 1] = '\0';
+ strncat(temp, "_", sizeof(temp) - strlen(temp) - 1);
+ strncat(temp, path_encode(name), sizeof(temp) - strlen(temp) - 1);
+ strncat(temp, ".track", sizeof(temp) - strlen(temp) - 1);
+ return temp;
+}
+
+#ifndef SYNC_PLAYER
+
+#define CLIENT_GREET "hello, synctracker!"
+#define SERVER_GREET "hello, demo!"
+
+enum {
+ SET_KEY = 0,
+ DELETE_KEY = 1,
+ GET_TRACK = 2,
+ SET_ROW = 3,
+ PAUSE = 4,
+ SAVE_TRACKS = 5
+};
+
+static inline int socket_poll(SOCKET socket)
+{
+ struct timeval to = { 0, 0 };
+ fd_set fds;
+
+ FD_ZERO(&fds);
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4127)
+#endif
+ FD_SET(socket, &fds);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ return select((int)socket + 1, &fds, NULL, NULL, &to) > 0;
+}
+
+static inline int xsend(SOCKET s, const void *buf, size_t len, int flags)
+{
+#ifdef WIN32
+ assert(len <= INT_MAX);
+ return send(s, (const char *)buf, (int)len, flags) != (int)len;
+#else
+ return send(s, (const char *)buf, len, flags) != (int)len;
+#endif
+}
+
+static inline int xrecv(SOCKET s, void *buf, size_t len, int flags)
+{
+#ifdef WIN32
+ assert(len <= INT_MAX);
+ return recv(s, (char *)buf, (int)len, flags) != (int)len;
+#else
+ return recv(s, (char *)buf, len, flags) != (int)len;
+#endif
+}
+
+#ifdef USE_AMITCP
+static struct Library *socket_base = NULL;
+#endif
+
+static SOCKET server_connect(const char *host, unsigned short nport)
+{
+ SOCKET sock = INVALID_SOCKET;
+#ifdef USE_GETADDRINFO
+ struct addrinfo *addr, *curr;
+ char port[6];
+#else
+ struct hostent *he;
+ char **ap;
+#endif
+
+#ifdef WIN32
+ static int need_init = 1;
+ if (need_init) {
+ WSADATA wsa;
+ if (WSAStartup(MAKEWORD(2, 0), &wsa))
+ return INVALID_SOCKET;
+ need_init = 0;
+ }
+#elif defined(USE_AMITCP)
+ if (!socket_base) {
+ socket_base = OpenLibrary("bsdsocket.library", 4);
+ if (!socket_base)
+ return INVALID_SOCKET;
+ }
+#endif
+
+#ifdef USE_GETADDRINFO
+
+ snprintf(port, sizeof(port), "%u", nport);
+ if (getaddrinfo(host, port, 0, &addr) != 0)
+ return INVALID_SOCKET;
+
+ for (curr = addr; curr; curr = curr->ai_next) {
+ int family = curr->ai_family;
+ struct sockaddr *sa = curr->ai_addr;
+ int sa_len = (int)curr->ai_addrlen;
+
+#else
+
+ he = gethostbyname(host);
+ if (!he)
+ return INVALID_SOCKET;
+
+ for (ap = he->h_addr_list; *ap; ++ap) {
+ int family = he->h_addrtype;
+ struct sockaddr_in sin;
+ struct sockaddr *sa = (struct sockaddr *)&sin;
+ int sa_len = sizeof(*sa);
+
+ sin.sin_family = he->h_addrtype;
+ sin.sin_port = htons(nport);
+ memcpy(&sin.sin_addr, *ap, he->h_length);
+ memset(&sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+#endif
+
+ sock = socket(family, SOCK_STREAM, 0);
+ if (sock == INVALID_SOCKET)
+ continue;
+
+ if (connect(sock, sa, sa_len) >= 0) {
+ char greet[128];
+
+ if (xsend(sock, CLIENT_GREET, strlen(CLIENT_GREET), 0) ||
+ xrecv(sock, greet, strlen(SERVER_GREET), 0)) {
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ continue;
+ }
+
+ if (!strncmp(SERVER_GREET, greet, strlen(SERVER_GREET)))
+ break;
+ }
+
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
+
+#ifdef USE_GETADDRINFO
+ freeaddrinfo(addr);
+#endif
+
+ return sock;
+}
+
+#else
+
+void sync_set_io_cb(struct sync_device *d, struct sync_io_cb *cb)
+{
+ d->io_cb.open = cb->open;
+ d->io_cb.read = cb->read;
+ d->io_cb.close = cb->close;
+}
+
+#endif
+
+#ifdef NEED_STRDUP
+static inline char *rocket_strdup(const char *str)
+{
+ char *ret = malloc(strlen(str) + 1);
+ if (ret)
+ strcpy(ret, str);
+ return ret;
+}
+#define strdup rocket_strdup
+#endif
+
+struct sync_device *sync_create_device(const char *base)
+{
+ struct sync_device *d = malloc(sizeof(*d));
+ if (!d)
+ return NULL;
+
+ d->base = strdup(path_encode(base));
+ if (!d->base) {
+ free(d);
+ return NULL;
+ }
+
+ d->tracks = NULL;
+ d->num_tracks = 0;
+
+#ifndef SYNC_PLAYER
+ d->row = -1;
+ d->sock = INVALID_SOCKET;
+#endif
+
+ d->io_cb.open = (void *(*)(const char *, const char *))fopen;
+ d->io_cb.read = (size_t (*)(void *, size_t, size_t, void *))fread;
+ d->io_cb.close = (int (*)(void *))fclose;
+
+ return d;
+}
+
+void sync_destroy_device(struct sync_device *d)
+{
+ int i;
+
+#ifndef SYNC_PLAYER
+ if (d->sock != INVALID_SOCKET)
+ closesocket(d->sock);
+#endif
+
+ for (i = 0; i < (int)d->num_tracks; ++i) {
+ free(d->tracks[i]->name);
+ free(d->tracks[i]->keys);
+ free(d->tracks[i]);
+ }
+ free(d->tracks);
+ free(d->base);
+ free(d);
+
+#if defined(USE_AMITCP) && !defined(SYNC_PLAYER)
+ if (socket_base) {
+ CloseLibrary(socket_base);
+ socket_base = NULL;
+ }
+#endif
+}
+
+static int read_track_data(struct sync_device *d, struct sync_track *t)
+{
+ int i;
+ void *fp = d->io_cb.open(sync_track_path(d->base, t->name), "rb");
+ if (!fp)
+ return -1;
+
+ d->io_cb.read(&t->num_keys, sizeof(int), 1, fp);
+ t->keys = malloc(sizeof(struct track_key) * t->num_keys);
+ if (!t->keys)
+ return -1;
+
+ for (i = 0; i < (int)t->num_keys; ++i) {
+ struct track_key *key = t->keys + i;
+ char type;
+ d->io_cb.read(&key->row, sizeof(int), 1, fp);
+ d->io_cb.read(&key->value, sizeof(float), 1, fp);
+ d->io_cb.read(&type, sizeof(char), 1, fp);
+ key->type = (enum key_type)type;
+ }
+
+ d->io_cb.close(fp);
+ return 0;
+}
+
+static int save_track(const struct sync_track *t, const char *path)
+{
+ int i;
+ FILE *fp = fopen(path, "wb");
+ if (!fp)
+ return -1;
+
+ fwrite(&t->num_keys, sizeof(int), 1, fp);
+ for (i = 0; i < (int)t->num_keys; ++i) {
+ char type = (char)t->keys[i].type;
+ fwrite(&t->keys[i].row, sizeof(int), 1, fp);
+ fwrite(&t->keys[i].value, sizeof(float), 1, fp);
+ fwrite(&type, sizeof(char), 1, fp);
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+void sync_save_tracks(const struct sync_device *d)
+{
+ int i;
+ for (i = 0; i < (int)d->num_tracks; ++i) {
+ const struct sync_track *t = d->tracks[i];
+ save_track(t, sync_track_path(d->base, t->name));
+ }
+}
+
+#ifndef SYNC_PLAYER
+
+static int fetch_track_data(struct sync_device *d, struct sync_track *t)
+{
+ unsigned char cmd = GET_TRACK;
+ uint32_t name_len;
+
+ assert(strlen(t->name) <= UINT32_MAX);
+ name_len = htonl((uint32_t)strlen(t->name));
+
+ /* send request data */
+ if (xsend(d->sock, (char *)&cmd, 1, 0) ||
+ xsend(d->sock, (char *)&name_len, sizeof(name_len), 0) ||
+ xsend(d->sock, t->name, (int)strlen(t->name), 0))
+ {
+ closesocket(d->sock);
+ d->sock = INVALID_SOCKET;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int handle_set_key_cmd(SOCKET sock, struct sync_device *data)
+{
+ uint32_t track, row;
+ union {
+ float f;
+ uint32_t i;
+ } v;
+ struct track_key key;
+ unsigned char type;
+
+ if (xrecv(sock, (char *)&track, sizeof(track), 0) ||
+ xrecv(sock, (char *)&row, sizeof(row), 0) ||
+ xrecv(sock, (char *)&v.i, sizeof(v.i), 0) ||
+ xrecv(sock, (char *)&type, 1, 0))
+ return -1;
+
+ track = ntohl(track);
+ v.i = ntohl(v.i);
+
+ key.row = ntohl(row);
+ key.value = v.f;
+
+ assert(type < KEY_TYPE_COUNT);
+ assert(track < data->num_tracks);
+ key.type = (enum key_type)type;
+ return sync_set_key(data->tracks[track], &key);
+}
+
+static int handle_del_key_cmd(SOCKET sock, struct sync_device *data)
+{
+ uint32_t track, row;
+
+ if (xrecv(sock, (char *)&track, sizeof(track), 0) ||
+ xrecv(sock, (char *)&row, sizeof(row), 0))
+ return -1;
+
+ track = ntohl(track);
+ row = ntohl(row);
+
+ assert(track < data->num_tracks);
+ return sync_del_key(data->tracks[track], row);
+}
+
+int sync_tcp_connect(struct sync_device *d, const char *host, unsigned short port)
+{
+ int i;
+ if (d->sock != INVALID_SOCKET)
+ closesocket(d->sock);
+
+ d->sock = server_connect(host, port);
+ if (d->sock == INVALID_SOCKET)
+ return -1;
+
+ for (i = 0; i < (int)d->num_tracks; ++i) {
+ free(d->tracks[i]->keys);
+ d->tracks[i]->keys = NULL;
+ d->tracks[i]->num_keys = 0;
+ }
+
+ for (i = 0; i < (int)d->num_tracks; ++i) {
+ if (fetch_track_data(d, d->tracks[i])) {
+ closesocket(d->sock);
+ d->sock = INVALID_SOCKET;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int sync_connect(struct sync_device *d, const char *host, unsigned short port)
+{
+ return sync_tcp_connect(d, host, port);
+}
+
+int sync_update(struct sync_device *d, int row, struct sync_cb *cb,
+ void *cb_param)
+{
+ if (d->sock == INVALID_SOCKET)
+ return -1;
+
+ /* look for new commands */
+ while (socket_poll(d->sock)) {
+ unsigned char cmd = 0, flag;
+ uint32_t new_row;
+ if (xrecv(d->sock, (char *)&cmd, 1, 0))
+ goto sockerr;
+
+ switch (cmd) {
+ case SET_KEY:
+ if (handle_set_key_cmd(d->sock, d))
+ goto sockerr;
+ break;
+ case DELETE_KEY:
+ if (handle_del_key_cmd(d->sock, d))
+ goto sockerr;
+ break;
+ case SET_ROW:
+ if (xrecv(d->sock, (char *)&new_row, sizeof(new_row), 0))
+ goto sockerr;
+ if (cb && cb->set_row)
+ cb->set_row(cb_param, ntohl(new_row));
+ break;
+ case PAUSE:
+ if (xrecv(d->sock, (char *)&flag, 1, 0))
+ goto sockerr;
+ if (cb && cb->pause)
+ cb->pause(cb_param, flag);
+ break;
+ case SAVE_TRACKS:
+ sync_save_tracks(d);
+ break;
+ default:
+ fprintf(stderr, "unknown cmd: %02x\n", cmd);
+ goto sockerr;
+ }
+ }
+
+ if (cb && cb->is_playing && cb->is_playing(cb_param)) {
+ if (d->row != row && d->sock != INVALID_SOCKET) {
+ unsigned char cmd = SET_ROW;
+ uint32_t nrow = htonl(row);
+ if (xsend(d->sock, (char*)&cmd, 1, 0) ||
+ xsend(d->sock, (char*)&nrow, sizeof(nrow), 0))
+ goto sockerr;
+ d->row = row;
+ }
+ }
+ return 0;
+
+sockerr:
+ closesocket(d->sock);
+ d->sock = INVALID_SOCKET;
+ return -1;
+}
+
+#endif /* !defined(SYNC_PLAYER) */
+
+static int create_track(struct sync_device *d, const char *name)
+{
+ struct sync_track *t;
+ assert(find_track(d, name) < 0);
+
+ t = malloc(sizeof(*t));
+ t->name = strdup(name);
+ t->keys = NULL;
+ t->num_keys = 0;
+
+ d->num_tracks++;
+ d->tracks = realloc(d->tracks, sizeof(d->tracks[0]) * d->num_tracks);
+ d->tracks[d->num_tracks - 1] = t;
+
+ return (int)d->num_tracks - 1;
+}
+
+const struct sync_track *sync_get_track(struct sync_device *d,
+ const char *name)
+{
+ struct sync_track *t;
+ int idx = find_track(d, name);
+ if (idx >= 0)
+ return d->tracks[idx];
+
+ idx = create_track(d, name);
+ t = d->tracks[idx];
+
+#ifndef SYNC_PLAYER
+ if (d->sock != INVALID_SOCKET)
+ fetch_track_data(d, t);
+ else
+#endif
+ read_track_data(d, t);
+
+ return t;
+}
diff --git a/src/device.h b/src/device.h
new file mode 100644
index 0000000..93b9802
--- /dev/null
+++ b/src/device.h
@@ -0,0 +1,54 @@
+#ifndef SYNC_DEVICE_H
+#define SYNC_DEVICE_H
+
+#include "base.h"
+#include "sync.h"
+
+#ifndef SYNC_PLAYER
+
+/* configure socket-stack */
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #define USE_GETADDRINFO
+ #ifndef NOMINMAX
+ #define NOMINMAX
+ #endif
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #include <windows.h>
+ #include <limits.h>
+#elif defined(USE_AMITCP)
+ #include <sys/socket.h>
+ #include <proto/exec.h>
+ #include <proto/socket.h>
+ #include <netdb.h>
+ #define SOCKET int
+ #define INVALID_SOCKET -1
+ #define select(n,r,w,e,t) WaitSelect(n,r,w,e,t,0)
+ #define closesocket(x) CloseSocket(x)
+#else
+ #include <sys/socket.h>
+ #include <sys/time.h>
+ #include <netinet/in.h>
+ #include <netdb.h>
+ #include <unistd.h>
+ #define SOCKET int
+ #define INVALID_SOCKET -1
+ #define closesocket(x) close(x)
+#endif
+
+#endif /* !defined(SYNC_PLAYER) */
+
+struct sync_device {
+ char *base;
+ struct sync_track **tracks;
+ size_t num_tracks;
+
+#ifndef SYNC_PLAYER
+ int row;
+ SOCKET sock;
+#endif
+ struct sync_io_cb io_cb;
+};
+
+#endif /* SYNC_DEVICE_H */
diff --git a/src/shader.frag b/src/shader.frag
new file mode 100644
index 0000000..b4cabf9
--- /dev/null
+++ b/src/shader.frag
@@ -0,0 +1,185 @@
+#version 410 core
+
+uniform vec2 u_resolution; // viewport resolution (in pixels)
+uniform float u_time; // in seconds
+uniform float u_xrot;
+uniform float u_yrot;
+uniform float u_zrot;
+uniform sampler1D texFFT;
+
+layout(location = 0) out vec4 out_color; // out_color must be written in order to see anything
+
+const int MAX_STEP = 255;
+const float MIN_DIST = 0.0;
+const float MAX_DIST = 100.0;
+const float EPSILON = 0.0001;
+
+float f(float x, float z)
+{
+ return 2.0;
+}
+
+
+// transformations
+mat4 rotateX(float theta)
+{
+ float c = cos(theta);
+ float s = sin(theta);
+
+ return mat4(
+ vec4(1, 0, 0, 0),
+ vec4(0, c, s, 0),
+ vec4(0, -s, c, 0),
+ vec4(0, 0, 0, 1)
+ );
+}
+
+// transformations
+mat4 rotateY(float theta)
+{
+ float c = cos(theta);
+ float s = sin(theta);
+
+ return mat4(
+ vec4(c, 0, s, 0),
+ vec4(0, 1, 0, 0),
+ vec4(-s, 0, c, 0),
+ vec4(0, 0, 0, 1)
+ );
+}
+
+// transformations
+mat4 rotateZ(float theta)
+{
+ float c = cos(theta);
+ float s = sin(theta);
+
+ return mat4(
+ vec4(c, s, 0, 0),
+ vec4(-s, c, 0, 0),
+ vec4(0, 0, 1, 0),
+ vec4(0, 0, 0, 1)
+ );
+}
+
+float tHeight(vec2 p)
+{
+ return smoothstep(0.0, 200.0, p.y*20000)*5;
+}
+
+float torusSDF( vec3 p, vec2 t )
+{
+ vec2 q = vec2(length(p.xz)-t.x,p.y);
+ float d1 = length(p)-0.4;
+ float d2 = tHeight(p.xy)*0.45;
+ return d1 + (d2/20);
+}
+
+vec3 transformScene(vec3 p)
+{
+ //p = (inverse(rotateX(u_time*3)) * vec4(p, 1.0)).xyz;
+ p = (inverse(rotateX(u_xrot)) *
+ inverse(rotateY(u_yrot)) *
+ inverse(rotateZ(u_zrot)) * vec4(p, 1.0)).xyz;
+ return p;
+}
+
+// Standard distance function for the whole scene!
+float sceneSDF(vec3 p)
+{
+ p = transformScene(p);
+ //p = p * vec3(1.0, sin(u_time),1.0);
+ return torusSDF(p, vec2(0.3, 0.1));
+}
+
+vec3 estNormal(vec3 p)
+{
+ return normalize(vec3(
+ sceneSDF(vec3(p.x + EPSILON, p.y, p.z)) - sceneSDF(vec3(p.x - EPSILON, p.y, p.z)),
+ sceneSDF(vec3(p.x, p.y + EPSILON, p.z)) - sceneSDF(vec3(p.x, p.y - EPSILON, p.z)),
+ sceneSDF(vec3(p.x, p.y, p.z + EPSILON)) - sceneSDF(vec3(p.x, p.y, p.z - EPSILON))
+ ));
+}
+
+vec3 rayDirection(float fov, vec2 size, vec2 fragCoord)
+{
+ vec2 xy = fragCoord - size / 2.0;
+ float z = size.y / tan(radians(fov) / 2.0);
+ return normalize(vec3(xy, -z));
+}
+
+// Raymarcher!
+float march(vec3 eye, vec3 dir, float start, float end)
+{
+ float depth = start;
+ for (int i=0; i<MAX_STEP; i++) {
+ float dist = sceneSDF(eye + depth * dir);
+ if (dist < 0.0001) {
+ return depth; // we're inside the scene
+ }
+ depth+=dist;
+ if (depth >= end) {
+ return end; // gone too far!
+ }
+ }
+}
+
+// Does something related to phong for each light that we want
+vec3 phongLightContrib(vec3 kd, vec3 ks, float alpha, vec3 p, vec3 eye, vec3 lightPos, vec3 lightInt)
+{
+ vec3 N = estNormal(p);
+ vec3 L = normalize(lightPos - p);
+ vec3 V = normalize(eye - p);
+ vec3 R = normalize(reflect(-L, N));
+
+ float dotLN = dot(L, N);
+ float dotRV = dot(R, V);
+
+ if (dotLN < 0.0) {return vec3(0.0);}
+ if (dotRV < 0.0) {return lightInt * (kd * dotLN);}
+ return lightInt * (kd * dotLN + ks * pow(dotRV, alpha));
+}
+
+vec3 phong(vec3 ka, vec3 kd, vec3 ks, float alpha, vec3 p, vec3 eye)
+{
+ const vec3 ambientLux = vec3(0.3);
+ vec3 colour = ambientLux * ka;
+
+ vec3 light1Pos = vec3(-1.0, -1.2, 0.7);
+ vec3 light1Int = vec3(0.2);
+
+ vec3 light2Pos = vec3(0.8, 0.8, 1.2);
+ vec3 light2Int = vec3(0.55);
+
+ colour += phongLightContrib(kd, ks, alpha, p, eye, light1Pos, light1Int);
+ colour += phongLightContrib(kd, ks, alpha, p, eye, light2Pos, light2Int);
+ return colour;
+}
+
+void main(void)
+{
+ vec3 dir = rayDirection(45.0, u_resolution.xy, gl_FragCoord.xy);
+ vec3 eye = vec3(0.0,0.0,5.0);
+ float dist = march(eye, dir, MIN_DIST, MAX_DIST);
+
+ if (dist > MAX_DIST - EPSILON) {out_color = vec4(0.1); return;}
+
+ vec3 p = eye + dist * dir;
+ vec3 colour;
+
+ vec3 Ka = vec3(0.0, 0.729, 0.745)*texture(texFFT, 0.123).r*1000; // ambient reflection constant
+ vec3 Kd = vec3(0.0, 0.467, 0.745); // diffuse reflection constant
+ vec3 Ks = vec3(0.0, 0.0, 0.0); // specular reflection constant
+ float shininess = 1.0;
+ colour = phong(Ka, Kd, Ks, shininess, p, eye);
+
+ if (transformScene(p).y< 0) {
+ vec3 Ka = vec3(0.2); // ambient reflection constant
+ vec3 Kd = vec3(0.95, 0.2, 0.1); // diffuse reflection constant
+ vec3 Ks = vec3(0.95, 0.2, 0.1); // specular reflection constant
+ float shininess = 300.0;
+ colour = phong(Ka, Kd, Ks, shininess, p, eye);
+ }
+
+ out_color = vec4(colour, 1.0);
+}
diff --git a/src/sync.h b/src/sync.h
new file mode 100644
index 0000000..2d5df42
--- /dev/null
+++ b/src/sync.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2010 Contributors
+ * For conditions of distribution and use, see copyright notice in COPYING
+ */
+
+#ifndef SYNC_H
+#define SYNC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+#ifdef __GNUC__
+#define SYNC_DEPRECATED(msg) __attribute__ ((deprecated(msg)))
+#elif defined(_MSC_VER)
+#define SYNC_DEPRECATED(msg) __declspec(deprecated("is deprecated: " msg))
+#else
+#define SYNC_DEPRECATED(msg)
+#endif
+
+struct sync_device;
+struct sync_track;
+
+struct sync_device *sync_create_device(const char *);
+void sync_destroy_device(struct sync_device *);
+
+#ifndef SYNC_PLAYER
+struct sync_cb {
+ void (*pause)(void *, int);
+ void (*set_row)(void *, int);
+ int (*is_playing)(void *);
+};
+#define SYNC_DEFAULT_PORT 1338
+int sync_tcp_connect(struct sync_device *, const char *, unsigned short);
+int SYNC_DEPRECATED("use sync_tcp_connect instead") sync_connect(struct sync_device *, const char *, unsigned short);
+int sync_update(struct sync_device *, int, struct sync_cb *, void *);
+void sync_save_tracks(const struct sync_device *);
+#endif /* defined(SYNC_PLAYER) */
+
+struct sync_io_cb {
+ void *(*open)(const char *filename, const char *mode);
+ size_t (*read)(void *ptr, size_t size, size_t nitems, void *stream);
+ int (*close)(void *stream);
+};
+void sync_set_io_cb(struct sync_device *d, struct sync_io_cb *cb);
+
+const struct sync_track *sync_get_track(struct sync_device *, const char *);
+double sync_get_val(const struct sync_track *, double);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined(SYNC_H) */
diff --git a/src/tiny.c b/src/tiny.c
new file mode 100644
index 0000000..11b7e65
--- /dev/null
+++ b/src/tiny.c
@@ -0,0 +1,216 @@
+#define GL_GLEXT_PROTOTYPES why
+//#include<stdio.h>
+//#include<stdbool.h>
+//#include<stdlib.h>
+//#include<stdint.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <GL/glu.h>
+#include <GL/glext.h>
+#include "shader.h"
+#include "sync.h"
+static char* vshader = "#version 450\nvec2 y=vec2(1.,-1);vec4 x[4]={y.yyxx,y.xyxx,y.yxxx,y.xxxx};void main(){gl_Position=x[gl_VertexID];}";
+
+#define CANVAS_WIDTH 1920
+#define CANVAS_HEIGHT 1080
+//#define DEBUG
+
+GLuint vao;
+GLuint p;
+
+// <sync stuff
+int curtime_ms = 0;
+int is_playing = 1;
+int rps = 24;
+static struct sync_device *device;
+#if !defined(SYNC_PLAYER)
+static struct sync_cb cb;
+#endif
+static const char* s_trackNames[] = {"cam_x", "cam_y", "cam_z"};
+static const struct sync_track* s_tracks[3];
+
+static int row_to_ms_round(int row, float rps){
+ const float newTime = (float) row / rps;
+ return (floor(newTime * 1000.0f + 0.5f));
+}
+static float ms_to_row_f(int time_ms, float rps) {
+ return rps * ((float) time_ms) * 1.0f/1000.0f;
+}
+static int ms_to_row_round(int time_ms, float rps){
+ const float r = ms_to_row_f(time_ms, rps);
+ return (int) (floor(r + 0.5f));
+}
+#if !defined(SYNC_PLAYER)
+static void xpause(void* data, int flag)
+{
+ (void)data;
+ if (flag)
+ is_playing = 0;
+ else
+ is_playing = 1;
+}
+static void xset_row(void* data, int row)
+{
+ int newtime_ms = row_to_ms_round(row, rps);
+ curtime_ms = newtime_ms;
+ (void)data;
+}
+static int xis_playing(void* data)
+{
+ (void)data;
+ return is_playing;
+}
+#endif
+static int rocket_update(){
+ if(is_playing) curtime_ms += 16;
+ #if !defined( SYNC_PLAYER )
+ int row = ms_to_row_round(curtime_ms, rps);
+ if (sync_update(device,row,&cb,0))
+ sync_tcp_connect(device, "localhost", SYNC_DEFAULT_PORT);
+ #endif
+ return -1;
+}
+// sync stuff>
+
+static gboolean
+on_render (GtkGLArea *glarea, GdkGLContext *context)
+{
+ glUseProgram(p);
+ float row_f = ms_to_row_f(curtime_ms, rps);
+ glProgramUniform1f(p, 0, curtime_ms/1000.0f);
+ glProgramUniform1f(p, 1, sync_get_val(s_tracks[0], row_f));
+ glProgramUniform1f(p, 2, sync_get_val(s_tracks[1], row_f));
+ glProgramUniform1f(p, 3, sync_get_val(s_tracks[2], row_f));
+ glBindVertexArray(vao);
+ glVertexAttrib1f(0, 0);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ rocket_update();
+ return TRUE;
+}
+
+static void on_realize(GtkGLArea *glarea)
+{
+ gtk_gl_area_make_current(glarea);
+
+ device = sync_create_device("data/sync");
+ for (int i = 0; i < 3; ++i)
+ s_tracks[i] = sync_get_track(device, s_trackNames[i]);
+ #if !defined( SYNC_PLAYER )
+ sync_tcp_connect(device, "localhost", SYNC_DEFAULT_PORT);
+ cb.is_playing = xis_playing;
+ cb.pause = xpause;
+ cb.set_row = xset_row;
+ #endif
+
+ // compile shader
+ GLuint f = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(f, 1, &shader_frag, NULL);
+ glCompileShader(f);
+
+ #ifdef DEBUG
+ GLint isCompiled = 0;
+ glGetShaderiv(f, GL_COMPILE_STATUS, &isCompiled);
+ if(isCompiled == GL_FALSE) {
+ GLint maxLength = 0;
+ glGetShaderiv(f, GL_INFO_LOG_LENGTH, &maxLength);
+
+ char* error = malloc(maxLength);
+ glGetShaderInfoLog(f, maxLength, &maxLength, error);
+ printf("%s\n", error);
+
+ exit(-10);
+ }
+ #endif
+
+ GLuint v = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(v, 1, &vshader, NULL);
+ glCompileShader(v);
+
+ #ifdef DEBUG
+ GLint isCompiled2 = 0;
+ glGetShaderiv(v, GL_COMPILE_STATUS, &isCompiled2);
+ if(isCompiled2 == GL_FALSE) {
+ GLint maxLength = 0;
+ glGetShaderiv(v, GL_INFO_LOG_LENGTH, &maxLength);
+
+ char* error = malloc(maxLength);
+ glGetShaderInfoLog(v, maxLength, &maxLength, error);
+ printf("%s\n", error);
+
+ exit(-10);
+ }
+ #endif
+
+ // link shader
+ p = glCreateProgram();
+ glAttachShader(p,v);
+ glAttachShader(p,f);
+ glLinkProgram(p);
+
+ #ifdef DEBUG
+ GLint isLinked = 0;
+ glGetProgramiv(p, GL_LINK_STATUS, (int *)&isLinked);
+ if (isLinked == GL_FALSE) {
+ GLint maxLength = 0;
+ glGetProgramiv(p, GL_INFO_LOG_LENGTH, &maxLength);
+
+ char* error = malloc(maxLength);
+ glGetProgramInfoLog(p, maxLength, &maxLength,error);
+ printf("%s\n", error);
+
+ exit(-10);
+ }
+ #endif
+
+ glProgramUniform2f(p, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
+ glGenVertexArrays(1, &vao);
+
+ // if you want to continuously render the shader once per frame
+ GdkGLContext *context = gtk_gl_area_get_context(glarea);
+ GdkWindow *glwindow = gdk_gl_context_get_window(context);
+ GdkFrameClock *frame_clock = gdk_window_get_frame_clock(glwindow);
+
+ // Connect update signal:
+ g_signal_connect_swapped(frame_clock, "update", G_CALLBACK(gtk_gl_area_queue_render), glarea);
+
+ // Start updating:
+ gdk_frame_clock_begin_updating(frame_clock);
+}
+
+void _start() {
+ asm volatile("sub $8, %rsp\n");
+
+ typedef void (*voidWithOneParam)(int*);
+ voidWithOneParam gtk_init_one_param = (voidWithOneParam)gtk_init;
+ (*gtk_init_one_param)(NULL);
+
+ GtkWidget *win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ GtkWidget *glarea = gtk_gl_area_new();
+ gtk_container_add(GTK_CONTAINER(win), glarea);
+
+ g_signal_connect(win, "destroy", gtk_main_quit, NULL);
+ g_signal_connect(glarea, "realize", G_CALLBACK(on_realize), NULL);
+ g_signal_connect(glarea, "render", G_CALLBACK(on_render), NULL);
+
+ gtk_widget_show_all (win);
+
+ //gtk_window_fullscreen((GtkWindow*)win);
+ GdkWindow* window = gtk_widget_get_window(win);
+ GdkCursor* Cursor = gdk_cursor_new(GDK_BLANK_CURSOR);
+ gdk_window_set_cursor(window, Cursor);
+
+ gtk_main();
+
+ asm volatile(".intel_syntax noprefix");
+ asm volatile("push 231"); //exit_group
+ asm volatile("pop rax");
+ // asm volatile("xor edi, edi");
+ asm volatile("syscall");
+ asm volatile(".att_syntax prefix");
+ __builtin_unreachable();
+ // return 0;
+}
diff --git a/src/track.c b/src/track.c
new file mode 100644
index 0000000..e0bbe9c
--- /dev/null
+++ b/src/track.c
@@ -0,0 +1,121 @@
+#include <stdlib.h>
+#include <assert.h>
+#include <math.h>
+
+#include "sync.h"
+#include "track.h"
+#include "base.h"
+
+static double key_linear(const struct track_key k[2], double row)
+{
+ double t = (row - k[0].row) / (k[1].row - k[0].row);
+ return k[0].value + (k[1].value - k[0].value) * t;
+}
+
+static double key_smooth(const struct track_key k[2], double row)
+{
+ double t = (row - k[0].row) / (k[1].row - k[0].row);
+ t = t * t * (3 - 2 * t);
+ return k[0].value + (k[1].value - k[0].value) * t;
+}
+
+static double key_ramp(const struct track_key k[2], double row)
+{
+ double t = (row - k[0].row) / (k[1].row - k[0].row);
+ t = pow(t, 2.0);
+ return k[0].value + (k[1].value - k[0].value) * t;
+}
+
+double sync_get_val(const struct sync_track *t, double row)
+{
+ int idx, irow;
+
+ /* If we have no keys at all, return a constant 0 */
+ if (!t->num_keys)
+ return 0.0f;
+
+ irow = (int)floor(row);
+ idx = key_idx_floor(t, irow);
+
+ /* at the edges, return the first/last value */
+ if (idx < 0)
+ return t->keys[0].value;
+ if (idx > (int)t->num_keys - 2)
+ return t->keys[t->num_keys - 1].value;
+
+ /* interpolate according to key-type */
+ switch (t->keys[idx].type) {
+ case KEY_STEP:
+ return t->keys[idx].value;
+ case KEY_LINEAR:
+ return key_linear(t->keys + idx, row);
+ case KEY_SMOOTH:
+ return key_smooth(t->keys + idx, row);
+ case KEY_RAMP:
+ return key_ramp(t->keys + idx, row);
+ default:
+ assert(0);
+ return 0.0f;
+ }
+}
+
+int sync_find_key(const struct sync_track *t, int row)
+{
+ int lo = 0, hi = t->num_keys;
+
+ /* binary search, t->keys is sorted by row */
+ while (lo < hi) {
+ int mi = (lo + hi) / 2;
+ assert(mi != hi);
+
+ if (t->keys[mi].row < row)
+ lo = mi + 1;
+ else if (t->keys[mi].row > row)
+ hi = mi;
+ else
+ return mi; /* exact hit */
+ }
+ assert(lo == hi);
+
+ /* return first key after row, negated and biased (to allow -0) */
+ return -lo - 1;
+}
+
+#ifndef SYNC_PLAYER
+int sync_set_key(struct sync_track *t, const struct track_key *k)
+{
+ int idx = sync_find_key(t, k->row);
+ if (idx < 0) {
+ /* no exact hit, we need to allocate a new key */
+ void *tmp;
+ idx = -idx - 1;
+ tmp = realloc(t->keys, sizeof(struct track_key) *
+ (t->num_keys + 1));
+ if (!tmp)
+ return -1;
+ t->num_keys++;
+ t->keys = tmp;
+ memmove(t->keys + idx + 1, t->keys + idx,
+ sizeof(struct track_key) * (t->num_keys - idx - 1));
+ }
+ t->keys[idx] = *k;
+ return 0;
+}
+
+int sync_del_key(struct sync_track *t, int pos)
+{
+ void *tmp;
+ int idx = sync_find_key(t, pos);
+ assert(idx >= 0);
+ memmove(t->keys + idx, t->keys + idx + 1,
+ sizeof(struct track_key) * (t->num_keys - idx - 1));
+ assert(t->keys);
+ tmp = realloc(t->keys, sizeof(struct track_key) *
+ (t->num_keys - 1));
+ if (t->num_keys != 1 && !tmp)
+ return -1;
+ t->num_keys--;
+ t->keys = tmp;
+ return 0;
+}
+#endif
diff --git a/src/track.h b/src/track.h
new file mode 100644
index 0000000..fd59ded
--- /dev/null
+++ b/src/track.h
@@ -0,0 +1,47 @@
+#ifndef SYNC_TRACK_H
+#define SYNC_TRACK_H
+
+#include <string.h>
+#include <stdlib.h>
+#include "base.h"
+
+enum key_type {
+ KEY_STEP, /* stay constant */
+ KEY_LINEAR, /* lerp to the next value */
+ KEY_SMOOTH, /* smooth curve to the next value */
+ KEY_RAMP,
+ KEY_TYPE_COUNT
+};
+
+struct track_key {
+ int row;
+ float value;
+ enum key_type type;
+};
+
+struct sync_track {
+ char *name;
+ struct track_key *keys;
+ int num_keys;
+};
+
+int sync_find_key(const struct sync_track *, int);
+static inline int key_idx_floor(const struct sync_track *t, int row)
+{
+ int idx = sync_find_key(t, row);
+ if (idx < 0)
+ idx = -idx - 2;
+ return idx;
+}
+
+#ifndef SYNC_PLAYER
+int sync_set_key(struct sync_track *, const struct track_key *);
+int sync_del_key(struct sync_track *, int);
+static inline int is_key_frame(const struct sync_track *t, int row)
+{
+ return sync_find_key(t, row) >= 0;
+}
+
+#endif /* !defined(SYNC_PLAYER) */
+
+#endif /* SYNC_TRACK_H */