Skip to content

Instantly share code, notes, and snippets.

@greggman
Last active September 15, 2021 04:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save greggman/57dafa41cb1d2d5bc1520832db49f946 to your computer and use it in GitHub Desktop.
Save greggman/57dafa41cb1d2d5bc1520832db49f946 to your computer and use it in GitHub Desktop.
WebGL-Memory Example
html, body {
height: 100%;
margin: 0;
}
#info {
position: absolute;
left: 0;
top: 0;
padding: 0.5em;
color: white;
background: rgba(0, 0, 0, 0.8);
white-space: pre;
font-family: monospace;
}
#c {
width: 100%;
height: 100%;
display: block;
}
#root {
position: absolute;
left: 0;
top: 0;
}
<canvas id="c"></canvas>
<div id="info"></div>
// Three.js - Cleanup Loaded Files
// from https://threejsfundamentals.org/threejs/threejs-cleanup-loaded-files.html
//import 'https://greggman.github.io/webgl-memory/webgl-memory.js';
import 'https://greggman.github.io/webgl-memory/webgl-memory.js';
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/build/three.module.js';
import {GLTFLoader} from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/loaders/GLTFLoader.js';
class ResourceTracker {
constructor() {
this.resources = new Set();
}
track(resource) {
if (!resource) {
return resource;
}
// handle children and when material is an array of materials or
// uniform is array of textures
if (Array.isArray(resource)) {
resource.forEach(resource => this.track(resource));
return resource;
}
if (resource.dispose || resource instanceof THREE.Object3D) {
this.resources.add(resource);
}
if (resource instanceof THREE.Object3D) {
this.track(resource.geometry);
this.track(resource.material);
this.track(resource.children);
} else if (resource instanceof THREE.Material) {
// We have to check if there are any textures on the material
for (const value of Object.values(resource)) {
if (value instanceof THREE.Texture) {
this.track(value);
}
}
// We also have to check if any uniforms reference textures or arrays of textures
if (resource.uniforms) {
for (const value of Object.values(resource.uniforms)) {
if (value) {
const uniformValue = value.value;
if (uniformValue instanceof THREE.Texture ||
Array.isArray(uniformValue)) {
this.track(uniformValue);
}
}
}
}
}
return resource;
}
untrack(resource) {
this.resources.delete(resource);
}
dispose() {
for (const resource of this.resources) {
if (resource instanceof THREE.Object3D) {
if (resource.parent) {
resource.parent.remove(resource);
}
}
if (resource.dispose) {
resource.dispose();
}
}
this.resources.clear();
}
}
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({canvas});
const ext = renderer.getContext().getExtension('GMAN_webgl_memory');
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 5;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 2;
const scene = new THREE.Scene();
scene.background = new THREE.Color('lightblue');
function addLight(...pos) {
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(...pos);
scene.add(light);
}
addLight(-1, 2, 4);
addLight( 2, -2, 3);
function frameArea(sizeToFitOnScreen, boxSize, boxCenter, camera) {
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
const halfFovY = THREE.MathUtils.degToRad(camera.fov * .5);
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
// compute a unit vector that points in the direction the camera is now
// in the xz plane from the center of the box
const direction = (new THREE.Vector3())
.subVectors(camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
// move the camera to a position distance units way from the center
// in whatever direction the camera was from the center already
camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
// pick some near and far values for the frustum that
// will contain the box.
camera.near = boxSize / 100;
camera.far = boxSize * 100;
camera.updateProjectionMatrix();
// point the camera to look at the center of the box
camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
}
function showInfo(msg = '') {
if (ext) {
console.log('---', msg, '---');
const info = ext.getMemoryInfo();
document.querySelector('#info').textContent = JSON.stringify(info, null, 2);
}
}
const gltfLoader = new GLTFLoader();
function loadGLTF(url) {
return new Promise((resolve, reject) => {
gltfLoader.load(url, resolve, undefined, reject);
});
}
function waitSeconds(seconds = 0) {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}
const fileURLs = [
'https://threejsfundamentals.org/threejs/resources/models/cartoon_lowpoly_small_city_free_pack/scene.gltf',
'https://threejsfundamentals.org/threejs/resources/models/3dbustchallange_submission/scene.gltf',
'https://threejsfundamentals.org/threejs/resources/models/mountain_landscape/scene.gltf',
'https://threejsfundamentals.org/threejs/resources/models/simple_house_scene/scene.gltf',
];
async function loadFiles() {
for (;;) {
for (const url of fileURLs) {
const resMgr = new ResourceTracker();
const track = resMgr.track.bind(resMgr);
const gltf = await loadGLTF(url);
const root = track(gltf.scene);
scene.add(root);
// compute the box that contains all the stuff
// from root and below
const box = new THREE.Box3().setFromObject(root);
const boxSize = box.getSize(new THREE.Vector3()).length();
const boxCenter = box.getCenter(new THREE.Vector3());
// set the camera to frame the box
frameArea(boxSize * 1.1, boxSize, boxCenter, camera);
renderer.render(scene, camera);
showInfo(`load ${url}`);
await waitSeconds(2);
renderer.clearColor();
resMgr.dispose();
showInfo(`dispose ${url}`);
await waitSeconds(1);
}
}
}
loadFiles();
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
{"name":"WebGL-Memory Example","settings":{},"filenames":["index.html","index.css","index.js"]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment