├── doc ├── doc-site.css ├── doc-site.js ├── scribble-style.css ├── manual-racket.js ├── racket.css ├── manual-racket.css └── manual-style.css ├── js ├── star2.png ├── raindrop.png ├── smokeparticle.png ├── aframe-extras.ocean.min.js ├── aframe-extras.animation-mixer.min.js ├── index.js ├── randomizer.js ├── aframe-mountain-component.min.js ├── aframe-event-set-component.min.js ├── aframe-environment-component.min.js └── aframe-particle-system-component.min.js ├── .gitignore ├── scribblings └── manual.scrbl ├── main.rkt ├── README.md ├── info.rkt ├── vr-engine.rkt ├── .travis.yml ├── my-ip-qr.rkt ├── attribute-definer.rkt ├── component-definer.rkt ├── vr.rkt └── vr-helpers.rkt /doc/doc-site.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/doc-site.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/scribble-style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/star2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoughtstem/vr-engine/HEAD/js/star2.png -------------------------------------------------------------------------------- /js/raindrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoughtstem/vr-engine/HEAD/js/raindrop.png -------------------------------------------------------------------------------- /js/smokeparticle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thoughtstem/vr-engine/HEAD/js/smokeparticle.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | compiled/ 2 | *~ 3 | demos 4 | doc 5 | *.dep 6 | *.exports 7 | *.swp 8 | *.swo 9 | *.swn 10 | *.zo 11 | *.bak 12 | *.DS_Store -------------------------------------------------------------------------------- /scribblings/manual.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/manual 2 | 3 | @(require scribble/extract) 4 | 5 | @title{vr-engine} 6 | 7 | @section{vr-helpers} 8 | @(include-extracted "../vr-helpers.rkt") 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | ;if using #lang vr-engine 4 | ;(module reader syntax/module-reader 5 | ; vr-engine/vr-engine) 6 | 7 | ;if you (require vr-engine) 8 | (provide (all-from-out "./vr-engine.rkt")) 9 | (require "./vr-engine.rkt" 10 | ) 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vr-lang 2 | Racket Lang for Virtual Reality (Aframe) 3 | 4 | We made this for educational purposes at ThoughtSTEM. We're releasing it open source because we love Racket and Aframe.io. 5 | 6 | [Here's the racket package](https://pkgd.racket-lang.org/pkgn/package/vr-lang) 7 | 8 | [Here are the docs](http://docs.racket-lang.org/vr-lang/index.html) 9 | 10 | -------------------------------------------------------------------------------- /info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | 3 | (define version "0.0.1") 4 | 5 | (define deps '("hostname" 6 | "simple-qr" 7 | "urlang" 8 | "https://github.com/thoughtstem/define-assets-from.git" 9 | "https://github.com/thoughtstem/image-coloring.git" 10 | "https://github.com/thoughtstem/TS-GE-Katas.git?path=ts-kata-util" 11 | )) 12 | 13 | (define scribblings '(("scribblings/manual.scrbl" (multi-page)))) 14 | -------------------------------------------------------------------------------- /vr-engine.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | 4 | (provide 5 | (all-from-out ;"./assets.rkt" 6 | "./attribute-definer.rkt" 7 | "./component-definer.rkt" 8 | "./my-ip-qr.rkt" 9 | "./vr-helpers.rkt" 10 | ) 11 | (except-out (all-from-out "./vr.rkt") 12 | color 13 | position 14 | scale) 15 | ;(all-from-out 2htdp/image) 16 | ) 17 | 18 | (require "./my-ip-qr.rkt" 19 | ;"./assets.rkt" 20 | "./component-definer.rkt" 21 | "./attribute-definer.rkt" 22 | (except-in "./vr.rkt" 23 | color 24 | position 25 | scale) 26 | "./vr-helpers.rkt" 27 | ) 28 | -------------------------------------------------------------------------------- /js/aframe-extras.ocean.min.js: -------------------------------------------------------------------------------- 1 | !function e(t,a,r){function n(o,s){if(!a[o]){if(!t[o]){var c="function"==typeof require&&require;if(!s&&c)return c(o,!0);if(i)return i(o,!0);var d=new Error("Cannot find module '"+o+"'");throw d.code="MODULE_NOT_FOUND",d}var p=a[o]={exports:{}};t[o][0].call(p.exports,function(e){var a=t[o][1][e];return n(a||e)},p,p.exports,e,t,a,r)}return a[o].exports}for(var i="function"==typeof require&&require,o=0;odata-uri : Pict -> String 17 | (define (pict->data-uri pict) 18 | (format "data:image/png;base64,~a" 19 | (base64-encode (convert pict 'png-bytes)))) 20 | 21 | (define (my-ip) 22 | (if (eq? (system-type 'os) 'windows) 23 | (begin 24 | (cond [(eq? (system-type 'os) 'windows) (maybe-create-tmp)]) 25 | (best-ipv4-ip-address)) 26 | (if (empty? (get-ipv4-addrs)) 27 | "127.0.0.1" 28 | (first (get-ipv4-addrs))))) 29 | 30 | (define (my-ip-qr-img (post-fix "")) 31 | (define my-ip-qr 32 | (qr-write (format "http://~a:8000~a" (my-ip) post-fix) 33 | (if (eq? (system-type 'os) 'windows) 34 | (string-append (path->string (find-system-path 'home-dir)) slash "tmp" slash "share.png") 35 | "/tmp/share.png"))) 36 | `(div ((style "position:absolute; top:0; left:0; z-index: 1")) 37 | (img ((src ,(pict->data-uri (bitmap (if (eq? (system-type 'os) 'windows) 38 | (string-append (path->string (find-system-path 'home-dir)) slash "tmp" slash "share.png") 39 | "/tmp/share.png")))))))) 40 | 41 | (define (interface-ip-addresses) 42 | (map 43 | (lambda (pieces) 44 | (car (car (filter-map (lambda (s) (regexp-match #px"\\d+.\\d+.\\d+.\\d+" s)) pieces)))) 45 | (filter (lambda (r) (and (pair? r) (string-ci=? (car r) "IPv4"))) 46 | (map string-split 47 | (string-split (with-output-to-string (lambda () (system "ipconfig"))) "\n"))))) 48 | 49 | (define (private-ip-address? x) 50 | (match (map string->number (string-split x ".")) 51 | [(list 10 _ _ _) #t] 52 | [(list 172 n _ _) (and (>= n 16) (< n 32))] 53 | [(list 192 168 _ _) #t] 54 | [_ #f])) 55 | 56 | (define (best-ipv4-ip-address) 57 | (define addresses (interface-ip-addresses)) 58 | (define private-addresses (filter private-ip-address? addresses)) 59 | 60 | (cond 61 | [(pair? private-addresses) (car private-addresses)] 62 | [else "127.0.0.1"])) 63 | 64 | (define (maybe-create-tmp) 65 | (define tmp (string-append (path->string (find-system-path 'home-dir)) slash "tmp" slash)) 66 | (or (directory-exists? tmp) 67 | (make-directory tmp))) 68 | 69 | -------------------------------------------------------------------------------- /js/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * ~ SCHEMA ~ 3 | * color : color of the stars 4 | * radius : distance from center of sphere to first star sphere 5 | * depth : distance between star spheres 6 | * starSize : size of each individual star 7 | * starCount : number of stars per star sphere 8 | * sphereCount : number of star spheres 9 | * texture : sprite used for individual stars 10 | */ 11 | 12 | AFRAME.registerComponent('star-system', { 13 | schema: { 14 | color: { 15 | type: 'string', 16 | default: "#FFF" 17 | }, 18 | radius: { 19 | type: 'number', 20 | default: 300, 21 | min: 0, 22 | }, 23 | depth: { 24 | type: 'number', 25 | default: 300, 26 | min: 0, 27 | }, 28 | starSize: { 29 | type: 'number', 30 | default: 1, 31 | min: 0, 32 | }, 33 | count: { 34 | type: 'number', 35 | default: 10000, 36 | min: 0, 37 | }, 38 | texture: { 39 | type: 'asset', 40 | default: '' 41 | } 42 | }, 43 | 44 | update: function() { 45 | // Check for and load star sprite 46 | let texture = {}; 47 | if (this.data.texture) { 48 | texture.transparent = true; 49 | texture.map = new THREE.TextureLoader().load(this.data.texture); 50 | } 51 | 52 | const stars = new THREE.Geometry(); 53 | 54 | // Randomly create the vertices for the stars 55 | while (stars.vertices.length < this.data.count) { 56 | stars.vertices.push(this.randomVectorBetweenSpheres(this.data.radius, this.data.depth)); 57 | } 58 | 59 | // Set the star display options 60 | const starMaterial = new THREE.PointsMaterial(Object.assign(texture, { 61 | color: this.data.color, 62 | size: this.data.starSize 63 | })); 64 | 65 | // Add the star particles to the element 66 | this.el.setObject3D('particle-system', new THREE.Points(stars, starMaterial)); 67 | }, 68 | 69 | // Returns a random vector between the inner sphere 70 | // and the outer sphere created with radius + depth 71 | randomVectorBetweenSpheres: function(radius, depth) { 72 | const randomRadius = Math.floor(Math.random() * (radius + depth - radius + 1) + radius); 73 | return this.randomSphereSurfaceVector(randomRadius); 74 | }, 75 | 76 | // Returns a vector on the face of sphere with given radius 77 | randomSphereSurfaceVector: function(radius) { 78 | const theta = 2 * Math.PI * Math.random(); 79 | const phi = Math.acos(2 * Math.random() - 1); 80 | const x = radius * Math.sin(phi) * Math.cos(theta); 81 | const y = radius * Math.sin(phi) * Math.sin(theta); 82 | const z = radius * Math.cos(phi); 83 | return new THREE.Vector3(x, y, z); 84 | } 85 | }); 86 | -------------------------------------------------------------------------------- /js/randomizer.js: -------------------------------------------------------------------------------- 1 | AFRAME.registerComponent('random-color', { 2 | schema: { 3 | sat: {default: '100%'}, 4 | lum: {default: '50%'} 5 | }, 6 | init: function(){ 7 | var randomHue = Math.floor(Math.random() * 360); 8 | var data = this.data; 9 | var sat = data.sat; 10 | var lum = data.lum; 11 | this.el.setAttribute('color', "hsl("+ randomHue +"," + sat + "," + lum + ")"); 12 | } 13 | }); 14 | 15 | AFRAME.registerComponent('random-position', { 16 | schema: { 17 | min: {default: {x: -10, y: 1, z: -10}, type: 'vec3'}, 18 | max: {default: {x: 10, y: 1, z: 10}, type: 'vec3'} 19 | }, 20 | init: function(){ 21 | var data = this.data; 22 | var max = data.max; 23 | var min = data.min; 24 | this.el.setAttribute('position', { 25 | x: Math.random() * (max.x - min.x) + min.x, 26 | y: Math.random() * (max.y - min.y) + min.y, 27 | z: Math.random() * (max.z - min.z) + min.z 28 | }); 29 | } 30 | }); 31 | 32 | AFRAME.registerComponent('random-bounce', { 33 | schema: { 34 | min: {default: 1}, 35 | max: {default: 3}, 36 | dur: {default: null} 37 | }, 38 | init: function(){ 39 | var data = this.data; 40 | var max = data.max; 41 | var min = data.min; 42 | var dur = data.dur; 43 | if (dur == null){ 44 | dur = Math.floor(Math.random() * 1500 + 400) 45 | }; 46 | var pos = this.el.getAttribute("position"); 47 | var randomHeight = Math.random() * (max - min) + min 48 | var animation = document.createElement("a-animation"); 49 | animation.setAttribute("attribute","position"); 50 | animation.setAttribute("to", pos.x + " " + (pos.y + randomHeight) + " " + pos.z); 51 | animation.setAttribute("direction", "alternate"); 52 | animation.setAttribute("dur", dur); 53 | animation.setAttribute("repeat", "indefinite"); 54 | this.el.appendChild(animation); 55 | } 56 | }); 57 | 58 | AFRAME.registerComponent('random-bounce-look', { 59 | schema: { 60 | min: {default: 2}, 61 | max: {default: 5}, 62 | dur: {default: null} 63 | }, 64 | init: function(){ 65 | var data = this.data; 66 | var max = data.max; 67 | var min = data.min; 68 | var dur = data.dur; 69 | if (dur == null){ 70 | dur = Math.floor(Math.random() * 800 + 200); 71 | }; 72 | var pos = this.el.getAttribute("position"); 73 | var randomHeight = Math.random() * (max - min) + min 74 | var animation = document.createElement("a-animation"); 75 | animation.setAttribute("attribute","position"); 76 | animation.setAttribute("begin", "mouseenter"); 77 | animation.setAttribute("to", pos.x + " " + (pos.y + randomHeight) + " " + pos.z); 78 | animation.setAttribute("direction", "alternate"); 79 | animation.setAttribute("dur", dur); 80 | animation.setAttribute("repeat", "1"); 81 | this.el.appendChild(animation); 82 | } 83 | }); -------------------------------------------------------------------------------- /attribute-definer.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide define-attribute 4 | fancy-define ;dumb test 5 | render) 6 | 7 | (define-syntax (fancy-define stx) 8 | (syntax-case stx () 9 | [(_ name val) 10 | #'(struct name () #:transparent)])) 11 | 12 | (require (for-syntax racket/syntax)) 13 | (require (for-syntax racket)) 14 | 15 | (require 2htdp/image 16 | ;pict 17 | ;net/base64 18 | ;file/convertible 19 | ) 20 | 21 | (define-for-syntax (repeat-str s n) 22 | (map (thunk* s) (range n))) 23 | 24 | 25 | 26 | (define (render a) 27 | (send a render)) 28 | 29 | 30 | (define all-imgs (make-hash )) 31 | 32 | (define (next-filename) 33 | (string-append (number->string (length (hash-keys all-imgs))) ".png")) 34 | 35 | (define (saved-img i) 36 | (displayln (string-append "SECOND " (path->string (current-directory)))) 37 | (define file-name (next-filename)) 38 | (define save-path (string-append (path->string (current-directory)) file-name)) 39 | (hash-set! all-imgs i file-name) 40 | (save-image i save-path) 41 | (string-append "./" file-name)) 42 | 43 | (define (image->filename i) 44 | (if (hash-has-key? all-imgs i) 45 | (hash-ref all-imgs i) 46 | (saved-img i))) 47 | 48 | (define (path->filename p) 49 | (define file-name (path->string (file-name-from-path p))) 50 | (define save-path (build-path (current-directory) file-name)) 51 | ;(delete-directory/files build-assets-path #:must-exist? #f) ;TODO delete and save to assets folder 52 | (copy-file p save-path #t) 53 | file-name) 54 | 55 | (define (convert-attr attr) 56 | (cond [(hash? attr) (string-join (map (λ(x) (format "~a:~a" (car x) (convert-attr (cdr x)))) 57 | (hash->list attr)) ";")] 58 | [(image? attr) (image->filename attr)] 59 | [(path? attr) (path->filename attr)] 60 | [else attr])) 61 | 62 | (define (convert-attrs . attrs) 63 | (map convert-attr attrs)) 64 | 65 | (define-syntax (define-attribute stx) 66 | (syntax-case stx () 67 | [(_ name (vars ...) format-str) 68 | (with-syntax* ([classname (format-id stx "~a%" #'name)] 69 | [vars/sym (map syntax-e (syntax->list #'(vars ...)))] 70 | [vars/id (map (λ(s) (format-id stx "~a-~a" #'name s)) 71 | (syntax->list #'vars/sym))] 72 | [init-fields (map (λ(s) #`(init-field #,(syntax->datum s))) (syntax-e #'(vars ...)))] 73 | [alt-vars (map (λ(s) (format-id stx "~a-alt" s)) 74 | (syntax-e #'(vars ...)))] 75 | [setters (map list (syntax-e #'(vars ...)) (syntax-e #'alt-vars) )] 76 | [accessors (map (λ(s) #`(dynamic-get-field '#,(syntax->datum s) this)) (syntax-e #'(vars ...)))] 77 | [name-s (symbol->string (syntax->datum #'name))] 78 | [alt-vars-with-defaults 79 | (map (λ(s) `(,s "")) 80 | (syntax-e #'alt-vars))]) 81 | #`(begin 82 | (provide name 83 | classname) 84 | (define classname 85 | (class object% 86 | #,@#'init-fields 87 | (define/public (render) 88 | (apply (curry format format-str) 89 | (convert-attrs #,@#'accessors))) 90 | (define/public (my-name) 91 | (string->symbol name-s)) 92 | (super-new))) 93 | (define (name #,@#'alt-vars-with-defaults) 94 | (new classname #,@#'setters))))])) 95 | 96 | -------------------------------------------------------------------------------- /js/aframe-mountain-component.min.js: -------------------------------------------------------------------------------- 1 | !function(t){function a(r){if(e[r])return e[r].exports;var n=e[r]={exports:{},id:r,loaded:!1};return t[r].call(n.exports,n,n.exports,a),n.loaded=!0,n.exports}var e={};return a.m=t,a.c=e,a.p="",a(0)}([function(t,a,e){function r(t,a){for(var e=t*a,r=new Uint8Array(e),n=new o,i=1,l=100*Math.random(),u=0;u<4;u++){for(var d=0;d