kopia lustrzana https://github.com/OpenDroneMap/WebODM
Conditionally load GLBs if available
rodzic
9a11f70f9b
commit
aa9e9fe5bb
|
@ -14,6 +14,8 @@ import { _, interpolate } from './classes/gettext';
|
||||||
require('./vendor/OBJLoader');
|
require('./vendor/OBJLoader');
|
||||||
require('./vendor/MTLLoader');
|
require('./vendor/MTLLoader');
|
||||||
require('./vendor/ColladaLoader');
|
require('./vendor/ColladaLoader');
|
||||||
|
require('./vendor/GLTFLoader');
|
||||||
|
require('./vendor/DRACOLoader');
|
||||||
|
|
||||||
class SetCameraView extends React.Component{
|
class SetCameraView extends React.Component{
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -150,14 +152,10 @@ class ModelView extends React.Component {
|
||||||
this.pointCloud = null;
|
this.pointCloud = null;
|
||||||
this.modelReference = null;
|
this.modelReference = null;
|
||||||
|
|
||||||
this.toggleTexturedModel = this.toggleTexturedModel.bind(this);
|
|
||||||
this.toggleCameras = this.toggleCameras.bind(this);
|
|
||||||
|
|
||||||
|
|
||||||
this.cameraMeshes = [];
|
this.cameraMeshes = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
assetsPath(){
|
assetsPath = () => {
|
||||||
return `/api/projects/${this.props.task.project}/tasks/${this.props.task.id}/assets`
|
return `/api/projects/${this.props.task.project}/tasks/${this.props.task.id}/assets`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,23 +222,28 @@ class ModelView extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
texturedModelDirectoryPath(){
|
texturedModelDirectoryPath = () => {
|
||||||
return this.assetsPath() + '/odm_texturing/';
|
return this.assetsPath() + '/odm_texturing/';
|
||||||
}
|
}
|
||||||
|
|
||||||
hasGeoreferencedAssets(){
|
hasGeoreferencedAssets = () => {
|
||||||
return this.props.task.available_assets.indexOf('orthophoto.tif') !== -1;
|
return this.props.task.available_assets.indexOf('orthophoto.tif') !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasTexturedModel(){
|
hasTexturedModel = () => {
|
||||||
return this.props.task.available_assets.indexOf('textured_model.zip') !== -1;
|
return this.props.task.available_assets.indexOf('textured_model.zip') !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasCameras(){
|
getTexturedModelType = () => {
|
||||||
|
if (this.props.task.available_assets.indexOf('textured_model.glb') !== -1) return 'gltf';
|
||||||
|
else return 'obj';
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCameras = () => {
|
||||||
return this.props.task.available_assets.indexOf('shots.geojson') !== -1;
|
return this.props.task.available_assets.indexOf('shots.geojson') !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
objFilePath(cb){
|
objFilePath = (cb) => {
|
||||||
// Mostly for backward compatibility
|
// Mostly for backward compatibility
|
||||||
// as newer versions of ODM do not have
|
// as newer versions of ODM do not have
|
||||||
// a odm_textured_model.obj
|
// a odm_textured_model.obj
|
||||||
|
@ -257,7 +260,11 @@ class ModelView extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mtlFilename(cb){
|
glbFilePath = () => {
|
||||||
|
return this.texturedModelDirectoryPath() + 'odm_textured_model_geo.glb';
|
||||||
|
}
|
||||||
|
|
||||||
|
mtlFilename = (cb) => {
|
||||||
// Mostly for backward compatibility
|
// Mostly for backward compatibility
|
||||||
// as newer versions of ODM do not have
|
// as newer versions of ODM do not have
|
||||||
// a odm_textured_model.mtl
|
// a odm_textured_model.mtl
|
||||||
|
@ -573,7 +580,7 @@ class ModelView extends React.Component {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCameras(e){
|
toggleCameras = (e) => {
|
||||||
if (this.cameraMeshes.length === 0){
|
if (this.cameraMeshes.length === 0){
|
||||||
this.loadCameras();
|
this.loadCameras();
|
||||||
if (this.cameraMeshes.length === 0) return;
|
if (this.cameraMeshes.length === 0) return;
|
||||||
|
@ -583,7 +590,25 @@ class ModelView extends React.Component {
|
||||||
this.cameraMeshes.forEach(cam => cam.visible = !isVisible);
|
this.cameraMeshes.forEach(cam => cam.visible = !isVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleTexturedModel(e){
|
loadGltf = (url, cb) => {
|
||||||
|
if (!this.gltfLoader) this.gltfLoader = new THREE.GLTFLoader();
|
||||||
|
if (!this.dracoLoader) {
|
||||||
|
this.dracoLoader = new THREE.DRACOLoader();
|
||||||
|
this.dracoLoader.setDecoderPath( '/static/app/js/vendor/draco/' );
|
||||||
|
this.gltfLoader.setDRACOLoader( this.dracoLoader );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a glTF resource
|
||||||
|
this.gltfLoader.load(url,
|
||||||
|
gltf => { cb(null, gltf) },
|
||||||
|
xhr => {
|
||||||
|
// called while loading is progressing
|
||||||
|
},
|
||||||
|
error => { cb(error); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleTexturedModel = (e) => {
|
||||||
const value = e.target.checked;
|
const value = e.target.checked;
|
||||||
|
|
||||||
if (value){
|
if (value){
|
||||||
|
@ -592,34 +617,57 @@ class ModelView extends React.Component {
|
||||||
|
|
||||||
this.setState({initializingModel: true});
|
this.setState({initializingModel: true});
|
||||||
|
|
||||||
const mtlLoader = new THREE.MTLLoader();
|
const addObject = (object, offset) => {
|
||||||
mtlLoader.setPath(this.texturedModelDirectoryPath());
|
object.translateX(offset.x);
|
||||||
|
object.translateY(offset.y);
|
||||||
|
|
||||||
this.mtlFilename(mtlPath => {
|
viewer.scene.scene.add(object);
|
||||||
mtlLoader.load(mtlPath, (materials) => {
|
|
||||||
materials.preload();
|
this.modelReference = object;
|
||||||
|
this.setPointCloudsVisible(false);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
initializingModel: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getTexturedModelType() === 'gltf'){
|
||||||
|
this.loadGltf(this.glbFilePath(), (err, gltf) => {
|
||||||
|
if (err){
|
||||||
|
this.setState({initializingModel: false, error: err});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset = {
|
||||||
|
x: gltf.scene.CESIUM_RTC.center[0],
|
||||||
|
y: gltf.scene.CESIUM_RTC.center[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
addObject(gltf.scene, offset);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
// Legacy OBJ
|
||||||
|
|
||||||
|
const mtlLoader = new THREE.MTLLoader();
|
||||||
|
mtlLoader.setPath(this.texturedModelDirectoryPath());
|
||||||
|
|
||||||
const objLoader = new THREE.OBJLoader();
|
this.mtlFilename(mtlPath => {
|
||||||
objLoader.setMaterials(materials);
|
mtlLoader.load(mtlPath, (materials) => {
|
||||||
this.objFilePath(filePath => {
|
materials.preload();
|
||||||
objLoader.load(filePath, (object) => {
|
|
||||||
this.loadGeoreferencingOffset((offset) => {
|
const objLoader = new THREE.OBJLoader();
|
||||||
object.translateX(offset.x);
|
objLoader.setMaterials(materials);
|
||||||
object.translateY(offset.y);
|
this.objFilePath(filePath => {
|
||||||
|
objLoader.load(filePath, (object) => {
|
||||||
viewer.scene.scene.add(object);
|
this.loadGeoreferencingOffset((offset) => {
|
||||||
|
console.log(object);
|
||||||
this.modelReference = object;
|
addObject(object, offset);
|
||||||
this.setPointCloudsVisible(false);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
initializingModel: false,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}else{
|
}else{
|
||||||
// Already initialized
|
// Already initialized
|
||||||
this.modelReference.visible = true;
|
this.modelReference.visible = true;
|
||||||
|
|
|
@ -0,0 +1,640 @@
|
||||||
|
const {
|
||||||
|
BufferAttribute,
|
||||||
|
BufferGeometry,
|
||||||
|
FileLoader,
|
||||||
|
Loader
|
||||||
|
} = THREE;
|
||||||
|
|
||||||
|
var DRACOLoader = function ( manager ) {
|
||||||
|
|
||||||
|
Loader.call( this, manager );
|
||||||
|
|
||||||
|
this.decoderPath = '';
|
||||||
|
this.decoderConfig = {};
|
||||||
|
this.decoderBinary = null;
|
||||||
|
this.decoderPending = null;
|
||||||
|
|
||||||
|
this.workerLimit = 4;
|
||||||
|
this.workerPool = [];
|
||||||
|
this.workerNextTaskID = 1;
|
||||||
|
this.workerSourceURL = '';
|
||||||
|
|
||||||
|
this.defaultAttributeIDs = {
|
||||||
|
position: 'POSITION',
|
||||||
|
normal: 'NORMAL',
|
||||||
|
color: 'COLOR',
|
||||||
|
uv: 'TEX_COORD'
|
||||||
|
};
|
||||||
|
this.defaultAttributeTypes = {
|
||||||
|
position: 'Float32Array',
|
||||||
|
normal: 'Float32Array',
|
||||||
|
color: 'Float32Array',
|
||||||
|
uv: 'Float32Array'
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
DRACOLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
|
||||||
|
|
||||||
|
constructor: DRACOLoader,
|
||||||
|
|
||||||
|
setDecoderPath: function ( path ) {
|
||||||
|
|
||||||
|
this.decoderPath = path;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
setDecoderConfig: function ( config ) {
|
||||||
|
|
||||||
|
this.decoderConfig = config;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
setWorkerLimit: function ( workerLimit ) {
|
||||||
|
|
||||||
|
this.workerLimit = workerLimit;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
|
setVerbosity: function () {
|
||||||
|
|
||||||
|
console.warn( 'THREE.DRACOLoader: The .setVerbosity() method has been removed.' );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
|
setDrawMode: function () {
|
||||||
|
|
||||||
|
console.warn( 'THREE.DRACOLoader: The .setDrawMode() method has been removed.' );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
|
setSkipDequantization: function () {
|
||||||
|
|
||||||
|
console.warn( 'THREE.DRACOLoader: The .setSkipDequantization() method has been removed.' );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
load: function ( url, onLoad, onProgress, onError ) {
|
||||||
|
|
||||||
|
var loader = new FileLoader( this.manager );
|
||||||
|
|
||||||
|
loader.setPath( this.path );
|
||||||
|
loader.setResponseType( 'arraybuffer' );
|
||||||
|
loader.setRequestHeader( this.requestHeader );
|
||||||
|
loader.setWithCredentials( this.withCredentials );
|
||||||
|
|
||||||
|
loader.load( url, ( buffer ) => {
|
||||||
|
|
||||||
|
var taskConfig = {
|
||||||
|
attributeIDs: this.defaultAttributeIDs,
|
||||||
|
attributeTypes: this.defaultAttributeTypes,
|
||||||
|
useUniqueIDs: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.decodeGeometry( buffer, taskConfig )
|
||||||
|
.then( onLoad )
|
||||||
|
.catch( onError );
|
||||||
|
|
||||||
|
}, onProgress, onError );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
/** @deprecated Kept for backward-compatibility with previous DRACOLoader versions. */
|
||||||
|
decodeDracoFile: function ( buffer, callback, attributeIDs, attributeTypes ) {
|
||||||
|
|
||||||
|
var taskConfig = {
|
||||||
|
attributeIDs: attributeIDs || this.defaultAttributeIDs,
|
||||||
|
attributeTypes: attributeTypes || this.defaultAttributeTypes,
|
||||||
|
useUniqueIDs: !! attributeIDs
|
||||||
|
};
|
||||||
|
|
||||||
|
this.decodeGeometry( buffer, taskConfig ).then( callback );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
decodeGeometry: function ( buffer, taskConfig ) {
|
||||||
|
|
||||||
|
// TODO: For backward-compatibility, support 'attributeTypes' objects containing
|
||||||
|
// references (rather than names) to typed array constructors. These must be
|
||||||
|
// serialized before sending them to the worker.
|
||||||
|
for ( var attribute in taskConfig.attributeTypes ) {
|
||||||
|
|
||||||
|
var type = taskConfig.attributeTypes[ attribute ];
|
||||||
|
|
||||||
|
if ( type.BYTES_PER_ELEMENT !== undefined ) {
|
||||||
|
|
||||||
|
taskConfig.attributeTypes[ attribute ] = type.name;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
var taskKey = JSON.stringify( taskConfig );
|
||||||
|
|
||||||
|
// Check for an existing task using this buffer. A transferred buffer cannot be transferred
|
||||||
|
// again from this thread.
|
||||||
|
if ( DRACOLoader.taskCache.has( buffer ) ) {
|
||||||
|
|
||||||
|
var cachedTask = DRACOLoader.taskCache.get( buffer );
|
||||||
|
|
||||||
|
if ( cachedTask.key === taskKey ) {
|
||||||
|
|
||||||
|
return cachedTask.promise;
|
||||||
|
|
||||||
|
} else if ( buffer.byteLength === 0 ) {
|
||||||
|
|
||||||
|
// Technically, it would be possible to wait for the previous task to complete,
|
||||||
|
// transfer the buffer back, and decode again with the second configuration. That
|
||||||
|
// is complex, and I don't know of any reason to decode a Draco buffer twice in
|
||||||
|
// different ways, so this is left unimplemented.
|
||||||
|
throw new Error(
|
||||||
|
|
||||||
|
'THREE.DRACOLoader: Unable to re-decode a buffer with different ' +
|
||||||
|
'settings. Buffer has already been transferred.'
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
var worker;
|
||||||
|
var taskID = this.workerNextTaskID ++;
|
||||||
|
var taskCost = buffer.byteLength;
|
||||||
|
|
||||||
|
// Obtain a worker and assign a task, and construct a geometry instance
|
||||||
|
// when the task completes.
|
||||||
|
var geometryPending = this._getWorker( taskID, taskCost )
|
||||||
|
.then( ( _worker ) => {
|
||||||
|
|
||||||
|
worker = _worker;
|
||||||
|
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
|
||||||
|
worker._callbacks[ taskID ] = { resolve, reject };
|
||||||
|
|
||||||
|
worker.postMessage( { type: 'decode', id: taskID, taskConfig, buffer }, [ buffer ] );
|
||||||
|
|
||||||
|
// this.debug();
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
} )
|
||||||
|
.then( ( message ) => this._createGeometry( message.geometry ) );
|
||||||
|
|
||||||
|
// Remove task from the task list.
|
||||||
|
// Note: replaced '.finally()' with '.catch().then()' block - iOS 11 support (#19416)
|
||||||
|
geometryPending
|
||||||
|
.catch( () => true )
|
||||||
|
.then( () => {
|
||||||
|
|
||||||
|
if ( worker && taskID ) {
|
||||||
|
|
||||||
|
this._releaseTask( worker, taskID );
|
||||||
|
|
||||||
|
// this.debug();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Cache the task result.
|
||||||
|
DRACOLoader.taskCache.set( buffer, {
|
||||||
|
|
||||||
|
key: taskKey,
|
||||||
|
promise: geometryPending
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
return geometryPending;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_createGeometry: function ( geometryData ) {
|
||||||
|
|
||||||
|
var geometry = new BufferGeometry();
|
||||||
|
|
||||||
|
if ( geometryData.index ) {
|
||||||
|
|
||||||
|
geometry.setIndex( new BufferAttribute( geometryData.index.array, 1 ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( var i = 0; i < geometryData.attributes.length; i ++ ) {
|
||||||
|
|
||||||
|
var attribute = geometryData.attributes[ i ];
|
||||||
|
var name = attribute.name;
|
||||||
|
var array = attribute.array;
|
||||||
|
var itemSize = attribute.itemSize;
|
||||||
|
|
||||||
|
geometry.setAttribute( name, new BufferAttribute( array, itemSize ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return geometry;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_loadLibrary: function ( url, responseType ) {
|
||||||
|
|
||||||
|
var loader = new FileLoader( this.manager );
|
||||||
|
loader.setPath( this.decoderPath );
|
||||||
|
loader.setResponseType( responseType );
|
||||||
|
loader.setWithCredentials( this.withCredentials );
|
||||||
|
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
|
||||||
|
loader.load( url, resolve, undefined, reject );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
preload: function () {
|
||||||
|
|
||||||
|
this._initDecoder();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_initDecoder: function () {
|
||||||
|
|
||||||
|
if ( this.decoderPending ) return this.decoderPending;
|
||||||
|
|
||||||
|
var useJS = typeof WebAssembly !== 'object' || this.decoderConfig.type === 'js';
|
||||||
|
var librariesPending = [];
|
||||||
|
|
||||||
|
if ( useJS ) {
|
||||||
|
|
||||||
|
librariesPending.push( this._loadLibrary( 'draco_decoder.js', 'text' ) );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
librariesPending.push( this._loadLibrary( 'draco_wasm_wrapper.js', 'text' ) );
|
||||||
|
librariesPending.push( this._loadLibrary( 'draco_decoder.wasm', 'arraybuffer' ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.decoderPending = Promise.all( librariesPending )
|
||||||
|
.then( ( libraries ) => {
|
||||||
|
|
||||||
|
var jsContent = libraries[ 0 ];
|
||||||
|
|
||||||
|
if ( ! useJS ) {
|
||||||
|
|
||||||
|
this.decoderConfig.wasmBinary = libraries[ 1 ];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var fn = DRACOLoader.DRACOWorker.toString();
|
||||||
|
|
||||||
|
var body = [
|
||||||
|
'/* draco decoder */',
|
||||||
|
jsContent,
|
||||||
|
'',
|
||||||
|
'/* worker */',
|
||||||
|
fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
|
||||||
|
].join( '\n' );
|
||||||
|
|
||||||
|
this.workerSourceURL = URL.createObjectURL( new Blob( [ body ] ) );
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
return this.decoderPending;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_getWorker: function ( taskID, taskCost ) {
|
||||||
|
|
||||||
|
return this._initDecoder().then( () => {
|
||||||
|
|
||||||
|
if ( this.workerPool.length < this.workerLimit ) {
|
||||||
|
|
||||||
|
var worker = new Worker( this.workerSourceURL );
|
||||||
|
|
||||||
|
worker._callbacks = {};
|
||||||
|
worker._taskCosts = {};
|
||||||
|
worker._taskLoad = 0;
|
||||||
|
|
||||||
|
worker.postMessage( { type: 'init', decoderConfig: this.decoderConfig } );
|
||||||
|
|
||||||
|
worker.onmessage = function ( e ) {
|
||||||
|
|
||||||
|
var message = e.data;
|
||||||
|
|
||||||
|
switch ( message.type ) {
|
||||||
|
|
||||||
|
case 'decode':
|
||||||
|
worker._callbacks[ message.id ].resolve( message );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
worker._callbacks[ message.id ].reject( message );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
console.error( 'THREE.DRACOLoader: Unexpected message, "' + message.type + '"' );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.workerPool.push( worker );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
this.workerPool.sort( function ( a, b ) {
|
||||||
|
|
||||||
|
return a._taskLoad > b._taskLoad ? - 1 : 1;
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var worker = this.workerPool[ this.workerPool.length - 1 ];
|
||||||
|
worker._taskCosts[ taskID ] = taskCost;
|
||||||
|
worker._taskLoad += taskCost;
|
||||||
|
return worker;
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_releaseTask: function ( worker, taskID ) {
|
||||||
|
|
||||||
|
worker._taskLoad -= worker._taskCosts[ taskID ];
|
||||||
|
delete worker._callbacks[ taskID ];
|
||||||
|
delete worker._taskCosts[ taskID ];
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
debug: function () {
|
||||||
|
|
||||||
|
console.log( 'Task load: ', this.workerPool.map( ( worker ) => worker._taskLoad ) );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
dispose: function () {
|
||||||
|
|
||||||
|
for ( var i = 0; i < this.workerPool.length; ++ i ) {
|
||||||
|
|
||||||
|
this.workerPool[ i ].terminate();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.workerPool.length = 0;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} );
|
||||||
|
|
||||||
|
/* WEB WORKER */
|
||||||
|
|
||||||
|
DRACOLoader.DRACOWorker = function () {
|
||||||
|
|
||||||
|
var decoderConfig;
|
||||||
|
var decoderPending;
|
||||||
|
|
||||||
|
onmessage = function ( e ) {
|
||||||
|
|
||||||
|
var message = e.data;
|
||||||
|
|
||||||
|
switch ( message.type ) {
|
||||||
|
|
||||||
|
case 'init':
|
||||||
|
decoderConfig = message.decoderConfig;
|
||||||
|
decoderPending = new Promise( function ( resolve/*, reject*/ ) {
|
||||||
|
|
||||||
|
decoderConfig.onModuleLoaded = function ( draco ) {
|
||||||
|
|
||||||
|
// Module is Promise-like. Wrap before resolving to avoid loop.
|
||||||
|
resolve( { draco: draco } );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
DracoDecoderModule( decoderConfig ); // eslint-disable-line no-undef
|
||||||
|
|
||||||
|
} );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'decode':
|
||||||
|
var buffer = message.buffer;
|
||||||
|
var taskConfig = message.taskConfig;
|
||||||
|
decoderPending.then( ( module ) => {
|
||||||
|
|
||||||
|
var draco = module.draco;
|
||||||
|
var decoder = new draco.Decoder();
|
||||||
|
var decoderBuffer = new draco.DecoderBuffer();
|
||||||
|
decoderBuffer.Init( new Int8Array( buffer ), buffer.byteLength );
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
var geometry = decodeGeometry( draco, decoder, decoderBuffer, taskConfig );
|
||||||
|
|
||||||
|
var buffers = geometry.attributes.map( ( attr ) => attr.array.buffer );
|
||||||
|
|
||||||
|
if ( geometry.index ) buffers.push( geometry.index.array.buffer );
|
||||||
|
|
||||||
|
self.postMessage( { type: 'decode', id: message.id, geometry }, buffers );
|
||||||
|
|
||||||
|
} catch ( error ) {
|
||||||
|
|
||||||
|
console.error( error );
|
||||||
|
|
||||||
|
self.postMessage( { type: 'error', id: message.id, error: error.message } );
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
draco.destroy( decoderBuffer );
|
||||||
|
draco.destroy( decoder );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} );
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function decodeGeometry( draco, decoder, decoderBuffer, taskConfig ) {
|
||||||
|
|
||||||
|
var attributeIDs = taskConfig.attributeIDs;
|
||||||
|
var attributeTypes = taskConfig.attributeTypes;
|
||||||
|
|
||||||
|
var dracoGeometry;
|
||||||
|
var decodingStatus;
|
||||||
|
|
||||||
|
var geometryType = decoder.GetEncodedGeometryType( decoderBuffer );
|
||||||
|
|
||||||
|
if ( geometryType === draco.TRIANGULAR_MESH ) {
|
||||||
|
|
||||||
|
dracoGeometry = new draco.Mesh();
|
||||||
|
decodingStatus = decoder.DecodeBufferToMesh( decoderBuffer, dracoGeometry );
|
||||||
|
|
||||||
|
} else if ( geometryType === draco.POINT_CLOUD ) {
|
||||||
|
|
||||||
|
dracoGeometry = new draco.PointCloud();
|
||||||
|
decodingStatus = decoder.DecodeBufferToPointCloud( decoderBuffer, dracoGeometry );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
throw new Error( 'THREE.DRACOLoader: Unexpected geometry type.' );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! decodingStatus.ok() || dracoGeometry.ptr === 0 ) {
|
||||||
|
|
||||||
|
throw new Error( 'THREE.DRACOLoader: Decoding failed: ' + decodingStatus.error_msg() );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var geometry = { index: null, attributes: [] };
|
||||||
|
|
||||||
|
// Gather all vertex attributes.
|
||||||
|
for ( var attributeName in attributeIDs ) {
|
||||||
|
|
||||||
|
var attributeType = self[ attributeTypes[ attributeName ] ];
|
||||||
|
|
||||||
|
var attribute;
|
||||||
|
var attributeID;
|
||||||
|
|
||||||
|
// A Draco file may be created with default vertex attributes, whose attribute IDs
|
||||||
|
// are mapped 1:1 from their semantic name (POSITION, NORMAL, ...). Alternatively,
|
||||||
|
// a Draco file may contain a custom set of attributes, identified by known unique
|
||||||
|
// IDs. glTF files always do the latter, and `.drc` files typically do the former.
|
||||||
|
if ( taskConfig.useUniqueIDs ) {
|
||||||
|
|
||||||
|
attributeID = attributeIDs[ attributeName ];
|
||||||
|
attribute = decoder.GetAttributeByUniqueId( dracoGeometry, attributeID );
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
attributeID = decoder.GetAttributeId( dracoGeometry, draco[ attributeIDs[ attributeName ] ] );
|
||||||
|
|
||||||
|
if ( attributeID === - 1 ) continue;
|
||||||
|
|
||||||
|
attribute = decoder.GetAttribute( dracoGeometry, attributeID );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry.attributes.push( decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add index.
|
||||||
|
if ( geometryType === draco.TRIANGULAR_MESH ) {
|
||||||
|
|
||||||
|
geometry.index = decodeIndex( draco, decoder, dracoGeometry );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
draco.destroy( dracoGeometry );
|
||||||
|
|
||||||
|
return geometry;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeIndex( draco, decoder, dracoGeometry ) {
|
||||||
|
|
||||||
|
var numFaces = dracoGeometry.num_faces();
|
||||||
|
var numIndices = numFaces * 3;
|
||||||
|
var byteLength = numIndices * 4;
|
||||||
|
|
||||||
|
var ptr = draco._malloc( byteLength );
|
||||||
|
decoder.GetTrianglesUInt32Array( dracoGeometry, byteLength, ptr );
|
||||||
|
var index = new Uint32Array( draco.HEAPF32.buffer, ptr, numIndices ).slice();
|
||||||
|
draco._free( ptr );
|
||||||
|
|
||||||
|
return { array: index, itemSize: 1 };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeAttribute( draco, decoder, dracoGeometry, attributeName, attributeType, attribute ) {
|
||||||
|
|
||||||
|
var numComponents = attribute.num_components();
|
||||||
|
var numPoints = dracoGeometry.num_points();
|
||||||
|
var numValues = numPoints * numComponents;
|
||||||
|
var byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
|
||||||
|
var dataType = getDracoDataType( draco, attributeType );
|
||||||
|
|
||||||
|
var ptr = draco._malloc( byteLength );
|
||||||
|
decoder.GetAttributeDataArrayForAllPoints( dracoGeometry, attribute, dataType, byteLength, ptr );
|
||||||
|
var array = new attributeType( draco.HEAPF32.buffer, ptr, numValues ).slice();
|
||||||
|
draco._free( ptr );
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: attributeName,
|
||||||
|
array: array,
|
||||||
|
itemSize: numComponents
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDracoDataType( draco, attributeType ) {
|
||||||
|
|
||||||
|
switch ( attributeType ) {
|
||||||
|
|
||||||
|
case Float32Array: return draco.DT_FLOAT32;
|
||||||
|
case Int8Array: return draco.DT_INT8;
|
||||||
|
case Int16Array: return draco.DT_INT16;
|
||||||
|
case Int32Array: return draco.DT_INT32;
|
||||||
|
case Uint8Array: return draco.DT_UINT8;
|
||||||
|
case Uint16Array: return draco.DT_UINT16;
|
||||||
|
case Uint32Array: return draco.DT_UINT32;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
DRACOLoader.taskCache = new WeakMap();
|
||||||
|
|
||||||
|
/** Deprecated static methods */
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
|
DRACOLoader.setDecoderPath = function () {
|
||||||
|
|
||||||
|
console.warn( 'THREE.DRACOLoader: The .setDecoderPath() method has been removed. Use instance methods.' );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
|
DRACOLoader.setDecoderConfig = function () {
|
||||||
|
|
||||||
|
console.warn( 'THREE.DRACOLoader: The .setDecoderConfig() method has been removed. Use instance methods.' );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
|
DRACOLoader.releaseDecoderModule = function () {
|
||||||
|
|
||||||
|
console.warn( 'THREE.DRACOLoader: The .releaseDecoderModule() method has been removed. Use instance methods.' );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
|
DRACOLoader.getDecoderModule = function () {
|
||||||
|
|
||||||
|
console.warn( 'THREE.DRACOLoader: The .getDecoderModule() method has been removed. Use instance methods.' );
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
THREE.DRACOLoader = DRACOLoader;
|
Plik diff jest za duży
Load Diff
Ładowanie…
Reference in New Issue