Files
filament/web/samples/helmet.html
Philip Rideout d2c883877d gltfio: hide entities with not-yet-ready textures.
This feature adds one new method to `FilamentAsset` and uses it in our
Kotlin, JavaScript, and C++ helpers:

    utils::Entity popRenderable() noexcept;

This pops a ready renderable off an internal queue, or returns 0 if no
renderables have become ready. It provides a simple way for clients to
gradually add renderables to the scene as they become ready. Previously
clients could only get the entire list of entities, regardless of
whether they had Renderable components or complete textures.

To facilitate this feature, this PR adds a new internal-only class to
gltfio called `DependencyGraph`, which is a temporary object used for
bookkeeping during the asynchronous load.

`DependencyGraph` discovers ready-to-render entities by tracking the
textures that each entity depends on. This is a graph because
renderables connect to a set of material instances, which in turn
connect to a set of parameter names, which in turn connect to a set of
texture objects. These relationships are not easily inspectable using
the Filament API or ECS.
2020-02-04 12:47:32 -08:00

152 lines
5.5 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.
// Note that we wait 1 ms after the last asset has downloaded, but before finalization.
// This gives the browser time to display the latest status.
const onFetched = (uri) => messages.innerText += `Downloaded ${uri}\n`;
const onDone = (finalize) => {
messages.innerText += 'Finalizing...\n'
setTimeout(() => {
// Decode the textures using stb.
finalize();
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();
}, 1);
};
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>