├── .gitmodules ├── append.mtl ├── append.obj ├── body00_MikuAp.tga ├── body01_MikuAp.tga ├── body02_MikuAp.tga ├── eyeM2.bmp ├── face_MikuAp.tga ├── gl-matrix-min.js ├── hair_MikuAp.tga ├── imgloader.js ├── index.html ├── miku.js ├── miku.mtl ├── miku.obj ├── objparser.js ├── tga.js └── wing_MikuAp.tga /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gl-matrix"] 2 | path = gl-matrix 3 | url = git@github.com:toji/gl-matrix.git 4 | [submodule "jsTGALoader"] 5 | path = jsTGALoader 6 | url = git@github.com:vthibault/jsTGALoader.git 7 | -------------------------------------------------------------------------------- /append.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 17 3 | 4 | newmtl body00 5 | Ns 96.078431 6 | Ka 0.000000 0.000000 0.000000 7 | Kd 0.800000 0.800000 0.800000 8 | Ks 0.000000 0.000000 0.000000 9 | Ni 1.000000 10 | d 1.000000 11 | illum 2 12 | map_Kd body00_MikuAp.tga 13 | map_d body00_MikuAp.tga 14 | 15 | newmtl body01 16 | Ns 96.078431 17 | Ka 0.000000 0.000000 0.000000 18 | Kd 0.800000 0.800000 0.800000 19 | Ks 0.000000 0.000000 0.000000 20 | Ni 1.000000 21 | d 1.000000 22 | illum 2 23 | map_Kd body01_MikuAp.tga 24 | map_d body01_MikuAp.tga 25 | 26 | newmtl body02 27 | Ns 96.078431 28 | Ka 0.000000 0.000000 0.000000 29 | Kd 0.800000 0.800000 0.800000 30 | Ks 0.000000 0.000000 0.000000 31 | Ni 1.000000 32 | d 1.000000 33 | illum 2 34 | map_Kd body02_MikuAp.tga 35 | map_d body02_MikuAp.tga 36 | 37 | newmtl body_green 38 | Ns 198.039216 39 | Ka 0.000000 0.000000 0.000000 40 | Kd 0.320000 0.720000 0.480000 41 | Ks 0.000000 0.000000 0.000000 42 | Ni 1.000000 43 | d 1.000000 44 | illum 2 45 | map_Kd body01_MikuAp.tga 46 | map_d body01_MikuAp.tga 47 | 48 | newmtl body_green2 49 | Ns 198.039216 50 | Ka 0.000000 0.000000 0.000000 51 | Kd 0.320000 0.720000 0.480000 52 | Ks 0.000000 0.000000 0.000000 53 | Ni 1.000000 54 | d 1.000000 55 | illum 2 56 | map_Kd body02_MikuAp.tga 57 | map_d body02_MikuAp.tga 58 | 59 | newmtl body_pink 60 | Ns 223.529412 61 | Ka 0.000000 0.000000 0.000000 62 | Kd 0.560000 0.144000 0.344000 63 | Ks 0.000000 0.000000 0.000000 64 | Ni 1.000000 65 | d 1.000000 66 | illum 2 67 | map_Kd body01_MikuAp.tga 68 | map_d body01_MikuAp.tga 69 | 70 | newmtl cheek 71 | Ns 96.078431 72 | Ka 0.000000 0.000000 0.000000 73 | Kd 0.800000 0.800000 0.800000 74 | Ks 0.000000 0.000000 0.000000 75 | Ni 1.000000 76 | d 1.000000 77 | illum 2 78 | map_Kd face_MikuAp.tga 79 | map_d face_MikuAp.tga 80 | 81 | newmtl eye_hi 82 | Ns 203.921569 83 | Ka 0.000000 0.000000 0.000000 84 | Kd 0.320000 0.640000 0.560000 85 | Ks 0.000000 0.000000 0.000000 86 | Ni 1.000000 87 | d 1.000000 88 | illum 2 89 | map_Kd face_MikuAp.tga 90 | map_d face_MikuAp.tga 91 | 92 | newmtl eye_hi2 93 | Ns 198.039216 94 | Ka 0.000000 0.000000 0.000000 95 | Kd 0.320000 0.640000 0.560000 96 | Ks 0.000000 0.000000 0.000000 97 | Ni 1.000000 98 | d 1.000000 99 | illum 2 100 | map_Kd face_MikuAp.tga 101 | map_d face_MikuAp.tga 102 | 103 | newmtl face00 104 | Ns 96.078431 105 | Ka 0.000000 0.000000 0.000000 106 | Kd 0.800000 0.800000 0.800000 107 | Ks 0.000000 0.000000 0.000000 108 | Ni 1.000000 109 | d 1.000000 110 | illum 2 111 | map_Kd face_MikuAp.tga 112 | map_d face_MikuAp.tga 113 | 114 | newmtl face01 115 | Ns 96.078431 116 | Ka 0.000000 0.000000 0.000000 117 | Kd 0.800000 0.800000 0.800000 118 | Ks 0.000000 0.000000 0.000000 119 | Ni 1.000000 120 | d 1.000000 121 | illum 2 122 | map_Kd face_MikuAp.tga 123 | map_d face_MikuAp.tga 124 | 125 | newmtl hair00 126 | Ns 96.078431 127 | Ka 0.000000 0.000000 0.000000 128 | Kd 0.800000 0.800000 0.800000 129 | Ks 0.000000 0.000000 0.000000 130 | Ni 1.000000 131 | d 1.000000 132 | illum 2 133 | map_Kd hair_MikuAp.tga 134 | map_d hair_MikuAp.tga 135 | 136 | newmtl hair01 137 | Ns 96.078431 138 | Ka 0.000000 0.000000 0.000000 139 | Kd 0.800000 0.800000 0.800000 140 | Ks 0.000000 0.000000 0.000000 141 | Ni 1.000000 142 | d 1.000000 143 | illum 2 144 | map_Kd hair_MikuAp.tga 145 | map_d hair_MikuAp.tga 146 | 147 | newmtl hairshadow 148 | Ns 96.078431 149 | Ka 0.000000 0.000000 0.000000 150 | Kd 0.800000 0.800000 0.800000 151 | Ks 0.000000 0.000000 0.000000 152 | Ni 1.000000 153 | d 1.000000 154 | illum 2 155 | map_Kd face_MikuAp.tga 156 | map_d face_MikuAp.tga 157 | 158 | newmtl lens 159 | Ns 96.078431 160 | Ka 0.000000 0.000000 0.000000 161 | Kd 0.800000 0.800000 0.800000 162 | Ks 0.000000 0.000000 0.000000 163 | Ni 1.000000 164 | d 1.000000 165 | illum 2 166 | map_Kd body02_MikuAp.tga 167 | map_d body02_MikuAp.tga 168 | 169 | newmtl skin 170 | Ns 96.078431 171 | Ka 0.000000 0.000000 0.000000 172 | Kd 0.800000 0.800000 0.800000 173 | Ks 0.000000 0.000000 0.000000 174 | Ni 1.000000 175 | d 1.000000 176 | illum 2 177 | map_Kd body00_MikuAp.tga 178 | map_d body00_MikuAp.tga 179 | 180 | newmtl wing 181 | Ns 200.000000 182 | Ka 0.000000 0.000000 0.000000 183 | Kd 0.320000 0.720000 0.480000 184 | Ks 0.000000 0.000000 0.000000 185 | Ni 1.000000 186 | d 1.000000 187 | illum 2 188 | map_Kd wing_MikuAp.tga 189 | map_d wing_MikuAp.tga 190 | -------------------------------------------------------------------------------- /body00_MikuAp.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkihira/webgl-miku-sample/f872808f2f9d5a2a80939cf01a6ac22103a43abd/body00_MikuAp.tga -------------------------------------------------------------------------------- /body01_MikuAp.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkihira/webgl-miku-sample/f872808f2f9d5a2a80939cf01a6ac22103a43abd/body01_MikuAp.tga -------------------------------------------------------------------------------- /body02_MikuAp.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkihira/webgl-miku-sample/f872808f2f9d5a2a80939cf01a6ac22103a43abd/body02_MikuAp.tga -------------------------------------------------------------------------------- /eyeM2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkihira/webgl-miku-sample/f872808f2f9d5a2a80939cf01a6ac22103a43abd/eyeM2.bmp -------------------------------------------------------------------------------- /face_MikuAp.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkihira/webgl-miku-sample/f872808f2f9d5a2a80939cf01a6ac22103a43abd/face_MikuAp.tga -------------------------------------------------------------------------------- /gl-matrix-min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview gl-matrix - High performance matrix and vector operations 3 | * @author Brandon Jones 4 | * @author Colin MacKenzie IV 5 | * @version 2.2.1 6 | */ 7 | /* Copyright (c) 2013, Brandon Jones, Colin MacKenzie IV. All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | * Redistributions in binary form must reproduce the above copyright notice, 15 | this list of conditions and the following disclaimer in the documentation 16 | and/or other materials provided with the distribution. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 28 | (function(e){"use strict";var t={};typeof exports=="undefined"?typeof define=="function"&&typeof define.amd=="object"&&define.amd?(t.exports={},define(function(){return t.exports})):t.exports=typeof window!="undefined"?window:e:t.exports=exports,function(e){if(!t)var t=1e-6;if(!n)var n=typeof Float32Array!="undefined"?Float32Array:Array;if(!r)var r=Math.random;var i={};i.setMatrixArrayType=function(e){n=e},typeof e!="undefined"&&(e.glMatrix=i);var s=Math.PI/180;i.toRadian=function(e){return e*s};var o={};o.create=function(){var e=new n(2);return e[0]=0,e[1]=0,e},o.clone=function(e){var t=new n(2);return t[0]=e[0],t[1]=e[1],t},o.fromValues=function(e,t){var r=new n(2);return r[0]=e,r[1]=t,r},o.copy=function(e,t){return e[0]=t[0],e[1]=t[1],e},o.set=function(e,t,n){return e[0]=t,e[1]=n,e},o.add=function(e,t,n){return e[0]=t[0]+n[0],e[1]=t[1]+n[1],e},o.subtract=function(e,t,n){return e[0]=t[0]-n[0],e[1]=t[1]-n[1],e},o.sub=o.subtract,o.multiply=function(e,t,n){return e[0]=t[0]*n[0],e[1]=t[1]*n[1],e},o.mul=o.multiply,o.divide=function(e,t,n){return e[0]=t[0]/n[0],e[1]=t[1]/n[1],e},o.div=o.divide,o.min=function(e,t,n){return e[0]=Math.min(t[0],n[0]),e[1]=Math.min(t[1],n[1]),e},o.max=function(e,t,n){return e[0]=Math.max(t[0],n[0]),e[1]=Math.max(t[1],n[1]),e},o.scale=function(e,t,n){return e[0]=t[0]*n,e[1]=t[1]*n,e},o.scaleAndAdd=function(e,t,n,r){return e[0]=t[0]+n[0]*r,e[1]=t[1]+n[1]*r,e},o.distance=function(e,t){var n=t[0]-e[0],r=t[1]-e[1];return Math.sqrt(n*n+r*r)},o.dist=o.distance,o.squaredDistance=function(e,t){var n=t[0]-e[0],r=t[1]-e[1];return n*n+r*r},o.sqrDist=o.squaredDistance,o.length=function(e){var t=e[0],n=e[1];return Math.sqrt(t*t+n*n)},o.len=o.length,o.squaredLength=function(e){var t=e[0],n=e[1];return t*t+n*n},o.sqrLen=o.squaredLength,o.negate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e},o.normalize=function(e,t){var n=t[0],r=t[1],i=n*n+r*r;return i>0&&(i=1/Math.sqrt(i),e[0]=t[0]*i,e[1]=t[1]*i),e},o.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]},o.cross=function(e,t,n){var r=t[0]*n[1]-t[1]*n[0];return e[0]=e[1]=0,e[2]=r,e},o.lerp=function(e,t,n,r){var i=t[0],s=t[1];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e},o.random=function(e,t){t=t||1;var n=r()*2*Math.PI;return e[0]=Math.cos(n)*t,e[1]=Math.sin(n)*t,e},o.transformMat2=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[2]*i,e[1]=n[1]*r+n[3]*i,e},o.transformMat2d=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[2]*i+n[4],e[1]=n[1]*r+n[3]*i+n[5],e},o.transformMat3=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[3]*i+n[6],e[1]=n[1]*r+n[4]*i+n[7],e},o.transformMat4=function(e,t,n){var r=t[0],i=t[1];return e[0]=n[0]*r+n[4]*i+n[12],e[1]=n[1]*r+n[5]*i+n[13],e},o.forEach=function(){var e=o.create();return function(t,n,r,i,s,o){var u,a;n||(n=2),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u0&&(s=1/Math.sqrt(s),e[0]=t[0]*s,e[1]=t[1]*s,e[2]=t[2]*s),e},u.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]},u.cross=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2];return e[0]=i*a-s*u,e[1]=s*o-r*a,e[2]=r*u-i*o,e},u.lerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e[2]=o+r*(n[2]-o),e},u.random=function(e,t){t=t||1;var n=r()*2*Math.PI,i=r()*2-1,s=Math.sqrt(1-i*i)*t;return e[0]=Math.cos(n)*s,e[1]=Math.sin(n)*s,e[2]=i*t,e},u.transformMat4=function(e,t,n){var r=t[0],i=t[1],s=t[2];return e[0]=n[0]*r+n[4]*i+n[8]*s+n[12],e[1]=n[1]*r+n[5]*i+n[9]*s+n[13],e[2]=n[2]*r+n[6]*i+n[10]*s+n[14],e},u.transformMat3=function(e,t,n){var r=t[0],i=t[1],s=t[2];return e[0]=r*n[0]+i*n[3]+s*n[6],e[1]=r*n[1]+i*n[4]+s*n[7],e[2]=r*n[2]+i*n[5]+s*n[8],e},u.transformQuat=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2],f=n[3],l=f*r+u*s-a*i,c=f*i+a*r-o*s,h=f*s+o*i-u*r,p=-o*r-u*i-a*s;return e[0]=l*f+p*-o+c*-a-h*-u,e[1]=c*f+p*-u+h*-o-l*-a,e[2]=h*f+p*-a+l*-u-c*-o,e},u.rotateX=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[0],s[1]=i[1]*Math.cos(r)-i[2]*Math.sin(r),s[2]=i[1]*Math.sin(r)+i[2]*Math.cos(r),e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.rotateY=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[2]*Math.sin(r)+i[0]*Math.cos(r),s[1]=i[1],s[2]=i[2]*Math.cos(r)-i[0]*Math.sin(r),e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.rotateZ=function(e,t,n,r){var i=[],s=[];return i[0]=t[0]-n[0],i[1]=t[1]-n[1],i[2]=t[2]-n[2],s[0]=i[0]*Math.cos(r)-i[1]*Math.sin(r),s[1]=i[0]*Math.sin(r)+i[1]*Math.cos(r),s[2]=i[2],e[0]=s[0]+n[0],e[1]=s[1]+n[1],e[2]=s[2]+n[2],e},u.forEach=function(){var e=u.create();return function(t,n,r,i,s,o){var u,a;n||(n=3),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u0&&(o=1/Math.sqrt(o),e[0]=t[0]*o,e[1]=t[1]*o,e[2]=t[2]*o,e[3]=t[3]*o),e},a.dot=function(e,t){return e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3]},a.lerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2],u=t[3];return e[0]=i+r*(n[0]-i),e[1]=s+r*(n[1]-s),e[2]=o+r*(n[2]-o),e[3]=u+r*(n[3]-u),e},a.random=function(e,t){return t=t||1,e[0]=r(),e[1]=r(),e[2]=r(),e[3]=r(),a.normalize(e,e),a.scale(e,e,t),e},a.transformMat4=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=t[3];return e[0]=n[0]*r+n[4]*i+n[8]*s+n[12]*o,e[1]=n[1]*r+n[5]*i+n[9]*s+n[13]*o,e[2]=n[2]*r+n[6]*i+n[10]*s+n[14]*o,e[3]=n[3]*r+n[7]*i+n[11]*s+n[15]*o,e},a.transformQuat=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=n[0],u=n[1],a=n[2],f=n[3],l=f*r+u*s-a*i,c=f*i+a*r-o*s,h=f*s+o*i-u*r,p=-o*r-u*i-a*s;return e[0]=l*f+p*-o+c*-a-h*-u,e[1]=c*f+p*-u+h*-o-l*-a,e[2]=h*f+p*-a+l*-u-c*-o,e},a.forEach=function(){var e=a.create();return function(t,n,r,i,s,o){var u,a;n||(n=4),r||(r=0),i?a=Math.min(i*n+r,t.length):a=t.length;for(u=r;u.999999?(r[0]=0,r[1]=0,r[2]=0,r[3]=1,r):(u.cross(e,i,s),r[0]=e[0],r[1]=e[1],r[2]=e[2],r[3]=1+o,p.normalize(r,r))}}(),p.setAxes=function(){var e=c.create();return function(t,n,r,i){return e[0]=r[0],e[3]=r[1],e[6]=r[2],e[1]=i[0],e[4]=i[1],e[7]=i[2],e[2]=-n[0],e[5]=-n[1],e[8]=-n[2],p.normalize(t,p.fromMat3(t,e))}}(),p.clone=a.clone,p.fromValues=a.fromValues,p.copy=a.copy,p.set=a.set,p.identity=function(e){return e[0]=0,e[1]=0,e[2]=0,e[3]=1,e},p.setAxisAngle=function(e,t,n){n*=.5;var r=Math.sin(n);return e[0]=r*t[0],e[1]=r*t[1],e[2]=r*t[2],e[3]=Math.cos(n),e},p.add=a.add,p.multiply=function(e,t,n){var r=t[0],i=t[1],s=t[2],o=t[3],u=n[0],a=n[1],f=n[2],l=n[3];return e[0]=r*l+o*u+i*f-s*a,e[1]=i*l+o*a+s*u-r*f,e[2]=s*l+o*f+r*a-i*u,e[3]=o*l-r*u-i*a-s*f,e},p.mul=p.multiply,p.scale=a.scale,p.rotateX=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a+o*u,e[1]=i*a+s*u,e[2]=s*a-i*u,e[3]=o*a-r*u,e},p.rotateY=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a-s*u,e[1]=i*a+o*u,e[2]=s*a+r*u,e[3]=o*a-i*u,e},p.rotateZ=function(e,t,n){n*=.5;var r=t[0],i=t[1],s=t[2],o=t[3],u=Math.sin(n),a=Math.cos(n);return e[0]=r*a+i*u,e[1]=i*a-r*u,e[2]=s*a+o*u,e[3]=o*a-s*u,e},p.calculateW=function(e,t){var n=t[0],r=t[1],i=t[2];return e[0]=n,e[1]=r,e[2]=i,e[3]=-Math.sqrt(Math.abs(1-n*n-r*r-i*i)),e},p.dot=a.dot,p.lerp=a.lerp,p.slerp=function(e,t,n,r){var i=t[0],s=t[1],o=t[2],u=t[3],a=n[0],f=n[1],l=n[2],c=n[3],h,p,d,v,m;return p=i*a+s*f+o*l+u*c,p<0&&(p=-p,a=-a,f=-f,l=-l,c=-c),1-p>1e-6?(h=Math.acos(p),d=Math.sin(h),v=Math.sin((1-r)*h)/d,m=Math.sin(r*h)/d):(v=1-r,m=r),e[0]=v*i+m*a,e[1]=v*s+m*f,e[2]=v*o+m*l,e[3]=v*u+m*c,e},p.invert=function(e,t){var n=t[0],r=t[1],i=t[2],s=t[3],o=n*n+r*r+i*i+s*s,u=o?1/o:0;return e[0]=-n*u,e[1]=-r*u,e[2]=-i*u,e[3]=s*u,e},p.conjugate=function(e,t){return e[0]=-t[0],e[1]=-t[1],e[2]=-t[2],e[3]=t[3],e},p.length=a.length,p.len=p.length,p.squaredLength=a.squaredLength,p.sqrLen=p.squaredLength,p.normalize=a.normalize,p.fromMat3=function(e,t){var n=t[0]+t[4]+t[8],r;if(n>0)r=Math.sqrt(n+1),e[3]=.5*r,r=.5/r,e[0]=(t[7]-t[5])*r,e[1]=(t[2]-t[6])*r,e[2]=(t[3]-t[1])*r;else{var i=0;t[4]>t[0]&&(i=1),t[8]>t[i*3+i]&&(i=2);var s=(i+1)%3,o=(i+2)%3;r=Math.sqrt(t[i*3+i]-t[s*3+s]-t[o*3+o]+1),e[i]=.5*r,r=.5/r,e[3]=(t[o*3+s]-t[s*3+o])*r,e[s]=(t[s*3+i]+t[i*3+s])*r,e[o]=(t[o*3+i]+t[i*3+o])*r}return e},p.str=function(e){return"quat("+e[0]+", "+e[1]+", "+e[2]+", "+e[3]+")"},typeof e!="undefined"&&(e.quat=p)}(t.exports)})(this); 29 | -------------------------------------------------------------------------------- /hair_MikuAp.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkihira/webgl-miku-sample/f872808f2f9d5a2a80939cf01a6ac22103a43abd/hair_MikuAp.tga -------------------------------------------------------------------------------- /imgloader.js: -------------------------------------------------------------------------------- 1 | ;var imgLoader = {}; 2 | (function() { 3 | var loadingCount = 0; 4 | imgLoader.images = {}; 5 | // ロードした画像を2のべき乗サイズに揃える 6 | var createTexture = function(name, img, func) { 7 | var size = 1; 8 | while(img.width > size || img.height > size) { 9 | size *= 2; 10 | } 11 | // canvas 2Dを作成し、そのサイズに拡大縮小する 12 | var canvas = document.createElement("canvas"); 13 | canvas.width = canvas.height = size; 14 | canvas.getContext("2d").drawImage(img, 0, 0, img.width, img.height, 0, 0, size, size); 15 | // canvasを保存してcallbackを呼び出す 16 | loadingCount--; 17 | imgLoader.images[name] = canvas; 18 | func(canvas); 19 | }; 20 | // 画像をロードする 21 | imgLoader.load = function(url, func) { 22 | // 既にあれば何もしない 23 | if(imgLoader.images[url]) { 24 | return; 25 | } 26 | // 読み込もうとしているフラグを設定 27 | imgLoader.images[url] = true; 28 | loadingCount++; 29 | // 拡張子で判断 30 | var ext = url.split(".").pop(); 31 | switch(ext.toLowerCase()) { 32 | default: 33 | // ブラウザ対応の画像の場合はDOMで生成 34 | var img = document.createElement("img"); 35 | img.onload = function() { 36 | createTexture(url, img, func); 37 | }; 38 | img.src = url; 39 | break; 40 | case "tga": 41 | // TGAの場合はCanvasで生成 42 | var tga = new TGA(); 43 | tga.open(url, function() { 44 | createTexture(url, tga.getCanvas(), func); 45 | }); 46 | break; 47 | } 48 | }; 49 | imgLoader.isLoading = function() { 50 | return loadingCount != 0; 51 | }; 52 | })(); 53 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3d miku 3 | 4 | 5 | 6 | 7 | 8 | 32 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /miku.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | var config = { 3 | objName: "append.obj", 4 | mtlName: "append.mtl" 5 | }; 6 | // XHRを使ってOBJファイルを取得する 7 | var fileCount = 0; 8 | window.onload = function() { 9 | var callback = function() { 10 | fileCount--; 11 | if(fileCount == 0) { 12 | // 全ファイルがロードされたら初期化 13 | initialize(); 14 | } 15 | }; 16 | loadFile(config.objName, "obj", callback); 17 | loadFile(config.mtlName, "mtl", callback); 18 | }; 19 | var files = {}; 20 | var loadFile = function(url, name, callback) { 21 | fileCount++; 22 | var xhr = new XMLHttpRequest(); 23 | xhr.onreadystatechange = function() { 24 | if(xhr.readyState == 4) { 25 | files[name] = xhr.responseText; 26 | callback(); 27 | } 28 | }; 29 | xhr.open("GET", url, true); 30 | xhr.send(""); 31 | }; 32 | 33 | var gl; // WebGLのcontext 34 | var prog; // コンパイル・リンクされたプログラム 35 | var glObj; // WebGL用に変換されたモデルデータ 36 | var initialize = function() { 37 | // OBJファイル、MTLファイルをパース 38 | var obj = objParser.objParse(files.obj); 39 | var mtl = objParser.mtlParse(files.mtl); 40 | // パースしたデータを元にWebGL用のObjectを作成する 41 | objParser.createGLObject(obj, mtl, function(ret) { 42 | // 全てのテクスチャがロードされたら呼ばれる 43 | glObj = ret; 44 | 45 | // WebGLのcontextを取得 46 | var canvas = document.getElementById("canvas"); 47 | gl = canvas.getContext("experimental-webgl") || canvas.getContext("webgl"); 48 | if(!gl) { 49 | document.write("This browser does not support webgl"); 50 | return; 51 | } 52 | 53 | // Vertex Shaderをコンパイル 54 | var vs = gl.createShader(gl.VERTEX_SHADER); 55 | gl.shaderSource(vs, document.getElementById("vs").text); 56 | gl.compileShader(vs); 57 | if(!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) { 58 | console.log("vertex shader compile error"); 59 | console.log(gl.getShaderInfoLog(vs)); 60 | return; 61 | } 62 | 63 | // Fragment Shaderをコンパイル 64 | var fs = gl.createShader(gl.FRAGMENT_SHADER); 65 | gl.shaderSource(fs, document.getElementById("fs").text); 66 | gl.compileShader(fs); 67 | if(!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) { 68 | console.log("fragment shader compile error"); 69 | console.log(gl.getShaderInfoLog(fs)); 70 | return; 71 | } 72 | 73 | // Shaderをリンク 74 | prog = gl.createProgram(); 75 | gl.attachShader(prog, vs); 76 | gl.attachShader(prog, fs); 77 | gl.linkProgram(prog); 78 | if(!gl.getProgramParameter(prog, gl.LINK_STATUS)) { 79 | console.log("link error"); 80 | console.log(gl.getShaderInfoLog(fs)); 81 | return; 82 | } 83 | // リンクしたプログラムの使用を指示 84 | gl.useProgram(prog); 85 | loadBuffer(); 86 | drawFrame(); 87 | }); 88 | }; 89 | var vbuf; // 頂点座標バッファ 90 | var nbuf; // 法線ベクトルバッファ 91 | var vtbuf; // テクスチャ頂点バッファ 92 | var textures = {}; // テクスチャ保存用 93 | var loadBuffer = function() { 94 | // 頂点座標に関し、バッファを生成してデータを指定 95 | vbuf = gl.createBuffer(); 96 | gl.bindBuffer(gl.ARRAY_BUFFER, vbuf); 97 | gl.bufferData(gl.ARRAY_BUFFER, glObj.vertices, gl.STATIC_DRAW); 98 | // 法線ベクトルに関しても同上。配列長は頂点数3×軸3=9個 99 | nbuf = gl.createBuffer(); 100 | gl.bindBuffer(gl.ARRAY_BUFFER, nbuf); 101 | gl.bufferData(gl.ARRAY_BUFFER, glObj.normals, gl.STATIC_DRAW); 102 | // テクスチャ頂点は、配列長が短いので注意 103 | vtbuf = gl.createBuffer(); 104 | gl.bindBuffer(gl.ARRAY_BUFFER, vtbuf); 105 | gl.bufferData(gl.ARRAY_BUFFER, glObj.texcoords, gl.STATIC_DRAW); 106 | 107 | // 必要なテクスチャを用意する 108 | for(var name in imgLoader.images) { 109 | var img = imgLoader.images[name]; 110 | var tex = gl.createTexture(); 111 | gl.bindTexture(gl.TEXTURE_2D, tex); // これから操作するテクスチャを指定する 112 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // WebGLで画像は上下反転されるので、それを防ぐ 113 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img); 114 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 115 | textures[name] = tex; 116 | } 117 | }; 118 | var frame = 0; 119 | var drawFrame = function() { 120 | frame++; 121 | // frustum行列の生成 122 | var proj_mat = mat4.create(); 123 | mat4.frustum(proj_mat, -1, 1, -1, 1, 3, 10); 124 | // 移動回転行列の生成 125 | var mv_mat = mat4.create(); 126 | mat4.translate(mv_mat, mv_mat, [0, -2, -7]); 127 | mat4.rotate(mv_mat, mv_mat, frame * 0.01, [0, 1, 0]); // 軸[0, 1, 0]で回転 128 | // uniformでShaderに送信。4fvはfloat4つの配列(vector)という意味 129 | gl.uniformMatrix4fv(gl.getUniformLocation(prog, "projectionMatrix"), false, proj_mat); 130 | gl.uniformMatrix4fv(gl.getUniformLocation(prog, "modelviewMatrix"), false, mv_mat); 131 | // Canvasの内容をクリア(色は RGB(0, 0, 0, 1): 黒) 132 | gl.clearColor(0, 0, 0, 1); 133 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 134 | gl.enable(gl.DEPTH_TEST); 135 | // ブレンドモードを設定 136 | gl.enable(gl.BLEND); 137 | gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); 138 | 139 | // attributeのindex(position)を取得 140 | var vpos = gl.getAttribLocation(prog, "vertex"); 141 | var npos = gl.getAttribLocation(prog, "normal"); 142 | var vtpos = gl.getAttribLocation(prog, "texcoord"); 143 | 144 | // 取得したattribute positionにバッファを送信 145 | gl.bindBuffer(gl.ARRAY_BUFFER, vbuf); // 頂点座標 146 | gl.vertexAttribPointer(vpos, 3, gl.FLOAT, false, 0, 0); // float型を1頂点につき3つ、と指定 147 | gl.enableVertexAttribArray(vpos); 148 | 149 | gl.bindBuffer(gl.ARRAY_BUFFER, nbuf); // 法線ベクトル 150 | gl.vertexAttribPointer(npos, 3, gl.FLOAT, true, 0, 0); // float型を1頂点につき3つ、と指定 151 | gl.enableVertexAttribArray(npos); 152 | 153 | gl.bindBuffer(gl.ARRAY_BUFFER, vtbuf); // テクスチャ座標 154 | gl.vertexAttribPointer(vtpos, 2, gl.FLOAT, false, 0, 0); // テクスチャ座標は1頂点につき2要素 155 | gl.enableVertexAttribArray(vtpos); 156 | 157 | // Alpha情報を出力しないように設定 158 | gl.colorMask(true, true, true, false); 159 | // 今まで設定した内容でWebGLに送信 160 | // 一度に送信せず、mtl情報ごとに分割して送信する 161 | var pos = 0; 162 | for(var i = 0; i < glObj.mtlInfos.length; i++) { 163 | var mtlInfo = glObj.mtlInfos[i]; 164 | 165 | // Kd, Ks, Nsをそれぞれuniformで送信 166 | gl.uniform3fv(gl.getUniformLocation(prog, "kdcolor"), mtlInfo.kd); 167 | gl.uniform3fv(gl.getUniformLocation(prog, "kscolor"), mtlInfo.ks); 168 | gl.uniform1f(gl.getUniformLocation(prog, "nscolor"), mtlInfo.ns); // 1fの意味はfloat1個 169 | 170 | // テクスチャがあればテクスチャを送信する 171 | if(mtlInfo.texture) { 172 | gl.bindTexture(gl.TEXTURE_2D, textures[mtlInfo.texture]); 173 | gl.uniform1i(gl.getUniformLocation(prog, "texture"), 0); 174 | gl.uniform1f(gl.getUniformLocation(prog, "hasTexture"), 1); 175 | } else { 176 | gl.uniform1f(gl.getUniformLocation(prog, "hasTexture"), 0); 177 | } 178 | // 前の最後の頂点(pos / 3)から、今回のmtlで描画する頂点数だけ送る 179 | gl.drawArrays(gl.TRIANGLES, pos / 3, (mtlInfo.endPos - pos) / 3); 180 | pos = mtlInfo.endPos; 181 | } 182 | // Alpha情報を出力するように設定 183 | gl.colorMask(true, true, true, true); 184 | setTimeout(drawFrame, 16); 185 | }; 186 | })(); 187 | -------------------------------------------------------------------------------- /miku.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 17 3 | 4 | newmtl mat00.002 5 | Ns 7.843137 6 | Ka 0.000000 0.000000 0.000000 7 | Kd 0.106400 0.492800 0.568000 8 | Ks 0.000000 0.000000 0.000000 9 | Ni 1.000000 10 | d 1.000000 11 | illum 2 12 | 13 | newmtl mat01.002 14 | Ns 17.647059 15 | Ka 0.000000 0.000000 0.000000 16 | Kd 0.000000 0.454400 0.454400 17 | Ks 0.125000 0.125000 0.125000 18 | Ni 1.000000 19 | d 1.000000 20 | illum 2 21 | 22 | newmtl mat02.002 23 | Ns 9.803922 24 | Ka 0.000000 0.000000 0.000000 25 | Kd 0.640000 0.569600 0.499200 26 | Ks 0.075000 0.075000 0.075000 27 | Ni 1.000000 28 | d 1.000000 29 | illum 2 30 | 31 | newmtl mat03.002 32 | Ns 27.450980 33 | Ka 0.000000 0.000000 0.000000 34 | Kd 0.128000 0.128000 0.128000 35 | Ks 0.200000 0.200000 0.200000 36 | Ni 1.000000 37 | d 1.000000 38 | illum 2 39 | 40 | newmtl mat04.002 41 | Ns 7.843137 42 | Ka 0.000000 0.000000 0.000000 43 | Kd 0.451840 0.451840 0.451840 44 | Ks 0.075000 0.075000 0.075000 45 | Ni 1.000000 46 | d 1.000000 47 | illum 2 48 | 49 | newmtl mat05.002 50 | Ns 7.843137 51 | Ka 0.000000 0.000000 0.000000 52 | Kd 0.376320 0.640000 0.522240 53 | Ks 0.000000 0.000000 0.000000 54 | Ni 1.000000 55 | d 1.000000 56 | illum 2 57 | 58 | newmtl mat06.002 59 | Ns 7.843137 60 | Ka 0.000000 0.000000 0.000000 61 | Kd 0.523200 0.523200 0.523200 62 | Ks 0.000000 0.000000 0.000000 63 | Ni 1.000000 64 | d 1.000000 65 | illum 2 66 | map_Kd eyeM2.bmp 67 | map_d eyeM2.bmp 68 | 69 | newmtl mat07.002 70 | Ns 7.843137 71 | Ka 0.000000 0.000000 0.000000 72 | Kd 0.092800 0.092800 0.092800 73 | Ks 0.190000 0.190000 0.190000 74 | Ni 1.000000 75 | d 1.000000 76 | illum 2 77 | 78 | newmtl mat08.002 79 | Ns 7.843137 80 | Ka 0.000000 0.000000 0.000000 81 | Kd 0.634880 0.147840 0.206080 82 | Ks 0.000000 0.000000 0.000000 83 | Ni 1.000000 84 | d 1.000000 85 | illum 2 86 | 87 | newmtl mat09.002 88 | Ns 7.843137 89 | Ka 0.000000 0.000000 0.000000 90 | Kd 0.462080 0.504320 0.130560 91 | Ks 0.000000 0.000000 0.000000 92 | Ni 1.000000 93 | d 1.000000 94 | illum 2 95 | 96 | newmtl mat10.002 97 | Ns 7.843137 98 | Ka 0.000000 0.000000 0.000000 99 | Kd 0.474240 0.544640 0.640000 100 | Ks 0.000000 0.000000 0.000000 101 | Ni 1.000000 102 | d 1.000000 103 | illum 2 104 | 105 | newmtl mat11.002 106 | Ns 7.843137 107 | Ka 0.000000 0.000000 0.000000 108 | Kd 0.000000 0.000000 0.000000 109 | Ks 0.000000 0.000000 0.000000 110 | Ni 1.000000 111 | d 1.000000 112 | illum 2 113 | 114 | newmtl mat12.002 115 | Ns 7.843137 116 | Ka 0.000000 0.000000 0.000000 117 | Kd 0.561920 0.080000 0.097920 118 | Ks 0.000000 0.000000 0.000000 119 | Ni 1.000000 120 | d 1.000000 121 | illum 2 122 | 123 | newmtl mat13.002 124 | Ns 7.843137 125 | Ka 0.000000 0.000000 0.000000 126 | Kd 0.165760 0.110720 0.065280 127 | Ks 0.000000 0.000000 0.000000 128 | Ni 1.000000 129 | d 1.000000 130 | illum 2 131 | 132 | newmtl mat14.002 133 | Ns 7.843137 134 | Ka 0.000000 0.000000 0.000000 135 | Kd 0.640000 0.304000 0.331520 136 | Ks 0.000000 0.000000 0.000000 137 | Ni 1.000000 138 | d 1.000000 139 | illum 2 140 | 141 | newmtl mat15.002 142 | Ns 7.843137 143 | Ka 0.000000 0.000000 0.000000 144 | Kd 0.640000 0.640000 0.640000 145 | Ks 0.090000 0.090000 0.090000 146 | Ni 1.000000 147 | d 1.000000 148 | illum 2 149 | 150 | newmtl mat16.002 151 | Ns 7.843137 152 | Ka 0.000000 0.000000 0.000000 153 | Kd 0.065600 0.307200 0.354400 154 | Ks 0.000000 0.000000 0.000000 155 | Ni 1.000000 156 | d 1.000000 157 | illum 2 158 | -------------------------------------------------------------------------------- /objparser.js: -------------------------------------------------------------------------------- 1 | ;var objParser = {}; 2 | (function() { 3 | // MTLファイルのパース 4 | objParser.mtlParse = function(text) { 5 | var mtl = {}; 6 | var current = null; // 現在のmtl情報 7 | // 今まで収集したmtl情報を保存する 8 | var addCurrent = function() { 9 | if(current) { 10 | mtl[current.name] = current; 11 | } 12 | current = {}; 13 | }; 14 | 15 | var lines = text.split('\n'); 16 | for(var i = 0; i < lines.length; i++) { 17 | var line = lines[i]; 18 | var words = line.split(' '); 19 | switch(words[0]) { 20 | case "newmtl": 21 | // 新しいmtlの登場 22 | addCurrent(); // 既存のmtlを保存 23 | current.name = words[1]; // 新しいmtlの名前を保存 24 | break; 25 | case "Kd": 26 | // 拡散光の保存 27 | current.kd = vec3.fromValues(+words[1], +words[2], +words[3]); 28 | break; 29 | case "Ks": 30 | // 鏡面光の保存 31 | current.ks = vec3.fromValues(+words[1], +words[2], +words[3]); 32 | break; 33 | case "Ns": 34 | // 光沢率の保存 35 | current.ns = +words[1]; 36 | break; 37 | case "map_Kd": 38 | // テクスチャの保存 39 | current.texture = words[1]; 40 | break; 41 | } 42 | } 43 | // ファイルの最後のmtl情報を保存する 44 | addCurrent(); 45 | return mtl; 46 | }; 47 | // OBJファイルのパース 48 | objParser.objParse = function(text) { 49 | var vertices = []; // 頂点座標 50 | var normals = []; // 法線ベクトル 51 | var texcoords = []; // テクスチャ座標 52 | var faces = []; // ポリゴン定義 53 | 54 | var lines = text.split('\n'); 55 | var mtlName = ""; 56 | for(var i = 0; i < lines.length; i++) { 57 | var line = lines[i]; 58 | var words = line.split(' '); 59 | switch(words[0]) { 60 | default: 61 | // 理解できない接頭辞の場合は単純に無視する 62 | break; 63 | case "usemtl": 64 | // mtl名が指定された場合は、それをポリゴン定義用に保存する 65 | mtlName = words[1]; 66 | break; 67 | case "v": 68 | // 頂点座標の定義を保存する(x, y, zの3つ) 69 | vertices.push(+words[1]); 70 | vertices.push(+words[2]); 71 | vertices.push(+words[3]); 72 | break; 73 | case "vt": 74 | // テクスチャ座標の定義を保存する(u, vの2つ) 75 | texcoords.push(+words[1]); 76 | texcoords.push(+words[2]); 77 | break; 78 | case "vn": 79 | // 法線ベクトルの定義を保存する(x, y, zの3つ) 80 | normals.push(+words[1]); 81 | normals.push(+words[2]); 82 | normals.push(+words[3]); 83 | break; 84 | case "f": 85 | // ポリゴンの定義を保存する 86 | var face = []; 87 | for(var wi = 1; wi < words.length; wi++) { 88 | // 定義は"数値" "数値/数値" "数値//数値" "数値/数値/数値"のどれか 89 | // 数値はindexの番号(1スタート) 90 | var nums = (words[wi]+"//").split('/'); 91 | var vindex = +nums[0]; 92 | var tindex = NaN; 93 | var nindex = NaN; 94 | if(nums[1].length) { 95 | tindex = +nums[1]; 96 | } 97 | if(nums[2].length) { 98 | nindex = +nums[2]; 99 | } 100 | // それぞれのindexを、もし存在すればmtl名と共に保存する 101 | face.push({vindex: vindex, tindex: tindex, nindex: nindex, mtlName: mtlName}); 102 | } 103 | faces.push(face); 104 | break; 105 | } 106 | } 107 | return { 108 | vertices: vertices, 109 | normals: normals, 110 | texcoords: texcoords, 111 | faces: faces 112 | }; 113 | }; 114 | 115 | // OBJファイルとMTLファイルの情報を元にWebGL用のTypedArrayを用意する 116 | objParser.createGLObject = function(obj, mtl, func) { 117 | // まずポリゴンの枚数を特定する 118 | var numTriangles = 0; 119 | for(var i = 0; i < obj.faces.length; i++) { 120 | numTriangles += obj.faces[i].length - 2; 121 | } 122 | // ポリゴンの枚数に応じたTypedArrayを確保する 123 | // 頂点座標(ポリゴン数×3頂点×3要素) 124 | var vertices = new Float32Array(numTriangles * 9); 125 | // 法線ベクトル(ポリゴン数×3頂点×3要素) 126 | var normals = new Float32Array(numTriangles * 9); 127 | // テクスチャ頂点座標(ポリゴン数×3頂点×2要素) 128 | var texcoords = new Float32Array(numTriangles * 6); 129 | 130 | // 頂点ごとの法線ベクトルの和用配列 131 | var normalAtVertex = new Array(numTriangles * 3); 132 | // 頂点ごとの法線ベクトルの計算用関数 133 | var addNormal = function(index, n) { 134 | if(!normalAtVertex[index]) { 135 | // その頂点で初めての登録の場合は、法線ベクトルをただ登録する 136 | normalAtVertex[index] = vec3.clone(n); 137 | return; 138 | } else { 139 | // すでに登録してあった頂点に追加する場合は、ベクトルの足し算をする 140 | var normal = normalAtVertex[index]; 141 | vec3.add(normal, normal, n); 142 | } 143 | }; 144 | 145 | var currentMtlName = ""; // 現在選択されているmtl名 146 | var mtlInfos = []; // mtl単位の情報 147 | // 現在のmtl情報の保存 148 | var saveMtlInfo = function() { 149 | if(!mtl) { 150 | // mtlの情報がなかった時には、適当な色情報を用意しておく 151 | mtlInfos.push({ 152 | endPos: triangleCount * 9, 153 | kd: vec3.fromValues(0.5, 0.5, 0.5), 154 | ks: vec3.fromValues(0.0, 0.0, 0.0), 155 | ns: 1, 156 | texture: null 157 | }); 158 | } else if(currentMtlName) { 159 | var textureName = mtl[currentMtlName].texture; 160 | if(textureName) { 161 | // テクスチャが必要であればロードする 162 | imgLoader.load(textureName, function() { 163 | if(!imgLoader.isLoading()) { 164 | func(ret); 165 | } 166 | }); 167 | } 168 | // mtlの情報とポリゴン情報を保存する 169 | mtlInfos.push({ 170 | endPos: triangleCount * 9, 171 | kd: mtl[currentMtlName].kd, 172 | ks: mtl[currentMtlName].ks, 173 | ns: mtl[currentMtlName].ns, 174 | texture: textureName 175 | }); 176 | } 177 | }; 178 | var triangleCount = 0; 179 | for(var fi = 0; fi < obj.faces.length; fi++) { 180 | // objファイルの"f"定義1行ごとに処理する 181 | var face = obj.faces[fi]; 182 | // mtlが変わった時には、現在までの情報を保存する 183 | if(currentMtlName != face[0].mtlName) { 184 | saveMtlInfo(); 185 | currentMtlName = face[0].mtlName; 186 | } 187 | // "f"が3つ以上あるときに、頂点は(0, 1, 2), (0, 2, 3), (0, 3, 4), ... という風に処理する 188 | for(var ti = 1; ti < face.length - 1; ti++) { 189 | // 三角形の頂点インデックスの取得(1ずれているのに注意) 190 | var vi0 = face[0].vindex - 1; 191 | var vi1 = face[ti].vindex - 1; 192 | var vi2 = face[ti + 1].vindex - 1; 193 | // インデックスから三角形の頂点座標を取得し、TypedArrayのvec3で保存する 194 | var v0 = vec3.fromValues(obj.vertices[vi0 * 3], obj.vertices[vi0 * 3 + 1], obj.vertices[vi0 * 3 + 2]); 195 | var v1 = vec3.fromValues(obj.vertices[vi1 * 3], obj.vertices[vi1 * 3 + 1], obj.vertices[vi1 * 3 + 2]); 196 | var v2 = vec3.fromValues(obj.vertices[vi2 * 3], obj.vertices[vi2 * 3 + 1], obj.vertices[vi2 * 3 + 2]); 197 | // 頂点座標をTypedArrayに保存 198 | vertices.set(v0, triangleCount * 9); 199 | vertices.set(v1, triangleCount * 9 + 3); 200 | vertices.set(v2, triangleCount * 9 + 6); 201 | // テクスチャのインデックスの取得(1ずれているのに注意) 202 | var vti0 = face[0].tindex - 1; 203 | var vti1 = face[ti].tindex - 1; 204 | var vti2 = face[ti + 1].tindex - 1; 205 | // インデックスからテクスチャ頂点を持ってくる 206 | var vt0 = vec2.fromValues(obj.texcoords[vti0 * 2], obj.texcoords[vti0 * 2 + 1]); 207 | var vt1 = vec2.fromValues(obj.texcoords[vti1 * 2], obj.texcoords[vti1 * 2 + 1]); 208 | var vt2 = vec2.fromValues(obj.texcoords[vti2 * 2], obj.texcoords[vti2 * 2 + 1]); 209 | // テクスチャ頂点をTypedArrayに保存 210 | texcoords.set(vt0, triangleCount * 6); 211 | texcoords.set(vt1, triangleCount * 6 + 2); 212 | texcoords.set(vt2, triangleCount * 6 + 4); 213 | // 法線を計算 214 | var n = vec3.create(); 215 | vec3.sub(v1, v1, v0); // 頂点v0→v1のベクトルを計算 216 | vec3.sub(v2, v2, v0); // 頂点v0→v2のベクトルを計算 217 | vec3.cross(n, v1, v2); // 上記2つのベクトルの外積を計算 218 | // 正規化 219 | vec3.normalize(n, n); 220 | // 頂点ごとの法線の平均を取るために保存 221 | addNormal(vi0, n); 222 | addNormal(vi1, n); 223 | addNormal(vi2, n); 224 | 225 | ++triangleCount; 226 | } 227 | } 228 | // 最後に今までのmtlを保存しておく 229 | saveMtlInfo(); 230 | 231 | // すべての法線ベクトルを加算し終わった後で、平均法線ベクトルを登録する 232 | var triangleCount= 0; 233 | for(var fi = 0; fi < obj.faces.length; fi++) { 234 | var face = obj.faces[fi]; 235 | for(var ti = 1; ti < face.length - 1; ti++) { 236 | // 三角形の頂点インデックスの取得(1ずれているのに注意) 237 | var vi0 = face[0].vindex - 1; 238 | var vi1 = face[ti].vindex - 1; 239 | var vi2 = face[ti + 1].vindex - 1; 240 | // 頂点ごとの法線ベクトルの和を取得 241 | var n0 = normalAtVertex[vi0]; 242 | var n1 = normalAtVertex[vi1]; 243 | var n2 = normalAtVertex[vi2]; 244 | // 正規化(ベクトルの長さを1にする) 245 | vec3.normalize(n0, n0); 246 | vec3.normalize(n1, n1); 247 | vec3.normalize(n2, n2); 248 | // 計算し終わった法線ベクトルをTypedArrayに保存 249 | normals.set(n0, triangleCount * 9); 250 | normals.set(n1, triangleCount * 9 + 3); 251 | normals.set(n2, triangleCount * 9 + 6); 252 | 253 | ++triangleCount; 254 | } 255 | } 256 | 257 | // すべてのデータが揃ったらcallback関数を呼び出す 258 | var ret = { 259 | vertices: vertices, 260 | normals: normals, 261 | texcoords: texcoords, 262 | mtlInfos: mtlInfos 263 | }; 264 | if(!imgLoader.isLoading()) { 265 | func(ret); 266 | } 267 | }; 268 | })(); 269 | 270 | -------------------------------------------------------------------------------- /tga.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview jsTGALoader - Javascript loader for TGA file 3 | * @author Vincent Thibault 4 | * @version 1.2.0 5 | * @blog http://blog.robrowser.com/javascript-tga-loader.html 6 | */ 7 | 8 | /* Copyright (c) 2013, Vincent Thibault. All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without modification, 11 | are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 29 | 30 | (function(_global) 31 | { 32 | 'use strict'; 33 | 34 | 35 | /** 36 | * TGA Namespace 37 | * @constructor 38 | */ 39 | function Targa() 40 | { 41 | } 42 | 43 | 44 | /** 45 | * @var {object} TGA type constants 46 | */ 47 | Targa.Type = { 48 | NO_DATA: 0, 49 | INDEXED: 1, 50 | RGB: 2, 51 | GREY: 3, 52 | RLE_INDEXED: 9, 53 | RLE_RGB: 10, 54 | RLE_GREY: 11 55 | }; 56 | 57 | 58 | /** 59 | * @var {object} TGA origin constants 60 | */ 61 | Targa.Origin = { 62 | BOTTOM_LEFT: 0x00, 63 | BOTTOM_RIGHT: 0x01, 64 | TOP_LEFT: 0x02, 65 | TOP_RIGHT: 0x03, 66 | SHIFT: 0x04, 67 | MASK: 0x30 68 | }; 69 | 70 | 71 | /** 72 | * Check the header of TGA file to detect errors 73 | * 74 | * @param {object} tga header structure 75 | * @throws Error 76 | */ 77 | function checkHeader( header ) 78 | { 79 | // What the need of a file without data ? 80 | if (header.imageType === Targa.Type.NO_DATA) { 81 | throw new Error('Targa::checkHeader() - No data'); 82 | } 83 | 84 | // Indexed type 85 | if (header.hasColorMap) { 86 | if (header.colorMapLength > 256 || header.colorMapSize !== 24 || header.colorMapType !== 1) { 87 | throw new Error('Targa::checkHeader() - Invalid colormap for indexed type'); 88 | } 89 | } 90 | else { 91 | if (header.colorMapType) { 92 | throw new Error('Targa::checkHeader() - Why does the image contain a palette ?'); 93 | } 94 | } 95 | 96 | // Check image size 97 | if (header.width <= 0 || header.height <= 0) { 98 | throw new Error('Targa::checkHeader() - Invalid image size'); 99 | } 100 | 101 | // Check pixel size 102 | if (header.pixelDepth !== 8 && 103 | header.pixelDepth !== 16 && 104 | header.pixelDepth !== 24 && 105 | header.pixelDepth !== 32) { 106 | throw new Error('Targa::checkHeader() - Invalid pixel size "' + header.pixelDepth + '"'); 107 | } 108 | } 109 | 110 | 111 | /** 112 | * Decode RLE compression 113 | * 114 | * @param {Uint8Array} data 115 | * @param {number} offset in data to start loading RLE 116 | * @param {number} pixel count 117 | * @param {number} output buffer size 118 | */ 119 | function decodeRLE( data, offset, pixelSize, outputSize) 120 | { 121 | var pos, c, count, i; 122 | var pixels, output; 123 | 124 | output = new Uint8Array(outputSize); 125 | pixels = new Uint8Array(pixelSize); 126 | pos = 0; 127 | 128 | while (pos < outputSize) { 129 | c = data[offset++]; 130 | count = (c & 0x7f) + 1; 131 | 132 | // RLE pixels. 133 | if (c & 0x80) { 134 | // Bind pixel tmp array 135 | for (i = 0; i < pixelSize; ++i) { 136 | pixels[i] = data[offset++]; 137 | } 138 | 139 | // Copy pixel array 140 | for (i = 0; i < count; ++i) { 141 | output.set(pixels, pos); 142 | pos += pixelSize; 143 | } 144 | } 145 | 146 | // Raw pixels. 147 | else { 148 | count *= pixelSize; 149 | for (i = 0; i < count; ++i) { 150 | output[pos++] = data[offset++]; 151 | } 152 | } 153 | } 154 | 155 | return output; 156 | } 157 | 158 | 159 | /** 160 | * Return a ImageData object from a TGA file (8bits) 161 | * 162 | * @param {Array} imageData - ImageData to bind 163 | * @param {Array} indexes - index to colormap 164 | * @param {Array} colormap 165 | * @param {number} width 166 | * @param {number} y_start - start at y pixel. 167 | * @param {number} x_start - start at x pixel. 168 | * @param {number} y_step - increment y pixel each time. 169 | * @param {number} y_end - stop at pixel y. 170 | * @param {number} x_step - increment x pixel each time. 171 | * @param {number} x_end - stop at pixel x. 172 | * @returns {Array} imageData 173 | */ 174 | function getImageData8bits(imageData, indexes, colormap, width, y_start, y_step, y_end, x_start, x_step, x_end) 175 | { 176 | var color, i, x, y; 177 | 178 | for (i = 0, y = y_start; y !== y_end; y += y_step) { 179 | for (x = x_start; x !== x_end; x += x_step, i++) { 180 | color = indexes[i]; 181 | imageData[(x + width * y) * 4 + 3] = 255; 182 | imageData[(x + width * y) * 4 + 2] = colormap[(color * 3) + 0]; 183 | imageData[(x + width * y) * 4 + 1] = colormap[(color * 3) + 1]; 184 | imageData[(x + width * y) * 4 + 0] = colormap[(color * 3) + 2]; 185 | } 186 | } 187 | 188 | return imageData; 189 | } 190 | 191 | 192 | /** 193 | * Return a ImageData object from a TGA file (16bits) 194 | * 195 | * @param {Array} imageData - ImageData to bind 196 | * @param {Array} pixels data 197 | * @param {Array} colormap - not used 198 | * @param {number} width 199 | * @param {number} y_start - start at y pixel. 200 | * @param {number} x_start - start at x pixel. 201 | * @param {number} y_step - increment y pixel each time. 202 | * @param {number} y_end - stop at pixel y. 203 | * @param {number} x_step - increment x pixel each time. 204 | * @param {number} x_end - stop at pixel x. 205 | * @returns {Array} imageData 206 | */ 207 | function getImageData16bits(imageData, pixels, colormap, width, y_start, y_step, y_end, x_start, x_step, x_end) 208 | { 209 | var color, i, x, y; 210 | 211 | for (i = 0, y = y_start; y !== y_end; y += y_step) { 212 | for (x = x_start; x !== x_end; x += x_step, i += 2) { 213 | color = pixels[i + 0] | (pixels[i + 1] << 8); 214 | imageData[(x + width * y) * 4 + 0] = (color & 0x7C00) >> 7; 215 | imageData[(x + width * y) * 4 + 1] = (color & 0x03E0) >> 2; 216 | imageData[(x + width * y) * 4 + 2] = (color & 0x001F) >> 3; 217 | imageData[(x + width * y) * 4 + 3] = (color & 0x8000) ? 0 : 255; 218 | } 219 | } 220 | 221 | return imageData; 222 | } 223 | 224 | 225 | /** 226 | * Return a ImageData object from a TGA file (24bits) 227 | * 228 | * @param {Array} imageData - ImageData to bind 229 | * @param {Array} pixels data 230 | * @param {Array} colormap - not used 231 | * @param {number} width 232 | * @param {number} y_start - start at y pixel. 233 | * @param {number} x_start - start at x pixel. 234 | * @param {number} y_step - increment y pixel each time. 235 | * @param {number} y_end - stop at pixel y. 236 | * @param {number} x_step - increment x pixel each time. 237 | * @param {number} x_end - stop at pixel x. 238 | * @returns {Array} imageData 239 | */ 240 | function getImageData24bits(imageData, pixels, colormap, width, y_start, y_step, y_end, x_start, x_step, x_end) 241 | { 242 | var i, x, y; 243 | 244 | for (i = 0, y = y_start; y !== y_end; y += y_step) { 245 | for (x = x_start; x !== x_end; x += x_step, i += 3) { 246 | imageData[(x + width * y) * 4 + 3] = 255; 247 | imageData[(x + width * y) * 4 + 2] = pixels[i + 0]; 248 | imageData[(x + width * y) * 4 + 1] = pixels[i + 1]; 249 | imageData[(x + width * y) * 4 + 0] = pixels[i + 2]; 250 | } 251 | } 252 | 253 | return imageData; 254 | } 255 | 256 | 257 | /** 258 | * Return a ImageData object from a TGA file (32bits) 259 | * 260 | * @param {Array} imageData - ImageData to bind 261 | * @param {Array} pixels data 262 | * @param {Array} colormap - not used 263 | * @param {number} width 264 | * @param {number} y_start - start at y pixel. 265 | * @param {number} x_start - start at x pixel. 266 | * @param {number} y_step - increment y pixel each time. 267 | * @param {number} y_end - stop at pixel y. 268 | * @param {number} x_step - increment x pixel each time. 269 | * @param {number} x_end - stop at pixel x. 270 | * @returns {Array} imageData 271 | */ 272 | function getImageData32bits(imageData, pixels, colormap, width, y_start, y_step, y_end, x_start, x_step, x_end) 273 | { 274 | var i, x, y; 275 | 276 | for (i = 0, y = y_start; y !== y_end; y += y_step) { 277 | for (x = x_start; x !== x_end; x += x_step, i += 4) { 278 | imageData[(x + width * y) * 4 + 2] = pixels[i + 0]; 279 | imageData[(x + width * y) * 4 + 1] = pixels[i + 1]; 280 | imageData[(x + width * y) * 4 + 0] = pixels[i + 2]; 281 | imageData[(x + width * y) * 4 + 3] = pixels[i + 3]; 282 | } 283 | } 284 | 285 | return imageData; 286 | } 287 | 288 | 289 | /** 290 | * Return a ImageData object from a TGA file (8bits grey) 291 | * 292 | * @param {Array} imageData - ImageData to bind 293 | * @param {Array} pixels data 294 | * @param {Array} colormap - not used 295 | * @param {number} width 296 | * @param {number} y_start - start at y pixel. 297 | * @param {number} x_start - start at x pixel. 298 | * @param {number} y_step - increment y pixel each time. 299 | * @param {number} y_end - stop at pixel y. 300 | * @param {number} x_step - increment x pixel each time. 301 | * @param {number} x_end - stop at pixel x. 302 | * @returns {Array} imageData 303 | */ 304 | function getImageDataGrey8bits(imageData, pixels, colormap, width, y_start, y_step, y_end, x_start, x_step, x_end) 305 | { 306 | var color, i, x, y; 307 | 308 | for (i = 0, y = y_start; y !== y_end; y += y_step) { 309 | for (x = x_start; x !== x_end; x += x_step, i++) { 310 | color = pixels[i]; 311 | imageData[(x + width * y) * 4 + 0] = color; 312 | imageData[(x + width * y) * 4 + 1] = color; 313 | imageData[(x + width * y) * 4 + 2] = color; 314 | imageData[(x + width * y) * 4 + 3] = 255; 315 | } 316 | } 317 | 318 | return imageData; 319 | } 320 | 321 | 322 | /** 323 | * Return a ImageData object from a TGA file (16bits grey) 324 | * 325 | * @param {Array} imageData - ImageData to bind 326 | * @param {Array} pixels data 327 | * @param {Array} colormap - not used 328 | * @param {number} width 329 | * @param {number} y_start - start at y pixel. 330 | * @param {number} x_start - start at x pixel. 331 | * @param {number} y_step - increment y pixel each time. 332 | * @param {number} y_end - stop at pixel y. 333 | * @param {number} x_step - increment x pixel each time. 334 | * @param {number} x_end - stop at pixel x. 335 | * @returns {Array} imageData 336 | */ 337 | function getImageDataGrey16bits(imageData, pixels, colormap, width, y_start, y_step, y_end, x_start, x_step, x_end) 338 | { 339 | var i, x, y; 340 | 341 | for (i = 0, y = y_start; y !== y_end; y += y_step) { 342 | for (x = x_start; x !== x_end; x += x_step, i += 2) { 343 | imageData[(x + width * y) * 4 + 0] = pixels[i + 0]; 344 | imageData[(x + width * y) * 4 + 1] = pixels[i + 0]; 345 | imageData[(x + width * y) * 4 + 2] = pixels[i + 0]; 346 | imageData[(x + width * y) * 4 + 3] = pixels[i + 1]; 347 | } 348 | } 349 | 350 | return imageData; 351 | } 352 | 353 | 354 | /** 355 | * Open a targa file using XHR, be aware with Cross Domain files... 356 | * 357 | * @param {string} path - Path of the filename to load 358 | * @param {function} callback - callback to trigger when the file is loaded 359 | */ 360 | Targa.prototype.open = function targaOpen(path, callback) 361 | { 362 | var req, tga = this; 363 | req = new XMLHttpRequest(); 364 | req.open('GET', path, true); 365 | req.responseType = 'arraybuffer'; 366 | req.onload = function() { 367 | if (this.status === 200) { 368 | tga.load(new Uint8Array(req.response)); 369 | if (callback) { 370 | callback.call(tga); 371 | } 372 | } 373 | }; 374 | req.send(null); 375 | }; 376 | 377 | 378 | /** 379 | * Load and parse a TGA file 380 | * 381 | * @param {Uint8Array} data - TGA file buffer array 382 | */ 383 | Targa.prototype.load = function targaLoad( data ) 384 | { 385 | var offset = 0; 386 | 387 | // Not enough data to contain header ? 388 | if (data.length < 0x12) { 389 | throw new Error('Targa::load() - Not enough data to contain header'); 390 | } 391 | 392 | // Read TgaHeader 393 | this.header = { 394 | /* 0x00 BYTE */ idLength: data[offset++], 395 | /* 0x01 BYTE */ colorMapType: data[offset++], 396 | /* 0x02 BYTE */ imageType: data[offset++], 397 | /* 0x03 WORD */ colorMapIndex: data[offset++] | data[offset++] << 8, 398 | /* 0x05 WORD */ colorMapLength: data[offset++] | data[offset++] << 8, 399 | /* 0x07 BYTE */ colorMapDepth: data[offset++], 400 | /* 0x08 WORD */ offsetX: data[offset++] | data[offset++] << 8, 401 | /* 0x0a WORD */ offsetY: data[offset++] | data[offset++] << 8, 402 | /* 0x0c WORD */ width: data[offset++] | data[offset++] << 8, 403 | /* 0x0e WORD */ height: data[offset++] | data[offset++] << 8, 404 | /* 0x10 BYTE */ pixelDepth: data[offset++], 405 | /* 0x11 BYTE */ flags: data[offset++] 406 | }; 407 | 408 | // Set shortcut 409 | this.header.hasEncoding = (this.header.imageType === Targa.Type.RLE_INDEXED || this.header.imageType === Targa.Type.RLE_RGB || this.header.imageType === Targa.Type.RLE_GREY); 410 | this.header.hasColorMap = (this.header.imageType === Targa.Type.RLE_INDEXED || this.header.imageType === Targa.Type.INDEXED); 411 | this.header.isGreyColor = (this.header.imageType === Targa.Type.RLE_GREY || this.header.imageType === Targa.Type.GREY); 412 | 413 | // Check if a valid TGA file (or if we can load it) 414 | checkHeader(this.header); 415 | 416 | // Move to data 417 | offset += this.header.idLength; 418 | if (offset >= data.length) { 419 | throw new Error('Targa::load() - No data'); 420 | } 421 | 422 | // Read palette 423 | if (this.header.hasColorMap) { 424 | var colorMapSize = this.header.colorMapLength * (this.header.colorMapDepth >> 3); 425 | this.palette = data.subarray( offset, offset + colorMapSize); 426 | offset += colorMapSize; 427 | } 428 | 429 | var pixelSize = this.header.pixelDepth >> 3; 430 | var imageSize = this.header.width * this.header.height; 431 | var pixelTotal = imageSize * pixelSize; 432 | 433 | // RLE encoded 434 | if (this.header.hasEncoding) { 435 | this.imageData = decodeRLE(data, offset, pixelSize, pixelTotal); 436 | } 437 | 438 | // RAW pixels 439 | else { 440 | this.imageData = data.subarray( offset, offset + (this.header.hasColorMap ? imageSize : pixelTotal) ); 441 | } 442 | }; 443 | 444 | 445 | /** 446 | * Return a ImageData object from a TGA file 447 | * 448 | * @param {object} imageData - Optional ImageData to work with 449 | * @returns {object} imageData 450 | */ 451 | Targa.prototype.getImageData = function targaGetImageData( imageData ) 452 | { 453 | var width = this.header.width; 454 | var height = this.header.height; 455 | var origin = (this.header.flags & Targa.Origin.MASK) >> Targa.Origin.SHIFT; 456 | var x_start, x_step, x_end, y_start, y_step, y_end; 457 | var getImageData; 458 | 459 | // Create an imageData 460 | if (!imageData) { 461 | if (document) { 462 | imageData = document.createElement('canvas').getContext('2d').createImageData(width, height); 463 | } 464 | // In Thread context ? 465 | else { 466 | imageData = { 467 | width: width, 468 | height: height, 469 | data: new Uint8ClampedArray(width * height * 4) 470 | }; 471 | } 472 | } 473 | 474 | if (origin === Targa.Origin.TOP_LEFT || origin === Targa.Origin.TOP_RIGHT) { 475 | y_start = 0; 476 | y_step = 1; 477 | y_end = height; 478 | } 479 | else { 480 | y_start = height - 1; 481 | y_step = -1; 482 | y_end = -1; 483 | } 484 | 485 | if (origin === Targa.Origin.TOP_LEFT || origin === Targa.Origin.BOTTOM_LEFT) { 486 | x_start = 0; 487 | x_step = 1; 488 | x_end = width; 489 | } 490 | else { 491 | x_start = width - 1; 492 | x_step = -1; 493 | x_end = -1; 494 | } 495 | 496 | // TODO: use this.header.offsetX and this.header.offsetY ? 497 | 498 | switch (this.header.pixelDepth) { 499 | case 8: 500 | getImageData = this.header.isGreyColor ? getImageDataGrey8bits : getImageData8bits; 501 | break; 502 | 503 | case 16: 504 | getImageData = this.header.isGreyColor ? getImageDataGrey16bits : getImageData16bits; 505 | break; 506 | 507 | case 24: 508 | getImageData = getImageData24bits; 509 | break; 510 | 511 | case 32: 512 | getImageData = getImageData32bits; 513 | break; 514 | } 515 | 516 | getImageData(imageData.data, this.imageData, this.palette, width, y_start, y_step, y_end, x_start, x_step, x_end); 517 | return imageData; 518 | }; 519 | 520 | 521 | /** 522 | * Return a canvas with the TGA render on it 523 | * 524 | * @returns {object} CanvasElement 525 | */ 526 | Targa.prototype.getCanvas = function targaGetCanvas() 527 | { 528 | var canvas, ctx, imageData; 529 | 530 | canvas = document.createElement('canvas'); 531 | ctx = canvas.getContext('2d'); 532 | imageData = ctx.createImageData(this.header.width, this.header.height); 533 | 534 | canvas.width = this.header.width; 535 | canvas.height = this.header.height; 536 | 537 | ctx.putImageData(this.getImageData(imageData), 0, 0); 538 | 539 | return canvas; 540 | }; 541 | 542 | 543 | /** 544 | * Return a dataURI of the TGA file 545 | * 546 | * @param {string} type - Optional image content-type to output (default: image/png) 547 | * @returns {string} url 548 | */ 549 | Targa.prototype.getDataURL = function targaGetDatURL( type ) 550 | { 551 | return this.getCanvas().toDataURL(type || 'image/png'); 552 | }; 553 | 554 | 555 | // Find Context 556 | var shim = {}; 557 | if (typeof(exports) === 'undefined') { 558 | if (typeof(define) === 'function' && typeof(define.amd) === 'object' && define.amd) { 559 | define(function(){ 560 | return Targa; 561 | }); 562 | } else { 563 | // Browser 564 | shim.exports = typeof(window) !== 'undefined' ? window : _global; 565 | } 566 | } 567 | else { 568 | // Commonjs 569 | shim.exports = exports; 570 | } 571 | 572 | 573 | // Export 574 | if (shim.exports) { 575 | shim.exports.TGA = Targa; 576 | } 577 | 578 | })(this); 579 | -------------------------------------------------------------------------------- /wing_MikuAp.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkihira/webgl-miku-sample/f872808f2f9d5a2a80939cf01a6ac22103a43abd/wing_MikuAp.tga --------------------------------------------------------------------------------