summaryrefslogtreecommitdiff
path: root/chromium/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js')
-rw-r--r--chromium/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js304
1 files changed, 304 insertions, 0 deletions
diff --git a/chromium/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js b/chromium/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js
new file mode 100644
index 00000000000..ba60b82b1f0
--- /dev/null
+++ b/chromium/third_party/webxr_test_pages/webxr-samples/js/cottontail/src/scenes/scene.js
@@ -0,0 +1,304 @@
+// Copyright 2018 The Immersive Web Community Group
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+import {RenderView} from '../core/renderer.js';
+import {InputRenderer} from '../nodes/input-renderer.js';
+import {StatsViewer} from '../nodes/stats-viewer.js';
+import {Node} from '../core/node.js';
+import {vec3, quat} from '../math/gl-matrix.js';
+
+export class WebXRView extends RenderView {
+ constructor(view, layer) {
+ super(
+ view ? view.projectionMatrix : null,
+ view ? view.viewMatrix : null,
+ (layer && view) ? layer.getViewport(view) : null,
+ view ? view.eye : 'left'
+ );
+ }
+}
+
+export class Scene extends Node {
+ constructor() {
+ super();
+
+ this._timestamp = -1;
+ this._frameDelta = 0;
+ this._statsStanding = false;
+ this._stats = null;
+ this._statsEnabled = false;
+ this.enableStats(true); // Ensure the stats are added correctly by default.
+
+ this._inputRenderer = null;
+ this._resetInputEndFrame = true;
+
+ this._lastTimestamp = 0;
+
+ this._hoverFrame = 0;
+ this._hoveredNodes = [];
+
+ this.clear = true;
+ }
+
+ setRenderer(renderer) {
+ this._setRenderer(renderer);
+ }
+
+ loseRenderer() {
+ if (this._renderer) {
+ this._stats = null;
+ this._renderer = null;
+ this._inputRenderer = null;
+ }
+ }
+
+ get inputRenderer() {
+ if (!this._inputRenderer) {
+ this._inputRenderer = new InputRenderer();
+ this.addNode(this._inputRenderer);
+ }
+ return this._inputRenderer;
+ }
+
+ // Helper function that automatically adds the appropriate visual elements for
+ // all input sources.
+ updateInputSources(frame, frameOfRef) {
+ // FIXME: Check for the existence of the API first. This check should be
+ // removed once the input API is part of the official spec.
+ if (!frame.session.getInputSources) {
+ return;
+ }
+
+ let inputSources = frame.session.getInputSources();
+
+ let newHoveredNodes = [];
+ let lastHoverFrame = this._hoverFrame;
+ this._hoverFrame++;
+
+ for (let inputSource of inputSources) {
+ let inputPose = frame.getInputPose(inputSource, frameOfRef);
+
+ if (!inputPose) {
+ continue;
+ }
+
+ // Any time that we have a grip matrix, we'll render a controller.
+ if (inputPose.gripMatrix) {
+ this.inputRenderer.addController(inputPose.gripMatrix);
+ }
+
+ if (inputPose.targetRay) {
+ if (inputSource.targetRayMode == 'tracked-pointer') {
+ // If we have a pointer matrix and the pointer origin is the users
+ // hand (as opposed to their head or the screen) use it to render
+ // a ray coming out of the input device to indicate the pointer
+ // direction.
+ this.inputRenderer.addLaserPointer(inputPose.targetRay);
+ }
+
+ // If we have a pointer matrix we can also use it to render a cursor
+ // for both handheld and gaze-based input sources.
+
+ // Check and see if the pointer is pointing at any selectable objects.
+ let hitResult = this.hitTest(inputPose.targetRay);
+
+ if (hitResult) {
+ // Render a cursor at the intersection point.
+ this.inputRenderer.addCursor(hitResult.intersection);
+
+ if (hitResult.node._hoverFrameId != lastHoverFrame) {
+ hitResult.node.onHoverStart();
+ }
+ hitResult.node._hoverFrameId = this._hoverFrame;
+ newHoveredNodes.push(hitResult.node);
+ } else {
+ // Statically render the cursor 1 meters down the ray since we didn't
+ // hit anything selectable.
+ let cursorDistance = 1.0;
+ let cursorPos = vec3.fromValues(
+ inputPose.targetRay.origin.x,
+ inputPose.targetRay.origin.y,
+ inputPose.targetRay.origin.z
+ );
+ vec3.add(cursorPos, cursorPos, [
+ inputPose.targetRay.direction.x * cursorDistance,
+ inputPose.targetRay.direction.y * cursorDistance,
+ inputPose.targetRay.direction.z * cursorDistance,
+ ]);
+ // let cursorPos = vec3.fromValues(0, 0, -1.0);
+ // vec3.transformMat4(cursorPos, cursorPos, inputPose.targetRay);
+ this.inputRenderer.addCursor(cursorPos);
+ }
+ }
+ }
+
+ for (let hoverNode of this._hoveredNodes) {
+ if (hoverNode._hoverFrameId != this._hoverFrame) {
+ hoverNode.onHoverEnd();
+ }
+ }
+
+ this._hoveredNodes = newHoveredNodes;
+ }
+
+ handleSelect(inputSource, frame, frameOfRef) {
+ let inputPose = frame.getInputPose(inputSource, frameOfRef);
+
+ if (!inputPose) {
+ return;
+ }
+
+ this.handleSelectPointer(inputPose.targetRay);
+ }
+
+ handleSelectPointer(targetRay) {
+ if (targetRay) {
+ // Check and see if the pointer is pointing at any selectable objects.
+ let hitResult = this.hitTest(targetRay);
+
+ if (hitResult) {
+ // Render a cursor at the intersection point.
+ hitResult.node.handleSelect();
+ }
+ }
+ }
+
+ enableStats(enable) {
+ if (enable == this._statsEnabled) {
+ return;
+ }
+
+ this._statsEnabled = enable;
+
+ if (enable) {
+ this._stats = new StatsViewer();
+ this._stats.selectable = true;
+ this.addNode(this._stats);
+
+ if (this._statsStanding) {
+ this._stats.translation = [0, 1.4, -0.75];
+ } else {
+ this._stats.translation = [0, -0.3, -0.5];
+ }
+ this._stats.scale = [0.3, 0.3, 0.3];
+ quat.fromEuler(this._stats.rotation, -45.0, 0.0, 0.0);
+ } else if (!enable) {
+ if (this._stats) {
+ this.removeNode(this._stats);
+ this._stats = null;
+ }
+ }
+ }
+
+ standingStats(enable) {
+ this._statsStanding = enable;
+ if (this._stats) {
+ if (this._statsStanding) {
+ this._stats.translation = [0, 1.4, -0.75];
+ } else {
+ this._stats.translation = [0, -0.3, -0.5];
+ }
+ this._stats.scale = [0.3, 0.3, 0.3];
+ quat.fromEuler(this._stats.rotation, -45.0, 0.0, 0.0);
+ }
+ }
+
+ draw(projectionMatrix, viewMatrix, eye) {
+ let view = new RenderView();
+ view.projectionMatrix = projectionMatrix;
+ view.viewMatrix = viewMatrix;
+ if (eye) {
+ view.eye = eye;
+ }
+
+ this.drawViewArray([view]);
+ }
+
+ /** Draws the scene into the base layer of the XRFrame's session */
+ drawXRFrame(xrFrame, pose) {
+ if (!this._renderer || !pose) {
+ return;
+ }
+
+ let gl = this._renderer.gl;
+ let session = xrFrame.session;
+ // Assumed to be a XRWebGLLayer for now.
+ let layer = session.baseLayer;
+
+ if (!gl) {
+ return;
+ }
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);
+
+ if (this.clear) {
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ }
+
+ let views = [];
+ for (let view of pose.views) {
+ views.push(new WebXRView(view, layer));
+ }
+
+ this.drawViewArray(views);
+ }
+
+ drawViewArray(views) {
+ // Don't draw when we don't have a valid context
+ if (!this._renderer) {
+ return;
+ }
+
+ this._renderer.drawViews(views, this);
+ }
+
+ startFrame() {
+ let prevTimestamp = this._timestamp;
+ this._timestamp = performance.now();
+ if (this._stats) {
+ this._stats.begin();
+ }
+
+ if (prevTimestamp >= 0) {
+ this._frameDelta = this._timestamp - prevTimestamp;
+ } else {
+ this._frameDelta = 0;
+ }
+
+ this._update(this._timestamp, this._frameDelta);
+
+ return this._frameDelta;
+ }
+
+ endFrame() {
+ if (this._inputRenderer && this._resetInputEndFrame) {
+ this._inputRenderer.reset();
+ }
+
+ if (this._stats) {
+ this._stats.end();
+ }
+ }
+
+ // Override to load scene resources on construction or context restore.
+ onLoadScene(renderer) {
+ return Promise.resolve();
+ }
+}