source: other-projects/playing-in-the-street/summer-2013/trunk/Playing-in-the-Street-WPF/Content/Web/mrdoob-three.js-4862f5f/src/extras/ImageUtils.js@ 28897

Last change on this file since 28897 was 28897, checked in by davidb, 10 years ago

GUI front-end to server base plus web page content

File size: 14.5 KB
Line 
1/**
2 * @author alteredq / http://alteredqualia.com/
3 * @author mrdoob / http://mrdoob.com/
4 */
5
6THREE.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};
Note: See TracBrowser for help on using the repository browser.