Reputation: 1855
I can get the model to render, I can rotate, and zoom and everything is good UNTIL, I try to clear the scene. I want to clear the scene by clicking the button#clear
.
I expect the scene to be traversed, and have its children added to an array. I then expect to iterate over the array and for each item in the array clear it out by calling dispose()
on it if it's a geometry or a material and then remove the mesh from the scene.
<html>
<head>
<title>Three.js</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<button id="clear">Clear Three Out</button>
<script src="js/three.js"></script>
<script src="js/OrbitControls.js"></script>
<script src="js/ObjectLoader.js"></script>
<script>
<script>
(function(){var script=document.createElement('script');script.onload=function(){var stats=new Stats();document.body.appendChild(stats.dom);requestAnimationFrame(function loop(){stats.update();requestAnimationFrame(loop)});};script.src='//rawgit.com/mrdoob/stats.js/master/build/stats.min.js';document.head.appendChild(script);})();
var scene = new THREE.Scene();
var width = window.innerWidth;
var height = window.innerHeight;
var camera = new THREE.OrthographicCamera(width / - 2, width / 2, height / 2, height / - 2, -1000, 1000); // new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var blackReflectiveMaterial = new THREE.MeshBasicMaterial();
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// handle resize event
window.addEventListener( 'resize', function() {
var width = window.innerWidth;
var height = window.innerHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
} );
// controls
controls = new THREE.OrbitControls( camera, renderer.domElement );
camera.position.z = 3;
// model loader
var loader = new THREE.ObjectLoader();
loader.load(
'models/revol-uv.json', // model of a compound bow
function( object ) {
console.log('object: ', object);
object.scale.x = object.scale.y = object.scale.z = 200;
object.traverse( function(child) {
child.material = blackReflectiveMaterial;
});
scene.add( object );
}
);
scene.translateY(-345);
// array to hold Scene's children
var holder = [];
// when click the #clear button, call this function
document.getElementById('clear').onclick = function() {
scene.traverse( function ( child ) {
if (child.type === "Mesh") {
holder.push(child);
}
} );
console.log('holder before clean: ', holder);
for (var i = 0; i < holder.length; i++) {
if (holder[i].geometry) {
holder[i].geometry.dispose();
}
console.log('holder[i].material: ', holder[i].material);
if (holder[i].material) {
if (holder[i].map) {
holder[i].material.map.dispose();
}
holder[i].material.dispose();
}
console.log('holder[i]: ', holder[i]);
scene.remove(holder[i]);
}
console.log('holder after clean: ', holder); // all the objects are still there
}
// logic
var update = function() {
// any code to rotate ... blah blah not important
};
// draw Scene
var render = function() {
renderer.render( scene, camera );
};
// run loop (update, render, repeat)
var bowLoop = function() {
requestAnimationFrame( bowLoop );
update();
render();
};
bowLoop();
</script>
</body>
</html>
Here's the console logs:
The children of the scene
Array(10)
0:THREE.Mesh {uuid: "5574129D-7093-4567-8927-4F07BB7A3928", name: "cable-arm", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
1:THREE.Mesh {uuid: "ED867F5C-F6C4-45BC-BBF7-9DD5061A0CF6", name: "cams", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
2:THREE.Mesh {uuid: "D69C58BB-1E03-4D00-8050-6F2F4739DFE9", name: "handles", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
3:THREE.Mesh {uuid: "50A98720-4E8F-4873-B987-0C993D05FE65", name: "hardware", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
4:THREE.Mesh {uuid: "6CE44421-0614-40E9-8013-FE939CFA0191", name: "limb-pockets", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
5:THREE.Mesh {uuid: "60FF4521-B19B-477D-8223-8B4BFA2A0F5A", name: "limbs", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
6:THREE.Mesh {uuid: "D2980C91-0B19-4757-A831-A3B46546E579", name: "limbsavers", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
7:THREE.Mesh {uuid: "D9858B1C-E4F7-40C4-98DD-163230F9A917", name: "riser", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
8:THREE.Mesh {uuid: "BC7C199B-2867-4446-86FE-D244D7A32E07", name: "string-supressor", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
9:THREE.Mesh {uuid: "02A038E9-FB0B-43FF-B3F2-BCCAFD7E10AC", name: "strings", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
The holder array before the clean:
Array(10)
0:THREE.Mesh {uuid: "5574129D-7093-4567-8927-4F07BB7A3928", name: "cable-arm", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
1:THREE.Mesh {uuid: "ED867F5C-F6C4-45BC-BBF7-9DD5061A0CF6", name: "cams", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
2:THREE.Mesh {uuid: "D69C58BB-1E03-4D00-8050-6F2F4739DFE9", name: "handles", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
3:THREE.Mesh {uuid: "50A98720-4E8F-4873-B987-0C993D05FE65", name: "hardware", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
4:THREE.Mesh {uuid: "6CE44421-0614-40E9-8013-FE939CFA0191", name: "limb-pockets", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
5:THREE.Mesh {uuid: "60FF4521-B19B-477D-8223-8B4BFA2A0F5A", name: "limbs", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
6:THREE.Mesh {uuid: "D2980C91-0B19-4757-A831-A3B46546E579", name: "limbsavers", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
7:THREE.Mesh {uuid: "D9858B1C-E4F7-40C4-98DD-163230F9A917", name: "riser", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
8:THREE.Mesh {uuid: "BC7C199B-2867-4446-86FE-D244D7A32E07", name: "string-supressor", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
9:THREE.Mesh {uuid: "02A038E9-FB0B-43FF-B3F2-BCCAFD7E10AC", name: "strings", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
The holder array after the clean:
Array(10)
0:THREE.Mesh {uuid: "5574129D-7093-4567-8927-4F07BB7A3928", name: "cable-arm", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
1:THREE.Mesh {uuid: "ED867F5C-F6C4-45BC-BBF7-9DD5061A0CF6", name: "cams", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
2:THREE.Mesh {uuid: "D69C58BB-1E03-4D00-8050-6F2F4739DFE9", name: "handles", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
3:THREE.Mesh {uuid: "50A98720-4E8F-4873-B987-0C993D05FE65", name: "hardware", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
4:THREE.Mesh {uuid: "6CE44421-0614-40E9-8013-FE939CFA0191", name: "limb-pockets", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
5:THREE.Mesh {uuid: "60FF4521-B19B-477D-8223-8B4BFA2A0F5A", name: "limbs", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
6:THREE.Mesh {uuid: "D2980C91-0B19-4757-A831-A3B46546E579", name: "limbsavers", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
7:THREE.Mesh {uuid: "D9858B1C-E4F7-40C4-98DD-163230F9A917", name: "riser", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
8:THREE.Mesh {uuid: "BC7C199B-2867-4446-86FE-D244D7A32E07", name: "string-supressor", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
9:THREE.Mesh {uuid: "02A038E9-FB0B-43FF-B3F2-BCCAFD7E10AC", name: "strings", type: "Mesh", parent: T…E.Scene, children: Array(0), …}
No change in memory ... objects are still in heap.
Upvotes: 3
Views: 5225
Reputation: 550
Please check the below code for loading the GLTF file and release memory...
let ZomModel = undefined;
function loadZoom() {
const gltfLoader = new GLTFLoader();
gltfLoader.load(modelName, (gltf) => {
const root = gltf.scene;
setmaterial(root);
ZomModel = root;
root.position.set(0, -3, 0);
root.scale.set(5, 5, 5);
scene.add(root);
console.log(root, 'root')
});
}
function loadNew(isLoad) {
console.log("reload " + isLoad);
// return;
if (isLoad) {
if (ZomModel == undefined)
loadZoom();
} else {
// defultmaterial(ZomModel);
if (ZomModel == undefined) return;
ZomModel.traverse(object => {
if (!object.isMesh) return
scene.remove(object);
console.log('dispose geometry!')
object.geometry.dispose()
if (object.material.isMaterial) {
cleanMaterial(object.material)
} else {
// an array of materials
for (const material of object.material) cleanMaterial(material)
}
object.geometry = undefined;
object = undefined;
});
scene.remove(ZomModel);
ZomModel = undefined;
}
}
const cleanMaterial = material => {
console.log('dispose material!')
// dispose textures
for (const key of Object.keys(material)) {
const value = material[key]
if (value && typeof value === 'object' && 'minFilter' in value) {
console.log('dispose texture! ' + key)
value.dispose();
}
}
material.dispose();
material = undefined;
}
Upvotes: 0
Reputation: 1855
@gaitat - thank you for supplying a tremendous bulk of the solution.
I needed to just modify some lines to meet my needs:
function disposeNode(node) {
if (node instanceof THREE.Mesh) {
if (node.geometry) {
node.geometry.dispose();
node.geometry = undefined; // fixed problem
}
if (node.material) {
if (node.material instanceof THREE.MeshFaceMaterial || node.material instanceof THREE.MultiMaterial) {
node.material.materials.forEach( function(mtrl, idx) {
if (mtrl.map) mtrl.map.dispose();
if (mtrl.lightMap) mtrl.lightMap.dispose();
if (mtrl.bumpMap) mtrl.bumpMap.dispose();
if (mtrl.normalMap) mtrl.normalMap.dispose();
if (mtrl.specularMap) mtrl.specularMap.dispose();
if (mtrl.envMap) mtrl.envMap.dispose();
mtrl.dispose();
mrtl = undefined; // fixed problem
} );
}
else {
if (node.material.map) node.material.map.dispose();
if (node.material.lightMap) node.material.lightMap.dispose();
if (node.material.bumpMap) node.material.bumpMap.dispose();
if (node.material.normalMap) node.material.normalMap.dispose();
if (node.material.specularMap) node.material.specularMap.dispose();
if (node.material.envMap) node.material.envMap.dispose();
node.material.dispose();
node.material = undefined; // fixed problem
}
}
}
console.log('node before removal: ', node);
scene.remove( node );
renderer.dispose(); // ***EDIT*** improved even memory more original scene heap is 12.4 MB; add objects increases to 116 MB or 250 MB (different models), clearing always brings down to 13.3 MB ... there still might be some artifacts.
node = undefined; // unnecessary
}
function disposeHierchy(node, callback) {
for (var i = node.children.length - 1; i >= 0; i--) {
var child = node.children[i];
disposeHierchy(child, callback);
callback(child);
}
}
Call it:
document.getElementById('clear').onclick = function() {
disposeHierchy(scene, disposeNode);
console.log('renderer.info.memory after: ', renderer.info);
}
Upvotes: 0
Reputation: 6986
Two things come to mind:
it looks like holder
is still holding references to all objects after the clear-function was called (h/t @pailhead). You should add something like holder = [];
at the end of the function to get rid of them as well. Also make sure that there are no other accessible variables holding references to any of the objects.
be aware that the objects will not automatically disappear from memory but only after the garbage-collector did its work. Depending on how much is going on otherwise that might take a moment.
Upvotes: 1