diff --git a/src/claygl.ts b/src/claygl.ts index 9a9a5fb0..0f30a673 100644 --- a/src/claygl.ts +++ b/src/claygl.ts @@ -79,8 +79,8 @@ export { } from './shader/index'; // Picking - export { pick as pickByRay } from './picking/rayPicking'; +export { default as PixelPicking } from './picking/PixelPicking'; // GLTF Loader export { load as loadGLTF, parse as parseGLTF, parseBinary as parseGLB } from './loader/GLTF'; @@ -106,5 +106,10 @@ export { default as App3D } from './App3D'; export { default as CompositorFullscreenQuadPass } from './composite/Pass'; // Deferred renderer - export { default as DeferredGBuffer } from './deferred/GBuffer'; +export { default as DeferredRenderer } from './deferred/Renderer'; + +// Particles +export { default as ParticleEmitter } from './particle/Emitter'; +export { default as ParticleRenderable } from './particle/ParticleRenderable'; +export { default as ForceField } from './particle/ForceField'; diff --git a/src/picking/PixelPicking.ts b/src/picking/PixelPicking.ts index 5511933f..07abbc5b 100644 --- a/src/picking/PixelPicking.ts +++ b/src/picking/PixelPicking.ts @@ -1,185 +1,173 @@ -// @ts-nocheck -import Base from '../core/Base'; import FrameBuffer from '../FrameBuffer'; import Texture2D from '../Texture2D'; import Shader from '../Shader'; import Material from '../Material'; import colorEssl from './color.glsl.js'; +import type Renderer from '../Renderer'; +import type Camera from '../Camera'; +import type Scene from '../Scene'; +import type ClayNode from '../Node'; +import type Renderable from '../Renderable'; Shader.import(colorEssl); /** * Pixel picking is gpu based picking, which is fast and accurate. * But not like ray picking, it can't get the intersection point and triangle. - * @constructor clay.picking.PixelPicking - * @extends clay.core.Base */ -const PixelPicking = Base.extend( - function () { - return /** @lends clay.picking.PixelPicking# */ { - /** - * Target renderer - * @type {clay.Renderer} - */ - renderer: null, - /** - * Downsample ratio of hidden frame buffer - * @type {number} - */ - downSampleRatio: 1, +class PixelPicking { + /** + * Target renderer + */ + renderer: Renderer; + /** + * Downsample ratio of hidden frame buffer + * @type {number} + */ + downSampleRatio = 1; - width: 100, - height: 100, + width: number; + height: number; - lookupOffset: 1, + lookupOffset = 1; - _frameBuffer: null, - _texture: null, - _shader: null, + private _frameBuffer = new FrameBuffer(); + private _texture = new Texture2D(); + private _shader = new Shader( + Shader.source('clay.picking.color.vertex'), + Shader.source('clay.picking.color.fragment') + ); - _idMaterials: [], - _lookupTable: [], + _idMaterials: Material[] = []; + _lookupTable: Renderable[] = []; - _meshMaterials: [], + _meshMaterials: Material[] = []; - _idOffset: 0 - }; - }, - function () { - if (this.renderer) { - this.width = this.renderer.getWidth(); - this.height = this.renderer.getHeight(); + _idOffset = 0; + + constructor(renderer: Renderer) { + this.renderer = renderer; + this.width = renderer.getWidth(); + this.height = renderer.getHeight(); + } + + /** + * Set picking presision + * @param {number} ratio + */ + setPrecision(ratio: number) { + this._texture.width = this.width * ratio; + this._texture.height = this.height * ratio; + this.downSampleRatio = ratio; + } + resize(width: number, height: number) { + this._texture.width = width * this.downSampleRatio; + this._texture.height = height * this.downSampleRatio; + this.width = width; + this.height = height; + this._texture.dirty(); + } + /** + * Update the picking framebuffer + * @param {number} ratio + */ + update(scene: Scene, camera: Camera) { + const renderer = this.renderer; + const frameBuffer = this._frameBuffer; + const newWidth = renderer.getWidth(); + const newHeight = renderer.getHeight(); + if (newWidth !== this.width || newHeight !== this.height) { + this.resize(newWidth, newHeight); } - this._init(); - }, - /** @lends clay.picking.PixelPicking.prototype */ { - _init: function () { - this._texture = new Texture2D({ - width: this.width * this.downSampleRatio, - height: this.height * this.downSampleRatio - }); - this._frameBuffer = new FrameBuffer(); - this._shader = new Shader( - Shader.source('clay.picking.color.vertex'), - Shader.source('clay.picking.color.fragment') - ); - }, - /** - * Set picking presision - * @param {number} ratio - */ - setPrecision: function (ratio) { - this._texture.width = this.width * ratio; - this._texture.height = this.height * ratio; - this.downSampleRatio = ratio; - }, - resize: function (width, height) { - this._texture.width = width * this.downSampleRatio; - this._texture.height = height * this.downSampleRatio; - this.width = width; - this.height = height; - this._texture.dirty(); - }, - /** - * Update the picking framebuffer - * @param {number} ratio - */ - update: function (scene, camera) { - const renderer = this.renderer; - const frameBuffer = this._frameBuffer; - if (renderer.getWidth() !== this.width || renderer.getHeight() !== this.height) { - this.resize(renderer.width, renderer.height); - } + frameBuffer.attach(this._texture); + frameBuffer.bind(renderer); + this._idOffset = this.lookupOffset; + this._setMaterial(scene); + renderer.render(scene, camera); + this._restoreMaterial(); + frameBuffer.unbind(renderer); + } - frameBuffer.attach(this._texture); - frameBuffer.bind(renderer); - this._idOffset = this.lookupOffset; - this._setMaterial(scene); - renderer.render(scene, camera); - this._restoreMaterial(); - frameBuffer.unbind(renderer); - }, - - _setMaterial: function (root) { - for (let i = 0; i < root._children.length; i++) { - const child = root._children[i]; - if (child.geometry && child.material && child.material.shader) { - const id = this._idOffset++; - const idx = id - this.lookupOffset; - let material = this._idMaterials[idx]; - if (!material) { - material = new Material({ - shader: this._shader - }); - const color = packID(id); - color[0] /= 255; - color[1] /= 255; - color[2] /= 255; - color[3] = 1.0; - material.set('color', color); - this._idMaterials[idx] = material; - } - this._meshMaterials[idx] = child.material; - this._lookupTable[idx] = child; - child.material = material; - } - if (child._children.length) { - this._setMaterial(child); + _setMaterial(root: ClayNode) { + const children = root.childrenRef(); + for (let i = 0; i < children.length; i++) { + const child = children[i]; + if (child.isRenderable()) { + const id = this._idOffset++; + const idx = id - this.lookupOffset; + let material = this._idMaterials[idx]; + if (!material) { + material = new Material({ + shader: this._shader + }); + const color = packID(id); + color[0] /= 255; + color[1] /= 255; + color[2] /= 255; + color[3] = 1.0; + material.set('color', color); + this._idMaterials[idx] = material; } + this._meshMaterials[idx] = child.material; + this._lookupTable[idx] = child; + child.material = material; } - }, - - /** - * Pick the object - * @param {number} x Mouse position x - * @param {number} y Mouse position y - * @return {clay.Node} - */ - pick: function (x, y) { - const renderer = this.renderer; - - const ratio = this.downSampleRatio; - x = Math.ceil(ratio * x); - y = Math.ceil(ratio * (this.height - y)); - - this._frameBuffer.bind(renderer); - const pixel = new Uint8Array(4); - const _gl = renderer.gl; - // TODO out of bounds ? - // preserveDrawingBuffer ? - _gl.readPixels(x, y, 1, 1, _gl.RGBA, _gl.UNSIGNED_BYTE, pixel); - this._frameBuffer.unbind(renderer); - // Skip interpolated pixel because of anti alias - if (pixel[3] === 255) { - const id = unpackID(pixel[0], pixel[1], pixel[2]); - if (id) { - const el = this._lookupTable[id - this.lookupOffset]; - return el; - } + if (child.childrenRef().length) { + this._setMaterial(child); } - }, - - _restoreMaterial: function () { - for (let i = 0; i < this._lookupTable.length; i++) { - this._lookupTable[i].material = this._meshMaterials[i]; - } - }, - - dispose: function (renderer) { - this._frameBuffer.dispose(renderer); } } -); -function packID(id) { + /** + * Pick the object + * @param {number} x Mouse position x + * @param {number} y Mouse position y + * @return {clay.Node} + */ + pick(x: number, y: number) { + const renderer = this.renderer; + + const ratio = this.downSampleRatio; + x = Math.ceil(ratio * x); + y = Math.ceil(ratio * (this.height - y)); + + this._frameBuffer.bind(renderer); + const pixel = new Uint8Array(4); + const _gl = renderer.gl; + // TODO out of bounds ? + // preserveDrawingBuffer ? + _gl.readPixels(x, y, 1, 1, _gl.RGBA, _gl.UNSIGNED_BYTE, pixel); + this._frameBuffer.unbind(renderer); + // Skip interpolated pixel because of anti alias + if (pixel[3] === 255) { + const id = unpackID(pixel[0], pixel[1], pixel[2]); + if (id) { + const el = this._lookupTable[id - this.lookupOffset]; + return el; + } + } + } + + _restoreMaterial() { + for (let i = 0; i < this._lookupTable.length; i++) { + this._lookupTable[i].material = this._meshMaterials[i]; + } + } + + dispose(renderer: Renderer) { + this._frameBuffer.dispose(renderer); + } +} + +function packID(id: number) { const r = id >> 16; const g = (id - (r << 16)) >> 8; const b = id - (r << 16) - (g << 8); return [r, g, b]; } -function unpackID(r, g, b) { +function unpackID(r: number, g: number, b: number) { return (r << 16) + (g << 8) + b; } diff --git a/src/vr/CardboardDistorter.ts b/src/vr/CardboardDistorter.ts index 0389f135..03142c49 100644 --- a/src/vr/CardboardDistorter.ts +++ b/src/vr/CardboardDistorter.ts @@ -1,4 +1,3 @@ -// @ts-nocheck // https://github.com/googlevr/webvr-polyfill/blob/master/src/cardboard-distorter.js // Use webvr may have scale problem. @@ -10,175 +9,174 @@ import Mesh from '../Mesh'; import Material from '../Material'; import Geometry from '../Geometry'; import Shader from '../Shader'; -import Base from '../core/Base'; import PerspectiveCamera from '../camera/Perspective'; import outputEssl from './output.glsl.js'; +import { Color } from '../core/type'; +import type Renderer from '../Renderer'; +import type Texture2D from '../Texture2D'; Shader.import(outputEssl); -function lerp(a, b, t) { +function lerp(a: number, b: number, t: number) { return a * (1 - t) + b * t; } -const CardboardDistorter = Base.extend( - function () { - return { - clearColor: [0, 0, 0, 1], +class CardboardDistorter { + clearColor: Color = [0, 0, 0, 1]; - _mesh: new Mesh({ - geometry: new Geometry({ - dynamic: true - }), - culling: false, - material: new Material({ - // FIXME Why disable depthMask will be wrong - // depthMask: false, - depthTest: false, - shader: new Shader({ - vertex: Shader.source('clay.vr.disorter.output.vertex'), - fragment: Shader.source('clay.vr.disorter.output.fragment') - }) - }) + private _mesh: Mesh; + private _fakeCamera = new PerspectiveCamera(); + constructor() { + this._mesh = new Mesh({ + geometry: new Geometry({ + dynamic: true }), - _fakeCamera: new PerspectiveCamera() - }; - }, - { - render: function (renderer, sourceTexture) { - const clearColor = this.clearColor; - const gl = renderer.gl; - gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - gl.clear(gl.COLOR_BUFFER_BIT); + culling: false, + material: new Material({ + // FIXME Why disable depthMask will be wrong + // depthMask: false, + depthTest: false, + shader: new Shader({ + vertex: Shader.source('clay.vr.disorter.output.vertex'), + fragment: Shader.source('clay.vr.disorter.output.fragment') + }) + }) + }); + } - gl.disable(gl.BLEND); + render(renderer: Renderer, sourceTexture: Texture2D) { + const clearColor = this.clearColor; + const gl = renderer.gl; + gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); + gl.clear(gl.COLOR_BUFFER_BIT); - this._mesh.material.set('texture', sourceTexture); + gl.disable(gl.BLEND); - // Full size? - renderer.saveViewport(); - renderer.setViewport(0, 0, renderer.getWidth(), renderer.getHeight()); - renderer.renderPass([this._mesh], this._fakeCamera); - renderer.restoreViewport(); - // this._mesh.material.shader.bind(renderer); - // this._mesh.material.bind(renderer); - // this._mesh.render(renderer.gl); - }, + this._mesh.material.set('texture', sourceTexture); - updateFromVRDisplay: function (vrDisplay) { - // FIXME - if (vrDisplay.deviceInfo_) { - // Hardcoded mesh size - this._updateMesh(20, 20, vrDisplay.deviceInfo_); - } else { - console.warn('Cant get vrDisplay.deviceInfo_, seems code changed'); - } - }, + // Full size? + renderer.saveViewport(); + renderer.setViewport(0, 0, renderer.getWidth(), renderer.getHeight()); + renderer.renderPass([this._mesh], this._fakeCamera); + renderer.restoreViewport(); + // this._mesh.material.shader.bind(renderer); + // this._mesh.material.bind(renderer); + // this._mesh.render(renderer.gl); + } - _updateMesh: function (width, height, deviceInfo) { - const positionAttr = this._mesh.geometry.attributes.position; - const texcoordAttr = this._mesh.geometry.attributes.texcoord0; - positionAttr.init(2 * width * height); - texcoordAttr.init(2 * width * height); - - const lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles(); - const noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles(); - const viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum); - - const pos = []; - const uv = []; - let vidx = 0; - - // Vertices - for (let e = 0; e < 2; e++) { - for (let j = 0; j < height; j++) { - for (let i = 0; i < width; i++, vidx++) { - let u = i / (width - 1); - let v = j / (height - 1); - - // Grid points regularly spaced in StreoScreen, and barrel distorted in - // the mesh. - const s = u; - const t = v; - const x = lerp(lensFrustum[0], lensFrustum[2], u); - const y = lerp(lensFrustum[3], lensFrustum[1], v); - const d = Math.sqrt(x * x + y * y); - const r = deviceInfo.distortion.distortInverse(d); - const p = (x * r) / d; - const q = (y * r) / d; - u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]); - v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]); - - // FIXME: The original Unity plugin multiplied U by the aspect ratio - // and didn't multiply either value by 2, but that seems to get it - // really close to correct looking for me. I hate this kind of "Don't - // know why it works" code though, and wold love a more logical - // explanation of what needs to happen here. - u = (viewport.x + u * viewport.width - 0.5) * 2.0; //* aspect; - v = (viewport.y + v * viewport.height - 0.5) * 2.0; - - pos[0] = u; - pos[1] = v; - pos[2] = 0; - - uv[0] = s * 0.5 + e * 0.5; - uv[1] = t; - - positionAttr.set(vidx, pos); - texcoordAttr.set(vidx, uv); - } - } - - let w = lensFrustum[2] - lensFrustum[0]; - lensFrustum[0] = -(w + lensFrustum[0]); - lensFrustum[2] = w - lensFrustum[2]; - w = noLensFrustum[2] - noLensFrustum[0]; - noLensFrustum[0] = -(w + noLensFrustum[0]); - noLensFrustum[2] = w - noLensFrustum[2]; - viewport.x = 1 - (viewport.x + viewport.width); - } - - // Indices - const indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6); - const halfwidth = width / 2; - const halfheight = height / 2; - vidx = 0; - let iidx = 0; - for (let e = 0; e < 2; e++) { - for (let j = 0; j < height; j++) { - for (let i = 0; i < width; i++, vidx++) { - if (i === 0 || j === 0) { - continue; - } - // Build a quad. Lower right and upper left quadrants have quads with - // the triangle diagonal flipped to get the vignette to interpolate - // correctly. - if (i <= halfwidth == j <= halfheight) { - // Quad diagonal lower left to upper right. - indices[iidx++] = vidx; - indices[iidx++] = vidx - width - 1; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx - width - 1; - indices[iidx++] = vidx; - indices[iidx++] = vidx - 1; - } else { - // Quad diagonal upper left to lower right. - indices[iidx++] = vidx - 1; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx; - indices[iidx++] = vidx - width; - indices[iidx++] = vidx - 1; - indices[iidx++] = vidx - width - 1; - } - } - } - } - - this._mesh.geometry.indices = indices; - - this._mesh.geometry.dirty(); + updateFromVRDisplay(vrDisplay: any) { + // FIXME + if (vrDisplay.deviceInfo_) { + // Hardcoded mesh size + this._updateMesh(20, 20, vrDisplay.deviceInfo_); + } else { + console.warn('Cant get vrDisplay.deviceInfo_, seems code changed'); } } -); + _updateMesh(width: number, height: number, deviceInfo: any) { + const positionAttr = this._mesh.geometry.attributes.position; + const texcoordAttr = this._mesh.geometry.attributes.texcoord0; + positionAttr.init(2 * width * height); + texcoordAttr.init(2 * width * height); + + const lensFrustum = deviceInfo.getLeftEyeVisibleTanAngles(); + const noLensFrustum = deviceInfo.getLeftEyeNoLensTanAngles(); + const viewport = deviceInfo.getLeftEyeVisibleScreenRect(noLensFrustum); + + const pos = []; + const uv = []; + let vidx = 0; + + // Vertices + for (let e = 0; e < 2; e++) { + for (let j = 0; j < height; j++) { + for (let i = 0; i < width; i++, vidx++) { + let u = i / (width - 1); + let v = j / (height - 1); + + // Grid points regularly spaced in StreoScreen, and barrel distorted in + // the mesh. + const s = u; + const t = v; + const x = lerp(lensFrustum[0], lensFrustum[2], u); + const y = lerp(lensFrustum[3], lensFrustum[1], v); + const d = Math.sqrt(x * x + y * y); + const r = deviceInfo.distortion.distortInverse(d); + const p = (x * r) / d; + const q = (y * r) / d; + u = (p - noLensFrustum[0]) / (noLensFrustum[2] - noLensFrustum[0]); + v = (q - noLensFrustum[3]) / (noLensFrustum[1] - noLensFrustum[3]); + + // FIXME: The original Unity plugin multiplied U by the aspect ratio + // and didn't multiply either value by 2, but that seems to get it + // really close to correct looking for me. I hate this kind of "Don't + // know why it works" code though, and wold love a more logical + // explanation of what needs to happen here. + u = (viewport.x + u * viewport.width - 0.5) * 2.0; //* aspect; + v = (viewport.y + v * viewport.height - 0.5) * 2.0; + + pos[0] = u; + pos[1] = v; + pos[2] = 0; + + uv[0] = s * 0.5 + e * 0.5; + uv[1] = t; + + positionAttr.set(vidx, pos); + texcoordAttr.set(vidx, uv); + } + } + + let w = lensFrustum[2] - lensFrustum[0]; + lensFrustum[0] = -(w + lensFrustum[0]); + lensFrustum[2] = w - lensFrustum[2]; + w = noLensFrustum[2] - noLensFrustum[0]; + noLensFrustum[0] = -(w + noLensFrustum[0]); + noLensFrustum[2] = w - noLensFrustum[2]; + viewport.x = 1 - (viewport.x + viewport.width); + } + + // Indices + const indices = new Uint16Array(2 * (width - 1) * (height - 1) * 6); + const halfwidth = width / 2; + const halfheight = height / 2; + vidx = 0; + let iidx = 0; + for (let e = 0; e < 2; e++) { + for (let j = 0; j < height; j++) { + for (let i = 0; i < width; i++, vidx++) { + if (i === 0 || j === 0) { + continue; + } + // Build a quad. Lower right and upper left quadrants have quads with + // the triangle diagonal flipped to get the vignette to interpolate + // correctly. + if (i <= halfwidth == j <= halfheight) { + // Quad diagonal lower left to upper right. + indices[iidx++] = vidx; + indices[iidx++] = vidx - width - 1; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx - width - 1; + indices[iidx++] = vidx; + indices[iidx++] = vidx - 1; + } else { + // Quad diagonal upper left to lower right. + indices[iidx++] = vidx - 1; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx; + indices[iidx++] = vidx - width; + indices[iidx++] = vidx - 1; + indices[iidx++] = vidx - width - 1; + } + } + } + } + + this._mesh.geometry.indices = indices; + + this._mesh.geometry.dirty(); + } +} export default CardboardDistorter; diff --git a/src/vr/StereoCamera.ts b/src/vr/StereoCamera.ts index 0c8f14fd..d986f7ba 100644 --- a/src/vr/StereoCamera.ts +++ b/src/vr/StereoCamera.ts @@ -1,121 +1,109 @@ -// @ts-nocheck -import Base from '../Node'; -import Camera from '../camera/Perspective'; +import PerspectiveCamera from '../camera/Perspective'; import Matrix4 from '../math/Matrix4'; +import type ClayNode from '../Node'; const tmpProjectionMatrix = new Matrix4(); -const StereoCamera = Base.extend( - function () { - return { - aspect: 0.5, +class StereoCamera { + aspect = 0.5; - _leftCamera: new Camera(), + _leftCamera = new PerspectiveCamera(); - _rightCamera: new Camera(), + _rightCamera = new PerspectiveCamera(); - _eyeLeft: new Matrix4(), - _eyeRight: new Matrix4(), + _eyeLeft = new Matrix4(); + _eyeRight = new Matrix4(); - _frameData: null - }; - }, - { - updateFromCamera: function (camera, focus, zoom, eyeSep) { - if (camera.transformNeedsUpdate()) { - console.warn('Node transform is not updated'); - } + _frameData: any; - focus = focus == null ? 10 : focus; - zoom = zoom == null ? 1 : zoom; - eyeSep = eyeSep == null ? 0.064 : eyeSep; + updateFromCamera(camera: PerspectiveCamera, focus: number, zoom: number, eyeSep: number) { + focus = focus == null ? 10 : focus; + zoom = zoom == null ? 1 : zoom; + eyeSep = eyeSep == null ? 0.064 : eyeSep; - const fov = camera.fov; - const aspect = camera.aspect * this.aspect; - const near = camera.near; + const fov = camera.fov; + const aspect = camera.aspect * this.aspect; + const near = camera.near; - // Off-axis stereoscopic effect based on - // http://paulbourke.net/stereographics/stereorender/ + // Off-axis stereoscopic effect based on + // http://paulbourke.net/stereographics/stereorender/ - tmpProjectionMatrix.copy(camera.projectionMatrix); - eyeSep = eyeSep / 2; - const eyeSepOnProjection = (eyeSep * near) / focus; - const ymax = (near * Math.tan((Math.PI / 180) * fov * 0.5)) / zoom; - let xmin, xmax; + tmpProjectionMatrix.copy(camera.projectionMatrix); + eyeSep = eyeSep / 2; + const eyeSepOnProjection = (eyeSep * near) / focus; + const ymax = (near * Math.tan((Math.PI / 180) * fov * 0.5)) / zoom; + let xmin, xmax; - // translate xOffset - this._eyeLeft.array[12] = -eyeSep; - this._eyeRight.array[12] = eyeSep; + // translate xOffset + this._eyeLeft.array[12] = -eyeSep; + this._eyeRight.array[12] = eyeSep; - // for left eye - xmin = -ymax * aspect + eyeSepOnProjection; - xmax = ymax * aspect + eyeSepOnProjection; + // for left eye + xmin = -ymax * aspect + eyeSepOnProjection; + xmax = ymax * aspect + eyeSepOnProjection; - tmpProjectionMatrix.array[0] = (2 * near) / (xmax - xmin); - tmpProjectionMatrix.array[8] = (xmax + xmin) / (xmax - xmin); + tmpProjectionMatrix.array[0] = (2 * near) / (xmax - xmin); + tmpProjectionMatrix.array[8] = (xmax + xmin) / (xmax - xmin); - this._leftCamera.projectionMatrix.copy(tmpProjectionMatrix); + this._leftCamera.projectionMatrix.copy(tmpProjectionMatrix); - // for right eye - xmin = -ymax * aspect - eyeSepOnProjection; - xmax = ymax * aspect - eyeSepOnProjection; + // for right eye + xmin = -ymax * aspect - eyeSepOnProjection; + xmax = ymax * aspect - eyeSepOnProjection; - tmpProjectionMatrix.array[0] = (2 * near) / (xmax - xmin); - tmpProjectionMatrix.array[8] = (xmax + xmin) / (xmax - xmin); + tmpProjectionMatrix.array[0] = (2 * near) / (xmax - xmin); + tmpProjectionMatrix.array[8] = (xmax + xmin) / (xmax - xmin); - this._rightCamera.projectionMatrix.copy(tmpProjectionMatrix); + this._rightCamera.projectionMatrix.copy(tmpProjectionMatrix); - this._leftCamera.worldTransform.copy(camera.worldTransform).multiply(this._eyeLeft); + this._leftCamera.worldTransform.copy(camera.worldTransform).multiply(this._eyeLeft); - this._rightCamera.worldTransform.copy(camera.worldTransform).multiply(this._eyeRight); + this._rightCamera.worldTransform.copy(camera.worldTransform).multiply(this._eyeRight); - this._leftCamera.decomposeWorldTransform(); - this._leftCamera.decomposeProjectionMatrix(); + this._leftCamera.decomposeWorldTransform(); + this._leftCamera.decomposeProjectionMatrix(); - this._rightCamera.decomposeWorldTransform(); - this._rightCamera.decomposeProjectionMatrix(); - }, + this._rightCamera.decomposeWorldTransform(); + this._rightCamera.decomposeProjectionMatrix(); + } - updateFromVRDisplay: function (vrDisplay, parentNode) { - if (typeof VRFrameData === 'undefined') { - return; - } + updateFromVRDisplay(vrDisplay: any, parentNode: ClayNode) { + // @ts-ignore + if (typeof VRFrameData === 'undefined') { + return; + } - /* global VRFrameData */ - const frameData = this._frameData || (this._frameData = new VRFrameData()); - vrDisplay.getFrameData(frameData); - const leftCamera = this._leftCamera; - const rightCamera = this._rightCamera; + /* global VRFrameData */ + // @ts-ignore + const frameData = this._frameData || (this._frameData = new VRFrameData()); + vrDisplay.getFrameData(frameData); + const leftCamera = this._leftCamera; + const rightCamera = this._rightCamera; - leftCamera.projectionMatrix.setArray(frameData.leftProjectionMatrix); - leftCamera.decomposeProjectionMatrix(); - leftCamera.viewMatrix.setArray(frameData.leftViewMatrix); - leftCamera.setViewMatrix(leftCamera.viewMatrix); + leftCamera.projectionMatrix.setArray(frameData.leftProjectionMatrix); + leftCamera.decomposeProjectionMatrix(); + leftCamera.viewMatrix.setArray(frameData.leftViewMatrix); + leftCamera.setViewMatrix(leftCamera.viewMatrix); - rightCamera.projectionMatrix.setArray(frameData.rightProjectionMatrix); - rightCamera.decomposeProjectionMatrix(); - rightCamera.viewMatrix.setArray(frameData.rightViewMatrix); - rightCamera.setViewMatrix(rightCamera.viewMatrix); + rightCamera.projectionMatrix.setArray(frameData.rightProjectionMatrix); + rightCamera.decomposeProjectionMatrix(); + rightCamera.viewMatrix.setArray(frameData.rightViewMatrix); + rightCamera.setViewMatrix(rightCamera.viewMatrix); - if (parentNode && parentNode.worldTransform) { - if (parentNode.transformNeedsUpdate()) { - console.warn('Node transform is not updated'); - } - leftCamera.worldTransform.multiplyLeft(parentNode.worldTransform); - leftCamera.decomposeWorldTransform(); - rightCamera.worldTransform.multiplyLeft(parentNode.worldTransform); - rightCamera.decomposeWorldTransform(); - } - }, - - getLeftCamera: function () { - return this._leftCamera; - }, - - getRightCamera: function () { - return this._rightCamera; + if (parentNode && parentNode.worldTransform) { + leftCamera.worldTransform.multiplyLeft(parentNode.worldTransform); + leftCamera.decomposeWorldTransform(); + rightCamera.worldTransform.multiplyLeft(parentNode.worldTransform); + rightCamera.decomposeWorldTransform(); } } -); + getLeftCamera() { + return this._leftCamera; + } + + getRightCamera() { + return this._rightCamera; + } +} export default StereoCamera;