wip(type): add type to pixel picking and vr

This commit is contained in:
pissang 2022-05-07 22:38:18 +08:00
parent 42a6df0852
commit 7effd48b50
4 changed files with 372 additions and 393 deletions

View File

@ -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';

View File

@ -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;
}

View File

@ -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;

View File

@ -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;