mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
103.07k stars 35.41k forks source link

Collada Loader Caching Textures/Not Updating per needsUpdate #7207

Closed krusk closed 9 years ago

krusk commented 9 years ago

I have a post on StackOverflow here: http://stackoverflow.com/questions/32632169/re-rerender-refresh-3d-object-once-texture-is-changed

However, the more I look at the issue, I want to verify that the Collada loader supports updating textures from a server or if we can make a feature request to change how the Collada Loader manages cached textures in three.js as currently if I update the texture (by uploading a new file that replaces the current texture to the server where it is stored/referenced), it won't re-render unless I refresh the entire page.

If I am missing something obvious (please see my stack post), I apologize, but didn't want to overlook an opportunity to improve three.js if possible.

mrdoob commented 9 years ago

Can't you use the usual anti-cache trick? var url = 'image.png?' + Math.random();

krusk commented 9 years ago

Thanks @mrdoob. Since the Collada file is directly looking for Texture_0.png (see stack post), where/how would I include this in my three.js call? I tried this, but it didn't work:

var url = 'https://myblob.net/Texture_0.png?' + Math.random(); newtexture = THREE.ImageUtils.loadTexture(url);

krusk commented 9 years ago

Any other ideas to resolve the caching issue/is this a feature that can/should be added (i.e. no cache, or browser cache control?)

mrdoob commented 9 years ago

Considering that ColladaLoader uses ImageLaoder internally... maybe you can do a hack like this:

THREE.ImageLoader.prototype._load = THREE.ImageLoader.prototype.load;
THREE.ImageLoader.prototype.load = function ( url, onLoad, onProgress, onError  ) {
    this._load( url + '?' + Math.random(), onLoad, onProgress, onError  );
};
krusk commented 9 years ago

Would that live in my init function or somewhere else (i.e.: within my collada loader call?)

mrdoob commented 9 years ago

In your init function.

krusk commented 9 years ago

Getting closer. I get the following error: RangeError: Maximum call stack size exceeded

Here is the sequence of events:

1) On first click to show the 3D box in a new session, the change displays

2) Change the texture and upload it, then click to the show the 3D box in the same session, it shows the changes. Previously, it would not show until the page was refreshed.

3) Change the texture and upload it, then click to the show the 3D box in the same session. RangeError: Maximum call stack size exceeded

mrdoob commented 9 years ago

Could you create a jsfiddle?

krusk commented 9 years ago

I can try. Will need to figure out where to store/reference the remote images. There is a lot of backend work with Azure/Express that I am not allowed to post publicly.

mrdoob commented 9 years ago

I just tried adding the suggested patch in webgl_loader_collada and it did the trick.

screen shot 2015-09-25 at 18 10 32

krusk commented 9 years ago

You have disable cache enabled as part of Chrome. Can you try it without the cache disabled? Any clues why mine is not working given my code as follows/does anything look suspicious (i.e. is calling newtexture needed with your code?). Really appreciate all the assistance and your work on the three.js library.

$scope.generate3D = function () {

// 3D OBJECT - Variables
var texture0 = baseBlobURL + 'Texture_0.png?' + Math.random();
var boxDAE = baseBlobURL + 'Box.dae?' + Math.random();
var scene;
var camera;
var renderer;
var box;
var controls;
var newtexture;

// 3D OBJECT - Generate  

newtexture = THREE.ImageUtils.loadTexture(texture0);

//Instantiate a Collada loader
var loader = new THREE.ColladaLoader();

loader.options.convertUpAxis = true;
loader.load(boxDAE, function (collada) {

box = collada.scene;

box.traverse(function (child) {

if (child instanceof THREE.SkinnedMesh) {

var animation = new THREE.Animation(child, child.geometry.animation);
animation.play();

}
});

box.scale.x = box.scale.y = box.scale.z = .2;
box.updateMatrix();

init();
animate();
});

function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer();

renderer.setClearColor(0xdddddd);

renderer.setSize(500, 500);

// Load the box file
scene.add(box);

// Lighting
var light = new THREE.AmbientLight();
scene.add(light);

// Camera
camera.position.x = 40;
camera.position.y = 40;
camera.position.z = 40;

camera.lookAt(scene.position);

// Rotation Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);

controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5;

controls.noZoom = false;
controls.noPan = false;

THREE.ImageLoader.prototype._load = THREE.ImageLoader.prototype.load;
THREE.ImageLoader.prototype.load = function ( url, onLoad, onProgress, onError  ) {
this._load( url + '?' + Math.random(), onLoad, onProgress, onError  );
};

var myEl = angular.element(document.querySelector('#webGL-container'));
myEl.append(renderer.domElement);

}

function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);               
}
}
krusk commented 9 years ago

Should this be rolled in as a change to the Collada loader, instead of a patch?

Thanks again for the assistance,

K.

mrdoob commented 9 years ago

You should add the patch before //Instantiate a Collada loader.

Should this be rolled in as a change to the Collada loader, instead of a patch?

If anything... maybe we could add something to THREE.ImageLoader to append random to the urls.

krusk commented 9 years ago

Thanks mrdoob. I will give that a try. I like the idea of the append. Unless someone has a reason to cache where they need to cache. Perhaps a method for add random/no cache?

krusk commented 9 years ago

... That way users can choose if they want to cache or not.

krusk commented 9 years ago

I moved the code as instructed but am still getting a "Maximum call stack size exceeded" error in Chrome and a "Max stack size" error in IE. I am using AngularJS and don't know if this could be the cause or if there is a circular reference somewhere.

krusk commented 9 years ago

Issue reposted on StackOverflow: http://stackoverflow.com/questions/32750463/angular-or-three-js-not-pulling-image-from-server-but-rather-browser-cache

Still no resolution. I tried the patch, but it keeps giving a

Uncaught RangeError: Maximum call stack size exceeded

krusk commented 9 years ago

I have been able to post a Plunk that shows the stack error when using the patch:

http://plnkr.co/edit/DGZA9LUBZWsLWrmXaaKD

Appreciate the assistance. I have learned a lot about three.js/WebGL in the process!

mrdoob commented 9 years ago

Please, remove ImageUtils.js and ImageLoader.js. These files are already included in three.min.js.

krusk commented 9 years ago

Thanks for following up on this. @WestLangley worked tirelessly on this with me today as well, and we found that by adding this to the ImageLoader.js file, it worked:

url = url + '?' + Math.random();

I just tested with the prototype patch you offered without the ImageUtils and ImageLoader, but it still threw the error for me, as it appeared that my program was calling itself. I noted in the following stack post that the majority of users would not have my issue/I don't want to encourage users to start hacking at the core libraries (see my answer where I denoted it):

http://stackoverflow.com/questions/32750463/angular-or-three-js-not-pulling-image-from-server-but-rather-browser-cache/32766069?noredirect=1#comment53582455_32766069

Thank you for all that you, and the contributors have done.