Skip to content

Commit

Permalink
fix external renderer (google#2365)
Browse files Browse the repository at this point in the history
* made Renderer-spec end-to-end

* fixed camera update

* added more tests
  • Loading branch information
elalish authored May 10, 2021
1 parent 56e9338 commit 867260c
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 18 deletions.
97 changes: 83 additions & 14 deletions packages/model-viewer/src/test/three-components/Renderer-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,43 @@
*/

import {USE_OFFSCREEN_CANVAS} from '../../constants.js';
import {LoadingMixin} from '../../features/loading.js';
import ModelViewerElementBase, {$intersectionObserver, $isElementInViewport, $onResize, $renderer, $scene} from '../../model-viewer-base.js';
import {$controls} from '../../features/controls.js';
import {$intersectionObserver, $isElementInViewport, $onResize, $renderer, $scene, Camera, RendererInterface} from '../../model-viewer-base.js';
import {ModelViewerElement} from '../../model-viewer.js';
import {ModelScene} from '../../three-components/ModelScene.js';
import {Renderer} from '../../three-components/Renderer.js';
import {waitForEvent} from '../../utilities.js';
import {resolveDpr, waitForEvent} from '../../utilities.js';
import {assetPath} from '../helpers.js';

const expect = chai.expect;

const ModelViewerElement = class extends LoadingMixin
(ModelViewerElementBase) {
static get is() {
return 'model-viewer-renderer';
let externalCamera: Camera;
let externalWidth = 0;
let externalHeight = 0;

class ExternalRenderer implements RendererInterface {
load(callback: (progress: number) => void) {
callback(1.0);
return Promise.resolve({framedRadius: 15, fieldOfViewAspect: 2});
}
};

customElements.define('model-viewer-renderer', ModelViewerElement);
render(camera: Camera) {
externalCamera = camera;
}
resize(width: number, height: number) {
externalWidth = width;
externalHeight = height;
}
}

function createScene(): ModelScene {
function createScene(external: boolean = false): ModelScene {
const element = new ModelViewerElement();
document.body.insertBefore(element, document.body.firstChild);
element[$intersectionObserver]!.unobserve(element);
element[$isElementInViewport] = false;

if (external) {
const externalRenderer = new ExternalRenderer();
element.registerRenderer(externalRenderer);
}
element.src = assetPath('models/Astronaut.glb');

// manual render loop
Expand All @@ -48,12 +61,15 @@ function createScene(): ModelScene {

function disposeScene(scene: ModelScene) {
const {element} = scene;
if (scene.externalRenderer != null) {
element.unregisterRenderer();
}
if (element.parentNode != null) {
element.parentNode.removeChild(element);
}
}

suite('Renderer', () => {
suite('Renderer with two scenes', () => {
let scene: ModelScene;
let otherScene: ModelScene;
let renderer: Renderer;
Expand All @@ -74,14 +90,67 @@ suite('Renderer', () => {

test('pre-renders eager, invisible scenes', async () => {
const sourceLoads = waitForEvent(scene.element, 'load');
(scene.element as any).loading = 'eager';
(scene.element as ModelViewerElement).loading = 'eager';
await sourceLoads;

renderer.render(performance.now());
expect(scene.renderCount).to.be.equal(1, 'scene first render');
expect(otherScene.renderCount).to.be.equal(0, 'otherScene first render');
});

suite('and an externally-rendered scene', () => {
let externalScene: ModelScene;
let externalElement: ModelViewerElement;

setup(() => {
externalScene = createScene(true);
externalElement = externalScene.element as any;
});

teardown(() => {
disposeScene(externalScene);
renderer.render(performance.now());
});

test('load sets framing', async () => {
expect(externalScene.fieldOfViewAspect).to.be.eq(0);

const sourceLoads = waitForEvent(externalScene.element, 'load');
externalElement[$isElementInViewport] = true;
await sourceLoads;

expect(externalScene.fieldOfViewAspect).to.be.eq(2);
expect((externalElement as any)[$controls].options.minimumRadius)
.to.be.greaterThan(15);
});

test('camera-orbit updates camera in external render method', async () => {
const sceneVisible = waitForEvent(externalElement, 'poster-dismissed');
externalElement[$isElementInViewport] = true;

const time = performance.now()
renderer.render(time);
const cameraY = externalCamera.viewMatrix[13];
expect(cameraY).to.not.eq(0);

externalElement.cameraOrbit = '45deg 45deg 1.6m';
await sceneVisible;
renderer.render(time + 1000);

expect(externalCamera.viewMatrix[13]).to.not.eq(cameraY);
});

test('resize forwards pixel dimensions', () => {
const width = 200;
const height = 400;
externalElement[$onResize]({width, height});

const dpr = resolveDpr();
expect(externalWidth).to.be.eq(width * dpr);
expect(externalHeight).to.be.eq(height * dpr);
});
});

suite('with two loaded scenes', () => {
setup(async () => {
const sceneVisible = waitForEvent(scene.element, 'poster-dismissed');
Expand Down
1 change: 1 addition & 0 deletions packages/model-viewer/src/three-components/Renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ export class Renderer extends EventDispatcher {
}

if (scene.externalRenderer != null) {
scene.camera.updateMatrix();
const {matrix, projectionMatrix} = scene.camera;
const viewMatrix = matrix.elements.slice();
const target = scene.getTarget();
Expand Down
9 changes: 5 additions & 4 deletions packages/model-viewer/src/three-components/SmoothControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,12 @@ export class SmoothControls extends EventDispatcher {
theta - clamp(deltaTheta, -dThetaLimit - dTheta, dThetaLimit - dTheta);
const goalPhi = phi - deltaPhi;

const deltaRatio = deltaZoom === 0 ? 0 :
deltaZoom > 0 ? (maximumRadius! - radius) /
(Math.log(maximumFieldOfView!) - this.goalLogFov) :
const deltaRatio = deltaZoom === 0 ?
0 :
deltaZoom > 0 ? (maximumRadius! - radius) /
(Math.log(maximumFieldOfView!) - this.goalLogFov) :
(radius - minimumRadius!) /
(this.goalLogFov - Math.log(minimumFieldOfView!));
(this.goalLogFov - Math.log(minimumFieldOfView!));

const goalRadius = radius +
deltaZoom *
Expand Down

0 comments on commit 867260c

Please sign in to comment.