After updating from v133 to 134, I get this error.
I assume it is due to this: " FileLoader now uses fetch instead of XMLHttpRequest ."
How can I fix this error? It was working fine before the switch to 134, and there is no migration guide on this topic.
See How to run things locally — use of a local server is required when developing web applications with three.js; that has been the case for quite a while due to browser security policies.
Perhaps NW.js was patching XmlHttpRequest to then. three.js does not officially maintain any support for Node.js, so switching from one browser API (XmlHttpRequest) to another browser API (fetch) would not appear as a migration guide topic.
You could try something like this after installing node-fetch, but I’m not familiar with NW.js enough to say whether it will work; normally you don’t make HTTP requests to paths on disk, and all three.js loaders are designed around HTTP requests.
I tried, but I couldn’t get it to work. I don’t know much about node modules, and it is confusing the information about it. I will ask the nw.js and node-fetch people.
I found that using
var url = URL.createObjectURL(e.target.files[0])
can work in some cases to load files, however when loading an MTL file that contains paths to texture images, it won’t load those because it tries to combine the objectURL with the relative paths, creating invalid paths. Another thing is that relative paths can’t work across different harddrives, and the application can be on separate drive from the files that the user wants to load.
The purpose of test.html was to show you the behavior of fetch() API when dealing with file protocol. The error has nothing to do with nw,js nor three.js lib.
Your best option is to set up a virtual host to fetch content of a file programically. window.location.href returns file URL, but that is not going to help you. You need to get the content of texture listed in MTL file not its location.
I’m not familiar with NW.js but i had a similar issue using electron and maybe my workaround gives you an idea (this worked for me).
Since FileLoader works on fetch requests, I detect them and I return my desired modified response.
const { fetch: origFetch } = window;
window.fetch = async (...args) => {
let response;
let args_ = args;
try {
response = await origFetch(...args);
} catch (error) {
//
} finally {
if (args_[0].url.includes('.gltf')) {
let myLocalFileName= args_[0].url.split('/')[args_[0].url.split('/').length - 1];
let myLocalFile = await myBackgroundAPI.readExtraResourceFile('extraResources/' + myLocalFileName);
let myBlob = new Blob([myLocalFile ], { type: 'model/gltf+json' });
let myModifiedResponse = new Response(myBlob, { status: 200, statusText: 'OK' });
return myModifiedResponse ;
}
if (args_[0].url.includes('.bin')) {
let myLocalFileName= args_[0].url.split('/')[args_[0].url.split('/').length - 1];
let myLocalFile = await backgroundAPI.readExtraResourceFile('extraResources/' + myLocalFileName);
let myBlob = new Blob([myLocalFile ], { type: 'application/octet-stream' });
let myModifiedResponse = new Response(myBlob , { status: 200, statusText: 'OK' });
return myModifiedResponse ;
}
}
};
I can’t do that, because I shouldn’t expect users of my application to have to setup their own servers. Also, the .mtl files are suppose to contain paths to other files- that is part of their purpose when working with 3d .obj files. Other 3d formats also contain paths to files. So I need to be able to load via the file url scheme.
Can you explain what your code does? I’m not that familiar with how fetch works- so I’m not sure what your code is doing. Is it modifying the original fetch to make it work differently? So whenever fetch is called, it does the modified version?
In my case I needed to use GLTFLoader (that use FileLoader ”behind the curtains”). It loads a .gltf file that calls (via fetch) for other .bin files (similar to what you wrote).
So in my code when a fetch request is detected, an error is expected. The “try-catch” detects the error and the trick is done in the “finally” block. Since the fetch has failed there in so a fetch response, so a costumed one is created. The needed files in my case are read by another API that uses the normal fs module of node and put in a Blob that becomes the body of the fetch custom response.
Thanks for the explanation. I tried to implement that, and it almost works… In the case of MTLLoader, there is a .preload() to load the textures, however it fails with this error: GET chrome-extension://jdgkbifmkkkcgglljmlojibojoafmlhi/texture_0.png net::ERR_FILE_NOT_FOUND
So I can’t get the textures to load that the MTL file references. It manages to load the .mtl file, but then it can’t continue because it uses some other way to load the textures.
If I look in the MTLLoader, it seems it goes wrong here: const map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
It’s the same sequence:
.mtl file is requested and loaded, and upon loaded the MTLLoader retrieves the file paths that are stored inside the .mtl file and attempts to load the textures into the new materials. The file paths are relative( I think ), ex: “texture_0.png”… however it combines it with “chrome-extension://jdgkbifmkkkcgglljmlojibojoafmlhi/” which is an invalid path.
It combines the two like this in the mtlloader const map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
You can see the scope.baseURL is the “chrome-extension://jdgkbifmkkkcgglljmlojibojoafmlhi/”
I think that you have to manage more in details each fetch.
When you load the .mtl file, you specify ‘totally’ the path/url while the other requested files the path/url is generated by the threejs loaders function and not by you.
If you go back to my case, I detect the fetch of .bin files. These fetch are not directly made by me but are generated by the loader of threejs.