Commit f8604c78 authored by dave griffiths's avatar dave griffiths
Browse files

pi merge

parents c5c85eae c0810fc5
......@@ -33,8 +33,8 @@ LOCAL_SRC_FILES := \
engine/obj_reader.cpp \
engine/nomadic.cpp \
engine/engine.cpp \
jellyfish/jellyfish.cpp \
jellyfish/jellyfish_primitive.cpp \
engine/jellyfish.cpp \
engine/jellyfish_primitive.cpp \
audio.cpp \
app-android.c
......
Jellyfish
=========
Aims to be a fluxus compatible programmable game engine and 3D renderer
for livecoding small devices.
Incorporating:
A minimalistic fluxus compatible programmable game engine and 3D
renderer for exprimenting with livecoding ARM devices such as Raspberry
Pi, OUYA and Android as well as legacy support for PlayStation2.
* Modified tinyscheme R5RS interpreter
* REPL for livecoding
* OSC REPL for remote livecoding
* Fixed point maths throughout
* An experimental vector processor and compiler for fast procedural rendering
* An experimental vector processor and compiler for fast procedural
rendering
* OpenGL ES backend for ARM/Android/Rasperry Pi/OUYA
* Linux target: reference version (also running fixed point)
* Playstation 2 target (legacy)
* Custom hardware renderer running on vu1 path
* Linux target as a reference version (also running fixed point)
* Playstation 2 target (legacy) a custom hardware renderer running on
vu1 path
Building
--------
You'll need scons and liblo-dev installed, the Linux version requires GLUT.
You'll need scons and liblo-dev installed, the Linux version requires
GLUT.
### Linux ###
......@@ -35,52 +36,183 @@ As part of an APK:
ndk-build
Running
-------
Inside the testing directory there is a python script for testing the
OSC mode, continuously sending test code to the jellyfish program -
which can be on another machine.
Jellyfish Lisp Language Reference
---------------------------------
### Example programs ###
Randomly move vertex positions
The purpose of a Jellyfish Lisp program is to manipulate 3D objects in a
scene, move/rotate/scale or change vertex positions, lighting normals,
texture coords. It can also act on input from outside. It can do this
faster than in interpreted Scheme, particually on ARM devices as the VM
(written in C++) is very small and doesn't require any memory allocation
so no garbage collection is required. The VM also has access to more
data than a GPU shader, although tight coupling to GPU functionality (ie
running parts of the VM on GPU cores) is planned.
Jellyfish Lisp programs are compiled to bytecode executed by the VM -
there is one per object potentially running in parallel threads. The
helper function (make-jelly-obj) takes a number of cycles to execute per
frame, an OpenGL primitive type and a program. It returns the primitive
id which can be operated on like any normal fluxus primitive.
Inside the jellyfish VM the 3D data exists in the same memory address
space as the program itself, starting at "positions-start"
address. Using (read) or (write!) you can access it using a value as an
address.
Here is a program that randomly moves vertex positions around:
;; normal fluxus code
(with-primitive
(make-jelly-obj 1000
(make-jelly-obj 1000 prim-tristrip
;; jellyfish lisp starts here
'(let ((vertex positions-start))
'(let ((vertex 0))
(forever
(set! vertex positions-start)
(loop (< vertex positions-end)
(write! vertex (+ (read vertex) (rndvec)))
(set! vertex (+ vertex 1)))
(set! vertex positions-start) ;; start at the first vertex number
(loop (< vertex positions-end) ;; go to the last vertex
(write! vertex (+ (read vertex) (rndvec))) ;; add a random amount
(set! vertex (+ vertex 1))) ;; increment vertex number
)))
;; jellyfish lisp ends here
;; normal fluxus code
(pdata-map! (lambda (p) (srndvec)) "p")
(pdata-map! (lambda (c) (rndvec)) "c"))
### Core forms ###
TDB
* let
* define
* cond
* loop
* forever
* do
* lambda
* +
* -
* *
* /
* *v
* cross
* dot
* eq?
* >
* <
* set!
* write!
Reference for all the commands in jellyfish lisp.
#### let ####
(let ((name value) (name value) ...) block)
Bind names to values. All values are internally vectors of length 3 in
jellyfish (as it's a vector processor), but you can specify them as
single numbers for convenience, which just get converted to
`(vector x 0 0)` where x is the number you specify.
*Tofix*: Let bindings are not scoped correctly, variables can be
referred to after scope is closed and conflict with function parameters
and other let variables of the same name. This is bad.
#### define ####
(define name value)
Assign a name to a value, can be a function or a vector.
*Tofix*: same scoping problem same as let.
#### if ####
(if pred true-expr false-expr)
An if expression - returns value of the executed expression, eg.:
(define a (if (< (some-func) 10) 100 200))
Will assign a to be 100 or 200.
#### when ####
(when pred do-this-block)
Same as if, without the false else block. Returns the value of the last
expression if true, zero otherwise.
#### cond ####
(cond (pred block) (pred block) ... (else block))
Internally evaluates to a bunch of if's
#### loop ####
(loop pred block)
Repeats until the predicate is false.
#### forever ####
(forever block)
Repeats forever
#### do ####
(do block)
Returns the value of the last expression.
#### lambda ####
(lambda (args) block)
Creates a procedure, more usually:
(define myproc
(lambda (a b)
(+ a b)))
#### + - ####
Vector addition and subtraction:
(+ (vector 1 2 3) (vector 2 3 4))
(+ 2 (vector 3 2 1))
Single numbers become (vector number 0 0) so the above lines evaluate to
`(vector 3 5 7)` and `(vector 5 2 1)` respectively. Same for subtraction.
#### * / ####
Multiplies and divides by the second parameter's x value, so:
(* (vector 1 1 1) 2) => (vector 2 2 2)
and
(/ (vector 1 1 1) 2) => (vector 0.5 0.5 0.5)
#### *v ####
Multiplies by each element, eg:
(*v (vector 1 2 3) (vector 0.5 4 3)) => (vector 0.5 8 9)
#### cross dot ####
Cross product and dot product of two vectors
#### eq? ####
Per-element equality
#### > < ####
Compares x values of supplied vectors.
Tofix: why no <= or >=?
#### set! ####
(set! name newvalue)
Mutates value of an existing variable.
#### write! ####
* write-add!
* swizzle
* rndvec
......@@ -103,11 +235,11 @@ TDB
### Low level instruction set ###
TDB
Soon...
jmp jmz jlt jgt ldl lda ldi sta sti
add sub mul div abs scs atn dot crs
sqr len dup drp cmp shf bld ret dbg
nrm add.x add.y add.z swp rnd mull
nrm mst mad msb swp rnd mull
jmr ldlv lensq noise lds sts mulv
synth-crt synth-con synth-ply flr
target = 'nomadic'
target = 'jellyfish'
platform = ARGUMENTS.get('TARGET','LINUX')
env = Environment(CCFLAGS='-O3 -std=gnu++0x -ggdb -DUSE_MATH=1 -Wno-write-strings')
source = ['main.cpp',
......@@ -22,14 +22,13 @@ source = ['main.cpp',
'engine/scenenode.cpp',
'engine/texture.cpp',
'engine/nomadic.cpp',
'jellyfish/jellyfish_primitive.cpp',
'jellyfish/jellyfish.cpp',
'rpi/input.cpp'
'engine/jellyfish_primitive.cpp',
'engine/jellyfish.cpp'
]
if platform=='LINUX':
env.Append(LIBS = ['glut', 'GL', 'png', 'pthread', 'dl', 'lo'])
env.Append(LIBS = ['glut', 'GL', 'png', 'pthread', 'dl', 'lo', 'jpeg'])
env.Append(CCFLAGS=' -fpermissive -DFLX_LINUX')
env.Append(CPPPATH = '.')
source.append(['core/db.cpp',
......@@ -38,7 +37,7 @@ if platform=='LINUX':
if platform=='RPI':
# raspberry pi
env.Append(LIBS = ['GLESv1_CM', 'EGL', 'bcm_host', 'X11', 'png', 'lo'])
env.Append(LIBS = ['GLESv1_CM', 'EGL', 'bcm_host', 'X11', 'png', 'lo', 'jpeg'])
env.Append(CCFLAGS=' -DFLX_RPI -fpermissive')
env.Append(CPPPATH = '/opt/vc/include/interface/vcos/pthreads/:/opt/vc/include/interface/vmcs_host/linux:/opt/vc/include/:.')
env.Append(LIBPATH = '/opt/vc/lib')
......
......@@ -529,3 +529,24 @@
(let ((code (diy-macro (append '(begin) code))))
; (display code)(newline)
(eval code)))
;;---------------------------------------------------------
;; jellyfish helpers
(define (jelly-compiled code)
(define addr 0)
(for-each
(lambda (v)
(pdata-set! "x" addr v)
(set! addr (+ addr 1)))
code))
(define (program-jelly speed prim-type code)
(let ((c (compile-program speed prim-type 1 code)))
;;(disassemble c)
(jelly-compiled c)))
(define (disassemble-compiled code)
(let ((c (compile-program 50 'triangles 1 code)))
(disassemble c))
code)
......@@ -2,6 +2,7 @@
;; vectorlisp: a strange language for procedural rendering
(define debug #f)
(define prim-size 4096)
(define nop 0) (define jmp 1) (define jmz 2) (define jlt 3) (define jgt 4)
(define ldl 5) (define lda 6) (define ldi 7) (define sta 8) (define sti 9)
......@@ -9,18 +10,19 @@
(define sincos 15) (define atn 16) (define dot 17) (define crs 18) (define sqr 19)
(define len 20) (define dup 21) (define drp 22) (define cmp 23) (define shf 24)
(define bld 25) (define ret 26) (define _dbg 27) (define nrm 28)
(define add.x 29) (define add.y 30) (define add.z 31) (define end-check 999)
(define mst 29) (define mad 30) (define msb 31) (define end-check 999)
(define swp 32) (define rnd 33) (define mull 34) (define jmr 35) (define ldlv 36)
(define lensq 37) (define noise 38) (define lds 39) (define sts 40) (define mulv 41)
(define synth-crt 42) (define synth-con 43) (define synth-ply 44) (define flr 45)
(define mod 46)
(define instr
'(nop jmp jmz jlt jgt ldl lda ldi sta sti
add sub mul div abs scs atn dot crs
sqr len dup drp cmp shf bld ret dbg
nrm add.x add.y add.z swp rnd mull
nrm mst mad msb swp rnd mull
jmr ldlv lensq noise lds sts mulv
synth-crt synth-con synth-ply flr))
synth-crt synth-con synth-ply flr mod))
(define prim-triangles 0)
(define prim-tristrip 1)
......@@ -66,7 +68,7 @@
addr))
;; segments are data areas, positions, normals, colours etc
(define segment-size 512)
(define segment-size prim-size)
(define (memseg n) (* segment-size n))
......@@ -142,6 +144,17 @@
(emit (vector drp 0 0))
(emit-expr-list (cdr l))))))))
;; append a bunch of expressions, don't drop
;; as we want to build the stack (for fn call)
(define (emit-expr-list-maintain-stack l)
(cond
((null? l) '())
(else
(append (emit-expr (car l))
(if (null? (cdr l)) '()
(emit-expr-list-maintain-stack (cdr l)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; primitive function calls follow
......@@ -155,68 +168,54 @@
;; (write! start-addr value value value ...)
(define (emit-write! x)
(append
(cadr
(foldl
(lambda (val r)
(list
(+ (car r) 1)
(append
(cadr r)
(emit-expr val) ;; data
(emit (vector ldl (car r) 0)) ;; offset
(emit-expr (cadr x)) ;; address
(emit (vector add 0 0)) ;; add offset
(emit (vector sts 0 0)))))
(list 0 '())
(cddr x)))
(emit (vector ldl 0 0))))
;; stick everything on the stack
(emit-expr-list-maintain-stack (reverse (cdr x)))
(emit (vector mst (length (cddr x)) 0))
(emit (vector ldl 0 0))))
(define (emit-write-add! x)
(append
(cadr
(foldl
(lambda (val r)
(list
(+ (car r) 1)
(append
(cadr r)
(emit-expr (cadr x)) ;; address
(emit (vector ldl (car r) 0)) ;; offset
(emit (vector add 0 0)) ;; add them
(emit (vector lds 0 0)) ;; load value
(emit-expr val) ;; data
(emit (vector add 0 0)) ;; add them
(emit (vector ldl (car r) 0)) ;; offset
(emit-expr (cadr x)) ;; address
(emit (vector add 0 0)) ;; add offset
(emit (vector sts 0 0)))))
(list 0 '())
(cddr x)))
(emit (vector ldl 0 0))))
;; stick everything on the stack
(emit-expr-list-maintain-stack (reverse (cdr x)))
(emit (vector mad (length (cddr x)) 0))
(emit (vector ldl 0 0))))
(define (emit-write-sub! x)
(append
;; stick everything on the stack
(emit-expr-list-maintain-stack (reverse (cdr x)))
(emit (vector msb (length (cddr x)) 0))
(emit (vector ldl 0 0))))
(define (emit-read x)
(append
(emit-expr (cadr x)) ;; address
(emit (vector lds 0 0))))
(define (emit-cond-part x)
(let ((block (emit-expr-list (cdr x))))
(append
(emit-expr (car x))
(emit (vector jmz (+ (length block) 1) 0))
block)))
(define (emit-addr x)
(emit (vector ldl (variable-address (cadr x)) 0)))
(define (emit-cond x)
(define (_ l)
(cond
((null? l) '())
(else (append (emit-cond-part (car l))
(_ (cdr l))))))
(_ (cdr x)))
(define (emit-if x)
(let ((tblock (emit-expr (caddr x)))
(fblock (emit-expr (cadddr x))))
(append
(emit-expr (cadr x))
(emit (vector jmz (+ (length tblock) 2) 0))
tblock
(emit (vector jmr (+ (length fblock) 1) 0))
fblock)))
(define (emit-when x)
(let ((block (emit-expr-list (cddr x))))
(append
(emit-expr (cadr x))
(emit (vector jmz (+ (length block) 2) 0))
block
(emit (vector jmr 2 0))
(emit (vector ldl 0 0)))))
(define (emit-fncall x addr)
(let ((args (emit-expr-list (cdr x))))
(let ((args (emit-expr-list-maintain-stack (cdr x))))
(append
;; offset from here -> stitch up in second pass
(emit (list 'add-abs-loc 'this 1
......@@ -234,7 +233,7 @@
;; for moment use global pile for arguments :O
(make-variable! arg)
(vector sta (variable-address arg) 0))
(cadr x))
(reverse (cadr x)))
;; now args are loaded, do body
(emit-expr-list (cddr x))
;; swap ret ptr to top
......@@ -400,17 +399,20 @@
((eq? (car x) '*v) (binary-procedure mulv x))
((eq? (car x) 'cross) (binary-procedure crs x))
((eq? (car x) 'dot) (binary-procedure dot x))
((eq? (car x) 'modulo) (binary-procedure mod x))
((eq? (car x) 'eq?) (emit-eq? x))
((eq? (car x) '>) (emit-> x))
((eq? (car x) '<) (emit-< x))
((eq? (car x) 'set!) (emit-set! x))
((eq? (car x) 'write!) (emit-write! x))
((eq? (car x) 'write-add!) (emit-write-add! x))
((eq? (car x) 'write-sub!) (emit-write-sub! x))
((eq? (car x) 'swizzle) (emit-swizzle x))
((eq? (car x) 'lambda) (emit-lambda x))
((eq? (car x) 'rndvec) (emit (vector rnd 0 0)))
((eq? (car x) 'trace) (emit-trace x))
((eq? (car x) 'read) (emit-read x))
((eq? (car x) 'addr) (emit-addr x))
((eq? (car x) 'not) (emit-not x))
((eq? (car x) 'mag) (unary-procedure len x))
((eq? (car x) 'magsq) (unary-procedure lensq x))
......@@ -448,7 +450,8 @@
(cond
((eq? (car x) 'let) (emit-let x))
((eq? (car x) 'define) (emit-define x))
((eq? (car x) 'cond) (emit-cond x))
((eq? (car x) 'if) (emit-if x))
((eq? (car x) 'when) (emit-when x))
((eq? (car x) 'loop) (emit-loop x))
((eq? (car x) 'do) (emit-expr-list (cdr x)))
(else (emit-procedure x)))
......@@ -462,7 +465,7 @@
(define (header code-start cycles prim hints)
(list
(vector code-start cycles 0) ;; control (pc, cycles, stack)
(vector 512 prim hints) ;; graphics
(vector prim-size prim hints) ;; graphics
(vector 0 0 0) ;; translate
(vector 1 0 0) ;; rota
(vector 0 1 0) ;; rotb
......@@ -543,6 +546,15 @@
(cdr x))))))))
(define (preprocess-cond-to-if x)
(define (_ l)
(cond
((null? l) 0)
((eq? (pre-process (caar l)) 'else) (cons 'do (pre-process (cdr (car l)))))
(else (list 'if (pre-process (caar l)) (cons 'do (pre-process (cdr (car l))))
(_ (cdr l))))))
(_ (cdr x)))
;; basically diy-macro from the main tinyscheme stuff
(define (pre-process s)
(cond
......@@ -560,6 +572,8 @@
((eq? (car i) '--!)
(let ((v (pre-process (cadr i))))
(list 'set! v (list '- v 1))))
((eq? (car i) 'cond)
(preprocess-cond-to-if i))
((eq? (car i) 'play-now)
(append
(list 'do)
......
......@@ -67,3 +67,54 @@
(rotate (vector 0 (if (key-pressed "a") 0.5 0) 0))))
<<<<<<< HEAD
=======
(define (make-jelly speed prim-type code)
(let ((p (list-ref jellyfish current)))
(msg p)
(with-primitive
p
(let ((c (compile-program speed prim-type 1 code)))
;; (disassemble c)
(jelly-compiled c))
(set! current (modulo (+ current 1) (length jellyfish)))
p)))
(with-primitive
(make-jelly
10000 prim-triangles
'(let ((vertex positions-start)
(t 0)
(v 0)
(np 0))
(forever
(set! vertex positions-start)
(loop (< vertex positions-end)
(set! np (+ (* (+ (read vertex) vertex) 0.1)
(swizzle yyx t)))
(set! v (+ (*v (noise np) (vector 1 0 0))
(*v (noise (+ np 101.1)) (vector 0 1 0))))
(set! v (*v (- v (vector 0.47 0.47 0.47)) (vector 0.1 0.1 0)))
(write-add! vertex v v v v v v)
(set! vertex (+ vertex 6)))
(set! t (+ t 0.01))
)))
(hint-unlit)
(pdata-index-map!
(lambda (i p)
(let ((z (* i 0.01)))
(if (odd? i)
(list-ref
(list (vector 0 0 z) (vector 1 0 z) (vector 1 1 z))
(modulo i 3))
(list-ref
(list (vector 1 1 z) (vector 0 1 z) (vector 0 0 z))
(modulo i 3))))) "p")
(texture (load-texture "raspberrypi.png"))
(translate (vector -0.5 -0.5 0))
(pdata-copy "p" "t")
(pdata-map! (lambda (t) (vmul t -1)) "t")
(pdata-map! (lambda (c) (vector 1 1 1)) "c")