Loading a local glTF file into Javascript memory - javascript

I am using three.js and Capacitor to create a native iOS and Android app. I have some GLTF models that I want to load from some sort of asset folder that is bundled up with the code and delivered to a user, but I am not sure how to go about this.
This documentation on the GLTF Loader's load function, requires some sort of url. Parse, however, takes in a "glTF asset to parse, as an ArrayBuffer". How can I load this glTF file into memory and what is the best practice for doing so? I tried import * as Model from './models/myModel.gltf' but got a Cannot find module error.

Here my method to load a gltf file, I'm using reader to load any file in my scene, then it's parsing with loader.parse.
But if you want sample models folder I think you don't need reader method and parser. Just using load GLTF basic method and store your samples in an array or object.
I hope I helped you.
loadGltf(file, filename) {
reader.onload = readerEvent => {
const contents = readerEvent.target.result;
const loader = new THREE.GLTFLoader()
try {
loader.parse(contents, '', function (gltf) {
gltf.scene.traverse(function(child) {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
currentModel = gltf.scene;
scene.add(gltf.scene);
});
}
catch(error) {
alert("Your file " + Load.filename + " was not parsed correctly." + "\n\n" + "ERROR MESSAGE : " + error.message);
}
}
reader.readAsArrayBuffer(file);
}

Related

Ifc.js: wasm streaming compile failed: LinkError: import object field 'a' is not a Memory

I am reading IFC files and ifc.js seemed like a solid option although I am not so experienced with javascript but I thought it could be a good opportunity to learn about it.
I followed the documentation example that can be food here ``https://ifcjs.github.io/info/docs/Hello%20world```.
I packed the app inside of a django project and everything is fine until I try to load up a file.
I am getting the following error:
RuntimeError: abort(LinkError: import object field 'a' is not a Memory). Build with -s ASSERTIONS=1 for more info.
On my browser debugger, the error links to the following class of my bundle.js file
class IFCLoader extends Loader {
constructor(manager) {
super(manager);
this.ifcManager = new IFCManager();
}
load(url, onLoad, onProgress, onError) {
const scope = this;
const loader = new FileLoader(scope.manager);
this.onProgress = onProgress;
loader.setPath(scope.path);
loader.setResponseType('arraybuffer');
loader.setRequestHeader(scope.requestHeader);
loader.setWithCredentials(scope.withCredentials);
loader.load(url, async function (buffer) {
try {
if (typeof buffer == 'string') {
throw new Error('IFC files must be given as a buffer!');
}
onLoad(await scope.parse(buffer));
} catch (e) {
if (onError) {
onError(e);
} else {
console.error(e);
}
I have no clue how to correct this issue and any help would be highly appreciated. I am happy to post additional files or code if needed. Thanks

Load gltfs from firebase

I have a gltf file which exist in firebase storage folders and related textures are also located in that folders. I want to load that object into my view. I using THREE js for do this.
I tried get download url of gltf file and pass it it GLTFLoader. But model was not loaded to view.
I tried with this :
const loader = new GLTFLoader()
loader.load(
url,
(gltf) => {
gltf.scene.traverse( ( child ) => {
if ( child instanceof THREE.Mesh ) {
console.log(child.material.metalness)
if(child.material.metalness){
child.material.envMap = texture;
}
}
} );
var parent = gltf.scene;
var box = new THREE.Box3().setFromObject(parent)
var center = box.getCenter(new THREE.Vector3())
var size = box.getSize(new THREE.Vector3())
var maxAxis = Math.max(size.x,size.y,size.z)
parent.scale.multiplyScalar(1/maxAxis)
box.setFromObject(parent);
box.getCenter(center)
box.getSize(size)
parent.position.copy(center).multiplyScalar(-1)
scene.add(gltf.scene)
},
(xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
},
(error) => {
console.log(error)
}
)
If I load this file from local device, it is works fine and model display in view(all textures are load properly).
If anyone can help me how to load gltf file from firebase storage
I believe you should use getDownloadURL() from firebase like any other file. check this out Firebase Cloud
May not be the best solution but if you save the file as a .glb (binary version of .gltf), then there is only one file with textures already wrapped up in it. If not, you will need to store all the gltf data in one folder and load the whole lot including textures.

Using pdf.js to render a PDF but it doesn't work and I don't get any error messages to help me debug the issue

I'm trying to build a Flask app where I upload pdf's and I'm working on previewing them before submitting to the back-end.
The script I'm using is as follows:
const imageUploadValidation = (function () {
"use strict";
pdfjsLib.GlobalWorkerOptions.workerSrc =
"https://mozilla.github.io/pdf.js/build/pdf.js";
const onFilePicked = function (event) {
// Select file Nodelist containing 1 file
const files = event.target.files;
const filename = files[0].name;
if (filename.lastIndexOf(".") <= 0) {
return alert("Please add a valid file!");
}
const fileReader = new FileReader();
fileReader.onload = function (e) {
const pdfData = e.target.result;
let loadingTask = pdfjsLib.getDocument({ data: pdfData })
loadingTask.promise.then(function (pdf) {
console.log("PDF loaded", pdf);
pdf.getPage(1).then((page) => {
console.log("page loaded", page);
// var scale = 1.5;
// var viewport = page.getViewport({ scale: scale });
var iframe = document.getElementById("image-preview");
iframe.src = page
// var context = canvas.getContext("2d");
// canvas.height = viewport.height;
// canvas.width = viewport.width;
// var renderContext = {
// canvasContext: context,
// viewport: viewport,
// };
// var renderTask = page.render(renderContext);
// renderTask.promise.then(function () {
// console.log("Page rendered");
// });
});
})
.catch((error) => {
console.log(error);
});
};
const pdf = fileReader.readAsArrayBuffer(files[0]);
console.log("read as Data URL", pdf);
};
const Constructor = function (selector) {
const publicAPI = {};
const changeHandler = (e) => {
// console.log(e)
onFilePicked(e);
};
publicAPI.init = function (selector) {
// Check for errors.
const fileInput = document.querySelector(selector);
if (!selector || typeof selector !== "string") {
throw new Error("Please provide a valid selector");
}
fileInput.addEventListener("change", changeHandler);
};
publicAPI.init(selector);
return publicAPI;
};
return Constructor;
})();
imageUploadValidation("form input[type=file]");
The loading task promise never seems to run. Everything seems to work up until that point. I'm not familiar with this Promise syntax, so I can't be sure if the problem is there or how I'm passing in the pdf file.
P.S. The commented out code is the original way I had this setup, what
s uncommented was just me testing a different way.
Check Datatype
First you might want to check what your getting back from your FileReader, specifically what is the datatype for pdfData. If you have a look at the documentation (direct link) getDocument is expecting a Unit8Array or a binary string.
Add Missing Parameters
The next problem you have is your missing required parameters in your call to getDocument. Here is the minimum required arguments:
var args = {
url: 'https://example.com/the-pdf-to-load.pdf',
cMapUrl: "./cmaps/",
cMapPacked: true,
}
I have never used the data argument in place of the url but as long as you supply the correct datatype you should be fine. Notice that cMapUrl should be a relative or absolute path to the cmap folder. PDFJS often needs these files to actually interpret a PDF file. Here are all the files from the demo repository (GitHub pages): cmaps You'll need to add these to your project.
Instead of using data I would recommend uploading your files as blobs and then all you have to do is supply the blob URL as url. I am not familiar with how to do that, I just know its possible in modern browsers.
Where Is Your Viewer / You Don't Need iFrame or Canvas
PDFJS just needs a div to place the PDF inside of. It's picky about some of the CSS rules, for exmaple it MUST be positioned absolute, otherwise PDFJS generates the pages as 0px height.
I don't see PDFViewer or PDFLinkService in your code. It looks like you are trying to build the entire viewer from scratch yourself. This is no small endeavor. When you get loadingTask working correctly the response should be handled something like this:
loadingTask.promise.then(
// Success function.
function( doc ) {
// viewer is holding: new pdfjsViewer.PDFViewer()
// linkService is: new pdfjsViewer.PDFLinkService()
viewer.setDocument( doc );
linkService.setDocument( doc );
},
// Error function.
function( exception ) {
// What type of error occurred?
if ( exception.name == 'PasswordException' ) {
// Password missing, prompt the user and try again.
elem.appendChild( getPdfPasswordBox() );
} else {
// Some other error, stop trying to load this PDF.
console.error( exception );
}
/**
* Additional exceptions can be reversed engineered from here:
* https://github.com/mozilla/pdf.js/blob/master/examples/mobile-viewer/viewer.js
*/
}
);
Notice that PDFViewer does all the hard work for you. PDFLinkService is needed if you want links in the PDF to work. You really should checkout the live demo and the example files.
Its a lot of work but these example files specifically can teach you all you need to know about PDFJS.
Example / Sample Code
Here is some sample code from a project I did with PDFJS. The code is a bit advanced but it should help you reverse engineer how PDFJS is working under the hood a bit better.
pdfObj = An object to store all the info and objects for this PDF file. I load multiple PDFs on a single page so I need this to keep them separate from each other.
updatePageInfo = My custome function that is called by PDFJS's eventBus when the user changes pages in the PDF; this happens as they scroll from page to page.
pdfjsViewer.DownloadManager = I allow users to download the PDFs so I need to use this.
pdfjsViewer.EventBus = Handles events like loading, page changing, and so on for the PDF. I am not 100% certain but I think the PDFViewer requires this.
pdfjsViewer.PDFViewer = What handles actually showing your PDF to users. container is the element on the page to render in, remember it must be positioned absolute.
// Create a new PDF object for this PDF.
var pdfObj = {
'container': elem.querySelector('.pdf-view-wrapper'),
'document': null,
'download': new pdfjsViewer.DownloadManager(),
'eventBus': new pdfjsViewer.EventBus(),
'history': null,
'id': id,
'linkService': null,
'loaded': 0,
'loader': null,
'pageTotal': 0,
'src': elem.dataset.pdf,
'timeoutCount': 0,
'viewer': null
};
// Update the eventBus to dispatch page change events to our own function.
pdfObj.eventBus.on( 'pagechanging', function pagechange(evt) {
updatePageInfo( evt );
} );
// Create and attach the PDFLinkService that handles links and navigation in the viewer.
var linkService = new pdfjsViewer.PDFLinkService( {
'eventBus': pdfObj.eventBus,
'externalLinkEnabled': true,
'externalLinkRel': 'noopener noreferrer nofollow',
'externalLinkTarget': 2 // Blank
} );
pdfObj.linkService = linkService;
// Create the actual PDFViewer that shows the PDF to the user.
var pdfViewer = new pdfjsViewer.PDFViewer(
{
'container': pdfObj.container,
'enableScripting': false, // Block embeded scripts for security
'enableWebGL': true,
'eventBus': pdfObj.eventBus,
'linkService': pdfObj.linkService,
'renderInteractiveForms': true, // Allow form fields to be editable
'textLayerMode': 2
}
);
pdfObj.viewer = pdfViewer;
pdfObj.linkService.setViewer( pdfObj.viewer );

How do I pass a file/blob from JavaScript to emscripten/WebAssembly (C++)?

I'm writing a WebExtension that uses C++ code compiled with emscripten. The WebExtension downloads files which I want to process inside the C++ code. I'm aware of the File System API and I think I read most of it, but I don't get it to work - making a downloaded file accessible in emscripten.
This is the relevant JavaScript part of my WebExtension:
// Download a file
let blob = await fetch('https://stackoverflow.com/favicon.ico').then(response => {
if (!response.ok) {
return null;
}
return response.blob();
});
// Setup FS API
FS.mkdir('/working');
FS.mount(IDBFS, {}, '/working');
FS.syncfs(true, function(err) {
if (err) {
console.error('Error: ' + err);
}
});
// Store the file "somehow"
let filename = 'favicon.ico';
// ???
// Call WebAssembly/C++ to process the file
Module.processFile(filename);
The directory is created, what can be seen, when inspecting the Web Storage of the browser. If I understand the File System API correctly, I have to "somehow" write my data to a file inside /working. Then, I should be able to call a function of my C++ code (from JavaScript) and open that file as if there was a directory called 'working' at the root, containing the file. The call of the C++ function works (I can print the provided filename).
But how do I add the file (currently a blob) to that directory?
C++ code:
#include "emscripten/bind.h"
using namespace emscripten;
std::string processFile(std::string filename)
{
// open and process the file
}
EMSCRIPTEN_BINDINGS(my_module)
{
function("processFile", &processFile);
}
It turned out, that I was mixing some things up while trying different methods, and I was also misinterpreting my debugging tools. So the easiest way to accomplish this task (without using IDBFS) is:
JS:
// Download a file
let blob = await fetch('https://stackoverflow.com/favicon.ico').then(response => {
if (!response.ok) {
return null;
}
return response.blob();
});
// Convert blob to Uint8Array (more abstract: ArrayBufferView)
let data = new Uint8Array(await blob.arrayBuffer());
// Store the file
let filename = 'favicon.ico';
let stream = FS.open(filename, 'w+');
FS.write(stream, data, 0, data.length, 0);
FS.close(stream);
// Call WebAssembly/C++ to process the file
console.log(Module.processFile(filename));
C++:
#include "emscripten/bind.h"
#include <fstream>
using namespace emscripten;
std::string processFile(std::string filename)
{
std::fstream fs;
fs.open (filename, std::fstream::in | std::fstream::binary);
if (fs) {
fs.close();
return "File '" + filename + "' exists!";
} else {
return "File '" + filename + "' does NOT exist!";
}
}
EMSCRIPTEN_BINDINGS(my_module)
{
function("processFile", &processFile);
}
If you want to do it with IDBFS, you can do it like this:
// Download a file
let blob = await fetch('https://stackoverflow.com/favicon.ico').then(response => {
if (!response.ok) {
return null;
}
return response.blob();
});
// Convert blob to Uint8Array (more abstract: ArrayBufferView)
let data = new Uint8Array(await blob.arrayBuffer());
// Setup FS API
FS.mkdir('/persist');
FS.mount(IDBFS, {}, '/persist');
// Load persistant files (sync from IDBFS to MEMFS), will do nothing on first run
FS.syncfs(true, function(err) {
if (err) {
console.error('Error: ' + err);
}
});
FS.chdir('/persist');
// Store the file
let filename = 'favicon.ico';
let stream = FS.open(filename, 'w+');
FS.write(stream, data, 0, data.length, 0);
FS.close(stream);
// Persist the changes (sync from MEMFS to IDBFS)
FS.syncfs(false, function(err) {
if (err) {
console.error('Error: ' + err);
}
});
// NOW you will be able to see the file in your browser's IndexedDB section of the web storage inspector!
// Call WebAssembly/C++ to process the file
console.log(Module.processFile(filename));
Notes:
When using FS.chdir() in the JS world to change the directory, this also changes the working directory in the C++ world. So respect that, when working with relative paths.
When working with IDBFS instead of MEMFS, you are actually still working with MEMFS and just have the opportunity to sync data from or to IDBFS on demand. But all your work is still done with MEMFS. I would consider IDBFS as an add-on to MEMFS. Didn't read that directly from the docs.

Upload file from Javascript to Google Cloud Endpoint

I'm creating a web app using only HTML5 + Javascript + jQueryMobile and I wanted to upload a file to a Google App Engine web application using a Google Cloud Endpoint, also created by me.
As I control both sides, I can (and want to) create the simplest interaction possible.
As for the Endpoint, I thought of creating a method like this:
#ApiMethod(
name = "uploadFile",
path = "upload_file",
httpMethod = HttpMethod.POST
)
public void uploadFile(File file) {
//process the file
}
This File class could contain a field fileData of type Blob, or byte[] or something like that, repersenting the file data... Something like:
public class File {
private String fileName;
private long fileSize;
private Blob fileData;
//getters and setters
}
So the first question would be: what's the most suitable type for this field fileData?
And, taking into account the type selected for the field, how could I create the necessary POST request for that endpoint method form Javascript/jQuery?
Basically I need to create a POST request to http://myappid.appspot.com/_ah/api/files/v1/upload_file adding the File object in the POST data.
Note: I'm sorry I haven't tried anything for the Javascript code because I'm not familiar at all with this technologies, so I'd appreciate any help...
Edit: The answer below targes python version of AppEngine
It is a common demand with no clear solution. Till now, gae-init-upload is a demonstration of how you can achieve that with AppEngine and CoffeeScript. Worth having a look, CoffeeScript is being compiled into JavaScript in case you are not familiar.
The JavaScript solution you are looking for is under
/main/static/src/coffee/common/upload.coffee
I eventually used this code in my AMD Javascript application. I'm sorry I cannot explain it too much because I've written a big amount of code since I wrote this project, and as you can see I didn't comment the code properly (fail!!), anyway maybe you can get some ideas...
Note that there's something about getting navigator position because I wanted to store the location where the file was uploaded from, but it's not necessary at all!
Controller.js
uploadFile: function(request, render) {
var self = this;
var file = $("#file").get(0).files[0];
var reader = new FileReader();
reader.onload = function (evt) {
var upload = {
provider: self.folder.provider,
folderIdentifier: self.folder.id,
fileName: file.name,
fileSize: file.size,
base64Data: btoa(evt.target.result),
location: {
latitude: self.position.coords.latitude,
longitude: self.position.coords.longitude
}
}
var uploadFilePromise = self.connector.uploadFile(self.sessionToken.token, upload);
uploadFilePromise.done(function (file) {
render("file", {
result: "DONE",
file: file
});
});
uploadFilePromise.fail(function (error) {
render("file", {
result: "FAIL"
});
});
}
navigator.geolocation.getCurrentPosition(function(position) {
self.position = position;
reader.readAsBinaryString(file);
});
}
Connector.js
uploadFile: function (sessionToken, upload) {
var self = this;
var promise = new Promise();
gapi.client.load('upload', 'v1', function() {
var request = gapi.client.upload.uploadFile({
session_token: sessionToken,
resource: upload
});
request.execute(function(response) {
if (response.error) {
promise.reject(response.error);
}
else {
var file = File.create(response.result.provider,
response.result.type,
response.result.identifier,
response.result.name,
response.result.description,
response.result.created,
response.result.size,
response.result.link,
{
latitude: response.result.location.latitude,
longitude: response.result.location.longitude
});
promise.resolve(file);
}
});
}, self.api);
return promise;
}
Endpoint.java
#Api(name="upload")
public class UploadEndpoint {
#ApiMethod(
name = "uploadFile",
path = "upload_file",
httpMethod = HttpMethod.POST
)
public File uploadFile (
#Named("session_token") String token,
Upload upload) throws InternalServerErrorException {
File file = new UploadController().uploadFile(token, upload);
return file;
}
}

Categories