Skip to content

Commit

Permalink
Merge 7c5d107 into 5a1606e
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen committed Sep 27, 2019
2 parents 5a1606e + 7c5d107 commit ad66dfc
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import test from 'tape-promise/tape';
import {parse, encodeSync} from '@loaders.gl/core';
import {Tile3DLoader, Tile3DWriter, TILE3D_TYPE} from '@loaders.gl/3d-tiles';
import {ImageLoader} from '@loaders.gl/images';
import {loadRootTileFromTileset} from '../utils/load-utils';

const WITH_BATCH_TABLE_URL =
Expand Down Expand Up @@ -115,7 +116,7 @@ test('batched model tile#with a mix of opaque and translucent features', async t
// TODO this should be a render test
test('batched model tile#with textures', async t => {
const tileData = await loadRootTileFromTileset(t, TEXTURED_URL);
const tile = await parse(tileData, Tile3DLoader);
const tile = await parse(tileData, [Tile3DLoader, ImageLoader]);
t.ok(tile, 'loaded tile with a mix of opaque and translucent features');
t.end();
});
Expand Down
6 changes: 3 additions & 3 deletions modules/core/src/lib/select-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const EXT_PATTERN = /[^.]+$/;
// TODO - Detect multiple matching loaders? Use heuristics to grade matches?
// TODO - Allow apps to pass context to disambiguate between multiple matches (e.g. multiple .json formats)?

// eslint-disable-next-line complexity
export function selectLoader(loaders, url = '', data = null, {nothrow = false} = {}) {
url = url || '';

Expand All @@ -22,9 +23,8 @@ export function selectLoader(loaders, url = '', data = null, {nothrow = false} =
return loader;
}

// merge input loaders with registered loaders
// the input loader will be picked over pre registeredLoaders
loaders = (loaders || []).concat(getRegisteredLoaders());
// If no loaders provided, get the registered loaders
loaders = [...(loaders || []), ...getRegisteredLoaders()];
normalizeLoaders(loaders);

url = url.replace(/\?.*/, '');
Expand Down
12 changes: 7 additions & 5 deletions modules/gltf/src/gltf-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,9 @@ const GLTFLoader = {
parserVersion: 1, // the new parser that will be the only option in V2 is not default in V1

// Note: The following options are used only when parserVersion === 2
fetchBuffers: true, // Fetch any linked .BIN buffers, decode base64
fetchImages: true, // Fetch any linked .BIN buffers, decode base64
createImages: false, // Create image objects
decompress: true, // Decompress meshes
loadBuffers: true, // Fetch any linked .BIN buffers, decode base64
loadImages: true, // Create image objects
decompressMeshes: true, // Decompress Draco encoded meshes
postProcess: true // Postprocess glTF and return json structure directly
},

Expand Down Expand Up @@ -63,7 +62,10 @@ export async function parse(arrayBuffer, options = {}, context) {

function addDeprecatedGLTFOptions(options) {
if ('fetchImages' in options) {
options.gltf.fetchImages = options.fetchImages;
options.gltf.loadImages = options.fetchImages;
}
if ('createImages' in options) {
options.gltf.loadImages = options.createImages;
}
if ('fetchLinkedResources' in options) {
options.gltf.fetchBuffers = options.fetchLinkedResources;
Expand Down
41 changes: 41 additions & 0 deletions modules/gltf/src/lib/gltf-utils/get-typed-array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// TODO - GLTFScenegraph should use these
import assert from '../utils/assert';

// accepts buffer view index or buffer view object
// returns a `Uint8Array`
export function getTypedArrayForBufferView(json, buffers, bufferViewIndex) {
const bufferView = json.bufferViews[bufferViewIndex];
assert(bufferView);

// Get hold of the arrayBuffer
const bufferIndex = bufferView.buffer;
const binChunk = buffers[bufferIndex];
assert(binChunk);

const byteOffset = (bufferView.byteOffset || 0) + binChunk.byteOffset;
return new Uint8Array(binChunk.arrayBuffer, byteOffset, bufferView.byteLength);
}

// accepts accessor index or accessor object
// returns a `Uint8Array`
export function getTypedArrayForImageData(json, buffers, imageIndex) {
const image = json.images[imageIndex];
const bufferViewIndex = json.bufferViews[image.bufferView];
return getTypedArrayForBufferView(json, buffers, bufferViewIndex);
}

/*
// accepts accessor index or accessor object
// returns a typed array with type that matches the types
export function getTypedArrayForAccessor(accessor) {
accessor = this.getAccessor(accessor);
const bufferView = this.getBufferView(accessor.bufferView);
const buffer = this.getBuffer(bufferView.buffer);
const arrayBuffer = buffer.data;
// Create a new typed array as a view into the combined buffer
const {ArrayType, length} = getAccessorArrayTypeAndLength(accessor, bufferView);
const byteOffset = bufferView.byteOffset + accessor.byteOffset;
return new ArrayType(arrayBuffer, byteOffset, length);
}
*/
79 changes: 34 additions & 45 deletions modules/gltf/src/lib/parse-gltf.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
/* eslint-disable camelcase, max-statements, no-restricted-globals */
/* global TextDecoder */
import {parseJSON} from '@loaders.gl/loader-utils';
import {ImageLoader} from '@loaders.gl/images';
import {parseJSON, getZeroOffsetArrayBuffer} from '@loaders.gl/loader-utils';
import assert from './utils/assert';
import {getFullUri} from './gltf-utils/gltf-utils';
import {getTypedArrayForBufferView} from './gltf-utils/get-typed-array';
import {decodeExtensions} from './extensions/gltf-extensions';
import parseGLBSync, {isGLB} from './parse-glb';
import postProcessGLTF from './post-process-gltf';
Expand All @@ -18,14 +20,14 @@ export async function parseGLTF(gltf, arrayBufferOrString, byteOffset = 0, optio

const promises = [];

if (options.gltf.fetchImages) {
const promise = fetchImages(gltf, options, context);
if (options.gltf.loadImages) {
const promise = loadImages(gltf, options, context);
promises.push(promise);
}

// Load linked buffers asynchronously and decodes base64 buffers in parallel
if (options.gltf.fetchBuffers) {
await fetchBuffers(gltf, options, context);
if (options.gltf.loadBuffers) {
await loadBuffers(gltf, options, context);
}

const promise = decodeExtensions(gltf, options, context);
Expand Down Expand Up @@ -92,7 +94,7 @@ function parseGLTFContainerSync(gltf, data, byteOffset, options) {
}

// Asynchronously fetch and parse buffers, store in buffers array outside of json
async function fetchBuffers(gltf, options, context) {
async function loadBuffers(gltf, options, context) {
for (let i = 0; i < gltf.json.buffers.length; ++i) {
const buffer = gltf.json.buffers[i];
if (buffer.uri) {
Expand Down Expand Up @@ -121,53 +123,40 @@ async function fetchBuffers(gltf, options, context) {
}
}

async function fetchImages(gltf, options, context) {
async function loadImages(gltf, options, context) {
const images = gltf.json.images || [];

const promises = [];
for (let i = 0; i < images.length; ++i) {
const image = images[i];
if ('uri' in image) {
promises.push(fetchAndParseLinkedImage(gltf, image, i, options));
}
promises.push(loadImage(gltf, images[i], i, options, context));
}

return await Promise.all(promises);
}

// Asynchronously fetches and parses one image, store in images array outside of json
async function fetchAndParseLinkedImage(gltf, image, i, options, context) {
// const fetch = options.fetch || window.fetch;
// assert(fetch);

/*
if (image.bufferView) {
gltf.images[i] = await new Promise(resolve => {
const arrayBufferView = this.getBufferView(image.bufferView);
const mimeType = image.mimeType || 'image/jpeg';
const blob = new Blob([arrayBufferView], { type: mimeType });
const urlCreator = self.URL || self.webkitURL;
const imageUrl = urlCreator.createObjectURL(blob);
const img = new Image();
img.onload = () => resolve(img);
img.src = imageUrl;
});
async function loadImage(gltf, image, i, options, context) {
const {fetch, parse} = context;

let arrayBuffer;

if (image.uri) {
const uri = getFullUri(image.uri, options.uri);
const response = await fetch(uri);
arrayBuffer = await response.arrayBuffer();
}
*/

const uri = getFullUri(image.uri, options.uri);

// TODO - Call `parse` and use registered image loaders?
// const response = await fetch(uri);
// const arrayBuffer = await response.arrayBuffer();
// Create a new 'buffer' to hold the arrayBuffer?
// const image = parse(arrayBuffer);

gltf.images[i] = await new Promise((resolve, reject) => {
/* global Image */
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => resolve(img);
img.onerror = error => reject(error);
img.src = uri;
});

if (Number.isFinite(image.bufferView)) {
const array = getTypedArrayForBufferView(gltf.json, gltf.buffers, image.bufferView);
arrayBuffer = getZeroOffsetArrayBuffer(array.buffer, array.byteOffset, array.byteLength);
}

assert(arrayBuffer, 'glTF image has no data');

// Call `parse`
const parsedImage = await parse(arrayBuffer, ImageLoader);
// TODO making sure ImageLoader is overridable by using array of loaders
// const parsedImage = await parse(arrayBuffer, [ImageLoader]);

gltf.images[i] = parsedImage;
}
56 changes: 11 additions & 45 deletions modules/gltf/src/lib/post-process-gltf.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {getFullUri} from './gltf-utils/gltf-utils';
import assert from './utils/assert';

// This is a post processor for loaded glTF files
Expand Down Expand Up @@ -320,53 +319,20 @@ class GLTFPostProcessor {
image.bufferView = this.getBufferView(image.bufferView);
}

if ('uri' in image) {
// Check if image has been preloaded by the GLTFLoader
// If so, link it into the JSON and drop the URI
const preloadedImage = this.images[index];
if (preloadedImage && preloadedImage.image) {
image.image = preloadedImage.image;
delete image.uri;
}

// If not, resolve the relative URI using the baseName
const baseUri = options.uri || this.baseUri;
if (baseUri) {
const uri = image.uri;
image.uri = getFullUri(image.uri, baseUri);

// Deprecated
image.originalUri = uri;
image.baseUri = baseUri;
image.fullUri = image.uri;
}
// Check if image has been preloaded by the GLTFLoader
// If so, link it into the JSON and drop the URI
const preloadedImage = this.images[index];
if (preloadedImage) {
image.image = preloadedImage;
// delete image.uri;
// delete image.bufferView;
}

// Deprecated, use image.image or image.uri or image loaders instead
// DEPRECATED: just use image.image
image.getImageAsync = () => {
/* global self, Blob, Image */
if (image.bufferView) {
return new Promise(resolve => {
const arrayBufferView = this.getBufferView(image.bufferView);
const mimeType = image.mimeType || 'image/jpeg';
const blob = new Blob([arrayBufferView.data], {type: mimeType});
const urlCreator = self.URL || self.webkitURL;
const imageUrl = urlCreator.createObjectURL(blob);
const img = new Image();
img.onload = () => resolve(img);
img.src = imageUrl;
});
}

return image.uri
? new Promise(resolve => {
/* global Image */
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => resolve(img);
img.src = image.fullUri || image.uri;
})
: null;
// Check if already loaded
assert(image.image);
return image.image;
};

return image;
Expand Down
5 changes: 3 additions & 2 deletions modules/gltf/test/gltf-loader.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {validateLoader} from 'test/common/conformance';
import {registerLoaders, load, parseSync, fetchFile} from '@loaders.gl/core';
import {GLTFLoader} from '@loaders.gl/gltf';
import {DracoLoader} from '@loaders.gl/draco';
import {ImageLoader} from '@loaders.gl/images';

const GLTF_BINARY_URL = '@loaders.gl/gltf/test/data/gltf-2.0/2CylinderEngine.glb';
const GLTF_JSON_URL = '@loaders.gl/gltf/test/data/gltf-2.0/2CylinderEngine.gltf';
Expand Down Expand Up @@ -69,12 +70,12 @@ async function testTileGLBs(t, loaderOptions, version) {
t.ok(await load(GLB_TILE_URL, GLTFLoader, loaderOptions), `Parser ${version}: Test GLB parses`);

t.ok(
await load(GLB_TILE_WITH_DRACO_URL, [GLTFLoader, DracoLoader], loaderOptions),
await load(GLB_TILE_WITH_DRACO_URL, [GLTFLoader, DracoLoader, ImageLoader], loaderOptions),
`Parser ${version}: Parses Draco GLB with supplied DracoLoader`
);

// TODO - prone to flakiness since we have async unregisterLoaders calls
registerLoaders([DracoLoader]);
registerLoaders([DracoLoader, ImageLoader]);

t.ok(
await load(GLB_TILE_WITH_DRACO_URL, GLTFLoader, loaderOptions),
Expand Down
3 changes: 2 additions & 1 deletion modules/gltf/test/lib/gltf-scenegraph-accessors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import test from 'tape-promise/tape';

import {GLTFScenegraph} from '@loaders.gl/gltf';
import {GLTFLoader} from '@loaders.gl/gltf';
import {DracoLoader} from '@loaders.gl/draco';
import {load} from '@loaders.gl/core';

// Extracted from Cesium 3D Tiles
Expand All @@ -15,7 +16,7 @@ test('GLTFScenegraph#ctor', t => {
});

test('GLTFScenegraph#BufferView indices resolve correctly', async t => {
const gltf = await load(GLB_TILE_WITH_DRACO_URL, GLTFLoader, {
const gltf = await load(GLB_TILE_WITH_DRACO_URL, [GLTFLoader, DracoLoader], {
gltf: {
parserVersion: 2,
decompress: false,
Expand Down
4 changes: 2 additions & 2 deletions modules/images/src/image-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const ImageLoader = {
extensions: EXTENSIONS,
parse: parseImage,
test: arrayBuffer => {
const dataView = new DataView(arrayBuffer);
return isJpeg(dataView) && isBmp(dataView) && isGif(dataView) && isPng(dataView);
const dataView = new DataView(arrayBuffer); // , byteOffset, byteLength);
return isJpeg(dataView) || isBmp(dataView) || isGif(dataView) || isPng(dataView);
},
options: {
images: {
Expand Down
2 changes: 1 addition & 1 deletion modules/images/test/image-loader.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test('ImageLoader#load(data URL)', async t => {
t.end();
});

test.only('ImageLoader#formats', async t => {
test('ImageLoader#formats', async t => {
for (const testCase of TEST_CASES) {
await testLoadImage(t, testCase);
}
Expand Down
2 changes: 1 addition & 1 deletion modules/images/test/image-writer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import path from 'path';
// import mkdirp from 'mkdirp';
// const TEST_DIR = path.join(__dirname, '..', 'data');

const TEST_FILE = path.join(__dirname, '../data/test.png');
const TEST_FILE = path.join(__dirname, '../test/data/test.png');

const IMAGE = {
width: 2,
Expand Down
Loading

0 comments on commit ad66dfc

Please sign in to comment.