From 7accef4c6eaa0a51ff40a086c256b09afd489803 Mon Sep 17 00:00:00 2001 From: Tait Brown Date: Tue, 6 Nov 2012 16:45:03 +1100 Subject: [PATCH] syncing 'gh-pages' with 'master' to add experiment 13 --- Battery3D/css/bootstrap.min.css | 356 + Battery3D/css/custom.css | 41 + Battery3D/img/.gitignore | 2 + Battery3D/img/noise.png | Bin 0 -> 6050 bytes Battery3D/img/particle.png | Bin 0 -> 1112 bytes Battery3D/img/texture.jpg | Bin 0 -> 36852 bytes Battery3D/img/texture.png | Bin 0 -> 512497 bytes Battery3D/index.html | 42 + Battery3D/js/custom.js | 257 + Battery3D/js/jquery.imgpreload.min.js | 3 + Battery3D/js/jquery.min.js | 4 + Battery3D/js/modernizr-2.0.6.min.js | 4 + Battery3D/js/three.js | 34526 ++++++++++++++++++++++++ Battery3D/js/tween.js | 645 + core/thumbs/013.png | Bin 0 -> 9269 bytes index.html | 22 + style.css | 3 + 17 files changed, 35905 insertions(+) create mode 100644 Battery3D/css/bootstrap.min.css create mode 100644 Battery3D/css/custom.css create mode 100644 Battery3D/img/.gitignore create mode 100644 Battery3D/img/noise.png create mode 100644 Battery3D/img/particle.png create mode 100644 Battery3D/img/texture.jpg create mode 100644 Battery3D/img/texture.png create mode 100644 Battery3D/index.html create mode 100644 Battery3D/js/custom.js create mode 100644 Battery3D/js/jquery.imgpreload.min.js create mode 100644 Battery3D/js/jquery.min.js create mode 100644 Battery3D/js/modernizr-2.0.6.min.js create mode 100644 Battery3D/js/three.js create mode 100644 Battery3D/js/tween.js create mode 100644 core/thumbs/013.png diff --git a/Battery3D/css/bootstrap.min.css b/Battery3D/css/bootstrap.min.css new file mode 100644 index 0000000..b9effee --- /dev/null +++ b/Battery3D/css/bootstrap.min.css @@ -0,0 +1,356 @@ +html,body{margin:0;padding:0;} +h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;} +table{border-collapse:collapse;border-spacing:0;} +ol,ul{list-style:none;} +q:before,q:after,blockquote:before,blockquote:after{content:"";} +html{overflow-y:scroll;font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} +a:focus{outline:thin dotted;} +a:hover,a:active{outline:0;} +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} +audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} +audio:not([controls]){display:none;} +sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;} +sup{top:-0.5em;} +sub{bottom:-0.25em;} +img{border:0;-ms-interpolation-mode:bicubic;} +button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;} +button,input{line-height:normal;*overflow:visible;} +button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;} +button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} +input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} +input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;} +textarea{overflow:auto;vertical-align:top;} +body{background-color:#ffffff;margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;color:#404040;} +.container{width:940px;margin-left:auto;margin-right:auto;zoom:1;}.container:before,.container:after{display:table;content:"";zoom:1;} +.container:after{clear:both;} +.container-fluid{position:relative;min-width:940px;padding-left:20px;padding-right:20px;zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";zoom:1;} +.container-fluid:after{clear:both;} +.container-fluid>.sidebar{position:absolute;top:0;left:20px;width:220px;} +.container-fluid>.content{margin-left:240px;} +a{color:#0069d6;text-decoration:none;line-height:inherit;font-weight:inherit;}a:hover{color:#00438a;text-decoration:underline;} +.pull-right{float:right;} +.pull-left{float:left;} +.hide{display:none;} +.show{display:block;} +.row{zoom:1;margin-left:-20px;}.row:before,.row:after{display:table;content:"";zoom:1;} +.row:after{clear:both;} +.row>[class*="span"]{display:inline;float:left;margin-left:20px;} +.span1{width:40px;} +.span2{width:100px;} +.span3{width:160px;} +.span4{width:220px;} +.span5{width:280px;} +.span6{width:340px;} +.span7{width:400px;} +.span8{width:460px;} +.span9{width:520px;} +.span10{width:580px;} +.span11{width:640px;} +.span12{width:700px;} +.span13{width:760px;} +.span14{width:820px;} +.span15{width:880px;} +.span16{width:940px;} +.span17{width:1000px;} +.span18{width:1060px;} +.span19{width:1120px;} +.span20{width:1180px;} +.span21{width:1240px;} +.span22{width:1300px;} +.span23{width:1360px;} +.span24{width:1420px;} +.row>.offset1{margin-left:80px;} +.row>.offset2{margin-left:140px;} +.row>.offset3{margin-left:200px;} +.row>.offset4{margin-left:260px;} +.row>.offset5{margin-left:320px;} +.row>.offset6{margin-left:380px;} +.row>.offset7{margin-left:440px;} +.row>.offset8{margin-left:500px;} +.row>.offset9{margin-left:560px;} +.row>.offset10{margin-left:620px;} +.row>.offset11{margin-left:680px;} +.row>.offset12{margin-left:740px;} +.span-one-third{width:300px;} +.span-two-thirds{width:620px;} +.row>.offset-one-third{margin-left:340px;} +.row>.offset-two-thirds{margin-left:660px;} +p{font-size:13px;font-weight:normal;line-height:18px;margin-bottom:9px;}p small{font-size:11px;color:#bfbfbf;} +h1,h2,h3,h4,h5,h6{font-weight:bold;color:#404040;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#bfbfbf;} +h1{margin-bottom:18px;font-size:30px;line-height:36px;}h1 small{font-size:18px;} +h2{font-size:24px;line-height:36px;}h2 small{font-size:14px;} +h3,h4,h5,h6{line-height:36px;} +h3{font-size:18px;}h3 small{font-size:14px;} +h4{font-size:16px;}h4 small{font-size:12px;} +h5{font-size:14px;} +h6{font-size:13px;color:#bfbfbf;text-transform:uppercase;} +ul,ol{margin:0 0 18px 25px;} +ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} +ul{list-style:disc;} +ol{list-style:decimal;} +li{line-height:18px;color:#808080;} +ul.unstyled{list-style:none;margin-left:0;} +dl{margin-bottom:18px;}dl dt,dl dd{line-height:18px;} +dl dt{font-weight:bold;} +dl dd{margin-left:9px;} +hr{margin:20px 0 19px;border:0;border-bottom:1px solid #eee;} +strong{font-style:inherit;font-weight:bold;} +em{font-style:italic;font-weight:inherit;line-height:inherit;} +.muted{color:#bfbfbf;} +blockquote{margin-bottom:18px;border-left:5px solid #eee;padding-left:15px;}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;} +blockquote small{display:block;font-size:12px;font-weight:300;line-height:18px;color:#bfbfbf;}blockquote small:before{content:'\2014 \00A0';} +address{display:block;line-height:18px;margin-bottom:18px;} +code,pre{padding:0 3px 2px;font-family:Monaco, Andale Mono, Courier New, monospace;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +code{background-color:#fee9cc;color:rgba(0, 0, 0, 0.75);padding:1px 3px;} +pre{background-color:#f5f5f5;display:block;padding:8.5px;margin:0 0 18px;line-height:18px;font-size:12px;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;white-space:pre;white-space:pre-wrap;word-wrap:break-word;} +form{margin-bottom:18px;} +fieldset{margin-bottom:18px;padding-top:18px;}fieldset legend{display:block;padding-left:150px;font-size:19.5px;line-height:1;color:#404040;*padding:0 0 5px 145px;*line-height:1.5;} +form .clearfix{margin-bottom:18px;zoom:1;}form .clearfix:before,form .clearfix:after{display:table;content:"";zoom:1;} +form .clearfix:after{clear:both;} +label,input,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:normal;} +label{padding-top:6px;font-size:13px;line-height:18px;float:left;width:130px;text-align:right;color:#404040;} +form .input{margin-left:150px;} +input[type=checkbox],input[type=radio]{cursor:pointer;} +input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;font-size:13px;line-height:18px;color:#808080;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +select{padding:initial;} +input[type=checkbox],input[type=radio]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;border:none;} +input[type=file]{background-color:#ffffff;padding:initial;border:initial;line-height:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +input[type=button],input[type=reset],input[type=submit]{width:auto;height:auto;} +select,input[type=file]{height:27px;*height:auto;line-height:27px;*margin-top:4px;} +select[multiple]{height:inherit;background-color:#ffffff;} +textarea{height:auto;} +.uneditable-input{background-color:#ffffff;display:block;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;} +:-moz-placeholder{color:#bfbfbf;} +::-webkit-input-placeholder{color:#bfbfbf;} +input,textarea{-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);} +input:focus,textarea:focus{outline:0;border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);} +input[type=file]:focus,input[type=checkbox]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:1px dotted #666;} +form .clearfix.error>label,form .clearfix.error .help-block,form .clearfix.error .help-inline{color:#b94a48;} +form .clearfix.error input,form .clearfix.error textarea{color:#b94a48;border-color:#ee5f5b;}form .clearfix.error input:focus,form .clearfix.error textarea:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;} +form .clearfix.error .input-prepend .add-on,form .clearfix.error .input-append .add-on{color:#b94a48;background-color:#fce6e6;border-color:#b94a48;} +form .clearfix.warning>label,form .clearfix.warning .help-block,form .clearfix.warning .help-inline{color:#c09853;} +form .clearfix.warning input,form .clearfix.warning textarea{color:#c09853;border-color:#ccae64;}form .clearfix.warning input:focus,form .clearfix.warning textarea:focus{border-color:#be9a3f;-webkit-box-shadow:0 0 6px #e5d6b1;-moz-box-shadow:0 0 6px #e5d6b1;box-shadow:0 0 6px #e5d6b1;} +form .clearfix.warning .input-prepend .add-on,form .clearfix.warning .input-append .add-on{color:#c09853;background-color:#d2b877;border-color:#c09853;} +form .clearfix.success>label,form .clearfix.success .help-block,form .clearfix.success .help-inline{color:#468847;} +form .clearfix.success input,form .clearfix.success textarea{color:#468847;border-color:#57a957;}form .clearfix.success input:focus,form .clearfix.success textarea:focus{border-color:#458845;-webkit-box-shadow:0 0 6px #9acc9a;-moz-box-shadow:0 0 6px #9acc9a;box-shadow:0 0 6px #9acc9a;} +form .clearfix.success .input-prepend .add-on,form .clearfix.success .input-append .add-on{color:#468847;background-color:#bcddbc;border-color:#468847;} +.input-mini,input.mini,textarea.mini,select.mini{width:60px;} +.input-small,input.small,textarea.small,select.small{width:90px;} +.input-medium,input.medium,textarea.medium,select.medium{width:150px;} +.input-large,input.large,textarea.large,select.large{width:210px;} +.input-xlarge,input.xlarge,textarea.xlarge,select.xlarge{width:270px;} +.input-xxlarge,input.xxlarge,textarea.xxlarge,select.xxlarge{width:530px;} +textarea.xxlarge{overflow-y:auto;} +input.span1,textarea.span1{display:inline-block;float:none;width:30px;margin-left:0;} +input.span2,textarea.span2{display:inline-block;float:none;width:90px;margin-left:0;} +input.span3,textarea.span3{display:inline-block;float:none;width:150px;margin-left:0;} +input.span4,textarea.span4{display:inline-block;float:none;width:210px;margin-left:0;} +input.span5,textarea.span5{display:inline-block;float:none;width:270px;margin-left:0;} +input.span6,textarea.span6{display:inline-block;float:none;width:330px;margin-left:0;} +input.span7,textarea.span7{display:inline-block;float:none;width:390px;margin-left:0;} +input.span8,textarea.span8{display:inline-block;float:none;width:450px;margin-left:0;} +input.span9,textarea.span9{display:inline-block;float:none;width:510px;margin-left:0;} +input.span10,textarea.span10{display:inline-block;float:none;width:570px;margin-left:0;} +input.span11,textarea.span11{display:inline-block;float:none;width:630px;margin-left:0;} +input.span12,textarea.span12{display:inline-block;float:none;width:690px;margin-left:0;} +input.span13,textarea.span13{display:inline-block;float:none;width:750px;margin-left:0;} +input.span14,textarea.span14{display:inline-block;float:none;width:810px;margin-left:0;} +input.span15,textarea.span15{display:inline-block;float:none;width:870px;margin-left:0;} +input.span16,textarea.span16{display:inline-block;float:none;width:930px;margin-left:0;} +input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;} +.actions{background:#f5f5f5;margin-top:18px;margin-bottom:18px;padding:17px 20px 18px 150px;border-top:1px solid #ddd;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;}.actions .secondary-action{float:right;}.actions .secondary-action a{line-height:30px;}.actions .secondary-action a:hover{text-decoration:underline;} +.help-inline,.help-block{font-size:13px;line-height:18px;color:#bfbfbf;} +.help-inline{padding-left:5px;*position:relative;*top:-5px;} +.help-block{display:block;max-width:600px;} +.inline-inputs{color:#808080;}.inline-inputs span{padding:0 2px 0 1px;} +.input-prepend input,.input-append input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.input-prepend .add-on,.input-append .add-on{position:relative;background:#f5f5f5;border:1px solid #ccc;z-index:2;float:left;display:block;width:auto;min-width:16px;height:18px;padding:4px 4px 4px 5px;margin-right:-1px;font-weight:normal;line-height:18px;color:#bfbfbf;text-align:center;text-shadow:0 1px 0 #ffffff;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-prepend .active,.input-append .active{background:#a9dba9;border-color:#46a546;} +.input-prepend .add-on{*margin-top:1px;} +.input-append input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append .add-on{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;margin-right:0;margin-left:-1px;} +.inputs-list{margin:0 0 5px;width:100%;}.inputs-list li{display:block;padding:0;width:100%;} +.inputs-list label{display:block;float:none;width:auto;padding:0;margin-left:20px;line-height:18px;text-align:left;white-space:normal;}.inputs-list label strong{color:#808080;} +.inputs-list label small{font-size:11px;font-weight:normal;} +.inputs-list .inputs-list{margin-left:25px;margin-bottom:10px;padding-top:0;} +.inputs-list:first-child{padding-top:6px;} +.inputs-list li+li{padding-top:2px;} +.inputs-list input[type=radio],.inputs-list input[type=checkbox]{margin-bottom:0;margin-left:-20px;float:left;} +.form-stacked{padding-left:20px;}.form-stacked fieldset{padding-top:9px;} +.form-stacked legend{padding-left:0;} +.form-stacked label{display:block;float:none;width:auto;font-weight:bold;text-align:left;line-height:20px;padding-top:0;} +.form-stacked .clearfix{margin-bottom:9px;}.form-stacked .clearfix div.input{margin-left:0;} +.form-stacked .inputs-list{margin-bottom:0;}.form-stacked .inputs-list li{padding-top:0;}.form-stacked .inputs-list li label{font-weight:normal;padding-top:0;} +.form-stacked div.clearfix.error{padding-top:10px;padding-bottom:10px;padding-left:10px;margin-top:0;margin-left:-10px;} +.form-stacked .actions{margin-left:-20px;padding-left:20px;} +table{width:100%;margin-bottom:18px;padding:0;font-size:13px;border-collapse:collapse;}table th,table td{padding:10px 10px 9px;line-height:18px;text-align:left;} +table th{padding-top:9px;font-weight:bold;vertical-align:middle;} +table td{vertical-align:top;border-top:1px solid #ddd;} +table tbody th{border-top:1px solid #ddd;vertical-align:top;} +.condensed-table th,.condensed-table td{padding:5px 5px 4px;} +.bordered-table{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.bordered-table th+th,.bordered-table td+td,.bordered-table th+td{border-left:1px solid #ddd;} +.bordered-table thead tr:first-child th:first-child,.bordered-table tbody tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} +.bordered-table thead tr:first-child th:last-child,.bordered-table tbody tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} +.bordered-table tbody tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} +.bordered-table tbody tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} +table .span1{width:20px;} +table .span2{width:60px;} +table .span3{width:100px;} +table .span4{width:140px;} +table .span5{width:180px;} +table .span6{width:220px;} +table .span7{width:260px;} +table .span8{width:300px;} +table .span9{width:340px;} +table .span10{width:380px;} +table .span11{width:420px;} +table .span12{width:460px;} +table .span13{width:500px;} +table .span14{width:540px;} +table .span15{width:580px;} +table .span16{width:620px;} +.zebra-striped tbody tr:nth-child(odd) td,.zebra-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} +.zebra-striped tbody tr:hover td,.zebra-striped tbody tr:hover th{background-color:#f5f5f5;} +table .header{cursor:pointer;}table .header:after{content:"";float:right;margin-top:7px;border-width:0 4px 4px;border-style:solid;border-color:#000 transparent;visibility:hidden;} +table .headerSortUp,table .headerSortDown{background-color:rgba(141, 192, 219, 0.25);text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);} +table .header:hover:after{visibility:visible;} +table .headerSortDown:after,table .headerSortDown:hover:after{visibility:visible;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;} +table .headerSortUp:after{border-bottom:none;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000;visibility:visible;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;} +table .blue{color:#049cdb;border-bottom-color:#049cdb;} +table .headerSortUp.blue,table .headerSortDown.blue{background-color:#ade6fe;} +table .green{color:#46a546;border-bottom-color:#46a546;} +table .headerSortUp.green,table .headerSortDown.green{background-color:#cdeacd;} +table .red{color:#9d261d;border-bottom-color:#9d261d;} +table .headerSortUp.red,table .headerSortDown.red{background-color:#f4c8c5;} +table .yellow{color:#ffc40d;border-bottom-color:#ffc40d;} +table .headerSortUp.yellow,table .headerSortDown.yellow{background-color:#fff6d9;} +table .orange{color:#f89406;border-bottom-color:#f89406;} +table .headerSortUp.orange,table .headerSortDown.orange{background-color:#fee9cc;} +table .purple{color:#7a43b6;border-bottom-color:#7a43b6;} +table .headerSortUp.purple,table .headerSortDown.purple{background-color:#e2d5f0;} +.topbar{height:40px;position:fixed;top:0;left:0;right:0;z-index:10000;overflow:visible;}.topbar a{color:#bfbfbf;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} +.topbar h3 a:hover,.topbar .brand:hover,.topbar ul .active>a{background-color:#333;background-color:rgba(255, 255, 255, 0.05);color:#ffffff;text-decoration:none;} +.topbar h3{position:relative;} +.topbar h3 a,.topbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;color:#ffffff;font-size:20px;font-weight:200;line-height:1;} +.topbar p{margin:0;line-height:40px;}.topbar p a:hover{background-color:transparent;color:#ffffff;} +.topbar form{float:left;margin:5px 0 0 0;position:relative;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;} +.topbar form.pull-right{float:right;} +.topbar input{background-color:#444;background-color:rgba(255, 255, 255, 0.3);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:normal;font-weight:13px;line-height:1;padding:4px 9px;color:#ffffff;color:rgba(255, 255, 255, 0.75);border:1px solid #111;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.topbar input:-moz-placeholder{color:#e6e6e6;} +.topbar input::-webkit-input-placeholder{color:#e6e6e6;} +.topbar input:hover{background-color:#bfbfbf;background-color:rgba(255, 255, 255, 0.5);color:#ffffff;} +.topbar input:focus,.topbar input.focused{outline:0;background-color:#ffffff;color:#404040;text-shadow:0 1px 0 #ffffff;border:0;padding:5px 10px;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);} +.topbar-inner,.topbar .fill{background-color:#222;background-color:#222222;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222));background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} +.topbar div>ul,.nav{display:block;float:left;margin:0 10px 0 0;position:relative;left:0;}.topbar div>ul>li,.nav>li{display:block;float:left;} +.topbar div>ul a,.nav a{display:block;float:none;padding:10px 10px 11px;line-height:19px;text-decoration:none;}.topbar div>ul a:hover,.nav a:hover{color:#ffffff;text-decoration:none;} +.topbar div>ul .active>a,.nav .active>a{background-color:#222;background-color:rgba(0, 0, 0, 0.5);} +.topbar div>ul.secondary-nav,.nav.secondary-nav{float:right;margin-left:10px;margin-right:0;}.topbar div>ul.secondary-nav .menu-dropdown,.nav.secondary-nav .menu-dropdown,.topbar div>ul.secondary-nav .dropdown-menu,.nav.secondary-nav .dropdown-menu{right:0;border:0;} +.topbar div>ul a.menu:hover,.nav a.menu:hover,.topbar div>ul li.open .menu,.nav li.open .menu,.topbar div>ul .dropdown-toggle:hover,.nav .dropdown-toggle:hover,.topbar div>ul .dropdown.open .dropdown-toggle,.nav .dropdown.open .dropdown-toggle{background:#444;background:rgba(255, 255, 255, 0.05);} +.topbar div>ul .menu-dropdown,.nav .menu-dropdown,.topbar div>ul .dropdown-menu,.nav .dropdown-menu{background-color:#333;}.topbar div>ul .menu-dropdown a.menu,.nav .menu-dropdown a.menu,.topbar div>ul .dropdown-menu a.menu,.nav .dropdown-menu a.menu,.topbar div>ul .menu-dropdown .dropdown-toggle,.nav .menu-dropdown .dropdown-toggle,.topbar div>ul .dropdown-menu .dropdown-toggle,.nav .dropdown-menu .dropdown-toggle{color:#ffffff;}.topbar div>ul .menu-dropdown a.menu.open,.nav .menu-dropdown a.menu.open,.topbar div>ul .dropdown-menu a.menu.open,.nav .dropdown-menu a.menu.open,.topbar div>ul .menu-dropdown .dropdown-toggle.open,.nav .menu-dropdown .dropdown-toggle.open,.topbar div>ul .dropdown-menu .dropdown-toggle.open,.nav .dropdown-menu .dropdown-toggle.open{background:#444;background:rgba(255, 255, 255, 0.05);} +.topbar div>ul .menu-dropdown li a,.nav .menu-dropdown li a,.topbar div>ul .dropdown-menu li a,.nav .dropdown-menu li a{color:#999;text-shadow:0 1px 0 rgba(0, 0, 0, 0.5);}.topbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.topbar div>ul .dropdown-menu li a:hover,.nav .dropdown-menu li a:hover{background-color:#191919;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919));background-image:-moz-linear-gradient(top, #292929, #191919);background-image:-ms-linear-gradient(top, #292929, #191919);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919));background-image:-webkit-linear-gradient(top, #292929, #191919);background-image:-o-linear-gradient(top, #292929, #191919);background-image:linear-gradient(top, #292929, #191919);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0);color:#ffffff;} +.topbar div>ul .menu-dropdown .active a,.nav .menu-dropdown .active a,.topbar div>ul .dropdown-menu .active a,.nav .dropdown-menu .active a{color:#ffffff;} +.topbar div>ul .menu-dropdown .divider,.nav .menu-dropdown .divider,.topbar div>ul .dropdown-menu .divider,.nav .dropdown-menu .divider{background-color:#222;border-color:#444;} +.topbar ul .menu-dropdown li a,.topbar ul .dropdown-menu li a{padding:4px 15px;} +li.menu,.dropdown{position:relative;} +a.menu:after,.dropdown-toggle:after{width:0;height:0;display:inline-block;content:"↓";text-indent:-99999px;vertical-align:top;margin-top:8px;margin-left:4px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #ffffff;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;} +.menu-dropdown,.dropdown-menu{background-color:#ffffff;float:left;display:none;position:absolute;top:40px;z-index:900;min-width:160px;max-width:220px;_width:160px;margin-left:0;margin-right:0;padding:6px 0;zoom:1;border-color:#999;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:0 1px 1px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.menu-dropdown li,.dropdown-menu li{float:none;display:block;background-color:none;} +.menu-dropdown .divider,.dropdown-menu .divider{height:1px;margin:5px 0;overflow:hidden;background-color:#eee;border-bottom:1px solid #ffffff;} +.topbar .dropdown-menu a,.dropdown-menu a{display:block;padding:4px 15px;clear:both;font-weight:normal;line-height:18px;color:#808080;text-shadow:0 1px 0 #ffffff;}.topbar .dropdown-menu a:hover,.dropdown-menu a:hover,.topbar .dropdown-menu a.hover,.dropdown-menu a.hover{background-color:#dddddd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd));background-image:-moz-linear-gradient(top, #eeeeee, #dddddd);background-image:-ms-linear-gradient(top, #eeeeee, #dddddd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd));background-image:-webkit-linear-gradient(top, #eeeeee, #dddddd);background-image:-o-linear-gradient(top, #eeeeee, #dddddd);background-image:linear-gradient(top, #eeeeee, #dddddd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0);color:#404040;text-decoration:none;-webkit-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);} +.open .menu,.dropdown.open .menu,.open .dropdown-toggle,.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);} +.open .menu-dropdown,.dropdown.open .menu-dropdown,.open .dropdown-menu,.dropdown.open .dropdown-menu{display:block;} +.tabs,.pills{margin:0 0 18px;padding:0;list-style:none;zoom:1;}.tabs:before,.pills:before,.tabs:after,.pills:after{display:table;content:"";zoom:1;} +.tabs:after,.pills:after{clear:both;} +.tabs>li,.pills>li{float:left;}.tabs>li>a,.pills>li>a{display:block;} +.tabs{border-color:#ddd;border-style:solid;border-width:0 0 1px;}.tabs>li{position:relative;margin-bottom:-1px;}.tabs>li>a{padding:0 15px;margin-right:2px;line-height:34px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.tabs>li>a:hover{text-decoration:none;background-color:#eee;border-color:#eee #eee #ddd;} +.tabs .active>a,.tabs .active>a:hover{color:#808080;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} +.tabs .menu-dropdown,.tabs .dropdown-menu{top:35px;border-width:1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;} +.tabs a.menu:after,.tabs .dropdown-toggle:after{border-top-color:#999;margin-top:15px;margin-left:5px;} +.tabs li.open.menu .menu,.tabs .open.dropdown .dropdown-toggle{border-color:#999;} +.tabs li.open a.menu:after,.tabs .dropdown.open .dropdown-toggle:after{border-top-color:#555;} +.pills a{margin:5px 3px 5px 0;padding:0 15px;line-height:30px;text-shadow:0 1px 1px #ffffff;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}.pills a:hover{color:#ffffff;text-decoration:none;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#00438a;} +.pills .active a{color:#ffffff;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#0069d6;} +.pills-vertical>li{float:none;} +.tab-content>.tab-pane,.pill-content>.pill-pane,.tab-content>div,.pill-content>div{display:none;} +.tab-content>.active,.pill-content>.active{display:block;} +.breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#f5f5f5;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5));background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline;text-shadow:0 1px 0 #ffffff;} +.breadcrumb .divider{padding:0 5px;color:#bfbfbf;} +.breadcrumb .active a{color:#404040;} +.hero-unit{background-color:#f5f5f5;margin-bottom:30px;padding:60px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} +.hero-unit p{font-size:18px;font-weight:200;line-height:27px;} +footer{margin-top:17px;padding-top:17px;border-top:1px solid #eee;} +.page-header{margin-bottom:17px;border-bottom:1px solid #ddd;-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}.page-header h1{margin-bottom:8px;} +.btn.danger,.alert-message.danger,.btn.danger:hover,.alert-message.danger:hover,.btn.error,.alert-message.error,.btn.error:hover,.alert-message.error:hover,.btn.success,.alert-message.success,.btn.success:hover,.alert-message.success:hover,.btn.info,.alert-message.info,.btn.info:hover,.alert-message.info:hover{color:#ffffff;} +.btn .close,.alert-message .close{font-family:Arial,sans-serif;line-height:18px;} +.btn.danger,.alert-message.danger,.btn.error,.alert-message.error{background-color:#c43c35;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#c43c35 #c43c35 #882a25;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.success,.alert-message.success{background-color:#57a957;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#57a957 #57a957 #3d773d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.info,.alert-message.info{background-color:#339bb9;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#339bb9 #339bb9 #22697d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn{cursor:pointer;display:inline-block;background-color:#e6e6e6;background-repeat:no-repeat;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);padding:5px 14px 6px;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);color:#333;font-size:13px;line-height:normal;border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-webkit-transition:0.1s linear all;-moz-transition:0.1s linear all;-ms-transition:0.1s linear all;-o-transition:0.1s linear all;transition:0.1s linear all;}.btn:hover{background-position:0 -15px;color:#333;text-decoration:none;} +.btn:focus{outline:1px dotted #666;} +.btn.primary{color:#ffffff;background-color:#0064cd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));background-image:-moz-linear-gradient(top, #049cdb, #0064cd);background-image:-ms-linear-gradient(top, #049cdb, #0064cd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));background-image:-webkit-linear-gradient(top, #049cdb, #0064cd);background-image:-o-linear-gradient(top, #049cdb, #0064cd);background-image:linear-gradient(top, #049cdb, #0064cd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#0064cd #0064cd #003f81;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.active,.btn:active{-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);} +.btn.disabled{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn[disabled]{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn.large{font-size:15px;line-height:normal;padding:9px 14px 9px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.btn.small{padding:7px 9px 7px;font-size:11px;} +:root .alert-message,:root .btn{border-radius:0 \0;} +button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0;} +.close{float:right;color:#000000;font-size:20px;font-weight:bold;line-height:13.5px;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=25);-khtml-opacity:0.25;-moz-opacity:0.25;opacity:0.25;}.close:hover{color:#000000;text-decoration:none;filter:alpha(opacity=40);-khtml-opacity:0.4;-moz-opacity:0.4;opacity:0.4;} +.alert-message{position:relative;padding:7px 15px;margin-bottom:18px;color:#404040;background-color:#eedc94;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94));background-image:-moz-linear-gradient(top, #fceec1, #eedc94);background-image:-ms-linear-gradient(top, #fceec1, #eedc94);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94));background-image:-webkit-linear-gradient(top, #fceec1, #eedc94);background-image:-o-linear-gradient(top, #fceec1, #eedc94);background-image:linear-gradient(top, #fceec1, #eedc94);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#eedc94 #eedc94 #e4c652;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);border-width:1px;border-style:solid;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);}.alert-message .close{margin-top:1px;*margin-top:0;} +.alert-message a{font-weight:bold;color:#404040;} +.alert-message.danger p a,.alert-message.error p a,.alert-message.success p a,.alert-message.info p a{color:#ffffff;} +.alert-message h5{line-height:18px;} +.alert-message p{margin-bottom:0;} +.alert-message div{margin-top:5px;margin-bottom:2px;line-height:28px;} +.alert-message .btn{-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);} +.alert-message.block-message{background-image:none;background-color:#fdf5d9;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);padding:14px;border-color:#fceec1;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.alert-message.block-message ul,.alert-message.block-message p{margin-right:30px;} +.alert-message.block-message ul{margin-bottom:0;} +.alert-message.block-message li{color:#404040;} +.alert-message.block-message .alert-actions{margin-top:5px;} +.alert-message.block-message.error,.alert-message.block-message.success,.alert-message.block-message.info{color:#404040;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} +.alert-message.block-message.error{background-color:#fddfde;border-color:#fbc7c6;} +.alert-message.block-message.success{background-color:#d1eed1;border-color:#bfe7bf;} +.alert-message.block-message.info{background-color:#ddf4fb;border-color:#c6edf9;} +.alert-message.block-message.danger p a,.alert-message.block-message.error p a,.alert-message.block-message.success p a,.alert-message.block-message.info p a{color:#404040;} +.pagination{height:36px;margin:18px 0;}.pagination ul{float:left;margin:0;border:1px solid #ddd;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);} +.pagination li{display:inline;} +.pagination a{float:left;padding:0 14px;line-height:34px;border-right:1px solid;border-right-color:#ddd;border-right-color:rgba(0, 0, 0, 0.15);*border-right-color:#ddd;text-decoration:none;} +.pagination a:hover,.pagination .active a{background-color:#c7eefe;} +.pagination .disabled a,.pagination .disabled a:hover{background-color:transparent;color:#bfbfbf;} +.pagination .next a{border:0;} +.well{background-color:#f5f5f5;margin-bottom:20px;padding:19px;min-height:20px;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);} +.modal-backdrop{background-color:#000000;position:fixed;top:0;left:0;right:0;bottom:0;z-index:10000;}.modal-backdrop.fade{opacity:0;} +.modal-backdrop,.modal-backdrop.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;} +.modal{position:fixed;top:50%;left:50%;z-index:11000;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal .close{margin-top:7px;} +.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;} +.modal.fade.in{top:50%;} +.modal-header{border-bottom:1px solid #eee;padding:5px 15px;} +.modal-body{padding:15px;} +.modal-body form{margin-bottom:0;} +.modal-footer{background-color:#f5f5f5;padding:14px 15px 15px;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;zoom:1;margin-bottom:0;}.modal-footer:before,.modal-footer:after{display:table;content:"";zoom:1;} +.modal-footer:after{clear:both;} +.modal-footer .btn{float:right;margin-left:5px;} +.modal .popover,.modal .twipsy{z-index:12000;} +.twipsy{display:block;position:absolute;visibility:visible;padding:5px;font-size:11px;z-index:1000;filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}.twipsy.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;} +.twipsy.above .twipsy-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.twipsy.left .twipsy-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.twipsy.below .twipsy-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.twipsy.right .twipsy-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.twipsy-inner{padding:3px 8px;background-color:#000000;color:white;text-align:center;max-width:200px;text-decoration:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.twipsy-arrow{position:absolute;width:0;height:0;} +.popover{position:absolute;top:0;left:0;z-index:1000;padding:5px;display:none;}.popover.above .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.popover.below .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.popover .arrow{position:absolute;width:0;height:0;} +.popover .inner{background:#000000;background:rgba(0, 0, 0, 0.8);padding:3px;overflow:hidden;width:280px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);} +.popover .title{background-color:#f5f5f5;padding:9px 15px;line-height:1;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;border-bottom:1px solid #eee;} +.popover .content{background-color:#ffffff;padding:14px;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover .content p,.popover .content ul,.popover .content ol{margin-bottom:0;} +.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;} +.label{padding:1px 3px 2px;font-size:9.75px;font-weight:bold;color:#ffffff;text-transform:uppercase;white-space:nowrap;background-color:#bfbfbf;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;}.label.important{background-color:#c43c35;} +.label.warning{background-color:#f89406;} +.label.success{background-color:#46a546;} +.label.notice{background-color:#62cffc;} +.media-grid{margin-left:-20px;margin-bottom:0;zoom:1;}.media-grid:before,.media-grid:after{display:table;content:"";zoom:1;} +.media-grid:after{clear:both;} +.media-grid li{display:inline;} +.media-grid a{float:left;padding:4px;margin:0 0 18px 20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}.media-grid a img{display:block;} +.media-grid a:hover{border-color:#0069d6;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);} diff --git a/Battery3D/css/custom.css b/Battery3D/css/custom.css new file mode 100644 index 0000000..7f84f5a --- /dev/null +++ b/Battery3D/css/custom.css @@ -0,0 +1,41 @@ +html { + background: #2f3035; /* Old browsers */ + background-image: -moz-linear-gradient(top, #191a1e 1%, #2f3035 100%); /* FF3.6+ */ + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(1%,#191a1e), color-stop(100%,#2f3035)); /* Chrome,Safari4+ */ + background-image: -webkit-linear-gradient(top, #191a1e 1%,#2f3035 100%); /* Chrome10+,Safari5.1+ */ + background-image: -o-linear-gradient(top, #191a1e 1%,#2f3035 100%); /* Opera 11.10+ */ + background-image: -ms-linear-gradient(top, #191a1e 1%,#2f3035 100%); /* IE10+ */ + background-image: linear-gradient(to bottom, #191a1e 1%,#2f3035 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#191a1e', endColorstr='#2f3035',GradientType=0 ); /* IE6-9 */ + background-size: auto 100%; + min-height: 100%; +} +body { + background: transparent; + text-align: center; + min-height: 100%; +} +p { + display: none; + color: #888; + width: 50%; + margin: 10px auto; +} +h1 { + padding: 100px 0 10px 0; + color: #777; + font-weight: 100; + font-family: "Segoe UI Light","Helvetica Neue","Open Sans"; + font-size: 4em; + text-shadow: 0 -1px 0 rgba(0,0,0,0.75); +} +#output { + margin: 0 auto; + width: 700px; +} + +.no-webgl #fail, +.no-js #fail, +.is-file-protocol #fileprotocol { + display: block; +} \ No newline at end of file diff --git a/Battery3D/img/.gitignore b/Battery3D/img/.gitignore new file mode 100644 index 0000000..4397c3a --- /dev/null +++ b/Battery3D/img/.gitignore @@ -0,0 +1,2 @@ +!.gitignore + diff --git a/Battery3D/img/noise.png b/Battery3D/img/noise.png new file mode 100644 index 0000000000000000000000000000000000000000..819fc09cd0096fd764e3f602c9b07d7911645afa GIT binary patch literal 6050 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 JQkX@^{{Y5wFH8Uc literal 0 HcmV?d00001 diff --git a/Battery3D/img/particle.png b/Battery3D/img/particle.png new file mode 100644 index 0000000000000000000000000000000000000000..c69448d1e7912df63a0287edc2d923a1627c3ca1 GIT binary patch literal 1112 zcmaJ=TSyd97@i7RTazr3E+7mebfcY{?QPmMcX!rYXk^)4Xi*EtnWH=DT$(w$yFNsj z8a-5WA@V7x7awA&9x958>Y)M?E2xN|f}nyRdx*@yI-{=k&^B=9od58B-+w(v<4rp& zoa>z!hE>EG`2^Zm+pBCT`X4)5U4gcBFq(!*y#r=N6JQ}(ZwGix6+1x!h;n}aV-Ucw zC4`bn!?duAlXR67Z48-H4aCNb_DN?icr?VwZ9g2dNbcL`jPgT!vPKnq3$WR%7} z6D0eZQc_=+TQDeI@xQGcv zsGBKW?=Ds`p3$LhWpo3NB;B|mN{VLNGk8JZVwwd-O#(4KNFW7LQDiQ}_!?L*%QIn* zmtmr8h>x%#kDFonP>5w{f04`UQjZEWSmeqJ+-OOzJqoIUJoCU*dV$MA~yGN+V| zC0tT(jw_drB~p@0p)6vRPObc zGgqAV>B^zg3CHH1>?h*FoqpfXiDw%tQ*ZixEhouSlaA@|NG0{^isSA0n{WGH4m=<8 zyJdObyS0N)Z?-1Rm-AKU4zCK_t6tld9IU&(X#3^m1G^1u<3!WSslv6k6)z55ZqKgh z-Lk6pVqsIu!OfZB9~WwFeBXIuSvf6r?)8X;5BYoC$?-L}>mM+;#+na!M_WEWIxFq* z-E>aBAO5%@Ix-p9cO3LxZ>{}ZHTCdX?7)%c8+We0Tiksqe&W}}*U2&(bDR$j-QSd{ Px8Hg!+Qb*aZT)`$%!PK@ literal 0 HcmV?d00001 diff --git a/Battery3D/img/texture.jpg b/Battery3D/img/texture.jpg new file mode 100644 index 0000000000000000000000000000000000000000..95092bf8a6ee24173a232d0516b0f23b9a594abc GIT binary patch literal 36852 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 literal 0 HcmV?d00001 diff --git a/Battery3D/img/texture.png b/Battery3D/img/texture.png new file mode 100644 index 0000000000000000000000000000000000000000..72435b794833ef367d0e93c34a2842dd4f890ece GIT binary patch literal 512497 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�abG)(V&Nj5OF?M~FH%M{>hEIBJLhMQb*((}Dja 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
    _m$zSW6e57wTazv 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,G{zpv` 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%0DaSlpKB6dSFGY7rOhIg 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�fHKyeSPW_@W#0A|g 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 z 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 z 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 literal 0 HcmV?d00001 diff --git a/Battery3D/index.html b/Battery3D/index.html new file mode 100644 index 0000000..e7d6c22 --- /dev/null +++ b/Battery3D/index.html @@ -0,0 +1,42 @@ + + + + + + + + + ThreeJS Battery + + + + + + + + + + + + + +

      ThreeJS Battery

      + +

      + Oops, it appears you're using a browser that doesn't support WebGL, or has JavaScript disabled. +

      + +

      + Oops, it appears you're trying to run this locally. It will 'work', but the images used for reflection mapping and particles will not be loaded in because of CORS control. +

      + +
      + + + + + + + + + diff --git a/Battery3D/js/custom.js b/Battery3D/js/custom.js new file mode 100644 index 0000000..cee77f7 --- /dev/null +++ b/Battery3D/js/custom.js @@ -0,0 +1,257 @@ +/*global $,console,THREE,TWEEN*/ +/*jshint unused:false*/ + +var testRender = (function() { + + "use strict"; + + // set the scene size + var WIDTH = 700; + var HEIGHT = 700; + + // set some camera attributes + var VIEW_ANGLE = 45; + var ASPECT = WIDTH / HEIGHT; + var NEAR = 0.1; + var FAR = 10000; + + // create a WebGL renderer, camera + // and a scene + var renderer = new THREE.WebGLRenderer({ + antialias: true + }); + var camera = new THREE.PerspectiveCamera( + VIEW_ANGLE, + ASPECT, + NEAR, + FAR + ); + + var objs = {}; + + var scene = new THREE.Scene(); + + var render = function() { + renderer.render(scene, camera); + }; + + var animateProgress = function() { + window.requestAnimationFrame(animateProgress); + TWEEN.update(); + camera.lookAt(new THREE.Vector3(0,0,0)); + objs.particleSystem.rotation.y += 0.01; + render(); + }; + + var startAnimations = function() { + + new TWEEN.Tween(camera.position) + .to({z: -800, y:600}, 4000 ) + .easing( TWEEN.Easing.Quadratic.InOut ) + .start(); + + // animate height + new TWEEN.Tween(objs.goop.scale) + .to({y:8},5000) + .easing( TWEEN.Easing.Quadratic.InOut ) + .delay(1750) + .start(); + + // animate height + new TWEEN.Tween(objs.goop.position) + .to({y:130},5000) + .easing( TWEEN.Easing.Quadratic.InOut ) + .delay(1750) + .start(); + + // animate colour of goop + new TWEEN.Tween(objs.goop.material.color) + .to({r:0.25,g:0.75},3000) + .easing( TWEEN.Easing.Quadratic.InOut ) + .delay(1750) + .start(); + + // animate particle height + new TWEEN.Tween(objs.particleSystem.scale) + .to({y:8},5000) + .easing( TWEEN.Easing.Quadratic.InOut ) + .delay(1750) + .start(); + + }; + + var init = function() { + + // get the DOM element to attach to + // - assume we've got jQuery to hand + var $container = $('#output'); + + // add the camera to the scene + scene.add(camera); + + // the camera starts at 0,0,0 + // so pull it back + camera.position.z = -500; + camera.position.y = 800; + camera.lookAt(new THREE.Vector3(0,0,0)); + + // texture cube images + var urls = [ + 'img/texture.jpg', + 'img/texture.jpg', + 'img/texture.jpg', + 'img/texture.jpg', + 'img/texture.jpg', + 'img/texture.jpg' + ]; + + // wrap it up into the object that we need + var textureCube = THREE.ImageUtils.loadTextureCube(urls); + + // glass material + var glassMaterial = new THREE.MeshLambertMaterial({ + color: 0xffffff, + opacity: 0.25, + transparent: true, + reflectivity: 1, + envMap: textureCube, + shininess: 500, + shading: THREE.SmoothShading + }); + var goopMaterial = new THREE.MeshBasicMaterial({ + color: 0xff0000, + opacity: 0.5, + transparent: true + }); + var baseMaterial = new THREE.MeshBasicMaterial({ + color: 0x333333, + envMap: textureCube, + shininess: 200, + shading: THREE.SmoothShading + }); + var terminalMaterial = new THREE.MeshBasicMaterial({ + color: 0x444444, + envMap: textureCube, + shininess: 40, + shading: THREE.SmoothShading + }); + var planeMaterial = new THREE.MeshBasicMaterial({ + color: 0xFFFFFF + }); + + // edge softener + var edgeModifier = new THREE.SubdivisionModifier(2); + + // battery base + var batteryBase = new THREE.Mesh(new THREE.CylinderGeometry(100,100,10,50,50,false),baseMaterial); + batteryBase.position.y = 0; + objs.batteryBase = batteryBase; + scene.add(batteryBase); + + // battery outside + var batteryShell = new THREE.Mesh(new THREE.CylinderGeometry(99,99,300,50,50,false),glassMaterial); + batteryShell.position.y = 150; + objs.batteryShell = batteryShell; + scene.add(batteryShell); + + // battery goop + var goop = new THREE.Mesh(new THREE.CylinderGeometry(98,98,30,50,50,false),goopMaterial); + goop.position.y = 17; + objs.goop = goop; + scene.add(goop); + + // battery top + var batteryTop = new THREE.Mesh(new THREE.CylinderGeometry(100,100,10,50,50,false),baseMaterial); + batteryTop.position.y = 300; + edgeModifier.modify(batteryTop.geometry); + objs.batteryTop = batteryTop; + scene.add(batteryTop); + + // battery terminal + var batteryTerminal = new THREE.Mesh(new THREE.CylinderGeometry(20,20,20,50,50,false),terminalMaterial); + batteryTerminal.position.y = 305; + edgeModifier.modify(batteryTerminal.geometry); + objs.batteryTerminal = batteryTerminal; + scene.add(batteryTerminal); + + // attach the render-supplied DOM element + $container.append(renderer.domElement); + + // add some directional lighting + var directionalLight = new THREE.DirectionalLight(0xffffff); + directionalLight.position.set(200, 50, 0).normalize(); + scene.add(directionalLight); + + // create the particle variables + var particleCount = 90; + var particles = new THREE.Geometry(); + // create the particle variables + var pMaterial = new THREE.ParticleBasicMaterial({ + color: 0xFFFFFF, + size: 15, + map: THREE.ImageUtils.loadTexture( + "img/particle.png" + ), + depthWrite: false, + opacity: 0.65, + blending: THREE.AdditiveBlending, + transparent: true + }); + + // now create the individual particles + for(var p = 0; p < particleCount; p++) { + + // create a particle with random position values + var pX = (Math.random() * 120) - 60, + pY = Math.random() * 30, + pZ = (Math.random() * 120) - 60, + particle = new THREE.Vertex( + new THREE.Vector3(pX, pY, pZ) + ); + + // add it to the geometry + particles.vertices.push(particle); + } + + // create the particle system + var particleSystem = new THREE.ParticleSystem(particles, pMaterial); + + // add it to the scene + objs.particleSystem = particleSystem; + scene.add(particleSystem); + + + + + // start the renderer + renderer.setSize(WIDTH, HEIGHT); + renderer.shadowMapEnabled = true; + renderer.shadowMapSoft = true; + render(); + + // kick of camera dolly animation and colour/particle animation + startAnimations(); + animateProgress(); + + }; + + if (document.location.protocol === "file:") { + $("html").addClass("is-file-protocol"); + } + + $.imgpreload(["img/texture.jpg","img/particle.png"], { + all: function() { + init(); + } + }); + + var getObjs = function() { + return objs; + }; + + return { + getObjs: getObjs, + render: render + }; + +}()); \ No newline at end of file diff --git a/Battery3D/js/jquery.imgpreload.min.js b/Battery3D/js/jquery.imgpreload.min.js new file mode 100644 index 0000000..7c68119 --- /dev/null +++ b/Battery3D/js/jquery.imgpreload.min.js @@ -0,0 +1,3 @@ +/* v1.4 */ +/* https://github.com/farinspace/jquery.imgpreload */ +if("undefined"!=typeof jQuery){(function(a){a.imgpreload=function(b,c){c=a.extend({},a.fn.imgpreload.defaults,c instanceof Function?{all:c}:c);if("string"==typeof b){b=new Array(b)}var d=new Array;a.each(b,function(e,f){var g=new Image;var h=f;var i=g;if("string"!=typeof f){h=a(f).attr("src");i=f}a(g).bind("load error",function(e){d.push(i);a.data(i,"loaded","error"==e.type?false:true);if(c.each instanceof Function){c.each.call(i)}if(d.length>=b.length&&c.all instanceof Function){c.all.call(d)}a(this).unbind("load error")});g.src=h})};a.fn.imgpreload=function(b){a.imgpreload(this,b);return this};a.fn.imgpreload.defaults={each:null,all:null}})(jQuery)} \ No newline at end of file diff --git a/Battery3D/js/jquery.min.js b/Battery3D/js/jquery.min.js new file mode 100644 index 0000000..ee02337 --- /dev/null +++ b/Battery3D/js/jquery.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.1 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
      a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
      "+""+"
      ",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
      t
      ",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
      ",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; +f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

      ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
      ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
      ","
      "],thead:[1,"","
      "],tr:[2,"","
      "],td:[3,"","
      "],col:[2,"","
      "],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
      ","
      "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() +{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
      ").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/Battery3D/js/modernizr-2.0.6.min.js b/Battery3D/js/modernizr-2.0.6.min.js new file mode 100644 index 0000000..4f00b71 --- /dev/null +++ b/Battery3D/js/modernizr-2.0.6.min.js @@ -0,0 +1,4 @@ +/* Modernizr 2.0.6 | MIT & BSD + * Contains: All core tests, html5shiv, yepnope, respond.js. Get your own custom build at www.modernizr.com/download/ + */ +;window.Modernizr=function(a,b,c){function I(){e.input=function(a){for(var b=0,c=a.length;b",a,""].join(""),k.id=i,k.innerHTML+=f,g.appendChild(k),h=c(k,a),k.parentNode.removeChild(k);return!!h},w=function(b){if(a.matchMedia)return matchMedia(b).matches;var c;v("@media "+b+" { #"+i+" { position: absolute; } }",function(b){c=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle).position=="absolute"});return c},x=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=D(e[d],"function"),D(e[d],c)||(e[d]=c),e.removeAttribute(d))),e=null;return f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),y,z={}.hasOwnProperty,A;!D(z,c)&&!D(z.call,c)?A=function(a,b){return z.call(a,b)}:A=function(a,b){return b in a&&D(a.constructor.prototype[b],c)};var H=function(c,d){var f=c.join(""),g=d.length;v(f,function(c,d){var f=b.styleSheets[b.styleSheets.length-1],h=f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"",i=c.childNodes,j={};while(g--)j[i[g].id]=i[g];e.touch="ontouchstart"in a||j.touch.offsetTop===9,e.csstransforms3d=j.csstransforms3d.offsetLeft===9,e.generatedcontent=j.generatedcontent.offsetHeight>=1,e.fontface=/src/i.test(h)&&h.indexOf(d.split(" ")[0])===0},g,d)}(['@font-face {font-family:"font";src:url("https://")}',["@media (",o.join("touch-enabled),("),i,")","{#touch{top:9px;position:absolute}}"].join(""),["@media (",o.join("transform-3d),("),i,")","{#csstransforms3d{left:9px;position:absolute}}"].join(""),['#generatedcontent:after{content:"',m,'";visibility:hidden}'].join("")],["fontface","touch","csstransforms3d","generatedcontent"]);r.flexbox=function(){function c(a,b,c,d){a.style.cssText=o.join(b+":"+c+";")+(d||"")}function a(a,b,c,d){b+=":",a.style.cssText=(b+o.join(c+";"+b)).slice(0,-b.length)+(d||"")}var d=b.createElement("div"),e=b.createElement("div");a(d,"display","box","width:42px;padding:0;"),c(e,"box-flex","1","width:10px;"),d.appendChild(e),g.appendChild(d);var f=e.offsetWidth===42;d.removeChild(e),g.removeChild(d);return f},r.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},r.canvastext=function(){return!!e.canvas&&!!D(b.createElement("canvas").getContext("2d").fillText,"function")},r.webgl=function(){return!!a.WebGLRenderingContext},r.touch=function(){return e.touch},r.geolocation=function(){return!!navigator.geolocation},r.postmessage=function(){return!!a.postMessage},r.websqldatabase=function(){var b=!!a.openDatabase;return b},r.indexedDB=function(){for(var b=-1,c=p.length;++b7)},r.history=function(){return!!a.history&&!!history.pushState},r.draganddrop=function(){return x("dragstart")&&x("drop")},r.websockets=function(){for(var b=-1,c=p.length;++b";return(a.firstChild&&a.firstChild.namespaceURI)==q.svg},r.smil=function(){return!!b.createElementNS&&/SVG/.test(n.call(b.createElementNS(q.svg,"animate")))},r.svgclippaths=function(){return!!b.createElementNS&&/SVG/.test(n.call(b.createElementNS(q.svg,"clipPath")))};for(var J in r)A(r,J)&&(y=J.toLowerCase(),e[y]=r[J](),u.push((e[y]?"":"no-")+y));e.input||I(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)A(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return;b=typeof b=="boolean"?b:!!b(),g.className+=" "+(b?"":"no-")+a,e[a]=b}return e},B(""),j=l=null,a.attachEvent&&function(){var a=b.createElement("div");a.innerHTML="";return a.childNodes.length!==1}()&&function(a,b){function s(a){var b=-1;while(++b=u.minw)&&(!u.maxw||u.maxw&&l<=u.maxw))m[u.media]||(m[u.media]=[]),m[u.media].push(f[u.rules])}for(var t in g)g[t]&&g[t].parentNode===j&&j.removeChild(g[t]);for(var t in m){var v=c.createElement("style"),w=m[t].join("\n");v.type="text/css",v.media=t,v.styleSheet?v.styleSheet.cssText=w:v.appendChild(c.createTextNode(w)),n.appendChild(v),g.push(v)}j.insertBefore(n,o.nextSibling)}},s=function(a,b){var c=t();if(!!c){c.open("GET",a,!0),c.onreadystatechange=function(){c.readyState==4&&(c.status==200||c.status==304)&&b(c.responseText)};if(c.readyState==4)return;c.send()}},t=function(){var a=!1,b=[function(){return new ActiveXObject("Microsoft.XMLHTTP")},function(){return new XMLHttpRequest}],c=b.length;while(c--){try{a=b[c]()}catch(d){continue}break}return function(){return a}}();m(),respond.update=m,a.addEventListener?a.addEventListener("resize",u,!1):a.attachEvent&&a.attachEvent("onresize",u)}}(this,Modernizr.mq("only all")),function(a,b,c){function k(a){return!a||a=="loaded"||a=="complete"}function j(){var a=1,b=-1;while(p.length- ++b)if(p[b].s&&!(a=p[b].r))break;a&&g()}function i(a){var c=b.createElement("script"),d;c.src=a.s,c.onreadystatechange=c.onload=function(){!d&&k(c.readyState)&&(d=1,j(),c.onload=c.onreadystatechange=null)},m(function(){d||(d=1,j())},H.errorTimeout),a.e?c.onload():n.parentNode.insertBefore(c,n)}function h(a){var c=b.createElement("link"),d;c.href=a.s,c.rel="stylesheet",c.type="text/css";if(!a.e&&(w||r)){var e=function(a){m(function(){if(!d)try{a.sheet.cssRules.length?(d=1,j()):e(a)}catch(b){b.code==1e3||b.message=="security"||b.message=="denied"?(d=1,m(function(){j()},0)):e(a)}},0)};e(c)}else c.onload=function(){d||(d=1,m(function(){j()},0))},a.e&&c.onload();m(function(){d||(d=1,j())},H.errorTimeout),!a.e&&n.parentNode.insertBefore(c,n)}function g(){var a=p.shift();q=1,a?a.t?m(function(){a.t=="c"?h(a):i(a)},0):(a(),j()):q=0}function f(a,c,d,e,f,h){function i(){!o&&k(l.readyState)&&(r.r=o=1,!q&&j(),l.onload=l.onreadystatechange=null,m(function(){u.removeChild(l)},0))}var l=b.createElement(a),o=0,r={t:d,s:c,e:h};l.src=l.data=c,!s&&(l.style.display="none"),l.width=l.height="0",a!="object"&&(l.type=d),l.onload=l.onreadystatechange=i,a=="img"?l.onerror=i:a=="script"&&(l.onerror=function(){r.e=r.r=1,g()}),p.splice(e,0,r),u.insertBefore(l,s?null:n),m(function(){o||(u.removeChild(l),r.r=r.e=o=1,j())},H.errorTimeout)}function e(a,b,c){var d=b=="c"?z:y;q=0,b=b||"j",C(a)?f(d,a,b,this.i++,l,c):(p.splice(this.i++,0,a),p.length==1&&g());return this}function d(){var a=H;a.loader={load:e,i:0};return a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=r&&!s,u=s?l:n.parentNode,v=a.opera&&o.call(a.opera)=="[object Opera]",w="webkitAppearance"in l.style,x=w&&"async"in b.createElement("script"),y=r?"object":v||x?"img":"script",z=w?"img":y,A=Array.isArray||function(a){return o.call(a)=="[object Array]"},B=function(a){return Object(a)===a},C=function(a){return typeof a=="string"},D=function(a){return o.call(a)=="[object Function]"},E=[],F={},G,H;H=function(a){function f(a){var b=a.split("!"),c=E.length,d=b.pop(),e=b.length,f={url:d,origUrl:d,prefixes:b},g,h;for(h=0;h + + var i, f, p, q, t; + + if ( v === 0 ) { + + this.r = this.g = this.b = 0; + + } else { + + i = Math.floor( h * 6 ); + f = ( h * 6 ) - i; + p = v * ( 1 - s ); + q = v * ( 1 - ( s * f ) ); + t = v * ( 1 - ( s * ( 1 - f ) ) ); + + if ( i === 0 ) { + + this.r = v; + this.g = t; + this.b = p; + + } else if ( i === 1 ) { + + this.r = q; + this.g = v; + this.b = p; + + } else if ( i === 2 ) { + + this.r = p; + this.g = v; + this.b = t; + + } else if ( i === 3 ) { + + this.r = p; + this.g = q; + this.b = v; + + } else if ( i === 4 ) { + + this.r = t; + this.g = p; + this.b = v; + + } else if ( i === 5 ) { + + this.r = v; + this.g = p; + this.b = q; + + } + + } + + return this; + + }, + + setHex: function ( hex ) { + + hex = Math.floor( hex ); + + this.r = ( hex >> 16 & 255 ) / 255; + this.g = ( hex >> 8 & 255 ) / 255; + this.b = ( hex & 255 ) / 255; + + return this; + + }, + + lerpSelf: function ( color, alpha ) { + + this.r += ( color.r - this.r ) * alpha; + this.g += ( color.g - this.g ) * alpha; + this.b += ( color.b - this.b ) * alpha; + + return this; + + }, + + getHex: function () { + + return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; + + }, + + getContextStyle: function () { + + return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; + + }, + + clone: function () { + + return new THREE.Color().setRGB( this.r, this.g, this.b ); + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.x = x || 0; + this.y = y || 0; + +}; + +THREE.Vector2.prototype = { + + constructor: THREE.Vector2, + + set: function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + add: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + + return this; + + }, + + addSelf: function ( v ) { + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + sub: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + + return this; + + }, + + subSelf: function ( v ) { + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.x *= s; + this.y *= s; + + return this; + + }, + + divideScalar: function ( s ) { + + if ( s ) { + + this.x /= s; + this.y /= s; + + } else { + + this.set( 0, 0 ); + + } + + return this; + + }, + + negate: function() { + + return this.multiplyScalar( - 1 ); + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y; + + }, + + length: function () { + + return Math.sqrt( this.lengthSq() ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + setLength: function ( l ) { + + return this.normalize().multiplyScalar( l ); + + }, + + lerpSelf: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + + return this; + + }, + + equals: function( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) ); + + }, + + isZero: function ( v ) { + + return this.lengthSq() < ( v !== undefined ? v : 0.0001 ); + + }, + + clone: function () { + + return new THREE.Vector2( this.x, this.y ); + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + +}; + + +THREE.Vector3.prototype = { + + constructor: THREE.Vector3, + + set: function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + setX: function ( x ) { + + this.x = x; + + return this; + + }, + + setY: function ( y ) { + + this.y = y; + + return this; + + }, + + setZ: function ( z ) { + + this.z = z; + + return this; + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + add: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + + return this; + + }, + + addSelf: function ( v ) { + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar: function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + sub: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + + return this; + + }, + + subSelf: function ( v ) { + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + multiply: function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + multiplySelf: function ( v ) { + + this.x *= v.x; + this.y *= v.y; + this.z *= v.z; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.x *= s; + this.y *= s; + this.z *= s; + + return this; + + }, + + divideSelf: function ( v ) { + + this.x /= v.x; + this.y /= v.y; + this.z /= v.z; + + return this; + + }, + + divideScalar: function ( s ) { + + if ( s ) { + + this.x /= s; + this.y /= s; + this.z /= s; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + + } + + return this; + + }, + + + negate: function() { + + return this.multiplyScalar( - 1 ); + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq: function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length: function () { + + return Math.sqrt( this.lengthSq() ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + return this.normalize().multiplyScalar( l ); + + }, + + lerpSelf: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + + return this; + + }, + + cross: function ( a, b ) { + + this.x = a.y * b.z - a.z * b.y; + this.y = a.z * b.x - a.x * b.z; + this.z = a.x * b.y - a.y * b.x; + + return this; + + }, + + crossSelf: function ( v ) { + + var x = this.x, y = this.y, z = this.z; + + this.x = y * v.z - z * v.y; + this.y = z * v.x - x * v.z; + this.z = x * v.y - y * v.x; + + return this; + + }, + + angleTo: function ( v ) { + + return Math.acos( this.dot( v ) / this.length() / v.length() ); + + }, + + distanceTo: function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared: function ( v ) { + + return new THREE.Vector3().sub( this, v ).lengthSq(); + + }, + + getPositionFromMatrix: function ( m ) { + + this.x = m.elements[12]; + this.y = m.elements[13]; + this.z = m.elements[14]; + + return this; + + }, + + setEulerFromRotationMatrix: function ( m, order ) { + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + // clamp, to handle numerical problems + + function clamp( x ) { + + return Math.min( Math.max( x, -1 ), 1 ); + + } + + var te = m.elements; + var m11 = te[0], m12 = te[4], m13 = te[8]; + var m21 = te[1], m22 = te[5], m23 = te[9]; + var m31 = te[2], m32 = te[6], m33 = te[10]; + + if ( order === undefined || order === 'XYZ' ) { + + this.y = Math.asin( clamp( m13 ) ); + + if ( Math.abs( m13 ) < 0.99999 ) { + + this.x = Math.atan2( - m23, m33 ); + this.z = Math.atan2( - m12, m11 ); + + } else { + + this.x = Math.atan2( m32, m22 ); + this.z = 0; + + } + + } else if ( order === 'YXZ' ) { + + this.x = Math.asin( - clamp( m23 ) ); + + if ( Math.abs( m23 ) < 0.99999 ) { + + this.y = Math.atan2( m13, m33 ); + this.z = Math.atan2( m21, m22 ); + + } else { + + this.y = Math.atan2( - m31, m11 ); + this.z = 0; + + } + + } else if ( order === 'ZXY' ) { + + this.x = Math.asin( clamp( m32 ) ); + + if ( Math.abs( m32 ) < 0.99999 ) { + + this.y = Math.atan2( - m31, m33 ); + this.z = Math.atan2( - m12, m22 ); + + } else { + + this.y = 0; + this.z = Math.atan2( m21, m11 ); + + } + + } else if ( order === 'ZYX' ) { + + this.y = Math.asin( - clamp( m31 ) ); + + if ( Math.abs( m31 ) < 0.99999 ) { + + this.x = Math.atan2( m32, m33 ); + this.z = Math.atan2( m21, m11 ); + + } else { + + this.x = 0; + this.z = Math.atan2( - m12, m22 ); + + } + + } else if ( order === 'YZX' ) { + + this.z = Math.asin( clamp( m21 ) ); + + if ( Math.abs( m21 ) < 0.99999 ) { + + this.x = Math.atan2( - m23, m22 ); + this.y = Math.atan2( - m31, m11 ); + + } else { + + this.x = 0; + this.y = Math.atan2( m13, m33 ); + + } + + } else if ( order === 'XZY' ) { + + this.z = Math.asin( - clamp( m12 ) ); + + if ( Math.abs( m12 ) < 0.99999 ) { + + this.x = Math.atan2( m32, m22 ); + this.y = Math.atan2( m13, m11 ); + + } else { + + this.x = Math.atan2( - m23, m33 ); + this.y = 0; + + } + + } + + return this; + + }, + + setEulerFromQuaternion: function ( q, order ) { + + // q is assumed to be normalized + + // clamp, to handle numerical problems + + function clamp( x ) { + + return Math.min( Math.max( x, -1 ), 1 ); + + } + + // http://www.mathworks.com/matlabcentral/fileexchange/20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/content/SpinCalc.m + + var sqx = q.x * q.x; + var sqy = q.y * q.y; + var sqz = q.z * q.z; + var sqw = q.w * q.w; + + if ( order === undefined || order === 'XYZ' ) { + + this.x = Math.atan2( 2 * ( q.x * q.w - q.y * q.z ), ( sqw - sqx - sqy + sqz ) ); + this.y = Math.asin( clamp( 2 * ( q.x * q.z + q.y * q.w ) ) ); + this.z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw + sqx - sqy - sqz ) ); + + } else if ( order === 'YXZ' ) { + + this.x = Math.asin( clamp( 2 * ( q.x * q.w - q.y * q.z ) ) ); + this.y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw - sqx - sqy + sqz ) ); + this.z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw - sqx + sqy - sqz ) ); + + } else if ( order === 'ZXY' ) { + + this.x = Math.asin( clamp( 2 * ( q.x * q.w + q.y * q.z ) ) ); + this.y = Math.atan2( 2 * ( q.y * q.w - q.z * q.x ), ( sqw - sqx - sqy + sqz ) ); + this.z = Math.atan2( 2 * ( q.z * q.w - q.x * q.y ), ( sqw - sqx + sqy - sqz ) ); + + } else if ( order === 'ZYX' ) { + + this.x = Math.atan2( 2 * ( q.x * q.w + q.z * q.y ), ( sqw - sqx - sqy + sqz ) ); + this.y = Math.asin( clamp( 2 * ( q.y * q.w - q.x * q.z ) ) ); + this.z = Math.atan2( 2 * ( q.x * q.y + q.z * q.w ), ( sqw + sqx - sqy - sqz ) ); + + } else if ( order === 'YZX' ) { + + this.x = Math.atan2( 2 * ( q.x * q.w - q.z * q.y ), ( sqw - sqx + sqy - sqz ) ); + this.y = Math.atan2( 2 * ( q.y * q.w - q.x * q.z ), ( sqw + sqx - sqy - sqz ) ); + this.z = Math.asin( clamp( 2 * ( q.x * q.y + q.z * q.w ) ) ); + + } else if ( order === 'XZY' ) { + + this.x = Math.atan2( 2 * ( q.x * q.w + q.y * q.z ), ( sqw - sqx + sqy - sqz ) ); + this.y = Math.atan2( 2 * ( q.x * q.z + q.y * q.w ), ( sqw + sqx - sqy - sqz ) ); + this.z = Math.asin( clamp( 2 * ( q.z * q.w - q.x * q.y ) ) ); + + } + + return this; + + }, + + getScaleFromMatrix: function ( m ) { + + var sx = this.set( m.elements[0], m.elements[1], m.elements[2] ).length(); + var sy = this.set( m.elements[4], m.elements[5], m.elements[6] ).length(); + var sz = this.set( m.elements[8], m.elements[9], m.elements[10] ).length(); + + this.x = sx; + this.y = sy; + this.z = sz; + + return this; + }, + + equals: function ( v ) { + + return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); + + }, + + isZero: function ( v ) { + + return this.lengthSq() < ( v !== undefined ? v : 0.0001 ); + + }, + + clone: function () { + + return new THREE.Vector3( this.x, this.y, this.z ); + + } + +}; +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Vector4 = function ( x, y, z, w ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Vector4.prototype = { + + constructor: THREE.Vector4, + + set: function ( x, y, z, w ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + copy: function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + this.w = ( v.w !== undefined ) ? v.w : 1; + + return this; + + }, + + add: function ( a, b ) { + + this.x = a.x + b.x; + this.y = a.y + b.y; + this.z = a.z + b.z; + this.w = a.w + b.w; + + return this; + + }, + + addSelf: function ( v ) { + + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; + + return this; + + }, + + sub: function ( a, b ) { + + this.x = a.x - b.x; + this.y = a.y - b.y; + this.z = a.z - b.z; + this.w = a.w - b.w; + + return this; + + }, + + subSelf: function ( v ) { + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + + return this; + + }, + + multiplyScalar: function ( s ) { + + this.x *= s; + this.y *= s; + this.z *= s; + this.w *= s; + + return this; + + }, + + divideScalar: function ( s ) { + + if ( s ) { + + this.x /= s; + this.y /= s; + this.z /= s; + this.w /= s; + + } else { + + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 1; + + } + + return this; + + }, + + + negate: function() { + + return this.multiplyScalar( -1 ); + + }, + + dot: function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + + }, + + lengthSq: function () { + + return this.dot( this ); + + }, + + length: function () { + + return Math.sqrt( this.lengthSq() ); + + }, + + lengthManhattan: function () { + + return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); + + }, + + normalize: function () { + + return this.divideScalar( this.length() ); + + }, + + setLength: function ( l ) { + + return this.normalize().multiplyScalar( l ); + + }, + + lerpSelf: function ( v, alpha ) { + + this.x += ( v.x - this.x ) * alpha; + this.y += ( v.y - this.y ) * alpha; + this.z += ( v.z - this.z ) * alpha; + this.w += ( v.w - this.w ) * alpha; + + return this; + + }, + + clone: function () { + + return new THREE.Vector4( this.x, this.y, this.z, this.w ); + + }, + + setAxisAngleFromQuaternion: function ( q ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm + + // q is assumed to be normalized + + this.w = 2 * Math.acos( q.w ); + + var s = Math.sqrt( 1 - q.w * q.w ); + + if ( s < 0.0001 ) { + + this.x = 1; + this.y = 0; + this.z = 0; + + } else { + + this.x = q.x / s; + this.y = q.y / s; + this.z = q.z / s; + + } + + return this; + + }, + + setAxisAngleFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var angle, x, y, z, // variables for result + epsilon = 0.01, // margin to allow for rounding errors + epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees + + te = m.elements, + + m11 = te[0], m12 = te[4], m13 = te[8], + m21 = te[1], m22 = te[5], m23 = te[9], + m31 = te[2], m32 = te[6], m33 = te[10]; + + if ( ( Math.abs( m12 - m21 ) < epsilon ) + && ( Math.abs( m13 - m31 ) < epsilon ) + && ( Math.abs( m23 - m32 ) < epsilon ) ) { + + // singularity found + // first check for identity matrix which must have +1 for all terms + // in leading diagonal and zero in other terms + + if ( ( Math.abs( m12 + m21 ) < epsilon2 ) + && ( Math.abs( m13 + m31 ) < epsilon2 ) + && ( Math.abs( m23 + m32 ) < epsilon2 ) + && ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { + + // this singularity is identity matrix so angle = 0 + + this.set( 1, 0, 0, 0 ); + + return this; // zero angle, arbitrary axis + + } + + // otherwise this singularity is angle = 180 + + angle = Math.PI; + + var xx = ( m11 + 1 ) / 2; + var yy = ( m22 + 1 ) / 2; + var zz = ( m33 + 1 ) / 2; + var xy = ( m12 + m21 ) / 4; + var xz = ( m13 + m31 ) / 4; + var yz = ( m23 + m32 ) / 4; + + if ( ( xx > yy ) && ( xx > zz ) ) { // m11 is the largest diagonal term + + if ( xx < epsilon ) { + + x = 0; + y = 0.707106781; + z = 0.707106781; + + } else { + + x = Math.sqrt( xx ); + y = xy / x; + z = xz / x; + + } + + } else if ( yy > zz ) { // m22 is the largest diagonal term + + if ( yy < epsilon ) { + + x = 0.707106781; + y = 0; + z = 0.707106781; + + } else { + + y = Math.sqrt( yy ); + x = xy / y; + z = yz / y; + + } + + } else { // m33 is the largest diagonal term so base result on this + + if ( zz < epsilon ) { + + x = 0.707106781; + y = 0.707106781; + z = 0; + + } else { + + z = Math.sqrt( zz ); + x = xz / z; + y = yz / z; + + } + + } + + this.set( x, y, z, angle ); + + return this; // return 180 deg rotation + + } + + // as we have reached here there are no singularities so we can handle normally + + var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) + + ( m13 - m31 ) * ( m13 - m31 ) + + ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize + + if ( Math.abs( s ) < 0.001 ) s = 1; + + // prevent divide by zero, should not happen if matrix is orthogonal and should be + // caught by singularity test above, but I've left it in just in case + + this.x = ( m32 - m23 ) / s; + this.y = ( m13 - m31 ) / s; + this.z = ( m21 - m12 ) / s; + this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); + + return this; + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Matrix3 = function () { + + this.elements = new Float32Array(9); + +}; + +THREE.Matrix3.prototype = { + + constructor: THREE.Matrix3, + + getInverse: function ( matrix ) { + + // input: THREE.Matrix4 + // ( based on http://code.google.com/p/webgl-mjs/ ) + + var me = matrix.elements; + + var a11 = me[10] * me[5] - me[6] * me[9]; + var a21 = - me[10] * me[1] + me[2] * me[9]; + var a31 = me[6] * me[1] - me[2] * me[5]; + var a12 = - me[10] * me[4] + me[6] * me[8]; + var a22 = me[10] * me[0] - me[2] * me[8]; + var a32 = - me[6] * me[0] + me[2] * me[4]; + var a13 = me[9] * me[4] - me[5] * me[8]; + var a23 = - me[9] * me[0] + me[1] * me[8]; + var a33 = me[5] * me[0] - me[1] * me[4]; + + var det = me[0] * a11 + me[1] * a12 + me[2] * a13; + + // no inverse + + if ( det === 0 ) { + + console.warn( "Matrix3.getInverse(): determinant == 0" ); + + } + + var idet = 1.0 / det; + + var m = this.elements; + + m[ 0 ] = idet * a11; m[ 1 ] = idet * a21; m[ 2 ] = idet * a31; + m[ 3 ] = idet * a12; m[ 4 ] = idet * a22; m[ 5 ] = idet * a32; + m[ 6 ] = idet * a13; m[ 7 ] = idet * a23; m[ 8 ] = idet * a33; + + return this; + + }, + + + transpose: function () { + + var tmp, m = this.elements; + + tmp = m[1]; m[1] = m[3]; m[3] = tmp; + tmp = m[2]; m[2] = m[6]; m[6] = tmp; + tmp = m[5]; m[5] = m[7]; m[7] = tmp; + + return this; + + }, + + + transposeIntoArray: function ( r ) { + + var m = this.m; + + r[ 0 ] = m[ 0 ]; + r[ 1 ] = m[ 3 ]; + r[ 2 ] = m[ 6 ]; + r[ 3 ] = m[ 1 ]; + r[ 4 ] = m[ 4 ]; + r[ 5 ] = m[ 7 ]; + r[ 6 ] = m[ 2 ]; + r[ 7 ] = m[ 5 ]; + r[ 8 ] = m[ 8 ]; + + return this; + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author jordi_ros / http://plattsoft.com + * @author D1plo1d / http://github.com/D1plo1d + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author timknip / http://www.floorplanner.com/ + */ + + +THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + this.elements = new Float32Array( 16 ); + + this.set( + + ( n11 !== undefined ) ? n11 : 1, n12 || 0, n13 || 0, n14 || 0, + n21 || 0, ( n22 !== undefined ) ? n22 : 1, n23 || 0, n24 || 0, + n31 || 0, n32 || 0, ( n33 !== undefined ) ? n33 : 1, n34 || 0, + n41 || 0, n42 || 0, n43 || 0, ( n44 !== undefined ) ? n44 : 1 + + ); + +}; + +THREE.Matrix4.prototype = { + + constructor: THREE.Matrix4, + + set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + var te = this.elements; + + te[0] = n11; te[4] = n12; te[8] = n13; te[12] = n14; + te[1] = n21; te[5] = n22; te[9] = n23; te[13] = n24; + te[2] = n31; te[6] = n32; te[10] = n33; te[14] = n34; + te[3] = n41; te[7] = n42; te[11] = n43; te[15] = n44; + + return this; + + }, + + identity: function () { + + this.set( + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + copy: function ( m ) { + + var me = m.elements; + + this.set( + + me[0], me[4], me[8], me[12], + me[1], me[5], me[9], me[13], + me[2], me[6], me[10], me[14], + me[3], me[7], me[11], me[15] + + ); + + return this; + + }, + + lookAt: function ( eye, target, up ) { + + var te = this.elements; + + var x = THREE.Matrix4.__v1; + var y = THREE.Matrix4.__v2; + var z = THREE.Matrix4.__v3; + + z.sub( eye, target ).normalize(); + + if ( z.length() === 0 ) { + + z.z = 1; + + } + + x.cross( up, z ).normalize(); + + if ( x.length() === 0 ) { + + z.x += 0.0001; + x.cross( up, z ).normalize(); + + } + + y.cross( z, x ); + + + te[0] = x.x; te[4] = y.x; te[8] = z.x; + te[1] = x.y; te[5] = y.y; te[9] = z.y; + te[2] = x.z; te[6] = y.z; te[10] = z.z; + + return this; + + }, + + multiply: function ( a, b ) { + + var ae = a.elements; + var be = b.elements; + var te = this.elements; + + var a11 = ae[0], a12 = ae[4], a13 = ae[8], a14 = ae[12]; + var a21 = ae[1], a22 = ae[5], a23 = ae[9], a24 = ae[13]; + var a31 = ae[2], a32 = ae[6], a33 = ae[10], a34 = ae[14]; + var a41 = ae[3], a42 = ae[7], a43 = ae[11], a44 = ae[15]; + + var b11 = be[0], b12 = be[4], b13 = be[8], b14 = be[12]; + var b21 = be[1], b22 = be[5], b23 = be[9], b24 = be[13]; + var b31 = be[2], b32 = be[6], b33 = be[10], b34 = be[14]; + var b41 = be[3], b42 = be[7], b43 = be[11], b44 = be[15]; + + te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + + te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + + te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + + te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + + return this; + + }, + + multiplySelf: function ( m ) { + + return this.multiply( this, m ); + + }, + + multiplyToArray: function ( a, b, r ) { + + var te = this.elements; + + this.multiply( a, b ); + + r[ 0 ] = te[0]; r[ 1 ] = te[1]; r[ 2 ] = te[2]; r[ 3 ] = te[3]; + r[ 4 ] = te[4]; r[ 5 ] = te[5]; r[ 6 ] = te[6]; r[ 7 ] = te[7]; + r[ 8 ] = te[8]; r[ 9 ] = te[9]; r[ 10 ] = te[10]; r[ 11 ] = te[11]; + r[ 12 ] = te[12]; r[ 13 ] = te[13]; r[ 14 ] = te[14]; r[ 15 ] = te[15]; + + return this; + + }, + + multiplyScalar: function ( s ) { + + var te = this.elements; + + te[0] *= s; te[4] *= s; te[8] *= s; te[12] *= s; + te[1] *= s; te[5] *= s; te[9] *= s; te[13] *= s; + te[2] *= s; te[6] *= s; te[10] *= s; te[14] *= s; + te[3] *= s; te[7] *= s; te[11] *= s; te[15] *= s; + + return this; + + }, + + multiplyVector3: function ( v ) { + + var te = this.elements; + + var vx = v.x, vy = v.y, vz = v.z; + var d = 1 / ( te[3] * vx + te[7] * vy + te[11] * vz + te[15] ); + + v.x = ( te[0] * vx + te[4] * vy + te[8] * vz + te[12] ) * d; + v.y = ( te[1] * vx + te[5] * vy + te[9] * vz + te[13] ) * d; + v.z = ( te[2] * vx + te[6] * vy + te[10] * vz + te[14] ) * d; + + return v; + + }, + + multiplyVector4: function ( v ) { + + var te = this.elements; + var vx = v.x, vy = v.y, vz = v.z, vw = v.w; + + v.x = te[0] * vx + te[4] * vy + te[8] * vz + te[12] * vw; + v.y = te[1] * vx + te[5] * vy + te[9] * vz + te[13] * vw; + v.z = te[2] * vx + te[6] * vy + te[10] * vz + te[14] * vw; + v.w = te[3] * vx + te[7] * vy + te[11] * vz + te[15] * vw; + + return v; + + }, + + multiplyVector3Array: function ( a ) { + + var tmp = THREE.Matrix4.__v1; + + for ( var i = 0, il = a.length; i < il; i += 3 ) { + + tmp.x = a[ i ]; + tmp.y = a[ i + 1 ]; + tmp.z = a[ i + 2 ]; + + this.multiplyVector3( tmp ); + + a[ i ] = tmp.x; + a[ i + 1 ] = tmp.y; + a[ i + 2 ] = tmp.z; + + } + + return a; + + }, + + rotateAxis: function ( v ) { + + var te = this.elements; + var vx = v.x, vy = v.y, vz = v.z; + + v.x = vx * te[0] + vy * te[4] + vz * te[8]; + v.y = vx * te[1] + vy * te[5] + vz * te[9]; + v.z = vx * te[2] + vy * te[6] + vz * te[10]; + + v.normalize(); + + return v; + + }, + + crossVector: function ( a ) { + + var te = this.elements; + var v = new THREE.Vector4(); + + v.x = te[0] * a.x + te[4] * a.y + te[8] * a.z + te[12] * a.w; + v.y = te[1] * a.x + te[5] * a.y + te[9] * a.z + te[13] * a.w; + v.z = te[2] * a.x + te[6] * a.y + te[10] * a.z + te[14] * a.w; + + v.w = ( a.w ) ? te[3] * a.x + te[7] * a.y + te[11] * a.z + te[15] * a.w : 1; + + return v; + + }, + + determinant: function () { + + var te = this.elements; + + var n11 = te[0], n12 = te[4], n13 = te[8], n14 = te[12]; + var n21 = te[1], n22 = te[5], n23 = te[9], n24 = te[13]; + var n31 = te[2], n32 = te[6], n33 = te[10], n34 = te[14]; + var n41 = te[3], n42 = te[7], n43 = te[11], n44 = te[15]; + + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + + return ( + n14 * n23 * n32 * n41- + n13 * n24 * n32 * n41- + n14 * n22 * n33 * n41+ + n12 * n24 * n33 * n41+ + + n13 * n22 * n34 * n41- + n12 * n23 * n34 * n41- + n14 * n23 * n31 * n42+ + n13 * n24 * n31 * n42+ + + n14 * n21 * n33 * n42- + n11 * n24 * n33 * n42- + n13 * n21 * n34 * n42+ + n11 * n23 * n34 * n42+ + + n14 * n22 * n31 * n43- + n12 * n24 * n31 * n43- + n14 * n21 * n32 * n43+ + n11 * n24 * n32 * n43+ + + n12 * n21 * n34 * n43- + n11 * n22 * n34 * n43- + n13 * n22 * n31 * n44+ + n12 * n23 * n31 * n44+ + + n13 * n21 * n32 * n44- + n11 * n23 * n32 * n44- + n12 * n21 * n33 * n44+ + n11 * n22 * n33 * n44 + ); + + }, + + transpose: function () { + + var te = this.elements; + var tmp; + + tmp = te[1]; te[1] = te[4]; te[4] = tmp; + tmp = te[2]; te[2] = te[8]; te[8] = tmp; + tmp = te[6]; te[6] = te[9]; te[9] = tmp; + + tmp = te[3]; te[3] = te[12]; te[12] = tmp; + tmp = te[7]; te[7] = te[13]; te[13] = tmp; + tmp = te[11]; te[11] = te[14]; te[14] = tmp; + + return this; + + }, + + flattenToArray: function ( flat ) { + + var te = this.elements; + flat[ 0 ] = te[0]; flat[ 1 ] = te[1]; flat[ 2 ] = te[2]; flat[ 3 ] = te[3]; + flat[ 4 ] = te[4]; flat[ 5 ] = te[5]; flat[ 6 ] = te[6]; flat[ 7 ] = te[7]; + flat[ 8 ] = te[8]; flat[ 9 ] = te[9]; flat[ 10 ] = te[10]; flat[ 11 ] = te[11]; + flat[ 12 ] = te[12]; flat[ 13 ] = te[13]; flat[ 14 ] = te[14]; flat[ 15 ] = te[15]; + + return flat; + + }, + + flattenToArrayOffset: function( flat, offset ) { + + var te = this.elements; + flat[ offset ] = te[0]; + flat[ offset + 1 ] = te[1]; + flat[ offset + 2 ] = te[2]; + flat[ offset + 3 ] = te[3]; + + flat[ offset + 4 ] = te[4]; + flat[ offset + 5 ] = te[5]; + flat[ offset + 6 ] = te[6]; + flat[ offset + 7 ] = te[7]; + + flat[ offset + 8 ] = te[8]; + flat[ offset + 9 ] = te[9]; + flat[ offset + 10 ] = te[10]; + flat[ offset + 11 ] = te[11]; + + flat[ offset + 12 ] = te[12]; + flat[ offset + 13 ] = te[13]; + flat[ offset + 14 ] = te[14]; + flat[ offset + 15 ] = te[15]; + + return flat; + + }, + + getPosition: function () { + + var te = this.elements; + return THREE.Matrix4.__v1.set( te[12], te[13], te[14] ); + + }, + + setPosition: function ( v ) { + + var te = this.elements; + + te[12] = v.x; + te[13] = v.y; + te[14] = v.z; + + return this; + + }, + + getColumnX: function () { + + var te = this.elements; + return THREE.Matrix4.__v1.set( te[0], te[1], te[2] ); + + }, + + getColumnY: function () { + + var te = this.elements; + return THREE.Matrix4.__v1.set( te[4], te[5], te[6] ); + + }, + + getColumnZ: function() { + + var te = this.elements; + return THREE.Matrix4.__v1.set( te[8], te[9], te[10] ); + + }, + + getInverse: function ( m ) { + + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + var te = this.elements; + var me = m.elements; + + var n11 = me[0], n12 = me[4], n13 = me[8], n14 = me[12]; + var n21 = me[1], n22 = me[5], n23 = me[9], n24 = me[13]; + var n31 = me[2], n32 = me[6], n33 = me[10], n34 = me[14]; + var n41 = me[3], n42 = me[7], n43 = me[11], n44 = me[15]; + + te[0] = n23*n34*n42 - n24*n33*n42 + n24*n32*n43 - n22*n34*n43 - n23*n32*n44 + n22*n33*n44; + te[4] = n14*n33*n42 - n13*n34*n42 - n14*n32*n43 + n12*n34*n43 + n13*n32*n44 - n12*n33*n44; + te[8] = n13*n24*n42 - n14*n23*n42 + n14*n22*n43 - n12*n24*n43 - n13*n22*n44 + n12*n23*n44; + te[12] = n14*n23*n32 - n13*n24*n32 - n14*n22*n33 + n12*n24*n33 + n13*n22*n34 - n12*n23*n34; + te[1] = n24*n33*n41 - n23*n34*n41 - n24*n31*n43 + n21*n34*n43 + n23*n31*n44 - n21*n33*n44; + te[5] = n13*n34*n41 - n14*n33*n41 + n14*n31*n43 - n11*n34*n43 - n13*n31*n44 + n11*n33*n44; + te[9] = n14*n23*n41 - n13*n24*n41 - n14*n21*n43 + n11*n24*n43 + n13*n21*n44 - n11*n23*n44; + te[13] = n13*n24*n31 - n14*n23*n31 + n14*n21*n33 - n11*n24*n33 - n13*n21*n34 + n11*n23*n34; + te[2] = n22*n34*n41 - n24*n32*n41 + n24*n31*n42 - n21*n34*n42 - n22*n31*n44 + n21*n32*n44; + te[6] = n14*n32*n41 - n12*n34*n41 - n14*n31*n42 + n11*n34*n42 + n12*n31*n44 - n11*n32*n44; + te[10] = n12*n24*n41 - n14*n22*n41 + n14*n21*n42 - n11*n24*n42 - n12*n21*n44 + n11*n22*n44; + te[14] = n14*n22*n31 - n12*n24*n31 - n14*n21*n32 + n11*n24*n32 + n12*n21*n34 - n11*n22*n34; + te[3] = n23*n32*n41 - n22*n33*n41 - n23*n31*n42 + n21*n33*n42 + n22*n31*n43 - n21*n32*n43; + te[7] = n12*n33*n41 - n13*n32*n41 + n13*n31*n42 - n11*n33*n42 - n12*n31*n43 + n11*n32*n43; + te[11] = n13*n22*n41 - n12*n23*n41 - n13*n21*n42 + n11*n23*n42 + n12*n21*n43 - n11*n22*n43; + te[15] = n12*n23*n31 - n13*n22*n31 + n13*n21*n32 - n11*n23*n32 - n12*n21*n33 + n11*n22*n33; + this.multiplyScalar( 1 / m.determinant() ); + + return this; + + }, + + setRotationFromEuler: function ( v, order ) { + + var te = this.elements; + + var x = v.x, y = v.y, z = v.z; + var a = Math.cos( x ), b = Math.sin( x ); + var c = Math.cos( y ), d = Math.sin( y ); + var e = Math.cos( z ), f = Math.sin( z ); + + if ( order === undefined || order === 'XYZ' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[0] = c * e; + te[4] = - c * f; + te[8] = d; + + te[1] = af + be * d; + te[5] = ae - bf * d; + te[9] = - b * c; + + te[2] = bf - ae * d; + te[6] = be + af * d; + te[10] = a * c; + + } else if ( order === 'YXZ' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[0] = ce + df * b; + te[4] = de * b - cf; + te[8] = a * d; + + te[1] = a * f; + te[5] = a * e; + te[9] = - b; + + te[2] = cf * b - de; + te[6] = df + ce * b; + te[10] = a * c; + + } else if ( order === 'ZXY' ) { + + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + te[0] = ce - df * b; + te[4] = - a * f; + te[8] = de + cf * b; + + te[1] = cf + de * b; + te[5] = a * e; + te[9] = df - ce * b; + + te[2] = - a * d; + te[6] = b; + te[10] = a * c; + + } else if ( order === 'ZYX' ) { + + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + te[0] = c * e; + te[4] = be * d - af; + te[8] = ae * d + bf; + + te[1] = c * f; + te[5] = bf * d + ae; + te[9] = af * d - be; + + te[2] = - d; + te[6] = b * c; + te[10] = a * c; + + } else if ( order === 'YZX' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[0] = c * e; + te[4] = bd - ac * f; + te[8] = bc * f + ad; + + te[1] = f; + te[5] = a * e; + te[9] = - b * e; + + te[2] = - d * e; + te[6] = ad * f + bc; + te[10] = ac - bd * f; + + } else if ( order === 'XZY' ) { + + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + te[0] = c * e; + te[4] = - f; + te[8] = d * e; + + te[1] = ac * f + bd; + te[5] = a * e; + te[9] = ad * f - bc; + + te[2] = bc * f - ad; + te[6] = b * e; + te[10] = bd * f + ac; + + } + + return this; + + }, + + + setRotationFromQuaternion: function ( q ) { + + var te = this.elements; + + var x = q.x, y = q.y, z = q.z, w = q.w; + var x2 = x + x, y2 = y + y, z2 = z + z; + var xx = x * x2, xy = x * y2, xz = x * z2; + var yy = y * y2, yz = y * z2, zz = z * z2; + var wx = w * x2, wy = w * y2, wz = w * z2; + + te[0] = 1 - ( yy + zz ); + te[4] = xy - wz; + te[8] = xz + wy; + + te[1] = xy + wz; + te[5] = 1 - ( xx + zz ); + te[9] = yz - wx; + + te[2] = xz - wy; + te[6] = yz + wx; + te[10] = 1 - ( xx + yy ); + + return this; + + }, + + compose: function ( translation, rotation, scale ) { + + var te = this.elements; + var mRotation = THREE.Matrix4.__m1; + var mScale = THREE.Matrix4.__m2; + + mRotation.identity(); + mRotation.setRotationFromQuaternion( rotation ); + + mScale.makeScale( scale.x, scale.y, scale.z ); + + this.multiply( mRotation, mScale ); + + te[12] = translation.x; + te[13] = translation.y; + te[14] = translation.z; + + return this; + + }, + + decompose: function ( translation, rotation, scale ) { + + var te = this.elements; + + // grab the axis vectors + var x = THREE.Matrix4.__v1; + var y = THREE.Matrix4.__v2; + var z = THREE.Matrix4.__v3; + + x.set( te[0], te[1], te[2] ); + y.set( te[4], te[5], te[6] ); + z.set( te[8], te[9], te[10] ); + + translation = ( translation instanceof THREE.Vector3 ) ? translation : new THREE.Vector3(); + rotation = ( rotation instanceof THREE.Quaternion ) ? rotation : new THREE.Quaternion(); + scale = ( scale instanceof THREE.Vector3 ) ? scale : new THREE.Vector3(); + + scale.x = x.length(); + scale.y = y.length(); + scale.z = z.length(); + + translation.x = te[12]; + translation.y = te[13]; + translation.z = te[14]; + + // scale the rotation part + + var matrix = THREE.Matrix4.__m1; + + matrix.copy( this ); + + matrix.elements[0] /= scale.x; + matrix.elements[1] /= scale.x; + matrix.elements[2] /= scale.x; + + matrix.elements[4] /= scale.y; + matrix.elements[5] /= scale.y; + matrix.elements[6] /= scale.y; + + matrix.elements[8] /= scale.z; + matrix.elements[9] /= scale.z; + matrix.elements[10] /= scale.z; + + rotation.setFromRotationMatrix( matrix ); + + return [ translation, rotation, scale ]; + + }, + + extractPosition: function ( m ) { + + var te = this.elements; + var me = m.elements; + + te[12] = me[12]; + te[13] = me[13]; + te[14] = me[14]; + + return this; + + }, + + extractRotation: function ( m ) { + + var te = this.elements; + var me = m.elements; + + var vector = THREE.Matrix4.__v1; + + var scaleX = 1 / vector.set( me[0], me[1], me[2] ).length(); + var scaleY = 1 / vector.set( me[4], me[5], me[6] ).length(); + var scaleZ = 1 / vector.set( me[8], me[9], me[10] ).length(); + + te[0] = me[0] * scaleX; + te[1] = me[1] * scaleX; + te[2] = me[2] * scaleX; + + te[4] = me[4] * scaleY; + te[5] = me[5] * scaleY; + te[6] = me[6] * scaleY; + + te[8] = me[8] * scaleZ; + te[9] = me[9] * scaleZ; + te[10] = me[10] * scaleZ; + + return this; + + }, + + // + + translate: function ( v ) { + + var te = this.elements; + var x = v.x, y = v.y, z = v.z; + + te[12] = te[0] * x + te[4] * y + te[8] * z + te[12]; + te[13] = te[1] * x + te[5] * y + te[9] * z + te[13]; + te[14] = te[2] * x + te[6] * y + te[10] * z + te[14]; + te[15] = te[3] * x + te[7] * y + te[11] * z + te[15]; + + return this; + + }, + + rotateX: function ( angle ) { + + var te = this.elements; + var m12 = te[4]; + var m22 = te[5]; + var m32 = te[6]; + var m42 = te[7]; + var m13 = te[8]; + var m23 = te[9]; + var m33 = te[10]; + var m43 = te[11]; + var c = Math.cos( angle ); + var s = Math.sin( angle ); + + te[4] = c * m12 + s * m13; + te[5] = c * m22 + s * m23; + te[6] = c * m32 + s * m33; + te[7] = c * m42 + s * m43; + + te[8] = c * m13 - s * m12; + te[9] = c * m23 - s * m22; + te[10] = c * m33 - s * m32; + te[11] = c * m43 - s * m42; + + return this; + + }, + + rotateY: function ( angle ) { + + var te = this.elements; + var m11 = te[0]; + var m21 = te[1]; + var m31 = te[2]; + var m41 = te[3]; + var m13 = te[8]; + var m23 = te[9]; + var m33 = te[10]; + var m43 = te[11]; + var c = Math.cos( angle ); + var s = Math.sin( angle ); + + te[0] = c * m11 - s * m13; + te[1] = c * m21 - s * m23; + te[2] = c * m31 - s * m33; + te[3] = c * m41 - s * m43; + + te[8] = c * m13 + s * m11; + te[9] = c * m23 + s * m21; + te[10] = c * m33 + s * m31; + te[11] = c * m43 + s * m41; + + return this; + + }, + + rotateZ: function ( angle ) { + + var te = this.elements; + var m11 = te[0]; + var m21 = te[1]; + var m31 = te[2]; + var m41 = te[3]; + var m12 = te[4]; + var m22 = te[5]; + var m32 = te[6]; + var m42 = te[7]; + var c = Math.cos( angle ); + var s = Math.sin( angle ); + + te[0] = c * m11 + s * m12; + te[1] = c * m21 + s * m22; + te[2] = c * m31 + s * m32; + te[3] = c * m41 + s * m42; + + te[4] = c * m12 - s * m11; + te[5] = c * m22 - s * m21; + te[6] = c * m32 - s * m31; + te[7] = c * m42 - s * m41; + + return this; + + }, + + rotateByAxis: function ( axis, angle ) { + + var te = this.elements; + + // optimize by checking axis + + if ( axis.x === 1 && axis.y === 0 && axis.z === 0 ) { + + return this.rotateX( angle ); + + } else if ( axis.x === 0 && axis.y === 1 && axis.z === 0 ) { + + return this.rotateY( angle ); + + } else if ( axis.x === 0 && axis.y === 0 && axis.z === 1 ) { + + return this.rotateZ( angle ); + + } + + var x = axis.x, y = axis.y, z = axis.z; + var n = Math.sqrt(x * x + y * y + z * z); + + x /= n; + y /= n; + z /= n; + + var xx = x * x, yy = y * y, zz = z * z; + var c = Math.cos( angle ); + var s = Math.sin( angle ); + var oneMinusCosine = 1 - c; + var xy = x * y * oneMinusCosine; + var xz = x * z * oneMinusCosine; + var yz = y * z * oneMinusCosine; + var xs = x * s; + var ys = y * s; + var zs = z * s; + + var r11 = xx + (1 - xx) * c; + var r21 = xy + zs; + var r31 = xz - ys; + var r12 = xy - zs; + var r22 = yy + (1 - yy) * c; + var r32 = yz + xs; + var r13 = xz + ys; + var r23 = yz - xs; + var r33 = zz + (1 - zz) * c; + + var m11 = te[0], m21 = te[1], m31 = te[2], m41 = te[3]; + var m12 = te[4], m22 = te[5], m32 = te[6], m42 = te[7]; + var m13 = te[8], m23 = te[9], m33 = te[10], m43 = te[11]; + var m14 = te[12], m24 = te[13], m34 = te[14], m44 = te[15]; + + te[0] = r11 * m11 + r21 * m12 + r31 * m13; + te[1] = r11 * m21 + r21 * m22 + r31 * m23; + te[2] = r11 * m31 + r21 * m32 + r31 * m33; + te[3] = r11 * m41 + r21 * m42 + r31 * m43; + + te[4] = r12 * m11 + r22 * m12 + r32 * m13; + te[5] = r12 * m21 + r22 * m22 + r32 * m23; + te[6] = r12 * m31 + r22 * m32 + r32 * m33; + te[7] = r12 * m41 + r22 * m42 + r32 * m43; + + te[8] = r13 * m11 + r23 * m12 + r33 * m13; + te[9] = r13 * m21 + r23 * m22 + r33 * m23; + te[10] = r13 * m31 + r23 * m32 + r33 * m33; + te[11] = r13 * m41 + r23 * m42 + r33 * m43; + + return this; + + }, + + scale: function ( v ) { + + var te = this.elements; + var x = v.x, y = v.y, z = v.z; + + te[0] *= x; te[4] *= y; te[8] *= z; + te[1] *= x; te[5] *= y; te[9] *= z; + te[2] *= x; te[6] *= y; te[10] *= z; + te[3] *= x; te[7] *= y; te[11] *= z; + + return this; + + }, + + getMaxScaleOnAxis: function () { + + var te = this.elements; + + var scaleXSq = te[0] * te[0] + te[1] * te[1] + te[2] * te[2]; + var scaleYSq = te[4] * te[4] + te[5] * te[5] + te[6] * te[6]; + var scaleZSq = te[8] * te[8] + te[9] * te[9] + te[10] * te[10]; + + return Math.sqrt( Math.max( scaleXSq, Math.max( scaleYSq, scaleZSq ) ) ); + + }, + + // + + makeTranslation: function ( x, y, z ) { + + this.set( + + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationX: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + 1, 0, 0, 0, + 0, c, -s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationY: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, 0, s, 0, + 0, 1, 0, 0, + -s, 0, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationZ: function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, -s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeRotationAxis: function ( axis, angle ) { + + // Based on http://www.gamedev.net/reference/articles/article1199.asp + + var c = Math.cos( angle ); + var s = Math.sin( angle ); + var t = 1 - c; + var x = axis.x, y = axis.y, z = axis.z; + var tx = t * x, ty = t * y; + + this.set( + + tx * x + c, tx * y - s * z, tx * z + s * y, 0, + tx * y + s * z, ty * y + c, ty * z - s * x, 0, + tx * z - s * y, ty * z + s * x, t * z * z + c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeScale: function ( x, y, z ) { + + this.set( + + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + makeFrustum: function ( left, right, bottom, top, near, far ) { + + var te = this.elements; + var x = 2 * near / ( right - left ); + var y = 2 * near / ( top - bottom ); + + var a = ( right + left ) / ( right - left ); + var b = ( top + bottom ) / ( top - bottom ); + var c = - ( far + near ) / ( far - near ); + var d = - 2 * far * near / ( far - near ); + + te[0] = x; te[4] = 0; te[8] = a; te[12] = 0; + te[1] = 0; te[5] = y; te[9] = b; te[13] = 0; + te[2] = 0; te[6] = 0; te[10] = c; te[14] = d; + te[3] = 0; te[7] = 0; te[11] = - 1; te[15] = 0; + + return this; + + }, + + makePerspective: function ( fov, aspect, near, far ) { + + var ymax = near * Math.tan( fov * Math.PI / 360 ); + var ymin = - ymax; + var xmin = ymin * aspect; + var xmax = ymax * aspect; + + return this.makeFrustum( xmin, xmax, ymin, ymax, near, far ); + + }, + + makeOrthographic: function ( left, right, top, bottom, near, far ) { + + var te = this.elements; + var w = right - left; + var h = top - bottom; + var p = far - near; + + var x = ( right + left ) / w; + var y = ( top + bottom ) / h; + var z = ( far + near ) / p; + + te[0] = 2 / w; te[4] = 0; te[8] = 0; te[12] = -x; + te[1] = 0; te[5] = 2 / h; te[9] = 0; te[13] = -y; + te[2] = 0; te[6] = 0; te[10] = -2 / p; te[14] = -z; + te[3] = 0; te[7] = 0; te[11] = 0; te[15] = 1; + + return this; + + }, + + + clone: function () { + + var te = this.elements; + + return new THREE.Matrix4( + + te[0], te[4], te[8], te[12], + te[1], te[5], te[9], te[13], + te[2], te[6], te[10], te[14], + te[3], te[7], te[11], te[15] + + ); + + } + +}; + +THREE.Matrix4.__v1 = new THREE.Vector3(); +THREE.Matrix4.__v2 = new THREE.Vector3(); +THREE.Matrix4.__v3 = new THREE.Vector3(); + +THREE.Matrix4.__m1 = new THREE.Matrix4(); +THREE.Matrix4.__m2 = new THREE.Matrix4(); +/** + * https://github.com/mrdoob/eventtarget.js/ + */ + +THREE.EventTarget = function () { + + var listeners = {}; + + this.addEventListener = function ( type, listener ) { + + if ( listeners[ type ] === undefined ) { + + listeners[ type ] = []; + + } + + if ( listeners[ type ].indexOf( listener ) === - 1 ) { + + listeners[ type ].push( listener ); + + } + + }; + + this.dispatchEvent = function ( event ) { + + for ( var listener in listeners[ event.type ] ) { + + listeners[ event.type ][ listener ]( event ); + + } + + }; + + this.removeEventListener = function ( type, listener ) { + + var index = listeners[ type ].indexOf( listener ); + + if ( index !== - 1 ) { + + listeners[ type ].splice( index, 1 ); + + } + + }; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Frustum = function ( ) { + + this.planes = [ + + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4() + + ]; + +}; + +THREE.Frustum.prototype.setFromMatrix = function ( m ) { + + var plane; + var planes = this.planes; + + var me = m.elements; + var me0 = me[0], me1 = me[1], me2 = me[2], me3 = me[3]; + var me4 = me[4], me5 = me[5], me6 = me[6], me7 = me[7]; + var me8 = me[8], me9 = me[9], me10 = me[10], me11 = me[11]; + var me12 = me[12], me13 = me[13], me14 = me[14], me15 = me[15]; + + planes[ 0 ].set( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ); + planes[ 1 ].set( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ); + planes[ 2 ].set( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ); + planes[ 3 ].set( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ); + planes[ 4 ].set( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ); + planes[ 5 ].set( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ); + + for ( var i = 0; i < 6; i ++ ) { + + plane = planes[ i ]; + plane.divideScalar( Math.sqrt( plane.x * plane.x + plane.y * plane.y + plane.z * plane.z ) ); + + } + +}; + +THREE.Frustum.prototype.contains = function ( object ) { + + var distance = 0.0; + var planes = this.planes; + var matrix = object.matrixWorld; + var me = matrix.elements; + var radius = - object.geometry.boundingSphere.radius * matrix.getMaxScaleOnAxis(); + + for ( var i = 0; i < 6; i ++ ) { + + distance = planes[ i ].x * me[12] + planes[ i ].y * me[13] + planes[ i ].z * me[14] + planes[ i ].w; + if ( distance <= radius ) return false; + + } + + return true; + +}; + +THREE.Frustum.__v1 = new THREE.Vector3(); +/** + * @author mrdoob / http://mrdoob.com/ + */ + +( function ( THREE ) { + + THREE.Ray = function ( origin, direction, near, far ) { + + this.origin = origin || new THREE.Vector3(); + this.direction = direction || new THREE.Vector3(); + this.near = near || 0; + this.far = far || Infinity; + + }; + + var originCopy = new THREE.Vector3(); + + var localOriginCopy = new THREE.Vector3(); + var localDirectionCopy = new THREE.Vector3(); + + var vector = new THREE.Vector3(); + var normal = new THREE.Vector3(); + var intersectPoint = new THREE.Vector3(); + + var inverseMatrix = new THREE.Matrix4(); + + var descSort = function ( a, b ) { + + return a.distance - b.distance; + + }; + + var v0 = new THREE.Vector3(), v1 = new THREE.Vector3(), v2 = new THREE.Vector3(); + + var distanceFromIntersection = function ( origin, direction, position ) { + + v0.sub( position, origin ); + + var dot = v0.dot( direction ); + + var intersect = v1.add( origin, v2.copy( direction ).multiplyScalar( dot ) ); + var distance = position.distanceTo( intersect ); + + return distance; + + }; + + // http://www.blackpawn.com/texts/pointinpoly/default.html + + var pointInFace3 = function ( p, a, b, c ) { + + v0.sub( c, a ); + v1.sub( b, a ); + v2.sub( p, a ); + + var dot00 = v0.dot( v0 ); + var dot01 = v0.dot( v1 ); + var dot02 = v0.dot( v2 ); + var dot11 = v1.dot( v1 ); + var dot12 = v1.dot( v2 ); + + var invDenom = 1 / ( dot00 * dot11 - dot01 * dot01 ); + var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; + var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; + + return ( u >= 0 ) && ( v >= 0 ) && ( u + v < 1 ); + + }; + + var intersectObject = function ( object, ray, intersects ) { + + var distance,intersect; + + if ( object instanceof THREE.Particle ) { + + distance = distanceFromIntersection( ray.origin, ray.direction, object.matrixWorld.getPosition() ); + + if ( distance > object.scale.x ) { + + return intersects; + + } + + intersect = { + + distance: distance, + point: object.position, + face: null, + object: object + + }; + + intersects.push( intersect ); + + } else if ( object instanceof THREE.Mesh ) { + + // Checking boundingSphere + + var scaledRadius = object.geometry.boundingSphere.radius * object.matrixWorld.getMaxScaleOnAxis(); + + // Checking distance to ray + + distance = distanceFromIntersection( ray.origin, ray.direction, object.matrixWorld.getPosition() ); + + if ( distance > scaledRadius) { + + return intersects; + + } + + // Checking faces + + var f, fl, face, dot, scalar, + geometry = object.geometry, + vertices = geometry.vertices, + objMatrix, geometryMaterials, + isFaceMaterial, material, side, point; + + geometryMaterials = object.geometry.materials; + isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial; + side = object.material.side; + + var a, b, c, d; + var precision = ray.precision; + + object.matrixRotationWorld.extractRotation( object.matrixWorld ); + + originCopy.copy( ray.origin ); + + objMatrix = object.matrixWorld; + inverseMatrix.getInverse( objMatrix ); + + localOriginCopy.copy( originCopy ); + inverseMatrix.multiplyVector3( localOriginCopy ); + + localDirectionCopy.copy( ray.direction ); + inverseMatrix.rotateAxis( localDirectionCopy ).normalize(); + + for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + + face = geometry.faces[ f ]; + + material = isFaceMaterial === true ? geometryMaterials[ face.materialIndex ] : object.material; + if ( material === undefined ) continue; + side = material.side; + + vector.sub( face.centroid, localOriginCopy ); + normal = face.normal; + dot = localDirectionCopy.dot( normal ); + + // bail if ray and plane are parallel + + if ( Math.abs( dot ) < precision ) continue; + + // calc distance to plane + + scalar = normal.dot( vector ) / dot; + + // if negative distance, then plane is behind ray + + if ( scalar < 0 ) continue; + + if ( side === THREE.DoubleSide || ( side === THREE.FrontSide ? dot < 0 : dot > 0 ) ) { + + intersectPoint.add( localOriginCopy, localDirectionCopy.multiplyScalar( scalar ) ); + + if ( face instanceof THREE.Face3 ) { + + a = vertices[ face.a ]; + b = vertices[ face.b ]; + c = vertices[ face.c ]; + + if ( pointInFace3( intersectPoint, a, b, c ) ) { + + point = object.matrixWorld.multiplyVector3( intersectPoint.clone() ); + distance = originCopy.distanceTo( point ); + + if ( distance < ray.near || distance > ray.far ) continue; + + intersect = { + + distance: distance, + point: point, + face: face, + faceIndex: f, + object: object + + }; + + intersects.push( intersect ); + + } + + } else if ( face instanceof THREE.Face4 ) { + + a = vertices[ face.a ]; + b = vertices[ face.b ]; + c = vertices[ face.c ]; + d = vertices[ face.d ]; + + if ( pointInFace3( intersectPoint, a, b, d ) || pointInFace3( intersectPoint, b, c, d ) ) { + + point = object.matrixWorld.multiplyVector3( intersectPoint.clone() ); + distance = originCopy.distanceTo( point ); + + if ( distance < ray.near || distance > ray.far ) continue; + + intersect = { + + distance: distance, + point: point, + face: face, + faceIndex: f, + object: object + + }; + + intersects.push( intersect ); + + } + + } + + } + + } + + } + + }; + + var intersectDescendants = function ( object, ray, intersects ) { + + var descendants = object.getDescendants(); + + for ( var i = 0, l = descendants.length; i < l; i ++ ) { + + intersectObject( descendants[ i ], ray, intersects ); + + } + }; + + // + + THREE.Ray.prototype.precision = 0.0001; + + THREE.Ray.prototype.set = function ( origin, direction ) { + + this.origin = origin; + this.direction = direction; + + }; + + THREE.Ray.prototype.intersectObject = function ( object, recursive ) { + + var intersects = []; + + if ( recursive === true ) { + + intersectDescendants( object, this, intersects ); + + } + + intersectObject( object, this, intersects ); + + intersects.sort( descSort ); + + return intersects; + + }; + + THREE.Ray.prototype.intersectObjects = function ( objects, recursive ) { + + var intersects = []; + + for ( var i = 0, l = objects.length; i < l; i ++ ) { + + intersectObject( objects[ i ], this, intersects ); + + if ( recursive === true ) { + + intersectDescendants( objects[ i ], this, intersects ); + + } + } + + intersects.sort( descSort ); + + return intersects; + + }; + +}( THREE ) ); +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Rectangle = function () { + + var _left = 0; + var _top = 0; + var _right = 0; + var _bottom = 0; + var _width = 0; + var _height = 0; + var _isEmpty = true; + + function resize() { + + _width = _right - _left; + _height = _bottom - _top; + + } + + this.getX = function () { + + return _left; + + }; + + this.getY = function () { + + return _top; + + }; + + this.getWidth = function () { + + return _width; + + }; + + this.getHeight = function () { + + return _height; + + }; + + this.getLeft = function() { + + return _left; + + }; + + this.getTop = function() { + + return _top; + + }; + + this.getRight = function() { + + return _right; + + }; + + this.getBottom = function() { + + return _bottom; + + }; + + this.set = function ( left, top, right, bottom ) { + + _isEmpty = false; + + _left = left; _top = top; + _right = right; _bottom = bottom; + + resize(); + + }; + + this.addPoint = function ( x, y ) { + + if ( _isEmpty === true ) { + + _isEmpty = false; + _left = x; _top = y; + _right = x; _bottom = y; + + resize(); + + } else { + + _left = _left < x ? _left : x; // Math.min( _left, x ); + _top = _top < y ? _top : y; // Math.min( _top, y ); + _right = _right > x ? _right : x; // Math.max( _right, x ); + _bottom = _bottom > y ? _bottom : y; // Math.max( _bottom, y ); + + resize(); + } + + }; + + this.add3Points = function ( x1, y1, x2, y2, x3, y3 ) { + + if ( _isEmpty === true ) { + + _isEmpty = false; + _left = x1 < x2 ? ( x1 < x3 ? x1 : x3 ) : ( x2 < x3 ? x2 : x3 ); + _top = y1 < y2 ? ( y1 < y3 ? y1 : y3 ) : ( y2 < y3 ? y2 : y3 ); + _right = x1 > x2 ? ( x1 > x3 ? x1 : x3 ) : ( x2 > x3 ? x2 : x3 ); + _bottom = y1 > y2 ? ( y1 > y3 ? y1 : y3 ) : ( y2 > y3 ? y2 : y3 ); + + resize(); + + } else { + + _left = x1 < x2 ? ( x1 < x3 ? ( x1 < _left ? x1 : _left ) : ( x3 < _left ? x3 : _left ) ) : ( x2 < x3 ? ( x2 < _left ? x2 : _left ) : ( x3 < _left ? x3 : _left ) ); + _top = y1 < y2 ? ( y1 < y3 ? ( y1 < _top ? y1 : _top ) : ( y3 < _top ? y3 : _top ) ) : ( y2 < y3 ? ( y2 < _top ? y2 : _top ) : ( y3 < _top ? y3 : _top ) ); + _right = x1 > x2 ? ( x1 > x3 ? ( x1 > _right ? x1 : _right ) : ( x3 > _right ? x3 : _right ) ) : ( x2 > x3 ? ( x2 > _right ? x2 : _right ) : ( x3 > _right ? x3 : _right ) ); + _bottom = y1 > y2 ? ( y1 > y3 ? ( y1 > _bottom ? y1 : _bottom ) : ( y3 > _bottom ? y3 : _bottom ) ) : ( y2 > y3 ? ( y2 > _bottom ? y2 : _bottom ) : ( y3 > _bottom ? y3 : _bottom ) ); + + resize(); + + }; + + }; + + this.addRectangle = function ( r ) { + + if ( _isEmpty === true ) { + + _isEmpty = false; + _left = r.getLeft(); _top = r.getTop(); + _right = r.getRight(); _bottom = r.getBottom(); + + resize(); + + } else { + + _left = _left < r.getLeft() ? _left : r.getLeft(); // Math.min(_left, r.getLeft() ); + _top = _top < r.getTop() ? _top : r.getTop(); // Math.min(_top, r.getTop() ); + _right = _right > r.getRight() ? _right : r.getRight(); // Math.max(_right, r.getRight() ); + _bottom = _bottom > r.getBottom() ? _bottom : r.getBottom(); // Math.max(_bottom, r.getBottom() ); + + resize(); + + } + + }; + + this.inflate = function ( v ) { + + _left -= v; _top -= v; + _right += v; _bottom += v; + + resize(); + + }; + + this.minSelf = function ( r ) { + + _left = _left > r.getLeft() ? _left : r.getLeft(); // Math.max( _left, r.getLeft() ); + _top = _top > r.getTop() ? _top : r.getTop(); // Math.max( _top, r.getTop() ); + _right = _right < r.getRight() ? _right : r.getRight(); // Math.min( _right, r.getRight() ); + _bottom = _bottom < r.getBottom() ? _bottom : r.getBottom(); // Math.min( _bottom, r.getBottom() ); + + resize(); + + }; + + this.intersects = function ( r ) { + + // http://gamemath.com/2011/09/detecting-whether-two-boxes-overlap/ + + if ( _right < r.getLeft() ) return false; + if ( _left > r.getRight() ) return false; + if ( _bottom < r.getTop() ) return false; + if ( _top > r.getBottom() ) return false; + + return true; + + }; + + this.empty = function () { + + _isEmpty = true; + + _left = 0; _top = 0; + _right = 0; _bottom = 0; + + resize(); + + }; + + this.isEmpty = function () { + + return _isEmpty; + + }; + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Math = { + + // Clamp value to range + + clamp: function ( x, a, b ) { + + return ( x < a ) ? a : ( ( x > b ) ? b : x ); + + }, + + // Clamp value to range to range + + mapLinear: function ( x, a1, a2, b1, b2 ) { + + return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); + + }, + + // Random float from <0, 1> with 16 bits of randomness + // (standard Math.random() creates repetitive patterns when applied over larger space) + + random16: function () { + + return ( 65280 * Math.random() + 255 * Math.random() ) / 65535; + + }, + + // Random integer from interval + + randInt: function ( low, high ) { + + return low + Math.floor( Math.random() * ( high - low + 1 ) ); + + }, + + // Random float from interval + + randFloat: function ( low, high ) { + + return low + Math.random() * ( high - low ); + + }, + + // Random float from <-range/2, range/2> interval + + randFloatSpread: function ( range ) { + + return range * ( 0.5 - Math.random() ); + + }, + + sign: function ( x ) { + + return ( x < 0 ) ? -1 : ( ( x > 0 ) ? 1 : 0 ); + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Object3D = function () { + + THREE.Object3DLibrary.push( this ); + + this.id = THREE.Object3DIdCount ++; + + this.name = ''; + this.properties = {}; + + this.parent = undefined; + this.children = []; + + this.up = new THREE.Vector3( 0, 1, 0 ); + + this.position = new THREE.Vector3(); + this.rotation = new THREE.Vector3(); + this.eulerOrder = THREE.Object3D.defaultEulerOrder; + this.scale = new THREE.Vector3( 1, 1, 1 ); + + this.renderDepth = null; + + this.rotationAutoUpdate = true; + + this.matrix = new THREE.Matrix4(); + this.matrixWorld = new THREE.Matrix4(); + this.matrixRotationWorld = new THREE.Matrix4(); + + this.matrixAutoUpdate = true; + this.matrixWorldNeedsUpdate = true; + + this.quaternion = new THREE.Quaternion(); + this.useQuaternion = false; + + this.boundRadius = 0.0; + this.boundRadiusScale = 1.0; + + this.visible = true; + + this.castShadow = false; + this.receiveShadow = false; + + this.frustumCulled = true; + + this._vector = new THREE.Vector3(); + +}; + + +THREE.Object3D.prototype = { + + constructor: THREE.Object3D, + + applyMatrix: function ( matrix ) { + + this.matrix.multiply( matrix, this.matrix ); + + this.scale.getScaleFromMatrix( this.matrix ); + + var mat = new THREE.Matrix4().extractRotation( this.matrix ); + this.rotation.setEulerFromRotationMatrix( mat, this.eulerOrder ); + + this.position.getPositionFromMatrix( this.matrix ); + + }, + + translate: function ( distance, axis ) { + + this.matrix.rotateAxis( axis ); + this.position.addSelf( axis.multiplyScalar( distance ) ); + + }, + + translateX: function ( distance ) { + + this.translate( distance, this._vector.set( 1, 0, 0 ) ); + + }, + + translateY: function ( distance ) { + + this.translate( distance, this._vector.set( 0, 1, 0 ) ); + + }, + + translateZ: function ( distance ) { + + this.translate( distance, this._vector.set( 0, 0, 1 ) ); + + }, + + localToWorld: function ( vector ) { + + return this.matrixWorld.multiplyVector3( vector ); + + }, + + worldToLocal: function ( vector ) { + + return THREE.Object3D.__m1.getInverse( this.matrixWorld ).multiplyVector3( vector ); + + }, + + lookAt: function ( vector ) { + + // TODO: Add hierarchy support. + + this.matrix.lookAt( vector, this.position, this.up ); + + if ( this.rotationAutoUpdate ) { + + this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder ); + + } + + }, + + add: function ( object ) { + + if ( object === this ) { + + console.warn( 'THREE.Object3D.add: An object can\'t be added as a child of itself.' ); + return; + + } + + if ( object instanceof THREE.Object3D ) { + + if ( object.parent !== undefined ) { + + object.parent.remove( object ); + + } + + object.parent = this; + this.children.push( object ); + + // add to scene + + var scene = this; + + while ( scene.parent !== undefined ) { + + scene = scene.parent; + + } + + if ( scene !== undefined && scene instanceof THREE.Scene ) { + + scene.__addObject( object ); + + } + + } + + }, + + remove: function ( object ) { + + var index = this.children.indexOf( object ); + + if ( index !== - 1 ) { + + object.parent = undefined; + this.children.splice( index, 1 ); + + // remove from scene + + var scene = this; + + while ( scene.parent !== undefined ) { + + scene = scene.parent; + + } + + if ( scene !== undefined && scene instanceof THREE.Scene ) { + + scene.__removeObject( object ); + + } + + } + + }, + + traverse: function ( callback ) { + + callback( this ); + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].traverse( callback ); + + } + + }, + + getChildByName: function ( name, recursive ) { + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + + if ( child.name === name ) { + + return child; + + } + + if ( recursive === true ) { + + child = child.getChildByName( name, recursive ); + + if ( child !== undefined ) { + + return child; + + } + + } + + } + + return undefined; + + }, + + getDescendants: function ( array ) { + + if ( array === undefined ) array = []; + + Array.prototype.push.apply( array, this.children ); + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].getDescendants( array ); + + } + + return array; + + }, + + updateMatrix: function () { + + this.matrix.setPosition( this.position ); + + if ( this.useQuaternion === false ) { + + this.matrix.setRotationFromEuler( this.rotation, this.eulerOrder ); + + } else { + + this.matrix.setRotationFromQuaternion( this.quaternion ); + + } + + if ( this.scale.x !== 1 || this.scale.y !== 1 || this.scale.z !== 1 ) { + + this.matrix.scale( this.scale ); + this.boundRadiusScale = Math.max( this.scale.x, Math.max( this.scale.y, this.scale.z ) ); + + } + + this.matrixWorldNeedsUpdate = true; + + }, + + updateMatrixWorld: function ( force ) { + + if ( this.matrixAutoUpdate === true ) this.updateMatrix(); + + if ( this.matrixWorldNeedsUpdate === true || force === true ) { + + if ( this.parent === undefined ) { + + this.matrixWorld.copy( this.matrix ); + + } else { + + this.matrixWorld.multiply( this.parent.matrixWorld, this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + + }, + + clone: function ( object ) { + + if ( object === undefined ) object = new THREE.Object3D(); + + object.name = this.name; + + object.up.copy( this.up ); + + object.position.copy( this.position ); + if ( object.rotation instanceof THREE.Vector3 ) object.rotation.copy( this.rotation ); // because of Sprite madness + object.eulerOrder = this.eulerOrder; + object.scale.copy( this.scale ); + + object.renderDepth = this.renderDepth; + + object.rotationAutoUpdate = this.rotationAutoUpdate; + + object.matrix.copy( this.matrix ); + object.matrixWorld.copy( this.matrixWorld ); + object.matrixRotationWorld.copy( this.matrixRotationWorld ); + + object.matrixAutoUpdate = this.matrixAutoUpdate; + object.matrixWorldNeedsUpdate = this.matrixWorldNeedsUpdate; + + object.quaternion.copy( this.quaternion ); + object.useQuaternion = this.useQuaternion; + + object.boundRadius = this.boundRadius; + object.boundRadiusScale = this.boundRadiusScale; + + object.visible = this.visible; + + object.castShadow = this.castShadow; + object.receiveShadow = this.receiveShadow; + + object.frustumCulled = this.frustumCulled; + + return object; + + }, + + deallocate: function () { + + var index = THREE.Object3DLibrary.indexOf( this ); + if ( index !== -1 ) THREE.Object3DLibrary.splice( index, 1 ); + + } + +}; + +THREE.Object3D.__m1 = new THREE.Matrix4(); +THREE.Object3D.defaultEulerOrder = 'XYZ', + +THREE.Object3DIdCount = 0; +THREE.Object3DLibrary = []; +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author julianwa / https://github.com/julianwa + */ + +THREE.Projector = function() { + + var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, + _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, + _face, _face3Count, _face3Pool = [], _face3PoolLength = 0, + _face4Count, _face4Pool = [], _face4PoolLength = 0, + _line, _lineCount, _linePool = [], _linePoolLength = 0, + _particle, _particleCount, _particlePool = [], _particlePoolLength = 0, + + _renderData = { objects: [], sprites: [], lights: [], elements: [] }, + + _vector3 = new THREE.Vector3(), + _vector4 = new THREE.Vector4(), + + _viewProjectionMatrix = new THREE.Matrix4(), + _modelViewProjectionMatrix = new THREE.Matrix4(), + + _frustum = new THREE.Frustum(), + + _clippedVertex1PositionScreen = new THREE.Vector4(), + _clippedVertex2PositionScreen = new THREE.Vector4(), + + _face3VertexNormals; + + this.projectVector = function ( vector, camera ) { + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _viewProjectionMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse ); + _viewProjectionMatrix.multiplyVector3( vector ); + + return vector; + + }; + + this.unprojectVector = function ( vector, camera ) { + + camera.projectionMatrixInverse.getInverse( camera.projectionMatrix ); + + _viewProjectionMatrix.multiply( camera.matrixWorld, camera.projectionMatrixInverse ); + _viewProjectionMatrix.multiplyVector3( vector ); + + return vector; + + }; + + this.pickingRay = function ( vector, camera ) { + + var end, ray, t; + + // set two vectors with opposing z values + vector.z = -1.0; + end = new THREE.Vector3( vector.x, vector.y, 1.0 ); + + this.unprojectVector( vector, camera ); + this.unprojectVector( end, camera ); + + // find direction from vector to end + end.subSelf( vector ).normalize(); + + return new THREE.Ray( vector, end ); + + }; + + var projectGraph = function ( root, sortObjects ) { + + _objectCount = 0; + + _renderData.objects.length = 0; + _renderData.sprites.length = 0; + _renderData.lights.length = 0; + + var projectObject = function ( parent ) { + + for ( var c = 0, cl = parent.children.length; c < cl; c ++ ) { + + var object = parent.children[ c ]; + + if ( object.visible === false ) continue; + + if ( object instanceof THREE.Light ) { + + _renderData.lights.push( object ); + + } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line ) { + + if ( object.frustumCulled === false || _frustum.contains( object ) === true ) { + + _object = getNextObjectInPool(); + _object.object = object; + + if ( object.renderDepth !== null ) { + + _object.z = object.renderDepth; + + } else { + + _vector3.copy( object.matrixWorld.getPosition() ); + _viewProjectionMatrix.multiplyVector3( _vector3 ); + _object.z = _vector3.z; + + } + + _renderData.objects.push( _object ); + + } + + } else if ( object instanceof THREE.Sprite || object instanceof THREE.Particle ) { + + _object = getNextObjectInPool(); + _object.object = object; + + // TODO: Find an elegant and performant solution and remove this dupe code. + + if ( object.renderDepth !== null ) { + + _object.z = object.renderDepth; + + } else { + + _vector3.copy( object.matrixWorld.getPosition() ); + _viewProjectionMatrix.multiplyVector3( _vector3 ); + _object.z = _vector3.z; + + } + + _renderData.sprites.push( _object ); + + } else { + + _object = getNextObjectInPool(); + _object.object = object; + + if ( object.renderDepth !== null ) { + + _object.z = object.renderDepth; + + } else { + + _vector3.copy( object.matrixWorld.getPosition() ); + _viewProjectionMatrix.multiplyVector3( _vector3 ); + _object.z = _vector3.z; + + } + + _renderData.objects.push( _object ); + + } + + projectObject( object ); + + } + + }; + + projectObject( root ); + + if ( sortObjects === true ) _renderData.objects.sort( painterSort ); + + return _renderData; + + }; + + this.projectScene = function ( scene, camera, sortObjects, sortElements ) { + + var near = camera.near, far = camera.far, visible = false, + o, ol, v, vl, f, fl, n, nl, c, cl, u, ul, object, + modelMatrix, rotationMatrix, + geometry, geometryMaterials, vertices, vertex, vertexPositionScreen, + faces, face, faceVertexNormals, normal, faceVertexUvs, uvs, + v1, v2, v3, v4, isFaceMaterial, material, side; + + _face3Count = 0; + _face4Count = 0; + _lineCount = 0; + _particleCount = 0; + + _renderData.elements.length = 0; + + scene.updateMatrixWorld(); + + if ( camera.parent === undefined ) camera.updateMatrixWorld(); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + _viewProjectionMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse ); + + _frustum.setFromMatrix( _viewProjectionMatrix ); + + _renderData = projectGraph( scene, sortObjects ); + + for ( o = 0, ol = _renderData.objects.length; o < ol; o++ ) { + + object = _renderData.objects[ o ].object; + + modelMatrix = object.matrixWorld; + + _vertexCount = 0; + + if ( object instanceof THREE.Mesh ) { + + geometry = object.geometry; + geometryMaterials = object.geometry.materials; + vertices = geometry.vertices; + faces = geometry.faces; + faceVertexUvs = geometry.faceVertexUvs; + + rotationMatrix = object.matrixRotationWorld.extractRotation( modelMatrix ); + + isFaceMaterial = object.material instanceof THREE.MeshFaceMaterial; + side = object.material.side; + + for ( v = 0, vl = vertices.length; v < vl; v ++ ) { + + _vertex = getNextVertexInPool(); + _vertex.positionWorld.copy( vertices[ v ] ); + + modelMatrix.multiplyVector3( _vertex.positionWorld ); + + _vertex.positionScreen.copy( _vertex.positionWorld ); + _viewProjectionMatrix.multiplyVector4( _vertex.positionScreen ); + + _vertex.positionScreen.x /= _vertex.positionScreen.w; + _vertex.positionScreen.y /= _vertex.positionScreen.w; + + _vertex.visible = _vertex.positionScreen.z > near && _vertex.positionScreen.z < far; + + } + + for ( f = 0, fl = faces.length; f < fl; f ++ ) { + + face = faces[ f ]; + + material = isFaceMaterial === true ? geometryMaterials[ face.materialIndex ] : object.material; + + if ( material === undefined ) continue; + + side = material.side; + + if ( face instanceof THREE.Face3 ) { + + v1 = _vertexPool[ face.a ]; + v2 = _vertexPool[ face.b ]; + v3 = _vertexPool[ face.c ]; + + if ( v1.visible === true && v2.visible === true && v3.visible === true ) { + + visible = ( ( v3.positionScreen.x - v1.positionScreen.x ) * ( v2.positionScreen.y - v1.positionScreen.y ) - + ( v3.positionScreen.y - v1.positionScreen.y ) * ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; + + if ( side === THREE.DoubleSide || visible === ( side === THREE.FrontSide ) ) { + + _face = getNextFace3InPool(); + + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + + } else { + + continue; + + } + + } else { + + continue; + + } + + } else if ( face instanceof THREE.Face4 ) { + + v1 = _vertexPool[ face.a ]; + v2 = _vertexPool[ face.b ]; + v3 = _vertexPool[ face.c ]; + v4 = _vertexPool[ face.d ]; + + if ( v1.visible === true && v2.visible === true && v3.visible === true && v4.visible === true ) { + + visible = ( v4.positionScreen.x - v1.positionScreen.x ) * ( v2.positionScreen.y - v1.positionScreen.y ) - + ( v4.positionScreen.y - v1.positionScreen.y ) * ( v2.positionScreen.x - v1.positionScreen.x ) < 0 || + ( v2.positionScreen.x - v3.positionScreen.x ) * ( v4.positionScreen.y - v3.positionScreen.y ) - + ( v2.positionScreen.y - v3.positionScreen.y ) * ( v4.positionScreen.x - v3.positionScreen.x ) < 0; + + + if ( side === THREE.DoubleSide || visible === ( side === THREE.FrontSide ) ) { + + _face = getNextFace4InPool(); + + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + _face.v4.copy( v4 ); + + } else { + + continue; + + } + + } else { + + continue; + + } + + } + + _face.normalWorld.copy( face.normal ); + + if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) _face.normalWorld.negate(); + rotationMatrix.multiplyVector3( _face.normalWorld ); + + _face.centroidWorld.copy( face.centroid ); + modelMatrix.multiplyVector3( _face.centroidWorld ); + + _face.centroidScreen.copy( _face.centroidWorld ); + _viewProjectionMatrix.multiplyVector3( _face.centroidScreen ); + + faceVertexNormals = face.vertexNormals; + + for ( n = 0, nl = faceVertexNormals.length; n < nl; n ++ ) { + + normal = _face.vertexNormalsWorld[ n ]; + normal.copy( faceVertexNormals[ n ] ); + + if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) normal.negate(); + + rotationMatrix.multiplyVector3( normal ); + + } + + _face.vertexNormalsLength = faceVertexNormals.length; + + for ( c = 0, cl = faceVertexUvs.length; c < cl; c ++ ) { + + uvs = faceVertexUvs[ c ][ f ]; + + if ( uvs === undefined ) continue; + + for ( u = 0, ul = uvs.length; u < ul; u ++ ) { + + _face.uvs[ c ][ u ] = uvs[ u ]; + + } + + } + + _face.color = face.color; + _face.material = material; + + _face.z = _face.centroidScreen.z; + + _renderData.elements.push( _face ); + + } + + } else if ( object instanceof THREE.Line ) { + + _modelViewProjectionMatrix.multiply( _viewProjectionMatrix, modelMatrix ); + + vertices = object.geometry.vertices; + + v1 = getNextVertexInPool(); + v1.positionScreen.copy( vertices[ 0 ] ); + _modelViewProjectionMatrix.multiplyVector4( v1.positionScreen ); + + // Handle LineStrip and LinePieces + var step = object.type === THREE.LinePieces ? 2 : 1; + + for ( v = 1, vl = vertices.length; v < vl; v ++ ) { + + v1 = getNextVertexInPool(); + v1.positionScreen.copy( vertices[ v ] ); + _modelViewProjectionMatrix.multiplyVector4( v1.positionScreen ); + + if ( ( v + 1 ) % step > 0 ) continue; + + v2 = _vertexPool[ _vertexCount - 2 ]; + + _clippedVertex1PositionScreen.copy( v1.positionScreen ); + _clippedVertex2PositionScreen.copy( v2.positionScreen ); + + if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { + + // Perform the perspective divide + _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); + _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); + + _line = getNextLineInPool(); + _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); + _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); + + _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); + + _line.material = object.material; + + _renderData.elements.push( _line ); + + } + + } + + } + + } + + for ( o = 0, ol = _renderData.sprites.length; o < ol; o++ ) { + + object = _renderData.sprites[ o ].object; + + modelMatrix = object.matrixWorld; + + if ( object instanceof THREE.Particle ) { + + _vector4.set( modelMatrix.elements[12], modelMatrix.elements[13], modelMatrix.elements[14], 1 ); + _viewProjectionMatrix.multiplyVector4( _vector4 ); + + _vector4.z /= _vector4.w; + + if ( _vector4.z > 0 && _vector4.z < 1 ) { + + _particle = getNextParticleInPool(); + _particle.object = object; + _particle.x = _vector4.x / _vector4.w; + _particle.y = _vector4.y / _vector4.w; + _particle.z = _vector4.z; + + _particle.rotation = object.rotation.z; + + _particle.scale.x = object.scale.x * Math.abs( _particle.x - ( _vector4.x + camera.projectionMatrix.elements[0] ) / ( _vector4.w + camera.projectionMatrix.elements[12] ) ); + _particle.scale.y = object.scale.y * Math.abs( _particle.y - ( _vector4.y + camera.projectionMatrix.elements[5] ) / ( _vector4.w + camera.projectionMatrix.elements[13] ) ); + + _particle.material = object.material; + + _renderData.elements.push( _particle ); + + } + + } + + } + + if ( sortElements === true ) _renderData.elements.sort( painterSort ); + + return _renderData; + + }; + + // Pools + + function getNextObjectInPool() { + + if ( _objectCount === _objectPoolLength ) { + + var object = new THREE.RenderableObject(); + _objectPool.push( object ); + _objectPoolLength ++; + _objectCount ++; + return object; + + } + + return _objectPool[ _objectCount ++ ]; + + } + + function getNextVertexInPool() { + + if ( _vertexCount === _vertexPoolLength ) { + + var vertex = new THREE.RenderableVertex(); + _vertexPool.push( vertex ); + _vertexPoolLength ++; + _vertexCount ++; + return vertex; + + } + + return _vertexPool[ _vertexCount ++ ]; + + } + + function getNextFace3InPool() { + + if ( _face3Count === _face3PoolLength ) { + + var face = new THREE.RenderableFace3(); + _face3Pool.push( face ); + _face3PoolLength ++; + _face3Count ++; + return face; + + } + + return _face3Pool[ _face3Count ++ ]; + + + } + + function getNextFace4InPool() { + + if ( _face4Count === _face4PoolLength ) { + + var face = new THREE.RenderableFace4(); + _face4Pool.push( face ); + _face4PoolLength ++; + _face4Count ++; + return face; + + } + + return _face4Pool[ _face4Count ++ ]; + + } + + function getNextLineInPool() { + + if ( _lineCount === _linePoolLength ) { + + var line = new THREE.RenderableLine(); + _linePool.push( line ); + _linePoolLength ++; + _lineCount ++ + return line; + + } + + return _linePool[ _lineCount ++ ]; + + } + + function getNextParticleInPool() { + + if ( _particleCount === _particlePoolLength ) { + + var particle = new THREE.RenderableParticle(); + _particlePool.push( particle ); + _particlePoolLength ++; + _particleCount ++ + return particle; + + } + + return _particlePool[ _particleCount ++ ]; + + } + + // + + function painterSort( a, b ) { + + return b.z - a.z; + + } + + function clipLine( s1, s2 ) { + + var alpha1 = 0, alpha2 = 1, + + // Calculate the boundary coordinate of each vertex for the near and far clip planes, + // Z = -1 and Z = +1, respectively. + bc1near = s1.z + s1.w, + bc2near = s2.z + s2.w, + bc1far = - s1.z + s1.w, + bc2far = - s2.z + s2.w; + + if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { + + // Both vertices lie entirely within all clip planes. + return true; + + } else if ( ( bc1near < 0 && bc2near < 0) || (bc1far < 0 && bc2far < 0 ) ) { + + // Both vertices lie entirely outside one of the clip planes. + return false; + + } else { + + // The line segment spans at least one clip plane. + + if ( bc1near < 0 ) { + + // v1 lies outside the near plane, v2 inside + alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); + + } else if ( bc2near < 0 ) { + + // v2 lies outside the near plane, v1 inside + alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); + + } + + if ( bc1far < 0 ) { + + // v1 lies outside the far plane, v2 inside + alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); + + } else if ( bc2far < 0 ) { + + // v2 lies outside the far plane, v2 inside + alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); + + } + + if ( alpha2 < alpha1 ) { + + // The line segment spans two boundaries, but is outside both of them. + // (This can't happen when we're only clipping against just near/far but good + // to leave the check here for future usage if other clip planes are added.) + return false; + + } else { + + // Update the s1 and s2 vertices to match the clipped line segment. + s1.lerpSelf( s2, alpha1 ); + s2.lerpSelf( s1, 1 - alpha2 ); + + return true; + + } + + } + + } + +}; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author WestLangley / http://github.com/WestLangley + */ + +THREE.Quaternion = function( x, y, z, w ) { + + this.x = x || 0; + this.y = y || 0; + this.z = z || 0; + this.w = ( w !== undefined ) ? w : 1; + +}; + +THREE.Quaternion.prototype = { + + constructor: THREE.Quaternion, + + set: function ( x, y, z, w ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + copy: function ( q ) { + + this.x = q.x; + this.y = q.y; + this.z = q.z; + this.w = q.w; + + return this; + + }, + + setFromEuler: function ( v, order ) { + + // http://www.mathworks.com/matlabcentral/fileexchange/ + // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ + // content/SpinCalc.m + + var c1 = Math.cos( v.x / 2 ); + var c2 = Math.cos( v.y / 2 ); + var c3 = Math.cos( v.z / 2 ); + var s1 = Math.sin( v.x / 2 ); + var s2 = Math.sin( v.y / 2 ); + var s3 = Math.sin( v.z / 2 ); + + if ( order === undefined || order === 'XYZ' ) { + + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 + s1 * s2 * c3; + this.w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'YXZ' ) { + + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 - s1 * s2 * c3; + this.w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( order === 'ZXY' ) { + + this.x = s1 * c2 * c3 - c1 * s2 * s3; + this.y = c1 * s2 * c3 + s1 * c2 * s3; + this.z = c1 * c2 * s3 + s1 * s2 * c3; + this.w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'ZYX' ) { + + this.x = s1 * c2 * c3 - c1 * s2 * s3; + this.y = c1 * s2 * c3 + s1 * c2 * s3; + this.z = c1 * c2 * s3 - s1 * s2 * c3; + this.w = c1 * c2 * c3 + s1 * s2 * s3; + + } else if ( order === 'YZX' ) { + + this.x = s1 * c2 * c3 + c1 * s2 * s3; + this.y = c1 * s2 * c3 + s1 * c2 * s3; + this.z = c1 * c2 * s3 - s1 * s2 * c3; + this.w = c1 * c2 * c3 - s1 * s2 * s3; + + } else if ( order === 'XZY' ) { + + this.x = s1 * c2 * c3 - c1 * s2 * s3; + this.y = c1 * s2 * c3 - s1 * c2 * s3; + this.z = c1 * c2 * s3 + s1 * s2 * c3; + this.w = c1 * c2 * c3 + s1 * s2 * s3; + + } + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // from http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + // axis have to be normalized + + var halfAngle = angle / 2, + s = Math.sin( halfAngle ); + + this.x = axis.x * s; + this.y = axis.y * s; + this.z = axis.z * s; + this.w = Math.cos( halfAngle ); + + return this; + + }, + + setFromRotationMatrix: function ( m ) { + + // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm + + // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) + + var te = m.elements, + + m11 = te[0], m12 = te[4], m13 = te[8], + m21 = te[1], m22 = te[5], m23 = te[9], + m31 = te[2], m32 = te[6], m33 = te[10], + + trace = m11 + m22 + m33, + s; + + if( trace > 0 ) { + + s = 0.5 / Math.sqrt( trace + 1.0 ); + + this.w = 0.25 / s; + this.x = ( m32 - m23 ) * s; + this.y = ( m13 - m31 ) * s; + this.z = ( m21 - m12 ) * s; + + } else if ( m11 > m22 && m11 > m33 ) { + + s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); + + this.w = (m32 - m23 ) / s; + this.x = 0.25 * s; + this.y = (m12 + m21 ) / s; + this.z = (m13 + m31 ) / s; + + } else if (m22 > m33) { + + s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); + + this.w = (m13 - m31 ) / s; + this.x = (m12 + m21 ) / s; + this.y = 0.25 * s; + this.z = (m23 + m32 ) / s; + + } else { + + s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); + + this.w = ( m21 - m12 ) / s; + this.x = ( m13 + m31 ) / s; + this.y = ( m23 + m32 ) / s; + this.z = 0.25 * s; + + } + + return this; + + }, + + calculateW : function () { + + this.w = - Math.sqrt( Math.abs( 1.0 - this.x * this.x - this.y * this.y - this.z * this.z ) ); + + return this; + + }, + + inverse: function () { + + this.x *= -1; + this.y *= -1; + this.z *= -1; + + return this; + + }, + + length: function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + + }, + + normalize: function () { + + var l = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + + if ( l === 0 ) { + + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + + } else { + + l = 1 / l; + + this.x = this.x * l; + this.y = this.y * l; + this.z = this.z * l; + this.w = this.w * l; + + } + + return this; + + }, + + multiply: function ( a, b ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + var qax = a.x, qay = a.y, qaz = a.z, qaw = a.w, + qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w; + + this.x = qax * qbw + qay * qbz - qaz * qby + qaw * qbx; + this.y = -qax * qbz + qay * qbw + qaz * qbx + qaw * qby; + this.z = qax * qby - qay * qbx + qaz * qbw + qaw * qbz; + this.w = -qax * qbx - qay * qby - qaz * qbz + qaw * qbw; + + return this; + + }, + + multiplySelf: function ( b ) { + + var qax = this.x, qay = this.y, qaz = this.z, qaw = this.w, + qbx = b.x, qby = b.y, qbz = b.z, qbw = b.w; + + this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + return this; + + }, + + multiplyVector3: function ( vector, dest ) { + + if ( !dest ) { dest = vector; } + + var x = vector.x, y = vector.y, z = vector.z, + qx = this.x, qy = this.y, qz = this.z, qw = this.w; + + // calculate quat * vector + + var ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + dest.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + dest.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + dest.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + + return dest; + + }, + + slerpSelf: function ( qb, t ) { + + var x = this.x, y = this.y, z = this.z, w = this.w; + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = w * qb.w + x * qb.x + y * qb.y + z * qb.z; + + if ( cosHalfTheta < 0 ) { + + this.w = -qb.w; + this.x = -qb.x; + this.y = -qb.y; + this.z = -qb.z; + + cosHalfTheta = -cosHalfTheta; + + } else { + + this.copy( qb ); + + } + + if ( cosHalfTheta >= 1.0 ) { + + this.w = w; + this.x = x; + this.y = y; + this.z = z; + + return this; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + this.w = 0.5 * ( w + this.w ); + this.x = 0.5 * ( x + this.x ); + this.y = 0.5 * ( y + this.y ); + this.z = 0.5 * ( z + this.z ); + + return this; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + this.w = ( w * ratioA + this.w * ratioB ); + this.x = ( x * ratioA + this.x * ratioB ); + this.y = ( y * ratioA + this.y * ratioB ); + this.z = ( z * ratioA + this.z * ratioB ); + + return this; + + }, + + clone: function () { + + return new THREE.Quaternion( this.x, this.y, this.z, this.w ); + + } + +} + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ + + var cosHalfTheta = qa.w * qb.w + qa.x * qb.x + qa.y * qb.y + qa.z * qb.z; + + if ( cosHalfTheta < 0 ) { + + qm.w = -qb.w; + qm.x = -qb.x; + qm.y = -qb.y; + qm.z = -qb.z; + + cosHalfTheta = -cosHalfTheta; + + } else { + + qm.copy( qb ); + + } + + if ( Math.abs( cosHalfTheta ) >= 1.0 ) { + + qm.w = qa.w; + qm.x = qa.x; + qm.y = qa.y; + qm.z = qa.z; + + return qm; + + } + + var halfTheta = Math.acos( cosHalfTheta ); + var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + qm.w = 0.5 * ( qa.w + qm.w ); + qm.x = 0.5 * ( qa.x + qm.x ); + qm.y = 0.5 * ( qa.y + qm.y ); + qm.z = 0.5 * ( qa.z + qm.z ); + + return qm; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta; + var ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + qm.w = ( qa.w * ratioA + qm.w * ratioB ); + qm.x = ( qa.x * ratioA + qm.x * ratioB ); + qm.y = ( qa.y * ratioA + qm.y * ratioB ); + qm.z = ( qa.z * ratioA + qm.z * ratioB ); + + return qm; + +} +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Vertex = function ( v ) { + + console.warn( 'THREE.Vertex has been DEPRECATED. Use THREE.Vector3 instead.') + return v; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { + + this.a = a; + this.b = b; + this.c = c; + + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = normal instanceof Array ? normal : [ ]; + + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = color instanceof Array ? color : []; + + this.vertexTangents = []; + + this.materialIndex = materialIndex; + + this.centroid = new THREE.Vector3(); + +}; + +THREE.Face3.prototype = { + + constructor: THREE.Face3, + + clone: function () { + + var face = new THREE.Face3( this.a, this.b, this.c ); + + face.normal.copy( this.normal ); + face.color.copy( this.color ); + face.centroid.copy( this.centroid ); + + face.materialIndex = this.materialIndex; + + var i, il; + for ( i = 0, il = this.vertexNormals.length; i < il; i ++ ) face.vertexNormals[ i ] = this.vertexNormals[ i ].clone(); + for ( i = 0, il = this.vertexColors.length; i < il; i ++ ) face.vertexColors[ i ] = this.vertexColors[ i ].clone(); + for ( i = 0, il = this.vertexTangents.length; i < il; i ++ ) face.vertexTangents[ i ] = this.vertexTangents[ i ].clone(); + + return face; + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Face4 = function ( a, b, c, d, normal, color, materialIndex ) { + + this.a = a; + this.b = b; + this.c = c; + this.d = d; + + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = normal instanceof Array ? normal : [ ]; + + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = color instanceof Array ? color : []; + + this.vertexTangents = []; + + this.materialIndex = materialIndex; + + this.centroid = new THREE.Vector3(); + +}; + +THREE.Face4.prototype = { + + constructor: THREE.Face4, + + clone: function () { + + var face = new THREE.Face4( this.a, this.b, this.c, this.d ); + + face.normal.copy( this.normal ); + face.color.copy( this.color ); + face.centroid.copy( this.centroid ); + + face.materialIndex = this.materialIndex; + + var i, il; + for ( i = 0, il = this.vertexNormals.length; i < il; i ++ ) face.vertexNormals[ i ] = this.vertexNormals[ i ].clone(); + for ( i = 0, il = this.vertexColors.length; i < il; i ++ ) face.vertexColors[ i ] = this.vertexColors[ i ].clone(); + for ( i = 0, il = this.vertexTangents.length; i < il; i ++ ) face.vertexTangents[ i ] = this.vertexTangents[ i ].clone(); + + return face; + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.UV = function ( u, v ) { + + this.u = u || 0; + this.v = v || 0; + +}; + +THREE.UV.prototype = { + + constructor: THREE.UV, + + set: function ( u, v ) { + + this.u = u; + this.v = v; + + return this; + + }, + + copy: function ( uv ) { + + this.u = uv.u; + this.v = uv.v; + + return this; + + }, + + lerpSelf: function ( uv, alpha ) { + + this.u += ( uv.u - this.u ) * alpha; + this.v += ( uv.v - this.v ) * alpha; + + return this; + + }, + + clone: function () { + + return new THREE.UV( this.u, this.v ); + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Geometry = function () { + + THREE.GeometryLibrary.push( this ); + + this.id = THREE.GeometryIdCount ++; + + this.name = ''; + + this.vertices = []; + this.colors = []; // one-to-one vertex colors, used in ParticleSystem, Line and Ribbon + + this.materials = []; + + this.faces = []; + + this.faceUvs = [[]]; + this.faceVertexUvs = [[]]; + + this.morphTargets = []; + this.morphColors = []; + this.morphNormals = []; + + this.skinWeights = []; + this.skinIndices = []; + + this.boundingBox = null; + this.boundingSphere = null; + + this.hasTangents = false; + + this.dynamic = true; // the intermediate typearrays will be deleted when set to false + + // update flags + + this.verticesNeedUpdate = false; + this.elementsNeedUpdate = false; + this.uvsNeedUpdate = false; + this.normalsNeedUpdate = false; + this.tangentsNeedUpdate = false; + this.colorsNeedUpdate = false; + +}; + +THREE.Geometry.prototype = { + + constructor : THREE.Geometry, + + applyMatrix: function ( matrix ) { + + var matrixRotation = new THREE.Matrix4(); + matrixRotation.extractRotation( matrix ); + + for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { + + var vertex = this.vertices[ i ]; + + matrix.multiplyVector3( vertex ); + + } + + for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + + var face = this.faces[ i ]; + + matrixRotation.multiplyVector3( face.normal ); + + for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { + + matrixRotation.multiplyVector3( face.vertexNormals[ j ] ); + + } + + matrix.multiplyVector3( face.centroid ); + + } + + }, + + computeCentroids: function () { + + var f, fl, face; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + face.centroid.set( 0, 0, 0 ); + + if ( face instanceof THREE.Face3 ) { + + face.centroid.addSelf( this.vertices[ face.a ] ); + face.centroid.addSelf( this.vertices[ face.b ] ); + face.centroid.addSelf( this.vertices[ face.c ] ); + face.centroid.divideScalar( 3 ); + + } else if ( face instanceof THREE.Face4 ) { + + face.centroid.addSelf( this.vertices[ face.a ] ); + face.centroid.addSelf( this.vertices[ face.b ] ); + face.centroid.addSelf( this.vertices[ face.c ] ); + face.centroid.addSelf( this.vertices[ face.d ] ); + face.centroid.divideScalar( 4 ); + + } + + } + + }, + + computeFaceNormals: function () { + + var n, nl, v, vl, vertex, f, fl, face, vA, vB, vC, + cb = new THREE.Vector3(), ab = new THREE.Vector3(); + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + vA = this.vertices[ face.a ]; + vB = this.vertices[ face.b ]; + vC = this.vertices[ face.c ]; + + cb.sub( vC, vB ); + ab.sub( vA, vB ); + cb.crossSelf( ab ); + + if ( !cb.isZero() ) { + + cb.normalize(); + + } + + face.normal.copy( cb ); + + } + + }, + + computeVertexNormals: function () { + + var v, vl, f, fl, face, vertices; + + // create internal buffers for reuse when calling this method repeatedly + // (otherwise memory allocation / deallocation every frame is big resource hog) + + if ( this.__tmpVertices === undefined ) { + + this.__tmpVertices = new Array( this.vertices.length ); + vertices = this.__tmpVertices; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ] = new THREE.Vector3(); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( face instanceof THREE.Face3 ) { + + face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + + } else if ( face instanceof THREE.Face4 ) { + + face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + + } + + } + + } else { + + vertices = this.__tmpVertices; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ].set( 0, 0, 0 ); + + } + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( face instanceof THREE.Face3 ) { + + vertices[ face.a ].addSelf( face.normal ); + vertices[ face.b ].addSelf( face.normal ); + vertices[ face.c ].addSelf( face.normal ); + + } else if ( face instanceof THREE.Face4 ) { + + vertices[ face.a ].addSelf( face.normal ); + vertices[ face.b ].addSelf( face.normal ); + vertices[ face.c ].addSelf( face.normal ); + vertices[ face.d ].addSelf( face.normal ); + + } + + } + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ].normalize(); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( face instanceof THREE.Face3 ) { + + face.vertexNormals[ 0 ].copy( vertices[ face.a ] ); + face.vertexNormals[ 1 ].copy( vertices[ face.b ] ); + face.vertexNormals[ 2 ].copy( vertices[ face.c ] ); + + } else if ( face instanceof THREE.Face4 ) { + + face.vertexNormals[ 0 ].copy( vertices[ face.a ] ); + face.vertexNormals[ 1 ].copy( vertices[ face.b ] ); + face.vertexNormals[ 2 ].copy( vertices[ face.c ] ); + face.vertexNormals[ 3 ].copy( vertices[ face.d ] ); + + } + + } + + }, + + computeMorphNormals: function () { + + var i, il, f, fl, face; + + // save original normals + // - create temp variables on first access + // otherwise just copy (for faster repeated calls) + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( ! face.__originalFaceNormal ) { + + face.__originalFaceNormal = face.normal.clone(); + + } else { + + face.__originalFaceNormal.copy( face.normal ); + + } + + if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; + + for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { + + if ( ! face.__originalVertexNormals[ i ] ) { + + face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); + + } else { + + face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); + + } + + } + + } + + // use temp geometry to compute face and vertex normals for each morph + + var tmpGeo = new THREE.Geometry(); + tmpGeo.faces = this.faces; + + for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { + + // create on first access + + if ( ! this.morphNormals[ i ] ) { + + this.morphNormals[ i ] = {}; + this.morphNormals[ i ].faceNormals = []; + this.morphNormals[ i ].vertexNormals = []; + + var dstNormalsFace = this.morphNormals[ i ].faceNormals; + var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + faceNormal = new THREE.Vector3(); + + if ( face instanceof THREE.Face3 ) { + + vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; + + } else { + + vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3(), d: new THREE.Vector3() }; + + } + + dstNormalsFace.push( faceNormal ); + dstNormalsVertex.push( vertexNormals ); + + } + + } + + var morphNormals = this.morphNormals[ i ]; + + // set vertices to morph target + + tmpGeo.vertices = this.morphTargets[ i ].vertices; + + // compute morph normals + + tmpGeo.computeFaceNormals(); + tmpGeo.computeVertexNormals(); + + // store morph normals + + var faceNormal, vertexNormals; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + faceNormal = morphNormals.faceNormals[ f ]; + vertexNormals = morphNormals.vertexNormals[ f ]; + + faceNormal.copy( face.normal ); + + if ( face instanceof THREE.Face3 ) { + + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + + } else { + + vertexNormals.a.copy( face.vertexNormals[ 0 ] ); + vertexNormals.b.copy( face.vertexNormals[ 1 ] ); + vertexNormals.c.copy( face.vertexNormals[ 2 ] ); + vertexNormals.d.copy( face.vertexNormals[ 3 ] ); + + } + + } + + } + + // restore original normals + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + face.normal = face.__originalFaceNormal; + face.vertexNormals = face.__originalVertexNormals; + + } + + }, + + computeTangents: function () { + + // based on http://www.terathon.com/code/tangent.html + // tangents go to vertices + + var f, fl, v, vl, i, il, vertexIndex, + face, uv, vA, vB, vC, uvA, uvB, uvC, + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r, t, test, + tan1 = [], tan2 = [], + sdir = new THREE.Vector3(), tdir = new THREE.Vector3(), + tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(), + n = new THREE.Vector3(), w; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + tan1[ v ] = new THREE.Vector3(); + tan2[ v ] = new THREE.Vector3(); + + } + + function handleTriangle( context, a, b, c, ua, ub, uc ) { + + vA = context.vertices[ a ]; + vB = context.vertices[ b ]; + vC = context.vertices[ c ]; + + uvA = uv[ ua ]; + uvB = uv[ ub ]; + uvC = uv[ uc ]; + + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; + + s1 = uvB.u - uvA.u; + s2 = uvC.u - uvA.u; + t1 = uvB.v - uvA.v; + t2 = uvC.v - uvA.v; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + sdir.set( ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r ); + tdir.set( ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r ); + + tan1[ a ].addSelf( sdir ); + tan1[ b ].addSelf( sdir ); + tan1[ c ].addSelf( sdir ); + + tan2[ a ].addSelf( tdir ); + tan2[ b ].addSelf( tdir ); + tan2[ c ].addSelf( tdir ); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents + + if ( face instanceof THREE.Face3 ) { + + handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); + + } else if ( face instanceof THREE.Face4 ) { + + handleTriangle( this, face.a, face.b, face.d, 0, 1, 3 ); + handleTriangle( this, face.b, face.c, face.d, 1, 2, 3 ); + + } + + } + + var faceIndex = [ 'a', 'b', 'c', 'd' ]; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + for ( i = 0; i < face.vertexNormals.length; i++ ) { + + n.copy( face.vertexNormals[ i ] ); + + vertexIndex = face[ faceIndex[ i ] ]; + + t = tan1[ vertexIndex ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.subSelf( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.cross( face.vertexNormals[ i ], t ); + test = tmp2.dot( tan2[ vertexIndex ] ); + w = (test < 0.0) ? -1.0 : 1.0; + + face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w ); + + } + + } + + this.hasTangents = true; + + }, + + computeBoundingBox: function () { + + if ( ! this.boundingBox ) { + + this.boundingBox = { min: new THREE.Vector3(), max: new THREE.Vector3() }; + + } + + if ( this.vertices.length > 0 ) { + + var position, firstPosition = this.vertices[ 0 ]; + + this.boundingBox.min.copy( firstPosition ); + this.boundingBox.max.copy( firstPosition ); + + var min = this.boundingBox.min, + max = this.boundingBox.max; + + for ( var v = 1, vl = this.vertices.length; v < vl; v ++ ) { + + position = this.vertices[ v ]; + + if ( position.x < min.x ) { + + min.x = position.x; + + } else if ( position.x > max.x ) { + + max.x = position.x; + + } + + if ( position.y < min.y ) { + + min.y = position.y; + + } else if ( position.y > max.y ) { + + max.y = position.y; + + } + + if ( position.z < min.z ) { + + min.z = position.z; + + } else if ( position.z > max.z ) { + + max.z = position.z; + + } + + } + + } else { + + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); + + } + + }, + + computeBoundingSphere: function () { + + var maxRadiusSq = 0; + + if ( this.boundingSphere === null ) this.boundingSphere = { radius: 0 }; + + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + + var radiusSq = this.vertices[ i ].lengthSq(); + if ( radiusSq > maxRadiusSq ) maxRadiusSq = radiusSq; + + } + + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + + }, + + /* + * Checks for duplicate vertices with hashmap. + * Duplicated vertices are removed + * and faces' vertices are updated. + */ + + mergeVertices: function () { + + var verticesMap = {}; // Hashmap for looking up vertice by position coordinates (and making sure they are unique) + var unique = [], changes = []; + + var v, key; + var precisionPoints = 4; // number of decimal points, eg. 4 for epsilon of 0.0001 + var precision = Math.pow( 10, precisionPoints ); + var i,il, face; + var abcd = 'abcd', o, k, j, jl, u; + + for ( i = 0, il = this.vertices.length; i < il; i ++ ) { + + v = this.vertices[ i ]; + key = [ Math.round( v.x * precision ), Math.round( v.y * precision ), Math.round( v.z * precision ) ].join( '_' ); + + if ( verticesMap[ key ] === undefined ) { + + verticesMap[ key ] = i; + unique.push( this.vertices[ i ] ); + changes[ i ] = unique.length - 1; + + } else { + + //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); + changes[ i ] = changes[ verticesMap[ key ] ]; + + } + + }; + + + // Start to patch face indices + + for( i = 0, il = this.faces.length; i < il; i ++ ) { + + face = this.faces[ i ]; + + if ( face instanceof THREE.Face3 ) { + + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; + + } else if ( face instanceof THREE.Face4 ) { + + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; + face.d = changes[ face.d ]; + + // check dups in (a, b, c, d) and convert to -> face3 + + o = [ face.a, face.b, face.c, face.d ]; + + for ( k = 3; k > 0; k -- ) { + + if ( o.indexOf( face[ abcd[ k ] ] ) !== k ) { + + // console.log('faces', face.a, face.b, face.c, face.d, 'dup at', k); + + o.splice( k, 1 ); + + this.faces[ i ] = new THREE.Face3( o[0], o[1], o[2], face.normal, face.color, face.materialIndex ); + + for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { + + u = this.faceVertexUvs[ j ][ i ]; + if ( u ) u.splice( k, 1 ); + + } + + this.faces[ i ].vertexColors = face.vertexColors; + + break; + } + + } + + } + + } + + // Use unique set of vertices + + var diff = this.vertices.length - unique.length; + this.vertices = unique; + return diff; + + }, + + clone: function () { + + // TODO + + }, + + deallocate: function () { + + var index = THREE.GeometryLibrary.indexOf( this ); + if ( index !== -1 ) THREE.GeometryLibrary.splice( index, 1 ); + + } + +}; + +THREE.GeometryIdCount = 0; +THREE.GeometryLibrary = []; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.BufferGeometry = function () { + + THREE.GeometryLibrary.push( this ); + + this.id = THREE.GeometryIdCount ++; + + // attributes + + this.attributes = {}; + + // attributes typed arrays are kept only if dynamic flag is set + + this.dynamic = false; + + // boundings + + this.boundingBox = null; + this.boundingSphere = null; + + this.hasTangents = false; + + // for compatibility + + this.morphTargets = []; + +}; + +THREE.BufferGeometry.prototype = { + + constructor : THREE.BufferGeometry, + + applyMatrix: function ( matrix ) { + + var positionArray; + var normalArray; + + if ( this.attributes[ "position" ] ) positionArray = this.attributes[ "position" ].array; + if ( this.attributes[ "normal" ] ) normalArray = this.attributes[ "normal" ].array; + + if ( positionArray !== undefined ) { + + matrix.multiplyVector3Array( positionArray ); + this.verticesNeedUpdate = true; + + } + + if ( normalArray !== undefined ) { + + var matrixRotation = new THREE.Matrix4(); + matrixRotation.extractRotation( matrix ); + + matrixRotation.multiplyVector3Array( normalArray ); + this.normalsNeedUpdate = true; + + } + + }, + + computeBoundingBox: function () { + + if ( ! this.boundingBox ) { + + this.boundingBox = { + + min: new THREE.Vector3( Infinity, Infinity, Infinity ), + max: new THREE.Vector3( -Infinity, -Infinity, -Infinity ) + + }; + + } + + var positions = this.attributes[ "position" ].array; + + if ( positions ) { + + var bb = this.boundingBox; + var x, y, z; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + x = positions[ i ]; + y = positions[ i + 1 ]; + z = positions[ i + 2 ]; + + // bounding box + + if ( x < bb.min.x ) { + + bb.min.x = x; + + } else if ( x > bb.max.x ) { + + bb.max.x = x; + + } + + if ( y < bb.min.y ) { + + bb.min.y = y; + + } else if ( y > bb.max.y ) { + + bb.max.y = y; + + } + + if ( z < bb.min.z ) { + + bb.min.z = z; + + } else if ( z > bb.max.z ) { + + bb.max.z = z; + + } + + } + + } + + if ( positions === undefined || positions.length === 0 ) { + + this.boundingBox.min.set( 0, 0, 0 ); + this.boundingBox.max.set( 0, 0, 0 ); + + } + + }, + + computeBoundingSphere: function () { + + if ( ! this.boundingSphere ) this.boundingSphere = { radius: 0 }; + + var positions = this.attributes[ "position" ].array; + + if ( positions ) { + + var radiusSq, maxRadiusSq = 0; + var x, y, z; + + for ( var i = 0, il = positions.length; i < il; i += 3 ) { + + x = positions[ i ]; + y = positions[ i + 1 ]; + z = positions[ i + 2 ]; + + radiusSq = x * x + y * y + z * z; + if ( radiusSq > maxRadiusSq ) maxRadiusSq = radiusSq; + + } + + this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); + + } + + }, + + computeVertexNormals: function () { + + if ( this.attributes[ "position" ] && this.attributes[ "index" ] ) { + + var i, il; + var j, jl; + + var nVertexElements = this.attributes[ "position" ].array.length; + + if ( this.attributes[ "normal" ] === undefined ) { + + this.attributes[ "normal" ] = { + + itemSize: 3, + array: new Float32Array( nVertexElements ), + numItems: nVertexElements + + }; + + } else { + + // reset existing normals to zero + + for ( i = 0, il = this.attributes[ "normal" ].array.length; i < il; i ++ ) { + + this.attributes[ "normal" ].array[ i ] = 0; + + } + + } + + var offsets = this.offsets; + + var indices = this.attributes[ "index" ].array; + var positions = this.attributes[ "position" ].array; + var normals = this.attributes[ "normal" ].array; + + var vA, vB, vC, x, y, z, + + pA = new THREE.Vector3(), + pB = new THREE.Vector3(), + pC = new THREE.Vector3(), + + cb = new THREE.Vector3(), + ab = new THREE.Vector3(); + + for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + + var start = offsets[ j ].start; + var count = offsets[ j ].count; + var index = offsets[ j ].index; + + for ( i = start, il = start + count; i < il; i += 3 ) { + + vA = index + indices[ i ]; + vB = index + indices[ i + 1 ]; + vC = index + indices[ i + 2 ]; + + x = positions[ vA * 3 ]; + y = positions[ vA * 3 + 1 ]; + z = positions[ vA * 3 + 2 ]; + pA.set( x, y, z ); + + x = positions[ vB * 3 ]; + y = positions[ vB * 3 + 1 ]; + z = positions[ vB * 3 + 2 ]; + pB.set( x, y, z ); + + x = positions[ vC * 3 ]; + y = positions[ vC * 3 + 1 ]; + z = positions[ vC * 3 + 2 ]; + pC.set( x, y, z ); + + cb.sub( pC, pB ); + ab.sub( pA, pB ); + cb.crossSelf( ab ); + + normals[ vA * 3 ] += cb.x; + normals[ vA * 3 + 1 ] += cb.y; + normals[ vA * 3 + 2 ] += cb.z; + + normals[ vB * 3 ] += cb.x; + normals[ vB * 3 + 1 ] += cb.y; + normals[ vB * 3 + 2 ] += cb.z; + + normals[ vC * 3 ] += cb.x; + normals[ vC * 3 + 1 ] += cb.y; + normals[ vC * 3 + 2 ] += cb.z; + + } + + } + + // normalize normals + + for ( i = 0, il = normals.length; i < il; i += 3 ) { + + x = normals[ i ]; + y = normals[ i + 1 ]; + z = normals[ i + 2 ]; + + var n = 1.0 / Math.sqrt( x * x + y * y + z * z ); + + normals[ i ] *= n; + normals[ i + 1 ] *= n; + normals[ i + 2 ] *= n; + + } + + this.normalsNeedUpdate = true; + + } + + }, + + computeTangents: function () { + + // based on http://www.terathon.com/code/tangent.html + // (per vertex tangents) + + if ( this.attributes[ "index" ] === undefined || + this.attributes[ "position" ] === undefined || + this.attributes[ "normal" ] === undefined || + this.attributes[ "uv" ] === undefined ) { + + console.warn( "Missing required attributes (index, position, normal or uv) in BufferGeometry.computeTangents()" ); + return; + + } + + var indices = this.attributes[ "index" ].array; + var positions = this.attributes[ "position" ].array; + var normals = this.attributes[ "normal" ].array; + var uvs = this.attributes[ "uv" ].array; + + var nVertices = positions.length / 3; + + if ( this.attributes[ "tangent" ] === undefined ) { + + var nTangentElements = 4 * nVertices; + + this.attributes[ "tangent" ] = { + + itemSize: 4, + array: new Float32Array( nTangentElements ), + numItems: nTangentElements + + }; + + } + + var tangents = this.attributes[ "tangent" ].array; + + var tan1 = [], tan2 = []; + + for ( var k = 0; k < nVertices; k ++ ) { + + tan1[ k ] = new THREE.Vector3(); + tan2[ k ] = new THREE.Vector3(); + + } + + var xA, yA, zA, + xB, yB, zB, + xC, yC, zC, + + uA, vA, + uB, vB, + uC, vC, + + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r; + + var sdir = new THREE.Vector3(), tdir = new THREE.Vector3(); + + function handleTriangle( a, b, c ) { + + xA = positions[ a * 3 ]; + yA = positions[ a * 3 + 1 ]; + zA = positions[ a * 3 + 2 ]; + + xB = positions[ b * 3 ]; + yB = positions[ b * 3 + 1 ]; + zB = positions[ b * 3 + 2 ]; + + xC = positions[ c * 3 ]; + yC = positions[ c * 3 + 1 ]; + zC = positions[ c * 3 + 2 ]; + + uA = uvs[ a * 2 ]; + vA = uvs[ a * 2 + 1 ]; + + uB = uvs[ b * 2 ]; + vB = uvs[ b * 2 + 1 ]; + + uC = uvs[ c * 2 ]; + vC = uvs[ c * 2 + 1 ]; + + x1 = xB - xA; + x2 = xC - xA; + + y1 = yB - yA; + y2 = yC - yA; + + z1 = zB - zA; + z2 = zC - zA; + + s1 = uB - uA; + s2 = uC - uA; + + t1 = vB - vA; + t2 = vC - vA; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + + sdir.set( + ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r + ); + + tdir.set( + ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r + ); + + tan1[ a ].addSelf( sdir ); + tan1[ b ].addSelf( sdir ); + tan1[ c ].addSelf( sdir ); + + tan2[ a ].addSelf( tdir ); + tan2[ b ].addSelf( tdir ); + tan2[ c ].addSelf( tdir ); + + } + + var i, il; + var j, jl; + var iA, iB, iC; + + var offsets = this.offsets; + + for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + + var start = offsets[ j ].start; + var count = offsets[ j ].count; + var index = offsets[ j ].index; + + for ( i = start, il = start + count; i < il; i += 3 ) { + + iA = index + indices[ i ]; + iB = index + indices[ i + 1 ]; + iC = index + indices[ i + 2 ]; + + handleTriangle( iA, iB, iC ); + + } + + } + + var tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(); + var n = new THREE.Vector3(), n2 = new THREE.Vector3(); + var w, t, test; + var nx, ny, nz; + + function handleVertex( v ) { + + n.x = normals[ v * 3 ]; + n.y = normals[ v * 3 + 1 ]; + n.z = normals[ v * 3 + 2 ]; + + n2.copy( n ); + + t = tan1[ v ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.subSelf( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.cross( n2, t ); + test = tmp2.dot( tan2[ v ] ); + w = ( test < 0.0 ) ? -1.0 : 1.0; + + tangents[ v * 4 ] = tmp.x; + tangents[ v * 4 + 1 ] = tmp.y; + tangents[ v * 4 + 2 ] = tmp.z; + tangents[ v * 4 + 3 ] = w; + + } + + for ( j = 0, jl = offsets.length; j < jl; ++ j ) { + + var start = offsets[ j ].start; + var count = offsets[ j ].count; + var index = offsets[ j ].index; + + for ( i = start, il = start + count; i < il; i += 3 ) { + + iA = index + indices[ i ]; + iB = index + indices[ i + 1 ]; + iC = index + indices[ i + 2 ]; + + handleVertex( iA ); + handleVertex( iB ); + handleVertex( iC ); + + } + + } + + this.hasTangents = true; + this.tangentsNeedUpdate = true; + + }, + + deallocate: function () { + + var index = THREE.GeometryLibrary.indexOf( this ); + if ( index !== -1 ) THREE.GeometryLibrary.splice( index, 1 ); + + } + +}; + +/** + * Spline from Tween.js, slightly optimized (and trashed) + * http://sole.github.com/tween.js/examples/05_spline.html + * + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Spline = function ( points ) { + + this.points = points; + + var c = [], v3 = { x: 0, y: 0, z: 0 }, + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + this.initFromArray = function( a ) { + + this.points = []; + + for ( var i = 0; i < a.length; i++ ) { + + this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; + + } + + }; + + this.getPoint = function ( k ) { + + point = ( this.points.length - 1 ) * k; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1; + c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2; + + pa = this.points[ c[ 0 ] ]; + pb = this.points[ c[ 1 ] ]; + pc = this.points[ c[ 2 ] ]; + pd = this.points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); + v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); + v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); + + return v3; + + }; + + this.getControlPointsArray = function () { + + var i, p, l = this.points.length, + coords = []; + + for ( i = 0; i < l; i ++ ) { + + p = this.points[ i ]; + coords[ i ] = [ p.x, p.y, p.z ]; + + } + + return coords; + + }; + + // approximate length by summing linear segments + + this.getLength = function ( nSubDivisions ) { + + var i, index, nSamples, position, + point = 0, intPoint = 0, oldIntPoint = 0, + oldPosition = new THREE.Vector3(), + tmpVec = new THREE.Vector3(), + chunkLengths = [], + totalLength = 0; + + // first point has 0 length + + chunkLengths[ 0 ] = 0; + + if ( !nSubDivisions ) nSubDivisions = 100; + + nSamples = this.points.length * nSubDivisions; + + oldPosition.copy( this.points[ 0 ] ); + + for ( i = 1; i < nSamples; i ++ ) { + + index = i / nSamples; + + position = this.getPoint( index ); + tmpVec.copy( position ); + + totalLength += tmpVec.distanceTo( oldPosition ); + + oldPosition.copy( position ); + + point = ( this.points.length - 1 ) * index; + intPoint = Math.floor( point ); + + if ( intPoint != oldIntPoint ) { + + chunkLengths[ intPoint ] = totalLength; + oldIntPoint = intPoint; + + } + + } + + // last point ends with total length + + chunkLengths[ chunkLengths.length ] = totalLength; + + return { chunks: chunkLengths, total: totalLength }; + + }; + + this.reparametrizeByArcLength = function ( samplingCoef ) { + + var i, j, + index, indexCurrent, indexNext, + linearDistance, realDistance, + sampling, position, + newpoints = [], + tmpVec = new THREE.Vector3(), + sl = this.getLength(); + + newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); + + for ( i = 1; i < this.points.length; i++ ) { + + //tmpVec.copy( this.points[ i - 1 ] ); + //linearDistance = tmpVec.distanceTo( this.points[ i ] ); + + realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; + + sampling = Math.ceil( samplingCoef * realDistance / sl.total ); + + indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); + indexNext = i / ( this.points.length - 1 ); + + for ( j = 1; j < sampling - 1; j++ ) { + + index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); + + position = this.getPoint( index ); + newpoints.push( tmpVec.copy( position ).clone() ); + + } + + newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); + + } + + this.points = newpoints; + + }; + + // Catmull-Rom + + function interpolate( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + }; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Camera = function () { + + THREE.Object3D.call( this ); + + this.matrixWorldInverse = new THREE.Matrix4(); + + this.projectionMatrix = new THREE.Matrix4(); + this.projectionMatrixInverse = new THREE.Matrix4(); + +}; + +THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Camera.prototype.lookAt = function ( vector ) { + + // TODO: Add hierarchy support. + + this.matrix.lookAt( this.position, vector, this.up ); + + if ( this.rotationAutoUpdate === true ) { + + this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder ); + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { + + THREE.Camera.call( this ); + + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + + this.near = ( near !== undefined ) ? near : 0.1; + this.far = ( far !== undefined ) ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); + +THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { + + this.projectionMatrix.makeOrthographic( this.left, this.right, this.top, this.bottom, this.near, this.far ); + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author greggman / http://games.greggman.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { + + THREE.Camera.call( this ); + + this.fov = fov !== undefined ? fov : 50; + this.aspect = aspect !== undefined ? aspect : 1; + this.near = near !== undefined ? near : 0.1; + this.far = far !== undefined ? far : 2000; + + this.updateProjectionMatrix(); + +}; + +THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); + + +/** + * Uses Focal Length (in mm) to estimate and set FOV + * 35mm (fullframe) camera is used if frame size is not specified; + * Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html + */ + +THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { + + if ( frameHeight === undefined ) frameHeight = 24; + + this.fov = 2 * Math.atan( frameHeight / ( focalLength * 2 ) ) * ( 180 / Math.PI ); + this.updateProjectionMatrix(); + +} + + +/** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for each monitor you would call it like this + * + * var w = 1920; + * var h = 1080; + * var fullWidth = w * 3; + * var fullHeight = h * 2; + * + * --A-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ + +THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { + + this.fullWidth = fullWidth; + this.fullHeight = fullHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.updateProjectionMatrix(); + +}; + + +THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { + + if ( this.fullWidth ) { + + var aspect = this.fullWidth / this.fullHeight; + var top = Math.tan( this.fov * Math.PI / 360 ) * this.near; + var bottom = -top; + var left = aspect * bottom; + var right = aspect * top; + var width = Math.abs( right - left ); + var height = Math.abs( top - bottom ); + + this.projectionMatrix.makeFrustum( + left + this.x * width / this.fullWidth, + left + ( this.x + this.width ) * width / this.fullWidth, + top - ( this.y + this.height ) * height / this.fullHeight, + top - this.y * height / this.fullHeight, + this.near, + this.far + ); + + } else { + + this.projectionMatrix.makePerspective( this.fov, this.aspect, this.near, this.far ); + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Light = function ( hex ) { + + THREE.Object3D.call( this ); + + this.color = new THREE.Color( hex ); + +}; + +THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AmbientLight = function ( hex ) { + + THREE.Light.call( this, hex ); + +}; + +THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DirectionalLight = function ( hex, intensity, distance ) { + + THREE.Light.call( this, hex ); + + this.position = new THREE.Vector3( 0, 1, 0 ); + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + + this.castShadow = false; + this.onlyShadow = false; + + // + + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; + + this.shadowCameraLeft = -500; + this.shadowCameraRight = 500; + this.shadowCameraTop = 500; + this.shadowCameraBottom = -500; + + this.shadowCameraVisible = false; + + this.shadowBias = 0; + this.shadowDarkness = 0.5; + + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; + + // + + this.shadowCascade = false; + + this.shadowCascadeOffset = new THREE.Vector3( 0, 0, -1000 ); + this.shadowCascadeCount = 2; + + this.shadowCascadeBias = [ 0, 0, 0 ]; + this.shadowCascadeWidth = [ 512, 512, 512 ]; + this.shadowCascadeHeight = [ 512, 512, 512 ]; + + this.shadowCascadeNearZ = [ -1.000, 0.990, 0.998 ]; + this.shadowCascadeFarZ = [ 0.990, 0.998, 1.000 ]; + + this.shadowCascadeArray = []; + + // + + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; + +}; + +THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.HemisphereLight = function ( skyColorHex, groundColorHex, intensity ) { + + THREE.Light.call( this, skyColorHex ); + + this.groundColor = new THREE.Color( groundColorHex ); + + this.position = new THREE.Vector3( 0, 100, 0 ); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + +}; + +THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.PointLight = function ( hex, intensity, distance ) { + + THREE.Light.call( this, hex ); + + this.position = new THREE.Vector3( 0, 0, 0 ); + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + +}; + +THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpotLight = function ( hex, intensity, distance, angle, exponent ) { + + THREE.Light.call( this, hex ); + + this.position = new THREE.Vector3( 0, 1, 0 ); + this.target = new THREE.Object3D(); + + this.intensity = ( intensity !== undefined ) ? intensity : 1; + this.distance = ( distance !== undefined ) ? distance : 0; + this.angle = ( angle !== undefined ) ? angle : Math.PI / 2; + this.exponent = ( exponent !== undefined ) ? exponent : 10; + + this.castShadow = false; + this.onlyShadow = false; + + // + + this.shadowCameraNear = 50; + this.shadowCameraFar = 5000; + this.shadowCameraFov = 50; + + this.shadowCameraVisible = false; + + this.shadowBias = 0; + this.shadowDarkness = 0.5; + + this.shadowMapWidth = 512; + this.shadowMapHeight = 512; + + // + + this.shadowMap = null; + this.shadowMapSize = null; + this.shadowCamera = null; + this.shadowMatrix = null; + +}; + +THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Loader = function ( showStatus ) { + + this.showStatus = showStatus; + this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null; + + this.onLoadStart = function () {}; + this.onLoadProgress = function () {}; + this.onLoadComplete = function () {}; + +}; + +THREE.Loader.prototype = { + + constructor: THREE.Loader, + + crossOrigin: 'anonymous', + + addStatusElement: function () { + + var e = document.createElement( "div" ); + + e.style.position = "absolute"; + e.style.right = "0px"; + e.style.top = "0px"; + e.style.fontSize = "0.8em"; + e.style.textAlign = "left"; + e.style.background = "rgba(0,0,0,0.25)"; + e.style.color = "#fff"; + e.style.width = "120px"; + e.style.padding = "0.5em 0.5em 0.5em 0.5em"; + e.style.zIndex = 1000; + + e.innerHTML = "Loading ..."; + + return e; + + }, + + updateProgress: function ( progress ) { + + var message = "Loaded "; + + if ( progress.total ) { + + message += ( 100 * progress.loaded / progress.total ).toFixed(0) + "%"; + + + } else { + + message += ( progress.loaded / 1000 ).toFixed(2) + " KB"; + + } + + this.statusDomElement.innerHTML = message; + + }, + + extractUrlBase: function ( url ) { + + var parts = url.split( '/' ); + parts.pop(); + return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; + + }, + + initMaterials: function ( scope, materials, texturePath ) { + + scope.materials = []; + + for ( var i = 0; i < materials.length; ++ i ) { + + scope.materials[ i ] = THREE.Loader.prototype.createMaterial( materials[ i ], texturePath ); + + } + + }, + + hasNormals: function ( scope ) { + + var m, i, il = scope.materials.length; + + for( i = 0; i < il; i ++ ) { + + m = scope.materials[ i ]; + + if ( m instanceof THREE.ShaderMaterial ) return true; + + } + + return false; + + }, + + createMaterial: function ( m, texturePath ) { + + var _this = this; + + function is_pow2( n ) { + + var l = Math.log( n ) / Math.LN2; + return Math.floor( l ) == l; + + } + + function nearest_pow2( n ) { + + var l = Math.log( n ) / Math.LN2; + return Math.pow( 2, Math.round( l ) ); + + } + + function load_image( where, url ) { + + var image = new Image(); + + image.onload = function () { + + if ( !is_pow2( this.width ) || !is_pow2( this.height ) ) { + + var width = nearest_pow2( this.width ); + var height = nearest_pow2( this.height ); + + where.image.width = width; + where.image.height = height; + where.image.getContext( '2d' ).drawImage( this, 0, 0, width, height ); + + } else { + + where.image = this; + + } + + where.needsUpdate = true; + + }; + + image.crossOrigin = _this.crossOrigin; + image.src = url; + + } + + function create_texture( where, name, sourceFile, repeat, offset, wrap, anisotropy ) { + + var isCompressed = sourceFile.toLowerCase().endsWith( ".dds" ); + var fullPath = texturePath + "/" + sourceFile; + + if ( isCompressed ) { + + var texture = THREE.ImageUtils.loadCompressedTexture( fullPath ); + + where[ name ] = texture; + + } else { + + var texture = document.createElement( 'canvas' ); + + where[ name ] = new THREE.Texture( texture ); + + } + + where[ name ].sourceFile = sourceFile; + + if( repeat ) { + + where[ name ].repeat.set( repeat[ 0 ], repeat[ 1 ] ); + + if ( repeat[ 0 ] !== 1 ) where[ name ].wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) where[ name ].wrapT = THREE.RepeatWrapping; + + } + + if ( offset ) { + + where[ name ].offset.set( offset[ 0 ], offset[ 1 ] ); + + } + + if ( wrap ) { + + var wrapMap = { + "repeat": THREE.RepeatWrapping, + "mirror": THREE.MirroredRepeatWrapping + } + + if ( wrapMap[ wrap[ 0 ] ] !== undefined ) where[ name ].wrapS = wrapMap[ wrap[ 0 ] ]; + if ( wrapMap[ wrap[ 1 ] ] !== undefined ) where[ name ].wrapT = wrapMap[ wrap[ 1 ] ]; + + } + + if ( anisotropy ) { + + where[ name ].anisotropy = anisotropy; + + } + + if ( ! isCompressed ) { + + load_image( where[ name ], fullPath ); + + } + + } + + function rgb2hex( rgb ) { + + return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; + + } + + // defaults + + var mtype = "MeshLambertMaterial"; + var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false }; + + // parameters from model file + + if ( m.shading ) { + + var shading = m.shading.toLowerCase(); + + if ( shading === "phong" ) mtype = "MeshPhongMaterial"; + else if ( shading === "basic" ) mtype = "MeshBasicMaterial"; + + } + + if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { + + mpars.blending = THREE[ m.blending ]; + + } + + if ( m.transparent !== undefined || m.opacity < 1.0 ) { + + mpars.transparent = m.transparent; + + } + + if ( m.depthTest !== undefined ) { + + mpars.depthTest = m.depthTest; + + } + + if ( m.depthWrite !== undefined ) { + + mpars.depthWrite = m.depthWrite; + + } + + if ( m.visible !== undefined ) { + + mpars.visible = m.visible; + + } + + if ( m.flipSided !== undefined ) { + + mpars.side = THREE.BackSide; + + } + + if ( m.doubleSided !== undefined ) { + + mpars.side = THREE.DoubleSide; + + } + + if ( m.wireframe !== undefined ) { + + mpars.wireframe = m.wireframe; + + } + + if ( m.vertexColors !== undefined ) { + + if ( m.vertexColors === "face" ) { + + mpars.vertexColors = THREE.FaceColors; + + } else if ( m.vertexColors ) { + + mpars.vertexColors = THREE.VertexColors; + + } + + } + + // colors + + if ( m.colorDiffuse ) { + + mpars.color = rgb2hex( m.colorDiffuse ); + + } else if ( m.DbgColor ) { + + mpars.color = m.DbgColor; + + } + + if ( m.colorSpecular ) { + + mpars.specular = rgb2hex( m.colorSpecular ); + + } + + if ( m.colorAmbient ) { + + mpars.ambient = rgb2hex( m.colorAmbient ); + + } + + // modifiers + + if ( m.transparency ) { + + mpars.opacity = m.transparency; + + } + + if ( m.specularCoef ) { + + mpars.shininess = m.specularCoef; + + } + + // textures + + if ( m.mapDiffuse && texturePath ) { + + create_texture( mpars, "map", m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); + + } + + if ( m.mapLight && texturePath ) { + + create_texture( mpars, "lightMap", m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); + + } + + if ( m.mapBump && texturePath ) { + + create_texture( mpars, "bumpMap", m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); + + } + + if ( m.mapNormal && texturePath ) { + + create_texture( mpars, "normalMap", m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); + + } + + if ( m.mapSpecular && texturePath ) { + + create_texture( mpars, "specularMap", m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); + + } + + // + + if ( m.mapBumpScale ) { + + mpars.bumpScale = m.mapBumpScale; + + } + + // special case for normal mapped material + + if ( m.mapNormal ) { + + var shader = THREE.ShaderUtils.lib[ "normal" ]; + var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + uniforms[ "tNormal" ].value = mpars.normalMap; + + if ( m.mapNormalFactor ) { + + uniforms[ "uNormalScale" ].value.set( m.mapNormalFactor, m.mapNormalFactor ); + + } + + if ( mpars.map ) { + + uniforms[ "tDiffuse" ].value = mpars.map; + uniforms[ "enableDiffuse" ].value = true; + + } + + if ( mpars.specularMap ) { + + uniforms[ "tSpecular" ].value = mpars.specularMap; + uniforms[ "enableSpecular" ].value = true; + + } + + if ( mpars.lightMap ) { + + uniforms[ "tAO" ].value = mpars.lightMap; + uniforms[ "enableAO" ].value = true; + + } + + // for the moment don't handle displacement texture + + uniforms[ "uDiffuseColor" ].value.setHex( mpars.color ); + uniforms[ "uSpecularColor" ].value.setHex( mpars.specular ); + uniforms[ "uAmbientColor" ].value.setHex( mpars.ambient ); + + uniforms[ "uShininess" ].value = mpars.shininess; + + if ( mpars.opacity !== undefined ) { + + uniforms[ "uOpacity" ].value = mpars.opacity; + + } + + var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true }; + var material = new THREE.ShaderMaterial( parameters ); + + } else { + + var material = new THREE[ mtype ]( mpars ); + + } + + if ( m.DbgName !== undefined ) material.name = m.DbgName; + + return material; + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.BinaryLoader = function ( showStatus ) { + + THREE.Loader.call( this, showStatus ); + +}; + +THREE.BinaryLoader.prototype = Object.create( THREE.Loader.prototype ); + +// Load models generated by slim OBJ converter with BINARY option (converter_obj_three_slim.py -t binary) +// - binary models consist of two files: JS and BIN +// - parameters +// - url (required) +// - callback (required) +// - texturePath (optional: if not specified, textures will be assumed to be in the same folder as JS model file) +// - binaryPath (optional: if not specified, binary file will be assumed to be in the same folder as JS model file) + +THREE.BinaryLoader.prototype.load = function( url, callback, texturePath, binaryPath ) { + + texturePath = texturePath ? texturePath : this.extractUrlBase( url ); + binaryPath = binaryPath ? binaryPath : this.extractUrlBase( url ); + + var callbackProgress = this.showProgress ? THREE.Loader.prototype.updateProgress : null; + + this.onLoadStart(); + + // #1 load JS part via web worker + + this.loadAjaxJSON( this, url, callback, texturePath, binaryPath, callbackProgress ); + +}; + +THREE.BinaryLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, binaryPath, callbackProgress ) { + + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function () { + + if ( xhr.readyState == 4 ) { + + if ( xhr.status == 200 || xhr.status == 0 ) { + + var json = JSON.parse( xhr.responseText ); + context.loadAjaxBuffers( json, callback, binaryPath, texturePath, callbackProgress ); + + } else { + + console.error( "THREE.BinaryLoader: Couldn't load [" + url + "] [" + xhr.status + "]" ); + + } + + } + + }; + + xhr.open( "GET", url, true ); + xhr.send( null ); + +}; + +THREE.BinaryLoader.prototype.loadAjaxBuffers = function ( json, callback, binaryPath, texturePath, callbackProgress ) { + + var xhr = new XMLHttpRequest(), + url = binaryPath + "/" + json.buffers; + + var length = 0; + + xhr.onreadystatechange = function () { + + if ( xhr.readyState == 4 ) { + + if ( xhr.status == 200 || xhr.status == 0 ) { + + var buffer = xhr.response; + if ( buffer === undefined ) buffer = ( new Uint8Array( xhr.responseBody ) ).buffer; // IEWEBGL needs this + THREE.BinaryLoader.prototype.createBinModel( buffer, callback, texturePath, json.materials ); + + } else { + + console.error( "THREE.BinaryLoader: Couldn't load [" + url + "] [" + xhr.status + "]" ); + + } + + } else if ( xhr.readyState == 3 ) { + + if ( callbackProgress ) { + + if ( length == 0 ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + callbackProgress( { total: length, loaded: xhr.responseText.length } ); + + } + + } else if ( xhr.readyState == 2 ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + }; + + xhr.open( "GET", url, true ); + xhr.responseType = "arraybuffer"; + xhr.send( null ); + +}; + +// Binary AJAX parser + +THREE.BinaryLoader.prototype.createBinModel = function ( data, callback, texturePath, materials ) { + + var Model = function ( texturePath ) { + + var scope = this, + currentOffset = 0, + md, + normals = [], + uvs = [], + start_tri_flat, start_tri_smooth, start_tri_flat_uv, start_tri_smooth_uv, + start_quad_flat, start_quad_smooth, start_quad_flat_uv, start_quad_smooth_uv, + tri_size, quad_size, + len_tri_flat, len_tri_smooth, len_tri_flat_uv, len_tri_smooth_uv, + len_quad_flat, len_quad_smooth, len_quad_flat_uv, len_quad_smooth_uv; + + + THREE.Geometry.call( this ); + + THREE.Loader.prototype.initMaterials( scope, materials, texturePath ); + + md = parseMetaData( data, currentOffset ); + + currentOffset += md.header_bytes; +/* + md.vertex_index_bytes = Uint32Array.BYTES_PER_ELEMENT; + md.material_index_bytes = Uint16Array.BYTES_PER_ELEMENT; + md.normal_index_bytes = Uint32Array.BYTES_PER_ELEMENT; + md.uv_index_bytes = Uint32Array.BYTES_PER_ELEMENT; +*/ + // buffers sizes + + tri_size = md.vertex_index_bytes * 3 + md.material_index_bytes; + quad_size = md.vertex_index_bytes * 4 + md.material_index_bytes; + + len_tri_flat = md.ntri_flat * ( tri_size ); + len_tri_smooth = md.ntri_smooth * ( tri_size + md.normal_index_bytes * 3 ); + len_tri_flat_uv = md.ntri_flat_uv * ( tri_size + md.uv_index_bytes * 3 ); + len_tri_smooth_uv = md.ntri_smooth_uv * ( tri_size + md.normal_index_bytes * 3 + md.uv_index_bytes * 3 ); + + len_quad_flat = md.nquad_flat * ( quad_size ); + len_quad_smooth = md.nquad_smooth * ( quad_size + md.normal_index_bytes * 4 ); + len_quad_flat_uv = md.nquad_flat_uv * ( quad_size + md.uv_index_bytes * 4 ); + len_quad_smooth_uv = md.nquad_smooth_uv * ( quad_size + md.normal_index_bytes * 4 + md.uv_index_bytes * 4 ); + + // read buffers + + currentOffset += init_vertices( currentOffset ); + + currentOffset += init_normals( currentOffset ); + currentOffset += handlePadding( md.nnormals * 3 ); + + currentOffset += init_uvs( currentOffset ); + + start_tri_flat = currentOffset; + start_tri_smooth = start_tri_flat + len_tri_flat + handlePadding( md.ntri_flat * 2 ); + start_tri_flat_uv = start_tri_smooth + len_tri_smooth + handlePadding( md.ntri_smooth * 2 ); + start_tri_smooth_uv = start_tri_flat_uv + len_tri_flat_uv + handlePadding( md.ntri_flat_uv * 2 ); + + start_quad_flat = start_tri_smooth_uv + len_tri_smooth_uv + handlePadding( md.ntri_smooth_uv * 2 ); + start_quad_smooth = start_quad_flat + len_quad_flat + handlePadding( md.nquad_flat * 2 ); + start_quad_flat_uv = start_quad_smooth + len_quad_smooth + handlePadding( md.nquad_smooth * 2 ); + start_quad_smooth_uv= start_quad_flat_uv + len_quad_flat_uv + handlePadding( md.nquad_flat_uv * 2 ); + + // have to first process faces with uvs + // so that face and uv indices match + + init_triangles_flat_uv( start_tri_flat_uv ); + init_triangles_smooth_uv( start_tri_smooth_uv ); + + init_quads_flat_uv( start_quad_flat_uv ); + init_quads_smooth_uv( start_quad_smooth_uv ); + + // now we can process untextured faces + + init_triangles_flat( start_tri_flat ); + init_triangles_smooth( start_tri_smooth ); + + init_quads_flat( start_quad_flat ); + init_quads_smooth( start_quad_smooth ); + + this.computeCentroids(); + this.computeFaceNormals(); + + if ( THREE.Loader.prototype.hasNormals( this ) ) this.computeTangents(); + + function handlePadding( n ) { + + return ( n % 4 ) ? ( 4 - n % 4 ) : 0; + + }; + + function parseMetaData( data, offset ) { + + var metaData = { + + 'signature' :parseString( data, offset, 12 ), + 'header_bytes' :parseUChar8( data, offset + 12 ), + + 'vertex_coordinate_bytes' :parseUChar8( data, offset + 13 ), + 'normal_coordinate_bytes' :parseUChar8( data, offset + 14 ), + 'uv_coordinate_bytes' :parseUChar8( data, offset + 15 ), + + 'vertex_index_bytes' :parseUChar8( data, offset + 16 ), + 'normal_index_bytes' :parseUChar8( data, offset + 17 ), + 'uv_index_bytes' :parseUChar8( data, offset + 18 ), + 'material_index_bytes' :parseUChar8( data, offset + 19 ), + + 'nvertices' :parseUInt32( data, offset + 20 ), + 'nnormals' :parseUInt32( data, offset + 20 + 4*1 ), + 'nuvs' :parseUInt32( data, offset + 20 + 4*2 ), + + 'ntri_flat' :parseUInt32( data, offset + 20 + 4*3 ), + 'ntri_smooth' :parseUInt32( data, offset + 20 + 4*4 ), + 'ntri_flat_uv' :parseUInt32( data, offset + 20 + 4*5 ), + 'ntri_smooth_uv' :parseUInt32( data, offset + 20 + 4*6 ), + + 'nquad_flat' :parseUInt32( data, offset + 20 + 4*7 ), + 'nquad_smooth' :parseUInt32( data, offset + 20 + 4*8 ), + 'nquad_flat_uv' :parseUInt32( data, offset + 20 + 4*9 ), + 'nquad_smooth_uv' :parseUInt32( data, offset + 20 + 4*10 ) + + }; +/* + console.log( "signature: " + metaData.signature ); + + console.log( "header_bytes: " + metaData.header_bytes ); + console.log( "vertex_coordinate_bytes: " + metaData.vertex_coordinate_bytes ); + console.log( "normal_coordinate_bytes: " + metaData.normal_coordinate_bytes ); + console.log( "uv_coordinate_bytes: " + metaData.uv_coordinate_bytes ); + + console.log( "vertex_index_bytes: " + metaData.vertex_index_bytes ); + console.log( "normal_index_bytes: " + metaData.normal_index_bytes ); + console.log( "uv_index_bytes: " + metaData.uv_index_bytes ); + console.log( "material_index_bytes: " + metaData.material_index_bytes ); + + console.log( "nvertices: " + metaData.nvertices ); + console.log( "nnormals: " + metaData.nnormals ); + console.log( "nuvs: " + metaData.nuvs ); + + console.log( "ntri_flat: " + metaData.ntri_flat ); + console.log( "ntri_smooth: " + metaData.ntri_smooth ); + console.log( "ntri_flat_uv: " + metaData.ntri_flat_uv ); + console.log( "ntri_smooth_uv: " + metaData.ntri_smooth_uv ); + + console.log( "nquad_flat: " + metaData.nquad_flat ); + console.log( "nquad_smooth: " + metaData.nquad_smooth ); + console.log( "nquad_flat_uv: " + metaData.nquad_flat_uv ); + console.log( "nquad_smooth_uv: " + metaData.nquad_smooth_uv ); + + var total = metaData.header_bytes + + metaData.nvertices * metaData.vertex_coordinate_bytes * 3 + + metaData.nnormals * metaData.normal_coordinate_bytes * 3 + + metaData.nuvs * metaData.uv_coordinate_bytes * 2 + + metaData.ntri_flat * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes ) + + metaData.ntri_smooth * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 ) + + metaData.ntri_flat_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.uv_index_bytes*3 ) + + metaData.ntri_smooth_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 + metaData.uv_index_bytes*3 ) + + metaData.nquad_flat * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes ) + + metaData.nquad_smooth * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 ) + + metaData.nquad_flat_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.uv_index_bytes*4 ) + + metaData.nquad_smooth_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 + metaData.uv_index_bytes*4 ); + console.log( "total bytes: " + total ); +*/ + + return metaData; + + }; + + function parseString( data, offset, length ) { + + var charArray = new Uint8Array( data, offset, length ); + + var text = ""; + + for ( var i = 0; i < length; i ++ ) { + + text += String.fromCharCode( charArray[ offset + i ] ); + + } + + return text; + + }; + + function parseUChar8( data, offset ) { + + var charArray = new Uint8Array( data, offset, 1 ); + + return charArray[ 0 ]; + + }; + + function parseUInt32( data, offset ) { + + var intArray = new Uint32Array( data, offset, 1 ); + + return intArray[ 0 ]; + + }; + + function init_vertices( start ) { + + var nElements = md.nvertices; + + var coordArray = new Float32Array( data, start, nElements * 3 ); + + var i, x, y, z; + + for( i = 0; i < nElements; i ++ ) { + + x = coordArray[ i * 3 ]; + y = coordArray[ i * 3 + 1 ]; + z = coordArray[ i * 3 + 2 ]; + + vertex( scope, x, y, z ); + + } + + return nElements * 3 * Float32Array.BYTES_PER_ELEMENT; + + }; + + function init_normals( start ) { + + var nElements = md.nnormals; + + if ( nElements ) { + + var normalArray = new Int8Array( data, start, nElements * 3 ); + + var i, x, y, z; + + for( i = 0; i < nElements; i ++ ) { + + x = normalArray[ i * 3 ]; + y = normalArray[ i * 3 + 1 ]; + z = normalArray[ i * 3 + 2 ]; + + normals.push( x/127, y/127, z/127 ); + + } + + } + + return nElements * 3 * Int8Array.BYTES_PER_ELEMENT; + + }; + + function init_uvs( start ) { + + var nElements = md.nuvs; + + if ( nElements ) { + + var uvArray = new Float32Array( data, start, nElements * 2 ); + + var i, u, v; + + for( i = 0; i < nElements; i ++ ) { + + u = uvArray[ i * 2 ]; + v = uvArray[ i * 2 + 1 ]; + + uvs.push( u, v ); + + } + + } + + return nElements * 2 * Float32Array.BYTES_PER_ELEMENT; + + }; + + function init_uvs3( nElements, offset ) { + + var i, uva, uvb, uvc, u1, u2, u3, v1, v2, v3; + + var uvIndexBuffer = new Uint32Array( data, offset, 3 * nElements ); + + for( i = 0; i < nElements; i ++ ) { + + uva = uvIndexBuffer[ i * 3 ]; + uvb = uvIndexBuffer[ i * 3 + 1 ]; + uvc = uvIndexBuffer[ i * 3 + 2 ]; + + u1 = uvs[ uva*2 ]; + v1 = uvs[ uva*2 + 1 ]; + + u2 = uvs[ uvb*2 ]; + v2 = uvs[ uvb*2 + 1 ]; + + u3 = uvs[ uvc*2 ]; + v3 = uvs[ uvc*2 + 1 ]; + + uv3( scope.faceVertexUvs[ 0 ], u1, v1, u2, v2, u3, v3 ); + + } + + }; + + function init_uvs4( nElements, offset ) { + + var i, uva, uvb, uvc, uvd, u1, u2, u3, u4, v1, v2, v3, v4; + + var uvIndexBuffer = new Uint32Array( data, offset, 4 * nElements ); + + for( i = 0; i < nElements; i ++ ) { + + uva = uvIndexBuffer[ i * 4 ]; + uvb = uvIndexBuffer[ i * 4 + 1 ]; + uvc = uvIndexBuffer[ i * 4 + 2 ]; + uvd = uvIndexBuffer[ i * 4 + 3 ]; + + u1 = uvs[ uva*2 ]; + v1 = uvs[ uva*2 + 1 ]; + + u2 = uvs[ uvb*2 ]; + v2 = uvs[ uvb*2 + 1 ]; + + u3 = uvs[ uvc*2 ]; + v3 = uvs[ uvc*2 + 1 ]; + + u4 = uvs[ uvd*2 ]; + v4 = uvs[ uvd*2 + 1 ]; + + uv4( scope.faceVertexUvs[ 0 ], u1, v1, u2, v2, u3, v3, u4, v4 ); + + } + + }; + + function init_faces3_flat( nElements, offsetVertices, offsetMaterials ) { + + var i, a, b, c, m; + + var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 3 * nElements ); + var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); + + for( i = 0; i < nElements; i ++ ) { + + a = vertexIndexBuffer[ i * 3 ]; + b = vertexIndexBuffer[ i * 3 + 1 ]; + c = vertexIndexBuffer[ i * 3 + 2 ]; + + m = materialIndexBuffer[ i ]; + + f3( scope, a, b, c, m ); + + } + + }; + + function init_faces4_flat( nElements, offsetVertices, offsetMaterials ) { + + var i, a, b, c, d, m; + + var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 4 * nElements ); + var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); + + for( i = 0; i < nElements; i ++ ) { + + a = vertexIndexBuffer[ i * 4 ]; + b = vertexIndexBuffer[ i * 4 + 1 ]; + c = vertexIndexBuffer[ i * 4 + 2 ]; + d = vertexIndexBuffer[ i * 4 + 3 ]; + + m = materialIndexBuffer[ i ]; + + f4( scope, a, b, c, d, m ); + + } + + }; + + function init_faces3_smooth( nElements, offsetVertices, offsetNormals, offsetMaterials ) { + + var i, a, b, c, m; + var na, nb, nc; + + var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 3 * nElements ); + var normalIndexBuffer = new Uint32Array( data, offsetNormals, 3 * nElements ); + var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); + + for( i = 0; i < nElements; i ++ ) { + + a = vertexIndexBuffer[ i * 3 ]; + b = vertexIndexBuffer[ i * 3 + 1 ]; + c = vertexIndexBuffer[ i * 3 + 2 ]; + + na = normalIndexBuffer[ i * 3 ]; + nb = normalIndexBuffer[ i * 3 + 1 ]; + nc = normalIndexBuffer[ i * 3 + 2 ]; + + m = materialIndexBuffer[ i ]; + + f3n( scope, normals, a, b, c, m, na, nb, nc ); + + } + + }; + + function init_faces4_smooth( nElements, offsetVertices, offsetNormals, offsetMaterials ) { + + var i, a, b, c, d, m; + var na, nb, nc, nd; + + var vertexIndexBuffer = new Uint32Array( data, offsetVertices, 4 * nElements ); + var normalIndexBuffer = new Uint32Array( data, offsetNormals, 4 * nElements ); + var materialIndexBuffer = new Uint16Array( data, offsetMaterials, nElements ); + + for( i = 0; i < nElements; i ++ ) { + + a = vertexIndexBuffer[ i * 4 ]; + b = vertexIndexBuffer[ i * 4 + 1 ]; + c = vertexIndexBuffer[ i * 4 + 2 ]; + d = vertexIndexBuffer[ i * 4 + 3 ]; + + na = normalIndexBuffer[ i * 4 ]; + nb = normalIndexBuffer[ i * 4 + 1 ]; + nc = normalIndexBuffer[ i * 4 + 2 ]; + nd = normalIndexBuffer[ i * 4 + 3 ]; + + m = materialIndexBuffer[ i ]; + + f4n( scope, normals, a, b, c, d, m, na, nb, nc, nd ); + + } + + }; + + function init_triangles_flat( start ) { + + var nElements = md.ntri_flat; + + if ( nElements ) { + + var offsetMaterials = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + init_faces3_flat( nElements, start, offsetMaterials ); + + } + + }; + + function init_triangles_flat_uv( start ) { + + var nElements = md.ntri_flat_uv; + + if ( nElements ) { + + var offsetUvs = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + + init_faces3_flat( nElements, start, offsetMaterials ); + init_uvs3( nElements, offsetUvs ); + + } + + }; + + function init_triangles_smooth( start ) { + + var nElements = md.ntri_smooth; + + if ( nElements ) { + + var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + var offsetMaterials = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + + init_faces3_smooth( nElements, start, offsetNormals, offsetMaterials ); + + } + + }; + + function init_triangles_smooth_uv( start ) { + + var nElements = md.ntri_smooth_uv; + + if ( nElements ) { + + var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + var offsetUvs = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 3; + + init_faces3_smooth( nElements, start, offsetNormals, offsetMaterials ); + init_uvs3( nElements, offsetUvs ); + + } + + }; + + function init_quads_flat( start ) { + + var nElements = md.nquad_flat; + + if ( nElements ) { + + var offsetMaterials = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + init_faces4_flat( nElements, start, offsetMaterials ); + + } + + }; + + function init_quads_flat_uv( start ) { + + var nElements = md.nquad_flat_uv; + + if ( nElements ) { + + var offsetUvs = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + + init_faces4_flat( nElements, start, offsetMaterials ); + init_uvs4( nElements, offsetUvs ); + + } + + }; + + function init_quads_smooth( start ) { + + var nElements = md.nquad_smooth; + + if ( nElements ) { + + var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + var offsetMaterials = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + + init_faces4_smooth( nElements, start, offsetNormals, offsetMaterials ); + + } + + }; + + function init_quads_smooth_uv( start ) { + + var nElements = md.nquad_smooth_uv; + + if ( nElements ) { + + var offsetNormals = start + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + var offsetUvs = offsetNormals + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + var offsetMaterials = offsetUvs + nElements * Uint32Array.BYTES_PER_ELEMENT * 4; + + init_faces4_smooth( nElements, start, offsetNormals, offsetMaterials ); + init_uvs4( nElements, offsetUvs ); + + } + + }; + + }; + + function vertex ( scope, x, y, z ) { + + scope.vertices.push( new THREE.Vector3( x, y, z ) ); + + }; + + function f3 ( scope, a, b, c, mi ) { + + scope.faces.push( new THREE.Face3( a, b, c, null, null, mi ) ); + + }; + + function f4 ( scope, a, b, c, d, mi ) { + + scope.faces.push( new THREE.Face4( a, b, c, d, null, null, mi ) ); + + }; + + function f3n ( scope, normals, a, b, c, mi, na, nb, nc ) { + + var nax = normals[ na*3 ], + nay = normals[ na*3 + 1 ], + naz = normals[ na*3 + 2 ], + + nbx = normals[ nb*3 ], + nby = normals[ nb*3 + 1 ], + nbz = normals[ nb*3 + 2 ], + + ncx = normals[ nc*3 ], + ncy = normals[ nc*3 + 1 ], + ncz = normals[ nc*3 + 2 ]; + + scope.faces.push( new THREE.Face3( a, b, c, + [new THREE.Vector3( nax, nay, naz ), + new THREE.Vector3( nbx, nby, nbz ), + new THREE.Vector3( ncx, ncy, ncz )], + null, + mi ) ); + + }; + + function f4n ( scope, normals, a, b, c, d, mi, na, nb, nc, nd ) { + + var nax = normals[ na*3 ], + nay = normals[ na*3 + 1 ], + naz = normals[ na*3 + 2 ], + + nbx = normals[ nb*3 ], + nby = normals[ nb*3 + 1 ], + nbz = normals[ nb*3 + 2 ], + + ncx = normals[ nc*3 ], + ncy = normals[ nc*3 + 1 ], + ncz = normals[ nc*3 + 2 ], + + ndx = normals[ nd*3 ], + ndy = normals[ nd*3 + 1 ], + ndz = normals[ nd*3 + 2 ]; + + scope.faces.push( new THREE.Face4( a, b, c, d, + [new THREE.Vector3( nax, nay, naz ), + new THREE.Vector3( nbx, nby, nbz ), + new THREE.Vector3( ncx, ncy, ncz ), + new THREE.Vector3( ndx, ndy, ndz )], + null, + mi ) ); + + }; + + function uv3 ( where, u1, v1, u2, v2, u3, v3 ) { + + var uv = []; + uv.push( new THREE.UV( u1, v1 ) ); + uv.push( new THREE.UV( u2, v2 ) ); + uv.push( new THREE.UV( u3, v3 ) ); + where.push( uv ); + + }; + + function uv4 ( where, u1, v1, u2, v2, u3, v3, u4, v4 ) { + + var uv = []; + uv.push( new THREE.UV( u1, v1 ) ); + uv.push( new THREE.UV( u2, v2 ) ); + uv.push( new THREE.UV( u3, v3 ) ); + uv.push( new THREE.UV( u4, v4 ) ); + where.push( uv ); + + }; + + Model.prototype = Object.create( THREE.Geometry.prototype ); + + callback( new Model( texturePath ) ); + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ImageLoader = function () { + + THREE.EventTarget.call( this ); + + this.crossOrigin = null; + +}; + +THREE.ImageLoader.prototype = { + + constructor: THREE.ImageLoader, + + load: function ( url, image ) { + + var scope = this; + + if ( image === undefined ) image = new Image(); + + image.addEventListener( 'load', function () { + + scope.dispatchEvent( { type: 'load', content: image } ); + + }, false ); + + image.addEventListener( 'error', function () { + + scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } ); + + }, false ); + + if ( scope.crossOrigin ) image.crossOrigin = scope.crossOrigin; + + image.src = url; + + } + +} +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.JSONLoader = function ( showStatus ) { + + THREE.Loader.call( this, showStatus ); + +}; + +THREE.JSONLoader.prototype = Object.create( THREE.Loader.prototype ); + +THREE.JSONLoader.prototype.load = function ( url, callback, texturePath ) { + + var scope = this; + + texturePath = texturePath ? texturePath : this.extractUrlBase( url ); + + this.onLoadStart(); + this.loadAjaxJSON( this, url, callback, texturePath ); + +}; + +THREE.JSONLoader.prototype.loadAjaxJSON = function ( context, url, callback, texturePath, callbackProgress ) { + + var xhr = new XMLHttpRequest(); + + var length = 0; + + xhr.onreadystatechange = function () { + + if ( xhr.readyState === xhr.DONE ) { + + if ( xhr.status === 200 || xhr.status === 0 ) { + + if ( xhr.responseText ) { + + var json = JSON.parse( xhr.responseText ); + context.createModel( json, callback, texturePath ); + + } else { + + console.warn( "THREE.JSONLoader: [" + url + "] seems to be unreachable or file there is empty" ); + + } + + // in context of more complex asset initialization + // do not block on single failed file + // maybe should go even one more level up + + context.onLoadComplete(); + + } else { + + console.error( "THREE.JSONLoader: Couldn't load [" + url + "] [" + xhr.status + "]" ); + + } + + } else if ( xhr.readyState === xhr.LOADING ) { + + if ( callbackProgress ) { + + if ( length === 0 ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + callbackProgress( { total: length, loaded: xhr.responseText.length } ); + + } + + } else if ( xhr.readyState === xhr.HEADERS_RECEIVED ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + }; + + xhr.open( "GET", url, true ); + xhr.send( null ); + +}; + +THREE.JSONLoader.prototype.createModel = function ( json, callback, texturePath ) { + + var scope = this, + geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + + this.initMaterials( geometry, json.materials, texturePath ); + + parseModel( scale ); + + parseSkin(); + parseMorphing( scale ); + + geometry.computeCentroids(); + geometry.computeFaceNormals(); + + if ( this.hasNormals( geometry ) ) geometry.computeTangents(); + + + function parseModel( scale ) { + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + } + + var i, j, fi, + + offset, zLength, nVertices, + + colorIndex, normalIndex, uvIndex, materialIndex, + + type, + isQuad, + hasMaterial, + hasFaceUv, hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, + + vertex, face, color, normal, + + uvLayer, uvs, u, v, + + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, + + nUvLayers = 0; + + // disregard empty arrays + + for ( i = 0; i < json.uvs.length; i++ ) { + + if ( json.uvs[ i ].length ) nUvLayers ++; + + } + + for ( i = 0; i < nUvLayers; i++ ) { + + geometry.faceUvs[ i ] = []; + geometry.faceVertexUvs[ i ] = []; + + } + + offset = 0; + zLength = vertices.length; + + while ( offset < zLength ) { + + vertex = new THREE.Vector3(); + + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; + + geometry.vertices.push( vertex ); + + } + + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + type = faces[ offset ++ ]; + + + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceUv = isBitSet( type, 2 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); + + //console.log("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + face = new THREE.Face4(); + + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + face.d = faces[ offset ++ ]; + + nVertices = 4; + + } else { + + face = new THREE.Face3(); + + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + nVertices = 3; + + } + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceUv ) { + + for ( i = 0; i < nUvLayers; i++ ) { + + uvLayer = json.uvs[ i ]; + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + geometry.faceUvs[ i ][ fi ] = new THREE.UV( u, v ); + + } + + } + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i++ ) { + + uvLayer = json.uvs[ i ]; + + uvs = []; + + for ( j = 0; j < nVertices; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uvs[ j ] = new THREE.UV( u, v ); + + } + + geometry.faceVertexUvs[ i ][ fi ] = uvs; + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3(); + + normal.x = normals[ normalIndex ++ ]; + normal.y = normals[ normalIndex ++ ]; + normal.z = normals[ normalIndex ]; + + face.normal = normal; + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < nVertices; i++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3(); + + normal.x = normals[ normalIndex ++ ]; + normal.y = normals[ normalIndex ++ ]; + normal.z = normals[ normalIndex ]; + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + + color = new THREE.Color( colors[ colorIndex ] ); + face.color = color; + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < nVertices; i++ ) { + + colorIndex = faces[ offset ++ ]; + + color = new THREE.Color( colors[ colorIndex ] ); + face.vertexColors.push( color ); + + } + + } + + geometry.faces.push( face ); + + } + + }; + + function parseSkin() { + + var i, l, x, y, z, w, a, b, c, d; + + if ( json.skinWeights ) { + + for ( i = 0, l = json.skinWeights.length; i < l; i += 2 ) { + + x = json.skinWeights[ i ]; + y = json.skinWeights[ i + 1 ]; + z = 0; + w = 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); + + } + + } + + if ( json.skinIndices ) { + + for ( i = 0, l = json.skinIndices.length; i < l; i += 2 ) { + + a = json.skinIndices[ i ]; + b = json.skinIndices[ i + 1 ]; + c = 0; + d = 0; + + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = json.bones; + geometry.animation = json.animation; + + }; + + function parseMorphing( scale ) { + + if ( json.morphTargets !== undefined ) { + + var i, l, v, vl, dstVertices, srcVertices; + + for ( i = 0, l = json.morphTargets.length; i < l; i ++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + dstVertices = geometry.morphTargets[ i ].vertices; + srcVertices = json.morphTargets [ i ].vertices; + + for( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( vertex ); + + } + + } + + } + + if ( json.morphColors !== undefined ) { + + var i, l, c, cl, dstColors, srcColors, color; + + for ( i = 0, l = json.morphColors.length; i < l; i++ ) { + + geometry.morphColors[ i ] = {}; + geometry.morphColors[ i ].name = json.morphColors[ i ].name; + geometry.morphColors[ i ].colors = []; + + dstColors = geometry.morphColors[ i ].colors; + srcColors = json.morphColors [ i ].colors; + + for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { + + color = new THREE.Color( 0xffaa00 ); + color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); + dstColors.push( color ); + + } + + } + + } + + }; + + callback( geometry ); + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LoadingMonitor = function () { + + THREE.EventTarget.call( this ); + + var scope = this; + + var loaded = 0; + var total = 0; + + var onLoad = function ( event ) { + + loaded ++; + + scope.dispatchEvent( { type: 'progress', loaded: loaded, total: total } ); + + if ( loaded === total ) { + + scope.dispatchEvent( { type: 'load' } ); + + } + + }; + + this.add = function ( loader ) { + + total ++; + + loader.addEventListener( 'load', onLoad, false ); + + }; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.GeometryLoader = function () { + + THREE.EventTarget.call( this ); + + this.crossOrigin = null; + this.path = null; + + +}; + +THREE.GeometryLoader.prototype = { + + constructor: THREE.GeometryLoader, + + load: function ( url ) { + + var scope = this; + var geometry = null; + + if ( scope.path === null ) { + + var parts = url.split( '/' ); parts.pop(); + scope.path = ( parts.length < 1 ? '.' : parts.join( '/' ) ); + + } + + // + + var xhr = new XMLHttpRequest(); + + xhr.addEventListener( 'load', function ( event ) { + + if ( event.target.responseText ) { + + geometry = scope.parse( JSON.parse( event.target.responseText ), monitor ); + + } else { + + scope.dispatchEvent( { type: 'error', message: 'Invalid file [' + url + ']' } ); + + } + + }, false ); + + xhr.addEventListener( 'error', function () { + + scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } ); + + }, false ); + + xhr.open( 'GET', url, true ); + xhr.send( null ); + + // + + var monitor = new THREE.LoadingMonitor(); + + monitor.addEventListener( 'load', function ( event ) { + + scope.dispatchEvent( { type: 'load', content: geometry } ); + + } ); + + monitor.add( xhr ); + + }, + + parse: function ( data, monitor ) { + + var scope = this; + var geometry = new THREE.Geometry(); + + var scale = ( data.scale !== undefined ) ? 1 / data.scale : 1; + + // materials + + if ( data.materials ) { + + geometry.materials = []; + + for ( var i = 0; i < data.materials.length; ++ i ) { + + var m = data.materials[ i ]; + + function isPow2( n ) { + + var l = Math.log( n ) / Math.LN2; + return Math.floor( l ) == l; + + } + + function nearestPow2( n ) { + + var l = Math.log( n ) / Math.LN2; + return Math.pow( 2, Math.round( l ) ); + + } + + function createTexture( where, name, sourceFile, repeat, offset, wrap ) { + + where[ name ] = new THREE.Texture(); + where[ name ].sourceFile = sourceFile; + + if ( repeat ) { + + where[ name ].repeat.set( repeat[ 0 ], repeat[ 1 ] ); + + if ( repeat[ 0 ] !== 1 ) where[ name ].wrapS = THREE.RepeatWrapping; + if ( repeat[ 1 ] !== 1 ) where[ name ].wrapT = THREE.RepeatWrapping; + + } + + if ( offset ) { + + where[ name ].offset.set( offset[ 0 ], offset[ 1 ] ); + + } + + if ( wrap ) { + + var wrapMap = { + + "repeat": THREE.RepeatWrapping, + "mirror": THREE.MirroredRepeatWrapping + + } + + if ( wrapMap[ wrap[ 0 ] ] !== undefined ) where[ name ].wrapS = wrapMap[ wrap[ 0 ] ]; + if ( wrapMap[ wrap[ 1 ] ] !== undefined ) where[ name ].wrapT = wrapMap[ wrap[ 1 ] ]; + + } + + // load image + + var texture = where[ name ]; + + var loader = new THREE.ImageLoader(); + loader.addEventListener( 'load', function ( event ) { + + var image = event.content; + + if ( !isPow2( image.width ) || !isPow2( image.height ) ) { + + var width = nearestPow2( image.width ); + var height = nearestPow2( image.height ); + + texture.image = document.createElement( 'canvas' ); + texture.image.width = width; + texture.image.height = height; + texture.image.getContext( '2d' ).drawImage( image, 0, 0, width, height ); + + } else { + + texture.image = image; + + } + + texture.needsUpdate = true; + + } ); + loader.crossOrigin = scope.crossOrigin; + loader.load( scope.path + '/' + sourceFile ); + + if ( monitor ) monitor.add( loader ); + + } + + function rgb2hex( rgb ) { + + return ( rgb[ 0 ] * 255 << 16 ) + ( rgb[ 1 ] * 255 << 8 ) + rgb[ 2 ] * 255; + + } + + // defaults + + var mtype = "MeshLambertMaterial"; + var mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, normalMap: null, bumpMap: null, wireframe: false }; + + // parameters from model file + + if ( m.shading ) { + + var shading = m.shading.toLowerCase(); + + if ( shading === "phong" ) mtype = "MeshPhongMaterial"; + else if ( shading === "basic" ) mtype = "MeshBasicMaterial"; + + } + + if ( m.blending !== undefined && THREE[ m.blending ] !== undefined ) { + + mpars.blending = THREE[ m.blending ]; + + } + + if ( m.transparent !== undefined || m.opacity < 1.0 ) { + + mpars.transparent = m.transparent; + + } + + if ( m.depthTest !== undefined ) { + + mpars.depthTest = m.depthTest; + + } + + if ( m.depthWrite !== undefined ) { + + mpars.depthWrite = m.depthWrite; + + } + + if ( m.vertexColors !== undefined ) { + + if ( m.vertexColors == "face" ) { + + mpars.vertexColors = THREE.FaceColors; + + } else if ( m.vertexColors ) { + + mpars.vertexColors = THREE.VertexColors; + + } + + } + + // colors + + if ( m.colorDiffuse ) { + + mpars.color = rgb2hex( m.colorDiffuse ); + + } else if ( m.DbgColor ) { + + mpars.color = m.DbgColor; + + } + + if ( m.colorSpecular ) { + + mpars.specular = rgb2hex( m.colorSpecular ); + + } + + if ( m.colorAmbient ) { + + mpars.ambient = rgb2hex( m.colorAmbient ); + + } + + // modifiers + + if ( m.transparency ) { + + mpars.opacity = m.transparency; + + } + + if ( m.specularCoef ) { + + mpars.shininess = m.specularCoef; + + } + + if ( m.visible !== undefined ) { + + mpars.visible = m.visible; + + } + + if ( m.flipSided !== undefined ) { + + mpars.side = THREE.BackSide; + + } + + if ( m.doubleSided !== undefined ) { + + mpars.side = THREE.DoubleSide; + + } + + if ( m.wireframe !== undefined ) { + + mpars.wireframe = m.wireframe; + + } + + // textures + + if ( m.mapDiffuse ) { + + createTexture( mpars, "map", m.mapDiffuse, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap ); + + } + + if ( m.mapLight ) { + + createTexture( mpars, "lightMap", m.mapLight, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap ); + + } + + if ( m.mapBump ) { + + createTexture( mpars, "bumpMap", m.mapBump, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap ); + + } + + if ( m.mapNormal ) { + + createTexture( mpars, "normalMap", m.mapNormal, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap ); + + } + + if ( m.mapSpecular ) { + + createTexture( mpars, "specularMap", m.mapSpecular, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap ); + + } + + // special case for normal mapped material + + if ( m.mapNormal ) { + + var shader = THREE.ShaderUtils.lib[ "normal" ]; + var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + uniforms[ "tNormal" ].value = mpars.normalMap; + + if ( m.mapNormalFactor ) { + + uniforms[ "uNormalScale" ].value.set( m.mapNormalFactor, m.mapNormalFactor ); + + } + + if ( mpars.map ) { + + uniforms[ "tDiffuse" ].value = mpars.map; + uniforms[ "enableDiffuse" ].value = true; + + } + + if ( mpars.specularMap ) { + + uniforms[ "tSpecular" ].value = mpars.specularMap; + uniforms[ "enableSpecular" ].value = true; + + } + + if ( mpars.lightMap ) { + + uniforms[ "tAO" ].value = mpars.lightMap; + uniforms[ "enableAO" ].value = true; + + } + + // for the moment don't handle displacement texture + + uniforms[ "uDiffuseColor" ].value.setHex( mpars.color ); + uniforms[ "uSpecularColor" ].value.setHex( mpars.specular ); + uniforms[ "uAmbientColor" ].value.setHex( mpars.ambient ); + + uniforms[ "uShininess" ].value = mpars.shininess; + + if ( mpars.opacity !== undefined ) { + + uniforms[ "uOpacity" ].value = mpars.opacity; + + } + + var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true }; + var material = new THREE.ShaderMaterial( parameters ); + + } else { + + var material = new THREE[ mtype ]( mpars ); + + } + + if ( m.DbgName !== undefined ) material.name = m.DbgName; + + geometry.materials[ i ] = material; + + } + + } + + // geometry + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + } + + var faces = data.faces; + var vertices = data.vertices; + var normals = data.normals; + var colors = data.colors; + var nUvLayers = 0; + + // disregard empty arrays + + if ( data.uvs ) { + + for ( var i = 0; i < data.uvs.length; i ++ ) { + + if ( data.uvs[ i ].length ) nUvLayers ++; + + } + + } + + for ( var i = 0; i < nUvLayers; i ++ ) { + + geometry.faceUvs[ i ] = []; + geometry.faceVertexUvs[ i ] = []; + + } + + var offset = 0; + var zLength = vertices.length; + + while ( offset < zLength ) { + + var vertex = new THREE.Vector3(); + + vertex.x = vertices[ offset ++ ] * scale; + vertex.y = vertices[ offset ++ ] * scale; + vertex.z = vertices[ offset ++ ] * scale; + + geometry.vertices.push( vertex ); + + } + + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + var type = faces[ offset ++ ]; + + var isQuad = isBitSet( type, 0 ); + + var hasMaterial = isBitSet( type, 1 ); + var hasFaceUv = isBitSet( type, 2 ); + var hasFaceVertexUv = isBitSet( type, 3 ); + var hasFaceNormal = isBitSet( type, 4 ); + var hasFaceVertexNormal = isBitSet( type, 5 ); + var hasFaceColor = isBitSet( type, 6 ); + var hasFaceVertexColor = isBitSet( type, 7 ); + + // console.log("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + var face = new THREE.Face4(); + + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + face.d = faces[ offset ++ ]; + + var nVertices = 4; + + } else { + + var face = new THREE.Face3(); + + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + var nVertices = 3; + + } + + if ( hasMaterial ) { + + var materialIndex = faces[ offset ++ ]; + face.materialIndex = materialIndex; + + } + + // to get face <=> uv index correspondence + + var fi = geometry.faces.length; + + if ( hasFaceUv ) { + + for ( var i = 0; i < nUvLayers; i ++ ) { + + var uvLayer = data.uvs[ i ]; + + var uvIndex = faces[ offset ++ ]; + + var u = uvLayer[ uvIndex * 2 ]; + var v = uvLayer[ uvIndex * 2 + 1 ]; + + geometry.faceUvs[ i ][ fi ] = new THREE.UV( u, v ); + + } + + } + + if ( hasFaceVertexUv ) { + + for ( var i = 0; i < nUvLayers; i ++ ) { + + var uvLayer = data.uvs[ i ]; + + var uvs = []; + + for ( var j = 0; j < nVertices; j ++ ) { + + var uvIndex = faces[ offset ++ ]; + + var u = uvLayer[ uvIndex * 2 ]; + var v = uvLayer[ uvIndex * 2 + 1 ]; + + uvs[ j ] = new THREE.UV( u, v ); + + } + + geometry.faceVertexUvs[ i ][ fi ] = uvs; + + } + + } + + if ( hasFaceNormal ) { + + var normalIndex = faces[ offset ++ ] * 3; + + var normal = new THREE.Vector3(); + + normal.x = normals[ normalIndex ++ ]; + normal.y = normals[ normalIndex ++ ]; + normal.z = normals[ normalIndex ]; + + face.normal = normal; + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < nVertices; i ++ ) { + + var normalIndex = faces[ offset ++ ] * 3; + + var normal = new THREE.Vector3(); + + normal.x = normals[ normalIndex ++ ]; + normal.y = normals[ normalIndex ++ ]; + normal.z = normals[ normalIndex ]; + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + var colorIndex = faces[ offset ++ ]; + + face.color = new THREE.Color( colors[ colorIndex ] ); + + } + + + if ( hasFaceVertexColor ) { + + for ( var i = 0; i < nVertices; i ++ ) { + + var colorIndex = faces[ offset ++ ]; + + face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); + + } + + } + + geometry.faces.push( face ); + + } + + + // skin + + if ( data.skinWeights ) { + + for ( var i = 0, l = data.skinWeights.length; i < l; i += 2 ) { + + var x = data.skinWeights[ i ]; + var y = data.skinWeights[ i + 1 ]; + var z = 0; + var w = 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); + + } + + } + + if ( data.skinIndices ) { + + for ( var i = 0, l = data.skinIndices.length; i < l; i += 2 ) { + + var a = data.skinIndices[ i ]; + var b = data.skinIndices[ i + 1 ]; + var c = 0; + var d = 0; + + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = data.bones; + geometry.animation = data.animation; + + + // morphing + + if ( data.morphTargets ) { + + for ( var i = 0, l = data.morphTargets.length; i < l; i ++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = data.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + var dstVertices = geometry.morphTargets[ i ].vertices; + var srcVertices = data.morphTargets [ i ].vertices; + + for( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + var vertex = new THREE.Vector3(); + vertex.x = srcVertices[ v ] * scale; + vertex.y = srcVertices[ v + 1 ] * scale; + vertex.z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( vertex ); + + } + + } + + } + + if ( data.morphColors ) { + + for ( var i = 0, l = data.morphColors.length; i < l; i++ ) { + + geometry.morphColors[ i ] = {}; + geometry.morphColors[ i ].name = data.morphColors[ i ].name; + geometry.morphColors[ i ].colors = []; + + var dstColors = geometry.morphColors[ i ].colors; + var srcColors = data.morphColors [ i ].colors; + + for ( var c = 0, cl = srcColors.length; c < cl; c += 3 ) { + + var color = new THREE.Color( 0xffaa00 ); + color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); + + dstColors.push( color ); + + } + + } + + } + + geometry.computeCentroids(); + geometry.computeFaceNormals(); + + return geometry; + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneLoader = function () { + + this.onLoadStart = function () {}; + this.onLoadProgress = function() {}; + this.onLoadComplete = function () {}; + + this.callbackSync = function () {}; + this.callbackProgress = function () {}; + + this.geometryHandlerMap = {}; + + this.addGeometryHandler( "ascii", THREE.JSONLoader ); + this.addGeometryHandler( "binary", THREE.BinaryLoader ); + +}; + +THREE.SceneLoader.prototype.constructor = THREE.SceneLoader; + +THREE.SceneLoader.prototype.load = function ( url, callbackFinished ) { + + var scope = this; + + var xhr = new XMLHttpRequest(); + + xhr.onreadystatechange = function () { + + if ( xhr.readyState === 4 ) { + + if ( xhr.status === 200 || xhr.status === 0 ) { + + var json = JSON.parse( xhr.responseText ); + scope.parse( json, callbackFinished, url ); + + } else { + + console.error( "THREE.SceneLoader: Couldn't load [" + url + "] [" + xhr.status + "]" ); + + } + + } + + }; + + xhr.open( "GET", url, true ); + xhr.send( null ); + +}; + +THREE.SceneLoader.prototype.addGeometryHandler = function ( typeID, loaderClass ) { + + this.geometryHandlerMap[ typeID ] = { "loaderClass": loaderClass }; + +}; + +THREE.SceneLoader.prototype.parse = function ( json, callbackFinished, url ) { + + var scope = this; + + var urlBase = THREE.Loader.prototype.extractUrlBase( url ); + + var dg, dm, dl, dc, df, dt, + g, m, l, d, p, r, q, s, c, t, f, tt, pp, u, + geometry, material, camera, fog, + texture, images, + light, + counter_models, counter_textures, + total_models, total_textures, + result; + + var data = json; + + // async geometry loaders + + for ( var typeID in this.geometryHandlerMap ) { + + var loaderClass = this.geometryHandlerMap[ typeID ][ "loaderClass" ]; + this.geometryHandlerMap[ typeID ][ "loaderObject" ] = new loaderClass(); + + } + + counter_models = 0; + counter_textures = 0; + + result = { + + scene: new THREE.Scene(), + geometries: {}, + materials: {}, + textures: {}, + objects: {}, + cameras: {}, + lights: {}, + fogs: {}, + empties: {} + + }; + + if ( data.transform ) { + + var position = data.transform.position, + rotation = data.transform.rotation, + scale = data.transform.scale; + + if ( position ) + result.scene.position.set( position[ 0 ], position[ 1 ], position [ 2 ] ); + + if ( rotation ) + result.scene.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation [ 2 ] ); + + if ( scale ) + result.scene.scale.set( scale[ 0 ], scale[ 1 ], scale [ 2 ] ); + + if ( position || rotation || scale ) { + + result.scene.updateMatrix(); + result.scene.updateMatrixWorld(); + + } + + } + + function get_url( source_url, url_type ) { + + if ( url_type == "relativeToHTML" ) { + + return source_url; + + } else { + + return urlBase + "/" + source_url; + + } + + }; + + // toplevel loader function, delegates to handle_children + + function handle_objects() { + + handle_children( result.scene, data.objects ); + + } + + // handle all the children from the loaded json and attach them to given parent + + function handle_children( parent, children ) { + + for ( var dd in children ) { + + // check by id if child has already been handled, + // if not, create new object + + if ( result.objects[ dd ] === undefined ) { + + var o = children[ dd ]; + + var object = null; + + if ( o.geometry !== undefined ) { + + geometry = result.geometries[ o.geometry ]; + + // geometry already loaded + + if ( geometry ) { + + var hasNormals = false; + + // not anymore support for multiple materials + // shouldn't really be array + + material = result.materials[ o.materials[ 0 ] ]; + hasNormals = material instanceof THREE.ShaderMaterial; + + if ( hasNormals ) { + + geometry.computeTangents(); + + } + + p = o.position; + r = o.rotation; + q = o.quaternion; + s = o.scale; + m = o.matrix; + + // turn off quaternions, for the moment + + q = 0; + + if ( o.materials.length === 0 ) { + + material = new THREE.MeshFaceMaterial(); + + } + + // dirty hack to handle meshes with multiple materials + // just use face materials defined in model + + if ( o.materials.length > 1 ) { + + material = new THREE.MeshFaceMaterial(); + + } + + if ( o.morph ) { + + object = new THREE.MorphAnimMesh( geometry, material ); + + if ( o.duration !== undefined ) { + + object.duration = o.duration; + + } + + if ( o.time !== undefined ) { + + object.time = o.time; + + } + + if ( o.mirroredLoop !== undefined ) { + + object.mirroredLoop = o.mirroredLoop; + + } + + if ( material.morphNormals ) { + + geometry.computeMorphNormals(); + + } + + } else { + + object = new THREE.Mesh( geometry, material ); + + } + + object.name = dd; + + if ( m ) { + + object.matrixAutoUpdate = false; + object.matrix.set( + m[0], m[1], m[2], m[3], + m[4], m[5], m[6], m[7], + m[8], m[9], m[10], m[11], + m[12], m[13], m[14], m[15] + ); + + } else { + + object.position.set( p[0], p[1], p[2] ); + + if ( q ) { + + object.quaternion.set( q[0], q[1], q[2], q[3] ); + object.useQuaternion = true; + + } else { + + object.rotation.set( r[0], r[1], r[2] ); + + } + + object.scale.set( s[0], s[1], s[2] ); + + } + + object.visible = o.visible; + object.castShadow = o.castShadow; + object.receiveShadow = o.receiveShadow; + + parent.add( object ); + + result.objects[ dd ] = object; + + } + + // pure Object3D + + } else { + + p = o.position; + r = o.rotation; + q = o.quaternion; + s = o.scale; + + // turn off quaternions, for the moment + + q = 0; + + object = new THREE.Object3D(); + object.name = dd; + object.position.set( p[0], p[1], p[2] ); + + if ( q ) { + + object.quaternion.set( q[0], q[1], q[2], q[3] ); + object.useQuaternion = true; + + } else { + + object.rotation.set( r[0], r[1], r[2] ); + + } + + object.scale.set( s[0], s[1], s[2] ); + object.visible = ( o.visible !== undefined ) ? o.visible : false; + + parent.add( object ); + + result.objects[ dd ] = object; + result.empties[ dd ] = object; + + } + + if ( object ) { + + if ( o.properties !== undefined ) { + + for ( var key in o.properties ) { + + var value = o.properties[ key ]; + object.properties[ key ] = value; + + } + + } + + if ( o.children !== undefined ) { + + handle_children( object, o.children ); + + } + + } + + } + + } + + }; + + function handle_mesh( geo, id ) { + + result.geometries[ id ] = geo; + handle_objects(); + + }; + + function create_callback( id ) { + + return function( geo ) { + + handle_mesh( geo, id ); + + counter_models -= 1; + + scope.onLoadComplete(); + + async_callback_gate(); + + } + + }; + + function create_callback_embed( id ) { + + return function( geo ) { + + result.geometries[ id ] = geo; + + } + + }; + + function async_callback_gate() { + + var progress = { + + totalModels : total_models, + totalTextures : total_textures, + loadedModels : total_models - counter_models, + loadedTextures : total_textures - counter_textures + + }; + + scope.callbackProgress( progress, result ); + + scope.onLoadProgress(); + + if ( counter_models === 0 && counter_textures === 0 ) { + + callbackFinished( result ); + + } + + }; + + var callbackTexture = function ( count ) { + + counter_textures -= count; + async_callback_gate(); + + scope.onLoadComplete(); + + }; + + // must use this instead of just directly calling callbackTexture + // because of closure in the calling context loop + + var generateTextureCallback = function ( count ) { + + return function() { + + callbackTexture( count ); + + }; + + }; + + // first go synchronous elements + + // cameras + + for( dc in data.cameras ) { + + c = data.cameras[ dc ]; + + if ( c.type === "perspective" ) { + + camera = new THREE.PerspectiveCamera( c.fov, c.aspect, c.near, c.far ); + + } else if ( c.type === "ortho" ) { + + camera = new THREE.OrthographicCamera( c.left, c.right, c.top, c.bottom, c.near, c.far ); + + } + + p = c.position; + t = c.target; + u = c.up; + + camera.position.set( p[0], p[1], p[2] ); + camera.target = new THREE.Vector3( t[0], t[1], t[2] ); + if ( u ) camera.up.set( u[0], u[1], u[2] ); + + result.cameras[ dc ] = camera; + + } + + // lights + + var hex, intensity; + + for ( dl in data.lights ) { + + l = data.lights[ dl ]; + + hex = ( l.color !== undefined ) ? l.color : 0xffffff; + intensity = ( l.intensity !== undefined ) ? l.intensity : 1; + + if ( l.type === "directional" ) { + + p = l.direction; + + light = new THREE.DirectionalLight( hex, intensity ); + light.position.set( p[0], p[1], p[2] ); + light.position.normalize(); + + } else if ( l.type === "point" ) { + + p = l.position; + d = l.distance; + + light = new THREE.PointLight( hex, intensity, d ); + light.position.set( p[0], p[1], p[2] ); + + } else if ( l.type === "ambient" ) { + + light = new THREE.AmbientLight( hex ); + + } + + result.scene.add( light ); + + result.lights[ dl ] = light; + + } + + // fogs + + for( df in data.fogs ) { + + f = data.fogs[ df ]; + + if ( f.type === "linear" ) { + + fog = new THREE.Fog( 0x000000, f.near, f.far ); + + } else if ( f.type === "exp2" ) { + + fog = new THREE.FogExp2( 0x000000, f.density ); + + } + + c = f.color; + fog.color.setRGB( c[0], c[1], c[2] ); + + result.fogs[ df ] = fog; + + } + + // defaults + + if ( result.cameras && data.defaults.camera ) { + + result.currentCamera = result.cameras[ data.defaults.camera ]; + + } + + if ( result.fogs && data.defaults.fog ) { + + result.scene.fog = result.fogs[ data.defaults.fog ]; + + } + + c = data.defaults.bgcolor; + result.bgColor = new THREE.Color(); + result.bgColor.setRGB( c[0], c[1], c[2] ); + + result.bgColorAlpha = data.defaults.bgalpha; + + // now come potentially asynchronous elements + + // geometries + + // count how many models will be loaded asynchronously + + for( dg in data.geometries ) { + + g = data.geometries[ dg ]; + + if ( g.type in this.geometryHandlerMap ) { + + counter_models += 1; + + scope.onLoadStart(); + + } + + } + + total_models = counter_models; + + for ( dg in data.geometries ) { + + g = data.geometries[ dg ]; + + if ( g.type === "cube" ) { + + geometry = new THREE.CubeGeometry( g.width, g.height, g.depth, g.segmentsWidth, g.segmentsHeight, g.segmentsDepth, null, g.flipped, g.sides ); + result.geometries[ dg ] = geometry; + + } else if ( g.type === "plane" ) { + + geometry = new THREE.PlaneGeometry( g.width, g.height, g.segmentsWidth, g.segmentsHeight ); + result.geometries[ dg ] = geometry; + + } else if ( g.type === "sphere" ) { + + geometry = new THREE.SphereGeometry( g.radius, g.segmentsWidth, g.segmentsHeight ); + result.geometries[ dg ] = geometry; + + } else if ( g.type === "cylinder" ) { + + geometry = new THREE.CylinderGeometry( g.topRad, g.botRad, g.height, g.radSegs, g.heightSegs ); + result.geometries[ dg ] = geometry; + + } else if ( g.type === "torus" ) { + + geometry = new THREE.TorusGeometry( g.radius, g.tube, g.segmentsR, g.segmentsT ); + result.geometries[ dg ] = geometry; + + } else if ( g.type === "icosahedron" ) { + + geometry = new THREE.IcosahedronGeometry( g.radius, g.subdivisions ); + result.geometries[ dg ] = geometry; + + } else if ( g.type in this.geometryHandlerMap ) { + + var loaderParameters = {}; + for ( var parType in g ) { + + if ( parType !== "type" && parType !== "url" ) { + + loaderParameters[ parType ] = g[ parType ]; + + } + + } + + var loader = this.geometryHandlerMap[ g.type ][ "loaderObject" ]; + loader.load( get_url( g.url, data.urlBaseType ), create_callback( dg ), loaderParameters ); + + } else if ( g.type === "embedded" ) { + + var modelJson = data.embeds[ g.id ], + texture_path = ""; + + // pass metadata along to jsonLoader so it knows the format version + + modelJson.metadata = data.metadata; + + if ( modelJson ) { + + var jsonLoader = this.geometryHandlerMap[ "ascii" ][ "loaderObject" ]; + jsonLoader.createModel( modelJson, create_callback_embed( dg ), texture_path ); + + } + + } + + } + + // textures + + // count how many textures will be loaded asynchronously + + for( dt in data.textures ) { + + tt = data.textures[ dt ]; + + if( tt.url instanceof Array ) { + + counter_textures += tt.url.length; + + for( var n = 0; n < tt.url.length; n ++ ) { + + scope.onLoadStart(); + + } + + } else { + + counter_textures += 1; + + scope.onLoadStart(); + + } + + } + + total_textures = counter_textures; + + for ( dt in data.textures ) { + + tt = data.textures[ dt ]; + + if ( tt.mapping !== undefined && THREE[ tt.mapping ] !== undefined ) { + + tt.mapping = new THREE[ tt.mapping ](); + + } + + if ( tt.url instanceof Array ) { + + var count = tt.url.length; + var url_array = []; + + for( var i = 0; i < count; i ++ ) { + + url_array[ i ] = get_url( tt.url[ i ], data.urlBaseType ); + + } + + var isCompressed = url_array[ 0 ].endsWith( ".dds" ); + + if ( isCompressed ) { + + texture = THREE.ImageUtils.loadCompressedTextureCube( url_array, tt.mapping, generateTextureCallback( count ) ); + + } else { + + texture = THREE.ImageUtils.loadTextureCube( url_array, tt.mapping, generateTextureCallback( count ) ); + + } + + } else { + + var isCompressed = tt.url.toLowerCase().endsWith( ".dds" ); + var fullUrl = get_url( tt.url, data.urlBaseType ); + var textureCallback = generateTextureCallback( 1 ); + + if ( isCompressed ) { + + texture = THREE.ImageUtils.loadCompressedTexture( fullUrl, tt.mapping, textureCallback ); + + } else { + + texture = THREE.ImageUtils.loadTexture( fullUrl, tt.mapping, textureCallback ); + + } + + if ( THREE[ tt.minFilter ] !== undefined ) + texture.minFilter = THREE[ tt.minFilter ]; + + if ( THREE[ tt.magFilter ] !== undefined ) + texture.magFilter = THREE[ tt.magFilter ]; + + if ( tt.anisotropy ) texture.anisotropy = tt.anisotropy; + + if ( tt.repeat ) { + + texture.repeat.set( tt.repeat[ 0 ], tt.repeat[ 1 ] ); + + if ( tt.repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; + if ( tt.repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; + + } + + if ( tt.offset ) { + + texture.offset.set( tt.offset[ 0 ], tt.offset[ 1 ] ); + + } + + // handle wrap after repeat so that default repeat can be overriden + + if ( tt.wrap ) { + + var wrapMap = { + "repeat" : THREE.RepeatWrapping, + "mirror" : THREE.MirroredRepeatWrapping + } + + if ( wrapMap[ tt.wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ tt.wrap[ 0 ] ]; + if ( wrapMap[ tt.wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ tt.wrap[ 1 ] ]; + + } + + } + + result.textures[ dt ] = texture; + + } + + // materials + + for ( dm in data.materials ) { + + m = data.materials[ dm ]; + + for ( pp in m.parameters ) { + + if ( pp === "envMap" || pp === "map" || pp === "lightMap" || pp === "bumpMap" ) { + + m.parameters[ pp ] = result.textures[ m.parameters[ pp ] ]; + + } else if ( pp === "shading" ) { + + m.parameters[ pp ] = ( m.parameters[ pp ] === "flat" ) ? THREE.FlatShading : THREE.SmoothShading; + + } else if ( pp === "side" ) { + + if ( m.parameters[ pp ] == "double" ) { + + m.parameters[ pp ] = THREE.DoubleSide; + + } else if ( m.parameters[ pp ] == "back" ) { + + m.parameters[ pp ] = THREE.BackSide; + + } else { + + m.parameters[ pp ] = THREE.FrontSide; + + } + + } else if ( pp === "blending" ) { + + m.parameters[ pp ] = m.parameters[ pp ] in THREE ? THREE[ m.parameters[ pp ] ] : THREE.NormalBlending; + + } else if ( pp === "combine" ) { + + m.parameters[ pp ] = ( m.parameters[ pp ] == "MixOperation" ) ? THREE.MixOperation : THREE.MultiplyOperation; + + } else if ( pp === "vertexColors" ) { + + if ( m.parameters[ pp ] == "face" ) { + + m.parameters[ pp ] = THREE.FaceColors; + + // default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false + + } else if ( m.parameters[ pp ] ) { + + m.parameters[ pp ] = THREE.VertexColors; + + } + + } else if ( pp === "wrapRGB" ) { + + var v3 = m.parameters[ pp ]; + m.parameters[ pp ] = new THREE.Vector3( v3[ 0 ], v3[ 1 ], v3[ 2 ] ); + + } + + } + + if ( m.parameters.opacity !== undefined && m.parameters.opacity < 1.0 ) { + + m.parameters.transparent = true; + + } + + if ( m.parameters.normalMap ) { + + var shader = THREE.ShaderUtils.lib[ "normal" ]; + var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); + + var diffuse = m.parameters.color; + var specular = m.parameters.specular; + var ambient = m.parameters.ambient; + var shininess = m.parameters.shininess; + + uniforms[ "tNormal" ].value = result.textures[ m.parameters.normalMap ]; + + if ( m.parameters.normalScale ) { + + uniforms[ "uNormalScale" ].value.set( m.parameters.normalScale[ 0 ], m.parameters.normalScale[ 1 ] ); + + } + + if ( m.parameters.map ) { + + uniforms[ "tDiffuse" ].value = m.parameters.map; + uniforms[ "enableDiffuse" ].value = true; + + } + + if ( m.parameters.envMap ) { + + uniforms[ "tCube" ].value = m.parameters.envMap; + uniforms[ "enableReflection" ].value = true; + uniforms[ "uReflectivity" ].value = m.parameters.reflectivity; + + } + + if ( m.parameters.lightMap ) { + + uniforms[ "tAO" ].value = m.parameters.lightMap; + uniforms[ "enableAO" ].value = true; + + } + + if ( m.parameters.specularMap ) { + + uniforms[ "tSpecular" ].value = result.textures[ m.parameters.specularMap ]; + uniforms[ "enableSpecular" ].value = true; + + } + + if ( m.parameters.displacementMap ) { + + uniforms[ "tDisplacement" ].value = result.textures[ m.parameters.displacementMap ]; + uniforms[ "enableDisplacement" ].value = true; + + uniforms[ "uDisplacementBias" ].value = m.parameters.displacementBias; + uniforms[ "uDisplacementScale" ].value = m.parameters.displacementScale; + + } + + uniforms[ "uDiffuseColor" ].value.setHex( diffuse ); + uniforms[ "uSpecularColor" ].value.setHex( specular ); + uniforms[ "uAmbientColor" ].value.setHex( ambient ); + + uniforms[ "uShininess" ].value = shininess; + + if ( m.parameters.opacity ) { + + uniforms[ "uOpacity" ].value = m.parameters.opacity; + + } + + var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true }; + + material = new THREE.ShaderMaterial( parameters ); + + } else { + + material = new THREE[ m.type ]( m.parameters ); + + } + + result.materials[ dm ] = material; + + } + + // objects ( synchronous init of procedural primitives ) + + handle_objects(); + + // synchronous callback + + scope.callbackSync( result ); + + // just in case there are no async elements + + async_callback_gate(); + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.TextureLoader = function () { + + THREE.EventTarget.call( this ); + + this.crossOrigin = null; + +}; + +THREE.TextureLoader.prototype = { + + constructor: THREE.TextureLoader, + + load: function ( url ) { + + var scope = this; + + var image = new Image(); + + image.addEventListener( 'load', function () { + + var texture = new THREE.Texture( image ); + texture.needsUpdate = true; + + scope.dispatchEvent( { type: 'load', content: texture } ); + + }, false ); + + image.addEventListener( 'error', function () { + + scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } ); + + }, false ); + + if ( scope.crossOrigin ) image.crossOrigin = scope.crossOrigin; + + image.src = url; + + } + +} +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Material = function () { + + THREE.MaterialLibrary.push( this ); + + this.id = THREE.MaterialIdCount ++; + + this.name = ''; + + this.side = THREE.FrontSide; + + this.opacity = 1; + this.transparent = false; + + this.blending = THREE.NormalBlending; + + this.blendSrc = THREE.SrcAlphaFactor; + this.blendDst = THREE.OneMinusSrcAlphaFactor; + this.blendEquation = THREE.AddEquation; + + this.depthTest = true; + this.depthWrite = true; + + this.polygonOffset = false; + this.polygonOffsetFactor = 0; + this.polygonOffsetUnits = 0; + + this.alphaTest = 0; + + this.overdraw = false; // Boolean for fixing antialiasing gaps in CanvasRenderer + + this.visible = true; + + this.needsUpdate = true; + +}; + +THREE.Material.prototype.setValues = function ( values ) { + + if ( values === undefined ) return; + + for ( var key in values ) { + + var newValue = values[ key ]; + + if ( newValue === undefined ) { + + console.warn( 'THREE.Material: \'' + key + '\' parameter is undefined.' ); + continue; + + } + + if ( key in this ) { + + var currentValue = this[ key ]; + + if ( currentValue instanceof THREE.Color && newValue instanceof THREE.Color ) { + + currentValue.copy( newValue ); + + } else if ( currentValue instanceof THREE.Color && typeof( newValue ) === "number" ) { + + currentValue.setHex( newValue ); + + } else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { + + currentValue.copy( newValue ); + + } else { + + this[ key ] = newValue; + + } + + } + + } + +}; + +THREE.Material.prototype.clone = function ( material ) { + + if ( material === undefined ) material = new THREE.Material(); + + material.name = this.name; + + material.side = this.side; + + material.opacity = this.opacity; + material.transparent = this.transparent; + + material.blending = this.blending; + + material.blendSrc = this.blendSrc; + material.blendDst = this.blendDst; + material.blendEquation = this.blendEquation; + + material.depthTest = this.depthTest; + material.depthWrite = this.depthWrite; + + material.polygonOffset = this.polygonOffset; + material.polygonOffsetFactor = this.polygonOffsetFactor; + material.polygonOffsetUnits = this.polygonOffsetUnits; + + material.alphaTest = this.alphaTest; + + material.overdraw = this.overdraw; + + material.visible = this.visible; + + return material; + +}; + +THREE.Material.prototype.deallocate = function () { + + var index = THREE.MaterialLibrary.indexOf( this ); + if ( index !== -1 ) THREE.MaterialLibrary.splice( index, 1 ); + +}; + +THREE.MaterialIdCount = 0; +THREE.MaterialLibrary = []; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * + * linewidth: , + * linecap: "round", + * linejoin: "round", + * + * vertexColors: + * + * fog: + * } + */ + +THREE.LineBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); + + this.linewidth = 1; + this.linecap = 'round'; + this.linejoin = 'round'; + + this.vertexColors = false; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.LineBasicMaterial.prototype.clone = function () { + + var material = new THREE.LineBasicMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.linewidth = this.linewidth; + material.linecap = this.linecap; + material.linejoin = this.linejoin; + + material.vertexColors = this.vertexColors; + + material.fog = this.fog; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * + * fog: + * } + */ + +THREE.MeshBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); // emissive + + this.map = null; + + this.lightMap = null; + + this.specularMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + + this.setValues( parameters ); + +}; + +THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshBasicMaterial.prototype.clone = function () { + + var material = new THREE.MeshBasicMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.specularMap = this.specularMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * ambient: , + * emissive: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * specularMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshLambertMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.ambient = new THREE.Color( 0xffffff ); + this.emissive = new THREE.Color( 0x000000 ); + + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + + this.map = null; + + this.lightMap = null; + + this.specularMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshLambertMaterial.prototype.clone = function () { + + var material = new THREE.MeshLambertMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); + + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.specularMap = this.specularMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * ambient: , + * emissive: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * bumpMap: new THREE.Texture( ), + * bumpScale: , + * + * normalMap: new THREE.Texture( ), + * normalScale: , + * + * specularMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.MeshPhongMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); // diffuse + this.ambient = new THREE.Color( 0xffffff ); + this.emissive = new THREE.Color( 0x000000 ); + this.specular = new THREE.Color( 0x111111 ); + this.shininess = 30; + + this.metal = false; + this.perPixel = false; + + this.wrapAround = false; + this.wrapRGB = new THREE.Vector3( 1, 1, 1 ); + + this.map = null; + + this.lightMap = null; + + this.bumpMap = null; + this.bumpScale = 1; + + this.normalMap = null; + this.normalScale = new THREE.Vector2( 1, 1 ); + + this.specularMap = null; + + this.envMap = null; + this.combine = THREE.MultiplyOperation; + this.reflectivity = 1; + this.refractionRatio = 0.98; + + this.fog = true; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + this.wireframeLinecap = 'round'; + this.wireframeLinejoin = 'round'; + + this.vertexColors = THREE.NoColors; + + this.skinning = false; + this.morphTargets = false; + this.morphNormals = false; + + this.setValues( parameters ); + +}; + +THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshPhongMaterial.prototype.clone = function () { + + var material = new THREE.MeshPhongMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.ambient.copy( this.ambient ); + material.emissive.copy( this.emissive ); + material.specular.copy( this.specular ); + material.shininess = this.shininess; + + material.metal = this.metal; + material.perPixel = this.perPixel; + + material.wrapAround = this.wrapAround; + material.wrapRGB.copy( this.wrapRGB ); + + material.map = this.map; + + material.lightMap = this.lightMap; + + material.bumpMap = this.bumpMap; + material.bumpScale = this.bumpScale; + + material.normalMap = this.normalMap; + material.normalScale.copy( this.normalScale ); + + material.specularMap = this.specularMap; + + material.envMap = this.envMap; + material.combine = this.combine; + material.reflectivity = this.reflectivity; + material.refractionRatio = this.refractionRatio; + + material.fog = this.fog; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + material.wireframeLinecap = this.wireframeLinecap; + material.wireframeLinejoin = this.wireframeLinejoin; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * opacity: , + + * blending: THREE.NormalBlending, + * depthTest: , + + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshDepthMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.setValues( parameters ); + +}; + +THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshDepthMaterial.prototype.clone = function () { + + var material = new THREE.LineBasicMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + + * shading: THREE.FlatShading, + * blending: THREE.NormalBlending, + * depthTest: , + + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshNormalMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + this.shading = THREE.FlatShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.setValues( parameters ); + +}; + +THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.MeshNormalMaterial.prototype.clone = function () { + + var material = new THREE.MeshNormalMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.MeshFaceMaterial = function () {}; + +THREE.MeshFaceMaterial.prototype.clone = function () { + + return new THREE.MeshFaceMaterial(); + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * size: , + * + * blending: THREE.NormalBlending, + * depthTest: , + * + * vertexColors: , + * + * fog: + * } + */ + +THREE.ParticleBasicMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); + + this.map = null; + + this.size = 1; + this.sizeAttenuation = true; + + this.vertexColors = false; + + this.fog = true; + + this.setValues( parameters ); + +}; + +THREE.ParticleBasicMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.ParticleBasicMaterial.prototype.clone = function () { + + var material = new THREE.ParticleBasicMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + + material.map = this.map; + + material.size = this.size; + material.sizeAttenuation = this.sizeAttenuation; + + material.vertexColors = this.vertexColors; + + material.fog = this.fog; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * + * parameters = { + * color: , + * program: , + * opacity: , + * blending: THREE.NormalBlending + * } + */ + +THREE.ParticleCanvasMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.color = new THREE.Color( 0xffffff ); + this.program = function ( context, color ) {}; + + this.setValues( parameters ); + +}; + +THREE.ParticleCanvasMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.ParticleCanvasMaterial.prototype.clone = function () { + + var material = new THREE.ParticleCanvasMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.color.copy( this.color ); + material.program = this.program; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ParticleDOMMaterial = function ( element ) { + + this.element = element; + +}; + +THREE.ParticleDOMMaterial.prototype.clone = function(){ + + return new THREE.ParticleDOMMaterial( this.element ); + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * fragmentShader: , + * vertexShader: , + * + * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, + * + * defines: { "label" : "value" }, + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * + * wireframe: , + * wireframeLinewidth: , + * + * lights: , + * + * vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, + * + * skinning: , + * morphTargets: , + * morphNormals: , + * + * fog: + * } + */ + +THREE.ShaderMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.fragmentShader = "void main() {}"; + this.vertexShader = "void main() {}"; + this.uniforms = {}; + this.defines = {}; + this.attributes = null; + + this.shading = THREE.SmoothShading; + + this.wireframe = false; + this.wireframeLinewidth = 1; + + this.fog = false; // set to use scene fog + + this.lights = false; // set to use scene lights + + this.vertexColors = THREE.NoColors; // set to use "color" attribute stream + + this.skinning = false; // set to use skinning attribute streams + + this.morphTargets = false; // set to use morph targets + this.morphNormals = false; // set to use morph normals + + this.setValues( parameters ); + +}; + +THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); + +THREE.ShaderMaterial.prototype.clone = function () { + + var material = new THREE.ShaderMaterial(); + + THREE.Material.prototype.clone.call( this, material ); + + material.fragmentShader = this.fragmentShader; + material.vertexShader = this.vertexShader; + material.uniforms = this.uniforms; + material.attributes = this.attributes; + material.defines = this.defines; + + material.shading = this.shading; + + material.wireframe = this.wireframe; + material.wireframeLinewidth = this.wireframeLinewidth; + + material.fog = this.fog; + + material.lights = this.lights; + + material.vertexColors = this.vertexColors; + + material.skinning = this.skinning; + + material.morphTargets = this.morphTargets; + material.morphNormals = this.morphNormals; + + return material; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { + + THREE.TextureLibrary.push( this ); + + this.id = THREE.TextureIdCount ++; + + this.image = image; + + this.mapping = mapping !== undefined ? mapping : new THREE.UVMapping(); + + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = anisotropy !== undefined ? anisotropy : 1; + + this.format = format !== undefined ? format : THREE.RGBAFormat; + this.type = type !== undefined ? type : THREE.UnsignedByteType; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.generateMipmaps = true; + this.premultiplyAlpha = false; + this.flipY = true; + + this.needsUpdate = false; + this.onUpdate = null; + +}; + +THREE.Texture.prototype = { + + constructor: THREE.Texture, + + clone: function () { + + var texture = new THREE.Texture(); + + texture.image = this.image; + + texture.mapping = this.mapping; + + texture.wrapS = this.wrapS; + texture.wrapT = this.wrapT; + + texture.magFilter = this.magFilter; + texture.minFilter = this.minFilter; + + texture.anisotropy = this.anisotropy; + + texture.format = this.format; + texture.type = this.type; + + texture.offset.copy( this.offset ); + texture.repeat.copy( this.repeat ); + + texture.generateMipmaps = this.generateMipmaps; + texture.premultiplyAlpha = this.premultiplyAlpha; + texture.flipY = this.flipY; + + return texture; + + }, + + deallocate: function () { + + var index = THREE.TextureLibrary.indexOf( this ); + if ( index !== -1 ) THREE.TextureLibrary.splice( index, 1 ); + + } + +}; + +THREE.TextureIdCount = 0; +THREE.TextureLibrary = []; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type ); + + this.image = { width: width, height: height }; + this.mipmaps = mipmaps; + +}; + +THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); + +THREE.CompressedTexture.prototype.clone = function () { + + var texture = new THREE.CompressedTexture(); + + texture.image = this.image; + texture.mipmaps = this.mipmaps; + + texture.format = this.format; + texture.type = this.type; + + texture.mapping = this.mapping; + + texture.wrapS = this.wrapS; + texture.wrapT = this.wrapT; + + texture.magFilter = this.magFilter; + texture.minFilter = this.minFilter; + + texture.anisotropy = this.anisotropy; + + texture.offset.copy( this.offset ); + texture.repeat.copy( this.repeat ); + + return texture; + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter ) { + + THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type ); + + this.image = { data: data, width: width, height: height }; + +}; + +THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); + +THREE.DataTexture.prototype.clone = function () { + + var clonedTexture = new THREE.DataTexture( this.image.data, this.image.width, this.image.height, this.format, this.type, this.mapping, this.wrapS, this.wrapT, this.magFilter, this.minFilter ); + + clonedTexture.offset.copy( this.offset ); + clonedTexture.repeat.copy( this.repeat ); + + return clonedTexture; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Particle = function ( material ) { + + THREE.Object3D.call( this ); + + this.material = material; + +}; + +THREE.Particle.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Particle.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Particle( this.material ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ParticleSystem = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.ParticleBasicMaterial( { color: Math.random() * 0xffffff } ); + + this.sortParticles = false; + + if ( this.geometry ) { + + // calc bound radius + + if( this.geometry.boundingSphere === null ) { + + this.geometry.computeBoundingSphere(); + + } + + this.boundRadius = geometry.boundingSphere.radius; + + } + + this.frustumCulled = false; + +}; + +THREE.ParticleSystem.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.ParticleSystem.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.ParticleSystem( this.geometry, this.material ); + object.sortParticles = this.sortParticles; + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Line = function ( geometry, material, type ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); + this.type = ( type !== undefined ) ? type : THREE.LineStrip; + + if ( this.geometry ) { + + if ( ! this.geometry.boundingSphere ) { + + this.geometry.computeBoundingSphere(); + + } + + } + +}; + +THREE.LineStrip = 0; +THREE.LinePieces = 1; + +THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Line.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Line( this.geometry, this.material, this.type ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Mesh = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.material = ( material !== undefined ) ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, wireframe: true } ); + + if ( this.geometry ) { + + // calc bound radius + + if ( this.geometry.boundingSphere === null ) { + + this.geometry.computeBoundingSphere(); + + } + + this.boundRadius = geometry.boundingSphere.radius; + + + // setup morph targets + + if ( this.geometry.morphTargets.length ) { + + this.morphTargetBase = -1; + this.morphTargetForcedOrder = []; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for( var m = 0; m < this.geometry.morphTargets.length; m ++ ) { + + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; + + } + + } + + } + +} + +THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { + + if ( this.morphTargetDictionary[ name ] !== undefined ) { + + return this.morphTargetDictionary[ name ]; + + } + + console.log( "THREE.Mesh.getMorphTargetIndexByName: morph target " + name + " does not exist. Returning 0." ); + + return 0; + +}; + +THREE.Mesh.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Mesh( this.geometry, this.material ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Bone = function( belongsToSkin ) { + + THREE.Object3D.call( this ); + + this.skin = belongsToSkin; + this.skinMatrix = new THREE.Matrix4(); + +}; + +THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Bone.prototype.update = function( parentSkinMatrix, forceUpdate ) { + + // update local + + if ( this.matrixAutoUpdate ) { + + forceUpdate |= this.updateMatrix(); + + } + + // update skin matrix + + if ( forceUpdate || this.matrixWorldNeedsUpdate ) { + + if( parentSkinMatrix ) { + + this.skinMatrix.multiply( parentSkinMatrix, this.matrix ); + + } else { + + this.skinMatrix.copy( this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + forceUpdate = true; + + } + + // update children + + var child, i, l = this.children.length; + + for ( i = 0; i < l; i ++ ) { + + this.children[ i ].update( this.skinMatrix, forceUpdate ); + + } + +}; + +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { + + THREE.Mesh.call( this, geometry, material ); + + // + + this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; + + // init bones + + this.identityMatrix = new THREE.Matrix4(); + + this.bones = []; + this.boneMatrices = []; + + var b, bone, gbone, p, q, s; + + if ( this.geometry.bones !== undefined ) { + + for ( b = 0; b < this.geometry.bones.length; b ++ ) { + + gbone = this.geometry.bones[ b ]; + + p = gbone.pos; + q = gbone.rotq; + s = gbone.scl; + + bone = this.addBone(); + + bone.name = gbone.name; + bone.position.set( p[0], p[1], p[2] ); + bone.quaternion.set( q[0], q[1], q[2], q[3] ); + bone.useQuaternion = true; + + if ( s !== undefined ) { + + bone.scale.set( s[0], s[1], s[2] ); + + } else { + + bone.scale.set( 1, 1, 1 ); + + } + + } + + for ( b = 0; b < this.bones.length; b ++ ) { + + gbone = this.geometry.bones[ b ]; + bone = this.bones[ b ]; + + if ( gbone.parent === -1 ) { + + this.add( bone ); + + } else { + + this.bones[ gbone.parent ].add( bone ); + + } + + } + + // + + var nBones = this.bones.length; + + if ( this.useVertexTexture ) { + + // layout (1 matrix = 4 pixels) + // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) + // with 8x8 pixel texture max 16 bones (8 * 8 / 4) + // 16x16 pixel texture max 64 bones (16 * 16 / 4) + // 32x32 pixel texture max 256 bones (32 * 32 / 4) + // 64x64 pixel texture max 1024 bones (64 * 64 / 4) + + var size; + + if ( nBones > 256 ) + size = 64; + else if ( nBones > 64 ) + size = 32; + else if ( nBones > 16 ) + size = 16; + else + size = 8; + + this.boneTextureWidth = size; + this.boneTextureHeight = size; + + this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel + this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); + this.boneTexture.minFilter = THREE.NearestFilter; + this.boneTexture.magFilter = THREE.NearestFilter; + this.boneTexture.generateMipmaps = false; + this.boneTexture.flipY = false; + + } else { + + this.boneMatrices = new Float32Array( 16 * nBones ); + + } + + this.pose(); + + } + +}; + +THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.SkinnedMesh.prototype.addBone = function( bone ) { + + if ( bone === undefined ) { + + bone = new THREE.Bone( this ); + + } + + this.bones.push( bone ); + + return bone; + +}; + +THREE.SkinnedMesh.prototype.updateMatrixWorld = function ( force ) { + + this.matrixAutoUpdate && this.updateMatrix(); + + // update matrixWorld + + if ( this.matrixWorldNeedsUpdate || force ) { + + if ( this.parent ) { + + this.matrixWorld.multiply( this.parent.matrixWorld, this.matrix ); + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + var child = this.children[ i ]; + + if ( child instanceof THREE.Bone ) { + + child.update( this.identityMatrix, false ); + + } else { + + child.updateMatrixWorld( true ); + + } + + } + + // make a snapshot of the bones' rest position + + if ( this.boneInverses == undefined ) { + + this.boneInverses = []; + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + var inverse = new THREE.Matrix4(); + + inverse.getInverse( this.bones[ b ].skinMatrix ); + + this.boneInverses.push( inverse ); + + } + + } + + // flatten bone matrices to array + + for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { + + // compute the offset between the current and the original transform; + + //TODO: we could get rid of this multiplication step if the skinMatrix + // was already representing the offset; however, this requires some + // major changes to the animation system + + THREE.SkinnedMesh.offsetMatrix.multiply( this.bones[ b ].skinMatrix, this.boneInverses[ b ] ); + + THREE.SkinnedMesh.offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + + } + + if ( this.useVertexTexture ) { + + this.boneTexture.needsUpdate = true; + + } + +}; + +THREE.SkinnedMesh.prototype.pose = function() { + + this.updateMatrixWorld( true ); + + for ( var i = 0; i < this.geometry.skinIndices.length; i ++ ) { + + // normalize weights + + var sw = this.geometry.skinWeights[ i ]; + + var scale = 1.0 / sw.lengthManhattan(); + + if ( scale !== Infinity ) { + + sw.multiplyScalar( scale ); + + } else { + + sw.set( 1 ); // this will be normalized by the shader anyway + + } + + } + +}; + +THREE.SkinnedMesh.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.SkinnedMesh( this.geometry, this.material, this.useVertexTexture ); + + THREE.Mesh.prototype.clone.call( this, object ); + + return object; + +}; + +THREE.SkinnedMesh.offsetMatrix = new THREE.Matrix4(); +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphAnimMesh = function ( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + // API + + this.duration = 1000; // milliseconds + this.mirroredLoop = false; + this.time = 0; + + // internals + + this.lastKeyframe = 0; + this.currentKeyframe = 0; + + this.direction = 1; + this.directionBackwards = false; + + this.setFrameRange( 0, this.geometry.morphTargets.length - 1 ); + +}; + +THREE.MorphAnimMesh.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.MorphAnimMesh.prototype.setFrameRange = function ( start, end ) { + + this.startKeyframe = start; + this.endKeyframe = end; + + this.length = this.endKeyframe - this.startKeyframe + 1; + +}; + +THREE.MorphAnimMesh.prototype.setDirectionForward = function () { + + this.direction = 1; + this.directionBackwards = false; + +}; + +THREE.MorphAnimMesh.prototype.setDirectionBackward = function () { + + this.direction = -1; + this.directionBackwards = true; + +}; + +THREE.MorphAnimMesh.prototype.parseAnimations = function () { + + var geometry = this.geometry; + + if ( ! geometry.animations ) geometry.animations = {}; + + var firstAnimation, animations = geometry.animations; + + var pattern = /([a-z]+)(\d+)/; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var parts = morph.name.match( pattern ); + + if ( parts && parts.length > 1 ) { + + var label = parts[ 1 ]; + var num = parts[ 2 ]; + + if ( ! animations[ label ] ) animations[ label ] = { start: Infinity, end: -Infinity }; + + var animation = animations[ label ]; + + if ( i < animation.start ) animation.start = i; + if ( i > animation.end ) animation.end = i; + + if ( ! firstAnimation ) firstAnimation = label; + + } + + } + + geometry.firstAnimation = firstAnimation; + +}; + +THREE.MorphAnimMesh.prototype.setAnimationLabel = function ( label, start, end ) { + + if ( ! this.geometry.animations ) this.geometry.animations = {}; + + this.geometry.animations[ label ] = { start: start, end: end }; + +}; + +THREE.MorphAnimMesh.prototype.playAnimation = function ( label, fps ) { + + var animation = this.geometry.animations[ label ]; + + if ( animation ) { + + this.setFrameRange( animation.start, animation.end ); + this.duration = 1000 * ( ( animation.end - animation.start ) / fps ); + this.time = 0; + + } else { + + console.warn( "animation[" + label + "] undefined" ); + + } + +}; + +THREE.MorphAnimMesh.prototype.updateAnimation = function ( delta ) { + + var frameTime = this.duration / this.length; + + this.time += this.direction * delta; + + if ( this.mirroredLoop ) { + + if ( this.time > this.duration || this.time < 0 ) { + + this.direction *= -1; + + if ( this.time > this.duration ) { + + this.time = this.duration; + this.directionBackwards = true; + + } + + if ( this.time < 0 ) { + + this.time = 0; + this.directionBackwards = false; + + } + + } + + } else { + + this.time = this.time % this.duration; + + if ( this.time < 0 ) this.time += this.duration; + + } + + var keyframe = this.startKeyframe + THREE.Math.clamp( Math.floor( this.time / frameTime ), 0, this.length - 1 ); + + if ( keyframe !== this.currentKeyframe ) { + + this.morphTargetInfluences[ this.lastKeyframe ] = 0; + this.morphTargetInfluences[ this.currentKeyframe ] = 1; + + this.morphTargetInfluences[ keyframe ] = 0; + + this.lastKeyframe = this.currentKeyframe; + this.currentKeyframe = keyframe; + + } + + var mix = ( this.time % frameTime ) / frameTime; + + if ( this.directionBackwards ) { + + mix = 1 - mix; + + } + + this.morphTargetInfluences[ this.currentKeyframe ] = mix; + this.morphTargetInfluences[ this.lastKeyframe ] = 1 - mix; + +}; + +THREE.MorphAnimMesh.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.MorphAnimMesh( this.geometry, this.material ); + + object.duration = this.duration; + object.mirroredLoop = this.mirroredLoop; + object.time = this.time; + + object.lastKeyframe = this.lastKeyframe; + object.currentKeyframe = this.currentKeyframe; + + object.direction = this.direction; + object.directionBackwards = this.directionBackwards; + + THREE.Mesh.prototype.clone.call( this, object ); + + return object; + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Ribbon = function ( geometry, material ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.material = material; + +}; + +THREE.Ribbon.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Ribbon.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Ribbon( this.geometry, this.material ); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.LOD = function () { + + THREE.Object3D.call( this ); + + this.LODs = []; + +}; + + +THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.LOD.prototype.addLevel = function ( object3D, visibleAtDistance ) { + + if ( visibleAtDistance === undefined ) { + + visibleAtDistance = 0; + + } + + visibleAtDistance = Math.abs( visibleAtDistance ); + + for ( var l = 0; l < this.LODs.length; l ++ ) { + + if ( visibleAtDistance < this.LODs[ l ].visibleAtDistance ) { + + break; + + } + + } + + this.LODs.splice( l, 0, { visibleAtDistance: visibleAtDistance, object3D: object3D } ); + this.add( object3D ); + +}; + +THREE.LOD.prototype.update = function ( camera ) { + + if ( this.LODs.length > 1 ) { + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + var inverse = camera.matrixWorldInverse; + var distance = -( inverse.elements[2] * this.matrixWorld.elements[12] + inverse.elements[6] * this.matrixWorld.elements[13] + inverse.elements[10] * this.matrixWorld.elements[14] + inverse.elements[14] ); + + this.LODs[ 0 ].object3D.visible = true; + + for ( var l = 1; l < this.LODs.length; l ++ ) { + + if( distance >= this.LODs[ l ].visibleAtDistance ) { + + this.LODs[ l - 1 ].object3D.visible = false; + this.LODs[ l ].object3D.visible = true; + + } else { + + break; + + } + + } + + for( ; l < this.LODs.length; l ++ ) { + + this.LODs[ l ].object3D.visible = false; + + } + + } + +}; + +THREE.LOD.prototype.clone = function () { + + // TODO + +}; +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Sprite = function ( parameters ) { + + THREE.Object3D.call( this ); + + this.color = ( parameters.color !== undefined ) ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff ); + this.map = ( parameters.map !== undefined ) ? parameters.map : new THREE.Texture(); + + this.blending = ( parameters.blending !== undefined ) ? parameters.blending : THREE.NormalBlending; + + this.blendSrc = parameters.blendSrc !== undefined ? parameters.blendSrc : THREE.SrcAlphaFactor; + this.blendDst = parameters.blendDst !== undefined ? parameters.blendDst : THREE.OneMinusSrcAlphaFactor; + this.blendEquation = parameters.blendEquation !== undefined ? parameters.blendEquation : THREE.AddEquation; + + this.useScreenCoordinates = ( parameters.useScreenCoordinates !== undefined ) ? parameters.useScreenCoordinates : true; + this.mergeWith3D = ( parameters.mergeWith3D !== undefined ) ? parameters.mergeWith3D : !this.useScreenCoordinates; + this.affectedByDistance = ( parameters.affectedByDistance !== undefined ) ? parameters.affectedByDistance : !this.useScreenCoordinates; + this.scaleByViewport = ( parameters.scaleByViewport !== undefined ) ? parameters.scaleByViewport : !this.affectedByDistance; + this.alignment = ( parameters.alignment instanceof THREE.Vector2 ) ? parameters.alignment : THREE.SpriteAlignment.center; + + this.rotation3d = this.rotation; + this.rotation = 0; + this.opacity = 1; + + this.uvOffset = new THREE.Vector2( 0, 0 ); + this.uvScale = new THREE.Vector2( 1, 1 ); + +}; + +THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); + +/* + * Custom update matrix + */ + +THREE.Sprite.prototype.updateMatrix = function () { + + this.matrix.setPosition( this.position ); + + this.rotation3d.set( 0, 0, this.rotation ); + this.matrix.setRotationFromEuler( this.rotation3d ); + + if ( this.scale.x !== 1 || this.scale.y !== 1 ) { + + this.matrix.scale( this.scale ); + this.boundRadiusScale = Math.max( this.scale.x, this.scale.y ); + + } + + this.matrixWorldNeedsUpdate = true; + +}; + +THREE.Sprite.prototype.clone = function ( object ) { + + if ( object === undefined ) object = new THREE.Sprite( {} ); + + object.color.copy( this.color ); + object.map = this.map; + object.blending = this.blending; + + object.useScreenCoordinates = this.useScreenCoordinates; + object.mergeWith3D = this.mergeWith3D; + object.affectedByDistance = this.affectedByDistance; + object.scaleByViewport = this.scaleByViewport; + object.alignment = this.alignment; + + object.rotation3d.copy( this.rotation3d ); + object.rotation = this.rotation; + object.opacity = this.opacity; + + object.uvOffset.copy( this.uvOffset ); + object.uvScale.copy( this.uvScale); + + THREE.Object3D.prototype.clone.call( this, object ); + + return object; + +}; + +/* + * Alignment + */ + +THREE.SpriteAlignment = {}; +THREE.SpriteAlignment.topLeft = new THREE.Vector2( 1, -1 ); +THREE.SpriteAlignment.topCenter = new THREE.Vector2( 0, -1 ); +THREE.SpriteAlignment.topRight = new THREE.Vector2( -1, -1 ); +THREE.SpriteAlignment.centerLeft = new THREE.Vector2( 1, 0 ); +THREE.SpriteAlignment.center = new THREE.Vector2( 0, 0 ); +THREE.SpriteAlignment.centerRight = new THREE.Vector2( -1, 0 ); +THREE.SpriteAlignment.bottomLeft = new THREE.Vector2( 1, 1 ); +THREE.SpriteAlignment.bottomCenter = new THREE.Vector2( 0, 1 ); +THREE.SpriteAlignment.bottomRight = new THREE.Vector2( -1, 1 ); +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.Scene = function () { + + THREE.Object3D.call( this ); + + this.fog = null; + this.overrideMaterial = null; + + this.matrixAutoUpdate = false; + + this.__objects = []; + this.__lights = []; + + this.__objectsAdded = []; + this.__objectsRemoved = []; + +}; + +THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Scene.prototype.__addObject = function ( object ) { + + if ( object instanceof THREE.Light ) { + + if ( this.__lights.indexOf( object ) === - 1 ) { + + this.__lights.push( object ); + + } + + if ( object.target && object.target.parent === undefined ) { + + this.add( object.target ); + + } + + } else if ( !( object instanceof THREE.Camera || object instanceof THREE.Bone ) ) { + + if ( this.__objects.indexOf( object ) === - 1 ) { + + this.__objects.push( object ); + this.__objectsAdded.push( object ); + + // check if previously removed + + var i = this.__objectsRemoved.indexOf( object ); + + if ( i !== -1 ) { + + this.__objectsRemoved.splice( i, 1 ); + + } + + } + + } + + for ( var c = 0; c < object.children.length; c ++ ) { + + this.__addObject( object.children[ c ] ); + + } + +}; + +THREE.Scene.prototype.__removeObject = function ( object ) { + + if ( object instanceof THREE.Light ) { + + var i = this.__lights.indexOf( object ); + + if ( i !== -1 ) { + + this.__lights.splice( i, 1 ); + + } + + } else if ( !( object instanceof THREE.Camera ) ) { + + var i = this.__objects.indexOf( object ); + + if( i !== -1 ) { + + this.__objects.splice( i, 1 ); + this.__objectsRemoved.push( object ); + + // check if previously added + + var ai = this.__objectsAdded.indexOf( object ); + + if ( ai !== -1 ) { + + this.__objectsAdded.splice( ai, 1 ); + + } + + } + + } + + for ( var c = 0; c < object.children.length; c ++ ) { + + this.__removeObject( object.children[ c ] ); + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Fog = function ( hex, near, far ) { + + this.color = new THREE.Color( hex ); + + this.near = ( near !== undefined ) ? near : 1; + this.far = ( far !== undefined ) ? far : 1000; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.FogExp2 = function ( hex, density ) { + + this.color = new THREE.Color( hex ); + this.density = ( density !== undefined ) ? density : 0.00025; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CanvasRenderer = function ( parameters ) { + + console.log( 'THREE.CanvasRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var _this = this, + _renderData, _elements, _lights, + _projector = new THREE.Projector(), + + _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + + _canvasWidth, _canvasHeight, _canvasWidthHalf, _canvasHeightHalf, + _context = _canvas.getContext( '2d' ), + + _clearColor = new THREE.Color( 0x000000 ), + _clearOpacity = 0, + + _contextGlobalAlpha = 1, + _contextGlobalCompositeOperation = 0, + _contextStrokeStyle = null, + _contextFillStyle = null, + _contextLineWidth = null, + _contextLineCap = null, + _contextLineJoin = null, + + _v1, _v2, _v3, _v4, + _v5 = new THREE.RenderableVertex(), + _v6 = new THREE.RenderableVertex(), + + _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, + _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, + + _color = new THREE.Color(), + _color1 = new THREE.Color(), + _color2 = new THREE.Color(), + _color3 = new THREE.Color(), + _color4 = new THREE.Color(), + + _diffuseColor = new THREE.Color(), + _emissiveColor = new THREE.Color(), + + _patterns = {}, _imagedatas = {}, + + _near, _far, + + _image, _uvs, + _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, + + _clipRect = new THREE.Rectangle(), + _clearRect = new THREE.Rectangle(), + _bboxRect = new THREE.Rectangle(), + + _enableLighting = false, + _ambientLight = new THREE.Color(), + _directionalLights = new THREE.Color(), + _pointLights = new THREE.Color(), + + _pi2 = Math.PI * 2, + _vector3 = new THREE.Vector3(), // Needed for PointLight + + _pixelMap, _pixelMapContext, _pixelMapImage, _pixelMapData, + _gradientMap, _gradientMapContext, _gradientMapQuality = 16; + + _pixelMap = document.createElement( 'canvas' ); + _pixelMap.width = _pixelMap.height = 2; + + _pixelMapContext = _pixelMap.getContext( '2d' ); + _pixelMapContext.fillStyle = 'rgba(0,0,0,1)'; + _pixelMapContext.fillRect( 0, 0, 2, 2 ); + + _pixelMapImage = _pixelMapContext.getImageData( 0, 0, 2, 2 ); + _pixelMapData = _pixelMapImage.data; + + _gradientMap = document.createElement( 'canvas' ); + _gradientMap.width = _gradientMap.height = _gradientMapQuality; + + _gradientMapContext = _gradientMap.getContext( '2d' ); + _gradientMapContext.translate( - _gradientMapQuality / 2, - _gradientMapQuality / 2 ); + _gradientMapContext.scale( _gradientMapQuality, _gradientMapQuality ); + + _gradientMapQuality --; // Fix UVs + + this.domElement = _canvas; + + this.autoClear = true; + this.sortObjects = true; + this.sortElements = true; + + this.info = { + + render: { + + vertices: 0, + faces: 0 + + } + + } + + this.setSize = function ( width, height ) { + + _canvasWidth = width; + _canvasHeight = height; + _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); + _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); + + _canvas.width = _canvasWidth; + _canvas.height = _canvasHeight; + + _clipRect.set( - _canvasWidthHalf, - _canvasHeightHalf, _canvasWidthHalf, _canvasHeightHalf ); + _clearRect.set( - _canvasWidthHalf, - _canvasHeightHalf, _canvasWidthHalf, _canvasHeightHalf ); + + _contextGlobalAlpha = 1; + _contextGlobalCompositeOperation = 0; + _contextStrokeStyle = null; + _contextFillStyle = null; + _contextLineWidth = null; + _contextLineCap = null; + _contextLineJoin = null; + + }; + + this.setClearColor = function ( color, opacity ) { + + _clearColor.copy( color ); + _clearOpacity = opacity !== undefined ? opacity : 1; + + _clearRect.set( - _canvasWidthHalf, - _canvasHeightHalf, _canvasWidthHalf, _canvasHeightHalf ); + + }; + + this.setClearColorHex = function ( hex, opacity ) { + + _clearColor.setHex( hex ); + _clearOpacity = opacity !== undefined ? opacity : 1; + + _clearRect.set( - _canvasWidthHalf, - _canvasHeightHalf, _canvasWidthHalf, _canvasHeightHalf ); + + }; + + this.getMaxAnisotropy = function () { + + return 0; + + }; + + this.clear = function () { + + _context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf ); + + if ( _clearRect.isEmpty() === false ) { + + _clearRect.minSelf( _clipRect ); + _clearRect.inflate( 2 ); + + if ( _clearOpacity < 1 ) { + + _context.clearRect( Math.floor( _clearRect.getX() ), Math.floor( _clearRect.getY() ), Math.floor( _clearRect.getWidth() ), Math.floor( _clearRect.getHeight() ) ); + + } + + if ( _clearOpacity > 0 ) { + + setBlending( THREE.NormalBlending ); + setOpacity( 1 ); + + setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearOpacity + ')' ); + + _context.fillRect( Math.floor( _clearRect.getX() ), Math.floor( _clearRect.getY() ), Math.floor( _clearRect.getWidth() ), Math.floor( _clearRect.getHeight() ) ); + + } + + _clearRect.empty(); + + } + + + }; + + this.render = function ( scene, camera ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + var e, el, element, material; + + this.autoClear === true + ? this.clear() + : _context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf ); + + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + + _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); + _elements = _renderData.elements; + _lights = _renderData.lights; + + /* DEBUG + _context.fillStyle = 'rgba( 0, 255, 255, 0.5 )'; + _context.fillRect( _clipRect.getX(), _clipRect.getY(), _clipRect.getWidth(), _clipRect.getHeight() ); + */ + + _enableLighting = _lights.length > 0; + + if ( _enableLighting === true ) { + + calculateLights(); + + } + + for ( e = 0, el = _elements.length; e < el; e++ ) { + + element = _elements[ e ]; + + material = element.material; + + if ( material === undefined || material.visible === false ) continue; + + _bboxRect.empty(); + + if ( element instanceof THREE.RenderableParticle ) { + + _v1 = element; + _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; + + renderParticle( _v1, element, material, scene ); + + } else if ( element instanceof THREE.RenderableLine ) { + + _v1 = element.v1; _v2 = element.v2; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + + _bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y ); + _bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y ); + + if ( _clipRect.intersects( _bboxRect ) === true ) { + + renderLine( _v1, _v2, element, material, scene ); + + } + + + } else if ( element instanceof THREE.RenderableFace3 ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + + if ( material.overdraw === true ) { + + expand( _v1.positionScreen, _v2.positionScreen ); + expand( _v2.positionScreen, _v3.positionScreen ); + expand( _v3.positionScreen, _v1.positionScreen ); + + } + + _bboxRect.add3Points( _v1.positionScreen.x, _v1.positionScreen.y, + _v2.positionScreen.x, _v2.positionScreen.y, + _v3.positionScreen.x, _v3.positionScreen.y ); + + if ( _clipRect.intersects( _bboxRect ) === true ) { + + renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material, scene ); + + } + + } else if ( element instanceof THREE.RenderableFace4 ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + _v4.positionScreen.x *= _canvasWidthHalf; _v4.positionScreen.y *= _canvasHeightHalf; + + _v5.positionScreen.copy( _v2.positionScreen ); + _v6.positionScreen.copy( _v4.positionScreen ); + + if ( material.overdraw === true ) { + + expand( _v1.positionScreen, _v2.positionScreen ); + expand( _v2.positionScreen, _v4.positionScreen ); + expand( _v4.positionScreen, _v1.positionScreen ); + + expand( _v3.positionScreen, _v5.positionScreen ); + expand( _v3.positionScreen, _v6.positionScreen ); + + } + + _bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y ); + _bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y ); + _bboxRect.addPoint( _v3.positionScreen.x, _v3.positionScreen.y ); + _bboxRect.addPoint( _v4.positionScreen.x, _v4.positionScreen.y ); + + if ( _clipRect.intersects( _bboxRect ) === true ) { + + renderFace4( _v1, _v2, _v3, _v4, _v5, _v6, element, material, scene ); + + } + + } + + /* DEBUG + _context.lineWidth = 1; + _context.strokeStyle = 'rgba( 0, 255, 0, 0.5 )'; + _context.strokeRect( _bboxRect.getX(), _bboxRect.getY(), _bboxRect.getWidth(), _bboxRect.getHeight() ); + */ + + _clearRect.addRectangle( _bboxRect ); + + + } + + /* DEBUG + _context.lineWidth = 1; + _context.strokeStyle = 'rgba( 255, 0, 0, 0.5 )'; + _context.strokeRect( _clearRect.getX(), _clearRect.getY(), _clearRect.getWidth(), _clearRect.getHeight() ); + */ + + _context.setTransform( 1, 0, 0, 1, 0, 0 ); + + // + + function calculateLights() { + + _ambientLight.setRGB( 0, 0, 0 ); + _directionalLights.setRGB( 0, 0, 0 ); + _pointLights.setRGB( 0, 0, 0 ); + + for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + + var light = _lights[ l ]; + var lightColor = light.color; + + if ( light instanceof THREE.AmbientLight ) { + + _ambientLight.r += lightColor.r; + _ambientLight.g += lightColor.g; + _ambientLight.b += lightColor.b; + + } else if ( light instanceof THREE.DirectionalLight ) { + + // for particles + + _directionalLights.r += lightColor.r; + _directionalLights.g += lightColor.g; + _directionalLights.b += lightColor.b; + + } else if ( light instanceof THREE.PointLight ) { + + // for particles + + _pointLights.r += lightColor.r; + _pointLights.g += lightColor.g; + _pointLights.b += lightColor.b; + + } + + } + + } + + function calculateLight( position, normal, color ) { + + for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + + var light = _lights[ l ]; + var lightColor = light.color; + + if ( light instanceof THREE.DirectionalLight ) { + + var lightPosition = light.matrixWorld.getPosition().normalize(); + + var amount = normal.dot( lightPosition ); + + if ( amount <= 0 ) continue; + + amount *= light.intensity; + + color.r += lightColor.r * amount; + color.g += lightColor.g * amount; + color.b += lightColor.b * amount; + + } else if ( light instanceof THREE.PointLight ) { + + var lightPosition = light.matrixWorld.getPosition(); + + var amount = normal.dot( _vector3.sub( lightPosition, position ).normalize() ); + + if ( amount <= 0 ) continue; + + amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + + if ( amount == 0 ) continue; + + amount *= light.intensity; + + color.r += lightColor.r * amount; + color.g += lightColor.g * amount; + color.b += lightColor.b * amount; + + } + + } + + } + + function renderParticle( v1, element, material, scene ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + var width, height, scaleX, scaleY, + bitmap, bitmapWidth, bitmapHeight; + + if ( material instanceof THREE.ParticleBasicMaterial ) { + + if ( material.map === null ) { + + scaleX = element.object.scale.x; + scaleY = element.object.scale.y; + + // TODO: Be able to disable this + + scaleX *= element.scale.x * _canvasWidthHalf; + scaleY *= element.scale.y * _canvasHeightHalf; + + _bboxRect.set( v1.x - scaleX, v1.y - scaleY, v1.x + scaleX, v1.y + scaleY ); + + if ( _clipRect.intersects( _bboxRect ) === false ) { + + return; + + } + + setFillStyle( material.color.getContextStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + _context.rotate( - element.rotation ); + _context.scale( scaleX, scaleY ); + _context.fillRect( -1, -1, 2, 2 ); + _context.restore(); + + } else { + + bitmap = material.map.image; + bitmapWidth = bitmap.width >> 1; + bitmapHeight = bitmap.height >> 1; + + scaleX = element.scale.x * _canvasWidthHalf; + scaleY = element.scale.y * _canvasHeightHalf; + + width = scaleX * bitmapWidth; + height = scaleY * bitmapHeight; + + // TODO: Rotations break this... + + _bboxRect.set( v1.x - width, v1.y - height, v1.x + width, v1.y + height ); + + if ( _clipRect.intersects( _bboxRect ) === false ) { + + return; + + } + + _context.save(); + _context.translate( v1.x, v1.y ); + _context.rotate( - element.rotation ); + _context.scale( scaleX, - scaleY ); + + _context.translate( - bitmapWidth, - bitmapHeight ); + _context.drawImage( bitmap, 0, 0 ); + _context.restore(); + + } + + /* DEBUG + setStrokeStyle( 'rgb(255,255,0)' ); + _context.beginPath(); + _context.moveTo( v1.x - 10, v1.y ); + _context.lineTo( v1.x + 10, v1.y ); + _context.moveTo( v1.x, v1.y - 10 ); + _context.lineTo( v1.x, v1.y + 10 ); + _context.stroke(); + */ + + } else if ( material instanceof THREE.ParticleCanvasMaterial ) { + + width = element.scale.x * _canvasWidthHalf; + height = element.scale.y * _canvasHeightHalf; + + _bboxRect.set( v1.x - width, v1.y - height, v1.x + width, v1.y + height ); + + if ( _clipRect.intersects( _bboxRect ) === false ) { + + return; + + } + + setStrokeStyle( material.color.getContextStyle() ); + setFillStyle( material.color.getContextStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + _context.rotate( - element.rotation ); + _context.scale( width, height ); + + material.program( _context ); + + _context.restore(); + + } + + } + + function renderLine( v1, v2, element, material, scene ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _context.beginPath(); + _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); + _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); + + if ( material instanceof THREE.LineBasicMaterial ) { + + setLineWidth( material.linewidth ); + setLineCap( material.linecap ); + setLineJoin( material.linejoin ); + setStrokeStyle( material.color.getContextStyle() ); + + _context.stroke(); + _bboxRect.inflate( material.linewidth * 2 ); + + } + + } + + function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material, scene ) { + + _this.info.render.vertices += 3; + _this.info.render.faces ++; + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; + _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; + _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); + + if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null && material.map === null ) { + + _diffuseColor.copy( material.color ); + _emissiveColor.copy( material.emissive ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _diffuseColor.r *= element.color.r; + _diffuseColor.g *= element.color.g; + _diffuseColor.b *= element.color.b; + + } + + if ( _enableLighting === true ) { + + if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 3 ) { + + _color1.r = _color2.r = _color3.r = _ambientLight.r; + _color1.g = _color2.g = _color3.g = _ambientLight.g; + _color1.b = _color2.b = _color3.b = _ambientLight.b; + + calculateLight( element.v1.positionWorld, element.vertexNormalsWorld[ 0 ], _color1 ); + calculateLight( element.v2.positionWorld, element.vertexNormalsWorld[ 1 ], _color2 ); + calculateLight( element.v3.positionWorld, element.vertexNormalsWorld[ 2 ], _color3 ); + + _color1.r = _color1.r * _diffuseColor.r + _emissiveColor.r; + _color1.g = _color1.g * _diffuseColor.g + _emissiveColor.g; + _color1.b = _color1.b * _diffuseColor.b + _emissiveColor.b; + + _color2.r = _color2.r * _diffuseColor.r + _emissiveColor.r; + _color2.g = _color2.g * _diffuseColor.g + _emissiveColor.g; + _color2.b = _color2.b * _diffuseColor.b + _emissiveColor.b; + + _color3.r = _color3.r * _diffuseColor.r + _emissiveColor.r; + _color3.g = _color3.g * _diffuseColor.g + _emissiveColor.g; + _color3.b = _color3.b * _diffuseColor.b + _emissiveColor.b; + + _color4.r = ( _color2.r + _color3.r ) * 0.5; + _color4.g = ( _color2.g + _color3.g ) * 0.5; + _color4.b = ( _color2.b + _color3.b ) * 0.5; + + _image = getGradientTexture( _color1, _color2, _color3, _color4 ); + + clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image ); + + } else { + + _color.r = _ambientLight.r; + _color.g = _ambientLight.g; + _color.b = _ambientLight.b; + + calculateLight( element.centroidWorld, element.normalWorld, _color ); + + _color.r = _color.r * _diffuseColor.r + _emissiveColor.r; + _color.g = _color.g * _diffuseColor.g + _emissiveColor.g; + _color.b = _color.b * _diffuseColor.b + _emissiveColor.b; + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } else { + + material.wireframe === true + ? strokePath( material.color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( material.color ); + + } + + } else if ( material instanceof THREE.MeshBasicMaterial || material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { + + if ( material.map !== null ) { + + if ( material.map.mapping instanceof THREE.UVMapping ) { + + _uvs = element.uvs[ 0 ]; + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].u, _uvs[ uv1 ].v, _uvs[ uv2 ].u, _uvs[ uv2 ].v, _uvs[ uv3 ].u, _uvs[ uv3 ].v, material.map ); + + } + + + } else if ( material.envMap !== null ) { + + if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) { + + var cameraMatrix = camera.matrixWorldInverse; + + _vector3.copy( element.vertexNormalsWorld[ uv1 ] ); + _uv1x = ( _vector3.x * cameraMatrix.elements[0] + _vector3.y * cameraMatrix.elements[4] + _vector3.z * cameraMatrix.elements[8] ) * 0.5 + 0.5; + _uv1y = ( _vector3.x * cameraMatrix.elements[1] + _vector3.y * cameraMatrix.elements[5] + _vector3.z * cameraMatrix.elements[9] ) * 0.5 + 0.5; + + _vector3.copy( element.vertexNormalsWorld[ uv2 ] ); + _uv2x = ( _vector3.x * cameraMatrix.elements[0] + _vector3.y * cameraMatrix.elements[4] + _vector3.z * cameraMatrix.elements[8] ) * 0.5 + 0.5; + _uv2y = ( _vector3.x * cameraMatrix.elements[1] + _vector3.y * cameraMatrix.elements[5] + _vector3.z * cameraMatrix.elements[9] ) * 0.5 + 0.5; + + _vector3.copy( element.vertexNormalsWorld[ uv3 ] ); + _uv3x = ( _vector3.x * cameraMatrix.elements[0] + _vector3.y * cameraMatrix.elements[4] + _vector3.z * cameraMatrix.elements[8] ) * 0.5 + 0.5; + _uv3y = ( _vector3.x * cameraMatrix.elements[1] + _vector3.y * cameraMatrix.elements[5] + _vector3.z * cameraMatrix.elements[9] ) * 0.5 + 0.5; + + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + + }/* else if ( material.envMap.mapping == THREE.SphericalRefractionMapping ) { + + + + }*/ + + + } else { + + _color.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _color.r *= element.color.r; + _color.g *= element.color.g; + _color.b *= element.color.b; + + } + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _near = camera.near; + _far = camera.far; + + _color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z, _near, _far ); + _color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z, _near, _far ); + _color3.r = _color3.g = _color3.b = 1 - smoothstep( v3.positionScreen.z, _near, _far ); + + _color4.r = ( _color2.r + _color3.r ) * 0.5; + _color4.g = ( _color2.g + _color3.g ) * 0.5; + _color4.b = ( _color2.b + _color3.b ) * 0.5; + + _image = getGradientTexture( _color1, _color2, _color3, _color4 ); + + clipImage( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 0, 0, 1, 0, 0, 1, _image ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _color.r = normalToComponent( element.normalWorld.x ); + _color.g = normalToComponent( element.normalWorld.y ); + _color.b = normalToComponent( element.normalWorld.z ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } + + function renderFace4( v1, v2, v3, v4, v5, v6, element, material, scene ) { + + _this.info.render.vertices += 4; + _this.info.render.faces ++; + + setOpacity( material.opacity ); + setBlending( material.blending ); + + if ( ( material.map !== undefined && material.map !== null ) || ( material.envMap !== undefined && material.envMap !== null ) ) { + + // Let renderFace3() handle this + + renderFace3( v1, v2, v4, 0, 1, 3, element, material, scene ); + renderFace3( v5, v3, v6, 1, 2, 3, element, material, scene ); + + return; + + } + + _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; + _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; + _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + _v4x = v4.positionScreen.x; _v4y = v4.positionScreen.y; + _v5x = v5.positionScreen.x; _v5y = v5.positionScreen.y; + _v6x = v6.positionScreen.x; _v6y = v6.positionScreen.y; + + if ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) { + + _diffuseColor.copy( material.color ); + _emissiveColor.copy( material.emissive ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _diffuseColor.r *= element.color.r; + _diffuseColor.g *= element.color.g; + _diffuseColor.b *= element.color.b; + + } + + if ( _enableLighting === true ) { + + if ( material.wireframe === false && material.shading == THREE.SmoothShading && element.vertexNormalsLength == 4 ) { + + _color1.r = _color2.r = _color3.r = _color4.r = _ambientLight.r; + _color1.g = _color2.g = _color3.g = _color4.g = _ambientLight.g; + _color1.b = _color2.b = _color3.b = _color4.b = _ambientLight.b; + + calculateLight( element.v1.positionWorld, element.vertexNormalsWorld[ 0 ], _color1 ); + calculateLight( element.v2.positionWorld, element.vertexNormalsWorld[ 1 ], _color2 ); + calculateLight( element.v4.positionWorld, element.vertexNormalsWorld[ 3 ], _color3 ); + calculateLight( element.v3.positionWorld, element.vertexNormalsWorld[ 2 ], _color4 ); + + _color1.r = _color1.r * _diffuseColor.r + _emissiveColor.r; + _color1.g = _color1.g * _diffuseColor.g + _emissiveColor.g; + _color1.b = _color1.b * _diffuseColor.b + _emissiveColor.b; + + _color2.r = _color2.r * _diffuseColor.r + _emissiveColor.r; + _color2.g = _color2.g * _diffuseColor.g + _emissiveColor.g; + _color2.b = _color2.b * _diffuseColor.b + _emissiveColor.b; + + _color3.r = _color3.r * _diffuseColor.r + _emissiveColor.r; + _color3.g = _color3.g * _diffuseColor.g + _emissiveColor.g; + _color3.b = _color3.b * _diffuseColor.b + _emissiveColor.b; + + _color4.r = _color4.r * _diffuseColor.r + _emissiveColor.r; + _color4.g = _color4.g * _diffuseColor.g + _emissiveColor.g; + _color4.b = _color4.b * _diffuseColor.b + _emissiveColor.b; + + _image = getGradientTexture( _color1, _color2, _color3, _color4 ); + + // TODO: UVs are incorrect, v4->v3? + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y ); + clipImage( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, 0, 0, 1, 0, 0, 1, _image ); + + drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y ); + clipImage( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, 1, 0, 1, 1, 0, 1, _image ); + + } else { + + _color.r = _ambientLight.r; + _color.g = _ambientLight.g; + _color.b = _ambientLight.b; + + calculateLight( element.centroidWorld, element.normalWorld, _color ); + + _color.r = _color.r * _diffuseColor.r + _emissiveColor.r; + _color.g = _color.g * _diffuseColor.g + _emissiveColor.g; + _color.b = _color.b * _diffuseColor.b + _emissiveColor.b; + + drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } else { + + _color.r = _diffuseColor.r + _emissiveColor.r; + _color.g = _diffuseColor.g + _emissiveColor.g; + _color.b = _diffuseColor.b + _emissiveColor.b; + + drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } else if ( material instanceof THREE.MeshBasicMaterial ) { + + _color.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _color.r *= element.color.r; + _color.g *= element.color.g; + _color.b *= element.color.b; + + } + + drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _color.r = normalToComponent( element.normalWorld.x ); + _color.g = normalToComponent( element.normalWorld.y ); + _color.b = normalToComponent( element.normalWorld.z ); + + drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _near = camera.near; + _far = camera.far; + + _color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z, _near, _far ); + _color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z, _near, _far ); + _color3.r = _color3.g = _color3.b = 1 - smoothstep( v4.positionScreen.z, _near, _far ); + _color4.r = _color4.g = _color4.b = 1 - smoothstep( v3.positionScreen.z, _near, _far ); + + _image = getGradientTexture( _color1, _color2, _color3, _color4 ); + + // TODO: UVs are incorrect, v4->v3? + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y ); + clipImage( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, 0, 0, 1, 0, 0, 1, _image ); + + drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y ); + clipImage( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, 1, 0, 1, 1, 0, 1, _image ); + + } + + } + + // + + function drawTriangle( x0, y0, x1, y1, x2, y2 ) { + + _context.beginPath(); + _context.moveTo( x0, y0 ); + _context.lineTo( x1, y1 ); + _context.lineTo( x2, y2 ); + _context.closePath(); + + } + + function drawQuad( x0, y0, x1, y1, x2, y2, x3, y3 ) { + + _context.beginPath(); + _context.moveTo( x0, y0 ); + _context.lineTo( x1, y1 ); + _context.lineTo( x2, y2 ); + _context.lineTo( x3, y3 ); + _context.closePath(); + + } + + function strokePath( color, linewidth, linecap, linejoin ) { + + setLineWidth( linewidth ); + setLineCap( linecap ); + setLineJoin( linejoin ); + setStrokeStyle( color.getContextStyle() ); + + _context.stroke(); + + _bboxRect.inflate( linewidth * 2 ); + + } + + function fillPath( color ) { + + setFillStyle( color.getContextStyle() ); + _context.fill(); + + } + + function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { + + if ( texture instanceof THREE.DataTexture || texture.image === undefined || texture.image.width == 0 ) return; + + if ( texture.needsUpdate === true ) { + + var repeatX = texture.wrapS == THREE.RepeatWrapping; + var repeatY = texture.wrapT == THREE.RepeatWrapping; + + _patterns[ texture.id ] = _context.createPattern( + texture.image, repeatX === true && repeatY === true + ? 'repeat' + : repeatX === true && repeatY === false + ? 'repeat-x' + : repeatX === false && repeatY === true + ? 'repeat-y' + : 'no-repeat' + ); + + texture.needsUpdate = false; + + } + + _patterns[ texture.id ] === undefined + ? setFillStyle( 'rgba(0,0,0,1)' ) + : setFillStyle( _patterns[ texture.id ] ); + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + offsetX = texture.offset.x / texture.repeat.x, + offsetY = texture.offset.y / texture.repeat.y, + width = texture.image.width * texture.repeat.x, + height = texture.image.height * texture.repeat.y; + + u0 = ( u0 + offsetX ) * width; + v0 = ( 1.0 - v0 + offsetY ) * height; + + u1 = ( u1 + offsetX ) * width; + v1 = ( 1.0 - v1 + offsetY ) * height; + + u2 = ( u2 + offsetX ) * width; + v2 = ( 1.0 - v2 + offsetY ) * height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + if ( det === 0 ) { + + if ( _imagedatas[ texture.id ] === undefined ) { + + var canvas = document.createElement( 'canvas' ) + canvas.width = texture.image.width; + canvas.height = texture.image.height; + + var context = canvas.getContext( '2d' ); + context.drawImage( texture.image, 0, 0 ); + + _imagedatas[ texture.id ] = context.getImageData( 0, 0, texture.image.width, texture.image.height ).data; + + } + + var data = _imagedatas[ texture.id ]; + var index = ( Math.floor( u0 ) + Math.floor( v0 ) * texture.image.width ) * 4; + + _color.setRGB( data[ index ] / 255, data[ index + 1 ] / 255, data[ index + 2 ] / 255 ); + fillPath( _color ); + + return; + + } + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.fill(); + _context.restore(); + + } + + function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + width = image.width - 1, + height = image.height - 1; + + u0 *= width; v0 *= height; + u1 *= width; v1 *= height; + u2 *= width; v2 *= height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.clip(); + _context.drawImage( image, 0, 0 ); + _context.restore(); + + } + + function getGradientTexture( color1, color2, color3, color4 ) { + + // http://mrdoob.com/blog/post/710 + + _pixelMapData[ 0 ] = ( color1.r * 255 ) | 0; + _pixelMapData[ 1 ] = ( color1.g * 255 ) | 0; + _pixelMapData[ 2 ] = ( color1.b * 255 ) | 0; + + _pixelMapData[ 4 ] = ( color2.r * 255 ) | 0; + _pixelMapData[ 5 ] = ( color2.g * 255 ) | 0; + _pixelMapData[ 6 ] = ( color2.b * 255 ) | 0; + + _pixelMapData[ 8 ] = ( color3.r * 255 ) | 0; + _pixelMapData[ 9 ] = ( color3.g * 255 ) | 0; + _pixelMapData[ 10 ] = ( color3.b * 255 ) | 0; + + _pixelMapData[ 12 ] = ( color4.r * 255 ) | 0; + _pixelMapData[ 13 ] = ( color4.g * 255 ) | 0; + _pixelMapData[ 14 ] = ( color4.b * 255 ) | 0; + + _pixelMapContext.putImageData( _pixelMapImage, 0, 0 ); + _gradientMapContext.drawImage( _pixelMap, 0, 0 ); + + return _gradientMap; + + } + + function smoothstep( value, min, max ) { + + var x = ( value - min ) / ( max - min ); + return x * x * ( 3 - 2 * x ); + + } + + function normalToComponent( normal ) { + + var component = ( normal + 1 ) * 0.5; + return component < 0 ? 0 : ( component > 1 ? 1 : component ); + + } + + // Hide anti-alias gaps + + function expand( v1, v2 ) { + + var x = v2.x - v1.x, y = v2.y - v1.y, + det = x * x + y * y, idet; + + if ( det === 0 ) return; + + idet = 1 / Math.sqrt( det ); + + x *= idet; y *= idet; + + v2.x += x; v2.y += y; + v1.x -= x; v1.y -= y; + + } + }; + + // Context cached methods. + + function setOpacity( value ) { + + if ( _contextGlobalAlpha !== value ) { + + _context.globalAlpha = value; + _contextGlobalAlpha = value; + + } + + } + + function setBlending( value ) { + + if ( _contextGlobalCompositeOperation !== value ) { + + if ( value === THREE.NormalBlending ) { + + _context.globalCompositeOperation = 'source-over'; + + } else if ( value === THREE.AdditiveBlending ) { + + _context.globalCompositeOperation = 'lighter'; + + } else if ( value === THREE.SubtractiveBlending ) { + + _context.globalCompositeOperation = 'darker'; + + } + + _contextGlobalCompositeOperation = value; + + } + + } + + function setLineWidth( value ) { + + if ( _contextLineWidth !== value ) { + + _context.lineWidth = value; + _contextLineWidth = value; + + } + + } + + function setLineCap( value ) { + + // "butt", "round", "square" + + if ( _contextLineCap !== value ) { + + _context.lineCap = value; + _contextLineCap = value; + + } + + } + + function setLineJoin( value ) { + + // "round", "bevel", "miter" + + if ( _contextLineJoin !== value ) { + + _context.lineJoin = value; + _contextLineJoin = value; + + } + + } + + function setStrokeStyle( value ) { + + if ( _contextStrokeStyle !== value ) { + + _context.strokeStyle = value; + _contextStrokeStyle = value; + + } + + } + + function setFillStyle( value ) { + + if ( _contextFillStyle !== value ) { + + _context.fillStyle = value; + _contextFillStyle = value; + + } + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.ShaderChunk = { + + // FOG + + fog_pars_fragment: [ + + "#ifdef USE_FOG", + + "uniform vec3 fogColor;", + + "#ifdef FOG_EXP2", + + "uniform float fogDensity;", + + "#else", + + "uniform float fogNear;", + "uniform float fogFar;", + + "#endif", + + "#endif" + + ].join("\n"), + + fog_fragment: [ + + "#ifdef USE_FOG", + + "float depth = gl_FragCoord.z / gl_FragCoord.w;", + + "#ifdef FOG_EXP2", + + "const float LOG2 = 1.442695;", + "float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );", + "fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );", + + "#else", + + "float fogFactor = smoothstep( fogNear, fogFar, depth );", + + "#endif", + + "gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );", + + "#endif" + + ].join("\n"), + + // ENVIRONMENT MAP + + envmap_pars_fragment: [ + + "#ifdef USE_ENVMAP", + + "uniform float reflectivity;", + "uniform samplerCube envMap;", + "uniform float flipEnvMap;", + "uniform int combine;", + + "#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )", + + "uniform bool useRefract;", + "uniform float refractionRatio;", + + "#else", + + "varying vec3 vReflect;", + + "#endif", + + "#endif" + + ].join("\n"), + + envmap_fragment: [ + + "#ifdef USE_ENVMAP", + + "vec3 reflectVec;", + + "#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )", + + "vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );", + + "if ( useRefract ) {", + + "reflectVec = refract( cameraToVertex, normal, refractionRatio );", + + "} else { ", + + "reflectVec = reflect( cameraToVertex, normal );", + + "}", + + "#else", + + "reflectVec = vReflect;", + + "#endif", + + "#ifdef DOUBLE_SIDED", + + "float flipNormal = ( -1.0 + 2.0 * float( gl_FrontFacing ) );", + "vec4 cubeColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );", + + "#else", + + "vec4 cubeColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );", + + "#endif", + + "#ifdef GAMMA_INPUT", + + "cubeColor.xyz *= cubeColor.xyz;", + + "#endif", + + "if ( combine == 1 ) {", + + "gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularStrength * reflectivity );", + + "} else {", + + "gl_FragColor.xyz = mix( gl_FragColor.xyz, gl_FragColor.xyz * cubeColor.xyz, specularStrength * reflectivity );", + + "}", + + "#endif" + + ].join("\n"), + + envmap_pars_vertex: [ + + "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )", + + "varying vec3 vReflect;", + + "uniform float refractionRatio;", + "uniform bool useRefract;", + + "#endif" + + ].join("\n"), + + worldpos_vertex : [ + + "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )", + + "#ifdef USE_SKINNING", + + "vec4 mPosition = modelMatrix * skinned;", + + "#endif", + + "#if defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )", + + "vec4 mPosition = modelMatrix * vec4( morphed, 1.0 );", + + "#endif", + + "#if ! defined( USE_MORPHTARGETS ) && ! defined( USE_SKINNING )", + + "vec4 mPosition = modelMatrix * vec4( position, 1.0 );", + + "#endif", + + "#endif" + + ].join("\n"), + + envmap_vertex : [ + + "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP )", + + "vec3 nWorld = mat3( modelMatrix[ 0 ].xyz, modelMatrix[ 1 ].xyz, modelMatrix[ 2 ].xyz ) * objectNormal;", + + "if ( useRefract ) {", + + "vReflect = refract( normalize( mPosition.xyz - cameraPosition ), normalize( nWorld.xyz ), refractionRatio );", + + "} else {", + + "vReflect = reflect( normalize( mPosition.xyz - cameraPosition ), normalize( nWorld.xyz ) );", + + "}", + + "#endif" + + ].join("\n"), + + // COLOR MAP (particles) + + map_particle_pars_fragment: [ + + "#ifdef USE_MAP", + + "uniform sampler2D map;", + + "#endif" + + ].join("\n"), + + + map_particle_fragment: [ + + "#ifdef USE_MAP", + + "gl_FragColor = gl_FragColor * texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );", + + "#endif" + + ].join("\n"), + + // COLOR MAP (triangles) + + map_pars_vertex: [ + + "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )", + + "varying vec2 vUv;", + "uniform vec4 offsetRepeat;", + + "#endif" + + ].join("\n"), + + map_pars_fragment: [ + + "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )", + + "varying vec2 vUv;", + + "#endif", + + "#ifdef USE_MAP", + + "uniform sampler2D map;", + + "#endif", + + ].join("\n"), + + map_vertex: [ + + "#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP )", + + "vUv = uv * offsetRepeat.zw + offsetRepeat.xy;", + + "#endif" + + ].join("\n"), + + map_fragment: [ + + "#ifdef USE_MAP", + + "#ifdef GAMMA_INPUT", + + "vec4 texelColor = texture2D( map, vUv );", + "texelColor.xyz *= texelColor.xyz;", + + "gl_FragColor = gl_FragColor * texelColor;", + + "#else", + + "gl_FragColor = gl_FragColor * texture2D( map, vUv );", + + "#endif", + + "#endif" + + ].join("\n"), + + // LIGHT MAP + + lightmap_pars_fragment: [ + + "#ifdef USE_LIGHTMAP", + + "varying vec2 vUv2;", + "uniform sampler2D lightMap;", + + "#endif" + + ].join("\n"), + + lightmap_pars_vertex: [ + + "#ifdef USE_LIGHTMAP", + + "varying vec2 vUv2;", + + "#endif" + + ].join("\n"), + + lightmap_fragment: [ + + "#ifdef USE_LIGHTMAP", + + "gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );", + + "#endif" + + ].join("\n"), + + lightmap_vertex: [ + + "#ifdef USE_LIGHTMAP", + + "vUv2 = uv2;", + + "#endif" + + ].join("\n"), + + // BUMP MAP + + bumpmap_pars_fragment: [ + + "#ifdef USE_BUMPMAP", + + "uniform sampler2D bumpMap;", + "uniform float bumpScale;", + + // Derivative maps - bump mapping unparametrized surfaces by Morten Mikkelsen + // http://mmikkelsen3d.blogspot.sk/2011/07/derivative-maps.html + + // Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2) + + "vec2 dHdxy_fwd() {", + + "vec2 dSTdx = dFdx( vUv );", + "vec2 dSTdy = dFdy( vUv );", + + "float Hll = bumpScale * texture2D( bumpMap, vUv ).x;", + "float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;", + "float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;", + + "return vec2( dBx, dBy );", + + "}", + + "vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {", + + "vec3 vSigmaX = dFdx( surf_pos );", + "vec3 vSigmaY = dFdy( surf_pos );", + "vec3 vN = surf_norm;", // normalized + + "vec3 R1 = cross( vSigmaY, vN );", + "vec3 R2 = cross( vN, vSigmaX );", + + "float fDet = dot( vSigmaX, R1 );", + + "vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );", + "return normalize( abs( fDet ) * surf_norm - vGrad );", + + "}", + + "#endif" + + ].join("\n"), + + // NORMAL MAP + + normalmap_pars_fragment: [ + + "#ifdef USE_NORMALMAP", + + "uniform sampler2D normalMap;", + "uniform vec2 normalScale;", + + // Per-Pixel Tangent Space Normal Mapping + // http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html + + "vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {", + + "vec3 q0 = dFdx( eye_pos.xyz );", + "vec3 q1 = dFdy( eye_pos.xyz );", + "vec2 st0 = dFdx( vUv.st );", + "vec2 st1 = dFdy( vUv.st );", + + "vec3 S = normalize( q0 * st1.t - q1 * st0.t );", + "vec3 T = normalize( -q0 * st1.s + q1 * st0.s );", + "vec3 N = normalize( surf_norm );", + + "vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;", + "mapN.xy = normalScale * mapN.xy;", + "mat3 tsn = mat3( S, T, N );", + "return normalize( tsn * mapN );", + + "}", + + "#endif" + + ].join("\n"), + + // SPECULAR MAP + + specularmap_pars_fragment: [ + + "#ifdef USE_SPECULARMAP", + + "uniform sampler2D specularMap;", + + "#endif" + + ].join("\n"), + + specularmap_fragment: [ + + "float specularStrength;", + + "#ifdef USE_SPECULARMAP", + + "vec4 texelSpecular = texture2D( specularMap, vUv );", + "specularStrength = texelSpecular.r;", + + "#else", + + "specularStrength = 1.0;", + + "#endif" + + ].join("\n"), + + // LIGHTS LAMBERT + + lights_lambert_pars_vertex: [ + + "uniform vec3 ambient;", + "uniform vec3 diffuse;", + "uniform vec3 emissive;", + + "uniform vec3 ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + + "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightPosition[ MAX_HEMI_LIGHTS ];", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", + "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", + "uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightAngle[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", + + "#endif", + + "#ifdef WRAP_AROUND", + + "uniform vec3 wrapRGB;", + + "#endif" + + ].join("\n"), + + lights_lambert_vertex: [ + + "vLightFront = vec3( 0.0 );", + + "#ifdef DOUBLE_SIDED", + + "vLightBack = vec3( 0.0 );", + + "#endif", + + "transformedNormal = normalize( transformedNormal );", + + "#if MAX_DIR_LIGHTS > 0", + + "for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {", + + "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", + "vec3 dirVector = normalize( lDirection.xyz );", + + "float dotProduct = dot( transformedNormal, dirVector );", + "vec3 directionalLightWeighting = vec3( max( dotProduct, 0.0 ) );", + + "#ifdef DOUBLE_SIDED", + + "vec3 directionalLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );", + + "#ifdef WRAP_AROUND", + + "vec3 directionalLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );", + + "#endif", + + "#endif", + + "#ifdef WRAP_AROUND", + + "vec3 directionalLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );", + "directionalLightWeighting = mix( directionalLightWeighting, directionalLightWeightingHalf, wrapRGB );", + + "#ifdef DOUBLE_SIDED", + + "directionalLightWeightingBack = mix( directionalLightWeightingBack, directionalLightWeightingHalfBack, wrapRGB );", + + "#endif", + + "#endif", + + "vLightFront += directionalLightColor[ i ] * directionalLightWeighting;", + + "#ifdef DOUBLE_SIDED", + + "vLightBack += directionalLightColor[ i ] * directionalLightWeightingBack;", + + "#endif", + + "}", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", + "vec3 lVector = lPosition.xyz - mvPosition.xyz;", + + "float lDistance = 1.0;", + "if ( pointLightDistance[ i ] > 0.0 )", + "lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );", + + "lVector = normalize( lVector );", + "float dotProduct = dot( transformedNormal, lVector );", + + "vec3 pointLightWeighting = vec3( max( dotProduct, 0.0 ) );", + + "#ifdef DOUBLE_SIDED", + + "vec3 pointLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );", + + "#ifdef WRAP_AROUND", + + "vec3 pointLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );", + + "#endif", + + "#endif", + + "#ifdef WRAP_AROUND", + + "vec3 pointLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );", + "pointLightWeighting = mix( pointLightWeighting, pointLightWeightingHalf, wrapRGB );", + + "#ifdef DOUBLE_SIDED", + + "pointLightWeightingBack = mix( pointLightWeightingBack, pointLightWeightingHalfBack, wrapRGB );", + + "#endif", + + "#endif", + + "vLightFront += pointLightColor[ i ] * pointLightWeighting * lDistance;", + + "#ifdef DOUBLE_SIDED", + + "vLightBack += pointLightColor[ i ] * pointLightWeightingBack * lDistance;", + + "#endif", + + "}", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", + "vec3 lVector = lPosition.xyz - mvPosition.xyz;", + + "lVector = normalize( lVector );", + + "float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - mPosition.xyz ) );", + + "if ( spotEffect > spotLightAngle[ i ] ) {", + + "spotEffect = pow( spotEffect, spotLightExponent[ i ] );", + + "float lDistance = 1.0;", + "if ( spotLightDistance[ i ] > 0.0 )", + "lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );", + + "float dotProduct = dot( transformedNormal, lVector );", + "vec3 spotLightWeighting = vec3( max( dotProduct, 0.0 ) );", + + "#ifdef DOUBLE_SIDED", + + "vec3 spotLightWeightingBack = vec3( max( -dotProduct, 0.0 ) );", + + "#ifdef WRAP_AROUND", + + "vec3 spotLightWeightingHalfBack = vec3( max( -0.5 * dotProduct + 0.5, 0.0 ) );", + + "#endif", + + "#endif", + + "#ifdef WRAP_AROUND", + + "vec3 spotLightWeightingHalf = vec3( max( 0.5 * dotProduct + 0.5, 0.0 ) );", + "spotLightWeighting = mix( spotLightWeighting, spotLightWeightingHalf, wrapRGB );", + + "#ifdef DOUBLE_SIDED", + + "spotLightWeightingBack = mix( spotLightWeightingBack, spotLightWeightingHalfBack, wrapRGB );", + + "#endif", + + "#endif", + + "vLightFront += spotLightColor[ i ] * spotLightWeighting * lDistance * spotEffect;", + + "#ifdef DOUBLE_SIDED", + + "vLightBack += spotLightColor[ i ] * spotLightWeightingBack * lDistance * spotEffect;", + + "#endif", + + "}", + + "}", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( hemisphereLightPosition[ i ], 1.0 );", + "vec3 lVector = lPosition.xyz - mvPosition.xyz;", + + "lVector = normalize( lVector );", + + "float dotProduct = dot( transformedNormal, lVector );", + + "float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + "float hemiDiffuseWeightBack = -0.5 * dotProduct + 0.5;", + + "vLightFront += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + + "#ifdef DOUBLE_SIDED", + + "vLightBack += mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeightBack );", + + "#endif", + + "}", + + "#endif", + + "vLightFront = vLightFront * diffuse + ambient * ambientLightColor + emissive;", + + "#ifdef DOUBLE_SIDED", + + "vLightBack = vLightBack * diffuse + ambient * ambientLightColor + emissive;", + + "#endif" + + ].join("\n"), + + // LIGHTS PHONG + + lights_phong_pars_vertex: [ + + "#ifndef PHONG_PER_PIXEL", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + + "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", + + "varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];", + + "#endif", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )", + + "varying vec3 vWorldPosition;", + + "#endif" + + ].join("\n"), + + + lights_phong_vertex: [ + + "#ifndef PHONG_PER_PIXEL", + + "#if MAX_POINT_LIGHTS > 0", + + "for( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", + "vec3 lVector = lPosition.xyz - mvPosition.xyz;", + + "float lDistance = 1.0;", + "if ( pointLightDistance[ i ] > 0.0 )", + "lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );", + + "vPointLight[ i ] = vec4( lVector, lDistance );", + + "}", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "for( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", + "vec3 lVector = lPosition.xyz - mvPosition.xyz;", + + "float lDistance = 1.0;", + "if ( spotLightDistance[ i ] > 0.0 )", + "lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );", + + "vSpotLight[ i ] = vec4( lVector, lDistance );", + + "}", + + "#endif", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )", + + "vWorldPosition = mPosition.xyz;", + + "#endif" + + ].join("\n"), + + lights_phong_pars_fragment: [ + + "uniform vec3 ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + + "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightPosition[ MAX_HEMI_LIGHTS ];", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + + "#ifdef PHONG_PER_PIXEL", + + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + + "#else", + + "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + + "#endif", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", + "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", + "uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightAngle[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", + + "#ifdef PHONG_PER_PIXEL", + + "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", + + "#else", + + "varying vec4 vSpotLight[ MAX_SPOT_LIGHTS ];", + + "#endif", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0 || defined( USE_BUMPMAP )", + + "varying vec3 vWorldPosition;", + + "#endif", + + "#ifdef WRAP_AROUND", + + "uniform vec3 wrapRGB;", + + "#endif", + + "varying vec3 vViewPosition;", + "varying vec3 vNormal;" + + ].join("\n"), + + lights_phong_fragment: [ + + "vec3 normal = normalize( vNormal );", + "vec3 viewPosition = normalize( vViewPosition );", + + "#ifdef DOUBLE_SIDED", + + "normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );", + + "#endif", + + "#ifdef USE_NORMALMAP", + + "normal = perturbNormal2Arb( -viewPosition, normal );", + + "#elif defined( USE_BUMPMAP )", + + "normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "vec3 pointDiffuse = vec3( 0.0 );", + "vec3 pointSpecular = vec3( 0.0 );", + + "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + "#ifdef PHONG_PER_PIXEL", + + "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", + "vec3 lVector = lPosition.xyz + vViewPosition.xyz;", + + "float lDistance = 1.0;", + "if ( pointLightDistance[ i ] > 0.0 )", + "lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );", + + "lVector = normalize( lVector );", + + "#else", + + "vec3 lVector = normalize( vPointLight[ i ].xyz );", + "float lDistance = vPointLight[ i ].w;", + + "#endif", + + // diffuse + + "float dotProduct = dot( normal, lVector );", + + "#ifdef WRAP_AROUND", + + "float pointDiffuseWeightFull = max( dotProduct, 0.0 );", + "float pointDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );", + + "vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );", + + "#else", + + "float pointDiffuseWeight = max( dotProduct, 0.0 );", + + "#endif", + + "pointDiffuse += diffuse * pointLightColor[ i ] * pointDiffuseWeight * lDistance;", + + // specular + + "vec3 pointHalfVector = normalize( lVector + viewPosition );", + "float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", + "float pointSpecularWeight = specularStrength * max( pow( pointDotNormalHalf, shininess ), 0.0 );", + + "#ifdef PHYSICALLY_BASED_SHADING", + + // 2.0 => 2.0001 is hack to work around ANGLE bug + + "float specularNormalization = ( shininess + 2.0001 ) / 8.0;", + + "vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, pointHalfVector ), 5.0 );", + "pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance * specularNormalization;", + + "#else", + + "pointSpecular += specular * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * lDistance;", + + "#endif", + + "}", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "vec3 spotDiffuse = vec3( 0.0 );", + "vec3 spotSpecular = vec3( 0.0 );", + + "for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + + "#ifdef PHONG_PER_PIXEL", + + "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", + "vec3 lVector = lPosition.xyz + vViewPosition.xyz;", + + "float lDistance = 1.0;", + "if ( spotLightDistance[ i ] > 0.0 )", + "lDistance = 1.0 - min( ( length( lVector ) / spotLightDistance[ i ] ), 1.0 );", + + "lVector = normalize( lVector );", + + "#else", + + "vec3 lVector = normalize( vSpotLight[ i ].xyz );", + "float lDistance = vSpotLight[ i ].w;", + + "#endif", + + "float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );", + + "if ( spotEffect > spotLightAngle[ i ] ) {", + + "spotEffect = pow( spotEffect, spotLightExponent[ i ] );", + + // diffuse + + "float dotProduct = dot( normal, lVector );", + + "#ifdef WRAP_AROUND", + + "float spotDiffuseWeightFull = max( dotProduct, 0.0 );", + "float spotDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );", + + "vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );", + + "#else", + + "float spotDiffuseWeight = max( dotProduct, 0.0 );", + + "#endif", + + "spotDiffuse += diffuse * spotLightColor[ i ] * spotDiffuseWeight * lDistance * spotEffect;", + + // specular + + "vec3 spotHalfVector = normalize( lVector + viewPosition );", + "float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );", + "float spotSpecularWeight = specularStrength * max( pow( spotDotNormalHalf, shininess ), 0.0 );", + + "#ifdef PHYSICALLY_BASED_SHADING", + + // 2.0 => 2.0001 is hack to work around ANGLE bug + + "float specularNormalization = ( shininess + 2.0001 ) / 8.0;", + + "vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, spotHalfVector ), 5.0 );", + "spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * specularNormalization * spotEffect;", + + "#else", + + "spotSpecular += specular * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * lDistance * spotEffect;", + + "#endif", + + "}", + + "}", + + "#endif", + + "#if MAX_DIR_LIGHTS > 0", + + "vec3 dirDiffuse = vec3( 0.0 );", + "vec3 dirSpecular = vec3( 0.0 );" , + + "for( int i = 0; i < MAX_DIR_LIGHTS; i ++ ) {", + + "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", + "vec3 dirVector = normalize( lDirection.xyz );", + + // diffuse + + "float dotProduct = dot( normal, dirVector );", + + "#ifdef WRAP_AROUND", + + "float dirDiffuseWeightFull = max( dotProduct, 0.0 );", + "float dirDiffuseWeightHalf = max( 0.5 * dotProduct + 0.5, 0.0 );", + + "vec3 dirDiffuseWeight = mix( vec3( dirDiffuseWeightFull ), vec3( dirDiffuseWeightHalf ), wrapRGB );", + + "#else", + + "float dirDiffuseWeight = max( dotProduct, 0.0 );", + + "#endif", + + "dirDiffuse += diffuse * directionalLightColor[ i ] * dirDiffuseWeight;", + + // specular + + "vec3 dirHalfVector = normalize( dirVector + viewPosition );", + "float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", + "float dirSpecularWeight = specularStrength * max( pow( dirDotNormalHalf, shininess ), 0.0 );", + + "#ifdef PHYSICALLY_BASED_SHADING", + + /* + // fresnel term from skin shader + "const float F0 = 0.128;", + + "float base = 1.0 - dot( viewPosition, dirHalfVector );", + "float exponential = pow( base, 5.0 );", + + "float fresnel = exponential + F0 * ( 1.0 - exponential );", + */ + + /* + // fresnel term from fresnel shader + "const float mFresnelBias = 0.08;", + "const float mFresnelScale = 0.3;", + "const float mFresnelPower = 5.0;", + + "float fresnel = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( -viewPosition ), normal ), mFresnelPower );", + */ + + // 2.0 => 2.0001 is hack to work around ANGLE bug + + "float specularNormalization = ( shininess + 2.0001 ) / 8.0;", + + //"dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization * fresnel;", + + "vec3 schlick = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );", + "dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;", + + "#else", + + "dirSpecular += specular * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight;", + + "#endif", + + "}", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "vec3 hemiDiffuse = vec3( 0.0 );", + "vec3 hemiSpecular = vec3( 0.0 );" , + + "for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( hemisphereLightPosition[ i ], 1.0 );", + "vec3 lVector = normalize( lPosition.xyz + vViewPosition.xyz );", + + // diffuse + + "float dotProduct = dot( normal, lVector );", + "float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + + "vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + + "hemiDiffuse += diffuse * hemiColor;", + + // specular (sky light) + + "vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );", + "float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;", + "float hemiSpecularWeightSky = specularStrength * max( pow( hemiDotNormalHalfSky, shininess ), 0.0 );", + + // specular (ground light) + + "vec3 lVectorGround = normalize( -lPosition.xyz + vViewPosition.xyz );", + + "vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );", + "float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;", + "float hemiSpecularWeightGround = specularStrength * max( pow( hemiDotNormalHalfGround, shininess ), 0.0 );", + + "#ifdef PHYSICALLY_BASED_SHADING", + + "float dotProductGround = dot( normal, lVectorGround );", + + // 2.0 => 2.0001 is hack to work around ANGLE bug + + "float specularNormalization = ( shininess + 2.0001 ) / 8.0;", + + "vec3 schlickSky = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );", + "vec3 schlickGround = specular + vec3( 1.0 - specular ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );", + "hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );", + + "#else", + + "hemiSpecular += specular * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;", + + "#endif", + + "}", + + "#endif", + + "vec3 totalDiffuse = vec3( 0.0 );", + "vec3 totalSpecular = vec3( 0.0 );", + + "#if MAX_DIR_LIGHTS > 0", + + "totalDiffuse += dirDiffuse;", + "totalSpecular += dirSpecular;", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "totalDiffuse += hemiDiffuse;", + "totalSpecular += hemiSpecular;", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "totalDiffuse += pointDiffuse;", + "totalSpecular += pointSpecular;", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "totalDiffuse += spotDiffuse;", + "totalSpecular += spotSpecular;", + + "#endif", + + "#ifdef METAL", + + "gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient + totalSpecular );", + + "#else", + + "gl_FragColor.xyz = gl_FragColor.xyz * ( emissive + totalDiffuse + ambientLightColor * ambient ) + totalSpecular;", + + "#endif" + + ].join("\n"), + + // VERTEX COLORS + + color_pars_fragment: [ + + "#ifdef USE_COLOR", + + "varying vec3 vColor;", + + "#endif" + + ].join("\n"), + + + color_fragment: [ + + "#ifdef USE_COLOR", + + "gl_FragColor = gl_FragColor * vec4( vColor, opacity );", + + "#endif" + + ].join("\n"), + + color_pars_vertex: [ + + "#ifdef USE_COLOR", + + "varying vec3 vColor;", + + "#endif" + + ].join("\n"), + + + color_vertex: [ + + "#ifdef USE_COLOR", + + "#ifdef GAMMA_INPUT", + + "vColor = color * color;", + + "#else", + + "vColor = color;", + + "#endif", + + "#endif" + + ].join("\n"), + + // SKINNING + + skinning_pars_vertex: [ + + "#ifdef USE_SKINNING", + + "#ifdef BONE_TEXTURE", + + "uniform sampler2D boneTexture;", + + "mat4 getBoneMatrix( const in float i ) {", + + "float j = i * 4.0;", + "float x = mod( j, N_BONE_PIXEL_X );", + "float y = floor( j / N_BONE_PIXEL_X );", + + "const float dx = 1.0 / N_BONE_PIXEL_X;", + "const float dy = 1.0 / N_BONE_PIXEL_Y;", + + "y = dy * ( y + 0.5 );", + + "vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );", + "vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );", + "vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );", + "vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );", + + "mat4 bone = mat4( v1, v2, v3, v4 );", + + "return bone;", + + "}", + + "#else", + + "uniform mat4 boneGlobalMatrices[ MAX_BONES ];", + + "mat4 getBoneMatrix( const in float i ) {", + + "mat4 bone = boneGlobalMatrices[ int(i) ];", + "return bone;", + + "}", + + "#endif", + + "#endif" + + ].join("\n"), + + skinbase_vertex: [ + + "#ifdef USE_SKINNING", + + "mat4 boneMatX = getBoneMatrix( skinIndex.x );", + "mat4 boneMatY = getBoneMatrix( skinIndex.y );", + + "#endif" + + ].join("\n"), + + skinning_vertex: [ + + "#ifdef USE_SKINNING", + + "#ifdef USE_MORPHTARGETS", + + "vec4 skinVertex = vec4( morphed, 1.0 );", + + "#else", + + "vec4 skinVertex = vec4( position, 1.0 );", + + "#endif", + + "vec4 skinned = boneMatX * skinVertex * skinWeight.x;", + "skinned += boneMatY * skinVertex * skinWeight.y;", + + "#endif" + + ].join("\n"), + + // MORPHING + + morphtarget_pars_vertex: [ + + "#ifdef USE_MORPHTARGETS", + + "#ifndef USE_MORPHNORMALS", + + "uniform float morphTargetInfluences[ 8 ];", + + "#else", + + "uniform float morphTargetInfluences[ 4 ];", + + "#endif", + + "#endif" + + ].join("\n"), + + morphtarget_vertex: [ + + "#ifdef USE_MORPHTARGETS", + + "vec3 morphed = vec3( 0.0 );", + "morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];", + "morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];", + "morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];", + "morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];", + + "#ifndef USE_MORPHNORMALS", + + "morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];", + "morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];", + "morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];", + "morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];", + + "#endif", + + "morphed += position;", + + "#endif" + + ].join("\n"), + + default_vertex : [ + + "vec4 mvPosition;", + + "#ifdef USE_SKINNING", + + "mvPosition = modelViewMatrix * skinned;", + + "#endif", + + "#if !defined( USE_SKINNING ) && defined( USE_MORPHTARGETS )", + + "mvPosition = modelViewMatrix * vec4( morphed, 1.0 );", + + "#endif", + + "#if !defined( USE_SKINNING ) && ! defined( USE_MORPHTARGETS )", + + "mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + "#endif", + + "gl_Position = projectionMatrix * mvPosition;", + + ].join("\n"), + + morphnormal_vertex: [ + + "#ifdef USE_MORPHNORMALS", + + "vec3 morphedNormal = vec3( 0.0 );", + + "morphedNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];", + "morphedNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];", + "morphedNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];", + "morphedNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];", + + "morphedNormal += normal;", + + "#endif" + + ].join("\n"), + + skinnormal_vertex: [ + + "#ifdef USE_SKINNING", + + "mat4 skinMatrix = skinWeight.x * boneMatX;", + "skinMatrix += skinWeight.y * boneMatY;", + + "#ifdef USE_MORPHNORMALS", + + "vec4 skinnedNormal = skinMatrix * vec4( morphedNormal, 0.0 );", + + "#else", + + "vec4 skinnedNormal = skinMatrix * vec4( normal, 0.0 );", + + "#endif", + + "#endif" + + ].join("\n"), + + defaultnormal_vertex: [ + + "vec3 objectNormal;", + + "#ifdef USE_SKINNING", + + "objectNormal = skinnedNormal.xyz;", + + "#endif", + + "#if !defined( USE_SKINNING ) && defined( USE_MORPHNORMALS )", + + "objectNormal = morphedNormal;", + + "#endif", + + "#if !defined( USE_SKINNING ) && ! defined( USE_MORPHNORMALS )", + + "objectNormal = normal;", + + "#endif", + + "#ifdef FLIP_SIDED", + + "objectNormal = -objectNormal;", + + "#endif", + + "vec3 transformedNormal = normalMatrix * objectNormal;", + + ].join("\n"), + + // SHADOW MAP + + // based on SpiderGL shadow map and Fabien Sanglard's GLSL shadow mapping examples + // http://spidergl.org/example.php?id=6 + // http://fabiensanglard.net/shadowmapping + + shadowmap_pars_fragment: [ + + "#ifdef USE_SHADOWMAP", + + "uniform sampler2D shadowMap[ MAX_SHADOWS ];", + "uniform vec2 shadowMapSize[ MAX_SHADOWS ];", + + "uniform float shadowDarkness[ MAX_SHADOWS ];", + "uniform float shadowBias[ MAX_SHADOWS ];", + + "varying vec4 vShadowCoord[ MAX_SHADOWS ];", + + "float unpackDepth( const in vec4 rgba_depth ) {", + + "const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );", + "float depth = dot( rgba_depth, bit_shift );", + "return depth;", + + "}", + + "#endif" + + ].join("\n"), + + shadowmap_fragment: [ + + "#ifdef USE_SHADOWMAP", + + "#ifdef SHADOWMAP_DEBUG", + + "vec3 frustumColors[3];", + "frustumColors[0] = vec3( 1.0, 0.5, 0.0 );", + "frustumColors[1] = vec3( 0.0, 1.0, 0.8 );", + "frustumColors[2] = vec3( 0.0, 0.5, 1.0 );", + + "#endif", + + "#ifdef SHADOWMAP_CASCADE", + + "int inFrustumCount = 0;", + + "#endif", + + "float fDepth;", + "vec3 shadowColor = vec3( 1.0 );", + + "for( int i = 0; i < MAX_SHADOWS; i ++ ) {", + + "vec3 shadowCoord = vShadowCoord[ i ].xyz / vShadowCoord[ i ].w;", + + // "if ( something && something )" breaks ATI OpenGL shader compiler + // "if ( all( something, something ) )" using this instead + + "bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );", + "bool inFrustum = all( inFrustumVec );", + + // don't shadow pixels outside of light frustum + // use just first frustum (for cascades) + // don't shadow pixels behind far plane of light frustum + + "#ifdef SHADOWMAP_CASCADE", + + "inFrustumCount += int( inFrustum );", + "bvec3 frustumTestVec = bvec3( inFrustum, inFrustumCount == 1, shadowCoord.z <= 1.0 );", + + "#else", + + "bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );", + + "#endif", + + "bool frustumTest = all( frustumTestVec );", + + "if ( frustumTest ) {", + + "shadowCoord.z += shadowBias[ i ];", + + "#ifdef SHADOWMAP_SOFT", + + // Percentage-close filtering + // (9 pixel kernel) + // http://fabiensanglard.net/shadowmappingPCF/ + + "float shadow = 0.0;", + + /* + // nested loops breaks shader compiler / validator on some ATI cards when using OpenGL + // must enroll loop manually + + "for ( float y = -1.25; y <= 1.25; y += 1.25 )", + "for ( float x = -1.25; x <= 1.25; x += 1.25 ) {", + + "vec4 rgbaDepth = texture2D( shadowMap[ i ], vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy );", + + // doesn't seem to produce any noticeable visual difference compared to simple "texture2D" lookup + //"vec4 rgbaDepth = texture2DProj( shadowMap[ i ], vec4( vShadowCoord[ i ].w * ( vec2( x * xPixelOffset, y * yPixelOffset ) + shadowCoord.xy ), 0.05, vShadowCoord[ i ].w ) );", + + "float fDepth = unpackDepth( rgbaDepth );", + + "if ( fDepth < shadowCoord.z )", + "shadow += 1.0;", + + "}", + + "shadow /= 9.0;", + + */ + + "const float shadowDelta = 1.0 / 9.0;", + + "float xPixelOffset = 1.0 / shadowMapSize[ i ].x;", + "float yPixelOffset = 1.0 / shadowMapSize[ i ].y;", + + "float dx0 = -1.25 * xPixelOffset;", + "float dy0 = -1.25 * yPixelOffset;", + "float dx1 = 1.25 * xPixelOffset;", + "float dy1 = 1.25 * yPixelOffset;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy0 ) ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy0 ) ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy0 ) ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, 0.0 ) ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, 0.0 ) ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx0, dy1 ) ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( 0.0, dy1 ) ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "fDepth = unpackDepth( texture2D( shadowMap[ i ], shadowCoord.xy + vec2( dx1, dy1 ) ) );", + "if ( fDepth < shadowCoord.z ) shadow += shadowDelta;", + + "shadowColor = shadowColor * vec3( ( 1.0 - shadowDarkness[ i ] * shadow ) );", + + "#else", + + "vec4 rgbaDepth = texture2D( shadowMap[ i ], shadowCoord.xy );", + "float fDepth = unpackDepth( rgbaDepth );", + + "if ( fDepth < shadowCoord.z )", + + // spot with multiple shadows is darker + + "shadowColor = shadowColor * vec3( 1.0 - shadowDarkness[ i ] );", + + // spot with multiple shadows has the same color as single shadow spot + + //"shadowColor = min( shadowColor, vec3( shadowDarkness[ i ] ) );", + + "#endif", + + "}", + + + "#ifdef SHADOWMAP_DEBUG", + + "#ifdef SHADOWMAP_CASCADE", + + "if ( inFrustum && inFrustumCount == 1 ) gl_FragColor.xyz *= frustumColors[ i ];", + + "#else", + + "if ( inFrustum ) gl_FragColor.xyz *= frustumColors[ i ];", + + "#endif", + + "#endif", + + "}", + + "#ifdef GAMMA_OUTPUT", + + "shadowColor *= shadowColor;", + + "#endif", + + "gl_FragColor.xyz = gl_FragColor.xyz * shadowColor;", + + "#endif" + + ].join("\n"), + + shadowmap_pars_vertex: [ + + "#ifdef USE_SHADOWMAP", + + "varying vec4 vShadowCoord[ MAX_SHADOWS ];", + "uniform mat4 shadowMatrix[ MAX_SHADOWS ];", + + "#endif" + + ].join("\n"), + + shadowmap_vertex: [ + + "#ifdef USE_SHADOWMAP", + + "for( int i = 0; i < MAX_SHADOWS; i ++ ) {", + + "vShadowCoord[ i ] = shadowMatrix[ i ] * mPosition;", + + "}", + + "#endif" + + ].join("\n"), + + // ALPHATEST + + alphatest_fragment: [ + + "#ifdef ALPHATEST", + + "if ( gl_FragColor.a < ALPHATEST ) discard;", + + "#endif" + + ].join("\n"), + + // LINEAR SPACE + + linear_to_gamma_fragment: [ + + "#ifdef GAMMA_OUTPUT", + + "gl_FragColor.xyz = sqrt( gl_FragColor.xyz );", + + "#endif" + + ].join("\n"), + + +}; + +THREE.UniformsUtils = { + + merge: function ( uniforms ) { + + var u, p, tmp, merged = {}; + + for ( u = 0; u < uniforms.length; u ++ ) { + + tmp = this.clone( uniforms[ u ] ); + + for ( p in tmp ) { + + merged[ p ] = tmp[ p ]; + + } + + } + + return merged; + + }, + + clone: function ( uniforms_src ) { + + var u, p, parameter, parameter_src, uniforms_dst = {}; + + for ( u in uniforms_src ) { + + uniforms_dst[ u ] = {}; + + for ( p in uniforms_src[ u ] ) { + + parameter_src = uniforms_src[ u ][ p ]; + + if ( parameter_src instanceof THREE.Color || + parameter_src instanceof THREE.Vector2 || + parameter_src instanceof THREE.Vector3 || + parameter_src instanceof THREE.Vector4 || + parameter_src instanceof THREE.Matrix4 || + parameter_src instanceof THREE.Texture ) { + + uniforms_dst[ u ][ p ] = parameter_src.clone(); + + } else if ( parameter_src instanceof Array ) { + + uniforms_dst[ u ][ p ] = parameter_src.slice(); + + } else { + + uniforms_dst[ u ][ p ] = parameter_src; + + } + + } + + } + + return uniforms_dst; + + } + +}; + +THREE.UniformsLib = { + + common: { + + "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + + "map" : { type: "t", value: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "lightMap" : { type: "t", value: null }, + "specularMap" : { type: "t", value: null }, + + "envMap" : { type: "t", value: null }, + "flipEnvMap" : { type: "f", value: -1 }, + "useRefract" : { type: "i", value: 0 }, + "reflectivity" : { type: "f", value: 1.0 }, + "refractionRatio" : { type: "f", value: 0.98 }, + "combine" : { type: "i", value: 0 }, + + "morphTargetInfluences" : { type: "f", value: 0 } + + }, + + bump: { + + "bumpMap" : { type: "t", value: null }, + "bumpScale" : { type: "f", value: 1 } + + }, + + normalmap: { + + "normalMap" : { type: "t", value: null }, + "normalScale" : { type: "v2", value: new THREE.Vector2( 1, 1 ) } + }, + + fog : { + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + lights: { + + "ambientLightColor" : { type: "fv", value: [] }, + + "directionalLightDirection" : { type: "fv", value: [] }, + "directionalLightColor" : { type: "fv", value: [] }, + + "hemisphereLightPosition" : { type: "fv", value: [] }, + "hemisphereLightSkyColor" : { type: "fv", value: [] }, + "hemisphereLightGroundColor" : { type: "fv", value: [] }, + + "pointLightColor" : { type: "fv", value: [] }, + "pointLightPosition" : { type: "fv", value: [] }, + "pointLightDistance" : { type: "fv1", value: [] }, + + "spotLightColor" : { type: "fv", value: [] }, + "spotLightPosition" : { type: "fv", value: [] }, + "spotLightDirection" : { type: "fv", value: [] }, + "spotLightDistance" : { type: "fv1", value: [] }, + "spotLightAngle" : { type: "fv1", value: [] }, + "spotLightExponent" : { type: "fv1", value: [] } + + }, + + particle: { + + "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + "size" : { type: "f", value: 1.0 }, + "scale" : { type: "f", value: 1.0 }, + "map" : { type: "t", value: null }, + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + }, + + shadowmap: { + + "shadowMap": { type: "tv", value: [] }, + "shadowMapSize": { type: "v2v", value: [] }, + + "shadowBias" : { type: "fv1", value: [] }, + "shadowDarkness": { type: "fv1", value: [] }, + + "shadowMatrix" : { type: "m4v", value: [] }, + + } + +}; + +THREE.ShaderLib = { + + 'depth': { + + uniforms: { + + "mNear": { type: "f", value: 1.0 }, + "mFar" : { type: "f", value: 2000.0 }, + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "void main() {", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float mNear;", + "uniform float mFar;", + "uniform float opacity;", + + "void main() {", + + "float depth = gl_FragCoord.z / gl_FragCoord.w;", + "float color = 1.0 - smoothstep( mNear, mFar, depth );", + "gl_FragColor = vec4( vec3( color ), opacity );", + + "}" + + ].join("\n") + + }, + + 'normal': { + + uniforms: { + + "opacity" : { type: "f", value: 1.0 } + + }, + + vertexShader: [ + + "varying vec3 vNormal;", + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vNormal = normalMatrix * normal;", + + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float opacity;", + "varying vec3 vNormal;", + + "void main() {", + + "gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + + "}" + + ].join("\n") + + }, + + 'basic': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + "#ifdef USE_ENVMAP", + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + "#endif", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( diffuse, opacity );", + + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'lambert': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } + + ] ), + + vertexShader: [ + + "#define LAMBERT", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + "varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_lambert_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_lambert_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform float opacity;", + + "varying vec3 vLightFront;", + + "#ifdef DOUBLE_SIDED", + + "varying vec3 vLightBack;", + + "#endif", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( vec3 ( 1.0 ), opacity );", + + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + + "#ifdef DOUBLE_SIDED", + + //"float isFront = float( gl_FrontFacing );", + //"gl_FragColor.xyz *= isFront * vLightFront + ( 1.0 - isFront ) * vLightBack;", + + "if ( gl_FrontFacing )", + "gl_FragColor.xyz *= vLightFront;", + "else", + "gl_FragColor.xyz *= vLightBack;", + + "#else", + + "gl_FragColor.xyz *= vLightFront;", + + "#endif", + + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'phong': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "bump" ], + THREE.UniformsLib[ "normalmap" ], + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + "ambient" : { type: "c", value: new THREE.Color( 0xffffff ) }, + "emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, + "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 }, + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + } + + ] ), + + vertexShader: [ + + "#define PHONG", + + "varying vec3 vViewPosition;", + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_phong_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + THREE.ShaderChunk[ "morphnormal_vertex" ], + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + THREE.ShaderChunk[ "defaultnormal_vertex" ], + + "vNormal = transformedNormal;", + + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + + "vViewPosition = -mvPosition.xyz;", + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "lights_phong_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "uniform vec3 ambient;", + "uniform vec3 emissive;", + "uniform vec3 specular;", + "uniform float shininess;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "lights_phong_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "bumpmap_pars_fragment" ], + THREE.ShaderChunk[ "normalmap_pars_fragment" ], + THREE.ShaderChunk[ "specularmap_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( vec3 ( 1.0 ), opacity );", + + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "specularmap_fragment" ], + + THREE.ShaderChunk[ "lights_phong_fragment" ], + + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + 'particle_basic': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "particle" ], + THREE.UniformsLib[ "shadowmap" ] + + ] ), + + vertexShader: [ + + "uniform float size;", + "uniform float scale;", + + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + "#ifdef USE_SIZEATTENUATION", + "gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + "#else", + "gl_PointSize = size;", + "#endif", + + "gl_Position = projectionMatrix * mvPosition;", + + THREE.ShaderChunk[ "worldpos_vertex" ], + THREE.ShaderChunk[ "shadowmap_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform vec3 psColor;", + "uniform float opacity;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_particle_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( psColor, opacity );", + + THREE.ShaderChunk[ "map_particle_fragment" ], + THREE.ShaderChunk[ "alphatest_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n") + + }, + + // Depth encoding into RGBA texture + // based on SpiderGL shadow map example + // http://spidergl.org/example.php?id=6 + // originally from + // http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD + // see also here: + // http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ + + 'depthRGBA': { + + uniforms: {}, + + vertexShader: [ + + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + + "}" + + ].join("\n"), + + fragmentShader: [ + + "vec4 pack_depth( const in float depth ) {", + + "const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", + "const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", + "vec4 res = fract( depth * bit_shift );", + "res -= res.xxyz * bit_mask;", + "return res;", + + "}", + + "void main() {", + + "gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", + + //"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );", + //"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );", + //"gl_FragData[ 0 ] = pack_depth( z );", + //"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );", + + "}" + + ].join("\n") + + } + +}; +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.WebGLRenderer = function ( parameters ) { + + console.log( 'THREE.WebGLRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + + _precision = parameters.precision !== undefined ? parameters.precision : 'highp', + + _alpha = parameters.alpha !== undefined ? parameters.alpha : true, + _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false, + + _clearColor = parameters.clearColor !== undefined ? new THREE.Color( parameters.clearColor ) : new THREE.Color( 0x000000 ), + _clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0, + + _maxLights = parameters.maxLights !== undefined ? parameters.maxLights : 4; + + // public properties + + this.domElement = _canvas; + this.context = null; + + // clearing + + this.autoClear = true; + this.autoClearColor = true; + this.autoClearDepth = true; + this.autoClearStencil = true; + + // scene graph + + this.sortObjects = true; + + this.autoUpdateObjects = true; + this.autoUpdateScene = true; + + // physically based shading + + this.gammaInput = false; + this.gammaOutput = false; + this.physicallyBasedShading = false; + + // shadow map + + this.shadowMapEnabled = false; + this.shadowMapAutoUpdate = true; + this.shadowMapSoft = true; + this.shadowMapCullFrontFaces = true; + this.shadowMapDebug = false; + this.shadowMapCascade = false; + + // morphs + + this.maxMorphTargets = 8; + this.maxMorphNormals = 4; + + // flags + + this.autoScaleCubemaps = true; + + // custom render plugins + + this.renderPluginsPre = []; + this.renderPluginsPost = []; + + // info + + this.info = { + + memory: { + + programs: 0, + geometries: 0, + textures: 0 + + }, + + render: { + + calls: 0, + vertices: 0, + faces: 0, + points: 0 + + } + + }; + + // internal properties + + var _this = this, + + _programs = [], + _programs_counter = 0, + + // internal state cache + + _currentProgram = null, + _currentFramebuffer = null, + _currentMaterialId = -1, + _currentGeometryGroupHash = null, + _currentCamera = null, + _geometryGroupCounter = 0, + + _usedTextureUnits = 0, + + // GL state cache + + _oldDoubleSided = -1, + _oldFlipSided = -1, + + _oldBlending = -1, + + _oldBlendEquation = -1, + _oldBlendSrc = -1, + _oldBlendDst = -1, + + _oldDepthTest = -1, + _oldDepthWrite = -1, + + _oldPolygonOffset = null, + _oldPolygonOffsetFactor = null, + _oldPolygonOffsetUnits = null, + + _oldLineWidth = null, + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = 0, + _viewportHeight = 0, + _currentWidth = 0, + _currentHeight = 0, + + // frustum + + _frustum = new THREE.Frustum(), + + // camera matrices cache + + _projScreenMatrix = new THREE.Matrix4(), + _projScreenMatrixPS = new THREE.Matrix4(), + + _vector3 = new THREE.Vector4(), + + // light arrays cache + + _direction = new THREE.Vector3(), + + _lightsNeedUpdate = true, + + _lights = { + + ambient: [ 0, 0, 0 ], + directional: { length: 0, colors: new Array(), positions: new Array() }, + point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() }, + spot: { length: 0, colors: new Array(), positions: new Array(), distances: new Array(), directions: new Array(), angles: new Array(), exponents: new Array() }, + hemi: { length: 0, skyColors: new Array(), groundColors: new Array(), positions: new Array() } + + }; + + // initialize + + var _gl; + + var _glExtensionTextureFloat; + var _glExtensionStandardDerivatives; + var _glExtensionTextureFilterAnisotropic; + var _glExtensionCompressedTextureS3TC; + + initGL(); + + setDefaultGLState(); + + this.context = _gl; + + // GPU capabilities + + var _maxTextures = _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ); + var _maxVertexTextures = _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + var _maxTextureSize = _gl.getParameter( _gl.MAX_TEXTURE_SIZE ); + var _maxCubemapSize = _gl.getParameter( _gl.MAX_CUBE_MAP_TEXTURE_SIZE ); + + var _maxAnisotropy = _glExtensionTextureFilterAnisotropic ? _gl.getParameter( _glExtensionTextureFilterAnisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT ) : 0; + + var _supportsVertexTextures = ( _maxVertexTextures > 0 ); + var _supportsBoneTextures = _supportsVertexTextures && _glExtensionTextureFloat; + + var _compressedTextureFormats = _glExtensionCompressedTextureS3TC ? _gl.getParameter( _gl.COMPRESSED_TEXTURE_FORMATS ) : []; + + // API + + this.getContext = function () { + + return _gl; + + }; + + this.supportsVertexTextures = function () { + + return _supportsVertexTextures; + + }; + + this.getMaxAnisotropy = function () { + + return _maxAnisotropy; + + }; + + this.setSize = function ( width, height ) { + + _canvas.width = width; + _canvas.height = height; + + this.setViewport( 0, 0, _canvas.width, _canvas.height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x !== undefined ? x : 0; + _viewportY = y !== undefined ? y : 0; + + _viewportWidth = width !== undefined ? width : _canvas.width; + _viewportHeight = height !== undefined ? height : _canvas.height; + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + }; + + this.setScissor = function ( x, y, width, height ) { + + _gl.scissor( x, y, width, height ); + + }; + + this.enableScissorTest = function ( enable ) { + + enable ? _gl.enable( _gl.SCISSOR_TEST ) : _gl.disable( _gl.SCISSOR_TEST ); + + }; + + // Clearing + + this.setClearColorHex = function ( hex, alpha ) { + + _clearColor.setHex( hex ); + _clearAlpha = alpha; + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.copy( color ); + _clearAlpha = alpha; + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.getClearColor = function () { + + return _clearColor; + + }; + + this.getClearAlpha = function () { + + return _clearAlpha; + + }; + + this.clear = function ( color, depth, stencil ) { + + var bits = 0; + + if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; + if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; + if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; + + _gl.clear( bits ); + + }; + + this.clearTarget = function ( renderTarget, color, depth, stencil ) { + + this.setRenderTarget( renderTarget ); + this.clear( color, depth, stencil ); + + }; + + // Plugins + + this.addPostPlugin = function ( plugin ) { + + plugin.init( this ); + this.renderPluginsPost.push( plugin ); + + }; + + this.addPrePlugin = function ( plugin ) { + + plugin.init( this ); + this.renderPluginsPre.push( plugin ); + + }; + + // Deallocation + + this.deallocateObject = function ( object ) { + + if ( ! object.__webglInit ) return; + + object.__webglInit = false; + + delete object._modelViewMatrix; + delete object._normalMatrix; + + delete object._normalMatrixArray; + delete object._modelViewMatrixArray; + delete object._modelMatrixArray; + + if ( object instanceof THREE.Mesh ) { + + for ( var g in object.geometry.geometryGroups ) { + + deleteMeshBuffers( object.geometry.geometryGroups[ g ] ); + + } + + } else if ( object instanceof THREE.Ribbon ) { + + deleteRibbonBuffers( object.geometry ); + + } else if ( object instanceof THREE.Line ) { + + deleteLineBuffers( object.geometry ); + + } else if ( object instanceof THREE.ParticleSystem ) { + + deleteParticleBuffers( object.geometry ); + + } + + }; + + this.deallocateTexture = function ( texture ) { + + if ( ! texture.__webglInit ) return; + + texture.__webglInit = false; + _gl.deleteTexture( texture.__webglTexture ); + + _this.info.memory.textures --; + + }; + + this.deallocateRenderTarget = function ( renderTarget ) { + + if ( !renderTarget || ! renderTarget.__webglTexture ) return; + + _gl.deleteTexture( renderTarget.__webglTexture ); + + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + + for ( var i = 0; i < 6; i ++ ) { + + _gl.deleteFramebuffer( renderTarget.__webglFramebuffer[ i ] ); + _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer[ i ] ); + + } + + } else { + + _gl.deleteFramebuffer( renderTarget.__webglFramebuffer ); + _gl.deleteRenderbuffer( renderTarget.__webglRenderbuffer ); + + } + + }; + + this.deallocateMaterial = function ( material ) { + + var program = material.program; + + if ( ! program ) return; + + material.program = undefined; + + // only deallocate GL program if this was the last use of shared program + // assumed there is only single copy of any program in the _programs list + // (that's how it's constructed) + + var i, il, programInfo; + var deleteProgram = false; + + for ( i = 0, il = _programs.length; i < il; i ++ ) { + + programInfo = _programs[ i ]; + + if ( programInfo.program === program ) { + + programInfo.usedTimes --; + + if ( programInfo.usedTimes === 0 ) { + + deleteProgram = true; + + } + + break; + + } + + } + + if ( deleteProgram ) { + + // avoid using array.splice, this is costlier than creating new array from scratch + + var newPrograms = []; + + for ( i = 0, il = _programs.length; i < il; i ++ ) { + + programInfo = _programs[ i ]; + + if ( programInfo.program !== program ) { + + newPrograms.push( programInfo ); + + } + + } + + _programs = newPrograms; + + _gl.deleteProgram( program ); + + _this.info.memory.programs --; + + } + + }; + + // Rendering + + this.updateShadowMap = function ( scene, camera ) { + + _currentProgram = null; + _oldBlending = -1; + _oldDepthTest = -1; + _oldDepthWrite = -1; + _currentGeometryGroupHash = -1; + _currentMaterialId = -1; + _lightsNeedUpdate = true; + _oldDoubleSided = -1; + _oldFlipSided = -1; + + this.shadowMapPlugin.update( scene, camera ); + + }; + + // Internal functions + + // Buffer allocation + + function createParticleBuffers ( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + + _this.info.memory.geometries ++; + + }; + + function createLineBuffers ( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + + _this.info.memory.geometries ++; + + }; + + function createRibbonBuffers ( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + + _this.info.memory.geometries ++; + + }; + + function createMeshBuffers ( geometryGroup ) { + + geometryGroup.__webglVertexBuffer = _gl.createBuffer(); + geometryGroup.__webglNormalBuffer = _gl.createBuffer(); + geometryGroup.__webglTangentBuffer = _gl.createBuffer(); + geometryGroup.__webglColorBuffer = _gl.createBuffer(); + geometryGroup.__webglUVBuffer = _gl.createBuffer(); + geometryGroup.__webglUV2Buffer = _gl.createBuffer(); + + geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); + geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); + + geometryGroup.__webglFaceBuffer = _gl.createBuffer(); + geometryGroup.__webglLineBuffer = _gl.createBuffer(); + + var m, ml; + + if ( geometryGroup.numMorphTargets ) { + + geometryGroup.__webglMorphTargetsBuffers = []; + + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + + geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); + + } + + } + + if ( geometryGroup.numMorphNormals ) { + + geometryGroup.__webglMorphNormalsBuffers = []; + + for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + + geometryGroup.__webglMorphNormalsBuffers.push( _gl.createBuffer() ); + + } + + } + + _this.info.memory.geometries ++; + + }; + + // Buffer deallocation + + function deleteParticleBuffers ( geometry ) { + + _gl.deleteBuffer( geometry.__webglVertexBuffer ); + _gl.deleteBuffer( geometry.__webglColorBuffer ); + + _this.info.memory.geometries --; + + }; + + function deleteLineBuffers ( geometry ) { + + _gl.deleteBuffer( geometry.__webglVertexBuffer ); + _gl.deleteBuffer( geometry.__webglColorBuffer ); + + _this.info.memory.geometries --; + + }; + + function deleteRibbonBuffers ( geometry ) { + + _gl.deleteBuffer( geometry.__webglVertexBuffer ); + _gl.deleteBuffer( geometry.__webglColorBuffer ); + + _this.info.memory.geometries --; + + }; + + function deleteMeshBuffers ( geometryGroup ) { + + _gl.deleteBuffer( geometryGroup.__webglVertexBuffer ); + _gl.deleteBuffer( geometryGroup.__webglNormalBuffer ); + _gl.deleteBuffer( geometryGroup.__webglTangentBuffer ); + _gl.deleteBuffer( geometryGroup.__webglColorBuffer ); + _gl.deleteBuffer( geometryGroup.__webglUVBuffer ); + _gl.deleteBuffer( geometryGroup.__webglUV2Buffer ); + + _gl.deleteBuffer( geometryGroup.__webglSkinIndicesBuffer ); + _gl.deleteBuffer( geometryGroup.__webglSkinWeightsBuffer ); + + _gl.deleteBuffer( geometryGroup.__webglFaceBuffer ); + _gl.deleteBuffer( geometryGroup.__webglLineBuffer ); + + var m, ml; + + if ( geometryGroup.numMorphTargets ) { + + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + + _gl.deleteBuffer( geometryGroup.__webglMorphTargetsBuffers[ m ] ); + + } + + } + + if ( geometryGroup.numMorphNormals ) { + + for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + + _gl.deleteBuffer( geometryGroup.__webglMorphNormalsBuffers[ m ] ); + + } + + } + + + if ( geometryGroup.__webglCustomAttributesList ) { + + for ( var id in geometryGroup.__webglCustomAttributesList ) { + + _gl.deleteBuffer( geometryGroup.__webglCustomAttributesList[ id ].buffer ); + + } + + } + + _this.info.memory.geometries --; + + }; + + // Buffer initialization + + function initCustomAttributes ( geometry, object ) { + + var nvertices = geometry.vertices.length; + + var material = object.material; + + if ( material.attributes ) { + + if ( geometry.__webglCustomAttributesList === undefined ) { + + geometry.__webglCustomAttributesList = []; + + } + + for ( var a in material.attributes ) { + + var attribute = material.attributes[ a ]; + + if( !attribute.__webglInitialized || attribute.createUniqueBuffers ) { + + attribute.__webglInitialized = true; + + var size = 1; // "f" and "i" + + if ( attribute.type === "v2" ) size = 2; + else if ( attribute.type === "v3" ) size = 3; + else if ( attribute.type === "v4" ) size = 4; + else if ( attribute.type === "c" ) size = 3; + + attribute.size = size; + + attribute.array = new Float32Array( nvertices * size ); + + attribute.buffer = _gl.createBuffer(); + attribute.buffer.belongsToAttribute = a; + + attribute.needsUpdate = true; + + } + + geometry.__webglCustomAttributesList.push( attribute ); + + } + + } + + }; + + function initParticleBuffers ( geometry, object ) { + + var nvertices = geometry.vertices.length; + + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + + geometry.__sortArray = []; + + geometry.__webglParticleCount = nvertices; + + initCustomAttributes ( geometry, object ); + + }; + + function initLineBuffers ( geometry, object ) { + + var nvertices = geometry.vertices.length; + + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + + geometry.__webglLineCount = nvertices; + + initCustomAttributes ( geometry, object ); + + }; + + function initRibbonBuffers ( geometry ) { + + var nvertices = geometry.vertices.length; + + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + + geometry.__webglVertexCount = nvertices; + + }; + + function initMeshBuffers ( geometryGroup, object ) { + + var geometry = object.geometry, + faces3 = geometryGroup.faces3, + faces4 = geometryGroup.faces4, + + nvertices = faces3.length * 3 + faces4.length * 4, + ntris = faces3.length * 1 + faces4.length * 2, + nlines = faces3.length * 3 + faces4.length * 4, + + material = getBufferMaterial( object, geometryGroup ), + + uvType = bufferGuessUVType( material ), + normalType = bufferGuessNormalType( material ), + vertexColorType = bufferGuessVertexColorType( material ); + + //console.log( "uvType", uvType, "normalType", normalType, "vertexColorType", vertexColorType, object, geometryGroup, material ); + + geometryGroup.__vertexArray = new Float32Array( nvertices * 3 ); + + if ( normalType ) { + + geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); + + } + + if ( geometry.hasTangents ) { + + geometryGroup.__tangentArray = new Float32Array( nvertices * 4 ); + + } + + if ( vertexColorType ) { + + geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); + + } + + if ( uvType ) { + + if ( geometry.faceUvs.length > 0 || geometry.faceVertexUvs.length > 0 ) { + + geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); + + } + + if ( geometry.faceUvs.length > 1 || geometry.faceVertexUvs.length > 1 ) { + + geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); + + } + + } + + if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { + + geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); + geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); + + } + + geometryGroup.__faceArray = new Uint16Array( ntris * 3 ); + geometryGroup.__lineArray = new Uint16Array( nlines * 2 ); + + var m, ml; + + if ( geometryGroup.numMorphTargets ) { + + geometryGroup.__morphTargetsArrays = []; + + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m ++ ) { + + geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); + + } + + } + + if ( geometryGroup.numMorphNormals ) { + + geometryGroup.__morphNormalsArrays = []; + + for ( m = 0, ml = geometryGroup.numMorphNormals; m < ml; m ++ ) { + + geometryGroup.__morphNormalsArrays.push( new Float32Array( nvertices * 3 ) ); + + } + + } + + geometryGroup.__webglFaceCount = ntris * 3; + geometryGroup.__webglLineCount = nlines * 2; + + + // custom attributes + + if ( material.attributes ) { + + if ( geometryGroup.__webglCustomAttributesList === undefined ) { + + geometryGroup.__webglCustomAttributesList = []; + + } + + for ( var a in material.attributes ) { + + // Do a shallow copy of the attribute object so different geometryGroup chunks use different + // attribute buffers which are correctly indexed in the setMeshBuffers function + + var originalAttribute = material.attributes[ a ]; + + var attribute = {}; + + for ( var property in originalAttribute ) { + + attribute[ property ] = originalAttribute[ property ]; + + } + + if( !attribute.__webglInitialized || attribute.createUniqueBuffers ) { + + attribute.__webglInitialized = true; + + var size = 1; // "f" and "i" + + if( attribute.type === "v2" ) size = 2; + else if( attribute.type === "v3" ) size = 3; + else if( attribute.type === "v4" ) size = 4; + else if( attribute.type === "c" ) size = 3; + + attribute.size = size; + + attribute.array = new Float32Array( nvertices * size ); + + attribute.buffer = _gl.createBuffer(); + attribute.buffer.belongsToAttribute = a; + + originalAttribute.needsUpdate = true; + attribute.__original = originalAttribute; + + } + + geometryGroup.__webglCustomAttributesList.push( attribute ); + + } + + } + + geometryGroup.__inittedArrays = true; + + }; + + function getBufferMaterial( object, geometryGroup ) { + + if ( object.material && ! ( object.material instanceof THREE.MeshFaceMaterial ) ) { + + return object.material; + + } else if ( geometryGroup.materialIndex >= 0 ) { + + return object.geometry.materials[ geometryGroup.materialIndex ]; + + } + + }; + + function materialNeedsSmoothNormals ( material ) { + + return material && material.shading !== undefined && material.shading === THREE.SmoothShading; + + }; + + function bufferGuessNormalType ( material ) { + + // only MeshBasicMaterial and MeshDepthMaterial don't need normals + + if ( ( material instanceof THREE.MeshBasicMaterial && !material.envMap ) || material instanceof THREE.MeshDepthMaterial ) { + + return false; + + } + + if ( materialNeedsSmoothNormals( material ) ) { + + return THREE.SmoothShading; + + } else { + + return THREE.FlatShading; + + } + + }; + + function bufferGuessVertexColorType ( material ) { + + if ( material.vertexColors ) { + + return material.vertexColors; + + } + + return false; + + }; + + function bufferGuessUVType ( material ) { + + // material must use some texture to require uvs + + if ( material.map || material.lightMap || material.bumpMap || material.normalMap || material.specularMap || material instanceof THREE.ShaderMaterial ) { + + return true; + + } + + return false; + + }; + + // + + function initDirectBuffers( geometry ) { + + var a, attribute, type; + + for ( a in geometry.attributes ) { + + if ( a === "index" ) { + + type = _gl.ELEMENT_ARRAY_BUFFER; + + } else { + + type = _gl.ARRAY_BUFFER; + + } + + attribute = geometry.attributes[ a ]; + + attribute.buffer = _gl.createBuffer(); + + _gl.bindBuffer( type, attribute.buffer ); + _gl.bufferData( type, attribute.array, _gl.STATIC_DRAW ); + + } + + }; + + // Buffer setting + + function setParticleBuffers ( geometry, hint, object ) { + + var v, c, vertex, offset, index, color, + + vertices = geometry.vertices, + vl = vertices.length, + + colors = geometry.colors, + cl = colors.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + + sortArray = geometry.__sortArray, + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyElements = geometry.elementsNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + + customAttributes = geometry.__webglCustomAttributesList, + i, il, + a, ca, cal, value, + customAttribute; + + if ( object.sortParticles ) { + + _projScreenMatrixPS.copy( _projScreenMatrix ); + _projScreenMatrixPS.multiplySelf( object.matrixWorld ); + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + _vector3.copy( vertex ); + _projScreenMatrixPS.multiplyVector3( _vector3 ); + + sortArray[ v ] = [ _vector3.z, v ]; + + } + + sortArray.sort( function( a, b ) { return b[ 0 ] - a[ 0 ]; } ); + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ sortArray[v][1] ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + for ( c = 0; c < cl; c ++ ) { + + offset = c * 3; + + color = colors[ sortArray[c][1] ]; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( ! ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) ) continue; + + offset = 0; + + cal = customAttribute.value.length; + + if ( customAttribute.size === 1 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + customAttribute.array[ ca ] = customAttribute.value[ index ]; + + } + + } else if ( customAttribute.size === 2 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + value = customAttribute.value[ index ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + + offset += 2; + + } + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === "c" ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + value = customAttribute.value[ index ]; + + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; + + offset += 3; + + } + + } else { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + value = customAttribute.value[ index ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + + offset += 3; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + index = sortArray[ ca ][ 1 ]; + + value = customAttribute.value[ index ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; + + offset += 4; + + } + + } + + } + + } + + } else { + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c ++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( customAttribute.needsUpdate && + ( customAttribute.boundTo === undefined || + customAttribute.boundTo === "vertices") ) { + + cal = customAttribute.value.length; + + offset = 0; + + if ( customAttribute.size === 1 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + customAttribute.array[ ca ] = customAttribute.value[ ca ]; + + } + + } else if ( customAttribute.size === 2 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + + offset += 2; + + } + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === "c" ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; + + offset += 3; + + } + + } else { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + + offset += 3; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; + + offset += 4; + + } + + } + + } + + } + + } + + } + + if ( dirtyVertices || object.sortParticles ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors || object.sortParticles ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( customAttribute.needsUpdate || object.sortParticles ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + } + + + }; + + function setLineBuffers ( geometry, hint ) { + + var v, c, vertex, offset, color, + + vertices = geometry.vertices, + colors = geometry.colors, + vl = vertices.length, + cl = colors.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + + customAttributes = geometry.__webglCustomAttributesList, + + i, il, + a, ca, cal, value, + customAttribute; + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c ++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( customAttribute.needsUpdate && + ( customAttribute.boundTo === undefined || + customAttribute.boundTo === "vertices" ) ) { + + offset = 0; + + cal = customAttribute.value.length; + + if ( customAttribute.size === 1 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + customAttribute.array[ ca ] = customAttribute.value[ ca ]; + + } + + } else if ( customAttribute.size === 2 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + + offset += 2; + + } + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === "c" ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.r; + customAttribute.array[ offset + 1 ] = value.g; + customAttribute.array[ offset + 2 ] = value.b; + + offset += 3; + + } + + } else { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + + offset += 3; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + for ( ca = 0; ca < cal; ca ++ ) { + + value = customAttribute.value[ ca ]; + + customAttribute.array[ offset ] = value.x; + customAttribute.array[ offset + 1 ] = value.y; + customAttribute.array[ offset + 2 ] = value.z; + customAttribute.array[ offset + 3 ] = value.w; + + offset += 4; + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + } + + }; + + function setRibbonBuffers ( geometry, hint ) { + + var v, c, vertex, offset, color, + + vertices = geometry.vertices, + colors = geometry.colors, + vl = vertices.length, + cl = colors.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate; + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v ++ ) { + + vertex = vertices[ v ]; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c ++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + }; + + function setMeshBuffers( geometryGroup, object, hint, dispose, material ) { + + if ( ! geometryGroup.__inittedArrays ) { + + // console.log( object ); + return; + + } + + var normalType = bufferGuessNormalType( material ), + vertexColorType = bufferGuessVertexColorType( material ), + uvType = bufferGuessUVType( material ), + + needsSmoothNormals = ( normalType === THREE.SmoothShading ); + + var f, fl, fi, face, + vertexNormals, faceNormal, normal, + vertexColors, faceColor, + vertexTangents, + uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, n1, n2, n3, n4, + c1, c2, c3, c4, + sw1, sw2, sw3, sw4, + si1, si2, si3, si4, + sa1, sa2, sa3, sa4, + sb1, sb2, sb3, sb4, + m, ml, i, il, + vn, uvi, uv2i, + vk, vkl, vka, + nka, chf, faceVertexNormals, + a, + + vertexIndex = 0, + + offset = 0, + offset_uv = 0, + offset_uv2 = 0, + offset_face = 0, + offset_normal = 0, + offset_tangent = 0, + offset_line = 0, + offset_color = 0, + offset_skin = 0, + offset_morphTarget = 0, + offset_custom = 0, + offset_customSrc = 0, + + value, + + vertexArray = geometryGroup.__vertexArray, + uvArray = geometryGroup.__uvArray, + uv2Array = geometryGroup.__uv2Array, + normalArray = geometryGroup.__normalArray, + tangentArray = geometryGroup.__tangentArray, + colorArray = geometryGroup.__colorArray, + + skinIndexArray = geometryGroup.__skinIndexArray, + skinWeightArray = geometryGroup.__skinWeightArray, + + morphTargetsArrays = geometryGroup.__morphTargetsArrays, + morphNormalsArrays = geometryGroup.__morphNormalsArrays, + + customAttributes = geometryGroup.__webglCustomAttributesList, + customAttribute, + + faceArray = geometryGroup.__faceArray, + lineArray = geometryGroup.__lineArray, + + geometry = object.geometry, // this is shared for all chunks + + dirtyVertices = geometry.verticesNeedUpdate, + dirtyElements = geometry.elementsNeedUpdate, + dirtyUvs = geometry.uvsNeedUpdate, + dirtyNormals = geometry.normalsNeedUpdate, + dirtyTangents = geometry.tangentsNeedUpdate, + dirtyColors = geometry.colorsNeedUpdate, + dirtyMorphTargets = geometry.morphTargetsNeedUpdate, + + vertices = geometry.vertices, + chunk_faces3 = geometryGroup.faces3, + chunk_faces4 = geometryGroup.faces4, + obj_faces = geometry.faces, + + obj_uvs = geometry.faceVertexUvs[ 0 ], + obj_uvs2 = geometry.faceVertexUvs[ 1 ], + + obj_colors = geometry.colors, + + obj_skinIndices = geometry.skinIndices, + obj_skinWeights = geometry.skinWeights, + + morphTargets = geometry.morphTargets, + morphNormals = geometry.morphNormals; + + if ( dirtyVertices ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = vertices[ face.a ]; + v2 = vertices[ face.b ]; + v3 = vertices[ face.c ]; + + vertexArray[ offset ] = v1.x; + vertexArray[ offset + 1 ] = v1.y; + vertexArray[ offset + 2 ] = v1.z; + + vertexArray[ offset + 3 ] = v2.x; + vertexArray[ offset + 4 ] = v2.y; + vertexArray[ offset + 5 ] = v2.z; + + vertexArray[ offset + 6 ] = v3.x; + vertexArray[ offset + 7 ] = v3.y; + vertexArray[ offset + 8 ] = v3.z; + + offset += 9; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + v1 = vertices[ face.a ]; + v2 = vertices[ face.b ]; + v3 = vertices[ face.c ]; + v4 = vertices[ face.d ]; + + vertexArray[ offset ] = v1.x; + vertexArray[ offset + 1 ] = v1.y; + vertexArray[ offset + 2 ] = v1.z; + + vertexArray[ offset + 3 ] = v2.x; + vertexArray[ offset + 4 ] = v2.y; + vertexArray[ offset + 5 ] = v2.z; + + vertexArray[ offset + 6 ] = v3.x; + vertexArray[ offset + 7 ] = v3.y; + vertexArray[ offset + 8 ] = v3.z; + + vertexArray[ offset + 9 ] = v4.x; + vertexArray[ offset + 10 ] = v4.y; + vertexArray[ offset + 11 ] = v4.z; + + offset += 12; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyMorphTargets ) { + + for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { + + offset_morphTarget = 0; + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + chf = chunk_faces3[ f ]; + face = obj_faces[ chf ]; + + // morph positions + + v1 = morphTargets[ vk ].vertices[ face.a ]; + v2 = morphTargets[ vk ].vertices[ face.b ]; + v3 = morphTargets[ vk ].vertices[ face.c ]; + + vka = morphTargetsArrays[ vk ]; + + vka[ offset_morphTarget ] = v1.x; + vka[ offset_morphTarget + 1 ] = v1.y; + vka[ offset_morphTarget + 2 ] = v1.z; + + vka[ offset_morphTarget + 3 ] = v2.x; + vka[ offset_morphTarget + 4 ] = v2.y; + vka[ offset_morphTarget + 5 ] = v2.z; + + vka[ offset_morphTarget + 6 ] = v3.x; + vka[ offset_morphTarget + 7 ] = v3.y; + vka[ offset_morphTarget + 8 ] = v3.z; + + // morph normals + + if ( material.morphNormals ) { + + if ( needsSmoothNormals ) { + + faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; + + n1 = faceVertexNormals.a; + n2 = faceVertexNormals.b; + n3 = faceVertexNormals.c; + + } else { + + n1 = morphNormals[ vk ].faceNormals[ chf ]; + n2 = n1; + n3 = n1; + + } + + nka = morphNormalsArrays[ vk ]; + + nka[ offset_morphTarget ] = n1.x; + nka[ offset_morphTarget + 1 ] = n1.y; + nka[ offset_morphTarget + 2 ] = n1.z; + + nka[ offset_morphTarget + 3 ] = n2.x; + nka[ offset_morphTarget + 4 ] = n2.y; + nka[ offset_morphTarget + 5 ] = n2.z; + + nka[ offset_morphTarget + 6 ] = n3.x; + nka[ offset_morphTarget + 7 ] = n3.y; + nka[ offset_morphTarget + 8 ] = n3.z; + + } + + // + + offset_morphTarget += 9; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + chf = chunk_faces4[ f ]; + face = obj_faces[ chf ]; + + // morph positions + + v1 = morphTargets[ vk ].vertices[ face.a ]; + v2 = morphTargets[ vk ].vertices[ face.b ]; + v3 = morphTargets[ vk ].vertices[ face.c ]; + v4 = morphTargets[ vk ].vertices[ face.d ]; + + vka = morphTargetsArrays[ vk ]; + + vka[ offset_morphTarget ] = v1.x; + vka[ offset_morphTarget + 1 ] = v1.y; + vka[ offset_morphTarget + 2 ] = v1.z; + + vka[ offset_morphTarget + 3 ] = v2.x; + vka[ offset_morphTarget + 4 ] = v2.y; + vka[ offset_morphTarget + 5 ] = v2.z; + + vka[ offset_morphTarget + 6 ] = v3.x; + vka[ offset_morphTarget + 7 ] = v3.y; + vka[ offset_morphTarget + 8 ] = v3.z; + + vka[ offset_morphTarget + 9 ] = v4.x; + vka[ offset_morphTarget + 10 ] = v4.y; + vka[ offset_morphTarget + 11 ] = v4.z; + + // morph normals + + if ( material.morphNormals ) { + + if ( needsSmoothNormals ) { + + faceVertexNormals = morphNormals[ vk ].vertexNormals[ chf ]; + + n1 = faceVertexNormals.a; + n2 = faceVertexNormals.b; + n3 = faceVertexNormals.c; + n4 = faceVertexNormals.d; + + } else { + + n1 = morphNormals[ vk ].faceNormals[ chf ]; + n2 = n1; + n3 = n1; + n4 = n1; + + } + + nka = morphNormalsArrays[ vk ]; + + nka[ offset_morphTarget ] = n1.x; + nka[ offset_morphTarget + 1 ] = n1.y; + nka[ offset_morphTarget + 2 ] = n1.z; + + nka[ offset_morphTarget + 3 ] = n2.x; + nka[ offset_morphTarget + 4 ] = n2.y; + nka[ offset_morphTarget + 5 ] = n2.z; + + nka[ offset_morphTarget + 6 ] = n3.x; + nka[ offset_morphTarget + 7 ] = n3.y; + nka[ offset_morphTarget + 8 ] = n3.z; + + nka[ offset_morphTarget + 9 ] = n4.x; + nka[ offset_morphTarget + 10 ] = n4.y; + nka[ offset_morphTarget + 11 ] = n4.z; + + } + + // + + offset_morphTarget += 12; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); + + if ( material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphNormalsArrays[ vk ], hint ); + + } + + } + + } + + if ( obj_skinWeights.length ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + // weights + + sw1 = obj_skinWeights[ face.a ]; + sw2 = obj_skinWeights[ face.b ]; + sw3 = obj_skinWeights[ face.c ]; + + skinWeightArray[ offset_skin ] = sw1.x; + skinWeightArray[ offset_skin + 1 ] = sw1.y; + skinWeightArray[ offset_skin + 2 ] = sw1.z; + skinWeightArray[ offset_skin + 3 ] = sw1.w; + + skinWeightArray[ offset_skin + 4 ] = sw2.x; + skinWeightArray[ offset_skin + 5 ] = sw2.y; + skinWeightArray[ offset_skin + 6 ] = sw2.z; + skinWeightArray[ offset_skin + 7 ] = sw2.w; + + skinWeightArray[ offset_skin + 8 ] = sw3.x; + skinWeightArray[ offset_skin + 9 ] = sw3.y; + skinWeightArray[ offset_skin + 10 ] = sw3.z; + skinWeightArray[ offset_skin + 11 ] = sw3.w; + + // indices + + si1 = obj_skinIndices[ face.a ]; + si2 = obj_skinIndices[ face.b ]; + si3 = obj_skinIndices[ face.c ]; + + skinIndexArray[ offset_skin ] = si1.x; + skinIndexArray[ offset_skin + 1 ] = si1.y; + skinIndexArray[ offset_skin + 2 ] = si1.z; + skinIndexArray[ offset_skin + 3 ] = si1.w; + + skinIndexArray[ offset_skin + 4 ] = si2.x; + skinIndexArray[ offset_skin + 5 ] = si2.y; + skinIndexArray[ offset_skin + 6 ] = si2.z; + skinIndexArray[ offset_skin + 7 ] = si2.w; + + skinIndexArray[ offset_skin + 8 ] = si3.x; + skinIndexArray[ offset_skin + 9 ] = si3.y; + skinIndexArray[ offset_skin + 10 ] = si3.z; + skinIndexArray[ offset_skin + 11 ] = si3.w; + + offset_skin += 12; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + // weights + + sw1 = obj_skinWeights[ face.a ]; + sw2 = obj_skinWeights[ face.b ]; + sw3 = obj_skinWeights[ face.c ]; + sw4 = obj_skinWeights[ face.d ]; + + skinWeightArray[ offset_skin ] = sw1.x; + skinWeightArray[ offset_skin + 1 ] = sw1.y; + skinWeightArray[ offset_skin + 2 ] = sw1.z; + skinWeightArray[ offset_skin + 3 ] = sw1.w; + + skinWeightArray[ offset_skin + 4 ] = sw2.x; + skinWeightArray[ offset_skin + 5 ] = sw2.y; + skinWeightArray[ offset_skin + 6 ] = sw2.z; + skinWeightArray[ offset_skin + 7 ] = sw2.w; + + skinWeightArray[ offset_skin + 8 ] = sw3.x; + skinWeightArray[ offset_skin + 9 ] = sw3.y; + skinWeightArray[ offset_skin + 10 ] = sw3.z; + skinWeightArray[ offset_skin + 11 ] = sw3.w; + + skinWeightArray[ offset_skin + 12 ] = sw4.x; + skinWeightArray[ offset_skin + 13 ] = sw4.y; + skinWeightArray[ offset_skin + 14 ] = sw4.z; + skinWeightArray[ offset_skin + 15 ] = sw4.w; + + // indices + + si1 = obj_skinIndices[ face.a ]; + si2 = obj_skinIndices[ face.b ]; + si3 = obj_skinIndices[ face.c ]; + si4 = obj_skinIndices[ face.d ]; + + skinIndexArray[ offset_skin ] = si1.x; + skinIndexArray[ offset_skin + 1 ] = si1.y; + skinIndexArray[ offset_skin + 2 ] = si1.z; + skinIndexArray[ offset_skin + 3 ] = si1.w; + + skinIndexArray[ offset_skin + 4 ] = si2.x; + skinIndexArray[ offset_skin + 5 ] = si2.y; + skinIndexArray[ offset_skin + 6 ] = si2.z; + skinIndexArray[ offset_skin + 7 ] = si2.w; + + skinIndexArray[ offset_skin + 8 ] = si3.x; + skinIndexArray[ offset_skin + 9 ] = si3.y; + skinIndexArray[ offset_skin + 10 ] = si3.z; + skinIndexArray[ offset_skin + 11 ] = si3.w; + + skinIndexArray[ offset_skin + 12 ] = si4.x; + skinIndexArray[ offset_skin + 13 ] = si4.y; + skinIndexArray[ offset_skin + 14 ] = si4.z; + skinIndexArray[ offset_skin + 15 ] = si4.w; + + offset_skin += 16; + + } + + if ( offset_skin > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); + + } + + } + + if ( dirtyColors && vertexColorType ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexColors = face.vertexColors; + faceColor = face.color; + + if ( vertexColors.length === 3 && vertexColorType === THREE.VertexColors ) { + + c1 = vertexColors[ 0 ]; + c2 = vertexColors[ 1 ]; + c3 = vertexColors[ 2 ]; + + } else { + + c1 = faceColor; + c2 = faceColor; + c3 = faceColor; + + } + + colorArray[ offset_color ] = c1.r; + colorArray[ offset_color + 1 ] = c1.g; + colorArray[ offset_color + 2 ] = c1.b; + + colorArray[ offset_color + 3 ] = c2.r; + colorArray[ offset_color + 4 ] = c2.g; + colorArray[ offset_color + 5 ] = c2.b; + + colorArray[ offset_color + 6 ] = c3.r; + colorArray[ offset_color + 7 ] = c3.g; + colorArray[ offset_color + 8 ] = c3.b; + + offset_color += 9; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + vertexColors = face.vertexColors; + faceColor = face.color; + + if ( vertexColors.length === 4 && vertexColorType === THREE.VertexColors ) { + + c1 = vertexColors[ 0 ]; + c2 = vertexColors[ 1 ]; + c3 = vertexColors[ 2 ]; + c4 = vertexColors[ 3 ]; + + } else { + + c1 = faceColor; + c2 = faceColor; + c3 = faceColor; + c4 = faceColor; + + } + + colorArray[ offset_color ] = c1.r; + colorArray[ offset_color + 1 ] = c1.g; + colorArray[ offset_color + 2 ] = c1.b; + + colorArray[ offset_color + 3 ] = c2.r; + colorArray[ offset_color + 4 ] = c2.g; + colorArray[ offset_color + 5 ] = c2.b; + + colorArray[ offset_color + 6 ] = c3.r; + colorArray[ offset_color + 7 ] = c3.g; + colorArray[ offset_color + 8 ] = c3.b; + + colorArray[ offset_color + 9 ] = c4.r; + colorArray[ offset_color + 10 ] = c4.g; + colorArray[ offset_color + 11 ] = c4.b; + + offset_color += 12; + + } + + if ( offset_color > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + } + + if ( dirtyTangents && geometry.hasTangents ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexTangents = face.vertexTangents; + + t1 = vertexTangents[ 0 ]; + t2 = vertexTangents[ 1 ]; + t3 = vertexTangents[ 2 ]; + + tangentArray[ offset_tangent ] = t1.x; + tangentArray[ offset_tangent + 1 ] = t1.y; + tangentArray[ offset_tangent + 2 ] = t1.z; + tangentArray[ offset_tangent + 3 ] = t1.w; + + tangentArray[ offset_tangent + 4 ] = t2.x; + tangentArray[ offset_tangent + 5 ] = t2.y; + tangentArray[ offset_tangent + 6 ] = t2.z; + tangentArray[ offset_tangent + 7 ] = t2.w; + + tangentArray[ offset_tangent + 8 ] = t3.x; + tangentArray[ offset_tangent + 9 ] = t3.y; + tangentArray[ offset_tangent + 10 ] = t3.z; + tangentArray[ offset_tangent + 11 ] = t3.w; + + offset_tangent += 12; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + vertexTangents = face.vertexTangents; + + t1 = vertexTangents[ 0 ]; + t2 = vertexTangents[ 1 ]; + t3 = vertexTangents[ 2 ]; + t4 = vertexTangents[ 3 ]; + + tangentArray[ offset_tangent ] = t1.x; + tangentArray[ offset_tangent + 1 ] = t1.y; + tangentArray[ offset_tangent + 2 ] = t1.z; + tangentArray[ offset_tangent + 3 ] = t1.w; + + tangentArray[ offset_tangent + 4 ] = t2.x; + tangentArray[ offset_tangent + 5 ] = t2.y; + tangentArray[ offset_tangent + 6 ] = t2.z; + tangentArray[ offset_tangent + 7 ] = t2.w; + + tangentArray[ offset_tangent + 8 ] = t3.x; + tangentArray[ offset_tangent + 9 ] = t3.y; + tangentArray[ offset_tangent + 10 ] = t3.z; + tangentArray[ offset_tangent + 11 ] = t3.w; + + tangentArray[ offset_tangent + 12 ] = t4.x; + tangentArray[ offset_tangent + 13 ] = t4.y; + tangentArray[ offset_tangent + 14 ] = t4.z; + tangentArray[ offset_tangent + 15 ] = t4.w; + + offset_tangent += 16; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); + + } + + if ( dirtyNormals && normalType ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + vertexNormals = face.vertexNormals; + faceNormal = face.normal; + + if ( vertexNormals.length === 3 && needsSmoothNormals ) { + + for ( i = 0; i < 3; i ++ ) { + + vn = vertexNormals[ i ]; + + normalArray[ offset_normal ] = vn.x; + normalArray[ offset_normal + 1 ] = vn.y; + normalArray[ offset_normal + 2 ] = vn.z; + + offset_normal += 3; + + } + + } else { + + for ( i = 0; i < 3; i ++ ) { + + normalArray[ offset_normal ] = faceNormal.x; + normalArray[ offset_normal + 1 ] = faceNormal.y; + normalArray[ offset_normal + 2 ] = faceNormal.z; + + offset_normal += 3; + + } + + } + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + vertexNormals = face.vertexNormals; + faceNormal = face.normal; + + if ( vertexNormals.length === 4 && needsSmoothNormals ) { + + for ( i = 0; i < 4; i ++ ) { + + vn = vertexNormals[ i ]; + + normalArray[ offset_normal ] = vn.x; + normalArray[ offset_normal + 1 ] = vn.y; + normalArray[ offset_normal + 2 ] = vn.z; + + offset_normal += 3; + + } + + } else { + + for ( i = 0; i < 4; i ++ ) { + + normalArray[ offset_normal ] = faceNormal.x; + normalArray[ offset_normal + 1 ] = faceNormal.y; + normalArray[ offset_normal + 2 ] = faceNormal.z; + + offset_normal += 3; + + } + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); + + } + + if ( dirtyUvs && obj_uvs && uvType ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + fi = chunk_faces3[ f ]; + + uv = obj_uvs[ fi ]; + + if ( uv === undefined ) continue; + + for ( i = 0; i < 3; i ++ ) { + + uvi = uv[ i ]; + + uvArray[ offset_uv ] = uvi.u; + uvArray[ offset_uv + 1 ] = uvi.v; + + offset_uv += 2; + + } + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + fi = chunk_faces4[ f ]; + + uv = obj_uvs[ fi ]; + + if ( uv === undefined ) continue; + + for ( i = 0; i < 4; i ++ ) { + + uvi = uv[ i ]; + + uvArray[ offset_uv ] = uvi.u; + uvArray[ offset_uv + 1 ] = uvi.v; + + offset_uv += 2; + + } + + } + + if ( offset_uv > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); + + } + + } + + if ( dirtyUvs && obj_uvs2 && uvType ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + fi = chunk_faces3[ f ]; + + uv2 = obj_uvs2[ fi ]; + + if ( uv2 === undefined ) continue; + + for ( i = 0; i < 3; i ++ ) { + + uv2i = uv2[ i ]; + + uv2Array[ offset_uv2 ] = uv2i.u; + uv2Array[ offset_uv2 + 1 ] = uv2i.v; + + offset_uv2 += 2; + + } + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + fi = chunk_faces4[ f ]; + + uv2 = obj_uvs2[ fi ]; + + if ( uv2 === undefined ) continue; + + for ( i = 0; i < 4; i ++ ) { + + uv2i = uv2[ i ]; + + uv2Array[ offset_uv2 ] = uv2i.u; + uv2Array[ offset_uv2 + 1 ] = uv2i.v; + + offset_uv2 += 2; + + } + + } + + if ( offset_uv2 > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); + + } + + } + + if ( dirtyElements ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + faceArray[ offset_face ] = vertexIndex; + faceArray[ offset_face + 1 ] = vertexIndex + 1; + faceArray[ offset_face + 2 ] = vertexIndex + 2; + + offset_face += 3; + + lineArray[ offset_line ] = vertexIndex; + lineArray[ offset_line + 1 ] = vertexIndex + 1; + + lineArray[ offset_line + 2 ] = vertexIndex; + lineArray[ offset_line + 3 ] = vertexIndex + 2; + + lineArray[ offset_line + 4 ] = vertexIndex + 1; + lineArray[ offset_line + 5 ] = vertexIndex + 2; + + offset_line += 6; + + vertexIndex += 3; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + faceArray[ offset_face ] = vertexIndex; + faceArray[ offset_face + 1 ] = vertexIndex + 1; + faceArray[ offset_face + 2 ] = vertexIndex + 3; + + faceArray[ offset_face + 3 ] = vertexIndex + 1; + faceArray[ offset_face + 4 ] = vertexIndex + 2; + faceArray[ offset_face + 5 ] = vertexIndex + 3; + + offset_face += 6; + + lineArray[ offset_line ] = vertexIndex; + lineArray[ offset_line + 1 ] = vertexIndex + 1; + + lineArray[ offset_line + 2 ] = vertexIndex; + lineArray[ offset_line + 3 ] = vertexIndex + 3; + + lineArray[ offset_line + 4 ] = vertexIndex + 1; + lineArray[ offset_line + 5 ] = vertexIndex + 2; + + lineArray[ offset_line + 6 ] = vertexIndex + 2; + lineArray[ offset_line + 7 ] = vertexIndex + 3; + + offset_line += 8; + + vertexIndex += 4; + + } + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); + + } + + if ( customAttributes ) { + + for ( i = 0, il = customAttributes.length; i < il; i ++ ) { + + customAttribute = customAttributes[ i ]; + + if ( ! customAttribute.__original.needsUpdate ) continue; + + offset_custom = 0; + offset_customSrc = 0; + + if ( customAttribute.size === 1 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; + + offset_custom += 3; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + customAttribute.array[ offset_custom ] = customAttribute.value[ face.a ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; + customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ face.d ]; + + offset_custom += 4; + + } + + } else if ( customAttribute.boundTo === "faces" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + customAttribute.array[ offset_custom ] = value; + customAttribute.array[ offset_custom + 1 ] = value; + customAttribute.array[ offset_custom + 2 ] = value; + + offset_custom += 3; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces4[ f ] ]; + + customAttribute.array[ offset_custom ] = value; + customAttribute.array[ offset_custom + 1 ] = value; + customAttribute.array[ offset_custom + 2 ] = value; + customAttribute.array[ offset_custom + 3 ] = value; + + offset_custom += 4; + + } + + } + + } else if ( customAttribute.size === 2 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + offset_custom += 6; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + v4 = customAttribute.value[ face.d ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + customAttribute.array[ offset_custom + 6 ] = v4.x; + customAttribute.array[ offset_custom + 7 ] = v4.y; + + offset_custom += 8; + + } + + } else if ( customAttribute.boundTo === "faces" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + offset_custom += 6; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces4[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + v4 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + customAttribute.array[ offset_custom + 6 ] = v4.x; + customAttribute.array[ offset_custom + 7 ] = v4.y; + + offset_custom += 8; + + } + + } + + } else if ( customAttribute.size === 3 ) { + + var pp; + + if ( customAttribute.type === "c" ) { + + pp = [ "r", "g", "b" ]; + + } else { + + pp = [ "x", "y", "z" ]; + + } + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + v4 = customAttribute.value[ face.d ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 9 ] = v4[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ]; + + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === "faces" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces4[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + v4 = value; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 9 ] = v4[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ]; + + offset_custom += 12; + + } + + } else if ( customAttribute.boundTo === "faceVertices" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + offset_custom += 9; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces4[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + v4 = value[ 3 ]; + + customAttribute.array[ offset_custom ] = v1[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 1 ] = v1[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 2 ] = v1[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 3 ] = v2[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 4 ] = v2[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 5 ] = v2[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 6 ] = v3[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 7 ] = v3[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 8 ] = v3[ pp[ 2 ] ]; + + customAttribute.array[ offset_custom + 9 ] = v4[ pp[ 0 ] ]; + customAttribute.array[ offset_custom + 10 ] = v4[ pp[ 1 ] ]; + customAttribute.array[ offset_custom + 11 ] = v4[ pp[ 2 ] ]; + + offset_custom += 12; + + } + + } + + } else if ( customAttribute.size === 4 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces3[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + face = obj_faces[ chunk_faces4[ f ] ]; + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + v4 = customAttribute.value[ face.d ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + customAttribute.array[ offset_custom + 12 ] = v4.x; + customAttribute.array[ offset_custom + 13 ] = v4.y; + customAttribute.array[ offset_custom + 14 ] = v4.z; + customAttribute.array[ offset_custom + 15 ] = v4.w; + + offset_custom += 16; + + } + + } else if ( customAttribute.boundTo === "faces" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces4[ f ] ]; + + v1 = value; + v2 = value; + v3 = value; + v4 = value; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + customAttribute.array[ offset_custom + 12 ] = v4.x; + customAttribute.array[ offset_custom + 13 ] = v4.y; + customAttribute.array[ offset_custom + 14 ] = v4.z; + customAttribute.array[ offset_custom + 15 ] = v4.w; + + offset_custom += 16; + + } + + } else if ( customAttribute.boundTo === "faceVertices" ) { + + for ( f = 0, fl = chunk_faces3.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces3[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + offset_custom += 12; + + } + + for ( f = 0, fl = chunk_faces4.length; f < fl; f ++ ) { + + value = customAttribute.value[ chunk_faces4[ f ] ]; + + v1 = value[ 0 ]; + v2 = value[ 1 ]; + v3 = value[ 2 ]; + v4 = value[ 3 ]; + + customAttribute.array[ offset_custom ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + customAttribute.array[ offset_custom + 12 ] = v4.x; + customAttribute.array[ offset_custom + 13 ] = v4.y; + customAttribute.array[ offset_custom + 14 ] = v4.z; + customAttribute.array[ offset_custom + 15 ] = v4.w; + + offset_custom += 16; + + } + + } + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + if ( dispose ) { + + delete geometryGroup.__inittedArrays; + delete geometryGroup.__colorArray; + delete geometryGroup.__normalArray; + delete geometryGroup.__tangentArray; + delete geometryGroup.__uvArray; + delete geometryGroup.__uv2Array; + delete geometryGroup.__faceArray; + delete geometryGroup.__vertexArray; + delete geometryGroup.__lineArray; + delete geometryGroup.__skinIndexArray; + delete geometryGroup.__skinWeightArray; + + } + + }; + + function setDirectBuffers ( geometry, hint, dispose ) { + + var attributes = geometry.attributes; + + var index = attributes[ "index" ]; + var position = attributes[ "position" ]; + var normal = attributes[ "normal" ]; + var uv = attributes[ "uv" ]; + var color = attributes[ "color" ]; + var tangent = attributes[ "tangent" ]; + + if ( geometry.elementsNeedUpdate && index !== undefined ) { + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, index.array, hint ); + + } + + if ( geometry.verticesNeedUpdate && position !== undefined ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, position.array, hint ); + + } + + if ( geometry.normalsNeedUpdate && normal !== undefined ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, normal.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, normal.array, hint ); + + } + + if ( geometry.uvsNeedUpdate && uv !== undefined ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, uv.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uv.array, hint ); + + } + + if ( geometry.colorsNeedUpdate && color !== undefined ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, color.array, hint ); + + } + + if ( geometry.tangentsNeedUpdate && tangent !== undefined ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, tangent.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, tangent.array, hint ); + + } + + if ( dispose ) { + + for ( var i in geometry.attributes ) { + + delete geometry.attributes[ i ].array; + + } + + } + + }; + + // Buffer rendering + + this.renderBufferImmediate = function ( object, program, material ) { + + if ( object.hasPositions && ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); + if ( object.hasNormals && ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); + if ( object.hasUvs && ! object.__webglUvBuffer ) object.__webglUvBuffer = _gl.createBuffer(); + if ( object.hasColors && ! object.__webglColorBuffer ) object.__webglColorBuffer = _gl.createBuffer(); + + if ( object.hasPositions ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.position ); + _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); + + if ( material.shading === THREE.FlatShading ) { + + var nx, ny, nz, + nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, + normalArray, + i, il = object.count * 3; + + for( i = 0; i < il; i += 9 ) { + + normalArray = object.normalArray; + + nax = normalArray[ i ]; + nay = normalArray[ i + 1 ]; + naz = normalArray[ i + 2 ]; + + nbx = normalArray[ i + 3 ]; + nby = normalArray[ i + 4 ]; + nbz = normalArray[ i + 5 ]; + + ncx = normalArray[ i + 6 ]; + ncy = normalArray[ i + 7 ]; + ncz = normalArray[ i + 8 ]; + + nx = ( nax + nbx + ncx ) / 3; + ny = ( nay + nby + ncy ) / 3; + nz = ( naz + nbz + ncz ) / 3; + + normalArray[ i ] = nx; + normalArray[ i + 1 ] = ny; + normalArray[ i + 2 ] = nz; + + normalArray[ i + 3 ] = nx; + normalArray[ i + 4 ] = ny; + normalArray[ i + 5 ] = nz; + + normalArray[ i + 6 ] = nx; + normalArray[ i + 7 ] = ny; + normalArray[ i + 8 ] = nz; + + } + + } + + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.normal ); + _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasUvs && material.map ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglUvBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.uv ); + _gl.vertexAttribPointer( program.attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.color ); + _gl.vertexAttribPointer( program.attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } + + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + + object.count = 0; + + }; + + this.renderBufferDirect = function ( camera, lights, fog, material, geometry, object ) { + + if ( material.visible === false ) return; + + var program, attributes, linewidth, primitives, a, attribute; + + program = setProgram( camera, lights, fog, material, object ); + + attributes = program.attributes; + + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryHash = ( geometry.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + + if ( geometryHash !== _currentGeometryGroupHash ) { + + _currentGeometryGroupHash = geometryHash; + updateBuffers = true; + + } + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + var offsets = geometry.offsets; + + // if there is more than 1 chunk + // must set attribute pointers to use new offsets for each chunk + // even if geometry and materials didn't change + + if ( offsets.length > 1 ) updateBuffers = true; + + for ( var i = 0, il = offsets.length; i < il; ++ i ) { + + var startIndex = offsets[ i ].index; + + if ( updateBuffers ) { + + // vertices + + var position = geometry.attributes[ "position" ]; + var positionSize = position.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer ); + _gl.vertexAttribPointer( attributes.position, positionSize, _gl.FLOAT, false, 0, startIndex * positionSize * 4 ); // 4 bytes per Float32 + + // normals + + var normal = geometry.attributes[ "normal" ]; + + if ( attributes.normal >= 0 && normal ) { + + var normalSize = normal.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, normal.buffer ); + _gl.vertexAttribPointer( attributes.normal, normalSize, _gl.FLOAT, false, 0, startIndex * normalSize * 4 ); + + } + + // uvs + + var uv = geometry.attributes[ "uv" ]; + + if ( attributes.uv >= 0 && uv ) { + + if ( uv.buffer ) { + + var uvSize = uv.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, uv.buffer ); + _gl.vertexAttribPointer( attributes.uv, uvSize, _gl.FLOAT, false, 0, startIndex * uvSize * 4 ); + + _gl.enableVertexAttribArray( attributes.uv ); + + } else { + + _gl.disableVertexAttribArray( attributes.uv ); + + } + + } + + // colors + + var color = geometry.attributes[ "color" ]; + + if ( attributes.color >= 0 && color ) { + + var colorSize = color.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer ); + _gl.vertexAttribPointer( attributes.color, colorSize, _gl.FLOAT, false, 0, startIndex * colorSize * 4 ); + + } + + // tangents + + var tangent = geometry.attributes[ "tangent" ]; + + if ( attributes.tangent >= 0 && tangent ) { + + var tangentSize = tangent.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, tangent.buffer ); + _gl.vertexAttribPointer( attributes.tangent, tangentSize, _gl.FLOAT, false, 0, startIndex * tangentSize * 4 ); + + } + + // indices + + var index = geometry.attributes[ "index" ]; + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, index.buffer ); + + } + + // render indexed triangles + + _gl.drawElements( _gl.TRIANGLES, offsets[ i ].count, _gl.UNSIGNED_SHORT, offsets[ i ].start * 2 ); // 2 bytes per Uint16 + + _this.info.render.calls ++; + _this.info.render.vertices += offsets[ i ].count; // not really true, here vertices can be shared + _this.info.render.faces += offsets[ i ].count / 3; + + } + + // render particles + + } else if ( object instanceof THREE.ParticleSystem ) { + + if ( updateBuffers ) { + + // vertices + + var position = geometry.attributes[ "position" ]; + var positionSize = position.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, position.buffer ); + _gl.vertexAttribPointer( attributes.position, positionSize, _gl.FLOAT, false, 0, 0 ); + + // colors + + var color = geometry.attributes[ "color" ]; + + if ( attributes.color >= 0 && color ) { + + var colorSize = color.itemSize; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, color.buffer ); + _gl.vertexAttribPointer( attributes.color, colorSize, _gl.FLOAT, false, 0, 0 ); + + } + + // render particles + + _gl.drawArrays( _gl.POINTS, 0, position.numItems / 3 ); + + _this.info.render.calls ++; + _this.info.render.points += position.numItems / 3; + + } + + } + + }; + + this.renderBuffer = function ( camera, lights, fog, material, geometryGroup, object ) { + + if ( material.visible === false ) return; + + var program, attributes, linewidth, primitives, a, attribute, i, il; + + program = setProgram( camera, lights, fog, material, object ); + + attributes = program.attributes; + + var updateBuffers = false, + wireframeBit = material.wireframe ? 1 : 0, + geometryGroupHash = ( geometryGroup.id * 0xffffff ) + ( program.id * 2 ) + wireframeBit; + + if ( geometryGroupHash !== _currentGeometryGroupHash ) { + + _currentGeometryGroupHash = geometryGroupHash; + updateBuffers = true; + + } + + // vertices + + if ( !material.morphTargets && attributes.position >= 0 ) { + + if ( updateBuffers ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + } else { + + if ( object.morphTargetBase ) { + + setupMorphTargets( material, geometryGroup, object ); + + } + + } + + + if ( updateBuffers ) { + + // custom attributes + + // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers + + if ( geometryGroup.__webglCustomAttributesList ) { + + for ( i = 0, il = geometryGroup.__webglCustomAttributesList.length; i < il; i ++ ) { + + attribute = geometryGroup.__webglCustomAttributesList[ i ]; + + if( attributes[ attribute.buffer.belongsToAttribute ] >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); + _gl.vertexAttribPointer( attributes[ attribute.buffer.belongsToAttribute ], attribute.size, _gl.FLOAT, false, 0, 0 ); + + } + + } + + } + + + // colors + + if ( attributes.color >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } + + // normals + + if ( attributes.normal >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + // tangents + + if ( attributes.tangent >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // uvs + + if ( attributes.uv >= 0 ) { + + if ( geometryGroup.__webglUVBuffer ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + _gl.enableVertexAttribArray( attributes.uv ); + + } else { + + _gl.disableVertexAttribArray( attributes.uv ); + + } + + } + + if ( attributes.uv2 >= 0 ) { + + if ( geometryGroup.__webglUV2Buffer ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); + + _gl.enableVertexAttribArray( attributes.uv2 ); + + } else { + + _gl.disableVertexAttribArray( attributes.uv2 ); + + } + + } + + if ( material.skinning && + attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); + + } + + } + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + // wireframe + + if ( material.wireframe ) { + + setLineWidth( material.wireframeLinewidth ); + + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 ); + + // triangles + + } else { + + if ( updateBuffers ) _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 ); + + } + + _this.info.render.calls ++; + _this.info.render.vertices += geometryGroup.__webglFaceCount; + _this.info.render.faces += geometryGroup.__webglFaceCount / 3; + + // render lines + + } else if ( object instanceof THREE.Line ) { + + primitives = ( object.type === THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + + setLineWidth( material.linewidth ); + + _gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount ); + + _this.info.render.calls ++; + + // render particles + + } else if ( object instanceof THREE.ParticleSystem ) { + + _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); + + _this.info.render.calls ++; + _this.info.render.points += geometryGroup.__webglParticleCount; + + // render ribbon + + } else if ( object instanceof THREE.Ribbon ) { + + _gl.drawArrays( _gl.TRIANGLE_STRIP, 0, geometryGroup.__webglVertexCount ); + + _this.info.render.calls ++; + + } + + }; + + function setupMorphTargets ( material, geometryGroup, object ) { + + // set base + + var attributes = material.program.attributes; + + if ( object.morphTargetBase !== -1 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } else if ( attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.morphTargetForcedOrder.length ) { + + // set forced order + + var m = 0; + var order = object.morphTargetForcedOrder; + var influences = object.morphTargetInfluences; + + while ( m < material.numSupportedMorphTargets && m < order.length ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + if ( material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ order[ m ] ] ); + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ] ]; + + m ++; + } + + } else { + + // find the most influencing + + var influence, activeInfluenceIndices = []; + var influences = object.morphTargetInfluences; + var i, il = influences.length; + + for ( i = 0; i < il; i ++ ) { + + influence = influences[ i ]; + + if ( influence > 0 ) { + + activeInfluenceIndices.push( [ i, influence ] ); + + } + + } + + if ( activeInfluenceIndices.length > material.numSupportedMorphTargets ) { + + activeInfluenceIndices.sort( numericalSort ); + activeInfluenceIndices.length = material.numSupportedMorphTargets; + + } else if ( activeInfluenceIndices.length > material.numSupportedMorphNormals ) { + + activeInfluenceIndices.sort( numericalSort ); + + } else if ( activeInfluenceIndices.length === 0 ) { + + activeInfluenceIndices.push( [ 0, 0 ] ); + + }; + + var influenceIndex, m = 0; + + while ( m < material.numSupportedMorphTargets ) { + + if ( activeInfluenceIndices[ m ] ) { + + influenceIndex = activeInfluenceIndices[ m ][ 0 ]; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ influenceIndex ] ); + + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + if ( material.morphNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphNormalsBuffers[ influenceIndex ] ); + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + object.__webglMorphTargetInfluences[ m ] = influences[ influenceIndex ]; + + } else { + + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + if ( material.morphNormals ) { + + _gl.vertexAttribPointer( attributes[ "morphNormal" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + } + + object.__webglMorphTargetInfluences[ m ] = 0; + + } + + m ++; + + } + + } + + // load updated influences uniform + + if ( material.program.uniforms.morphTargetInfluences !== null ) { + + _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); + + } + + }; + + // Sorting + + function painterSort ( a, b ) { + + return b.z - a.z; + + }; + + function numericalSort ( a, b ) { + + return b[ 1 ] - a[ 1 ]; + + }; + + + // Rendering + + this.render = function ( scene, camera, renderTarget, forceClear ) { + + if ( camera instanceof THREE.Camera === false ) { + + console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + var i, il, + + webglObject, object, + renderList, + + lights = scene.__lights, + fog = scene.fog; + + // reset caching for this frame + + _currentMaterialId = -1; + _lightsNeedUpdate = true; + + // update scene graph + + if ( this.autoUpdateScene ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + if ( camera.parent === undefined ) camera.updateMatrixWorld(); + + if ( ! camera._viewMatrixArray ) camera._viewMatrixArray = new Float32Array( 16 ); + if ( ! camera._projectionMatrixArray ) camera._projectionMatrixArray = new Float32Array( 16 ); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + camera.matrixWorldInverse.flattenToArray( camera._viewMatrixArray ); + camera.projectionMatrix.flattenToArray( camera._projectionMatrixArray ); + + _projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // update WebGL objects + + if ( this.autoUpdateObjects ) this.initWebGLObjects( scene ); + + // custom render plugins (pre pass) + + renderPlugins( this.renderPluginsPre, scene, camera ); + + // + + _this.info.render.calls = 0; + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + _this.info.render.points = 0; + + this.setRenderTarget( renderTarget ); + + if ( this.autoClear || forceClear ) { + + this.clear( this.autoClearColor, this.autoClearDepth, this.autoClearStencil ); + + } + + // set matrices for regular objects (frustum culled) + + renderList = scene.__webglObjects; + + for ( i = 0, il = renderList.length; i < il; i ++ ) { + + webglObject = renderList[ i ]; + object = webglObject.object; + + webglObject.render = false; + + if ( object.visible ) { + + if ( ! ( object instanceof THREE.Mesh || object instanceof THREE.ParticleSystem ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) { + + //object.matrixWorld.flattenToArray( object._modelMatrixArray ); + + setupMatrices( object, camera ); + + unrollBufferMaterial( webglObject ); + + webglObject.render = true; + + if ( this.sortObjects === true ) { + + if ( object.renderDepth !== null ) { + + webglObject.z = object.renderDepth; + + } else { + + _vector3.copy( object.matrixWorld.getPosition() ); + _projScreenMatrix.multiplyVector3( _vector3 ); + + webglObject.z = _vector3.z; + + } + + } + + } + + } + + } + + if ( this.sortObjects ) { + + renderList.sort( painterSort ); + + } + + // set matrices for immediate objects + + renderList = scene.__webglObjectsImmediate; + + for ( i = 0, il = renderList.length; i < il; i ++ ) { + + webglObject = renderList[ i ]; + object = webglObject.object; + + if ( object.visible ) { + + /* + if ( object.matrixAutoUpdate ) { + + object.matrixWorld.flattenToArray( object._modelMatrixArray ); + + } + */ + + setupMatrices( object, camera ); + + unrollImmediateBufferMaterial( webglObject ); + + } + + } + + if ( scene.overrideMaterial ) { + + var material = scene.overrideMaterial; + + this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + this.setDepthTest( material.depthTest ); + this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + renderObjects( scene.__webglObjects, false, "", camera, lights, fog, true, material ); + renderObjectsImmediate( scene.__webglObjectsImmediate, "", camera, lights, fog, false, material ); + + } else { + + // opaque pass (front-to-back order) + + this.setBlending( THREE.NormalBlending ); + + renderObjects( scene.__webglObjects, true, "opaque", camera, lights, fog, false ); + renderObjectsImmediate( scene.__webglObjectsImmediate, "opaque", camera, lights, fog, false ); + + // transparent pass (back-to-front order) + + renderObjects( scene.__webglObjects, false, "transparent", camera, lights, fog, true ); + renderObjectsImmediate( scene.__webglObjectsImmediate, "transparent", camera, lights, fog, true ); + + } + + // custom render plugins (post pass) + + renderPlugins( this.renderPluginsPost, scene, camera ); + + + // Generate mipmap if we're using any kind of mipmap filtering + + if ( renderTarget && renderTarget.generateMipmaps && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { + + updateRenderTargetMipmap( renderTarget ); + + } + + // Ensure depth buffer writing is enabled so it can be cleared on next render + + this.setDepthTest( true ); + this.setDepthWrite( true ); + + // _gl.finish(); + + }; + + function renderPlugins( plugins, scene, camera ) { + + if ( ! plugins.length ) return; + + for ( var i = 0, il = plugins.length; i < il; i ++ ) { + + // reset state for plugin (to start from clean slate) + + _currentProgram = null; + _currentCamera = null; + + _oldBlending = -1; + _oldDepthTest = -1; + _oldDepthWrite = -1; + _oldDoubleSided = -1; + _oldFlipSided = -1; + _currentGeometryGroupHash = -1; + _currentMaterialId = -1; + + _lightsNeedUpdate = true; + + plugins[ i ].render( scene, camera, _currentWidth, _currentHeight ); + + // reset state after plugin (anything could have changed) + + _currentProgram = null; + _currentCamera = null; + + _oldBlending = -1; + _oldDepthTest = -1; + _oldDepthWrite = -1; + _oldDoubleSided = -1; + _oldFlipSided = -1; + _currentGeometryGroupHash = -1; + _currentMaterialId = -1; + + _lightsNeedUpdate = true; + + } + + }; + + function renderObjects ( renderList, reverse, materialType, camera, lights, fog, useBlending, overrideMaterial ) { + + var webglObject, object, buffer, material, start, end, delta; + + if ( reverse ) { + + start = renderList.length - 1; + end = -1; + delta = -1; + + } else { + + start = 0; + end = renderList.length; + delta = 1; + } + + for ( var i = start; i !== end; i += delta ) { + + webglObject = renderList[ i ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + + if ( overrideMaterial ) { + + material = overrideMaterial; + + } else { + + material = webglObject[ materialType ]; + + if ( ! material ) continue; + + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + _this.setMaterialFaces( material ); + + if ( buffer instanceof THREE.BufferGeometry ) { + + _this.renderBufferDirect( camera, lights, fog, material, buffer, object ); + + } else { + + _this.renderBuffer( camera, lights, fog, material, buffer, object ); + + } + + } + + } + + }; + + function renderObjectsImmediate ( renderList, materialType, camera, lights, fog, useBlending, overrideMaterial ) { + + var webglObject, object, material, program; + + for ( var i = 0, il = renderList.length; i < il; i ++ ) { + + webglObject = renderList[ i ]; + object = webglObject.object; + + if ( object.visible ) { + + if ( overrideMaterial ) { + + material = overrideMaterial; + + } else { + + material = webglObject[ materialType ]; + + if ( ! material ) continue; + + if ( useBlending ) _this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst ); + + _this.setDepthTest( material.depthTest ); + _this.setDepthWrite( material.depthWrite ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + } + + _this.renderImmediateObject( camera, lights, fog, material, object ); + + } + + } + + }; + + this.renderImmediateObject = function ( camera, lights, fog, material, object ) { + + var program = setProgram( camera, lights, fog, material, object ); + + _currentGeometryGroupHash = -1; + + _this.setMaterialFaces( material ); + + if ( object.immediateRenderCallback ) { + + object.immediateRenderCallback( program, _gl, _frustum ); + + } else { + + object.render( function( object ) { _this.renderBufferImmediate( object, program, material ); } ); + + } + + }; + + function unrollImmediateBufferMaterial ( globject ) { + + var object = globject.object, + material = object.material; + + if ( material.transparent ) { + + globject.transparent = material; + globject.opaque = null; + + } else { + + globject.opaque = material; + globject.transparent = null; + + } + + }; + + function unrollBufferMaterial ( globject ) { + + var object = globject.object, + buffer = globject.buffer, + material, materialIndex, meshMaterial; + + meshMaterial = object.material; + + if ( meshMaterial instanceof THREE.MeshFaceMaterial ) { + + materialIndex = buffer.materialIndex; + + if ( materialIndex >= 0 ) { + + material = object.geometry.materials[ materialIndex ]; + + if ( material.transparent ) { + + globject.transparent = material; + globject.opaque = null; + + } else { + + globject.opaque = material; + globject.transparent = null; + + } + + } + + } else { + + material = meshMaterial; + + if ( material ) { + + if ( material.transparent ) { + + globject.transparent = material; + globject.opaque = null; + + } else { + + globject.opaque = material; + globject.transparent = null; + + } + + } + + } + + }; + + // Geometry splitting + + function sortFacesByMaterial ( geometry ) { + + var f, fl, face, materialIndex, vertices, + materialHash, groupHash, + hash_map = {}; + + var numMorphTargets = geometry.morphTargets.length; + var numMorphNormals = geometry.morphNormals.length; + + geometry.geometryGroups = {}; + + for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + + face = geometry.faces[ f ]; + materialIndex = face.materialIndex; + + materialHash = ( materialIndex !== undefined ) ? materialIndex : -1; + + if ( hash_map[ materialHash ] === undefined ) { + + hash_map[ materialHash ] = { 'hash': materialHash, 'counter': 0 }; + + } + + groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].counter; + + if ( geometry.geometryGroups[ groupHash ] === undefined ) { + + geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; + + } + + vertices = face instanceof THREE.Face3 ? 3 : 4; + + if ( geometry.geometryGroups[ groupHash ].vertices + vertices > 65535 ) { + + hash_map[ materialHash ].counter += 1; + groupHash = hash_map[ materialHash ].hash + '_' + hash_map[ materialHash ].counter; + + if ( geometry.geometryGroups[ groupHash ] === undefined ) { + + geometry.geometryGroups[ groupHash ] = { 'faces3': [], 'faces4': [], 'materialIndex': materialIndex, 'vertices': 0, 'numMorphTargets': numMorphTargets, 'numMorphNormals': numMorphNormals }; + + } + + } + + if ( face instanceof THREE.Face3 ) { + + geometry.geometryGroups[ groupHash ].faces3.push( f ); + + } else { + + geometry.geometryGroups[ groupHash ].faces4.push( f ); + + } + + geometry.geometryGroups[ groupHash ].vertices += vertices; + + } + + geometry.geometryGroupsList = []; + + for ( var g in geometry.geometryGroups ) { + + geometry.geometryGroups[ g ].id = _geometryGroupCounter ++; + + geometry.geometryGroupsList.push( geometry.geometryGroups[ g ] ); + + } + + }; + + // Objects refresh + + this.initWebGLObjects = function ( scene ) { + + if ( !scene.__webglObjects ) { + + scene.__webglObjects = []; + scene.__webglObjectsImmediate = []; + scene.__webglSprites = []; + scene.__webglFlares = []; + + } + + while ( scene.__objectsAdded.length ) { + + addObject( scene.__objectsAdded[ 0 ], scene ); + scene.__objectsAdded.splice( 0, 1 ); + + } + + while ( scene.__objectsRemoved.length ) { + + removeObject( scene.__objectsRemoved[ 0 ], scene ); + scene.__objectsRemoved.splice( 0, 1 ); + + } + + // update must be called after objects adding / removal + + for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) { + + updateObject( scene.__webglObjects[ o ].object ); + + } + + }; + + // Objects adding + + function addObject ( object, scene ) { + + var g, geometry, geometryGroup; + + if ( ! object.__webglInit ) { + + object.__webglInit = true; + + object._modelViewMatrix = new THREE.Matrix4(); + object._normalMatrix = new THREE.Matrix3(); + + if ( object instanceof THREE.Mesh ) { + + geometry = object.geometry; + + if ( geometry instanceof THREE.Geometry ) { + + if ( geometry.geometryGroups === undefined ) { + + sortFacesByMaterial( geometry ); + + } + + // create separate VBOs per geometry chunk + + for ( g in geometry.geometryGroups ) { + + geometryGroup = geometry.geometryGroups[ g ]; + + // initialise VBO on the first access + + if ( ! geometryGroup.__webglVertexBuffer ) { + + createMeshBuffers( geometryGroup ); + initMeshBuffers( geometryGroup, object ); + + geometry.verticesNeedUpdate = true; + geometry.morphTargetsNeedUpdate = true; + geometry.elementsNeedUpdate = true; + geometry.uvsNeedUpdate = true; + geometry.normalsNeedUpdate = true; + geometry.tangentsNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + } + + } + + } else if ( geometry instanceof THREE.BufferGeometry ) { + + initDirectBuffers( geometry ); + + } + + } else if ( object instanceof THREE.Ribbon ) { + + geometry = object.geometry; + + if( ! geometry.__webglVertexBuffer ) { + + createRibbonBuffers( geometry ); + initRibbonBuffers( geometry ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + } + + } else if ( object instanceof THREE.Line ) { + + geometry = object.geometry; + + if( ! geometry.__webglVertexBuffer ) { + + createLineBuffers( geometry ); + initLineBuffers( geometry, object ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + } + + } else if ( object instanceof THREE.ParticleSystem ) { + + geometry = object.geometry; + + if ( ! geometry.__webglVertexBuffer ) { + + if ( geometry instanceof THREE.Geometry ) { + + createParticleBuffers( geometry ); + initParticleBuffers( geometry, object ); + + geometry.verticesNeedUpdate = true; + geometry.colorsNeedUpdate = true; + + } else if ( geometry instanceof THREE.BufferGeometry ) { + + initDirectBuffers( geometry ); + + } + + + } + + } + + } + + if ( ! object.__webglActive ) { + + if ( object instanceof THREE.Mesh ) { + + geometry = object.geometry; + + if ( geometry instanceof THREE.BufferGeometry ) { + + addBuffer( scene.__webglObjects, geometry, object ); + + } else { + + for ( g in geometry.geometryGroups ) { + + geometryGroup = geometry.geometryGroups[ g ]; + + addBuffer( scene.__webglObjects, geometryGroup, object ); + + } + + } + + } else if ( object instanceof THREE.Ribbon || + object instanceof THREE.Line || + object instanceof THREE.ParticleSystem ) { + + geometry = object.geometry; + addBuffer( scene.__webglObjects, geometry, object ); + + } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { + + addBufferImmediate( scene.__webglObjectsImmediate, object ); + + } else if ( object instanceof THREE.Sprite ) { + + scene.__webglSprites.push( object ); + + } else if ( object instanceof THREE.LensFlare ) { + + scene.__webglFlares.push( object ); + + } + + object.__webglActive = true; + + } + + }; + + function addBuffer ( objlist, buffer, object ) { + + objlist.push( + { + buffer: buffer, + object: object, + opaque: null, + transparent: null + } + ); + + }; + + function addBufferImmediate ( objlist, object ) { + + objlist.push( + { + object: object, + opaque: null, + transparent: null + } + ); + + }; + + // Objects updates + + function updateObject ( object ) { + + var geometry = object.geometry, + geometryGroup, customAttributesDirty, material; + + if ( object instanceof THREE.Mesh ) { + + if ( geometry instanceof THREE.BufferGeometry ) { + + if ( geometry.verticesNeedUpdate || geometry.elementsNeedUpdate || + geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || + geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate ) { + + setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic ); + + } + + geometry.verticesNeedUpdate = false; + geometry.elementsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.tangentsNeedUpdate = false; + + } else { + + // check all geometry groups + + for( var i = 0, il = geometry.geometryGroupsList.length; i < il; i ++ ) { + + geometryGroup = geometry.geometryGroupsList[ i ]; + + material = getBufferMaterial( object, geometryGroup ); + + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.morphTargetsNeedUpdate || geometry.elementsNeedUpdate || + geometry.uvsNeedUpdate || geometry.normalsNeedUpdate || + geometry.colorsNeedUpdate || geometry.tangentsNeedUpdate || customAttributesDirty ) { + + setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW, !geometry.dynamic, material ); + + } + + } + + geometry.verticesNeedUpdate = false; + geometry.morphTargetsNeedUpdate = false; + geometry.elementsNeedUpdate = false; + geometry.uvsNeedUpdate = false; + geometry.normalsNeedUpdate = false; + geometry.colorsNeedUpdate = false; + geometry.tangentsNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + } + + } else if ( object instanceof THREE.Ribbon ) { + + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate ) { + + setRibbonBuffers( geometry, _gl.DYNAMIC_DRAW ); + + } + + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + + } else if ( object instanceof THREE.Line ) { + + material = getBufferMaterial( object, geometryGroup ); + + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || customAttributesDirty ) { + + setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); + + } + + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + } else if ( object instanceof THREE.ParticleSystem ) { + + if ( geometry instanceof THREE.BufferGeometry ) { + + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate ) { + + setDirectBuffers( geometry, _gl.DYNAMIC_DRAW, !geometry.dynamic ); + + } + + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + + } else { + + material = getBufferMaterial( object, geometryGroup ); + + customAttributesDirty = material.attributes && areCustomAttributesDirty( material ); + + if ( geometry.verticesNeedUpdate || geometry.colorsNeedUpdate || object.sortParticles || customAttributesDirty ) { + + setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); + + } + + geometry.verticesNeedUpdate = false; + geometry.colorsNeedUpdate = false; + + material.attributes && clearCustomAttributes( material ); + + } + + } + + }; + + // Objects updates - custom attributes check + + function areCustomAttributesDirty ( material ) { + + for ( var a in material.attributes ) { + + if ( material.attributes[ a ].needsUpdate ) return true; + + } + + return false; + + }; + + function clearCustomAttributes ( material ) { + + for ( var a in material.attributes ) { + + material.attributes[ a ].needsUpdate = false; + + } + + }; + + // Objects removal + + function removeObject ( object, scene ) { + + if ( object instanceof THREE.Mesh || + object instanceof THREE.ParticleSystem || + object instanceof THREE.Ribbon || + object instanceof THREE.Line ) { + + removeInstances( scene.__webglObjects, object ); + + } else if ( object instanceof THREE.Sprite ) { + + removeInstancesDirect( scene.__webglSprites, object ); + + } else if ( object instanceof THREE.LensFlare ) { + + removeInstancesDirect( scene.__webglFlares, object ); + + } else if ( object instanceof THREE.ImmediateRenderObject || object.immediateRenderCallback ) { + + removeInstances( scene.__webglObjectsImmediate, object ); + + } + + object.__webglActive = false; + + }; + + function removeInstances ( objlist, object ) { + + for ( var o = objlist.length - 1; o >= 0; o -- ) { + + if ( objlist[ o ].object === object ) { + + objlist.splice( o, 1 ); + + } + + } + + }; + + function removeInstancesDirect ( objlist, object ) { + + for ( var o = objlist.length - 1; o >= 0; o -- ) { + + if ( objlist[ o ] === object ) { + + objlist.splice( o, 1 ); + + } + + } + + }; + + // Materials + + this.initMaterial = function ( material, lights, fog, object ) { + + var u, a, identifiers, i, parameters, maxLightCount, maxBones, maxShadows, shaderID; + + if ( material instanceof THREE.MeshDepthMaterial ) { + + shaderID = 'depth'; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + shaderID = 'normal'; + + } else if ( material instanceof THREE.MeshBasicMaterial ) { + + shaderID = 'basic'; + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + shaderID = 'lambert'; + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + shaderID = 'phong'; + + } else if ( material instanceof THREE.LineBasicMaterial ) { + + shaderID = 'basic'; + + } else if ( material instanceof THREE.ParticleBasicMaterial ) { + + shaderID = 'particle_basic'; + + } + + if ( shaderID ) { + + setMaterialShaders( material, THREE.ShaderLib[ shaderID ] ); + + } + + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + maxLightCount = allocateLights( lights ); + + maxShadows = allocateShadows( lights ); + + maxBones = allocateBones( object ); + + parameters = { + + map: !!material.map, + envMap: !!material.envMap, + lightMap: !!material.lightMap, + bumpMap: !!material.bumpMap, + normalMap: !!material.normalMap, + specularMap: !!material.specularMap, + + vertexColors: material.vertexColors, + + fog: fog, + useFog: material.fog, + + sizeAttenuation: material.sizeAttenuation, + + skinning: material.skinning, + maxBones: maxBones, + useVertexTexture: _supportsBoneTextures && object && object.useVertexTexture, + boneTextureWidth: object && object.boneTextureWidth, + boneTextureHeight: object && object.boneTextureHeight, + + morphTargets: material.morphTargets, + morphNormals: material.morphNormals, + maxMorphTargets: this.maxMorphTargets, + maxMorphNormals: this.maxMorphNormals, + + maxDirLights: maxLightCount.directional, + maxPointLights: maxLightCount.point, + maxSpotLights: maxLightCount.spot, + maxHemiLights: maxLightCount.hemi, + + maxShadows: maxShadows, + shadowMapEnabled: this.shadowMapEnabled && object.receiveShadow, + shadowMapSoft: this.shadowMapSoft, + shadowMapDebug: this.shadowMapDebug, + shadowMapCascade: this.shadowMapCascade, + + alphaTest: material.alphaTest, + metal: material.metal, + perPixel: material.perPixel, + wrapAround: material.wrapAround, + doubleSided: material.side === THREE.DoubleSide, + flipSided: material.side === THREE.BackSide + + }; + + material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, material.defines, parameters ); + + var attributes = material.program.attributes; + + if ( attributes.position >= 0 ) _gl.enableVertexAttribArray( attributes.position ); + if ( attributes.color >= 0 ) _gl.enableVertexAttribArray( attributes.color ); + if ( attributes.normal >= 0 ) _gl.enableVertexAttribArray( attributes.normal ); + if ( attributes.tangent >= 0 ) _gl.enableVertexAttribArray( attributes.tangent ); + + if ( material.skinning && + attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + + _gl.enableVertexAttribArray( attributes.skinIndex ); + _gl.enableVertexAttribArray( attributes.skinWeight ); + + } + + if ( material.attributes ) { + + for ( a in material.attributes ) { + + if( attributes[ a ] !== undefined && attributes[ a ] >= 0 ) _gl.enableVertexAttribArray( attributes[ a ] ); + + } + + } + + if ( material.morphTargets ) { + + material.numSupportedMorphTargets = 0; + + var id, base = "morphTarget"; + + for ( i = 0; i < this.maxMorphTargets; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + _gl.enableVertexAttribArray( attributes[ id ] ); + material.numSupportedMorphTargets ++; + + } + + } + + } + + if ( material.morphNormals ) { + + material.numSupportedMorphNormals = 0; + + var id, base = "morphNormal"; + + for ( i = 0; i < this.maxMorphNormals; i ++ ) { + + id = base + i; + + if ( attributes[ id ] >= 0 ) { + + _gl.enableVertexAttribArray( attributes[ id ] ); + material.numSupportedMorphNormals ++; + + } + + } + + } + + material.uniformsList = []; + + for ( u in material.uniforms ) { + + material.uniformsList.push( [ material.uniforms[ u ], u ] ); + + } + + }; + + function setMaterialShaders( material, shaders ) { + + material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms ); + material.vertexShader = shaders.vertexShader; + material.fragmentShader = shaders.fragmentShader; + + }; + + function setProgram( camera, lights, fog, material, object ) { + + _usedTextureUnits = 0; + + if ( material.needsUpdate ) { + + if ( material.program ) _this.deallocateMaterial( material ); + + _this.initMaterial( material, lights, fog, object ); + material.needsUpdate = false; + + } + + if ( material.morphTargets ) { + + if ( ! object.__webglMorphTargetInfluences ) { + + object.__webglMorphTargetInfluences = new Float32Array( _this.maxMorphTargets ); + + } + + } + + var refreshMaterial = false; + + var program = material.program, + p_uniforms = program.uniforms, + m_uniforms = material.uniforms; + + if ( program !== _currentProgram ) { + + _gl.useProgram( program ); + _currentProgram = program; + + refreshMaterial = true; + + } + + if ( material.id !== _currentMaterialId ) { + + _currentMaterialId = material.id; + refreshMaterial = true; + + } + + if ( refreshMaterial || camera !== _currentCamera ) { + + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, camera._projectionMatrixArray ); + + if ( camera !== _currentCamera ) _currentCamera = camera; + + } + + // skinning uniforms must be set even if material didn't change + // auto-setting of texture unit for bone texture must go before other textures + // not sure why, but otherwise weird things happen + + if ( material.skinning ) { + + if ( _supportsBoneTextures && object.useVertexTexture ) { + + if ( p_uniforms.boneTexture !== null ) { + + var textureUnit = getTextureUnit(); + + _gl.uniform1i( p_uniforms.boneTexture, textureUnit ); + _this.setTexture( object.boneTexture, textureUnit ); + + } + + } else { + + if ( p_uniforms.boneGlobalMatrices !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.boneGlobalMatrices, false, object.boneMatrices ); + + } + + } + + } + + if ( refreshMaterial ) { + + // refresh uniforms common to several materials + + if ( fog && material.fog ) { + + refreshUniformsFog( m_uniforms, fog ); + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { + + if ( _lightsNeedUpdate ) { + + setupLights( program, lights ); + _lightsNeedUpdate = false; + + } + + refreshUniformsLights( m_uniforms, _lights ); + + } + + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsCommon( m_uniforms, material ); + + } + + // refresh single material specific uniforms + + if ( material instanceof THREE.LineBasicMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + + } else if ( material instanceof THREE.ParticleBasicMaterial ) { + + refreshUniformsParticle( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsPhong( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + refreshUniformsLambert( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + m_uniforms.opacity.value = material.opacity; + + } + + if ( object.receiveShadow && ! material._shadowPass ) { + + refreshUniformsShadow( m_uniforms, lights ); + + } + + // load common uniforms + + loadUniformsGeneric( program, material.uniformsList ); + + // load material specific uniforms + // (shader material also gets them for the sake of genericity) + + if ( material instanceof THREE.ShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { + + if ( p_uniforms.cameraPosition !== null ) { + + var position = camera.matrixWorld.getPosition(); + _gl.uniform3f( p_uniforms.cameraPosition, position.x, position.y, position.z ); + + } + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.ShaderMaterial || + material.skinning ) { + + if ( p_uniforms.viewMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, camera._viewMatrixArray ); + + } + + } + + } + + loadUniformsMatrices( p_uniforms, object ); + + if ( p_uniforms.modelMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.modelMatrix, false, object.matrixWorld.elements ); + + } + + return program; + + }; + + // Uniforms (refresh uniforms objects) + + function refreshUniformsCommon ( uniforms, material ) { + + uniforms.opacity.value = material.opacity; + + if ( _this.gammaInput ) { + + uniforms.diffuse.value.copyGammaToLinear( material.color ); + + } else { + + uniforms.diffuse.value = material.color; + + } + + uniforms.map.value = material.map; + uniforms.lightMap.value = material.lightMap; + uniforms.specularMap.value = material.specularMap; + + if ( material.bumpMap ) { + + uniforms.bumpMap.value = material.bumpMap; + uniforms.bumpScale.value = material.bumpScale; + + } + + if ( material.normalMap ) { + + uniforms.normalMap.value = material.normalMap; + uniforms.normalScale.value.copy( material.normalScale ); + + } + + // uv repeat and offset setting priorities + // 1. color map + // 2. specular map + // 3. normal map + // 4. bump map + + var uvScaleMap; + + if ( material.map ) { + + uvScaleMap = material.map; + + } else if ( material.specularMap ) { + + uvScaleMap = material.specularMap; + + } else if ( material.normalMap ) { + + uvScaleMap = material.normalMap; + + } else if ( material.bumpMap ) { + + uvScaleMap = material.bumpMap; + + } + + if ( uvScaleMap !== undefined ) { + + var offset = uvScaleMap.offset; + var repeat = uvScaleMap.repeat; + + uniforms.offsetRepeat.value.set( offset.x, offset.y, repeat.x, repeat.y ); + + } + + uniforms.envMap.value = material.envMap; + uniforms.flipEnvMap.value = ( material.envMap instanceof THREE.WebGLRenderTargetCube ) ? 1 : -1; + + if ( _this.gammaInput ) { + + //uniforms.reflectivity.value = material.reflectivity * material.reflectivity; + uniforms.reflectivity.value = material.reflectivity; + + } else { + + uniforms.reflectivity.value = material.reflectivity; + + } + + uniforms.refractionRatio.value = material.refractionRatio; + uniforms.combine.value = material.combine; + uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping; + + }; + + function refreshUniformsLine ( uniforms, material ) { + + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + + }; + + function refreshUniformsParticle ( uniforms, material ) { + + uniforms.psColor.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size; + uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + + uniforms.map.value = material.map; + + }; + + function refreshUniformsFog ( uniforms, fog ) { + + uniforms.fogColor.value = fog.color; + + if ( fog instanceof THREE.Fog ) { + + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; + + } else if ( fog instanceof THREE.FogExp2 ) { + + uniforms.fogDensity.value = fog.density; + + } + + }; + + function refreshUniformsPhong ( uniforms, material ) { + + uniforms.shininess.value = material.shininess; + + if ( _this.gammaInput ) { + + uniforms.ambient.value.copyGammaToLinear( material.ambient ); + uniforms.emissive.value.copyGammaToLinear( material.emissive ); + uniforms.specular.value.copyGammaToLinear( material.specular ); + + } else { + + uniforms.ambient.value = material.ambient; + uniforms.emissive.value = material.emissive; + uniforms.specular.value = material.specular; + + } + + if ( material.wrapAround ) { + + uniforms.wrapRGB.value.copy( material.wrapRGB ); + + } + + }; + + function refreshUniformsLambert ( uniforms, material ) { + + if ( _this.gammaInput ) { + + uniforms.ambient.value.copyGammaToLinear( material.ambient ); + uniforms.emissive.value.copyGammaToLinear( material.emissive ); + + } else { + + uniforms.ambient.value = material.ambient; + uniforms.emissive.value = material.emissive; + + } + + if ( material.wrapAround ) { + + uniforms.wrapRGB.value.copy( material.wrapRGB ); + + } + + }; + + function refreshUniformsLights ( uniforms, lights ) { + + uniforms.ambientLightColor.value = lights.ambient; + + uniforms.directionalLightColor.value = lights.directional.colors; + uniforms.directionalLightDirection.value = lights.directional.positions; + + uniforms.pointLightColor.value = lights.point.colors; + uniforms.pointLightPosition.value = lights.point.positions; + uniforms.pointLightDistance.value = lights.point.distances; + + uniforms.spotLightColor.value = lights.spot.colors; + uniforms.spotLightPosition.value = lights.spot.positions; + uniforms.spotLightDistance.value = lights.spot.distances; + uniforms.spotLightDirection.value = lights.spot.directions; + uniforms.spotLightAngle.value = lights.spot.angles; + uniforms.spotLightExponent.value = lights.spot.exponents; + + uniforms.hemisphereLightSkyColor.value = lights.hemi.skyColors; + uniforms.hemisphereLightGroundColor.value = lights.hemi.groundColors; + uniforms.hemisphereLightPosition.value = lights.hemi.positions; + + }; + + function refreshUniformsShadow ( uniforms, lights ) { + + if ( uniforms.shadowMatrix ) { + + var j = 0; + + for ( var i = 0, il = lights.length; i < il; i ++ ) { + + var light = lights[ i ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight || ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) ) { + + uniforms.shadowMap.value[ j ] = light.shadowMap; + uniforms.shadowMapSize.value[ j ] = light.shadowMapSize; + + uniforms.shadowMatrix.value[ j ] = light.shadowMatrix; + + uniforms.shadowDarkness.value[ j ] = light.shadowDarkness; + uniforms.shadowBias.value[ j ] = light.shadowBias; + + j ++; + + } + + } + + } + + }; + + // Uniforms (load to GPU) + + function loadUniformsMatrices ( uniforms, object ) { + + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrix.elements ); + + if ( uniforms.normalMatrix ) { + + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrix.elements ); + + } + + }; + + function getTextureUnit() { + + var textureUnit = _usedTextureUnits; + + if ( textureUnit >= _maxTextures ) { + + console.warn( "Trying to use " + textureUnit + " texture units while this GPU supports only " + _maxTextures ); + + } + + _usedTextureUnits += 1; + + return textureUnit; + + }; + + function loadUniformsGeneric ( program, uniforms ) { + + var uniform, value, type, location, texture, textureUnit, i, il, j, jl, offset; + + for ( j = 0, jl = uniforms.length; j < jl; j ++ ) { + + location = program.uniforms[ uniforms[ j ][ 1 ] ]; + if ( !location ) continue; + + uniform = uniforms[ j ][ 0 ]; + + type = uniform.type; + value = uniform.value; + + if ( type === "i" ) { // single integer + + _gl.uniform1i( location, value ); + + } else if ( type === "f" ) { // single float + + _gl.uniform1f( location, value ); + + } else if ( type === "v2" ) { // single THREE.Vector2 + + _gl.uniform2f( location, value.x, value.y ); + + } else if ( type === "v3" ) { // single THREE.Vector3 + + _gl.uniform3f( location, value.x, value.y, value.z ); + + } else if ( type === "v4" ) { // single THREE.Vector4 + + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + + } else if ( type === "c" ) { // single THREE.Color + + _gl.uniform3f( location, value.r, value.g, value.b ); + + } else if ( type === "iv1" ) { // flat array of integers (JS or typed array) + + _gl.uniform1iv( location, value ); + + } else if ( type === "iv" ) { // flat array of integers with 3 x N size (JS or typed array) + + _gl.uniform3iv( location, value ); + + } else if ( type === "fv1" ) { // flat array of floats (JS or typed array) + + _gl.uniform1fv( location, value ); + + } else if ( type === "fv" ) { // flat array of floats with 3 x N size (JS or typed array) + + _gl.uniform3fv( location, value ); + + } else if ( type === "v2v" ) { // array of THREE.Vector2 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 2 * value.length ); + + } + + for ( i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 2; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + + } + + _gl.uniform2fv( location, uniform._array ); + + } else if ( type === "v3v" ) { // array of THREE.Vector3 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 3 * value.length ); + + } + + for ( i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 3; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + + } + + _gl.uniform3fv( location, uniform._array ); + + } else if ( type === "v4v" ) { // array of THREE.Vector4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 4 * value.length ); + + } + + for ( i = 0, il = value.length; i < il; i ++ ) { + + offset = i * 4; + + uniform._array[ offset ] = value[ i ].x; + uniform._array[ offset + 1 ] = value[ i ].y; + uniform._array[ offset + 2 ] = value[ i ].z; + uniform._array[ offset + 3 ] = value[ i ].w; + + } + + _gl.uniform4fv( location, uniform._array ); + + } else if ( type === "m4") { // single THREE.Matrix4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 16 ); + + } + + value.flattenToArray( uniform._array ); + _gl.uniformMatrix4fv( location, false, uniform._array ); + + } else if ( type === "m4v" ) { // array of THREE.Matrix4 + + if ( uniform._array === undefined ) { + + uniform._array = new Float32Array( 16 * value.length ); + + } + + for ( i = 0, il = value.length; i < il; i ++ ) { + + value[ i ].flattenToArrayOffset( uniform._array, i * 16 ); + + } + + _gl.uniformMatrix4fv( location, false, uniform._array ); + + } else if ( type === "t" ) { // single THREE.Texture (2d or cube) + + texture = value; + textureUnit = getTextureUnit(); + + _gl.uniform1i( location, textureUnit ); + + if ( !texture ) continue; + + if ( texture.image instanceof Array && texture.image.length === 6 ) { + + setCubeTexture( texture, textureUnit ); + + } else if ( texture instanceof THREE.WebGLRenderTargetCube ) { + + setCubeTextureDynamic( texture, textureUnit ); + + } else { + + _this.setTexture( texture, textureUnit ); + + } + + } else if ( type === "tv" ) { // array of THREE.Texture (2d) + + if ( uniform._array === undefined ) { + + uniform._array = []; + + } + + for( i = 0, il = uniform.value.length; i < il; i ++ ) { + + uniform._array[ i ] = getTextureUnit(); + + } + + _gl.uniform1iv( location, uniform._array ); + + for( i = 0, il = uniform.value.length; i < il; i ++ ) { + + texture = uniform.value[ i ]; + textureUnit = uniform._array[ i ]; + + if ( !texture ) continue; + + _this.setTexture( texture, textureUnit ); + + } + + } + + } + + }; + + function setupMatrices ( object, camera ) { + + object._modelViewMatrix.multiply( camera.matrixWorldInverse, object.matrixWorld ); + + object._normalMatrix.getInverse( object._modelViewMatrix ); + object._normalMatrix.transpose(); + + }; + + // + + function setColorGamma( array, offset, color, intensitySq ) { + + array[ offset ] = color.r * color.r * intensitySq; + array[ offset + 1 ] = color.g * color.g * intensitySq; + array[ offset + 2 ] = color.b * color.b * intensitySq; + + }; + + function setColorLinear( array, offset, color, intensity ) { + + array[ offset ] = color.r * intensity; + array[ offset + 1 ] = color.g * intensity; + array[ offset + 2 ] = color.b * intensity; + + }; + + function setupLights ( program, lights ) { + + var l, ll, light, n, + r = 0, g = 0, b = 0, + color, skyColor, groundColor, + intensity, intensitySq, + position, + distance, + + zlights = _lights, + + dirColors = zlights.directional.colors, + dirPositions = zlights.directional.positions, + + pointColors = zlights.point.colors, + pointPositions = zlights.point.positions, + pointDistances = zlights.point.distances, + + spotColors = zlights.spot.colors, + spotPositions = zlights.spot.positions, + spotDistances = zlights.spot.distances, + spotDirections = zlights.spot.directions, + spotAngles = zlights.spot.angles, + spotExponents = zlights.spot.exponents, + + hemiSkyColors = zlights.hemi.skyColors, + hemiGroundColors = zlights.hemi.groundColors, + hemiPositions = zlights.hemi.positions, + + dirLength = 0, + pointLength = 0, + spotLength = 0, + hemiLength = 0, + + dirOffset = 0, + pointOffset = 0, + spotOffset = 0, + hemiOffset = 0; + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + + if ( light.onlyShadow || ! light.visible ) continue; + + color = light.color; + intensity = light.intensity; + distance = light.distance; + + if ( light instanceof THREE.AmbientLight ) { + + if ( _this.gammaInput ) { + + r += color.r * color.r; + g += color.g * color.g; + b += color.b * color.b; + + } else { + + r += color.r; + g += color.g; + b += color.b; + + } + + } else if ( light instanceof THREE.DirectionalLight ) { + + dirOffset = dirLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( dirColors, dirOffset, color, intensity * intensity ); + + } else { + + setColorLinear( dirColors, dirOffset, color, intensity ); + + } + + _direction.copy( light.matrixWorld.getPosition() ); + _direction.subSelf( light.target.matrixWorld.getPosition() ); + _direction.normalize(); + + dirPositions[ dirOffset ] = _direction.x; + dirPositions[ dirOffset + 1 ] = _direction.y; + dirPositions[ dirOffset + 2 ] = _direction.z; + + dirLength += 1; + + } else if( light instanceof THREE.PointLight ) { + + pointOffset = pointLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( pointColors, pointOffset, color, intensity * intensity ); + + } else { + + setColorLinear( pointColors, pointOffset, color, intensity ); + + } + + position = light.matrixWorld.getPosition(); + + pointPositions[ pointOffset ] = position.x; + pointPositions[ pointOffset + 1 ] = position.y; + pointPositions[ pointOffset + 2 ] = position.z; + + pointDistances[ pointLength ] = distance; + + pointLength += 1; + + } else if( light instanceof THREE.SpotLight ) { + + spotOffset = spotLength * 3; + + if ( _this.gammaInput ) { + + setColorGamma( spotColors, spotOffset, color, intensity * intensity ); + + } else { + + setColorLinear( spotColors, spotOffset, color, intensity ); + + } + + position = light.matrixWorld.getPosition(); + + spotPositions[ spotOffset ] = position.x; + spotPositions[ spotOffset + 1 ] = position.y; + spotPositions[ spotOffset + 2 ] = position.z; + + spotDistances[ spotLength ] = distance; + + _direction.copy( position ); + _direction.subSelf( light.target.matrixWorld.getPosition() ); + _direction.normalize(); + + spotDirections[ spotOffset ] = _direction.x; + spotDirections[ spotOffset + 1 ] = _direction.y; + spotDirections[ spotOffset + 2 ] = _direction.z; + + spotAngles[ spotLength ] = Math.cos( light.angle ); + spotExponents[ spotLength ] = light.exponent; + + spotLength += 1; + + } else if ( light instanceof THREE.HemisphereLight ) { + + skyColor = light.color; + groundColor = light.groundColor; + + hemiOffset = hemiLength * 3; + + if ( _this.gammaInput ) { + + intensitySq = intensity * intensity; + + setColorGamma( hemiSkyColors, hemiOffset, skyColor, intensitySq ); + setColorGamma( hemiGroundColors, hemiOffset, groundColor, intensitySq ); + + } else { + + setColorLinear( hemiSkyColors, hemiOffset, skyColor, intensity ); + setColorLinear( hemiGroundColors, hemiOffset, groundColor, intensity ); + + } + + position = light.matrixWorld.getPosition(); + + hemiPositions[ hemiOffset ] = position.x; + hemiPositions[ hemiOffset + 1 ] = position.y; + hemiPositions[ hemiOffset + 2 ] = position.z; + + hemiLength += 1; + + } + + } + + // null eventual remains from removed lights + // (this is to avoid if in shader) + + for ( l = dirLength * 3, ll = dirColors.length; l < ll; l ++ ) dirColors[ l ] = 0.0; + for ( l = pointLength * 3, ll = pointColors.length; l < ll; l ++ ) pointColors[ l ] = 0.0; + for ( l = spotLength * 3, ll = spotColors.length; l < ll; l ++ ) spotColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = hemiSkyColors.length; l < ll; l ++ ) hemiSkyColors[ l ] = 0.0; + for ( l = hemiLength * 3, ll = hemiGroundColors.length; l < ll; l ++ ) hemiGroundColors[ l ] = 0.0; + + zlights.directional.length = dirLength; + zlights.point.length = pointLength; + zlights.spot.length = spotLength; + zlights.hemi.length = hemiLength; + + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; + + }; + + // GL state setting + + this.setFaceCulling = function ( cullFace, frontFace ) { + + if ( cullFace ) { + + if ( !frontFace || frontFace === "ccw" ) { + + _gl.frontFace( _gl.CCW ); + + } else { + + _gl.frontFace( _gl.CW ); + + } + + if( cullFace === "back" ) { + + _gl.cullFace( _gl.BACK ); + + } else if( cullFace === "front" ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.FRONT_AND_BACK ); + + } + + _gl.enable( _gl.CULL_FACE ); + + } else { + + _gl.disable( _gl.CULL_FACE ); + + } + + }; + + this.setMaterialFaces = function ( material ) { + + var doubleSided = material.side === THREE.DoubleSide; + var flipSided = material.side === THREE.BackSide; + + if ( _oldDoubleSided !== doubleSided ) { + + if ( doubleSided ) { + + _gl.disable( _gl.CULL_FACE ); + + } else { + + _gl.enable( _gl.CULL_FACE ); + + } + + _oldDoubleSided = doubleSided; + + } + + if ( _oldFlipSided !== flipSided ) { + + if ( flipSided ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + _oldFlipSided = flipSided; + + } + + }; + + this.setDepthTest = function ( depthTest ) { + + if ( _oldDepthTest !== depthTest ) { + + if ( depthTest ) { + + _gl.enable( _gl.DEPTH_TEST ); + + } else { + + _gl.disable( _gl.DEPTH_TEST ); + + } + + _oldDepthTest = depthTest; + + } + + }; + + this.setDepthWrite = function ( depthWrite ) { + + if ( _oldDepthWrite !== depthWrite ) { + + _gl.depthMask( depthWrite ); + _oldDepthWrite = depthWrite; + + } + + }; + + function setLineWidth ( width ) { + + if ( width !== _oldLineWidth ) { + + _gl.lineWidth( width ); + + _oldLineWidth = width; + + } + + }; + + function setPolygonOffset ( polygonoffset, factor, units ) { + + if ( _oldPolygonOffset !== polygonoffset ) { + + if ( polygonoffset ) { + + _gl.enable( _gl.POLYGON_OFFSET_FILL ); + + } else { + + _gl.disable( _gl.POLYGON_OFFSET_FILL ); + + } + + _oldPolygonOffset = polygonoffset; + + } + + if ( polygonoffset && ( _oldPolygonOffsetFactor !== factor || _oldPolygonOffsetUnits !== units ) ) { + + _gl.polygonOffset( factor, units ); + + _oldPolygonOffsetFactor = factor; + _oldPolygonOffsetUnits = units; + + } + + }; + + this.setBlending = function ( blending, blendEquation, blendSrc, blendDst ) { + + if ( blending !== _oldBlending ) { + + if ( blending === THREE.NoBlending ) { + + _gl.disable( _gl.BLEND ); + + } else if ( blending === THREE.AdditiveBlending ) { + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); + + } else if ( blending === THREE.SubtractiveBlending ) { + + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); + + } else if ( blending === THREE.MultiplyBlending ) { + + // TODO: Find blendFuncSeparate() combination + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); + + } else if ( blending === THREE.CustomBlending ) { + + _gl.enable( _gl.BLEND ); + + } else { + + _gl.enable( _gl.BLEND ); + _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); + _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); + + } + + _oldBlending = blending; + + } + + if ( blending === THREE.CustomBlending ) { + + if ( blendEquation !== _oldBlendEquation ) { + + _gl.blendEquation( paramThreeToGL( blendEquation ) ); + + _oldBlendEquation = blendEquation; + + } + + if ( blendSrc !== _oldBlendSrc || blendDst !== _oldBlendDst ) { + + _gl.blendFunc( paramThreeToGL( blendSrc ), paramThreeToGL( blendDst ) ); + + _oldBlendSrc = blendSrc; + _oldBlendDst = blendDst; + + } + + } else { + + _oldBlendEquation = null; + _oldBlendSrc = null; + _oldBlendDst = null; + + } + + }; + + // Defines + + function generateDefines ( defines ) { + + var value, chunk, chunks = []; + + for ( var d in defines ) { + + value = defines[ d ]; + if ( value === false ) continue; + + chunk = "#define " + d + " " + value; + chunks.push( chunk ); + + } + + return chunks.join( "\n" ); + + }; + + // Shaders + + function buildProgram ( shaderID, fragmentShader, vertexShader, uniforms, attributes, defines, parameters ) { + + var p, pl, d, program, code; + var chunks = []; + + // Generate code + + if ( shaderID ) { + + chunks.push( shaderID ); + + } else { + + chunks.push( fragmentShader ); + chunks.push( vertexShader ); + + } + + for ( d in defines ) { + + chunks.push( d ); + chunks.push( defines[ d ] ); + + } + + for ( p in parameters ) { + + chunks.push( p ); + chunks.push( parameters[ p ] ); + + } + + code = chunks.join(); + + // Check if code has been already compiled + + for ( p = 0, pl = _programs.length; p < pl; p ++ ) { + + var programInfo = _programs[ p ]; + + if ( programInfo.code === code ) { + + // console.log( "Code already compiled." /*: \n\n" + code*/ ); + + programInfo.usedTimes ++; + + return programInfo.program; + + } + + } + + //console.log( "building new program " ); + + // + + var customDefines = generateDefines( defines ); + + // + + program = _gl.createProgram(); + + var prefix_vertex = [ + + "precision " + _precision + " float;", + + customDefines, + + _supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", + + _this.gammaInput ? "#define GAMMA_INPUT" : "", + _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", + _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", + + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, + "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + + "#define MAX_SHADOWS " + parameters.maxShadows, + + "#define MAX_BONES " + parameters.maxBones, + + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.bumpMap ? "#define USE_BUMPMAP" : "", + parameters.normalMap ? "#define USE_NORMALMAP" : "", + parameters.specularMap ? "#define USE_SPECULARMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", + + parameters.skinning ? "#define USE_SKINNING" : "", + parameters.useVertexTexture ? "#define BONE_TEXTURE" : "", + parameters.boneTextureWidth ? "#define N_BONE_PIXEL_X " + parameters.boneTextureWidth.toFixed( 1 ) : "", + parameters.boneTextureHeight ? "#define N_BONE_PIXEL_Y " + parameters.boneTextureHeight.toFixed( 1 ) : "", + + parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", + parameters.morphNormals ? "#define USE_MORPHNORMALS" : "", + parameters.perPixel ? "#define PHONG_PER_PIXEL" : "", + parameters.wrapAround ? "#define WRAP_AROUND" : "", + parameters.doubleSided ? "#define DOUBLE_SIDED" : "", + parameters.flipSided ? "#define FLIP_SIDED" : "", + + parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", + parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "", + parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", + parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + + parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", + + "uniform mat4 modelMatrix;", + "uniform mat4 modelViewMatrix;", + "uniform mat4 projectionMatrix;", + "uniform mat4 viewMatrix;", + "uniform mat3 normalMatrix;", + "uniform vec3 cameraPosition;", + + "attribute vec3 position;", + "attribute vec3 normal;", + "attribute vec2 uv;", + "attribute vec2 uv2;", + + "#ifdef USE_COLOR", + + "attribute vec3 color;", + + "#endif", + + "#ifdef USE_MORPHTARGETS", + + "attribute vec3 morphTarget0;", + "attribute vec3 morphTarget1;", + "attribute vec3 morphTarget2;", + "attribute vec3 morphTarget3;", + + "#ifdef USE_MORPHNORMALS", + + "attribute vec3 morphNormal0;", + "attribute vec3 morphNormal1;", + "attribute vec3 morphNormal2;", + "attribute vec3 morphNormal3;", + + "#else", + + "attribute vec3 morphTarget4;", + "attribute vec3 morphTarget5;", + "attribute vec3 morphTarget6;", + "attribute vec3 morphTarget7;", + + "#endif", + + "#endif", + + "#ifdef USE_SKINNING", + + "attribute vec4 skinIndex;", + "attribute vec4 skinWeight;", + + "#endif", + + "" + + ].join("\n"); + + var prefix_fragment = [ + + "precision " + _precision + " float;", + + ( parameters.bumpMap || parameters.normalMap ) ? "#extension GL_OES_standard_derivatives : enable" : "", + + customDefines, + + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + "#define MAX_SPOT_LIGHTS " + parameters.maxSpotLights, + "#define MAX_HEMI_LIGHTS " + parameters.maxHemiLights, + + "#define MAX_SHADOWS " + parameters.maxShadows, + + parameters.alphaTest ? "#define ALPHATEST " + parameters.alphaTest: "", + + _this.gammaInput ? "#define GAMMA_INPUT" : "", + _this.gammaOutput ? "#define GAMMA_OUTPUT" : "", + _this.physicallyBasedShading ? "#define PHYSICALLY_BASED_SHADING" : "", + + ( parameters.useFog && parameters.fog ) ? "#define USE_FOG" : "", + ( parameters.useFog && parameters.fog instanceof THREE.FogExp2 ) ? "#define FOG_EXP2" : "", + + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.bumpMap ? "#define USE_BUMPMAP" : "", + parameters.normalMap ? "#define USE_NORMALMAP" : "", + parameters.specularMap ? "#define USE_SPECULARMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", + + parameters.metal ? "#define METAL" : "", + parameters.perPixel ? "#define PHONG_PER_PIXEL" : "", + parameters.wrapAround ? "#define WRAP_AROUND" : "", + parameters.doubleSided ? "#define DOUBLE_SIDED" : "", + parameters.flipSided ? "#define FLIP_SIDED" : "", + + parameters.shadowMapEnabled ? "#define USE_SHADOWMAP" : "", + parameters.shadowMapSoft ? "#define SHADOWMAP_SOFT" : "", + parameters.shadowMapDebug ? "#define SHADOWMAP_DEBUG" : "", + parameters.shadowMapCascade ? "#define SHADOWMAP_CASCADE" : "", + + "uniform mat4 viewMatrix;", + "uniform vec3 cameraPosition;", + "" + + ].join("\n"); + + var glFragmentShader = getShader( "fragment", prefix_fragment + fragmentShader ); + var glVertexShader = getShader( "vertex", prefix_vertex + vertexShader ); + + _gl.attachShader( program, glVertexShader ); + _gl.attachShader( program, glFragmentShader ); + + _gl.linkProgram( program ); + + if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) { + + console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" ); + + } + + // clean up + + _gl.deleteShader( glFragmentShader ); + _gl.deleteShader( glVertexShader ); + + //console.log( prefix_fragment + fragmentShader ); + //console.log( prefix_vertex + vertexShader ); + + program.uniforms = {}; + program.attributes = {}; + + var identifiers, u, a, i; + + // cache uniform locations + + identifiers = [ + + 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'modelMatrix', 'cameraPosition', + 'morphTargetInfluences' + + ]; + + if ( parameters.useVertexTexture ) { + + identifiers.push( 'boneTexture' ); + + } else { + + identifiers.push( 'boneGlobalMatrices' ); + + } + + for ( u in uniforms ) { + + identifiers.push( u ); + + } + + cacheUniformLocations( program, identifiers ); + + // cache attributes locations + + identifiers = [ + + "position", "normal", "uv", "uv2", "tangent", "color", + "skinIndex", "skinWeight" + + ]; + + for ( i = 0; i < parameters.maxMorphTargets; i ++ ) { + + identifiers.push( "morphTarget" + i ); + + } + + for ( i = 0; i < parameters.maxMorphNormals; i ++ ) { + + identifiers.push( "morphNormal" + i ); + + } + + for ( a in attributes ) { + + identifiers.push( a ); + + } + + cacheAttributeLocations( program, identifiers ); + + program.id = _programs_counter ++; + + _programs.push( { program: program, code: code, usedTimes: 1 } ); + + _this.info.memory.programs = _programs.length; + + return program; + + }; + + // Shader parameters cache + + function cacheUniformLocations ( program, identifiers ) { + + var i, l, id; + + for( i = 0, l = identifiers.length; i < l; i ++ ) { + + id = identifiers[ i ]; + program.uniforms[ id ] = _gl.getUniformLocation( program, id ); + + } + + }; + + function cacheAttributeLocations ( program, identifiers ) { + + var i, l, id; + + for( i = 0, l = identifiers.length; i < l; i ++ ) { + + id = identifiers[ i ]; + program.attributes[ id ] = _gl.getAttribLocation( program, id ); + + } + + }; + + function addLineNumbers ( string ) { + + var chunks = string.split( "\n" ); + + for ( var i = 0, il = chunks.length; i < il; i ++ ) { + + // Chrome reports shader errors on lines + // starting counting from 1 + + chunks[ i ] = ( i + 1 ) + ": " + chunks[ i ]; + + } + + return chunks.join( "\n" ); + + }; + + function getShader ( type, string ) { + + var shader; + + if ( type === "fragment" ) { + + shader = _gl.createShader( _gl.FRAGMENT_SHADER ); + + } else if ( type === "vertex" ) { + + shader = _gl.createShader( _gl.VERTEX_SHADER ); + + } + + _gl.shaderSource( shader, string ); + _gl.compileShader( shader ); + + if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) { + + console.error( _gl.getShaderInfoLog( shader ) ); + console.error( addLineNumbers( string ) ); + return null; + + } + + return shader; + + }; + + // Textures + + + function isPowerOfTwo ( value ) { + + return ( value & ( value - 1 ) ) === 0; + + }; + + function setTextureParameters ( textureType, texture, isImagePowerOfTwo ) { + + if ( isImagePowerOfTwo ) { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + + } else { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + + } + + if ( _glExtensionTextureFilterAnisotropic && texture.type !== THREE.FloatType ) { + + if ( texture.anisotropy > 1 || texture.__oldAnisotropy ) { + + _gl.texParameterf( textureType, _glExtensionTextureFilterAnisotropic.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, _maxAnisotropy ) ); + texture.__oldAnisotropy = texture.anisotropy; + + } + + } + + }; + + this.setTexture = function ( texture, slot ) { + + if ( texture.needsUpdate ) { + + if ( ! texture.__webglInit ) { + + texture.__webglInit = true; + texture.__webglTexture = _gl.createTexture(); + + _this.info.memory.textures ++; + + } + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha ); + + var image = texture.image, + isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_2D, texture, isImagePowerOfTwo ); + + if ( texture instanceof THREE.CompressedTexture ) { + + var mipmap, mipmaps = texture.mipmaps; + + for( var i = 0, il = mipmaps.length; i < il; i ++ ) { + + mipmap = mipmaps[ i ]; + _gl.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } + + } else if ( texture instanceof THREE.DataTexture ) { + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data ); + + } else { + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, texture.image ); + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + } else { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + } + + }; + + function clampToMaxSize ( image, maxSize ) { + + if ( image.width <= maxSize && image.height <= maxSize ) { + + return image; + + } + + // Warning: Scaling through the canvas will only work with images that use + // premultiplied alpha. + + var maxDimension = Math.max( image.width, image.height ); + var newWidth = Math.floor( image.width * maxSize / maxDimension ); + var newHeight = Math.floor( image.height * maxSize / maxDimension ); + + var canvas = document.createElement( 'canvas' ); + canvas.width = newWidth; + canvas.height = newHeight; + + var ctx = canvas.getContext( "2d" ); + ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, newWidth, newHeight ); + + return canvas; + + } + + function setCubeTexture ( texture, slot ) { + + if ( texture.image.length === 6 ) { + + if ( texture.needsUpdate ) { + + if ( ! texture.image.__webglTextureCube ) { + + texture.image.__webglTextureCube = _gl.createTexture(); + + } + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY ); + + var isCompressed = texture instanceof THREE.CompressedTexture; + + var cubeImage = []; + + for ( var i = 0; i < 6; i ++ ) { + + if ( _this.autoScaleCubemaps && ! isCompressed ) { + + cubeImage[ i ] = clampToMaxSize( texture.image[ i ], _maxCubemapSize ); + + } else { + + cubeImage[ i ] = texture.image[ i ]; + + } + + } + + var image = cubeImage[ 0 ], + isImagePowerOfTwo = isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ), + glFormat = paramThreeToGL( texture.format ), + glType = paramThreeToGL( texture.type ); + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isImagePowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + if ( isCompressed ) { + + var mipmap, mipmaps = cubeImage[ i ].mipmaps; + + for( var j = 0, jl = mipmaps.length; j < jl; j ++ ) { + + mipmap = mipmaps[ j ]; + _gl.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data ); + + } + + } else { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] ); + + } + + } + + if ( texture.generateMipmaps && isImagePowerOfTwo ) { + + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } + + texture.needsUpdate = false; + + if ( texture.onUpdate ) texture.onUpdate(); + + } else { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + } + + } + + }; + + function setCubeTextureDynamic ( texture, slot ) { + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.__webglTexture ); + + }; + + // Render targets + + function setupFrameBuffer ( framebuffer, renderTarget, textureTarget ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureTarget, renderTarget.__webglTexture, 0 ); + + }; + + function setupRenderBuffer ( renderbuffer, renderTarget ) { + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); + + if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + /* For some reason this is not working. Defaulting to RGBA4. + } else if( ! renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + */ + } else if( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + + } + + }; + + this.setRenderTarget = function ( renderTarget ) { + + var isCube = ( renderTarget instanceof THREE.WebGLRenderTargetCube ); + + if ( renderTarget && ! renderTarget.__webglFramebuffer ) { + + if ( renderTarget.depthBuffer === undefined ) renderTarget.depthBuffer = true; + if ( renderTarget.stencilBuffer === undefined ) renderTarget.stencilBuffer = true; + + renderTarget.__webglTexture = _gl.createTexture(); + + // Setup texture, create render and frame buffers + + var isTargetPowerOfTwo = isPowerOfTwo( renderTarget.width ) && isPowerOfTwo( renderTarget.height ), + glFormat = paramThreeToGL( renderTarget.format ), + glType = paramThreeToGL( renderTarget.type ); + + if ( isCube ) { + + renderTarget.__webglFramebuffer = []; + renderTarget.__webglRenderbuffer = []; + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget, isTargetPowerOfTwo ); + + for ( var i = 0; i < 6; i ++ ) { + + renderTarget.__webglFramebuffer[ i ] = _gl.createFramebuffer(); + renderTarget.__webglRenderbuffer[ i ] = _gl.createRenderbuffer(); + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTarget.__webglFramebuffer[ i ], renderTarget, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i ); + setupRenderBuffer( renderTarget.__webglRenderbuffer[ i ], renderTarget ); + + } + + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + + } else { + + renderTarget.__webglFramebuffer = _gl.createFramebuffer(); + renderTarget.__webglRenderbuffer = _gl.createRenderbuffer(); + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + setTextureParameters( _gl.TEXTURE_2D, renderTarget, isTargetPowerOfTwo ); + + _gl.texImage2D( _gl.TEXTURE_2D, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null ); + + setupFrameBuffer( renderTarget.__webglFramebuffer, renderTarget, _gl.TEXTURE_2D ); + setupRenderBuffer( renderTarget.__webglRenderbuffer, renderTarget ); + + if ( isTargetPowerOfTwo ) _gl.generateMipmap( _gl.TEXTURE_2D ); + + } + + // Release everything + + if ( isCube ) { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + } + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null); + + } + + var framebuffer, width, height, vx, vy; + + if ( renderTarget ) { + + if ( isCube ) { + + framebuffer = renderTarget.__webglFramebuffer[ renderTarget.activeCubeFace ]; + + } else { + + framebuffer = renderTarget.__webglFramebuffer; + + } + + width = renderTarget.width; + height = renderTarget.height; + + vx = 0; + vy = 0; + + } else { + + framebuffer = null; + + width = _viewportWidth; + height = _viewportHeight; + + vx = _viewportX; + vy = _viewportY; + + } + + if ( framebuffer !== _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( vx, vy, width, height ); + + _currentFramebuffer = framebuffer; + + } + + _currentWidth = width; + _currentHeight = height; + + }; + + function updateRenderTargetMipmap ( renderTarget ) { + + if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_CUBE_MAP ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_2D ); + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + } + + }; + + // Fallback filters for non-power-of-2 textures + + function filterFallback ( f ) { + + if ( f === THREE.NearestFilter || f === THREE.NearestMipMapNearestFilter || f === THREE.NearestMipMapLinearFilter ) { + + return _gl.NEAREST; + + } + + return _gl.LINEAR; + + }; + + // Map three.js constants to WebGL constants + + function paramThreeToGL ( p ) { + + if ( p === THREE.RepeatWrapping ) return _gl.REPEAT; + if ( p === THREE.ClampToEdgeWrapping ) return _gl.CLAMP_TO_EDGE; + if ( p === THREE.MirroredRepeatWrapping ) return _gl.MIRRORED_REPEAT; + + if ( p === THREE.NearestFilter ) return _gl.NEAREST; + if ( p === THREE.NearestMipMapNearestFilter ) return _gl.NEAREST_MIPMAP_NEAREST; + if ( p === THREE.NearestMipMapLinearFilter ) return _gl.NEAREST_MIPMAP_LINEAR; + + if ( p === THREE.LinearFilter ) return _gl.LINEAR; + if ( p === THREE.LinearMipMapNearestFilter ) return _gl.LINEAR_MIPMAP_NEAREST; + if ( p === THREE.LinearMipMapLinearFilter ) return _gl.LINEAR_MIPMAP_LINEAR; + + if ( p === THREE.UnsignedByteType ) return _gl.UNSIGNED_BYTE; + if ( p === THREE.UnsignedShort4444Type ) return _gl.UNSIGNED_SHORT_4_4_4_4; + if ( p === THREE.UnsignedShort5551Type ) return _gl.UNSIGNED_SHORT_5_5_5_1; + if ( p === THREE.UnsignedShort565Type ) return _gl.UNSIGNED_SHORT_5_6_5; + + if ( p === THREE.ByteType ) return _gl.BYTE; + if ( p === THREE.ShortType ) return _gl.SHORT; + if ( p === THREE.UnsignedShortType ) return _gl.UNSIGNED_SHORT; + if ( p === THREE.IntType ) return _gl.INT; + if ( p === THREE.UnsignedIntType ) return _gl.UNSIGNED_INT; + if ( p === THREE.FloatType ) return _gl.FLOAT; + + if ( p === THREE.AlphaFormat ) return _gl.ALPHA; + if ( p === THREE.RGBFormat ) return _gl.RGB; + if ( p === THREE.RGBAFormat ) return _gl.RGBA; + if ( p === THREE.LuminanceFormat ) return _gl.LUMINANCE; + if ( p === THREE.LuminanceAlphaFormat ) return _gl.LUMINANCE_ALPHA; + + if ( p === THREE.AddEquation ) return _gl.FUNC_ADD; + if ( p === THREE.SubtractEquation ) return _gl.FUNC_SUBTRACT; + if ( p === THREE.ReverseSubtractEquation ) return _gl.FUNC_REVERSE_SUBTRACT; + + if ( p === THREE.ZeroFactor ) return _gl.ZERO; + if ( p === THREE.OneFactor ) return _gl.ONE; + if ( p === THREE.SrcColorFactor ) return _gl.SRC_COLOR; + if ( p === THREE.OneMinusSrcColorFactor ) return _gl.ONE_MINUS_SRC_COLOR; + if ( p === THREE.SrcAlphaFactor ) return _gl.SRC_ALPHA; + if ( p === THREE.OneMinusSrcAlphaFactor ) return _gl.ONE_MINUS_SRC_ALPHA; + if ( p === THREE.DstAlphaFactor ) return _gl.DST_ALPHA; + if ( p === THREE.OneMinusDstAlphaFactor ) return _gl.ONE_MINUS_DST_ALPHA; + + if ( p === THREE.DstColorFactor ) return _gl.DST_COLOR; + if ( p === THREE.OneMinusDstColorFactor ) return _gl.ONE_MINUS_DST_COLOR; + if ( p === THREE.SrcAlphaSaturateFactor ) return _gl.SRC_ALPHA_SATURATE; + + if ( _glExtensionCompressedTextureS3TC !== undefined ) { + + if ( p === THREE.RGB_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGB_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT1_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if ( p === THREE.RGBA_S3TC_DXT3_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT3_EXT; + if ( p === THREE.RGBA_S3TC_DXT5_Format ) return _glExtensionCompressedTextureS3TC.COMPRESSED_RGBA_S3TC_DXT5_EXT; + + } + + return 0; + + }; + + // Allocations + + function allocateBones ( object ) { + + if ( _supportsBoneTextures && object && object.useVertexTexture ) { + + return 1024; + + } else { + + // default for when object is not specified + // ( for example when prebuilding shader + // to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + + var nVertexUniforms = _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ); + var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 ); + + var maxBones = nVertexMatrices; + + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + + maxBones = Math.min( object.bones.length, maxBones ); + + if ( maxBones < object.bones.length ) { + + console.warn( "WebGLRenderer: too many bones - " + object.bones.length + ", this GPU supports just " + maxBones + " (try OpenGL instead of ANGLE)" ); + + } + + } + + return maxBones; + + } + + }; + + function allocateLights ( lights ) { + + var l, ll, light, dirLights, pointLights, spotLights, hemiLights, maxDirLights, maxPointLights, maxSpotLights, maxHemiLights; + + dirLights = pointLights = spotLights = hemiLights = maxDirLights = maxPointLights = maxSpotLights = maxHemiLights = 0; + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + + if ( light.onlyShadow ) continue; + + if ( light instanceof THREE.DirectionalLight ) dirLights ++; + if ( light instanceof THREE.PointLight ) pointLights ++; + if ( light instanceof THREE.SpotLight ) spotLights ++; + if ( light instanceof THREE.HemisphereLight ) hemiLights ++; + + } + + if ( ( pointLights + spotLights + dirLights + hemiLights) <= _maxLights ) { + + maxDirLights = dirLights; + maxPointLights = pointLights; + maxSpotLights = spotLights; + maxHemiLights = hemiLights; + + } else { + + maxDirLights = Math.ceil( _maxLights * dirLights / ( pointLights + dirLights ) ); + maxPointLights = _maxLights - maxDirLights; + + // these are not really correct + + maxSpotLights = maxPointLights; + maxHemiLights = maxDirLights; + + } + + return { 'directional' : maxDirLights, 'point' : maxPointLights, 'spot': maxSpotLights, 'hemi': maxHemiLights }; + + }; + + function allocateShadows ( lights ) { + + var l, ll, light, maxShadows = 0; + + for ( l = 0, ll = lights.length; l < ll; l++ ) { + + light = lights[ l ]; + + if ( ! light.castShadow ) continue; + + if ( light instanceof THREE.SpotLight ) maxShadows ++; + if ( light instanceof THREE.DirectionalLight && ! light.shadowCascade ) maxShadows ++; + + } + + return maxShadows; + + }; + + // Initialization + + function initGL () { + + try { + + if ( ! ( _gl = _canvas.getContext( 'experimental-webgl', { alpha: _alpha, premultipliedAlpha: _premultipliedAlpha, antialias: _antialias, stencil: _stencil, preserveDrawingBuffer: _preserveDrawingBuffer } ) ) ) { + + throw 'Error creating WebGL context.'; + + } + + } catch ( error ) { + + console.error( error ); + + } + + _glExtensionTextureFloat = _gl.getExtension( 'OES_texture_float' ); + _glExtensionStandardDerivatives = _gl.getExtension( 'OES_standard_derivatives' ); + + _glExtensionTextureFilterAnisotropic = _gl.getExtension( 'EXT_texture_filter_anisotropic' ) || + _gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || + _gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' ); + + + _glExtensionCompressedTextureS3TC = _gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || + _gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || + _gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' ); + + if ( ! _glExtensionTextureFloat ) { + + console.log( 'THREE.WebGLRenderer: Float textures not supported.' ); + + } + + if ( ! _glExtensionStandardDerivatives ) { + + console.log( 'THREE.WebGLRenderer: Standard derivatives not supported.' ); + + } + + if ( ! _glExtensionTextureFilterAnisotropic ) { + + console.log( 'THREE.WebGLRenderer: Anisotropic texture filtering not supported.' ); + + } + + if ( ! _glExtensionCompressedTextureS3TC ) { + + console.log( 'THREE.WebGLRenderer: S3TC compressed textures not supported.' ); + + } + + }; + + function setDefaultGLState () { + + _gl.clearColor( 0, 0, 0, 1 ); + _gl.clearDepth( 1 ); + _gl.clearStencil( 0 ); + + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthFunc( _gl.LEQUAL ); + + _gl.frontFace( _gl.CCW ); + _gl.cullFace( _gl.BACK ); + _gl.enable( _gl.CULL_FACE ); + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + // default plugins (order is important) + + this.shadowMapPlugin = new THREE.ShadowMapPlugin(); + this.addPrePlugin( this.shadowMapPlugin ); + + this.addPostPlugin( new THREE.SpritePlugin() ); + this.addPostPlugin( new THREE.LensFlarePlugin() ); + +}; +/** + * @author szimek / https://github.com/szimek/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.WebGLRenderTarget = function ( width, height, options ) { + + this.width = width; + this.height = height; + + options = options || {}; + + this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; + this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; + + this.anisotropy = options.anisotropy !== undefined ? options.anisotropy : 1; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; + this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + + this.generateMipmaps = true; + +}; + +THREE.WebGLRenderTarget.prototype.clone = function() { + + var tmp = new THREE.WebGLRenderTarget( this.width, this.height ); + + tmp.wrapS = this.wrapS; + tmp.wrapT = this.wrapT; + + tmp.magFilter = this.magFilter; + tmp.anisotropy = this.anisotropy; + + tmp.minFilter = this.minFilter; + + tmp.offset.copy( this.offset ); + tmp.repeat.copy( this.repeat ); + + tmp.format = this.format; + tmp.type = this.type; + + tmp.depthBuffer = this.depthBuffer; + tmp.stencilBuffer = this.stencilBuffer; + + tmp.generateMipmaps = this.generateMipmaps; + + return tmp; + +}; +/** + * @author alteredq / http://alteredqualia.com + */ + +THREE.WebGLRenderTargetCube = function ( width, height, options ) { + + THREE.WebGLRenderTarget.call( this, width, height, options ); + + this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5 + +}; + +THREE.WebGLRenderTargetCube.prototype = Object.create( THREE.WebGLRenderTarget.prototype ); +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableVertex = function () { + + this.positionWorld = new THREE.Vector3(); + this.positionScreen = new THREE.Vector4(); + + this.visible = true; + +}; + +THREE.RenderableVertex.prototype.copy = function ( vertex ) { + + this.positionWorld.copy( vertex.positionWorld ); + this.positionScreen.copy( vertex.positionScreen ); + +} +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableFace3 = function () { + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + this.v3 = new THREE.RenderableVertex(); + + this.centroidWorld = new THREE.Vector3(); + this.centroidScreen = new THREE.Vector3(); + + this.normalWorld = new THREE.Vector3(); + this.vertexNormalsWorld = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + this.vertexNormalsLength = 0; + + this.color = null; + this.material = null; + this.uvs = [[]]; + + this.z = null; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableFace4 = function () { + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + this.v3 = new THREE.RenderableVertex(); + this.v4 = new THREE.RenderableVertex(); + + this.centroidWorld = new THREE.Vector3(); + this.centroidScreen = new THREE.Vector3(); + + this.normalWorld = new THREE.Vector3(); + this.vertexNormalsWorld = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + this.vertexNormalsLength = 0; + + this.color = null; + this.material = null; + this.uvs = [[]]; + + this.z = null; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableObject = function () { + + this.object = null; + this.z = null; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableParticle = function () { + + this.object = null; + + this.x = null; + this.y = null; + this.z = null; + + this.rotation = null; + this.scale = new THREE.Vector2(); + + this.material = null; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.RenderableLine = function () { + + this.z = null; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + + this.material = null; + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ColorUtils = { + + adjustHSV : function ( color, h, s, v ) { + + var hsv = THREE.ColorUtils.__hsv; + + THREE.ColorUtils.rgbToHsv( color, hsv ); + + hsv.h = THREE.Math.clamp( hsv.h + h, 0, 1 ); + hsv.s = THREE.Math.clamp( hsv.s + s, 0, 1 ); + hsv.v = THREE.Math.clamp( hsv.v + v, 0, 1 ); + + color.setHSV( hsv.h, hsv.s, hsv.v ); + + }, + + // based on MochiKit implementation by Bob Ippolito + + rgbToHsv : function ( color, hsv ) { + + var r = color.r; + var g = color.g; + var b = color.b; + + var max = Math.max( Math.max( r, g ), b ); + var min = Math.min( Math.min( r, g ), b ); + + var hue; + var saturation; + var value = max; + + if ( min === max ) { + + hue = 0; + saturation = 0; + + } else { + + var delta = ( max - min ); + saturation = delta / max; + + if ( r === max ) { + + hue = ( g - b ) / delta; + + } else if ( g === max ) { + + hue = 2 + ( ( b - r ) / delta ); + + } else { + + hue = 4 + ( ( r - g ) / delta ); + } + + hue /= 6; + + if ( hue < 0 ) { + + hue += 1; + + } + + if ( hue > 1 ) { + + hue -= 1; + + } + + } + + if ( hsv === undefined ) { + + hsv = { h: 0, s: 0, v: 0 }; + + } + + hsv.h = hue; + hsv.s = saturation; + hsv.v = value; + + return hsv; + + } + +}; + +THREE.ColorUtils.__hsv = { h: 0, s: 0, v: 0 };/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.GeometryUtils = { + + // Merge two geometries or geometry and geometry from object (using object's transform) + + merge: function ( geometry1, object2 /* mesh | geometry */ ) { + + var matrix, matrixRotation, + vertexOffset = geometry1.vertices.length, + uvPosition = geometry1.faceVertexUvs[ 0 ].length, + geometry2 = object2 instanceof THREE.Mesh ? object2.geometry : object2, + vertices1 = geometry1.vertices, + vertices2 = geometry2.vertices, + faces1 = geometry1.faces, + faces2 = geometry2.faces, + uvs1 = geometry1.faceVertexUvs[ 0 ], + uvs2 = geometry2.faceVertexUvs[ 0 ]; + + var geo1MaterialsMap = {}; + + for ( var i = 0; i < geometry1.materials.length; i ++ ) { + + var id = geometry1.materials[ i ].id; + + geo1MaterialsMap[ id ] = i; + + } + + if ( object2 instanceof THREE.Mesh ) { + + object2.matrixAutoUpdate && object2.updateMatrix(); + + matrix = object2.matrix; + matrixRotation = new THREE.Matrix4(); + matrixRotation.extractRotation( matrix, object2.scale ); + + } + + // vertices + + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + + var vertex = vertices2[ i ]; + + var vertexCopy = vertex.clone(); + + if ( matrix ) matrix.multiplyVector3( vertexCopy ); + + vertices1.push( vertexCopy ); + + } + + // faces + + for ( i = 0, il = faces2.length; i < il; i ++ ) { + + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; + + if ( face instanceof THREE.Face3 ) { + + faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + + } else if ( face instanceof THREE.Face4 ) { + + faceCopy = new THREE.Face4( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset, face.d + vertexOffset ); + + } + + faceCopy.normal.copy( face.normal ); + + if ( matrixRotation ) matrixRotation.multiplyVector3( faceCopy.normal ); + + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + + normal = faceVertexNormals[ j ].clone(); + + if ( matrixRotation ) matrixRotation.multiplyVector3( normal ); + + faceCopy.vertexNormals.push( normal ); + + } + + faceCopy.color.copy( face.color ); + + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); + + } + + if ( face.materialIndex !== undefined ) { + + var material2 = geometry2.materials[ face.materialIndex ]; + var materialId2 = material2.id; + + var materialIndex = geo1MaterialsMap[ materialId2 ]; + + if ( materialIndex === undefined ) { + + materialIndex = geometry1.materials.length; + geo1MaterialsMap[ materialId2 ] = materialIndex; + + geometry1.materials.push( material2 ); + + } + + faceCopy.materialIndex = materialIndex; + + } + + faceCopy.centroid.copy( face.centroid ); + if ( matrix ) matrix.multiplyVector3( faceCopy.centroid ); + + faces1.push( faceCopy ); + + } + + // uvs + + for ( i = 0, il = uvs2.length; i < il; i ++ ) { + + var uv = uvs2[ i ], uvCopy = []; + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( new THREE.UV( uv[ j ].u, uv[ j ].v ) ); + + } + + uvs1.push( uvCopy ); + + } + + }, + + clone: function ( geometry ) { + + var cloneGeo = new THREE.Geometry(); + + var i, il; + + var vertices = geometry.vertices, + faces = geometry.faces, + uvs = geometry.faceVertexUvs[ 0 ]; + + // materials + + if ( geometry.materials ) { + + cloneGeo.materials = geometry.materials.slice(); + + } + + // vertices + + for ( i = 0, il = vertices.length; i < il; i ++ ) { + + var vertex = vertices[ i ]; + + cloneGeo.vertices.push( vertex.clone() ); + + } + + // faces + + for ( i = 0, il = faces.length; i < il; i ++ ) { + + var face = faces[ i ]; + + cloneGeo.faces.push( face.clone() ); + + } + + // uvs + + for ( i = 0, il = uvs.length; i < il; i ++ ) { + + var uv = uvs[ i ], uvCopy = []; + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( new THREE.UV( uv[ j ].u, uv[ j ].v ) ); + + } + + cloneGeo.faceVertexUvs[ 0 ].push( uvCopy ); + + } + + return cloneGeo; + + }, + + // Get random point in triangle (via barycentric coordinates) + // (uniform distribution) + // http://www.cgafaq.info/wiki/Random_Point_In_Triangle + + randomPointInTriangle: function ( vectorA, vectorB, vectorC ) { + + var a, b, c, + point = new THREE.Vector3(), + tmp = THREE.GeometryUtils.__v1; + + a = THREE.GeometryUtils.random(); + b = THREE.GeometryUtils.random(); + + if ( ( a + b ) > 1 ) { + + a = 1 - a; + b = 1 - b; + + } + + c = 1 - a - b; + + point.copy( vectorA ); + point.multiplyScalar( a ); + + tmp.copy( vectorB ); + tmp.multiplyScalar( b ); + + point.addSelf( tmp ); + + tmp.copy( vectorC ); + tmp.multiplyScalar( c ); + + point.addSelf( tmp ); + + return point; + + }, + + // Get random point in face (triangle / quad) + // (uniform distribution) + + randomPointInFace: function ( face, geometry, useCachedAreas ) { + + var vA, vB, vC, vD; + + if ( face instanceof THREE.Face3 ) { + + vA = geometry.vertices[ face.a ]; + vB = geometry.vertices[ face.b ]; + vC = geometry.vertices[ face.c ]; + + return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC ); + + } else if ( face instanceof THREE.Face4 ) { + + vA = geometry.vertices[ face.a ]; + vB = geometry.vertices[ face.b ]; + vC = geometry.vertices[ face.c ]; + vD = geometry.vertices[ face.d ]; + + var area1, area2; + + if ( useCachedAreas ) { + + if ( face._area1 && face._area2 ) { + + area1 = face._area1; + area2 = face._area2; + + } else { + + area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ); + area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); + + face._area1 = area1; + face._area2 = area2; + + } + + } else { + + area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ), + area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); + + } + + var r = THREE.GeometryUtils.random() * ( area1 + area2 ); + + if ( r < area1 ) { + + return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vD ); + + } else { + + return THREE.GeometryUtils.randomPointInTriangle( vB, vC, vD ); + + } + + } + + }, + + // Get uniformly distributed random points in mesh + // - create array with cumulative sums of face areas + // - pick random number from 0 to total area + // - find corresponding place in area array by binary search + // - get random point in face + + randomPointsInGeometry: function ( geometry, n ) { + + var face, i, + faces = geometry.faces, + vertices = geometry.vertices, + il = faces.length, + totalArea = 0, + cumulativeAreas = [], + vA, vB, vC, vD; + + // precompute face areas + + for ( i = 0; i < il; i ++ ) { + + face = faces[ i ]; + + if ( face instanceof THREE.Face3 ) { + + vA = vertices[ face.a ]; + vB = vertices[ face.b ]; + vC = vertices[ face.c ]; + + face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC ); + + } else if ( face instanceof THREE.Face4 ) { + + vA = vertices[ face.a ]; + vB = vertices[ face.b ]; + vC = vertices[ face.c ]; + vD = vertices[ face.d ]; + + face._area1 = THREE.GeometryUtils.triangleArea( vA, vB, vD ); + face._area2 = THREE.GeometryUtils.triangleArea( vB, vC, vD ); + + face._area = face._area1 + face._area2; + + } + + totalArea += face._area; + + cumulativeAreas[ i ] = totalArea; + + } + + // binary search cumulative areas array + + function binarySearchIndices( value ) { + + function binarySearch( start, end ) { + + // return closest larger index + // if exact number is not found + + if ( end < start ) + return start; + + var mid = start + Math.floor( ( end - start ) / 2 ); + + if ( cumulativeAreas[ mid ] > value ) { + + return binarySearch( start, mid - 1 ); + + } else if ( cumulativeAreas[ mid ] < value ) { + + return binarySearch( mid + 1, end ); + + } else { + + return mid; + + } + + } + + var result = binarySearch( 0, cumulativeAreas.length - 1 ) + return result; + + } + + // pick random face weighted by face area + + var r, index, + result = []; + + var stats = {}; + + for ( i = 0; i < n; i ++ ) { + + r = THREE.GeometryUtils.random() * totalArea; + + index = binarySearchIndices( r ); + + result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true ); + + if ( ! stats[ index ] ) { + + stats[ index ] = 1; + + } else { + + stats[ index ] += 1; + + } + + } + + return result; + + }, + + // Get triangle area (by Heron's formula) + // http://en.wikipedia.org/wiki/Heron%27s_formula + + triangleArea: function ( vectorA, vectorB, vectorC ) { + + var s, a, b, c, + tmp = THREE.GeometryUtils.__v1; + + tmp.sub( vectorA, vectorB ); + a = tmp.length(); + + tmp.sub( vectorA, vectorC ); + b = tmp.length(); + + tmp.sub( vectorB, vectorC ); + c = tmp.length(); + + s = 0.5 * ( a + b + c ); + + return Math.sqrt( s * ( s - a ) * ( s - b ) * ( s - c ) ); + + }, + + // Center geometry so that 0,0,0 is in center of bounding box + + center: function ( geometry ) { + + geometry.computeBoundingBox(); + + var bb = geometry.boundingBox; + + var offset = new THREE.Vector3(); + + offset.add( bb.min, bb.max ); + offset.multiplyScalar( -0.5 ); + + geometry.applyMatrix( new THREE.Matrix4().makeTranslation( offset.x, offset.y, offset.z ) ); + geometry.computeBoundingBox(); + + return offset; + + }, + + // Normalize UVs to be from <0,1> + // (for now just the first set of UVs) + + normalizeUVs: function ( geometry ) { + + var uvSet = geometry.faceVertexUvs[ 0 ]; + + for ( var i = 0, il = uvSet.length; i < il; i ++ ) { + + var uvs = uvSet[ i ]; + + for ( var j = 0, jl = uvs.length; j < jl; j ++ ) { + + // texture repeat + + if( uvs[ j ].u !== 1.0 ) uvs[ j ].u = uvs[ j ].u - Math.floor( uvs[ j ].u ); + if( uvs[ j ].v !== 1.0 ) uvs[ j ].v = uvs[ j ].v - Math.floor( uvs[ j ].v ); + + } + + } + + }, + + triangulateQuads: function ( geometry ) { + + var i, il, j, jl; + + var faces = []; + var faceUvs = []; + var faceVertexUvs = []; + + for ( i = 0, il = geometry.faceUvs.length; i < il; i ++ ) { + + faceUvs[ i ] = []; + + } + + for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { + + faceVertexUvs[ i ] = []; + + } + + for ( i = 0, il = geometry.faces.length; i < il; i ++ ) { + + var face = geometry.faces[ i ]; + + if ( face instanceof THREE.Face4 ) { + + var a = face.a; + var b = face.b; + var c = face.c; + var d = face.d; + + var triA = new THREE.Face3(); + var triB = new THREE.Face3(); + + triA.color.copy( face.color ); + triB.color.copy( face.color ); + + triA.materialIndex = face.materialIndex; + triB.materialIndex = face.materialIndex; + + triA.a = a; + triA.b = b; + triA.c = d; + + triB.a = b; + triB.b = c; + triB.c = d; + + if ( face.vertexColors.length === 4 ) { + + triA.vertexColors[ 0 ] = face.vertexColors[ 0 ].clone(); + triA.vertexColors[ 1 ] = face.vertexColors[ 1 ].clone(); + triA.vertexColors[ 2 ] = face.vertexColors[ 3 ].clone(); + + triB.vertexColors[ 0 ] = face.vertexColors[ 1 ].clone(); + triB.vertexColors[ 1 ] = face.vertexColors[ 2 ].clone(); + triB.vertexColors[ 2 ] = face.vertexColors[ 3 ].clone(); + + } + + faces.push( triA, triB ); + + for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + if ( geometry.faceVertexUvs[ j ].length ) { + + var uvs = geometry.faceVertexUvs[ j ][ i ]; + + var uvA = uvs[ 0 ]; + var uvB = uvs[ 1 ]; + var uvC = uvs[ 2 ]; + var uvD = uvs[ 3 ]; + + var uvsTriA = [ uvA.clone(), uvB.clone(), uvD.clone() ]; + var uvsTriB = [ uvB.clone(), uvC.clone(), uvD.clone() ]; + + faceVertexUvs[ j ].push( uvsTriA, uvsTriB ); + + } + + } + + for ( j = 0, jl = geometry.faceUvs.length; j < jl; j ++ ) { + + if ( geometry.faceUvs[ j ].length ) { + + var faceUv = geometry.faceUvs[ j ][ i ]; + + faceUvs[ j ].push( faceUv, faceUv ); + + } + + } + + } else { + + faces.push( face ); + + for ( j = 0, jl = geometry.faceUvs.length; j < jl; j ++ ) { + + faceUvs[ j ].push( geometry.faceUvs[ j ][ i ] ); + + } + + for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); + + } + + } + + } + + geometry.faces = faces; + geometry.faceUvs = faceUvs; + geometry.faceVertexUvs = faceVertexUvs; + + geometry.computeCentroids(); + geometry.computeFaceNormals(); + geometry.computeVertexNormals(); + + if ( geometry.hasTangents ) geometry.computeTangents(); + + }, + + // Make all faces use unique vertices + // so that each face can be separated from others + + explode: function( geometry ) { + + var vertices = []; + + for ( var i = 0, il = geometry.faces.length; i < il; i ++ ) { + + var n = vertices.length; + + var face = geometry.faces[ i ]; + + if ( face instanceof THREE.Face4 ) { + + var a = face.a; + var b = face.b; + var c = face.c; + var d = face.d; + + var va = geometry.vertices[ a ]; + var vb = geometry.vertices[ b ]; + var vc = geometry.vertices[ c ]; + var vd = geometry.vertices[ d ]; + + vertices.push( va.clone() ); + vertices.push( vb.clone() ); + vertices.push( vc.clone() ); + vertices.push( vd.clone() ); + + face.a = n; + face.b = n + 1; + face.c = n + 2; + face.d = n + 3; + + } else { + + var a = face.a; + var b = face.b; + var c = face.c; + + var va = geometry.vertices[ a ]; + var vb = geometry.vertices[ b ]; + var vc = geometry.vertices[ c ]; + + vertices.push( va.clone() ); + vertices.push( vb.clone() ); + vertices.push( vc.clone() ); + + face.a = n; + face.b = n + 1; + face.c = n + 2; + + } + + } + + geometry.vertices = vertices; + delete geometry.__tmpVertices; + + }, + + // Break faces with edges longer than maxEdgeLength + // - not recursive + + tessellate: function ( geometry, maxEdgeLength ) { + + var i, il, face, + a, b, c, d, + va, vb, vc, vd, + dab, dbc, dac, dcd, dad, + m, m1, m2, + vm, vm1, vm2, + vnm, vnm1, vnm2, + vcm, vcm1, vcm2, + triA, triB, + quadA, quadB, + edge; + + var faces = []; + var faceVertexUvs = []; + + for ( i = 0, il = geometry.faceVertexUvs.length; i < il; i ++ ) { + + faceVertexUvs[ i ] = []; + + } + + for ( i = 0, il = geometry.faces.length; i < il; i ++ ) { + + face = geometry.faces[ i ]; + + if ( face instanceof THREE.Face3 ) { + + a = face.a; + b = face.b; + c = face.c; + + va = geometry.vertices[ a ]; + vb = geometry.vertices[ b ]; + vc = geometry.vertices[ c ]; + + dab = va.distanceTo( vb ); + dbc = vb.distanceTo( vc ); + dac = va.distanceTo( vc ); + + if ( dab > maxEdgeLength || dbc > maxEdgeLength || dac > maxEdgeLength ) { + + m = geometry.vertices.length; + + triA = face.clone(); + triB = face.clone(); + + if ( dab >= dbc && dab >= dac ) { + + vm = va.clone(); + vm.lerpSelf( vb, 0.5 ); + + triA.a = a; + triA.b = m; + triA.c = c; + + triB.a = m; + triB.b = b; + triB.c = c; + + if ( face.vertexNormals.length === 3 ) { + + vnm = face.vertexNormals[ 0 ].clone(); + vnm.lerpSelf( face.vertexNormals[ 1 ], 0.5 ); + + triA.vertexNormals[ 1 ].copy( vnm ); + triB.vertexNormals[ 0 ].copy( vnm ); + + } + + if ( face.vertexColors.length === 3 ) { + + vcm = face.vertexColors[ 0 ].clone(); + vcm.lerpSelf( face.vertexColors[ 1 ], 0.5 ); + + triA.vertexColors[ 1 ].copy( vcm ); + triB.vertexColors[ 0 ].copy( vcm ); + + } + + edge = 0; + + } else if ( dbc >= dab && dbc >= dac ) { + + vm = vb.clone(); + vm.lerpSelf( vc, 0.5 ); + + triA.a = a; + triA.b = b; + triA.c = m; + + triB.a = m; + triB.b = c; + triB.c = a; + + if ( face.vertexNormals.length === 3 ) { + + vnm = face.vertexNormals[ 1 ].clone(); + vnm.lerpSelf( face.vertexNormals[ 2 ], 0.5 ); + + triA.vertexNormals[ 2 ].copy( vnm ); + + triB.vertexNormals[ 0 ].copy( vnm ); + triB.vertexNormals[ 1 ].copy( face.vertexNormals[ 2 ] ); + triB.vertexNormals[ 2 ].copy( face.vertexNormals[ 0 ] ); + + } + + if ( face.vertexColors.length === 3 ) { + + vcm = face.vertexColors[ 1 ].clone(); + vcm.lerpSelf( face.vertexColors[ 2 ], 0.5 ); + + triA.vertexColors[ 2 ].copy( vcm ); + + triB.vertexColors[ 0 ].copy( vcm ); + triB.vertexColors[ 1 ].copy( face.vertexColors[ 2 ] ); + triB.vertexColors[ 2 ].copy( face.vertexColors[ 0 ] ); + + } + + edge = 1; + + } else { + + vm = va.clone(); + vm.lerpSelf( vc, 0.5 ); + + triA.a = a; + triA.b = b; + triA.c = m; + + triB.a = m; + triB.b = b; + triB.c = c; + + if ( face.vertexNormals.length === 3 ) { + + vnm = face.vertexNormals[ 0 ].clone(); + vnm.lerpSelf( face.vertexNormals[ 2 ], 0.5 ); + + triA.vertexNormals[ 2 ].copy( vnm ); + triB.vertexNormals[ 0 ].copy( vnm ); + + } + + if ( face.vertexColors.length === 3 ) { + + vcm = face.vertexColors[ 0 ].clone(); + vcm.lerpSelf( face.vertexColors[ 2 ], 0.5 ); + + triA.vertexColors[ 2 ].copy( vcm ); + triB.vertexColors[ 0 ].copy( vcm ); + + } + + edge = 2; + + } + + faces.push( triA, triB ); + geometry.vertices.push( vm ); + + var j, jl, uvs, uvA, uvB, uvC, uvM, uvsTriA, uvsTriB; + + for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + if ( geometry.faceVertexUvs[ j ].length ) { + + uvs = geometry.faceVertexUvs[ j ][ i ]; + + uvA = uvs[ 0 ]; + uvB = uvs[ 1 ]; + uvC = uvs[ 2 ]; + + // AB + + if ( edge === 0 ) { + + uvM = uvA.clone(); + uvM.lerpSelf( uvB, 0.5 ); + + uvsTriA = [ uvA.clone(), uvM.clone(), uvC.clone() ]; + uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ]; + + // BC + + } else if ( edge === 1 ) { + + uvM = uvB.clone(); + uvM.lerpSelf( uvC, 0.5 ); + + uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ]; + uvsTriB = [ uvM.clone(), uvC.clone(), uvA.clone() ]; + + // AC + + } else { + + uvM = uvA.clone(); + uvM.lerpSelf( uvC, 0.5 ); + + uvsTriA = [ uvA.clone(), uvB.clone(), uvM.clone() ]; + uvsTriB = [ uvM.clone(), uvB.clone(), uvC.clone() ]; + + } + + faceVertexUvs[ j ].push( uvsTriA, uvsTriB ); + + } + + } + + } else { + + faces.push( face ); + + for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); + + } + + } + + } else { + + a = face.a; + b = face.b; + c = face.c; + d = face.d; + + va = geometry.vertices[ a ]; + vb = geometry.vertices[ b ]; + vc = geometry.vertices[ c ]; + vd = geometry.vertices[ d ]; + + dab = va.distanceTo( vb ); + dbc = vb.distanceTo( vc ); + dcd = vc.distanceTo( vd ); + dad = va.distanceTo( vd ); + + if ( dab > maxEdgeLength || dbc > maxEdgeLength || dcd > maxEdgeLength || dad > maxEdgeLength ) { + + m1 = geometry.vertices.length; + m2 = geometry.vertices.length + 1; + + quadA = face.clone(); + quadB = face.clone(); + + if ( ( dab >= dbc && dab >= dcd && dab >= dad ) || ( dcd >= dbc && dcd >= dab && dcd >= dad ) ) { + + vm1 = va.clone(); + vm1.lerpSelf( vb, 0.5 ); + + vm2 = vc.clone(); + vm2.lerpSelf( vd, 0.5 ); + + quadA.a = a; + quadA.b = m1; + quadA.c = m2; + quadA.d = d; + + quadB.a = m1; + quadB.b = b; + quadB.c = c; + quadB.d = m2; + + if ( face.vertexNormals.length === 4 ) { + + vnm1 = face.vertexNormals[ 0 ].clone(); + vnm1.lerpSelf( face.vertexNormals[ 1 ], 0.5 ); + + vnm2 = face.vertexNormals[ 2 ].clone(); + vnm2.lerpSelf( face.vertexNormals[ 3 ], 0.5 ); + + quadA.vertexNormals[ 1 ].copy( vnm1 ); + quadA.vertexNormals[ 2 ].copy( vnm2 ); + + quadB.vertexNormals[ 0 ].copy( vnm1 ); + quadB.vertexNormals[ 3 ].copy( vnm2 ); + + } + + if ( face.vertexColors.length === 4 ) { + + vcm1 = face.vertexColors[ 0 ].clone(); + vcm1.lerpSelf( face.vertexColors[ 1 ], 0.5 ); + + vcm2 = face.vertexColors[ 2 ].clone(); + vcm2.lerpSelf( face.vertexColors[ 3 ], 0.5 ); + + quadA.vertexColors[ 1 ].copy( vcm1 ); + quadA.vertexColors[ 2 ].copy( vcm2 ); + + quadB.vertexColors[ 0 ].copy( vcm1 ); + quadB.vertexColors[ 3 ].copy( vcm2 ); + + } + + edge = 0; + + } else { + + vm1 = vb.clone(); + vm1.lerpSelf( vc, 0.5 ); + + vm2 = vd.clone(); + vm2.lerpSelf( va, 0.5 ); + + quadA.a = a; + quadA.b = b; + quadA.c = m1; + quadA.d = m2; + + quadB.a = m2; + quadB.b = m1; + quadB.c = c; + quadB.d = d; + + if ( face.vertexNormals.length === 4 ) { + + vnm1 = face.vertexNormals[ 1 ].clone(); + vnm1.lerpSelf( face.vertexNormals[ 2 ], 0.5 ); + + vnm2 = face.vertexNormals[ 3 ].clone(); + vnm2.lerpSelf( face.vertexNormals[ 0 ], 0.5 ); + + quadA.vertexNormals[ 2 ].copy( vnm1 ); + quadA.vertexNormals[ 3 ].copy( vnm2 ); + + quadB.vertexNormals[ 0 ].copy( vnm2 ); + quadB.vertexNormals[ 1 ].copy( vnm1 ); + + } + + if ( face.vertexColors.length === 4 ) { + + vcm1 = face.vertexColors[ 1 ].clone(); + vcm1.lerpSelf( face.vertexColors[ 2 ], 0.5 ); + + vcm2 = face.vertexColors[ 3 ].clone(); + vcm2.lerpSelf( face.vertexColors[ 0 ], 0.5 ); + + quadA.vertexColors[ 2 ].copy( vcm1 ); + quadA.vertexColors[ 3 ].copy( vcm2 ); + + quadB.vertexColors[ 0 ].copy( vcm2 ); + quadB.vertexColors[ 1 ].copy( vcm1 ); + + } + + edge = 1; + + } + + faces.push( quadA, quadB ); + geometry.vertices.push( vm1, vm2 ); + + var j, jl, uvs, uvA, uvB, uvC, uvD, uvM1, uvM2, uvsQuadA, uvsQuadB; + + for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + if ( geometry.faceVertexUvs[ j ].length ) { + + uvs = geometry.faceVertexUvs[ j ][ i ]; + + uvA = uvs[ 0 ]; + uvB = uvs[ 1 ]; + uvC = uvs[ 2 ]; + uvD = uvs[ 3 ]; + + // AB + CD + + if ( edge === 0 ) { + + uvM1 = uvA.clone(); + uvM1.lerpSelf( uvB, 0.5 ); + + uvM2 = uvC.clone(); + uvM2.lerpSelf( uvD, 0.5 ); + + uvsQuadA = [ uvA.clone(), uvM1.clone(), uvM2.clone(), uvD.clone() ]; + uvsQuadB = [ uvM1.clone(), uvB.clone(), uvC.clone(), uvM2.clone() ]; + + // BC + AD + + } else { + + uvM1 = uvB.clone(); + uvM1.lerpSelf( uvC, 0.5 ); + + uvM2 = uvD.clone(); + uvM2.lerpSelf( uvA, 0.5 ); + + uvsQuadA = [ uvA.clone(), uvB.clone(), uvM1.clone(), uvM2.clone() ]; + uvsQuadB = [ uvM2.clone(), uvM1.clone(), uvC.clone(), uvD.clone() ]; + + } + + faceVertexUvs[ j ].push( uvsQuadA, uvsQuadB ); + + } + + } + + } else { + + faces.push( face ); + + for ( j = 0, jl = geometry.faceVertexUvs.length; j < jl; j ++ ) { + + faceVertexUvs[ j ].push( geometry.faceVertexUvs[ j ][ i ] ); + + } + + } + + } + + } + + geometry.faces = faces; + geometry.faceVertexUvs = faceVertexUvs; + + } + +}; + +THREE.GeometryUtils.random = THREE.Math.random16; + +THREE.GeometryUtils.__v1 = new THREE.Vector3(); +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.ImageUtils = { + + crossOrigin: 'anonymous', + + loadTexture: function ( url, mapping, onLoad, onError ) { + + var image = new Image(); + var texture = new THREE.Texture( image, mapping ); + + var loader = new THREE.ImageLoader(); + + loader.addEventListener( 'load', function ( event ) { + + texture.image = event.content; + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } ); + + loader.addEventListener( 'error', function ( event ) { + + if ( onError ) onError( event.message ); + + } ); + + loader.crossOrigin = this.crossOrigin; + loader.load( url, image ); + + return texture; + + }, + + loadCompressedTexture: function ( url, mapping, onLoad, onError ) { + + var texture = new THREE.CompressedTexture(); + texture.mapping = mapping; + + var request = new XMLHttpRequest(); + + request.onload = function () { + + var buffer = request.response; + var dds = THREE.ImageUtils.parseDDS( buffer, true ); + + texture.format = dds.format; + + texture.mipmaps = dds.mipmaps; + texture.image.width = dds.width; + texture.image.height = dds.height; + + // gl.generateMipmap fails for compressed textures + // mipmaps must be embedded in the DDS file + // or texture filters must not use mipmapping + + texture.generateMipmaps = false; + + texture.needsUpdate = true; + + if ( onLoad ) onLoad( texture ); + + } + + request.onerror = onError; + + request.open( 'GET', url, true ); + request.responseType = "arraybuffer"; + request.send( null ); + + return texture; + + }, + + loadTextureCube: function ( array, mapping, onLoad, onError ) { + + var images = []; + images.loadCount = 0; + + var texture = new THREE.Texture(); + texture.image = images; + if ( mapping !== undefined ) texture.mapping = mapping; + + // no flipping needed for cube textures + + texture.flipY = false; + + for ( var i = 0, il = array.length; i < il; ++ i ) { + + var cubeImage = new Image(); + images[ i ] = cubeImage; + + cubeImage.onload = function () { + + images.loadCount += 1; + + if ( images.loadCount === 6 ) { + + texture.needsUpdate = true; + if ( onLoad ) onLoad(); + + } + + }; + + cubeImage.onerror = onError; + + cubeImage.crossOrigin = this.crossOrigin; + cubeImage.src = array[ i ]; + + } + + return texture; + + }, + + loadCompressedTextureCube: function ( array, mapping, onLoad, onError ) { + + var images = []; + images.loadCount = 0; + + var texture = new THREE.CompressedTexture(); + texture.image = images; + if ( mapping !== undefined ) texture.mapping = mapping; + + // no flipping for cube textures + // (also flipping doesn't work for compressed textures ) + + texture.flipY = false; + + // can't generate mipmaps for compressed textures + // mips must be embedded in DDS files + + texture.generateMipmaps = false; + + var generateCubeFaceCallback = function ( rq, img ) { + + return function () { + + var buffer = rq.response; + var dds = THREE.ImageUtils.parseDDS( buffer, true ); + + img.format = dds.format; + + img.mipmaps = dds.mipmaps; + img.width = dds.width; + img.height = dds.height; + + images.loadCount += 1; + + if ( images.loadCount === 6 ) { + + texture.format = dds.format; + texture.needsUpdate = true; + if ( onLoad ) onLoad(); + + } + + } + + } + + for ( var i = 0, il = array.length; i < il; ++ i ) { + + var cubeImage = {}; + images[ i ] = cubeImage; + + var request = new XMLHttpRequest(); + + request.onload = generateCubeFaceCallback( request, cubeImage ); + request.onerror = onError; + + var url = array[ i ]; + + request.open( 'GET', url, true ); + request.responseType = "arraybuffer"; + request.send( null ); + + } + + return texture; + + }, + + parseDDS: function ( buffer, loadMipmaps ) { + + var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 }; + + // Adapted from @toji's DDS utils + // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js + + // All values and structures referenced from: + // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ + + var DDS_MAGIC = 0x20534444; + + var DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PITCH = 0x8, + DDSD_PIXELFORMAT = 0x1000, + DDSD_MIPMAPCOUNT = 0x20000, + DDSD_LINEARSIZE = 0x80000, + DDSD_DEPTH = 0x800000; + + var DDSCAPS_COMPLEX = 0x8, + DDSCAPS_MIPMAP = 0x400000, + DDSCAPS_TEXTURE = 0x1000; + + var DDSCAPS2_CUBEMAP = 0x200, + DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, + DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, + DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, + DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, + DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, + DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, + DDSCAPS2_VOLUME = 0x200000; + + var DDPF_ALPHAPIXELS = 0x1, + DDPF_ALPHA = 0x2, + DDPF_FOURCC = 0x4, + DDPF_RGB = 0x40, + DDPF_YUV = 0x200, + DDPF_LUMINANCE = 0x20000; + + function fourCCToInt32( value ) { + + return value.charCodeAt(0) + + (value.charCodeAt(1) << 8) + + (value.charCodeAt(2) << 16) + + (value.charCodeAt(3) << 24); + + } + + function int32ToFourCC( value ) { + + return String.fromCharCode( + value & 0xff, + (value >> 8) & 0xff, + (value >> 16) & 0xff, + (value >> 24) & 0xff + ); + } + + var FOURCC_DXT1 = fourCCToInt32("DXT1"); + var FOURCC_DXT3 = fourCCToInt32("DXT3"); + var FOURCC_DXT5 = fourCCToInt32("DXT5"); + + var headerLengthInt = 31; // The header length in 32 bit ints + + // Offsets into the header array + + var off_magic = 0; + + var off_size = 1; + var off_flags = 2; + var off_height = 3; + var off_width = 4; + + var off_mipmapCount = 7; + + var off_pfFlags = 20; + var off_pfFourCC = 21; + + // Parse header + + var header = new Int32Array( buffer, 0, headerLengthInt ); + + if ( header[ off_magic ] !== DDS_MAGIC ) { + + console.error( "ImageUtils.parseDDS(): Invalid magic number in DDS header" ); + return dds; + + } + + if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { + + console.error( "ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code" ); + return dds; + + } + + var blockBytes; + + var fourCC = header[ off_pfFourCC ]; + + switch ( fourCC ) { + + case FOURCC_DXT1: + + blockBytes = 8; + dds.format = THREE.RGB_S3TC_DXT1_Format; + break; + + case FOURCC_DXT3: + + blockBytes = 16; + dds.format = THREE.RGBA_S3TC_DXT3_Format; + break; + + case FOURCC_DXT5: + + blockBytes = 16; + dds.format = THREE.RGBA_S3TC_DXT5_Format; + break; + + default: + + console.error( "ImageUtils.parseDDS(): Unsupported FourCC code: ", int32ToFourCC( fourCC ) ); + return dds; + + } + + dds.mipmapCount = 1; + + if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { + + dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); + + } + + dds.width = header[ off_width ]; + dds.height = header[ off_height ]; + + var dataOffset = header[ off_size ] + 4; + + // Extract mipmaps buffers + + var width = dds.width; + var height = dds.height; + + for ( var i = 0; i < dds.mipmapCount; i ++ ) { + + var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; + var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); + + var mipmap = { "data": byteArray, "width": width, "height": height }; + dds.mipmaps.push( mipmap ); + + dataOffset += dataLength; + + width = Math.max( width * 0.5, 1 ); + height = Math.max( height * 0.5, 1 ); + + } + + return dds; + + }, + + getNormalMap: function ( image, depth ) { + + // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/ + + var cross = function ( a, b ) { + + return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ]; + + } + + var subtract = function ( a, b ) { + + return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ]; + + } + + var normalize = function ( a ) { + + var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] ); + return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ]; + + } + + depth = depth | 1; + + var width = image.width; + var height = image.height; + + var canvas = document.createElement( 'canvas' ); + canvas.width = width; + canvas.height = height; + + var context = canvas.getContext( '2d' ); + context.drawImage( image, 0, 0 ); + + var data = context.getImageData( 0, 0, width, height ).data; + var imageData = context.createImageData( width, height ); + var output = imageData.data; + + for ( var x = 0; x < width; x ++ ) { + + for ( var y = 0; y < height; y ++ ) { + + var ly = y - 1 < 0 ? 0 : y - 1; + var uy = y + 1 > height - 1 ? height - 1 : y + 1; + var lx = x - 1 < 0 ? 0 : x - 1; + var ux = x + 1 > width - 1 ? width - 1 : x + 1; + + var points = []; + var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ]; + points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] ); + points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] ); + points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] ); + points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] ); + + var normals = []; + var num_points = points.length; + + for ( var i = 0; i < num_points; i ++ ) { + + var v1 = points[ i ]; + var v2 = points[ ( i + 1 ) % num_points ]; + v1 = subtract( v1, origin ); + v2 = subtract( v2, origin ); + normals.push( normalize( cross( v1, v2 ) ) ); + + } + + var normal = [ 0, 0, 0 ]; + + for ( var i = 0; i < normals.length; i ++ ) { + + normal[ 0 ] += normals[ i ][ 0 ]; + normal[ 1 ] += normals[ i ][ 1 ]; + normal[ 2 ] += normals[ i ][ 2 ]; + + } + + normal[ 0 ] /= normals.length; + normal[ 1 ] /= normals.length; + normal[ 2 ] /= normals.length; + + var idx = ( y * width + x ) * 4; + + output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0; + output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0; + output[ idx + 3 ] = 255; + + } + + } + + context.putImageData( imageData, 0, 0 ); + + return canvas; + + }, + + generateDataTexture: function ( width, height, color ) { + + var size = width * height; + var data = new Uint8Array( 3 * size ); + + var r = Math.floor( color.r * 255 ); + var g = Math.floor( color.g * 255 ); + var b = Math.floor( color.b * 255 ); + + for ( var i = 0; i < size; i ++ ) { + + data[ i * 3 ] = r; + data[ i * 3 + 1 ] = g; + data[ i * 3 + 2 ] = b; + + } + + var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat ); + texture.needsUpdate = true; + + return texture; + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneUtils = { + + createMultiMaterialObject: function ( geometry, materials ) { + + var group = new THREE.Object3D(); + + for ( var i = 0, l = materials.length; i < l; i ++ ) { + + group.add( new THREE.Mesh( geometry, materials[ i ] ) ); + + } + + return group; + + }, + + detach : function ( child, parent, scene ) { + + child.applyMatrix( parent.matrixWorld ); + parent.remove( child ); + scene.add( child ); + + }, + + attach: function ( child, scene, parent ) { + + var matrixWorldInverse = new THREE.Matrix4(); + matrixWorldInverse.getInverse( parent.matrixWorld ); + child.applyMatrix( matrixWorldInverse ); + + scene.remove( child ); + parent.add( child ); + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * + * ShaderUtils currently contains: + * + * fresnel + * normal + * cube + * + */ + +if ( THREE.WebGLRenderer ) { + +THREE.ShaderUtils = { + + lib: { + + /* ------------------------------------------------------------------------- + // Fresnel shader + // - based on Nvidia Cg tutorial + ------------------------------------------------------------------------- */ + + 'fresnel': { + + uniforms: { + + "mRefractionRatio": { type: "f", value: 1.02 }, + "mFresnelBias": { type: "f", value: 0.1 }, + "mFresnelPower": { type: "f", value: 2.0 }, + "mFresnelScale": { type: "f", value: 1.0 }, + "tCube": { type: "t", value: null } + + }, + + fragmentShader: [ + + "uniform samplerCube tCube;", + + "varying vec3 vReflect;", + "varying vec3 vRefract[3];", + "varying float vReflectionFactor;", + + "void main() {", + + "vec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );", + "vec4 refractedColor = vec4( 1.0, 1.0, 1.0, 1.0 );", + + "refractedColor.r = textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r;", + "refractedColor.g = textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g;", + "refractedColor.b = textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b;", + "refractedColor.a = 1.0;", + + "gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );", + + "}" + + ].join("\n"), + + vertexShader: [ + + "uniform float mRefractionRatio;", + "uniform float mFresnelBias;", + "uniform float mFresnelScale;", + "uniform float mFresnelPower;", + + "varying vec3 vReflect;", + "varying vec3 vRefract[3];", + "varying float vReflectionFactor;", + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vec4 mPosition = modelMatrix * vec4( position, 1.0 );", + + "vec3 nWorld = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal );", + + "vec3 I = mPosition.xyz - cameraPosition;", + + "vReflect = reflect( I, nWorld );", + "vRefract[0] = refract( normalize( I ), nWorld, mRefractionRatio );", + "vRefract[1] = refract( normalize( I ), nWorld, mRefractionRatio * 0.99 );", + "vRefract[2] = refract( normalize( I ), nWorld, mRefractionRatio * 0.98 );", + "vReflectionFactor = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( I ), nWorld ), mFresnelPower );", + + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------- + // Normal map shader + // - Blinn-Phong + // - normal + diffuse + specular + AO + displacement + reflection + shadow maps + // - point and directional lights (use with "lights: true" material option) + ------------------------------------------------------------------------- */ + + 'normal' : { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "fog" ], + THREE.UniformsLib[ "lights" ], + THREE.UniformsLib[ "shadowmap" ], + + { + + "enableAO" : { type: "i", value: 0 }, + "enableDiffuse" : { type: "i", value: 0 }, + "enableSpecular" : { type: "i", value: 0 }, + "enableReflection": { type: "i", value: 0 }, + "enableDisplacement": { type: "i", value: 0 }, + + "tDisplacement": { type: "t", value: null }, // must go first as this is vertex texture + "tDiffuse" : { type: "t", value: null }, + "tCube" : { type: "t", value: null }, + "tNormal" : { type: "t", value: null }, + "tSpecular" : { type: "t", value: null }, + "tAO" : { type: "t", value: null }, + + "uNormalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + + "uDisplacementBias": { type: "f", value: 0.0 }, + "uDisplacementScale": { type: "f", value: 1.0 }, + + "uDiffuseColor": { type: "c", value: new THREE.Color( 0xffffff ) }, + "uSpecularColor": { type: "c", value: new THREE.Color( 0x111111 ) }, + "uAmbientColor": { type: "c", value: new THREE.Color( 0xffffff ) }, + "uShininess": { type: "f", value: 30 }, + "uOpacity": { type: "f", value: 1 }, + + "useRefract": { type: "i", value: 0 }, + "uRefractionRatio": { type: "f", value: 0.98 }, + "uReflectivity": { type: "f", value: 0.5 }, + + "uOffset" : { type: "v2", value: new THREE.Vector2( 0, 0 ) }, + "uRepeat" : { type: "v2", value: new THREE.Vector2( 1, 1 ) }, + + "wrapRGB" : { type: "v3", value: new THREE.Vector3( 1, 1, 1 ) } + + } + + ] ), + + fragmentShader: [ + + "uniform vec3 uAmbientColor;", + "uniform vec3 uDiffuseColor;", + "uniform vec3 uSpecularColor;", + "uniform float uShininess;", + "uniform float uOpacity;", + + "uniform bool enableDiffuse;", + "uniform bool enableSpecular;", + "uniform bool enableAO;", + "uniform bool enableReflection;", + + "uniform sampler2D tDiffuse;", + "uniform sampler2D tNormal;", + "uniform sampler2D tSpecular;", + "uniform sampler2D tAO;", + + "uniform samplerCube tCube;", + + "uniform vec2 uNormalScale;", + + "uniform bool useRefract;", + "uniform float uRefractionRatio;", + "uniform float uReflectivity;", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "uniform vec3 ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + + "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "uniform vec3 hemisphereLightSkyColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightGroundColor[ MAX_HEMI_LIGHTS ];", + "uniform vec3 hemisphereLightPosition[ MAX_HEMI_LIGHTS ];", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "uniform vec3 spotLightColor[ MAX_SPOT_LIGHTS ];", + "uniform vec3 spotLightPosition[ MAX_SPOT_LIGHTS ];", + "uniform vec3 spotLightDirection[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightAngle[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightExponent[ MAX_SPOT_LIGHTS ];", + "uniform float spotLightDistance[ MAX_SPOT_LIGHTS ];", + + "#endif", + + "#ifdef WRAP_AROUND", + + "uniform vec3 wrapRGB;", + + "#endif", + + "varying vec3 vWorldPosition;", + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "shadowmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( vec3( 1.0 ), uOpacity );", + + "vec3 specularTex = vec3( 1.0 );", + + "vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;", + "normalTex.xy *= uNormalScale;", + "normalTex = normalize( normalTex );", + + "if( enableDiffuse ) {", + + "#ifdef GAMMA_INPUT", + + "vec4 texelColor = texture2D( tDiffuse, vUv );", + "texelColor.xyz *= texelColor.xyz;", + + "gl_FragColor = gl_FragColor * texelColor;", + + "#else", + + "gl_FragColor = gl_FragColor * texture2D( tDiffuse, vUv );", + + "#endif", + + "}", + + "if( enableAO ) {", + + "#ifdef GAMMA_INPUT", + + "vec4 aoColor = texture2D( tAO, vUv );", + "aoColor.xyz *= aoColor.xyz;", + + "gl_FragColor.xyz = gl_FragColor.xyz * aoColor.xyz;", + + "#else", + + "gl_FragColor.xyz = gl_FragColor.xyz * texture2D( tAO, vUv ).xyz;", + + "#endif", + + "}", + + "if( enableSpecular )", + "specularTex = texture2D( tSpecular, vUv ).xyz;", + + "mat3 tsb = mat3( normalize( vTangent ), normalize( vBinormal ), normalize( vNormal ) );", + "vec3 finalNormal = tsb * normalTex;", + + "#ifdef FLIP_SIDED", + + "finalNormal = -finalNormal;", + + "#endif", + + "vec3 normal = normalize( finalNormal );", + "vec3 viewPosition = normalize( vViewPosition );", + + // point lights + + "#if MAX_POINT_LIGHTS > 0", + + "vec3 pointDiffuse = vec3( 0.0 );", + "vec3 pointSpecular = vec3( 0.0 );", + + "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", + "vec3 pointVector = lPosition.xyz + vViewPosition.xyz;", + + "float pointDistance = 1.0;", + "if ( pointLightDistance[ i ] > 0.0 )", + "pointDistance = 1.0 - min( ( length( pointVector ) / pointLightDistance[ i ] ), 1.0 );", + + "pointVector = normalize( pointVector );", + + // diffuse + + "#ifdef WRAP_AROUND", + + "float pointDiffuseWeightFull = max( dot( normal, pointVector ), 0.0 );", + "float pointDiffuseWeightHalf = max( 0.5 * dot( normal, pointVector ) + 0.5, 0.0 );", + + "vec3 pointDiffuseWeight = mix( vec3 ( pointDiffuseWeightFull ), vec3( pointDiffuseWeightHalf ), wrapRGB );", + + "#else", + + "float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", + + "#endif", + + "pointDiffuse += pointDistance * pointLightColor[ i ] * uDiffuseColor * pointDiffuseWeight;", + + // specular + + "vec3 pointHalfVector = normalize( pointVector + viewPosition );", + "float pointDotNormalHalf = max( dot( normal, pointHalfVector ), 0.0 );", + "float pointSpecularWeight = specularTex.r * max( pow( pointDotNormalHalf, uShininess ), 0.0 );", + + "#ifdef PHYSICALLY_BASED_SHADING", + + // 2.0 => 2.0001 is hack to work around ANGLE bug + + "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;", + + "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( pointVector, pointHalfVector ), 5.0 );", + "pointSpecular += schlick * pointLightColor[ i ] * pointSpecularWeight * pointDiffuseWeight * pointDistance * specularNormalization;", + + "#else", + + "pointSpecular += pointDistance * pointLightColor[ i ] * uSpecularColor * pointSpecularWeight * pointDiffuseWeight;", + + "#endif", + + "}", + + "#endif", + + // spot lights + + "#if MAX_SPOT_LIGHTS > 0", + + "vec3 spotDiffuse = vec3( 0.0 );", + "vec3 spotSpecular = vec3( 0.0 );", + + "for ( int i = 0; i < MAX_SPOT_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( spotLightPosition[ i ], 1.0 );", + "vec3 spotVector = lPosition.xyz + vViewPosition.xyz;", + + "float spotDistance = 1.0;", + "if ( spotLightDistance[ i ] > 0.0 )", + "spotDistance = 1.0 - min( ( length( spotVector ) / spotLightDistance[ i ] ), 1.0 );", + + "spotVector = normalize( spotVector );", + + "float spotEffect = dot( spotLightDirection[ i ], normalize( spotLightPosition[ i ] - vWorldPosition ) );", + + "if ( spotEffect > spotLightAngle[ i ] ) {", + + "spotEffect = pow( spotEffect, spotLightExponent[ i ] );", + + // diffuse + + "#ifdef WRAP_AROUND", + + "float spotDiffuseWeightFull = max( dot( normal, spotVector ), 0.0 );", + "float spotDiffuseWeightHalf = max( 0.5 * dot( normal, spotVector ) + 0.5, 0.0 );", + + "vec3 spotDiffuseWeight = mix( vec3 ( spotDiffuseWeightFull ), vec3( spotDiffuseWeightHalf ), wrapRGB );", + + "#else", + + "float spotDiffuseWeight = max( dot( normal, spotVector ), 0.0 );", + + "#endif", + + "spotDiffuse += spotDistance * spotLightColor[ i ] * uDiffuseColor * spotDiffuseWeight * spotEffect;", + + // specular + + "vec3 spotHalfVector = normalize( spotVector + viewPosition );", + "float spotDotNormalHalf = max( dot( normal, spotHalfVector ), 0.0 );", + "float spotSpecularWeight = specularTex.r * max( pow( spotDotNormalHalf, uShininess ), 0.0 );", + + "#ifdef PHYSICALLY_BASED_SHADING", + + // 2.0 => 2.0001 is hack to work around ANGLE bug + + "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;", + + "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( spotVector, spotHalfVector ), 5.0 );", + "spotSpecular += schlick * spotLightColor[ i ] * spotSpecularWeight * spotDiffuseWeight * spotDistance * specularNormalization * spotEffect;", + + "#else", + + "spotSpecular += spotDistance * spotLightColor[ i ] * uSpecularColor * spotSpecularWeight * spotDiffuseWeight * spotEffect;", + + "#endif", + + "}", + + "}", + + "#endif", + + // directional lights + + "#if MAX_DIR_LIGHTS > 0", + + "vec3 dirDiffuse = vec3( 0.0 );", + "vec3 dirSpecular = vec3( 0.0 );", + + "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + + "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", + "vec3 dirVector = normalize( lDirection.xyz );", + + // diffuse + + "#ifdef WRAP_AROUND", + + "float directionalLightWeightingFull = max( dot( normal, dirVector ), 0.0 );", + "float directionalLightWeightingHalf = max( 0.5 * dot( normal, dirVector ) + 0.5, 0.0 );", + + "vec3 dirDiffuseWeight = mix( vec3( directionalLightWeightingFull ), vec3( directionalLightWeightingHalf ), wrapRGB );", + + "#else", + + "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + + "#endif", + + "dirDiffuse += directionalLightColor[ i ] * uDiffuseColor * dirDiffuseWeight;", + + // specular + + "vec3 dirHalfVector = normalize( dirVector + viewPosition );", + "float dirDotNormalHalf = max( dot( normal, dirHalfVector ), 0.0 );", + "float dirSpecularWeight = specularTex.r * max( pow( dirDotNormalHalf, uShininess ), 0.0 );", + + "#ifdef PHYSICALLY_BASED_SHADING", + + // 2.0 => 2.0001 is hack to work around ANGLE bug + + "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;", + + "vec3 schlick = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( dirVector, dirHalfVector ), 5.0 );", + "dirSpecular += schlick * directionalLightColor[ i ] * dirSpecularWeight * dirDiffuseWeight * specularNormalization;", + + "#else", + + "dirSpecular += directionalLightColor[ i ] * uSpecularColor * dirSpecularWeight * dirDiffuseWeight;", + + "#endif", + + "}", + + "#endif", + + // hemisphere lights + + "#if MAX_HEMI_LIGHTS > 0", + + "vec3 hemiDiffuse = vec3( 0.0 );", + "vec3 hemiSpecular = vec3( 0.0 );" , + + "for( int i = 0; i < MAX_HEMI_LIGHTS; i ++ ) {", + + "vec4 lPosition = viewMatrix * vec4( hemisphereLightPosition[ i ], 1.0 );", + "vec3 lVector = normalize( lPosition.xyz + vViewPosition.xyz );", + + // diffuse + + "float dotProduct = dot( normal, lVector );", + "float hemiDiffuseWeight = 0.5 * dotProduct + 0.5;", + + "vec3 hemiColor = mix( hemisphereLightGroundColor[ i ], hemisphereLightSkyColor[ i ], hemiDiffuseWeight );", + + "hemiDiffuse += uDiffuseColor * hemiColor;", + + // specular (sky light) + + + "vec3 hemiHalfVectorSky = normalize( lVector + viewPosition );", + "float hemiDotNormalHalfSky = 0.5 * dot( normal, hemiHalfVectorSky ) + 0.5;", + "float hemiSpecularWeightSky = specularTex.r * max( pow( hemiDotNormalHalfSky, uShininess ), 0.0 );", + + // specular (ground light) + + "vec3 lVectorGround = normalize( -lPosition.xyz + vViewPosition.xyz );", + + "vec3 hemiHalfVectorGround = normalize( lVectorGround + viewPosition );", + "float hemiDotNormalHalfGround = 0.5 * dot( normal, hemiHalfVectorGround ) + 0.5;", + "float hemiSpecularWeightGround = specularTex.r * max( pow( hemiDotNormalHalfGround, uShininess ), 0.0 );", + + "#ifdef PHYSICALLY_BASED_SHADING", + + "float dotProductGround = dot( normal, lVectorGround );", + + // 2.0 => 2.0001 is hack to work around ANGLE bug + + "float specularNormalization = ( uShininess + 2.0001 ) / 8.0;", + + "vec3 schlickSky = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVector, hemiHalfVectorSky ), 5.0 );", + "vec3 schlickGround = uSpecularColor + vec3( 1.0 - uSpecularColor ) * pow( 1.0 - dot( lVectorGround, hemiHalfVectorGround ), 5.0 );", + "hemiSpecular += hemiColor * specularNormalization * ( schlickSky * hemiSpecularWeightSky * max( dotProduct, 0.0 ) + schlickGround * hemiSpecularWeightGround * max( dotProductGround, 0.0 ) );", + + "#else", + + "hemiSpecular += uSpecularColor * hemiColor * ( hemiSpecularWeightSky + hemiSpecularWeightGround ) * hemiDiffuseWeight;", + + "#endif", + + "}", + + "#endif", + + // all lights contribution summation + + "vec3 totalDiffuse = vec3( 0.0 );", + "vec3 totalSpecular = vec3( 0.0 );", + + "#if MAX_DIR_LIGHTS > 0", + + "totalDiffuse += dirDiffuse;", + "totalSpecular += dirSpecular;", + + "#endif", + + "#if MAX_HEMI_LIGHTS > 0", + + "totalDiffuse += hemiDiffuse;", + "totalSpecular += hemiSpecular;", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "totalDiffuse += pointDiffuse;", + "totalSpecular += pointSpecular;", + + "#endif", + + "#if MAX_SPOT_LIGHTS > 0", + + "totalDiffuse += spotDiffuse;", + "totalSpecular += spotSpecular;", + + "#endif", + + "#ifdef METAL", + + "gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor + totalSpecular );", + + "#else", + + "gl_FragColor.xyz = gl_FragColor.xyz * ( totalDiffuse + ambientLightColor * uAmbientColor ) + totalSpecular;", + + "#endif", + + "if ( enableReflection ) {", + + "vec3 vReflect;", + "vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );", + + "if ( useRefract ) {", + + "vReflect = refract( cameraToVertex, normal, uRefractionRatio );", + + "} else {", + + "vReflect = reflect( cameraToVertex, normal );", + + "}", + + "vec4 cubeColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );", + + "#ifdef GAMMA_INPUT", + + "cubeColor.xyz *= cubeColor.xyz;", + + "#endif", + + "gl_FragColor.xyz = mix( gl_FragColor.xyz, cubeColor.xyz, specularTex.r * uReflectivity );", + + "}", + + THREE.ShaderChunk[ "shadowmap_fragment" ], + THREE.ShaderChunk[ "linear_to_gamma_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n"), + + vertexShader: [ + + "attribute vec4 tangent;", + + "uniform vec2 uOffset;", + "uniform vec2 uRepeat;", + + "uniform bool enableDisplacement;", + + "#ifdef VERTEX_TEXTURES", + + "uniform sampler2D tDisplacement;", + "uniform float uDisplacementScale;", + "uniform float uDisplacementBias;", + + "#endif", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "varying vec3 vWorldPosition;", + "varying vec3 vViewPosition;", + + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "shadowmap_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "skinbase_vertex" ], + THREE.ShaderChunk[ "skinnormal_vertex" ], + + // normal, tangent and binormal vectors + + "#ifdef USE_SKINNING", + + "vNormal = normalMatrix * skinnedNormal.xyz;", + + "vec4 skinnedTangent = skinMatrix * vec4( tangent.xyz, 0.0 );", + "vTangent = normalMatrix * skinnedTangent.xyz;", + + "#else", + + "vNormal = normalMatrix * normal;", + "vTangent = normalMatrix * tangent.xyz;", + + "#endif", + + "vBinormal = cross( vNormal, vTangent ) * tangent.w;", + + "vUv = uv * uRepeat + uOffset;", + + // displacement mapping + + "vec3 displacedPosition;", + + "#ifdef VERTEX_TEXTURES", + + "if ( enableDisplacement ) {", + + "vec3 dv = texture2D( tDisplacement, uv ).xyz;", + "float df = uDisplacementScale * dv.x + uDisplacementBias;", + "displacedPosition = position + normalize( normal ) * df;", + + "} else {", + + "#ifdef USE_SKINNING", + + "vec4 skinVertex = vec4( position, 1.0 );", + + "vec4 skinned = boneMatX * skinVertex * skinWeight.x;", + "skinned += boneMatY * skinVertex * skinWeight.y;", + + "displacedPosition = skinned.xyz;", + + "#else", + + "displacedPosition = position;", + + "#endif", + + "}", + + "#else", + + "#ifdef USE_SKINNING", + + "vec4 skinVertex = vec4( position, 1.0 );", + + "vec4 skinned = boneMatX * skinVertex * skinWeight.x;", + "skinned += boneMatY * skinVertex * skinWeight.y;", + + "displacedPosition = skinned.xyz;", + + "#else", + + "displacedPosition = position;", + + "#endif", + + "#endif", + + // + + "vec4 mvPosition = modelViewMatrix * vec4( displacedPosition, 1.0 );", + "vec4 mPosition = modelMatrix * vec4( displacedPosition, 1.0 );", + + "gl_Position = projectionMatrix * mvPosition;", + + // + + "vWorldPosition = mPosition.xyz;", + "vViewPosition = -mvPosition.xyz;", + + // shadows + + "#ifdef USE_SHADOWMAP", + + "for( int i = 0; i < MAX_SHADOWS; i ++ ) {", + + "vShadowCoord[ i ] = shadowMatrix[ i ] * mPosition;", + + "}", + + "#endif", + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'cube': { + + uniforms: { "tCube": { type: "t", value: null }, + "tFlip": { type: "f", value: -1 } }, + + vertexShader: [ + + "varying vec3 vViewPosition;", + + "void main() {", + + "vec4 mPosition = modelMatrix * vec4( position, 1.0 );", + "vViewPosition = cameraPosition - mPosition.xyz;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform samplerCube tCube;", + "uniform float tFlip;", + + "varying vec3 vViewPosition;", + + "void main() {", + + "vec3 wPos = cameraPosition - vViewPosition;", + "gl_FragColor = textureCube( tCube, vec3( tFlip * wPos.x, wPos.yz ) );", + + "}" + + ].join("\n") + + } + + } + +}; + +}; +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For Text operations in three.js (See TextGeometry) + * + * It uses techniques used in: + * + * typeface.js and canvastext + * For converting fonts and rendering with javascript + * http://typeface.neocracy.org + * + * Triangulation ported from AS3 + * Simple Polygon Triangulation + * http://actionsnippet.com/?p=1462 + * + * A Method to triangulate shapes with holes + * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ + * + */ + +THREE.FontUtils = { + + faces : {}, + + // Just for now. face[weight][style] + + face : "helvetiker", + weight: "normal", + style : "normal", + size : 150, + divisions : 10, + + getFace : function() { + + return this.faces[ this.face ][ this.weight ][ this.style ]; + + }, + + loadFace : function( data ) { + + var family = data.familyName.toLowerCase(); + + var ThreeFont = this; + + ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + + ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + return data; + + }, + + drawText : function( text ) { + + var characterPts = [], allPts = []; + + // RenderText + + var i, p, + face = this.getFace(), + scale = this.size / face.resolution, + offset = 0, + chars = String( text ).split( '' ), + length = chars.length; + + var fontPaths = []; + + for ( i = 0; i < length; i ++ ) { + + var path = new THREE.Path(); + + var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset, path ); + offset += ret.offset; + + fontPaths.push( ret.path ); + + } + + // get the width + + var width = offset / 2; + // + // for ( p = 0; p < allPts.length; p++ ) { + // + // allPts[ p ].x -= width; + // + // } + + //var extract = this.extractPoints( allPts, characterPts ); + //extract.contour = allPts; + + //extract.paths = fontPaths; + //extract.offset = width; + + return { paths : fontPaths, offset : width }; + + }, + + + + + extractGlyphPoints : function( c, face, scale, offset, path ) { + + var pts = []; + + var i, i2, divisions, + outline, action, length, + scaleX, scaleY, + x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, + laste, + glyph = face.glyphs[ c ] || face.glyphs[ '?' ]; + + if ( !glyph ) return; + + if ( glyph.o ) { + + outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + length = outline.length; + + scaleX = scale; + scaleY = scale; + + for ( i = 0; i < length; ) { + + action = outline[ i ++ ]; + + //console.log( action ); + + switch( action ) { + + case 'm': + + // Move To + + x = outline[ i++ ] * scaleX + offset; + y = outline[ i++ ] * scaleY; + + path.moveTo( x, y ); + break; + + case 'l': + + // Line To + + x = outline[ i++ ] * scaleX + offset; + y = outline[ i++ ] * scaleY; + path.lineTo(x,y); + break; + + case 'q': + + // QuadraticCurveTo + + cpx = outline[ i++ ] * scaleX + offset; + cpy = outline[ i++ ] * scaleY; + cpx1 = outline[ i++ ] * scaleX + offset; + cpy1 = outline[ i++ ] * scaleY; + + path.quadraticCurveTo(cpx1, cpy1, cpx, cpy); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + var ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + } + + } + + break; + + case 'b': + + // Cubic Bezier Curve + + cpx = outline[ i++ ] * scaleX + offset; + cpy = outline[ i++ ] * scaleY; + cpx1 = outline[ i++ ] * scaleX + offset; + cpy1 = outline[ i++ ] * -scaleY; + cpx2 = outline[ i++ ] * scaleX + offset; + cpy2 = outline[ i++ ] * -scaleY; + + path.bezierCurveTo( cpx, cpy, cpx1, cpy1, cpx2, cpy2 ); + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2 ++ ) { + + var t = i2 / divisions; + var tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + var ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + + } + + } + + break; + + } + + } + } + + + + return { offset: glyph.ha*scale, path:path}; + } + +}; + + +THREE.FontUtils.generateShapes = function( text, parameters ) { + + // Parameters + + parameters = parameters || {}; + + var size = parameters.size !== undefined ? parameters.size : 100; + var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4; + + var font = parameters.font !== undefined ? parameters.font : "helvetiker"; + var weight = parameters.weight !== undefined ? parameters.weight : "normal"; + var style = parameters.style !== undefined ? parameters.style : "normal"; + + THREE.FontUtils.size = size; + THREE.FontUtils.divisions = curveSegments; + + THREE.FontUtils.face = font; + THREE.FontUtils.weight = weight; + THREE.FontUtils.style = style; + + // Get a Font data json object + + var data = THREE.FontUtils.drawText( text ); + + var paths = data.paths; + var shapes = []; + + for ( var p = 0, pl = paths.length; p < pl; p ++ ) { + + Array.prototype.push.apply( shapes, paths[ p ].toShapes() ); + + } + + return shapes; + +}; + + +/** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ + + +( function( namespace ) { + + var EPSILON = 0.0000000001; + + // takes in an contour array and returns + + var process = function( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count-- ) <= 0 ) { + + //** Triangulate: ERROR - probable bad polygon! + + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + // Sometimes warning is fine, especially polygons are triangulated in reverse. + console.log( "Warning, unable to triangulate polygon!" ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + /* + result.push( contour[ a ] ); + result.push( contour[ b ] ); + result.push( contour[ c ] ); + */ + result.push( [ contour[ a ], + contour[ b ], + contour[ c ] ] ); + + + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + + /* remove v from the remaining polygon */ + + for( s = v, t = v + 1; t < nv; s++, t++ ) { + + verts[ s ] = verts[ t ]; + + } + + nv--; + + /* reset error detection counter */ + + count = 2 * nv; + + } + + } + + if ( indices ) return vertIndices; + return result; + + }; + + // calculate area of the contour polygon + + var area = function ( contour ) { + + var n = contour.length; + var a = 0.0; + + for( var p = n - 1, q = 0; q < n; p = q++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + + } + + return a * 0.5; + + }; + + // see if p is inside triangle abc + + var insideTriangle = function( ax, ay, + bx, by, + cx, cy, + px, py ) { + + var aX, aY, bX, bY; + var cX, cY, apx, apy; + var bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; + + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; + apx= px -ax; apy= py - ay; + bpx= px - bx; bpy= py - by; + cpx= px - cx; cpy= py - cy; + + aCROSSbp = aX*bpy - aY*bpx; + cCROSSap = cX*apy - cY*apx; + bCROSScp = bX*cpy - bY*cpx; + + return ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) ); + + }; + + + var snip = function ( contour, u, v, w, n, verts ) { + + var p; + var ax, ay, bx, by; + var cx, cy, px, py; + + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; + + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; + + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; + + if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false; + + for ( p = 0; p < n; p++ ) { + + if( (p == u) || (p == v) || (p == w) ) continue; + + px = contour[ verts[ p ] ].x + py = contour[ verts[ p ] ].y + + if ( insideTriangle( ax, ay, bx, by, cx, cy, px, py ) ) return false; + + } + + return true; + + }; + + + namespace.Triangulate = process; + namespace.Triangulate.area = area; + + return namespace; + +})(THREE.FontUtils); + +// To use the typeface.js face files, hook up the API +self._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace };/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Extensible curve object + * + * Some common of Curve methods + * .getPoint(t), getTangent(t) + * .getPointAt(u), getTagentAt(u) + * .getPoints(), .getSpacedPoints() + * .getLength() + * .updateArcLengths() + * + * This file contains following classes: + * + * -- 2d classes -- + * THREE.Curve + * THREE.LineCurve + * THREE.QuadraticBezierCurve + * THREE.CubicBezierCurve + * THREE.SplineCurve + * THREE.ArcCurve + * THREE.EllipseCurve + * + * -- 3d classes -- + * THREE.LineCurve3 + * THREE.QuadraticBezierCurve3 + * THREE.CubicBezierCurve3 + * THREE.SplineCurve3 + * THREE.ClosedSplineCurve3 + * + * A series of curves can be represented as a THREE.CurvePath + * + **/ + +/************************************************************** + * Abstract Curve base class + **************************************************************/ + +THREE.Curve = function () { + +}; + +// Virtual base class method to overwrite and implement in subclasses +// - t [0 .. 1] + +THREE.Curve.prototype.getPoint = function ( t ) { + + console.log( "Warning, getPoint() not implemented!" ); + return null; + +}; + +// Get point at relative position in curve according to arc length +// - u [0 .. 1] + +THREE.Curve.prototype.getPointAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getPoint( t ); + +}; + +// Get sequence of points using getPoint( t ) + +THREE.Curve.prototype.getPoints = function ( divisions ) { + + if ( !divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPoint( d / divisions ) ); + + } + + return pts; + +}; + +// Get sequence of points using getPointAt( u ) + +THREE.Curve.prototype.getSpacedPoints = function ( divisions ) { + + if ( !divisions ) divisions = 5; + + var d, pts = []; + + for ( d = 0; d <= divisions; d ++ ) { + + pts.push( this.getPointAt( d / divisions ) ); + + } + + return pts; + +}; + +// Get total curve arc length + +THREE.Curve.prototype.getLength = function () { + + var lengths = this.getLengths(); + return lengths[ lengths.length - 1 ]; + +}; + +// Get list of cumulative segment lengths + +THREE.Curve.prototype.getLengths = function ( divisions ) { + + if ( !divisions ) divisions = (this.__arcLengthDivisions) ? (this.__arcLengthDivisions): 200; + + if ( this.cacheArcLengths + && ( this.cacheArcLengths.length == divisions + 1 ) + && !this.needsUpdate) { + + //console.log( "cached", this.cacheArcLengths ); + return this.cacheArcLengths; + + } + + this.needsUpdate = false; + + var cache = []; + var current, last = this.getPoint( 0 ); + var p, sum = 0; + + cache.push( 0 ); + + for ( p = 1; p <= divisions; p ++ ) { + + current = this.getPoint ( p / divisions ); + sum += current.distanceTo( last ); + cache.push( sum ); + last = current; + + } + + this.cacheArcLengths = cache; + + return cache; // { sums: cache, sum:sum }; Sum is in the last element. + +}; + + +THREE.Curve.prototype.updateArcLengths = function() { + this.needsUpdate = true; + this.getLengths(); +}; + +// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equi distance + +THREE.Curve.prototype.getUtoTmapping = function ( u, distance ) { + + var arcLengths = this.getLengths(); + + var i = 0, il = arcLengths.length; + + var targetArcLength; // The targeted u distance value to get + + if ( distance ) { + + targetArcLength = distance; + + } else { + + targetArcLength = u * arcLengths[ il - 1 ]; + + } + + //var time = Date.now(); + + // binary search for the index with largest value smaller than target u distance + + var low = 0, high = il - 1, comparison; + + while ( low <= high ) { + + i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats + + comparison = arcLengths[ i ] - targetArcLength; + + if ( comparison < 0 ) { + + low = i + 1; + continue; + + } else if ( comparison > 0 ) { + + high = i - 1; + continue; + + } else { + + high = i; + break; + + // DONE + + } + + } + + i = high; + + //console.log('b' , i, low, high, Date.now()- time); + + if ( arcLengths[ i ] == targetArcLength ) { + + var t = i / ( il - 1 ); + return t; + + } + + // we could get finer grain at lengths, or use simple interpolatation between two points + + var lengthBefore = arcLengths[ i ]; + var lengthAfter = arcLengths[ i + 1 ]; + + var segmentLength = lengthAfter - lengthBefore; + + // determine where we are between the 'before' and 'after' points + + var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength; + + // add that fractional amount to t + + var t = ( i + segmentFraction ) / ( il -1 ); + + return t; + +}; + + +// In 2D space, there are actually 2 normal vectors, +// and in 3D space, infinte +// TODO this should be depreciated. +THREE.Curve.prototype.getNormalVector = function( t ) { + + var vec = this.getTangent( t ); + + return new THREE.Vector2( -vec.y , vec.x ); + +}; + +// Returns a unit vector tangent at t +// In case any sub curve does not implement its tangent / normal finding, +// we get 2 points with a small delta and find a gradient of the 2 points +// which seems to make a reasonable approximation + +THREE.Curve.prototype.getTangent = function( t ) { + + var delta = 0.0001; + var t1 = t - delta; + var t2 = t + delta; + + // Capping in case of danger + + if ( t1 < 0 ) t1 = 0; + if ( t2 > 1 ) t2 = 1; + + var pt1 = this.getPoint( t1 ); + var pt2 = this.getPoint( t2 ); + + var vec = pt2.clone().subSelf(pt1); + return vec.normalize(); + +}; + + +THREE.Curve.prototype.getTangentAt = function ( u ) { + + var t = this.getUtoTmapping( u ); + return this.getTangent( t ); + +}; + +/************************************************************** + * Line + **************************************************************/ + +THREE.LineCurve = function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.LineCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.LineCurve.prototype.getPoint = function ( t ) { + + var point = this.v2.clone().subSelf(this.v1); + point.multiplyScalar( t ).addSelf( this.v1 ); + + return point; + +}; + +// Line curve is linear, so we can overwrite default getPointAt + +THREE.LineCurve.prototype.getPointAt = function ( u ) { + + return this.getPoint( u ); + +}; + +THREE.LineCurve.prototype.getTangent = function( t ) { + + var tangent = this.v2.clone().subSelf(this.v1); + + return tangent.normalize(); + +}; + +/************************************************************** + * Quadratic Bezier curve + **************************************************************/ + + +THREE.QuadraticBezierCurve = function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + +}; + +THREE.QuadraticBezierCurve.prototype = Object.create( THREE.Curve.prototype ); + + +THREE.QuadraticBezierCurve.prototype.getPoint = function ( t ) { + + var tx, ty; + + tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + + return new THREE.Vector2( tx, ty ); + +}; + + +THREE.QuadraticBezierCurve.prototype.getTangent = function( t ) { + + var tx, ty; + + tx = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Curve.Utils.tangentQuadraticBezier( t, this.v0.y, this.v1.y, this.v2.y ); + + // returns unit vector + + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); + + return tangent; + +}; + + +/************************************************************** + * Cubic Bezier curve + **************************************************************/ + +THREE.CubicBezierCurve = function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + +}; + +THREE.CubicBezierCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.CubicBezierCurve.prototype.getPoint = function ( t ) { + + var tx, ty; + + tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + + return new THREE.Vector2( tx, ty ); + +}; + +THREE.CubicBezierCurve.prototype.getTangent = function( t ) { + + var tx, ty; + + tx = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Curve.Utils.tangentCubicBezier( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + + var tangent = new THREE.Vector2( tx, ty ); + tangent.normalize(); + + return tangent; + +}; + + +/************************************************************** + * Spline curve + **************************************************************/ + +THREE.SplineCurve = function ( points /* array of Vector2 */ ) { + + this.points = (points == undefined) ? [] : points; + +}; + +THREE.SplineCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.SplineCurve.prototype.getPoint = function ( t ) { + + var v = new THREE.Vector2(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 1 ) * t; + + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? points.length -1 : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? points.length -1 : intPoint + 2; + + v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); + v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); + + return v; + +}; + +/************************************************************** + * Ellipse curve + **************************************************************/ + +THREE.EllipseCurve = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, + aClockwise ) { + + this.aX = aX; + this.aY = aY; + + this.xRadius = xRadius; + this.yRadius = yRadius; + + this.aStartAngle = aStartAngle; + this.aEndAngle = aEndAngle; + + this.aClockwise = aClockwise; + +}; + +THREE.EllipseCurve.prototype = Object.create( THREE.Curve.prototype ); + +THREE.EllipseCurve.prototype.getPoint = function ( t ) { + + var deltaAngle = this.aEndAngle - this.aStartAngle; + + if ( !this.aClockwise ) { + + t = 1 - t; + + } + + var angle = this.aStartAngle + t * deltaAngle; + + var tx = this.aX + this.xRadius * Math.cos( angle ); + var ty = this.aY + this.yRadius * Math.sin( angle ); + + return new THREE.Vector2( tx, ty ); + +}; + +/************************************************************** + * Arc curve + **************************************************************/ + +THREE.ArcCurve = function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) { + + THREE.EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise ); +}; + +THREE.ArcCurve.prototype = Object.create( THREE.EllipseCurve.prototype ); + + +/************************************************************** + * Utils + **************************************************************/ + +THREE.Curve.Utils = { + + tangentQuadraticBezier: function ( t, p0, p1, p2 ) { + + return 2 * ( 1 - t ) * ( p1 - p0 ) + 2 * t * ( p2 - p1 ); + + }, + + // Puay Bing, thanks for helping with this derivative! + + tangentCubicBezier: function (t, p0, p1, p2, p3 ) { + + return -3 * p0 * (1 - t) * (1 - t) + + 3 * p1 * (1 - t) * (1-t) - 6 *t *p1 * (1-t) + + 6 * t * p2 * (1-t) - 3 * t * t * p2 + + 3 * t * t * p3; + }, + + + tangentSpline: function ( t, p0, p1, p2, p3 ) { + + // To check if my formulas are correct + + var h00 = 6 * t * t - 6 * t; // derived from 2t^3 − 3t^2 + 1 + var h10 = 3 * t * t - 4 * t + 1; // t^3 − 2t^2 + t + var h01 = -6 * t * t + 6 * t; // − 2t3 + 3t2 + var h11 = 3 * t * t - 2 * t; // t3 − t2 + + return h00 + h10 + h01 + h11; + + }, + + // Catmull-Rom + + interpolate: function( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5; + var v1 = ( p3 - p1 ) * 0.5; + var t2 = t * t; + var t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + +}; + + +// TODO: Transformation for Curves? + +/************************************************************** + * 3D Curves + **************************************************************/ + +// A Factory method for creating new curve subclasses + +THREE.Curve.create = function ( constructor, getPointFunc ) { + + constructor.prototype = Object.create( THREE.Curve.prototype ); + constructor.prototype.getPoint = getPointFunc; + + return constructor; + +}; + + +/************************************************************** + * Line3D + **************************************************************/ + +THREE.LineCurve3 = THREE.Curve.create( + + function ( v1, v2 ) { + + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var r = new THREE.Vector3(); + + + r.sub( this.v2, this.v1 ); // diff + r.multiplyScalar( t ); + r.addSelf( this.v1 ); + + return r; + + } + +); + + +/************************************************************** + * Quadratic Bezier 3D curve + **************************************************************/ + +THREE.QuadraticBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + + }, + + function ( t ) { + + var tx, ty, tz; + + tx = THREE.Shape.Utils.b2( t, this.v0.x, this.v1.x, this.v2.x ); + ty = THREE.Shape.Utils.b2( t, this.v0.y, this.v1.y, this.v2.y ); + tz = THREE.Shape.Utils.b2( t, this.v0.z, this.v1.z, this.v2.z ); + + return new THREE.Vector3( tx, ty, tz ); + + } + +); + + + +/************************************************************** + * Cubic Bezier 3D curve + **************************************************************/ + +THREE.CubicBezierCurve3 = THREE.Curve.create( + + function ( v0, v1, v2, v3 ) { + + this.v0 = v0; + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + }, + + function ( t ) { + + var tx, ty, tz; + + tx = THREE.Shape.Utils.b3( t, this.v0.x, this.v1.x, this.v2.x, this.v3.x ); + ty = THREE.Shape.Utils.b3( t, this.v0.y, this.v1.y, this.v2.y, this.v3.y ); + tz = THREE.Shape.Utils.b3( t, this.v0.z, this.v1.z, this.v2.z, this.v3.z ); + + return new THREE.Vector3( tx, ty, tz ); + + } + +); + + + +/************************************************************** + * Spline 3D curve + **************************************************************/ + + +THREE.SplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */) { + + this.points = (points == undefined) ? [] : points; + + }, + + function ( t ) { + + var v = new THREE.Vector3(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 1 ) * t; + + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + + var pt0 = points[ c[0] ], + pt1 = points[ c[1] ], + pt2 = points[ c[2] ], + pt3 = points[ c[3] ]; + + v.x = THREE.Curve.Utils.interpolate(pt0.x, pt1.x, pt2.x, pt3.x, weight); + v.y = THREE.Curve.Utils.interpolate(pt0.y, pt1.y, pt2.y, pt3.y, weight); + v.z = THREE.Curve.Utils.interpolate(pt0.z, pt1.z, pt2.z, pt3.z, weight); + + return v; + + } + +); + + +// THREE.SplineCurve3.prototype.getTangent = function(t) { +// var v = new THREE.Vector3(); +// var c = []; +// var points = this.points, point, intPoint, weight; +// point = ( points.length - 1 ) * t; + +// intPoint = Math.floor( point ); +// weight = point - intPoint; + +// c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; +// c[ 1 ] = intPoint; +// c[ 2 ] = intPoint > points.length - 2 ? points.length - 1 : intPoint + 1; +// c[ 3 ] = intPoint > points.length - 3 ? points.length - 1 : intPoint + 2; + +// var pt0 = points[ c[0] ], +// pt1 = points[ c[1] ], +// pt2 = points[ c[2] ], +// pt3 = points[ c[3] ]; + +// // t = weight; +// v.x = THREE.Curve.Utils.tangentSpline( t, pt0.x, pt1.x, pt2.x, pt3.x ); +// v.y = THREE.Curve.Utils.tangentSpline( t, pt0.y, pt1.y, pt2.y, pt3.y ); +// v.z = THREE.Curve.Utils.tangentSpline( t, pt0.z, pt1.z, pt2.z, pt3.z ); + +// return v; + +// } + +/************************************************************** + * Closed Spline 3D curve + **************************************************************/ + + +THREE.ClosedSplineCurve3 = THREE.Curve.create( + + function ( points /* array of Vector3 */) { + + this.points = (points == undefined) ? [] : points; + + }, + + function ( t ) { + + var v = new THREE.Vector3(); + var c = []; + var points = this.points, point, intPoint, weight; + point = ( points.length - 0 ) * t; + // This needs to be from 0-length +1 + + intPoint = Math.floor( point ); + weight = point - intPoint; + + intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length; + c[ 0 ] = ( intPoint - 1 ) % points.length; + c[ 1 ] = ( intPoint ) % points.length; + c[ 2 ] = ( intPoint + 1 ) % points.length; + c[ 3 ] = ( intPoint + 2 ) % points.length; + + v.x = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].x, points[ c[ 1 ] ].x, points[ c[ 2 ] ].x, points[ c[ 3 ] ].x, weight ); + v.y = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].y, points[ c[ 1 ] ].y, points[ c[ 2 ] ].y, points[ c[ 3 ] ].y, weight ); + v.z = THREE.Curve.Utils.interpolate( points[ c[ 0 ] ].z, points[ c[ 1 ] ].z, points[ c[ 2 ] ].z, points[ c[ 3 ] ].z, weight ); + + return v; + + } + +); +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + **/ + +/************************************************************** + * Curved Path - a curve path is simply a array of connected + * curves, but retains the api of a curve + **************************************************************/ + +THREE.CurvePath = function () { + + this.curves = []; + this.bends = []; + + this.autoClose = false; // Automatically closes the path +}; + +THREE.CurvePath.prototype = Object.create( THREE.Curve.prototype ); + +THREE.CurvePath.prototype.add = function ( curve ) { + + this.curves.push( curve ); + +}; + +THREE.CurvePath.prototype.checkConnection = function() { + // TODO + // If the ending of curve is not connected to the starting + // or the next curve, then, this is not a real path +}; + +THREE.CurvePath.prototype.closePath = function() { + // TODO Test + // and verify for vector3 (needs to implement equals) + // Add a line curve if start and end of lines are not connected + var startPoint = this.curves[0].getPoint(0); + var endPoint = this.curves[this.curves.length-1].getPoint(1); + + if (!startPoint.equals(endPoint)) { + this.curves.push( new THREE.LineCurve(endPoint, startPoint) ); + } + +}; + +// To get accurate point with reference to +// entire path distance at time t, +// following has to be done: + +// 1. Length of each sub path have to be known +// 2. Locate and identify type of curve +// 3. Get t for the curve +// 4. Return curve.getPointAt(t') + +THREE.CurvePath.prototype.getPoint = function( t ) { + + var d = t * this.getLength(); + var curveLengths = this.getCurveLengths(); + var i = 0, diff, curve; + + // To think about boundaries points. + + while ( i < curveLengths.length ) { + + if ( curveLengths[ i ] >= d ) { + + diff = curveLengths[ i ] - d; + curve = this.curves[ i ]; + + var u = 1 - diff / curve.getLength(); + + return curve.getPointAt( u ); + + break; + } + + i ++; + + } + + return null; + + // loop where sum != 0, sum > d , sum+1 maxX ) maxX = p.x; + else if ( p.x < minX ) minX = p.x; + + if ( p.y > maxY ) maxY = p.y; + else if ( p.y < minY ) minY = p.y; + + if (v3) { + + if ( p.z > maxZ ) maxZ = p.z; + else if ( p.z < minZ ) minZ = p.z; + + } + + sum.addSelf( p ); + + } + + var ret = { + + minX: minX, + minY: minY, + maxX: maxX, + maxY: maxY, + centroid: sum.divideScalar( il ) + + }; + + if (v3) { + + ret.maxZ = maxZ; + ret.minZ = minZ; + + } + + return ret; + +}; + +/************************************************************** + * Create Geometries Helpers + **************************************************************/ + +/// Generate geometry from path points (for Line or ParticleSystem objects) + +THREE.CurvePath.prototype.createPointsGeometry = function( divisions ) { + + var pts = this.getPoints( divisions, true ); + return this.createGeometry( pts ); + +}; + +// Generate geometry from equidistance sampling along the path + +THREE.CurvePath.prototype.createSpacedPointsGeometry = function( divisions ) { + + var pts = this.getSpacedPoints( divisions, true ); + return this.createGeometry( pts ); + +}; + +THREE.CurvePath.prototype.createGeometry = function( points ) { + + var geometry = new THREE.Geometry(); + + for ( var i = 0; i < points.length; i ++ ) { + + geometry.vertices.push( new THREE.Vector3( points[ i ].x, points[ i ].y, points[ i ].z || 0) ); + + } + + return geometry; + +}; + + +/************************************************************** + * Bend / Wrap Helper Methods + **************************************************************/ + +// Wrap path / Bend modifiers? + +THREE.CurvePath.prototype.addWrapPath = function ( bendpath ) { + + this.bends.push( bendpath ); + +}; + +THREE.CurvePath.prototype.getTransformedPoints = function( segments, bends ) { + + var oldPts = this.getPoints( segments ); // getPoints getSpacedPoints + var i, il; + + if ( !bends ) { + + bends = this.bends; + + } + + for ( i = 0, il = bends.length; i < il; i ++ ) { + + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + + } + + return oldPts; + +}; + +THREE.CurvePath.prototype.getTransformedSpacedPoints = function( segments, bends ) { + + var oldPts = this.getSpacedPoints( segments ); + + var i, il; + + if ( !bends ) { + + bends = this.bends; + + } + + for ( i = 0, il = bends.length; i < il; i ++ ) { + + oldPts = this.getWrapPoints( oldPts, bends[ i ] ); + + } + + return oldPts; + +}; + +// This returns getPoints() bend/wrapped around the contour of a path. +// Read http://www.planetclegg.com/projects/WarpingTextToSplines.html + +THREE.CurvePath.prototype.getWrapPoints = function ( oldPts, path ) { + + var bounds = this.getBoundingBox(); + + var i, il, p, oldX, oldY, xNorm; + + for ( i = 0, il = oldPts.length; i < il; i ++ ) { + + p = oldPts[ i ]; + + oldX = p.x; + oldY = p.y; + + xNorm = oldX / bounds.maxX; + + // If using actual distance, for length > path, requires line extrusions + //xNorm = path.getUtoTmapping(xNorm, oldX); // 3 styles. 1) wrap stretched. 2) wrap stretch by arc length 3) warp by actual distance + + xNorm = path.getUtoTmapping( xNorm, oldX ); + + // check for out of bounds? + + var pathPt = path.getPoint( xNorm ); + var normal = path.getNormalVector( xNorm ).multiplyScalar( oldY ); + + p.x = pathPt.x + normal.x; + p.y = pathPt.y + normal.y; + + } + + return oldPts; + +}; + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Gyroscope = function () { + + THREE.Object3D.call( this ); + +}; + +THREE.Gyroscope.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.Gyroscope.prototype.updateMatrixWorld = function ( force ) { + + this.matrixAutoUpdate && this.updateMatrix(); + + // update matrixWorld + + if ( this.matrixWorldNeedsUpdate || force ) { + + if ( this.parent ) { + + this.matrixWorld.multiply( this.parent.matrixWorld, this.matrix ); + + this.matrixWorld.decompose( this.translationWorld, this.rotationWorld, this.scaleWorld ); + this.matrix.decompose( this.translationObject, this.rotationObject, this.scaleObject ); + + this.matrixWorld.compose( this.translationWorld, this.rotationObject, this.scaleWorld ); + + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + + this.matrixWorldNeedsUpdate = false; + + force = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].updateMatrixWorld( force ); + + } + +}; + +THREE.Gyroscope.prototype.translationWorld = new THREE.Vector3(); +THREE.Gyroscope.prototype.translationObject = new THREE.Vector3(); +THREE.Gyroscope.prototype.rotationWorld = new THREE.Quaternion(); +THREE.Gyroscope.prototype.rotationObject = new THREE.Quaternion(); +THREE.Gyroscope.prototype.scaleWorld = new THREE.Vector3(); +THREE.Gyroscope.prototype.scaleObject = new THREE.Vector3(); + +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Creates free form 2d path using series of points, lines or curves. + * + **/ + +THREE.Path = function ( points ) { + + THREE.CurvePath.call(this); + + this.actions = []; + + if ( points ) { + + this.fromPoints( points ); + + } + +}; + +THREE.Path.prototype = Object.create( THREE.CurvePath.prototype ); + +THREE.PathActions = { + + MOVE_TO: 'moveTo', + LINE_TO: 'lineTo', + QUADRATIC_CURVE_TO: 'quadraticCurveTo', // Bezier quadratic curve + BEZIER_CURVE_TO: 'bezierCurveTo', // Bezier cubic curve + CSPLINE_THRU: 'splineThru', // Catmull-rom spline + ARC: 'arc', // Circle + ELLIPSE: 'ellipse' +}; + +// TODO Clean up PATH API + +// Create path using straight lines to connect all points +// - vectors: array of Vector2 + +THREE.Path.prototype.fromPoints = function ( vectors ) { + + this.moveTo( vectors[ 0 ].x, vectors[ 0 ].y ); + + for ( var v = 1, vlen = vectors.length; v < vlen; v ++ ) { + + this.lineTo( vectors[ v ].x, vectors[ v ].y ); + + }; + +}; + +// startPath() endPath()? + +THREE.Path.prototype.moveTo = function ( x, y ) { + + var args = Array.prototype.slice.call( arguments ); + this.actions.push( { action: THREE.PathActions.MOVE_TO, args: args } ); + +}; + +THREE.Path.prototype.lineTo = function ( x, y ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.LineCurve( new THREE.Vector2( x0, y0 ), new THREE.Vector2( x, y ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.LINE_TO, args: args } ); + +}; + +THREE.Path.prototype.quadraticCurveTo = function( aCPx, aCPy, aX, aY ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.QuadraticBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCPx, aCPy ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.QUADRATIC_CURVE_TO, args: args } ); + +}; + +THREE.Path.prototype.bezierCurveTo = function( aCP1x, aCP1y, + aCP2x, aCP2y, + aX, aY ) { + + var args = Array.prototype.slice.call( arguments ); + + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + var curve = new THREE.CubicBezierCurve( new THREE.Vector2( x0, y0 ), + new THREE.Vector2( aCP1x, aCP1y ), + new THREE.Vector2( aCP2x, aCP2y ), + new THREE.Vector2( aX, aY ) ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.BEZIER_CURVE_TO, args: args } ); + +}; + +THREE.Path.prototype.splineThru = function( pts /*Array of Vector*/ ) { + + var args = Array.prototype.slice.call( arguments ); + var lastargs = this.actions[ this.actions.length - 1 ].args; + + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; +//--- + var npts = [ new THREE.Vector2( x0, y0 ) ]; + Array.prototype.push.apply( npts, pts ); + + var curve = new THREE.SplineCurve( npts ); + this.curves.push( curve ); + + this.actions.push( { action: THREE.PathActions.CSPLINE_THRU, args: args } ); + +}; + +// FUTURE: Change the API or follow canvas API? + +THREE.Path.prototype.arc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + this.absarc(aX + x0, aY + y0, aRadius, + aStartAngle, aEndAngle, aClockwise ); + + }; + + THREE.Path.prototype.absarc = function ( aX, aY, aRadius, + aStartAngle, aEndAngle, aClockwise ) { + this.absellipse(aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise); + }; + +THREE.Path.prototype.ellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var lastargs = this.actions[ this.actions.length - 1].args; + var x0 = lastargs[ lastargs.length - 2 ]; + var y0 = lastargs[ lastargs.length - 1 ]; + + this.absellipse(aX + x0, aY + y0, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + + }; + + +THREE.Path.prototype.absellipse = function ( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ) { + + var args = Array.prototype.slice.call( arguments ); + var curve = new THREE.EllipseCurve( aX, aY, xRadius, yRadius, + aStartAngle, aEndAngle, aClockwise ); + this.curves.push( curve ); + + var lastPoint = curve.getPoint(aClockwise ? 1 : 0); + args.push(lastPoint.x); + args.push(lastPoint.y); + + this.actions.push( { action: THREE.PathActions.ELLIPSE, args: args } ); + + }; + +THREE.Path.prototype.getSpacedPoints = function ( divisions, closedPath ) { + + if ( ! divisions ) divisions = 40; + + var points = []; + + for ( var i = 0; i < divisions; i ++ ) { + + points.push( this.getPoint( i / divisions ) ); + + //if( !this.getPoint( i / divisions ) ) throw "DIE"; + + } + + // if ( closedPath ) { + // + // points.push( points[ 0 ] ); + // + // } + + return points; + +}; + +/* Return an array of vectors based on contour of the path */ + +THREE.Path.prototype.getPoints = function( divisions, closedPath ) { + + if (this.useSpacedPoints) { + console.log('tata'); + return this.getSpacedPoints( divisions, closedPath ); + } + + divisions = divisions || 12; + + var points = []; + + var i, il, item, action, args; + var cpx, cpy, cpx2, cpy2, cpx1, cpy1, cpx0, cpy0, + laste, j, + t, tx, ty; + + for ( i = 0, il = this.actions.length; i < il; i ++ ) { + + item = this.actions[ i ]; + + action = item.action; + args = item.args; + + switch( action ) { + + case THREE.PathActions.MOVE_TO: + + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + + break; + + case THREE.PathActions.LINE_TO: + + points.push( new THREE.Vector2( args[ 0 ], args[ 1 ] ) ); + + break; + + case THREE.PathActions.QUADRATIC_CURVE_TO: + + cpx = args[ 2 ]; + cpy = args[ 3 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + for ( j = 1; j <= divisions; j ++ ) { + + t = j / divisions; + + tx = THREE.Shape.Utils.b2( t, cpx0, cpx1, cpx ); + ty = THREE.Shape.Utils.b2( t, cpy0, cpy1, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case THREE.PathActions.BEZIER_CURVE_TO: + + cpx = args[ 4 ]; + cpy = args[ 5 ]; + + cpx1 = args[ 0 ]; + cpy1 = args[ 1 ]; + + cpx2 = args[ 2 ]; + cpy2 = args[ 3 ]; + + if ( points.length > 0 ) { + + laste = points[ points.length - 1 ]; + + cpx0 = laste.x; + cpy0 = laste.y; + + } else { + + laste = this.actions[ i - 1 ].args; + + cpx0 = laste[ laste.length - 2 ]; + cpy0 = laste[ laste.length - 1 ]; + + } + + + for ( j = 1; j <= divisions; j ++ ) { + + t = j / divisions; + + tx = THREE.Shape.Utils.b3( t, cpx0, cpx1, cpx2, cpx ); + ty = THREE.Shape.Utils.b3( t, cpy0, cpy1, cpy2, cpy ); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + break; + + case THREE.PathActions.CSPLINE_THRU: + + laste = this.actions[ i - 1 ].args; + + var last = new THREE.Vector2( laste[ laste.length - 2 ], laste[ laste.length - 1 ] ); + var spts = [ last ]; + + var n = divisions * args[ 0 ].length; + + spts = spts.concat( args[ 0 ] ); + + var spline = new THREE.SplineCurve( spts ); + + for ( j = 1; j <= n; j ++ ) { + + points.push( spline.getPointAt( j / n ) ) ; + + } + + break; + + case THREE.PathActions.ARC: + + var aX = args[ 0 ], aY = args[ 1 ], + aRadius = args[ 2 ], + aStartAngle = args[ 3 ], aEndAngle = args[ 4 ], + aClockwise = !!args[ 5 ]; + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( j = 1; j <= tdivisions; j ++ ) { + + t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + aRadius * Math.cos( angle ); + ty = aY + aRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + case THREE.PathActions.ELLIPSE: + + var aX = args[ 0 ], aY = args[ 1 ], + xRadius = args[ 2 ], + yRadius = args[ 3 ], + aStartAngle = args[ 4 ], aEndAngle = args[ 5 ], + aClockwise = !!args[ 6 ]; + + + var deltaAngle = aEndAngle - aStartAngle; + var angle; + var tdivisions = divisions * 2; + + for ( j = 1; j <= tdivisions; j ++ ) { + + t = j / tdivisions; + + if ( ! aClockwise ) { + + t = 1 - t; + + } + + angle = aStartAngle + t * deltaAngle; + + tx = aX + xRadius * Math.cos( angle ); + ty = aY + yRadius * Math.sin( angle ); + + //console.log('t', t, 'angle', angle, 'tx', tx, 'ty', ty); + + points.push( new THREE.Vector2( tx, ty ) ); + + } + + //console.log(points); + + break; + + } // end switch + + } + + + + // Normalize to remove the closing point by default. + var lastPoint = points[ points.length - 1]; + var EPSILON = 0.0000000001; + if ( Math.abs(lastPoint.x - points[ 0 ].x) < EPSILON && + Math.abs(lastPoint.y - points[ 0 ].y) < EPSILON) + points.splice( points.length - 1, 1); + if ( closedPath ) { + + points.push( points[ 0 ] ); + + } + + return points; + +}; + +// Breaks path into shapes + +THREE.Path.prototype.toShapes = function() { + + var i, il, item, action, args; + + var subPaths = [], lastPath = new THREE.Path(); + + for ( i = 0, il = this.actions.length; i < il; i ++ ) { + + item = this.actions[ i ]; + + args = item.args; + action = item.action; + + if ( action == THREE.PathActions.MOVE_TO ) { + + if ( lastPath.actions.length != 0 ) { + + subPaths.push( lastPath ); + lastPath = new THREE.Path(); + + } + + } + + lastPath[ action ].apply( lastPath, args ); + + } + + if ( lastPath.actions.length != 0 ) { + + subPaths.push( lastPath ); + + } + + // console.log(subPaths); + + if ( subPaths.length == 0 ) return []; + + var tmpPath, tmpShape, shapes = []; + + var holesFirst = !THREE.Shape.Utils.isClockWise( subPaths[ 0 ].getPoints() ); + // console.log("Holes first", holesFirst); + + if ( subPaths.length == 1) { + tmpPath = subPaths[0]; + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + shapes.push( tmpShape ); + return shapes; + }; + + if ( holesFirst ) { + + tmpShape = new THREE.Shape(); + + for ( i = 0, il = subPaths.length; i < il; i ++ ) { + + tmpPath = subPaths[ i ]; + + if ( THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ) ) { + + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + + shapes.push( tmpShape ); + tmpShape = new THREE.Shape(); + + //console.log('cw', i); + + } else { + + tmpShape.holes.push( tmpPath ); + + //console.log('ccw', i); + + } + + } + + } else { + + // Shapes first + + for ( i = 0, il = subPaths.length; i < il; i ++ ) { + + tmpPath = subPaths[ i ]; + + if ( THREE.Shape.Utils.isClockWise( tmpPath.getPoints() ) ) { + + + if ( tmpShape ) shapes.push( tmpShape ); + + tmpShape = new THREE.Shape(); + tmpShape.actions = tmpPath.actions; + tmpShape.curves = tmpPath.curves; + + } else { + + tmpShape.holes.push( tmpPath ); + + } + + } + + shapes.push( tmpShape ); + + } + + //console.log("shape", shapes); + + return shapes; + +}; +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * Defines a 2d shape plane using paths. + **/ + +// STEP 1 Create a path. +// STEP 2 Turn path into shape. +// STEP 3 ExtrudeGeometry takes in Shape/Shapes +// STEP 3a - Extract points from each shape, turn to vertices +// STEP 3b - Triangulate each shape, add faces. + +THREE.Shape = function ( ) { + + THREE.Path.apply( this, arguments ); + this.holes = []; + +}; + +THREE.Shape.prototype = Object.create( THREE.Path.prototype ); + +// Convenience method to return ExtrudeGeometry + +THREE.Shape.prototype.extrude = function ( options ) { + + var extruded = new THREE.ExtrudeGeometry( this, options ); + return extruded; + +}; + +// Convenience method to return ShapeGeometry + +THREE.Shape.prototype.makeGeometry = function ( options ) { + + var geometry = new THREE.ShapeGeometry( this, options ); + return geometry; + +}; + +// Get points of holes + +THREE.Shape.prototype.getPointsHoles = function ( divisions ) { + + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedPoints( divisions, this.bends ); + + } + + return holesPts; + +}; + +// Get points of holes (spaced by regular distance) + +THREE.Shape.prototype.getSpacedPointsHoles = function ( divisions ) { + + var i, il = this.holes.length, holesPts = []; + + for ( i = 0; i < il; i ++ ) { + + holesPts[ i ] = this.holes[ i ].getTransformedSpacedPoints( divisions, this.bends ); + + } + + return holesPts; + +}; + + +// Get points of shape and holes (keypoints based on segments parameter) + +THREE.Shape.prototype.extractAllPoints = function ( divisions ) { + + return { + + shape: this.getTransformedPoints( divisions ), + holes: this.getPointsHoles( divisions ) + + }; + +}; + +THREE.Shape.prototype.extractPoints = function ( divisions ) { + + if (this.useSpacedPoints) { + return this.extractAllSpacedPoints(divisions); + } + + return this.extractAllPoints(divisions); + +}; + +// +// THREE.Shape.prototype.extractAllPointsWithBend = function ( divisions, bend ) { +// +// return { +// +// shape: this.transform( bend, divisions ), +// holes: this.getPointsHoles( divisions, bend ) +// +// }; +// +// }; + +// Get points of shape and holes (spaced by regular distance) + +THREE.Shape.prototype.extractAllSpacedPoints = function ( divisions ) { + + return { + + shape: this.getTransformedSpacedPoints( divisions ), + holes: this.getSpacedPointsHoles( divisions ) + + }; + +}; + +/************************************************************** + * Utils + **************************************************************/ + +THREE.Shape.Utils = { + + /* + contour - array of vector2 for contour + holes - array of array of vector2 + */ + + removeHoles: function ( contour, holes ) { + + var shape = contour.concat(); // work on this shape + var allpoints = shape.concat(); + + /* For each isolated shape, find the closest points and break to the hole to allow triangulation */ + + + var prevShapeVert, nextShapeVert, + prevHoleVert, nextHoleVert, + holeIndex, shapeIndex, + shapeId, shapeGroup, + h, h2, + hole, shortest, d, + p, pts1, pts2, + tmpShape1, tmpShape2, + tmpHole1, tmpHole2, + verts = []; + + for ( h = 0; h < holes.length; h ++ ) { + + hole = holes[ h ]; + + /* + shapeholes[ h ].concat(); // preserves original + holes.push( hole ); + */ + + Array.prototype.push.apply( allpoints, hole ); + + shortest = Number.POSITIVE_INFINITY; + + + // Find the shortest pair of pts between shape and hole + + // Note: Actually, I'm not sure now if we could optimize this to be faster than O(m*n) + // Using distanceToSquared() intead of distanceTo() should speed a little + // since running square roots operations are reduced. + + for ( h2 = 0; h2 < hole.length; h2 ++ ) { + + pts1 = hole[ h2 ]; + var dist = []; + + for ( p = 0; p < shape.length; p++ ) { + + pts2 = shape[ p ]; + d = pts1.distanceToSquared( pts2 ); + dist.push( d ); + + if ( d < shortest ) { + + shortest = d; + holeIndex = h2; + shapeIndex = p; + + } + + } + + } + + //console.log("shortest", shortest, dist); + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + + var areaapts = [ + + hole[ holeIndex ], + shape[ shapeIndex ], + shape[ prevShapeVert ] + + ]; + + var areaa = THREE.FontUtils.Triangulate.area( areaapts ); + + var areabpts = [ + + hole[ holeIndex ], + hole[ prevHoleVert ], + shape[ shapeIndex ] + + ]; + + var areab = THREE.FontUtils.Triangulate.area( areabpts ); + + var shapeOffset = 1; + var holeOffset = -1; + + var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex; + shapeIndex += shapeOffset; + holeIndex += holeOffset; + + if ( shapeIndex < 0 ) { shapeIndex += shape.length; } + shapeIndex %= shape.length; + + if ( holeIndex < 0 ) { holeIndex += hole.length; } + holeIndex %= hole.length; + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + + areaapts = [ + + hole[ holeIndex ], + shape[ shapeIndex ], + shape[ prevShapeVert ] + + ]; + + var areaa2 = THREE.FontUtils.Triangulate.area( areaapts ); + + areabpts = [ + + hole[ holeIndex ], + hole[ prevHoleVert ], + shape[ shapeIndex ] + + ]; + + var areab2 = THREE.FontUtils.Triangulate.area( areabpts ); + //console.log(areaa,areab ,areaa2,areab2, ( areaa + areab ), ( areaa2 + areab2 )); + + if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) { + + // In case areas are not correct. + //console.log("USE THIS"); + + shapeIndex = oldShapeIndex; + holeIndex = oldHoleIndex ; + + if ( shapeIndex < 0 ) { shapeIndex += shape.length; } + shapeIndex %= shape.length; + + if ( holeIndex < 0 ) { holeIndex += hole.length; } + holeIndex %= hole.length; + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + + } else { + + //console.log("USE THAT ") + + } + + tmpShape1 = shape.slice( 0, shapeIndex ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex ); + + // Should check orders here again? + + var trianglea = [ + + hole[ holeIndex ], + shape[ shapeIndex ], + shape[ prevShapeVert ] + + ]; + + var triangleb = [ + + hole[ holeIndex ] , + hole[ prevHoleVert ], + shape[ shapeIndex ] + + ]; + + verts.push( trianglea ); + verts.push( triangleb ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + } + + return { + + shape:shape, /* shape with no holes */ + isolatedPts: verts, /* isolated faces */ + allpoints: allpoints + + } + + + }, + + triangulateShape: function ( contour, holes ) { + + var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes ); + + var shape = shapeWithoutHoles.shape, + allpoints = shapeWithoutHoles.allpoints, + isolatedPts = shapeWithoutHoles.isolatedPts; + + var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape + + // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It's probably easier to do the first. + + //console.log( "triangles",triangles, triangles.length ); + //console.log( "allpoints",allpoints, allpoints.length ); + + var i, il, f, face, + key, index, + allPointsMap = {}, + isolatedPointsMap = {}; + + // prepare all points map + + for ( i = 0, il = allpoints.length; i < il; i ++ ) { + + key = allpoints[ i ].x + ":" + allpoints[ i ].y; + + if ( allPointsMap[ key ] !== undefined ) { + + console.log( "Duplicate point", key ); + + } + + allPointsMap[ key ] = i; + + } + + // check all face vertices against all points map + + for ( i = 0, il = triangles.length; i < il; i ++ ) { + + face = triangles[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + // check isolated points vertices against all points map + + for ( i = 0, il = isolatedPts.length; i < il; i ++ ) { + + face = isolatedPts[ i ]; + + for ( f = 0; f < 3; f ++ ) { + + key = face[ f ].x + ":" + face[ f ].y; + + index = allPointsMap[ key ]; + + if ( index !== undefined ) { + + face[ f ] = index; + + } + + } + + } + + return triangles.concat( isolatedPts ); + + }, // end triangulate shapes + + /* + triangulate2 : function( pts, holes ) { + + // For use with Poly2Tri.js + + var allpts = pts.concat(); + var shape = []; + for (var p in pts) { + shape.push(new js.poly2tri.Point(pts[p].x, pts[p].y)); + } + + var swctx = new js.poly2tri.SweepContext(shape); + + for (var h in holes) { + var aHole = holes[h]; + var newHole = [] + for (i in aHole) { + newHole.push(new js.poly2tri.Point(aHole[i].x, aHole[i].y)); + allpts.push(aHole[i]); + } + swctx.AddHole(newHole); + } + + var find; + var findIndexForPt = function (pt) { + find = new THREE.Vector2(pt.x, pt.y); + var p; + for (p=0, pl = allpts.length; p 1 ) { + + console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h ); + scale = scale < 0 ? 0 : 1; + + } + + // interpolate + + if ( type === "pos" ) { + + vector = object.position; + + if ( this.interpolationType === THREE.AnimationHandler.LINEAR ) { + + vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; + this.points[ 1 ] = prevXYZ; + this.points[ 2 ] = nextXYZ; + this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + + scale = scale * 0.33 + 0.33; + + currentPoint = this.interpolateCatmullRom( this.points, scale ); + + vector.x = currentPoint[ 0 ]; + vector.y = currentPoint[ 1 ]; + vector.z = currentPoint[ 2 ]; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 ); + + this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); + this.target.subSelf( vector ); + this.target.y = 0; + this.target.normalize(); + + angle = Math.atan2( this.target.x, this.target.z ); + object.rotation.set( 0, angle, 0 ); + + } + + } + + } else if ( type === "rot" ) { + + THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale ); + + } else if ( type === "scl" ) { + + vector = object.scale; + + vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + } + + } + + } + +}; + +// Catmull-Rom spline + +THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) { + + var c = [], v3 = [], + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + point = ( points.length - 1 ) * scale; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; + + pa = points[ c[ 0 ] ]; + pb = points[ c[ 1 ] ]; + pc = points[ c[ 2 ] ]; + pd = points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3[ 0 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); + v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); + v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); + + return v3; + +}; + +THREE.Animation.prototype.interpolate = function ( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + +}; + + + +// Get next key with + +THREE.Animation.prototype.getNextKeyWith = function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key < keys.length - 1 ? key : keys.length - 1; + + } else { + + key = key % keys.length; + + } + + for ( ; key < keys.length; key++ ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ 0 ]; + +}; + +// Get previous key with + +THREE.Animation.prototype.getPrevKeyWith = function ( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key > 0 ? key : 0; + + } else { + + key = key >= 0 ? key : key + keys.length; + + } + + + for ( ; key >= 0; key -- ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; + +}; +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author khang duong + * @author erik kitson + */ + +THREE.KeyFrameAnimation = function( root, data, JITCompile ) { + + this.root = root; + this.data = THREE.AnimationHandler.get( data ); + this.hierarchy = THREE.AnimationHandler.parse( root ); + this.currentTime = 0; + this.timeScale = 0.001; + this.isPlaying = false; + this.isPaused = true; + this.loop = true; + this.JITCompile = JITCompile !== undefined ? JITCompile : true; + + // initialize to first keyframes + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + + var keys = this.data.hierarchy[h].keys, + sids = this.data.hierarchy[h].sids, + obj = this.hierarchy[h]; + + if ( keys.length && sids ) { + + for ( var s = 0; s < sids.length; s++ ) { + + var sid = sids[ s ], + next = this.getNextKeyWith( sid, h, 0 ); + + if ( next ) { + + next.apply( sid ); + + } + + } + + obj.matrixAutoUpdate = false; + this.data.hierarchy[h].node.updateMatrix(); + obj.matrixWorldNeedsUpdate = true; + + } + + } + +}; + +// Play + +THREE.KeyFrameAnimation.prototype.play = function( loop, startTimeMS ) { + + if( !this.isPlaying ) { + + this.isPlaying = true; + this.loop = loop !== undefined ? loop : true; + this.currentTime = startTimeMS !== undefined ? startTimeMS : 0; + this.startTimeMs = startTimeMS; + this.startTime = 10000000; + this.endTime = -this.startTime; + + + // reset key cache + + var h, hl = this.hierarchy.length, + object, + node; + + for ( h = 0; h < hl; h++ ) { + + object = this.hierarchy[ h ]; + node = this.data.hierarchy[ h ]; + object.useQuaternion = true; + + if ( node.animationCache === undefined ) { + + node.animationCache = {}; + node.animationCache.prevKey = null; + node.animationCache.nextKey = null; + node.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix; + + } + + var keys = this.data.hierarchy[h].keys; + + if (keys.length) { + + node.animationCache.prevKey = keys[ 0 ]; + node.animationCache.nextKey = keys[ 1 ]; + + this.startTime = Math.min( keys[0].time, this.startTime ); + this.endTime = Math.max( keys[keys.length - 1].time, this.endTime ); + + } + + } + + this.update( 0 ); + + } + + this.isPaused = false; + + THREE.AnimationHandler.addToUpdate( this ); + +}; + + + +// Pause + +THREE.KeyFrameAnimation.prototype.pause = function() { + + if( this.isPaused ) { + + THREE.AnimationHandler.addToUpdate( this ); + + } else { + + THREE.AnimationHandler.removeFromUpdate( this ); + + } + + this.isPaused = !this.isPaused; + +}; + + +// Stop + +THREE.KeyFrameAnimation.prototype.stop = function() { + + this.isPlaying = false; + this.isPaused = false; + THREE.AnimationHandler.removeFromUpdate( this ); + + + // reset JIT matrix and remove cache + + for ( var h = 0; h < this.data.hierarchy.length; h++ ) { + + var obj = this.hierarchy[ h ]; + var node = this.data.hierarchy[ h ]; + + if ( node.animationCache !== undefined ) { + + var original = node.animationCache.originalMatrix; + + if( obj instanceof THREE.Bone ) { + + original.copy( obj.skinMatrix ); + obj.skinMatrix = original; + + } else { + + original.copy( obj.matrix ); + obj.matrix = original; + + } + + delete node.animationCache; + + } + + } + +}; + + +// Update + +THREE.KeyFrameAnimation.prototype.update = function( deltaTimeMS ) { + + // early out + + if( !this.isPlaying ) return; + + + // vars + + var prevKey, nextKey; + var object; + var node; + var frame; + var JIThierarchy = this.data.JIT.hierarchy; + var currentTime, unloopedCurrentTime; + var looped; + + + // update + + this.currentTime += deltaTimeMS * this.timeScale; + + unloopedCurrentTime = this.currentTime; + currentTime = this.currentTime = this.currentTime % this.data.length; + + // if looped around, the current time should be based on the startTime + if ( currentTime < this.startTimeMs ) { + + currentTime = this.currentTime = this.startTimeMs + currentTime; + + } + + frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 ); + looped = currentTime < unloopedCurrentTime; + + if ( looped && !this.loop ) { + + // Set the animation to the last keyframes and stop + for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + + var keys = this.data.hierarchy[h].keys, + sids = this.data.hierarchy[h].sids, + end = keys.length-1, + obj = this.hierarchy[h]; + + if ( keys.length ) { + + for ( var s = 0; s < sids.length; s++ ) { + + var sid = sids[ s ], + prev = this.getPrevKeyWith( sid, h, end ); + + if ( prev ) { + prev.apply( sid ); + + } + + } + + this.data.hierarchy[h].node.updateMatrix(); + obj.matrixWorldNeedsUpdate = true; + + } + + } + + this.stop(); + return; + + } + + // check pre-infinity + if ( currentTime < this.startTime ) { + + return; + + } + + // update + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + + object = this.hierarchy[ h ]; + node = this.data.hierarchy[ h ]; + + var keys = node.keys, + animationCache = node.animationCache; + + // use JIT? + + if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) { + + if( object instanceof THREE.Bone ) { + + object.skinMatrix = JIThierarchy[ h ][ frame ]; + object.matrixWorldNeedsUpdate = false; + + } else { + + object.matrix = JIThierarchy[ h ][ frame ]; + object.matrixWorldNeedsUpdate = true; + + } + + // use interpolation + + } else if ( keys.length ) { + + // make sure so original matrix and not JIT matrix is set + + if ( this.JITCompile && animationCache ) { + + if( object instanceof THREE.Bone ) { + + object.skinMatrix = animationCache.originalMatrix; + + } else { + + object.matrix = animationCache.originalMatrix; + + } + + } + + prevKey = animationCache.prevKey; + nextKey = animationCache.nextKey; + + if ( prevKey && nextKey ) { + + // switch keys? + + if ( nextKey.time <= unloopedCurrentTime ) { + + // did we loop? + + if ( looped && this.loop ) { + + prevKey = keys[ 0 ]; + nextKey = keys[ 1 ]; + + while ( nextKey.time < currentTime ) { + + prevKey = nextKey; + nextKey = keys[ prevKey.index + 1 ]; + + } + + } else if ( !looped ) { + + var lastIndex = keys.length - 1; + + while ( nextKey.time < currentTime && nextKey.index !== lastIndex ) { + + prevKey = nextKey; + nextKey = keys[ prevKey.index + 1 ]; + + } + + } + + animationCache.prevKey = prevKey; + animationCache.nextKey = nextKey; + + } + if(nextKey.time >= currentTime) + prevKey.interpolate( nextKey, currentTime ); + else + prevKey.interpolate( nextKey, nextKey.time); + + } + + this.data.hierarchy[h].node.updateMatrix(); + object.matrixWorldNeedsUpdate = true; + + } + + } + + // update JIT? + + if ( this.JITCompile ) { + + if ( JIThierarchy[ 0 ][ frame ] === undefined ) { + + this.hierarchy[ 0 ].updateMatrixWorld( true ); + + for ( var h = 0; h < this.hierarchy.length; h++ ) { + + if( this.hierarchy[ h ] instanceof THREE.Bone ) { + + JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone(); + + } else { + + JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone(); + + } + + } + + } + + } + +}; + +// Get next key with + +THREE.KeyFrameAnimation.prototype.getNextKeyWith = function( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key % keys.length; + + for ( ; key < keys.length; key++ ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ 0 ]; + +}; + +// Get previous key with + +THREE.KeyFrameAnimation.prototype.getPrevKeyWith = function( sid, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + key = key >= 0 ? key : key + keys.length; + + for ( ; key >= 0; key-- ) { + + if ( keys[ key ].hasTarget( sid ) ) { + + return keys[ key ]; + + } + + } + + return keys[ keys.length - 1 ]; + +}; +/** + * Camera for rendering cube maps + * - renders scene into axis-aligned cube + * + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.CubeCamera = function ( near, far, cubeResolution ) { + + THREE.Object3D.call( this ); + + var fov = 90, aspect = 1; + + var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPX.up.set( 0, -1, 0 ); + cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); + this.add( cameraPX ); + + var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNX.up.set( 0, -1, 0 ); + cameraNX.lookAt( new THREE.Vector3( -1, 0, 0 ) ); + this.add( cameraNX ); + + var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPY.up.set( 0, 0, 1 ); + cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); + this.add( cameraPY ); + + var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNY.up.set( 0, 0, -1 ); + cameraNY.lookAt( new THREE.Vector3( 0, -1, 0 ) ); + this.add( cameraNY ); + + var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraPZ.up.set( 0, -1, 0 ); + cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); + this.add( cameraPZ ); + + var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); + cameraNZ.up.set( 0, -1, 0 ); + cameraNZ.lookAt( new THREE.Vector3( 0, 0, -1 ) ); + this.add( cameraNZ ); + + this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter } ); + + this.updateCubeMap = function ( renderer, scene ) { + + var renderTarget = this.renderTarget; + var generateMipmaps = renderTarget.generateMipmaps; + + renderTarget.generateMipmaps = false; + + renderTarget.activeCubeFace = 0; + renderer.render( scene, cameraPX, renderTarget ); + + renderTarget.activeCubeFace = 1; + renderer.render( scene, cameraNX, renderTarget ); + + renderTarget.activeCubeFace = 2; + renderer.render( scene, cameraPY, renderTarget ); + + renderTarget.activeCubeFace = 3; + renderer.render( scene, cameraNY, renderTarget ); + + renderTarget.activeCubeFace = 4; + renderer.render( scene, cameraPZ, renderTarget ); + + renderTarget.generateMipmaps = generateMipmaps; + + renderTarget.activeCubeFace = 5; + renderer.render( scene, cameraNZ, renderTarget ); + + }; + +}; + +THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); +/* + * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog + * + * A general perpose camera, for setting FOV, Lens Focal Length, + * and switching between perspective and orthographic views easily. + * Use this only if you do not wish to manage + * both a Orthographic and Perspective Camera + * + */ + + +THREE.CombinedCamera = function ( width, height, fov, near, far, orthoNear, orthoFar ) { + + THREE.Camera.call( this ); + + this.fov = fov; + + this.left = -width / 2; + this.right = width / 2 + this.top = height / 2; + this.bottom = -height / 2; + + // We could also handle the projectionMatrix internally, but just wanted to test nested camera objects + + this.cameraO = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, orthoNear, orthoFar ); + this.cameraP = new THREE.PerspectiveCamera( fov, width / height, near, far ); + + this.zoom = 1; + + this.toPerspective(); + + var aspect = width/height; + +}; + +THREE.CombinedCamera.prototype = Object.create( THREE.Camera.prototype ); + +THREE.CombinedCamera.prototype.toPerspective = function () { + + // Switches to the Perspective Camera + + this.near = this.cameraP.near; + this.far = this.cameraP.far; + + this.cameraP.fov = this.fov / this.zoom ; + + this.cameraP.updateProjectionMatrix(); + + this.projectionMatrix = this.cameraP.projectionMatrix; + + this.inPerspectiveMode = true; + this.inOrthographicMode = false; + +}; + +THREE.CombinedCamera.prototype.toOrthographic = function () { + + // Switches to the Orthographic camera estimating viewport from Perspective + + var fov = this.fov; + var aspect = this.cameraP.aspect; + var near = this.cameraP.near; + var far = this.cameraP.far; + + // The size that we set is the mid plane of the viewing frustum + + var hyperfocus = ( near + far ) / 2; + + var halfHeight = Math.tan( fov / 2 ) * hyperfocus; + var planeHeight = 2 * halfHeight; + var planeWidth = planeHeight * aspect; + var halfWidth = planeWidth / 2; + + halfHeight /= this.zoom; + halfWidth /= this.zoom; + + this.cameraO.left = -halfWidth; + this.cameraO.right = halfWidth; + this.cameraO.top = halfHeight; + this.cameraO.bottom = -halfHeight; + + // this.cameraO.left = -farHalfWidth; + // this.cameraO.right = farHalfWidth; + // this.cameraO.top = farHalfHeight; + // this.cameraO.bottom = -farHalfHeight; + + // this.cameraO.left = this.left / this.zoom; + // this.cameraO.right = this.right / this.zoom; + // this.cameraO.top = this.top / this.zoom; + // this.cameraO.bottom = this.bottom / this.zoom; + + this.cameraO.updateProjectionMatrix(); + + this.near = this.cameraO.near; + this.far = this.cameraO.far; + this.projectionMatrix = this.cameraO.projectionMatrix; + + this.inPerspectiveMode = false; + this.inOrthographicMode = true; + +}; + + +THREE.CombinedCamera.prototype.setSize = function( width, height ) { + + this.cameraP.aspect = width / height; + this.left = -width / 2; + this.right = width / 2 + this.top = height / 2; + this.bottom = -height / 2; + +}; + + +THREE.CombinedCamera.prototype.setFov = function( fov ) { + + this.fov = fov; + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toOrthographic(); + + } + +}; + +// For mantaining similar API with PerspectiveCamera + +THREE.CombinedCamera.prototype.updateProjectionMatrix = function() { + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toPerspective(); + this.toOrthographic(); + + } + +}; + +/* +* Uses Focal Length (in mm) to estimate and set FOV +* 35mm (fullframe) camera is used if frame size is not specified; +* Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html +*/ +THREE.CombinedCamera.prototype.setLens = function ( focalLength, frameHeight ) { + + if ( frameHeight === undefined ) frameHeight = 24; + + var fov = 2 * Math.atan( frameHeight / ( focalLength * 2 ) ) * ( 180 / Math.PI ); + + this.setFov( fov ); + + return fov; +}; + + +THREE.CombinedCamera.prototype.setZoom = function( zoom ) { + + this.zoom = zoom; + + if ( this.inPerspectiveMode ) { + + this.toPerspective(); + + } else { + + this.toOrthographic(); + + } + +}; + +THREE.CombinedCamera.prototype.toFrontView = function() { + + this.rotation.x = 0; + this.rotation.y = 0; + this.rotation.z = 0; + + // should we be modifing the matrix instead? + + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toBackView = function() { + + this.rotation.x = 0; + this.rotation.y = Math.PI; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toLeftView = function() { + + this.rotation.x = 0; + this.rotation.y = - Math.PI / 2; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toRightView = function() { + + this.rotation.x = 0; + this.rotation.y = Math.PI / 2; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toTopView = function() { + + this.rotation.x = - Math.PI / 2; + this.rotation.y = 0; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +THREE.CombinedCamera.prototype.toBottomView = function() { + + this.rotation.x = Math.PI / 2; + this.rotation.y = 0; + this.rotation.z = 0; + this.rotationAutoUpdate = false; + +}; + +/** + * @author hughes + */ + +THREE.CircleGeometry = function ( radius, segments, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + radius = radius || 50; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2; + segments = segments !== undefined ? Math.max( 3, segments ) : 8; + + var i, uvs = [], + center = new THREE.Vector3(), centerUV = new THREE.UV( 0.5, 0.5 ); + + this.vertices.push(center); + uvs.push( centerUV ); + + for ( i = 0; i <= segments; i ++ ) { + + var vertex = new THREE.Vector3(); + + vertex.x = radius * Math.cos( thetaStart + i / segments * thetaLength ); + vertex.y = radius * Math.sin( thetaStart + i / segments * thetaLength ); + + this.vertices.push( vertex ); + uvs.push( new THREE.UV( ( vertex.x / radius + 1 ) / 2, - ( vertex.y / radius + 1 ) / 2 + 1 ) ); + + } + + var n = new THREE.Vector3( 0, 0, -1 ); + + for ( i = 1; i <= segments; i ++ ) { + + var v1 = i; + var v2 = i + 1 ; + var v3 = 0; + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n, n, n ] ) ); + this.faceVertexUvs[ 0 ].push( [ uvs[ i ], uvs[ i + 1 ], centerUV ] ); + + } + + this.computeCentroids(); + this.computeFaceNormals(); + + this.boundingSphere = { radius: radius }; + +}; + +THREE.CircleGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ + +THREE.CubeGeometry = function ( width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, sides ) { + + THREE.Geometry.call( this ); + + var scope = this, + width_half = width / 2, + height_half = height / 2, + depth_half = depth / 2; + + var mpx, mpy, mpz, mnx, mny, mnz; + + if ( materials !== undefined ) { + + if ( materials instanceof Array ) { + + this.materials = materials; + + } else { + + this.materials = []; + + for ( var i = 0; i < 6; i ++ ) { + + this.materials.push( materials ); + + } + + } + + mpx = 0; mnx = 1; mpy = 2; mny = 3; mpz = 4; mnz = 5; + + } else { + + this.materials = []; + + } + + this.sides = { px: true, nx: true, py: true, ny: true, pz: true, nz: true }; + + if ( sides != undefined ) { + + for ( var s in sides ) { + + if ( this.sides[ s ] !== undefined ) { + + this.sides[ s ] = sides[ s ]; + + } + + } + + } + + this.sides.px && buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, mpx ); // px + this.sides.nx && buildPlane( 'z', 'y', 1, - 1, depth, height, - width_half, mnx ); // nx + this.sides.py && buildPlane( 'x', 'z', 1, 1, width, depth, height_half, mpy ); // py + this.sides.ny && buildPlane( 'x', 'z', 1, - 1, width, depth, - height_half, mny ); // ny + this.sides.pz && buildPlane( 'x', 'y', 1, - 1, width, height, depth_half, mpz ); // pz + this.sides.nz && buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, mnz ); // nz + + function buildPlane( u, v, udir, vdir, width, height, depth, material ) { + + var w, ix, iy, + gridX = segmentsWidth || 1, + gridY = segmentsHeight || 1, + width_half = width / 2, + height_half = height / 2, + offset = scope.vertices.length; + + if ( ( u === 'x' && v === 'y' ) || ( u === 'y' && v === 'x' ) ) { + + w = 'z'; + + } else if ( ( u === 'x' && v === 'z' ) || ( u === 'z' && v === 'x' ) ) { + + w = 'y'; + gridY = segmentsDepth || 1; + + } else if ( ( u === 'z' && v === 'y' ) || ( u === 'y' && v === 'z' ) ) { + + w = 'x'; + gridX = segmentsDepth || 1; + + } + + var gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY, + normal = new THREE.Vector3(); + + normal[ w ] = depth > 0 ? 1 : - 1; + + for ( iy = 0; iy < gridY1; iy ++ ) { + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var vector = new THREE.Vector3(); + vector[ u ] = ( ix * segment_width - width_half ) * udir; + vector[ v ] = ( iy * segment_height - height_half ) * vdir; + vector[ w ] = depth; + + scope.vertices.push( vector ); + + } + + } + + for ( iy = 0; iy < gridY; iy++ ) { + + for ( ix = 0; ix < gridX; ix++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + var face = new THREE.Face4( a + offset, b + offset, c + offset, d + offset ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone(), normal.clone() ); + face.materialIndex = material; + + scope.faces.push( face ); + scope.faceVertexUvs[ 0 ].push( [ + new THREE.UV( ix / gridX, 1 - iy / gridY ), + new THREE.UV( ix / gridX, 1 - ( iy + 1 ) / gridY ), + new THREE.UV( ( ix + 1 ) / gridX, 1- ( iy + 1 ) / gridY ), + new THREE.UV( ( ix + 1 ) / gridX, 1 - iy / gridY ) + ] ); + + } + + } + + } + + this.computeCentroids(); + this.mergeVertices(); + +}; + +THREE.CubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.CylinderGeometry = function ( radiusTop, radiusBottom, height, radiusSegments, heightSegments, openEnded ) { + + THREE.Geometry.call( this ); + + radiusTop = radiusTop !== undefined ? radiusTop : 20; + radiusBottom = radiusBottom !== undefined ? radiusBottom : 20; + height = height !== undefined ? height : 100; + + var heightHalf = height / 2; + var segmentsX = radiusSegments || 8; + var segmentsY = heightSegments || 1; + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= segmentsY; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + var v = y / segmentsY; + var radius = v * ( radiusBottom - radiusTop ) + radiusTop; + + for ( x = 0; x <= segmentsX; x ++ ) { + + var u = x / segmentsX; + + var vertex = new THREE.Vector3(); + vertex.x = radius * Math.sin( u * Math.PI * 2 ); + vertex.y = - v * height + heightHalf; + vertex.z = radius * Math.cos( u * Math.PI * 2 ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.UV( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + var tanTheta = ( radiusBottom - radiusTop ) / height; + var na, nb; + + for ( x = 0; x < segmentsX; x ++ ) { + + if ( radiusTop !== 0 ) { + + na = this.vertices[ vertices[ 0 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 0 ][ x + 1 ] ].clone(); + + } else { + + na = this.vertices[ vertices[ 1 ][ x ] ].clone(); + nb = this.vertices[ vertices[ 1 ][ x + 1 ] ].clone(); + + } + + na.setY( Math.sqrt( na.x * na.x + na.z * na.z ) * tanTheta ).normalize(); + nb.setY( Math.sqrt( nb.x * nb.x + nb.z * nb.z ) * tanTheta ).normalize(); + + for ( y = 0; y < segmentsY; y ++ ) { + + var v1 = vertices[ y ][ x ]; + var v2 = vertices[ y + 1 ][ x ]; + var v3 = vertices[ y + 1 ][ x + 1 ]; + var v4 = vertices[ y ][ x + 1 ]; + + var n1 = na.clone(); + var n2 = na.clone(); + var n3 = nb.clone(); + var n4 = nb.clone(); + + var uv1 = uvs[ y ][ x ].clone(); + var uv2 = uvs[ y + 1 ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x + 1 ].clone(); + var uv4 = uvs[ y ][ x + 1 ].clone(); + + this.faces.push( new THREE.Face4( v1, v2, v3, v4, [ n1, n2, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3, uv4 ] ); + + } + + } + + // top cap + + if ( !openEnded && radiusTop > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, heightHalf, 0 ) ); + + for ( x = 0; x < segmentsX; x ++ ) { + + var v1 = vertices[ 0 ][ x ]; + var v2 = vertices[ 0 ][ x + 1 ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, 1, 0 ); + var n2 = new THREE.Vector3( 0, 1, 0 ); + var n3 = new THREE.Vector3( 0, 1, 0 ); + + var uv1 = uvs[ 0 ][ x ].clone(); + var uv2 = uvs[ 0 ][ x + 1 ].clone(); + var uv3 = new THREE.UV( uv2.u, 0 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + // bottom cap + + if ( !openEnded && radiusBottom > 0 ) { + + this.vertices.push( new THREE.Vector3( 0, - heightHalf, 0 ) ); + + for ( x = 0; x < segmentsX; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = this.vertices.length - 1; + + var n1 = new THREE.Vector3( 0, - 1, 0 ); + var n2 = new THREE.Vector3( 0, - 1, 0 ); + var n3 = new THREE.Vector3( 0, - 1, 0 ); + + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = new THREE.UV( uv2.u, 1 ); + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } + + } + + this.computeCentroids(); + this.computeFaceNormals(); + +} + +THREE.CylinderGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * + * Creates extruded geometry from a path shape. + * + * parameters = { + * + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves + * steps: , // number of points for z-side extrusions / used for subdividing segements of extrude spline too + * amount: , // Amount + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into text bevel goes + * bevelSize: , // how far from text outline is bevel + * bevelSegments: , // number of bevel layers + * + * extrudePath: // 3d spline path to extrude shape along. (creates Frames if .frames aren't defined) + * frames: // containing arrays of tangents, normals, binormals + * + * material: // material index for front and back faces + * extrudeMaterial: // material index for extrusion and beveled faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ + +THREE.ExtrudeGeometry = function ( shapes, options ) { + + if ( typeof( shapes ) === "undefined" ) { + shapes = []; + return; + } + + THREE.Geometry.call( this ); + + shapes = shapes instanceof Array ? shapes : [ shapes ]; + + this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox(); + + this.addShapeList( shapes, options ); + + this.computeCentroids(); + this.computeFaceNormals(); + + // can't really use automatic vertex normals + // as then front and back sides get smoothed too + // should do separate smoothing just for sides + + //this.computeVertexNormals(); + + //console.log( "took", ( Date.now() - startTime ) ); + +}; + +THREE.ExtrudeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +THREE.ExtrudeGeometry.prototype.addShapeList = function ( shapes, options ) { + var sl = shapes.length; + + for ( var s = 0; s < sl; s ++ ) { + var shape = shapes[ s ]; + this.addShape( shape, options ); + } +}; + +THREE.ExtrudeGeometry.prototype.addShape = function ( shape, options ) { + + var amount = options.amount !== undefined ? options.amount : 100; + + var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10 + var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8 + var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3; + + var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false + + var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12; + + var steps = options.steps !== undefined ? options.steps : 1; + + var extrudePath = options.extrudePath; + var extrudePts, extrudeByPath = false; + + var material = options.material; + var extrudeMaterial = options.extrudeMaterial; + + // Use default WorldUVGenerator if no UV generators are specified. + var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : THREE.ExtrudeGeometry.WorldUVGenerator; + + var shapebb = this.shapebb; + //shapebb = shape.getBoundingBox(); + + + + var splineTube, binormal, normal, position2; + if ( extrudePath ) { + + extrudePts = extrudePath.getSpacedPoints( steps ); + + extrudeByPath = true; + bevelEnabled = false; // bevels not supported for path extrusion + + // SETUP TNB variables + + // Reuse TNB from TubeGeomtry for now. + // TODO1 - have a .isClosed in spline? + + splineTube = options.frames !== undefined ? options.frames : new THREE.TubeGeometry.FrenetFrames(extrudePath, steps, false); + + // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length); + + binormal = new THREE.Vector3(); + normal = new THREE.Vector3(); + position2 = new THREE.Vector3(); + + } + + // Safeguards if bevels are not enabled + + if ( ! bevelEnabled ) { + + bevelSegments = 0; + bevelThickness = 0; + bevelSize = 0; + + } + + // Variables initalization + + var ahole, h, hl; // looping of holes + var scope = this; + var bevelPoints = []; + + var shapesOffset = this.vertices.length; + + var shapePoints = shape.extractPoints(); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = !THREE.Shape.Utils.isClockWise( vertices ) ; + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe ... + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + if ( THREE.Shape.Utils.isClockWise( ahole ) ) { + + holes[ h ] = ahole.reverse(); + + } + + } + + reverse = false; // If vertices are in order now, we shouldn't need to worry about them again (hopefully)! + + } + + + var faces = THREE.Shape.Utils.triangulateShape ( vertices, holes ); + + /* Vertices */ + + var contour = vertices; // vertices has all points but contour has only points of circumference + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + vertices = vertices.concat( ahole ); + + } + + + function scalePt2 ( pt, vec, size ) { + + if ( !vec ) console.log( "die" ); + + return vec.clone().multiplyScalar( size ).addSelf( pt ); + + } + + var b, bs, t, z, + vert, vlen = vertices.length, + face, flen = faces.length, + cont, clen = contour.length; + + + // Find directions for point movement + + var RAD_TO_DEGREES = 180 / Math.PI; + + + function getBevelVec( pt_i, pt_j, pt_k ) { + + // Algorithm 2 + + return getBevelVec2( pt_i, pt_j, pt_k ); + + } + + function getBevelVec1( pt_i, pt_j, pt_k ) { + + var anglea = Math.atan2( pt_j.y - pt_i.y, pt_j.x - pt_i.x ); + var angleb = Math.atan2( pt_k.y - pt_i.y, pt_k.x - pt_i.x ); + + if ( anglea > angleb ) { + + angleb += Math.PI * 2; + + } + + var anglec = ( anglea + angleb ) / 2; + + + //console.log('angle1', anglea * RAD_TO_DEGREES,'angle2', angleb * RAD_TO_DEGREES, 'anglec', anglec *RAD_TO_DEGREES); + + var x = - Math.cos( anglec ); + var y = - Math.sin( anglec ); + + var vec = new THREE.Vector2( x, y ); //.normalize(); + + return vec; + + } + + function getBevelVec2( pt_i, pt_j, pt_k ) { + + var a = THREE.ExtrudeGeometry.__v1, + b = THREE.ExtrudeGeometry.__v2, + v_hat = THREE.ExtrudeGeometry.__v3, + w_hat = THREE.ExtrudeGeometry.__v4, + p = THREE.ExtrudeGeometry.__v5, + q = THREE.ExtrudeGeometry.__v6, + v, w, + v_dot_w_hat, q_sub_p_dot_w_hat, + s, intersection; + + // good reading for line-line intersection + // http://sputsoft.com/blog/2010/03/line-line-intersection.html + + // define a as vector j->i + // define b as vectot k->i + + a.set( pt_i.x - pt_j.x, pt_i.y - pt_j.y ); + b.set( pt_i.x - pt_k.x, pt_i.y - pt_k.y ); + + // get unit vectors + + v = a.normalize(); + w = b.normalize(); + + // normals from pt i + + v_hat.set( -v.y, v.x ); + w_hat.set( w.y, -w.x ); + + // pts from i + + p.copy( pt_i ).addSelf( v_hat ); + q.copy( pt_i ).addSelf( w_hat ); + + if ( p.equals( q ) ) { + + //console.log("Warning: lines are straight"); + return w_hat.clone(); + + } + + // Points from j, k. helps prevents points cross overover most of the time + + p.copy( pt_j ).addSelf( v_hat ); + q.copy( pt_k ).addSelf( w_hat ); + + v_dot_w_hat = v.dot( w_hat ); + q_sub_p_dot_w_hat = q.subSelf( p ).dot( w_hat ); + + // We should not reach these conditions + + if ( v_dot_w_hat === 0 ) { + + console.log( "Either infinite or no solutions!" ); + + if ( q_sub_p_dot_w_hat === 0 ) { + + console.log( "Its finite solutions." ); + + } else { + + console.log( "Too bad, no solutions." ); + + } + + } + + s = q_sub_p_dot_w_hat / v_dot_w_hat; + + if ( s < 0 ) { + + // in case of emergecy, revert to algorithm 1. + + return getBevelVec1( pt_i, pt_j, pt_k ); + + } + + intersection = v.multiplyScalar( s ).addSelf( p ); + + return intersection.subSelf( pt_i ).clone(); // Don't normalize!, otherwise sharp corners become ugly + + } + + var contourMovements = []; + + for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + // console.log('i,j,k', i, j , k) + + var pt_i = contour[ i ]; + var pt_j = contour[ j ]; + var pt_k = contour[ k ]; + + contourMovements[ i ]= getBevelVec( contour[ i ], contour[ j ], contour[ k ] ); + + } + + var holesMovements = [], oneHoleMovements, verticesMovements = contourMovements.concat(); + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + + oneHoleMovements = []; + + for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) { + + if ( j === il ) j = 0; + if ( k === il ) k = 0; + + // (j)---(i)---(k) + oneHoleMovements[ i ]= getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] ); + + } + + holesMovements.push( oneHoleMovements ); + verticesMovements = verticesMovements.concat( oneHoleMovements ); + + } + + + // Loop bevelSegments, 1 for the front, 1 for the back + + for ( b = 0; b < bevelSegments; b ++ ) { + //for ( b = bevelSegments; b > 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + + //z = bevelThickness * t; + bs = bevelSize * ( Math.sin ( t * Math.PI/2 ) ) ; // curved + //bs = bevelSize * t ; // linear + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + //vert = scalePt( contour[ i ], contourCentroid, bs, false ); + v( vert.x, vert.y, - z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + //vert = scalePt( ahole[ i ], holesCentroids[ h ], bs, true ); + + v( vert.x, vert.y, -z ); + + } + + } + + } + + bs = bevelSize; + + // Back facing vertices + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( !extrudeByPath ) { + + v( vert.x, vert.y, 0 ); + + } else { + + // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x ); + + normal.copy( splineTube.normals[0] ).multiplyScalar(vert.x); + binormal.copy( splineTube.binormals[0] ).multiplyScalar(vert.y); + + position2.copy( extrudePts[0] ).addSelf(normal).addSelf(binormal); + + v( position2.x, position2.y, position2.z ); + + } + + } + + // Add stepped vertices... + // Including front facing vertices + + var s; + + for ( s = 1; s <= steps; s ++ ) { + + for ( i = 0; i < vlen; i ++ ) { + + vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ]; + + if ( !extrudeByPath ) { + + v( vert.x, vert.y, amount / steps * s ); + + } else { + + // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x ); + + normal.copy( splineTube.normals[s] ).multiplyScalar( vert.x ); + binormal.copy( splineTube.binormals[s] ).multiplyScalar( vert.y ); + + position2.copy( extrudePts[s] ).addSelf( normal ).addSelf( binormal ); + + v( position2.x, position2.y, position2.z ); + + } + + } + + } + + + // Add bevel segments planes + + //for ( b = 1; b <= bevelSegments; b ++ ) { + for ( b = bevelSegments - 1; b >= 0; b -- ) { + + t = b / bevelSegments; + z = bevelThickness * ( 1 - t ); + //bs = bevelSize * ( 1-Math.sin ( ( 1 - t ) * Math.PI/2 ) ); + bs = bevelSize * Math.sin ( t * Math.PI/2 ) ; + + // contract shape + + for ( i = 0, il = contour.length; i < il; i ++ ) { + + vert = scalePt2( contour[ i ], contourMovements[ i ], bs ); + v( vert.x, vert.y, amount + z ); + + } + + // expand holes + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + oneHoleMovements = holesMovements[ h ]; + + for ( i = 0, il = ahole.length; i < il; i ++ ) { + + vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs ); + + if ( !extrudeByPath ) { + + v( vert.x, vert.y, amount + z ); + + } else { + + v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z ); + + } + + } + + } + + } + + /* Faces */ + + // Top and bottom faces + + buildLidFaces(); + + // Sides faces + + buildSideFaces(); + + + ///// Internal functions + + function buildLidFaces() { + + if ( bevelEnabled ) { + + var layer = 0 ; // steps + 1 + var offset = vlen * layer; + + // Bottom faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 2 ]+ offset, face[ 1 ]+ offset, face[ 0 ] + offset, true ); + + } + + layer = steps + bevelSegments * 2; + offset = vlen * layer; + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset, false ); + + } + + } else { + + // Bottom faces + + for ( i = 0; i < flen; i++ ) { + + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ], true ); + + } + + // Top faces + + for ( i = 0; i < flen; i ++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps, false ); + + } + } + + } + + // Create faces for the z-sides of the shape + + function buildSideFaces() { + + var layeroffset = 0; + sidewalls( contour, layeroffset ); + layeroffset += contour.length; + + for ( h = 0, hl = holes.length; h < hl; h ++ ) { + + ahole = holes[ h ]; + sidewalls( ahole, layeroffset ); + + //, true + layeroffset += ahole.length; + + } + + } + + function sidewalls( contour, layeroffset ) { + + var j, k; + i = contour.length; + + while ( --i >= 0 ) { + + j = i; + k = i - 1; + if ( k < 0 ) k = contour.length - 1; + + //console.log('b', i,j, i-1, k,vertices.length); + + var s = 0, sl = steps + bevelSegments * 2; + + for ( s = 0; s < sl; s ++ ) { + + var slen1 = vlen * s; + var slen2 = vlen * ( s + 1 ); + + var a = layeroffset + j + slen1, + b = layeroffset + k + slen1, + c = layeroffset + k + slen2, + d = layeroffset + j + slen2; + + f4( a, b, c, d, contour, s, sl, j, k ); + + } + } + + } + + + function v( x, y, z ) { + + scope.vertices.push( new THREE.Vector3( x, y, z ) ); + + } + + function f3( a, b, c, isBottom ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + + // normal, color, material + scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + + var uvs = isBottom ? uvgen.generateBottomUV( scope, shape, options, a, b, c ) : uvgen.generateTopUV( scope, shape, options, a, b, c ); + + scope.faceVertexUvs[ 0 ].push( uvs ); + + } + + function f4( a, b, c, d, wallContour, stepIndex, stepsLength, contourIndex1, contourIndex2 ) { + + a += shapesOffset; + b += shapesOffset; + c += shapesOffset; + d += shapesOffset; + + scope.faces.push( new THREE.Face4( a, b, c, d, null, null, extrudeMaterial ) ); + + var uvs = uvgen.generateSideWallUV( scope, shape, wallContour, options, a, b, c, d, + stepIndex, stepsLength, contourIndex1, contourIndex2 ); + scope.faceVertexUvs[ 0 ].push( uvs ); + + } + +}; + +THREE.ExtrudeGeometry.WorldUVGenerator = { + + generateTopUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { + var ax = geometry.vertices[ indexA ].x, + ay = geometry.vertices[ indexA ].y, + + bx = geometry.vertices[ indexB ].x, + by = geometry.vertices[ indexB ].y, + + cx = geometry.vertices[ indexC ].x, + cy = geometry.vertices[ indexC ].y; + + return [ + new THREE.UV( ax, ay ), + new THREE.UV( bx, by ), + new THREE.UV( cx, cy ) + ]; + + }, + + generateBottomUV: function( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ) { + + return this.generateTopUV( geometry, extrudedShape, extrudeOptions, indexA, indexB, indexC ); + + }, + + generateSideWallUV: function( geometry, extrudedShape, wallContour, extrudeOptions, + indexA, indexB, indexC, indexD, stepIndex, stepsLength, + contourIndex1, contourIndex2 ) { + + var ax = geometry.vertices[ indexA ].x, + ay = geometry.vertices[ indexA ].y, + az = geometry.vertices[ indexA ].z, + + bx = geometry.vertices[ indexB ].x, + by = geometry.vertices[ indexB ].y, + bz = geometry.vertices[ indexB ].z, + + cx = geometry.vertices[ indexC ].x, + cy = geometry.vertices[ indexC ].y, + cz = geometry.vertices[ indexC ].z, + + dx = geometry.vertices[ indexD ].x, + dy = geometry.vertices[ indexD ].y, + dz = geometry.vertices[ indexD ].z; + + if ( Math.abs( ay - by ) < 0.01 ) { + return [ + new THREE.UV( ax, 1 - az ), + new THREE.UV( bx, 1 - bz ), + new THREE.UV( cx, 1 - cz ), + new THREE.UV( dx, 1 - dz ) + ]; + } else { + return [ + new THREE.UV( ay, 1 - az ), + new THREE.UV( by, 1 - bz ), + new THREE.UV( cy, 1 - cz ), + new THREE.UV( dy, 1 - dz ) + ]; + } + } +}; + +THREE.ExtrudeGeometry.__v1 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v2 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v3 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v4 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v5 = new THREE.Vector2(); +THREE.ExtrudeGeometry.__v6 = new THREE.Vector2(); +/** + * @author jonobr1 / http://jonobr1.com + * + * Creates a one-sided polygonal geometry from a path shape. Similar to + * ExtrudeGeometry. + * + * parameters = { + * + * curveSegments: , // number of points on the curves. NOT USED AT THE MOMENT. + * + * material: // material index for front and back faces + * uvGenerator: // object that provides UV generator functions + * + * } + **/ + +THREE.ShapeGeometry = function ( shapes, options ) { + + THREE.Geometry.call( this ); + + if ( shapes instanceof Array === false ) shapes = [ shapes ]; + + this.shapebb = shapes[ shapes.length - 1 ].getBoundingBox(); + + this.addShapeList( shapes, options ); + + this.computeCentroids(); + this.computeFaceNormals(); + +}; + +THREE.ShapeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + +/** + * Add an array of shapes to THREE.ShapeGeometry. + */ +THREE.ShapeGeometry.prototype.addShapeList = function ( shapes, options ) { + + for ( var i = 0, l = shapes.length; i < l; i++ ) { + + this.addShape( shapes[ i ], options ); + + } + + return this; + +}; + +/** + * Adds a shape to THREE.ShapeGeometry, based on THREE.ExtrudeGeometry. + */ +THREE.ShapeGeometry.prototype.addShape = function ( shape, options ) { + + if ( options === undefined ) options = {}; + + var material = options.material; + var uvgen = options.UVGenerator === undefined ? THREE.ExtrudeGeometry.WorldUVGenerator : options.UVGenerator; + + var shapebb = this.shapebb; + + // + + var i, l, hole, s; + + var shapesOffset = this.vertices.length; + var shapePoints = shape.extractPoints(); + + var vertices = shapePoints.shape; + var holes = shapePoints.holes; + + var reverse = !THREE.Shape.Utils.isClockWise( vertices ); + + if ( reverse ) { + + vertices = vertices.reverse(); + + // Maybe we should also check if holes are in the opposite direction, just to be safe... + + for ( i = 0, l = holes.length; i < l; i++ ) { + + hole = holes[ i ]; + + if ( THREE.Shape.Utils.isClockWise( hole ) ) { + + holes[ i ] = hole.reverse(); + + } + + } + + reverse = false; + + } + + var faces = THREE.Shape.Utils.triangulateShape( vertices, holes ); + + // Vertices + + var contour = vertices; + + for ( i = 0, l = holes.length; i < l; i++ ) { + + hole = holes[ i ]; + vertices = vertices.concat( hole ); + + } + + // + + var vert, vlen = vertices.length; + var face, flen = faces.length; + var cont, clen = contour.length; + + for ( i = 0; i < vlen; i++ ) { + + vert = vertices[ i ]; + + this.vertices.push( new THREE.Vector3( vert.x, vert.y, 0 ) ); + + } + + for ( i = 0; i < flen; i++ ) { + + face = faces[ i ]; + + var a = face[ 0 ] + shapesOffset; + var b = face[ 1 ] + shapesOffset; + var c = face[ 2 ] + shapesOffset; + + this.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + this.faceVertexUvs[ 0 ].push( uvgen.generateBottomUV( this, shape, options, a, b, c ) ); + + } + +}; +/** + * @author astrodud / http://astrodud.isgreat.org/ + * @author zz85 / https://github.com/zz85 + */ + +THREE.LatheGeometry = function ( points, steps, angle ) { + + THREE.Geometry.call( this ); + + var _steps = steps || 12; + var _angle = angle || 2 * Math.PI; + + var _newV = []; + var _matrix = new THREE.Matrix4().makeRotationZ( _angle / _steps ); + + for ( var j = 0; j < points.length; j ++ ) { + + _newV[ j ] = points[ j ].clone(); + this.vertices.push( _newV[ j ] ); + + } + + var i, il = _steps + 1; + + for ( i = 0; i < il; i ++ ) { + + for ( var j = 0; j < _newV.length; j ++ ) { + + _newV[ j ] = _matrix.multiplyVector3( _newV[ j ].clone() ); + this.vertices.push( _newV[ j ] ); + + } + + } + + for ( i = 0; i < _steps; i ++ ) { + + for ( var k = 0, kl = points.length; k < kl - 1; k ++ ) { + + var a = i * kl + k; + var b = ( ( i + 1 ) % il ) * kl + k; + var c = ( ( i + 1 ) % il ) * kl + ( k + 1 ) % kl; + var d = i * kl + ( k + 1 ) % kl; + + this.faces.push( new THREE.Face4( a, b, c, d ) ); + + this.faceVertexUvs[ 0 ].push( [ + + new THREE.UV( 1 - i / _steps, k / kl ), + new THREE.UV( 1 - ( i + 1 ) / _steps, k / kl ), + new THREE.UV( 1 - ( i + 1 ) / _steps, ( k + 1 ) / kl ), + new THREE.UV( 1 - i / _steps, ( k + 1 ) / kl ) + + ] ); + + } + + } + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.LatheGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author mrdoob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneGeometry = function ( width, height, widthSegments, heightSegments ) { + + THREE.Geometry.call( this ); + + var ix, iz, + width_half = width / 2, + height_half = height / 2, + gridX = widthSegments || 1, + gridZ = heightSegments || 1, + gridX1 = gridX + 1, + gridZ1 = gridZ + 1, + segment_width = width / gridX, + segment_height = height / gridZ, + normal = new THREE.Vector3( 0, 0, 1 ); + + for ( iz = 0; iz < gridZ1; iz ++ ) { + + for ( ix = 0; ix < gridX1; ix ++ ) { + + var x = ix * segment_width - width_half; + var y = iz * segment_height - height_half; + + this.vertices.push( new THREE.Vector3( x, - y, 0 ) ); + + } + + } + + for ( iz = 0; iz < gridZ; iz ++ ) { + + for ( ix = 0; ix < gridX; ix ++ ) { + + var a = ix + gridX1 * iz; + var b = ix + gridX1 * ( iz + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iz + 1 ); + var d = ( ix + 1 ) + gridX1 * iz; + + var face = new THREE.Face4( a, b, c, d ); + face.normal.copy( normal ); + face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone(), normal.clone() ); + + this.faces.push( face ); + this.faceVertexUvs[ 0 ].push( [ + new THREE.UV( ix / gridX, 1 - iz / gridZ ), + new THREE.UV( ix / gridX, 1 - ( iz + 1 ) / gridZ ), + new THREE.UV( ( ix + 1 ) / gridX, 1 - ( iz + 1 ) / gridZ ), + new THREE.UV( ( ix + 1 ) / gridX, 1 - iz / gridZ ) + ] ); + + } + + } + + this.computeCentroids(); + +}; + +THREE.PlaneGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SphereGeometry = function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) { + + THREE.Geometry.call( this ); + + radius = radius || 50; + + phiStart = phiStart !== undefined ? phiStart : 0; + phiLength = phiLength !== undefined ? phiLength : Math.PI * 2; + + thetaStart = thetaStart !== undefined ? thetaStart : 0; + thetaLength = thetaLength !== undefined ? thetaLength : Math.PI; + + var segmentsX = Math.max( 3, Math.floor( widthSegments ) || 8 ); + var segmentsY = Math.max( 2, Math.floor( heightSegments ) || 6 ); + + var x, y, vertices = [], uvs = []; + + for ( y = 0; y <= segmentsY; y ++ ) { + + var verticesRow = []; + var uvsRow = []; + + for ( x = 0; x <= segmentsX; x ++ ) { + + var u = x / segmentsX; + var v = y / segmentsY; + + var vertex = new THREE.Vector3(); + vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + vertex.y = radius * Math.cos( thetaStart + v * thetaLength ); + vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength ); + + this.vertices.push( vertex ); + + verticesRow.push( this.vertices.length - 1 ); + uvsRow.push( new THREE.UV( u, 1 - v ) ); + + } + + vertices.push( verticesRow ); + uvs.push( uvsRow ); + + } + + for ( y = 0; y < segmentsY; y ++ ) { + + for ( x = 0; x < segmentsX; x ++ ) { + + var v1 = vertices[ y ][ x + 1 ]; + var v2 = vertices[ y ][ x ]; + var v3 = vertices[ y + 1 ][ x ]; + var v4 = vertices[ y + 1 ][ x + 1 ]; + + var n1 = this.vertices[ v1 ].clone().normalize(); + var n2 = this.vertices[ v2 ].clone().normalize(); + var n3 = this.vertices[ v3 ].clone().normalize(); + var n4 = this.vertices[ v4 ].clone().normalize(); + + var uv1 = uvs[ y ][ x + 1 ].clone(); + var uv2 = uvs[ y ][ x ].clone(); + var uv3 = uvs[ y + 1 ][ x ].clone(); + var uv4 = uvs[ y + 1 ][ x + 1 ].clone(); + + if ( Math.abs( this.vertices[ v1 ].y ) == radius ) { + + this.faces.push( new THREE.Face3( v1, v3, v4, [ n1, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv3, uv4 ] ); + + } else if ( Math.abs( this.vertices[ v3 ].y ) == radius ) { + + this.faces.push( new THREE.Face3( v1, v2, v3, [ n1, n2, n3 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3 ] ); + + } else { + + this.faces.push( new THREE.Face4( v1, v2, v3, v4, [ n1, n2, n3, n4 ] ) ); + this.faceVertexUvs[ 0 ].push( [ uv1, uv2, uv3, uv4 ] ); + + } + + } + + } + + this.computeCentroids(); + this.computeFaceNormals(); + + this.boundingSphere = { radius: radius }; + +}; + +THREE.SphereGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For creating 3D text geometry in three.js + * + * Text = 3D Text + * + * parameters = { + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves + * + * font: , // font name + * weight: , // font weight (normal, bold) + * style: , // font style (normal, italics) + * + * bevelEnabled: , // turn on bevel + * bevelThickness: , // how deep into text bevel goes + * bevelSize: , // how far from text outline is bevel + * } + * + */ + +/* Usage Examples + + // TextGeometry wrapper + + var text3d = new TextGeometry( text, options ); + + // Complete manner + + var textShapes = THREE.FontUtils.generateShapes( text, options ); + var text3d = new ExtrudeGeometry( textShapes, options ); + +*/ + + +THREE.TextGeometry = function ( text, parameters ) { + + var textShapes = THREE.FontUtils.generateShapes( text, parameters ); + + // translate parameters to ExtrudeGeometry API + + parameters.amount = parameters.height !== undefined ? parameters.height : 50; + + // defaults + + if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10; + if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8; + if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false; + + THREE.ExtrudeGeometry.call( this, textShapes, parameters ); + +}; + +THREE.TextGeometry.prototype = Object.create( THREE.ExtrudeGeometry.prototype ); +/** + * @author oosmoxiecode + * @author mrdoob / http://mrdoob.com/ + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 + */ + +THREE.TorusGeometry = function ( radius, tube, radialSegments, tubularSegments, arc ) { + + THREE.Geometry.call( this ); + + var scope = this; + + this.radius = radius || 100; + this.tube = tube || 40; + this.radialSegments = radialSegments || 8; + this.tubularSegments = tubularSegments || 6; + this.arc = arc || Math.PI * 2; + + var center = new THREE.Vector3(), uvs = [], normals = []; + + for ( var j = 0; j <= this.radialSegments; j ++ ) { + + for ( var i = 0; i <= this.tubularSegments; i ++ ) { + + var u = i / this.tubularSegments * this.arc; + var v = j / this.radialSegments * Math.PI * 2; + + center.x = this.radius * Math.cos( u ); + center.y = this.radius * Math.sin( u ); + + var vertex = new THREE.Vector3(); + vertex.x = ( this.radius + this.tube * Math.cos( v ) ) * Math.cos( u ); + vertex.y = ( this.radius + this.tube * Math.cos( v ) ) * Math.sin( u ); + vertex.z = this.tube * Math.sin( v ); + + this.vertices.push( vertex ); + + uvs.push( new THREE.UV( i / this.tubularSegments, j / this.radialSegments ) ); + normals.push( vertex.clone().subSelf( center ).normalize() ); + + } + } + + + for ( var j = 1; j <= this.radialSegments; j ++ ) { + + for ( var i = 1; i <= this.tubularSegments; i ++ ) { + + var a = ( this.tubularSegments + 1 ) * j + i - 1; + var b = ( this.tubularSegments + 1 ) * ( j - 1 ) + i - 1; + var c = ( this.tubularSegments + 1 ) * ( j - 1 ) + i; + var d = ( this.tubularSegments + 1 ) * j + i; + + var face = new THREE.Face4( a, b, c, d, [ normals[ a ], normals[ b ], normals[ c ], normals[ d ] ] ); + face.normal.addSelf( normals[ a ] ); + face.normal.addSelf( normals[ b ] ); + face.normal.addSelf( normals[ c ] ); + face.normal.addSelf( normals[ d ] ); + face.normal.normalize(); + + this.faces.push( face ); + + this.faceVertexUvs[ 0 ].push( [ uvs[ a ].clone(), uvs[ b ].clone(), uvs[ c ].clone(), uvs[ d ].clone() ] ); + } + + } + + this.computeCentroids(); + +}; + +THREE.TorusGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 + */ + +THREE.TorusKnotGeometry = function ( radius, tube, radialSegments, tubularSegments, p, q, heightScale ) { + + THREE.Geometry.call( this ); + + var scope = this; + + this.radius = radius || 200; + this.tube = tube || 40; + this.radialSegments = radialSegments || 64; + this.tubularSegments = tubularSegments || 8; + this.p = p || 2; + this.q = q || 3; + this.heightScale = heightScale || 1; + this.grid = new Array(this.radialSegments); + + var tang = new THREE.Vector3(); + var n = new THREE.Vector3(); + var bitan = new THREE.Vector3(); + + for ( var i = 0; i < this.radialSegments; ++ i ) { + + this.grid[ i ] = new Array( this.tubularSegments ); + + for ( var j = 0; j < this.tubularSegments; ++ j ) { + + var u = i / this.radialSegments * 2 * this.p * Math.PI; + var v = j / this.tubularSegments * 2 * Math.PI; + var p1 = getPos( u, v, this.q, this.p, this.radius, this.heightScale ); + var p2 = getPos( u + 0.01, v, this.q, this.p, this.radius, this.heightScale ); + var cx, cy; + + tang.sub( p2, p1 ); + n.add( p2, p1 ); + + bitan.cross( tang, n ); + n.cross( bitan, tang ); + bitan.normalize(); + n.normalize(); + + cx = - this.tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = this.tube * Math.sin( v ); + + p1.x += cx * n.x + cy * bitan.x; + p1.y += cx * n.y + cy * bitan.y; + p1.z += cx * n.z + cy * bitan.z; + + this.grid[ i ][ j ] = vert( p1.x, p1.y, p1.z ); + + } + + } + + for ( var i = 0; i < this.radialSegments; ++ i ) { + + for ( var j = 0; j < this.tubularSegments; ++ j ) { + + var ip = ( i + 1 ) % this.radialSegments; + var jp = ( j + 1 ) % this.tubularSegments; + + var a = this.grid[ i ][ j ]; + var b = this.grid[ ip ][ j ]; + var c = this.grid[ ip ][ jp ]; + var d = this.grid[ i ][ jp ]; + + var uva = new THREE.UV( i / this.radialSegments, j / this.tubularSegments ); + var uvb = new THREE.UV( ( i + 1 ) / this.radialSegments, j / this.tubularSegments ); + var uvc = new THREE.UV( ( i + 1 ) / this.radialSegments, ( j + 1 ) / this.tubularSegments ); + var uvd = new THREE.UV( i / this.radialSegments, ( j + 1 ) / this.tubularSegments ); + + this.faces.push( new THREE.Face4( a, b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva,uvb,uvc, uvd ] ); + + } + } + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + + function vert( x, y, z ) { + + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + + } + + function getPos( u, v, in_q, in_p, radius, heightScale ) { + + var cu = Math.cos( u ); + var cv = Math.cos( v ); + var su = Math.sin( u ); + var quOverP = in_q / in_p * u; + var cs = Math.cos( quOverP ); + + var tx = radius * ( 2 + cs ) * 0.5 * cu; + var ty = radius * ( 2 + cs ) * su * 0.5; + var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + + return new THREE.Vector3( tx, ty, tz ); + + } + +}; + +THREE.TorusKnotGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author WestLangley / https://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * @author miningold / https://github.com/miningold + * + * Modified from the TorusKnotGeometry by @oosmoxiecode + * + * Creates a tube which extrudes along a 3d spline + * + * Uses parallel transport frames as described in + * http://www.cs.indiana.edu/pub/techreports/TR425.pdf + */ + +THREE.TubeGeometry = function( path, segments, radius, radiusSegments, closed, debug ) { + + THREE.Geometry.call( this ); + + this.path = path; + this.segments = segments || 64; + this.radius = radius || 1; + this.radiusSegments = radiusSegments || 8; + this.closed = closed || false; + + if ( debug ) this.debug = new THREE.Object3D(); + + this.grid = []; + + var scope = this, + + tangent, + normal, + binormal, + + numpoints = this.segments + 1, + + x, y, z, + tx, ty, tz, + u, v, + + cx, cy, + pos, pos2 = new THREE.Vector3(), + i, j, + ip, jp, + a, b, c, d, + uva, uvb, uvc, uvd; + + var frames = new THREE.TubeGeometry.FrenetFrames(path, segments, closed), + tangents = frames.tangents, + normals = frames.normals, + binormals = frames.binormals; + + // proxy internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + function vert( x, y, z ) { + + return scope.vertices.push( new THREE.Vector3( x, y, z ) ) - 1; + + } + + + // consruct the grid + + for ( i = 0; i < numpoints; i++ ) { + + this.grid[ i ] = []; + + u = i / ( numpoints - 1 ); + + pos = path.getPointAt( u ); + + tangent = tangents[ i ]; + normal = normals[ i ]; + binormal = binormals[ i ]; + + if ( this.debug ) { + + this.debug.add( new THREE.ArrowHelper(tangent, pos, radius, 0x0000ff ) ); + this.debug.add( new THREE.ArrowHelper(normal, pos, radius, 0xff0000 ) ); + this.debug.add( new THREE.ArrowHelper(binormal, pos, radius, 0x00ff00 ) ); + + } + + for ( j = 0; j < this.radiusSegments; j++ ) { + + v = j / this.radiusSegments * 2 * Math.PI; + + cx = -this.radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = this.radius * Math.sin( v ); + + pos2.copy( pos ); + pos2.x += cx * normal.x + cy * binormal.x; + pos2.y += cx * normal.y + cy * binormal.y; + pos2.z += cx * normal.z + cy * binormal.z; + + this.grid[ i ][ j ] = vert( pos2.x, pos2.y, pos2.z ); + + } + } + + + // construct the mesh + + for ( i = 0; i < this.segments; i++ ) { + + for ( j = 0; j < this.radiusSegments; j++ ) { + + ip = ( closed ) ? (i + 1) % this.segments : i + 1; + jp = (j + 1) % this.radiusSegments; + + a = this.grid[ i ][ j ]; // *** NOT NECESSARILY PLANAR ! *** + b = this.grid[ ip ][ j ]; + c = this.grid[ ip ][ jp ]; + d = this.grid[ i ][ jp ]; + + uva = new THREE.UV( i / this.segments, j / this.radiusSegments ); + uvb = new THREE.UV( ( i + 1 ) / this.segments, j / this.radiusSegments ); + uvc = new THREE.UV( ( i + 1 ) / this.segments, ( j + 1 ) / this.radiusSegments ); + uvd = new THREE.UV( i / this.segments, ( j + 1 ) / this.radiusSegments ); + + this.faces.push( new THREE.Face4( a, b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva, uvb, uvc, uvd ] ); + + } + } + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.TubeGeometry.prototype = Object.create( THREE.Geometry.prototype ); + + +// For computing of Frenet frames, exposing the tangents, normals and binormals the spline +THREE.TubeGeometry.FrenetFrames = function(path, segments, closed) { + + var + tangent = new THREE.Vector3(), + normal = new THREE.Vector3(), + binormal = new THREE.Vector3(), + + tangents = [], + normals = [], + binormals = [], + + vec = new THREE.Vector3(), + mat = new THREE.Matrix4(), + + numpoints = segments + 1, + theta, + epsilon = 0.0001, + smallest, + + tx, ty, tz, + i, u, v; + + + // expose internals + this.tangents = tangents; + this.normals = normals; + this.binormals = binormals; + + // compute the tangent vectors for each segment on the path + + for ( i = 0; i < numpoints; i++ ) { + + u = i / ( numpoints - 1 ); + + tangents[ i ] = path.getTangentAt( u ); + tangents[ i ].normalize(); + + } + + initialNormal3(); + + function initialNormal1(lastBinormal) { + // fixed start binormal. Has dangers of 0 vectors + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + if (lastBinormal===undefined) lastBinormal = new THREE.Vector3( 0, 0, 1 ); + normals[ 0 ].cross( lastBinormal, tangents[ 0 ] ).normalize(); + binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] ).normalize(); + } + + function initialNormal2() { + + // This uses the Frenet-Serret formula for deriving binormal + var t2 = path.getTangentAt( epsilon ); + + normals[ 0 ] = new THREE.Vector3().sub( t2, tangents[ 0 ] ).normalize(); + binormals[ 0 ] = new THREE.Vector3().cross( tangents[ 0 ], normals[ 0 ] ); + + normals[ 0 ].cross( binormals[ 0 ], tangents[ 0 ] ).normalize(); // last binormal x tangent + binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] ).normalize(); + + } + + function initialNormal3() { + // select an initial normal vector perpenicular to the first tangent vector, + // and in the direction of the smallest tangent xyz component + + normals[ 0 ] = new THREE.Vector3(); + binormals[ 0 ] = new THREE.Vector3(); + smallest = Number.MAX_VALUE; + tx = Math.abs( tangents[ 0 ].x ); + ty = Math.abs( tangents[ 0 ].y ); + tz = Math.abs( tangents[ 0 ].z ); + + if ( tx <= smallest ) { + smallest = tx; + normal.set( 1, 0, 0 ); + } + + if ( ty <= smallest ) { + smallest = ty; + normal.set( 0, 1, 0 ); + } + + if ( tz <= smallest ) { + normal.set( 0, 0, 1 ); + } + + vec.cross( tangents[ 0 ], normal ).normalize(); + + normals[ 0 ].cross( tangents[ 0 ], vec ); + binormals[ 0 ].cross( tangents[ 0 ], normals[ 0 ] ); + } + + + // compute the slowly-varying normal and binormal vectors for each segment on the path + + for ( i = 1; i < numpoints; i++ ) { + + normals[ i ] = normals[ i-1 ].clone(); + + binormals[ i ] = binormals[ i-1 ].clone(); + + vec.cross( tangents[ i-1 ], tangents[ i ] ); + + if ( vec.length() > epsilon ) { + + vec.normalize(); + + theta = Math.acos( tangents[ i-1 ].dot( tangents[ i ] ) ); + + mat.makeRotationAxis( vec, theta ).multiplyVector3( normals[ i ] ); + + } + + binormals[ i ].cross( tangents[ i ], normals[ i ] ); + + } + + + // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same + + if ( closed ) { + + theta = Math.acos( normals[ 0 ].dot( normals[ numpoints-1 ] ) ); + theta /= ( numpoints - 1 ); + + if ( tangents[ 0 ].dot( vec.cross( normals[ 0 ], normals[ numpoints-1 ] ) ) > 0 ) { + + theta = -theta; + + } + + for ( i = 1; i < numpoints; i++ ) { + + // twist a little... + mat.makeRotationAxis( tangents[ i ], theta * i ).multiplyVector3( normals[ i ] ); + binormals[ i ].cross( tangents[ i ], normals[ i ] ); + + } + + } +}; +/** + * @author clockworkgeek / https://github.com/clockworkgeek + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.PolyhedronGeometry = function ( vertices, faces, radius, detail ) { + + THREE.Geometry.call( this ); + + radius = radius || 1; + detail = detail || 0; + + var that = this; + + for ( var i = 0, l = vertices.length; i < l; i ++ ) { + + prepare( new THREE.Vector3( vertices[ i ][ 0 ], vertices[ i ][ 1 ], vertices[ i ][ 2 ] ) ); + + } + + var midpoints = [], p = this.vertices; + + for ( var i = 0, l = faces.length; i < l; i ++ ) { + + make( p[ faces[ i ][ 0 ] ], p[ faces[ i ][ 1 ] ], p[ faces[ i ][ 2 ] ], detail ); + + } + + this.mergeVertices(); + + // Apply radius + + for ( var i = 0, l = this.vertices.length; i < l; i ++ ) { + + this.vertices[ i ].multiplyScalar( radius ); + + } + + + // Project vector onto sphere's surface + + function prepare( vector ) { + + var vertex = vector.normalize().clone(); + vertex.index = that.vertices.push( vertex ) - 1; + + // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle. + + var u = azimuth( vector ) / 2 / Math.PI + 0.5; + var v = inclination( vector ) / Math.PI + 0.5; + vertex.uv = new THREE.UV( u, 1 - v ); + + return vertex; + + } + + + // Approximate a curved face with recursively sub-divided triangles. + + function make( v1, v2, v3, detail ) { + + if ( detail < 1 ) { + + var face = new THREE.Face3( v1.index, v2.index, v3.index, [ v1.clone(), v2.clone(), v3.clone() ] ); + face.centroid.addSelf( v1 ).addSelf( v2 ).addSelf( v3 ).divideScalar( 3 ); + face.normal = face.centroid.clone().normalize(); + that.faces.push( face ); + + var azi = azimuth( face.centroid ); + that.faceVertexUvs[ 0 ].push( [ + correctUV( v1.uv, v1, azi ), + correctUV( v2.uv, v2, azi ), + correctUV( v3.uv, v3, azi ) + ] ); + + } else { + + detail -= 1; + + // split triangle into 4 smaller triangles + + make( v1, midpoint( v1, v2 ), midpoint( v1, v3 ), detail ); // top quadrant + make( midpoint( v1, v2 ), v2, midpoint( v2, v3 ), detail ); // left quadrant + make( midpoint( v1, v3 ), midpoint( v2, v3 ), v3, detail ); // right quadrant + make( midpoint( v1, v2 ), midpoint( v2, v3 ), midpoint( v1, v3 ), detail ); // center quadrant + + } + + } + + function midpoint( v1, v2 ) { + + if ( !midpoints[ v1.index ] ) midpoints[ v1.index ] = []; + if ( !midpoints[ v2.index ] ) midpoints[ v2.index ] = []; + + var mid = midpoints[ v1.index ][ v2.index ]; + + if ( mid === undefined ) { + + // generate mean point and project to surface with prepare() + + midpoints[ v1.index ][ v2.index ] = midpoints[ v2.index ][ v1.index ] = mid = prepare( + new THREE.Vector3().add( v1, v2 ).divideScalar( 2 ) + ); + } + + return mid; + + } + + + // Angle around the Y axis, counter-clockwise when looking from above. + + function azimuth( vector ) { + + return Math.atan2( vector.z, -vector.x ); + + } + + + // Angle above the XZ plane. + + function inclination( vector ) { + + return Math.atan2( -vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) ); + + } + + + // Texture fixing helper. Spheres have some odd behaviours. + + function correctUV( uv, vector, azimuth ) { + + if ( ( azimuth < 0 ) && ( uv.u === 1 ) ) uv = new THREE.UV( uv.u - 1, uv.v ); + if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) uv = new THREE.UV( azimuth / 2 / Math.PI + 0.5, uv.v ); + return uv; + + } + + this.computeCentroids(); + + this.boundingSphere = { radius: radius }; + +}; + +THREE.PolyhedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.IcosahedronGeometry = function ( radius, detail ) { + + var t = ( 1 + Math.sqrt( 5 ) ) / 2; + + var vertices = [ + [ -1, t, 0 ], [ 1, t, 0 ], [ -1, -t, 0 ], [ 1, -t, 0 ], + [ 0, -1, t ], [ 0, 1, t ], [ 0, -1, -t ], [ 0, 1, -t ], + [ t, 0, -1 ], [ t, 0, 1 ], [ -t, 0, -1 ], [ -t, 0, 1 ] + ]; + + var faces = [ + [ 0, 11, 5 ], [ 0, 5, 1 ], [ 0, 1, 7 ], [ 0, 7, 10 ], [ 0, 10, 11 ], + [ 1, 5, 9 ], [ 5, 11, 4 ], [ 11, 10, 2 ], [ 10, 7, 6 ], [ 7, 1, 8 ], + [ 3, 9, 4 ], [ 3, 4, 2 ], [ 3, 2, 6 ], [ 3, 6, 8 ], [ 3, 8, 9 ], + [ 4, 9, 5 ], [ 2, 4, 11 ], [ 6, 2, 10 ], [ 8, 6, 7 ], [ 9, 8, 1 ] + ]; + + THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); + +}; + +THREE.IcosahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.OctahedronGeometry = function ( radius, detail ) { + + var vertices = [ + [ 1, 0, 0 ], [ -1, 0, 0 ], [ 0, 1, 0 ], [ 0, -1, 0 ], [ 0, 0, 1 ], [ 0, 0, -1 ] + ]; + + var faces = [ + [ 0, 2, 4 ], [ 0, 4, 3 ], [ 0, 3, 5 ], [ 0, 5, 2 ], [ 1, 2, 5 ], [ 1, 5, 3 ], [ 1, 3, 4 ], [ 1, 4, 2 ] + ]; + + THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); +}; + +THREE.OctahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author timothypratley / https://github.com/timothypratley + */ + +THREE.TetrahedronGeometry = function ( radius, detail ) { + + var vertices = [ + [ 1, 1, 1 ], [ -1, -1, 1 ], [ -1, 1, -1 ], [ 1, -1, -1 ] + ]; + + var faces = [ + [ 2, 1, 0 ], [ 0, 3, 2 ], [ 1, 3, 0 ], [ 2, 3, 1 ] + ]; + + THREE.PolyhedronGeometry.call( this, vertices, faces, radius, detail ); + +}; + +THREE.TetrahedronGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author zz85 / https://github.com/zz85 + * Parametric Surfaces Geometry + * based on the brilliant article by @prideout http://prideout.net/blog/?p=44 + * + * new THREE.ParametricGeometry( parametricFunction, uSegments, ySegements, useTris ); + * + */ + +THREE.ParametricGeometry = function ( func, slices, stacks, useTris ) { + + THREE.Geometry.call( this ); + + var verts = this.vertices; + var faces = this.faces; + var uvs = this.faceVertexUvs[ 0 ]; + + useTris = (useTris === undefined) ? false : useTris; + + var i, il, j, p; + var u, v; + + var stackCount = stacks + 1; + var sliceCount = slices + 1; + + for ( i = 0; i <= stacks; i ++ ) { + + v = i / stacks; + + for ( j = 0; j <= slices; j ++ ) { + + u = j / slices; + + p = func( u, v ); + verts.push( p ); + + } + } + + var a, b, c, d; + var uva, uvb, uvc, uvd; + + for ( i = 0; i < stacks; i ++ ) { + + for ( j = 0; j < slices; j ++ ) { + + a = i * sliceCount + j; + b = i * sliceCount + j + 1; + c = (i + 1) * sliceCount + j; + d = (i + 1) * sliceCount + j + 1; + + uva = new THREE.UV( j / slices, i / stacks ); + uvb = new THREE.UV( ( j + 1 ) / slices, i / stacks ); + uvc = new THREE.UV( j / slices, ( i + 1 ) / stacks ); + uvd = new THREE.UV( ( j + 1 ) / slices, ( i + 1 ) / stacks ); + + if ( useTris ) { + + faces.push( new THREE.Face3( a, b, c ) ); + faces.push( new THREE.Face3( b, d, c ) ); + + uvs.push( [ uva, uvb, uvc ] ); + uvs.push( [ uvb, uvd, uvc ] ); + + } else { + + faces.push( new THREE.Face4( a, b, d, c ) ); + uvs.push( [ uva, uvb, uvd, uvc ] ); + + } + + } + + } + + // console.log(this); + + // magic bullet + // var diff = this.mergeVertices(); + // console.log('removed ', diff, ' vertices by merging'); + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ParametricGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author qiao / https://github.com/qiao + * @fileoverview This is a convex hull generator using the incremental method. + * The complexity is O(n^2) where n is the number of vertices. + * O(nlogn) algorithms do exist, but they are much more complicated. + * + * Benchmark: + * + * Platform: CPU: P7350 @2.00GHz Engine: V8 + * + * Num Vertices Time(ms) + * + * 10 1 + * 20 3 + * 30 19 + * 40 48 + * 50 107 + */ + +THREE.ConvexGeometry = function( vertices ) { + + THREE.Geometry.call( this ); + + var faces = [ [ 0, 1, 2 ], [ 0, 2, 1 ] ]; + + for ( var i = 3; i < vertices.length; i++ ) { + + addPoint( i ); + + } + + + function addPoint( vertexId ) { + + var vertex = vertices[ vertexId ].clone(); + + var mag = vertex.length(); + vertex.x += mag * randomOffset(); + vertex.y += mag * randomOffset(); + vertex.z += mag * randomOffset(); + + var hole = []; + + for ( var f = 0; f < faces.length; ) { + + var face = faces[ f ]; + + // for each face, if the vertex can see it, + // then we try to add the face's edges into the hole. + if ( visible( face, vertex ) ) { + + for ( var e = 0; e < 3; e++ ) { + + var edge = [ face[ e ], face[ ( e + 1 ) % 3 ] ]; + var boundary = true; + + // remove duplicated edges. + for ( var h = 0; h < hole.length; h++ ) { + + if ( equalEdge( hole[ h ], edge ) ) { + + hole[ h ] = hole[ hole.length - 1 ]; + hole.pop(); + boundary = false; + break; + + } + + } + + if ( boundary ) { + + hole.push( edge ); + + } + + } + + // remove faces[ f ] + faces[ f ] = faces[ faces.length - 1 ]; + faces.pop(); + + } else { // not visible + + f++; + + } + } + + // construct the new faces formed by the edges of the hole and the vertex + for ( var h = 0; h < hole.length; h++ ) { + + faces.push( [ + hole[ h ][ 0 ], + hole[ h ][ 1 ], + vertexId + ] ); + + } + } + + /** + * Whether the face is visible from the vertex + */ + function visible( face, vertex ) { + + var va = vertices[ face[ 0 ] ]; + var vb = vertices[ face[ 1 ] ]; + var vc = vertices[ face[ 2 ] ]; + + var n = normal( va, vb, vc ); + + // distance from face to origin + var dist = n.dot( va ); + + return n.dot( vertex ) >= dist; + + } + + /** + * Face normal + */ + function normal( va, vb, vc ) { + + var cb = new THREE.Vector3(); + var ab = new THREE.Vector3(); + + cb.sub( vc, vb ); + ab.sub( va, vb ); + cb.crossSelf( ab ); + + if ( !cb.isZero() ) { + + cb.normalize(); + + } + + return cb; + + } + + /** + * Detect whether two edges are equal. + * Note that when constructing the convex hull, two same edges can only + * be of the negative direction. + */ + function equalEdge( ea, eb ) { + + return ea[ 0 ] === eb[ 1 ] && ea[ 1 ] === eb[ 0 ]; + + } + + /** + * Create a random offset between -1e-6 and 1e-6. + */ + function randomOffset() { + + return ( Math.random() - 0.5 ) * 2 * 1e-6; + + } + + + /** + * XXX: Not sure if this is the correct approach. Need someone to review. + */ + function vertexUv( vertex ) { + + var mag = vertex.length(); + return new THREE.UV( vertex.x / mag, vertex.y / mag ); + + } + + // Push vertices into `this.vertices`, skipping those inside the hull + var id = 0; + var newId = new Array( vertices.length ); // map from old vertex id to new id + + for ( var i = 0; i < faces.length; i++ ) { + + var face = faces[ i ]; + + for ( var j = 0; j < 3; j++ ) { + + if ( newId[ face[ j ] ] === undefined ) { + + newId[ face[ j ] ] = id++; + this.vertices.push( vertices[ face[ j ] ] ); + + } + + face[ j ] = newId[ face[ j ] ]; + + } + + } + + // Convert faces into instances of THREE.Face3 + for ( var i = 0; i < faces.length; i++ ) { + + this.faces.push( new THREE.Face3( + faces[ i ][ 0 ], + faces[ i ][ 1 ], + faces[ i ][ 2 ] + ) ); + + } + + // Compute UVs + for ( var i = 0; i < this.faces.length; i++ ) { + + var face = this.faces[ i ]; + + this.faceVertexUvs[ 0 ].push( [ + vertexUv( this.vertices[ face.a ] ), + vertexUv( this.vertices[ face.b ] ), + vertexUv( this.vertices[ face.c ]) + ] ); + + } + + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.ConvexGeometry.prototype = Object.create( THREE.Geometry.prototype ); +/** + * @author sroucheray / http://sroucheray.org/ + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.AxisHelper = function ( size ) { + + var geometry = new THREE.Geometry(); + + geometry.vertices.push( + new THREE.Vector3(), new THREE.Vector3( size || 1, 0, 0 ), + new THREE.Vector3(), new THREE.Vector3( 0, size || 1, 0 ), + new THREE.Vector3(), new THREE.Vector3( 0, 0, size || 1 ) + ); + + geometry.colors.push( + new THREE.Color( 0xff0000 ), new THREE.Color( 0xffaa00 ), + new THREE.Color( 0x00ff00 ), new THREE.Color( 0xaaff00 ), + new THREE.Color( 0x0000ff ), new THREE.Color( 0x00aaff ) + ); + + var material = new THREE.LineBasicMaterial( { vertexColors: THREE.VertexColors } ); + + THREE.Line.call( this, geometry, material, THREE.LinePieces ); + +}; + +THREE.AxisHelper.prototype = Object.create( THREE.Line.prototype ); +/** + * @author WestLangley / http://github.com/WestLangley + * @author zz85 / https://github.com/zz85 + * + * Creates an arrow for visualizing directions + * + * Parameters: + * dir - Vector3 + * origin - Vector3 + * length - Number + * hex - color in hex value + */ + +THREE.ArrowHelper = function ( dir, origin, length, hex ) { + + THREE.Object3D.call( this ); + + if ( hex === undefined ) hex = 0xffff00; + if ( length === undefined ) length = 20; + + var lineGeometry = new THREE.Geometry(); + lineGeometry.vertices.push( new THREE.Vector3( 0, 0, 0 ) ); + lineGeometry.vertices.push( new THREE.Vector3( 0, 1, 0 ) ); + + this.line = new THREE.Line( lineGeometry, new THREE.LineBasicMaterial( { color: hex } ) ); + this.add( this.line ); + + var coneGeometry = new THREE.CylinderGeometry( 0, 0.05, 0.25, 5, 1 ); + + this.cone = new THREE.Mesh( coneGeometry, new THREE.MeshBasicMaterial( { color: hex } ) ); + this.cone.position.set( 0, 1, 0 ); + this.add( this.cone ); + + if ( origin instanceof THREE.Vector3 ) this.position = origin; + + this.setDirection( dir ); + this.setLength( length ); + +}; + +THREE.ArrowHelper.prototype = Object.create( THREE.Object3D.prototype ); + +THREE.ArrowHelper.prototype.setDirection = function ( dir ) { + + var axis = new THREE.Vector3( 0, 1, 0 ).crossSelf( dir ); + + var radians = Math.acos( new THREE.Vector3( 0, 1, 0 ).dot( dir.clone().normalize() ) ); + + this.matrix = new THREE.Matrix4().makeRotationAxis( axis.normalize(), radians ); + + this.rotation.setEulerFromRotationMatrix( this.matrix, this.eulerOrder ); + +}; + +THREE.ArrowHelper.prototype.setLength = function ( length ) { + + this.scale.set( length, length, length ); + +}; + +THREE.ArrowHelper.prototype.setColor = function ( hex ) { + + this.line.material.color.setHex( hex ); + this.cone.material.color.setHex( hex ); + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * + * - shows frustum, line of sight and up of the camera + * - suitable for fast updates + * - based on frustum visualization in lightgl.js shadowmap example + * http://evanw.github.com/lightgl.js/tests/shadowmap.html + */ + +THREE.CameraHelper = function ( camera ) { + + THREE.Line.call( this ); + + var scope = this; + + this.geometry = new THREE.Geometry(); + this.material = new THREE.LineBasicMaterial( { color: 0xffffff, vertexColors: THREE.FaceColors } ); + this.type = THREE.LinePieces; + + this.matrixWorld = camera.matrixWorld; + this.matrixAutoUpdate = false; + + this.pointMap = {}; + + // colors + + var hexFrustum = 0xffaa00; + var hexCone = 0xff0000; + var hexUp = 0x00aaff; + var hexTarget = 0xffffff; + var hexCross = 0x333333; + + // near + + addLine( "n1", "n2", hexFrustum ); + addLine( "n2", "n4", hexFrustum ); + addLine( "n4", "n3", hexFrustum ); + addLine( "n3", "n1", hexFrustum ); + + // far + + addLine( "f1", "f2", hexFrustum ); + addLine( "f2", "f4", hexFrustum ); + addLine( "f4", "f3", hexFrustum ); + addLine( "f3", "f1", hexFrustum ); + + // sides + + addLine( "n1", "f1", hexFrustum ); + addLine( "n2", "f2", hexFrustum ); + addLine( "n3", "f3", hexFrustum ); + addLine( "n4", "f4", hexFrustum ); + + // cone + + addLine( "p", "n1", hexCone ); + addLine( "p", "n2", hexCone ); + addLine( "p", "n3", hexCone ); + addLine( "p", "n4", hexCone ); + + // up + + addLine( "u1", "u2", hexUp ); + addLine( "u2", "u3", hexUp ); + addLine( "u3", "u1", hexUp ); + + // target + + addLine( "c", "t", hexTarget ); + addLine( "p", "c", hexCross ); + + // cross + + addLine( "cn1", "cn2", hexCross ); + addLine( "cn3", "cn4", hexCross ); + + addLine( "cf1", "cf2", hexCross ); + addLine( "cf3", "cf4", hexCross ); + + this.camera = camera; + + function addLine( a, b, hex ) { + + addPoint( a, hex ); + addPoint( b, hex ); + + } + + function addPoint( id, hex ) { + + scope.geometry.vertices.push( new THREE.Vector3() ); + scope.geometry.colors.push( new THREE.Color( hex ) ); + + if ( scope.pointMap[ id ] === undefined ) scope.pointMap[ id ] = []; + + scope.pointMap[ id ].push( scope.geometry.vertices.length - 1 ); + + } + + this.update( camera ); + +}; + +THREE.CameraHelper.prototype = Object.create( THREE.Line.prototype ); + +THREE.CameraHelper.prototype.update = function () { + + var scope = this; + + var w = 1, h = 1; + + // we need just camera projection matrix + // world matrix must be identity + + THREE.CameraHelper.__c.projectionMatrix.copy( this.camera.projectionMatrix ); + + // center / target + + setPoint( "c", 0, 0, -1 ); + setPoint( "t", 0, 0, 1 ); + + // near + + setPoint( "n1", -w, -h, -1 ); + setPoint( "n2", w, -h, -1 ); + setPoint( "n3", -w, h, -1 ); + setPoint( "n4", w, h, -1 ); + + // far + + setPoint( "f1", -w, -h, 1 ); + setPoint( "f2", w, -h, 1 ); + setPoint( "f3", -w, h, 1 ); + setPoint( "f4", w, h, 1 ); + + // up + + setPoint( "u1", w * 0.7, h * 1.1, -1 ); + setPoint( "u2", -w * 0.7, h * 1.1, -1 ); + setPoint( "u3", 0, h * 2, -1 ); + + // cross + + setPoint( "cf1", -w, 0, 1 ); + setPoint( "cf2", w, 0, 1 ); + setPoint( "cf3", 0, -h, 1 ); + setPoint( "cf4", 0, h, 1 ); + + setPoint( "cn1", -w, 0, -1 ); + setPoint( "cn2", w, 0, -1 ); + setPoint( "cn3", 0, -h, -1 ); + setPoint( "cn4", 0, h, -1 ); + + function setPoint( point, x, y, z ) { + + THREE.CameraHelper.__v.set( x, y, z ); + THREE.CameraHelper.__projector.unprojectVector( THREE.CameraHelper.__v, THREE.CameraHelper.__c ); + + var points = scope.pointMap[ point ]; + + if ( points !== undefined ) { + + for ( var i = 0, il = points.length; i < il; i ++ ) { + + scope.geometry.vertices[ points[ i ] ].copy( THREE.CameraHelper.__v ); + + } + + } + + } + + this.geometry.verticesNeedUpdate = true; + +}; + +THREE.CameraHelper.__projector = new THREE.Projector(); +THREE.CameraHelper.__v = new THREE.Vector3(); +THREE.CameraHelper.__c = new THREE.Camera(); + +/* + * @author zz85 / http://twitter.com/blurspline / http://www.lab4games.net/zz85/blog + * + * Subdivision Geometry Modifier + * using Catmull-Clark Subdivision Surfaces + * for creating smooth geometry meshes + * + * Note: a modifier modifies vertices and faces of geometry, + * so use THREE.GeometryUtils.clone() if orignal geoemtry needs to be retained + * + * Readings: + * http://en.wikipedia.org/wiki/Catmull%E2%80%93Clark_subdivision_surface + * http://www.rorydriscoll.com/2008/08/01/catmull-clark-subdivision-the-basics/ + * http://xrt.wikidot.com/blog:31 + * "Subdivision Surfaces in Character Animation" + * + * (on boundary edges) + * http://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface + * https://graphics.stanford.edu/wikis/cs148-09-summer/Assignment3Description + * + * Supports: + * Closed and Open geometries. + * + * TODO: + * crease vertex and "semi-sharp" features + * selective subdivision + */ + + +THREE.SubdivisionModifier = function( subdivisions ) { + + this.subdivisions = (subdivisions === undefined ) ? 1 : subdivisions; + + // Settings + this.useOldVertexColors = false; + this.supportUVs = true; + this.debug = false; + +}; + +// Applies the "modify" pattern +THREE.SubdivisionModifier.prototype.modify = function ( geometry ) { + + var repeats = this.subdivisions; + + while ( repeats-- > 0 ) { + this.smooth( geometry ); + } + +}; + +/// REFACTORING THIS OUT + +THREE.GeometryUtils.orderedKey = function ( a, b ) { + + return Math.min( a, b ) + "_" + Math.max( a, b ); + +}; + + +// Returns a hashmap - of { edge_key: face_index } +THREE.GeometryUtils.computeEdgeFaces = function ( geometry ) { + + var i, il, v1, v2, j, k, + face, faceIndices, faceIndex, + edge, + hash, + edgeFaceMap = {}; + + var orderedKey = THREE.GeometryUtils.orderedKey; + + function mapEdgeHash( hash, i ) { + + if ( edgeFaceMap[ hash ] === undefined ) { + + edgeFaceMap[ hash ] = []; + + } + + edgeFaceMap[ hash ].push( i ); + } + + + // construct vertex -> face map + + for( i = 0, il = geometry.faces.length; i < il; i ++ ) { + + face = geometry.faces[ i ]; + + if ( face instanceof THREE.Face3 ) { + + hash = orderedKey( face.a, face.b ); + mapEdgeHash( hash, i ); + + hash = orderedKey( face.b, face.c ); + mapEdgeHash( hash, i ); + + hash = orderedKey( face.c, face.a ); + mapEdgeHash( hash, i ); + + } else if ( face instanceof THREE.Face4 ) { + + hash = orderedKey( face.a, face.b ); + mapEdgeHash( hash, i ); + + hash = orderedKey( face.b, face.c ); + mapEdgeHash( hash, i ); + + hash = orderedKey( face.c, face.d ); + mapEdgeHash( hash, i ); + + hash = orderedKey( face.d, face.a ); + mapEdgeHash( hash, i ); + + } + + } + + // extract faces + + // var edges = []; + // + // var numOfEdges = 0; + // for (i in edgeFaceMap) { + // numOfEdges++; + // + // edge = edgeFaceMap[i]; + // edges.push(edge); + // + // } + + //debug('edgeFaceMap', edgeFaceMap, 'geometry.edges',geometry.edges, 'numOfEdges', numOfEdges); + + return edgeFaceMap; + +} + +///////////////////////////// + +// Performs an iteration of Catmull-Clark Subdivision +THREE.SubdivisionModifier.prototype.smooth = function ( oldGeometry ) { + + //debug( 'running smooth' ); + + // New set of vertices, faces and uvs + var newVertices = [], newFaces = [], newUVs = []; + + function v( x, y, z ) { + newVertices.push( new THREE.Vector3( x, y, z ) ); + } + + var scope = this; + var orderedKey = THREE.GeometryUtils.orderedKey; + var computeEdgeFaces = THREE.GeometryUtils.computeEdgeFaces; + + function assert() { + if (scope.debug && console && console.assert) console.assert.apply(console, arguments); + } + + function debug() { + if (scope.debug) console.log.apply(console, arguments); + } + + function warn() { + if (console) + console.log.apply(console, arguments); + } + + function f4( a, b, c, d, oldFace, orders, facei ) { + + // TODO move vertex selection over here! + + var newFace = new THREE.Face4( a, b, c, d, null, oldFace.color, oldFace.materialIndex ); + + if (scope.useOldVertexColors) { + + newFace.vertexColors = []; + + var color, tmpColor, order; + for (var i=0;i<4;i++) { + order = orders[i]; + + color = new THREE.Color(), + color.setRGB(0,0,0); + + for (var j=0, jl=0; j=originalVerticesLength && vertexNo < (originalVerticesLength + originalFaces.length)) { + debug('face pt'); + } else { + debug('edge pt'); + } + + warn('warning, UV not found for', key); + + return null; + } + + return theUV; + + // Original faces -> Vertex Nos. + // new Facepoint -> Vertex Nos. + // edge Points + + } + + function addUV(vertexNo, oldFaceNo, value) { + + var key = vertexNo+':'+oldFaceNo; + if (!(key in uvForVertices)) { + uvForVertices[key] = value; + } else { + warn('dup vertexNo', vertexNo, 'oldFaceNo', oldFaceNo, 'value', value, 'key', key, uvForVertices[key]); + } + } + + // Step 1 + // For each face, add a face point + // Set each face point to be the centroid of all original points for the respective face. + // debug(oldGeometry); + var i, il, j, jl, face; + + // For Uvs + var uvs = oldGeometry.faceVertexUvs[0]; + var abcd = 'abcd', vertice; + + debug('originalFaces, uvs, originalVerticesLength', originalFaces.length, uvs.length, originalVerticesLength); + if (scope.supportUVs) + for (i=0, il = uvs.length; i Faces Index eg { edge_key: [face_index, face_index2 ]} + var edge, faceIndexA, faceIndexB, avg; + + // debug('edgeFaceMap', edgeFaceMap); + + var edgeCount = 0; + + var edgeVertex, edgeVertexA, edgeVertexB; + + //// + + var vertexEdgeMap = {}; // Gives edges connecting from each vertex + var vertexFaceMap = {}; // Gives faces connecting from each vertex + + function addVertexEdgeMap(vertex, edge) { + if (vertexEdgeMap[vertex]===undefined) { + vertexEdgeMap[vertex] = []; + } + + vertexEdgeMap[vertex].push(edge); + } + + function addVertexFaceMap(vertex, face, edge) { + if (vertexFaceMap[vertex]===undefined) { + vertexFaceMap[vertex] = {}; + } + + vertexFaceMap[vertex][face] = edge; + // vertexFaceMap[vertex][face] = null; + } + + // Prepares vertexEdgeMap and vertexFaceMap + for (i in edgeFaceMap) { // This is for every edge + edge = edgeFaceMap[i]; + + edgeVertex = i.split('_'); + edgeVertexA = edgeVertex[0]; + edgeVertexB = edgeVertex[1]; + + // Maps an edgeVertex to connecting edges + addVertexEdgeMap(edgeVertexA, [edgeVertexA, edgeVertexB] ); + addVertexEdgeMap(edgeVertexB, [edgeVertexA, edgeVertexB] ); + + + for (j=0,jl=edge.length;j 0, 'an edge without faces?!'); + + if (edge.length==1) { + + avg.addSelf(originalPoints[edgeVertexA]); + avg.addSelf(originalPoints[edgeVertexB]); + avg.multiplyScalar(0.5); + + sharpVertices[newPoints.length] = true; + + } else { + + avg.addSelf(facePoints[faceIndexA]); + avg.addSelf(facePoints[faceIndexB]); + + avg.addSelf(originalPoints[edgeVertexA]); + avg.addSelf(originalPoints[edgeVertexB]); + + avg.multiplyScalar(0.25); + + } + + edgePoints[i] = originalVerticesLength + originalFaces.length + edgeCount; + + newPoints.push( avg ); + + edgeCount ++; + + if (!scope.supportUVs) { + continue; + } + + // Prepare subdivided uv + + avgUv = new THREE.UV(); + + avgUv.u = getUV(edgeVertexA, faceIndexA).u + getUV(edgeVertexB, faceIndexA).u; + avgUv.v = getUV(edgeVertexA, faceIndexA).v + getUV(edgeVertexB, faceIndexA).v; + avgUv.u /= 2; + avgUv.v /= 2; + + addUV(edgePoints[i], faceIndexA, avgUv); + + if (edge.length>=2) { + assert(edge.length == 2, 'did we plan for more than 2 edges?'); + avgUv = new THREE.UV(); + + avgUv.u = getUV(edgeVertexA, faceIndexB).u + getUV(edgeVertexB, faceIndexB).u; + avgUv.v = getUV(edgeVertexA, faceIndexB).v + getUV(edgeVertexB, faceIndexB).v; + avgUv.u /= 2; + avgUv.v /= 2; + + addUV(edgePoints[i], faceIndexB, avgUv); + } + + } + + debug('-- Step 2 done'); + + // Step 3 + // For each face point, add an edge for every edge of the face, + // connecting the face point to each edge point for the face. + + var facePt, currentVerticeIndex; + + var hashAB, hashBC, hashCD, hashDA, hashCA; + + var abc123 = ['123', '12', '2', '23']; + var bca123 = ['123', '23', '3', '31']; + var cab123 = ['123', '31', '1', '12']; + var abc1234 = ['1234', '12', '2', '23']; + var bcd1234 = ['1234', '23', '3', '34']; + var cda1234 = ['1234', '34', '4', '41']; + var dab1234 = ['1234', '41', '1', '12']; + + + for (i=0, il = facePoints.length; i2) { + // TODO + } + */ + + F.divideScalar(f); + + + var boundary_edges = 0; + + + + if (boundary_case) { + + var bb_edge; + for (j=0; j 1) z = 0 is ontop z = 1 is back + scale: 1, // scale + rotation: 1, // rotation + opacity: opacity, // opacity + color: color, // color + blending: blending } ); // blending + +}; + + +/* + * Update lens flares update positions on all flares based on the screen position + * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. + */ + +THREE.LensFlare.prototype.updateLensFlares = function () { + + var f, fl = this.lensFlares.length; + var flare; + var vecX = -this.positionScreen.x * 2; + var vecY = -this.positionScreen.y * 2; + + for( f = 0; f < fl; f ++ ) { + + flare = this.lensFlares[ f ]; + + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; + + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + + } + +}; + + + + + + + + + + + + +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.MorphBlendMesh = function( geometry, material ) { + + THREE.Mesh.call( this, geometry, material ); + + this.animationsMap = {}; + this.animationsList = []; + + // prepare default animation + // (all frames played together in 1 second) + + var numFrames = this.geometry.morphTargets.length; + + var name = "__default"; + + var startFrame = 0; + var endFrame = numFrames - 1; + + var fps = numFrames / 1; + + this.createAnimation( name, startFrame, endFrame, fps ); + this.setAnimationWeight( name, 1 ); + +}; + +THREE.MorphBlendMesh.prototype = Object.create( THREE.Mesh.prototype ); + +THREE.MorphBlendMesh.prototype.createAnimation = function ( name, start, end, fps ) { + + var animation = { + + startFrame: start, + endFrame: end, + + length: end - start + 1, + + fps: fps, + duration: ( end - start ) / fps, + + lastFrame: 0, + currentFrame: 0, + + active: false, + + time: 0, + direction: 1, + weight: 1, + + directionBackwards: false, + mirroredLoop: false + + }; + + this.animationsMap[ name ] = animation; + this.animationsList.push( animation ); + +}; + +THREE.MorphBlendMesh.prototype.autoCreateAnimations = function ( fps ) { + + var pattern = /([a-z]+)(\d+)/; + + var firstAnimation, frameRanges = {}; + + var geometry = this.geometry; + + for ( var i = 0, il = geometry.morphTargets.length; i < il; i ++ ) { + + var morph = geometry.morphTargets[ i ]; + var chunks = morph.name.match( pattern ); + + if ( chunks && chunks.length > 1 ) { + + var name = chunks[ 1 ]; + var num = chunks[ 2 ]; + + if ( ! frameRanges[ name ] ) frameRanges[ name ] = { start: Infinity, end: -Infinity }; + + var range = frameRanges[ name ]; + + if ( i < range.start ) range.start = i; + if ( i > range.end ) range.end = i; + + if ( ! firstAnimation ) firstAnimation = name; + + } + + } + + for ( var name in frameRanges ) { + + var range = frameRanges[ name ]; + this.createAnimation( name, range.start, range.end, fps ); + + } + + this.firstAnimation = firstAnimation; + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionForward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = 1; + animation.directionBackwards = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDirectionBackward = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.direction = -1; + animation.directionBackwards = true; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationFPS = function ( name, fps ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.fps = fps; + animation.duration = ( animation.end - animation.start ) / animation.fps; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationDuration = function ( name, duration ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.duration = duration; + animation.fps = ( animation.end - animation.start ) / animation.duration; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationWeight = function ( name, weight ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.weight = weight; + + } + +}; + +THREE.MorphBlendMesh.prototype.setAnimationTime = function ( name, time ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = time; + + } + +}; + +THREE.MorphBlendMesh.prototype.getAnimationTime = function ( name ) { + + var time = 0; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + time = animation.time; + + } + + return time; + +}; + +THREE.MorphBlendMesh.prototype.getAnimationDuration = function ( name ) { + + var duration = -1; + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + duration = animation.duration; + + } + + return duration; + +}; + +THREE.MorphBlendMesh.prototype.playAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.time = 0; + animation.active = true; + + } else { + + console.warn( "animation[" + name + "] undefined" ); + + } + +}; + +THREE.MorphBlendMesh.prototype.stopAnimation = function ( name ) { + + var animation = this.animationsMap[ name ]; + + if ( animation ) { + + animation.active = false; + + } + +}; + +THREE.MorphBlendMesh.prototype.update = function ( delta ) { + + for ( var i = 0, il = this.animationsList.length; i < il; i ++ ) { + + var animation = this.animationsList[ i ]; + + if ( ! animation.active ) continue; + + var frameTime = animation.duration / animation.length; + + animation.time += animation.direction * delta; + + if ( animation.mirroredLoop ) { + + if ( animation.time > animation.duration || animation.time < 0 ) { + + animation.direction *= -1; + + if ( animation.time > animation.duration ) { + + animation.time = animation.duration; + animation.directionBackwards = true; + + } + + if ( animation.time < 0 ) { + + animation.time = 0; + animation.directionBackwards = false; + + } + + } + + } else { + + animation.time = animation.time % animation.duration; + + if ( animation.time < 0 ) animation.time += animation.duration; + + } + + var keyframe = animation.startFrame + THREE.Math.clamp( Math.floor( animation.time / frameTime ), 0, animation.length - 1 ); + var weight = animation.weight; + + if ( keyframe !== animation.currentFrame ) { + + this.morphTargetInfluences[ animation.lastFrame ] = 0; + this.morphTargetInfluences[ animation.currentFrame ] = 1 * weight; + + this.morphTargetInfluences[ keyframe ] = 0; + + animation.lastFrame = animation.currentFrame; + animation.currentFrame = keyframe; + + } + + var mix = ( animation.time % frameTime ) / frameTime; + + if ( animation.directionBackwards ) mix = 1 - mix; + + this.morphTargetInfluences[ animation.currentFrame ] = mix * weight; + this.morphTargetInfluences[ animation.lastFrame ] = ( 1 - mix ) * weight; + + } + +}; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.LensFlarePlugin = function ( ) { + + var _gl, _renderer, _lensFlare = {}; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + _lensFlare.vertices = new Float32Array( 8 + 8 ); + _lensFlare.faces = new Uint16Array( 6 ); + + var i = 0; + _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = -1; // vertex + _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 0; // uv... etc. + + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = -1; + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 0; + + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1; + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1; + + _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1; + _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 1; + + i = 0; + _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 1; _lensFlare.faces[ i++ ] = 2; + _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 2; _lensFlare.faces[ i++ ] = 3; + + // buffers + + _lensFlare.vertexBuffer = _gl.createBuffer(); + _lensFlare.elementBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW ); + + // textures + + _lensFlare.tempTexture = _gl.createTexture(); + _lensFlare.occlusionTexture = _gl.createTexture(); + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, 16, 16, 0, _gl.RGB, _gl.UNSIGNED_BYTE, null ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + + if ( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) { + + _lensFlare.hasVertexTexture = false; + _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlare" ] ); + + } else { + + _lensFlare.hasVertexTexture = true; + _lensFlare.program = createProgram( THREE.ShaderFlares[ "lensFlareVertexTexture" ] ); + + } + + _lensFlare.attributes = {}; + _lensFlare.uniforms = {}; + + _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" ); + _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "uv" ); + + _lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" ); + _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" ); + _lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" ); + _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" ); + _lensFlare.uniforms.color = _gl.getUniformLocation( _lensFlare.program, "color" ); + _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" ); + _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" ); + _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" ); + + _lensFlare.attributesEnabled = false; + + }; + + + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + * Then _lensFlare.update_lensFlares() is called to re-position and + * update transparency of flares. Then they are rendered. + * + */ + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + var flares = scene.__webglFlares, + nFlares = flares.length; + + if ( ! nFlares ) return; + + var tempPosition = new THREE.Vector3(); + + var invAspect = viewportHeight / viewportWidth, + halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + var size = 16 / viewportHeight, + scale = new THREE.Vector2( size * invAspect, size ); + + var screenPosition = new THREE.Vector3( 1, 1, 0 ), + screenPositionPixels = new THREE.Vector2( 1, 1 ); + + var uniforms = _lensFlare.uniforms, + attributes = _lensFlare.attributes; + + // set _lensFlare program and reset blending + + _gl.useProgram( _lensFlare.program ); + + if ( ! _lensFlare.attributesEnabled ) { + + _gl.enableVertexAttribArray( _lensFlare.attributes.vertex ); + _gl.enableVertexAttribArray( _lensFlare.attributes.uv ); + + _lensFlare.attributesEnabled = true; + + } + + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/unforms + + _gl.uniform1i( uniforms.occlusionMap, 0 ); + _gl.uniform1i( uniforms.map, 1 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); + _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + + _gl.disable( _gl.CULL_FACE ); + _gl.depthMask( false ); + + var i, j, jl, flare, sprite; + + for ( i = 0; i < nFlares; i ++ ) { + + size = 16 / viewportHeight; + scale.set( size * invAspect, size ); + + // calc object screen position + + flare = flares[ i ]; + + tempPosition.set( flare.matrixWorld.elements[12], flare.matrixWorld.elements[13], flare.matrixWorld.elements[14] ); + + camera.matrixWorldInverse.multiplyVector3( tempPosition ); + camera.projectionMatrix.multiplyVector3( tempPosition ); + + // setup arrays for gl programs + + screenPosition.copy( tempPosition ) + + screenPositionPixels.x = screenPosition.x * halfViewportWidth + halfViewportWidth; + screenPositionPixels.y = screenPosition.y * halfViewportHeight + halfViewportHeight; + + // screen cull + + if ( _lensFlare.hasVertexTexture || ( + screenPositionPixels.x > 0 && + screenPositionPixels.x < viewportWidth && + screenPositionPixels.y > 0 && + screenPositionPixels.y < viewportHeight ) ) { + + // save current RGB to temp texture + + _gl.activeTexture( _gl.TEXTURE1 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // render pink quad + + _gl.uniform1i( uniforms.renderType, 0 ); + _gl.uniform2f( uniforms.scale, scale.x, scale.y ); + _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + + _gl.disable( _gl.BLEND ); + _gl.enable( _gl.DEPTH_TEST ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // copy result to occlusionMap + + _gl.activeTexture( _gl.TEXTURE0 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels.x - 8, screenPositionPixels.y - 8, 16, 16, 0 ); + + + // restore graphics + + _gl.uniform1i( uniforms.renderType, 1 ); + _gl.disable( _gl.DEPTH_TEST ); + + _gl.activeTexture( _gl.TEXTURE1 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // update object positions + + flare.positionScreen.copy( screenPosition ) + + if ( flare.customUpdateCallback ) { + + flare.customUpdateCallback( flare ); + + } else { + + flare.updateLensFlares(); + + } + + // render flares + + _gl.uniform1i( uniforms.renderType, 2 ); + _gl.enable( _gl.BLEND ); + + for ( j = 0, jl = flare.lensFlares.length; j < jl; j ++ ) { + + sprite = flare.lensFlares[ j ]; + + if ( sprite.opacity > 0.001 && sprite.scale > 0.001 ) { + + screenPosition.x = sprite.x; + screenPosition.y = sprite.y; + screenPosition.z = sprite.z; + + size = sprite.size * sprite.scale / viewportHeight; + + scale.x = size * invAspect; + scale.y = size; + + _gl.uniform3f( uniforms.screenPosition, screenPosition.x, screenPosition.y, screenPosition.z ); + _gl.uniform2f( uniforms.scale, scale.x, scale.y ); + _gl.uniform1f( uniforms.rotation, sprite.rotation ); + + _gl.uniform1f( uniforms.opacity, sprite.opacity ); + _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + + _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + _renderer.setTexture( sprite.texture, 1 ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + } + + } + + } + + } + + // restore gl + + _gl.enable( _gl.CULL_FACE ); + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthMask( true ); + + }; + + function createProgram ( shader ) { + + var program = _gl.createProgram(); + + var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); + var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + + _gl.shaderSource( fragmentShader, shader.fragmentShader ); + _gl.shaderSource( vertexShader, shader.vertexShader ); + + _gl.compileShader( fragmentShader ); + _gl.compileShader( vertexShader ); + + _gl.attachShader( program, fragmentShader ); + _gl.attachShader( program, vertexShader ); + + _gl.linkProgram( program ); + + return program; + + }; + +};/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ShadowMapPlugin = function ( ) { + + var _gl, + _renderer, + _depthMaterial, _depthMaterialMorph, _depthMaterialSkin, _depthMaterialMorphSkin, + + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(), + + _min = new THREE.Vector3(), + _max = new THREE.Vector3(); + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); + _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); + _depthMaterialSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, skinning: true } ); + _depthMaterialMorphSkin = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true, skinning: true } ); + + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + _depthMaterialSkin._shadowPass = true; + _depthMaterialMorphSkin._shadowPass = true; + + }; + + this.render = function ( scene, camera ) { + + if ( ! ( _renderer.shadowMapEnabled && _renderer.shadowMapAutoUpdate ) ) return; + + this.update( scene, camera ); + + }; + + this.update = function ( scene, camera ) { + + var i, il, j, jl, n, + + shadowMap, shadowMatrix, shadowCamera, + program, buffer, material, + webglObject, object, light, + renderList, + + lights = [], + k = 0, + + fog = null; + + // set GL state for depth map + + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); + + _gl.enable( _gl.CULL_FACE ); + _gl.frontFace( _gl.CCW ); + + if ( _renderer.shadowMapCullFrontFaces ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.BACK ); + + } + + _renderer.setDepthTest( true ); + + // preprocess lights + // - skip lights that are not casting shadows + // - create virtual lights for cascaded shadow maps + + for ( i = 0, il = scene.__lights.length; i < il; i ++ ) { + + light = scene.__lights[ i ]; + + if ( ! light.castShadow ) continue; + + if ( ( light instanceof THREE.DirectionalLight ) && light.shadowCascade ) { + + for ( n = 0; n < light.shadowCascadeCount; n ++ ) { + + var virtualLight; + + if ( ! light.shadowCascadeArray[ n ] ) { + + virtualLight = createVirtualLight( light, n ); + virtualLight.originalCamera = camera; + + var gyro = new THREE.Gyroscope(); + gyro.position = light.shadowCascadeOffset; + + gyro.add( virtualLight ); + gyro.add( virtualLight.target ); + + camera.add( gyro ); + + light.shadowCascadeArray[ n ] = virtualLight; + + console.log( "Created virtualLight", virtualLight ); + + } else { + + virtualLight = light.shadowCascadeArray[ n ]; + + } + + updateVirtualLight( light, n ); + + lights[ k ] = virtualLight; + k ++; + + } + + } else { + + lights[ k ] = light; + k ++; + + } + + } + + // render depth map + + for ( i = 0, il = lights.length; i < il; i ++ ) { + + light = lights[ i ]; + + if ( ! light.shadowMap ) { + + var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat }; + + light.shadowMap = new THREE.WebGLRenderTarget( light.shadowMapWidth, light.shadowMapHeight, pars ); + light.shadowMapSize = new THREE.Vector2( light.shadowMapWidth, light.shadowMapHeight ); + + light.shadowMatrix = new THREE.Matrix4(); + + } + + if ( ! light.shadowCamera ) { + + if ( light instanceof THREE.SpotLight ) { + + light.shadowCamera = new THREE.PerspectiveCamera( light.shadowCameraFov, light.shadowMapWidth / light.shadowMapHeight, light.shadowCameraNear, light.shadowCameraFar ); + + } else if ( light instanceof THREE.DirectionalLight ) { + + light.shadowCamera = new THREE.OrthographicCamera( light.shadowCameraLeft, light.shadowCameraRight, light.shadowCameraTop, light.shadowCameraBottom, light.shadowCameraNear, light.shadowCameraFar ); + + } else { + + console.error( "Unsupported light type for shadow" ); + continue; + + } + + scene.add( light.shadowCamera ); + + if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld(); + + } + + if ( light.shadowCameraVisible && ! light.cameraHelper ) { + + light.cameraHelper = new THREE.CameraHelper( light.shadowCamera ); + light.shadowCamera.add( light.cameraHelper ); + + } + + if ( light.isVirtual && virtualLight.originalCamera == camera ) { + + updateShadowCamera( camera, light ); + + } + + shadowMap = light.shadowMap; + shadowMatrix = light.shadowMatrix; + shadowCamera = light.shadowCamera; + + shadowCamera.position.copy( light.matrixWorld.getPosition() ); + shadowCamera.lookAt( light.target.matrixWorld.getPosition() ); + shadowCamera.updateMatrixWorld(); + + shadowCamera.matrixWorldInverse.getInverse( shadowCamera.matrixWorld ); + + if ( light.cameraHelper ) light.cameraHelper.visible = light.shadowCameraVisible; + if ( light.shadowCameraVisible ) light.cameraHelper.update(); + + // compute shadow matrix + + shadowMatrix.set( 0.5, 0.0, 0.0, 0.5, + 0.0, 0.5, 0.0, 0.5, + 0.0, 0.0, 0.5, 0.5, + 0.0, 0.0, 0.0, 1.0 ); + + shadowMatrix.multiplySelf( shadowCamera.projectionMatrix ); + shadowMatrix.multiplySelf( shadowCamera.matrixWorldInverse ); + + // update camera matrices and frustum + + if ( ! shadowCamera._viewMatrixArray ) shadowCamera._viewMatrixArray = new Float32Array( 16 ); + if ( ! shadowCamera._projectionMatrixArray ) shadowCamera._projectionMatrixArray = new Float32Array( 16 ); + + shadowCamera.matrixWorldInverse.flattenToArray( shadowCamera._viewMatrixArray ); + shadowCamera.projectionMatrix.flattenToArray( shadowCamera._projectionMatrixArray ); + + _projScreenMatrix.multiply( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // render shadow map + + _renderer.setRenderTarget( shadowMap ); + _renderer.clear(); + + // set object matrices & frustum culling + + renderList = scene.__webglObjects; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + webglObject.render = false; + + if ( object.visible && object.castShadow ) { + + if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) { + + object._modelViewMatrix.multiply( shadowCamera.matrixWorldInverse, object.matrixWorld ); + + webglObject.render = true; + + } + + } + + } + + // render regular objects + + var objectMaterial, useMorphing, useSkinning; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + + // culling is overriden globally for all objects + // while rendering depth map + + // need to deal with MeshFaceMaterial somehow + // in that case just use the first of geometry.materials for now + // (proper solution would require to break objects by materials + // similarly to regular rendering and then set corresponding + // depth materials per each chunk instead of just once per object) + + objectMaterial = getObjectMaterial( object ); + + useMorphing = object.geometry.morphTargets.length > 0 && objectMaterial.morphTargets; + useSkinning = object instanceof THREE.SkinnedMesh && objectMaterial.skinning; + + if ( object.customDepthMaterial ) { + + material = object.customDepthMaterial; + + } else if ( useSkinning ) { + + material = useMorphing ? _depthMaterialMorphSkin : _depthMaterialSkin; + + } else if ( useMorphing ) { + + material = _depthMaterialMorph; + + } else { + + material = _depthMaterial; + + } + + if ( buffer instanceof THREE.BufferGeometry ) { + + _renderer.renderBufferDirect( shadowCamera, scene.__lights, fog, material, buffer, object ); + + } else { + + _renderer.renderBuffer( shadowCamera, scene.__lights, fog, material, buffer, object ); + + } + + } + + } + + // set matrices and render immediate objects + + renderList = scene.__webglObjectsImmediate; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + if ( object.visible && object.castShadow ) { + + object._modelViewMatrix.multiply( shadowCamera.matrixWorldInverse, object.matrixWorld ); + + _renderer.renderImmediateObject( shadowCamera, scene.__lights, fog, _depthMaterial, object ); + + } + + } + + } + + // restore GL state + + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); + + if ( _renderer.shadowMapCullFrontFaces ) { + + _gl.cullFace( _gl.BACK ); + + } + + }; + + function createVirtualLight( light, cascade ) { + + var virtualLight = new THREE.DirectionalLight(); + + virtualLight.isVirtual = true; + + virtualLight.onlyShadow = true; + virtualLight.castShadow = true; + + virtualLight.shadowCameraNear = light.shadowCameraNear; + virtualLight.shadowCameraFar = light.shadowCameraFar; + + virtualLight.shadowCameraLeft = light.shadowCameraLeft; + virtualLight.shadowCameraRight = light.shadowCameraRight; + virtualLight.shadowCameraBottom = light.shadowCameraBottom; + virtualLight.shadowCameraTop = light.shadowCameraTop; + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + virtualLight.shadowMapWidth = light.shadowCascadeWidth[ cascade ]; + virtualLight.shadowMapHeight = light.shadowCascadeHeight[ cascade ]; + + virtualLight.pointsWorld = []; + virtualLight.pointsFrustum = []; + + var pointsWorld = virtualLight.pointsWorld, + pointsFrustum = virtualLight.pointsFrustum; + + for ( var i = 0; i < 8; i ++ ) { + + pointsWorld[ i ] = new THREE.Vector3(); + pointsFrustum[ i ] = new THREE.Vector3(); + + } + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + pointsFrustum[ 0 ].set( -1, -1, nearZ ); + pointsFrustum[ 1 ].set( 1, -1, nearZ ); + pointsFrustum[ 2 ].set( -1, 1, nearZ ); + pointsFrustum[ 3 ].set( 1, 1, nearZ ); + + pointsFrustum[ 4 ].set( -1, -1, farZ ); + pointsFrustum[ 5 ].set( 1, -1, farZ ); + pointsFrustum[ 6 ].set( -1, 1, farZ ); + pointsFrustum[ 7 ].set( 1, 1, farZ ); + + return virtualLight; + + } + + // Synchronize virtual light with the original light + + function updateVirtualLight( light, cascade ) { + + var virtualLight = light.shadowCascadeArray[ cascade ]; + + virtualLight.position.copy( light.position ); + virtualLight.target.position.copy( light.target.position ); + virtualLight.lookAt( virtualLight.target ); + + virtualLight.shadowCameraVisible = light.shadowCameraVisible; + virtualLight.shadowDarkness = light.shadowDarkness; + + virtualLight.shadowBias = light.shadowCascadeBias[ cascade ]; + + var nearZ = light.shadowCascadeNearZ[ cascade ]; + var farZ = light.shadowCascadeFarZ[ cascade ]; + + var pointsFrustum = virtualLight.pointsFrustum; + + pointsFrustum[ 0 ].z = nearZ; + pointsFrustum[ 1 ].z = nearZ; + pointsFrustum[ 2 ].z = nearZ; + pointsFrustum[ 3 ].z = nearZ; + + pointsFrustum[ 4 ].z = farZ; + pointsFrustum[ 5 ].z = farZ; + pointsFrustum[ 6 ].z = farZ; + pointsFrustum[ 7 ].z = farZ; + + } + + // Fit shadow camera's ortho frustum to camera frustum + + function updateShadowCamera( camera, light ) { + + var shadowCamera = light.shadowCamera, + pointsFrustum = light.pointsFrustum, + pointsWorld = light.pointsWorld; + + _min.set( Infinity, Infinity, Infinity ); + _max.set( -Infinity, -Infinity, -Infinity ); + + for ( var i = 0; i < 8; i ++ ) { + + var p = pointsWorld[ i ]; + + p.copy( pointsFrustum[ i ] ); + THREE.ShadowMapPlugin.__projector.unprojectVector( p, camera ); + + shadowCamera.matrixWorldInverse.multiplyVector3( p ); + + if ( p.x < _min.x ) _min.x = p.x; + if ( p.x > _max.x ) _max.x = p.x; + + if ( p.y < _min.y ) _min.y = p.y; + if ( p.y > _max.y ) _max.y = p.y; + + if ( p.z < _min.z ) _min.z = p.z; + if ( p.z > _max.z ) _max.z = p.z; + + } + + shadowCamera.left = _min.x; + shadowCamera.right = _max.x; + shadowCamera.top = _max.y; + shadowCamera.bottom = _min.y; + + // can't really fit near/far + //shadowCamera.near = _min.z; + //shadowCamera.far = _max.z; + + shadowCamera.updateProjectionMatrix(); + + } + + // For the moment just ignore objects that have multiple materials with different animation methods + // Only the first material will be taken into account for deciding which depth material to use for shadow maps + + function getObjectMaterial( object ) { + + return object.material instanceof THREE.MeshFaceMaterial ? object.geometry.materials[ 0 ] : object.material; + + } + +}; + +THREE.ShadowMapPlugin.__projector = new THREE.Projector(); +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SpritePlugin = function ( ) { + + var _gl, _renderer, _sprite = {}; + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + _sprite.vertices = new Float32Array( 8 + 8 ); + _sprite.faces = new Uint16Array( 6 ); + + var i = 0; + + _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = -1; // vertex 0 + _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 0; // uv 0 + + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = -1; // vertex 1 + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 0; // uv 1 + + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // vertex 2 + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; // uv 2 + + _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1; // vertex 3 + _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 1; // uv 3 + + i = 0; + + _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2; + _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3; + + _sprite.vertexBuffer = _gl.createBuffer(); + _sprite.elementBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW ); + + _sprite.program = createProgram( THREE.ShaderSprite[ "sprite" ] ); + + _sprite.attributes = {}; + _sprite.uniforms = {}; + + _sprite.attributes.position = _gl.getAttribLocation ( _sprite.program, "position" ); + _sprite.attributes.uv = _gl.getAttribLocation ( _sprite.program, "uv" ); + + _sprite.uniforms.uvOffset = _gl.getUniformLocation( _sprite.program, "uvOffset" ); + _sprite.uniforms.uvScale = _gl.getUniformLocation( _sprite.program, "uvScale" ); + + _sprite.uniforms.rotation = _gl.getUniformLocation( _sprite.program, "rotation" ); + _sprite.uniforms.scale = _gl.getUniformLocation( _sprite.program, "scale" ); + _sprite.uniforms.alignment = _gl.getUniformLocation( _sprite.program, "alignment" ); + + _sprite.uniforms.color = _gl.getUniformLocation( _sprite.program, "color" ); + _sprite.uniforms.map = _gl.getUniformLocation( _sprite.program, "map" ); + _sprite.uniforms.opacity = _gl.getUniformLocation( _sprite.program, "opacity" ); + + _sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" ); + _sprite.uniforms.affectedByDistance = _gl.getUniformLocation( _sprite.program, "affectedByDistance" ); + _sprite.uniforms.screenPosition = _gl.getUniformLocation( _sprite.program, "screenPosition" ); + _sprite.uniforms.modelViewMatrix = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" ); + _sprite.uniforms.projectionMatrix = _gl.getUniformLocation( _sprite.program, "projectionMatrix" ); + + _sprite.attributesEnabled = false; + + }; + + this.render = function ( scene, camera, viewportWidth, viewportHeight ) { + + var sprites = scene.__webglSprites, + nSprites = sprites.length; + + if ( ! nSprites ) return; + + var attributes = _sprite.attributes, + uniforms = _sprite.uniforms; + + var invAspect = viewportHeight / viewportWidth; + + var halfViewportWidth = viewportWidth * 0.5, + halfViewportHeight = viewportHeight * 0.5; + + var mergeWith3D = true; + + // setup gl + + _gl.useProgram( _sprite.program ); + + if ( ! _sprite.attributesEnabled ) { + + _gl.enableVertexAttribArray( attributes.position ); + _gl.enableVertexAttribArray( attributes.uv ); + + _sprite.attributesEnabled = true; + + } + + _gl.disable( _gl.CULL_FACE ); + _gl.enable( _gl.BLEND ); + _gl.depthMask( true ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); + + _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera._projectionMatrixArray ); + + _gl.activeTexture( _gl.TEXTURE0 ); + _gl.uniform1i( uniforms.map, 0 ); + + // update positions and sort + + var i, sprite, screenPosition, size, scale = []; + + for( i = 0; i < nSprites; i ++ ) { + + sprite = sprites[ i ]; + + if ( ! sprite.visible || sprite.opacity === 0 ) continue; + + if( ! sprite.useScreenCoordinates ) { + + sprite._modelViewMatrix.multiply( camera.matrixWorldInverse, sprite.matrixWorld ); + sprite.z = - sprite._modelViewMatrix.elements[14]; + + } else { + + sprite.z = - sprite.position.z; + + } + + } + + sprites.sort( painterSort ); + + // render all sprites + + for( i = 0; i < nSprites; i ++ ) { + + sprite = sprites[ i ]; + + if ( ! sprite.visible || sprite.opacity === 0 ) continue; + + if ( sprite.map && sprite.map.image && sprite.map.image.width ) { + + if ( sprite.useScreenCoordinates ) { + + _gl.uniform1i( uniforms.useScreenCoordinates, 1 ); + _gl.uniform3f( + uniforms.screenPosition, + ( sprite.position.x - halfViewportWidth ) / halfViewportWidth, + ( halfViewportHeight - sprite.position.y ) / halfViewportHeight, + Math.max( 0, Math.min( 1, sprite.position.z ) ) + ); + + } else { + + _gl.uniform1i( uniforms.useScreenCoordinates, 0 ); + _gl.uniform1i( uniforms.affectedByDistance, sprite.affectedByDistance ? 1 : 0 ); + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite._modelViewMatrix.elements ); + + } + + size = sprite.map.image.width / ( sprite.scaleByViewport ? viewportHeight : 1 ); + + scale[ 0 ] = size * invAspect * sprite.scale.x; + scale[ 1 ] = size * sprite.scale.y; + + _gl.uniform2f( uniforms.uvScale, sprite.uvScale.x, sprite.uvScale.y ); + _gl.uniform2f( uniforms.uvOffset, sprite.uvOffset.x, sprite.uvOffset.y ); + _gl.uniform2f( uniforms.alignment, sprite.alignment.x, sprite.alignment.y ); + + _gl.uniform1f( uniforms.opacity, sprite.opacity ); + _gl.uniform3f( uniforms.color, sprite.color.r, sprite.color.g, sprite.color.b ); + + _gl.uniform1f( uniforms.rotation, sprite.rotation ); + _gl.uniform2fv( uniforms.scale, scale ); + + if ( sprite.mergeWith3D && !mergeWith3D ) { + + _gl.enable( _gl.DEPTH_TEST ); + mergeWith3D = true; + + } else if ( ! sprite.mergeWith3D && mergeWith3D ) { + + _gl.disable( _gl.DEPTH_TEST ); + mergeWith3D = false; + + } + + _renderer.setBlending( sprite.blending, sprite.blendEquation, sprite.blendSrc, sprite.blendDst ); + _renderer.setTexture( sprite.map, 0 ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + } + + } + + // restore gl + + _gl.enable( _gl.CULL_FACE ); + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthMask( true ); + + }; + + function createProgram ( shader ) { + + var program = _gl.createProgram(); + + var fragmentShader = _gl.createShader( _gl.FRAGMENT_SHADER ); + var vertexShader = _gl.createShader( _gl.VERTEX_SHADER ); + + _gl.shaderSource( fragmentShader, shader.fragmentShader ); + _gl.shaderSource( vertexShader, shader.vertexShader ); + + _gl.compileShader( fragmentShader ); + _gl.compileShader( vertexShader ); + + _gl.attachShader( program, fragmentShader ); + _gl.attachShader( program, vertexShader ); + + _gl.linkProgram( program ); + + return program; + + }; + + function painterSort ( a, b ) { + + return b.z - a.z; + + }; + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.DepthPassPlugin = function ( ) { + + this.enabled = false; + this.renderTarget = null; + + var _gl, + _renderer, + _depthMaterial, _depthMaterialMorph, + + _frustum = new THREE.Frustum(), + _projScreenMatrix = new THREE.Matrix4(); + + this.init = function ( renderer ) { + + _gl = renderer.context; + _renderer = renderer; + + var depthShader = THREE.ShaderLib[ "depthRGBA" ]; + var depthUniforms = THREE.UniformsUtils.clone( depthShader.uniforms ); + + _depthMaterial = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms } ); + _depthMaterialMorph = new THREE.ShaderMaterial( { fragmentShader: depthShader.fragmentShader, vertexShader: depthShader.vertexShader, uniforms: depthUniforms, morphTargets: true } ); + + _depthMaterial._shadowPass = true; + _depthMaterialMorph._shadowPass = true; + + }; + + this.render = function ( scene, camera ) { + + if ( ! this.enabled ) return; + + this.update( scene, camera ); + + }; + + this.update = function ( scene, camera ) { + + var i, il, j, jl, n, + + program, buffer, material, + webglObject, object, light, + renderList, + + fog = null; + + // set GL state for depth map + + _gl.clearColor( 1, 1, 1, 1 ); + _gl.disable( _gl.BLEND ); + + _renderer.setDepthTest( true ); + + // update scene + + if ( _renderer.autoUpdateScene ) scene.updateMatrixWorld(); + + // update camera matrices and frustum + + if ( ! camera._viewMatrixArray ) camera._viewMatrixArray = new Float32Array( 16 ); + if ( ! camera._projectionMatrixArray ) camera._projectionMatrixArray = new Float32Array( 16 ); + + camera.matrixWorldInverse.getInverse( camera.matrixWorld ); + + camera.matrixWorldInverse.flattenToArray( camera._viewMatrixArray ); + camera.projectionMatrix.flattenToArray( camera._projectionMatrixArray ); + + _projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse ); + _frustum.setFromMatrix( _projScreenMatrix ); + + // render depth map + + _renderer.setRenderTarget( this.renderTarget ); + _renderer.clear(); + + // set object matrices & frustum culling + + renderList = scene.__webglObjects; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + webglObject.render = false; + + if ( object.visible ) { + + if ( ! ( object instanceof THREE.Mesh ) || ! ( object.frustumCulled ) || _frustum.contains( object ) ) { + + //object.matrixWorld.flattenToArray( object._modelMatrixArray ); + object._modelViewMatrix.multiply( camera.matrixWorldInverse, object.matrixWorld); + + webglObject.render = true; + + } + + } + + } + + // render regular objects + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + + if ( object.material ) _renderer.setMaterialFaces( object.material ); + + if ( object.customDepthMaterial ) { + + material = object.customDepthMaterial; + + } else if ( object.geometry.morphTargets.length ) { + + material = _depthMaterialMorph; + + } else { + + material = _depthMaterial; + + } + + if ( buffer instanceof THREE.BufferGeometry ) { + + _renderer.renderBufferDirect( camera, scene.__lights, fog, material, buffer, object ); + + } else { + + _renderer.renderBuffer( camera, scene.__lights, fog, material, buffer, object ); + + } + + } + + } + + // set matrices and render immediate objects + + renderList = scene.__webglObjectsImmediate; + + for ( j = 0, jl = renderList.length; j < jl; j ++ ) { + + webglObject = renderList[ j ]; + object = webglObject.object; + + if ( object.visible && object.castShadow ) { + + /* + if ( object.matrixAutoUpdate ) { + + object.matrixWorld.flattenToArray( object._modelMatrixArray ); + + } + */ + + object._modelViewMatrix.multiply( camera.matrixWorldInverse, object.matrixWorld); + + _renderer.renderImmediateObject( camera, scene.__lights, fog, _depthMaterial, object ); + + } + + } + + // restore GL state + + var clearColor = _renderer.getClearColor(), + clearAlpha = _renderer.getClearAlpha(); + + _gl.clearColor( clearColor.r, clearColor.g, clearColor.b, clearAlpha ); + _gl.enable( _gl.BLEND ); + + }; + +}; + +/** + * @author mikael emtinger / http://gomo.se/ + * + */ + +THREE.ShaderFlares = { + + 'lensFlareVertexTexture': { + + vertexShader: [ + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + "uniform int renderType;", + + "uniform sampler2D occlusionMap;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 ) ) +", + "texture2D( occlusionMap, vec2( 0.5, 0.1 ) ) +", + "texture2D( occlusionMap, vec2( 0.9, 0.1 ) ) +", + "texture2D( occlusionMap, vec2( 0.9, 0.5 ) ) +", + "texture2D( occlusionMap, vec2( 0.9, 0.9 ) ) +", + "texture2D( occlusionMap, vec2( 0.5, 0.9 ) ) +", + "texture2D( occlusionMap, vec2( 0.1, 0.9 ) ) +", + "texture2D( occlusionMap, vec2( 0.1, 0.5 ) ) +", + "texture2D( occlusionMap, vec2( 0.5, 0.5 ) );", + + "vVisibility = ( visibility.r / 9.0 ) *", + "( 1.0 - visibility.g / 9.0 ) *", + "( visibility.b / 9.0 ) *", + "( 1.0 - visibility.a / 9.0 );", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "precision mediump float;", + + "uniform sampler2D map;", + "uniform float opacity;", + "uniform int renderType;", + "uniform vec3 color;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main() {", + + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * vVisibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + ].join( "\n" ) + + }, + + + 'lensFlare': { + + vertexShader: [ + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + "uniform int renderType;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main() {", + + "vUV = uv;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + + "}", + + "gl_Position = vec4( ( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "precision mediump float;", + + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform int renderType;", + "uniform vec3 color;", + + "varying vec2 vUV;", + + "void main() {", + + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 ) ).a +", + "texture2D( occlusionMap, vec2( 0.9, 0.5 ) ).a +", + "texture2D( occlusionMap, vec2( 0.5, 0.9 ) ).a +", + "texture2D( occlusionMap, vec2( 0.1, 0.5 ) ).a;", + + "visibility = ( 1.0 - visibility / 4.0 );", + + "vec4 texture = texture2D( map, vUV );", + "texture.a *= opacity * visibility;", + "gl_FragColor = texture;", + "gl_FragColor.rgb *= color;", + + "}", + + "}" + + ].join( "\n" ) + + } + +}; +/** + * @author mikael emtinger / http://gomo.se/ + * + */ + +THREE.ShaderSprite = { + + 'sprite': { + + vertexShader: [ + + "uniform int useScreenCoordinates;", + "uniform int affectedByDistance;", + "uniform vec3 screenPosition;", + "uniform mat4 modelViewMatrix;", + "uniform mat4 projectionMatrix;", + "uniform float rotation;", + "uniform vec2 scale;", + "uniform vec2 alignment;", + "uniform vec2 uvOffset;", + "uniform vec2 uvScale;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main() {", + + "vUV = uvOffset + uv * uvScale;", + + "vec2 alignedPosition = position + alignment;", + + "vec2 rotatedPosition;", + "rotatedPosition.x = ( cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y ) * scale.x;", + "rotatedPosition.y = ( sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y ) * scale.y;", + + "vec4 finalPosition;", + + "if( useScreenCoordinates != 0 ) {", + + "finalPosition = vec4( screenPosition.xy + rotatedPosition, screenPosition.z, 1.0 );", + + "} else {", + + "finalPosition = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );", + "finalPosition.xy += rotatedPosition * ( affectedByDistance == 1 ? 1.0 : finalPosition.z );", + + "}", + + "gl_Position = finalPosition;", + + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "precision mediump float;", + + "uniform vec3 color;", + "uniform sampler2D map;", + "uniform float opacity;", + + "varying vec2 vUV;", + + "void main() {", + + "vec4 texture = texture2D( map, vUV );", + "gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );", + + "}" + + ].join( "\n" ) + + } + +}; diff --git a/Battery3D/js/tween.js b/Battery3D/js/tween.js new file mode 100644 index 0000000..09bd92f --- /dev/null +++ b/Battery3D/js/tween.js @@ -0,0 +1,645 @@ +/** + * @author sole / http://soledadpenades.com + * @author mrdoob / http://mrdoob.com + * @author Robert Eisele / http://www.xarg.org + * @author Philippe / http://philippe.elsass.me + * @author Robert Penner / http://www.robertpenner.com/easing_terms_of_use.html + * @author Paul Lewis / http://www.aerotwist.com/ + * @author lechecacharro + * @author Josh Faul / http://jocafa.com/ + * @author egraether / http://egraether.com/ + */ + +var TWEEN = TWEEN || ( function () { + + var _tweens = []; + + return { + + REVISION: '7', + + getAll: function () { + + return _tweens; + + }, + + removeAll: function () { + + _tweens = []; + + }, + + add: function ( tween ) { + + _tweens.push( tween ); + + }, + + remove: function ( tween ) { + + var i = _tweens.indexOf( tween ); + + if ( i !== -1 ) { + + _tweens.splice( i, 1 ); + + } + + }, + + update: function ( time ) { + + if ( _tweens.length === 0 ) return false; + + var i = 0, l = _tweens.length; + + time = time !== undefined ? time : Date.now(); + + while ( i < l ) { + + if ( _tweens[ i ].update( time ) ) { + + i ++; + + } else { + + _tweens.splice( i, 1 ); + + l --; + + } + + } + + return true; + + } + + }; + +} )(); + +TWEEN.Tween = function ( object ) { + + var _object = object; + var _valuesStart = {}; + var _valuesEnd = {}; + var _duration = 1000; + var _delayTime = 0; + var _startTime = null; + var _easingFunction = TWEEN.Easing.Linear.None; + var _interpolationFunction = TWEEN.Interpolation.Linear; + var _chainedTweens = []; + var _onStartCallback = null; + var _onStartCallbackFired = false; + var _onUpdateCallback = null; + var _onCompleteCallback = null; + + this.to = function ( properties, duration ) { + + if ( duration !== undefined ) { + + _duration = duration; + + } + + _valuesEnd = properties; + + return this; + + }; + + this.start = function ( time ) { + + TWEEN.add( this ); + + _onStartCallbackFired = false; + + _startTime = time !== undefined ? time : Date.now(); + _startTime += _delayTime; + + for ( var property in _valuesEnd ) { + + // This prevents the engine from interpolating null values + if ( _object[ property ] === null ) { + + continue; + + } + + // check if an Array was provided as property value + if ( _valuesEnd[ property ] instanceof Array ) { + + if ( _valuesEnd[ property ].length === 0 ) { + + continue; + + } + + // create a local copy of the Array with the start value at the front + _valuesEnd[ property ] = [ _object[ property ] ].concat( _valuesEnd[ property ] ); + + } + + _valuesStart[ property ] = _object[ property ]; + + } + + return this; + + }; + + this.stop = function () { + + TWEEN.remove( this ); + return this; + + }; + + this.delay = function ( amount ) { + + _delayTime = amount; + return this; + + }; + + this.easing = function ( easing ) { + + _easingFunction = easing; + return this; + + }; + + this.interpolation = function ( interpolation ) { + + _interpolationFunction = interpolation; + return this; + + }; + + this.chain = function () { + + _chainedTweens = arguments; + return this; + + }; + + this.onStart = function ( callback ) { + + _onStartCallback = callback; + return this; + + }; + + this.onUpdate = function ( callback ) { + + _onUpdateCallback = callback; + return this; + + }; + + this.onComplete = function ( callback ) { + + _onCompleteCallback = callback; + return this; + + }; + + this.update = function ( time ) { + + if ( time < _startTime ) { + + return true; + + } + + if ( _onStartCallbackFired === false ) { + + if ( _onStartCallback !== null ) { + + _onStartCallback.call( _object ); + + } + + _onStartCallbackFired = true; + + } + + var elapsed = ( time - _startTime ) / _duration; + elapsed = elapsed > 1 ? 1 : elapsed; + + var value = _easingFunction( elapsed ); + + for ( var property in _valuesStart ) { + + var start = _valuesStart[ property ]; + var end = _valuesEnd[ property ]; + + if ( end instanceof Array ) { + + _object[ property ] = _interpolationFunction( end, value ); + + } else { + + _object[ property ] = start + ( end - start ) * value; + + } + + } + + if ( _onUpdateCallback !== null ) { + + _onUpdateCallback.call( _object, value ); + + } + + if ( elapsed == 1 ) { + + if ( _onCompleteCallback !== null ) { + + _onCompleteCallback.call( _object ); + + } + + for ( var i = 0, l = _chainedTweens.length; i < l; i ++ ) { + + _chainedTweens[ i ].start( time ); + + } + + return false; + + } + + return true; + + }; + +}; + +TWEEN.Easing = { + + Linear: { + + None: function ( k ) { + + return k; + + } + + }, + + Quadratic: { + + In: function ( k ) { + + return k * k; + + }, + + Out: function ( k ) { + + return k * ( 2 - k ); + + }, + + InOut: function ( k ) { + + if ( ( k *= 2 ) < 1 ) return 0.5 * k * k; + return - 0.5 * ( --k * ( k - 2 ) - 1 ); + + } + + }, + + Cubic: { + + In: function ( k ) { + + return k * k * k; + + }, + + Out: function ( k ) { + + return --k * k * k + 1; + + }, + + InOut: function ( k ) { + + if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k; + return 0.5 * ( ( k -= 2 ) * k * k + 2 ); + + } + + }, + + Quartic: { + + In: function ( k ) { + + return k * k * k * k; + + }, + + Out: function ( k ) { + + return 1 - ( --k * k * k * k ); + + }, + + InOut: function ( k ) { + + if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k; + return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 ); + + } + + }, + + Quintic: { + + In: function ( k ) { + + return k * k * k * k * k; + + }, + + Out: function ( k ) { + + return --k * k * k * k * k + 1; + + }, + + InOut: function ( k ) { + + if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k; + return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 ); + + } + + }, + + Sinusoidal: { + + In: function ( k ) { + + return 1 - Math.cos( k * Math.PI / 2 ); + + }, + + Out: function ( k ) { + + return Math.sin( k * Math.PI / 2 ); + + }, + + InOut: function ( k ) { + + return 0.5 * ( 1 - Math.cos( Math.PI * k ) ); + + } + + }, + + Exponential: { + + In: function ( k ) { + + return k === 0 ? 0 : Math.pow( 1024, k - 1 ); + + }, + + Out: function ( k ) { + + return k === 1 ? 1 : 1 - Math.pow( 2, - 10 * k ); + + }, + + InOut: function ( k ) { + + if ( k === 0 ) return 0; + if ( k === 1 ) return 1; + if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 1024, k - 1 ); + return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 ); + + } + + }, + + Circular: { + + In: function ( k ) { + + return 1 - Math.sqrt( 1 - k * k ); + + }, + + Out: function ( k ) { + + return Math.sqrt( 1 - ( --k * k ) ); + + }, + + InOut: function ( k ) { + + if ( ( k *= 2 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1); + return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1); + + } + + }, + + Elastic: { + + In: function ( k ) { + + var s, a = 0.1, p = 0.4; + if ( k === 0 ) return 0; + if ( k === 1 ) return 1; + if ( !a || a < 1 ) { a = 1; s = p / 4; } + else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); + return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); + + }, + + Out: function ( k ) { + + var s, a = 0.1, p = 0.4; + if ( k === 0 ) return 0; + if ( k === 1 ) return 1; + if ( !a || a < 1 ) { a = 1; s = p / 4; } + else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); + return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 ); + + }, + + InOut: function ( k ) { + + var s, a = 0.1, p = 0.4; + if ( k === 0 ) return 0; + if ( k === 1 ) return 1; + if ( !a || a < 1 ) { a = 1; s = p / 4; } + else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); + if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); + return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1; + + } + + }, + + Back: { + + In: function ( k ) { + + var s = 1.70158; + return k * k * ( ( s + 1 ) * k - s ); + + }, + + Out: function ( k ) { + + var s = 1.70158; + return --k * k * ( ( s + 1 ) * k + s ) + 1; + + }, + + InOut: function ( k ) { + + var s = 1.70158 * 1.525; + if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) ); + return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 ); + + } + + }, + + Bounce: { + + In: function ( k ) { + + return 1 - TWEEN.Easing.Bounce.Out( 1 - k ); + + }, + + Out: function ( k ) { + + if ( k < ( 1 / 2.75 ) ) { + + return 7.5625 * k * k; + + } else if ( k < ( 2 / 2.75 ) ) { + + return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75; + + } else if ( k < ( 2.5 / 2.75 ) ) { + + return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375; + + } else { + + return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375; + + } + + }, + + InOut: function ( k ) { + + if ( k < 0.5 ) return TWEEN.Easing.Bounce.In( k * 2 ) * 0.5; + return TWEEN.Easing.Bounce.Out( k * 2 - 1 ) * 0.5 + 0.5; + + } + + } + +}; + +TWEEN.Interpolation = { + + Linear: function ( v, k ) { + + var m = v.length - 1, f = m * k, i = Math.floor( f ), fn = TWEEN.Interpolation.Utils.Linear; + + if ( k < 0 ) return fn( v[ 0 ], v[ 1 ], f ); + if ( k > 1 ) return fn( v[ m ], v[ m - 1 ], m - f ); + + return fn( v[ i ], v[ i + 1 > m ? m : i + 1 ], f - i ); + + }, + + Bezier: function ( v, k ) { + + var b = 0, n = v.length - 1, pw = Math.pow, bn = TWEEN.Interpolation.Utils.Bernstein, i; + + for ( i = 0; i <= n; i++ ) { + b += pw( 1 - k, n - i ) * pw( k, i ) * v[ i ] * bn( n, i ); + } + + return b; + + }, + + CatmullRom: function ( v, k ) { + + var m = v.length - 1, f = m * k, i = Math.floor( f ), fn = TWEEN.Interpolation.Utils.CatmullRom; + + if ( v[ 0 ] === v[ m ] ) { + + if ( k < 0 ) i = Math.floor( f = m * ( 1 + k ) ); + + return fn( v[ ( i - 1 + m ) % m ], v[ i ], v[ ( i + 1 ) % m ], v[ ( i + 2 ) % m ], f - i ); + + } else { + + if ( k < 0 ) return v[ 0 ] - ( fn( v[ 0 ], v[ 0 ], v[ 1 ], v[ 1 ], -f ) - v[ 0 ] ); + if ( k > 1 ) return v[ m ] - ( fn( v[ m ], v[ m ], v[ m - 1 ], v[ m - 1 ], f - m ) - v[ m ] ); + + return fn( v[ i ? i - 1 : 0 ], v[ i ], v[ m < i + 1 ? m : i + 1 ], v[ m < i + 2 ? m : i + 2 ], f - i ); + + } + + }, + + Utils: { + + Linear: function ( p0, p1, t ) { + + return ( p1 - p0 ) * t + p0; + + }, + + Bernstein: function ( n , i ) { + + var fc = TWEEN.Interpolation.Utils.Factorial; + return fc( n ) / fc( i ) / fc( n - i ); + + }, + + Factorial: ( function () { + + var a = [ 1 ]; + + return function ( n ) { + + var s = 1, i; + if ( a[ n ] ) return a[ n ]; + for ( i = n; i > 1; i-- ) s *= i; + return a[ n ] = s; + + }; + + } )(), + + CatmullRom: function ( p0, p1, p2, p3, t ) { + + var v0 = ( p2 - p0 ) * 0.5, v1 = ( p3 - p1 ) * 0.5, t2 = t * t, t3 = t * t2; + return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + } + + } + +}; diff --git a/core/thumbs/013.png b/core/thumbs/013.png new file mode 100644 index 0000000000000000000000000000000000000000..d6fb276be415f7ed8862b152f1e6c063aef0335a GIT binary patch literal 9269 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 literal 0 HcmV?d00001 diff --git a/index.html b/index.html index d199fd1..74df998 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,7 @@ + @@ -27,6 +28,27 @@

      UX LAB