├── .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
--------------------------------------------------------------------------------