After the native async functionality landed, there was no way for web clients to be notified that the decoding has finished. This PR changes the existing `onDone` callback so that it gets called after all textures have been decoded. (Previously it was called after downloading rather than decoding.) This has the side effect of simplifying the API because clients no longer need to call a finalize function.
145 lines
5.2 KiB
HTML
145 lines
5.2 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>FlightHelmet</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
|
|
<link href="https://google.github.io/filament/favicon.png" rel="icon" type="image/x-icon" />
|
|
<style>
|
|
html, body { height: 100%; }
|
|
body { margin: 0; overflow: hidden; }
|
|
#container { position: relative; height: 100%; }
|
|
canvas { position: absolute; width: 100%; height: 100%; }
|
|
#messages { position: absolute; width: 100%; height: 100%; padding-left: 10px; color:white; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="container">
|
|
<canvas></canvas>
|
|
<pre id="messages"></pre>
|
|
</div>
|
|
<script src="filament.js"></script>
|
|
<script src="gl-matrix-min.js"></script>
|
|
<script src="https://unpkg.com/gltumble"></script>
|
|
<script>
|
|
|
|
const env = 'venetian_crossroads_2k';
|
|
const ibl_url = `${env}/${env}_ibl.ktx`;
|
|
const sky_url = `${env}/${env}_skybox.ktx`;
|
|
const mesh_url = 'FlightHelmet.gltf';
|
|
|
|
Filament.init([mesh_url, ibl_url, sky_url], () => {
|
|
window.gltfio = Filament.gltfio;
|
|
window.Fov = Filament.Camera$Fov;
|
|
window.LightType = Filament.LightManager$Type;
|
|
window.IndirectLight = Filament.IndirectLight;
|
|
window.app = new App(document.getElementsByTagName('canvas')[0]);
|
|
});
|
|
|
|
class App {
|
|
constructor(canvas) {
|
|
this.canvas = canvas;
|
|
const engine = this.engine = Filament.Engine.create(this.canvas);
|
|
const scene = this.scene = engine.createScene();
|
|
this.trackball = new Trackball(canvas, {startSpin: 0.035});
|
|
|
|
const indirectLight = this.ibl = engine.createIblFromKtx(ibl_url);
|
|
this.scene.setIndirectLight(indirectLight);
|
|
|
|
const iblDirection = IndirectLight.getDirectionEstimate(indirectLight.shfloats);
|
|
const iblColor = IndirectLight.getColorEstimate(indirectLight.shfloats, iblDirection);
|
|
|
|
indirectLight.setIntensity(50000);
|
|
|
|
const skybox = engine.createSkyFromKtx(sky_url);
|
|
this.scene.setSkybox(skybox);
|
|
|
|
const sunlight = Filament.EntityManager.get().create();
|
|
Filament.LightManager.Builder(LightType.SUN)
|
|
.color(iblColor.slice(0, 3))
|
|
.intensity(150000)
|
|
.direction(iblDirection)
|
|
.sunAngularRadius(1.9)
|
|
.castShadows(true)
|
|
.sunHaloSize(10.0)
|
|
.sunHaloFalloff(80.0)
|
|
.build(engine, sunlight);
|
|
this.scene.addEntity(sunlight);
|
|
|
|
const loader = engine.createAssetLoader();
|
|
const asset = this.asset = loader.createAssetFromJson(mesh_url);
|
|
|
|
const messages = document.getElementById('messages');
|
|
|
|
// Crudely indicate progress by printing the URI of each resource as it is loaded.
|
|
const onFetched = (uri) => messages.innerText += `Downloaded ${uri}\n`;
|
|
const onDone = () => {
|
|
// Destroy the asset loader.
|
|
loader.delete();
|
|
|
|
// Enable shadows on every renderable.
|
|
const entities = asset.getEntities();
|
|
const rm = engine.getRenderableManager();
|
|
for (let i = 0; i < entities.size(); i++) {
|
|
const instance = rm.getInstance(entities.get(i));
|
|
rm.setCastShadows(instance, true);
|
|
instance.delete();
|
|
}
|
|
|
|
// Hide the HUD.
|
|
messages.remove();
|
|
};
|
|
asset.loadResources(onDone, onFetched);
|
|
|
|
this.swapChain = engine.createSwapChain();
|
|
this.renderer = engine.createRenderer();
|
|
this.camera = engine.createCamera();
|
|
this.camera.setExposure(32, 1/125.0, 100);
|
|
this.view = engine.createView();
|
|
this.view.setCamera(this.camera);
|
|
this.view.setScene(this.scene);
|
|
this.resize();
|
|
this.render = this.render.bind(this);
|
|
this.resize = this.resize.bind(this);
|
|
window.addEventListener('resize', this.resize);
|
|
window.requestAnimationFrame(this.render);
|
|
}
|
|
|
|
render() {
|
|
// Spin the model according to the trackball controller.
|
|
const tcm = this.engine.getTransformManager();
|
|
const inst = tcm.getInstance(this.asset.getRoot());
|
|
tcm.setTransform(inst, this.trackball.getMatrix());
|
|
inst.delete();
|
|
|
|
// Add renderable entities to the scene as they become ready.
|
|
let entity;
|
|
const popRenderable = () => {
|
|
entity = this.asset.popRenderable();
|
|
return entity.getId() != 0;
|
|
}
|
|
while (popRenderable()) {
|
|
this.scene.addEntity(entity);
|
|
}
|
|
|
|
// Render the scene and request the next animation frame.
|
|
this.renderer.render(this.swapChain, this.view);
|
|
window.requestAnimationFrame(this.render);
|
|
}
|
|
|
|
resize() {
|
|
const dpr = window.devicePixelRatio;
|
|
const width = this.canvas.width = window.innerWidth * dpr;
|
|
const height = this.canvas.height = window.innerHeight * dpr;
|
|
this.view.setViewport([0, 0, width, height]);
|
|
const y = -0.125, eye = [0, y, 2], center = [0, y, 0], up = [0, 1, 0];
|
|
this.camera.lookAt(eye, center, up);
|
|
const aspect = width / height;
|
|
const fov = aspect < 1 ? Fov.HORIZONTAL : Fov.VERTICAL;
|
|
this.camera.setProjectionFov(30, aspect, 1.0, 10.0, fov);
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|