Render loop exploding - three.js

I made a small demo/boilerplate for loading a custom mesh with Threejs and inspecting it using orbital controls.
The problem here is that when rotating the object the render loop seems to explode and I can't figure out why.
https://codepen.io/rosefalk/pen/ooBYme?editors=0010
function render() {
console.log('render')
canvasPosition = document.getElementById( "container" ).children;
renderer.render( scene, camera );
requestAnimationFrame( render );
}
If you look in the console it's chugging away merily at what I assume is ~60 calls a second until you start rotating, then it goes up at extreme rate.

Solution - Removal of this part:
controls.addEventListener( 'change', render );
When controls are updating it calls render, render calls itself and that explodes the whole thing, adding a massive amount of extra calls. It's a remnant from when it made sense to only update the scene on user interaction.

Related

Spotlight object attached to an animated model doesn't move with the object in my ThreeJS application?

I have a camera drone animation model that I attach two objects to. The camera drone is just a black sphere with a texture. I attach a red sphere to the camera drone where the lens appears on its textureis, to make it look like a security camera lens. Works great. When I move the model, the red sphere moves with the camera drone perfectly as it moves and rotates.
However, I also attach a spotlight at the exact same place on the camera drone object. My desire is to have the spotlight beam come out of the camera drone lens so that it appears to emanate from the lens. However, when the camera drone moves, the spotlight stays in place and gets left behind, despite the fact I attach the spotlight the exact same way and in the exact same place as I attach the red sphere. In other words, when the camera drone moves away, I see the spotlight hanging in space where it was first created instead of it traveling in lock step with the camera drone.
Below is the code I use to create and attach the red sphere and spotlight. The code executes when the GLB loader callback executes.
Note: self.CAC.modelInstance is where the ThreeJS object for the camera drone animation model is stored. Also, AnimatedLight is the object I created to build a spotlight and animate it. Finally, initModelArgs is an object that contains some size, position, and other settings that are loaded from a file earlier.
Can someone tell me what I am doing wrong and how to fix this problem? Also, can you tell me what it is about the way that I think how the spotlight should work that is also wrong?
// PARAMETERS: url, onLoad, onProgress, onError
self.CAC.modelLoader.load(
// url
urlToModel,
// onLoad
(glb) => {
// Size.
glb.scene.scale.x = initModelArgs.theScale;
glb.scene.scale.y = initModelArgs.theScale;
glb.scene.scale.z = initModelArgs.theScale;
self.CAC.setModelInstance(glb.scene);
// Set the initial position.
self.CAC.modelInstance.position.x = initModelArgs.initialPos_X;
self.CAC.modelInstance.position.y = initModelArgs.initialPos_Y;
self.CAC.modelInstance.position.z = initModelArgs.initialPos_Z;
// Create an animation mixer for this model.
self.CAC.modelAnimationMixer = new THREE.AnimationMixer(self.CAC.modelInstance);
// Speed
self.CAC.modelAnimationMixer.timeScale = initModelArgs.theTimeScale;
// Add the model to the scene.
g_ThreeJsScene.add(self.CAC.modelInstance);
// Initialize the model completely. The camera drone has no
// animations because we animate it manually, so we pass
// NULL to let the initializeModelCompletely() function
// know that.
self.CAC.initializeModelCompletely(null);
// NOTE: Camera drone has no animations/actions. We animate it
// manually.
// -------------------- BEGIN: SUB-OBJECTS ------------
// Call updateCenterOfModel so our bounding box and center properties
// are correct.
self.CAC.updateCenterOfModel();
// Create the sub-objects necessary for emulating a light
// projector on the camera drone.
// .................... BEGIN: SUB-OBJECT - Camera Lens ............
const radius = 3;
const cameraLensObj =
new THREE.Mesh(
new THREE.SphereBufferGeometry( radius, 20, 20 ),
new THREE.MeshPhongMaterial( { color: 0xFF0000 } ));
cameraLensObj.position.set(0, 0, 15);
// Add it to the top level animation model.
self.CAC.modelInstance.add(cameraLensObj);
// .................... END : SUB-OBJECT - Camera Lens ............
// .................... BEGIN: SUB-OBJECT - Light Beam ............
const spotLightAsLightBeam_anim_obj =
new AnimatedLight(
`${self.CAC.modelName}-lens-light-beam`,
modelDeclJsonObj,
'SpotLight',
// Tell the animated light to augment our mini-menu
// instead of creating its own.
self);
// Call initialize model on the spotlight or we will not have
// the model instance filled in.
spotLightAsLightBeam_anim_obj.initializeModel(self.CAC.parentAnimManagerObj, initModelArgs);
const spotLightModelInstanceObj = spotLightAsLightBeam_anim_obj.CAC.modelInstance;
// Position the spotlight that acts as the camera drone's light
// beam right on top of the small sphere we use to emulate
// a projector's lens.
spotLightModelInstanceObj.position.set(0, 0, 15);
// Add it to the top level animation model.
self.CAC.modelInstance.add(spotLightAsLightBeam_anim_obj.CAC.modelInstance);
// .................... END : SUB-OBJECT - Light Beam ............

Three.js: Is throttling / intelligent calling of rendering necessary?

Currently I am using a simple loop in Three.js based on requestAnimationFrame:
...
requestAnimationFrame(animate); // initial call
...
function animate() {
requestAnimationFrame(animate);
// do something
renderer.render(scene, camera); // always render the scene
}
So the render function is called each time animate is executed via requestAnimationFrame. Is that considered good practise? Is the renderfunction optimized so that it does not rerender the scene if nothing (visible) has changed? Or do I have to throttle the call / call the function only if something has changed?

Clean up Threejs WebGl contexts

I have a problem while cleaning up my WebGl-Scenes. I'm using Three.js with a WebGlRenderer. In my application I have to change the views quite often and therefore need to render new scenes all the time. Uptil now I destroy and reinitialize the entire Threejs scene. After switching the scenes about 15 - 20 times I get following warning:
WARNING: Too many active WebGL contexts. Oldest context will be lost.
After switching a couple of times more the context is lost completly and the application crashes.
Is there a way to destroy the current WebGl context, when cleaning up? Or does the WebGlRenderer always create a new WebGl context when being instantiated?
I'm using Three.js R64.
I've got same problem, but i couldn't solve it using SPA, because of requirements.
There is .forceContextLoss() method in WebGLRenderer (rev 71, maybe was early) for this situations.
So, my code in 'deallocate' method is somemethig like
_self.renderer.forceContextLoss();
_self.renderer.context = null;
_self.renderer.domElement = null;
_self.renderer = null;
You can keep the same renderer for different scenes. The renderer does not care what scene it will render. You can supply a different Scene everytime you call render() if you like.
// instantiate only once and keep it
var renderer = new THREE.WebGLRenderer();
// current scene and camera. Switch whenever you like
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(...);
fillScene(scene);
// rendering always uses current scene
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
/* ...
* somewhere in your application
* ...
*/
if(condition) {
// switch scene
scene = new THREE.Scene();
fillOtherScene(scene);
}
You should create and use only one WebGlRenderer. In my SPA (single page application) I had weird problem with camera/scene in THREE.js after few redirects. It was because WebGlRenderer was created every time when one particular page was rendered. There was no error in console log (only the warning you wrote).
Bug appeared as change in camera position and problems with rendering.

three.js object is black before texture loads - can I change this behavior?

I am very new to three.js. In my project you can move through a series of planes with cross section images of an object. The problem is that the material doesn't load right away and moving up and down too quickly causes some of the planes to display black. I need to change this behavior. Is there a way to either
change some property so the plane is transparent - but the image should still be opaque when loaded
or don't display/render the plane at all until the texture is loaded?
I'm not at all sure I am on the right track, and I am hoping someone more experienced can suggest a specific fix.
Thanks!
Not sure if you already cleared this up but I made a handy little function to take care of this by modifying the opacity setting, the basic of which are:
function loadTexture( path ){
var mat = new THREE.MeshBasicMaterial({
map: new THREE.ImageUtils.loadTexture( path, null, function() {
mat.opacity = 1;
} ),
opacity: 0
});
return mat;
}

Can't seem to make a functional THREE.js collada loader

I'm having issues getting a custom made collada object with no built in camera or lighting to render. I more or less copied what I had seen in a few collada examples to create my own loader, which looks like this:
var loader = new THREE.ColladaLoader();
var room, scene, stats;
loader.load('../Models/Rooms/boot.dae', function colladaReady( collada ){
collada.scene.getChildByName('Cube',true).doubleSided = true;
room = collada.scene;
room.scale.x = room.scale.y = room.scale.z = 1;
room.updateMatrix();
init();
});
The init function is fairly basic and looks like this
scene = new THREE.Scene();
scene.add( room );
scene.add( camera );
renderer.render(scene, camera);
Here is the actual object I'm trying to render. I have also tried it out with the monster.dae file that is in the examples folder without success. The Chrome javascript console isn't showing any errors, so I'm not quite sure where to look in my code. It all resembles functional examples, so I'm not sure why it's not functional. Is there something I'm unaware of that is relevant to collada loading?
RESOLVED: The item was rendering, but had no skin or texture associated with it. So it was rendering at the same colour as the background, which understandably appears to not be rendering at all. Discovered by adding a grid to the ground just to check.

Resources