├── favicon.ico ├── screenshot.jpg ├── index.html ├── js └── vendor │ ├── threejs │ ├── texture-loader.js │ ├── obj-loader.js │ └── collada-loader.js │ └── codemirror │ ├── active-line.js │ ├── matchbrackets.js │ ├── closebrackets.js │ └── clojure.js ├── css ├── vendor │ ├── neo.css │ └── codemirror.css └── main.css └── README.md /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman01la/threejs-cljs-playground/HEAD/favicon.ico -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roman01la/threejs-cljs-playground/HEAD/screenshot.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | three.js playground in ClojureScript 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /js/vendor/threejs/texture-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.TextureLoader = function ( manager ) { 6 | 7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 8 | 9 | }; 10 | 11 | THREE.TextureLoader.prototype = { 12 | 13 | constructor: THREE.TextureLoader, 14 | 15 | load: function ( url, onLoad, onProgress, onError ) { 16 | 17 | var scope = this; 18 | 19 | var loader = new THREE.ImageLoader( scope.manager ); 20 | loader.setCrossOrigin( this.crossOrigin ); 21 | loader.load( url, function ( image ) { 22 | 23 | var texture = new THREE.Texture( image ); 24 | texture.needsUpdate = true; 25 | 26 | if ( onLoad !== undefined ) { 27 | 28 | onLoad( texture ); 29 | 30 | } 31 | 32 | }, onProgress, onError ); 33 | 34 | }, 35 | 36 | setCrossOrigin: function ( value ) { 37 | 38 | this.crossOrigin = value; 39 | 40 | } 41 | 42 | }; 43 | -------------------------------------------------------------------------------- /css/vendor/neo.css: -------------------------------------------------------------------------------- 1 | /* neo theme for codemirror */ 2 | 3 | /* Color scheme */ 4 | 5 | .cm-s-neo.CodeMirror { 6 | background-color:#ffffff; 7 | color:#2e383c; 8 | line-height:1.4375; 9 | } 10 | .cm-s-neo .cm-comment {color:#75787b} 11 | .cm-s-neo .cm-keyword, .cm-s-neo .cm-property {color:#1d75b3} 12 | .cm-s-neo .cm-atom,.cm-s-neo .cm-number {color:#75438a} 13 | .cm-s-neo .cm-node,.cm-s-neo .cm-tag {color:#9c3328} 14 | .cm-s-neo .cm-string {color:#b35e14} 15 | .cm-s-neo .cm-variable,.cm-s-neo .cm-qualifier {color:#047d65} 16 | 17 | 18 | /* Editor styling */ 19 | 20 | .cm-s-neo pre { 21 | padding:0; 22 | } 23 | 24 | .cm-s-neo .CodeMirror-gutters { 25 | border:none; 26 | border-right:10px solid transparent; 27 | background-color:transparent; 28 | } 29 | 30 | .cm-s-neo .CodeMirror-linenumber { 31 | padding:0; 32 | color:#e0e2e5; 33 | } 34 | 35 | .cm-s-neo .CodeMirror-guttermarker { color: #1d75b3; } 36 | .cm-s-neo .CodeMirror-guttermarker-subtle { color: #e0e2e5; } 37 | 38 | .cm-s-neo div.CodeMirror-cursor { 39 | width: auto; 40 | border: 0; 41 | background: rgba(155,157,162,0.37); 42 | z-index: 1; 43 | } 44 | -------------------------------------------------------------------------------- /css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | overflow: hidden; 4 | } 5 | 6 | .editor { 7 | width: 50vw; 8 | position: relative; 9 | } 10 | 11 | .editor .CodeMirror { 12 | height: calc(100vh - 34px); 13 | } 14 | 15 | .viewport { 16 | width: 50vw; 17 | height: 100vh; 18 | position: absolute; 19 | top: 0; 20 | right: 0; 21 | } 22 | 23 | .bar { 24 | position: absolute; 25 | left: 0; 26 | font-family: sans-serif; 27 | text-align: right; 28 | background: #fff; 29 | z-index: 6; 30 | width: 50vw; 31 | border-top: 1px dashed #ccc; 32 | } 33 | 34 | .toolbar { 35 | bottom: 0; 36 | height: 34px; 37 | } 38 | 39 | .load-bar { 40 | bottom: 34px; 41 | } 42 | 43 | .list { 44 | margin: 0; 45 | padding: 0; 46 | list-style: none; 47 | } 48 | 49 | .list.left { 50 | float: left; 51 | } 52 | 53 | .list-item { 54 | display: inline-block; 55 | } 56 | 57 | .btn { 58 | border-radius: 0; 59 | border: none; 60 | background-color: #fff; 61 | padding: 10px 12px; 62 | font-weight: bold; 63 | cursor: pointer; 64 | } 65 | 66 | .btn:hover { 67 | background: #ccc; 68 | } 69 | 70 | label { 71 | font: normal normal normal normal 11px/normal '.HelveticaNeueDeskInterface-Regular'; 72 | } 73 | 74 | .gh-link { 75 | position: absolute; 76 | bottom: 0; 77 | right: 0; 78 | margin: 10px; 79 | } 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # three.js playground in ClojureScript 2 | 3 | ![demo](screenshot.jpg) 4 | 5 | The playground is running on bootstrapped ClojureScript and stores shared demos on Firebase. 6 | 7 | Write some code, upload textures, OBJ/Collada models (more formats is coming soon) and hit `Alt-Enter` to evaluate and see results immediately. 8 | 9 | After every evaluation the namespace is populated with the following vars: 10 | 11 | - `THREE` — three.js namespace 12 | - `VIEWPORT` — reference to canvas DOM element 13 | - `WIDTH` & `HEIGHT` — viewport dimensions in `px` 14 | - `MODELS` — JS array with your compiled models 15 | - `MODELS_DATA` — JS array with parsed models data to be compiled and stored into `MODELS` 16 | - `TEXTURES` — JS array with compiled textures 17 | - `TEXTURES_DATA` — JS array with images to be compiled and stored into `TEXTURES` 18 | - `RENDERER` & `CAMERA` — assign your renderer and camera instances to these vars to let me handle window resizing for you... 19 | 20 | ```clojurescript 21 | (set! RENDERER renderer) 22 | (set! CAMERA camera) 23 | ``` 24 | 25 | - `RAF` — ID of the current `requestAnimationFrame` call, this is the important one... 26 | 27 | Make sure you are always assigning returning value of `requestAnimationFrame` call to global `RAF` var, like this: 28 | 29 | ```clojurescript 30 | (set! RAF (js/requestAnimationFrame render)) 31 | ``` 32 | 33 | This is required to drop current rendering loop before running a new one after each evaluation. 34 | -------------------------------------------------------------------------------- /js/vendor/codemirror/active-line.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | // Because sometimes you need to style the cursor's line. 5 | // 6 | // Adds an option 'styleActiveLine' which, when enabled, gives the 7 | // active line's wrapping
the CSS class "CodeMirror-activeline", 8 | // and gives its background
the class "CodeMirror-activeline-background". 9 | 10 | (function(mod) { 11 | if (typeof exports == "object" && typeof module == "object") // CommonJS 12 | mod(require("../../lib/codemirror")); 13 | else if (typeof define == "function" && define.amd) // AMD 14 | define(["../../lib/codemirror"], mod); 15 | else // Plain browser env 16 | mod(CodeMirror); 17 | })(function(CodeMirror) { 18 | "use strict"; 19 | var WRAP_CLASS = "CodeMirror-activeline"; 20 | var BACK_CLASS = "CodeMirror-activeline-background"; 21 | 22 | CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { 23 | var prev = old && old != CodeMirror.Init; 24 | if (val && !prev) { 25 | cm.state.activeLines = []; 26 | updateActiveLines(cm, cm.listSelections()); 27 | cm.on("beforeSelectionChange", selectionChange); 28 | } else if (!val && prev) { 29 | cm.off("beforeSelectionChange", selectionChange); 30 | clearActiveLines(cm); 31 | delete cm.state.activeLines; 32 | } 33 | }); 34 | 35 | function clearActiveLines(cm) { 36 | for (var i = 0; i < cm.state.activeLines.length; i++) { 37 | cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); 38 | cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); 39 | } 40 | } 41 | 42 | function sameArray(a, b) { 43 | if (a.length != b.length) return false; 44 | for (var i = 0; i < a.length; i++) 45 | if (a[i] != b[i]) return false; 46 | return true; 47 | } 48 | 49 | function updateActiveLines(cm, ranges) { 50 | var active = []; 51 | for (var i = 0; i < ranges.length; i++) { 52 | var range = ranges[i]; 53 | if (!range.empty()) continue; 54 | var line = cm.getLineHandleVisualStart(range.head.line); 55 | if (active[active.length - 1] != line) active.push(line); 56 | } 57 | if (sameArray(cm.state.activeLines, active)) return; 58 | cm.operation(function() { 59 | clearActiveLines(cm); 60 | for (var i = 0; i < active.length; i++) { 61 | cm.addLineClass(active[i], "wrap", WRAP_CLASS); 62 | cm.addLineClass(active[i], "background", BACK_CLASS); 63 | } 64 | cm.state.activeLines = active; 65 | }); 66 | } 67 | 68 | function selectionChange(cm, sel) { 69 | updateActiveLines(cm, sel.ranges); 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /js/vendor/codemirror/matchbrackets.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && 13 | (document.documentMode == null || document.documentMode < 8); 14 | 15 | var Pos = CodeMirror.Pos; 16 | 17 | var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; 18 | 19 | function findMatchingBracket(cm, where, strict, config) { 20 | var line = cm.getLineHandle(where.line), pos = where.ch - 1; 21 | var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; 22 | if (!match) return null; 23 | var dir = match.charAt(1) == ">" ? 1 : -1; 24 | if (strict && (dir > 0) != (pos == where.ch)) return null; 25 | var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); 26 | 27 | var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); 28 | if (found == null) return null; 29 | return {from: Pos(where.line, pos), to: found && found.pos, 30 | match: found && found.ch == match.charAt(0), forward: dir > 0}; 31 | } 32 | 33 | // bracketRegex is used to specify which type of bracket to scan 34 | // should be a regexp, e.g. /[[\]]/ 35 | // 36 | // Note: If "where" is on an open bracket, then this bracket is ignored. 37 | // 38 | // Returns false when no bracket was found, null when it reached 39 | // maxScanLines and gave up 40 | function scanForBracket(cm, where, dir, style, config) { 41 | var maxScanLen = (config && config.maxScanLineLength) || 10000; 42 | var maxScanLines = (config && config.maxScanLines) || 1000; 43 | 44 | var stack = []; 45 | var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; 46 | var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) 47 | : Math.max(cm.firstLine() - 1, where.line - maxScanLines); 48 | for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { 49 | var line = cm.getLine(lineNo); 50 | if (!line) continue; 51 | var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; 52 | if (line.length > maxScanLen) continue; 53 | if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); 54 | for (; pos != end; pos += dir) { 55 | var ch = line.charAt(pos); 56 | if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { 57 | var match = matching[ch]; 58 | if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch); 59 | else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; 60 | else stack.pop(); 61 | } 62 | } 63 | } 64 | return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; 65 | } 66 | 67 | function matchBrackets(cm, autoclear, config) { 68 | // Disable brace matching in long lines, since it'll cause hugely slow updates 69 | var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; 70 | var marks = [], ranges = cm.listSelections(); 71 | for (var i = 0; i < ranges.length; i++) { 72 | var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config); 73 | if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { 74 | var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; 75 | marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); 76 | if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) 77 | marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); 78 | } 79 | } 80 | 81 | if (marks.length) { 82 | // Kludge to work around the IE bug from issue #1193, where text 83 | // input stops going to the textare whever this fires. 84 | if (ie_lt8 && cm.state.focused) cm.focus(); 85 | 86 | var clear = function() { 87 | cm.operation(function() { 88 | for (var i = 0; i < marks.length; i++) marks[i].clear(); 89 | }); 90 | }; 91 | if (autoclear) setTimeout(clear, 800); 92 | else return clear; 93 | } 94 | } 95 | 96 | var currentlyHighlighted = null; 97 | function doMatchBrackets(cm) { 98 | cm.operation(function() { 99 | if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} 100 | currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); 101 | }); 102 | } 103 | 104 | CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { 105 | if (old && old != CodeMirror.Init) 106 | cm.off("cursorActivity", doMatchBrackets); 107 | if (val) { 108 | cm.state.matchBrackets = typeof val == "object" ? val : {}; 109 | cm.on("cursorActivity", doMatchBrackets); 110 | } 111 | }); 112 | 113 | CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); 114 | CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){ 115 | return findMatchingBracket(this, pos, strict, config); 116 | }); 117 | CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ 118 | return scanForBracket(this, pos, dir, style, config); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /js/vendor/codemirror/closebrackets.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | (function(mod) { 5 | if (typeof exports == "object" && typeof module == "object") // CommonJS 6 | mod(require("../../lib/codemirror")); 7 | else if (typeof define == "function" && define.amd) // AMD 8 | define(["../../lib/codemirror"], mod); 9 | else // Plain browser env 10 | mod(CodeMirror); 11 | })(function(CodeMirror) { 12 | var defaults = { 13 | pairs: "()[]{}''\"\"", 14 | triples: "", 15 | explode: "[]{}" 16 | }; 17 | 18 | var Pos = CodeMirror.Pos; 19 | 20 | CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { 21 | if (old && old != CodeMirror.Init) { 22 | cm.removeKeyMap(keyMap); 23 | cm.state.closeBrackets = null; 24 | } 25 | if (val) { 26 | cm.state.closeBrackets = val; 27 | cm.addKeyMap(keyMap); 28 | } 29 | }); 30 | 31 | function getOption(conf, name) { 32 | if (name == "pairs" && typeof conf == "string") return conf; 33 | if (typeof conf == "object" && conf[name] != null) return conf[name]; 34 | return defaults[name]; 35 | } 36 | 37 | var bind = defaults.pairs + "`"; 38 | var keyMap = {Backspace: handleBackspace, Enter: handleEnter}; 39 | for (var i = 0; i < bind.length; i++) 40 | keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i)); 41 | 42 | function handler(ch) { 43 | return function(cm) { return handleChar(cm, ch); }; 44 | } 45 | 46 | function getConfig(cm) { 47 | var deflt = cm.state.closeBrackets; 48 | if (!deflt) return null; 49 | var mode = cm.getModeAt(cm.getCursor()); 50 | return mode.closeBrackets || deflt; 51 | } 52 | 53 | function handleBackspace(cm) { 54 | var conf = getConfig(cm); 55 | if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass; 56 | 57 | var pairs = getOption(conf, "pairs"); 58 | var ranges = cm.listSelections(); 59 | for (var i = 0; i < ranges.length; i++) { 60 | if (!ranges[i].empty()) return CodeMirror.Pass; 61 | var around = charsAround(cm, ranges[i].head); 62 | if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; 63 | } 64 | for (var i = ranges.length - 1; i >= 0; i--) { 65 | var cur = ranges[i].head; 66 | cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); 67 | } 68 | } 69 | 70 | function handleEnter(cm) { 71 | var conf = getConfig(cm); 72 | var explode = conf && getOption(conf, "explode"); 73 | if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass; 74 | 75 | var ranges = cm.listSelections(); 76 | for (var i = 0; i < ranges.length; i++) { 77 | if (!ranges[i].empty()) return CodeMirror.Pass; 78 | var around = charsAround(cm, ranges[i].head); 79 | if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass; 80 | } 81 | cm.operation(function() { 82 | cm.replaceSelection("\n\n", null); 83 | cm.execCommand("goCharLeft"); 84 | ranges = cm.listSelections(); 85 | for (var i = 0; i < ranges.length; i++) { 86 | var line = ranges[i].head.line; 87 | cm.indentLine(line, null, true); 88 | cm.indentLine(line + 1, null, true); 89 | } 90 | }); 91 | } 92 | 93 | function handleChar(cm, ch) { 94 | var conf = getConfig(cm); 95 | if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass; 96 | 97 | var pairs = getOption(conf, "pairs"); 98 | var pos = pairs.indexOf(ch); 99 | if (pos == -1) return CodeMirror.Pass; 100 | var triples = getOption(conf, "triples"); 101 | 102 | var identical = pairs.charAt(pos + 1) == ch; 103 | var ranges = cm.listSelections(); 104 | var opening = pos % 2 == 0; 105 | 106 | var type, next; 107 | for (var i = 0; i < ranges.length; i++) { 108 | var range = ranges[i], cur = range.head, curType; 109 | var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1)); 110 | if (opening && !range.empty()) { 111 | curType = "surround"; 112 | } else if ((identical || !opening) && next == ch) { 113 | if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch) 114 | curType = "skipThree"; 115 | else 116 | curType = "skip"; 117 | } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 && 118 | cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch && 119 | (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) { 120 | curType = "addFour"; 121 | } else if (identical) { 122 | if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both"; 123 | else return CodeMirror.Pass; 124 | } else if (opening && (cm.getLine(cur.line).length == cur.ch || 125 | isClosingBracket(next, pairs) || 126 | /\s/.test(next))) { 127 | curType = "both"; 128 | } else { 129 | return CodeMirror.Pass; 130 | } 131 | if (!type) type = curType; 132 | else if (type != curType) return CodeMirror.Pass; 133 | } 134 | 135 | var left = pos % 2 ? pairs.charAt(pos - 1) : ch; 136 | var right = pos % 2 ? ch : pairs.charAt(pos + 1); 137 | cm.operation(function() { 138 | if (type == "skip") { 139 | cm.execCommand("goCharRight"); 140 | } else if (type == "skipThree") { 141 | for (var i = 0; i < 3; i++) 142 | cm.execCommand("goCharRight"); 143 | } else if (type == "surround") { 144 | var sels = cm.getSelections(); 145 | for (var i = 0; i < sels.length; i++) 146 | sels[i] = left + sels[i] + right; 147 | cm.replaceSelections(sels, "around"); 148 | } else if (type == "both") { 149 | cm.replaceSelection(left + right, null); 150 | cm.triggerElectric(left + right); 151 | cm.execCommand("goCharLeft"); 152 | } else if (type == "addFour") { 153 | cm.replaceSelection(left + left + left + left, "before"); 154 | cm.execCommand("goCharRight"); 155 | } 156 | }); 157 | } 158 | 159 | function isClosingBracket(ch, pairs) { 160 | var pos = pairs.lastIndexOf(ch); 161 | return pos > -1 && pos % 2 == 1; 162 | } 163 | 164 | function charsAround(cm, pos) { 165 | var str = cm.getRange(Pos(pos.line, pos.ch - 1), 166 | Pos(pos.line, pos.ch + 1)); 167 | return str.length == 2 ? str : null; 168 | } 169 | 170 | // Project the token type that will exists after the given char is 171 | // typed, and use it to determine whether it would cause the start 172 | // of a string token. 173 | function enteringString(cm, pos, ch) { 174 | var line = cm.getLine(pos.line); 175 | var token = cm.getTokenAt(pos); 176 | if (/\bstring2?\b/.test(token.type)) return false; 177 | var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4); 178 | stream.pos = stream.start = token.start; 179 | for (;;) { 180 | var type1 = cm.getMode().token(stream, token.state); 181 | if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1); 182 | stream.start = stream.pos; 183 | } 184 | } 185 | }); 186 | -------------------------------------------------------------------------------- /css/vendor/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | } 9 | 10 | /* PADDING */ 11 | 12 | .CodeMirror-lines { 13 | padding: 4px 0; /* Vertical padding around content */ 14 | } 15 | .CodeMirror pre { 16 | padding: 0 4px; /* Horizontal padding of content */ 17 | } 18 | 19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 20 | background-color: white; /* The little square between H and V scrollbars */ 21 | } 22 | 23 | /* GUTTER */ 24 | 25 | .CodeMirror-gutters { 26 | border-right: 1px solid #ddd; 27 | background-color: #f7f7f7; 28 | white-space: nowrap; 29 | } 30 | .CodeMirror-linenumbers {} 31 | .CodeMirror-linenumber { 32 | padding: 0 3px 0 5px; 33 | min-width: 20px; 34 | text-align: right; 35 | color: #999; 36 | white-space: nowrap; 37 | } 38 | 39 | .CodeMirror-guttermarker { color: black; } 40 | .CodeMirror-guttermarker-subtle { color: #999; } 41 | 42 | /* CURSOR */ 43 | 44 | .CodeMirror div.CodeMirror-cursor { 45 | border-left: 1px solid black; 46 | } 47 | /* Shown when moving in bi-directional text */ 48 | .CodeMirror div.CodeMirror-secondarycursor { 49 | border-left: 1px solid silver; 50 | } 51 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursor { 52 | width: auto; 53 | border: 0; 54 | background: #7e7; 55 | } 56 | .CodeMirror.cm-fat-cursor div.CodeMirror-cursors { 57 | z-index: 1; 58 | } 59 | 60 | .cm-animate-fat-cursor { 61 | width: auto; 62 | border: 0; 63 | -webkit-animation: blink 1.06s steps(1) infinite; 64 | -moz-animation: blink 1.06s steps(1) infinite; 65 | animation: blink 1.06s steps(1) infinite; 66 | background-color: #7e7; 67 | } 68 | @-moz-keyframes blink { 69 | 0% {} 70 | 50% { background-color: transparent; } 71 | 100% {} 72 | } 73 | @-webkit-keyframes blink { 74 | 0% {} 75 | 50% { background-color: transparent; } 76 | 100% {} 77 | } 78 | @keyframes blink { 79 | 0% {} 80 | 50% { background-color: transparent; } 81 | 100% {} 82 | } 83 | 84 | /* Can style cursor different in overwrite (non-insert) mode */ 85 | div.CodeMirror-overwrite div.CodeMirror-cursor {} 86 | 87 | .cm-tab { display: inline-block; text-decoration: inherit; } 88 | 89 | .CodeMirror-ruler { 90 | border-left: 1px solid #ccc; 91 | position: absolute; 92 | } 93 | 94 | /* DEFAULT THEME */ 95 | 96 | .cm-s-default .cm-header {color: blue;} 97 | .cm-s-default .cm-quote {color: #090;} 98 | .cm-negative {color: #d44;} 99 | .cm-positive {color: #292;} 100 | .cm-header, .cm-strong {font-weight: bold;} 101 | .cm-em {font-style: italic;} 102 | .cm-link {text-decoration: underline;} 103 | .cm-strikethrough {text-decoration: line-through;} 104 | 105 | .cm-s-default .cm-keyword {color: #708;} 106 | .cm-s-default .cm-atom {color: #219;} 107 | .cm-s-default .cm-number {color: #164;} 108 | .cm-s-default .cm-def {color: #00f;} 109 | .cm-s-default .cm-variable, 110 | .cm-s-default .cm-punctuation, 111 | .cm-s-default .cm-property, 112 | .cm-s-default .cm-operator {} 113 | .cm-s-default .cm-variable-2 {color: #05a;} 114 | .cm-s-default .cm-variable-3 {color: #085;} 115 | .cm-s-default .cm-comment {color: #a50;} 116 | .cm-s-default .cm-string {color: #a11;} 117 | .cm-s-default .cm-string-2 {color: #f50;} 118 | .cm-s-default .cm-meta {color: #555;} 119 | .cm-s-default .cm-qualifier {color: #555;} 120 | .cm-s-default .cm-builtin {color: #30a;} 121 | .cm-s-default .cm-bracket {color: #997;} 122 | .cm-s-default .cm-tag {color: #170;} 123 | .cm-s-default .cm-attribute {color: #00c;} 124 | .cm-s-default .cm-hr {color: #999;} 125 | .cm-s-default .cm-link {color: #00c;} 126 | 127 | .cm-s-default .cm-error {color: #f00;} 128 | .cm-invalidchar {color: #f00;} 129 | 130 | .CodeMirror-composing { border-bottom: 2px solid; } 131 | 132 | /* Default styles for common addons */ 133 | 134 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 135 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 136 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 137 | .CodeMirror-activeline-background {background: #e8f2ff;} 138 | 139 | /* STOP */ 140 | 141 | /* The rest of this file contains styles related to the mechanics of 142 | the editor. You probably shouldn't touch them. */ 143 | 144 | .CodeMirror { 145 | position: relative; 146 | overflow: hidden; 147 | background: white; 148 | } 149 | 150 | .CodeMirror-scroll { 151 | overflow: scroll !important; /* Things will break if this is overridden */ 152 | /* 30px is the magic margin used to hide the element's real scrollbars */ 153 | /* See overflow: hidden in .CodeMirror */ 154 | margin-bottom: -30px; margin-right: -30px; 155 | padding-bottom: 30px; 156 | height: 100%; 157 | outline: none; /* Prevent dragging from highlighting the element */ 158 | position: relative; 159 | } 160 | .CodeMirror-sizer { 161 | position: relative; 162 | border-right: 30px solid transparent; 163 | } 164 | 165 | /* The fake, visible scrollbars. Used to force redraw during scrolling 166 | before actuall scrolling happens, thus preventing shaking and 167 | flickering artifacts. */ 168 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 169 | position: absolute; 170 | z-index: 6; 171 | display: none; 172 | } 173 | .CodeMirror-vscrollbar { 174 | right: 0; top: 0; 175 | overflow-x: hidden; 176 | overflow-y: scroll; 177 | } 178 | .CodeMirror-hscrollbar { 179 | bottom: 0; left: 0; 180 | overflow-y: hidden; 181 | overflow-x: scroll; 182 | } 183 | .CodeMirror-scrollbar-filler { 184 | right: 0; bottom: 0; 185 | } 186 | .CodeMirror-gutter-filler { 187 | left: 0; bottom: 0; 188 | } 189 | 190 | .CodeMirror-gutters { 191 | position: absolute; left: 0; top: 0; 192 | z-index: 3; 193 | } 194 | .CodeMirror-gutter { 195 | white-space: normal; 196 | height: 100%; 197 | display: inline-block; 198 | margin-bottom: -30px; 199 | /* Hack to make IE7 behave */ 200 | *zoom:1; 201 | *display:inline; 202 | } 203 | .CodeMirror-gutter-wrapper { 204 | position: absolute; 205 | z-index: 4; 206 | background: none !important; 207 | border: none !important; 208 | } 209 | .CodeMirror-gutter-background { 210 | position: absolute; 211 | top: 0; bottom: 0; 212 | z-index: 4; 213 | } 214 | .CodeMirror-gutter-elt { 215 | position: absolute; 216 | cursor: default; 217 | z-index: 4; 218 | } 219 | .CodeMirror-gutter-wrapper { 220 | -webkit-user-select: none; 221 | -moz-user-select: none; 222 | user-select: none; 223 | } 224 | 225 | .CodeMirror-lines { 226 | cursor: text; 227 | min-height: 1px; /* prevents collapsing before first draw */ 228 | } 229 | .CodeMirror pre { 230 | /* Reset some styles that the rest of the page might have set */ 231 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 232 | border-width: 0; 233 | background: transparent; 234 | font-family: inherit; 235 | font-size: inherit; 236 | margin: 0; 237 | white-space: pre; 238 | word-wrap: normal; 239 | line-height: inherit; 240 | color: inherit; 241 | z-index: 2; 242 | position: relative; 243 | overflow: visible; 244 | -webkit-tap-highlight-color: transparent; 245 | } 246 | .CodeMirror-wrap pre { 247 | word-wrap: break-word; 248 | white-space: pre-wrap; 249 | word-break: normal; 250 | } 251 | 252 | .CodeMirror-linebackground { 253 | position: absolute; 254 | left: 0; right: 0; top: 0; bottom: 0; 255 | z-index: 0; 256 | } 257 | 258 | .CodeMirror-linewidget { 259 | position: relative; 260 | z-index: 2; 261 | overflow: auto; 262 | } 263 | 264 | .CodeMirror-widget {} 265 | 266 | .CodeMirror-code { 267 | outline: none; 268 | } 269 | 270 | /* Force content-box sizing for the elements where we expect it */ 271 | .CodeMirror-scroll, 272 | .CodeMirror-sizer, 273 | .CodeMirror-gutter, 274 | .CodeMirror-gutters, 275 | .CodeMirror-linenumber { 276 | -moz-box-sizing: content-box; 277 | box-sizing: content-box; 278 | } 279 | 280 | .CodeMirror-measure { 281 | position: absolute; 282 | width: 100%; 283 | height: 0; 284 | overflow: hidden; 285 | visibility: hidden; 286 | } 287 | .CodeMirror-measure pre { position: static; } 288 | 289 | .CodeMirror div.CodeMirror-cursor { 290 | position: absolute; 291 | border-right: none; 292 | width: 0; 293 | } 294 | 295 | div.CodeMirror-cursors { 296 | visibility: hidden; 297 | position: relative; 298 | z-index: 3; 299 | } 300 | .CodeMirror-focused div.CodeMirror-cursors { 301 | visibility: visible; 302 | } 303 | 304 | .CodeMirror-selected { background: #d9d9d9; } 305 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 306 | .CodeMirror-crosshair { cursor: crosshair; } 307 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 308 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 309 | 310 | .cm-searching { 311 | background: #ffa; 312 | background: rgba(255, 255, 0, .4); 313 | } 314 | 315 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 316 | .CodeMirror span { *vertical-align: text-bottom; } 317 | 318 | /* Used to force a border model for a node */ 319 | .cm-force-border { padding-right: .1px; } 320 | 321 | @media print { 322 | /* Hide the cursor when printing */ 323 | .CodeMirror div.CodeMirror-cursors { 324 | visibility: hidden; 325 | } 326 | } 327 | 328 | /* See issue #2901 */ 329 | .cm-tab-wrap-hack:after { content: ''; } 330 | 331 | /* Help users use markselection to safely style text background */ 332 | span.CodeMirror-selectedtext { background: none; } 333 | -------------------------------------------------------------------------------- /js/vendor/threejs/obj-loader.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 | }; 10 | 11 | THREE.OBJLoader.prototype = { 12 | 13 | constructor: THREE.OBJLoader, 14 | 15 | load: function ( url, onLoad, onProgress, onError ) { 16 | 17 | var scope = this; 18 | 19 | var loader = new THREE.XHRLoader( scope.manager ); 20 | loader.setCrossOrigin( this.crossOrigin ); 21 | loader.load( url, function ( text ) { 22 | 23 | onLoad( scope.parse( text ) ); 24 | 25 | }, onProgress, onError ); 26 | 27 | }, 28 | 29 | parse: function ( text ) { 30 | 31 | console.time( 'OBJLoader' ); 32 | 33 | var object, objects = []; 34 | var geometry, material; 35 | 36 | function parseVertexIndex( value ) { 37 | 38 | var index = parseInt( value ); 39 | 40 | return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3; 41 | 42 | } 43 | 44 | function parseNormalIndex( value ) { 45 | 46 | var index = parseInt( value ); 47 | 48 | return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3; 49 | 50 | } 51 | 52 | function parseUVIndex( value ) { 53 | 54 | var index = parseInt( value ); 55 | 56 | return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2; 57 | 58 | } 59 | 60 | function addVertex( a, b, c ) { 61 | 62 | geometry.vertices.push( 63 | vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ], 64 | vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ], 65 | vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ] 66 | ); 67 | 68 | } 69 | 70 | function addNormal( a, b, c ) { 71 | 72 | geometry.normals.push( 73 | normals[ a ], normals[ a + 1 ], normals[ a + 2 ], 74 | normals[ b ], normals[ b + 1 ], normals[ b + 2 ], 75 | normals[ c ], normals[ c + 1 ], normals[ c + 2 ] 76 | ); 77 | 78 | } 79 | 80 | function addUV( a, b, c ) { 81 | 82 | geometry.uvs.push( 83 | uvs[ a ], uvs[ a + 1 ], 84 | uvs[ b ], uvs[ b + 1 ], 85 | uvs[ c ], uvs[ c + 1 ] 86 | ); 87 | 88 | } 89 | 90 | function addFace( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) { 91 | 92 | var ia = parseVertexIndex( a ); 93 | var ib = parseVertexIndex( b ); 94 | var ic = parseVertexIndex( c ); 95 | var id; 96 | 97 | if ( d === undefined ) { 98 | 99 | addVertex( ia, ib, ic ); 100 | 101 | } else { 102 | 103 | id = parseVertexIndex( d ); 104 | 105 | addVertex( ia, ib, id ); 106 | addVertex( ib, ic, id ); 107 | 108 | } 109 | 110 | if ( ua !== undefined ) { 111 | 112 | ia = parseUVIndex( ua ); 113 | ib = parseUVIndex( ub ); 114 | ic = parseUVIndex( uc ); 115 | 116 | if ( d === undefined ) { 117 | 118 | addUV( ia, ib, ic ); 119 | 120 | } else { 121 | 122 | id = parseUVIndex( ud ); 123 | 124 | addUV( ia, ib, id ); 125 | addUV( ib, ic, id ); 126 | 127 | } 128 | 129 | } 130 | 131 | if ( na !== undefined ) { 132 | 133 | ia = parseNormalIndex( na ); 134 | ib = parseNormalIndex( nb ); 135 | ic = parseNormalIndex( nc ); 136 | 137 | if ( d === undefined ) { 138 | 139 | addNormal( ia, ib, ic ); 140 | 141 | } else { 142 | 143 | id = parseNormalIndex( nd ); 144 | 145 | addNormal( ia, ib, id ); 146 | addNormal( ib, ic, id ); 147 | 148 | } 149 | 150 | } 151 | 152 | } 153 | 154 | // create mesh if no objects in text 155 | 156 | if ( /^o /gm.test( text ) === false ) { 157 | 158 | geometry = { 159 | vertices: [], 160 | normals: [], 161 | uvs: [] 162 | }; 163 | 164 | material = { 165 | name: '' 166 | }; 167 | 168 | object = { 169 | name: '', 170 | geometry: geometry, 171 | material: material 172 | }; 173 | 174 | objects.push( object ); 175 | 176 | } 177 | 178 | var vertices = []; 179 | var normals = []; 180 | var uvs = []; 181 | 182 | // v float float float 183 | 184 | var vertex_pattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; 185 | 186 | // vn float float float 187 | 188 | var normal_pattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; 189 | 190 | // vt float float 191 | 192 | var uv_pattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; 193 | 194 | // f vertex vertex vertex ... 195 | 196 | var face_pattern1 = /f( +-?\d+)( +-?\d+)( +-?\d+)( +-?\d+)?/; 197 | 198 | // f vertex/uv vertex/uv vertex/uv ... 199 | 200 | var face_pattern2 = /f( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))?/; 201 | 202 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... 203 | 204 | var face_pattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; 205 | 206 | // f vertex//normal vertex//normal vertex//normal ... 207 | 208 | var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/ 209 | 210 | // 211 | 212 | var lines = text.split( '\n' ); 213 | 214 | for ( var i = 0; i < lines.length; i ++ ) { 215 | 216 | var line = lines[ i ]; 217 | line = line.trim(); 218 | 219 | var result; 220 | 221 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 222 | 223 | continue; 224 | 225 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { 226 | 227 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 228 | 229 | vertices.push( 230 | parseFloat( result[ 1 ] ), 231 | parseFloat( result[ 2 ] ), 232 | parseFloat( result[ 3 ] ) 233 | ); 234 | 235 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { 236 | 237 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 238 | 239 | normals.push( 240 | parseFloat( result[ 1 ] ), 241 | parseFloat( result[ 2 ] ), 242 | parseFloat( result[ 3 ] ) 243 | ); 244 | 245 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { 246 | 247 | // ["vt 0.1 0.2", "0.1", "0.2"] 248 | 249 | uvs.push( 250 | parseFloat( result[ 1 ] ), 251 | parseFloat( result[ 2 ] ) 252 | ); 253 | 254 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { 255 | 256 | // ["f 1 2 3", "1", "2", "3", undefined] 257 | 258 | addFace( 259 | result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] 260 | ); 261 | 262 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { 263 | 264 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] 265 | 266 | addFace( 267 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], 268 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] 269 | ); 270 | 271 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { 272 | 273 | // ["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] 274 | 275 | addFace( 276 | result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ], 277 | result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ], 278 | result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] 279 | ); 280 | 281 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { 282 | 283 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] 284 | 285 | addFace( 286 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], 287 | undefined, undefined, undefined, undefined, 288 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] 289 | ); 290 | 291 | } else if ( /^o /.test( line ) ) { 292 | 293 | geometry = { 294 | vertices: [], 295 | normals: [], 296 | uvs: [] 297 | }; 298 | 299 | material = { 300 | name: '' 301 | }; 302 | 303 | object = { 304 | name: line.substring( 2 ).trim(), 305 | geometry: geometry, 306 | material: material 307 | }; 308 | 309 | objects.push( object ) 310 | 311 | } else if ( /^g /.test( line ) ) { 312 | 313 | // group 314 | 315 | } else if ( /^usemtl /.test( line ) ) { 316 | 317 | // material 318 | 319 | material.name = line.substring( 7 ).trim(); 320 | 321 | } else if ( /^mtllib /.test( line ) ) { 322 | 323 | // mtl file 324 | 325 | } else if ( /^s /.test( line ) ) { 326 | 327 | // smooth shading 328 | 329 | } else { 330 | 331 | // console.log( "THREE.OBJLoader: Unhandled line " + line ); 332 | 333 | } 334 | 335 | } 336 | 337 | var container = new THREE.Object3D(); 338 | 339 | for ( var i = 0, l = objects.length; i < l; i ++ ) { 340 | 341 | object = objects[ i ]; 342 | geometry = object.geometry; 343 | 344 | var buffergeometry = new THREE.BufferGeometry(); 345 | 346 | buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) ); 347 | 348 | if ( geometry.normals.length > 0 ) { 349 | buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) ); 350 | } 351 | 352 | if ( geometry.uvs.length > 0 ) { 353 | buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) ); 354 | } 355 | 356 | material = new THREE.MeshLambertMaterial(); 357 | material.name = object.material.name; 358 | 359 | var mesh = new THREE.Mesh( buffergeometry, material ); 360 | mesh.name = object.name; 361 | 362 | container.add( mesh ); 363 | 364 | } 365 | 366 | console.timeEnd( 'OBJLoader' ); 367 | 368 | return container; 369 | 370 | } 371 | 372 | }; 373 | -------------------------------------------------------------------------------- /js/vendor/codemirror/clojure.js: -------------------------------------------------------------------------------- 1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 | // Distributed under an MIT license: http://codemirror.net/LICENSE 3 | 4 | /** 5 | * Author: Hans Engel 6 | * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun) 7 | */ 8 | 9 | (function(mod) { 10 | if (typeof exports == "object" && typeof module == "object") // CommonJS 11 | mod(require("../../lib/codemirror")); 12 | else if (typeof define == "function" && define.amd) // AMD 13 | define(["../../lib/codemirror"], mod); 14 | else // Plain browser env 15 | mod(CodeMirror); 16 | })(function(CodeMirror) { 17 | "use strict"; 18 | 19 | CodeMirror.defineMode("clojure", function (options) { 20 | var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2", 21 | ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", VAR = "variable"; 22 | var INDENT_WORD_SKIP = options.indentUnit || 2; 23 | var NORMAL_INDENT_UNIT = options.indentUnit || 2; 24 | 25 | function makeKeywords(str) { 26 | var obj = {}, words = str.split(" "); 27 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true; 28 | return obj; 29 | } 30 | 31 | var atoms = makeKeywords("true false nil"); 32 | 33 | var keywords = makeKeywords( 34 | "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle"); 35 | 36 | var builtins = makeKeywords( 37 | "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> ->> ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? declare default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? fnext fnil for force format frequencies future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash hash-combine hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc inc' init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt keep keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int rand-nth range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string realized? reduce reduce-kv reductions ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents slurp some some-fn sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap *default-data-reader-fn* as-> cond-> cond->> reduced reduced? send-via set-agent-send-executor! set-agent-send-off-executor! some-> some->>"); 38 | 39 | var indentKeys = makeKeywords( 40 | // Built-ins 41 | "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch " + 42 | 43 | // Binding forms 44 | "let letfn binding loop for doseq dotimes when-let if-let " + 45 | 46 | // Data structures 47 | "defstruct struct-map assoc " + 48 | 49 | // clojure.test 50 | "testing deftest " + 51 | 52 | // contrib 53 | "handler-case handle dotrace deftrace"); 54 | 55 | var tests = { 56 | digit: /\d/, 57 | digit_or_colon: /[\d:]/, 58 | hex: /[0-9a-f]/i, 59 | sign: /[+-]/, 60 | exponent: /e/i, 61 | keyword_char: /[^\s\(\[\;\)\]]/, 62 | symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/ 63 | }; 64 | 65 | function stateStack(indent, type, prev) { // represents a state stack object 66 | this.indent = indent; 67 | this.type = type; 68 | this.prev = prev; 69 | } 70 | 71 | function pushStack(state, indent, type) { 72 | state.indentStack = new stateStack(indent, type, state.indentStack); 73 | } 74 | 75 | function popStack(state) { 76 | state.indentStack = state.indentStack.prev; 77 | } 78 | 79 | function isNumber(ch, stream){ 80 | // hex 81 | if ( ch === '0' && stream.eat(/x/i) ) { 82 | stream.eatWhile(tests.hex); 83 | return true; 84 | } 85 | 86 | // leading sign 87 | if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) { 88 | stream.eat(tests.sign); 89 | ch = stream.next(); 90 | } 91 | 92 | if ( tests.digit.test(ch) ) { 93 | stream.eat(ch); 94 | stream.eatWhile(tests.digit); 95 | 96 | if ( '.' == stream.peek() ) { 97 | stream.eat('.'); 98 | stream.eatWhile(tests.digit); 99 | } 100 | 101 | if ( stream.eat(tests.exponent) ) { 102 | stream.eat(tests.sign); 103 | stream.eatWhile(tests.digit); 104 | } 105 | 106 | return true; 107 | } 108 | 109 | return false; 110 | } 111 | 112 | // Eat character that starts after backslash \ 113 | function eatCharacter(stream) { 114 | var first = stream.next(); 115 | // Read special literals: backspace, newline, space, return. 116 | // Just read all lowercase letters. 117 | if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) { 118 | return; 119 | } 120 | // Read unicode character: \u1000 \uA0a1 121 | if (first === "u") { 122 | stream.match(/[0-9a-z]{4}/i, true); 123 | } 124 | } 125 | 126 | return { 127 | startState: function () { 128 | return { 129 | indentStack: null, 130 | indentation: 0, 131 | mode: false 132 | }; 133 | }, 134 | 135 | token: function (stream, state) { 136 | if (state.indentStack == null && stream.sol()) { 137 | // update indentation, but only if indentStack is empty 138 | state.indentation = stream.indentation(); 139 | } 140 | 141 | // skip spaces 142 | if (stream.eatSpace()) { 143 | return null; 144 | } 145 | var returnType = null; 146 | 147 | switch(state.mode){ 148 | case "string": // multi-line string parsing mode 149 | var next, escaped = false; 150 | while ((next = stream.next()) != null) { 151 | if (next == "\"" && !escaped) { 152 | 153 | state.mode = false; 154 | break; 155 | } 156 | escaped = !escaped && next == "\\"; 157 | } 158 | returnType = STRING; // continue on in string mode 159 | break; 160 | default: // default parsing mode 161 | var ch = stream.next(); 162 | 163 | if (ch == "\"") { 164 | state.mode = "string"; 165 | returnType = STRING; 166 | } else if (ch == "\\") { 167 | eatCharacter(stream); 168 | returnType = CHARACTER; 169 | } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) { 170 | returnType = ATOM; 171 | } else if (ch == ";") { // comment 172 | stream.skipToEnd(); // rest of the line is a comment 173 | returnType = COMMENT; 174 | } else if (isNumber(ch,stream)){ 175 | returnType = NUMBER; 176 | } else if (ch == "(" || ch == "[" || ch == "{" ) { 177 | var keyWord = '', indentTemp = stream.column(), letter; 178 | /** 179 | Either 180 | (indent-word .. 181 | (non-indent-word .. 182 | (;something else, bracket, etc. 183 | */ 184 | 185 | if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) { 186 | keyWord += letter; 187 | } 188 | 189 | if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) || 190 | /^(?:def|with)/.test(keyWord))) { // indent-word 191 | pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); 192 | } else { // non-indent word 193 | // we continue eating the spaces 194 | stream.eatSpace(); 195 | if (stream.eol() || stream.peek() == ";") { 196 | // nothing significant after 197 | // we restart indentation the user defined spaces after 198 | pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch); 199 | } else { 200 | pushStack(state, indentTemp + stream.current().length, ch); // else we match 201 | } 202 | } 203 | stream.backUp(stream.current().length - 1); // undo all the eating 204 | 205 | returnType = BRACKET; 206 | } else if (ch == ")" || ch == "]" || ch == "}") { 207 | returnType = BRACKET; 208 | if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : (ch == "]" ? "[" :"{"))) { 209 | popStack(state); 210 | } 211 | } else if ( ch == ":" ) { 212 | stream.eatWhile(tests.symbol); 213 | return ATOM; 214 | } else { 215 | stream.eatWhile(tests.symbol); 216 | 217 | if (keywords && keywords.propertyIsEnumerable(stream.current())) { 218 | returnType = KEYWORD; 219 | } else if (builtins && builtins.propertyIsEnumerable(stream.current())) { 220 | returnType = BUILTIN; 221 | } else if (atoms && atoms.propertyIsEnumerable(stream.current())) { 222 | returnType = ATOM; 223 | } else { 224 | returnType = VAR; 225 | } 226 | } 227 | } 228 | 229 | return returnType; 230 | }, 231 | 232 | indent: function (state) { 233 | if (state.indentStack == null) return state.indentation; 234 | return state.indentStack.indent; 235 | }, 236 | 237 | closeBrackets: {pairs: "()[]{}\"\""}, 238 | lineComment: ";;" 239 | }; 240 | }); 241 | 242 | CodeMirror.defineMIME("text/x-clojure", "clojure"); 243 | 244 | }); 245 | -------------------------------------------------------------------------------- /js/vendor/threejs/collada-loader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tim Knip / http://www.floorplanner.com/ / tim at floorplanner.com 3 | * @author Tony Parisi / http://www.tonyparisi.com/ 4 | */ 5 | 6 | THREE.ColladaLoader = function () { 7 | 8 | var COLLADA = null; 9 | var scene = null; 10 | var visualScene; 11 | var kinematicsModel; 12 | 13 | var readyCallbackFunc = null; 14 | 15 | var sources = {}; 16 | var images = {}; 17 | var animations = {}; 18 | var controllers = {}; 19 | var geometries = {}; 20 | var materials = {}; 21 | var effects = {}; 22 | var cameras = {}; 23 | var lights = {}; 24 | 25 | var animData; 26 | var kinematics; 27 | var visualScenes; 28 | var kinematicsModels; 29 | var baseUrl; 30 | var morphs; 31 | var skins; 32 | 33 | var flip_uv = true; 34 | var preferredShading = THREE.SmoothShading; 35 | 36 | var options = { 37 | // Force Geometry to always be centered at the local origin of the 38 | // containing Mesh. 39 | centerGeometry: false, 40 | 41 | // Axis conversion is done for geometries, animations, and controllers. 42 | // If we ever pull cameras or lights out of the COLLADA file, they'll 43 | // need extra work. 44 | convertUpAxis: false, 45 | 46 | subdivideFaces: true, 47 | 48 | upAxis: 'Y', 49 | 50 | // For reflective or refractive materials we'll use this cubemap 51 | defaultEnvMap: null 52 | 53 | }; 54 | 55 | var colladaUnit = 1.0; 56 | var colladaUp = 'Y'; 57 | var upConversion = null; 58 | 59 | function load ( url, readyCallback, progressCallback, failCallback ) { 60 | 61 | var length = 0; 62 | 63 | if ( document.implementation && document.implementation.createDocument ) { 64 | 65 | var request = new XMLHttpRequest(); 66 | 67 | request.onreadystatechange = function() { 68 | 69 | if ( request.readyState === 4 ) { 70 | 71 | if ( request.status === 0 || request.status === 200 ) { 72 | 73 | 74 | if ( request.responseXML ) { 75 | 76 | readyCallbackFunc = readyCallback; 77 | parse( request.responseXML, undefined, url ); 78 | 79 | } else if ( request.responseText ) { 80 | 81 | readyCallbackFunc = readyCallback; 82 | var xmlParser = new DOMParser(); 83 | var responseXML = xmlParser.parseFromString( request.responseText, "application/xml" ); 84 | parse( responseXML, undefined, url ); 85 | 86 | } else { 87 | 88 | if ( faillCallback ) { 89 | 90 | failCallback(); 91 | 92 | } else { 93 | 94 | console.error( "ColladaLoader: Empty or non-existing file (" + url + ")" ); 95 | 96 | } 97 | 98 | } 99 | 100 | } 101 | 102 | } else if ( request.readyState === 3 ) { 103 | 104 | if ( progressCallback ) { 105 | 106 | if ( length === 0 ) { 107 | 108 | length = request.getResponseHeader( "Content-Length" ); 109 | 110 | } 111 | 112 | progressCallback( { total: length, loaded: request.responseText.length } ); 113 | 114 | } 115 | 116 | } 117 | 118 | } 119 | 120 | request.open( "GET", url, true ); 121 | request.send( null ); 122 | 123 | } else { 124 | 125 | alert( "Don't know how to parse XML!" ); 126 | 127 | } 128 | 129 | } 130 | 131 | function parse( doc, callBack, url ) { 132 | 133 | COLLADA = doc; 134 | callBack = callBack || readyCallbackFunc; 135 | 136 | if ( url !== undefined ) { 137 | 138 | var parts = url.split( '/' ); 139 | parts.pop(); 140 | baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; 141 | 142 | } 143 | 144 | parseAsset(); 145 | setUpConversion(); 146 | images = parseLib( "library_images image", _Image, "image" ); 147 | materials = parseLib( "library_materials material", Material, "material" ); 148 | effects = parseLib( "library_effects effect", Effect, "effect" ); 149 | geometries = parseLib( "library_geometries geometry", Geometry, "geometry" ); 150 | cameras = parseLib( "library_cameras camera", Camera, "camera" ); 151 | lights = parseLib( "library_lights light", Light, "light" ); 152 | controllers = parseLib( "library_controllers controller", Controller, "controller" ); 153 | animations = parseLib( "library_animations animation", Animation, "animation" ); 154 | visualScenes = parseLib( "library_visual_scenes visual_scene", VisualScene, "visual_scene" ); 155 | kinematicsModels = parseLib( "library_kinematics_models kinematics_model", KinematicsModel, "kinematics_model" ); 156 | 157 | morphs = []; 158 | skins = []; 159 | 160 | visualScene = parseScene(); 161 | scene = new THREE.Group(); 162 | 163 | for ( var i = 0; i < visualScene.nodes.length; i ++ ) { 164 | 165 | scene.add( createSceneGraph( visualScene.nodes[ i ] ) ); 166 | 167 | } 168 | 169 | // unit conversion 170 | scene.scale.multiplyScalar( colladaUnit ); 171 | 172 | createAnimations(); 173 | 174 | kinematicsModel = parseKinematicsModel(); 175 | createKinematics(); 176 | 177 | var result = { 178 | 179 | scene: scene, 180 | morphs: morphs, 181 | skins: skins, 182 | animations: animData, 183 | kinematics: kinematics, 184 | dae: { 185 | images: images, 186 | materials: materials, 187 | cameras: cameras, 188 | lights: lights, 189 | effects: effects, 190 | geometries: geometries, 191 | controllers: controllers, 192 | animations: animations, 193 | visualScenes: visualScenes, 194 | visualScene: visualScene, 195 | scene: visualScene, 196 | kinematicsModels: kinematicsModels, 197 | kinematicsModel: kinematicsModel 198 | } 199 | 200 | }; 201 | 202 | if ( callBack ) { 203 | 204 | callBack( result ); 205 | 206 | } 207 | 208 | return result; 209 | 210 | } 211 | 212 | function setPreferredShading ( shading ) { 213 | 214 | preferredShading = shading; 215 | 216 | } 217 | 218 | function parseAsset () { 219 | 220 | var elements = COLLADA.querySelectorAll('asset'); 221 | 222 | var element = elements[0]; 223 | 224 | if ( element && element.childNodes ) { 225 | 226 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 227 | 228 | var child = element.childNodes[ i ]; 229 | 230 | switch ( child.nodeName ) { 231 | 232 | case 'unit': 233 | 234 | var meter = child.getAttribute( 'meter' ); 235 | 236 | if ( meter ) { 237 | 238 | colladaUnit = parseFloat( meter ); 239 | 240 | } 241 | 242 | break; 243 | 244 | case 'up_axis': 245 | 246 | colladaUp = child.textContent.charAt(0); 247 | break; 248 | 249 | } 250 | 251 | } 252 | 253 | } 254 | 255 | } 256 | 257 | function parseLib ( q, classSpec, prefix ) { 258 | 259 | var elements = COLLADA.querySelectorAll(q); 260 | 261 | var lib = {}; 262 | 263 | var i = 0; 264 | 265 | var elementsLength = elements.length; 266 | 267 | for ( var j = 0; j < elementsLength; j ++ ) { 268 | 269 | var element = elements[j]; 270 | var daeElement = ( new classSpec() ).parse( element ); 271 | 272 | if ( !daeElement.id || daeElement.id.length === 0 ) daeElement.id = prefix + ( i ++ ); 273 | lib[ daeElement.id ] = daeElement; 274 | 275 | } 276 | 277 | return lib; 278 | 279 | } 280 | 281 | function parseScene() { 282 | 283 | var sceneElement = COLLADA.querySelectorAll('scene instance_visual_scene')[0]; 284 | 285 | if ( sceneElement ) { 286 | 287 | var url = sceneElement.getAttribute( 'url' ).replace( /^#/, '' ); 288 | return visualScenes[ url.length > 0 ? url : 'visual_scene0' ]; 289 | 290 | } else { 291 | 292 | return null; 293 | 294 | } 295 | 296 | } 297 | 298 | function parseKinematicsModel() { 299 | 300 | var kinematicsModelElement = COLLADA.querySelectorAll('instance_kinematics_model')[0]; 301 | 302 | if ( kinematicsModelElement ) { 303 | 304 | var url = kinematicsModelElement.getAttribute( 'url' ).replace(/^#/, ''); 305 | return kinematicsModels[ url.length > 0 ? url : 'kinematics_model0' ]; 306 | 307 | } else { 308 | 309 | return null; 310 | 311 | } 312 | 313 | } 314 | 315 | function createAnimations() { 316 | 317 | animData = []; 318 | 319 | // fill in the keys 320 | recurseHierarchy( scene ); 321 | 322 | } 323 | 324 | function recurseHierarchy( node ) { 325 | 326 | var n = visualScene.getChildById( node.colladaId, true ), 327 | newData = null; 328 | 329 | if ( n && n.keys ) { 330 | 331 | newData = { 332 | fps: 60, 333 | hierarchy: [ { 334 | node: n, 335 | keys: n.keys, 336 | sids: n.sids 337 | } ], 338 | node: node, 339 | name: 'animation_' + node.name, 340 | length: 0 341 | }; 342 | 343 | animData.push(newData); 344 | 345 | for ( var i = 0, il = n.keys.length; i < il; i ++ ) { 346 | 347 | newData.length = Math.max( newData.length, n.keys[i].time ); 348 | 349 | } 350 | 351 | } else { 352 | 353 | newData = { 354 | hierarchy: [ { 355 | keys: [], 356 | sids: [] 357 | } ] 358 | } 359 | 360 | } 361 | 362 | for ( var i = 0, il = node.children.length; i < il; i ++ ) { 363 | 364 | var d = recurseHierarchy( node.children[i] ); 365 | 366 | for ( var j = 0, jl = d.hierarchy.length; j < jl; j ++ ) { 367 | 368 | newData.hierarchy.push( { 369 | keys: [], 370 | sids: [] 371 | } ); 372 | 373 | } 374 | 375 | } 376 | 377 | return newData; 378 | 379 | } 380 | 381 | function calcAnimationBounds () { 382 | 383 | var start = 1000000; 384 | var end = -start; 385 | var frames = 0; 386 | var ID; 387 | for ( var id in animations ) { 388 | 389 | var animation = animations[ id ]; 390 | ID = ID || animation.id; 391 | for ( var i = 0; i < animation.sampler.length; i ++ ) { 392 | 393 | var sampler = animation.sampler[ i ]; 394 | 395 | sampler.create(); 396 | 397 | start = Math.min( start, sampler.startTime ); 398 | end = Math.max( end, sampler.endTime ); 399 | frames = Math.max( frames, sampler.input.length ); 400 | 401 | } 402 | 403 | } 404 | 405 | return { start:start, end:end, frames:frames,ID:ID }; 406 | 407 | } 408 | 409 | function createMorph ( geometry, ctrl ) { 410 | 411 | var morphCtrl = ctrl instanceof InstanceController ? controllers[ ctrl.url ] : ctrl; 412 | 413 | if ( !morphCtrl || !morphCtrl.morph ) { 414 | 415 | console.log("could not find morph controller!"); 416 | return; 417 | 418 | } 419 | 420 | var morph = morphCtrl.morph; 421 | 422 | for ( var i = 0; i < morph.targets.length; i ++ ) { 423 | 424 | var target_id = morph.targets[ i ]; 425 | var daeGeometry = geometries[ target_id ]; 426 | 427 | if ( !daeGeometry.mesh || 428 | !daeGeometry.mesh.primitives || 429 | !daeGeometry.mesh.primitives.length ) { 430 | continue; 431 | } 432 | 433 | var target = daeGeometry.mesh.primitives[ 0 ].geometry; 434 | 435 | if ( target.vertices.length === geometry.vertices.length ) { 436 | 437 | geometry.morphTargets.push( { name: "target_1", vertices: target.vertices } ); 438 | 439 | } 440 | 441 | } 442 | 443 | geometry.morphTargets.push( { name: "target_Z", vertices: geometry.vertices } ); 444 | 445 | }; 446 | 447 | function createSkin ( geometry, ctrl, applyBindShape ) { 448 | 449 | var skinCtrl = controllers[ ctrl.url ]; 450 | 451 | if ( !skinCtrl || !skinCtrl.skin ) { 452 | 453 | console.log( "could not find skin controller!" ); 454 | return; 455 | 456 | } 457 | 458 | if ( !ctrl.skeleton || !ctrl.skeleton.length ) { 459 | 460 | console.log( "could not find the skeleton for the skin!" ); 461 | return; 462 | 463 | } 464 | 465 | var skin = skinCtrl.skin; 466 | var skeleton = visualScene.getChildById( ctrl.skeleton[ 0 ] ); 467 | var hierarchy = []; 468 | 469 | applyBindShape = applyBindShape !== undefined ? applyBindShape : true; 470 | 471 | var bones = []; 472 | geometry.skinWeights = []; 473 | geometry.skinIndices = []; 474 | 475 | //createBones( geometry.bones, skin, hierarchy, skeleton, null, -1 ); 476 | //createWeights( skin, geometry.bones, geometry.skinIndices, geometry.skinWeights ); 477 | 478 | /* 479 | geometry.animation = { 480 | name: 'take_001', 481 | fps: 30, 482 | length: 2, 483 | JIT: true, 484 | hierarchy: hierarchy 485 | }; 486 | */ 487 | 488 | if ( applyBindShape ) { 489 | 490 | for ( var i = 0; i < geometry.vertices.length; i ++ ) { 491 | 492 | geometry.vertices[ i ].applyMatrix4( skin.bindShapeMatrix ); 493 | 494 | } 495 | 496 | } 497 | 498 | } 499 | 500 | function setupSkeleton ( node, bones, frame, parent ) { 501 | 502 | node.world = node.world || new THREE.Matrix4(); 503 | node.localworld = node.localworld || new THREE.Matrix4(); 504 | node.world.copy( node.matrix ); 505 | node.localworld.copy( node.matrix ); 506 | 507 | if ( node.channels && node.channels.length ) { 508 | 509 | var channel = node.channels[ 0 ]; 510 | var m = channel.sampler.output[ frame ]; 511 | 512 | if ( m instanceof THREE.Matrix4 ) { 513 | 514 | node.world.copy( m ); 515 | node.localworld.copy(m); 516 | if (frame === 0) 517 | node.matrix.copy(m); 518 | } 519 | 520 | } 521 | 522 | if ( parent ) { 523 | 524 | node.world.multiplyMatrices( parent, node.world ); 525 | 526 | } 527 | 528 | bones.push( node ); 529 | 530 | for ( var i = 0; i < node.nodes.length; i ++ ) { 531 | 532 | setupSkeleton( node.nodes[ i ], bones, frame, node.world ); 533 | 534 | } 535 | 536 | } 537 | 538 | function setupSkinningMatrices ( bones, skin ) { 539 | 540 | // FIXME: this is dumb... 541 | 542 | for ( var i = 0; i < bones.length; i ++ ) { 543 | 544 | var bone = bones[ i ]; 545 | var found = -1; 546 | 547 | if ( bone.type != 'JOINT' ) continue; 548 | 549 | for ( var j = 0; j < skin.joints.length; j ++ ) { 550 | 551 | if ( bone.sid === skin.joints[ j ] ) { 552 | 553 | found = j; 554 | break; 555 | 556 | } 557 | 558 | } 559 | 560 | if ( found >= 0 ) { 561 | 562 | var inv = skin.invBindMatrices[ found ]; 563 | 564 | bone.invBindMatrix = inv; 565 | bone.skinningMatrix = new THREE.Matrix4(); 566 | bone.skinningMatrix.multiplyMatrices(bone.world, inv); // (IBMi * JMi) 567 | bone.animatrix = new THREE.Matrix4(); 568 | 569 | bone.animatrix.copy(bone.localworld); 570 | bone.weights = []; 571 | 572 | for ( var j = 0; j < skin.weights.length; j ++ ) { 573 | 574 | for (var k = 0; k < skin.weights[ j ].length; k ++ ) { 575 | 576 | var w = skin.weights[ j ][ k ]; 577 | 578 | if ( w.joint === found ) { 579 | 580 | bone.weights.push( w ); 581 | 582 | } 583 | 584 | } 585 | 586 | } 587 | 588 | } else { 589 | 590 | console.warn( "ColladaLoader: Could not find joint '" + bone.sid + "'." ); 591 | 592 | bone.skinningMatrix = new THREE.Matrix4(); 593 | bone.weights = []; 594 | 595 | } 596 | } 597 | 598 | } 599 | 600 | //Walk the Collada tree and flatten the bones into a list, extract the position, quat and scale from the matrix 601 | function flattenSkeleton(skeleton) { 602 | 603 | var list = []; 604 | var walk = function(parentid, node, list) { 605 | 606 | var bone = {}; 607 | bone.name = node.sid; 608 | bone.parent = parentid; 609 | bone.matrix = node.matrix; 610 | var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; 611 | bone.matrix.decompose(data[0], data[1], data[2]); 612 | 613 | bone.pos = [ data[0].x,data[0].y,data[0].z ]; 614 | 615 | bone.scl = [ data[2].x,data[2].y,data[2].z ]; 616 | bone.rotq = [ data[1].x,data[1].y,data[1].z,data[1].w ]; 617 | list.push(bone); 618 | 619 | for (var i in node.nodes) { 620 | 621 | walk(node.sid, node.nodes[i], list); 622 | 623 | } 624 | 625 | }; 626 | 627 | walk(-1, skeleton, list); 628 | return list; 629 | 630 | } 631 | 632 | //Move the vertices into the pose that is proper for the start of the animation 633 | function skinToBindPose(geometry,skeleton,skinController) { 634 | 635 | var bones = []; 636 | setupSkeleton( skeleton, bones, -1 ); 637 | setupSkinningMatrices( bones, skinController.skin ); 638 | v = new THREE.Vector3(); 639 | var skinned = []; 640 | 641 | for (var i = 0; i < geometry.vertices.length; i ++) { 642 | 643 | skinned.push(new THREE.Vector3()); 644 | 645 | } 646 | 647 | for ( i = 0; i < bones.length; i ++ ) { 648 | 649 | if ( bones[ i ].type != 'JOINT' ) continue; 650 | 651 | for ( j = 0; j < bones[ i ].weights.length; j ++ ) { 652 | 653 | w = bones[ i ].weights[ j ]; 654 | vidx = w.index; 655 | weight = w.weight; 656 | 657 | o = geometry.vertices[vidx]; 658 | s = skinned[vidx]; 659 | 660 | v.x = o.x; 661 | v.y = o.y; 662 | v.z = o.z; 663 | 664 | v.applyMatrix4( bones[i].skinningMatrix ); 665 | 666 | s.x += (v.x * weight); 667 | s.y += (v.y * weight); 668 | s.z += (v.z * weight); 669 | } 670 | 671 | } 672 | 673 | for (var i = 0; i < geometry.vertices.length; i ++) { 674 | 675 | geometry.vertices[i] = skinned[i]; 676 | 677 | } 678 | 679 | } 680 | 681 | function applySkin ( geometry, instanceCtrl, frame ) { 682 | 683 | var skinController = controllers[ instanceCtrl.url ]; 684 | 685 | frame = frame !== undefined ? frame : 40; 686 | 687 | if ( !skinController || !skinController.skin ) { 688 | 689 | console.log( 'ColladaLoader: Could not find skin controller.' ); 690 | return; 691 | 692 | } 693 | 694 | if ( !instanceCtrl.skeleton || !instanceCtrl.skeleton.length ) { 695 | 696 | console.log( 'ColladaLoader: Could not find the skeleton for the skin. ' ); 697 | return; 698 | 699 | } 700 | 701 | var animationBounds = calcAnimationBounds(); 702 | var skeleton = visualScene.getChildById( instanceCtrl.skeleton[0], true ) || visualScene.getChildBySid( instanceCtrl.skeleton[0], true ); 703 | 704 | //flatten the skeleton into a list of bones 705 | var bonelist = flattenSkeleton(skeleton); 706 | var joints = skinController.skin.joints; 707 | 708 | //sort that list so that the order reflects the order in the joint list 709 | var sortedbones = []; 710 | for (var i = 0; i < joints.length; i ++) { 711 | 712 | for (var j = 0; j < bonelist.length; j ++) { 713 | 714 | if (bonelist[j].name === joints[i]) { 715 | 716 | sortedbones[i] = bonelist[j]; 717 | 718 | } 719 | 720 | } 721 | 722 | } 723 | 724 | //hook up the parents by index instead of name 725 | for (var i = 0; i < sortedbones.length; i ++) { 726 | 727 | for (var j = 0; j < sortedbones.length; j ++) { 728 | 729 | if (sortedbones[i].parent === sortedbones[j].name) { 730 | 731 | sortedbones[i].parent = j; 732 | 733 | } 734 | 735 | } 736 | 737 | } 738 | 739 | 740 | var i, j, w, vidx, weight; 741 | var v = new THREE.Vector3(), o, s; 742 | 743 | // move vertices to bind shape 744 | for ( i = 0; i < geometry.vertices.length; i ++ ) { 745 | geometry.vertices[i].applyMatrix4( skinController.skin.bindShapeMatrix ); 746 | } 747 | 748 | var skinIndices = []; 749 | var skinWeights = []; 750 | var weights = skinController.skin.weights; 751 | 752 | // hook up the skin weights 753 | // TODO - this might be a good place to choose greatest 4 weights 754 | for ( var i =0; i < weights.length; i ++ ) { 755 | 756 | var indicies = new THREE.Vector4(weights[i][0] ? weights[i][0].joint : 0,weights[i][1] ? weights[i][1].joint : 0,weights[i][2] ? weights[i][2].joint : 0,weights[i][3] ? weights[i][3].joint : 0); 757 | var weight = new THREE.Vector4(weights[i][0] ? weights[i][0].weight : 0,weights[i][1] ? weights[i][1].weight : 0,weights[i][2] ? weights[i][2].weight : 0,weights[i][3] ? weights[i][3].weight : 0); 758 | 759 | skinIndices.push(indicies); 760 | skinWeights.push(weight); 761 | 762 | } 763 | 764 | geometry.skinIndices = skinIndices; 765 | geometry.skinWeights = skinWeights; 766 | geometry.bones = sortedbones; 767 | // process animation, or simply pose the rig if no animation 768 | 769 | //create an animation for the animated bones 770 | //NOTE: this has no effect when using morphtargets 771 | var animationdata = { "name":animationBounds.ID,"fps":30,"length":animationBounds.frames / 30,"hierarchy":[] }; 772 | 773 | for (var j = 0; j < sortedbones.length; j ++) { 774 | 775 | animationdata.hierarchy.push({ parent:sortedbones[j].parent, name:sortedbones[j].name, keys:[] }); 776 | 777 | } 778 | 779 | console.log( 'ColladaLoader:', animationBounds.ID + ' has ' + sortedbones.length + ' bones.' ); 780 | 781 | 782 | 783 | skinToBindPose(geometry, skeleton, skinController); 784 | 785 | 786 | for ( frame = 0; frame < animationBounds.frames; frame ++ ) { 787 | 788 | var bones = []; 789 | var skinned = []; 790 | // process the frame and setup the rig with a fresh 791 | // transform, possibly from the bone's animation channel(s) 792 | 793 | setupSkeleton( skeleton, bones, frame ); 794 | setupSkinningMatrices( bones, skinController.skin ); 795 | 796 | for (var i = 0; i < bones.length; i ++) { 797 | 798 | for (var j = 0; j < animationdata.hierarchy.length; j ++) { 799 | 800 | if (animationdata.hierarchy[j].name === bones[i].sid) { 801 | 802 | var key = {}; 803 | key.time = (frame / 30); 804 | key.matrix = bones[i].animatrix; 805 | 806 | if (frame === 0) 807 | bones[i].matrix = key.matrix; 808 | 809 | var data = [ new THREE.Vector3(),new THREE.Quaternion(),new THREE.Vector3() ]; 810 | key.matrix.decompose(data[0], data[1], data[2]); 811 | 812 | key.pos = [ data[0].x,data[0].y,data[0].z ]; 813 | 814 | key.scl = [ data[2].x,data[2].y,data[2].z ]; 815 | key.rot = data[1]; 816 | 817 | animationdata.hierarchy[j].keys.push(key); 818 | 819 | } 820 | 821 | } 822 | 823 | } 824 | 825 | geometry.animation = animationdata; 826 | 827 | } 828 | 829 | }; 830 | 831 | function createKinematics() { 832 | 833 | if ( kinematicsModel && kinematicsModel.joints.length === 0 ) { 834 | kinematics = undefined; 835 | return; 836 | } 837 | 838 | var jointMap = {}; 839 | 840 | var _addToMap = function( jointIndex, parentVisualElement ) { 841 | 842 | var parentVisualElementId = parentVisualElement.getAttribute( 'id' ); 843 | var colladaNode = visualScene.getChildById( parentVisualElementId, true ); 844 | var joint = kinematicsModel.joints[ jointIndex ]; 845 | 846 | scene.traverse(function( node ) { 847 | 848 | if ( node.colladaId == parentVisualElementId ) { 849 | 850 | jointMap[ jointIndex ] = { 851 | node: node, 852 | transforms: colladaNode.transforms, 853 | joint: joint, 854 | position: joint.zeroPosition 855 | }; 856 | 857 | } 858 | 859 | }); 860 | 861 | }; 862 | 863 | kinematics = { 864 | 865 | joints: kinematicsModel && kinematicsModel.joints, 866 | 867 | getJointValue: function( jointIndex ) { 868 | 869 | var jointData = jointMap[ jointIndex ]; 870 | 871 | if ( jointData ) { 872 | 873 | return jointData.position; 874 | 875 | } else { 876 | 877 | console.log( 'getJointValue: joint ' + jointIndex + ' doesn\'t exist' ); 878 | 879 | } 880 | 881 | }, 882 | 883 | setJointValue: function( jointIndex, value ) { 884 | 885 | var jointData = jointMap[ jointIndex ]; 886 | 887 | if ( jointData ) { 888 | 889 | var joint = jointData.joint; 890 | 891 | if ( value > joint.limits.max || value < joint.limits.min ) { 892 | 893 | console.log( 'setJointValue: joint ' + jointIndex + ' value ' + value + ' outside of limits (min: ' + joint.limits.min + ', max: ' + joint.limits.max + ')' ); 894 | 895 | } else if ( joint.static ) { 896 | 897 | console.log( 'setJointValue: joint ' + jointIndex + ' is static' ); 898 | 899 | } else { 900 | 901 | var threejsNode = jointData.node; 902 | var axis = joint.axis; 903 | var transforms = jointData.transforms; 904 | 905 | var matrix = new THREE.Matrix4(); 906 | 907 | for (i = 0; i < transforms.length; i ++ ) { 908 | 909 | var transform = transforms[ i ]; 910 | 911 | // kinda ghetto joint detection 912 | if ( transform.sid && transform.sid.indexOf( 'joint' + jointIndex ) !== -1 ) { 913 | 914 | // apply actual joint value here 915 | switch ( joint.type ) { 916 | 917 | case 'revolute': 918 | 919 | matrix.multiply( m1.makeRotationAxis( axis, THREE.Math.degToRad(value) ) ); 920 | break; 921 | 922 | case 'prismatic': 923 | 924 | matrix.multiply( m1.makeTranslation(axis.x * value, axis.y * value, axis.z * value ) ); 925 | break; 926 | 927 | default: 928 | 929 | console.warn( 'setJointValue: unknown joint type: ' + joint.type ); 930 | break; 931 | 932 | } 933 | 934 | } else { 935 | 936 | var m1 = new THREE.Matrix4(); 937 | 938 | switch ( transform.type ) { 939 | 940 | case 'matrix': 941 | 942 | matrix.multiply( transform.obj ); 943 | 944 | break; 945 | 946 | case 'translate': 947 | 948 | matrix.multiply( m1.makeTranslation( transform.obj.x, transform.obj.y, transform.obj.z ) ); 949 | 950 | break; 951 | 952 | case 'rotate': 953 | 954 | matrix.multiply( m1.makeRotationAxis( transform.obj, transform.angle ) ); 955 | 956 | break; 957 | 958 | } 959 | } 960 | } 961 | 962 | // apply the matrix to the threejs node 963 | var elementsFloat32Arr = matrix.elements; 964 | var elements = Array.prototype.slice.call( elementsFloat32Arr ); 965 | 966 | var elementsRowMajor = [ 967 | elements[ 0 ], 968 | elements[ 4 ], 969 | elements[ 8 ], 970 | elements[ 12 ], 971 | elements[ 1 ], 972 | elements[ 5 ], 973 | elements[ 9 ], 974 | elements[ 13 ], 975 | elements[ 2 ], 976 | elements[ 6 ], 977 | elements[ 10 ], 978 | elements[ 14 ], 979 | elements[ 3 ], 980 | elements[ 7 ], 981 | elements[ 11 ], 982 | elements[ 15 ] 983 | ]; 984 | 985 | threejsNode.matrix.set.apply( threejsNode.matrix, elementsRowMajor ); 986 | threejsNode.matrix.decompose( threejsNode.position, threejsNode.quaternion, threejsNode.scale ); 987 | } 988 | 989 | } else { 990 | 991 | console.log( 'setJointValue: joint ' + jointIndex + ' doesn\'t exist' ); 992 | 993 | } 994 | 995 | } 996 | 997 | }; 998 | 999 | var element = COLLADA.querySelector('scene instance_kinematics_scene'); 1000 | 1001 | if ( element ) { 1002 | 1003 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1004 | 1005 | var child = element.childNodes[ i ]; 1006 | 1007 | if ( child.nodeType != 1 ) continue; 1008 | 1009 | switch ( child.nodeName ) { 1010 | 1011 | case 'bind_joint_axis': 1012 | 1013 | var visualTarget = child.getAttribute( 'target' ).split( '/' ).pop(); 1014 | var axis = child.querySelector('axis param').textContent; 1015 | var jointIndex = parseInt( axis.split( 'joint' ).pop().split( '.' )[0] ); 1016 | var visualTargetElement = COLLADA.querySelector( '[sid="' + visualTarget + '"]' ); 1017 | 1018 | if ( visualTargetElement ) { 1019 | var parentVisualElement = visualTargetElement.parentElement; 1020 | _addToMap(jointIndex, parentVisualElement); 1021 | } 1022 | 1023 | break; 1024 | 1025 | default: 1026 | 1027 | break; 1028 | 1029 | } 1030 | 1031 | } 1032 | } 1033 | 1034 | }; 1035 | 1036 | function createSceneGraph ( node, parent ) { 1037 | 1038 | var obj = new THREE.Object3D(); 1039 | var skinned = false; 1040 | var skinController; 1041 | var morphController; 1042 | var i, j; 1043 | 1044 | // FIXME: controllers 1045 | 1046 | for ( i = 0; i < node.controllers.length; i ++ ) { 1047 | 1048 | var controller = controllers[ node.controllers[ i ].url ]; 1049 | 1050 | switch ( controller.type ) { 1051 | 1052 | case 'skin': 1053 | 1054 | if ( geometries[ controller.skin.source ] ) { 1055 | 1056 | var inst_geom = new InstanceGeometry(); 1057 | 1058 | inst_geom.url = controller.skin.source; 1059 | inst_geom.instance_material = node.controllers[ i ].instance_material; 1060 | 1061 | node.geometries.push( inst_geom ); 1062 | skinned = true; 1063 | skinController = node.controllers[ i ]; 1064 | 1065 | } else if ( controllers[ controller.skin.source ] ) { 1066 | 1067 | // urgh: controller can be chained 1068 | // handle the most basic case... 1069 | 1070 | var second = controllers[ controller.skin.source ]; 1071 | morphController = second; 1072 | // skinController = node.controllers[i]; 1073 | 1074 | if ( second.morph && geometries[ second.morph.source ] ) { 1075 | 1076 | var inst_geom = new InstanceGeometry(); 1077 | 1078 | inst_geom.url = second.morph.source; 1079 | inst_geom.instance_material = node.controllers[ i ].instance_material; 1080 | 1081 | node.geometries.push( inst_geom ); 1082 | 1083 | } 1084 | 1085 | } 1086 | 1087 | break; 1088 | 1089 | case 'morph': 1090 | 1091 | if ( geometries[ controller.morph.source ] ) { 1092 | 1093 | var inst_geom = new InstanceGeometry(); 1094 | 1095 | inst_geom.url = controller.morph.source; 1096 | inst_geom.instance_material = node.controllers[ i ].instance_material; 1097 | 1098 | node.geometries.push( inst_geom ); 1099 | morphController = node.controllers[ i ]; 1100 | 1101 | } 1102 | 1103 | console.log( 'ColladaLoader: Morph-controller partially supported.' ); 1104 | 1105 | default: 1106 | break; 1107 | 1108 | } 1109 | 1110 | } 1111 | 1112 | // geometries 1113 | 1114 | var double_sided_materials = {}; 1115 | 1116 | for ( i = 0; i < node.geometries.length; i ++ ) { 1117 | 1118 | var instance_geometry = node.geometries[i]; 1119 | var instance_materials = instance_geometry.instance_material; 1120 | var geometry = geometries[ instance_geometry.url ]; 1121 | var used_materials = {}; 1122 | var used_materials_array = []; 1123 | var num_materials = 0; 1124 | var first_material; 1125 | 1126 | if ( geometry ) { 1127 | 1128 | if ( !geometry.mesh || !geometry.mesh.primitives ) 1129 | continue; 1130 | 1131 | if ( obj.name.length === 0 ) { 1132 | 1133 | obj.name = geometry.id; 1134 | 1135 | } 1136 | 1137 | // collect used fx for this geometry-instance 1138 | 1139 | if ( instance_materials ) { 1140 | 1141 | for ( j = 0; j < instance_materials.length; j ++ ) { 1142 | 1143 | var instance_material = instance_materials[ j ]; 1144 | var mat = materials[ instance_material.target ]; 1145 | var effect_id = mat.instance_effect.url; 1146 | var shader = effects[ effect_id ].shader; 1147 | var material3js = shader.material; 1148 | 1149 | if ( geometry.doubleSided ) { 1150 | 1151 | if ( !( instance_material.symbol in double_sided_materials ) ) { 1152 | 1153 | var _copied_material = material3js.clone(); 1154 | _copied_material.side = THREE.DoubleSide; 1155 | double_sided_materials[ instance_material.symbol ] = _copied_material; 1156 | 1157 | } 1158 | 1159 | material3js = double_sided_materials[ instance_material.symbol ]; 1160 | 1161 | } 1162 | 1163 | material3js.opacity = !material3js.opacity ? 1 : material3js.opacity; 1164 | used_materials[ instance_material.symbol ] = num_materials; 1165 | used_materials_array.push( material3js ); 1166 | first_material = material3js; 1167 | first_material.name = mat.name === null || mat.name === '' ? mat.id : mat.name; 1168 | num_materials ++; 1169 | 1170 | } 1171 | 1172 | } 1173 | 1174 | var mesh; 1175 | var material = first_material || new THREE.MeshLambertMaterial( { color: 0xdddddd, side: geometry.doubleSided ? THREE.DoubleSide : THREE.FrontSide } ); 1176 | var geom = geometry.mesh.geometry3js; 1177 | 1178 | if ( num_materials > 1 ) { 1179 | 1180 | material = new THREE.MeshFaceMaterial( used_materials_array ); 1181 | 1182 | for ( j = 0; j < geom.faces.length; j ++ ) { 1183 | 1184 | var face = geom.faces[ j ]; 1185 | face.materialIndex = used_materials[ face.daeMaterial ] 1186 | 1187 | } 1188 | 1189 | } 1190 | 1191 | if ( skinController !== undefined ) { 1192 | 1193 | 1194 | applySkin( geom, skinController ); 1195 | 1196 | if ( geom.morphTargets.length > 0 ) { 1197 | 1198 | material.morphTargets = true; 1199 | material.skinning = false; 1200 | 1201 | } else { 1202 | 1203 | material.morphTargets = false; 1204 | material.skinning = true; 1205 | 1206 | } 1207 | 1208 | 1209 | mesh = new THREE.SkinnedMesh( geom, material, false ); 1210 | 1211 | 1212 | //mesh.skeleton = skinController.skeleton; 1213 | //mesh.skinController = controllers[ skinController.url ]; 1214 | //mesh.skinInstanceController = skinController; 1215 | mesh.name = 'skin_' + skins.length; 1216 | 1217 | 1218 | 1219 | //mesh.animationHandle.setKey(0); 1220 | skins.push( mesh ); 1221 | 1222 | } else if ( morphController !== undefined ) { 1223 | 1224 | createMorph( geom, morphController ); 1225 | 1226 | material.morphTargets = true; 1227 | 1228 | mesh = new THREE.Mesh( geom, material ); 1229 | mesh.name = 'morph_' + morphs.length; 1230 | 1231 | morphs.push( mesh ); 1232 | 1233 | } else { 1234 | 1235 | if ( geom.isLineStrip === true ) { 1236 | 1237 | mesh = new THREE.Line( geom ); 1238 | 1239 | } else { 1240 | 1241 | mesh = new THREE.Mesh( geom, material ); 1242 | 1243 | } 1244 | 1245 | } 1246 | 1247 | obj.add(mesh); 1248 | 1249 | } 1250 | 1251 | } 1252 | 1253 | for ( i = 0; i < node.cameras.length; i ++ ) { 1254 | 1255 | var instance_camera = node.cameras[i]; 1256 | var cparams = cameras[instance_camera.url]; 1257 | 1258 | var cam = new THREE.PerspectiveCamera(cparams.yfov, parseFloat(cparams.aspect_ratio), 1259 | parseFloat(cparams.znear), parseFloat(cparams.zfar)); 1260 | 1261 | obj.add(cam); 1262 | } 1263 | 1264 | for ( i = 0; i < node.lights.length; i ++ ) { 1265 | 1266 | var light = null; 1267 | var instance_light = node.lights[i]; 1268 | var lparams = lights[instance_light.url]; 1269 | 1270 | if ( lparams && lparams.technique ) { 1271 | 1272 | var color = lparams.color.getHex(); 1273 | var intensity = lparams.intensity; 1274 | var distance = lparams.distance; 1275 | var angle = lparams.falloff_angle; 1276 | var exponent; // Intentionally undefined, don't know what this is yet 1277 | 1278 | switch ( lparams.technique ) { 1279 | 1280 | case 'directional': 1281 | 1282 | light = new THREE.DirectionalLight( color, intensity, distance ); 1283 | light.position.set(0, 0, 1); 1284 | break; 1285 | 1286 | case 'point': 1287 | 1288 | light = new THREE.PointLight( color, intensity, distance ); 1289 | break; 1290 | 1291 | case 'spot': 1292 | 1293 | light = new THREE.SpotLight( color, intensity, distance, angle, exponent ); 1294 | light.position.set(0, 0, 1); 1295 | break; 1296 | 1297 | case 'ambient': 1298 | 1299 | light = new THREE.AmbientLight( color ); 1300 | break; 1301 | 1302 | } 1303 | 1304 | } 1305 | 1306 | if (light) { 1307 | obj.add(light); 1308 | } 1309 | } 1310 | 1311 | obj.name = node.name || node.id || ""; 1312 | obj.colladaId = node.id || ""; 1313 | obj.layer = node.layer || ""; 1314 | obj.matrix = node.matrix; 1315 | obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); 1316 | 1317 | if ( options.centerGeometry && obj.geometry ) { 1318 | 1319 | var delta = obj.geometry.center(); 1320 | delta.multiply( obj.scale ); 1321 | delta.applyQuaternion( obj.quaternion ); 1322 | 1323 | obj.position.sub( delta ); 1324 | 1325 | } 1326 | 1327 | for ( i = 0; i < node.nodes.length; i ++ ) { 1328 | 1329 | obj.add( createSceneGraph( node.nodes[i], node ) ); 1330 | 1331 | } 1332 | 1333 | return obj; 1334 | 1335 | }; 1336 | 1337 | function getJointId( skin, id ) { 1338 | 1339 | for ( var i = 0; i < skin.joints.length; i ++ ) { 1340 | 1341 | if ( skin.joints[ i ] === id ) { 1342 | 1343 | return i; 1344 | 1345 | } 1346 | 1347 | } 1348 | 1349 | }; 1350 | 1351 | function getLibraryNode( id ) { 1352 | 1353 | var nodes = COLLADA.querySelectorAll('library_nodes node'); 1354 | 1355 | for ( var i = 0; i < nodes.length; i++ ) { 1356 | 1357 | var attObj = nodes[i].attributes.getNamedItem('id'); 1358 | 1359 | if ( attObj && attObj.value === id ) { 1360 | 1361 | return nodes[i]; 1362 | 1363 | } 1364 | 1365 | } 1366 | 1367 | return undefined; 1368 | 1369 | }; 1370 | 1371 | function getChannelsForNode ( node ) { 1372 | 1373 | var channels = []; 1374 | var startTime = 1000000; 1375 | var endTime = -1000000; 1376 | 1377 | for ( var id in animations ) { 1378 | 1379 | var animation = animations[id]; 1380 | 1381 | for ( var i = 0; i < animation.channel.length; i ++ ) { 1382 | 1383 | var channel = animation.channel[i]; 1384 | var sampler = animation.sampler[i]; 1385 | var id = channel.target.split('/')[0]; 1386 | 1387 | if ( id == node.id ) { 1388 | 1389 | sampler.create(); 1390 | channel.sampler = sampler; 1391 | startTime = Math.min(startTime, sampler.startTime); 1392 | endTime = Math.max(endTime, sampler.endTime); 1393 | channels.push(channel); 1394 | 1395 | } 1396 | 1397 | } 1398 | 1399 | } 1400 | 1401 | if ( channels.length ) { 1402 | 1403 | node.startTime = startTime; 1404 | node.endTime = endTime; 1405 | 1406 | } 1407 | 1408 | return channels; 1409 | 1410 | }; 1411 | 1412 | function calcFrameDuration( node ) { 1413 | 1414 | var minT = 10000000; 1415 | 1416 | for ( var i = 0; i < node.channels.length; i ++ ) { 1417 | 1418 | var sampler = node.channels[i].sampler; 1419 | 1420 | for ( var j = 0; j < sampler.input.length - 1; j ++ ) { 1421 | 1422 | var t0 = sampler.input[ j ]; 1423 | var t1 = sampler.input[ j + 1 ]; 1424 | minT = Math.min( minT, t1 - t0 ); 1425 | 1426 | } 1427 | } 1428 | 1429 | return minT; 1430 | 1431 | }; 1432 | 1433 | function calcMatrixAt( node, t ) { 1434 | 1435 | var animated = {}; 1436 | 1437 | var i, j; 1438 | 1439 | for ( i = 0; i < node.channels.length; i ++ ) { 1440 | 1441 | var channel = node.channels[ i ]; 1442 | animated[ channel.sid ] = channel; 1443 | 1444 | } 1445 | 1446 | var matrix = new THREE.Matrix4(); 1447 | 1448 | for ( i = 0; i < node.transforms.length; i ++ ) { 1449 | 1450 | var transform = node.transforms[ i ]; 1451 | var channel = animated[ transform.sid ]; 1452 | 1453 | if ( channel !== undefined ) { 1454 | 1455 | var sampler = channel.sampler; 1456 | var value; 1457 | 1458 | for ( j = 0; j < sampler.input.length - 1; j ++ ) { 1459 | 1460 | if ( sampler.input[ j + 1 ] > t ) { 1461 | 1462 | value = sampler.output[ j ]; 1463 | //console.log(value.flatten) 1464 | break; 1465 | 1466 | } 1467 | 1468 | } 1469 | 1470 | if ( value !== undefined ) { 1471 | 1472 | if ( value instanceof THREE.Matrix4 ) { 1473 | 1474 | matrix.multiplyMatrices( matrix, value ); 1475 | 1476 | } else { 1477 | 1478 | // FIXME: handle other types 1479 | 1480 | matrix.multiplyMatrices( matrix, transform.matrix ); 1481 | 1482 | } 1483 | 1484 | } else { 1485 | 1486 | matrix.multiplyMatrices( matrix, transform.matrix ); 1487 | 1488 | } 1489 | 1490 | } else { 1491 | 1492 | matrix.multiplyMatrices( matrix, transform.matrix ); 1493 | 1494 | } 1495 | 1496 | } 1497 | 1498 | return matrix; 1499 | 1500 | }; 1501 | 1502 | function bakeAnimations ( node ) { 1503 | 1504 | if ( node.channels && node.channels.length ) { 1505 | 1506 | var keys = [], 1507 | sids = []; 1508 | 1509 | for ( var i = 0, il = node.channels.length; i < il; i ++ ) { 1510 | 1511 | var channel = node.channels[i], 1512 | fullSid = channel.fullSid, 1513 | sampler = channel.sampler, 1514 | input = sampler.input, 1515 | transform = node.getTransformBySid( channel.sid ), 1516 | member; 1517 | 1518 | if ( channel.arrIndices ) { 1519 | 1520 | member = []; 1521 | 1522 | for ( var j = 0, jl = channel.arrIndices.length; j < jl; j ++ ) { 1523 | 1524 | member[ j ] = getConvertedIndex( channel.arrIndices[ j ] ); 1525 | 1526 | } 1527 | 1528 | } else { 1529 | 1530 | member = getConvertedMember( channel.member ); 1531 | 1532 | } 1533 | 1534 | if ( transform ) { 1535 | 1536 | if ( sids.indexOf( fullSid ) === -1 ) { 1537 | 1538 | sids.push( fullSid ); 1539 | 1540 | } 1541 | 1542 | for ( var j = 0, jl = input.length; j < jl; j ++ ) { 1543 | 1544 | var time = input[j], 1545 | data = sampler.getData( transform.type, j, member ), 1546 | key = findKey( keys, time ); 1547 | 1548 | if ( !key ) { 1549 | 1550 | key = new Key( time ); 1551 | var timeNdx = findTimeNdx( keys, time ); 1552 | keys.splice( timeNdx === -1 ? keys.length : timeNdx, 0, key ); 1553 | 1554 | } 1555 | 1556 | key.addTarget( fullSid, transform, member, data ); 1557 | 1558 | } 1559 | 1560 | } else { 1561 | 1562 | console.log( 'Could not find transform "' + channel.sid + '" in node ' + node.id ); 1563 | 1564 | } 1565 | 1566 | } 1567 | 1568 | // post process 1569 | for ( var i = 0; i < sids.length; i ++ ) { 1570 | 1571 | var sid = sids[ i ]; 1572 | 1573 | for ( var j = 0; j < keys.length; j ++ ) { 1574 | 1575 | var key = keys[ j ]; 1576 | 1577 | if ( !key.hasTarget( sid ) ) { 1578 | 1579 | interpolateKeys( keys, key, j, sid ); 1580 | 1581 | } 1582 | 1583 | } 1584 | 1585 | } 1586 | 1587 | node.keys = keys; 1588 | node.sids = sids; 1589 | 1590 | } 1591 | 1592 | }; 1593 | 1594 | function findKey ( keys, time) { 1595 | 1596 | var retVal = null; 1597 | 1598 | for ( var i = 0, il = keys.length; i < il && retVal === null; i ++ ) { 1599 | 1600 | var key = keys[i]; 1601 | 1602 | if ( key.time === time ) { 1603 | 1604 | retVal = key; 1605 | 1606 | } else if ( key.time > time ) { 1607 | 1608 | break; 1609 | 1610 | } 1611 | 1612 | } 1613 | 1614 | return retVal; 1615 | 1616 | }; 1617 | 1618 | function findTimeNdx ( keys, time) { 1619 | 1620 | var ndx = -1; 1621 | 1622 | for ( var i = 0, il = keys.length; i < il && ndx === -1; i ++ ) { 1623 | 1624 | var key = keys[i]; 1625 | 1626 | if ( key.time >= time ) { 1627 | 1628 | ndx = i; 1629 | 1630 | } 1631 | 1632 | } 1633 | 1634 | return ndx; 1635 | 1636 | }; 1637 | 1638 | function interpolateKeys ( keys, key, ndx, fullSid ) { 1639 | 1640 | var prevKey = getPrevKeyWith( keys, fullSid, ndx ? ndx - 1 : 0 ), 1641 | nextKey = getNextKeyWith( keys, fullSid, ndx + 1 ); 1642 | 1643 | if ( prevKey && nextKey ) { 1644 | 1645 | var scale = (key.time - prevKey.time) / (nextKey.time - prevKey.time), 1646 | prevTarget = prevKey.getTarget( fullSid ), 1647 | nextData = nextKey.getTarget( fullSid ).data, 1648 | prevData = prevTarget.data, 1649 | data; 1650 | 1651 | if ( prevTarget.type === 'matrix' ) { 1652 | 1653 | data = prevData; 1654 | 1655 | } else if ( prevData.length ) { 1656 | 1657 | data = []; 1658 | 1659 | for ( var i = 0; i < prevData.length; ++ i ) { 1660 | 1661 | data[ i ] = prevData[ i ] + ( nextData[ i ] - prevData[ i ] ) * scale; 1662 | 1663 | } 1664 | 1665 | } else { 1666 | 1667 | data = prevData + ( nextData - prevData ) * scale; 1668 | 1669 | } 1670 | 1671 | key.addTarget( fullSid, prevTarget.transform, prevTarget.member, data ); 1672 | 1673 | } 1674 | 1675 | }; 1676 | 1677 | // Get next key with given sid 1678 | 1679 | function getNextKeyWith( keys, fullSid, ndx ) { 1680 | 1681 | for ( ; ndx < keys.length; ndx ++ ) { 1682 | 1683 | var key = keys[ ndx ]; 1684 | 1685 | if ( key.hasTarget( fullSid ) ) { 1686 | 1687 | return key; 1688 | 1689 | } 1690 | 1691 | } 1692 | 1693 | return null; 1694 | 1695 | }; 1696 | 1697 | // Get previous key with given sid 1698 | 1699 | function getPrevKeyWith( keys, fullSid, ndx ) { 1700 | 1701 | ndx = ndx >= 0 ? ndx : ndx + keys.length; 1702 | 1703 | for ( ; ndx >= 0; ndx -- ) { 1704 | 1705 | var key = keys[ ndx ]; 1706 | 1707 | if ( key.hasTarget( fullSid ) ) { 1708 | 1709 | return key; 1710 | 1711 | } 1712 | 1713 | } 1714 | 1715 | return null; 1716 | 1717 | }; 1718 | 1719 | function _Image() { 1720 | 1721 | this.id = ""; 1722 | this.init_from = ""; 1723 | 1724 | }; 1725 | 1726 | _Image.prototype.parse = function(element) { 1727 | 1728 | this.id = element.getAttribute('id'); 1729 | 1730 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1731 | 1732 | var child = element.childNodes[ i ]; 1733 | 1734 | if ( child.nodeName === 'init_from' ) { 1735 | 1736 | this.init_from = child.textContent; 1737 | 1738 | } 1739 | 1740 | } 1741 | 1742 | return this; 1743 | 1744 | }; 1745 | 1746 | function Controller() { 1747 | 1748 | this.id = ""; 1749 | this.name = ""; 1750 | this.type = ""; 1751 | this.skin = null; 1752 | this.morph = null; 1753 | 1754 | }; 1755 | 1756 | Controller.prototype.parse = function( element ) { 1757 | 1758 | this.id = element.getAttribute('id'); 1759 | this.name = element.getAttribute('name'); 1760 | this.type = "none"; 1761 | 1762 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1763 | 1764 | var child = element.childNodes[ i ]; 1765 | 1766 | switch ( child.nodeName ) { 1767 | 1768 | case 'skin': 1769 | 1770 | this.skin = (new Skin()).parse(child); 1771 | this.type = child.nodeName; 1772 | break; 1773 | 1774 | case 'morph': 1775 | 1776 | this.morph = (new Morph()).parse(child); 1777 | this.type = child.nodeName; 1778 | break; 1779 | 1780 | default: 1781 | break; 1782 | 1783 | } 1784 | } 1785 | 1786 | return this; 1787 | 1788 | }; 1789 | 1790 | function Morph() { 1791 | 1792 | this.method = null; 1793 | this.source = null; 1794 | this.targets = null; 1795 | this.weights = null; 1796 | 1797 | }; 1798 | 1799 | Morph.prototype.parse = function( element ) { 1800 | 1801 | var sources = {}; 1802 | var inputs = []; 1803 | var i; 1804 | 1805 | this.method = element.getAttribute( 'method' ); 1806 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); 1807 | 1808 | for ( i = 0; i < element.childNodes.length; i ++ ) { 1809 | 1810 | var child = element.childNodes[ i ]; 1811 | if ( child.nodeType != 1 ) continue; 1812 | 1813 | switch ( child.nodeName ) { 1814 | 1815 | case 'source': 1816 | 1817 | var source = ( new Source() ).parse( child ); 1818 | sources[ source.id ] = source; 1819 | break; 1820 | 1821 | case 'targets': 1822 | 1823 | inputs = this.parseInputs( child ); 1824 | break; 1825 | 1826 | default: 1827 | 1828 | console.log( child.nodeName ); 1829 | break; 1830 | 1831 | } 1832 | 1833 | } 1834 | 1835 | for ( i = 0; i < inputs.length; i ++ ) { 1836 | 1837 | var input = inputs[ i ]; 1838 | var source = sources[ input.source ]; 1839 | 1840 | switch ( input.semantic ) { 1841 | 1842 | case 'MORPH_TARGET': 1843 | 1844 | this.targets = source.read(); 1845 | break; 1846 | 1847 | case 'MORPH_WEIGHT': 1848 | 1849 | this.weights = source.read(); 1850 | break; 1851 | 1852 | default: 1853 | break; 1854 | 1855 | } 1856 | } 1857 | 1858 | return this; 1859 | 1860 | }; 1861 | 1862 | Morph.prototype.parseInputs = function(element) { 1863 | 1864 | var inputs = []; 1865 | 1866 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1867 | 1868 | var child = element.childNodes[i]; 1869 | if ( child.nodeType != 1) continue; 1870 | 1871 | switch ( child.nodeName ) { 1872 | 1873 | case 'input': 1874 | 1875 | inputs.push( (new Input()).parse(child) ); 1876 | break; 1877 | 1878 | default: 1879 | break; 1880 | } 1881 | } 1882 | 1883 | return inputs; 1884 | 1885 | }; 1886 | 1887 | function Skin() { 1888 | 1889 | this.source = ""; 1890 | this.bindShapeMatrix = null; 1891 | this.invBindMatrices = []; 1892 | this.joints = []; 1893 | this.weights = []; 1894 | 1895 | }; 1896 | 1897 | Skin.prototype.parse = function( element ) { 1898 | 1899 | var sources = {}; 1900 | var joints, weights; 1901 | 1902 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); 1903 | this.invBindMatrices = []; 1904 | this.joints = []; 1905 | this.weights = []; 1906 | 1907 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1908 | 1909 | var child = element.childNodes[i]; 1910 | if ( child.nodeType != 1 ) continue; 1911 | 1912 | switch ( child.nodeName ) { 1913 | 1914 | case 'bind_shape_matrix': 1915 | 1916 | var f = _floats(child.textContent); 1917 | this.bindShapeMatrix = getConvertedMat4( f ); 1918 | break; 1919 | 1920 | case 'source': 1921 | 1922 | var src = new Source().parse(child); 1923 | sources[ src.id ] = src; 1924 | break; 1925 | 1926 | case 'joints': 1927 | 1928 | joints = child; 1929 | break; 1930 | 1931 | case 'vertex_weights': 1932 | 1933 | weights = child; 1934 | break; 1935 | 1936 | default: 1937 | 1938 | console.log( child.nodeName ); 1939 | break; 1940 | 1941 | } 1942 | } 1943 | 1944 | this.parseJoints( joints, sources ); 1945 | this.parseWeights( weights, sources ); 1946 | 1947 | return this; 1948 | 1949 | }; 1950 | 1951 | Skin.prototype.parseJoints = function ( element, sources ) { 1952 | 1953 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1954 | 1955 | var child = element.childNodes[ i ]; 1956 | if ( child.nodeType != 1 ) continue; 1957 | 1958 | switch ( child.nodeName ) { 1959 | 1960 | case 'input': 1961 | 1962 | var input = ( new Input() ).parse( child ); 1963 | var source = sources[ input.source ]; 1964 | 1965 | if ( input.semantic === 'JOINT' ) { 1966 | 1967 | this.joints = source.read(); 1968 | 1969 | } else if ( input.semantic === 'INV_BIND_MATRIX' ) { 1970 | 1971 | this.invBindMatrices = source.read(); 1972 | 1973 | } 1974 | 1975 | break; 1976 | 1977 | default: 1978 | break; 1979 | } 1980 | 1981 | } 1982 | 1983 | }; 1984 | 1985 | Skin.prototype.parseWeights = function ( element, sources ) { 1986 | 1987 | var v, vcount, inputs = []; 1988 | 1989 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 1990 | 1991 | var child = element.childNodes[ i ]; 1992 | if ( child.nodeType != 1 ) continue; 1993 | 1994 | switch ( child.nodeName ) { 1995 | 1996 | case 'input': 1997 | 1998 | inputs.push( ( new Input() ).parse( child ) ); 1999 | break; 2000 | 2001 | case 'v': 2002 | 2003 | v = _ints( child.textContent ); 2004 | break; 2005 | 2006 | case 'vcount': 2007 | 2008 | vcount = _ints( child.textContent ); 2009 | break; 2010 | 2011 | default: 2012 | break; 2013 | 2014 | } 2015 | 2016 | } 2017 | 2018 | var index = 0; 2019 | 2020 | for ( var i = 0; i < vcount.length; i ++ ) { 2021 | 2022 | var numBones = vcount[i]; 2023 | var vertex_weights = []; 2024 | 2025 | for ( var j = 0; j < numBones; j ++ ) { 2026 | 2027 | var influence = {}; 2028 | 2029 | for ( var k = 0; k < inputs.length; k ++ ) { 2030 | 2031 | var input = inputs[ k ]; 2032 | var value = v[ index + input.offset ]; 2033 | 2034 | switch ( input.semantic ) { 2035 | 2036 | case 'JOINT': 2037 | 2038 | influence.joint = value;//this.joints[value]; 2039 | break; 2040 | 2041 | case 'WEIGHT': 2042 | 2043 | influence.weight = sources[ input.source ].data[ value ]; 2044 | break; 2045 | 2046 | default: 2047 | break; 2048 | 2049 | } 2050 | 2051 | } 2052 | 2053 | vertex_weights.push( influence ); 2054 | index += inputs.length; 2055 | } 2056 | 2057 | for ( var j = 0; j < vertex_weights.length; j ++ ) { 2058 | 2059 | vertex_weights[ j ].index = i; 2060 | 2061 | } 2062 | 2063 | this.weights.push( vertex_weights ); 2064 | 2065 | } 2066 | 2067 | }; 2068 | 2069 | function VisualScene () { 2070 | 2071 | this.id = ""; 2072 | this.name = ""; 2073 | this.nodes = []; 2074 | this.scene = new THREE.Group(); 2075 | 2076 | }; 2077 | 2078 | VisualScene.prototype.getChildById = function( id, recursive ) { 2079 | 2080 | for ( var i = 0; i < this.nodes.length; i ++ ) { 2081 | 2082 | var node = this.nodes[ i ].getChildById( id, recursive ); 2083 | 2084 | if ( node ) { 2085 | 2086 | return node; 2087 | 2088 | } 2089 | 2090 | } 2091 | 2092 | return null; 2093 | 2094 | }; 2095 | 2096 | VisualScene.prototype.getChildBySid = function( sid, recursive ) { 2097 | 2098 | for ( var i = 0; i < this.nodes.length; i ++ ) { 2099 | 2100 | var node = this.nodes[ i ].getChildBySid( sid, recursive ); 2101 | 2102 | if ( node ) { 2103 | 2104 | return node; 2105 | 2106 | } 2107 | 2108 | } 2109 | 2110 | return null; 2111 | 2112 | }; 2113 | 2114 | VisualScene.prototype.parse = function( element ) { 2115 | 2116 | this.id = element.getAttribute( 'id' ); 2117 | this.name = element.getAttribute( 'name' ); 2118 | this.nodes = []; 2119 | 2120 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2121 | 2122 | var child = element.childNodes[ i ]; 2123 | if ( child.nodeType != 1 ) continue; 2124 | 2125 | switch ( child.nodeName ) { 2126 | 2127 | case 'node': 2128 | 2129 | this.nodes.push( ( new Node() ).parse( child ) ); 2130 | break; 2131 | 2132 | default: 2133 | break; 2134 | 2135 | } 2136 | 2137 | } 2138 | 2139 | return this; 2140 | 2141 | }; 2142 | 2143 | function Node() { 2144 | 2145 | this.id = ""; 2146 | this.name = ""; 2147 | this.sid = ""; 2148 | this.nodes = []; 2149 | this.controllers = []; 2150 | this.transforms = []; 2151 | this.geometries = []; 2152 | this.channels = []; 2153 | this.matrix = new THREE.Matrix4(); 2154 | 2155 | }; 2156 | 2157 | Node.prototype.getChannelForTransform = function( transformSid ) { 2158 | 2159 | for ( var i = 0; i < this.channels.length; i ++ ) { 2160 | 2161 | var channel = this.channels[i]; 2162 | var parts = channel.target.split('/'); 2163 | var id = parts.shift(); 2164 | var sid = parts.shift(); 2165 | var dotSyntax = (sid.indexOf(".") >= 0); 2166 | var arrSyntax = (sid.indexOf("(") >= 0); 2167 | var arrIndices; 2168 | var member; 2169 | 2170 | if ( dotSyntax ) { 2171 | 2172 | parts = sid.split("."); 2173 | sid = parts.shift(); 2174 | member = parts.shift(); 2175 | 2176 | } else if ( arrSyntax ) { 2177 | 2178 | arrIndices = sid.split("("); 2179 | sid = arrIndices.shift(); 2180 | 2181 | for ( var j = 0; j < arrIndices.length; j ++ ) { 2182 | 2183 | arrIndices[ j ] = parseInt( arrIndices[ j ].replace( /\)/, '' ) ); 2184 | 2185 | } 2186 | 2187 | } 2188 | 2189 | if ( sid === transformSid ) { 2190 | 2191 | channel.info = { sid: sid, dotSyntax: dotSyntax, arrSyntax: arrSyntax, arrIndices: arrIndices }; 2192 | return channel; 2193 | 2194 | } 2195 | 2196 | } 2197 | 2198 | return null; 2199 | 2200 | }; 2201 | 2202 | Node.prototype.getChildById = function ( id, recursive ) { 2203 | 2204 | if ( this.id === id ) { 2205 | 2206 | return this; 2207 | 2208 | } 2209 | 2210 | if ( recursive ) { 2211 | 2212 | for ( var i = 0; i < this.nodes.length; i ++ ) { 2213 | 2214 | var n = this.nodes[ i ].getChildById( id, recursive ); 2215 | 2216 | if ( n ) { 2217 | 2218 | return n; 2219 | 2220 | } 2221 | 2222 | } 2223 | 2224 | } 2225 | 2226 | return null; 2227 | 2228 | }; 2229 | 2230 | Node.prototype.getChildBySid = function ( sid, recursive ) { 2231 | 2232 | if ( this.sid === sid ) { 2233 | 2234 | return this; 2235 | 2236 | } 2237 | 2238 | if ( recursive ) { 2239 | 2240 | for ( var i = 0; i < this.nodes.length; i ++ ) { 2241 | 2242 | var n = this.nodes[ i ].getChildBySid( sid, recursive ); 2243 | 2244 | if ( n ) { 2245 | 2246 | return n; 2247 | 2248 | } 2249 | 2250 | } 2251 | } 2252 | 2253 | return null; 2254 | 2255 | }; 2256 | 2257 | Node.prototype.getTransformBySid = function ( sid ) { 2258 | 2259 | for ( var i = 0; i < this.transforms.length; i ++ ) { 2260 | 2261 | if ( this.transforms[ i ].sid === sid ) return this.transforms[ i ]; 2262 | 2263 | } 2264 | 2265 | return null; 2266 | 2267 | }; 2268 | 2269 | Node.prototype.parse = function( element ) { 2270 | 2271 | var url; 2272 | 2273 | this.id = element.getAttribute('id'); 2274 | this.sid = element.getAttribute('sid'); 2275 | this.name = element.getAttribute('name'); 2276 | this.type = element.getAttribute('type'); 2277 | this.layer = element.getAttribute('layer'); 2278 | 2279 | this.type = this.type === 'JOINT' ? this.type : 'NODE'; 2280 | 2281 | this.nodes = []; 2282 | this.transforms = []; 2283 | this.geometries = []; 2284 | this.cameras = []; 2285 | this.lights = []; 2286 | this.controllers = []; 2287 | this.matrix = new THREE.Matrix4(); 2288 | 2289 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2290 | 2291 | var child = element.childNodes[ i ]; 2292 | if ( child.nodeType != 1 ) continue; 2293 | 2294 | switch ( child.nodeName ) { 2295 | 2296 | case 'node': 2297 | 2298 | this.nodes.push( ( new Node() ).parse( child ) ); 2299 | break; 2300 | 2301 | case 'instance_camera': 2302 | 2303 | this.cameras.push( ( new InstanceCamera() ).parse( child ) ); 2304 | break; 2305 | 2306 | case 'instance_controller': 2307 | 2308 | this.controllers.push( ( new InstanceController() ).parse( child ) ); 2309 | break; 2310 | 2311 | case 'instance_geometry': 2312 | 2313 | this.geometries.push( ( new InstanceGeometry() ).parse( child ) ); 2314 | break; 2315 | 2316 | case 'instance_light': 2317 | 2318 | this.lights.push( ( new InstanceLight() ).parse( child ) ); 2319 | break; 2320 | 2321 | case 'instance_node': 2322 | 2323 | url = child.getAttribute( 'url' ).replace( /^#/, '' ); 2324 | var iNode = getLibraryNode( url ); 2325 | 2326 | if ( iNode ) { 2327 | 2328 | this.nodes.push( ( new Node() ).parse( iNode )) ; 2329 | 2330 | } 2331 | 2332 | break; 2333 | 2334 | case 'rotate': 2335 | case 'translate': 2336 | case 'scale': 2337 | case 'matrix': 2338 | case 'lookat': 2339 | case 'skew': 2340 | 2341 | this.transforms.push( ( new Transform() ).parse( child ) ); 2342 | break; 2343 | 2344 | case 'extra': 2345 | break; 2346 | 2347 | default: 2348 | 2349 | console.log( child.nodeName ); 2350 | break; 2351 | 2352 | } 2353 | 2354 | } 2355 | 2356 | this.channels = getChannelsForNode( this ); 2357 | bakeAnimations( this ); 2358 | 2359 | this.updateMatrix(); 2360 | 2361 | return this; 2362 | 2363 | }; 2364 | 2365 | Node.prototype.updateMatrix = function () { 2366 | 2367 | this.matrix.identity(); 2368 | 2369 | for ( var i = 0; i < this.transforms.length; i ++ ) { 2370 | 2371 | this.transforms[ i ].apply( this.matrix ); 2372 | 2373 | } 2374 | 2375 | }; 2376 | 2377 | function Transform () { 2378 | 2379 | this.sid = ""; 2380 | this.type = ""; 2381 | this.data = []; 2382 | this.obj = null; 2383 | 2384 | }; 2385 | 2386 | Transform.prototype.parse = function ( element ) { 2387 | 2388 | this.sid = element.getAttribute( 'sid' ); 2389 | this.type = element.nodeName; 2390 | this.data = _floats( element.textContent ); 2391 | this.convert(); 2392 | 2393 | return this; 2394 | 2395 | }; 2396 | 2397 | Transform.prototype.convert = function () { 2398 | 2399 | switch ( this.type ) { 2400 | 2401 | case 'matrix': 2402 | 2403 | this.obj = getConvertedMat4( this.data ); 2404 | break; 2405 | 2406 | case 'rotate': 2407 | 2408 | this.angle = THREE.Math.degToRad( this.data[3] ); 2409 | 2410 | case 'translate': 2411 | 2412 | fixCoords( this.data, -1 ); 2413 | this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); 2414 | break; 2415 | 2416 | case 'scale': 2417 | 2418 | fixCoords( this.data, 1 ); 2419 | this.obj = new THREE.Vector3( this.data[ 0 ], this.data[ 1 ], this.data[ 2 ] ); 2420 | break; 2421 | 2422 | default: 2423 | console.log( 'Can not convert Transform of type ' + this.type ); 2424 | break; 2425 | 2426 | } 2427 | 2428 | }; 2429 | 2430 | Transform.prototype.apply = function () { 2431 | 2432 | var m1 = new THREE.Matrix4(); 2433 | 2434 | return function ( matrix ) { 2435 | 2436 | switch ( this.type ) { 2437 | 2438 | case 'matrix': 2439 | 2440 | matrix.multiply( this.obj ); 2441 | 2442 | break; 2443 | 2444 | case 'translate': 2445 | 2446 | matrix.multiply( m1.makeTranslation( this.obj.x, this.obj.y, this.obj.z ) ); 2447 | 2448 | break; 2449 | 2450 | case 'rotate': 2451 | 2452 | matrix.multiply( m1.makeRotationAxis( this.obj, this.angle ) ); 2453 | 2454 | break; 2455 | 2456 | case 'scale': 2457 | 2458 | matrix.scale( this.obj ); 2459 | 2460 | break; 2461 | 2462 | } 2463 | 2464 | }; 2465 | 2466 | }(); 2467 | 2468 | Transform.prototype.update = function ( data, member ) { 2469 | 2470 | var members = [ 'X', 'Y', 'Z', 'ANGLE' ]; 2471 | 2472 | switch ( this.type ) { 2473 | 2474 | case 'matrix': 2475 | 2476 | if ( ! member ) { 2477 | 2478 | this.obj.copy( data ); 2479 | 2480 | } else if ( member.length === 1 ) { 2481 | 2482 | switch ( member[ 0 ] ) { 2483 | 2484 | case 0: 2485 | 2486 | this.obj.n11 = data[ 0 ]; 2487 | this.obj.n21 = data[ 1 ]; 2488 | this.obj.n31 = data[ 2 ]; 2489 | this.obj.n41 = data[ 3 ]; 2490 | 2491 | break; 2492 | 2493 | case 1: 2494 | 2495 | this.obj.n12 = data[ 0 ]; 2496 | this.obj.n22 = data[ 1 ]; 2497 | this.obj.n32 = data[ 2 ]; 2498 | this.obj.n42 = data[ 3 ]; 2499 | 2500 | break; 2501 | 2502 | case 2: 2503 | 2504 | this.obj.n13 = data[ 0 ]; 2505 | this.obj.n23 = data[ 1 ]; 2506 | this.obj.n33 = data[ 2 ]; 2507 | this.obj.n43 = data[ 3 ]; 2508 | 2509 | break; 2510 | 2511 | case 3: 2512 | 2513 | this.obj.n14 = data[ 0 ]; 2514 | this.obj.n24 = data[ 1 ]; 2515 | this.obj.n34 = data[ 2 ]; 2516 | this.obj.n44 = data[ 3 ]; 2517 | 2518 | break; 2519 | 2520 | } 2521 | 2522 | } else if ( member.length === 2 ) { 2523 | 2524 | var propName = 'n' + ( member[ 0 ] + 1 ) + ( member[ 1 ] + 1 ); 2525 | this.obj[ propName ] = data; 2526 | 2527 | } else { 2528 | 2529 | console.log('Incorrect addressing of matrix in transform.'); 2530 | 2531 | } 2532 | 2533 | break; 2534 | 2535 | case 'translate': 2536 | case 'scale': 2537 | 2538 | if ( Object.prototype.toString.call( member ) === '[object Array]' ) { 2539 | 2540 | member = members[ member[ 0 ] ]; 2541 | 2542 | } 2543 | 2544 | switch ( member ) { 2545 | 2546 | case 'X': 2547 | 2548 | this.obj.x = data; 2549 | break; 2550 | 2551 | case 'Y': 2552 | 2553 | this.obj.y = data; 2554 | break; 2555 | 2556 | case 'Z': 2557 | 2558 | this.obj.z = data; 2559 | break; 2560 | 2561 | default: 2562 | 2563 | this.obj.x = data[ 0 ]; 2564 | this.obj.y = data[ 1 ]; 2565 | this.obj.z = data[ 2 ]; 2566 | break; 2567 | 2568 | } 2569 | 2570 | break; 2571 | 2572 | case 'rotate': 2573 | 2574 | if ( Object.prototype.toString.call( member ) === '[object Array]' ) { 2575 | 2576 | member = members[ member[ 0 ] ]; 2577 | 2578 | } 2579 | 2580 | switch ( member ) { 2581 | 2582 | case 'X': 2583 | 2584 | this.obj.x = data; 2585 | break; 2586 | 2587 | case 'Y': 2588 | 2589 | this.obj.y = data; 2590 | break; 2591 | 2592 | case 'Z': 2593 | 2594 | this.obj.z = data; 2595 | break; 2596 | 2597 | case 'ANGLE': 2598 | 2599 | this.angle = THREE.Math.degToRad( data ); 2600 | break; 2601 | 2602 | default: 2603 | 2604 | this.obj.x = data[ 0 ]; 2605 | this.obj.y = data[ 1 ]; 2606 | this.obj.z = data[ 2 ]; 2607 | this.angle = THREE.Math.degToRad( data[ 3 ] ); 2608 | break; 2609 | 2610 | } 2611 | break; 2612 | 2613 | } 2614 | 2615 | }; 2616 | 2617 | function InstanceController() { 2618 | 2619 | this.url = ""; 2620 | this.skeleton = []; 2621 | this.instance_material = []; 2622 | 2623 | }; 2624 | 2625 | InstanceController.prototype.parse = function ( element ) { 2626 | 2627 | this.url = element.getAttribute('url').replace(/^#/, ''); 2628 | this.skeleton = []; 2629 | this.instance_material = []; 2630 | 2631 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2632 | 2633 | var child = element.childNodes[ i ]; 2634 | if ( child.nodeType !== 1 ) continue; 2635 | 2636 | switch ( child.nodeName ) { 2637 | 2638 | case 'skeleton': 2639 | 2640 | this.skeleton.push( child.textContent.replace(/^#/, '') ); 2641 | break; 2642 | 2643 | case 'bind_material': 2644 | 2645 | var instances = child.querySelectorAll('instance_material'); 2646 | 2647 | for ( var j = 0; j < instances.length; j ++ ) { 2648 | 2649 | var instance = instances[j]; 2650 | this.instance_material.push( (new InstanceMaterial()).parse(instance) ); 2651 | 2652 | } 2653 | 2654 | 2655 | break; 2656 | 2657 | case 'extra': 2658 | break; 2659 | 2660 | default: 2661 | break; 2662 | 2663 | } 2664 | } 2665 | 2666 | return this; 2667 | 2668 | }; 2669 | 2670 | function InstanceMaterial () { 2671 | 2672 | this.symbol = ""; 2673 | this.target = ""; 2674 | 2675 | }; 2676 | 2677 | InstanceMaterial.prototype.parse = function ( element ) { 2678 | 2679 | this.symbol = element.getAttribute('symbol'); 2680 | this.target = element.getAttribute('target').replace(/^#/, ''); 2681 | return this; 2682 | 2683 | }; 2684 | 2685 | function InstanceGeometry() { 2686 | 2687 | this.url = ""; 2688 | this.instance_material = []; 2689 | 2690 | }; 2691 | 2692 | InstanceGeometry.prototype.parse = function ( element ) { 2693 | 2694 | this.url = element.getAttribute('url').replace(/^#/, ''); 2695 | this.instance_material = []; 2696 | 2697 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2698 | 2699 | var child = element.childNodes[i]; 2700 | if ( child.nodeType != 1 ) continue; 2701 | 2702 | if ( child.nodeName === 'bind_material' ) { 2703 | 2704 | var instances = child.querySelectorAll('instance_material'); 2705 | 2706 | for ( var j = 0; j < instances.length; j ++ ) { 2707 | 2708 | var instance = instances[j]; 2709 | this.instance_material.push( (new InstanceMaterial()).parse(instance) ); 2710 | 2711 | } 2712 | 2713 | break; 2714 | 2715 | } 2716 | 2717 | } 2718 | 2719 | return this; 2720 | 2721 | }; 2722 | 2723 | function Geometry() { 2724 | 2725 | this.id = ""; 2726 | this.mesh = null; 2727 | 2728 | }; 2729 | 2730 | Geometry.prototype.parse = function ( element ) { 2731 | 2732 | this.id = element.getAttribute('id'); 2733 | 2734 | extractDoubleSided( this, element ); 2735 | 2736 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2737 | 2738 | var child = element.childNodes[i]; 2739 | 2740 | switch ( child.nodeName ) { 2741 | 2742 | case 'mesh': 2743 | 2744 | this.mesh = (new Mesh(this)).parse(child); 2745 | break; 2746 | 2747 | case 'extra': 2748 | 2749 | // console.log( child ); 2750 | break; 2751 | 2752 | default: 2753 | break; 2754 | } 2755 | } 2756 | 2757 | return this; 2758 | 2759 | }; 2760 | 2761 | function Mesh( geometry ) { 2762 | 2763 | this.geometry = geometry.id; 2764 | this.primitives = []; 2765 | this.vertices = null; 2766 | this.geometry3js = null; 2767 | 2768 | }; 2769 | 2770 | Mesh.prototype.parse = function ( element ) { 2771 | 2772 | this.primitives = []; 2773 | 2774 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 2775 | 2776 | var child = element.childNodes[ i ]; 2777 | 2778 | switch ( child.nodeName ) { 2779 | 2780 | case 'source': 2781 | 2782 | _source( child ); 2783 | break; 2784 | 2785 | case 'vertices': 2786 | 2787 | this.vertices = ( new Vertices() ).parse( child ); 2788 | break; 2789 | 2790 | case 'linestrips': 2791 | 2792 | this.primitives.push( ( new LineStrips().parse( child ) ) ); 2793 | break; 2794 | 2795 | case 'triangles': 2796 | 2797 | this.primitives.push( ( new Triangles().parse( child ) ) ); 2798 | break; 2799 | 2800 | case 'polygons': 2801 | 2802 | this.primitives.push( ( new Polygons().parse( child ) ) ); 2803 | break; 2804 | 2805 | case 'polylist': 2806 | 2807 | this.primitives.push( ( new Polylist().parse( child ) ) ); 2808 | break; 2809 | 2810 | default: 2811 | break; 2812 | 2813 | } 2814 | 2815 | } 2816 | 2817 | this.geometry3js = new THREE.Geometry(); 2818 | 2819 | if ( this.vertices === null ) { 2820 | 2821 | // TODO (mrdoob): Study case when this is null (carrier.dae) 2822 | 2823 | return this; 2824 | 2825 | } 2826 | 2827 | var vertexData = sources[ this.vertices.input['POSITION'].source ].data; 2828 | 2829 | for ( var i = 0; i < vertexData.length; i += 3 ) { 2830 | 2831 | this.geometry3js.vertices.push( getConvertedVec3( vertexData, i ).clone() ); 2832 | 2833 | } 2834 | 2835 | for ( var i = 0; i < this.primitives.length; i ++ ) { 2836 | 2837 | var primitive = this.primitives[ i ]; 2838 | primitive.setVertices( this.vertices ); 2839 | this.handlePrimitive( primitive, this.geometry3js ); 2840 | 2841 | } 2842 | 2843 | if ( this.geometry3js.calcNormals ) { 2844 | 2845 | this.geometry3js.computeVertexNormals(); 2846 | delete this.geometry3js.calcNormals; 2847 | 2848 | } 2849 | 2850 | return this; 2851 | 2852 | }; 2853 | 2854 | Mesh.prototype.handlePrimitive = function ( primitive, geom ) { 2855 | 2856 | if ( primitive instanceof LineStrips ) { 2857 | 2858 | // TODO: Handle indices. Maybe easier with BufferGeometry? 2859 | 2860 | geom.isLineStrip = true; 2861 | return; 2862 | 2863 | } 2864 | 2865 | var j, k, pList = primitive.p, inputs = primitive.inputs; 2866 | var input, index, idx32; 2867 | var source, numParams; 2868 | var vcIndex = 0, vcount = 3, maxOffset = 0; 2869 | var texture_sets = []; 2870 | 2871 | for ( j = 0; j < inputs.length; j ++ ) { 2872 | 2873 | input = inputs[ j ]; 2874 | 2875 | var offset = input.offset + 1; 2876 | maxOffset = (maxOffset < offset) ? offset : maxOffset; 2877 | 2878 | switch ( input.semantic ) { 2879 | 2880 | case 'TEXCOORD': 2881 | texture_sets.push( input.set ); 2882 | break; 2883 | 2884 | } 2885 | 2886 | } 2887 | 2888 | for ( var pCount = 0; pCount < pList.length; ++ pCount ) { 2889 | 2890 | var p = pList[ pCount ], i = 0; 2891 | 2892 | while ( i < p.length ) { 2893 | 2894 | var vs = []; 2895 | var ns = []; 2896 | var ts = null; 2897 | var cs = []; 2898 | 2899 | if ( primitive.vcount ) { 2900 | 2901 | vcount = primitive.vcount.length ? primitive.vcount[ vcIndex ++ ] : primitive.vcount; 2902 | 2903 | } else { 2904 | 2905 | vcount = p.length / maxOffset; 2906 | 2907 | } 2908 | 2909 | 2910 | for ( j = 0; j < vcount; j ++ ) { 2911 | 2912 | for ( k = 0; k < inputs.length; k ++ ) { 2913 | 2914 | input = inputs[ k ]; 2915 | source = sources[ input.source ]; 2916 | 2917 | index = p[ i + ( j * maxOffset ) + input.offset ]; 2918 | numParams = source.accessor.params.length; 2919 | idx32 = index * numParams; 2920 | 2921 | switch ( input.semantic ) { 2922 | 2923 | case 'VERTEX': 2924 | 2925 | vs.push( index ); 2926 | 2927 | break; 2928 | 2929 | case 'NORMAL': 2930 | 2931 | ns.push( getConvertedVec3( source.data, idx32 ) ); 2932 | 2933 | break; 2934 | 2935 | case 'TEXCOORD': 2936 | 2937 | ts = ts || { }; 2938 | if ( ts[ input.set ] === undefined ) ts[ input.set ] = []; 2939 | // invert the V 2940 | ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], source.data[ idx32 + 1 ] ) ); 2941 | 2942 | break; 2943 | 2944 | case 'COLOR': 2945 | 2946 | cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); 2947 | 2948 | break; 2949 | 2950 | default: 2951 | 2952 | break; 2953 | 2954 | } 2955 | 2956 | } 2957 | 2958 | } 2959 | 2960 | if ( ns.length === 0 ) { 2961 | 2962 | // check the vertices inputs 2963 | input = this.vertices.input.NORMAL; 2964 | 2965 | if ( input ) { 2966 | 2967 | source = sources[ input.source ]; 2968 | numParams = source.accessor.params.length; 2969 | 2970 | for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { 2971 | 2972 | ns.push( getConvertedVec3( source.data, vs[ ndx ] * numParams ) ); 2973 | 2974 | } 2975 | 2976 | } else { 2977 | 2978 | geom.calcNormals = true; 2979 | 2980 | } 2981 | 2982 | } 2983 | 2984 | if ( !ts ) { 2985 | 2986 | ts = { }; 2987 | // check the vertices inputs 2988 | input = this.vertices.input.TEXCOORD; 2989 | 2990 | if ( input ) { 2991 | 2992 | texture_sets.push( input.set ); 2993 | source = sources[ input.source ]; 2994 | numParams = source.accessor.params.length; 2995 | 2996 | for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { 2997 | 2998 | idx32 = vs[ ndx ] * numParams; 2999 | if ( ts[ input.set ] === undefined ) ts[ input.set ] = [ ]; 3000 | // invert the V 3001 | ts[ input.set ].push( new THREE.Vector2( source.data[ idx32 ], 1.0 - source.data[ idx32 + 1 ] ) ); 3002 | 3003 | } 3004 | 3005 | } 3006 | 3007 | } 3008 | 3009 | if ( cs.length === 0 ) { 3010 | 3011 | // check the vertices inputs 3012 | input = this.vertices.input.COLOR; 3013 | 3014 | if ( input ) { 3015 | 3016 | source = sources[ input.source ]; 3017 | numParams = source.accessor.params.length; 3018 | 3019 | for ( var ndx = 0, len = vs.length; ndx < len; ndx ++ ) { 3020 | 3021 | idx32 = vs[ ndx ] * numParams; 3022 | cs.push( new THREE.Color().setRGB( source.data[ idx32 ], source.data[ idx32 + 1 ], source.data[ idx32 + 2 ] ) ); 3023 | 3024 | } 3025 | 3026 | } 3027 | 3028 | } 3029 | 3030 | var face = null, faces = [], uv, uvArr; 3031 | 3032 | if ( vcount === 3 ) { 3033 | 3034 | faces.push( new THREE.Face3( vs[0], vs[1], vs[2], ns, cs.length ? cs : new THREE.Color() ) ); 3035 | 3036 | } else if ( vcount === 4 ) { 3037 | 3038 | faces.push( new THREE.Face3( vs[0], vs[1], vs[3], [ ns[0].clone(), ns[1].clone(), ns[3].clone() ], cs.length ? [ cs[0], cs[1], cs[3] ] : new THREE.Color() ) ); 3039 | 3040 | faces.push( new THREE.Face3( vs[1], vs[2], vs[3], [ ns[1].clone(), ns[2].clone(), ns[3].clone() ], cs.length ? [ cs[1], cs[2], cs[3] ] : new THREE.Color() ) ); 3041 | 3042 | } else if ( vcount > 4 && options.subdivideFaces ) { 3043 | 3044 | var clr = cs.length ? cs : new THREE.Color(), 3045 | vec1, vec2, vec3, v1, v2, norm; 3046 | 3047 | // subdivide into multiple Face3s 3048 | 3049 | for ( k = 1; k < vcount - 1; ) { 3050 | 3051 | faces.push( new THREE.Face3( vs[0], vs[k], vs[k + 1], [ ns[0].clone(), ns[k ++].clone(), ns[k].clone() ], clr ) ); 3052 | 3053 | } 3054 | 3055 | } 3056 | 3057 | if ( faces.length ) { 3058 | 3059 | for ( var ndx = 0, len = faces.length; ndx < len; ndx ++ ) { 3060 | 3061 | face = faces[ndx]; 3062 | face.daeMaterial = primitive.material; 3063 | geom.faces.push( face ); 3064 | 3065 | for ( k = 0; k < texture_sets.length; k ++ ) { 3066 | 3067 | uv = ts[ texture_sets[k] ]; 3068 | 3069 | if ( vcount > 4 ) { 3070 | 3071 | // Grab the right UVs for the vertices in this face 3072 | uvArr = [ uv[0], uv[ndx + 1], uv[ndx + 2] ]; 3073 | 3074 | } else if ( vcount === 4 ) { 3075 | 3076 | if ( ndx === 0 ) { 3077 | 3078 | uvArr = [ uv[0], uv[1], uv[3] ]; 3079 | 3080 | } else { 3081 | 3082 | uvArr = [ uv[1].clone(), uv[2], uv[3].clone() ]; 3083 | 3084 | } 3085 | 3086 | } else { 3087 | 3088 | uvArr = [ uv[0], uv[1], uv[2] ]; 3089 | 3090 | } 3091 | 3092 | if ( geom.faceVertexUvs[k] === undefined ) { 3093 | 3094 | geom.faceVertexUvs[k] = []; 3095 | 3096 | } 3097 | 3098 | geom.faceVertexUvs[k].push( uvArr ); 3099 | 3100 | } 3101 | 3102 | } 3103 | 3104 | } else { 3105 | 3106 | console.log( 'dropped face with vcount ' + vcount + ' for geometry with id: ' + geom.id ); 3107 | 3108 | } 3109 | 3110 | i += maxOffset * vcount; 3111 | 3112 | } 3113 | 3114 | } 3115 | 3116 | }; 3117 | 3118 | function Polygons () { 3119 | 3120 | this.material = ""; 3121 | this.count = 0; 3122 | this.inputs = []; 3123 | this.vcount = null; 3124 | this.p = []; 3125 | this.geometry = new THREE.Geometry(); 3126 | 3127 | }; 3128 | 3129 | Polygons.prototype.setVertices = function ( vertices ) { 3130 | 3131 | for ( var i = 0; i < this.inputs.length; i ++ ) { 3132 | 3133 | if ( this.inputs[ i ].source === vertices.id ) { 3134 | 3135 | this.inputs[ i ].source = vertices.input[ 'POSITION' ].source; 3136 | 3137 | } 3138 | 3139 | } 3140 | 3141 | }; 3142 | 3143 | Polygons.prototype.parse = function ( element ) { 3144 | 3145 | this.material = element.getAttribute( 'material' ); 3146 | this.count = _attr_as_int( element, 'count', 0 ); 3147 | 3148 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3149 | 3150 | var child = element.childNodes[ i ]; 3151 | 3152 | switch ( child.nodeName ) { 3153 | 3154 | case 'input': 3155 | 3156 | this.inputs.push( ( new Input() ).parse( element.childNodes[ i ] ) ); 3157 | break; 3158 | 3159 | case 'vcount': 3160 | 3161 | this.vcount = _ints( child.textContent ); 3162 | break; 3163 | 3164 | case 'p': 3165 | 3166 | this.p.push( _ints( child.textContent ) ); 3167 | break; 3168 | 3169 | case 'ph': 3170 | 3171 | console.warn( 'polygon holes not yet supported!' ); 3172 | break; 3173 | 3174 | default: 3175 | break; 3176 | 3177 | } 3178 | 3179 | } 3180 | 3181 | return this; 3182 | 3183 | }; 3184 | 3185 | function Polylist () { 3186 | 3187 | Polygons.call( this ); 3188 | 3189 | this.vcount = []; 3190 | 3191 | }; 3192 | 3193 | Polylist.prototype = Object.create( Polygons.prototype ); 3194 | Polylist.prototype.constructor = Polylist; 3195 | 3196 | function LineStrips() { 3197 | 3198 | Polygons.call( this ); 3199 | 3200 | this.vcount = 1; 3201 | 3202 | }; 3203 | 3204 | LineStrips.prototype = Object.create( Polygons.prototype ); 3205 | LineStrips.prototype.constructor = LineStrips; 3206 | 3207 | function Triangles () { 3208 | 3209 | Polygons.call( this ); 3210 | 3211 | this.vcount = 3; 3212 | 3213 | }; 3214 | 3215 | Triangles.prototype = Object.create( Polygons.prototype ); 3216 | Triangles.prototype.constructor = Triangles; 3217 | 3218 | function Accessor() { 3219 | 3220 | this.source = ""; 3221 | this.count = 0; 3222 | this.stride = 0; 3223 | this.params = []; 3224 | 3225 | }; 3226 | 3227 | Accessor.prototype.parse = function ( element ) { 3228 | 3229 | this.params = []; 3230 | this.source = element.getAttribute( 'source' ); 3231 | this.count = _attr_as_int( element, 'count', 0 ); 3232 | this.stride = _attr_as_int( element, 'stride', 0 ); 3233 | 3234 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3235 | 3236 | var child = element.childNodes[ i ]; 3237 | 3238 | if ( child.nodeName === 'param' ) { 3239 | 3240 | var param = {}; 3241 | param[ 'name' ] = child.getAttribute( 'name' ); 3242 | param[ 'type' ] = child.getAttribute( 'type' ); 3243 | this.params.push( param ); 3244 | 3245 | } 3246 | 3247 | } 3248 | 3249 | return this; 3250 | 3251 | }; 3252 | 3253 | function Vertices() { 3254 | 3255 | this.input = {}; 3256 | 3257 | }; 3258 | 3259 | Vertices.prototype.parse = function ( element ) { 3260 | 3261 | this.id = element.getAttribute('id'); 3262 | 3263 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3264 | 3265 | if ( element.childNodes[i].nodeName === 'input' ) { 3266 | 3267 | var input = ( new Input() ).parse( element.childNodes[ i ] ); 3268 | this.input[ input.semantic ] = input; 3269 | 3270 | } 3271 | 3272 | } 3273 | 3274 | return this; 3275 | 3276 | }; 3277 | 3278 | function Input () { 3279 | 3280 | this.semantic = ""; 3281 | this.offset = 0; 3282 | this.source = ""; 3283 | this.set = 0; 3284 | 3285 | }; 3286 | 3287 | Input.prototype.parse = function ( element ) { 3288 | 3289 | this.semantic = element.getAttribute('semantic'); 3290 | this.source = element.getAttribute('source').replace(/^#/, ''); 3291 | this.set = _attr_as_int(element, 'set', -1); 3292 | this.offset = _attr_as_int(element, 'offset', 0); 3293 | 3294 | if ( this.semantic === 'TEXCOORD' && this.set < 0 ) { 3295 | 3296 | this.set = 0; 3297 | 3298 | } 3299 | 3300 | return this; 3301 | 3302 | }; 3303 | 3304 | function Source ( id ) { 3305 | 3306 | this.id = id; 3307 | this.type = null; 3308 | 3309 | }; 3310 | 3311 | Source.prototype.parse = function ( element ) { 3312 | 3313 | this.id = element.getAttribute( 'id' ); 3314 | 3315 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3316 | 3317 | var child = element.childNodes[i]; 3318 | 3319 | switch ( child.nodeName ) { 3320 | 3321 | case 'bool_array': 3322 | 3323 | this.data = _bools( child.textContent ); 3324 | this.type = child.nodeName; 3325 | break; 3326 | 3327 | case 'float_array': 3328 | 3329 | this.data = _floats( child.textContent ); 3330 | this.type = child.nodeName; 3331 | break; 3332 | 3333 | case 'int_array': 3334 | 3335 | this.data = _ints( child.textContent ); 3336 | this.type = child.nodeName; 3337 | break; 3338 | 3339 | case 'IDREF_array': 3340 | case 'Name_array': 3341 | 3342 | this.data = _strings( child.textContent ); 3343 | this.type = child.nodeName; 3344 | break; 3345 | 3346 | case 'technique_common': 3347 | 3348 | for ( var j = 0; j < child.childNodes.length; j ++ ) { 3349 | 3350 | if ( child.childNodes[ j ].nodeName === 'accessor' ) { 3351 | 3352 | this.accessor = ( new Accessor() ).parse( child.childNodes[ j ] ); 3353 | break; 3354 | 3355 | } 3356 | } 3357 | break; 3358 | 3359 | default: 3360 | // console.log(child.nodeName); 3361 | break; 3362 | 3363 | } 3364 | 3365 | } 3366 | 3367 | return this; 3368 | 3369 | }; 3370 | 3371 | Source.prototype.read = function () { 3372 | 3373 | var result = []; 3374 | 3375 | //for (var i = 0; i < this.accessor.params.length; i++) { 3376 | 3377 | var param = this.accessor.params[ 0 ]; 3378 | 3379 | //console.log(param.name + " " + param.type); 3380 | 3381 | switch ( param.type ) { 3382 | 3383 | case 'IDREF': 3384 | case 'Name': case 'name': 3385 | case 'float': 3386 | 3387 | return this.data; 3388 | 3389 | case 'float4x4': 3390 | 3391 | for ( var j = 0; j < this.data.length; j += 16 ) { 3392 | 3393 | var s = this.data.slice( j, j + 16 ); 3394 | var m = getConvertedMat4( s ); 3395 | result.push( m ); 3396 | } 3397 | 3398 | break; 3399 | 3400 | default: 3401 | 3402 | console.log( 'ColladaLoader: Source: Read dont know how to read ' + param.type + '.' ); 3403 | break; 3404 | 3405 | } 3406 | 3407 | //} 3408 | 3409 | return result; 3410 | 3411 | }; 3412 | 3413 | function Material () { 3414 | 3415 | this.id = ""; 3416 | this.name = ""; 3417 | this.instance_effect = null; 3418 | 3419 | }; 3420 | 3421 | Material.prototype.parse = function ( element ) { 3422 | 3423 | this.id = element.getAttribute( 'id' ); 3424 | this.name = element.getAttribute( 'name' ); 3425 | 3426 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3427 | 3428 | if ( element.childNodes[ i ].nodeName === 'instance_effect' ) { 3429 | 3430 | this.instance_effect = ( new InstanceEffect() ).parse( element.childNodes[ i ] ); 3431 | break; 3432 | 3433 | } 3434 | 3435 | } 3436 | 3437 | return this; 3438 | 3439 | }; 3440 | 3441 | function ColorOrTexture () { 3442 | 3443 | this.color = new THREE.Color(); 3444 | this.color.setRGB( Math.random(), Math.random(), Math.random() ); 3445 | this.color.a = 1.0; 3446 | 3447 | this.texture = null; 3448 | this.texcoord = null; 3449 | this.texOpts = null; 3450 | 3451 | }; 3452 | 3453 | ColorOrTexture.prototype.isColor = function () { 3454 | 3455 | return ( this.texture === null ); 3456 | 3457 | }; 3458 | 3459 | ColorOrTexture.prototype.isTexture = function () { 3460 | 3461 | return ( this.texture != null ); 3462 | 3463 | }; 3464 | 3465 | ColorOrTexture.prototype.parse = function ( element ) { 3466 | 3467 | if (element.nodeName === 'transparent') { 3468 | 3469 | this.opaque = element.getAttribute('opaque'); 3470 | 3471 | } 3472 | 3473 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3474 | 3475 | var child = element.childNodes[ i ]; 3476 | if ( child.nodeType != 1 ) continue; 3477 | 3478 | switch ( child.nodeName ) { 3479 | 3480 | case 'color': 3481 | 3482 | var rgba = _floats( child.textContent ); 3483 | this.color = new THREE.Color(); 3484 | this.color.setRGB( rgba[0], rgba[1], rgba[2] ); 3485 | this.color.a = rgba[3]; 3486 | break; 3487 | 3488 | case 'texture': 3489 | 3490 | this.texture = child.getAttribute('texture'); 3491 | this.texcoord = child.getAttribute('texcoord'); 3492 | // Defaults from: 3493 | // https://collada.org/mediawiki/index.php/Maya_texture_placement_MAYA_extension 3494 | this.texOpts = { 3495 | offsetU: 0, 3496 | offsetV: 0, 3497 | repeatU: 1, 3498 | repeatV: 1, 3499 | wrapU: 1, 3500 | wrapV: 1 3501 | }; 3502 | this.parseTexture( child ); 3503 | break; 3504 | 3505 | default: 3506 | break; 3507 | 3508 | } 3509 | 3510 | } 3511 | 3512 | return this; 3513 | 3514 | }; 3515 | 3516 | ColorOrTexture.prototype.parseTexture = function ( element ) { 3517 | 3518 | if ( ! element.childNodes ) return this; 3519 | 3520 | // This should be supported by Maya, 3dsMax, and MotionBuilder 3521 | 3522 | if ( element.childNodes[1] && element.childNodes[1].nodeName === 'extra' ) { 3523 | 3524 | element = element.childNodes[1]; 3525 | 3526 | if ( element.childNodes[1] && element.childNodes[1].nodeName === 'technique' ) { 3527 | 3528 | element = element.childNodes[1]; 3529 | 3530 | } 3531 | 3532 | } 3533 | 3534 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3535 | 3536 | var child = element.childNodes[ i ]; 3537 | 3538 | switch ( child.nodeName ) { 3539 | 3540 | case 'offsetU': 3541 | case 'offsetV': 3542 | case 'repeatU': 3543 | case 'repeatV': 3544 | 3545 | this.texOpts[ child.nodeName ] = parseFloat( child.textContent ); 3546 | 3547 | break; 3548 | 3549 | case 'wrapU': 3550 | case 'wrapV': 3551 | 3552 | // some dae have a value of true which becomes NaN via parseInt 3553 | 3554 | if ( child.textContent.toUpperCase() === 'TRUE' ) { 3555 | 3556 | this.texOpts[ child.nodeName ] = 1; 3557 | 3558 | } else { 3559 | 3560 | this.texOpts[ child.nodeName ] = parseInt( child.textContent ); 3561 | 3562 | } 3563 | break; 3564 | 3565 | default: 3566 | 3567 | this.texOpts[ child.nodeName ] = child.textContent; 3568 | 3569 | break; 3570 | 3571 | } 3572 | 3573 | } 3574 | 3575 | return this; 3576 | 3577 | }; 3578 | 3579 | function Shader ( type, effect ) { 3580 | 3581 | this.type = type; 3582 | this.effect = effect; 3583 | this.material = null; 3584 | 3585 | }; 3586 | 3587 | Shader.prototype.parse = function ( element ) { 3588 | 3589 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3590 | 3591 | var child = element.childNodes[ i ]; 3592 | if ( child.nodeType != 1 ) continue; 3593 | 3594 | switch ( child.nodeName ) { 3595 | 3596 | case 'emission': 3597 | case 'diffuse': 3598 | case 'specular': 3599 | case 'transparent': 3600 | 3601 | this[ child.nodeName ] = ( new ColorOrTexture() ).parse( child ); 3602 | break; 3603 | 3604 | case 'bump': 3605 | 3606 | // If 'bumptype' is 'heightfield', create a 'bump' property 3607 | // Else if 'bumptype' is 'normalmap', create a 'normal' property 3608 | // (Default to 'bump') 3609 | var bumpType = child.getAttribute( 'bumptype' ); 3610 | if ( bumpType ) { 3611 | if ( bumpType.toLowerCase() === "heightfield" ) { 3612 | this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); 3613 | } else if ( bumpType.toLowerCase() === "normalmap" ) { 3614 | this[ 'normal' ] = ( new ColorOrTexture() ).parse( child ); 3615 | } else { 3616 | console.error( "Shader.prototype.parse: Invalid value for attribute 'bumptype' (" + bumpType + ") - valid bumptypes are 'HEIGHTFIELD' and 'NORMALMAP' - defaulting to 'HEIGHTFIELD'" ); 3617 | this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); 3618 | } 3619 | } else { 3620 | console.warn( "Shader.prototype.parse: Attribute 'bumptype' missing from bump node - defaulting to 'HEIGHTFIELD'" ); 3621 | this[ 'bump' ] = ( new ColorOrTexture() ).parse( child ); 3622 | } 3623 | 3624 | break; 3625 | 3626 | case 'shininess': 3627 | case 'reflectivity': 3628 | case 'index_of_refraction': 3629 | case 'transparency': 3630 | 3631 | var f = child.querySelectorAll('float'); 3632 | 3633 | if ( f.length > 0 ) 3634 | this[ child.nodeName ] = parseFloat( f[ 0 ].textContent ); 3635 | 3636 | break; 3637 | 3638 | default: 3639 | break; 3640 | 3641 | } 3642 | 3643 | } 3644 | 3645 | this.create(); 3646 | return this; 3647 | 3648 | }; 3649 | 3650 | Shader.prototype.create = function() { 3651 | 3652 | var props = {}; 3653 | 3654 | var transparent = false; 3655 | 3656 | if (this['transparency'] !== undefined && this['transparent'] !== undefined) { 3657 | // convert transparent color RBG to average value 3658 | var transparentColor = this['transparent']; 3659 | var transparencyLevel = (this.transparent.color.r + this.transparent.color.g + this.transparent.color.b) / 3 * this.transparency; 3660 | 3661 | if (transparencyLevel > 0) { 3662 | transparent = true; 3663 | props[ 'transparent' ] = true; 3664 | props[ 'opacity' ] = 1 - transparencyLevel; 3665 | 3666 | } 3667 | 3668 | } 3669 | 3670 | var keys = { 3671 | 'diffuse':'map', 3672 | 'ambient':'lightMap', 3673 | 'specular':'specularMap', 3674 | 'emission':'emissionMap', 3675 | 'bump':'bumpMap', 3676 | 'normal':'normalMap' 3677 | }; 3678 | 3679 | for ( var prop in this ) { 3680 | 3681 | switch ( prop ) { 3682 | 3683 | case 'ambient': 3684 | case 'emission': 3685 | case 'diffuse': 3686 | case 'specular': 3687 | case 'bump': 3688 | case 'normal': 3689 | 3690 | var cot = this[ prop ]; 3691 | 3692 | if ( cot instanceof ColorOrTexture ) { 3693 | 3694 | if ( cot.isTexture() ) { 3695 | 3696 | var samplerId = cot.texture; 3697 | var surfaceId = this.effect.sampler[samplerId]; 3698 | 3699 | if ( surfaceId !== undefined && surfaceId.source !== undefined ) { 3700 | 3701 | var surface = this.effect.surface[surfaceId.source]; 3702 | 3703 | if ( surface !== undefined ) { 3704 | 3705 | var image = images[ surface.init_from ]; 3706 | 3707 | if ( image ) { 3708 | 3709 | var url = baseUrl + image.init_from; 3710 | 3711 | var texture; 3712 | var loader = THREE.Loader.Handlers.get( url ); 3713 | 3714 | if ( loader !== null ) { 3715 | 3716 | texture = loader.load( url ); 3717 | 3718 | } else { 3719 | 3720 | texture = new THREE.Texture(); 3721 | 3722 | loadTextureImage( texture, url ); 3723 | 3724 | } 3725 | 3726 | texture.wrapS = cot.texOpts.wrapU ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; 3727 | texture.wrapT = cot.texOpts.wrapV ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping; 3728 | texture.offset.x = cot.texOpts.offsetU; 3729 | texture.offset.y = cot.texOpts.offsetV; 3730 | texture.repeat.x = cot.texOpts.repeatU; 3731 | texture.repeat.y = cot.texOpts.repeatV; 3732 | props[keys[prop]] = texture; 3733 | 3734 | // Texture with baked lighting? 3735 | if (prop === 'emission') props['emissive'] = 0xffffff; 3736 | 3737 | } 3738 | 3739 | } 3740 | 3741 | } 3742 | 3743 | } else if ( prop === 'diffuse' || !transparent ) { 3744 | 3745 | if ( prop === 'emission' ) { 3746 | 3747 | props[ 'emissive' ] = cot.color.getHex(); 3748 | 3749 | } else { 3750 | 3751 | props[ prop ] = cot.color.getHex(); 3752 | 3753 | } 3754 | 3755 | } 3756 | 3757 | } 3758 | 3759 | break; 3760 | 3761 | case 'shininess': 3762 | 3763 | props[ prop ] = this[ prop ]; 3764 | break; 3765 | 3766 | case 'reflectivity': 3767 | 3768 | props[ prop ] = this[ prop ]; 3769 | if ( props[ prop ] > 0.0 ) props['envMap'] = options.defaultEnvMap; 3770 | props['combine'] = THREE.MixOperation; //mix regular shading with reflective component 3771 | break; 3772 | 3773 | case 'index_of_refraction': 3774 | 3775 | props[ 'refractionRatio' ] = this[ prop ]; //TODO: "index_of_refraction" becomes "refractionRatio" in shader, but I'm not sure if the two are actually comparable 3776 | if ( this[ prop ] !== 1.0 ) props['envMap'] = options.defaultEnvMap; 3777 | break; 3778 | 3779 | case 'transparency': 3780 | // gets figured out up top 3781 | break; 3782 | 3783 | default: 3784 | break; 3785 | 3786 | } 3787 | 3788 | } 3789 | 3790 | props[ 'shading' ] = preferredShading; 3791 | props[ 'side' ] = this.effect.doubleSided ? THREE.DoubleSide : THREE.FrontSide; 3792 | 3793 | switch ( this.type ) { 3794 | 3795 | case 'constant': 3796 | 3797 | if (props.emissive != undefined) props.color = props.emissive; 3798 | this.material = new THREE.MeshBasicMaterial( props ); 3799 | break; 3800 | 3801 | case 'phong': 3802 | case 'blinn': 3803 | 3804 | if (props.diffuse != undefined) props.color = props.diffuse; 3805 | this.material = new THREE.MeshPhongMaterial( props ); 3806 | break; 3807 | 3808 | case 'lambert': 3809 | default: 3810 | 3811 | if (props.diffuse != undefined) props.color = props.diffuse; 3812 | this.material = new THREE.MeshLambertMaterial( props ); 3813 | break; 3814 | 3815 | } 3816 | 3817 | return this.material; 3818 | 3819 | }; 3820 | 3821 | function Surface ( effect ) { 3822 | 3823 | this.effect = effect; 3824 | this.init_from = null; 3825 | this.format = null; 3826 | 3827 | }; 3828 | 3829 | Surface.prototype.parse = function ( element ) { 3830 | 3831 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3832 | 3833 | var child = element.childNodes[ i ]; 3834 | if ( child.nodeType != 1 ) continue; 3835 | 3836 | switch ( child.nodeName ) { 3837 | 3838 | case 'init_from': 3839 | 3840 | this.init_from = child.textContent; 3841 | break; 3842 | 3843 | case 'format': 3844 | 3845 | this.format = child.textContent; 3846 | break; 3847 | 3848 | default: 3849 | 3850 | console.log( "unhandled Surface prop: " + child.nodeName ); 3851 | break; 3852 | 3853 | } 3854 | 3855 | } 3856 | 3857 | return this; 3858 | 3859 | }; 3860 | 3861 | function Sampler2D ( effect ) { 3862 | 3863 | this.effect = effect; 3864 | this.source = null; 3865 | this.wrap_s = null; 3866 | this.wrap_t = null; 3867 | this.minfilter = null; 3868 | this.magfilter = null; 3869 | this.mipfilter = null; 3870 | 3871 | }; 3872 | 3873 | Sampler2D.prototype.parse = function ( element ) { 3874 | 3875 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3876 | 3877 | var child = element.childNodes[ i ]; 3878 | if ( child.nodeType != 1 ) continue; 3879 | 3880 | switch ( child.nodeName ) { 3881 | 3882 | case 'source': 3883 | 3884 | this.source = child.textContent; 3885 | break; 3886 | 3887 | case 'minfilter': 3888 | 3889 | this.minfilter = child.textContent; 3890 | break; 3891 | 3892 | case 'magfilter': 3893 | 3894 | this.magfilter = child.textContent; 3895 | break; 3896 | 3897 | case 'mipfilter': 3898 | 3899 | this.mipfilter = child.textContent; 3900 | break; 3901 | 3902 | case 'wrap_s': 3903 | 3904 | this.wrap_s = child.textContent; 3905 | break; 3906 | 3907 | case 'wrap_t': 3908 | 3909 | this.wrap_t = child.textContent; 3910 | break; 3911 | 3912 | default: 3913 | 3914 | console.log( "unhandled Sampler2D prop: " + child.nodeName ); 3915 | break; 3916 | 3917 | } 3918 | 3919 | } 3920 | 3921 | return this; 3922 | 3923 | }; 3924 | 3925 | function Effect () { 3926 | 3927 | this.id = ""; 3928 | this.name = ""; 3929 | this.shader = null; 3930 | this.surface = {}; 3931 | this.sampler = {}; 3932 | 3933 | }; 3934 | 3935 | Effect.prototype.create = function () { 3936 | 3937 | if ( this.shader === null ) { 3938 | 3939 | return null; 3940 | 3941 | } 3942 | 3943 | }; 3944 | 3945 | Effect.prototype.parse = function ( element ) { 3946 | 3947 | this.id = element.getAttribute( 'id' ); 3948 | this.name = element.getAttribute( 'name' ); 3949 | 3950 | extractDoubleSided( this, element ); 3951 | 3952 | this.shader = null; 3953 | 3954 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3955 | 3956 | var child = element.childNodes[ i ]; 3957 | if ( child.nodeType != 1 ) continue; 3958 | 3959 | switch ( child.nodeName ) { 3960 | 3961 | case 'profile_COMMON': 3962 | 3963 | this.parseTechnique( this.parseProfileCOMMON( child ) ); 3964 | break; 3965 | 3966 | default: 3967 | break; 3968 | 3969 | } 3970 | 3971 | } 3972 | 3973 | return this; 3974 | 3975 | }; 3976 | 3977 | Effect.prototype.parseNewparam = function ( element ) { 3978 | 3979 | var sid = element.getAttribute( 'sid' ); 3980 | 3981 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 3982 | 3983 | var child = element.childNodes[ i ]; 3984 | if ( child.nodeType != 1 ) continue; 3985 | 3986 | switch ( child.nodeName ) { 3987 | 3988 | case 'surface': 3989 | 3990 | this.surface[sid] = ( new Surface( this ) ).parse( child ); 3991 | break; 3992 | 3993 | case 'sampler2D': 3994 | 3995 | this.sampler[sid] = ( new Sampler2D( this ) ).parse( child ); 3996 | break; 3997 | 3998 | case 'extra': 3999 | 4000 | break; 4001 | 4002 | default: 4003 | 4004 | console.log( child.nodeName ); 4005 | break; 4006 | 4007 | } 4008 | 4009 | } 4010 | 4011 | }; 4012 | 4013 | Effect.prototype.parseProfileCOMMON = function ( element ) { 4014 | 4015 | var technique; 4016 | 4017 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4018 | 4019 | var child = element.childNodes[ i ]; 4020 | 4021 | if ( child.nodeType != 1 ) continue; 4022 | 4023 | switch ( child.nodeName ) { 4024 | 4025 | case 'profile_COMMON': 4026 | 4027 | this.parseProfileCOMMON( child ); 4028 | break; 4029 | 4030 | case 'technique': 4031 | 4032 | technique = child; 4033 | break; 4034 | 4035 | case 'newparam': 4036 | 4037 | this.parseNewparam( child ); 4038 | break; 4039 | 4040 | case 'image': 4041 | 4042 | var _image = ( new _Image() ).parse( child ); 4043 | images[ _image.id ] = _image; 4044 | break; 4045 | 4046 | case 'extra': 4047 | break; 4048 | 4049 | default: 4050 | 4051 | console.log( child.nodeName ); 4052 | break; 4053 | 4054 | } 4055 | 4056 | } 4057 | 4058 | return technique; 4059 | 4060 | }; 4061 | 4062 | Effect.prototype.parseTechnique = function ( element ) { 4063 | 4064 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4065 | 4066 | var child = element.childNodes[i]; 4067 | if ( child.nodeType != 1 ) continue; 4068 | 4069 | switch ( child.nodeName ) { 4070 | 4071 | case 'constant': 4072 | case 'lambert': 4073 | case 'blinn': 4074 | case 'phong': 4075 | 4076 | this.shader = ( new Shader( child.nodeName, this ) ).parse( child ); 4077 | break; 4078 | case 'extra': 4079 | this.parseExtra(child); 4080 | break; 4081 | default: 4082 | break; 4083 | 4084 | } 4085 | 4086 | } 4087 | 4088 | }; 4089 | 4090 | Effect.prototype.parseExtra = function ( element ) { 4091 | 4092 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4093 | 4094 | var child = element.childNodes[i]; 4095 | if ( child.nodeType != 1 ) continue; 4096 | 4097 | switch ( child.nodeName ) { 4098 | 4099 | case 'technique': 4100 | this.parseExtraTechnique( child ); 4101 | break; 4102 | default: 4103 | break; 4104 | 4105 | } 4106 | 4107 | } 4108 | 4109 | }; 4110 | 4111 | Effect.prototype.parseExtraTechnique = function ( element ) { 4112 | 4113 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4114 | 4115 | var child = element.childNodes[i]; 4116 | if ( child.nodeType != 1 ) continue; 4117 | 4118 | switch ( child.nodeName ) { 4119 | 4120 | case 'bump': 4121 | this.shader.parse( element ); 4122 | break; 4123 | default: 4124 | break; 4125 | 4126 | } 4127 | 4128 | } 4129 | 4130 | }; 4131 | 4132 | function InstanceEffect () { 4133 | 4134 | this.url = ""; 4135 | 4136 | }; 4137 | 4138 | InstanceEffect.prototype.parse = function ( element ) { 4139 | 4140 | this.url = element.getAttribute( 'url' ).replace( /^#/, '' ); 4141 | return this; 4142 | 4143 | }; 4144 | 4145 | function Animation() { 4146 | 4147 | this.id = ""; 4148 | this.name = ""; 4149 | this.source = {}; 4150 | this.sampler = []; 4151 | this.channel = []; 4152 | 4153 | }; 4154 | 4155 | Animation.prototype.parse = function ( element ) { 4156 | 4157 | this.id = element.getAttribute( 'id' ); 4158 | this.name = element.getAttribute( 'name' ); 4159 | this.source = {}; 4160 | 4161 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4162 | 4163 | var child = element.childNodes[ i ]; 4164 | 4165 | if ( child.nodeType != 1 ) continue; 4166 | 4167 | switch ( child.nodeName ) { 4168 | 4169 | case 'animation': 4170 | 4171 | var anim = ( new Animation() ).parse( child ); 4172 | 4173 | for ( var src in anim.source ) { 4174 | 4175 | this.source[ src ] = anim.source[ src ]; 4176 | 4177 | } 4178 | 4179 | for ( var j = 0; j < anim.channel.length; j ++ ) { 4180 | 4181 | this.channel.push( anim.channel[ j ] ); 4182 | this.sampler.push( anim.sampler[ j ] ); 4183 | 4184 | } 4185 | 4186 | break; 4187 | 4188 | case 'source': 4189 | 4190 | var src = ( new Source() ).parse( child ); 4191 | this.source[ src.id ] = src; 4192 | break; 4193 | 4194 | case 'sampler': 4195 | 4196 | this.sampler.push( ( new Sampler( this ) ).parse( child ) ); 4197 | break; 4198 | 4199 | case 'channel': 4200 | 4201 | this.channel.push( ( new Channel( this ) ).parse( child ) ); 4202 | break; 4203 | 4204 | default: 4205 | break; 4206 | 4207 | } 4208 | 4209 | } 4210 | 4211 | return this; 4212 | 4213 | }; 4214 | 4215 | function Channel( animation ) { 4216 | 4217 | this.animation = animation; 4218 | this.source = ""; 4219 | this.target = ""; 4220 | this.fullSid = null; 4221 | this.sid = null; 4222 | this.dotSyntax = null; 4223 | this.arrSyntax = null; 4224 | this.arrIndices = null; 4225 | this.member = null; 4226 | 4227 | }; 4228 | 4229 | Channel.prototype.parse = function ( element ) { 4230 | 4231 | this.source = element.getAttribute( 'source' ).replace( /^#/, '' ); 4232 | this.target = element.getAttribute( 'target' ); 4233 | 4234 | var parts = this.target.split( '/' ); 4235 | 4236 | var id = parts.shift(); 4237 | var sid = parts.shift(); 4238 | 4239 | var dotSyntax = ( sid.indexOf(".") >= 0 ); 4240 | var arrSyntax = ( sid.indexOf("(") >= 0 ); 4241 | 4242 | if ( dotSyntax ) { 4243 | 4244 | parts = sid.split("."); 4245 | this.sid = parts.shift(); 4246 | this.member = parts.shift(); 4247 | 4248 | } else if ( arrSyntax ) { 4249 | 4250 | var arrIndices = sid.split("("); 4251 | this.sid = arrIndices.shift(); 4252 | 4253 | for (var j = 0; j < arrIndices.length; j ++ ) { 4254 | 4255 | arrIndices[j] = parseInt( arrIndices[j].replace(/\)/, '') ); 4256 | 4257 | } 4258 | 4259 | this.arrIndices = arrIndices; 4260 | 4261 | } else { 4262 | 4263 | this.sid = sid; 4264 | 4265 | } 4266 | 4267 | this.fullSid = sid; 4268 | this.dotSyntax = dotSyntax; 4269 | this.arrSyntax = arrSyntax; 4270 | 4271 | return this; 4272 | 4273 | }; 4274 | 4275 | function Sampler ( animation ) { 4276 | 4277 | this.id = ""; 4278 | this.animation = animation; 4279 | this.inputs = []; 4280 | this.input = null; 4281 | this.output = null; 4282 | this.strideOut = null; 4283 | this.interpolation = null; 4284 | this.startTime = null; 4285 | this.endTime = null; 4286 | this.duration = 0; 4287 | 4288 | }; 4289 | 4290 | Sampler.prototype.parse = function ( element ) { 4291 | 4292 | this.id = element.getAttribute( 'id' ); 4293 | this.inputs = []; 4294 | 4295 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4296 | 4297 | var child = element.childNodes[ i ]; 4298 | if ( child.nodeType != 1 ) continue; 4299 | 4300 | switch ( child.nodeName ) { 4301 | 4302 | case 'input': 4303 | 4304 | this.inputs.push( (new Input()).parse( child ) ); 4305 | break; 4306 | 4307 | default: 4308 | break; 4309 | 4310 | } 4311 | 4312 | } 4313 | 4314 | return this; 4315 | 4316 | }; 4317 | 4318 | Sampler.prototype.create = function () { 4319 | 4320 | for ( var i = 0; i < this.inputs.length; i ++ ) { 4321 | 4322 | var input = this.inputs[ i ]; 4323 | var source = this.animation.source[ input.source ]; 4324 | 4325 | switch ( input.semantic ) { 4326 | 4327 | case 'INPUT': 4328 | 4329 | this.input = source.read(); 4330 | break; 4331 | 4332 | case 'OUTPUT': 4333 | 4334 | this.output = source.read(); 4335 | this.strideOut = source.accessor.stride; 4336 | break; 4337 | 4338 | case 'INTERPOLATION': 4339 | 4340 | this.interpolation = source.read(); 4341 | break; 4342 | 4343 | case 'IN_TANGENT': 4344 | 4345 | break; 4346 | 4347 | case 'OUT_TANGENT': 4348 | 4349 | break; 4350 | 4351 | default: 4352 | 4353 | console.log(input.semantic); 4354 | break; 4355 | 4356 | } 4357 | 4358 | } 4359 | 4360 | this.startTime = 0; 4361 | this.endTime = 0; 4362 | this.duration = 0; 4363 | 4364 | if ( this.input.length ) { 4365 | 4366 | this.startTime = 100000000; 4367 | this.endTime = -100000000; 4368 | 4369 | for ( var i = 0; i < this.input.length; i ++ ) { 4370 | 4371 | this.startTime = Math.min( this.startTime, this.input[ i ] ); 4372 | this.endTime = Math.max( this.endTime, this.input[ i ] ); 4373 | 4374 | } 4375 | 4376 | this.duration = this.endTime - this.startTime; 4377 | 4378 | } 4379 | 4380 | }; 4381 | 4382 | Sampler.prototype.getData = function ( type, ndx, member ) { 4383 | 4384 | var data; 4385 | 4386 | if ( type === 'matrix' && this.strideOut === 16 ) { 4387 | 4388 | data = this.output[ ndx ]; 4389 | 4390 | } else if ( this.strideOut > 1 ) { 4391 | 4392 | data = []; 4393 | ndx *= this.strideOut; 4394 | 4395 | for ( var i = 0; i < this.strideOut; ++ i ) { 4396 | 4397 | data[ i ] = this.output[ ndx + i ]; 4398 | 4399 | } 4400 | 4401 | if ( this.strideOut === 3 ) { 4402 | 4403 | switch ( type ) { 4404 | 4405 | case 'rotate': 4406 | case 'translate': 4407 | 4408 | fixCoords( data, -1 ); 4409 | break; 4410 | 4411 | case 'scale': 4412 | 4413 | fixCoords( data, 1 ); 4414 | break; 4415 | 4416 | } 4417 | 4418 | } else if ( this.strideOut === 4 && type === 'matrix' ) { 4419 | 4420 | fixCoords( data, -1 ); 4421 | 4422 | } 4423 | 4424 | } else { 4425 | 4426 | data = this.output[ ndx ]; 4427 | 4428 | if ( member && type === 'translate' ) { 4429 | data = getConvertedTranslation( member, data ); 4430 | } 4431 | 4432 | } 4433 | 4434 | return data; 4435 | 4436 | }; 4437 | 4438 | function Key ( time ) { 4439 | 4440 | this.targets = []; 4441 | this.time = time; 4442 | 4443 | }; 4444 | 4445 | Key.prototype.addTarget = function ( fullSid, transform, member, data ) { 4446 | 4447 | this.targets.push( { 4448 | sid: fullSid, 4449 | member: member, 4450 | transform: transform, 4451 | data: data 4452 | } ); 4453 | 4454 | }; 4455 | 4456 | Key.prototype.apply = function ( opt_sid ) { 4457 | 4458 | for ( var i = 0; i < this.targets.length; ++ i ) { 4459 | 4460 | var target = this.targets[ i ]; 4461 | 4462 | if ( !opt_sid || target.sid === opt_sid ) { 4463 | 4464 | target.transform.update( target.data, target.member ); 4465 | 4466 | } 4467 | 4468 | } 4469 | 4470 | }; 4471 | 4472 | Key.prototype.getTarget = function ( fullSid ) { 4473 | 4474 | for ( var i = 0; i < this.targets.length; ++ i ) { 4475 | 4476 | if ( this.targets[ i ].sid === fullSid ) { 4477 | 4478 | return this.targets[ i ]; 4479 | 4480 | } 4481 | 4482 | } 4483 | 4484 | return null; 4485 | 4486 | }; 4487 | 4488 | Key.prototype.hasTarget = function ( fullSid ) { 4489 | 4490 | for ( var i = 0; i < this.targets.length; ++ i ) { 4491 | 4492 | if ( this.targets[ i ].sid === fullSid ) { 4493 | 4494 | return true; 4495 | 4496 | } 4497 | 4498 | } 4499 | 4500 | return false; 4501 | 4502 | }; 4503 | 4504 | // TODO: Currently only doing linear interpolation. Should support full COLLADA spec. 4505 | Key.prototype.interpolate = function ( nextKey, time ) { 4506 | 4507 | for ( var i = 0, l = this.targets.length; i < l; i ++ ) { 4508 | 4509 | var target = this.targets[ i ], 4510 | nextTarget = nextKey.getTarget( target.sid ), 4511 | data; 4512 | 4513 | if ( target.transform.type !== 'matrix' && nextTarget ) { 4514 | 4515 | var scale = ( time - this.time ) / ( nextKey.time - this.time ), 4516 | nextData = nextTarget.data, 4517 | prevData = target.data; 4518 | 4519 | if ( scale < 0 ) scale = 0; 4520 | if ( scale > 1 ) scale = 1; 4521 | 4522 | if ( prevData.length ) { 4523 | 4524 | data = []; 4525 | 4526 | for ( var j = 0; j < prevData.length; ++ j ) { 4527 | 4528 | data[ j ] = prevData[ j ] + ( nextData[ j ] - prevData[ j ] ) * scale; 4529 | 4530 | } 4531 | 4532 | } else { 4533 | 4534 | data = prevData + ( nextData - prevData ) * scale; 4535 | 4536 | } 4537 | 4538 | } else { 4539 | 4540 | data = target.data; 4541 | 4542 | } 4543 | 4544 | target.transform.update( data, target.member ); 4545 | 4546 | } 4547 | 4548 | }; 4549 | 4550 | // Camera 4551 | function Camera() { 4552 | 4553 | this.id = ""; 4554 | this.name = ""; 4555 | this.technique = ""; 4556 | 4557 | }; 4558 | 4559 | Camera.prototype.parse = function ( element ) { 4560 | 4561 | this.id = element.getAttribute( 'id' ); 4562 | this.name = element.getAttribute( 'name' ); 4563 | 4564 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4565 | 4566 | var child = element.childNodes[ i ]; 4567 | if ( child.nodeType != 1 ) continue; 4568 | 4569 | switch ( child.nodeName ) { 4570 | 4571 | case 'optics': 4572 | 4573 | this.parseOptics( child ); 4574 | break; 4575 | 4576 | default: 4577 | break; 4578 | 4579 | } 4580 | 4581 | } 4582 | 4583 | return this; 4584 | 4585 | }; 4586 | 4587 | Camera.prototype.parseOptics = function ( element ) { 4588 | 4589 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4590 | 4591 | if ( element.childNodes[ i ].nodeName === 'technique_common' ) { 4592 | 4593 | var technique = element.childNodes[ i ]; 4594 | 4595 | for ( var j = 0; j < technique.childNodes.length; j ++ ) { 4596 | 4597 | this.technique = technique.childNodes[ j ].nodeName; 4598 | 4599 | if ( this.technique === 'perspective' ) { 4600 | 4601 | var perspective = technique.childNodes[ j ]; 4602 | 4603 | for ( var k = 0; k < perspective.childNodes.length; k ++ ) { 4604 | 4605 | var param = perspective.childNodes[ k ]; 4606 | 4607 | switch ( param.nodeName ) { 4608 | 4609 | case 'yfov': 4610 | this.yfov = param.textContent; 4611 | break; 4612 | case 'xfov': 4613 | this.xfov = param.textContent; 4614 | break; 4615 | case 'znear': 4616 | this.znear = param.textContent; 4617 | break; 4618 | case 'zfar': 4619 | this.zfar = param.textContent; 4620 | break; 4621 | case 'aspect_ratio': 4622 | this.aspect_ratio = param.textContent; 4623 | break; 4624 | 4625 | } 4626 | 4627 | } 4628 | 4629 | } else if ( this.technique === 'orthographic' ) { 4630 | 4631 | var orthographic = technique.childNodes[ j ]; 4632 | 4633 | for ( var k = 0; k < orthographic.childNodes.length; k ++ ) { 4634 | 4635 | var param = orthographic.childNodes[ k ]; 4636 | 4637 | switch ( param.nodeName ) { 4638 | 4639 | case 'xmag': 4640 | this.xmag = param.textContent; 4641 | break; 4642 | case 'ymag': 4643 | this.ymag = param.textContent; 4644 | break; 4645 | case 'znear': 4646 | this.znear = param.textContent; 4647 | break; 4648 | case 'zfar': 4649 | this.zfar = param.textContent; 4650 | break; 4651 | case 'aspect_ratio': 4652 | this.aspect_ratio = param.textContent; 4653 | break; 4654 | 4655 | } 4656 | 4657 | } 4658 | 4659 | } 4660 | 4661 | } 4662 | 4663 | } 4664 | 4665 | } 4666 | 4667 | return this; 4668 | 4669 | }; 4670 | 4671 | function InstanceCamera() { 4672 | 4673 | this.url = ""; 4674 | 4675 | }; 4676 | 4677 | InstanceCamera.prototype.parse = function ( element ) { 4678 | 4679 | this.url = element.getAttribute('url').replace(/^#/, ''); 4680 | 4681 | return this; 4682 | 4683 | }; 4684 | 4685 | // Light 4686 | 4687 | function Light() { 4688 | 4689 | this.id = ""; 4690 | this.name = ""; 4691 | this.technique = ""; 4692 | 4693 | }; 4694 | 4695 | Light.prototype.parse = function ( element ) { 4696 | 4697 | this.id = element.getAttribute( 'id' ); 4698 | this.name = element.getAttribute( 'name' ); 4699 | 4700 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4701 | 4702 | var child = element.childNodes[ i ]; 4703 | if ( child.nodeType != 1 ) continue; 4704 | 4705 | switch ( child.nodeName ) { 4706 | 4707 | case 'technique_common': 4708 | 4709 | this.parseCommon( child ); 4710 | break; 4711 | 4712 | case 'technique': 4713 | 4714 | this.parseTechnique( child ); 4715 | break; 4716 | 4717 | default: 4718 | break; 4719 | 4720 | } 4721 | 4722 | } 4723 | 4724 | return this; 4725 | 4726 | }; 4727 | 4728 | Light.prototype.parseCommon = function ( element ) { 4729 | 4730 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4731 | 4732 | switch ( element.childNodes[ i ].nodeName ) { 4733 | 4734 | case 'directional': 4735 | case 'point': 4736 | case 'spot': 4737 | case 'ambient': 4738 | 4739 | this.technique = element.childNodes[ i ].nodeName; 4740 | 4741 | var light = element.childNodes[ i ]; 4742 | 4743 | for ( var j = 0; j < light.childNodes.length; j ++ ) { 4744 | 4745 | var child = light.childNodes[j]; 4746 | 4747 | switch ( child.nodeName ) { 4748 | 4749 | case 'color': 4750 | 4751 | var rgba = _floats( child.textContent ); 4752 | this.color = new THREE.Color(0); 4753 | this.color.setRGB( rgba[0], rgba[1], rgba[2] ); 4754 | this.color.a = rgba[3]; 4755 | break; 4756 | 4757 | case 'falloff_angle': 4758 | 4759 | this.falloff_angle = parseFloat( child.textContent ); 4760 | break; 4761 | 4762 | case 'quadratic_attenuation': 4763 | var f = parseFloat( child.textContent ); 4764 | this.distance = f ? Math.sqrt( 1 / f ) : 0; 4765 | } 4766 | 4767 | } 4768 | 4769 | } 4770 | 4771 | } 4772 | 4773 | return this; 4774 | 4775 | }; 4776 | 4777 | Light.prototype.parseTechnique = function ( element ) { 4778 | 4779 | this.profile = element.getAttribute( 'profile' ); 4780 | 4781 | for ( var i = 0; i < element.childNodes.length; i ++ ) { 4782 | 4783 | var child = element.childNodes[ i ]; 4784 | 4785 | switch ( child.nodeName ) { 4786 | 4787 | case 'intensity': 4788 | 4789 | this.intensity = parseFloat(child.textContent); 4790 | break; 4791 | 4792 | } 4793 | 4794 | } 4795 | 4796 | return this; 4797 | 4798 | }; 4799 | 4800 | function InstanceLight() { 4801 | 4802 | this.url = ""; 4803 | 4804 | }; 4805 | 4806 | InstanceLight.prototype.parse = function ( element ) { 4807 | 4808 | this.url = element.getAttribute('url').replace(/^#/, ''); 4809 | 4810 | return this; 4811 | 4812 | }; 4813 | 4814 | function KinematicsModel( ) { 4815 | 4816 | this.id = ''; 4817 | this.name = ''; 4818 | this.joints = []; 4819 | this.links = []; 4820 | 4821 | } 4822 | 4823 | KinematicsModel.prototype.parse = function( element ) { 4824 | 4825 | this.id = element.getAttribute('id'); 4826 | this.name = element.getAttribute('name'); 4827 | this.joints = []; 4828 | this.links = []; 4829 | 4830 | for (var i = 0; i < element.childNodes.length; i ++ ) { 4831 | 4832 | var child = element.childNodes[ i ]; 4833 | if ( child.nodeType != 1 ) continue; 4834 | 4835 | switch ( child.nodeName ) { 4836 | 4837 | case 'technique_common': 4838 | 4839 | this.parseCommon(child); 4840 | break; 4841 | 4842 | default: 4843 | break; 4844 | 4845 | } 4846 | 4847 | } 4848 | 4849 | return this; 4850 | 4851 | }; 4852 | 4853 | KinematicsModel.prototype.parseCommon = function( element ) { 4854 | 4855 | for (var i = 0; i < element.childNodes.length; i ++ ) { 4856 | 4857 | var child = element.childNodes[ i ]; 4858 | if ( child.nodeType != 1 ) continue; 4859 | 4860 | switch ( element.childNodes[ i ].nodeName ) { 4861 | 4862 | case 'joint': 4863 | this.joints.push( (new Joint()).parse(child) ); 4864 | break; 4865 | 4866 | case 'link': 4867 | this.links.push( (new Link()).parse(child) ); 4868 | break; 4869 | 4870 | default: 4871 | break; 4872 | 4873 | } 4874 | 4875 | } 4876 | 4877 | return this; 4878 | 4879 | }; 4880 | 4881 | function Joint( ) { 4882 | 4883 | this.sid = ''; 4884 | this.name = ''; 4885 | this.axis = new THREE.Vector3(); 4886 | this.limits = { 4887 | min: 0, 4888 | max: 0 4889 | }; 4890 | this.type = ''; 4891 | this.static = false; 4892 | this.zeroPosition = 0.0; 4893 | this.middlePosition = 0.0; 4894 | 4895 | } 4896 | 4897 | Joint.prototype.parse = function( element ) { 4898 | 4899 | this.sid = element.getAttribute('sid'); 4900 | this.name = element.getAttribute('name'); 4901 | this.axis = new THREE.Vector3(); 4902 | this.limits = { 4903 | min: 0, 4904 | max: 0 4905 | }; 4906 | this.type = ''; 4907 | this.static = false; 4908 | this.zeroPosition = 0.0; 4909 | this.middlePosition = 0.0; 4910 | 4911 | var axisElement = element.querySelector('axis'); 4912 | var _axis = _floats(axisElement.textContent); 4913 | this.axis = getConvertedVec3(_axis, 0); 4914 | 4915 | var min = element.querySelector('limits min') ? parseFloat(element.querySelector('limits min').textContent) : -360; 4916 | var max = element.querySelector('limits max') ? parseFloat(element.querySelector('limits max').textContent) : 360; 4917 | 4918 | this.limits = { 4919 | min: min, 4920 | max: max 4921 | }; 4922 | 4923 | var jointTypes = [ 'prismatic', 'revolute' ]; 4924 | for (var i = 0; i < jointTypes.length; i ++ ) { 4925 | 4926 | var type = jointTypes[ i ]; 4927 | 4928 | var jointElement = element.querySelector(type); 4929 | 4930 | if ( jointElement ) { 4931 | 4932 | this.type = type; 4933 | 4934 | } 4935 | 4936 | } 4937 | 4938 | // if the min is equal to or somehow greater than the max, consider the joint static 4939 | if ( this.limits.min >= this.limits.max ) { 4940 | 4941 | this.static = true; 4942 | 4943 | } 4944 | 4945 | this.middlePosition = (this.limits.min + this.limits.max) / 2.0; 4946 | return this; 4947 | 4948 | }; 4949 | 4950 | function Link( ) { 4951 | 4952 | this.sid = ''; 4953 | this.name = ''; 4954 | this.transforms = []; 4955 | this.attachments = []; 4956 | 4957 | } 4958 | 4959 | Link.prototype.parse = function( element ) { 4960 | 4961 | this.sid = element.getAttribute('sid'); 4962 | this.name = element.getAttribute('name'); 4963 | this.transforms = []; 4964 | this.attachments = []; 4965 | 4966 | for (var i = 0; i < element.childNodes.length; i ++ ) { 4967 | 4968 | var child = element.childNodes[ i ]; 4969 | if ( child.nodeType != 1 ) continue; 4970 | 4971 | switch ( child.nodeName ) { 4972 | 4973 | case 'attachment_full': 4974 | this.attachments.push( (new Attachment()).parse(child) ); 4975 | break; 4976 | 4977 | case 'rotate': 4978 | case 'translate': 4979 | case 'matrix': 4980 | 4981 | this.transforms.push( (new Transform()).parse(child) ); 4982 | break; 4983 | 4984 | default: 4985 | 4986 | break; 4987 | 4988 | } 4989 | 4990 | } 4991 | 4992 | return this; 4993 | 4994 | }; 4995 | 4996 | function Attachment( ) { 4997 | 4998 | this.joint = ''; 4999 | this.transforms = []; 5000 | this.links = []; 5001 | 5002 | } 5003 | 5004 | Attachment.prototype.parse = function( element ) { 5005 | 5006 | this.joint = element.getAttribute('joint').split('/').pop(); 5007 | this.links = []; 5008 | 5009 | for (var i = 0; i < element.childNodes.length; i ++ ) { 5010 | 5011 | var child = element.childNodes[ i ]; 5012 | if ( child.nodeType != 1 ) continue; 5013 | 5014 | switch ( child.nodeName ) { 5015 | 5016 | case 'link': 5017 | this.links.push( (new Link()).parse(child) ); 5018 | break; 5019 | 5020 | case 'rotate': 5021 | case 'translate': 5022 | case 'matrix': 5023 | 5024 | this.transforms.push( (new Transform()).parse(child) ); 5025 | break; 5026 | 5027 | default: 5028 | 5029 | break; 5030 | 5031 | } 5032 | 5033 | } 5034 | 5035 | return this; 5036 | 5037 | }; 5038 | 5039 | function _source( element ) { 5040 | 5041 | var id = element.getAttribute( 'id' ); 5042 | 5043 | if ( sources[ id ] != undefined ) { 5044 | 5045 | return sources[ id ]; 5046 | 5047 | } 5048 | 5049 | sources[ id ] = ( new Source(id )).parse( element ); 5050 | return sources[ id ]; 5051 | 5052 | }; 5053 | 5054 | function _nsResolver( nsPrefix ) { 5055 | 5056 | if ( nsPrefix === "dae" ) { 5057 | 5058 | return "http://www.collada.org/2005/11/COLLADASchema"; 5059 | 5060 | } 5061 | 5062 | return null; 5063 | 5064 | }; 5065 | 5066 | function _bools( str ) { 5067 | 5068 | var raw = _strings( str ); 5069 | var data = []; 5070 | 5071 | for ( var i = 0, l = raw.length; i < l; i ++ ) { 5072 | 5073 | data.push( (raw[i] === 'true' || raw[i] === '1') ? true : false ); 5074 | 5075 | } 5076 | 5077 | return data; 5078 | 5079 | }; 5080 | 5081 | function _floats( str ) { 5082 | 5083 | var raw = _strings(str); 5084 | var data = []; 5085 | 5086 | for ( var i = 0, l = raw.length; i < l; i ++ ) { 5087 | 5088 | data.push( parseFloat( raw[ i ] ) ); 5089 | 5090 | } 5091 | 5092 | return data; 5093 | 5094 | }; 5095 | 5096 | function _ints( str ) { 5097 | 5098 | var raw = _strings( str ); 5099 | var data = []; 5100 | 5101 | for ( var i = 0, l = raw.length; i < l; i ++ ) { 5102 | 5103 | data.push( parseInt( raw[ i ], 10 ) ); 5104 | 5105 | } 5106 | 5107 | return data; 5108 | 5109 | }; 5110 | 5111 | function _strings( str ) { 5112 | 5113 | return ( str.length > 0 ) ? _trimString( str ).split( /\s+/ ) : []; 5114 | 5115 | }; 5116 | 5117 | function _trimString( str ) { 5118 | 5119 | return str.replace( /^\s+/, "" ).replace( /\s+$/, "" ); 5120 | 5121 | }; 5122 | 5123 | function _attr_as_float( element, name, defaultValue ) { 5124 | 5125 | if ( element.hasAttribute( name ) ) { 5126 | 5127 | return parseFloat( element.getAttribute( name ) ); 5128 | 5129 | } else { 5130 | 5131 | return defaultValue; 5132 | 5133 | } 5134 | 5135 | }; 5136 | 5137 | function _attr_as_int( element, name, defaultValue ) { 5138 | 5139 | if ( element.hasAttribute( name ) ) { 5140 | 5141 | return parseInt( element.getAttribute( name ), 10) ; 5142 | 5143 | } else { 5144 | 5145 | return defaultValue; 5146 | 5147 | } 5148 | 5149 | }; 5150 | 5151 | function _attr_as_string( element, name, defaultValue ) { 5152 | 5153 | if ( element.hasAttribute( name ) ) { 5154 | 5155 | return element.getAttribute( name ); 5156 | 5157 | } else { 5158 | 5159 | return defaultValue; 5160 | 5161 | } 5162 | 5163 | }; 5164 | 5165 | function _format_float( f, num ) { 5166 | 5167 | if ( f === undefined ) { 5168 | 5169 | var s = '0.'; 5170 | 5171 | while ( s.length < num + 2 ) { 5172 | 5173 | s += '0'; 5174 | 5175 | } 5176 | 5177 | return s; 5178 | 5179 | } 5180 | 5181 | num = num || 2; 5182 | 5183 | var parts = f.toString().split( '.' ); 5184 | parts[ 1 ] = parts.length > 1 ? parts[ 1 ].substr( 0, num ) : "0"; 5185 | 5186 | while ( parts[ 1 ].length < num ) { 5187 | 5188 | parts[ 1 ] += '0'; 5189 | 5190 | } 5191 | 5192 | return parts.join( '.' ); 5193 | 5194 | }; 5195 | 5196 | function loadTextureImage ( texture, url ) { 5197 | 5198 | loader = new THREE.ImageLoader(); 5199 | 5200 | loader.load( url, function ( image ) { 5201 | 5202 | texture.image = image; 5203 | texture.needsUpdate = true; 5204 | 5205 | } ); 5206 | 5207 | }; 5208 | 5209 | function extractDoubleSided( obj, element ) { 5210 | 5211 | obj.doubleSided = false; 5212 | 5213 | var node = element.querySelectorAll('extra double_sided')[0]; 5214 | 5215 | if ( node ) { 5216 | 5217 | if ( node && parseInt( node.textContent, 10 ) === 1 ) { 5218 | 5219 | obj.doubleSided = true; 5220 | 5221 | } 5222 | 5223 | } 5224 | 5225 | }; 5226 | 5227 | // Up axis conversion 5228 | 5229 | function setUpConversion() { 5230 | 5231 | if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { 5232 | 5233 | upConversion = null; 5234 | 5235 | } else { 5236 | 5237 | switch ( colladaUp ) { 5238 | 5239 | case 'X': 5240 | 5241 | upConversion = options.upAxis === 'Y' ? 'XtoY' : 'XtoZ'; 5242 | break; 5243 | 5244 | case 'Y': 5245 | 5246 | upConversion = options.upAxis === 'X' ? 'YtoX' : 'YtoZ'; 5247 | break; 5248 | 5249 | case 'Z': 5250 | 5251 | upConversion = options.upAxis === 'X' ? 'ZtoX' : 'ZtoY'; 5252 | break; 5253 | 5254 | } 5255 | 5256 | } 5257 | 5258 | }; 5259 | 5260 | function fixCoords( data, sign ) { 5261 | 5262 | if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { 5263 | 5264 | return; 5265 | 5266 | } 5267 | 5268 | switch ( upConversion ) { 5269 | 5270 | case 'XtoY': 5271 | 5272 | var tmp = data[ 0 ]; 5273 | data[ 0 ] = sign * data[ 1 ]; 5274 | data[ 1 ] = tmp; 5275 | break; 5276 | 5277 | case 'XtoZ': 5278 | 5279 | var tmp = data[ 2 ]; 5280 | data[ 2 ] = data[ 1 ]; 5281 | data[ 1 ] = data[ 0 ]; 5282 | data[ 0 ] = tmp; 5283 | break; 5284 | 5285 | case 'YtoX': 5286 | 5287 | var tmp = data[ 0 ]; 5288 | data[ 0 ] = data[ 1 ]; 5289 | data[ 1 ] = sign * tmp; 5290 | break; 5291 | 5292 | case 'YtoZ': 5293 | 5294 | var tmp = data[ 1 ]; 5295 | data[ 1 ] = sign * data[ 2 ]; 5296 | data[ 2 ] = tmp; 5297 | break; 5298 | 5299 | case 'ZtoX': 5300 | 5301 | var tmp = data[ 0 ]; 5302 | data[ 0 ] = data[ 1 ]; 5303 | data[ 1 ] = data[ 2 ]; 5304 | data[ 2 ] = tmp; 5305 | break; 5306 | 5307 | case 'ZtoY': 5308 | 5309 | var tmp = data[ 1 ]; 5310 | data[ 1 ] = data[ 2 ]; 5311 | data[ 2 ] = sign * tmp; 5312 | break; 5313 | 5314 | } 5315 | 5316 | }; 5317 | 5318 | function getConvertedTranslation( axis, data ) { 5319 | 5320 | if ( options.convertUpAxis !== true || colladaUp === options.upAxis ) { 5321 | 5322 | return data; 5323 | 5324 | } 5325 | 5326 | switch ( axis ) { 5327 | case 'X': 5328 | data = upConversion === 'XtoY' ? data * -1 : data; 5329 | break; 5330 | case 'Y': 5331 | data = upConversion === 'YtoZ' || upConversion === 'YtoX' ? data * -1 : data; 5332 | break; 5333 | case 'Z': 5334 | data = upConversion === 'ZtoY' ? data * -1 : data ; 5335 | break; 5336 | default: 5337 | break; 5338 | } 5339 | 5340 | return data; 5341 | }; 5342 | 5343 | function getConvertedVec3( data, offset ) { 5344 | 5345 | var arr = [ data[ offset ], data[ offset + 1 ], data[ offset + 2 ] ]; 5346 | fixCoords( arr, -1 ); 5347 | return new THREE.Vector3( arr[ 0 ], arr[ 1 ], arr[ 2 ] ); 5348 | 5349 | }; 5350 | 5351 | function getConvertedMat4( data ) { 5352 | 5353 | if ( options.convertUpAxis ) { 5354 | 5355 | // First fix rotation and scale 5356 | 5357 | // Columns first 5358 | var arr = [ data[ 0 ], data[ 4 ], data[ 8 ] ]; 5359 | fixCoords( arr, -1 ); 5360 | data[ 0 ] = arr[ 0 ]; 5361 | data[ 4 ] = arr[ 1 ]; 5362 | data[ 8 ] = arr[ 2 ]; 5363 | arr = [ data[ 1 ], data[ 5 ], data[ 9 ] ]; 5364 | fixCoords( arr, -1 ); 5365 | data[ 1 ] = arr[ 0 ]; 5366 | data[ 5 ] = arr[ 1 ]; 5367 | data[ 9 ] = arr[ 2 ]; 5368 | arr = [ data[ 2 ], data[ 6 ], data[ 10 ] ]; 5369 | fixCoords( arr, -1 ); 5370 | data[ 2 ] = arr[ 0 ]; 5371 | data[ 6 ] = arr[ 1 ]; 5372 | data[ 10 ] = arr[ 2 ]; 5373 | // Rows second 5374 | arr = [ data[ 0 ], data[ 1 ], data[ 2 ] ]; 5375 | fixCoords( arr, -1 ); 5376 | data[ 0 ] = arr[ 0 ]; 5377 | data[ 1 ] = arr[ 1 ]; 5378 | data[ 2 ] = arr[ 2 ]; 5379 | arr = [ data[ 4 ], data[ 5 ], data[ 6 ] ]; 5380 | fixCoords( arr, -1 ); 5381 | data[ 4 ] = arr[ 0 ]; 5382 | data[ 5 ] = arr[ 1 ]; 5383 | data[ 6 ] = arr[ 2 ]; 5384 | arr = [ data[ 8 ], data[ 9 ], data[ 10 ] ]; 5385 | fixCoords( arr, -1 ); 5386 | data[ 8 ] = arr[ 0 ]; 5387 | data[ 9 ] = arr[ 1 ]; 5388 | data[ 10 ] = arr[ 2 ]; 5389 | 5390 | // Now fix translation 5391 | arr = [ data[ 3 ], data[ 7 ], data[ 11 ] ]; 5392 | fixCoords( arr, -1 ); 5393 | data[ 3 ] = arr[ 0 ]; 5394 | data[ 7 ] = arr[ 1 ]; 5395 | data[ 11 ] = arr[ 2 ]; 5396 | 5397 | } 5398 | 5399 | return new THREE.Matrix4().set( 5400 | data[0], data[1], data[2], data[3], 5401 | data[4], data[5], data[6], data[7], 5402 | data[8], data[9], data[10], data[11], 5403 | data[12], data[13], data[14], data[15] 5404 | ); 5405 | 5406 | }; 5407 | 5408 | function getConvertedIndex( index ) { 5409 | 5410 | if ( index > -1 && index < 3 ) { 5411 | 5412 | var members = [ 'X', 'Y', 'Z' ], 5413 | indices = { X: 0, Y: 1, Z: 2 }; 5414 | 5415 | index = getConvertedMember( members[ index ] ); 5416 | index = indices[ index ]; 5417 | 5418 | } 5419 | 5420 | return index; 5421 | 5422 | }; 5423 | 5424 | function getConvertedMember( member ) { 5425 | 5426 | if ( options.convertUpAxis ) { 5427 | 5428 | switch ( member ) { 5429 | 5430 | case 'X': 5431 | 5432 | switch ( upConversion ) { 5433 | 5434 | case 'XtoY': 5435 | case 'XtoZ': 5436 | case 'YtoX': 5437 | 5438 | member = 'Y'; 5439 | break; 5440 | 5441 | case 'ZtoX': 5442 | 5443 | member = 'Z'; 5444 | break; 5445 | 5446 | } 5447 | 5448 | break; 5449 | 5450 | case 'Y': 5451 | 5452 | switch ( upConversion ) { 5453 | 5454 | case 'XtoY': 5455 | case 'YtoX': 5456 | case 'ZtoX': 5457 | 5458 | member = 'X'; 5459 | break; 5460 | 5461 | case 'XtoZ': 5462 | case 'YtoZ': 5463 | case 'ZtoY': 5464 | 5465 | member = 'Z'; 5466 | break; 5467 | 5468 | } 5469 | 5470 | break; 5471 | 5472 | case 'Z': 5473 | 5474 | switch ( upConversion ) { 5475 | 5476 | case 'XtoZ': 5477 | 5478 | member = 'X'; 5479 | break; 5480 | 5481 | case 'YtoZ': 5482 | case 'ZtoX': 5483 | case 'ZtoY': 5484 | 5485 | member = 'Y'; 5486 | break; 5487 | 5488 | } 5489 | 5490 | break; 5491 | 5492 | } 5493 | 5494 | } 5495 | 5496 | return member; 5497 | 5498 | }; 5499 | 5500 | return { 5501 | 5502 | load: load, 5503 | parse: parse, 5504 | setPreferredShading: setPreferredShading, 5505 | applySkin: applySkin, 5506 | geometries : geometries, 5507 | options: options 5508 | 5509 | }; 5510 | 5511 | }; 5512 | --------------------------------------------------------------------------------