PageSourceSearch

https://www.asor.org/wp-content/plugins/woo-3d-viewer/includes/ext…/js/loaders/DRACOLoader.js?ver=1.8.5.3

js asor.org collected 2026-09-24 08:57:04 UTC 19,655 bytes, 521 lines download raw bytes

1// Copyright 2016 The Draco Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15'use strict';
16
17/**
18 * @param {THREEW.LoadingManager} manager
19 */
20THREEW.DRACOLoader = function(manager) {
21    this.timeLoaded = 0;
22    this.manager = manager || THREEW.DefaultLoadingManager;
23    this.materials = null;
24    this.verbosity = 0;
25    this.attributeOptions = {};
26    this.drawMode = THREEW.TrianglesDrawMode;
27    // Native Draco attribute type to THREEW.JS attribute type.
28    this.nativeAttributeMap = {
29      'position' : 'POSITION',
30      'normal' : 'NORMAL',
31      'color' : 'COLOR',
32      'uv' : 'TEX_COORD'
33    };
34};
35
36THREEW.DRACOLoader.prototype = {
37
38    constructor: THREEW.DRACOLoader,
39
40    load: function(url, onLoad, onProgress, onError) {
41        var scope = this;
42        var loader = new THREEW.FileLoader(scope.manager);
43        loader.setPath(this.path);
44        loader.setResponseType('arraybuffer');
45        loader.load(url, function(blob) {
46            scope.decodeDracoFile(blob, onLoad);
47        }, onProgress, onError);
48    },
49
50    setPath: function(value) {
51        this.path = value;
52        return this;
53    },
54
55    setVerbosity: function(level) {
56        this.verbosity = level;
57        return this;
58    },
59
60    /**
61     *  Sets desired mode for generated geometry indices.
62     *  Can be either:
63     *      THREEW.TrianglesDrawMode
64     *      THREEW.TriangleStripDrawMode
65     */
66    setDrawMode: function(drawMode) {
67        this.drawMode = drawMode;
68        return this;
69    },
70
71    /**
72     * Skips dequantization for a specific attribute.
73     * |attributeName| is the THREEW.js name of the given attribute type.
74     * The only currently supported |attributeName| is 'position', more may be
75     * added in future.
76     */
77    setSkipDequantization: function(attributeName, skip) {
78        var skipDequantization = true;
79        if (typeof skip !== 'undefined')
80          skipDequantization = skip;
81        this.getAttributeOptions(attributeName).skipDequantization =
82            skipDequantization;
83        return this;
84    },
85
86    /**
87     * Decompresses a Draco buffer. Names of attributes (for ID and type maps)
88     * must be one of the supported THREEW.js types, including: position, color,
89     * normal, uv, uv2, skinIndex, skinWeight.
90     *
91     * @param {ArrayBuffer} rawBuffer
92     * @param {Function} callback
93     * @param {Object|undefined} attributeUniqueIdMap Provides a pre-defined ID
94     *     for each attribute in the geometry to be decoded. If given,
95     *     `attributeTypeMap` is required and `nativeAttributeMap` will be
96     *     ignored.
97     * @param {Object|undefined} attributeTypeMap Provides a predefined data
98     *     type (as a typed array constructor) for each attribute in the
99     *     geometry to be decoded.
100     */
101    decodeDracoFile: function(rawBuffer, callback, attributeUniqueIdMap,
102                              attributeTypeMap) {
103      var scope = this;
104      THREEW.DRACOLoader.getDecoderModule()
105          .then( function ( module ) {
106            scope.decodeDracoFileInternal( rawBuffer, module.decoder, callback,
107              attributeUniqueIdMap, attributeTypeMap);
108          });
109    },
110
111    decodeDracoFileInternal: function(rawBuffer, dracoDecoder, callback,
112                                      attributeUniqueIdMap, attributeTypeMap) {
113      /*
114       * Here is how to use Draco Javascript decoder and get the geometry.
115       */
116      var buffer = new dracoDecoder.DecoderBuffer();
117      buffer.Init(new Int8Array(rawBuffer), rawBuffer.byteLength);
118      var decoder = new dracoDecoder.Decoder();
119
120      /*
121       * Determine what type is this file: mesh or point cloud.
122       */
123      var geometryType = decoder.GetEncodedGeometryType(buffer);
124      if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
125        if (this.verbosity > 0) {
126          console.log('Loaded a mesh.');
127        }
128      } else if (geometryType == dracoDecoder.POINT_CLOUD) {
129        if (this.verbosity > 0) {
130          console.log('Loaded a point cloud.');
131        }
132      } else {
133        var errorMsg = 'THREEW.DRACOLoader: Unknown geometry type.';
134        console.error(errorMsg);
135        throw new Error(errorMsg);
136      }
137      callback(this.convertDracoGeometryTo3JS(dracoDecoder, decoder,
138          geometryType, buffer, attributeUniqueIdMap, attributeTypeMap));
139    },
140
141    addAttributeToGeometry: function(dracoDecoder, decoder, dracoGeometry,
142                                     attributeName, attributeType, attribute,
143                                     geometry, geometryBuffer) {
144      if (attribute.ptr === 0) {
145        var errorMsg = 'THREEW.DRACOLoader: No attribute ' + attributeName;
146        console.error(errorMsg);
147        throw new Error(errorMsg);
148      }
149
150      var numComponents = attribute.num_components();
151      var numPoints = dracoGeometry.num_points();
152      var numValues = numPoints * numComponents;
153      var attributeData;
154      var TypedBufferAttribute;
155
156      switch ( attributeType ) {
157
158        case Float32Array:
159          attributeData = new dracoDecoder.DracoFloat32Array();
160          decoder.GetAttributeFloatForAllPoints(
161            dracoGeometry, attribute, attributeData);
162          geometryBuffer[ attributeName ] = new Float32Array( numValues );
163          TypedBufferAttribute = THREEW.Float32BufferAttribute;
164          break;
165
166        case Int8Array:
167          attributeData = new dracoDecoder.DracoInt8Array();
168          decoder.GetAttributeInt8ForAllPoints(
169            dracoGeometry, attribute, attributeData );
170          geometryBuffer[ attributeName ] = new Int8Array( numValues );
171          TypedBufferAttribute = THREEW.Int8BufferAttribute;
172          break;
173
174        case Int16Array:
175          attributeData = new dracoDecoder.DracoInt16Array();
176          decoder.GetAttributeInt16ForAllPoints(
177            dracoGeometry, attribute, attributeData);
178          geometryBuffer[ attributeName ] = new Int16Array( numValues );
179          TypedBufferAttribute = THREEW.Int16BufferAttribute;
180          break;
181
182        case Int32Array:
183          attributeData = new dracoDecoder.DracoInt32Array();
184          decoder.GetAttributeInt32ForAllPoints(
185            dracoGeometry, attribute, attributeData);
186          geometryBuffer[ attributeName ] = new Int32Array( numValues );
187          TypedBufferAttribute = THREEW.Int32BufferAttribute;
188          break;
189
190        case Uint8Array:
191          attributeData = new dracoDecoder.DracoUInt8Array();
192          decoder.GetAttributeUInt8ForAllPoints(
193            dracoGeometry, attribute, attributeData);
194          geometryBuffer[ attributeName ] = new Uint8Array( numValues );
195          TypedBufferAttribute = THREEW.Uint8BufferAttribute;
196          break;
197
198        case Uint16Array:
199          attributeData = new dracoDecoder.DracoUInt16Array();
200          decoder.GetAttributeUInt16ForAllPoints(
201            dracoGeometry, attribute, attributeData);
202          geometryBuffer[ attributeName ] = new Uint16Array( numValues );
203          TypedBufferAttribute = THREEW.Uint16BufferAttribute;
204          break;
205
206        case Uint32Array:
207          attributeData = new dracoDecoder.DracoUInt32Array();
208          decoder.GetAttributeUInt32ForAllPoints(
209            dracoGeometry, attribute, attributeData);
210          geometryBuffer[ attributeName ] = new Uint32Array( numValues );
211          TypedBufferAttribute = THREEW.Uint32BufferAttribute;
212          break;
213
214        default:
215          var errorMsg = 'THREEW.DRACOLoader: Unexpected attribute type.';
216          console.error( errorMsg );
217          throw new Error( errorMsg );
218
219      }
220
221      // Copy data from decoder.
222      for (var i = 0; i < numValues; i++) {
223        geometryBuffer[attributeName][i] = attributeData.GetValue(i);
224      }
225      // Add attribute to THREEJS geometry for rendering.
226      geometry.addAttribute(attributeName,
227          new TypedBufferAttribute(geometryBuffer[attributeName],
228            numComponents));
229      dracoDecoder.destroy(attributeData);
230    },
231
232    convertDracoGeometryTo3JS: function(dracoDecoder, decoder, geometryType,
233                                        buffer, attributeUniqueIdMap,
234                                        attributeTypeMap) {
235        // TODO: Should not assume native Draco attribute IDs apply.
236        if (this.getAttributeOptions('position').skipDequantization === true) {
237          decoder.SkipAttributeTransform(dracoDecoder.POSITION);
238        }
239        var dracoGeometry;
240        var decodingStatus;
241        var start_time = performance.now();
242        if (geometryType === dracoDecoder.TRIANGULAR_MESH) {
243          dracoGeometry = new dracoDecoder.Mesh();
244          decodingStatus = decoder.DecodeBufferToMesh(buffer, dracoGeometry);
245        } else {
246          dracoGeometry = new dracoDecoder.PointCloud();
247          decodingStatus =
248              decoder.DecodeBufferToPointCloud(buffer, dracoGeometry);
249        }
250        if (!decodingStatus.ok() || dracoGeometry.ptr == 0) {
251          var errorMsg = 'THREEW.DRACOLoader: Decoding failed: ';
252          errorMsg += decodingStatus.error_msg();
253          console.error(errorMsg);
254          dracoDecoder.destroy(decoder);
255          dracoDecoder.destroy(dracoGeometry);
256          throw new Error(errorMsg);
257        }
258
259        var decode_end = performance.now();
260        dracoDecoder.destroy(buffer);
261        /*
262         * Example on how to retrieve mesh and attributes.
263         */
264        var numFaces;
265        if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
266          numFaces = dracoGeometry.num_faces();
267          if (this.verbosity > 0) {
268            console.log('Number of faces loaded: ' + numFaces.toString());
269          }
270        } else {
271          numFaces = 0;
272        }
273
274        var numPoints = dracoGeometry.num_points();
275        var numAttributes = dracoGeometry.num_attributes();
276        if (this.verbosity > 0) {
277          console.log('Number of points loaded: ' + numPoints.toString());
278          console.log('Number of attributes loaded: ' +
279              numAttributes.toString());
280        }
281
282        // Verify if there is position attribute.
283        // TODO: Should not assume native Draco attribute IDs apply.
284        var posAttId = decoder.GetAttributeId(dracoGeometry,
285                                              dracoDecoder.POSITION);
286        if (posAttId == -1) {
287          var errorMsg = 'THREEW.DRACOLoader: No position attribute found.';
288          console.error(errorMsg);
289          dracoDecoder.destroy(decoder);
290          dracoDecoder.destroy(dracoGeometry);
291          throw new Error(errorMsg);
292        }
293        var posAttribute = decoder.GetAttribute(dracoGeometry, posAttId);
294
295        // Structure for converting to THREEJS geometry later.
296        var geometryBuffer = {};
297        // Import data to Three JS geometry.
298        var geometry = new THREEW.BufferGeometry();
299
300        // Do not use both the native attribute map and a provided (e.g. glTF) map.
301        if ( attributeUniqueIdMap ) {
302
303          // Add attributes of user specified unique id. E.g. GLTF models.
304          for (var attributeName in attributeUniqueIdMap) {
305            var attributeType = attributeTypeMap[attributeName];
306            var attributeId = attributeUniqueIdMap[attributeName];
307            var attribute = decoder.GetAttributeByUniqueId(dracoGeometry,
308                                                           attributeId);
309            this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
310                attributeName, attributeType, attribute, geometry, geometryBuffer);
311          }
312
313        } else {
314
315          // Add native Draco attribute type to geometry.
316          for (var attributeName in this.nativeAttributeMap) {
317            var attId = decoder.GetAttributeId(dracoGeometry,
318                dracoDecoder[this.nativeAttributeMap[attributeName]]);
319            if (attId !== -1) {
320              if (this.verbosity > 0) {
321                console.log('Loaded ' + attributeName + ' attribute.');
322              }
323              var attribute = decoder.GetAttribute(dracoGeometry, attId);
324              this.addAttributeToGeometry(dracoDecoder, decoder, dracoGeometry,
325                  attributeName, Float32Array, attribute, geometry, geometryBuffer);
326            }
327          }
328
329        }
330
331        // For mesh, we need to generate the faces.
332        if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
333          if (this.drawMode === THREEW.TriangleStripDrawMode) {
334            var stripsArray = new dracoDecoder.DracoInt32Array();
335            var numStrips = decoder.GetTriangleStripsFromMesh(
336                dracoGeometry, stripsArray);
337            geometryBuffer.indices = new Uint32Array(stripsArray.size());
338            for (var i = 0; i < stripsArray.size(); ++i) {
339              geometryBuffer.indices[i] = stripsArray.GetValue(i);
340            }
341            dracoDecoder.destroy(stripsArray);
342          } else {
343            var numIndices = numFaces * 3;
344            geometryBuffer.indices = new Uint32Array(numIndices);
345            var ia = new dracoDecoder.DracoInt32Array();
346            for (var i = 0; i < numFaces; ++i) {
347              decoder.GetFaceFromMesh(dracoGeometry, i, ia);
348              var index = i * 3;
349              geometryBuffer.indices[index] = ia.GetValue(0);
350              geometryBuffer.indices[index + 1] = ia.GetValue(1);
351              geometryBuffer.indices[index + 2] = ia.GetValue(2);
352            }
353            dracoDecoder.destroy(ia);
354         }
355        }
356
357        geometry.drawMode = this.drawMode;
358        if (geometryType == dracoDecoder.TRIANGULAR_MESH) {
359          geometry.setIndex(new(geometryBuffer.indices.length > 65535 ?
360                THREEW.Uint32BufferAttribute : THREEW.Uint16BufferAttribute)
361              (geometryBuffer.indices, 1));
362        }
363
364        // TODO: Should not assume native Draco attribute IDs apply.
365        // TODO: Can other attribute types be quantized?
366        var posTransform = new dracoDecoder.AttributeQuantizationTransform();
367        if (posTransform.InitFromAttribute(posAttribute)) {
368          // Quantized attribute. Store the quantization parameters into the
369          // THREEW.js attribute.
370          geometry.attributes['position'].isQuantized = true;
371          geometry.attributes['position'].maxRange = posTransform.range();
372          geometry.attributes['position'].numQuantizationBits =
373              posTransform.quantization_bits();
374          geometry.attributes['position'].minValues = new Float32Array(3);
375          for (var i = 0; i < 3; ++i) {
376            geometry.attributes['position'].minValues[i] =
377                posTransform.min_value(i);
378          }
379        }
380        dracoDecoder.destroy(posTransform);
381        dracoDecoder.destroy(decoder);
382        dracoDecoder.destroy(dracoGeometry);
383
384        this.decode_time = decode_end - start_time;
385        this.import_time = performance.now() - decode_end;
386
387        if (this.verbosity > 0) {
388          console.log('Decode time: ' + this.decode_time);
389          console.log('Import time: ' + this.import_time);
390        }
391        return geometry;
392    },
393
394    isVersionSupported: function(version, callback) {
395        THREEW.DRACOLoader.getDecoderModule()
396            .then( function ( module ) {
397              callback( module.decoder.isVersionSupported( version ) );
398            });
399    },
400
401    getAttributeOptions: function(attributeName) {
402        if (typeof this.attributeOptions[attributeName] === 'undefined')
403          this.attributeOptions[attributeName] = {};
404        return this.attributeOptions[attributeName];
405    }
406};
407
408THREEW.DRACOLoader.decoderPath = './';
409THREEW.DRACOLoader.decoderConfig = {};
410THREEW.DRACOLoader.decoderModulePromise = null;
411
412/**
413 * Sets the base path for decoder source files.
414 * @param {string} path
415 */
416THREEW.DRACOLoader.setDecoderPath = function ( path ) {
417  THREEW.DRACOLoader.decoderPath = path;
418};
419
420/**
421 * Sets decoder configuration and releases singleton decoder module. Module
422 * will be recreated with the next decoding call.
423 * @param {Object} config
424 */
425THREEW.DRACOLoader.setDecoderConfig = function ( config ) {
426  var wasmBinary = THREEW.DRACOLoader.decoderConfig.wasmBinary;
427  THREEW.DRACOLoader.decoderConfig = config || {};
428  THREEW.DRACOLoader.releaseDecoderModule();
429
430  // Reuse WASM binary.
431  if ( wasmBinary ) THREEW.DRACOLoader.decoderConfig.wasmBinary = wasmBinary;
432};
433
434/**
435 * Releases the singleton DracoDecoderModule instance. Module will be recreated
436 * with the next decoding call.
437 */
438THREEW.DRACOLoader.releaseDecoderModule = function () {
439  THREEW.DRACOLoader.decoderModulePromise = null;
440};
441
442/**
443 * Gets WebAssembly or asm.js singleton instance of DracoDecoderModule
444 * after testing for browser support. Returns Promise that resolves when
445 * module is available.
446 * @return {Promise<{decoder: DracoDecoderModule}>}
447 */
448THREEW.DRACOLoader.getDecoderModule = function () {
449  var scope = this;
450  var path = THREEW.DRACOLoader.decoderPath;
451  var config = THREEW.DRACOLoader.decoderConfig;
452  var promise = THREEW.DRACOLoader.decoderModulePromise;
453
454  if ( promise ) return promise;
455
456  // Load source files.
457  if ( typeof DracoDecoderModule !== 'undefined' ) {
458    // Loaded externally.
459    promise = Promise.resolve();
460  } else if ( typeof WebAssembly !== 'object' || config.type === 'js' ) {
461    // Load with asm.js.
462    promise = THREEW.DRACOLoader._loadScript( path + 'draco_decoder.js' );
463  } else {
464    // Load with WebAssembly.
465    config.wasmBinaryFile = path + 'draco_decoder.wasm';
466    promise = THREEW.DRACOLoader._loadScript( path + 'draco_wasm_wrapper.js' )
467        .then( function () {
468          return THREEW.DRACOLoader._loadArrayBuffer( config.wasmBinaryFile );
469        } )
470        .then( function ( wasmBinary ) {
471          config.wasmBinary = wasmBinary;
472        } );
473  }
474
475  // Wait for source files, then create and return a decoder.
476  promise = promise.then( function () {
477    return new Promise( function ( resolve ) {
478      config.onModuleLoaded = function ( decoder ) {
479        scope.timeLoaded = performance.now();
480        // Module is Promise-like. Wrap before resolving to avoid loop.
481        resolve( { decoder: decoder } );
482      };
483      DracoDecoderModule( config );
484    } );
485  } );
486
487  THREEW.DRACOLoader.decoderModulePromise = promise;
488  return promise;
489};
490
491/**
492 * @param {string} src
493 * @return {Promise}
494 */
495THREEW.DRACOLoader._loadScript = function ( src ) {
496  var prevScript = document.getElementById( 'decoder_script' );
497  if ( prevScript !== null ) {
498    prevScript.parentNode.removeChild( prevScript );
499  }
500  var head = document.getElementsByTagName( 'head' )[ 0 ];
501  var script = document.createElement( 'script' );
502  script.id = 'decoder_script';
503  script.type = 'text/javascript';
504  script.src = src;
505  return new Promise( function ( resolve ) {
506    script.onload = resolve;
507    head.appendChild( script );
508  });
509};
510
511/**
512 * @param {string} src
513 * @return {Promise}
514 */
515THREEW.DRACOLoader._loadArrayBuffer = function ( src ) {
516  var loader = new THREEW.FileLoader();
517  loader.setResponseType( 'arraybuffer' );
518  return new Promise( function( resolve, reject ) {
519    loader.load( src, resolve, undefined, reject );
520  });
521};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.