import { BufferLoader, GltfBufferViewLoader, Resource, ResourceCache, when, } from "../../Source/Cesium.js"; describe("Scene/GltfBufferViewLoader", function () { var gltfEmbedded = { buffers: [ { byteLength: 8, }, ], bufferViews: [ { buffer: 0, byteOffset: 2, byteLength: 3, }, ], }; var gltfExternal = { buffers: [ { uri: "external.bin", byteLength: 8, }, ], bufferViews: [ { buffer: 0, byteOffset: 2, byteLength: 3, }, ], }; var meshoptGltfEmbedded = { buffers: [{ byteLength: 29 }, { byteLength: 360 }], bufferViews: [ { buffer: 1, byteOffset: 96, byteLength: 192, byteStride: 8, target: 34962, extensions: { EXT_meshopt_compression: { buffer: 0, byteOffset: 0, byteLength: 124, byteStride: 8, mode: "ATTRIBUTES", count: 24, }, }, }, { buffer: 1, byteOffset: 288, byteLength: 72, target: 34963, extensions: { EXT_meshopt_compression: { buffer: 0, byteOffset: 0, byteLength: 29, byteStride: 2, mode: "TRIANGLES", count: 36, }, }, }, ], }; function getBase64FromTypedArray(typedArray) { return btoa(String.fromCharCode.apply(null, typedArray)); } function getTypedArrayFromBase64(base64) { return Uint8Array.from(atob(base64), function (c) { return c.charCodeAt(0); }); } var fallbackPositionBufferBase64 = "AAD/P/8/AAD/P/8//z8AAAAA/z8AAAAA/z//PwAAAAD/P/8//z8AAAAA/z//PwAA/z8AAP8/AAAAAAAA/z8AAP8//z8AAAAA/z//P/8/AAD/PwAAAAAAAP8/AAD/PwAAAAD/PwAAAAD/P/8/AAAAAAAAAAAAAAAA/z8AAAAAAAAAAP8//z8AAAAA/z8AAAAAAAAAAP8/AAAAAAAAAAAAAAAAAAD/PwAAAAAAAAAAAAD/PwAA/z8AAP8/AAAAAAAA"; var meshoptPositionBufferBase64 = "oAUZJkCZgAQAAAU/P8D/fn1+fX59fn1+fX7ADAAAfX4FAAhISEgAAAAFAAzMzH1+fX59zAAAAH59BQhAmYBmZgAABQzA/8B9fn1+fX59//8AAH59fn1+fX59AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8//z8AAA=="; var meshoptPositionTypedArray = getTypedArrayFromBase64( meshoptPositionBufferBase64 ); var bufferTypedArray = new Uint8Array([1, 3, 7, 15, 31, 63, 127, 255]); var bufferArrayBuffer = bufferTypedArray.buffer; var gltfUri = "https://example.com/model.glb"; var gltfResource = new Resource({ url: gltfUri, }); var bufferResource = new Resource({ url: "https://example.com/external.bin", }); afterEach(function () { ResourceCache.clearForSpecs(); }); it("throws if resourceCache is undefined", function () { expect(function () { return new GltfBufferViewLoader({ resourceCache: undefined, gltf: gltfExternal, bufferViewId: 0, gltfResource: gltfResource, baseResource: gltfResource, }); }).toThrowDeveloperError(); }); it("throws if gltf is undefined", function () { expect(function () { return new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: undefined, bufferViewId: 0, gltfResource: gltfResource, baseResource: gltfResource, }); }).toThrowDeveloperError(); }); it("throws if bufferViewId is undefined", function () { expect(function () { return new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: gltfExternal, bufferViewId: undefined, gltfResource: gltfResource, baseResource: gltfResource, }); }).toThrowDeveloperError(); }); it("throws if gltfResource is undefined", function () { expect(function () { return new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: gltfExternal, bufferViewId: 0, gltfResource: undefined, baseResource: gltfResource, }); }).toThrowDeveloperError(); }); it("throws if baseResource is undefined", function () { expect(function () { return new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: gltfExternal, bufferViewId: 0, gltfResource: gltfResource, baseResource: undefined, }); }).toThrowDeveloperError(); }); it("rejects promise if buffer fails to load", function () { var error = new Error("404 Not Found"); spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue( when.reject(error) ); var bufferViewLoader = new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: gltfExternal, bufferViewId: 0, gltfResource: gltfResource, baseResource: gltfResource, }); bufferViewLoader.load(); return bufferViewLoader.promise .then(function (bufferViewLoader) { fail(); }) .otherwise(function (runtimeError) { expect(runtimeError.message).toBe( "Failed to load buffer view\nFailed to load external buffer: https://example.com/external.bin\n404 Not Found" ); }); }); it("loads buffer view for embedded buffer", function () { ResourceCache.loadEmbeddedBuffer({ parentResource: gltfResource, bufferId: 0, typedArray: bufferTypedArray, }); var bufferViewLoader = new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: gltfEmbedded, bufferViewId: 0, gltfResource: gltfResource, baseResource: gltfResource, }); bufferViewLoader.load(); return bufferViewLoader.promise.then(function (bufferViewLoader) { expect(bufferViewLoader.typedArray).toEqual(new Uint8Array([7, 15, 31])); }); }); it("loads buffer view for external buffer", function () { spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue( when.resolve(bufferArrayBuffer) ); var bufferViewLoader = new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: gltfExternal, bufferViewId: 0, gltfResource: gltfResource, baseResource: gltfResource, }); bufferViewLoader.load(); return bufferViewLoader.promise.then(function (bufferViewLoader) { expect(bufferViewLoader.typedArray).toEqual(new Uint8Array([7, 15, 31])); }); }); it("destroys buffer view", function () { spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue( when.resolve(bufferArrayBuffer) ); var unloadBuffer = spyOn( BufferLoader.prototype, "unload" ).and.callThrough(); var bufferViewLoader = new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: gltfExternal, bufferViewId: 0, gltfResource: gltfResource, baseResource: gltfResource, }); expect(bufferViewLoader.typedArray).not.toBeDefined(); bufferViewLoader.load(); return bufferViewLoader.promise.then(function (bufferViewLoader) { expect(bufferViewLoader.typedArray).toEqual(new Uint8Array([7, 15, 31])); expect(bufferViewLoader.isDestroyed()).toBe(false); bufferViewLoader.destroy(); expect(bufferViewLoader.typedArray).not.toBeDefined(); expect(bufferViewLoader.isDestroyed()).toBe(true); expect(unloadBuffer).toHaveBeenCalled(); }); }); it("decodes positions with EXT_meshopt_compression", function () { ResourceCache.loadEmbeddedBuffer({ parentResource: gltfResource, bufferId: 0, typedArray: meshoptPositionTypedArray, }); var bufferViewLoader = new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: meshoptGltfEmbedded, bufferViewId: 0, gltfResource: gltfResource, baseResource: gltfResource, }); bufferViewLoader.load(); bufferViewLoader.process({}); return bufferViewLoader.promise.then(function (bufferViewLoader) { var decodedPositionBase64 = getBase64FromTypedArray( bufferViewLoader.typedArray ); expect(decodedPositionBase64).toEqual(fallbackPositionBufferBase64); }); }); function resolveAfterDestroy(reject) { var deferredPromise = when.defer(); spyOn(Resource.prototype, "fetchArrayBuffer").and.returnValue( deferredPromise.promise ); // Load a copy of the buffer into the cache so that the buffer promise // resolves even if the buffer view loader is destroyed var bufferLoaderCopy = ResourceCache.loadExternalBuffer({ resource: bufferResource, }); var bufferViewLoader = new GltfBufferViewLoader({ resourceCache: ResourceCache, gltf: gltfExternal, bufferViewId: 0, gltfResource: gltfResource, baseResource: gltfResource, }); expect(bufferViewLoader.typedArray).not.toBeDefined(); bufferViewLoader.load(); bufferViewLoader.destroy(); if (reject) { deferredPromise.reject(new Error()); } else { deferredPromise.resolve(bufferArrayBuffer); } expect(bufferViewLoader.typedArray).not.toBeDefined(); expect(bufferViewLoader.isDestroyed()).toBe(true); ResourceCache.unload(bufferLoaderCopy); } it("handles resolving buffer after destroy", function () { resolveAfterDestroy(false); }); it("handles rejecting buffer after destroy", function () { resolveAfterDestroy(true); }); });