Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FileProvider #705

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/3dtiles.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@

var menuGlobe = new GuiTools('menuDiv', view, 300);

itowns.Fetcher.json('layers/JSONLayers/Ortho.json').then(function (result) { return view.addLayer(result) });
itowns.Fetcher.json('layers/JSONLayers/OPENSM.json').then(function (result) { return view.addLayer(result) });

// function use :
// For preupdate Layer geomtry :
Expand Down Expand Up @@ -172,7 +172,7 @@
if (interAttributes._BATCHID) {
var face = intersects[i].face.a;
var batchID = interAttributes._BATCHID.array[face];
var batchTable = findBatchTableParent(intersects[i].object);
var batchTable = findBatchTableParent(intersects[i].object).json;

htmlInfo.innerHTML +='<li><b> Batch id: </b>'+ batchID +'</li>';
Object.keys(batchTable).map(function(objectKey) {
Expand Down
72 changes: 72 additions & 0 deletions examples/file.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<html>
<head>
<title>Itowns - File drop</title>

<style type="text/css">
html {
height: 100%;
}

body {
margin: 0;
overflow:hidden;
height:100%;
}

#viewerDiv {
margin : auto auto;
width: 100%;
height: 100%;
padding: 0;
}
</style>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="css/loading_screen.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="viewerDiv"></div>
<script src="../dist/itowns.js"></script>
<script src="js/loading_screen.js"></script>
<script type="text/javascript">

// Define initial camera position
var positionOnGlobe = { longitude: 0.089, latitude: 42.8989, altitude: 80000 };

// `viewerDiv` will contain iTowns' rendering area (`<canvas>`)
var viewerDiv = document.getElementById('viewerDiv');

// Instanciate iTowns GlobeView*
var globeView = new itowns.GlobeView(viewerDiv, positionOnGlobe);

var promises = [];

setupLoadingScreen(viewerDiv, globeView);

function addLayerCb(layer) {
return globeView.addLayer(layer);
}
promises.push(itowns.Fetcher.json('./layers/JSONLayers/Ortho.json').then(addLayerCb));
promises.push(itowns.Fetcher.json('./layers/JSONLayers/WORLD_DTM.json').then(addLayerCb));
promises.push(itowns.Fetcher.json('./layers/JSONLayers/IGN_MNT_HIGHRES.json').then(addLayerCb));

function addFiles(event, dataTransfer) {
event.preventDefault();
var files = dataTransfer.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
console.log(file);
globeView.addLayer({ protocol: 'file', file, id: file.name, options: { event, altitude: 500} });
}
}

function prevDefault(event){ event.preventDefault(); }
viewerDiv.addEventListener('dragenter', prevDefault, false);
viewerDiv.addEventListener('dragover', prevDefault, false);
viewerDiv.addEventListener('dragleave', prevDefault, false);
viewerDiv.addEventListener('drop', function(e) { addFiles(e, e.dataTransfer); }, false);
viewerDiv.addEventListener('paste', function(e) { addFiles(e, e.clipboardData); }, false);

</script>
</body>
</html>
4 changes: 2 additions & 2 deletions examples/gpx.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
globeView.addEventListener(itowns.GLOBE_VIEW_EVENTS.GLOBE_INITIALIZED, function () {
console.info('Globe initialized');
itowns.Fetcher.xml('https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/ULTRA2009.gpx').then(xml => itowns.GpxParser.parse(xml, { crs: globeView.referenceCrs })).then(function (gpx) {
if (gpx) {
globeView.scene.add(gpx);
if (gpx && gpx.object3d) {
globeView.scene.add(gpx.object3d);
globeView.controls.setTilt(45, true);
}
});
Expand Down
47 changes: 46 additions & 1 deletion src/Core/Scheduler/Scheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*/

import PriorityQueue from 'js-priority-queue';
import CancelledCommandException from './CancelledCommandException';

import WMTSProvider from '../../Provider/WMTSProvider';
import WMSProvider from '../../Provider/WMSProvider';
import TileProvider from '../../Provider/TileProvider';
Expand All @@ -14,7 +16,14 @@ import PointCloudProvider from '../../Provider/PointCloudProvider';
import WFSProvider from '../../Provider/WFSProvider';
import RasterProvider from '../../Provider/RasterProvider';
import StaticProvider from '../../Provider/StaticProvider';
import CancelledCommandException from './CancelledCommandException';
import FileProvider from '../../Provider/FileProvider';

import B3dmParser from '../../Parser/B3dmParser';
import GeoJsonParser from '../../Parser/GeoJsonParser';
import GpxParser from '../../Parser/GpxParser';
import PntsParser from '../../Parser/PntsParser';
import PotreeBinParser from '../../Parser/PotreeBinParser';
import PotreeCinParser from '../../Parser/PotreeCinParser';

var instanceScheduler = null;

Expand Down Expand Up @@ -80,12 +89,14 @@ function Scheduler() {
this.hostQueues = new Map();

this.providers = {};
this.parsers = {};

this.maxConcurrentCommands = 16;
this.maxCommandsPerHost = 6;

// TODO: add an options to not instanciate default providers
this.initDefaultProviders();
this.initDefaultParsers();
}

Scheduler.prototype.constructor = Scheduler;
Expand All @@ -103,6 +114,17 @@ Scheduler.prototype.initDefaultProviders = function initDefaultProviders() {
this.addProtocolProvider('wfs', WFSProvider);
this.addProtocolProvider('rasterizer', RasterProvider);
this.addProtocolProvider('static', StaticProvider);
this.addProtocolProvider('file', FileProvider);
};

Scheduler.prototype.initDefaultParsers = function initDefaultParsers() {
// Register all parsers
this.addFormatParser(B3dmParser);
this.addFormatParser(GeoJsonParser);
this.addFormatParser(GpxParser);
this.addFormatParser(PntsParser);
this.addFormatParser(PotreeBinParser);
this.addFormatParser(PotreeCinParser);
};

Scheduler.prototype.runCommand = function runCommand(command, queue, executingCounterUpToDate) {
Expand Down Expand Up @@ -256,6 +278,29 @@ Scheduler.prototype.getProtocolProvider = function getProtocolProvider(protocol)
return this.providers[protocol];
};


Scheduler.prototype.addFormatParser = function addFormatParser(parser) {
// eslint-disable-next-line no-console
console.log(`Registering format : ${parser.format}`);
if (typeof (parser.parse) !== 'function') {
throw new Error(`Can't add parser for ${parser.format}: missing a parse function.`);
}
var that = this;
function register(format) {
that.parsers[format] = that.parsers[format] || [];
that.parsers[format].push(parser);
}
register(parser.format);
parser.extensions.forEach(register);
parser.mimetypes.forEach(register);
};

Scheduler.prototype.getFormatParser = function getFormatParser(format) {
var parsers = this.parsers[format];
// format disambiguation is arbitrary to the first registration for now
return parsers ? parsers[0] : undefined;
};

Scheduler.prototype.commandsWaitingExecutionCount = function commandsWaitingExecutionCount() {
let sum = this.defaultQueue.storage.length + this.defaultQueue.counters.executing;
for (var q of this.hostQueues) {
Expand Down
81 changes: 81 additions & 0 deletions src/Parser/3dTilesHeaderParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import utf8Decoder from '../utils/Utf8Decoder';

export default {
/** @module 3dTilesHeaderParser */
/** Parse buffer and convert to JSON
* @function parse
* @param {ArrayBuffer} buffer - the input buffer.
* @param {Object} options - additional properties.
* @param {string=} [options.magic] - magic string.
* @return {Promise} - a promise that resolves with a 3dTilesHeader object.
*
*/
parse(buffer, options) {
if (!buffer) {
throw new Error('No array buffer provided.');
}
const header = {};
const parsed = { header };

// Magic type is unsigned char [4]
header.magic = utf8Decoder.decode(new Uint8Array(buffer, 0, 4));
if (header.magic !== options.magic) {
throw new Error(`Invalid 3d-tiles header : "${header.magic}" ("${options.magic}" was expected).`);
}

const view = new DataView(buffer);
let byteOffset = 4;

header.version = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

header.byteLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

if (options.magic === 'cmpt') {
header.tilesLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;
} else {
header.FTJSONLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

header.FTBinaryLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

header.BTJSONLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

header.BTBinaryLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

if (options.magic === 'i3dm') {
header.gltfFormat = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;
}
}

parsed.header = header;

if (header.FTJSONLength > 0) {
const json = new Uint8Array(buffer, byteOffset, header.FTJSONLength);
byteOffset += header.FTJSONLength;
parsed.featureTable = { json: JSON.parse(utf8Decoder.decode(json)) };
if (header.FTBinaryLength > 0) {
parsed.featureTable.buffer = buffer.slice(byteOffset, byteOffset + header.FTBinaryLength);
byteOffset += header.FTBinaryLength;
}
}

if (header.BTJSONLength > 0) {
const json = new Uint8Array(buffer, byteOffset, header.BTJSONLength);
byteOffset += header.BTJSONLength;
parsed.batchTable = { json: JSON.parse(utf8Decoder.decode(json)) };
if (header.BTBinaryLength > 0) {
parsed.batchTable.buffer = buffer.slice(byteOffset, header.BTBinaryLength);
byteOffset += header.BTBinaryLength;
}
}
parsed.byteOffset = byteOffset;
return Promise.resolve(parsed);
},
};
90 changes: 24 additions & 66 deletions src/Parser/B3dmParser.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as THREE from 'three';
import GLTFLoader from './GLTFLoader';
import LegacyGLTFLoader from './LegacyGLTFLoader';
import BatchTableParser from './BatchTableParser';
import Capabilities from '../Core/System/Capabilities';
import shaderUtils from '../Renderer/Shader/ShaderUtils';
import $3dTilesHeaderParser from './3dTilesHeaderParser';
import utf8Decoder from '../utils/Utf8Decoder';

const matrixChangeUpVectorZtoY = (new THREE.Matrix4()).makeRotationX(Math.PI / 2);
Expand Down Expand Up @@ -63,64 +63,25 @@ export default {
*
*/
parse(buffer, options) {
const gltfUpAxis = options.gltfUpAxis;
const urlBase = options.urlBase;
if (!buffer) {
throw new Error('No array buffer provided.');
}

const view = new DataView(buffer, 4); // starts after magic

let byteOffset = 0;
const b3dmHeader = {};

// Magic type is unsigned char [4]
b3dmHeader.magic = utf8Decoder.decode(new Uint8Array(buffer, 0, 4));
if (b3dmHeader.magic) {
// Version, byteLength, batchTableJSONByteLength, batchTableBinaryByteLength and batchTable types are uint32
b3dmHeader.version = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

b3dmHeader.byteLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

b3dmHeader.FTJSONLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

b3dmHeader.FTBinaryLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

b3dmHeader.BTJSONLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

b3dmHeader.BTBinaryLength = view.getUint32(byteOffset, true);
byteOffset += Uint32Array.BYTES_PER_ELEMENT;

const promises = [];
if (b3dmHeader.BTJSONLength > 0) {
const sizeBegin = 28 + b3dmHeader.FTJSONLength + b3dmHeader.FTBinaryLength;
promises.push(BatchTableParser.parse(
buffer.slice(sizeBegin, b3dmHeader.BTJSONLength + sizeBegin)));
} else {
promises.push(Promise.resolve({}));
}
// TODO: missing feature table
promises.push(new Promise((resolve/* , reject */) => {
const onload = (gltf) => {
options = options || {};
options.magic = 'b3dm';
return $3dTilesHeaderParser.parse(buffer, options).then((data) => {
const gltfBuffer = buffer.slice(data.byteOffset);
const version = new DataView(gltfBuffer, 0, 20).getUint32(4, true);
return new Promise((resolve/* , reject */) => {
function onload(gltf) {
for (const scene of gltf.scenes) {
scene.traverse(filterUnsupportedSemantics);
}
// Rotation managed
if (gltfUpAxis === undefined || gltfUpAxis === 'Y') {
if (options.gltfUpAxis === 'Y') {
gltf.scene.applyMatrix(matrixChangeUpVectorZtoY);
} else if (gltfUpAxis === 'X') {
} else if (options.gltfUpAxis === 'X') {
gltf.scene.applyMatrix(matrixChangeUpVectorZtoX);
}

// RTC managed
applyOptionalCesiumRTC(buffer.slice(28 + b3dmHeader.FTJSONLength +
b3dmHeader.FTBinaryLength + b3dmHeader.BTJSONLength +
b3dmHeader.BTBinaryLength), gltf.scene);
applyOptionalCesiumRTC(gltfBuffer, gltf.scene);

const init_mesh = function f_init(mesh) {
mesh.frustumCulled = false;
Expand All @@ -145,24 +106,21 @@ export default {
};
gltf.scene.traverse(init_mesh);

resolve(gltf);
};

const gltfBuffer = buffer.slice(28 + b3dmHeader.FTJSONLength +
b3dmHeader.FTBinaryLength + b3dmHeader.BTJSONLength +
b3dmHeader.BTBinaryLength);

const version = new DataView(gltfBuffer, 0, 20).getUint32(4, true);

resolve({
object3d: gltf.scene,
batchTable: data.batchTable,
});
}
if (version === 1) {
legacyGLTFLoader.parse(gltfBuffer, onload, urlBase);
legacyGLTFLoader.parse(gltfBuffer, onload, options.urlBase);
} else {
glTFLoader.parse(gltfBuffer, urlBase, onload);
glTFLoader.parse(gltfBuffer, options.urlBase, onload);
}
}));
return Promise.all(promises).then(values => ({ gltf: values[1], batchTable: values[0] }));
} else {
throw new Error('Invalid b3dm file.');
}
});
});
},
format: '3d-tiles/b3dm',
extensions: ['b3dm'],
mimetypes: ['application/octet-stream'],
fetchtype: 'arrayBuffer',
};
Loading