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.
99 lines
3.3 KiB
HTML
99 lines
3.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>glTF Animation</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>
|
|
body { margin: 0; overflow: hidden; }
|
|
canvas { touch-action: none; width: 100%; height: 100%; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<canvas></canvas>
|
|
<script src="filament.js"></script>
|
|
<script src="gl-matrix-min.js"></script>
|
|
<script>
|
|
|
|
const mesh_url = "AnimatedTriangle.gltf";
|
|
|
|
Filament.init([mesh_url], () => {
|
|
window.gltfio = Filament.gltfio;
|
|
window.Fov = Filament.Camera$Fov;
|
|
window.LightType = Filament.LightManager$Type;
|
|
window.app = new App(document.querySelector("canvas"));
|
|
});
|
|
|
|
class App {
|
|
constructor(canvas) {
|
|
this.canvas = canvas;
|
|
const engine = this.engine = Filament.Engine.create(this.canvas);
|
|
const scene = this.scene = engine.createScene();
|
|
|
|
const loader = engine.createAssetLoader();
|
|
const asset = this.asset = loader.createAssetFromJson(mesh_url);
|
|
|
|
const sunlight = Filament.EntityManager.get().create();
|
|
Filament.LightManager.Builder(LightType.SUN).direction([0, 0, -1]).build(engine, sunlight);
|
|
this.scene.addEntity(sunlight);
|
|
|
|
const onDone = () => {
|
|
loader.delete();
|
|
|
|
// Dynamically enable two-sided lighting for testing purposes.
|
|
const entities = asset.getEntities();
|
|
const rm = engine.getRenderableManager();
|
|
const instance = rm.getInstance(entities.get(0));
|
|
rm.getMaterialInstanceAt(instance, 0).setDoubleSided(true)
|
|
instance.delete();
|
|
|
|
scene.addEntities(entities);
|
|
this.animator = asset.getAnimator();
|
|
this.animationStartTime = Date.now();
|
|
};
|
|
asset.loadResources(onDone);
|
|
|
|
this.swapChain = engine.createSwapChain();
|
|
this.renderer = engine.createRenderer();
|
|
this.camera = engine.createCamera();
|
|
|
|
this.view = engine.createView();
|
|
this.view.setCamera(this.camera);
|
|
this.view.setScene(this.scene);
|
|
this.view.setClearColor([0.2, 0.3, 0.4, 1.0]);
|
|
|
|
this.resize();
|
|
this.render = this.render.bind(this);
|
|
this.resize = this.resize.bind(this);
|
|
window.addEventListener("resize", this.resize);
|
|
window.requestAnimationFrame(this.render);
|
|
}
|
|
|
|
render() {
|
|
if (this.animator) {
|
|
const ms = Date.now() - this.animationStartTime;
|
|
this.animator.applyAnimation(0, (ms / 1000) % 1.0);
|
|
this.animator.updateBoneMatrices();
|
|
}
|
|
|
|
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 eye = [0, 0, 5], center = [0, 0, 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, 20.0, fov);
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|