-
Sandro Weber authored9986f550
gzmultiview.js 9.81 KiB
/**
* Created by Sandro Weber (webers@in.tum.de).
*/
GZ3D.MULTIVIEW_MAX_VIEW_COUNT = 10;
GZ3D.MULTIVIEW_MAINVIEW_NAME = 'main_view';
/**
* GZ3D.MULTIVIEW_RENDER_VIEWPORTS uses view containers as transparent references to determine viewports for rendering (one single canvas).
* This is broken in combination with shadowmaps at the moment. See renderToViewport() method.
*
* @type {number}
*/
GZ3D.MULTIVIEW_RENDER_VIEWPORTS = 1;
/**
* GZ3D.MULTIVIEW_RENDER_COPY2CANVAS renders views offscreen then copies into the view containers (multiple separate canvases).
* @type {number}
*/
GZ3D.MULTIVIEW_RENDER_COPY2CANVAS = 2;
GZ3D.MultiView = function(gz3dScene, mainContainer)
{
this.gz3dScene = gz3dScene;
this.mainContainer = mainContainer;
this.init();
};
GZ3D.MultiView.prototype.init = function()
{
this.views = [];
this.mainContainer.style.zIndex = 0;
this.renderMethod = GZ3D.MULTIVIEW_RENDER_COPY2CANVAS;
if (this.renderMethod === GZ3D.MULTIVIEW_RENDER_VIEWPORTS) {
this.mainContainer.appendChild(this.gz3dScene.getDomElement());
}
this.createMainUserView();
};
GZ3D.MultiView.prototype.setCallbackCreateRenderContainer = function(callback)
{
this.createRenderContainerCallback = callback;
};
/**
* This creates the main view the user will interact with and should always be there.
*/
GZ3D.MultiView.prototype.createMainUserView = function()
{
var displayParams = {
left: '0px',
top: '0px',
width: '100%',
height: '100%',
adjustable: false
};
var cameraParams = {
width: 960,
height: 600,
fov: 60,
near: 0.1,
far: 100
};
this.mainUserView = this.createView(GZ3D.MULTIVIEW_MAINVIEW_NAME, displayParams, cameraParams);
return this.mainUserView;
};
/**
*
* @param name
* @param displayParams {left, top, width, height, zIndex, adjustable}
* @param cameraParams {width, height, fov, near, far}
*/
GZ3D.MultiView.prototype.createView = function(name, displayParams, cameraParams)
{
if (angular.isDefined(this.getViewByName(name))) {
console.error('GZ3D.MultiView.createView() - a view of that name already exists (' + name + ')');
return undefined;
}
if (this.views.length >= this.MULTIVIEW_MAX_VIEW_COUNT) {
console.warn('GZ3D.MultiView.createView() - creating new view will exceed MULTIVIEW_MAX_VIEW_COUNT(' + this.MULTIVIEW_MAX_VIEW_COUNT + '). This may cause z-ordering issues.');
}
var container = this.createViewContainer(displayParams, name);
if (!angular.isDefined(container)) {
return undefined;
}
// camera
var camera = new THREE.PerspectiveCamera(
cameraParams.fov,
cameraParams.aspectRatio,
cameraParams.near,
cameraParams.far
);
camera.name = name;
// assemble view
var view = {
name: name,
active: true,
container: container,
camera: camera
};
this.views.push(view);
this.mainContainer.appendChild(view.container);
return view;
};
GZ3D.MultiView.prototype.createViewContainer = function(displayParams, name)
{
if (!angular.isDefined(this.createRenderContainerCallback)) {
console.error('GZ3D.MultiView.createViewContainer() - no callback for creating view reference container defined');
return undefined;
}
// container div
var viewContainer;
if (name === GZ3D.MULTIVIEW_MAINVIEW_NAME) {
viewContainer = document.createElement('div');
} else {
viewContainer = this.createRenderContainerCallback(displayParams.adjustable, name);
}
if (!angular.isDefined(viewContainer)) {
console.error('GZ3D.MultiView.createViewContainer() - could not create view container via callback');
return undefined;
}
// z-index
var zIndexTop = parseInt(this.mainContainer.style.zIndex, 10) + this.views.length + 1;
viewContainer.style.zIndex = angular.isDefined(displayParams.zIndex) ? displayParams.zIndex : zIndexTop;
// positioning
viewContainer.style.position = 'absolute';
viewContainer.style.left = displayParams.left;
viewContainer.style.top = displayParams.top;
viewContainer.style.width = displayParams.width;
viewContainer.style.height = displayParams.height;
// We set 50px as a min-width for now and set the min height accordingly
viewContainer.style.minWidth = '50px';
var aspectRatio = parseInt(displayParams.height, 10) / parseInt(displayParams.width, 10);
viewContainer.style.minHeight = Math.floor(50 * aspectRatio) + 'px';
viewContainer.style.maxWidth = '100%';
viewContainer.style.maxHeight = '100%';
// Transparent view-container so that we can render viewport with one renderer in the same context
// view-container is only taken as reference for viewport
viewContainer.style.boxShadow = '0px 0px 0px 3px rgba(0,0,0,0.3)';
viewContainer.style.borderRadius = '2px';
viewContainer.style.background = 'rgba(0,0,0,0)';
if (this.renderMethod === GZ3D.MULTIVIEW_RENDER_COPY2CANVAS) {
// canvas
viewContainer.canvas = document.createElement('canvas');
viewContainer.appendChild( viewContainer.canvas );
viewContainer.canvas.style.width = '100%';
viewContainer.canvas.style.height = '100%';
}
return viewContainer;
};
GZ3D.MultiView.prototype.getViewByName = function(name)
{
for (var i = 0; i < this.views.length; i = i+1) {
if (this.views[i].name === name) {
return this.views[i];
}
}
return undefined;
};
GZ3D.MultiView.prototype.setViewVisibility = function(view, visible)
{
view.active = visible;
if (view.active) {
view.container.style.visibility = 'visible';
} else {
view.container.style.visibility = 'hidden';
}
};
GZ3D.MultiView.prototype.updateCamera = function(view)
{
view.camera.aspect = view.container.clientWidth / view.container.clientHeight;
view.camera.updateProjectionMatrix();
};
GZ3D.MultiView.prototype.getViewport = function(view)
{
var viewport = {
x: view.container.offsetLeft,
y: this.mainContainer.clientHeight - view.container.clientHeight - view.container.offsetTop,
w: view.container.clientWidth,
h: view.container.clientHeight
};
return viewport;
};
GZ3D.MultiView.prototype.setWindowSize = function(width, height)
{
};
GZ3D.MultiView.prototype.renderViews = function()
{
// sort views into rendering order
this.views.sort(function(a, b) {
return a.container.style.zIndex - b.container.style.zIndex;
});
for (var i = 0, l = this.views.length; i < l; i = i+1) {
var view = this.views[i];
if (view.active) {
switch(this.renderMethod) {
case GZ3D.MULTIVIEW_RENDER_VIEWPORTS:
this.renderToViewport(view);
break;
case GZ3D.MULTIVIEW_RENDER_COPY2CANVAS:
this.renderAndCopyToCanvas(view);
break;
}
}
}
};
/**
* !IMPORTANT!
* https://github.com/mrdoob/three.js/issues/3532
* This is at the moment not a good idea, see issue above. ScissorTest will scramble shadowmaps.
*
* @param view
*/
GZ3D.MultiView.prototype.renderToViewport = function(view)
{
this.updateCamera(view); //TODO: better solution with resize callback, also adjust camera helper
var webglRenderer = this.gz3dScene.renderer;
var viewport = this.getViewport(view);
webglRenderer.setViewport( viewport.x, viewport.y, viewport.w, viewport.h );
webglRenderer.setScissor( viewport.x, viewport.y, viewport.w, viewport.h );
webglRenderer.enableScissorTest ( true );
if (this.gz3dScene.effectsEnabled) {
this.gz3dScene.scene.overrideMaterial = this.depthMaterial;
this.gz3dScene.renderer.render(this.gz3dScene.scene, view.camera, this.depthTarget);
this.gz3dScene.scene.overrideMaterial = null;
this.gz3dScene.composer.render();
} else {
webglRenderer.render(this.gz3dScene.scene, view.camera);
}
};
GZ3D.MultiView.prototype.renderAndCopyToCanvas = function(view)
{
this.updateCamera(view); //TODO: better solution with resize callback, also adjust camera helper
var webglRenderer = this.gz3dScene.renderer;
var width = view.container.canvas.clientWidth;
var height = view.container.canvas.clientHeight;
view.container.canvas.width = width;
view.container.canvas.height = height;
if (webglRenderer.context.canvas.width < width) {
webglRenderer.context.canvas.width = width;
}
if (webglRenderer.context.canvas.height < height) {
webglRenderer.context.canvas.height = height;
}
webglRenderer.setViewport( 0, 0, width, height );
if (this.gz3dScene.effectsEnabled) {
this.gz3dScene.scene.overrideMaterial = this.depthMaterial;
this.gz3dScene.renderer.render(this.gz3dScene.scene, view.camera, this.depthTarget);
this.gz3dScene.scene.overrideMaterial = null;
this.gz3dScene.composer.render();
} else {
webglRenderer.render(this.gz3dScene.scene, view.camera);
}
// copy rendered image over to view canvas
var srcX = 0;
var srcY = webglRenderer.context.canvas.height - height + 1;
var dstX = 0;
var dstY = 0;
// no cleaner solution right now, should be coming though: https://github.com/mrdoob/three.js/pull/6723#issuecomment-134129027
// this kills performance a little ...
if ( srcX >= 0 && srcY >= 0 && width >= 1 && height >= 1 ) {
view.container.canvas.getContext('2d').drawImage( webglRenderer.context.canvas,
srcX, srcY, width, height,
dstX, dstY, width, height );
}
};
GZ3D.MultiView.prototype.showCameras = function(show)
{
for (var i = 0; i < this.views.length; i = i+1) {
if (this.views[i].type === 'camera') {
this.setViewVisibility(this.views[i], show);
}
}
};