├── asteroid.png
├── scratch
├── there.cljs
├── virtual.js
├── repl.cljs
├── virtual-vector.js
├── hello.cljs
├── virtual-thunk.js
├── virtual-pixi.js
├── virtual-dom.js
├── virtual-three.js
└── virtual-three-clj.js
├── js
├── loaders
│ ├── ctm
│ │ ├── CTMWorker.js
│ │ ├── license
│ │ │ ├── OpenCTM.txt
│ │ │ ├── js-lzma.txt
│ │ │ └── js-openctm.txt
│ │ └── CTMLoader.js
│ ├── SVGLoader.js
│ ├── KMZLoader.js
│ ├── PlayCanvasLoader.js
│ ├── collada
│ │ ├── AnimationHandler.js
│ │ ├── KeyFrameAnimation.js
│ │ └── Animation.js
│ ├── BabylonLoader.js
│ ├── gltf
│ │ ├── glTFAnimation.js
│ │ └── glTFLoaderUtils.js
│ ├── PDBLoader.js
│ ├── PVRLoader.js
│ ├── DDSLoader.js
│ ├── VTKLoader.js
│ ├── PCDLoader.js
│ ├── MTLLoader.js
│ ├── OBJLoader.js
│ ├── AssimpJSONLoader.js
│ └── RGBELoader.js
├── zajal.js
└── repl.js
├── zajal
├── input
│ └── keyboard.cljs
├── core.cljs
└── draw
│ ├── pixi.ts
│ ├── pixi.js
│ └── three.cljs
├── main.js
├── package.json
├── index.html
├── LICENSE
├── examples
├── other.cljs
└── asteroid.cljs
├── .gitignore
├── README.md
└── ts
└── zajal.ts
/asteroid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nasser/zajal/HEAD/asteroid.png
--------------------------------------------------------------------------------
/scratch/there.cljs:
--------------------------------------------------------------------------------
1 | (ns there)
2 |
3 | (defn prand [x]
4 | (str x :a x ))
--------------------------------------------------------------------------------
/js/loaders/ctm/CTMWorker.js:
--------------------------------------------------------------------------------
1 | importScripts( "lzma.js", "ctm.js" );
2 |
3 | self.onmessage = function( event ) {
4 |
5 | var files = [];
6 |
7 | for ( var i = 0; i < event.data.offsets.length; i ++ ) {
8 |
9 | var stream = new CTM.Stream( event.data.data );
10 | stream.offset = event.data.offsets[ i ];
11 |
12 | files[ i ] = new CTM.File( stream );
13 |
14 | }
15 |
16 | self.postMessage( files );
17 | self.close();
18 |
19 | };
20 |
--------------------------------------------------------------------------------
/js/zajal.js:
--------------------------------------------------------------------------------
1 | const {ipcRenderer} = require("electron"),
2 | fs = require("fs")
3 |
4 | ipcRenderer.on('new', (event, path) => {
5 | fs.readFile(path, "utf8", (err, source) => {
6 | console.log(source);
7 | clojurescript.core.eval(source);
8 | })
9 | })
10 |
11 | ipcRenderer.on('changed', (event, path) => {
12 | // hack
13 | var oldTitle = document.title;
14 | document.title = document.title + "*";
15 | fs.readFile(path, "utf8", (err, source) => {
16 | clojurescript.core.eval(source);
17 | document.title = oldTitle;
18 | })
19 | })
--------------------------------------------------------------------------------
/js/repl.js:
--------------------------------------------------------------------------------
1 | const dgram = require('dgram');
2 | const server = dgram.createSocket('udp4');
3 |
4 | server.on('error', (err) => {
5 | console.log(`server error:\n${err.stack}`);
6 | server.close();
7 | });
8 |
9 | server.on('message', (msg, rinfo) => {
10 | let code = msg.toString(),
11 | result = cljs.core.pr_str(clojurescript.core.eval(code));
12 | server.send(`${clojurescript.core.eval("(str *ns*)")}=> ${code}${result}\n\n`, rinfo.port, rinfo.address);
13 | });
14 |
15 | server.on('listening', () => {
16 | var address = server.address();
17 | console.log(`server listening ${address.address}:${address.port}`);
18 | });
19 |
20 | server.bind(10011);
--------------------------------------------------------------------------------
/zajal/input/keyboard.cljs:
--------------------------------------------------------------------------------
1 | (ns zajal.input.keyboard
2 | (:require [clojure.string :as string]))
3 |
4 | (defn- camels-to-hyphens [s]
5 | (-> s
6 | (string/replace #"([A-Za-z])([A-Z][a-z])" "$1-$2")
7 | (string/replace #"([A-Za-z]{2,})([A-Z0-9])$" "$1-$2")
8 | string/lower-case))
9 |
10 | (defn- kw [s]
11 | (-> s camels-to-hyphens keyword))
12 |
13 | (def pressed-keys (atom {}))
14 |
15 | (defn add-listeners! []
16 | (js/window.document.addEventListener
17 | "keydown"
18 | #(swap! pressed-keys assoc (.-code %) %))
19 | (js/window.document.addEventListener
20 | "keyup"
21 | #(swap! pressed-keys dissoc (.-code %))))
22 |
23 | (add-listeners!)
24 |
25 | (defn key-set []
26 | (->> @pressed-keys
27 | keys
28 | (map kw)
29 | set))
--------------------------------------------------------------------------------
/js/loaders/SVGLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | * @author zz85 / http://joshuakoo.com/
4 | */
5 |
6 | THREE.SVGLoader = function ( manager ) {
7 |
8 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
9 |
10 | };
11 |
12 | THREE.SVGLoader.prototype = {
13 |
14 | constructor: THREE.SVGLoader,
15 |
16 | load: function ( url, onLoad, onProgress, onError ) {
17 |
18 | var scope = this;
19 |
20 | var parser = new DOMParser();
21 |
22 | var loader = new THREE.XHRLoader( scope.manager );
23 | loader.load( url, function ( svgString ) {
24 |
25 | var doc = parser.parseFromString( svgString, 'image/svg+xml' ); // application/xml
26 |
27 | onLoad( doc.documentElement );
28 |
29 | }, onProgress, onError );
30 |
31 | }
32 |
33 | };
34 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | const {BrowserWindow, app} = require('electron');
2 | os = require('os'),
3 | path = require('path'),
4 | chokidar = require('chokidar');
5 |
6 | let win;
7 |
8 | var watcher = chokidar.watch(process.argv[2]);
9 | watcher.on('change', path => win.webContents.send('changed', path))
10 |
11 | function createWindow() {
12 | win = new BrowserWindow({width: 800, height: 600, 'title-bar-style': 'hidden'});
13 | win.loadURL(`file:///${__dirname}/index.html`);
14 | win.webContents.on('did-finish-load', () => {
15 | win.webContents.send('new', process.argv[2])
16 | });
17 | }
18 |
19 | app.on('ready', createWindow);
20 |
21 | app.on('window-all-closed', () => {
22 | if (process.platform !== 'darwin') {
23 | app.quit();
24 | }
25 | });
26 |
27 | app.on('activate', () => {
28 | if (win === null) {
29 | createWindow();
30 | }
31 | });
32 |
--------------------------------------------------------------------------------
/scratch/virtual.js:
--------------------------------------------------------------------------------
1 | var current;
2 | var next;
3 |
4 | function renderer(container, initial) {
5 | current = initial;
6 | next = initial;
7 |
8 | container.appendChild( current.render().domElement );
9 |
10 | current.camera.render().aspect = window.innerWidth / window.innerHeight;
11 | current.camera.render().updateProjectionMatrix();
12 | current.render().setSize( window.innerWidth, window.innerHeight );
13 |
14 | var _update = function(newState) {
15 | next = newState;
16 | }
17 |
18 | var _render = function() {
19 | requestAnimationFrame(_render);
20 | if(current === next) return;
21 |
22 | current.reconcile(next);
23 | current = next;
24 | current.render().render( current.scene.render(), current.camera.render() );
25 | }
26 |
27 |
28 | return {
29 | update: _update,
30 | render: _render
31 | }
32 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-react",
3 | "version": "0.6.1",
4 | "description": "Zajal Creative Coding Experiment",
5 | "main": "main.js",
6 | "dependencies": {
7 | "chokidar": "^1.6.1",
8 | "clojurescript": "^0.0.11",
9 | "pixi.js": "4.3.5",
10 | "three": "0.84.0"
11 | },
12 | "devDependencies": {},
13 | "scripts": {
14 | "test": "echo \"Error: no test specified\" && exit 1"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/nasser/zajal.git"
19 | },
20 | "keywords": [
21 | "functional",
22 | "clojurescript",
23 | "lisp",
24 | "creative",
25 | "threejs",
26 | "react"
27 | ],
28 | "author": "Ramsey Nasser",
29 | "license": "MIT",
30 | "bugs": {
31 | "url": "https://github.com/nasser/zajal/issues"
32 | },
33 | "homepage": "https://github.com/nasser/zajal#readme"
34 | }
35 |
--------------------------------------------------------------------------------
/scratch/repl.cljs:
--------------------------------------------------------------------------------
1 | (require '[zajal.three :as t])
2 | (require '[zajal.core :as z])
3 |
4 | (defn step [x]
5 | (update x :angle #(+ 0.01 %)))
6 |
7 | (def box-geo (js/THREE.BoxGeometry. 0.1 0.1 0.1))
8 | (def mesh-mat (js/THREE.MeshBasicMaterial.
9 | #js {:wireframe true
10 | :color "white"}))
11 |
12 | (defn box [x y z r]
13 | (t/mesh
14 | #js {:position (js/THREE.Vector3. x y z)
15 | :rotation (js/THREE.Euler. 0 r 0)
16 | :geometry box-geo
17 | :material mesh-mat}))
18 |
19 |
20 | (defn draw [{:keys [angle]}]
21 | (t/rednerer
22 | {}
23 | (t/perspectiveCamera
24 | #js {:position (js/THREE.Vector3. 0 0 3)})
25 | (t/scene
26 | {}
27 | (into-array
28 | (for [x (range -8 8)
29 | y (range -8 8)]
30 | (box (/ x 8) (/ y 8) 0 (* x angle)))))))
31 |
32 | (z/sketch {} #'step #'draw)
--------------------------------------------------------------------------------
/js/loaders/ctm/license/OpenCTM.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009-2010 Marcus Geelnard
2 |
3 | This software is provided 'as-is', without any express or implied
4 | warranty. In no event will the authors be held liable for any damages
5 | arising from the use of this software.
6 |
7 | Permission is granted to anyone to use this software for any purpose,
8 | including commercial applications, and to alter it and redistribute it
9 | freely, subject to the following restrictions:
10 |
11 | 1. The origin of this software must not be misrepresented; you must not
12 | claim that you wrote the original software. If you use this software
13 | in a product, an acknowledgment in the product documentation would be
14 | appreciated but is not required.
15 |
16 | 2. Altered source versions must be plainly marked as such, and must not
17 | be misrepresented as being the original software.
18 |
19 | 3. This notice may not be removed or altered from any source
20 | distribution.
21 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | zajal
4 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2011-2016 Ramsey Nasser
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/js/loaders/ctm/license/js-lzma.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Juan Mellado
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/js/loaders/ctm/license/js-openctm.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 Juan Mellado
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/js/loaders/KMZLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.KMZLoader = function ( manager ) {
6 |
7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 |
9 | };
10 |
11 | THREE.KMZLoader.prototype = {
12 |
13 | constructor: THREE.KMZLoader,
14 |
15 | load: function ( url, onLoad, onProgress, onError ) {
16 |
17 | var scope = this;
18 |
19 | var loader = new THREE.XHRLoader( scope.manager );
20 | loader.setResponseType( 'arraybuffer' );
21 | loader.load( url, function ( text ) {
22 |
23 | onLoad( scope.parse( text ) );
24 |
25 | }, onProgress, onError );
26 |
27 | },
28 |
29 | parse: function ( data ) {
30 |
31 | var zip = new JSZip( data );
32 |
33 | // console.log( zip );
34 |
35 | for ( var name in zip.files ) {
36 |
37 | if ( name.toLowerCase().substr( - 4 ) === '.dae' ) {
38 |
39 | return new THREE.ColladaLoader().parse( zip.file( name ).asText() );
40 |
41 | }
42 |
43 | }
44 |
45 | console.error( 'KZMLoader: Couldn\'t find .dae file.' );
46 |
47 | return {
48 | scene: new THREE.Group()
49 | }
50 |
51 | }
52 |
53 | };
54 |
--------------------------------------------------------------------------------
/examples/other.cljs:
--------------------------------------------------------------------------------
1 | (ns examples.other
2 | (:require [zajal.draw.pixi :refer
3 | [sketch v2 v2+]]
4 | [zajal.alt.draw.pixi :as p]
5 | [zajal.input.keyboard :as keyboard]))
6 |
7 | (def init {:count 0})
8 |
9 | (defn step [state input]
10 | (if (empty? input)
11 | (update state :count inc)
12 | (update state :count + 3)))
13 |
14 | (defn draw [state]
15 | (p/renderer
16 | #js {:width 400
17 | :alpha true
18 | :height 400}
19 | #js [(p/container
20 | nil
21 | (loop [texts #js []
22 | i 400
23 | j 10]
24 | (.push texts (p/text #js {:text "Hello"
25 | :fill "white"
26 | :fontSize 18
27 | :rotation (* 0.01 (:count state))
28 | :x i
29 | :y j}))
30 | (cond
31 | (zero? i) (recur texts 400 (dec j))
32 | (zero? j) texts
33 | :else (recur texts (dec i) j))))]))
34 |
35 | (defonce the-sketch (sketch init #'step #'draw #'keyboard/key-set))
--------------------------------------------------------------------------------
/zajal/core.cljs:
--------------------------------------------------------------------------------
1 | (ns zajal.core)
2 |
3 |
4 | ;; input
5 |
6 | (defonce start-time (js/Date.now))
7 | (defonce mouse-state (atom {:x 0 :y 0 :pressed false}))
8 |
9 | (js/window.document.addEventListener
10 | "mousemove"
11 | #(swap! mouse-state
12 | assoc
13 | :x (.-pageX %)
14 | :y (.-pageY %)))
15 |
16 | (js/window.document.addEventListener
17 | "mousedown"
18 | #(swap! mouse-state
19 | assoc :pressed? true))
20 |
21 | (js/window.document.addEventListener
22 | "mouseup"
23 | #(swap! mouse-state
24 | assoc :pressed? false))
25 |
26 | (defn input []
27 | (let [t (js/Date.now)]
28 | {:time {:elapsed (- t start-time)}
29 | :mouse @mouse-state }))
30 |
31 | (defn sketch [start step draw]
32 | (let [state (atom start)
33 | renderer (js/renderer (js/document.querySelector "#sketch")
34 | (draw start))]
35 | (letfn [(render-loop [t]
36 | (.requestAnimationFrame js/window render-loop)
37 | (swap! state step (input))
38 | (.update renderer (draw @state)))]
39 | (render-loop 0)
40 | (.render renderer)
41 | ))
42 | :ok)
43 |
44 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | */bin/*
2 | */obj/*
3 |
4 | ## Xamarin
5 | #User Specific
6 | *.userprefs
7 | *.usertasks
8 |
9 | #Mono Project Files
10 | *.pidb
11 | *.resources
12 | test-results/
13 |
14 | ## OSX
15 | .DS_Store
16 | .AppleDouble
17 | .LSOverride
18 |
19 | # Icon must end with two \r
20 | Icon
21 |
22 |
23 | # Thumbnails
24 | ._*
25 |
26 | # Files that might appear in the root of a volume
27 | .DocumentRevisions-V100
28 | .fseventsd
29 | .Spotlight-V100
30 | .TemporaryItems
31 | .Trashes
32 | .VolumeIcon.icns
33 |
34 | # Directories potentially created on remote AFP share
35 | .AppleDB
36 | .AppleDesktop
37 | Network Trash Folder
38 | Temporary Items
39 | .apdisk
40 | # Logs
41 | logs
42 | *.log
43 | npm-debug.log*
44 |
45 | # Runtime data
46 | pids
47 | *.pid
48 | *.seed
49 | *.pid.lock
50 |
51 | # Directory for instrumented libs generated by jscoverage/JSCover
52 | lib-cov
53 |
54 | # Coverage directory used by tools like istanbul
55 | coverage
56 |
57 | # nyc test coverage
58 | .nyc_output
59 |
60 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
61 | .grunt
62 |
63 | # node-waf configuration
64 | .lock-wscript
65 |
66 | # Compiled binary addons (http://nodejs.org/api/addons.html)
67 | build/Release
68 |
69 | # Dependency directories
70 | node_modules
71 | jspm_packages
72 |
73 | # Optional npm cache directory
74 | .npm
75 |
76 | # Optional eslint cache
77 | .eslintcache
78 |
79 | # Optional REPL history
80 | .node_repl_history
81 |
82 | # Output of 'npm pack'
83 | *.tgz
84 |
--------------------------------------------------------------------------------
/scratch/virtual-vector.js:
--------------------------------------------------------------------------------
1 | //////// CLJS VECTOR SYNTAX ////////
2 |
3 | // copied from virtual-thunk.js
4 | cljs.core.PersistentVector.prototype.type = "vector-thunk";
5 |
6 | cljs.core.PersistentVector.prototype.fn = function() {
7 | return this.cljs$core$IIndexed$_nth$arity$3(null, 0);
8 | }
9 |
10 | cljs.core.PersistentVector.prototype.args = function() {
11 | return cljs.core.subvec.cljs$core$IFn$_invoke$arity$2(this, 1);
12 | }
13 |
14 | cljs.core.PersistentVector.prototype.evaluate = function() {
15 | if(!this._cache) {
16 | this._cache = cljs.core.apply(this.fn(), this.args());
17 | }
18 |
19 | return this._cache;
20 | }
21 |
22 | cljs.core.PersistentVector.prototype.render = function() {
23 | return this.evaluate().render();
24 | }
25 |
26 | function vectorSame(a, b) {
27 | if(cljs.core.count(a) !== cljs.core.count(b))
28 | return false;
29 |
30 | var same = true;
31 | var i = cljs.core.count(a);
32 |
33 | while(same && i--) {
34 | same = a.cljs$core$IIndexed$_nth$arity$3(null, i) === b.cljs$core$IIndexed$_nth$arity$3(null, i)
35 | }
36 |
37 | return same;
38 | }
39 |
40 | cljs.core.PersistentVector.prototype.reconcile = function(other) {
41 | if(this === other)
42 | return;
43 |
44 | if(this.type !== other.type)
45 | throw "Cannot reconcile " + this.type + " and " + other.type;
46 |
47 | // fn changed, rerun
48 | if(this.fn() !== other.fn()) {
49 | this._cache.reconcile(other.evaluate());
50 |
51 | // args changed, rerun
52 | } else if(!vectorSame(this.args(), other.args())) {
53 | this._cache.reconcile(other.evaluate());
54 |
55 | }
56 |
57 | // propagate the cache
58 | other._cache = this._cache;
59 | }
--------------------------------------------------------------------------------
/scratch/hello.cljs:
--------------------------------------------------------------------------------
1 | (ns hello
2 | (:require zajal
3 | there))
4 |
5 | ;; dom functions
6 | (defn vdom-node [name]
7 | (fn [& body]
8 | (if (map? (first body))
9 | (zajal/node name (clj->js (first body)) (rest body))
10 | (zajal/node name nil body))))
11 |
12 | (def p (vdom-node "p"))
13 | (def em (vdom-node "em"))
14 | (def div (vdom-node "div"))
15 | (defn text [& s]
16 | (zajal/text (apply str s)))
17 |
18 | (defn custom-node [name impl]
19 | (fn [& body]
20 | (if (map? (first body))
21 | (zajal/custom name (clj->js (first body)) (rest body))
22 | (zajal/custom name nil body)))
23 | )
24 |
25 | (def scene (vdom-node "scene"))
26 | (def line (vdom-node "line"))
27 | (def object3d (vdom-node "object3d"))
28 | (def mesh (vdom-node "mesh"))
29 | (def three (vdom-node "three"))
30 |
31 | ;; sketch
32 | (def start {:click-count 0})
33 |
34 | (defn step [state {:keys [mouse]}]
35 | (if (:pressed? mouse)
36 | (update state :click-count inc)
37 | state))
38 |
39 | (defn draw [{:keys [click-count]}]
40 | (div
41 | (p (text "Here We Go"))
42 | (zajal/custom
43 | {}
44 | (three {:background 0x2C3E50})
45 | {:diff (fn [a b]
46 | (when-not (identical? (.-tag a) (.-tag b))
47 | {}
48 | ))
49 | :render (fn [vdom]
50 | (case (.-tag vdom)
51 | "three" (let [gl (js/THREE.WebGLRenderer.)]
52 | (.setSize gl js/window.-innerWidth js/window.-innerHeight)
53 | ; (.. gl -domElement)
54 | )
55 | "scene" (js/THREE.Scene.)
56 | "line" (js/THREE.Line.)
57 | "mesh" (js/THREE.Mesh.)
58 | "object3d" (js/THREE.Object3D.)
59 | (throw (str "Unknown node type " (.-tag vdom)))
60 | ))}
61 | )))
62 |
63 | (defonce test-sketch
64 | (zajal/sketch start #'step #'draw))
65 |
--------------------------------------------------------------------------------
/scratch/virtual-thunk.js:
--------------------------------------------------------------------------------
1 | //////// THUNK NODE ////////
2 |
3 |
4 | ThunkNode = function(fn, args) {
5 | this.type = "thunk"
6 | this.fn = fn;
7 | this.args = args;
8 | this._cache = undefined;
9 | }
10 |
11 | ThunkNode.prototype.evaluate = function() {
12 | if(!this._cache) {
13 | this._cache = this.fn.apply(null, this.args);
14 | }
15 |
16 | return this._cache;
17 | }
18 |
19 | ThunkNode.prototype.render = function() {
20 | return this.evaluate().render();
21 | }
22 |
23 | function arraySame(a, b) {
24 | if(a.length !== b.length)
25 | return false;
26 |
27 | var same = true;
28 | var i = a.length;
29 |
30 | while(same && i--) {
31 | same = a[i] === b[i]
32 | }
33 |
34 | return same;
35 | }
36 |
37 | ThunkNode.prototype.reconcile = function(other) {
38 | if(this === other)
39 | return;
40 |
41 | if(this.type !== other.type)
42 | throw "Cannot reconcile " + this.type + " and " + other.type;
43 |
44 | // fn changed, rerun
45 | if(this.fn !== other.fn) {
46 | this._cache.reconcile(other.evaluate());
47 |
48 | // args changed, rerun
49 | } else if(!arraySame(this.args, other.args)) {
50 | this._cache.reconcile(other.evaluate());
51 |
52 | }
53 |
54 | // propagate the cache
55 | other._cache = this._cache;
56 | }
57 |
58 |
59 | //////// API ////////
60 |
61 | goog.provide('zajal.thunk');
62 |
63 | zajal.thunk.thunk = function(fn, args) { return new ThunkNode(fn, args); }
64 |
65 |
66 |
67 | //////// TEST ////////
68 |
69 |
70 | // function testInner(t, a) {
71 | // return mesh({
72 | // rotation: {x: 0, y: 0, z:+t * 0.001},
73 | // position: new THREE.Vector3(a, 0, 0),
74 | // geometry:boxGeo,
75 | // material:meshMat})
76 | // }
77 |
78 | // function thunkTest(t) {
79 | // return new ThreeRendererNode(
80 | // {},
81 | // new ThreePerspectiveCameraNode({fov:60, aspect: 1, near:1, far:1000}),
82 | // new ThreeSceneNode({},
83 | // [thunk(testInner, [1, 0]),
84 | // thunk(testInner, [1, 1]),
85 | // thunk(testInner, [1*2, 2]),
86 | // thunk(testInner, [1*3, 3]),
87 | // thunk(testInner, [t*4, 4]),
88 | // ]));
89 | // }
--------------------------------------------------------------------------------
/zajal/draw/pixi.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import pixi = require("pixi.js");
4 |
5 | namespace Zajal {
6 | function applyProperties(obj, props) {
7 | for (var k in props) {
8 | obj[k] = props[k];
9 | }
10 | }
11 |
12 | interface IVirtual {
13 | parent: IVirtual;
14 | pointer: T;
15 | render(): T;
16 | }
17 |
18 | export class Virtual implements IVirtual {
19 | parent: IVirtual;
20 | pointer: T;
21 | props;
22 | children: Container[];
23 | type = "";
24 |
25 | constructor (props?, children?) {
26 | this.props = props || {};
27 | this.children = children || [];
28 | }
29 |
30 | render() { return this.pointer; }
31 | reconcile(other:Virtual) {
32 | if(this.type != other.type) {
33 | // replace child
34 | } else {
35 | // same type, apply props
36 | applyProperties(this.render(), other.props);
37 | this.reconcileChildren(other);
38 | }
39 | }
40 |
41 | reconcileChildren(other:Virtual) {
42 | let minLength = Math.min(this.children.length, other.children.length);
43 | for (var i = 0; i < minLength; i++) {
44 | this.children[i].reconcile(other.children[i]);
45 | }
46 | if(other.children.length > this.children.length) {
47 | // more children
48 | for (var i = minLength; i < other.children.length; i++) {
49 | // this.pointer.a
50 | }
51 | }
52 | }
53 | }
54 |
55 | export class Applicaiton extends Virtual {
56 | stage = new Container();
57 | type = "application";
58 |
59 | // seems faster
60 | constructor (props?, children?) { super(props, children); }
61 |
62 | render() {
63 | if(!this.pointer) {
64 | this.pointer = new pixi.Application(this.props);
65 | for (var i = 0; i < this.children.length; ++i) {
66 | this.pointer.stage.addChild(this.children[i].render());
67 | this.children[i].parent = this.stage;
68 | }
69 | }
70 |
71 | return this.pointer;
72 | }
73 | }
74 |
75 | export class Container extends Virtual {
76 | render() {
77 | if(!this.pointer) {
78 | this.pointer = new pixi.Container();
79 | applyProperties(this.pointer, this.props);
80 | for (var i = 0; i < this.children.length; ++i) {
81 | this.pointer.addChild(this.children[i].render());
82 | this.children[i].parent = this;
83 | }
84 | }
85 |
86 | return this.pointer;
87 | }
88 | }
89 |
90 | export class Sprite extends Container {
91 | render() {
92 | if(!this.pointer) {
93 | this.pointer = pixi.Sprite.fromImage(this.props.image);
94 | applyProperties(this.pointer, this.props);
95 | for (var i = 0; i < this.children.length; ++i) {
96 | this.pointer.addChild(this.children[i].render());
97 | this.children[i].parent = this;
98 | }
99 | }
100 |
101 | return this.pointer;
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Zajal 0.6 - Atlantic
2 | ====================
3 | Atlantic is the next major rewrite of the Zajal creative coding framework. It is built on [ClojureScript](http://clojurescript.org), [Pixi.js](http://www.pixijs.com/), [ThreeJS](https://threejs.org/) and the ongoing progress of the [Arcadia project](https://github.com/arcadia-unity/arcadia). The goal, as always, is to create a highly expressive, completely live creative coding experience suitable for beginners and advanced programmers alike.
4 |
5 | This fourth rewrite, continues the exploration of Clojure and Lisp in the service of creative coding, but builds on a web-based stack of functional reactive tools that have become practical in recent years. This incarnation of Zajal, like the previous one and the Arcadia project, experiments with the effects of functional programming on high performance interactive graphics programming. The hope is to provide a real semantic alternative to the imperative painters-algorithm style tools available today.
6 |
7 | Status
8 | ------
9 | Extremely early, nothing beyond the basic concept has been demonstrated to work.
10 |
11 | ```clojure
12 | (defn step [x] (inc x))
13 |
14 | (defn draw [t]
15 | (renderer
16 | {:width 400
17 | :height 400}
18 | [(graphics {:shape (circle 20)
19 | :line-width 4
20 | :line-color 0xffffff
21 | :x 100
22 | :y 100})
23 | (graphics {:shape (polygon [0 -15 -10 10 0 5 10 10 0 -15])
24 | :fill 0
25 | :line-width 1
26 | :line-color 0xffffff
27 | :x 100
28 | :y 75
29 | :rotation (* 0.05 t)})
30 | (text {:text "Zajal!"
31 | :fill "white"
32 | :x 50})
33 | (graphics {:shape (polygon [0 -15 -10 10 0 5 10 10 0 -15])
34 | :fill 0
35 | :line-width 1
36 | :line-color 0xffffff
37 | :x 100
38 | :y 125
39 | :rotation (* 0.05 t)
40 | })]))
41 |
42 | (sketch 0 #'step #'draw)
43 | ```
44 |
45 | 
46 |
47 |
48 | Plan
49 | ----
50 | The trajectory of the project is towards a functional approach to creative coding that uses a "virtual scene graph" representation akin to [React's](https://facebook.github.io/react/). The current implementation is faster than any other similar technology for our purposes and can target the HTML DOM, ThreeJS scene graph, Pixi,js scene graph, or any other mutable tree-like data structure. The current prototype a minimal [JavaScript Electron](http://electron.atom.io/) application that loads ClojureScript and sets up a socket REPL and filewatcher.
51 |
52 | Using
53 | -----
54 | [Electron](http://electron.atom.io/) needs to be installed.
55 |
56 | git clone https://github.com/nasser/zajal.git
57 | cd zajal
58 | git checkout atlantic
59 | npm install
60 | electron . zajal/draw/pixi.cljs
61 |
62 | Legal
63 | -----
64 | Zajal is a labor of love by [Ramsey Nasser](http://nas.sr/). Use it for good, not evil.
65 |
66 | Provided under the MIT License.
67 |
68 | Support
69 | -------
70 | This project has been generously supported by the following institutions. They believed in it, challenged it, and pushed it forward. Zajal would be nowhere without them, and I thank them all deeply.
71 |
72 | - [Parsons The New School for Design](http://amt.parsons.edu/), where Zajal was my thesis project towards an MFA in Design and Technology
73 | - [Karaj Beirut](http://www.karajbeirut.org/), where Zajal was the focus of my residency
74 | - [Eyebeam Art and Technology Center](http://eyebeam.org/), where Zajal was part of my fellowship exploring code as self expression
--------------------------------------------------------------------------------
/js/loaders/PlayCanvasLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.PlayCanvasLoader = function ( manager ) {
6 |
7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 |
9 | };
10 |
11 | THREE.PlayCanvasLoader.prototype = {
12 |
13 | constructor: THREE.PlayCanvasLoader,
14 |
15 | load: function ( url, onLoad, onProgress, onError ) {
16 |
17 | var scope = this;
18 |
19 | var loader = new THREE.XHRLoader( scope.manager );
20 | loader.load( url, function ( text ) {
21 |
22 | onLoad( scope.parse( JSON.parse( text ) ) );
23 |
24 | }, onProgress, onError );
25 |
26 | },
27 |
28 | parse: function ( json ) {
29 |
30 | function parseVertices( data ) {
31 |
32 | var attributes = {};
33 |
34 | for ( var name in data ) {
35 |
36 | var attribute = data[ name ];
37 |
38 | var type = attribute.type;
39 | var size = attribute.components;
40 |
41 | var array;
42 |
43 | if ( type === 'float32' ) array = new Float32Array( attribute.data );
44 | if ( array === undefined ) console.log( 'PlayCanvasLoader: TODO', type );
45 |
46 | attributes[ name ] = new THREE.BufferAttribute( array, size );
47 |
48 | }
49 |
50 | data._attributes = attributes;
51 |
52 | }
53 |
54 | function parseMeshes( data ) {
55 |
56 | var geometry = new THREE.BufferGeometry();
57 |
58 | geometry.setIndex( new THREE.Uint16Attribute( data.indices, 1 ) );
59 |
60 | var attributes = model.vertices[ data.vertices ]._attributes;
61 |
62 | for ( var name in attributes ) {
63 |
64 | var attribute = attributes[ name ];
65 |
66 | if ( name === 'texCoord0' ) name = 'uv';
67 |
68 | geometry.addAttribute( name, attribute );
69 |
70 | }
71 |
72 | data._geometry = geometry;
73 |
74 | }
75 |
76 | function parseMeshInstances( data ) {
77 |
78 | var node = model.nodes[ data.node ];
79 | var mesh = model.meshes[ data.mesh ];
80 |
81 | if ( node._geometries === undefined ) {
82 |
83 | node._geometries = [];
84 |
85 | }
86 |
87 | node._geometries.push( mesh._geometry );
88 |
89 | }
90 |
91 | function parseNodes( data ) {
92 |
93 | var object = new THREE.Group();
94 | object.name = data.name;
95 |
96 | if ( data._geometries !== undefined ) {
97 |
98 | var material = new THREE.MeshPhongMaterial();
99 |
100 | for ( var i = 0; i < data._geometries.length; i ++ ) {
101 |
102 | var geometry = data._geometries[ i ];
103 |
104 | object.add( new THREE.Mesh( geometry, material ) );
105 |
106 | }
107 |
108 | }
109 |
110 | for ( var i = 0; i < data.rotation.length; i ++ ) {
111 |
112 | data.rotation[ i ] *= Math.PI / 180;
113 |
114 | }
115 |
116 | object.position.fromArray( data.position );
117 | object.rotation.fromArray( data.rotation );
118 | object.scale.fromArray( data.scale );
119 |
120 | data._object = object;
121 |
122 | }
123 |
124 | //
125 |
126 | console.log( json );
127 |
128 | var model = json.model;
129 |
130 | for ( var i = 0; i < model.vertices.length; i ++ ) {
131 |
132 | parseVertices( model.vertices[ i ] );
133 |
134 | }
135 |
136 | for ( var i = 0; i < model.meshes.length; i ++ ) {
137 |
138 | parseMeshes( model.meshes[ i ] );
139 |
140 | }
141 |
142 | for ( var i = 0; i < model.meshInstances.length; i ++ ) {
143 |
144 | parseMeshInstances( model.meshInstances[ i ] );
145 |
146 | }
147 |
148 | for ( var i = 0; i < model.nodes.length; i ++ ) {
149 |
150 | parseNodes( model.nodes[ i ] );
151 |
152 | }
153 |
154 | for ( var i = 0; i < model.parents.length; i ++ ) {
155 |
156 | var parent = model.parents[ i ];
157 |
158 | if ( parent === -1 ) continue;
159 |
160 | model.nodes[ parent ]._object.add( model.nodes[ i ]._object );
161 |
162 |
163 | }
164 |
165 | return model.nodes[ 0 ]._object;
166 |
167 | }
168 |
169 | };
170 |
--------------------------------------------------------------------------------
/zajal/draw/pixi.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | ///
3 | var __extends = (this && this.__extends) || (function () {
4 | var extendStatics = Object.setPrototypeOf ||
5 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7 | return function (d, b) {
8 | extendStatics(d, b);
9 | function __() { this.constructor = d; }
10 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
11 | };
12 | })();
13 | exports.__esModule = true;
14 | var pixi = require("pixi.js");
15 | var Zajal;
16 | (function (Zajal) {
17 | function applyProperties(obj, props) {
18 | for (var k in props) {
19 | obj[k] = props[k];
20 | }
21 | }
22 | var Virtual = (function () {
23 | function Virtual(props, children) {
24 | this.type = "";
25 | this.props = props || {};
26 | this.children = children || [];
27 | }
28 | Virtual.prototype.render = function () { return this.pointer; };
29 | Virtual.prototype.reconcile = function (other) {
30 | if (this.type != other.type) {
31 | // replace child
32 | }
33 | else {
34 | // same type, apply props
35 | applyProperties(this.render(), other.props);
36 | }
37 | };
38 | return Virtual;
39 | }());
40 | Zajal.Virtual = Virtual;
41 | var Applicaiton = (function (_super) {
42 | __extends(Applicaiton, _super);
43 | // seems faster
44 | function Applicaiton(props, children) {
45 | var _this = _super.call(this, props, children) || this;
46 | _this.stage = new Container();
47 | _this.type = "application";
48 | return _this;
49 | }
50 | Applicaiton.prototype.render = function () {
51 | if (!this.pointer) {
52 | this.pointer = new pixi.Application(this.props);
53 | for (var i = 0; i < this.children.length; ++i) {
54 | this.pointer.stage.addChild(this.children[i].render());
55 | this.children[i].parent = this.stage;
56 | }
57 | }
58 | return this.pointer;
59 | };
60 | return Applicaiton;
61 | }(Virtual));
62 | Zajal.Applicaiton = Applicaiton;
63 | var Container = (function (_super) {
64 | __extends(Container, _super);
65 | function Container() {
66 | return _super !== null && _super.apply(this, arguments) || this;
67 | }
68 | Container.prototype.render = function () {
69 | if (!this.pointer) {
70 | this.pointer = new pixi.Container();
71 | applyProperties(this.pointer, this.props);
72 | for (var i = 0; i < this.children.length; ++i) {
73 | this.pointer.addChild(this.children[i].render());
74 | this.children[i].parent = this;
75 | }
76 | }
77 | return this.pointer;
78 | };
79 | return Container;
80 | }(Virtual));
81 | Zajal.Container = Container;
82 | var Sprite = (function (_super) {
83 | __extends(Sprite, _super);
84 | function Sprite() {
85 | return _super !== null && _super.apply(this, arguments) || this;
86 | }
87 | Sprite.prototype.render = function () {
88 | if (!this.pointer) {
89 | this.pointer = pixi.Sprite.fromImage(this.props.image);
90 | applyProperties(this.pointer, this.props);
91 | for (var i = 0; i < this.children.length; ++i) {
92 | this.pointer.addChild(this.children[i].render());
93 | this.children[i].parent = this;
94 | }
95 | }
96 | return this.pointer;
97 | };
98 | return Sprite;
99 | }(Container));
100 | Zajal.Sprite = Sprite;
101 | })(Zajal || (Zajal = {}));
102 |
--------------------------------------------------------------------------------
/js/loaders/collada/AnimationHandler.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mikael emtinger / http://gomo.se/
3 | */
4 |
5 | THREE.AnimationHandler = {
6 |
7 | LINEAR: 0,
8 | CATMULLROM: 1,
9 | CATMULLROM_FORWARD: 2,
10 |
11 | //
12 |
13 | add: function () {
14 |
15 | console.warn( 'THREE.AnimationHandler.add() has been deprecated.' );
16 |
17 | },
18 | get: function () {
19 |
20 | console.warn( 'THREE.AnimationHandler.get() has been deprecated.' );
21 |
22 | },
23 | remove: function () {
24 |
25 | console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' );
26 |
27 | },
28 |
29 | //
30 |
31 | animations: [],
32 |
33 | init: function ( data ) {
34 |
35 | if ( data.initialized === true ) return data;
36 |
37 | // loop through all keys
38 |
39 | for ( var h = 0; h < data.hierarchy.length; h ++ ) {
40 |
41 | for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
42 |
43 | // remove minus times
44 |
45 | if ( data.hierarchy[ h ].keys[ k ].time < 0 ) {
46 |
47 | data.hierarchy[ h ].keys[ k ].time = 0;
48 |
49 | }
50 |
51 | // create quaternions
52 |
53 | if ( data.hierarchy[ h ].keys[ k ].rot !== undefined &&
54 | ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) {
55 |
56 | var quat = data.hierarchy[ h ].keys[ k ].rot;
57 | data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat );
58 |
59 | }
60 |
61 | }
62 |
63 | // prepare morph target keys
64 |
65 | if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) {
66 |
67 | // get all used
68 |
69 | var usedMorphTargets = {};
70 |
71 | for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
72 |
73 | for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
74 |
75 | var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ];
76 | usedMorphTargets[ morphTargetName ] = - 1;
77 |
78 | }
79 |
80 | }
81 |
82 | data.hierarchy[ h ].usedMorphTargets = usedMorphTargets;
83 |
84 |
85 | // set all used on all frames
86 |
87 | for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
88 |
89 | var influences = {};
90 |
91 | for ( var morphTargetName in usedMorphTargets ) {
92 |
93 | for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) {
94 |
95 | if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) {
96 |
97 | influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ];
98 | break;
99 |
100 | }
101 |
102 | }
103 |
104 | if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) {
105 |
106 | influences[ morphTargetName ] = 0;
107 |
108 | }
109 |
110 | }
111 |
112 | data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences;
113 |
114 | }
115 |
116 | }
117 |
118 |
119 | // remove all keys that are on the same time
120 |
121 | for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) {
122 |
123 | if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) {
124 |
125 | data.hierarchy[ h ].keys.splice( k, 1 );
126 | k --;
127 |
128 | }
129 |
130 | }
131 |
132 |
133 | // set index
134 |
135 | for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) {
136 |
137 | data.hierarchy[ h ].keys[ k ].index = k;
138 |
139 | }
140 |
141 | }
142 |
143 | data.initialized = true;
144 |
145 | return data;
146 |
147 | },
148 |
149 | parse: function ( root ) {
150 |
151 | var parseRecurseHierarchy = function ( root, hierarchy ) {
152 |
153 | hierarchy.push( root );
154 |
155 | for ( var c = 0; c < root.children.length; c ++ )
156 | parseRecurseHierarchy( root.children[ c ], hierarchy );
157 |
158 | };
159 |
160 | // setup hierarchy
161 |
162 | var hierarchy = [];
163 |
164 | if ( root instanceof THREE.SkinnedMesh ) {
165 |
166 | for ( var b = 0; b < root.skeleton.bones.length; b ++ ) {
167 |
168 | hierarchy.push( root.skeleton.bones[ b ] );
169 |
170 | }
171 |
172 | } else {
173 |
174 | parseRecurseHierarchy( root, hierarchy );
175 |
176 | }
177 |
178 | return hierarchy;
179 |
180 | },
181 |
182 | play: function ( animation ) {
183 |
184 | if ( this.animations.indexOf( animation ) === - 1 ) {
185 |
186 | this.animations.push( animation );
187 |
188 | }
189 |
190 | },
191 |
192 | stop: function ( animation ) {
193 |
194 | var index = this.animations.indexOf( animation );
195 |
196 | if ( index !== - 1 ) {
197 |
198 | this.animations.splice( index, 1 );
199 |
200 | }
201 |
202 | },
203 |
204 | update: function ( deltaTimeMS ) {
205 |
206 | for ( var i = 0; i < this.animations.length; i ++ ) {
207 |
208 | this.animations[ i ].resetBlendWeights();
209 |
210 | }
211 |
212 | for ( var i = 0; i < this.animations.length; i ++ ) {
213 |
214 | this.animations[ i ].update( deltaTimeMS );
215 |
216 | }
217 |
218 | }
219 |
220 | };
221 |
--------------------------------------------------------------------------------
/scratch/virtual-pixi.js:
--------------------------------------------------------------------------------
1 | //////// PIXI ////////
2 |
3 | function applyProps(obj, props) {
4 | for (var propKey in props) {
5 | obj[propKey] = props[propKey];
6 | }
7 | }
8 |
9 | function reconcile(self, other) {
10 | if(self.type != other.type) {
11 | var index = self._cache.parent.getChildIndex(self._cache);
12 | self._cache.parent.removeChildAt(index)
13 | self._cache.parent.addChildAt(other.render(), index);
14 | } else {
15 | applyProps(self._cache, other.props)
16 | other._cache = self._cache;
17 | }
18 | }
19 |
20 | function reconcileChildren(self, other) {
21 | var selfChildCount = (self.children && self.children.length || 0);
22 | var otherChildCount = (other.children && other.children.length || 0);
23 | var minLength = Math.min(selfChildCount, otherChildCount);
24 | for (var i = 0; i < minLength; i++) {
25 | self.children[i].reconcile(other.children[i]);
26 | }
27 | if(otherChildCount > selfChildCount) {
28 | // more elements
29 | for(var i = minLength; i< otherChildCount; i++) {
30 | self._cache.addChild(other.children[i].render());
31 | }
32 | } else {
33 | // fewer elements
34 | for(var i = minLength; i < selfChildCount; i++) {
35 | self._cache.removeChild(self.children[i].render());
36 | }
37 | }
38 | }
39 |
40 | function ApplicationNode(props, children) {
41 | this.type = "pixi-application"
42 | this.props = props;
43 | this.children = children;
44 | this._cache = undefined;
45 | }
46 |
47 | ApplicationNode.prototype.render = function() {
48 | if(!this._cache) {
49 | // create and cache node
50 | this._cache = new PIXI.Application(this.props.width || 800, this.props.height || 600, this.props);
51 |
52 | // normalize interface
53 | this._cache.removeChild = function(c) { return this._cache.stage.removeChild(c); }
54 | this._cache.addChild = function(c) { return this._cache.stage.addChild(c); }
55 |
56 | // render and add children
57 | var childCount = this.children.length;
58 | for (var i = 0; i < childCount; i++) {
59 | this._cache.stage.addChild(this.children[i].render());
60 | }
61 | }
62 |
63 | return this._cache;
64 | }
65 |
66 | ApplicationNode.prototype.reconcile = function(other) {
67 | if(this.type != other.type)
68 | throw "Different types not supported " + this.type + " vs " + other.type;
69 | reconcile(this, other);
70 | reconcileChildren(this, other);
71 | }
72 |
73 | function DisplayObjectNode(ctor, props, children) {
74 | this.type = "pixi-displayobject"
75 | this.ctor = ctor;
76 | this.props = props;
77 | this.children = children;
78 | this._cache = undefined;
79 | }
80 |
81 | DisplayObjectNode.prototype.render = function() {
82 | if(!this._cache) {
83 | this._cache = Object.create(this.ctor.prototype);
84 | this.ctor.call(this._cache);
85 | applyProps(this._cache, this.props)
86 | if(this.children) {
87 | var childCount = this.children.length;
88 | for (var i = 0; i < childCount; i++) {
89 | this._cache.addChild(this.children[i].render());
90 | }
91 | }
92 |
93 | }
94 | return this._cache;
95 | }
96 |
97 | DisplayObjectNode.prototype.reconcile = function(other) {
98 | if(this === other) return; // ???
99 | reconcile(this, other);
100 | reconcileChildren(this, other);
101 | };
102 |
103 | function SpriteNode(props, children) {
104 | this.type = "pixi-sprite"
105 | this.props = props;
106 | this.children = children;
107 | this._cache = undefined;
108 | }
109 |
110 | SpriteNode.prototype.render = function() {
111 | if(!this._cache) {
112 | this._cache = PIXI.Sprite.fromImage(this.props.image);
113 | applyProps(this._cache, this.props)
114 | if(this.children) {
115 | var childCount = this.children.length;
116 | for (var i = 0; i < childCount; i++) {
117 | this._cache.addChild(this.children[i].render());
118 | }
119 | }
120 |
121 | }
122 | return this._cache;
123 | }
124 |
125 | SpriteNode.prototype.reconcile = function(other) {
126 | if(this === other) return; // ???
127 | reconcile(this, other);
128 | reconcileChildren(this, other);
129 | };
130 |
131 | function TextNode(props, children) {
132 | this.type = "pixi-text"
133 | this.props = props;
134 | this.children = children;
135 | this._cache = undefined;
136 | }
137 |
138 | TextNode.prototype.render = function() {
139 | if(!this._cache) {
140 | this._cache = new PIXI.Text();
141 | applyProps(this._cache, this.props);
142 | applyProps(this._cache.style, this.props);
143 | if(this.children) {
144 | var childCount = this.children.length;
145 | for (var i = 0; i < childCount; i++) {
146 | this._cache.addChild(this.children[i].render());
147 | }
148 | }
149 |
150 | }
151 | return this._cache;
152 | }
153 |
154 | TextNode.prototype.reconcile = function(other) {
155 | if(this === other) return; // ???
156 | reconcile(this, other);
157 | applyProps(this._cache.style, this.props);
158 | reconcileChildren(this, other);
159 | };
160 |
161 |
162 |
163 | goog.provide('zajal.alt.draw.pixi');
164 |
165 | zajal.alt.draw.pixi.renderer = function(props, children) { return new ApplicationNode(props, children); }
166 | zajal.alt.draw.pixi.object = function(ctor, props, children) { return new DisplayObjectNode(ctor, props, children); }
167 | zajal.alt.draw.pixi.container = function(props, children) { return zajal.alt.draw.pixi.object(PIXI.Container, props, children); }
168 | zajal.alt.draw.pixi.sprite = function(props, children) { return new SpriteNode(props, children); }
169 | zajal.alt.draw.pixi.text = function(props, children) { return new TextNode(props, children); }
170 |
--------------------------------------------------------------------------------
/js/loaders/collada/KeyFrameAnimation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mikael emtinger / http://gomo.se/
3 | * @author mrdoob / http://mrdoob.com/
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author khang duong
6 | * @author erik kitson
7 | */
8 |
9 | THREE.KeyFrameAnimation = function ( data ) {
10 |
11 | this.root = data.node;
12 | this.data = THREE.AnimationHandler.init( data );
13 | this.hierarchy = THREE.AnimationHandler.parse( this.root );
14 | this.currentTime = 0;
15 | this.timeScale = 0.001;
16 | this.isPlaying = false;
17 | this.isPaused = true;
18 | this.loop = true;
19 |
20 | // initialize to first keyframes
21 |
22 | for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
23 |
24 | var keys = this.data.hierarchy[ h ].keys,
25 | sids = this.data.hierarchy[ h ].sids,
26 | obj = this.hierarchy[ h ];
27 |
28 | if ( keys.length && sids ) {
29 |
30 | for ( var s = 0; s < sids.length; s ++ ) {
31 |
32 | var sid = sids[ s ],
33 | next = this.getNextKeyWith( sid, h, 0 );
34 |
35 | if ( next ) {
36 |
37 | next.apply( sid );
38 |
39 | }
40 |
41 | }
42 |
43 | obj.matrixAutoUpdate = false;
44 | this.data.hierarchy[ h ].node.updateMatrix();
45 | obj.matrixWorldNeedsUpdate = true;
46 |
47 | }
48 |
49 | }
50 |
51 | };
52 |
53 | THREE.KeyFrameAnimation.prototype = {
54 |
55 | constructor: THREE.KeyFrameAnimation,
56 |
57 | play: function ( startTime ) {
58 |
59 | this.currentTime = startTime !== undefined ? startTime : 0;
60 |
61 | if ( this.isPlaying === false ) {
62 |
63 | this.isPlaying = true;
64 |
65 | // reset key cache
66 |
67 | var h, hl = this.hierarchy.length,
68 | object,
69 | node;
70 |
71 | for ( h = 0; h < hl; h ++ ) {
72 |
73 | object = this.hierarchy[ h ];
74 | node = this.data.hierarchy[ h ];
75 |
76 | if ( node.animationCache === undefined ) {
77 |
78 | node.animationCache = {};
79 | node.animationCache.prevKey = null;
80 | node.animationCache.nextKey = null;
81 | node.animationCache.originalMatrix = object.matrix;
82 |
83 | }
84 |
85 | var keys = this.data.hierarchy[ h ].keys;
86 |
87 | if ( keys.length > 1 ) {
88 |
89 | node.animationCache.prevKey = keys[ 0 ];
90 | node.animationCache.nextKey = keys[ 1 ];
91 |
92 | this.startTime = Math.min( keys[ 0 ].time, this.startTime );
93 | this.endTime = Math.max( keys[ keys.length - 1 ].time, this.endTime );
94 |
95 | }
96 |
97 | }
98 |
99 | this.update( 0 );
100 |
101 | }
102 |
103 | this.isPaused = false;
104 | },
105 |
106 | stop: function () {
107 |
108 | this.isPlaying = false;
109 | this.isPaused = false;
110 |
111 | // reset JIT matrix and remove cache
112 |
113 | for ( var h = 0; h < this.data.hierarchy.length; h ++ ) {
114 |
115 | var obj = this.hierarchy[ h ];
116 | var node = this.data.hierarchy[ h ];
117 |
118 | if ( node.animationCache !== undefined ) {
119 |
120 | var original = node.animationCache.originalMatrix;
121 |
122 | original.copy( obj.matrix );
123 | obj.matrix = original;
124 |
125 | delete node.animationCache;
126 |
127 | }
128 |
129 | }
130 |
131 | },
132 |
133 | update: function ( delta ) {
134 |
135 | if ( this.isPlaying === false ) return;
136 |
137 | this.currentTime += delta * this.timeScale;
138 |
139 | //
140 |
141 | var duration = this.data.length;
142 |
143 | if ( this.loop === true && this.currentTime > duration ) {
144 |
145 | this.currentTime %= duration;
146 |
147 | }
148 |
149 | this.currentTime = Math.min( this.currentTime, duration );
150 |
151 | for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
152 |
153 | var object = this.hierarchy[ h ];
154 | var node = this.data.hierarchy[ h ];
155 |
156 | var keys = node.keys,
157 | animationCache = node.animationCache;
158 |
159 |
160 | if ( keys.length ) {
161 |
162 | var prevKey = animationCache.prevKey;
163 | var nextKey = animationCache.nextKey;
164 |
165 | if ( nextKey.time <= this.currentTime ) {
166 |
167 | while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
168 |
169 | prevKey = nextKey;
170 | nextKey = keys[ prevKey.index + 1 ];
171 |
172 | }
173 |
174 | animationCache.prevKey = prevKey;
175 | animationCache.nextKey = nextKey;
176 |
177 | }
178 |
179 | if ( nextKey.time >= this.currentTime ) {
180 |
181 | prevKey.interpolate( nextKey, this.currentTime );
182 |
183 | } else {
184 |
185 | prevKey.interpolate( nextKey, nextKey.time );
186 |
187 | }
188 |
189 | this.data.hierarchy[ h ].node.updateMatrix();
190 | object.matrixWorldNeedsUpdate = true;
191 |
192 | }
193 |
194 | }
195 |
196 | },
197 |
198 | getNextKeyWith: function ( sid, h, key ) {
199 |
200 | var keys = this.data.hierarchy[ h ].keys;
201 | key = key % keys.length;
202 |
203 | for ( ; key < keys.length; key ++ ) {
204 |
205 | if ( keys[ key ].hasTarget( sid ) ) {
206 |
207 | return keys[ key ];
208 |
209 | }
210 |
211 | }
212 |
213 | return keys[ 0 ];
214 |
215 | },
216 |
217 | getPrevKeyWith: function ( sid, h, key ) {
218 |
219 | var keys = this.data.hierarchy[ h ].keys;
220 | key = key >= 0 ? key : key + keys.length;
221 |
222 | for ( ; key >= 0; key -- ) {
223 |
224 | if ( keys[ key ].hasTarget( sid ) ) {
225 |
226 | return keys[ key ];
227 |
228 | }
229 |
230 | }
231 |
232 | return keys[ keys.length - 1 ];
233 |
234 | }
235 |
236 | };
237 |
--------------------------------------------------------------------------------
/scratch/virtual-dom.js:
--------------------------------------------------------------------------------
1 | //////// TEXT NODE ////////
2 |
3 |
4 | var DomTextNode = function(text) {
5 | this.type = "text"
6 | this.text = text;
7 | this._cache = undefined;
8 | }
9 |
10 | DomTextNode.prototype.render = function() {
11 | if(!this._cache) {
12 | this._cache = document.createTextNode(this.text);
13 | }
14 |
15 | return this._cache;
16 | }
17 |
18 | DomTextNode.prototype.reconcile = function(other) {
19 | // TODO dont copy/paste
20 | // identical other, noop
21 | if(other === this)
22 | return;
23 |
24 | // different type, bail?
25 | if(other.type !== this.type) {
26 | throw "Cannot reconcile " + this.type + " and " + other.type;
27 | }
28 |
29 | if(other.text !== this.text) {
30 | this._cache.textContent = other.text;
31 | }
32 |
33 | other._cache = this._cache;
34 | }
35 |
36 |
37 |
38 | //////// DOM NODE ////////
39 |
40 |
41 | var DomNode = function(tag, props, children) {
42 | this.type = "dom";
43 | this.tag = tag;
44 | this.props = props;
45 | this.children = children;
46 | this._cache = undefined;
47 | }
48 |
49 | DomNode.prototype.render = function() {
50 | if(!this._cache) {
51 | this._cache = document.createElement(this.tag);
52 | var c = this.children;
53 | for (var i = 0; i < c.length; i++) {
54 | this._cache.appendChild(c[i].render());
55 | }
56 |
57 | var thisProps = this.props;
58 | for (var thisKey in thisProps) {
59 | this._cache.setAttribute(thisKey, thisProps[thisKey]);
60 | }
61 | }
62 |
63 | return this._cache;
64 | };
65 |
66 | DomNode.prototype.reconcile = function(other) {
67 | // identical other, noop
68 | if(other === this)
69 | return;
70 |
71 | // different type, bail?
72 | if(other.type !== this.type) {
73 | throw "Cannot reconcile " + this.type + " and " + other.type;
74 | }
75 |
76 | // different tags, replace this node
77 | // assumes this._cache
78 | if(other.tag !== this.tag) {
79 | var newRoot = other.render();
80 | this._cache.parentElement.replaceChild(newRoot, this._cache);
81 | return;
82 | }
83 |
84 | // other is a different dom node w/same tag
85 | this.reconcileProps(other);
86 | this.reconcileChildren(other);
87 |
88 | // propagate cache
89 | other._cache = this._cache;
90 | };
91 |
92 | DomNode.prototype.reconcileProps = function(other) {
93 | var _cache = this._cache
94 | var thisProps = this.props;
95 | var otherProps = other.props;
96 |
97 | // check for changed/removed props
98 | for (var thisKey in thisProps) {
99 | // key missing, remove attribute it
100 | if (!(thisKey in otherProps)) {
101 | _cache.removeAttribute(thisKey);
102 | }
103 |
104 | var thisValue = thisProps[thisKey];
105 | var otherValue = otherProps[thisKey];
106 |
107 | // same value, move on
108 | if(thisValue === otherValue)
109 | continue;
110 |
111 | // TODO deep prop diff?
112 | // changed value, update attribute
113 | _cache.setAttribute(thisKey, otherValue);
114 | }
115 |
116 | // check for new props
117 | for (var otherKey in otherProps) {
118 | if (!(otherKey in thisProps)) {
119 | _cache.setAttribute(otherKey, otherProps[otherValue]);
120 | }
121 | }
122 | };
123 |
124 | // TODO keyed nodes?
125 | DomNode.prototype.reconcileChildren = function(other) {
126 | var thisChildren = this.children;
127 | var otherChildren = other.children;
128 | var minLength = Math.min(thisChildren.length, otherChildren.length);
129 |
130 | for (var i = 0; i < minLength; i++) {
131 | thisChildren[i].reconcile(otherChildren[i]);
132 | };
133 |
134 | // new elements
135 | if(otherChildren.length > thisChildren.length) {
136 | for (var i = minLength; i < otherChildren.length; i++) {
137 | this._cache.appendChild(otherChildren[i].render());
138 | };
139 |
140 | // fewer elements
141 | } else if(thisChildren.length > otherChildren.length) {
142 | for (var i = minLength; i < thisChildren.length; i++) {
143 | this._cache.removeChild(thisChildren[i]._cache);
144 | };
145 | }
146 | };
147 |
148 | function createDomNode(tag) {
149 | return function(props, children) {
150 | return new DomNode(tag, props, children)
151 | }
152 | }
153 |
154 |
155 | //////// API ////////
156 |
157 | goog.provide('zajal.dom');
158 |
159 | zajal.dom.p = createDomNode("p");
160 | zajal.dom.h1 = createDomNode("h1");
161 | zajal.dom.h2 = createDomNode("h2");
162 | zajal.dom.h3 = createDomNode("h3");
163 | zajal.dom.h4 = createDomNode("h4");
164 | zajal.dom.h5 = createDomNode("h5");
165 | zajal.dom.strong = createDomNode("strong");
166 | zajal.dom.em = createDomNode("em");
167 | zajal.dom.div = createDomNode("div");
168 | zajal.dom.span = createDomNode("span");
169 | zajal.dom.text = function(t) { return new DomTextNode(t.toString()) };
170 |
171 |
172 | //////// TEST ////////
173 |
174 |
175 | // var domTest = div(
176 | // {class:"pismo"},
177 | // [h1({}, [text("Here we go")]),
178 | // p({}, [
179 | // text("lorem impsum "),
180 | // span({}, [text("the jake")]),
181 | // text(" another one")])])
182 |
183 | // var domTest2 = div(
184 | // {class:"pismo"},
185 | // [h1({}, [text("Here we go")]),
186 | // p({}, [
187 | // text("lorem impsum "),
188 | // em({}, [text("the jake")]),
189 | // text(" another one")])])
190 |
191 | // var domClock = function(t) {
192 | // return div(
193 | // {class:"the-clock"},
194 | // [h1({}, [text("The time")]),
195 | // p({}, [
196 | // text("seems to be "),
197 | // t.getSeconds() % 2 == 1 ?
198 | // em({}, [text(t.getMilliseconds())]) :
199 | // span({}, [text(t.getMilliseconds())]),
200 | // text(" o'clock")])])
201 | // }
202 |
--------------------------------------------------------------------------------
/examples/asteroid.cljs:
--------------------------------------------------------------------------------
1 | (ns examples.asteroid
2 | (:require [zajal.draw.pixi :refer
3 | [sketch
4 | v2 v2+
5 | circle polygon
6 | renderer container sprite graphics text thunk]]
7 | [zajal.input.keyboard :as keyboard]))
8 |
9 | (defn vmap [f coll]
10 | (let [length (count coll)]
11 | (loop [coll* (transient [])
12 | i 0]
13 | (if (< i length)
14 | (recur (conj! coll* (f (nth coll i)))
15 | (inc i))
16 | (persistent! coll*)))))
17 |
18 | (defn asteroid* [x y]
19 | {:position (v2 x y)
20 | :radius (+ 0.5 (rand))
21 | :speed (rand)
22 | :variation (inc (js/Math.floor (rand 3)))})
23 |
24 | (def init
25 | {:player
26 | {:position (v2 0 100)
27 | :rotation 0
28 | :inertia 0}
29 | :asteroids
30 | (vec
31 | (for [x (range -5 5)
32 | y (range -5 5)]
33 | (asteroid* (+ 300 (* x 100))
34 | (+ 300 (* y 100)))))})
35 |
36 | (defn horizontal [player input]
37 | (cond (:arrow-right input)
38 | (update player :rotation + (* (:inertia player) 0.05))
39 | (:arrow-left input)
40 | (update player :rotation - (* (:inertia player) 0.05))
41 | :else player))
42 |
43 | (defn vertical [player input]
44 | (let [forward (if (:arrow-up input) 1 0)
45 | rotation (:rotation player)
46 | inertia (:inertia player)
47 | movement (v2 (* (+ forward inertia) (js/Math.cos rotation))
48 | (* (+ forward inertia) (js/Math.sin rotation)))]
49 | (update player :position v2+ movement)))
50 |
51 | (defn inertia [player input]
52 | (if (and (:arrow-up input)
53 | (< (:inertia player)
54 | 3))
55 | (update player :inertia + 0.1)
56 | (update player :inertia * 0.9)))
57 |
58 | (defn controls [player input]
59 | (-> player
60 | (inertia input)
61 | (horizontal input)
62 | (vertical input)))
63 |
64 | (defn inside? [point circle]
65 | (let [p1 (:position point)
66 | p2 (:position circle)
67 | r (* 80 (:radius circle))
68 | distance-squared (+ (* (- (.-x p2) (.-x p1))
69 | (- (.-x p2) (.-x p1)))
70 | (* (- (.-y p2) (.-y p1))
71 | (- (.-y p2) (.-y p1))))
72 | r-sqared (* r r)]
73 | (< distance-squared r-sqared)))
74 |
75 | (defn collision [{:keys [player asteroids] :as state}]
76 | (let [asteroids* (map #(if (inside? player %)
77 | (assoc % :colliding? true)
78 | (assoc % :colliding? false))
79 | asteroids)]
80 | (-> state
81 | (assoc :asteroids asteroids*)
82 | (update :player assoc :colliding? (some :colliding? asteroids*)))))
83 |
84 | (defn spin [objs]
85 | (map #(update % :rotation + (* 0.1 (:speed %))) objs))
86 |
87 | (defn step [state input]
88 | (-> state
89 | (update :player controls input)
90 | collision
91 | (update :asteroids spin)
92 | (assoc :input input)))
93 |
94 | (defn debug [state]
95 | (container {}
96 | [(text {:text (str "asteroids: " (:asteroids state))
97 | :fill "white"
98 | :y 1
99 | :fontSize 8
100 | :fontFamily "Menlo"})
101 | (text {:text (str "input: " (:input state))
102 | :fill "white"
103 | :y 10
104 | :fontSize 8
105 | :fontFamily "Menlo"})]))
106 |
107 | (defn draw-ship [position rotation colliding?]
108 | [:container
109 | {:position position
110 | :rotation rotation}
111 | [:sprite
112 | {:image "art/Cars/car_blue_5.png"
113 | ; :cacheAsBitmap true
114 | :tint (if colliding? 0x00ff00 0xffffff)
115 | :anchor (v2 0.5 0.5)
116 | :scale (v2 0.25 0.25)
117 | :rotation 1.57075}]])
118 |
119 | (defn -draw-asteroid [{:keys [position colliding? radius shape rotation] :as asteroid}]
120 | (container {:position position
121 | :rotation rotation}
122 | [(graphics
123 | {:shape shape
124 | :fill (if colliding? 0x00ff00 0xf0ff0f)
125 | :alpha 1
126 | :line-width 1
127 | :line-color 0xffffff})
128 | (graphics
129 | {:shape (circle radius)
130 | :alpha 0
131 | :fill 0xffffff
132 | :line-color 0x00ff00
133 | :line-width 1})]))
134 |
135 | (defn draw-asteroid [{:keys [position variation colliding? radius shape rotation] :as asteroid}]
136 | (sprite {:position position
137 | :rotation rotation
138 | :tint (if colliding? 0xff0000 0xffffff)
139 | :scale (v2 radius radius)
140 | :anchor (v2 0.5 0.5)
141 | :image (str "art/Objects/rock" variation ".png")}))
142 |
143 | (defn readout [s]
144 | (text {:text s
145 | :fill "white"
146 | :alpha 1
147 | :x 10
148 | :y 10}))
149 |
150 |
151 | (defn _draw [{:keys [player input] :as state}]
152 | (renderer
153 | {:width 800
154 | :height 800
155 | :resolution 1}
156 | [(container {:position (v2 100 100)}
157 | [(container {} (map draw-asteroid (:asteroids state)))
158 | (draw-ship (:position player)
159 | (:rotation player)
160 | (:colliding? player))
161 | (thunk readout (str input))])]))
162 |
163 | (defn draw [{:keys [player input] :as state}]
164 | [:renderer {:width 800
165 | :height 800
166 | :resolution 1}
167 | [:container {:position (v2 100 100)}
168 | ; [:container {} (map draw-asteroid (:asteroids state))]
169 | [draw-ship
170 | (:position player)
171 | (:rotation player)
172 | (:colliding? player)]
173 | ; [readout (str input)]
174 | ]])
175 |
176 | (defonce the-sketch (sketch init #'step #'draw #'keyboard/key-set))
177 |
178 |
--------------------------------------------------------------------------------
/js/loaders/BabylonLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.BabylonLoader = function ( manager ) {
6 |
7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 |
9 | };
10 |
11 | THREE.BabylonLoader.prototype = {
12 |
13 | constructor: THREE.BabylonLoader,
14 |
15 | load: function ( url, onLoad, onProgress, onError ) {
16 |
17 | var scope = this;
18 |
19 | var loader = new THREE.XHRLoader( scope.manager );
20 | loader.load( url, function ( text ) {
21 |
22 | onLoad( scope.parse( JSON.parse( text ) ) );
23 |
24 | }, onProgress, onError );
25 |
26 | },
27 |
28 | parse: function ( json ) {
29 |
30 | var materials = this.parseMaterials( json );
31 | var scene = this.parseObjects( json, materials );
32 |
33 | return scene;
34 |
35 | },
36 |
37 | parseMaterials: function ( json ) {
38 |
39 | var materials = {};
40 |
41 | for ( var i = 0, l = json.materials.length; i < l; i ++ ) {
42 |
43 | var data = json.materials[ i ];
44 |
45 | var material = new THREE.MeshPhongMaterial();
46 | material.name = data.name;
47 | material.color.fromArray( data.diffuse );
48 | material.emissive.fromArray( data.emissive );
49 | material.specular.fromArray( data.specular );
50 | material.shininess = data.specularPower;
51 | material.opacity = data.alpha;
52 |
53 | materials[ data.id ] = material;
54 |
55 | }
56 |
57 | if ( json.multiMaterials ) {
58 |
59 | for ( var i = 0, l = json.multiMaterials.length; i < l; i ++ ) {
60 |
61 | var data = json.multiMaterials[ i ];
62 |
63 | console.warn( 'THREE.BabylonLoader: Multi materials not yet supported.' );
64 |
65 | materials[ data.id ] = new THREE.MeshPhongMaterial();
66 |
67 | }
68 |
69 | }
70 |
71 | return materials;
72 |
73 | },
74 |
75 | parseGeometry: function ( json ) {
76 |
77 | var geometry = new THREE.BufferGeometry();
78 |
79 | // indices
80 |
81 | var indices = new Uint16Array( json.indices );
82 |
83 | geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );
84 |
85 | // positions
86 |
87 | var positions = new Float32Array( json.positions );
88 |
89 | for ( var j = 2, jl = positions.length; j < jl; j += 3 ) {
90 |
91 | positions[ j ] = - positions[ j ];
92 |
93 | }
94 |
95 | geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
96 |
97 | // normals
98 |
99 | if ( json.normals ) {
100 |
101 | var normals = new Float32Array( json.normals );
102 |
103 | for ( var j = 2, jl = normals.length; j < jl; j += 3 ) {
104 |
105 | normals[ j ] = - normals[ j ];
106 |
107 | }
108 |
109 | geometry.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
110 |
111 | }
112 |
113 | // uvs
114 |
115 | if ( json.uvs ) {
116 |
117 | var uvs = new Float32Array( json.uvs );
118 |
119 | geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
120 |
121 | }
122 |
123 | // offsets
124 |
125 | var subMeshes = json.subMeshes;
126 |
127 | if ( subMeshes ) {
128 |
129 | for ( var j = 0, jl = subMeshes.length; j < jl; j ++ ) {
130 |
131 | var subMesh = subMeshes[ j ];
132 |
133 | geometry.addGroup( subMesh.indexStart, subMesh.indexCount );
134 |
135 | }
136 |
137 | }
138 |
139 | return geometry;
140 |
141 | },
142 |
143 | parseObjects: function ( json, materials ) {
144 |
145 | var objects = {};
146 | var scene = new THREE.Scene();
147 |
148 | var cameras = json.cameras;
149 |
150 | for ( var i = 0, l = cameras.length; i < l; i ++ ) {
151 |
152 | var data = cameras[ i ];
153 |
154 | var camera = new THREE.PerspectiveCamera( ( data.fov / Math.PI ) * 180, 1.33, data.minZ, data.maxZ );
155 |
156 | camera.name = data.name;
157 | camera.position.fromArray( data.position );
158 | if ( data.rotation ) camera.rotation.fromArray( data.rotation );
159 |
160 | objects[ data.id ] = camera;
161 |
162 | }
163 |
164 | var lights = json.lights;
165 |
166 | for ( var i = 0, l = lights.length; i < l; i ++ ) {
167 |
168 | var data = lights[ i ];
169 |
170 | var light;
171 |
172 | switch ( data.type ) {
173 |
174 | case 0:
175 | light = new THREE.PointLight();
176 | break;
177 |
178 | case 1:
179 | light = new THREE.DirectionalLight();
180 | break;
181 |
182 | case 2:
183 | light = new THREE.SpotLight();
184 | break;
185 |
186 | case 3:
187 | light = new THREE.HemisphereLight();
188 | break;
189 | }
190 |
191 | light.name = data.name;
192 | if ( data.position ) light.position.set( data.position[ 0 ], data.position[ 1 ], - data.position[ 2 ] );
193 | light.color.fromArray( data.diffuse );
194 | if ( data.groundColor ) light.groundColor.fromArray( data.groundColor );
195 | if ( data.intensity ) light.intensity = data.intensity;
196 |
197 | objects[ data.id ] = light;
198 |
199 | scene.add( light );
200 |
201 | }
202 |
203 | var meshes = json.meshes;
204 |
205 | for ( var i = 0, l = meshes.length; i < l; i ++ ) {
206 |
207 | var data = meshes[ i ];
208 |
209 | var object;
210 |
211 | if ( data.indices ) {
212 |
213 | var geometry = this.parseGeometry( data );
214 |
215 | object = new THREE.Mesh( geometry, materials[ data.materialId ] );
216 |
217 | } else {
218 |
219 | object = new THREE.Group();
220 |
221 | }
222 |
223 | object.name = data.name;
224 | object.position.set( data.position[ 0 ], data.position[ 1 ], - data.position[ 2 ] );
225 | object.rotation.fromArray( data.rotation );
226 | if ( data.rotationQuaternion ) object.quaternion.fromArray( data.rotationQuaternion );
227 | object.scale.fromArray( data.scaling );
228 | // object.visible = data.isVisible;
229 |
230 | if ( data.parentId ) {
231 |
232 | objects[ data.parentId ].add( object );
233 |
234 | } else {
235 |
236 | scene.add( object );
237 |
238 | }
239 |
240 | objects[ data.id ] = object;
241 |
242 | }
243 |
244 | return scene;
245 |
246 | }
247 |
248 | };
249 |
--------------------------------------------------------------------------------
/js/loaders/gltf/glTFAnimation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Tony Parisi / http://www.tonyparisi.com/
3 | */
4 |
5 | THREE.glTFAnimator = ( function () {
6 |
7 | var animators = [];
8 |
9 | return {
10 | add : function(animator)
11 | {
12 | animators.push(animator);
13 | },
14 |
15 | remove: function(animator)
16 | {
17 |
18 | var i = animators.indexOf(animator);
19 |
20 | if ( i !== -1 ) {
21 | animators.splice( i, 1 );
22 | }
23 | },
24 |
25 | update : function()
26 | {
27 | for (i = 0; i < animators.length; i ++)
28 | {
29 | animators[i].update();
30 | }
31 | },
32 | };
33 | })();
34 |
35 | // Construction/initialization
36 | THREE.glTFAnimation = function(interps)
37 | {
38 | this.running = false;
39 | this.loop = false;
40 | this.duration = 0;
41 | this.startTime = 0;
42 | this.interps = [];
43 |
44 | if (interps)
45 | {
46 | this.createInterpolators(interps);
47 | }
48 | };
49 |
50 | THREE.glTFAnimation.prototype.createInterpolators = function(interps)
51 | {
52 | var i, len = interps.length;
53 | for (i = 0; i < len; i ++)
54 | {
55 | var interp = new THREE.glTFInterpolator(interps[i]);
56 | this.interps.push(interp);
57 | this.duration = Math.max(this.duration, interp.duration);
58 | }
59 | };
60 |
61 | // Start/stop
62 | THREE.glTFAnimation.prototype.play = function()
63 | {
64 | if (this.running)
65 | return;
66 |
67 | this.startTime = Date.now();
68 | this.running = true;
69 | THREE.glTFAnimator.add(this);
70 | };
71 |
72 | THREE.glTFAnimation.prototype.stop = function()
73 | {
74 | this.running = false;
75 | THREE.glTFAnimator.remove(this);
76 | };
77 |
78 | // Update - drive key frame evaluation
79 | THREE.glTFAnimation.prototype.update = function()
80 | {
81 | if (!this.running)
82 | return;
83 |
84 | var now = Date.now();
85 | var deltat = (now - this.startTime) / 1000;
86 | var t = deltat % this.duration;
87 | var nCycles = Math.floor(deltat / this.duration);
88 |
89 | if (nCycles >= 1 && !this.loop)
90 | {
91 | this.running = false;
92 | var i, len = this.interps.length;
93 | for (i = 0; i < len; i ++)
94 | {
95 | this.interps[i].interp(this.duration);
96 | }
97 | this.stop();
98 | return;
99 | }
100 | else
101 | {
102 | var i, len = this.interps.length;
103 | for (i = 0; i < len; i ++)
104 | {
105 | this.interps[i].interp(t);
106 | }
107 | }
108 | };
109 |
110 | //Interpolator class
111 | //Construction/initialization
112 | THREE.glTFInterpolator = function(param)
113 | {
114 | this.keys = param.keys;
115 | this.values = param.values;
116 | this.count = param.count;
117 | this.type = param.type;
118 | this.path = param.path;
119 | this.isRot = false;
120 |
121 | var node = param.target;
122 | node.updateMatrix();
123 | node.matrixAutoUpdate = true;
124 | this.targetNode = node;
125 |
126 | switch (param.path) {
127 | case "translation" :
128 | this.target = node.position;
129 | this.originalValue = node.position.clone();
130 | break;
131 | case "rotation" :
132 | this.target = node.quaternion;
133 | this.originalValue = node.quaternion.clone();
134 | this.isRot = true;
135 | break;
136 | case "scale" :
137 | this.target = node.scale;
138 | this.originalValue = node.scale.clone();
139 | break;
140 | }
141 |
142 | this.duration = this.keys[this.count - 1];
143 |
144 | this.vec1 = new THREE.Vector3;
145 | this.vec2 = new THREE.Vector3;
146 | this.vec3 = new THREE.Vector3;
147 | this.quat1 = new THREE.Quaternion;
148 | this.quat2 = new THREE.Quaternion;
149 | this.quat3 = new THREE.Quaternion;
150 | };
151 |
152 | //Interpolation and tweening methods
153 | THREE.glTFInterpolator.prototype.interp = function(t)
154 | {
155 | var i, j;
156 | if (t == this.keys[0])
157 | {
158 | if (this.isRot) {
159 | this.quat3.set(this.values[0], this.values[1], this.values[2], this.values[3]);
160 | }
161 | else {
162 | this.vec3.set(this.values[0], this.values[1], this.values[2]);
163 | }
164 | }
165 | else if (t < this.keys[0])
166 | {
167 | if (this.isRot) {
168 | this.quat1.set(this.originalValue.x,
169 | this.originalValue.y,
170 | this.originalValue.z,
171 | this.originalValue.w);
172 | this.quat2.set(this.values[0],
173 | this.values[1],
174 | this.values[2],
175 | this.values[3]);
176 | THREE.Quaternion.slerp(this.quat1, this.quat2, this.quat3, t / this.keys[0]);
177 | }
178 | else {
179 | this.vec3.set(this.originalValue.x,
180 | this.originalValue.y,
181 | this.originalValue.z);
182 | this.vec2.set(this.values[0],
183 | this.values[1],
184 | this.values[2]);
185 |
186 | this.vec3.lerp(this.vec2, t / this.keys[0]);
187 | }
188 | }
189 | else if (t >= this.keys[this.count - 1])
190 | {
191 | if (this.isRot) {
192 | this.quat3.set(this.values[(this.count - 1) * 4],
193 | this.values[(this.count - 1) * 4 + 1],
194 | this.values[(this.count - 1) * 4 + 2],
195 | this.values[(this.count - 1) * 4 + 3]);
196 | }
197 | else {
198 | this.vec3.set(this.values[(this.count - 1) * 3],
199 | this.values[(this.count - 1) * 3 + 1],
200 | this.values[(this.count - 1) * 3 + 2]);
201 | }
202 | }
203 | else
204 | {
205 | for (i = 0; i < this.count - 1; i ++)
206 | {
207 | var key1 = this.keys[i];
208 | var key2 = this.keys[i + 1];
209 |
210 | if (t >= key1 && t <= key2)
211 | {
212 | if (this.isRot) {
213 | this.quat1.set(this.values[i * 4],
214 | this.values[i * 4 + 1],
215 | this.values[i * 4 + 2],
216 | this.values[i * 4 + 3]);
217 | this.quat2.set(this.values[(i + 1) * 4],
218 | this.values[(i + 1) * 4 + 1],
219 | this.values[(i + 1) * 4 + 2],
220 | this.values[(i + 1) * 4 + 3]);
221 | THREE.Quaternion.slerp(this.quat1, this.quat2, this.quat3, (t - key1) / (key2 - key1));
222 | }
223 | else {
224 | this.vec3.set(this.values[i * 3],
225 | this.values[i * 3 + 1],
226 | this.values[i * 3 + 2]);
227 | this.vec2.set(this.values[(i + 1) * 3],
228 | this.values[(i + 1) * 3 + 1],
229 | this.values[(i + 1) * 3 + 2]);
230 |
231 | this.vec3.lerp(this.vec2, (t - key1) / (key2 - key1));
232 | }
233 | }
234 | }
235 | }
236 |
237 | if (this.target)
238 | {
239 | this.copyValue(this.target);
240 | }
241 | };
242 |
243 | THREE.glTFInterpolator.prototype.copyValue = function(target) {
244 |
245 | if (this.isRot) {
246 | target.copy(this.quat3);
247 | }
248 | else {
249 | target.copy(this.vec3);
250 | }
251 | };
252 |
--------------------------------------------------------------------------------
/js/loaders/ctm/CTMLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Loader for CTM encoded models generated by OpenCTM tools:
3 | * http://openctm.sourceforge.net/
4 | *
5 | * Uses js-openctm library by Juan Mellado
6 | * http://code.google.com/p/js-openctm/
7 | *
8 | * @author alteredq / http://alteredqualia.com/
9 | */
10 |
11 | THREE.CTMLoader = function () {
12 |
13 | THREE.Loader.call( this );
14 |
15 | // Deprecated
16 |
17 | Object.defineProperties( this, {
18 | statusDomElement: {
19 | get: function () {
20 |
21 | if ( this._statusDomElement === undefined ) {
22 |
23 | this._statusDomElement = document.createElement( 'div' );
24 |
25 | }
26 |
27 | console.warn( 'THREE.BinaryLoader: .statusDomElement has been removed.' );
28 | return this._statusDomElement;
29 |
30 | }
31 | },
32 | } );
33 |
34 | };
35 |
36 | THREE.CTMLoader.prototype = Object.create( THREE.Loader.prototype );
37 | THREE.CTMLoader.prototype.constructor = THREE.CTMLoader;
38 |
39 | // Load multiple CTM parts defined in JSON
40 |
41 | THREE.CTMLoader.prototype.loadParts = function( url, callback, parameters ) {
42 |
43 | parameters = parameters || {};
44 |
45 | var scope = this;
46 |
47 | var xhr = new XMLHttpRequest();
48 |
49 | var basePath = parameters.basePath ? parameters.basePath : this.extractUrlBase( url );
50 |
51 | xhr.onreadystatechange = function() {
52 |
53 | if ( xhr.readyState === 4 ) {
54 |
55 | if ( xhr.status === 200 || xhr.status === 0 ) {
56 |
57 | var jsonObject = JSON.parse( xhr.responseText );
58 |
59 | var materials = [], geometries = [], counter = 0;
60 |
61 | function callbackFinal( geometry ) {
62 |
63 | counter += 1;
64 |
65 | geometries.push( geometry );
66 |
67 | if ( counter === jsonObject.offsets.length ) {
68 |
69 | callback( geometries, materials );
70 |
71 | }
72 |
73 | }
74 |
75 |
76 | // init materials
77 |
78 | for ( var i = 0; i < jsonObject.materials.length; i ++ ) {
79 |
80 | materials[ i ] = scope.createMaterial( jsonObject.materials[ i ], basePath );
81 |
82 | }
83 |
84 | // load joined CTM file
85 |
86 | var partUrl = basePath + jsonObject.data;
87 | var parametersPart = { useWorker: parameters.useWorker, offsets: jsonObject.offsets };
88 | scope.load( partUrl, callbackFinal, parametersPart );
89 |
90 | }
91 |
92 | }
93 |
94 | };
95 |
96 | xhr.open( "GET", url, true );
97 | xhr.setRequestHeader( "Content-Type", "text/plain" );
98 | xhr.send( null );
99 |
100 | };
101 |
102 | // Load CTMLoader compressed models
103 | // - parameters
104 | // - url (required)
105 | // - callback (required)
106 |
107 | THREE.CTMLoader.prototype.load = function( url, callback, parameters ) {
108 |
109 | parameters = parameters || {};
110 |
111 | var scope = this;
112 |
113 | var offsets = parameters.offsets !== undefined ? parameters.offsets : [ 0 ];
114 |
115 | var xhr = new XMLHttpRequest(),
116 | callbackProgress = null;
117 |
118 | var length = 0;
119 |
120 | xhr.onreadystatechange = function() {
121 |
122 | if ( xhr.readyState === 4 ) {
123 |
124 | if ( xhr.status === 200 || xhr.status === 0 ) {
125 |
126 | var binaryData = new Uint8Array(xhr.response);
127 |
128 | var s = Date.now();
129 |
130 | if ( parameters.useWorker ) {
131 |
132 | var worker = parameters.worker || new Worker( "js/loaders/ctm/CTMWorker.js" );
133 |
134 | worker.onmessage = function( event ) {
135 |
136 | var files = event.data;
137 |
138 | for ( var i = 0; i < files.length; i ++ ) {
139 |
140 | var ctmFile = files[ i ];
141 |
142 | var e1 = Date.now();
143 | // console.log( "CTM data parse time [worker]: " + (e1-s) + " ms" );
144 |
145 | scope.createModel( ctmFile, callback );
146 |
147 | var e = Date.now();
148 | console.log( "model load time [worker]: " + (e - e1) + " ms, total: " + (e - s));
149 |
150 | }
151 |
152 |
153 | };
154 |
155 | worker.postMessage( { "data": binaryData, "offsets": offsets } );
156 |
157 | } else {
158 |
159 | for ( var i = 0; i < offsets.length; i ++ ) {
160 |
161 | var stream = new CTM.Stream( binaryData );
162 | stream.offset = offsets[ i ];
163 |
164 | var ctmFile = new CTM.File( stream );
165 |
166 | scope.createModel( ctmFile, callback );
167 |
168 | }
169 |
170 | //var e = Date.now();
171 | //console.log( "CTM data parse time [inline]: " + (e-s) + " ms" );
172 |
173 | }
174 |
175 | } else {
176 |
177 | console.error( "Couldn't load [" + url + "] [" + xhr.status + "]" );
178 |
179 | }
180 |
181 | } else if ( xhr.readyState === 3 ) {
182 |
183 | if ( callbackProgress ) {
184 |
185 | if ( length === 0 ) {
186 |
187 | length = xhr.getResponseHeader( "Content-Length" );
188 |
189 | }
190 |
191 | callbackProgress( { total: length, loaded: xhr.responseText.length } );
192 |
193 | }
194 |
195 | } else if ( xhr.readyState === 2 ) {
196 |
197 | length = xhr.getResponseHeader( "Content-Length" );
198 |
199 | }
200 |
201 | };
202 |
203 | xhr.open( "GET", url, true );
204 | xhr.responseType = "arraybuffer";
205 |
206 | xhr.send( null );
207 |
208 | };
209 |
210 |
211 | THREE.CTMLoader.prototype.createModel = function ( file, callback ) {
212 |
213 | var Model = function () {
214 |
215 | THREE.BufferGeometry.call( this );
216 |
217 | this.materials = [];
218 |
219 | var indices = file.body.indices,
220 | positions = file.body.vertices,
221 | normals = file.body.normals;
222 |
223 | var uvs, colors;
224 |
225 | var uvMaps = file.body.uvMaps;
226 |
227 | if ( uvMaps !== undefined && uvMaps.length > 0 ) {
228 |
229 | uvs = uvMaps[ 0 ].uv;
230 |
231 | }
232 |
233 | var attrMaps = file.body.attrMaps;
234 |
235 | if ( attrMaps !== undefined && attrMaps.length > 0 && attrMaps[ 0 ].name === 'Color' ) {
236 |
237 | colors = attrMaps[ 0 ].attr;
238 |
239 | }
240 |
241 | this.setIndex( new THREE.BufferAttribute( indices, 1 ) );
242 | this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
243 |
244 | if ( normals !== undefined ) {
245 |
246 | this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) );
247 |
248 | }
249 |
250 | if ( uvs !== undefined ) {
251 |
252 | this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
253 |
254 | }
255 |
256 | if ( colors !== undefined ) {
257 |
258 | this.addAttribute( 'color', new THREE.BufferAttribute( colors, 4 ) );
259 |
260 | }
261 |
262 | };
263 |
264 | Model.prototype = Object.create( THREE.BufferGeometry.prototype );
265 | Model.prototype.constructor = Model;
266 |
267 | var geometry = new Model();
268 |
269 | // compute vertex normals if not present in the CTM model
270 | if ( geometry.attributes.normal === undefined ) {
271 | geometry.computeVertexNormals();
272 | }
273 |
274 | callback( geometry );
275 |
276 | };
277 |
--------------------------------------------------------------------------------
/js/loaders/PDBLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | */
4 |
5 | THREE.PDBLoader = function ( manager ) {
6 |
7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 |
9 | };
10 |
11 | THREE.PDBLoader.prototype = {
12 |
13 | constructor: THREE.PDBLoader,
14 |
15 | load: function ( url, onLoad, onProgress, onError ) {
16 |
17 | var scope = this;
18 |
19 | var loader = new THREE.XHRLoader( scope.manager );
20 | loader.load( url, function ( text ) {
21 |
22 | var json = scope.parsePDB( text );
23 | scope.createModel( json, onLoad );
24 |
25 | }, onProgress, onError );
26 |
27 | },
28 |
29 | // Based on CanvasMol PDB parser
30 |
31 | parsePDB: function ( text ) {
32 |
33 | function trim( text ) {
34 |
35 | return text.replace( /^\s\s*/, '' ).replace( /\s\s*$/, '' );
36 |
37 | }
38 |
39 | function capitalize( text ) {
40 |
41 | return text.charAt( 0 ).toUpperCase() + text.substr( 1 ).toLowerCase();
42 |
43 | }
44 |
45 | function hash( s, e ) {
46 |
47 | return "s" + Math.min( s, e ) + "e" + Math.max( s, e );
48 |
49 | }
50 |
51 | function parseBond( start, length ) {
52 |
53 | var eatom = parseInt( lines[ i ].substr( start, length ) );
54 |
55 | if ( eatom ) {
56 |
57 | var h = hash( satom, eatom );
58 |
59 | if ( bhash[ h ] == undefined ) {
60 |
61 | bonds.push( [ satom - 1, eatom - 1, 1 ] );
62 | bhash[ h ] = bonds.length - 1;
63 |
64 | } else {
65 |
66 | // doesn't really work as almost all PDBs
67 | // have just normal bonds appearing multiple
68 | // times instead of being double/triple bonds
69 | // bonds[bhash[h]][2] += 1;
70 |
71 | }
72 |
73 | }
74 |
75 | }
76 |
77 | var CPK = { "h": [ 255, 255, 255 ], "he": [ 217, 255, 255 ], "li": [ 204, 128, 255 ], "be": [ 194, 255, 0 ], "b": [ 255, 181, 181 ], "c": [ 144, 144, 144 ], "n": [ 48, 80, 248 ], "o": [ 255, 13, 13 ], "f": [ 144, 224, 80 ], "ne": [ 179, 227, 245 ], "na": [ 171, 92, 242 ], "mg": [ 138, 255, 0 ], "al": [ 191, 166, 166 ], "si": [ 240, 200, 160 ], "p": [ 255, 128, 0 ], "s": [ 255, 255, 48 ], "cl": [ 31, 240, 31 ], "ar": [ 128, 209, 227 ], "k": [ 143, 64, 212 ], "ca": [ 61, 255, 0 ], "sc": [ 230, 230, 230 ], "ti": [ 191, 194, 199 ], "v": [ 166, 166, 171 ], "cr": [ 138, 153, 199 ], "mn": [ 156, 122, 199 ], "fe": [ 224, 102, 51 ], "co": [ 240, 144, 160 ], "ni": [ 80, 208, 80 ], "cu": [ 200, 128, 51 ], "zn": [ 125, 128, 176 ], "ga": [ 194, 143, 143 ], "ge": [ 102, 143, 143 ], "as": [ 189, 128, 227 ], "se": [ 255, 161, 0 ], "br": [ 166, 41, 41 ], "kr": [ 92, 184, 209 ], "rb": [ 112, 46, 176 ], "sr": [ 0, 255, 0 ], "y": [ 148, 255, 255 ], "zr": [ 148, 224, 224 ], "nb": [ 115, 194, 201 ], "mo": [ 84, 181, 181 ], "tc": [ 59, 158, 158 ], "ru": [ 36, 143, 143 ], "rh": [ 10, 125, 140 ], "pd": [ 0, 105, 133 ], "ag": [ 192, 192, 192 ], "cd": [ 255, 217, 143 ], "in": [ 166, 117, 115 ], "sn": [ 102, 128, 128 ], "sb": [ 158, 99, 181 ], "te": [ 212, 122, 0 ], "i": [ 148, 0, 148 ], "xe": [ 66, 158, 176 ], "cs": [ 87, 23, 143 ], "ba": [ 0, 201, 0 ], "la": [ 112, 212, 255 ], "ce": [ 255, 255, 199 ], "pr": [ 217, 255, 199 ], "nd": [ 199, 255, 199 ], "pm": [ 163, 255, 199 ], "sm": [ 143, 255, 199 ], "eu": [ 97, 255, 199 ], "gd": [ 69, 255, 199 ], "tb": [ 48, 255, 199 ], "dy": [ 31, 255, 199 ], "ho": [ 0, 255, 156 ], "er": [ 0, 230, 117 ], "tm": [ 0, 212, 82 ], "yb": [ 0, 191, 56 ], "lu": [ 0, 171, 36 ], "hf": [ 77, 194, 255 ], "ta": [ 77, 166, 255 ], "w": [ 33, 148, 214 ], "re": [ 38, 125, 171 ], "os": [ 38, 102, 150 ], "ir": [ 23, 84, 135 ], "pt": [ 208, 208, 224 ], "au": [ 255, 209, 35 ], "hg": [ 184, 184, 208 ], "tl": [ 166, 84, 77 ], "pb": [ 87, 89, 97 ], "bi": [ 158, 79, 181 ], "po": [ 171, 92, 0 ], "at": [ 117, 79, 69 ], "rn": [ 66, 130, 150 ], "fr": [ 66, 0, 102 ], "ra": [ 0, 125, 0 ], "ac": [ 112, 171, 250 ], "th": [ 0, 186, 255 ], "pa": [ 0, 161, 255 ], "u": [ 0, 143, 255 ], "np": [ 0, 128, 255 ], "pu": [ 0, 107, 255 ], "am": [ 84, 92, 242 ], "cm": [ 120, 92, 227 ], "bk": [ 138, 79, 227 ], "cf": [ 161, 54, 212 ], "es": [ 179, 31, 212 ], "fm": [ 179, 31, 186 ], "md": [ 179, 13, 166 ], "no": [ 189, 13, 135 ], "lr": [ 199, 0, 102 ], "rf": [ 204, 0, 89 ], "db": [ 209, 0, 79 ], "sg": [ 217, 0, 69 ], "bh": [ 224, 0, 56 ], "hs": [ 230, 0, 46 ], "mt": [ 235, 0, 38 ],
78 | "ds": [ 235, 0, 38 ], "rg": [ 235, 0, 38 ], "cn": [ 235, 0, 38 ], "uut": [ 235, 0, 38 ], "uuq": [ 235, 0, 38 ], "uup": [ 235, 0, 38 ], "uuh": [ 235, 0, 38 ], "uus": [ 235, 0, 38 ], "uuo": [ 235, 0, 38 ] };
79 |
80 |
81 | var atoms = [];
82 | var bonds = [];
83 | var histogram = {};
84 |
85 | var bhash = {};
86 |
87 | var lines = text.split( "\n" );
88 |
89 | var x, y, z, e;
90 |
91 | for ( var i = 0, il = lines.length; i < il; ++ i ) {
92 |
93 | if ( lines[ i ].substr( 0, 4 ) == "ATOM" || lines[ i ].substr( 0, 6 ) == "HETATM" ) {
94 |
95 | x = parseFloat( lines[ i ].substr( 30, 7 ) );
96 | y = parseFloat( lines[ i ].substr( 38, 7 ) );
97 | z = parseFloat( lines[ i ].substr( 46, 7 ) );
98 |
99 | e = trim( lines[ i ].substr( 76, 2 ) ).toLowerCase();
100 |
101 | if ( e == "" ) e = trim( lines[ i ].substr( 12, 2 ) ).toLowerCase();
102 | atoms.push( [ x, y, z, CPK[ e ], capitalize( e ) ] );
103 |
104 | if ( histogram[ e ] == undefined ) histogram[ e ] = 1;
105 | else histogram[ e ] += 1;
106 |
107 | } else if ( lines[ i ].substr( 0, 6 ) == "CONECT" ) {
108 |
109 | var satom = parseInt( lines[ i ].substr( 6, 5 ) );
110 |
111 | parseBond( 11, 5 );
112 | parseBond( 16, 5 );
113 | parseBond( 21, 5 );
114 | parseBond( 26, 5 );
115 |
116 | }
117 |
118 | }
119 |
120 | return { "ok": true, "atoms": atoms, "bonds": bonds, "histogram": histogram };
121 |
122 | },
123 |
124 | createModel: function ( json, callback ) {
125 |
126 | var scope = this,
127 | geometryAtoms = new THREE.Geometry(),
128 | geometryBonds = new THREE.Geometry();
129 |
130 | geometryAtoms.elements = [];
131 |
132 | var atoms = json.atoms;
133 | var bonds = json.bonds;
134 |
135 | for ( var i = 0; i < atoms.length; i ++ ) {
136 |
137 | var atom = atoms[ i ];
138 |
139 | var x = atom[ 0 ];
140 | var y = atom[ 1 ];
141 | var z = atom[ 2 ];
142 |
143 | var position = new THREE.Vector3( x, y, z );
144 | geometryAtoms.vertices.push( position );
145 |
146 | var r = atom[ 3 ][ 0 ] / 255;
147 | var g = atom[ 3 ][ 1 ] / 255;
148 | var b = atom[ 3 ][ 2 ] / 255;
149 |
150 | var color = new THREE.Color();
151 | color.setRGB( r, g, b );
152 |
153 | geometryAtoms.colors.push( color );
154 |
155 | geometryAtoms.elements.push( atom[ 4 ] );
156 |
157 | }
158 |
159 | for ( var i = 0; i < bonds.length; i ++ ) {
160 |
161 | var bond = bonds[ i ];
162 |
163 | var start = bond[ 0 ];
164 | var end = bond[ 1 ];
165 |
166 | var vertex1 = geometryAtoms.vertices[ start ];
167 | var vertex2 = geometryAtoms.vertices[ end ];
168 |
169 | geometryBonds.vertices.push( vertex1.clone() );
170 | geometryBonds.vertices.push( vertex2.clone() );
171 |
172 | }
173 |
174 | callback( geometryAtoms, geometryBonds, json );
175 |
176 | }
177 |
178 | };
179 |
180 |
--------------------------------------------------------------------------------
/js/loaders/PVRLoader.js:
--------------------------------------------------------------------------------
1 | /*
2 | * PVRLoader
3 | * Author: pierre lepers
4 | * Date: 17/09/2014 11:09
5 | *
6 | * PVR v2 (legacy) parser
7 | * TODO : Add Support for PVR v3 format
8 | * TODO : implement loadMipmaps option
9 | */
10 |
11 | THREE.PVRLoader = function ( manager ) {
12 |
13 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
14 |
15 | this._parser = THREE.PVRLoader.parse;
16 |
17 | };
18 |
19 | THREE.PVRLoader.prototype = Object.create( THREE.CompressedTextureLoader.prototype );
20 | THREE.PVRLoader.prototype.constructor = THREE.PVRLoader;
21 |
22 |
23 | THREE.PVRLoader.parse = function ( buffer, loadMipmaps ) {
24 |
25 | var headerLengthInt = 13;
26 | var header = new Uint32Array( buffer, 0, headerLengthInt );
27 |
28 | var pvrDatas = {
29 | buffer: buffer,
30 | header : header,
31 | loadMipmaps : loadMipmaps
32 | };
33 |
34 | // PVR v3
35 | if ( header[ 0 ] === 0x03525650 ) {
36 |
37 | return THREE.PVRLoader._parseV3( pvrDatas );
38 |
39 | }
40 | // PVR v2
41 | else if ( header[ 11 ] === 0x21525650 ) {
42 |
43 | return THREE.PVRLoader._parseV2( pvrDatas );
44 |
45 | } else {
46 |
47 | throw new Error( "[THREE.PVRLoader] Unknown PVR format" );
48 |
49 | }
50 |
51 | };
52 |
53 | THREE.PVRLoader._parseV3 = function ( pvrDatas ) {
54 |
55 | var header = pvrDatas.header;
56 | var bpp, format;
57 |
58 |
59 | var metaLen = header[ 12 ],
60 | pixelFormat = header[ 2 ],
61 | height = header[ 6 ],
62 | width = header[ 7 ],
63 | numSurfs = header[ 9 ],
64 | numFaces = header[ 10 ],
65 | numMipmaps = header[ 11 ];
66 |
67 | switch ( pixelFormat ) {
68 | case 0 : // PVRTC 2bpp RGB
69 | bpp = 2;
70 | format = THREE.RGB_PVRTC_2BPPV1_Format;
71 | break;
72 | case 1 : // PVRTC 2bpp RGBA
73 | bpp = 2;
74 | format = THREE.RGBA_PVRTC_2BPPV1_Format;
75 | break;
76 | case 2 : // PVRTC 4bpp RGB
77 | bpp = 4;
78 | format = THREE.RGB_PVRTC_4BPPV1_Format;
79 | break;
80 | case 3 : // PVRTC 4bpp RGBA
81 | bpp = 4;
82 | format = THREE.RGBA_PVRTC_4BPPV1_Format;
83 | break;
84 | default :
85 | throw new Error( "pvrtc - unsupported PVR format " + pixelFormat );
86 | }
87 |
88 | pvrDatas.dataPtr = 52 + metaLen;
89 | pvrDatas.bpp = bpp;
90 | pvrDatas.format = format;
91 | pvrDatas.width = width;
92 | pvrDatas.height = height;
93 | pvrDatas.numSurfaces = numFaces;
94 | pvrDatas.numMipmaps = numMipmaps;
95 |
96 | pvrDatas.isCubemap = ( numFaces === 6 );
97 |
98 | return THREE.PVRLoader._extract( pvrDatas );
99 |
100 | };
101 |
102 | THREE.PVRLoader._parseV2 = function ( pvrDatas ) {
103 |
104 | var header = pvrDatas.header;
105 |
106 | var headerLength = header[ 0 ],
107 | height = header[ 1 ],
108 | width = header[ 2 ],
109 | numMipmaps = header[ 3 ],
110 | flags = header[ 4 ],
111 | dataLength = header[ 5 ],
112 | bpp = header[ 6 ],
113 | bitmaskRed = header[ 7 ],
114 | bitmaskGreen = header[ 8 ],
115 | bitmaskBlue = header[ 9 ],
116 | bitmaskAlpha = header[ 10 ],
117 | pvrTag = header[ 11 ],
118 | numSurfs = header[ 12 ];
119 |
120 |
121 | var TYPE_MASK = 0xff;
122 | var PVRTC_2 = 24,
123 | PVRTC_4 = 25;
124 |
125 | var formatFlags = flags & TYPE_MASK;
126 |
127 |
128 |
129 | var bpp, format;
130 | var _hasAlpha = bitmaskAlpha > 0;
131 |
132 | if ( formatFlags === PVRTC_4 ) {
133 |
134 | format = _hasAlpha ? THREE.RGBA_PVRTC_4BPPV1_Format : THREE.RGB_PVRTC_4BPPV1_Format;
135 | bpp = 4;
136 |
137 | } else if ( formatFlags === PVRTC_2 ) {
138 |
139 | format = _hasAlpha ? THREE.RGBA_PVRTC_2BPPV1_Format : THREE.RGB_PVRTC_2BPPV1_Format;
140 | bpp = 2;
141 |
142 | } else
143 | throw new Error( "pvrtc - unknown format " + formatFlags );
144 |
145 |
146 |
147 | pvrDatas.dataPtr = headerLength;
148 | pvrDatas.bpp = bpp;
149 | pvrDatas.format = format;
150 | pvrDatas.width = width;
151 | pvrDatas.height = height;
152 | pvrDatas.numSurfaces = numSurfs;
153 | pvrDatas.numMipmaps = numMipmaps + 1;
154 |
155 | // guess cubemap type seems tricky in v2
156 | // it juste a pvr containing 6 surface (no explicit cubemap type)
157 | pvrDatas.isCubemap = ( numSurfs === 6 );
158 |
159 | return THREE.PVRLoader._extract( pvrDatas );
160 |
161 | };
162 |
163 |
164 | THREE.PVRLoader._extract = function ( pvrDatas ) {
165 |
166 | var pvr = {
167 | mipmaps: [],
168 | width: pvrDatas.width,
169 | height: pvrDatas.height,
170 | format: pvrDatas.format,
171 | mipmapCount: pvrDatas.numMipmaps,
172 | isCubemap : pvrDatas.isCubemap
173 | };
174 |
175 | var buffer = pvrDatas.buffer;
176 |
177 |
178 |
179 | // console.log( "--------------------------" );
180 |
181 | // console.log( "headerLength ", headerLength);
182 | // console.log( "height ", height );
183 | // console.log( "width ", width );
184 | // console.log( "numMipmaps ", numMipmaps );
185 | // console.log( "flags ", flags );
186 | // console.log( "dataLength ", dataLength );
187 | // console.log( "bpp ", bpp );
188 | // console.log( "bitmaskRed ", bitmaskRed );
189 | // console.log( "bitmaskGreen ", bitmaskGreen);
190 | // console.log( "bitmaskBlue ", bitmaskBlue );
191 | // console.log( "bitmaskAlpha ", bitmaskAlpha);
192 | // console.log( "pvrTag ", pvrTag );
193 | // console.log( "numSurfs ", numSurfs );
194 |
195 |
196 |
197 |
198 | var dataOffset = pvrDatas.dataPtr,
199 | bpp = pvrDatas.bpp,
200 | numSurfs = pvrDatas.numSurfaces,
201 | dataSize = 0,
202 | blockSize = 0,
203 | blockWidth = 0,
204 | blockHeight = 0,
205 | widthBlocks = 0,
206 | heightBlocks = 0;
207 |
208 |
209 |
210 | if ( bpp === 2 ) {
211 |
212 | blockWidth = 8;
213 | blockHeight = 4;
214 |
215 | } else {
216 |
217 | blockWidth = 4;
218 | blockHeight = 4;
219 |
220 | }
221 |
222 | blockSize = ( blockWidth * blockHeight ) * bpp / 8;
223 |
224 | pvr.mipmaps.length = pvrDatas.numMipmaps * numSurfs;
225 |
226 | var mipLevel = 0;
227 |
228 | while ( mipLevel < pvrDatas.numMipmaps ) {
229 |
230 | var sWidth = pvrDatas.width >> mipLevel,
231 | sHeight = pvrDatas.height >> mipLevel;
232 |
233 | widthBlocks = sWidth / blockWidth;
234 | heightBlocks = sHeight / blockHeight;
235 |
236 | // Clamp to minimum number of blocks
237 | if ( widthBlocks < 2 )
238 | widthBlocks = 2;
239 | if ( heightBlocks < 2 )
240 | heightBlocks = 2;
241 |
242 | dataSize = widthBlocks * heightBlocks * blockSize;
243 |
244 |
245 | for ( var surfIndex = 0; surfIndex < numSurfs; surfIndex ++ ) {
246 |
247 | var byteArray = new Uint8Array( buffer, dataOffset, dataSize );
248 |
249 | var mipmap = {
250 | data: byteArray,
251 | width: sWidth,
252 | height: sHeight
253 | };
254 |
255 | pvr.mipmaps[ surfIndex * pvrDatas.numMipmaps + mipLevel ] = mipmap;
256 |
257 | dataOffset += dataSize;
258 |
259 |
260 | }
261 |
262 | mipLevel ++;
263 |
264 | }
265 |
266 |
267 | return pvr;
268 |
269 | };
270 |
--------------------------------------------------------------------------------
/ts/zajal.ts:
--------------------------------------------------------------------------------
1 | namespace React {
2 | function mountTree(element: Element, container:HTMLElement) {
3 | if (container.firstChild) {
4 | var prevNode = container.firstChild;
5 | var prevRootComponent: Component = prevNode['_internalInstance'];
6 | var prevElement = prevRootComponent.currentElement;
7 | if (prevElement.type == element.type) {
8 | prevRootComponent.recieve(element);
9 | return;
10 | }
11 | // TODO unmountTree(container)
12 | }
13 | var rootComponent = instantiateComponent(element);
14 | var node = rootComponent.mount();
15 | container.appendChild(node.getHtmlNode());
16 | return node;
17 | }
18 |
19 | function isClass(type): boolean {
20 | return type.prototype && type.prototype.constructor;
21 | }
22 |
23 | function instantiateComponent(element: Element): Component {
24 | if (isClass(element.type)) {
25 | // Platform-specific components
26 | return new HostComponent(element);
27 | } else {
28 | // User-defined components
29 | return new CompositeComponent(element);
30 | }
31 | }
32 |
33 |
34 |
35 | // TODO cljs elements?
36 | class Element {
37 | type: any;
38 | props: any;
39 |
40 | constructor(type, props) {
41 | this.type = type;
42 | this.props = props;
43 | }
44 | }
45 |
46 | interface Component {
47 | mount(): HostNode;
48 | unmount(): void;
49 | getHostNode(): HostNode;
50 | recieve(nextElement: Element);
51 | currentElement: Element;
52 | }
53 |
54 | interface HostNode {
55 | removeProperty(prop:string);
56 | setProperty(prop:string, value);
57 | addChild(child:HostNode);
58 | replaceChild(child:HostNode,newChild:HostNode);
59 | removeChild(child:HostNode);
60 | getHtmlNode():HTMLElement;
61 | }
62 |
63 | class CompositeComponent implements Component {
64 | currentElement: Element;
65 | renderedComponent: Component;
66 |
67 | constructor(element: Element) {
68 | this.currentElement = element;
69 | this.renderedComponent = null;
70 | }
71 |
72 | getHostNode() {
73 | return this.renderedComponent.getHostNode();
74 | }
75 |
76 | recieve(nextElement: Element) {
77 | var prevProps = this.currentElement.props;
78 | var prevRenderedComponent = this.renderedComponent;
79 | var prevRenderedElement = prevRenderedComponent.currentElement;
80 |
81 | this.currentElement = nextElement;
82 | var type = nextElement.type;
83 | var nextProps = nextElement.props;
84 | var nextRenderedElement: Element = type(nextProps);
85 |
86 | if (prevRenderedElement.type === nextRenderedElement.type) {
87 | prevRenderedComponent.recieve(nextRenderedElement);
88 | return;
89 | }
90 |
91 | var prevNode = prevRenderedComponent.getHostNode();
92 | prevRenderedComponent.unmount();
93 | var nextRenderedComponent = instantiateComponent(nextRenderedElement);
94 | var nextNode = nextRenderedComponent.mount();
95 |
96 | this.renderedComponent = nextRenderedComponent;
97 | this.getHostNode().replaceChild(prevNode, nextNode);
98 | // TODO replace old node with new one, host specific
99 | }
100 |
101 | mount() {
102 | var element = this.currentElement;
103 | var type = this.currentElement.type;
104 | var props = this.currentElement.props;
105 |
106 | var renderedElement = type(props);
107 | this.renderedComponent = instantiateComponent(renderedElement);
108 |
109 | return this.renderedComponent.mount();
110 | }
111 |
112 | unmount() {
113 | this.renderedComponent.unmount();
114 | }
115 | }
116 |
117 | class HostComponent implements Component {
118 | currentElement: Element;
119 | renderedChildren: Component[];
120 | node: HostNode;
121 |
122 | constructor(element: Element) {
123 | this.currentElement = element;
124 | this.node = null;
125 | }
126 |
127 | getHostNode() {
128 | return this.node;
129 | }
130 |
131 | recieve(nextElement: Element) {
132 | var prevChildNode = this.node;
133 | var prevElement = this.currentElement;
134 | var prevProps = prevElement.props;
135 | var nextProps = nextElement.props;
136 | this.currentElement = nextElement;
137 |
138 | Object.keys(prevProps).forEach(propName => {
139 | if (propName !== 'children' && !nextProps.hasOwnProperty(propName)) {
140 | prevChildNode.removeProperty(propName);
141 | }
142 | });
143 |
144 | Object.keys(nextProps).forEach(propName => {
145 | if (propName !== 'children') {
146 | prevChildNode.setProperty(propName, nextProps[propName]);
147 | }
148 | });
149 |
150 | var prevChildren: Element[] = prevProps.children || [];
151 | if (!Array.isArray(prevChildren)) {
152 | prevChildren = [prevChildren];
153 | }
154 | var nextChildren: Element[] = nextProps.children || [];
155 | if (!Array.isArray(nextChildren)) {
156 | nextChildren = [nextChildren];
157 | }
158 |
159 | var prevRenderedChildren = this.renderedChildren;
160 | for (var i = 0; i < nextChildren.length; ++i) {
161 | var prevChild = prevRenderedChildren[i];
162 | if (!prevChild) {
163 | var nextChild = instantiateComponent(nextChildren[i]);
164 | var nextChildNode = nextChild.mount();
165 | this.node.addChild(nextChildNode); // TODO ADD
166 | continue;
167 | }
168 |
169 | var canUpdate = prevChildren[i].type === nextChildren[i].type;
170 | if (!canUpdate) {
171 | var prevChildNode = prevChild.getHostNode();
172 | prevChild.unmount();
173 | var nextChild = instantiateComponent(nextChildren[i]);
174 | var nextChildNode = nextChild.mount();
175 | this.node.replaceChild(prevChildNode, nextChildNode); // TODO REPLACE
176 | continue;
177 | }
178 |
179 | prevChild.recieve(nextChildren[i]);
180 | }
181 |
182 | for (var j = nextChildren.length; j < prevChildren.length; ++j) {
183 | var prevChild = prevRenderedChildren[j];
184 | var prevChildNode = prevChild.getHostNode();
185 | prevChild.unmount();
186 | this.node.removeChild(prevChildNode); // TODO REMOVE
187 | }
188 | }
189 |
190 | mount() {
191 | var element = this.currentElement;
192 | var type = element.type;
193 | var props = element.props;
194 | var children: Element[] = props.children || [];
195 |
196 | if (!Array.isArray(children)) {
197 | children = [children];
198 | }
199 |
200 | var node: HostNode = new type();
201 | this.node = node;
202 |
203 | Object.keys(props).forEach(propName => {
204 | if (propName !== 'children')
205 | node.setProperty(propName, props[propName]);
206 | });
207 |
208 | var renderedChildren = children.map(instantiateComponent);
209 | this.renderedChildren = renderedChildren;
210 |
211 | var childNodes = renderedChildren.map(child => child.mount());
212 | childNodes.forEach(childNode => node.addChild(childNode));
213 |
214 | return node;
215 | }
216 |
217 | unmount() {
218 | // TODO do more?
219 | this.renderedChildren.forEach(child => child.unmount());
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/js/loaders/gltf/glTFLoaderUtils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Tony Parisi / http://www.tonyparisi.com/
3 | */
4 |
5 | THREE.GLTFLoaderUtils = Object.create(Object, {
6 |
7 | // errors
8 | MISSING_DESCRIPTION: { value: "MISSING_DESCRIPTION" },
9 | INVALID_PATH: { value: "INVALID_PATH" },
10 | INVALID_TYPE: { value: "INVALID_TYPE" },
11 | XMLHTTPREQUEST_STATUS_ERROR: { value: "XMLHTTPREQUEST_STATUS_ERROR" },
12 | NOT_FOUND: { value: "NOT_FOUND" },
13 | // misc constants
14 | ARRAY_BUFFER: { value: "ArrayBuffer" },
15 |
16 | _streams : { value:{}, writable: true },
17 |
18 | _streamsStatus: { value: {}, writable: true },
19 |
20 | _resources: { value: {}, writable: true },
21 |
22 | _resourcesStatus: { value: {}, writable: true },
23 |
24 | // initialization
25 | init: {
26 | value: function() {
27 | this._streams = {};
28 | this._streamsStatus = {};
29 | this._resources = {};
30 | this._resourcesStatus = {};
31 | }
32 | },
33 |
34 | //manage entries
35 | _containsResource: {
36 | enumerable: false,
37 | value: function(resourceID) {
38 | return this._resources[resourceID] ? true : false;
39 | }
40 | },
41 |
42 | _storeResource: {
43 | enumerable: false,
44 | value: function(resourceID, resource) {
45 | if (!resourceID) {
46 | console.log("ERROR: entry does not contain id, cannot store");
47 | return;
48 | }
49 |
50 | if (this._containsResource[resourceID]) {
51 | console.log("WARNING: resource:" + resourceID + " is already stored, overriding");
52 | }
53 |
54 | this._resources[resourceID] = resource;
55 | }
56 | },
57 |
58 | _getResource: {
59 | enumerable: false,
60 | value: function(resourceID) {
61 | return this._resources[resourceID];
62 | }
63 | },
64 |
65 | _loadStream: {
66 | value: function(path, type, delegate) {
67 | var self = this;
68 |
69 | if (!type) {
70 | delegate.handleError(THREE.GLTFLoaderUtils.INVALID_TYPE, null);
71 | return;
72 | }
73 |
74 | if (!path) {
75 | delegate.handleError(THREE.GLTFLoaderUtils.INVALID_PATH);
76 | return;
77 | }
78 |
79 | var xhr = new XMLHttpRequest();
80 | xhr.open('GET', path, true);
81 | xhr.responseType = (type === this.ARRAY_BUFFER) ? "arraybuffer" : "text";
82 |
83 | //if this is not specified, 1 "big blob" scenes fails to load.
84 | xhr.setRequestHeader("If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT");
85 | xhr.addEventListener( 'load', function ( event ) {
86 | delegate.streamAvailable(path, xhr.response);
87 | }, false );
88 | xhr.addEventListener( 'error', function ( event ) {
89 | delegate.handleError(THREE.GLTFLoaderUtils.XMLHTTPREQUEST_STATUS_ERROR, xhr.status);
90 | }, false );
91 | xhr.send(null);
92 | }
93 | },
94 |
95 | send: { value: 0, writable: true },
96 | requested: { value: 0, writable: true },
97 |
98 | _handleRequest: {
99 | value: function(request) {
100 | var resourceStatus = this._resourcesStatus[request.id];
101 | if (resourceStatus)
102 | {
103 | this._resourcesStatus[request.id] ++;
104 | }
105 | else
106 | {
107 | this._resourcesStatus[request.id] = 1;
108 | }
109 |
110 | var streamStatus = this._streamsStatus[request.path];
111 | if (streamStatus && streamStatus.status === "loading" )
112 | {
113 | streamStatus.requests.push(request);
114 | return;
115 | }
116 |
117 | this._streamsStatus[request.path] = { status : "loading", requests : [ request ] };
118 |
119 | var self = this;
120 | var processResourceDelegate = {};
121 |
122 | processResourceDelegate.streamAvailable = function(path, res_) {
123 | var streamStatus = self._streamsStatus[path];
124 | var requests = streamStatus.requests;
125 | requests.forEach( function(req_) {
126 | var subArray = res_.slice(req_.range[0], req_.range[1]);
127 | var convertedResource = req_.delegate.convert(subArray, req_.ctx);
128 | self._storeResource(req_.id, convertedResource);
129 | req_.delegate.resourceAvailable(convertedResource, req_.ctx);
130 | -- self._resourcesStatus[req_.id];
131 |
132 | }, this);
133 |
134 | delete self._streamsStatus[path];
135 |
136 | };
137 |
138 | processResourceDelegate.handleError = function(errorCode, info) {
139 | request.delegate.handleError(errorCode, info);
140 | };
141 |
142 | this._loadStream(request.path, request.type, processResourceDelegate);
143 | }
144 | },
145 |
146 |
147 | _elementSizeForGLType: {
148 | value: function(glType) {
149 | switch (glType) {
150 | case WebGLRenderingContext.FLOAT :
151 | return Float32Array.BYTES_PER_ELEMENT;
152 | case WebGLRenderingContext.UNSIGNED_BYTE :
153 | return Uint8Array.BYTES_PER_ELEMENT;
154 | case WebGLRenderingContext.UNSIGNED_SHORT :
155 | return Uint16Array.BYTES_PER_ELEMENT;
156 | case WebGLRenderingContext.FLOAT_VEC2 :
157 | return Float32Array.BYTES_PER_ELEMENT * 2;
158 | case WebGLRenderingContext.FLOAT_VEC3 :
159 | return Float32Array.BYTES_PER_ELEMENT * 3;
160 | case WebGLRenderingContext.FLOAT_VEC4 :
161 | return Float32Array.BYTES_PER_ELEMENT * 4;
162 | case WebGLRenderingContext.FLOAT_MAT3 :
163 | return Float32Array.BYTES_PER_ELEMENT * 9;
164 | case WebGLRenderingContext.FLOAT_MAT4 :
165 | return Float32Array.BYTES_PER_ELEMENT * 16;
166 | default:
167 | return null;
168 | }
169 | }
170 | },
171 |
172 | _handleWrappedBufferViewResourceLoading: {
173 | value: function(wrappedBufferView, delegate, ctx) {
174 | var bufferView = wrappedBufferView.bufferView;
175 | var buffer = bufferView.buffer;
176 | var byteOffset = wrappedBufferView.byteOffset + bufferView.description.byteOffset;
177 | var range = [ byteOffset, (this._elementSizeForGLType(wrappedBufferView.type) * wrappedBufferView.count) + byteOffset ];
178 |
179 | this._handleRequest({ "id" : wrappedBufferView.id,
180 | "range" : range,
181 | "type" : buffer.description.type,
182 | "path" : buffer.description.path,
183 | "delegate" : delegate,
184 | "ctx" : ctx }, null);
185 | }
186 | },
187 |
188 | getBuffer: {
189 |
190 | value: function(wrappedBufferView, delegate, ctx) {
191 |
192 | var savedBuffer = this._getResource(wrappedBufferView.id);
193 | if (savedBuffer) {
194 | return savedBuffer;
195 | } else {
196 | this._handleWrappedBufferViewResourceLoading(wrappedBufferView, delegate, ctx);
197 | }
198 |
199 | return null;
200 | }
201 | },
202 |
203 | getFile: {
204 |
205 | value: function(request, delegate, ctx) {
206 |
207 | request.delegate = delegate;
208 | request.ctx = ctx;
209 |
210 | this._handleRequest({ "id" : request.id,
211 | "path" : request.path,
212 | "range" : [ 0 ],
213 | "type" : "text",
214 | "delegate" : delegate,
215 | "ctx" : ctx }, null);
216 |
217 | return null;
218 | }
219 | },
220 | });
221 |
--------------------------------------------------------------------------------
/js/loaders/DDSLoader.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.DDSLoader = function () {
6 |
7 | this._parser = THREE.DDSLoader.parse;
8 |
9 | };
10 |
11 | THREE.DDSLoader.prototype = Object.create( THREE.CompressedTextureLoader.prototype );
12 | THREE.DDSLoader.prototype.constructor = THREE.DDSLoader;
13 |
14 | THREE.DDSLoader.parse = function ( buffer, loadMipmaps ) {
15 |
16 | var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
17 |
18 | // Adapted from @toji's DDS utils
19 | // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
20 |
21 | // All values and structures referenced from:
22 | // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
23 |
24 | var DDS_MAGIC = 0x20534444;
25 |
26 | var DDSD_CAPS = 0x1,
27 | DDSD_HEIGHT = 0x2,
28 | DDSD_WIDTH = 0x4,
29 | DDSD_PITCH = 0x8,
30 | DDSD_PIXELFORMAT = 0x1000,
31 | DDSD_MIPMAPCOUNT = 0x20000,
32 | DDSD_LINEARSIZE = 0x80000,
33 | DDSD_DEPTH = 0x800000;
34 |
35 | var DDSCAPS_COMPLEX = 0x8,
36 | DDSCAPS_MIPMAP = 0x400000,
37 | DDSCAPS_TEXTURE = 0x1000;
38 |
39 | var DDSCAPS2_CUBEMAP = 0x200,
40 | DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
41 | DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
42 | DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
43 | DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
44 | DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
45 | DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
46 | DDSCAPS2_VOLUME = 0x200000;
47 |
48 | var DDPF_ALPHAPIXELS = 0x1,
49 | DDPF_ALPHA = 0x2,
50 | DDPF_FOURCC = 0x4,
51 | DDPF_RGB = 0x40,
52 | DDPF_YUV = 0x200,
53 | DDPF_LUMINANCE = 0x20000;
54 |
55 | function fourCCToInt32( value ) {
56 |
57 | return value.charCodeAt( 0 ) +
58 | ( value.charCodeAt( 1 ) << 8 ) +
59 | ( value.charCodeAt( 2 ) << 16 ) +
60 | ( value.charCodeAt( 3 ) << 24 );
61 |
62 | }
63 |
64 | function int32ToFourCC( value ) {
65 |
66 | return String.fromCharCode(
67 | value & 0xff,
68 | ( value >> 8 ) & 0xff,
69 | ( value >> 16 ) & 0xff,
70 | ( value >> 24 ) & 0xff
71 | );
72 |
73 | }
74 |
75 | function loadARGBMip( buffer, dataOffset, width, height ) {
76 |
77 | var dataLength = width * height * 4;
78 | var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
79 | var byteArray = new Uint8Array( dataLength );
80 | var dst = 0;
81 | var src = 0;
82 | for ( var y = 0; y < height; y ++ ) {
83 |
84 | for ( var x = 0; x < width; x ++ ) {
85 |
86 | var b = srcBuffer[ src ]; src ++;
87 | var g = srcBuffer[ src ]; src ++;
88 | var r = srcBuffer[ src ]; src ++;
89 | var a = srcBuffer[ src ]; src ++;
90 | byteArray[ dst ] = r; dst ++; //r
91 | byteArray[ dst ] = g; dst ++; //g
92 | byteArray[ dst ] = b; dst ++; //b
93 | byteArray[ dst ] = a; dst ++; //a
94 |
95 | }
96 |
97 | }
98 | return byteArray;
99 |
100 | }
101 |
102 | var FOURCC_DXT1 = fourCCToInt32( "DXT1" );
103 | var FOURCC_DXT3 = fourCCToInt32( "DXT3" );
104 | var FOURCC_DXT5 = fourCCToInt32( "DXT5" );
105 | var FOURCC_ETC1 = fourCCToInt32( "ETC1" );
106 |
107 | var headerLengthInt = 31; // The header length in 32 bit ints
108 |
109 | // Offsets into the header array
110 |
111 | var off_magic = 0;
112 |
113 | var off_size = 1;
114 | var off_flags = 2;
115 | var off_height = 3;
116 | var off_width = 4;
117 |
118 | var off_mipmapCount = 7;
119 |
120 | var off_pfFlags = 20;
121 | var off_pfFourCC = 21;
122 | var off_RGBBitCount = 22;
123 | var off_RBitMask = 23;
124 | var off_GBitMask = 24;
125 | var off_BBitMask = 25;
126 | var off_ABitMask = 26;
127 |
128 | var off_caps = 27;
129 | var off_caps2 = 28;
130 | var off_caps3 = 29;
131 | var off_caps4 = 30;
132 |
133 | // Parse header
134 |
135 | var header = new Int32Array( buffer, 0, headerLengthInt );
136 |
137 | if ( header[ off_magic ] !== DDS_MAGIC ) {
138 |
139 | console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' );
140 | return dds;
141 |
142 | }
143 |
144 | if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
145 |
146 | console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' );
147 | return dds;
148 |
149 | }
150 |
151 | var blockBytes;
152 |
153 | var fourCC = header[ off_pfFourCC ];
154 |
155 | var isRGBAUncompressed = false;
156 |
157 | switch ( fourCC ) {
158 |
159 | case FOURCC_DXT1:
160 |
161 | blockBytes = 8;
162 | dds.format = THREE.RGB_S3TC_DXT1_Format;
163 | break;
164 |
165 | case FOURCC_DXT3:
166 |
167 | blockBytes = 16;
168 | dds.format = THREE.RGBA_S3TC_DXT3_Format;
169 | break;
170 |
171 | case FOURCC_DXT5:
172 |
173 | blockBytes = 16;
174 | dds.format = THREE.RGBA_S3TC_DXT5_Format;
175 | break;
176 |
177 | case FOURCC_ETC1:
178 |
179 | blockBytes = 8;
180 | dds.format = THREE.RGB_ETC1_Format;
181 | break;
182 |
183 | default:
184 |
185 | if ( header[ off_RGBBitCount ] === 32
186 | && header[ off_RBitMask ] & 0xff0000
187 | && header[ off_GBitMask ] & 0xff00
188 | && header[ off_BBitMask ] & 0xff
189 | && header[ off_ABitMask ] & 0xff000000 ) {
190 |
191 | isRGBAUncompressed = true;
192 | blockBytes = 64;
193 | dds.format = THREE.RGBAFormat;
194 |
195 | } else {
196 |
197 | console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) );
198 | return dds;
199 |
200 | }
201 | }
202 |
203 | dds.mipmapCount = 1;
204 |
205 | if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
206 |
207 | dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
208 |
209 | }
210 |
211 | var caps2 = header[ off_caps2 ];
212 | dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false;
213 | if ( dds.isCubemap && (
214 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEX ) ||
215 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX ) ||
216 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEY ) ||
217 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY ) ||
218 | ! ( caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ ) ||
219 | ! ( caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ )
220 | ) ) {
221 |
222 | console.error( 'THREE.DDSLoader.parse: Incomplete cubemap faces' );
223 | return dds;
224 |
225 | }
226 |
227 | dds.width = header[ off_width ];
228 | dds.height = header[ off_height ];
229 |
230 | var dataOffset = header[ off_size ] + 4;
231 |
232 | // Extract mipmaps buffers
233 |
234 | var faces = dds.isCubemap ? 6 : 1;
235 |
236 | for ( var face = 0; face < faces; face ++ ) {
237 |
238 | var width = dds.width;
239 | var height = dds.height;
240 |
241 | for ( var i = 0; i < dds.mipmapCount; i ++ ) {
242 |
243 | if ( isRGBAUncompressed ) {
244 |
245 | var byteArray = loadARGBMip( buffer, dataOffset, width, height );
246 | var dataLength = byteArray.length;
247 |
248 | } else {
249 |
250 | var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
251 | var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
252 |
253 | }
254 |
255 | var mipmap = { "data": byteArray, "width": width, "height": height };
256 | dds.mipmaps.push( mipmap );
257 |
258 | dataOffset += dataLength;
259 |
260 | width = Math.max( width >> 1, 1 );
261 | height = Math.max( height >> 1, 1 );
262 |
263 | }
264 |
265 | }
266 |
267 | return dds;
268 |
269 | };
270 |
--------------------------------------------------------------------------------
/js/loaders/VTKLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | * @author Alex Pletzer
4 | */
5 |
6 | THREE.VTKLoader = function ( manager ) {
7 |
8 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
9 |
10 | };
11 |
12 | THREE.VTKLoader.prototype = {
13 |
14 | constructor: THREE.VTKLoader,
15 |
16 | load: function ( url, onLoad, onProgress, onError ) {
17 |
18 | // Will we bump into trouble reading the whole file into memory?
19 | var scope = this;
20 | var loader = new THREE.XHRLoader( scope.manager );
21 | loader.load( url, function ( text ) {
22 |
23 | onLoad( scope.parse( text ) );
24 |
25 | },
26 |
27 | onProgress, onError );
28 |
29 | },
30 |
31 | parse: function ( data ) {
32 |
33 | // connectivity of the triangles
34 | var indices = [];
35 |
36 | // triangles vertices
37 | var positions = [];
38 |
39 | // red, green, blue colors in the range 0 to 1
40 | var colors = [];
41 |
42 | // normal vector, one per vertex
43 | var normals = [];
44 |
45 | var result;
46 |
47 | // pattern for reading vertices, 3 floats or integers
48 | var pat3Floats = /(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)/g;
49 |
50 | // pattern for connectivity, an integer followed by any number of ints
51 | // the first integer is the number of polygon nodes
52 | var patConnectivity = /^(\d+)\s+([\s\d]*)/;
53 |
54 | // indicates start of vertex data section
55 | var patPOINTS = /^POINTS /;
56 |
57 | // indicates start of polygon connectivity section
58 | var patPOLYGONS = /^POLYGONS /;
59 |
60 | // POINT_DATA number_of_values
61 | var patPOINT_DATA = /^POINT_DATA[ ]+(\d+)/;
62 |
63 | // CELL_DATA number_of_polys
64 | var patCELL_DATA = /^CELL_DATA[ ]+(\d+)/;
65 |
66 | // Start of color section
67 | var patCOLOR_SCALARS = /^COLOR_SCALARS[ ]+(\w+)[ ]+3/;
68 |
69 | // NORMALS Normals float
70 | var patNORMALS = /^NORMALS[ ]+(\w+)[ ]+(\w+)/;
71 |
72 | var inPointsSection = false;
73 | var inPolygonsSection = false;
74 | var inPointDataSection = false;
75 | var inCellDataSection = false;
76 | var inColorSection = false;
77 | var inNormalsSection = false;
78 |
79 | var lines = data.split( '\n' );
80 |
81 | for ( var i in lines ) {
82 |
83 | var line = lines[ i ];
84 |
85 | if ( inPointsSection ) {
86 |
87 | // get the vertices
88 | while ( ( result = pat3Floats.exec( line ) ) !== null ) {
89 |
90 | var x = parseFloat( result[ 1 ] );
91 | var y = parseFloat( result[ 2 ] );
92 | var z = parseFloat( result[ 3 ] );
93 | positions.push( x, y, z );
94 |
95 | }
96 |
97 | } else if ( inPolygonsSection ) {
98 |
99 | if ( ( result = patConnectivity.exec( line ) ) !== null ) {
100 |
101 | // numVertices i0 i1 i2 ...
102 | var numVertices = parseInt( result[ 1 ] );
103 | var inds = result[ 2 ].split( /\s+/ );
104 |
105 | if ( numVertices >= 3 ) {
106 |
107 | var i0 = parseInt( inds[ 0 ] );
108 | var i1, i2;
109 | var k = 1;
110 | // split the polygon in numVertices - 2 triangles
111 | for ( var j = 0; j < numVertices - 2; ++ j ) {
112 |
113 | i1 = parseInt( inds[ k ] );
114 | i2 = parseInt( inds[ k + 1 ] );
115 | indices.push( i0, i1, i2 );
116 | k ++;
117 |
118 | }
119 |
120 | }
121 |
122 | }
123 |
124 | } else if ( inPointDataSection || inCellDataSection ) {
125 |
126 | if ( inColorSection ) {
127 |
128 | // Get the colors
129 |
130 | while ( ( result = pat3Floats.exec( line ) ) !== null ) {
131 |
132 | var r = parseFloat( result[ 1 ] );
133 | var g = parseFloat( result[ 2 ] );
134 | var b = parseFloat( result[ 3 ] );
135 | colors.push( r, g, b );
136 |
137 | }
138 |
139 | } else if ( inNormalsSection ) {
140 |
141 | // Get the normal vectors
142 |
143 | while ( ( result = pat3Floats.exec( line ) ) !== null ) {
144 |
145 | var nx = parseFloat( result[ 1 ] );
146 | var ny = parseFloat( result[ 2 ] );
147 | var nz = parseFloat( result[ 3 ] );
148 | normals.push( nx, ny, nz );
149 |
150 | }
151 |
152 | }
153 |
154 | }
155 |
156 | if ( patPOLYGONS.exec( line ) !== null ) {
157 |
158 | inPolygonsSection = true;
159 | inPointsSection = false;
160 |
161 | } else if ( patPOINTS.exec( line ) !== null ) {
162 |
163 | inPolygonsSection = false;
164 | inPointsSection = true;
165 |
166 | } else if ( patPOINT_DATA.exec( line ) !== null ) {
167 |
168 | inPointDataSection = true;
169 | inPointsSection = false;
170 | inPolygonsSection = false;
171 |
172 | } else if ( patCELL_DATA.exec( line ) !== null ) {
173 |
174 | inCellDataSection = true;
175 | inPointsSection = false;
176 | inPolygonsSection = false;
177 |
178 | } else if ( patCOLOR_SCALARS.exec( line ) !== null ) {
179 |
180 | inColorSection = true;
181 | inNormalsSection = false;
182 | inPointsSection = false;
183 | inPolygonsSection = false;
184 |
185 | } else if ( patNORMALS.exec( line ) !== null ) {
186 |
187 | inNormalsSection = true;
188 | inColorSection = false;
189 | inPointsSection = false;
190 | inPolygonsSection = false;
191 |
192 | }
193 |
194 | }
195 |
196 | var geometry;
197 | var stagger = 'point';
198 |
199 | if ( colors.length == indices.length ) {
200 |
201 | stagger = 'cell';
202 |
203 | }
204 |
205 | if ( stagger == 'point' ) {
206 |
207 | // Nodal. Use BufferGeometry
208 | geometry = new THREE.BufferGeometry();
209 | geometry.setIndex( new THREE.BufferAttribute( new ( indices.length > 65535 ? Uint32Array : Uint16Array )( indices ), 1 ) );
210 | geometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );
211 |
212 | if ( colors.length == positions.length ) {
213 |
214 | geometry.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( colors ), 3 ) );
215 |
216 | }
217 |
218 | if ( normals.length == positions.length ) {
219 |
220 | geometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );
221 |
222 | }
223 |
224 | } else {
225 |
226 | // Cell centered colors. The only way to attach a solid color to each triangle
227 | // is to use Geometry, which is less efficient than BufferGeometry
228 | geometry = new THREE.Geometry();
229 |
230 | var numTriangles = indices.length / 3;
231 | var numPoints = positions.length / 3;
232 | var va, vb, vc;
233 | var face;
234 | var ia, ib, ic;
235 | var x, y, z;
236 | var r, g, b;
237 |
238 | for ( var j = 0; j < numPoints; ++ j ) {
239 |
240 | x = positions[ 3 * j + 0 ];
241 | y = positions[ 3 * j + 1 ];
242 | z = positions[ 3 * j + 2 ];
243 | geometry.vertices.push( new THREE.Vector3( x, y, z ) );
244 |
245 | }
246 |
247 | for ( var i = 0; i < numTriangles; ++ i ) {
248 |
249 | ia = indices[ 3 * i + 0 ];
250 | ib = indices[ 3 * i + 1 ];
251 | ic = indices[ 3 * i + 2 ];
252 | geometry.faces.push( new THREE.Face3( ia, ib, ic ) );
253 |
254 | }
255 |
256 | if ( colors.length == numTriangles * 3 ) {
257 |
258 | for ( var i = 0; i < numTriangles; ++ i ) {
259 |
260 | face = geometry.faces[ i ];
261 | r = colors[ 3 * i + 0 ];
262 | g = colors[ 3 * i + 1 ];
263 | b = colors[ 3 * i + 2 ];
264 | face.color = new THREE.Color().setRGB( r, g, b );
265 |
266 | }
267 |
268 | }
269 |
270 | }
271 |
272 | return geometry;
273 |
274 | }
275 |
276 | };
277 |
278 | THREE.EventDispatcher.prototype.apply( THREE.VTKLoader.prototype );
279 |
--------------------------------------------------------------------------------
/zajal/draw/three.cljs:
--------------------------------------------------------------------------------
1 | (ns virtual-three)
2 |
3 | (deftype RendererNode [props camera scene ^:mutable _cache]
4 | Object
5 | (apply-property
6 | [self property value]
7 | (.render self)
8 | (case property
9 | :viewport
10 | (.. _cache
11 | (setViewport (:x value)
12 | (:y value)
13 | (:width value)
14 | (:height value)))
15 | :size
16 | (.. _cache
17 | (setSize (:width value)
18 | (:height value)))
19 | ;; TODO the rest
20 | nil))
21 | (render
22 | [self]
23 | (when-not _cache
24 | (set! _cache (new js/THREE.WebGLRenderer
25 | (clj->js props)))
26 | (doseq [prop (keys props)]
27 | (.apply-property self prop (props prop)))
28 | (.render camera)
29 | (.render scene))
30 | _cache)
31 | (reconcile
32 | [self other]
33 | (when-not (identical? self other)
34 | (when (not (identical? (type self) (type other)))
35 | (throw "Different types not supported"))
36 | (let [other-props (.-props other)]
37 | (doseq [prop (keys other-props)]
38 | (.apply-property self prop (other-props prop))))
39 | ;; TODO removing props
40 | (.reconcile camera (.-camera other))
41 | (.reconcile scene (.-scene other))
42 | ;; propagate the cache
43 | (set! (.-_cache other) _cache))))
44 |
45 | (deftype Object3DNode [ctor props children ^:mutable _cache]
46 | Object
47 | (apply-property
48 | [self property value]
49 | (.render self)
50 | (case property
51 | :rotation
52 | (.. _cache -rotation (copy value))
53 | :position
54 | (.. _cache -position (copy value))
55 | :scale
56 | (.. _cache -scale (copy value))
57 | ;; TODO the rest
58 | (aset _cache (.-fqn property) value)))
59 | (render
60 | [self]
61 | (when-not _cache
62 | (set! _cache (js/Object.create (.-prototype ctor)))
63 | (.call ctor _cache)
64 | (doseq [prop (keys props)]
65 | (.apply-property self prop (props prop)))
66 | (when children
67 | (doseq [child children]
68 | (.add _cache
69 | (if (.-uuid child)
70 | child
71 | (.render child))))))
72 | _cache)
73 | (reconcile
74 | [self other]
75 | (when-not (identical? self other)
76 | (when (not (identical? (type self) (type other)))
77 | (throw "Different types not supported"))
78 | ;; TODO removing props
79 | (when (not (identical? props (.-props other)))
80 | (let [other-props (.-props other)]
81 | (doseq [prop (keys other-props)]
82 | (.apply-property self prop (other-props prop)))))
83 | ;; children
84 | (when (and (not (nil? children))
85 | (not (nil? (.-children other))))
86 | (cond
87 | (seq? children)
88 | (loop [self-children children
89 | other-children (.-children other)]
90 | (let [self-child (first self-children)
91 | other-child (first other-children)
92 | self-next (next self-children)
93 | other-next (next other-children)]
94 | (cond (nil? self-child)
95 | (.. _cache (add (.render other-child)))
96 | (nil? other-child)
97 | (.. _cache (remove (.render self-child)))
98 | :else
99 | (.reconcile self-child other-child))
100 | (when (or (not (nil? self-next))
101 | (not (nil? other-next)))
102 | (recur self-next other-next))))
103 | :else
104 | (let [self-children children
105 | other-children (.-children other)
106 | self-children-count (count self-children)
107 | other-children-count (count other-children)
108 | min-length (js/Math.min self-children-count other-children-count)]
109 | ;; TODO what is the fastest way to iterate children?
110 | (loop [i 0]
111 | (let [self-child (nth self-children i)
112 | other-child (nth other-children i)]
113 | (when (not (identical? self-child other-child))
114 | (.reconcile (nth self-children i)
115 | (nth other-children i))))
116 | (when (< i (dec min-length))
117 | (recur (inc i))))
118 | (if (> other-children-count
119 | self-children-count)
120 | ;; more elements
121 | (loop [i min-length]
122 | (.. _cache (add (.render (nth other-children i))))
123 | (when (< i (dec other-children-count))
124 | (recur (inc i))))
125 | (if (> self-children-count
126 | other-children-count)
127 | ;; fewer elements
128 | (loop [i min-length]
129 | (.. _cache (remove (.-_cache (nth self-children i))))
130 | (when (< i (dec (count self-children)))
131 | (recur (inc i)))))))))
132 | ;; propagate the cache
133 | (set! (.-_cache other) _cache))))
134 |
135 | (defn renderer [props camera scene]
136 | (RendererNode. props camera scene nil))
137 |
138 | (defn camera [props children]
139 | (Object3DNode. js/THREE.PerspectiveCamera props children nil))
140 |
141 | (defn scene [props children]
142 | (Object3DNode. js/THREE.Scene props children nil))
143 |
144 | (defn mesh [props children]
145 | (Object3DNode. js/THREE.Mesh props children nil))
146 |
147 | (defn line [props children]
148 | (Object3DNode. js/THREE.Line props children nil))
149 |
150 | (defn group [props children]
151 | (Object3DNode. js/THREE.Group props children nil))
152 |
153 | (def box-geo (js/THREE.BoxGeometry. 1 1 1))
154 | (def mesh-mat (js/THREE.MeshBasicMaterial.
155 | #js {:wireframe true
156 | :color "white"}))
157 |
158 | (defn v3 [x y z] (js/THREE.Vector3. x y z))
159 | (defn euler [x y z] (js/THREE.Euler. x y z))
160 |
161 | (defn input [] nil)
162 |
163 | (defn sketch [start step draw]
164 | (let [state (atom start)
165 | renderer (draw start)
166 | current (atom renderer)]
167 | (js/document.body.appendChild (.. renderer render -domElement))
168 | (letfn [(render-loop
169 | [t]
170 | (.requestAnimationFrame js/window render-loop)
171 | (swap! state step (input))
172 | (let [next (draw @state)]
173 | (when (not (identical? @current next))
174 | (.reconcile @current next)
175 | (reset! current next)
176 | (.. @current render (render (.. @current -scene render)
177 | (.. @current -camera render) )))))]
178 | (render-loop 0)
179 | (.render renderer)))
180 | :ok)
181 |
182 | (def resources (atom {}))
183 |
184 | (.. (new js/THREE.OBJLoader)
185 | (load "models/watercraftPack_003.obj"
186 | (fn [obj]
187 | (doseq [child (.-children obj)]
188 | (js/console.log child)
189 | (aset child "material" mesh-mat))
190 | (swap! resources assoc :boat obj))))
191 |
192 | (defn step [x] (inc x))
193 |
194 | (defn draw [t]
195 | (renderer
196 | {:size {:width 500
197 | :height 500}}
198 | (camera {:position (v3 0 5 5)
199 | :rotation (euler -1 0 0)})
200 | (scene
201 | {}
202 | [(group {:position (v3 (* 0.1 1) (* 0.1 3) (* 0.5 4))
203 | :rotation (euler (* 0.1 t) 0 (* 0.05 t))
204 | :scale (v3 1 1 1)}
205 | #_
206 | [(mesh {:geometry box-geo
207 | :material mesh-mat})]
208 | (when (@resources :boat)
209 | [(@resources :boat)]))])))
210 |
211 | (sketch 0 #'step #'draw)
--------------------------------------------------------------------------------
/js/loaders/PCDLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Filipe Caixeta / http://filipecaixeta.com.br
3 | *
4 | * Description: A THREE loader for PCD ascii and binary files.
5 | *
6 | * Limitations: Compressed binary files are not supported.
7 | *
8 | */
9 |
10 | THREE.PCDLoader = function( manager ) {
11 |
12 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
13 | this.littleEndian = true;
14 |
15 | };
16 | THREE.PCDLoader.prototype = {
17 |
18 | constructor: THREE.PCDLoader,
19 |
20 | load: function( url, onLoad, onProgress, onError ) {
21 |
22 | var scope = this;
23 |
24 | var loader = new THREE.XHRLoader( scope.manager );
25 | loader.setResponseType( 'arraybuffer' );
26 | loader.load( url, function( data ) {
27 |
28 | onLoad( scope.parse( data, url ) );
29 |
30 | }, onProgress, onError );
31 |
32 | },
33 |
34 | binarryToStr: function( data ) {
35 |
36 | text = "";
37 | var charArray = new Uint8Array( data );
38 | for ( var i = 0; i < data.byteLength; i ++ ) {
39 |
40 | text += String.fromCharCode( charArray[ i ] );
41 |
42 | }
43 | return text;
44 |
45 | },
46 |
47 | parseHeader: function( data ) {
48 |
49 | var PCDheader = {};
50 | var result1 = data.search( /[\r\n]DATA\s(\S*)\s/i );
51 | var result2 = /[\r\n]DATA\s(\S*)\s/i.exec( data.substr( result1 - 1 ) );
52 | PCDheader.data = result2[ 1 ];
53 | PCDheader.headerLen = result2[ 0 ].length + result1;
54 | PCDheader.str = data.substr( 0, PCDheader.headerLen );
55 | // Remove comments
56 | PCDheader.str = PCDheader.str.replace( /\#.*/gi, "" );
57 | PCDheader.version = /VERSION (.*)/i.exec( PCDheader.str );
58 | if ( PCDheader.version != null )
59 | PCDheader.version = parseFloat( PCDheader.version[ 1 ] );
60 | PCDheader.fields = /FIELDS (.*)/i.exec( PCDheader.str );
61 | if ( PCDheader.fields != null )
62 | PCDheader.fields = PCDheader.fields[ 1 ].split( " " );
63 | PCDheader.size = /SIZE (.*)/i.exec( PCDheader.str );
64 | if ( PCDheader.size != null )
65 | PCDheader.size = PCDheader.size[ 1 ].split( " " ).map( function( x ) {
66 |
67 | return parseInt( x, 10 );
68 |
69 | } );
70 | PCDheader.type = /TYPE (.*)/i.exec( PCDheader.str );
71 | if ( PCDheader.type != null )
72 | PCDheader.type = PCDheader.type[ 1 ].split( " " );
73 | PCDheader.count = /COUNT (.*)/i.exec( PCDheader.str );
74 | if ( PCDheader.count != null )
75 | PCDheader.count = PCDheader.count[ 1 ].split( " " ).map( function( x ) {
76 |
77 | return parseInt( x, 10 );
78 |
79 | } );
80 | PCDheader.width = /WIDTH (.*)/i.exec( PCDheader.str );
81 | if ( PCDheader.width != null )
82 | PCDheader.width = parseInt( PCDheader.width[ 1 ] );
83 | PCDheader.height = /HEIGHT (.*)/i.exec( PCDheader.str );
84 | if ( PCDheader.height != null )
85 | PCDheader.height = parseInt( PCDheader.height[ 1 ] );
86 | PCDheader.viewpoint = /VIEWPOINT (.*)/i.exec( PCDheader.str );
87 | if ( PCDheader.viewpoint != null )
88 | PCDheader.viewpoint = PCDheader.viewpoint[ 1 ];
89 | PCDheader.points = /POINTS (.*)/i.exec( PCDheader.str );
90 | if ( PCDheader.points != null )
91 | PCDheader.points = parseInt( PCDheader.points[ 1 ], 10 );
92 | if ( PCDheader.points == null )
93 | PCDheader.points = PCDheader.width * PCDheader.height;
94 |
95 | if ( PCDheader.count == null ) {
96 |
97 | PCDheader.count = [];
98 | for ( var i = 0; i < PCDheader.fields; i ++ )
99 | PCDheader.count.push( 1 );
100 |
101 | }
102 |
103 | PCDheader.offset = {}
104 | var sizeSum = 0;
105 | for ( var i = 0; i < PCDheader.fields.length; i ++ ) {
106 |
107 | if ( PCDheader.data == "ascii" ) {
108 |
109 | PCDheader.offset[ PCDheader.fields[ i ]] = i;
110 |
111 | } else {
112 |
113 | PCDheader.offset[ PCDheader.fields[ i ]] = sizeSum;
114 | sizeSum += PCDheader.size[ i ];
115 |
116 | }
117 |
118 | }
119 | // For binary only
120 | PCDheader.rowSize = sizeSum;
121 |
122 | return PCDheader;
123 |
124 | },
125 |
126 | parse: function( data, url ) {
127 |
128 | textData = this.binarryToStr( data );
129 |
130 | // Parse the header
131 | // Header is always ascii format
132 | var PCDheader = this.parseHeader( textData );
133 |
134 | // Parse the data
135 | var position = false;
136 | if ( PCDheader.offset.x != undefined )
137 | position = new Float32Array( PCDheader.points * 3 );
138 | var color = false;
139 | if ( PCDheader.offset.rgb != undefined)
140 | color = new Float32Array( PCDheader.points * 3 );
141 | var normal = false;
142 | if ( PCDheader.offset.normal_x != undefined )
143 | normal = new Float32Array( PCDheader.points * 3 );
144 |
145 | if ( PCDheader.data == "ascii" ) {
146 |
147 | var offset = PCDheader.offset;
148 | var pcdData = textData.substr( PCDheader.headerLen );
149 | var lines = pcdData.split( '\n' );
150 | var i3 = 0;
151 | for ( var i = 0; i < lines.length; i ++, i3 += 3 ) {
152 |
153 | var line = lines[ i ].split( " " );
154 | if ( offset.x != undefined ) {
155 |
156 | position[ i3 + 0 ] = parseFloat( line[ offset.x ] );
157 | position[ i3 + 1 ] = parseFloat( line[ offset.y ] );
158 | position[ i3 + 2 ] = parseFloat( line[ offset.z ] );
159 |
160 | }
161 | if ( offset.rgb != undefined ) {
162 |
163 | var c = new Float32Array([parseFloat( line[ offset.rgb ] )]);
164 | var dataview = new DataView( c.buffer, 0 );
165 | color[ i3 + 0 ] = dataview.getUint8(0)/255.0;
166 | color[ i3 + 1 ] = dataview.getUint8(1)/255.0;
167 | color[ i3 + 2 ] = dataview.getUint8(2)/255.0;
168 |
169 | }
170 | if ( offset.normal_x != undefined ) {
171 |
172 | normal[ i3 + 0 ] = parseFloat( line[ offset.normal_x ] );
173 | normal[ i3 + 1 ] = parseFloat( line[ offset.normal_y ] );
174 | normal[ i3 + 2 ] = parseFloat( line[ offset.normal_z ] );
175 |
176 | }
177 |
178 | }
179 |
180 | }
181 |
182 | if ( PCDheader.data == "binary_compressed" ) {
183 |
184 | console.error( 'THREE.PCDLoader: binary_compressed files are not supported' );
185 | return;
186 |
187 | }
188 |
189 | if ( PCDheader.data == "binary" ) {
190 |
191 | var row = 0;
192 | var dataview = new DataView( data, PCDheader.headerLen );
193 | var i = 0;
194 | var offset = PCDheader.offset;
195 | for ( var i3 = 0; i < PCDheader.points; i3 += 3, row += PCDheader.rowSize, i ++ ) {
196 |
197 | if ( offset.x != undefined ) {
198 |
199 | position[ i3 + 0 ] = dataview.getFloat32( row + offset.x, this.littleEndian );
200 | position[ i3 + 1 ] = dataview.getFloat32( row + offset.y, this.littleEndian );
201 | position[ i3 + 2 ] = dataview.getFloat32( row + offset.z, this.littleEndian );
202 |
203 | }
204 | if ( offset.rgb != undefined ) {
205 |
206 | color[ i3 + 0 ] = dataview.getUint8( row + offset.rgb + 0 ) / 255.0;
207 | color[ i3 + 1 ] = dataview.getUint8( row + offset.rgb + 1 ) / 255.0;
208 | color[ i3 + 2 ] = dataview.getUint8( row + offset.rgb + 2 ) / 255.0;
209 |
210 | }
211 | if ( offset.normal_x != undefined ) {
212 |
213 | normal[ i3 + 0 ] = dataview.getFloat32( row + offset.normal_x, this.littleEndian );
214 | normal[ i3 + 1 ] = dataview.getFloat32( row + offset.normal_y, this.littleEndian );
215 | normal[ i3 + 2 ] = dataview.getFloat32( row + offset.normal_z, this.littleEndian );
216 |
217 | }
218 |
219 | }
220 |
221 | }
222 |
223 | var geometry = new THREE.BufferGeometry();
224 | if ( position != false )
225 | geometry.addAttribute( 'position', new THREE.BufferAttribute( position, 3 ) );
226 | if ( color != false )
227 | geometry.addAttribute( 'color', new THREE.BufferAttribute( color, 3 ) );
228 | if ( normal != false )
229 | geometry.addAttribute( 'normal', new THREE.BufferAttribute( normal, 3 ) );
230 |
231 | geometry.computeBoundingSphere();
232 |
233 | var material = new THREE.PointsMaterial( { size: 0.005,
234 | vertexColors: !(color == false) } );
235 | if ( color == false )
236 | material.color.setHex( Math.random() * 0xffffff );
237 |
238 | var mesh = new THREE.Points( geometry, material );
239 | var name = url.split( '' ).reverse().join( '' );
240 | name = /([^\/]*)/.exec( name );
241 | name = name[ 1 ].split( '' ).reverse().join( '' );
242 | mesh.name = name;
243 | mesh.PCDheader = PCDheader;
244 |
245 | return mesh;
246 |
247 | },
248 |
249 | };
250 |
251 | THREE.EventDispatcher.prototype.apply( THREE.PCDLoader.prototype );
252 |
--------------------------------------------------------------------------------
/scratch/virtual-three.js:
--------------------------------------------------------------------------------
1 | //////// THREE ////////
2 |
3 | function mergeProps(obj, fromProps, toProps) {
4 | // check for changed/removed props
5 | for (var fromKey in fromProps) {
6 | // key missing, remove attribute it
7 | if (toProps[fromKey] === undefined) {
8 | delete obj[fromKey];
9 |
10 | } else {
11 | var fromValue = fromProps[fromKey];
12 | var toValue = toProps[fromKey];
13 |
14 | // same value, move on
15 | if(fromValue === toValue)
16 | continue;
17 |
18 | // TODO deep prop diff?
19 | // changed value, update attribute
20 |
21 | switch(fromKey) {
22 | case "rotation":
23 | obj.rotation.set(toValue.x, toValue.y, toValue.z);
24 | break;
25 | case "position":
26 | obj.position.set(toValue.x, toValue.y, toValue.z);
27 | break;
28 | default:
29 | obj[fromKey] = toValue;
30 | }
31 | }
32 | }
33 |
34 | // check for new props
35 | for (var toKey in toProps) {
36 | if (!(toKey in fromProps)) {
37 | obj[toKey] = toProps[toValue];
38 | }
39 | }
40 | }
41 |
42 | // TODO multiple cameras
43 | function ThreeRendererNode(props, camera, scene) {
44 | this.type = "three-renderer"
45 | this.props = props;
46 | this.camera = camera;
47 | this.scene = scene;
48 | this._cache = undefined;
49 | }
50 |
51 | ThreeRendererNode.prototype.render = function() {
52 | if(!this._cache) {
53 | this._cache = new THREE.WebGLRenderer(this.props);
54 | for(k in this.props) {
55 | this._cache[k] = this.props[k];
56 | }
57 | this.camera.render();
58 | this.scene.render();
59 | }
60 |
61 | return this._cache;
62 | };
63 |
64 | ThreeRendererNode.prototype.reconcile = function(other) {
65 | if(this === other)
66 | return;
67 |
68 | if(this.type !== other.type)
69 | throw "Cannot reconcile " + this.type + " and " + other.type;
70 |
71 | if(this.props !== other.props)
72 | mergeProps(this._cache, this.props, other.props);
73 |
74 | this.camera.reconcile(other.camera);
75 | this.scene.reconcile(other.scene);
76 |
77 | // propagate the cache
78 | other._cache = this._cache;
79 | };
80 |
81 |
82 | var ThreeNode = function(type, ctor) {
83 | var nodeCtor = function(props, children) {
84 | this.type = "three-" + type;
85 | this.props = props;
86 | this.children = children;
87 | this._cache = undefined;
88 | }
89 |
90 | nodeCtor.prototype.render = function() {
91 | if(!this._cache) {
92 | this._cache = Object.create(ctor.prototype);
93 | ctor.call(this._cache); // TODO ctor args?
94 |
95 | // if(parent)
96 | // parent.add(this._cache);
97 |
98 | for(k in this.props) {
99 | switch(k) {
100 | case "rotation":
101 | this._cache.rotation.set(this.props[k].x, this.props[k].y, this.props[k].z);
102 | break;
103 | case "position":
104 | this._cache.position.set(this.props[k].x, this.props[k].y, this.props[k].z);
105 | break;
106 | default:
107 | this._cache[k] = this.props[k];
108 | }
109 | }
110 |
111 | if(this.children)
112 | for (var i = 0; i < this.children.length; i++) {
113 | this._cache.add(this.children[i].render());
114 | };
115 | }
116 |
117 | return this._cache;
118 | };
119 |
120 | nodeCtor.prototype.reconcile = function(other) {
121 | if(this === other)
122 | return;
123 |
124 | if(this.type !== other.type)
125 | throw "Cannot reconcile " + this.type + " and " + other.type;
126 |
127 | if(this.props !== other.props)
128 | mergeProps(this._cache, this.props, other.props)
129 |
130 | if(this.children && other.children)
131 | this.reconcileChildren(other);
132 |
133 | // propagate the cache
134 | other._cache = this._cache;
135 | };
136 |
137 | nodeCtor.prototype.reconcileChildren = function(other) {
138 | var thisChildren = this.children;
139 | var otherChildren = other.children;
140 | var minLength = Math.min(thisChildren.length, otherChildren.length);
141 |
142 | for (var i = 0; i < minLength; i++) {
143 | thisChildren[i].reconcile(otherChildren[i]);
144 | };
145 |
146 | // new elements
147 | if(otherChildren.length > thisChildren.length) {
148 | for (var i = minLength; i < otherChildren.length; i++) {
149 | this._cache.add(otherChildren[i].render());
150 | };
151 |
152 | // fewer elements
153 | } else if(thisChildren.length > otherChildren.length) {
154 | for (var i = minLength; i < thisChildren.length; i++) {
155 | this._cache.remove(thisChildren[i]._cache);
156 | };
157 | }
158 | };
159 |
160 | return nodeCtor;
161 | }
162 |
163 | var ThreeOrthographicCameraNode = ThreeNode("orthographic-camera", THREE.OrthographicCamera);
164 | var ThreePerspectiveCameraNode = ThreeNode("perspective-camera", THREE.PerspectiveCamera);
165 | var ThreeSceneNode = ThreeNode("scene", THREE.Scene);
166 | var ThreeObject3DNode = ThreeNode("node", THREE.Object3D);
167 | var ThreeMeshNode = ThreeNode("node", THREE.Mesh);
168 | var ThreeLineNode = ThreeNode("node", THREE.Line);
169 |
170 | //////// API ////////
171 |
172 | goog.provide('zajal.three');
173 |
174 | zajal.three.rednerer = function(props, camera, scene) { return new ThreeRendererNode(props, camera, scene); }
175 | zajal.three.perspectiveCamera = function(props) { return new ThreePerspectiveCameraNode(props); }
176 | zajal.three.scene = function(props, children) { return new ThreeSceneNode(props, children); }
177 | zajal.three.mesh = function(props, children) { return new ThreeMeshNode(props, children); }
178 | zajal.three.line = function(props, children) { return new ThreeLineNode(props, children); }
179 |
180 | //////// TEST ////////
181 |
182 |
183 | // var boxGeo = new THREE.BoxGeometry(1, 1, 1)
184 | // var meshMat = new THREE.MeshBasicMaterial({wireframe:true})
185 |
186 |
187 | // var box = function(t, children) {
188 | // if(!children)
189 | // return new ThreeMeshNode({
190 | // rotation: {x: 0, y: 0, z:+t/1000},
191 | // position: new THREE.Vector3(3, 0, 0),
192 | // geometry:boxGeo,
193 | // material:meshMat})
194 | // else
195 | // return new ThreeMeshNode({
196 | // rotation: {x: 0, y: 0, z:+t * 0.001},
197 | // position: new THREE.Vector3(1, 0, 0),
198 | // geometry:boxGeo,
199 | // material:meshMat},
200 | // children)
201 | // }
202 |
203 | // var boxes = function(t, i) {
204 | // if(i > 0)
205 | // return box(t * i * 0.001, [boxes(t, i - 1)]);
206 | // else
207 | // return box(t)
208 | // }
209 |
210 | // var threeTest = function(t) {
211 | // return new ThreeRendererNode(
212 | // {},
213 | // new ThreePerspectiveCameraNode({fov:60, aspect: 1, near:1, far:1000}),
214 | // new ThreeSceneNode({},
215 | // [boxes(0, 5),
216 | // boxes(t * 2, 5),
217 | // boxes(t * 4, 5),
218 | // boxes(t * 6, 5),
219 | // boxes(t * 8, 5),
220 | // boxes(t * 10, 5),
221 | // boxes(t * 20, 5),
222 | // boxes(t * 30, 5),
223 | // boxes(t * 40, 5),
224 | // boxes(t * 50, 5),
225 | // boxes(t * 60, 5),
226 | // boxes(t * 70, 5),
227 | // boxes(t * 80, 5),
228 | // boxes(t * 90, 5),
229 | // boxes(t * 100, 5),
230 | // boxes(t * 110, 5),
231 | // boxes(t * 120, 5),
232 | // boxes(t * 130, 5),
233 | // boxes(t * 140, 5),
234 | // boxes(t * 150, 5),
235 | // boxes(t * 2, 5),
236 | // boxes(t * 4, 5),
237 | // boxes(t * 6, 5),
238 | // boxes(t * 8, 5),
239 | // boxes(t * 10, 5),
240 | // boxes(t * 20, 5),
241 | // boxes(t * 30, 5),
242 | // boxes(t * 40, 5),
243 | // boxes(t * 50, 5),
244 | // boxes(t * 60, 5),
245 | // boxes(t * 70, 5),
246 | // boxes(t * 80, 5),
247 | // boxes(t * 90, 5),
248 | // boxes(t * 100, 5),
249 | // boxes(t * 110, 5),
250 | // boxes(t * 120, 5),
251 | // boxes(t * 130, 5),
252 | // boxes(t * 140, 5),
253 | // boxes(t * 150, 5),
254 | // boxes(t * 2, 5),
255 | // boxes(t * 4, 5),
256 | // boxes(t * 6, 5),
257 | // boxes(t * 8, 5),
258 | // boxes(t * 10, 5),
259 | // boxes(t * 20, 5),
260 | // boxes(t * 30, 5),
261 | // boxes(t * 40, 5),
262 | // boxes(t * 50, 5),
263 | // boxes(t * 60, 5),
264 | // boxes(t * 70, 5),
265 | // boxes(t * 80, 5),
266 | // boxes(t * 90, 5),
267 | // boxes(t * 100, 5),
268 | // boxes(t * 110, 5),
269 | // boxes(t * 120, 5),
270 | // boxes(t * 130, 5),
271 | // boxes(t * 140, 5),
272 | // boxes(t * 150, 5),
273 | // boxes(t * 2, 5),
274 | // boxes(t * 4, 5),
275 | // boxes(t * 6, 5),
276 | // boxes(t * 8, 5),
277 | // boxes(t * 10, 5),
278 | // boxes(t * 20, 5),
279 | // boxes(t * 30, 5),
280 | // boxes(t * 40, 5),
281 | // boxes(t * 50, 5),
282 | // boxes(t * 60, 5),
283 | // boxes(t * 70, 5),
284 | // boxes(t * 80, 5),
285 | // boxes(t * 90, 5),
286 | // boxes(t * 100, 5),
287 | // boxes(t * 110, 5),
288 | // boxes(t * 120, 5),
289 | // boxes(t * 130, 5),
290 | // boxes(t * 140, 5),
291 | // boxes(t * 150, 5),
292 | // ]));
293 | // }
--------------------------------------------------------------------------------
/scratch/virtual-three-clj.js:
--------------------------------------------------------------------------------
1 | function cachingFn(f) {
2 | var _gensym = cljs.core.gensym().toString();
3 | return function(a) {
4 | if(!a[_gensym])
5 | a[_gensym] = f(a);
6 | return a[_gensym];
7 | }
8 | }
9 |
10 | //////// THREE ////////
11 |
12 | function mergeProps(obj, fromProps, toProps) {
13 | // check for changed/removed props
14 | for (var fromKey in fromProps) {
15 | // key missing, remove attribute it
16 | if (toProps[fromKey] === undefined) {
17 | delete obj[fromKey];
18 |
19 | } else {
20 | var fromValue = fromProps[fromKey];
21 | var toValue = toProps[fromKey];
22 |
23 | // same value, move on
24 | if(fromValue === toValue)
25 | continue;
26 |
27 | // TODO deep prop diff?
28 | // changed value, update attribute
29 |
30 | switch(fromKey) {
31 | case "rotation":
32 | obj.rotation.set(toValue.x, toValue.y, toValue.z);
33 | break;
34 | case "position":
35 | obj.position.set(toValue.x, toValue.y, toValue.z);
36 | break;
37 | default:
38 | obj[fromKey] = toValue;
39 | }
40 | }
41 | }
42 |
43 | // check for new props
44 | for (var toKey in toProps) {
45 | if (!(toKey in fromProps)) {
46 | obj[toKey] = toProps[toValue];
47 | }
48 | }
49 | }
50 |
51 | // TODO multiple cameras
52 | function ThreeRendererNode(props, camera, scene) {
53 | this.type = "three-renderer"
54 | this.props = props;
55 | this.camera = camera;
56 | this.scene = scene;
57 | this._cache = undefined;
58 | }
59 |
60 | ThreeRendererNode.prototype.render = function() {
61 | if(!this._cache) {
62 | this._cache = new THREE.WebGLRenderer(this.props);
63 | for(k in this.props) {
64 | this._cache[k] = this.props[k];
65 | }
66 | this.camera.render();
67 | this.scene.render();
68 | }
69 |
70 | return this._cache;
71 | };
72 |
73 | ThreeRendererNode.prototype.reconcile = function(other) {
74 | if(this === other)
75 | return;
76 |
77 | if(this.type !== other.type)
78 | throw "Cannot reconcile " + this.type + " and " + other.type;
79 |
80 | if(this.props !== other.props)
81 | mergeProps(this._cache, this.props, other.props);
82 |
83 | this.camera.reconcile(other.camera);
84 | this.scene.reconcile(other.scene);
85 |
86 | // propagate the cache
87 | other._cache = this._cache;
88 | };
89 |
90 |
91 | var ThreeNode = function(type, ctor) {
92 | var nodeCtor = function(props, children) {
93 | this.type = "three-" + type;
94 | this.props = props;
95 | this.children = children;
96 | this._cache = undefined;
97 | }
98 |
99 | nodeCtor.prototype.render = function() {
100 | if(!this._cache) {
101 | this._cache = Object.create(ctor.prototype);
102 | ctor.call(this._cache); // TODO ctor args?
103 |
104 | // if(parent)
105 | // parent.add(this._cache);
106 |
107 | for(k in this.props) {
108 | switch(k) {
109 | case "rotation":
110 | this._cache.rotation.set(this.props[k].x, this.props[k].y, this.props[k].z);
111 | break;
112 | case "position":
113 | this._cache.position.set(this.props[k].x, this.props[k].y, this.props[k].z);
114 | break;
115 | default:
116 | this._cache[k] = this.props[k];
117 | }
118 | }
119 |
120 | if(this.children)
121 | for (var i = 0; i < this.children.length; i++) {
122 | this._cache.add(this.children[i].render());
123 | };
124 | }
125 |
126 | return this._cache;
127 | };
128 |
129 | nodeCtor.prototype.reconcile = function(other) {
130 | if(this === other)
131 | return;
132 |
133 | if(this.type !== other.type)
134 | throw "Cannot reconcile " + this.type + " and " + other.type;
135 |
136 | if(this.props !== other.props)
137 | mergeProps(this._cache, this.props, other.props)
138 |
139 | if(this.children && other.children)
140 | this.reconcileChildren(other);
141 |
142 | // propagate the cache
143 | other._cache = this._cache;
144 | };
145 |
146 | nodeCtor.prototype.reconcileChildren = function(other) {
147 | var thisChildren = this.children;
148 | var otherChildren = other.children;
149 | var minLength = Math.min(thisChildren.length, otherChildren.length);
150 |
151 | for (var i = 0; i < minLength; i++) {
152 | thisChildren[i].reconcile(otherChildren[i]);
153 | };
154 |
155 | // new elements
156 | if(otherChildren.length > thisChildren.length) {
157 | for (var i = minLength; i < otherChildren.length; i++) {
158 | this._cache.add(otherChildren[i].render());
159 | };
160 |
161 | // fewer elements
162 | } else if(thisChildren.length > otherChildren.length) {
163 | for (var i = minLength; i < thisChildren.length; i++) {
164 | this._cache.remove(thisChildren[i]._cache);
165 | };
166 | }
167 | };
168 |
169 | return nodeCtor;
170 | }
171 |
172 | var ThreeOrthographicCameraNode = ThreeNode("orthographic-camera", THREE.OrthographicCamera);
173 | var ThreePerspectiveCameraNode = ThreeNode("perspective-camera", THREE.PerspectiveCamera);
174 | var ThreeSceneNode = ThreeNode("scene", THREE.Scene);
175 | var ThreeObject3DNode = ThreeNode("node", THREE.Object3D);
176 | var ThreeMeshNode = ThreeNode("node", THREE.Mesh);
177 | var ThreeLineNode = ThreeNode("node", THREE.Line);
178 |
179 | //////// API ////////
180 |
181 | goog.provide('zajal.three');
182 |
183 | zajal.three.rednerer = function(props, camera, scene) { return new ThreeRendererNode(props, camera, scene); }
184 | zajal.three.perspectiveCamera = function(props) { return new ThreePerspectiveCameraNode(props); }
185 | zajal.three.scene = function(props, children) { return new ThreeSceneNode(props, children); }
186 | zajal.three.mesh = function(props, children) { return new ThreeMeshNode(props, children); }
187 | zajal.three.line = function(props, children) { return new ThreeLineNode(props, children); }
188 |
189 | //////// TEST ////////
190 |
191 |
192 | // var boxGeo = new THREE.BoxGeometry(1, 1, 1)
193 | // var meshMat = new THREE.MeshBasicMaterial({wireframe:true})
194 |
195 |
196 | // var box = function(t, children) {
197 | // if(!children)
198 | // return new ThreeMeshNode({
199 | // rotation: {x: 0, y: 0, z:+t/1000},
200 | // position: new THREE.Vector3(3, 0, 0),
201 | // geometry:boxGeo,
202 | // material:meshMat})
203 | // else
204 | // return new ThreeMeshNode({
205 | // rotation: {x: 0, y: 0, z:+t * 0.001},
206 | // position: new THREE.Vector3(1, 0, 0),
207 | // geometry:boxGeo,
208 | // material:meshMat},
209 | // children)
210 | // }
211 |
212 | // var boxes = function(t, i) {
213 | // if(i > 0)
214 | // return box(t * i * 0.001, [boxes(t, i - 1)]);
215 | // else
216 | // return box(t)
217 | // }
218 |
219 | // var threeTest = function(t) {
220 | // return new ThreeRendererNode(
221 | // {},
222 | // new ThreePerspectiveCameraNode({fov:60, aspect: 1, near:1, far:1000}),
223 | // new ThreeSceneNode({},
224 | // [boxes(0, 5),
225 | // boxes(t * 2, 5),
226 | // boxes(t * 4, 5),
227 | // boxes(t * 6, 5),
228 | // boxes(t * 8, 5),
229 | // boxes(t * 10, 5),
230 | // boxes(t * 20, 5),
231 | // boxes(t * 30, 5),
232 | // boxes(t * 40, 5),
233 | // boxes(t * 50, 5),
234 | // boxes(t * 60, 5),
235 | // boxes(t * 70, 5),
236 | // boxes(t * 80, 5),
237 | // boxes(t * 90, 5),
238 | // boxes(t * 100, 5),
239 | // boxes(t * 110, 5),
240 | // boxes(t * 120, 5),
241 | // boxes(t * 130, 5),
242 | // boxes(t * 140, 5),
243 | // boxes(t * 150, 5),
244 | // boxes(t * 2, 5),
245 | // boxes(t * 4, 5),
246 | // boxes(t * 6, 5),
247 | // boxes(t * 8, 5),
248 | // boxes(t * 10, 5),
249 | // boxes(t * 20, 5),
250 | // boxes(t * 30, 5),
251 | // boxes(t * 40, 5),
252 | // boxes(t * 50, 5),
253 | // boxes(t * 60, 5),
254 | // boxes(t * 70, 5),
255 | // boxes(t * 80, 5),
256 | // boxes(t * 90, 5),
257 | // boxes(t * 100, 5),
258 | // boxes(t * 110, 5),
259 | // boxes(t * 120, 5),
260 | // boxes(t * 130, 5),
261 | // boxes(t * 140, 5),
262 | // boxes(t * 150, 5),
263 | // boxes(t * 2, 5),
264 | // boxes(t * 4, 5),
265 | // boxes(t * 6, 5),
266 | // boxes(t * 8, 5),
267 | // boxes(t * 10, 5),
268 | // boxes(t * 20, 5),
269 | // boxes(t * 30, 5),
270 | // boxes(t * 40, 5),
271 | // boxes(t * 50, 5),
272 | // boxes(t * 60, 5),
273 | // boxes(t * 70, 5),
274 | // boxes(t * 80, 5),
275 | // boxes(t * 90, 5),
276 | // boxes(t * 100, 5),
277 | // boxes(t * 110, 5),
278 | // boxes(t * 120, 5),
279 | // boxes(t * 130, 5),
280 | // boxes(t * 140, 5),
281 | // boxes(t * 150, 5),
282 | // boxes(t * 2, 5),
283 | // boxes(t * 4, 5),
284 | // boxes(t * 6, 5),
285 | // boxes(t * 8, 5),
286 | // boxes(t * 10, 5),
287 | // boxes(t * 20, 5),
288 | // boxes(t * 30, 5),
289 | // boxes(t * 40, 5),
290 | // boxes(t * 50, 5),
291 | // boxes(t * 60, 5),
292 | // boxes(t * 70, 5),
293 | // boxes(t * 80, 5),
294 | // boxes(t * 90, 5),
295 | // boxes(t * 100, 5),
296 | // boxes(t * 110, 5),
297 | // boxes(t * 120, 5),
298 | // boxes(t * 130, 5),
299 | // boxes(t * 140, 5),
300 | // boxes(t * 150, 5),
301 | // ]));
302 | // }
--------------------------------------------------------------------------------
/js/loaders/MTLLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Loads a Wavefront .mtl file specifying materials
3 | *
4 | * @author angelxuanchang
5 | */
6 |
7 | THREE.MTLLoader = function( manager ) {
8 |
9 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
10 |
11 | };
12 |
13 | THREE.MTLLoader.prototype = {
14 |
15 | constructor: THREE.MTLLoader,
16 |
17 | load: function ( url, onLoad, onProgress, onError ) {
18 |
19 | var scope = this;
20 |
21 | var loader = new THREE.XHRLoader( this.manager );
22 | loader.setPath( this.path );
23 | loader.load( url, function ( text ) {
24 |
25 | onLoad( scope.parse( text ) );
26 |
27 | }, onProgress, onError );
28 |
29 | },
30 |
31 | setPath: function ( value ) {
32 |
33 | this.path = value;
34 |
35 | },
36 |
37 | setBaseUrl: function( value ) {
38 |
39 | // TODO: Merge with setPath()? Or rename to setTexturePath?
40 |
41 | this.baseUrl = value;
42 |
43 | },
44 |
45 | setCrossOrigin: function ( value ) {
46 |
47 | this.crossOrigin = value;
48 |
49 | },
50 |
51 | setMaterialOptions: function ( value ) {
52 |
53 | this.materialOptions = value;
54 |
55 | },
56 |
57 | /**
58 | * Parses loaded MTL file
59 | * @param text - Content of MTL file
60 | * @return {THREE.MTLLoader.MaterialCreator}
61 | */
62 | parse: function ( text ) {
63 |
64 | var lines = text.split( "\n" );
65 | var info = {};
66 | var delimiter_pattern = /\s+/;
67 | var materialsInfo = {};
68 |
69 | for ( var i = 0; i < lines.length; i ++ ) {
70 |
71 | var line = lines[ i ];
72 | line = line.trim();
73 |
74 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
75 |
76 | // Blank line or comment ignore
77 | continue;
78 |
79 | }
80 |
81 | var pos = line.indexOf( ' ' );
82 |
83 | var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
84 | key = key.toLowerCase();
85 |
86 | var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : "";
87 | value = value.trim();
88 |
89 | if ( key === "newmtl" ) {
90 |
91 | // New material
92 |
93 | info = { name: value };
94 | materialsInfo[ value ] = info;
95 |
96 | } else if ( info ) {
97 |
98 | if ( key === "ka" || key === "kd" || key === "ks" ) {
99 |
100 | var ss = value.split( delimiter_pattern, 3 );
101 | info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
102 |
103 | } else {
104 |
105 | info[ key ] = value;
106 |
107 | }
108 |
109 | }
110 |
111 | }
112 |
113 | var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.materialOptions );
114 | materialCreator.setCrossOrigin( this.crossOrigin );
115 | materialCreator.setManager( this.manager );
116 | materialCreator.setMaterials( materialsInfo );
117 | return materialCreator;
118 |
119 | }
120 |
121 | };
122 |
123 | /**
124 | * Create a new THREE-MTLLoader.MaterialCreator
125 | * @param baseUrl - Url relative to which textures are loaded
126 | * @param options - Set of options on how to construct the materials
127 | * side: Which side to apply the material
128 | * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
129 | * wrap: What type of wrapping to apply for textures
130 | * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
131 | * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
132 | * Default: false, assumed to be already normalized
133 | * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
134 | * Default: false
135 | * @constructor
136 | */
137 |
138 | THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) {
139 |
140 | this.baseUrl = baseUrl;
141 | this.options = options;
142 | this.materialsInfo = {};
143 | this.materials = {};
144 | this.materialsArray = [];
145 | this.nameLookup = {};
146 |
147 | this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
148 | this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
149 |
150 | };
151 |
152 | THREE.MTLLoader.MaterialCreator.prototype = {
153 |
154 | constructor: THREE.MTLLoader.MaterialCreator,
155 |
156 | setCrossOrigin: function ( value ) {
157 |
158 | this.crossOrigin = value;
159 |
160 | },
161 |
162 | setManager: function ( value ) {
163 |
164 | this.manager = value;
165 |
166 | },
167 |
168 | setMaterials: function( materialsInfo ) {
169 |
170 | this.materialsInfo = this.convert( materialsInfo );
171 | this.materials = {};
172 | this.materialsArray = [];
173 | this.nameLookup = {};
174 |
175 | },
176 |
177 | convert: function( materialsInfo ) {
178 |
179 | if ( ! this.options ) return materialsInfo;
180 |
181 | var converted = {};
182 |
183 | for ( var mn in materialsInfo ) {
184 |
185 | // Convert materials info into normalized form based on options
186 |
187 | var mat = materialsInfo[ mn ];
188 |
189 | var covmat = {};
190 |
191 | converted[ mn ] = covmat;
192 |
193 | for ( var prop in mat ) {
194 |
195 | var save = true;
196 | var value = mat[ prop ];
197 | var lprop = prop.toLowerCase();
198 |
199 | switch ( lprop ) {
200 |
201 | case 'kd':
202 | case 'ka':
203 | case 'ks':
204 |
205 | // Diffuse color (color under white light) using RGB values
206 |
207 | if ( this.options && this.options.normalizeRGB ) {
208 |
209 | value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
210 |
211 | }
212 |
213 | if ( this.options && this.options.ignoreZeroRGBs ) {
214 |
215 | if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) {
216 |
217 | // ignore
218 |
219 | save = false;
220 |
221 | }
222 |
223 | }
224 |
225 | break;
226 |
227 | default:
228 |
229 | break;
230 | }
231 |
232 | if ( save ) {
233 |
234 | covmat[ lprop ] = value;
235 |
236 | }
237 |
238 | }
239 |
240 | }
241 |
242 | return converted;
243 |
244 | },
245 |
246 | preload: function () {
247 |
248 | for ( var mn in this.materialsInfo ) {
249 |
250 | this.create( mn );
251 |
252 | }
253 |
254 | },
255 |
256 | getIndex: function( materialName ) {
257 |
258 | return this.nameLookup[ materialName ];
259 |
260 | },
261 |
262 | getAsArray: function() {
263 |
264 | var index = 0;
265 |
266 | for ( var mn in this.materialsInfo ) {
267 |
268 | this.materialsArray[ index ] = this.create( mn );
269 | this.nameLookup[ mn ] = index;
270 | index ++;
271 |
272 | }
273 |
274 | return this.materialsArray;
275 |
276 | },
277 |
278 | create: function ( materialName ) {
279 |
280 | if ( this.materials[ materialName ] === undefined ) {
281 |
282 | this.createMaterial_( materialName );
283 |
284 | }
285 |
286 | return this.materials[ materialName ];
287 |
288 | },
289 |
290 | createMaterial_: function ( materialName ) {
291 |
292 | // Create material
293 |
294 | var mat = this.materialsInfo[ materialName ];
295 | var params = {
296 |
297 | name: materialName,
298 | side: this.side
299 |
300 | };
301 |
302 | for ( var prop in mat ) {
303 |
304 | var value = mat[ prop ];
305 |
306 | if ( value === '' ) {
307 | continue;
308 | }
309 |
310 | switch ( prop.toLowerCase() ) {
311 |
312 | // Ns is material specular exponent
313 |
314 | case 'kd':
315 |
316 | // Diffuse color (color under white light) using RGB values
317 |
318 | params[ 'color' ] = new THREE.Color().fromArray( value );
319 |
320 | break;
321 |
322 | case 'ks':
323 |
324 | // Specular color (color when light is reflected from shiny surface) using RGB values
325 | params[ 'specular' ] = new THREE.Color().fromArray( value );
326 |
327 | break;
328 |
329 | case 'map_kd':
330 |
331 | // Diffuse texture map
332 |
333 | params[ 'map' ] = this.loadTexture( this.baseUrl + value );
334 | params[ 'map' ].wrapS = this.wrap;
335 | params[ 'map' ].wrapT = this.wrap;
336 |
337 | break;
338 |
339 | case 'ns':
340 |
341 | // The specular exponent (defines the focus of the specular highlight)
342 | // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
343 |
344 | params[ 'shininess' ] = parseFloat( value );
345 |
346 | break;
347 |
348 | case 'd':
349 |
350 | if ( value < 1 ) {
351 |
352 | params[ 'opacity' ] = value;
353 | params[ 'transparent' ] = true;
354 |
355 | }
356 |
357 | break;
358 |
359 | case 'Tr':
360 |
361 | if ( value > 0 ) {
362 |
363 | params[ 'opacity' ] = 1 - value;
364 | params[ 'transparent' ] = true;
365 |
366 | }
367 |
368 | break;
369 |
370 | case 'map_bump':
371 | case 'bump':
372 |
373 | // Bump texture map
374 |
375 | if ( params[ 'bumpMap' ] ) break; // Avoid loading twice.
376 |
377 | params[ 'bumpMap' ] = this.loadTexture( this.baseUrl + value );
378 | params[ 'bumpMap' ].wrapS = this.wrap;
379 | params[ 'bumpMap' ].wrapT = this.wrap;
380 |
381 | break;
382 |
383 | default:
384 | break;
385 |
386 | }
387 |
388 | }
389 |
390 | this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
391 | return this.materials[ materialName ];
392 |
393 | },
394 |
395 |
396 | loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
397 |
398 | var texture;
399 | var loader = THREE.Loader.Handlers.get( url );
400 | var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager;
401 |
402 | if ( loader === null ) {
403 |
404 | loader = new THREE.TextureLoader( manager );
405 |
406 | }
407 |
408 | if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
409 | texture = loader.load( url, onLoad, onProgress, onError );
410 |
411 | if ( mapping !== undefined ) texture.mapping = mapping;
412 |
413 | return texture;
414 |
415 | }
416 |
417 | };
418 |
419 | THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype );
420 |
--------------------------------------------------------------------------------
/js/loaders/OBJLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.OBJLoader = function ( manager ) {
6 |
7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 |
9 | this.materials = null;
10 |
11 | };
12 |
13 | THREE.OBJLoader.prototype = {
14 |
15 | constructor: THREE.OBJLoader,
16 |
17 | load: function ( url, onLoad, onProgress, onError ) {
18 |
19 | var scope = this;
20 |
21 | var loader = new THREE.XHRLoader( scope.manager );
22 | loader.setPath( this.path );
23 | loader.load( url, function ( text ) {
24 |
25 | onLoad( scope.parse( text ) );
26 |
27 | }, onProgress, onError );
28 |
29 | },
30 |
31 | setPath: function ( value ) {
32 |
33 | this.path = value;
34 |
35 | },
36 |
37 | setMaterials: function ( materials ) {
38 |
39 | this.materials = materials;
40 |
41 | },
42 |
43 | parse: function ( text ) {
44 |
45 | console.time( 'OBJLoader' );
46 |
47 | var objects = [];
48 | var object;
49 | var foundObjects = false;
50 | var vertices = [];
51 | var normals = [];
52 | var uvs = [];
53 |
54 | function addObject( name ) {
55 |
56 | var geometry = {
57 | vertices: [],
58 | normals: [],
59 | uvs: []
60 | };
61 |
62 | var material = {
63 | name: '',
64 | smooth: true
65 | };
66 |
67 | object = {
68 | name: name,
69 | geometry: geometry,
70 | material: material
71 | };
72 |
73 | objects.push( object );
74 |
75 | }
76 |
77 | function parseVertexIndex( value ) {
78 |
79 | var index = parseInt( value );
80 |
81 | return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3;
82 |
83 | }
84 |
85 | function parseNormalIndex( value ) {
86 |
87 | var index = parseInt( value );
88 |
89 | return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3;
90 |
91 | }
92 |
93 | function parseUVIndex( value ) {
94 |
95 | var index = parseInt( value );
96 |
97 | return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2;
98 |
99 | }
100 |
101 | function addVertex( a, b, c ) {
102 |
103 | object.geometry.vertices.push(
104 | vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ],
105 | vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ],
106 | vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ]
107 | );
108 |
109 | }
110 |
111 | function addNormal( a, b, c ) {
112 |
113 | object.geometry.normals.push(
114 | normals[ a ], normals[ a + 1 ], normals[ a + 2 ],
115 | normals[ b ], normals[ b + 1 ], normals[ b + 2 ],
116 | normals[ c ], normals[ c + 1 ], normals[ c + 2 ]
117 | );
118 |
119 | }
120 |
121 | function addUV( a, b, c ) {
122 |
123 | object.geometry.uvs.push(
124 | uvs[ a ], uvs[ a + 1 ],
125 | uvs[ b ], uvs[ b + 1 ],
126 | uvs[ c ], uvs[ c + 1 ]
127 | );
128 |
129 | }
130 |
131 | function addFace( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) {
132 |
133 | var ia = parseVertexIndex( a );
134 | var ib = parseVertexIndex( b );
135 | var ic = parseVertexIndex( c );
136 | var id;
137 |
138 | if ( d === undefined ) {
139 |
140 | addVertex( ia, ib, ic );
141 |
142 | } else {
143 |
144 | id = parseVertexIndex( d );
145 |
146 | addVertex( ia, ib, id );
147 | addVertex( ib, ic, id );
148 |
149 | }
150 |
151 | if ( ua !== undefined ) {
152 |
153 | ia = parseUVIndex( ua );
154 | ib = parseUVIndex( ub );
155 | ic = parseUVIndex( uc );
156 |
157 | if ( d === undefined ) {
158 |
159 | addUV( ia, ib, ic );
160 |
161 | } else {
162 |
163 | id = parseUVIndex( ud );
164 |
165 | addUV( ia, ib, id );
166 | addUV( ib, ic, id );
167 |
168 | }
169 |
170 | }
171 |
172 | if ( na !== undefined ) {
173 |
174 | ia = parseNormalIndex( na );
175 | ib = parseNormalIndex( nb );
176 | ic = parseNormalIndex( nc );
177 |
178 | if ( d === undefined ) {
179 |
180 | addNormal( ia, ib, ic );
181 |
182 | } else {
183 |
184 | id = parseNormalIndex( nd );
185 |
186 | addNormal( ia, ib, id );
187 | addNormal( ib, ic, id );
188 |
189 | }
190 |
191 | }
192 |
193 | }
194 |
195 | addObject( '' );
196 |
197 | // v float float float
198 | var vertex_pattern = /^v\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/;
199 |
200 | // vn float float float
201 | var normal_pattern = /^vn\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/;
202 |
203 | // vt float float
204 | var uv_pattern = /^vt\s+([\d|\.|\+|\-|e|E]+)\s+([\d|\.|\+|\-|e|E]+)/;
205 |
206 | // f vertex vertex vertex ...
207 | var face_pattern1 = /^f\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(?:\s+(-?\d+))?/;
208 |
209 | // f vertex/uv vertex/uv vertex/uv ...
210 | var face_pattern2 = /^f\s+((-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+))(?:\s+((-?\d+)\/(-?\d+)))?/;
211 |
212 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ...
213 | var face_pattern3 = /^f\s+((-?\d+)\/(-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+)\/(-?\d+))\s+((-?\d+)\/(-?\d+)\/(-?\d+))(?:\s+((-?\d+)\/(-?\d+)\/(-?\d+)))?/;
214 |
215 | // f vertex//normal vertex//normal vertex//normal ...
216 | var face_pattern4 = /^f\s+((-?\d+)\/\/(-?\d+))\s+((-?\d+)\/\/(-?\d+))\s+((-?\d+)\/\/(-?\d+))(?:\s+((-?\d+)\/\/(-?\d+)))?/;
217 |
218 | var object_pattern = /^[og]\s+(.+)/;
219 |
220 | var smoothing_pattern = /^s\s+([01]|on|off)/;
221 |
222 | //
223 |
224 | var lines = text.split( '\n' );
225 |
226 | for ( var i = 0; i < lines.length; i ++ ) {
227 |
228 | var line = lines[ i ];
229 | line = line.trim();
230 |
231 | var result;
232 |
233 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
234 |
235 | continue;
236 |
237 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) {
238 |
239 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
240 |
241 | vertices.push(
242 | parseFloat( result[ 1 ] ),
243 | parseFloat( result[ 2 ] ),
244 | parseFloat( result[ 3 ] )
245 | );
246 |
247 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) {
248 |
249 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"]
250 |
251 | normals.push(
252 | parseFloat( result[ 1 ] ),
253 | parseFloat( result[ 2 ] ),
254 | parseFloat( result[ 3 ] )
255 | );
256 |
257 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) {
258 |
259 | // ["vt 0.1 0.2", "0.1", "0.2"]
260 |
261 | uvs.push(
262 | parseFloat( result[ 1 ] ),
263 | parseFloat( result[ 2 ] )
264 | );
265 |
266 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) {
267 |
268 | // ["f 1 2 3", "1", "2", "3", undefined]
269 |
270 | addFace(
271 | result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ]
272 | );
273 |
274 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) {
275 |
276 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined]
277 |
278 | addFace(
279 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
280 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
281 | );
282 |
283 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) {
284 |
285 | // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined]
286 |
287 | addFace(
288 | result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ],
289 | result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ],
290 | result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ]
291 | );
292 |
293 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) {
294 |
295 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined]
296 |
297 | addFace(
298 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ],
299 | undefined, undefined, undefined, undefined,
300 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ]
301 | );
302 |
303 | } else if ( ( result = object_pattern.exec( line ) ) !== null ) {
304 |
305 | // o object_name
306 | // or
307 | // g group_name
308 |
309 | var name = result[ 1 ].trim();
310 |
311 | if ( foundObjects === false ) {
312 |
313 | foundObjects = true;
314 | object.name = name;
315 |
316 | } else {
317 |
318 | addObject( name );
319 |
320 | }
321 |
322 | } else if ( /^usemtl /.test( line ) ) {
323 |
324 | // material
325 |
326 | object.material.name = line.substring( 7 ).trim();
327 |
328 | } else if ( /^mtllib /.test( line ) ) {
329 |
330 | // mtl file
331 |
332 | } else if ( ( result = smoothing_pattern.exec( line ) ) !== null ) {
333 |
334 | // smooth shading
335 |
336 | object.material.smooth = result[ 1 ] === "1" || result[ 1 ] === "on";
337 |
338 | } else {
339 |
340 | throw new Error( "Unexpected line: " + line );
341 |
342 | }
343 |
344 | }
345 |
346 | var container = new THREE.Group();
347 |
348 | for ( var i = 0, l = objects.length; i < l; i ++ ) {
349 |
350 | object = objects[ i ];
351 | var geometry = object.geometry;
352 |
353 | var buffergeometry = new THREE.BufferGeometry();
354 |
355 | buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) );
356 |
357 | if ( geometry.normals.length > 0 ) {
358 |
359 | buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) );
360 |
361 | } else {
362 |
363 | buffergeometry.computeVertexNormals();
364 |
365 | }
366 |
367 | if ( geometry.uvs.length > 0 ) {
368 |
369 | buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) );
370 |
371 | }
372 |
373 | var material;
374 |
375 | if ( this.materials !== null ) {
376 |
377 | material = this.materials.create( object.material.name );
378 |
379 | }
380 |
381 | if ( !material ) {
382 |
383 | material = new THREE.MeshPhongMaterial();
384 | material.name = object.material.name;
385 |
386 | }
387 |
388 | material.shading = object.material.smooth ? THREE.SmoothShading : THREE.FlatShading;
389 |
390 | var mesh = new THREE.Mesh( buffergeometry, material );
391 | mesh.name = object.name;
392 |
393 | container.add( mesh );
394 |
395 | }
396 |
397 | console.timeEnd( 'OBJLoader' );
398 |
399 | return container;
400 |
401 | }
402 |
403 | };
404 |
--------------------------------------------------------------------------------
/js/loaders/AssimpJSONLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Alexander Gessler / http://www.greentoken.de/
3 | * https://github.com/acgessler
4 | *
5 | * Loader for models imported with Open Asset Import Library (http://assimp.sf.net)
6 | * through assimp2json (https://github.com/acgessler/assimp2json).
7 | *
8 | * Supports any input format that assimp supports, including 3ds, obj, dae, blend,
9 | * fbx, x, ms3d, lwo (and many more).
10 | *
11 | * See webgl_loader_assimp2json example.
12 | */
13 |
14 | THREE.AssimpJSONLoader = function ( manager ) {
15 |
16 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
17 |
18 | };
19 |
20 | THREE.AssimpJSONLoader.prototype = {
21 |
22 | constructor: THREE.AssimpJSONLoader,
23 |
24 | load: function ( url, onLoad, onProgress, onError ) {
25 |
26 | var scope = this;
27 |
28 | this.texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : this.extractUrlBase( url );
29 |
30 | var loader = new THREE.XHRLoader( this.manager );
31 | loader.load( url, function ( text ) {
32 |
33 | var json = JSON.parse( text ), scene, metadata;
34 |
35 | // Check __metadata__ meta header if present
36 | // This header is used to disambiguate between
37 | // different JSON-based file formats.
38 | metadata = json.__metadata__;
39 | if ( typeof metadata !== 'undefined' ) {
40 |
41 | // Check if assimp2json at all
42 | if ( metadata.format !== 'assimp2json' ) {
43 |
44 | onError( 'Not an assimp2json scene' );
45 | return;
46 |
47 | }
48 | // Check major format version
49 | else if ( metadata.version < 100 && metadata.version >= 200 ) {
50 |
51 | onError( 'Unsupported assimp2json file format version' );
52 | return;
53 |
54 | }
55 |
56 | }
57 |
58 | scene = scope.parse( json );
59 | onLoad( scene );
60 |
61 | }, onProgress, onError );
62 |
63 | },
64 |
65 | setCrossOrigin: function ( value ) {
66 |
67 | this.crossOrigin = value;
68 |
69 | },
70 |
71 | setTexturePath: function ( value ) {
72 |
73 | this.texturePath = value;
74 |
75 | },
76 |
77 | extractUrlBase: function ( url ) {
78 |
79 | // from three/src/loaders/Loader.js
80 | var parts = url.split( '/' );
81 | parts.pop();
82 | return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';
83 |
84 | },
85 |
86 | parse: function ( json ) {
87 |
88 | var meshes = this.parseList ( json.meshes, this.parseMesh );
89 | var materials = this.parseList ( json.materials, this.parseMaterial );
90 | return this.parseObject( json, json.rootnode, meshes, materials );
91 |
92 | },
93 |
94 | parseList : function( json, handler ) {
95 |
96 | var meshes = new Array( json.length );
97 | for ( var i = 0; i < json.length; ++ i ) {
98 |
99 | meshes[ i ] = handler.call( this, json[ i ] );
100 |
101 | }
102 | return meshes;
103 |
104 | },
105 |
106 | parseMesh : function( json ) {
107 |
108 | var vertex, geometry, i, e, in_data, src;
109 |
110 |
111 | geometry = new THREE.Geometry();
112 |
113 | // read vertex positions
114 | for ( in_data = json.vertices, i = 0, e = in_data.length; i < e; ) {
115 |
116 | geometry.vertices.push( new THREE.Vector3( in_data[ i ++ ], in_data[ i ++ ], in_data[ i ++ ] ) );
117 |
118 | }
119 |
120 | // read faces
121 | var cnt = 0;
122 | for ( in_data = json.faces, i = 0, e = in_data.length; i < e; ++ i ) {
123 |
124 | src = in_data[ i ];
125 | face = new THREE.Face3( src[ 0 ], src[ 1 ], src[ 2 ] );
126 | geometry.faces.push( face );
127 |
128 | }
129 |
130 | // read texture coordinates - three.js attaches them to its faces
131 | json.texturecoords = json.texturecoords || [];
132 | for ( i = 0, e = json.texturecoords.length; i < e; ++ i ) {
133 |
134 | function convertTextureCoords( in_uv, out_faces, out_vertex_uvs ) {
135 |
136 | var i, e, face, a, b, c;
137 |
138 | for ( i = 0, e = out_faces.length; i < e; ++ i ) {
139 |
140 | face = out_faces[ i ];
141 | a = face.a * 2;
142 | b = face.b * 2;
143 | c = face.c * 2;
144 | out_vertex_uvs.push( [
145 | new THREE.Vector2( in_uv[ a ], in_uv[ a + 1 ] ),
146 | new THREE.Vector2( in_uv[ b ], in_uv[ b + 1 ] ),
147 | new THREE.Vector2( in_uv[ c ], in_uv[ c + 1 ] )
148 | ] );
149 |
150 | }
151 |
152 | }
153 |
154 | convertTextureCoords( json.texturecoords[ i ], geometry.faces, geometry.faceVertexUvs[ i ] );
155 |
156 | }
157 |
158 | // read normals - three.js also attaches them to its faces
159 | if ( json.normals ) {
160 |
161 | function convertNormals( in_nor, out_faces ) {
162 |
163 | var i, e, face, a, b, c;
164 |
165 | for ( i = 0, e = out_faces.length; i < e; ++ i ) {
166 |
167 | face = out_faces[ i ];
168 | a = face.a * 3;
169 | b = face.b * 3;
170 | c = face.c * 3;
171 | face.vertexNormals = [
172 | new THREE.Vector3( in_nor[ a ], in_nor[ a + 1 ], in_nor[ a + 2 ] ),
173 | new THREE.Vector3( in_nor[ b ], in_nor[ b + 1 ], in_nor[ b + 2 ] ),
174 | new THREE.Vector3( in_nor[ c ], in_nor[ c + 1 ], in_nor[ c + 2 ] )
175 | ];
176 |
177 | }
178 |
179 | }
180 |
181 | convertNormals( json.normals, geometry.faces );
182 |
183 | }
184 |
185 | // read vertex colors - three.js also attaches them to its faces
186 | if ( json.colors && json.colors[ 0 ] ) {
187 |
188 | function convertColors( in_color, out_faces ) {
189 |
190 | for ( var i = 0, e = out_faces.length; i < e; ++ i ) {
191 |
192 | var face = out_faces[ i ];
193 | var a = face.a * 4;
194 | var b = face.b * 4;
195 | var c = face.c * 4;
196 |
197 | face.vertexColors = [
198 | new THREE.Color().fromArray( a ),
199 | new THREE.Color().fromArray( b ),
200 | new THREE.Color().fromArray( c )
201 | ];
202 |
203 | }
204 |
205 | }
206 |
207 | convertColors( json.colors[ 0 ], geometry.faces );
208 |
209 | }
210 |
211 |
212 | //geometry.computeFaceNormals();
213 | //geometry.computeVertexNormals();
214 | geometry.computeBoundingSphere();
215 |
216 | return geometry;
217 |
218 | },
219 |
220 | parseMaterial : function( json ) {
221 |
222 | var mat = null,
223 | scope = this, i, prop, has_textures = [],
224 |
225 | init_props = {
226 | shading : THREE.SmoothShading
227 | };
228 |
229 | function toColor( value_arr ) {
230 |
231 | var col = new THREE.Color();
232 | col.setRGB( value_arr[ 0 ], value_arr[ 1 ], value_arr[ 2 ] );
233 | return col;
234 |
235 | }
236 |
237 | function defaultTexture() {
238 |
239 | var im = new Image();
240 | im.width = 1;
241 | im.height = 1;
242 | return new THREE.Texture( im );
243 |
244 | }
245 |
246 | for ( var i in json.properties ) {
247 |
248 | prop = json.properties[ i ];
249 |
250 | if ( prop.key === '$tex.file' ) {
251 |
252 | // prop.semantic gives the type of the texture
253 | // 1: diffuse
254 | // 2: specular mao
255 | // 5: height map (bumps)
256 | // 6: normal map
257 | // more values (i.e. emissive, environment) are known by assimp and may be relevant
258 | if ( prop.semantic === 1 || prop.semantic === 5 || prop.semantic === 6 || prop.semantic === 2 ) {
259 |
260 | ( function( semantic ) {
261 |
262 | var loader = new THREE.TextureLoader( scope.manager ),
263 | keyname;
264 |
265 | if ( semantic === 1 ) {
266 |
267 | keyname = 'map';
268 |
269 | } else if ( semantic === 5 ) {
270 |
271 | keyname = 'bumpMap';
272 |
273 | } else if ( semantic === 6 ) {
274 |
275 | keyname = 'normalMap';
276 |
277 | } else if ( semantic === 2 ) {
278 |
279 | keyname = 'specularMap';
280 |
281 | }
282 |
283 | has_textures.push( keyname );
284 |
285 | loader.setCrossOrigin( this.crossOrigin );
286 | var material_url = scope.texturePath + '/' + prop.value;
287 | material_url = material_url.replace( /\\/g, '/' );
288 | loader.load( material_url, function( tex ) {
289 |
290 | if ( tex ) {
291 |
292 | // TODO: read texture settings from assimp.
293 | // Wrapping is the default, though.
294 | tex.wrapS = tex.wrapT = THREE.RepeatWrapping;
295 |
296 | mat[ keyname ] = tex;
297 | mat.needsUpdate = true;
298 |
299 | }
300 |
301 | } );
302 |
303 | } )( prop.semantic );
304 |
305 | }
306 |
307 | } else if ( prop.key === '?mat.name' ) {
308 |
309 | init_props.name = prop.value;
310 |
311 | } else if ( prop.key === '$clr.diffuse' ) {
312 |
313 | init_props.color = toColor( prop.value );
314 |
315 | } else if ( prop.key === '$clr.specular' ) {
316 |
317 | init_props.specular = toColor( prop.value );
318 |
319 | } else if ( prop.key === '$clr.emissive' ) {
320 |
321 | init_props.emissive = toColor( prop.value );
322 |
323 | } else if ( prop.key === '$mat.shadingm' ) {
324 |
325 | // aiShadingMode_Flat
326 | if ( prop.value === 1 ) {
327 |
328 | init_props.shading = THREE.FlatShading;
329 |
330 | }
331 |
332 | } else if ( prop.key === '$mat.shininess' ) {
333 |
334 | init_props.shininess = prop.value;
335 |
336 | }
337 |
338 | }
339 |
340 | // note: three.js does not like it when a texture is added after the geometry
341 | // has been rendered once, see http://stackoverflow.com/questions/16531759/.
342 | // for this reason we fill all slots upfront with default textures
343 | if ( has_textures.length ) {
344 |
345 | for ( i = has_textures.length - 1; i >= 0; -- i ) {
346 |
347 | init_props[ has_textures[ i ]] = defaultTexture();
348 |
349 | }
350 |
351 | }
352 |
353 | mat = new THREE.MeshPhongMaterial( init_props );
354 | return mat;
355 |
356 | },
357 |
358 | parseObject : function( json, node, meshes, materials ) {
359 |
360 | var obj = new THREE.Object3D()
361 | , i
362 | , idx
363 | ;
364 |
365 | obj.name = node.name || "";
366 | obj.matrix = new THREE.Matrix4().fromArray( node.transformation ).transpose();
367 | obj.matrix.decompose( obj.position, obj.quaternion, obj.scale );
368 |
369 | for ( i = 0; node.meshes && i < node.meshes.length; ++ i ) {
370 |
371 | idx = node.meshes[ i ];
372 | obj.add( new THREE.Mesh( meshes[ idx ], materials[ json.meshes[ idx ].materialindex ] ) );
373 |
374 | }
375 |
376 | for ( i = 0; node.children && i < node.children.length; ++ i ) {
377 |
378 | obj.add( this.parseObject( json, node.children[ i ], meshes, materials ) );
379 |
380 | }
381 |
382 | return obj;
383 |
384 | },
385 | };
386 |
--------------------------------------------------------------------------------
/js/loaders/RGBELoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Nikos M. / https://github.com/foo123/
3 | */
4 |
5 | // https://github.com/mrdoob/three.js/issues/5552
6 | // http://en.wikipedia.org/wiki/RGBE_image_format
7 |
8 | THREE.HDRLoader = THREE.RGBELoader = function ( manager ) {
9 |
10 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
11 |
12 | };
13 |
14 | // extend THREE.BinaryTextureLoader
15 | THREE.RGBELoader.prototype = Object.create( THREE.BinaryTextureLoader.prototype );
16 |
17 | // adapted from http://www.graphics.cornell.edu/~bjw/rgbe.html
18 | THREE.RGBELoader.prototype._parser = function( buffer ) {
19 |
20 | var
21 | /* return codes for rgbe routines */
22 | RGBE_RETURN_SUCCESS = 0,
23 | RGBE_RETURN_FAILURE = - 1,
24 |
25 | /* default error routine. change this to change error handling */
26 | rgbe_read_error = 1,
27 | rgbe_write_error = 2,
28 | rgbe_format_error = 3,
29 | rgbe_memory_error = 4,
30 | rgbe_error = function( rgbe_error_code, msg ) {
31 |
32 | switch ( rgbe_error_code ) {
33 | case rgbe_read_error: console.error( "THREE.RGBELoader Read Error: " + ( msg || '' ) );
34 | break;
35 | case rgbe_write_error: console.error( "THREE.RGBELoader Write Error: " + ( msg || '' ) );
36 | break;
37 | case rgbe_format_error: console.error( "THREE.RGBELoader Bad File Format: " + ( msg || '' ) );
38 | break;
39 | default:
40 | case rgbe_memory_error: console.error( "THREE.RGBELoader: Error: " + ( msg || '' ) );
41 | }
42 | return RGBE_RETURN_FAILURE;
43 |
44 | },
45 |
46 | /* offsets to red, green, and blue components in a data (float) pixel */
47 | RGBE_DATA_RED = 0,
48 | RGBE_DATA_GREEN = 1,
49 | RGBE_DATA_BLUE = 2,
50 |
51 | /* number of floats per pixel, use 4 since stored in rgba image format */
52 | RGBE_DATA_SIZE = 4,
53 |
54 | /* flags indicating which fields in an rgbe_header_info are valid */
55 | RGBE_VALID_PROGRAMTYPE = 1,
56 | RGBE_VALID_FORMAT = 2,
57 | RGBE_VALID_DIMENSIONS = 4,
58 |
59 | NEWLINE = "\n",
60 |
61 | fgets = function( buffer, lineLimit, consume ) {
62 |
63 | lineLimit = ! lineLimit ? 1024 : lineLimit;
64 | var p = buffer.pos,
65 | i = - 1, len = 0, s = '', chunkSize = 128,
66 | chunk = String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) )
67 | ;
68 | while ( ( 0 > ( i = chunk.indexOf( NEWLINE ) ) ) && ( len < lineLimit ) && ( p < buffer.byteLength ) ) {
69 |
70 | s += chunk; len += chunk.length;
71 | p += chunkSize;
72 | chunk += String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
73 |
74 | }
75 |
76 | if ( - 1 < i ) {
77 |
78 | /*for (i=l-1; i>=0; i--) {
79 | byteCode = m.charCodeAt(i);
80 | if (byteCode > 0x7f && byteCode <= 0x7ff) byteLen++;
81 | else if (byteCode > 0x7ff && byteCode <= 0xffff) byteLen += 2;
82 | if (byteCode >= 0xDC00 && byteCode <= 0xDFFF) i--; //trail surrogate
83 | }*/
84 | if ( false !== consume ) buffer.pos += len + i + 1;
85 | return s + chunk.slice( 0, i );
86 |
87 | }
88 | return false;
89 |
90 | },
91 |
92 | /* minimal header reading. modify if you want to parse more information */
93 | RGBE_ReadHeader = function( buffer ) {
94 |
95 | var line, match,
96 |
97 | // regexes to parse header info fields
98 | magic_token_re = /^#\?(\S+)$/,
99 | gamma_re = /^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/,
100 | exposure_re = /^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/,
101 | format_re = /^\s*FORMAT=(\S+)\s*$/,
102 | dimensions_re = /^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/,
103 |
104 | // RGBE format header struct
105 | header = {
106 |
107 | valid: 0, /* indicate which fields are valid */
108 |
109 | string: '', /* the actual header string */
110 |
111 | comments: '', /* comments found in header */
112 |
113 | programtype: 'RGBE', /* listed at beginning of file to identify it
114 | * after "#?". defaults to "RGBE" */
115 |
116 | format: '', /* RGBE format, default 32-bit_rle_rgbe */
117 |
118 | gamma: 1.0, /* image has already been gamma corrected with
119 | * given gamma. defaults to 1.0 (no correction) */
120 |
121 | exposure: 1.0, /* a value of 1.0 in an image corresponds to
122 | * watts/steradian/m^2.
123 | * defaults to 1.0 */
124 |
125 | width: 0, height: 0 /* image dimensions, width/height */
126 |
127 | }
128 | ;
129 |
130 | if ( buffer.pos >= buffer.byteLength || ! ( line = fgets( buffer ) ) ) {
131 |
132 | return rgbe_error( rgbe_read_error, "no header found" );
133 |
134 | }
135 | /* if you want to require the magic token then uncomment the next line */
136 | if ( ! ( match = line.match( magic_token_re ) ) ) {
137 |
138 | return rgbe_error( rgbe_format_error, "bad initial token" );
139 |
140 | }
141 | header.valid |= RGBE_VALID_PROGRAMTYPE;
142 | header.programtype = match[ 1 ];
143 | header.string += line + "\n";
144 |
145 | while ( true ) {
146 |
147 | line = fgets( buffer );
148 | if ( false === line ) break;
149 | header.string += line + "\n";
150 |
151 | if ( '#' === line.charAt( 0 ) ) {
152 |
153 | header.comments += line + "\n";
154 | continue; // comment line
155 |
156 | }
157 |
158 | if ( match = line.match( gamma_re ) ) {
159 |
160 | header.gamma = parseFloat( match[ 1 ], 10 );
161 |
162 | }
163 | if ( match = line.match( exposure_re ) ) {
164 |
165 | header.exposure = parseFloat( match[ 1 ], 10 );
166 |
167 | }
168 | if ( match = line.match( format_re ) ) {
169 |
170 | header.valid |= RGBE_VALID_FORMAT;
171 | header.format = match[ 1 ];//'32-bit_rle_rgbe';
172 |
173 | }
174 | if ( match = line.match( dimensions_re ) ) {
175 |
176 | header.valid |= RGBE_VALID_DIMENSIONS;
177 | header.height = parseInt( match[ 1 ], 10 );
178 | header.width = parseInt( match[ 2 ], 10 );
179 |
180 | }
181 |
182 | if ( ( header.valid & RGBE_VALID_FORMAT ) && ( header.valid & RGBE_VALID_DIMENSIONS ) ) break;
183 |
184 | }
185 |
186 | if ( ! ( header.valid & RGBE_VALID_FORMAT ) ) {
187 |
188 | return rgbe_error( rgbe_format_error, "missing format specifier" );
189 |
190 | }
191 | if ( ! ( header.valid & RGBE_VALID_DIMENSIONS ) ) {
192 |
193 | return rgbe_error( rgbe_format_error, "missing image size specifier" );
194 |
195 | }
196 |
197 | return header;
198 |
199 | },
200 |
201 | RGBE_ReadPixels_RLE = function( buffer, w, h ) {
202 |
203 | var data_rgba, offset, pos, count, byteValue,
204 | scanline_buffer, ptr, ptr_end, i, l, off, isEncodedRun,
205 | scanline_width = w, num_scanlines = h, rgbeStart
206 | ;
207 |
208 | if (
209 | // run length encoding is not allowed so read flat
210 | ( ( scanline_width < 8 ) || ( scanline_width > 0x7fff ) ) ||
211 | // this file is not run length encoded
212 | ( ( 2 !== buffer[ 0 ] ) || ( 2 !== buffer[ 1 ] ) || ( buffer[ 2 ] & 0x80 ) )
213 | ) {
214 |
215 | // return the flat buffer
216 | return new Uint8Array( buffer );
217 |
218 | }
219 |
220 | if ( scanline_width !== ( ( buffer[ 2 ] << 8 ) | buffer[ 3 ] ) ) {
221 |
222 | return rgbe_error( rgbe_format_error, "wrong scanline width" );
223 |
224 | }
225 |
226 | data_rgba = new Uint8Array( 4 * w * h );
227 |
228 | if ( ! data_rgba || ! data_rgba.length ) {
229 |
230 | return rgbe_error( rgbe_memory_error, "unable to allocate buffer space" );
231 |
232 | }
233 |
234 | offset = 0; pos = 0; ptr_end = 4 * scanline_width;
235 | rgbeStart = new Uint8Array( 4 );
236 | scanline_buffer = new Uint8Array( ptr_end );
237 |
238 | // read in each successive scanline
239 | while ( ( num_scanlines > 0 ) && ( pos < buffer.byteLength ) ) {
240 |
241 | if ( pos + 4 > buffer.byteLength ) {
242 |
243 | return rgbe_error( rgbe_read_error );
244 |
245 | }
246 |
247 | rgbeStart[ 0 ] = buffer[ pos ++ ];
248 | rgbeStart[ 1 ] = buffer[ pos ++ ];
249 | rgbeStart[ 2 ] = buffer[ pos ++ ];
250 | rgbeStart[ 3 ] = buffer[ pos ++ ];
251 |
252 | if ( ( 2 != rgbeStart[ 0 ] ) || ( 2 != rgbeStart[ 1 ] ) || ( ( ( rgbeStart[ 2 ] << 8 ) | rgbeStart[ 3 ] ) != scanline_width ) ) {
253 |
254 | return rgbe_error( rgbe_format_error, "bad rgbe scanline format" );
255 |
256 | }
257 |
258 | // read each of the four channels for the scanline into the buffer
259 | // first red, then green, then blue, then exponent
260 | ptr = 0;
261 | while ( ( ptr < ptr_end ) && ( pos < buffer.byteLength ) ) {
262 |
263 | count = buffer[ pos ++ ];
264 | isEncodedRun = count > 128;
265 | if ( isEncodedRun ) count -= 128;
266 |
267 | if ( ( 0 === count ) || ( ptr + count > ptr_end ) ) {
268 |
269 | return rgbe_error( rgbe_format_error, "bad scanline data" );
270 |
271 | }
272 |
273 | if ( isEncodedRun ) {
274 |
275 | // a (encoded) run of the same value
276 | byteValue = buffer[ pos ++ ];
277 | for ( i = 0; i < count; i ++ ) {
278 |
279 | scanline_buffer[ ptr ++ ] = byteValue;
280 |
281 | }
282 | //ptr += count;
283 |
284 | } else {
285 |
286 | // a literal-run
287 | scanline_buffer.set( buffer.subarray( pos, pos + count ), ptr );
288 | ptr += count; pos += count;
289 |
290 | }
291 |
292 | }
293 |
294 |
295 | // now convert data from buffer into rgba
296 | // first red, then green, then blue, then exponent (alpha)
297 | l = scanline_width; //scanline_buffer.byteLength;
298 | for ( i = 0; i < l; i ++ ) {
299 |
300 | off = 0;
301 | data_rgba[ offset ] = scanline_buffer[ i + off ];
302 | off += scanline_width; //1;
303 | data_rgba[ offset + 1 ] = scanline_buffer[ i + off ];
304 | off += scanline_width; //1;
305 | data_rgba[ offset + 2 ] = scanline_buffer[ i + off ];
306 | off += scanline_width; //1;
307 | data_rgba[ offset + 3 ] = scanline_buffer[ i + off ];
308 | offset += 4;
309 |
310 | }
311 |
312 | num_scanlines --;
313 |
314 | }
315 |
316 | return data_rgba;
317 |
318 | }
319 | ;
320 |
321 | var byteArray = new Uint8Array( buffer ),
322 | byteLength = byteArray.byteLength;
323 | byteArray.pos = 0;
324 | var rgbe_header_info = RGBE_ReadHeader( byteArray );
325 |
326 | if ( RGBE_RETURN_FAILURE !== rgbe_header_info ) {
327 |
328 | var w = rgbe_header_info.width,
329 | h = rgbe_header_info.height
330 | , image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h )
331 | ;
332 | if ( RGBE_RETURN_FAILURE !== image_rgba_data ) {
333 |
334 | return {
335 | width: w, height: h,
336 | data: image_rgba_data,
337 | header: rgbe_header_info.string,
338 | gamma: rgbe_header_info.gamma,
339 | exposure: rgbe_header_info.exposure,
340 | format: THREE.RGBEFormat, // handled as THREE.RGBAFormat in shaders
341 | type: THREE.UnsignedByteType
342 | };
343 |
344 | }
345 |
346 | }
347 | return null;
348 |
349 | };
350 |
--------------------------------------------------------------------------------
/js/loaders/collada/Animation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mikael emtinger / http://gomo.se/
3 | * @author mrdoob / http://mrdoob.com/
4 | * @author alteredq / http://alteredqualia.com/
5 | */
6 |
7 | THREE.Animation = function ( root, data ) {
8 |
9 | this.root = root;
10 | this.data = THREE.AnimationHandler.init( data );
11 | this.hierarchy = THREE.AnimationHandler.parse( root );
12 |
13 | this.currentTime = 0;
14 | this.timeScale = 1;
15 |
16 | this.isPlaying = false;
17 | this.loop = true;
18 | this.weight = 0;
19 |
20 | this.interpolationType = THREE.AnimationHandler.LINEAR;
21 |
22 | };
23 |
24 | THREE.Animation.prototype = {
25 |
26 | constructor: THREE.Animation,
27 |
28 | keyTypes: [ "pos", "rot", "scl" ],
29 |
30 | play: function ( startTime, weight ) {
31 |
32 | this.currentTime = startTime !== undefined ? startTime : 0;
33 | this.weight = weight !== undefined ? weight : 1;
34 |
35 | this.isPlaying = true;
36 |
37 | this.reset();
38 |
39 | THREE.AnimationHandler.play( this );
40 |
41 | },
42 |
43 | stop: function() {
44 |
45 | this.isPlaying = false;
46 |
47 | THREE.AnimationHandler.stop( this );
48 |
49 | },
50 |
51 | reset: function () {
52 |
53 | for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
54 |
55 | var object = this.hierarchy[ h ];
56 |
57 | if ( object.animationCache === undefined ) {
58 |
59 | object.animationCache = {
60 | animations: {},
61 | blending: {
62 | positionWeight: 0.0,
63 | quaternionWeight: 0.0,
64 | scaleWeight: 0.0
65 | }
66 | };
67 |
68 | }
69 |
70 | var name = this.data.name;
71 | var animations = object.animationCache.animations;
72 | var animationCache = animations[ name ];
73 |
74 | if ( animationCache === undefined ) {
75 |
76 | animationCache = {
77 | prevKey: { pos: 0, rot: 0, scl: 0 },
78 | nextKey: { pos: 0, rot: 0, scl: 0 },
79 | originalMatrix: object.matrix
80 | };
81 |
82 | animations[ name ] = animationCache;
83 |
84 | }
85 |
86 | // Get keys to match our current time
87 |
88 | for ( var t = 0; t < 3; t ++ ) {
89 |
90 | var type = this.keyTypes[ t ];
91 |
92 | var prevKey = this.data.hierarchy[ h ].keys[ 0 ];
93 | var nextKey = this.getNextKeyWith( type, h, 1 );
94 |
95 | while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
96 |
97 | prevKey = nextKey;
98 | nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
99 |
100 | }
101 |
102 | animationCache.prevKey[ type ] = prevKey;
103 | animationCache.nextKey[ type ] = nextKey;
104 |
105 | }
106 |
107 | }
108 |
109 | },
110 |
111 | resetBlendWeights: function () {
112 |
113 | for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
114 |
115 | var object = this.hierarchy[ h ];
116 | var animationCache = object.animationCache;
117 |
118 | if ( animationCache !== undefined ) {
119 |
120 | var blending = animationCache.blending;
121 |
122 | blending.positionWeight = 0.0;
123 | blending.quaternionWeight = 0.0;
124 | blending.scaleWeight = 0.0;
125 |
126 | }
127 |
128 | }
129 |
130 | },
131 |
132 | update: ( function() {
133 |
134 | var points = [];
135 | var target = new THREE.Vector3();
136 | var newVector = new THREE.Vector3();
137 | var newQuat = new THREE.Quaternion();
138 |
139 | // Catmull-Rom spline
140 |
141 | var interpolateCatmullRom = function ( points, scale ) {
142 |
143 | var c = [], v3 = [],
144 | point, intPoint, weight, w2, w3,
145 | pa, pb, pc, pd;
146 |
147 | point = ( points.length - 1 ) * scale;
148 | intPoint = Math.floor( point );
149 | weight = point - intPoint;
150 |
151 | c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1;
152 | c[ 1 ] = intPoint;
153 | c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1;
154 | c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2;
155 |
156 | pa = points[ c[ 0 ] ];
157 | pb = points[ c[ 1 ] ];
158 | pc = points[ c[ 2 ] ];
159 | pd = points[ c[ 3 ] ];
160 |
161 | w2 = weight * weight;
162 | w3 = weight * w2;
163 |
164 | v3[ 0 ] = interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 );
165 | v3[ 1 ] = interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 );
166 | v3[ 2 ] = interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 );
167 |
168 | return v3;
169 |
170 | };
171 |
172 | var interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) {
173 |
174 | var v0 = ( p2 - p0 ) * 0.5,
175 | v1 = ( p3 - p1 ) * 0.5;
176 |
177 | return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1;
178 |
179 | };
180 |
181 | return function ( delta ) {
182 |
183 | if ( this.isPlaying === false ) return;
184 |
185 | this.currentTime += delta * this.timeScale;
186 |
187 | if ( this.weight === 0 )
188 | return;
189 |
190 | //
191 |
192 | var duration = this.data.length;
193 |
194 | if ( this.currentTime > duration || this.currentTime < 0 ) {
195 |
196 | if ( this.loop ) {
197 |
198 | this.currentTime %= duration;
199 |
200 | if ( this.currentTime < 0 )
201 | this.currentTime += duration;
202 |
203 | this.reset();
204 |
205 | } else {
206 |
207 | this.stop();
208 |
209 | }
210 |
211 | }
212 |
213 | for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) {
214 |
215 | var object = this.hierarchy[ h ];
216 | var animationCache = object.animationCache.animations[ this.data.name ];
217 | var blending = object.animationCache.blending;
218 |
219 | // loop through pos/rot/scl
220 |
221 | for ( var t = 0; t < 3; t ++ ) {
222 |
223 | // get keys
224 |
225 | var type = this.keyTypes[ t ];
226 | var prevKey = animationCache.prevKey[ type ];
227 | var nextKey = animationCache.nextKey[ type ];
228 |
229 | if ( ( this.timeScale > 0 && nextKey.time <= this.currentTime ) ||
230 | ( this.timeScale < 0 && prevKey.time >= this.currentTime ) ) {
231 |
232 | prevKey = this.data.hierarchy[ h ].keys[ 0 ];
233 | nextKey = this.getNextKeyWith( type, h, 1 );
234 |
235 | while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) {
236 |
237 | prevKey = nextKey;
238 | nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 );
239 |
240 | }
241 |
242 | animationCache.prevKey[ type ] = prevKey;
243 | animationCache.nextKey[ type ] = nextKey;
244 |
245 | }
246 |
247 | var scale = ( this.currentTime - prevKey.time ) / ( nextKey.time - prevKey.time );
248 |
249 | var prevXYZ = prevKey[ type ];
250 | var nextXYZ = nextKey[ type ];
251 |
252 | if ( scale < 0 ) scale = 0;
253 | if ( scale > 1 ) scale = 1;
254 |
255 | // interpolate
256 |
257 | if ( type === "pos" ) {
258 |
259 | if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) {
260 |
261 | newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
262 | newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
263 | newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
264 |
265 | // blend
266 | var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
267 | object.position.lerp( newVector, proportionalWeight );
268 | blending.positionWeight += this.weight;
269 |
270 | } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
271 | this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
272 |
273 | points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ];
274 | points[ 1 ] = prevXYZ;
275 | points[ 2 ] = nextXYZ;
276 | points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ];
277 |
278 | scale = scale * 0.33 + 0.33;
279 |
280 | var currentPoint = interpolateCatmullRom( points, scale );
281 | var proportionalWeight = this.weight / ( this.weight + blending.positionWeight );
282 | blending.positionWeight += this.weight;
283 |
284 | // blend
285 |
286 | var vector = object.position;
287 |
288 | vector.x = vector.x + ( currentPoint[ 0 ] - vector.x ) * proportionalWeight;
289 | vector.y = vector.y + ( currentPoint[ 1 ] - vector.y ) * proportionalWeight;
290 | vector.z = vector.z + ( currentPoint[ 2 ] - vector.z ) * proportionalWeight;
291 |
292 | if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
293 |
294 | var forwardPoint = interpolateCatmullRom( points, scale * 1.01 );
295 |
296 | target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] );
297 | target.sub( vector );
298 | target.y = 0;
299 | target.normalize();
300 |
301 | var angle = Math.atan2( target.x, target.z );
302 | object.rotation.set( 0, angle, 0 );
303 |
304 | }
305 |
306 | }
307 |
308 | } else if ( type === "rot" ) {
309 |
310 | THREE.Quaternion.slerp( prevXYZ, nextXYZ, newQuat, scale );
311 |
312 | // Avoid paying the cost of an additional slerp if we don't have to
313 | if ( blending.quaternionWeight === 0 ) {
314 |
315 | object.quaternion.copy( newQuat );
316 | blending.quaternionWeight = this.weight;
317 |
318 | } else {
319 |
320 | var proportionalWeight = this.weight / ( this.weight + blending.quaternionWeight );
321 | THREE.Quaternion.slerp( object.quaternion, newQuat, object.quaternion, proportionalWeight );
322 | blending.quaternionWeight += this.weight;
323 |
324 | }
325 |
326 | } else if ( type === "scl" ) {
327 |
328 | newVector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale;
329 | newVector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale;
330 | newVector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale;
331 |
332 | var proportionalWeight = this.weight / ( this.weight + blending.scaleWeight );
333 | object.scale.lerp( newVector, proportionalWeight );
334 | blending.scaleWeight += this.weight;
335 |
336 | }
337 |
338 | }
339 |
340 | }
341 |
342 | return true;
343 |
344 | };
345 |
346 | } )(),
347 |
348 | getNextKeyWith: function ( type, h, key ) {
349 |
350 | var keys = this.data.hierarchy[ h ].keys;
351 |
352 | if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
353 | this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
354 |
355 | key = key < keys.length - 1 ? key : keys.length - 1;
356 |
357 | } else {
358 |
359 | key = key % keys.length;
360 |
361 | }
362 |
363 | for ( ; key < keys.length; key ++ ) {
364 |
365 | if ( keys[ key ][ type ] !== undefined ) {
366 |
367 | return keys[ key ];
368 |
369 | }
370 |
371 | }
372 |
373 | return this.data.hierarchy[ h ].keys[ 0 ];
374 |
375 | },
376 |
377 | getPrevKeyWith: function ( type, h, key ) {
378 |
379 | var keys = this.data.hierarchy[ h ].keys;
380 |
381 | if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM ||
382 | this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) {
383 |
384 | key = key > 0 ? key : 0;
385 |
386 | } else {
387 |
388 | key = key >= 0 ? key : key + keys.length;
389 |
390 | }
391 |
392 |
393 | for ( ; key >= 0; key -- ) {
394 |
395 | if ( keys[ key ][ type ] !== undefined ) {
396 |
397 | return keys[ key ];
398 |
399 | }
400 |
401 | }
402 |
403 | return this.data.hierarchy[ h ].keys[ keys.length - 1 ];
404 |
405 | }
406 |
407 | };
408 |
--------------------------------------------------------------------------------