1 | /**
|
---|
2 | * @author alteredq / http://alteredqualia.com/
|
---|
3 | * @author mrdoob / http://mrdoob.com/
|
---|
4 | */
|
---|
5 |
|
---|
6 | THREE.ImageUtils = {
|
---|
7 |
|
---|
8 | crossOrigin: undefined,
|
---|
9 |
|
---|
10 | loadTexture: function ( url, mapping, onLoad, onError ) {
|
---|
11 |
|
---|
12 | var loader = new THREE.ImageLoader();
|
---|
13 | loader.crossOrigin = this.crossOrigin;
|
---|
14 |
|
---|
15 | var texture = new THREE.Texture( undefined, mapping );
|
---|
16 |
|
---|
17 | var image = loader.load( url, function () {
|
---|
18 |
|
---|
19 | texture.needsUpdate = true;
|
---|
20 |
|
---|
21 | if ( onLoad ) onLoad( texture );
|
---|
22 |
|
---|
23 | } );
|
---|
24 |
|
---|
25 | texture.image = image;
|
---|
26 | texture.sourceFile = url;
|
---|
27 |
|
---|
28 | return texture;
|
---|
29 |
|
---|
30 | },
|
---|
31 |
|
---|
32 | loadCompressedTexture: function ( url, mapping, onLoad, onError ) {
|
---|
33 |
|
---|
34 | var texture = new THREE.CompressedTexture();
|
---|
35 | texture.mapping = mapping;
|
---|
36 |
|
---|
37 | var request = new XMLHttpRequest();
|
---|
38 |
|
---|
39 | request.onload = function () {
|
---|
40 |
|
---|
41 | var buffer = request.response;
|
---|
42 | var dds = THREE.ImageUtils.parseDDS( buffer, true );
|
---|
43 |
|
---|
44 | texture.format = dds.format;
|
---|
45 |
|
---|
46 | texture.mipmaps = dds.mipmaps;
|
---|
47 | texture.image.width = dds.width;
|
---|
48 | texture.image.height = dds.height;
|
---|
49 |
|
---|
50 | // gl.generateMipmap fails for compressed textures
|
---|
51 | // mipmaps must be embedded in the DDS file
|
---|
52 | // or texture filters must not use mipmapping
|
---|
53 |
|
---|
54 | texture.generateMipmaps = false;
|
---|
55 |
|
---|
56 | texture.needsUpdate = true;
|
---|
57 |
|
---|
58 | if ( onLoad ) onLoad( texture );
|
---|
59 |
|
---|
60 | }
|
---|
61 |
|
---|
62 | request.onerror = onError;
|
---|
63 |
|
---|
64 | request.open( 'GET', url, true );
|
---|
65 | request.responseType = "arraybuffer";
|
---|
66 | request.send( null );
|
---|
67 |
|
---|
68 | return texture;
|
---|
69 |
|
---|
70 | },
|
---|
71 |
|
---|
72 | loadTextureCube: function ( array, mapping, onLoad, onError ) {
|
---|
73 |
|
---|
74 | var images = [];
|
---|
75 | images.loadCount = 0;
|
---|
76 |
|
---|
77 | var texture = new THREE.Texture();
|
---|
78 | texture.image = images;
|
---|
79 | if ( mapping !== undefined ) texture.mapping = mapping;
|
---|
80 |
|
---|
81 | // no flipping needed for cube textures
|
---|
82 |
|
---|
83 | texture.flipY = false;
|
---|
84 |
|
---|
85 | for ( var i = 0, il = array.length; i < il; ++ i ) {
|
---|
86 |
|
---|
87 | var cubeImage = new Image();
|
---|
88 | images[ i ] = cubeImage;
|
---|
89 |
|
---|
90 | cubeImage.onload = function () {
|
---|
91 |
|
---|
92 | images.loadCount += 1;
|
---|
93 |
|
---|
94 | if ( images.loadCount === 6 ) {
|
---|
95 |
|
---|
96 | texture.needsUpdate = true;
|
---|
97 | if ( onLoad ) onLoad( texture );
|
---|
98 |
|
---|
99 | }
|
---|
100 |
|
---|
101 | };
|
---|
102 |
|
---|
103 | cubeImage.onerror = onError;
|
---|
104 |
|
---|
105 | cubeImage.crossOrigin = this.crossOrigin;
|
---|
106 | cubeImage.src = array[ i ];
|
---|
107 |
|
---|
108 | }
|
---|
109 |
|
---|
110 | return texture;
|
---|
111 |
|
---|
112 | },
|
---|
113 |
|
---|
114 | loadCompressedTextureCube: function ( array, mapping, onLoad, onError ) {
|
---|
115 |
|
---|
116 | var images = [];
|
---|
117 | images.loadCount = 0;
|
---|
118 |
|
---|
119 | var texture = new THREE.CompressedTexture();
|
---|
120 | texture.image = images;
|
---|
121 | if ( mapping !== undefined ) texture.mapping = mapping;
|
---|
122 |
|
---|
123 | // no flipping for cube textures
|
---|
124 | // (also flipping doesn't work for compressed textures )
|
---|
125 |
|
---|
126 | texture.flipY = false;
|
---|
127 |
|
---|
128 | // can't generate mipmaps for compressed textures
|
---|
129 | // mips must be embedded in DDS files
|
---|
130 |
|
---|
131 | texture.generateMipmaps = false;
|
---|
132 |
|
---|
133 | var generateCubeFaceCallback = function ( rq, img ) {
|
---|
134 |
|
---|
135 | return function () {
|
---|
136 |
|
---|
137 | var buffer = rq.response;
|
---|
138 | var dds = THREE.ImageUtils.parseDDS( buffer, true );
|
---|
139 |
|
---|
140 | img.format = dds.format;
|
---|
141 |
|
---|
142 | img.mipmaps = dds.mipmaps;
|
---|
143 | img.width = dds.width;
|
---|
144 | img.height = dds.height;
|
---|
145 |
|
---|
146 | images.loadCount += 1;
|
---|
147 |
|
---|
148 | if ( images.loadCount === 6 ) {
|
---|
149 |
|
---|
150 | texture.format = dds.format;
|
---|
151 | texture.needsUpdate = true;
|
---|
152 | if ( onLoad ) onLoad( texture );
|
---|
153 |
|
---|
154 | }
|
---|
155 |
|
---|
156 | }
|
---|
157 |
|
---|
158 | }
|
---|
159 |
|
---|
160 | // compressed cubemap textures as 6 separate DDS files
|
---|
161 |
|
---|
162 | if ( array instanceof Array ) {
|
---|
163 |
|
---|
164 | for ( var i = 0, il = array.length; i < il; ++ i ) {
|
---|
165 |
|
---|
166 | var cubeImage = {};
|
---|
167 | images[ i ] = cubeImage;
|
---|
168 |
|
---|
169 | var request = new XMLHttpRequest();
|
---|
170 |
|
---|
171 | request.onload = generateCubeFaceCallback( request, cubeImage );
|
---|
172 | request.onerror = onError;
|
---|
173 |
|
---|
174 | var url = array[ i ];
|
---|
175 |
|
---|
176 | request.open( 'GET', url, true );
|
---|
177 | request.responseType = "arraybuffer";
|
---|
178 | request.send( null );
|
---|
179 |
|
---|
180 | }
|
---|
181 |
|
---|
182 | // compressed cubemap texture stored in a single DDS file
|
---|
183 |
|
---|
184 | } else {
|
---|
185 |
|
---|
186 | var url = array;
|
---|
187 | var request = new XMLHttpRequest();
|
---|
188 |
|
---|
189 | request.onload = function( ) {
|
---|
190 |
|
---|
191 | var buffer = request.response;
|
---|
192 | var dds = THREE.ImageUtils.parseDDS( buffer, true );
|
---|
193 |
|
---|
194 | if ( dds.isCubemap ) {
|
---|
195 |
|
---|
196 | var faces = dds.mipmaps.length / dds.mipmapCount;
|
---|
197 |
|
---|
198 | for ( var f = 0; f < faces; f ++ ) {
|
---|
199 |
|
---|
200 | images[ f ] = { mipmaps : [] };
|
---|
201 |
|
---|
202 | for ( var i = 0; i < dds.mipmapCount; i ++ ) {
|
---|
203 |
|
---|
204 | images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] );
|
---|
205 | images[ f ].format = dds.format;
|
---|
206 | images[ f ].width = dds.width;
|
---|
207 | images[ f ].height = dds.height;
|
---|
208 |
|
---|
209 | }
|
---|
210 |
|
---|
211 | }
|
---|
212 |
|
---|
213 | texture.format = dds.format;
|
---|
214 | texture.needsUpdate = true;
|
---|
215 | if ( onLoad ) onLoad( texture );
|
---|
216 |
|
---|
217 | }
|
---|
218 |
|
---|
219 | }
|
---|
220 |
|
---|
221 | request.onerror = onError;
|
---|
222 |
|
---|
223 | request.open( 'GET', url, true );
|
---|
224 | request.responseType = "arraybuffer";
|
---|
225 | request.send( null );
|
---|
226 |
|
---|
227 | }
|
---|
228 |
|
---|
229 | return texture;
|
---|
230 |
|
---|
231 | },
|
---|
232 |
|
---|
233 | loadDDSTexture: function ( url, mapping, onLoad, onError ) {
|
---|
234 |
|
---|
235 | var images = [];
|
---|
236 | images.loadCount = 0;
|
---|
237 |
|
---|
238 | var texture = new THREE.CompressedTexture();
|
---|
239 | texture.image = images;
|
---|
240 | if ( mapping !== undefined ) texture.mapping = mapping;
|
---|
241 |
|
---|
242 | // no flipping for cube textures
|
---|
243 | // (also flipping doesn't work for compressed textures )
|
---|
244 |
|
---|
245 | texture.flipY = false;
|
---|
246 |
|
---|
247 | // can't generate mipmaps for compressed textures
|
---|
248 | // mips must be embedded in DDS files
|
---|
249 |
|
---|
250 | texture.generateMipmaps = false;
|
---|
251 |
|
---|
252 | {
|
---|
253 | var request = new XMLHttpRequest();
|
---|
254 |
|
---|
255 | request.onload = function( ) {
|
---|
256 |
|
---|
257 | var buffer = request.response;
|
---|
258 | var dds = THREE.ImageUtils.parseDDS( buffer, true );
|
---|
259 |
|
---|
260 | if ( dds.isCubemap ) {
|
---|
261 |
|
---|
262 | var faces = dds.mipmaps.length / dds.mipmapCount;
|
---|
263 |
|
---|
264 | for ( var f = 0; f < faces; f ++ ) {
|
---|
265 |
|
---|
266 | images[ f ] = { mipmaps : [] };
|
---|
267 |
|
---|
268 | for ( var i = 0; i < dds.mipmapCount; i ++ ) {
|
---|
269 |
|
---|
270 | images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] );
|
---|
271 | images[ f ].format = dds.format;
|
---|
272 | images[ f ].width = dds.width;
|
---|
273 | images[ f ].height = dds.height;
|
---|
274 |
|
---|
275 | }
|
---|
276 |
|
---|
277 | }
|
---|
278 |
|
---|
279 |
|
---|
280 | } else {
|
---|
281 | texture.image.width = dds.width;
|
---|
282 | texture.image.height = dds.height;
|
---|
283 | texture.mipmaps = dds.mipmaps;
|
---|
284 | }
|
---|
285 |
|
---|
286 | texture.format = dds.format;
|
---|
287 | texture.needsUpdate = true;
|
---|
288 | if ( onLoad ) onLoad( texture );
|
---|
289 |
|
---|
290 | }
|
---|
291 |
|
---|
292 | request.onerror = onError;
|
---|
293 |
|
---|
294 | request.open( 'GET', url, true );
|
---|
295 | request.responseType = "arraybuffer";
|
---|
296 | request.send( null );
|
---|
297 |
|
---|
298 | }
|
---|
299 |
|
---|
300 | return texture;
|
---|
301 |
|
---|
302 | },
|
---|
303 |
|
---|
304 | parseDDS: function ( buffer, loadMipmaps ) {
|
---|
305 |
|
---|
306 | var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
|
---|
307 |
|
---|
308 | // Adapted from @toji's DDS utils
|
---|
309 | // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
|
---|
310 |
|
---|
311 | // All values and structures referenced from:
|
---|
312 | // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
|
---|
313 |
|
---|
314 | var DDS_MAGIC = 0x20534444;
|
---|
315 |
|
---|
316 | var DDSD_CAPS = 0x1,
|
---|
317 | DDSD_HEIGHT = 0x2,
|
---|
318 | DDSD_WIDTH = 0x4,
|
---|
319 | DDSD_PITCH = 0x8,
|
---|
320 | DDSD_PIXELFORMAT = 0x1000,
|
---|
321 | DDSD_MIPMAPCOUNT = 0x20000,
|
---|
322 | DDSD_LINEARSIZE = 0x80000,
|
---|
323 | DDSD_DEPTH = 0x800000;
|
---|
324 |
|
---|
325 | var DDSCAPS_COMPLEX = 0x8,
|
---|
326 | DDSCAPS_MIPMAP = 0x400000,
|
---|
327 | DDSCAPS_TEXTURE = 0x1000;
|
---|
328 |
|
---|
329 | var DDSCAPS2_CUBEMAP = 0x200,
|
---|
330 | DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
|
---|
331 | DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
|
---|
332 | DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
|
---|
333 | DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
|
---|
334 | DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
|
---|
335 | DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
|
---|
336 | DDSCAPS2_VOLUME = 0x200000;
|
---|
337 |
|
---|
338 | var DDPF_ALPHAPIXELS = 0x1,
|
---|
339 | DDPF_ALPHA = 0x2,
|
---|
340 | DDPF_FOURCC = 0x4,
|
---|
341 | DDPF_RGB = 0x40,
|
---|
342 | DDPF_YUV = 0x200,
|
---|
343 | DDPF_LUMINANCE = 0x20000;
|
---|
344 |
|
---|
345 | function fourCCToInt32( value ) {
|
---|
346 |
|
---|
347 | return value.charCodeAt(0) +
|
---|
348 | (value.charCodeAt(1) << 8) +
|
---|
349 | (value.charCodeAt(2) << 16) +
|
---|
350 | (value.charCodeAt(3) << 24);
|
---|
351 |
|
---|
352 | }
|
---|
353 |
|
---|
354 | function int32ToFourCC( value ) {
|
---|
355 |
|
---|
356 | return String.fromCharCode(
|
---|
357 | value & 0xff,
|
---|
358 | (value >> 8) & 0xff,
|
---|
359 | (value >> 16) & 0xff,
|
---|
360 | (value >> 24) & 0xff
|
---|
361 | );
|
---|
362 | }
|
---|
363 |
|
---|
364 | function loadARGBMip( buffer, dataOffset, width, height ) {
|
---|
365 | var dataLength = width*height*4;
|
---|
366 | var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength );
|
---|
367 | var byteArray = new Uint8Array( dataLength );
|
---|
368 | var dst = 0;
|
---|
369 | var src = 0;
|
---|
370 | for ( var y = 0; y < height; y++ ) {
|
---|
371 | for ( var x = 0; x < width; x++ ) {
|
---|
372 | var b = srcBuffer[src]; src++;
|
---|
373 | var g = srcBuffer[src]; src++;
|
---|
374 | var r = srcBuffer[src]; src++;
|
---|
375 | var a = srcBuffer[src]; src++;
|
---|
376 | byteArray[dst] = r; dst++; //r
|
---|
377 | byteArray[dst] = g; dst++; //g
|
---|
378 | byteArray[dst] = b; dst++; //b
|
---|
379 | byteArray[dst] = a; dst++; //a
|
---|
380 | }
|
---|
381 | }
|
---|
382 | return byteArray;
|
---|
383 | }
|
---|
384 |
|
---|
385 | var FOURCC_DXT1 = fourCCToInt32("DXT1");
|
---|
386 | var FOURCC_DXT3 = fourCCToInt32("DXT3");
|
---|
387 | var FOURCC_DXT5 = fourCCToInt32("DXT5");
|
---|
388 |
|
---|
389 | var headerLengthInt = 31; // The header length in 32 bit ints
|
---|
390 |
|
---|
391 | // Offsets into the header array
|
---|
392 |
|
---|
393 | var off_magic = 0;
|
---|
394 |
|
---|
395 | var off_size = 1;
|
---|
396 | var off_flags = 2;
|
---|
397 | var off_height = 3;
|
---|
398 | var off_width = 4;
|
---|
399 |
|
---|
400 | var off_mipmapCount = 7;
|
---|
401 |
|
---|
402 | var off_pfFlags = 20;
|
---|
403 | var off_pfFourCC = 21;
|
---|
404 | var off_RGBBitCount = 22;
|
---|
405 | var off_RBitMask = 23;
|
---|
406 | var off_GBitMask = 24;
|
---|
407 | var off_BBitMask = 25;
|
---|
408 | var off_ABitMask = 26;
|
---|
409 |
|
---|
410 | var off_caps = 27;
|
---|
411 | var off_caps2 = 28;
|
---|
412 | var off_caps3 = 29;
|
---|
413 | var off_caps4 = 30;
|
---|
414 |
|
---|
415 | // Parse header
|
---|
416 |
|
---|
417 | var header = new Int32Array( buffer, 0, headerLengthInt );
|
---|
418 |
|
---|
419 | if ( header[ off_magic ] !== DDS_MAGIC ) {
|
---|
420 |
|
---|
421 | console.error( "ImageUtils.parseDDS(): Invalid magic number in DDS header" );
|
---|
422 | return dds;
|
---|
423 |
|
---|
424 | }
|
---|
425 |
|
---|
426 | if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) {
|
---|
427 |
|
---|
428 | console.error( "ImageUtils.parseDDS(): Unsupported format, must contain a FourCC code" );
|
---|
429 | return dds;
|
---|
430 |
|
---|
431 | }
|
---|
432 |
|
---|
433 | var blockBytes;
|
---|
434 |
|
---|
435 | var fourCC = header[ off_pfFourCC ];
|
---|
436 |
|
---|
437 | var isRGBAUncompressed = false;
|
---|
438 |
|
---|
439 | switch ( fourCC ) {
|
---|
440 |
|
---|
441 | case FOURCC_DXT1:
|
---|
442 |
|
---|
443 | blockBytes = 8;
|
---|
444 | dds.format = THREE.RGB_S3TC_DXT1_Format;
|
---|
445 | break;
|
---|
446 |
|
---|
447 | case FOURCC_DXT3:
|
---|
448 |
|
---|
449 | blockBytes = 16;
|
---|
450 | dds.format = THREE.RGBA_S3TC_DXT3_Format;
|
---|
451 | break;
|
---|
452 |
|
---|
453 | case FOURCC_DXT5:
|
---|
454 |
|
---|
455 | blockBytes = 16;
|
---|
456 | dds.format = THREE.RGBA_S3TC_DXT5_Format;
|
---|
457 | break;
|
---|
458 |
|
---|
459 | default:
|
---|
460 |
|
---|
461 | if( header[off_RGBBitCount] ==32
|
---|
462 | && header[off_RBitMask]&0xff0000
|
---|
463 | && header[off_GBitMask]&0xff00
|
---|
464 | && header[off_BBitMask]&0xff
|
---|
465 | && header[off_ABitMask]&0xff000000 ) {
|
---|
466 | isRGBAUncompressed = true;
|
---|
467 | blockBytes = 64;
|
---|
468 | dds.format = THREE.RGBAFormat;
|
---|
469 | } else {
|
---|
470 | console.error( "ImageUtils.parseDDS(): Unsupported FourCC code: ", int32ToFourCC( fourCC ) );
|
---|
471 | return dds;
|
---|
472 | }
|
---|
473 | }
|
---|
474 |
|
---|
475 | dds.mipmapCount = 1;
|
---|
476 |
|
---|
477 | if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) {
|
---|
478 |
|
---|
479 | dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] );
|
---|
480 |
|
---|
481 | }
|
---|
482 |
|
---|
483 | //TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc.
|
---|
484 |
|
---|
485 | dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false;
|
---|
486 |
|
---|
487 | dds.width = header[ off_width ];
|
---|
488 | dds.height = header[ off_height ];
|
---|
489 |
|
---|
490 | var dataOffset = header[ off_size ] + 4;
|
---|
491 |
|
---|
492 | // Extract mipmaps buffers
|
---|
493 |
|
---|
494 | var width = dds.width;
|
---|
495 | var height = dds.height;
|
---|
496 |
|
---|
497 | var faces = dds.isCubemap ? 6 : 1;
|
---|
498 |
|
---|
499 | for ( var face = 0; face < faces; face ++ ) {
|
---|
500 |
|
---|
501 | for ( var i = 0; i < dds.mipmapCount; i ++ ) {
|
---|
502 |
|
---|
503 | if( isRGBAUncompressed ) {
|
---|
504 | var byteArray = loadARGBMip( buffer, dataOffset, width, height );
|
---|
505 | var dataLength = byteArray.length;
|
---|
506 | } else {
|
---|
507 | var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes;
|
---|
508 | var byteArray = new Uint8Array( buffer, dataOffset, dataLength );
|
---|
509 | }
|
---|
510 |
|
---|
511 | var mipmap = { "data": byteArray, "width": width, "height": height };
|
---|
512 | dds.mipmaps.push( mipmap );
|
---|
513 |
|
---|
514 | dataOffset += dataLength;
|
---|
515 |
|
---|
516 | width = Math.max( width * 0.5, 1 );
|
---|
517 | height = Math.max( height * 0.5, 1 );
|
---|
518 |
|
---|
519 | }
|
---|
520 |
|
---|
521 | width = dds.width;
|
---|
522 | height = dds.height;
|
---|
523 |
|
---|
524 | }
|
---|
525 |
|
---|
526 | return dds;
|
---|
527 |
|
---|
528 | },
|
---|
529 |
|
---|
530 | getNormalMap: function ( image, depth ) {
|
---|
531 |
|
---|
532 | // Adapted from http://www.paulbrunt.co.uk/lab/heightnormal/
|
---|
533 |
|
---|
534 | var cross = function ( a, b ) {
|
---|
535 |
|
---|
536 | return [ a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ], a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ], a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ] ];
|
---|
537 |
|
---|
538 | }
|
---|
539 |
|
---|
540 | var subtract = function ( a, b ) {
|
---|
541 |
|
---|
542 | return [ a[ 0 ] - b[ 0 ], a[ 1 ] - b[ 1 ], a[ 2 ] - b[ 2 ] ];
|
---|
543 |
|
---|
544 | }
|
---|
545 |
|
---|
546 | var normalize = function ( a ) {
|
---|
547 |
|
---|
548 | var l = Math.sqrt( a[ 0 ] * a[ 0 ] + a[ 1 ] * a[ 1 ] + a[ 2 ] * a[ 2 ] );
|
---|
549 | return [ a[ 0 ] / l, a[ 1 ] / l, a[ 2 ] / l ];
|
---|
550 |
|
---|
551 | }
|
---|
552 |
|
---|
553 | depth = depth | 1;
|
---|
554 |
|
---|
555 | var width = image.width;
|
---|
556 | var height = image.height;
|
---|
557 |
|
---|
558 | var canvas = document.createElement( 'canvas' );
|
---|
559 | canvas.width = width;
|
---|
560 | canvas.height = height;
|
---|
561 |
|
---|
562 | var context = canvas.getContext( '2d' );
|
---|
563 | context.drawImage( image, 0, 0 );
|
---|
564 |
|
---|
565 | var data = context.getImageData( 0, 0, width, height ).data;
|
---|
566 | var imageData = context.createImageData( width, height );
|
---|
567 | var output = imageData.data;
|
---|
568 |
|
---|
569 | for ( var x = 0; x < width; x ++ ) {
|
---|
570 |
|
---|
571 | for ( var y = 0; y < height; y ++ ) {
|
---|
572 |
|
---|
573 | var ly = y - 1 < 0 ? 0 : y - 1;
|
---|
574 | var uy = y + 1 > height - 1 ? height - 1 : y + 1;
|
---|
575 | var lx = x - 1 < 0 ? 0 : x - 1;
|
---|
576 | var ux = x + 1 > width - 1 ? width - 1 : x + 1;
|
---|
577 |
|
---|
578 | var points = [];
|
---|
579 | var origin = [ 0, 0, data[ ( y * width + x ) * 4 ] / 255 * depth ];
|
---|
580 | points.push( [ - 1, 0, data[ ( y * width + lx ) * 4 ] / 255 * depth ] );
|
---|
581 | points.push( [ - 1, - 1, data[ ( ly * width + lx ) * 4 ] / 255 * depth ] );
|
---|
582 | points.push( [ 0, - 1, data[ ( ly * width + x ) * 4 ] / 255 * depth ] );
|
---|
583 | points.push( [ 1, - 1, data[ ( ly * width + ux ) * 4 ] / 255 * depth ] );
|
---|
584 | points.push( [ 1, 0, data[ ( y * width + ux ) * 4 ] / 255 * depth ] );
|
---|
585 | points.push( [ 1, 1, data[ ( uy * width + ux ) * 4 ] / 255 * depth ] );
|
---|
586 | points.push( [ 0, 1, data[ ( uy * width + x ) * 4 ] / 255 * depth ] );
|
---|
587 | points.push( [ - 1, 1, data[ ( uy * width + lx ) * 4 ] / 255 * depth ] );
|
---|
588 |
|
---|
589 | var normals = [];
|
---|
590 | var num_points = points.length;
|
---|
591 |
|
---|
592 | for ( var i = 0; i < num_points; i ++ ) {
|
---|
593 |
|
---|
594 | var v1 = points[ i ];
|
---|
595 | var v2 = points[ ( i + 1 ) % num_points ];
|
---|
596 | v1 = subtract( v1, origin );
|
---|
597 | v2 = subtract( v2, origin );
|
---|
598 | normals.push( normalize( cross( v1, v2 ) ) );
|
---|
599 |
|
---|
600 | }
|
---|
601 |
|
---|
602 | var normal = [ 0, 0, 0 ];
|
---|
603 |
|
---|
604 | for ( var i = 0; i < normals.length; i ++ ) {
|
---|
605 |
|
---|
606 | normal[ 0 ] += normals[ i ][ 0 ];
|
---|
607 | normal[ 1 ] += normals[ i ][ 1 ];
|
---|
608 | normal[ 2 ] += normals[ i ][ 2 ];
|
---|
609 |
|
---|
610 | }
|
---|
611 |
|
---|
612 | normal[ 0 ] /= normals.length;
|
---|
613 | normal[ 1 ] /= normals.length;
|
---|
614 | normal[ 2 ] /= normals.length;
|
---|
615 |
|
---|
616 | var idx = ( y * width + x ) * 4;
|
---|
617 |
|
---|
618 | output[ idx ] = ( ( normal[ 0 ] + 1.0 ) / 2.0 * 255 ) | 0;
|
---|
619 | output[ idx + 1 ] = ( ( normal[ 1 ] + 1.0 ) / 2.0 * 255 ) | 0;
|
---|
620 | output[ idx + 2 ] = ( normal[ 2 ] * 255 ) | 0;
|
---|
621 | output[ idx + 3 ] = 255;
|
---|
622 |
|
---|
623 | }
|
---|
624 |
|
---|
625 | }
|
---|
626 |
|
---|
627 | context.putImageData( imageData, 0, 0 );
|
---|
628 |
|
---|
629 | return canvas;
|
---|
630 |
|
---|
631 | },
|
---|
632 |
|
---|
633 | generateDataTexture: function ( width, height, color ) {
|
---|
634 |
|
---|
635 | var size = width * height;
|
---|
636 | var data = new Uint8Array( 3 * size );
|
---|
637 |
|
---|
638 | var r = Math.floor( color.r * 255 );
|
---|
639 | var g = Math.floor( color.g * 255 );
|
---|
640 | var b = Math.floor( color.b * 255 );
|
---|
641 |
|
---|
642 | for ( var i = 0; i < size; i ++ ) {
|
---|
643 |
|
---|
644 | data[ i * 3 ] = r;
|
---|
645 | data[ i * 3 + 1 ] = g;
|
---|
646 | data[ i * 3 + 2 ] = b;
|
---|
647 |
|
---|
648 | }
|
---|
649 |
|
---|
650 | var texture = new THREE.DataTexture( data, width, height, THREE.RGBFormat );
|
---|
651 | texture.needsUpdate = true;
|
---|
652 |
|
---|
653 | return texture;
|
---|
654 |
|
---|
655 | }
|
---|
656 |
|
---|
657 | };
|
---|