From feb48fb9f7fdb9c355515c8cd75823781ad44579 Mon Sep 17 00:00:00 2001 From: Vincent Vanwaelscappel Date: Wed, 22 Aug 2018 20:46:10 +0200 Subject: [PATCH] wip #809 @4 --- js/libs/fluidbook/fluidbook.3dflip.js | 59 +- js/libs/fluidbook/fluidbook.js | 1 - js/libs/fluidbook/fluidbook.loader.js | 65 +- .../fluidbook/fluidbook.pagetransitions.js | 3 +- js/libs/fluidbook/fluidbook.share.js | 5 - js/libs/fluidbook/fluidbook.utils.js | 1 - js/libs/fluidbook/fluidbook.zoom.js | 4 +- .../fluidbook/forms/fluidbook.form.bulle.js | 1 - js/libs/threejs/CanvasRenderer.js | 1153 +++++++++++++++++ js/libs/threejs/Projector.js | 1064 +++++++++++++++ 10 files changed, 2305 insertions(+), 51 deletions(-) create mode 100644 js/libs/threejs/CanvasRenderer.js create mode 100644 js/libs/threejs/Projector.js diff --git a/js/libs/fluidbook/fluidbook.3dflip.js b/js/libs/fluidbook/fluidbook.3dflip.js index 4d648a90..fc24ac7e 100644 --- a/js/libs/fluidbook/fluidbook.3dflip.js +++ b/js/libs/fluidbook/fluidbook.3dflip.js @@ -20,19 +20,27 @@ function Fluidbook3DFlip(fluidbook) { this.textures = []; // Performance settings + this.renderEngine = 'webgl'; this.veryLowPerf = this.fluidbook.support.IE > 0; - this.lowPerf = this.veryLowPerf || this.fluidbook.support.android || this.fluidbook.support.iOS || this.fluidbook.support.edge; - this.enableLight = !this.lowPerf; - this.enableShadow = !this.lowPerf && this.enableLight; + if (this.veryLowPerf) { + this.renderEngine = 'canvas'; + } + this.lowPerf = this.renderEngine === 'canvas' || this.veryLowPerf || this.fluidbook.support.edge || this.fluidbook.support.android || this.fluidbook.support.iOS; + if (this.fluidbook.support.android || this.fluidbook.support.iOS) { + this.renderEngine = 'webgl'; + } + this.enableLight = !this.lowPerf && this.renderEngine === 'webgl'; + this.enableShadow = !this.lowPerf && this.enableLight && this.renderEngine === 'webgl'; this.antialias = false; this.animationTime = parseFloat(this.fluidbook.datas.mobileTransitionDuration); + if (this.veryLowPerf) { this.qualityRatio = .7; - this.triangles = 1; + this.triangles = 2; this.animationTime /= 1.5; - } else if (this.lowPerf) { + } else if (this.lowPerf || this.veryLowPerf) { this.qualityRatio = 1.2; - this.triangles = 2; + this.triangles = 3; this.animationTime /= 1.25; } else { this.qualityRatio = 2; @@ -118,16 +126,17 @@ Fluidbook3DFlip.prototype = { geometry.merge(geometry2, new THREE.Matrix4().makeRotationY(Math.PI), 1); var roughness = 0.8; + var overdraw = this.renderEngine === 'canvas'; if (this.enableLight) { this.textures = [ - new THREE.MeshStandardMaterial({side: THREE.FrontSide, roughness: roughness}), - new THREE.MeshStandardMaterial({side: THREE.FrontSide, roughness: roughness}), + new THREE.MeshStandardMaterial({side: THREE.FrontSide, roughness: roughness, overdraw: overdraw}), + new THREE.MeshStandardMaterial({side: THREE.FrontSide, roughness: roughness, overdraw: overdraw}), ]; } else { this.textures = [ - new THREE.MeshBasicMaterial({side: THREE.FrontSide}), - new THREE.MeshBasicMaterial({side: THREE.FrontSide}), + new THREE.MeshBasicMaterial({side: THREE.FrontSide, overdraw: overdraw}), + new THREE.MeshBasicMaterial({side: THREE.FrontSide, overdraw: overdraw}), ]; } geometry.translate(this.fluidbook.datas.width * this.geometryScale * 0.5, 0, 0); @@ -149,7 +158,12 @@ Fluidbook3DFlip.prototype = { this.modifier.reset(); this.modifier.addModifier(this.bend); - this.renderer = new THREE.WebGLRenderer({antialias: this.antialias, alpha: true}); + if (this.renderEngine === 'webgl') { + this.renderer = new THREE.WebGLRenderer({antialias: this.antialias, alpha: true}); + } else if (this.renderEngine == 'canvas') { + this.renderer = new THREE.CanvasRenderer({antialias: this.antialias, alpha: true}); + } + if (this.enableShadow) { this.renderer.shadowMap.enabled = true; this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; @@ -163,9 +177,18 @@ Fluidbook3DFlip.prototype = { this.initAnimations(); this.animate(true); + + $(this.fluidbook).on('fluidbook.ready', function () { + $this.guessCameraZoom(true); + }) + this.resize(); }, + clean: function () { + this.disposeTextures(); + }, + initAnimations: function () { var force = 0.65; var easeOut = Power0.easeOut; @@ -200,7 +223,7 @@ Fluidbook3DFlip.prototype = { prepareTurn: function (turning, callback) { var $this = this; - this.disposeTextures(); + this.prepareTexture(turning.flip[1], function (t1) { $this.prepareTexture(turning.flip[0], function (t2) { @@ -234,17 +257,6 @@ Fluidbook3DFlip.prototype = { } }, - // prepareTexture: function (page, callback) { - // this.disposeTextures(); - // - // var $this = this; - // this.fluidbook.loader.loadTexture(page, function () { - // var texture = new THREE.Texture($this.fluidbook.loader.getTexture(page)); - // texture.needsUpdate = true; - // callback(texture); - // }); - // }, - prepareTexture: function (page, callback) { var texture = new THREE.Texture(this.fluidbook.loader.getTexture(page)); texture.needsUpdate = true; @@ -275,7 +287,6 @@ Fluidbook3DFlip.prototype = { ease: Power0.easeIn, onComplete: function () { $this.turnRunning = false; - $this.disposeTextures(); this.tlname = ''; callback(); } diff --git a/js/libs/fluidbook/fluidbook.js b/js/libs/fluidbook/fluidbook.js index fced84d6..6553d836 100644 --- a/js/libs/fluidbook/fluidbook.js +++ b/js/libs/fluidbook/fluidbook.js @@ -157,7 +157,6 @@ Fluidbook.prototype = { this.displayLoader(); var $this = this; this.loader.preloadStart(function () { - console.log('preload done'); $this.ready(); }); }, diff --git a/js/libs/fluidbook/fluidbook.loader.js b/js/libs/fluidbook/fluidbook.loader.js index b37a2a6e..194917f9 100644 --- a/js/libs/fluidbook/fluidbook.loader.js +++ b/js/libs/fluidbook/fluidbook.loader.js @@ -5,9 +5,12 @@ function FluidbookLoader(fluidbook) { this.textures = []; this.links = []; this.toPreload = []; + this.preloaded = []; this.imagesErrors = []; this.shadeLeft = null; this.shadeRight = null; + this.transitionPages = []; + this.cleanTimeout = null; this.init(); } @@ -32,6 +35,7 @@ FluidbookLoader.prototype = { preloadStart: function (callback) { this.toPreload = [1, 2, 3, 4, 5]; + this.preloaded = this.toPreload.slice(0); this.preloadPages(callback); }, @@ -43,6 +47,8 @@ FluidbookLoader.prototype = { return; } + this.transitionPages = pages.slice(0); + var $callback = callback; var $page = $pages.shift(); @@ -58,10 +64,11 @@ FluidbookLoader.prototype = { arePreloadedPages: function (pages) { for (var i in pages) { var page = pages[i]; - if (this.backgrounds[page] == undefined) { + if (this.backgrounds[page] === undefined) { return false; } } + this.preloaded.push(page); return true; }, @@ -96,10 +103,7 @@ FluidbookLoader.prototype = { } var $this = this; - if (this.backgrounds[page] != undefined) { - callback(); - return; - } + var $_callback; if (this.fluidbook.support.transitions3dacc) { $_callback = function () { @@ -109,12 +113,21 @@ FluidbookLoader.prototype = { $_callback = callback; } - this.backgrounds[page] = this.loadImage(this.getBackgroundURL(page), null, null, null, function () { + var $__callback = function () { if ($this.getVersionToLoad(page) === 'textasvector') { $this._loadTexts(page, $_callback); } else { $_callback(); } + }; + + if (this.backgrounds[page] !== undefined && this.backgrounds[page] !== null) { + $__callback(); + return; + } + + this.backgrounds[page] = this.loadImage(this.getBackgroundURL(page), null, null, null, function () { + $__callback(); }); }, @@ -137,22 +150,38 @@ FluidbookLoader.prototype = { for (var i = min; i <= max; i++) { this.toPreload.push(i); } - + this.preloaded=this.toPreload.slice(0); this.cleanPreloaded(); this.preloadPages(); }, - cleanPreloaded: function () { + + _cleanPreloaded: function () { for (var i = 1; i <= this.fluidbook.getMaxPage(); i++) { - if (this.backgrounds[i] !== undefined && this.toPreload.indexOf(i) === -1) { + if (this.preloaded.indexOf(i) === -1 && this.transitionPages.indexOf(i) === -1) { this.deletePage(i); } } }, + cleanPreloaded: function () { + var $this = this; + clearTimeout(this.cleanTimeout); + this.cleanTimeout = setTimeout(function () { + $this.fluidbook.executeWhenNetwork(function () { + $this._cleanPreloaded(); + }) + }, 10000); + }, deletePage: function (page) { - delete this.backgrounds[page]; - delete this.texts[page]; - delete this.links[page]; - if (this.textures[page] !== undefined) { + if (this.backgrounds[page] !== undefined) { + delete this.backgrounds[page]; + } + if (this.texts[page] !== undefined) { + delete this.texts[page]; + } + if (this.links[page] !== undefined) { + delete this.links[page]; + } + if (this.textures[page] !== undefined && this.textures[page] !== null) { delete this.textures[page]; } }, @@ -306,7 +335,8 @@ FluidbookLoader.prototype = { callback(); return; } - if (this.textures[page] != undefined) { + + if (this.textures[page] !== undefined && this.textures[page] !== null) { callback(); } else { this._loadTexture(page, callback); @@ -319,6 +349,7 @@ FluidbookLoader.prototype = { return; } + var $this = this; var d = 1024; var c = document.getElementById("pscanvas"); c.width = d; @@ -357,15 +388,15 @@ FluidbookLoader.prototype = { var i = new Image(); i.onload = function () { + $this.textures[page] = i; callback(); } i.src = c.toDataURL("image/png"); - - this.textures[page] = i; }, getTexture: function (page) { - return this.textures[page]; + var res = this.textures[page]; + return res; }, loadTexts: function (pageNr, callback) { diff --git a/js/libs/fluidbook/fluidbook.pagetransitions.js b/js/libs/fluidbook/fluidbook.pagetransitions.js index fe749e0d..cd0e94fa 100644 --- a/js/libs/fluidbook/fluidbook.pagetransitions.js +++ b/js/libs/fluidbook/fluidbook.pagetransitions.js @@ -99,13 +99,14 @@ FluidbookPageTransition.prototype = { this.fluidbook.displayLoader(); } + $this.fluidbook.pauseNetwork(3000); var currentDoublePage = $("#currentDoublePage"); this.fluidbook.loader.preloadPagesBeforeTransition(turning.end, function () { + $this.flip3d.clean(); $this.flip3d.prepareTurn(turning, function () { $this.fluidbook.loader.setContentsInDoublePage(currentDoublePage, turning.flat, true, function () { $this.beforeTransition(pageNr, 3, turning); - $this.fluidbook.pauseNetwork(2000); $this.flip3d.playTurn(turning.dir, function () { $this.fluidbook.loader.setContentsInDoublePage(currentDoublePage, turning.end, true, function () { $this.afterTransition(pageNr); diff --git a/js/libs/fluidbook/fluidbook.share.js b/js/libs/fluidbook/fluidbook.share.js index 201ea151..0e7b8d20 100644 --- a/js/libs/fluidbook/fluidbook.share.js +++ b/js/libs/fluidbook/fluidbook.share.js @@ -76,14 +76,12 @@ FluidbookShare.prototype = { } var e = url.split(':'); - console.log(e); if (e.length == 1) { return relativeToAbsoluteURL(url); } else { if (e[0] == 'http' || e[0] == 'https') { return url } else if (e[0] == 'article') { - console.log(e[1]); return relativeToAbsoluteURL('./p/' + this.getSEOArticle(e[1]).url); } } @@ -128,7 +126,6 @@ FluidbookShare.prototype = { body = body.replace(/\\r\\n/g, "\n"); body = body.replace(/\\r/g, "\n"); body = body.replace(/\\n/g, "\r\n"); - console.log(body); return body; }, @@ -182,7 +179,6 @@ FluidbookShare.prototype = { }, openShare: function (url, p2, callback) { - console.log('open share'); var view; if (url == undefined || url == null || url == 'undefined' || !url) { url = ''; @@ -192,7 +188,6 @@ FluidbookShare.prototype = { view += '
'; view += this.getShareLinks(false, url); view += '
'; - ; $("#view").append('
' + view + '
'); if (callback != undefined) { diff --git a/js/libs/fluidbook/fluidbook.utils.js b/js/libs/fluidbook/fluidbook.utils.js index 27e7659b..8c1a89f5 100644 --- a/js/libs/fluidbook/fluidbook.utils.js +++ b/js/libs/fluidbook/fluidbook.utils.js @@ -81,7 +81,6 @@ function getSpriteIcon(icon, attrs) { } function relativeToAbsoluteURL(relative) { - console.log(relative); var link = document.createElement("a"); link.href = relative; return link.href; diff --git a/js/libs/fluidbook/fluidbook.zoom.js b/js/libs/fluidbook/fluidbook.zoom.js index 8e8015a9..737ff616 100644 --- a/js/libs/fluidbook/fluidbook.zoom.js +++ b/js/libs/fluidbook/fluidbook.zoom.js @@ -169,7 +169,9 @@ FluidbookZoom.prototype = { var hiddenElements = $("header,footer,#interface,#links a.bookmark"); if (this.zoom != 1) { - this.fluidbook.help.hide(); // Hide the help view if it's showing + if (this.fluidbook.help !== undefined) { + this.fluidbook.help.hide(); // Hide the help view if it's showing + } $("#shadow").hide(); if (!$('header').hasClass('hidden')) { diff --git a/js/libs/fluidbook/forms/fluidbook.form.bulle.js b/js/libs/fluidbook/forms/fluidbook.form.bulle.js index 2c57d76c..40cc5a2f 100644 --- a/js/libs/fluidbook/forms/fluidbook.form.bulle.js +++ b/js/libs/fluidbook/forms/fluidbook.form.bulle.js @@ -55,7 +55,6 @@ FluidbookBulleForm.prototype = { return false; }); $('#formBulle form').submit(function() { - console.log('submit'); $(this).ajaxSubmit({ url: 'https://workshop.fluidbook.com/services/bulle', success: function(data) { diff --git a/js/libs/threejs/CanvasRenderer.js b/js/libs/threejs/CanvasRenderer.js new file mode 100644 index 00000000..1e86eb7a --- /dev/null +++ b/js/libs/threejs/CanvasRenderer.js @@ -0,0 +1,1153 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +THREE.SpriteCanvasMaterial = function ( parameters ) { + + THREE.Material.call( this ); + + this.type = 'SpriteCanvasMaterial'; + + this.color = new THREE.Color( 0xffffff ); + this.program = function () {}; + + this.setValues( parameters ); + +}; + +THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype ); +THREE.SpriteCanvasMaterial.prototype.constructor = THREE.SpriteCanvasMaterial; +THREE.SpriteCanvasMaterial.prototype.isSpriteCanvasMaterial = true; + +THREE.SpriteCanvasMaterial.prototype.clone = function () { + + var material = new THREE.SpriteCanvasMaterial(); + + material.copy( this ); + material.color.copy( this.color ); + material.program = this.program; + + return material; + +}; + +// + +THREE.CanvasRenderer = function ( parameters ) { + + console.log( 'THREE.CanvasRenderer', THREE.REVISION ); + + parameters = parameters || {}; + + var _this = this, + _renderData, _elements, _lights, + _projector = new THREE.Projector(), + + _canvas = parameters.canvas !== undefined + ? parameters.canvas + : document.createElement( 'canvas' ), + + _canvasWidth = _canvas.width, + _canvasHeight = _canvas.height, + _canvasWidthHalf = Math.floor( _canvasWidth / 2 ), + _canvasHeightHalf = Math.floor( _canvasHeight / 2 ), + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = _canvasWidth, + _viewportHeight = _canvasHeight, + + _pixelRatio = 1, + + _context = _canvas.getContext( '2d', { + alpha: parameters.alpha === true + } ), + + _clearColor = new THREE.Color( 0x000000 ), + _clearAlpha = parameters.alpha === true ? 0 : 1, + + _contextGlobalAlpha = 1, + _contextGlobalCompositeOperation = 0, + _contextStrokeStyle = null, + _contextFillStyle = null, + _contextLineWidth = null, + _contextLineCap = null, + _contextLineJoin = null, + _contextLineDash = [], + + _v1, _v2, _v3, + + _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, + + _color = new THREE.Color(), + + _diffuseColor = new THREE.Color(), + _emissiveColor = new THREE.Color(), + + _lightColor = new THREE.Color(), + + _patterns = {}, + + _uvs, + _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, + + _clipBox = new THREE.Box2(), + _clearBox = new THREE.Box2(), + _elemBox = new THREE.Box2(), + + _ambientLight = new THREE.Color(), + _directionalLights = new THREE.Color(), + _pointLights = new THREE.Color(), + + _vector3 = new THREE.Vector3(), // Needed for PointLight + _centroid = new THREE.Vector3(), + _normal = new THREE.Vector3(), + _normalViewMatrix = new THREE.Matrix3(); + + /* TODO + _canvas.mozImageSmoothingEnabled = false; + _canvas.webkitImageSmoothingEnabled = false; + _canvas.msImageSmoothingEnabled = false; + _canvas.imageSmoothingEnabled = false; + */ + + // dash+gap fallbacks for Firefox and everything else + + if ( _context.setLineDash === undefined ) { + + _context.setLineDash = function () {}; + + } + + this.domElement = _canvas; + + this.autoClear = true; + this.sortObjects = true; + this.sortElements = true; + + this.info = { + + render: { + + vertices: 0, + faces: 0 + + } + + }; + + // API + + this.getContext = function () { + + return _context; + + }; + + this.getContextAttributes = function () { + + return _context.getContextAttributes(); + + }; + + this.getPixelRatio = function () { + + return _pixelRatio; + + }; + + this.setPixelRatio = function ( value ) { + + if ( value !== undefined ) _pixelRatio = value; + + }; + + this.setSize = function ( width, height, updateStyle ) { + + _canvasWidth = width * _pixelRatio; + _canvasHeight = height * _pixelRatio; + + _canvas.width = _canvasWidth; + _canvas.height = _canvasHeight; + + _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); + _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); + + if ( updateStyle !== false ) { + + _canvas.style.width = width + 'px'; + _canvas.style.height = height + 'px'; + + } + + _clipBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); + _clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); + _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + _contextGlobalAlpha = 1; + _contextGlobalCompositeOperation = 0; + _contextStrokeStyle = null; + _contextFillStyle = null; + _contextLineWidth = null; + _contextLineCap = null; + _contextLineJoin = null; + + this.setViewport( 0, 0, width, height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x * _pixelRatio; + _viewportY = y * _pixelRatio; + + _viewportWidth = width * _pixelRatio; + _viewportHeight = height * _pixelRatio; + + }; + + this.setScissor = function () {}; + this.setScissorTest = function () {}; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.set( color ); + _clearAlpha = alpha !== undefined ? alpha : 1; + + _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); + _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); + + }; + + this.setClearColorHex = function ( hex, alpha ) { + + console.warn( 'THREE.CanvasRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); + this.setClearColor( hex, alpha ); + + }; + + this.getClearColor = function () { + + return _clearColor; + + }; + + this.getClearAlpha = function () { + + return _clearAlpha; + + }; + + this.getMaxAnisotropy = function () { + + return 0; + + }; + + this.clear = function () { + + if ( _clearBox.isEmpty() === false ) { + + _clearBox.intersect( _clipBox ); + _clearBox.expandByScalar( 2 ); + + _clearBox.min.x = _clearBox.min.x + _canvasWidthHalf; + _clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf; // higher y value ! + _clearBox.max.x = _clearBox.max.x + _canvasWidthHalf; + _clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf; // lower y value ! + + if ( _clearAlpha < 1 ) { + + _context.clearRect( + _clearBox.min.x | 0, + _clearBox.max.y | 0, + ( _clearBox.max.x - _clearBox.min.x ) | 0, + ( _clearBox.min.y - _clearBox.max.y ) | 0 + ); + + } + + if ( _clearAlpha > 0 ) { + + setOpacity( 1 ); + setBlending( THREE.NormalBlending ); + + setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); + + _context.fillRect( + _clearBox.min.x | 0, + _clearBox.max.y | 0, + ( _clearBox.max.x - _clearBox.min.x ) | 0, + ( _clearBox.min.y - _clearBox.max.y ) | 0 + ); + + } + + _clearBox.makeEmpty(); + + } + + }; + + // compatibility + + this.clearColor = function () {}; + this.clearDepth = function () {}; + this.clearStencil = function () {}; + + this.render = function ( scene, camera ) { + + if ( camera.isCamera === undefined ) { + + console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); + return; + + } + + var background = scene.background; + + if ( background && background.isColor ) { + + setOpacity( 1 ); + setBlending( THREE.NormalBlending ); + + setFillStyle( background.getStyle() ); + _context.fillRect( 0, 0, _canvasWidth, _canvasHeight ); + + } else if ( this.autoClear === true ) { + + this.clear(); + + } + + _this.info.render.vertices = 0; + _this.info.render.faces = 0; + + _context.setTransform( _viewportWidth / _canvasWidth, 0, 0, - _viewportHeight / _canvasHeight, _viewportX, _canvasHeight - _viewportY ); + _context.translate( _canvasWidthHalf, _canvasHeightHalf ); + + _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); + _elements = _renderData.elements; + _lights = _renderData.lights; + + _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); + + /* DEBUG + setFillStyle( 'rgba( 0, 255, 255, 0.5 )' ); + _context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y ); + */ + + calculateLights(); + + for ( var e = 0, el = _elements.length; e < el; e ++ ) { + + var element = _elements[ e ]; + + var material = element.material; + + if ( material === undefined || material.opacity === 0 ) continue; + + _elemBox.makeEmpty(); + + if ( element instanceof THREE.RenderableSprite ) { + + _v1 = element; + _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; + + renderSprite( _v1, element, material ); + + } else if ( element instanceof THREE.RenderableLine ) { + + _v1 = element.v1; _v2 = element.v2; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen + ] ); + + if ( _clipBox.intersectsBox( _elemBox ) === true ) { + + renderLine( _v1, _v2, element, material ); + + } + + } else if ( element instanceof THREE.RenderableFace ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; + if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; + if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + + if ( material.overdraw > 0 ) { + + expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); + expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); + expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); + + } + + _elemBox.setFromPoints( [ + _v1.positionScreen, + _v2.positionScreen, + _v3.positionScreen + ] ); + + if ( _clipBox.intersectsBox( _elemBox ) === true ) { + + renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); + + } + + } + + /* DEBUG + setLineWidth( 1 ); + setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' ); + _context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y ); + */ + + _clearBox.union( _elemBox ); + + } + + /* DEBUG + setLineWidth( 1 ); + setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' ); + _context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y ); + */ + + _context.setTransform( 1, 0, 0, 1, 0, 0 ); + + }; + + // + + function calculateLights() { + + _ambientLight.setRGB( 0, 0, 0 ); + _directionalLights.setRGB( 0, 0, 0 ); + _pointLights.setRGB( 0, 0, 0 ); + + for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + + var light = _lights[ l ]; + var lightColor = light.color; + + if ( light.isAmbientLight ) { + + _ambientLight.add( lightColor ); + + } else if ( light.isDirectionalLight ) { + + // for sprites + + _directionalLights.add( lightColor ); + + } else if ( light.isPointLight ) { + + // for sprites + + _pointLights.add( lightColor ); + + } + + } + + } + + function calculateLight( position, normal, color ) { + + for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { + + var light = _lights[ l ]; + + _lightColor.copy( light.color ); + + if ( light.isDirectionalLight ) { + + var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); + + var amount = normal.dot( lightPosition ); + + if ( amount <= 0 ) continue; + + amount *= light.intensity; + + color.add( _lightColor.multiplyScalar( amount ) ); + + } else if ( light.isPointLight ) { + + var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); + + var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); + + if ( amount <= 0 ) continue; + + amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); + + if ( amount == 0 ) continue; + + amount *= light.intensity; + + color.add( _lightColor.multiplyScalar( amount ) ); + + } + + } + + } + + function renderSprite( v1, element, material ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + var scaleX = element.scale.x * _canvasWidthHalf; + var scaleY = element.scale.y * _canvasHeightHalf; + + var dist = Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite + _elemBox.min.set( v1.x - dist, v1.y - dist ); + _elemBox.max.set( v1.x + dist, v1.y + dist ); + + if ( material.isSpriteMaterial ) { + + var texture = material.map; + + if ( texture !== null ) { + + var pattern = _patterns[ texture.id ]; + + if ( pattern === undefined || pattern.version !== texture.version ) { + + pattern = textureToPattern( texture ); + _patterns[ texture.id ] = pattern; + + } + + if ( pattern.canvas !== undefined ) { + + setFillStyle( pattern.canvas ); + + var bitmap = texture.image; + + var ox = bitmap.width * texture.offset.x; + var oy = bitmap.height * texture.offset.y; + + var sx = bitmap.width * texture.repeat.x; + var sy = bitmap.height * texture.repeat.y; + + var cx = scaleX / sx; + var cy = scaleY / sy; + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.translate( - scaleX / 2, - scaleY / 2 ); + _context.scale( cx, cy ); + _context.translate( - ox, - oy ); + _context.fillRect( ox, oy, sx, sy ); + _context.restore(); + + } + + } else { + + // no texture + + setFillStyle( material.color.getStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.scale( scaleX, - scaleY ); + _context.fillRect( - 0.5, - 0.5, 1, 1 ); + _context.restore(); + + } + + } else if ( material.isSpriteCanvasMaterial ) { + + setStrokeStyle( material.color.getStyle() ); + setFillStyle( material.color.getStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.scale( scaleX, scaleY ); + + material.program( _context ); + + _context.restore(); + + } else if ( material.isPointsMaterial ) { + + setFillStyle( material.color.getStyle() ); + + _context.save(); + _context.translate( v1.x, v1.y ); + if ( material.rotation !== 0 ) _context.rotate( material.rotation ); + _context.scale( scaleX * material.size, - scaleY * material.size ); + _context.fillRect( - 0.5, - 0.5, 1, 1 ); + _context.restore(); + + } + + /* DEBUG + setStrokeStyle( 'rgb(255,255,0)' ); + _context.beginPath(); + _context.moveTo( v1.x - 10, v1.y ); + _context.lineTo( v1.x + 10, v1.y ); + _context.moveTo( v1.x, v1.y - 10 ); + _context.lineTo( v1.x, v1.y + 10 ); + _context.stroke(); + */ + + } + + function renderLine( v1, v2, element, material ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _context.beginPath(); + _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); + _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); + + if ( material.isLineBasicMaterial ) { + + setLineWidth( material.linewidth ); + setLineCap( material.linecap ); + setLineJoin( material.linejoin ); + + if ( material.vertexColors !== THREE.VertexColors ) { + + setStrokeStyle( material.color.getStyle() ); + + } else { + + var colorStyle1 = element.vertexColors[ 0 ].getStyle(); + var colorStyle2 = element.vertexColors[ 1 ].getStyle(); + + if ( colorStyle1 === colorStyle2 ) { + + setStrokeStyle( colorStyle1 ); + + } else { + + try { + + var grad = _context.createLinearGradient( + v1.positionScreen.x, + v1.positionScreen.y, + v2.positionScreen.x, + v2.positionScreen.y + ); + grad.addColorStop( 0, colorStyle1 ); + grad.addColorStop( 1, colorStyle2 ); + + } catch ( exception ) { + + grad = colorStyle1; + + } + + setStrokeStyle( grad ); + + } + + } + + if ( material.isLineDashedMaterial ) { + + setLineDash( [ material.dashSize, material.gapSize ] ); + + } + + _context.stroke(); + _elemBox.expandByScalar( material.linewidth * 2 ); + + if ( material.isLineDashedMaterial ) { + + setLineDash( [] ); + + } + + } + + } + + function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { + + _this.info.render.vertices += 3; + _this.info.render.faces ++; + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; + _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; + _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); + + if ( ( material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial ) && material.map === null ) { + + _diffuseColor.copy( material.color ); + _emissiveColor.copy( material.emissive ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _diffuseColor.multiply( element.color ); + + } + + _color.copy( _ambientLight ); + + _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); + + calculateLight( _centroid, element.normalModel, _color ); + + _color.multiply( _diffuseColor ).add( _emissiveColor ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else if ( material.isMeshBasicMaterial || material.isMeshLambertMaterial || material.isMeshPhongMaterial || material.isMeshStandardMaterial ) { + + if ( material.map !== null ) { + + var mapping = material.map.mapping; + + if ( mapping === THREE.UVMapping ) { + + _uvs = element.uvs; + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map ); + + } + + } else if ( material.envMap !== null ) { + + if ( material.envMap.mapping === THREE.SphericalReflectionMapping ) { + + _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); + _uv1x = 0.5 * _normal.x + 0.5; + _uv1y = 0.5 * _normal.y + 0.5; + + _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); + _uv2x = 0.5 * _normal.x + 0.5; + _uv2y = 0.5 * _normal.y + 0.5; + + _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); + _uv3x = 0.5 * _normal.x + 0.5; + _uv3y = 0.5 * _normal.y + 0.5; + + patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); + + } + + } else { + + _color.copy( material.color ); + + if ( material.vertexColors === THREE.FaceColors ) { + + _color.multiply( element.color ); + + } + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } else if ( material.isMeshNormalMaterial ) { + + _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); + + _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } else { + + _color.setRGB( 1, 1, 1 ); + + material.wireframe === true + ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) + : fillPath( _color ); + + } + + } + + // + + function drawTriangle( x0, y0, x1, y1, x2, y2 ) { + + _context.beginPath(); + _context.moveTo( x0, y0 ); + _context.lineTo( x1, y1 ); + _context.lineTo( x2, y2 ); + _context.closePath(); + + } + + function strokePath( color, linewidth, linecap, linejoin ) { + + setLineWidth( linewidth ); + setLineCap( linecap ); + setLineJoin( linejoin ); + setStrokeStyle( color.getStyle() ); + + _context.stroke(); + + _elemBox.expandByScalar( linewidth * 2 ); + + } + + function fillPath( color ) { + + setFillStyle( color.getStyle() ); + _context.fill(); + + } + + function textureToPattern( texture ) { + + if ( texture.version === 0 || + texture instanceof THREE.CompressedTexture || + texture instanceof THREE.DataTexture ) { + + return { + canvas: undefined, + version: texture.version + }; + + } + + var image = texture.image; + + if ( image.complete === false ) { + + return { + canvas: undefined, + version: 0 + }; + + } + + var repeatX = texture.wrapS === THREE.RepeatWrapping || texture.wrapS === THREE.MirroredRepeatWrapping; + var repeatY = texture.wrapT === THREE.RepeatWrapping || texture.wrapT === THREE.MirroredRepeatWrapping; + + var mirrorX = texture.wrapS === THREE.MirroredRepeatWrapping; + var mirrorY = texture.wrapT === THREE.MirroredRepeatWrapping; + + // + + var canvas = document.createElement( 'canvas' ); + canvas.width = image.width * ( mirrorX ? 2 : 1 ); + canvas.height = image.height * ( mirrorY ? 2 : 1 ); + + var context = canvas.getContext( '2d' ); + context.setTransform( 1, 0, 0, - 1, 0, image.height ); + context.drawImage( image, 0, 0 ); + + if ( mirrorX === true ) { + + context.setTransform( - 1, 0, 0, - 1, image.width, image.height ); + context.drawImage( image, - image.width, 0 ); + + } + + if ( mirrorY === true ) { + + context.setTransform( 1, 0, 0, 1, 0, 0 ); + context.drawImage( image, 0, image.height ); + + } + + if ( mirrorX === true && mirrorY === true ) { + + context.setTransform( - 1, 0, 0, 1, image.width, 0 ); + context.drawImage( image, - image.width, image.height ); + + } + + var repeat = 'no-repeat'; + + if ( repeatX === true && repeatY === true ) { + + repeat = 'repeat'; + + } else if ( repeatX === true ) { + + repeat = 'repeat-x'; + + } else if ( repeatY === true ) { + + repeat = 'repeat-y'; + + } + + var pattern = _context.createPattern( canvas, repeat ); + + if ( texture.onUpdate ) texture.onUpdate( texture ); + + return { + canvas: pattern, + version: texture.version + }; + + } + + function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { + + var pattern = _patterns[ texture.id ]; + + if ( pattern === undefined || pattern.version !== texture.version ) { + + pattern = textureToPattern( texture ); + _patterns[ texture.id ] = pattern; + + } + + if ( pattern.canvas !== undefined ) { + + setFillStyle( pattern.canvas ); + + } else { + + setFillStyle( 'rgba( 0, 0, 0, 1)' ); + _context.fill(); + return; + + } + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + offsetX = texture.offset.x / texture.repeat.x, + offsetY = texture.offset.y / texture.repeat.y, + width = texture.image.width * texture.repeat.x, + height = texture.image.height * texture.repeat.y; + + u0 = ( u0 + offsetX ) * width; + v0 = ( v0 + offsetY ) * height; + + u1 = ( u1 + offsetX ) * width; + v1 = ( v1 + offsetY ) * height; + + u2 = ( u2 + offsetX ) * width; + v2 = ( v2 + offsetY ) * height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + if ( det === 0 ) return; + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.fill(); + _context.restore(); + + } + + /* + function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + width = image.width - 1, + height = image.height - 1; + + u0 *= width; v0 *= height; + u1 *= width; v1 *= height; + u2 *= width; v2 *= height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.clip(); + _context.drawImage( image, 0, 0 ); + _context.restore(); + + } + */ + + // Hide anti-alias gaps + + function expand( v1, v2, pixels ) { + + var x = v2.x - v1.x, y = v2.y - v1.y, + det = x * x + y * y, idet; + + if ( det === 0 ) return; + + idet = pixels / Math.sqrt( det ); + + x *= idet; y *= idet; + + v2.x += x; v2.y += y; + v1.x -= x; v1.y -= y; + + } + + // Context cached methods. + + function setOpacity( value ) { + + if ( _contextGlobalAlpha !== value ) { + + _context.globalAlpha = value; + _contextGlobalAlpha = value; + + } + + } + + function setBlending( value ) { + + if ( _contextGlobalCompositeOperation !== value ) { + + if ( value === THREE.NormalBlending ) { + + _context.globalCompositeOperation = 'source-over'; + + } else if ( value === THREE.AdditiveBlending ) { + + _context.globalCompositeOperation = 'lighter'; + + } else if ( value === THREE.SubtractiveBlending ) { + + _context.globalCompositeOperation = 'darker'; + + } else if ( value === THREE.MultiplyBlending ) { + + _context.globalCompositeOperation = 'multiply'; + + } + + _contextGlobalCompositeOperation = value; + + } + + } + + function setLineWidth( value ) { + + if ( _contextLineWidth !== value ) { + + _context.lineWidth = value; + _contextLineWidth = value; + + } + + } + + function setLineCap( value ) { + + // "butt", "round", "square" + + if ( _contextLineCap !== value ) { + + _context.lineCap = value; + _contextLineCap = value; + + } + + } + + function setLineJoin( value ) { + + // "round", "bevel", "miter" + + if ( _contextLineJoin !== value ) { + + _context.lineJoin = value; + _contextLineJoin = value; + + } + + } + + function setStrokeStyle( value ) { + + if ( _contextStrokeStyle !== value ) { + + _context.strokeStyle = value; + _contextStrokeStyle = value; + + } + + } + + function setFillStyle( value ) { + + if ( _contextFillStyle !== value ) { + + _context.fillStyle = value; + _contextFillStyle = value; + + } + + } + + function setLineDash( value ) { + + if ( _contextLineDash.length !== value.length ) { + + _context.setLineDash( value ); + _contextLineDash = value; + + } + + } + +}; diff --git a/js/libs/threejs/Projector.js b/js/libs/threejs/Projector.js new file mode 100644 index 00000000..5187f4e5 --- /dev/null +++ b/js/libs/threejs/Projector.js @@ -0,0 +1,1064 @@ +/** + * @author mrdoob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author julianwa / https://github.com/julianwa + */ + +THREE.RenderableObject = function () { + + this.id = 0; + + this.object = null; + this.z = 0; + this.renderOrder = 0; + +}; + +// + +THREE.RenderableFace = function () { + + this.id = 0; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + this.v3 = new THREE.RenderableVertex(); + + this.normalModel = new THREE.Vector3(); + + this.vertexNormalsModel = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()]; + this.vertexNormalsLength = 0; + + this.color = new THREE.Color(); + this.material = null; + this.uvs = [new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2()]; + + this.z = 0; + this.renderOrder = 0; + +}; + +// + +THREE.RenderableVertex = function () { + + this.position = new THREE.Vector3(); + this.positionWorld = new THREE.Vector3(); + this.positionScreen = new THREE.Vector4(); + + this.visible = true; + +}; + +THREE.RenderableVertex.prototype.copy = function (vertex) { + + this.positionWorld.copy(vertex.positionWorld); + this.positionScreen.copy(vertex.positionScreen); + +}; + +// + +THREE.RenderableLine = function () { + + this.id = 0; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + + this.vertexColors = [new THREE.Color(), new THREE.Color()]; + this.material = null; + + this.z = 0; + this.renderOrder = 0; + +}; + +// + +THREE.RenderableSprite = function () { + + this.id = 0; + + this.object = null; + + this.x = 0; + this.y = 0; + this.z = 0; + + this.rotation = 0; + this.scale = new THREE.Vector2(); + + this.material = null; + this.renderOrder = 0; + +}; + +// + +THREE.Projector = function () { + + var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, + _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, + _face, _faceCount, _facePool = [], _facePoolLength = 0, + _line, _lineCount, _linePool = [], _linePoolLength = 0, + _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, + + _renderData = {objects: [], lights: [], elements: []}, + + _vector3 = new THREE.Vector3(), + _vector4 = new THREE.Vector4(), + + _clipBox = new THREE.Box3(new THREE.Vector3(-1, -1, -1), new THREE.Vector3(1, 1, 1)), + _boundingBox = new THREE.Box3(), + _points3 = new Array(3), + + _viewMatrix = new THREE.Matrix4(), + _viewProjectionMatrix = new THREE.Matrix4(), + + _modelMatrix, + _modelViewProjectionMatrix = new THREE.Matrix4(), + + _normalMatrix = new THREE.Matrix3(), + + _frustum = new THREE.Frustum(), + + _clippedVertex1PositionScreen = new THREE.Vector4(), + _clippedVertex2PositionScreen = new THREE.Vector4(); + + // + + this.projectVector = function (vector, camera) { + + console.warn('THREE.Projector: .projectVector() is now vector.project().'); + vector.project(camera); + + }; + + this.unprojectVector = function (vector, camera) { + + console.warn('THREE.Projector: .unprojectVector() is now vector.unproject().'); + vector.unproject(camera); + + }; + + this.pickingRay = function () { + + console.error('THREE.Projector: .pickingRay() is now raycaster.setFromCamera().'); + + }; + + // + + var RenderList = function () { + + var normals = []; + var colors = []; + var uvs = []; + + var object = null; + var material = null; + + var normalMatrix = new THREE.Matrix3(); + + function setObject(value) { + + object = value; + material = object.material; + + normalMatrix.getNormalMatrix(object.matrixWorld); + + normals.length = 0; + colors.length = 0; + uvs.length = 0; + + } + + function projectVertex(vertex) { + + var position = vertex.position; + var positionWorld = vertex.positionWorld; + var positionScreen = vertex.positionScreen; + + positionWorld.copy(position).applyMatrix4(_modelMatrix); + positionScreen.copy(positionWorld).applyMatrix4(_viewProjectionMatrix); + + var invW = 1 / positionScreen.w; + + positionScreen.x *= invW; + positionScreen.y *= invW; + positionScreen.z *= invW; + + vertex.visible = positionScreen.x >= -1 && positionScreen.x <= 1 && + positionScreen.y >= -1 && positionScreen.y <= 1 && + positionScreen.z >= -1 && positionScreen.z <= 1; + + } + + function pushVertex(x, y, z) { + + _vertex = getNextVertexInPool(); + _vertex.position.set(x, y, z); + + projectVertex(_vertex); + + } + + function pushNormal(x, y, z) { + + normals.push(x, y, z); + + } + + function pushColor(r, g, b) { + + colors.push(r, g, b); + + } + + function pushUv(x, y) { + + uvs.push(x, y); + + } + + function checkTriangleVisibility(v1, v2, v3) { + + if (v1.visible === true || v2.visible === true || v3.visible === true) return true; + + _points3[0] = v1.positionScreen; + _points3[1] = v2.positionScreen; + _points3[2] = v3.positionScreen; + + return _clipBox.intersectsBox(_boundingBox.setFromPoints(_points3)); + + } + + function checkBackfaceCulling(v1, v2, v3) { + + return ((v3.positionScreen.x - v1.positionScreen.x) * + (v2.positionScreen.y - v1.positionScreen.y) - + (v3.positionScreen.y - v1.positionScreen.y) * + (v2.positionScreen.x - v1.positionScreen.x)) < 0; + + } + + function pushLine(a, b) { + + var v1 = _vertexPool[a]; + var v2 = _vertexPool[b]; + + // Clip + + v1.positionScreen.copy(v1.position).applyMatrix4(_modelViewProjectionMatrix); + v2.positionScreen.copy(v2.position).applyMatrix4(_modelViewProjectionMatrix); + + if (clipLine(v1.positionScreen, v2.positionScreen) === true) { + + // Perform the perspective divide + v1.positionScreen.multiplyScalar(1 / v1.positionScreen.w); + v2.positionScreen.multiplyScalar(1 / v2.positionScreen.w); + + _line = getNextLineInPool(); + _line.id = object.id; + _line.v1.copy(v1); + _line.v2.copy(v2); + _line.z = Math.max(v1.positionScreen.z, v2.positionScreen.z); + _line.renderOrder = object.renderOrder; + + _line.material = object.material; + + if (object.material.vertexColors === THREE.VertexColors) { + + _line.vertexColors[0].fromArray(colors, a * 3); + _line.vertexColors[1].fromArray(colors, b * 3); + + } + + _renderData.elements.push(_line); + + } + + } + + function pushTriangle(a, b, c, material) { + + var v1 = _vertexPool[a]; + var v2 = _vertexPool[b]; + var v3 = _vertexPool[c]; + + if (checkTriangleVisibility(v1, v2, v3) === false) return; + + if (material.side === THREE.DoubleSide || checkBackfaceCulling(v1, v2, v3) === true) { + + _face = getNextFaceInPool(); + + _face.id = object.id; + _face.v1.copy(v1); + _face.v2.copy(v2); + _face.v3.copy(v3); + _face.z = (v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z) / 3; + _face.renderOrder = object.renderOrder; + + // face normal + _vector3.subVectors(v3.position, v2.position); + _vector4.subVectors(v1.position, v2.position); + _vector3.cross(_vector4); + _face.normalModel.copy(_vector3); + _face.normalModel.applyMatrix3(normalMatrix).normalize(); + + for (var i = 0; i < 3; i++) { + + var normal = _face.vertexNormalsModel[i]; + normal.fromArray(normals, arguments[i] * 3); + normal.applyMatrix3(normalMatrix).normalize(); + + var uv = _face.uvs[i]; + uv.fromArray(uvs, arguments[i] * 2); + + } + + _face.vertexNormalsLength = 3; + + _face.material = material; + + _renderData.elements.push(_face); + + } + + } + + return { + setObject: setObject, + projectVertex: projectVertex, + checkTriangleVisibility: checkTriangleVisibility, + checkBackfaceCulling: checkBackfaceCulling, + pushVertex: pushVertex, + pushNormal: pushNormal, + pushColor: pushColor, + pushUv: pushUv, + pushLine: pushLine, + pushTriangle: pushTriangle + }; + + }; + + var renderList = new RenderList(); + + function projectObject(object) { + + if (object.visible === false) return; + + if (object instanceof THREE.Light) { + + _renderData.lights.push(object); + + } else if (object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Points) { + + if (object.material.visible === false) return; + if (object.frustumCulled === true && _frustum.intersectsObject(object) === false) return; + + addObject(object); + + } else if (object instanceof THREE.Sprite) { + + if (object.material.visible === false) return; + if (object.frustumCulled === true && _frustum.intersectsSprite(object) === false) return; + + addObject(object); + + } + + var children = object.children; + + for (var i = 0, l = children.length; i < l; i++) { + + projectObject(children[i]); + + } + + } + + function addObject(object) { + + _object = getNextObjectInPool(); + _object.id = object.id; + _object.object = object; + + _vector3.setFromMatrixPosition(object.matrixWorld); + _vector3.applyMatrix4(_viewProjectionMatrix); + _object.z = _vector3.z; + _object.renderOrder = object.renderOrder; + + _renderData.objects.push(_object); + + } + + this.projectScene = function (scene, camera, sortObjects, sortElements) { + + _faceCount = 0; + _lineCount = 0; + _spriteCount = 0; + + _renderData.elements.length = 0; + + if (scene.autoUpdate === true) scene.updateMatrixWorld(); + if (camera.parent === null) camera.updateMatrixWorld(); + + _viewMatrix.copy(camera.matrixWorldInverse); + _viewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, _viewMatrix); + + _frustum.setFromMatrix(_viewProjectionMatrix); + + // + + _objectCount = 0; + + _renderData.objects.length = 0; + _renderData.lights.length = 0; + + projectObject(scene); + + if (sortObjects === true) { + + _renderData.objects.sort(painterSort); + + } + + // + + var objects = _renderData.objects; + + for (var o = 0, ol = objects.length; o < ol; o++) { + + var object = objects[o].object; + var geometry = object.geometry; + + renderList.setObject(object); + + _modelMatrix = object.matrixWorld; + + _vertexCount = 0; + + if (object instanceof THREE.Mesh) { + + if (geometry instanceof THREE.BufferGeometry) { + + var material = object.material; + + var isMultiMaterial = Array.isArray(material); + + var attributes = geometry.attributes; + var groups = geometry.groups; + + if (attributes.position === undefined) continue; + + var positions = attributes.position.array; + + for (var i = 0, l = positions.length; i < l; i += 3) { + + renderList.pushVertex(positions[i], positions[i + 1], positions[i + 2]); + + } + + if (attributes.normal !== undefined) { + + var normals = attributes.normal.array; + + for (var i = 0, l = normals.length; i < l; i += 3) { + + renderList.pushNormal(normals[i], normals[i + 1], normals[i + 2]); + + } + + } + + if (attributes.uv !== undefined) { + + var uvs = attributes.uv.array; + + for (var i = 0, l = uvs.length; i < l; i += 2) { + + renderList.pushUv(uvs[i], uvs[i + 1]); + + } + + } + + if (geometry.index !== null) { + + var indices = geometry.index.array; + + if (groups.length > 0) { + + for (var g = 0; g < groups.length; g++) { + + var group = groups[g]; + + material = isMultiMaterial === true + ? object.material[group.materialIndex] + : object.material; + + if (material === undefined) continue; + + for (var i = group.start, l = group.start + group.count; i < l; i += 3) { + + renderList.pushTriangle(indices[i], indices[i + 1], indices[i + 2], material); + + } + + } + + } else { + + for (var i = 0, l = indices.length; i < l; i += 3) { + + renderList.pushTriangle(indices[i], indices[i + 1], indices[i + 2], material); + + } + + } + + } else { + + if (groups.length > 0) { + + for (var g = 0; g < groups.length; g++) { + + var group = groups[g]; + + material = isMultiMaterial === true + ? object.material[group.materialIndex] + : object.material; + + if (material === undefined) continue; + + for (var i = group.start, l = group.start + group.count; i < l; i += 3) { + + renderList.pushTriangle(i, i + 1, i + 2, material); + + } + + } + + } else { + + for (var i = 0, l = positions.length / 3; i < l; i += 3) { + + renderList.pushTriangle(i, i + 1, i + 2, material); + + } + + } + + } + + } else if (geometry instanceof THREE.Geometry) { + + var vertices = geometry.vertices; + var faces = geometry.faces; + var faceVertexUvs = geometry.faceVertexUvs[0]; + + _normalMatrix.getNormalMatrix(_modelMatrix); + + var material = object.material; + + var isMultiMaterial = Array.isArray(material); + + for (var v = 0, vl = vertices.length; v < vl; v++) { + + var vertex = vertices[v]; + + _vector3.copy(vertex); + + if (material.morphTargets === true) { + + var morphTargets = geometry.morphTargets; + var morphInfluences = object.morphTargetInfluences; + + for (var t = 0, tl = morphTargets.length; t < tl; t++) { + + var influence = morphInfluences[t]; + + if (influence === 0) continue; + + var target = morphTargets[t]; + var targetVertex = target.vertices[v]; + + _vector3.x += (targetVertex.x - vertex.x) * influence; + _vector3.y += (targetVertex.y - vertex.y) * influence; + _vector3.z += (targetVertex.z - vertex.z) * influence; + + } + + } + + renderList.pushVertex(_vector3.x, _vector3.y, _vector3.z); + + } + + for (var f = 0, fl = faces.length; f < fl; f++) { + + var face = faces[f]; + + material = isMultiMaterial === true + ? object.material[face.materialIndex] + : object.material; + + if (material === undefined) continue; + + var side = material.side; + + var v1 = _vertexPool[face.a]; + var v2 = _vertexPool[face.b]; + var v3 = _vertexPool[face.c]; + + if (renderList.checkTriangleVisibility(v1, v2, v3) === false) continue; + + var visible = renderList.checkBackfaceCulling(v1, v2, v3); + + if (side !== THREE.DoubleSide) { + + if (side === THREE.FrontSide && visible === false) continue; + if (side === THREE.BackSide && visible === true) continue; + + } + + _face = getNextFaceInPool(); + + _face.id = object.id; + _face.v1.copy(v1); + _face.v2.copy(v2); + _face.v3.copy(v3); + + _face.normalModel.copy(face.normal); + + if (visible === false && (side === THREE.BackSide || side === THREE.DoubleSide)) { + + _face.normalModel.negate(); + + } + + _face.normalModel.applyMatrix3(_normalMatrix).normalize(); + + var faceVertexNormals = face.vertexNormals; + + for (var n = 0, nl = Math.min(faceVertexNormals.length, 3); n < nl; n++) { + + var normalModel = _face.vertexNormalsModel[n]; + normalModel.copy(faceVertexNormals[n]); + + if (visible === false && (side === THREE.BackSide || side === THREE.DoubleSide)) { + + normalModel.negate(); + + } + + normalModel.applyMatrix3(_normalMatrix).normalize(); + + } + + _face.vertexNormalsLength = faceVertexNormals.length; + + var vertexUvs = faceVertexUvs[f]; + + if (vertexUvs !== undefined) { + + for (var u = 0; u < 3; u++) { + + _face.uvs[u].copy(vertexUvs[u]); + + } + + } + + _face.color = face.color; + _face.material = material; + + _face.z = (v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z) / 3; + _face.renderOrder = object.renderOrder; + + _renderData.elements.push(_face); + + } + + } + + } else if (object instanceof THREE.Line) { + + _modelViewProjectionMatrix.multiplyMatrices(_viewProjectionMatrix, _modelMatrix); + + if (geometry instanceof THREE.BufferGeometry) { + + var attributes = geometry.attributes; + + if (attributes.position !== undefined) { + + var positions = attributes.position.array; + + for (var i = 0, l = positions.length; i < l; i += 3) { + + renderList.pushVertex(positions[i], positions[i + 1], positions[i + 2]); + + } + + if (attributes.color !== undefined) { + + var colors = attributes.color.array; + + for (var i = 0, l = colors.length; i < l; i += 3) { + + renderList.pushColor(colors[i], colors[i + 1], colors[i + 2]); + + } + + } + + if (geometry.index !== null) { + + var indices = geometry.index.array; + + for (var i = 0, l = indices.length; i < l; i += 2) { + + renderList.pushLine(indices[i], indices[i + 1]); + + } + + } else { + + var step = object instanceof THREE.LineSegments ? 2 : 1; + + for (var i = 0, l = (positions.length / 3) - 1; i < l; i += step) { + + renderList.pushLine(i, i + 1); + + } + + } + + } + + } else if (geometry instanceof THREE.Geometry) { + + var vertices = object.geometry.vertices; + + if (vertices.length === 0) continue; + + v1 = getNextVertexInPool(); + v1.positionScreen.copy(vertices[0]).applyMatrix4(_modelViewProjectionMatrix); + + var step = object instanceof THREE.LineSegments ? 2 : 1; + + for (var v = 1, vl = vertices.length; v < vl; v++) { + + v1 = getNextVertexInPool(); + v1.positionScreen.copy(vertices[v]).applyMatrix4(_modelViewProjectionMatrix); + + if ((v + 1) % step > 0) continue; + + v2 = _vertexPool[_vertexCount - 2]; + + _clippedVertex1PositionScreen.copy(v1.positionScreen); + _clippedVertex2PositionScreen.copy(v2.positionScreen); + + if (clipLine(_clippedVertex1PositionScreen, _clippedVertex2PositionScreen) === true) { + + // Perform the perspective divide + _clippedVertex1PositionScreen.multiplyScalar(1 / _clippedVertex1PositionScreen.w); + _clippedVertex2PositionScreen.multiplyScalar(1 / _clippedVertex2PositionScreen.w); + + _line = getNextLineInPool(); + + _line.id = object.id; + _line.v1.positionScreen.copy(_clippedVertex1PositionScreen); + _line.v2.positionScreen.copy(_clippedVertex2PositionScreen); + + _line.z = Math.max(_clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z); + _line.renderOrder = object.renderOrder; + + _line.material = object.material; + + if (object.material.vertexColors === THREE.VertexColors) { + + _line.vertexColors[0].copy(object.geometry.colors[v]); + _line.vertexColors[1].copy(object.geometry.colors[v - 1]); + + } + + _renderData.elements.push(_line); + + } + + } + + } + + } else if (object instanceof THREE.Points) { + + _modelViewProjectionMatrix.multiplyMatrices(_viewProjectionMatrix, _modelMatrix); + + if (geometry instanceof THREE.Geometry) { + + var vertices = object.geometry.vertices; + + for (var v = 0, vl = vertices.length; v < vl; v++) { + + var vertex = vertices[v]; + + _vector4.set(vertex.x, vertex.y, vertex.z, 1); + _vector4.applyMatrix4(_modelViewProjectionMatrix); + + pushPoint(_vector4, object, camera); + + } + + } else if (geometry instanceof THREE.BufferGeometry) { + + var attributes = geometry.attributes; + + if (attributes.position !== undefined) { + + var positions = attributes.position.array; + + for (var i = 0, l = positions.length; i < l; i += 3) { + + _vector4.set(positions[i], positions[i + 1], positions[i + 2], 1); + _vector4.applyMatrix4(_modelViewProjectionMatrix); + + pushPoint(_vector4, object, camera); + + } + + } + + } + + } else if (object instanceof THREE.Sprite) { + + _vector4.set(_modelMatrix.elements[12], _modelMatrix.elements[13], _modelMatrix.elements[14], 1); + _vector4.applyMatrix4(_viewProjectionMatrix); + + pushPoint(_vector4, object, camera); + + } + + } + + if (sortElements === true) { + + _renderData.elements.sort(painterSort); + + } + + return _renderData; + + }; + + function pushPoint(_vector4, object, camera) { + + var invW = 1 / _vector4.w; + + _vector4.z *= invW; + + if (_vector4.z >= -1 && _vector4.z <= 1) { + + _sprite = getNextSpriteInPool(); + _sprite.id = object.id; + _sprite.x = _vector4.x * invW; + _sprite.y = _vector4.y * invW; + _sprite.z = _vector4.z; + _sprite.renderOrder = object.renderOrder; + _sprite.object = object; + + _sprite.rotation = object.rotation; + + _sprite.scale.x = object.scale.x * Math.abs(_sprite.x - (_vector4.x + camera.projectionMatrix.elements[0]) / (_vector4.w + camera.projectionMatrix.elements[12])); + _sprite.scale.y = object.scale.y * Math.abs(_sprite.y - (_vector4.y + camera.projectionMatrix.elements[5]) / (_vector4.w + camera.projectionMatrix.elements[13])); + + _sprite.material = object.material; + + _renderData.elements.push(_sprite); + + } + + } + + // Pools + + function getNextObjectInPool() { + + if (_objectCount === _objectPoolLength) { + + var object = new THREE.RenderableObject(); + _objectPool.push(object); + _objectPoolLength++; + _objectCount++; + return object; + + } + + return _objectPool[_objectCount++]; + + } + + function getNextVertexInPool() { + + if (_vertexCount === _vertexPoolLength) { + + var vertex = new THREE.RenderableVertex(); + _vertexPool.push(vertex); + _vertexPoolLength++; + _vertexCount++; + return vertex; + + } + + return _vertexPool[_vertexCount++]; + + } + + function getNextFaceInPool() { + + if (_faceCount === _facePoolLength) { + + var face = new THREE.RenderableFace(); + _facePool.push(face); + _facePoolLength++; + _faceCount++; + return face; + + } + + return _facePool[_faceCount++]; + + + } + + function getNextLineInPool() { + + if (_lineCount === _linePoolLength) { + + var line = new THREE.RenderableLine(); + _linePool.push(line); + _linePoolLength++; + _lineCount++; + return line; + + } + + return _linePool[_lineCount++]; + + } + + function getNextSpriteInPool() { + + if (_spriteCount === _spritePoolLength) { + + var sprite = new THREE.RenderableSprite(); + _spritePool.push(sprite); + _spritePoolLength++; + _spriteCount++; + return sprite; + + } + + return _spritePool[_spriteCount++]; + + } + + // + + function painterSort(a, b) { + + if (a.renderOrder !== b.renderOrder) { + + return a.renderOrder - b.renderOrder; + + } else if (a.z !== b.z) { + + return b.z - a.z; + + } else if (a.id !== b.id) { + + return a.id - b.id; + + } else { + + return 0; + + } + + } + + function clipLine(s1, s2) { + + var alpha1 = 0, alpha2 = 1, + + // Calculate the boundary coordinate of each vertex for the near and far clip planes, + // Z = -1 and Z = +1, respectively. + + bc1near = s1.z + s1.w, + bc2near = s2.z + s2.w, + bc1far = -s1.z + s1.w, + bc2far = -s2.z + s2.w; + + if (bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0) { + + // Both vertices lie entirely within all clip planes. + return true; + + } else if ((bc1near < 0 && bc2near < 0) || (bc1far < 0 && bc2far < 0)) { + + // Both vertices lie entirely outside one of the clip planes. + return false; + + } else { + + // The line segment spans at least one clip plane. + + if (bc1near < 0) { + + // v1 lies outside the near plane, v2 inside + alpha1 = Math.max(alpha1, bc1near / (bc1near - bc2near)); + + } else if (bc2near < 0) { + + // v2 lies outside the near plane, v1 inside + alpha2 = Math.min(alpha2, bc1near / (bc1near - bc2near)); + + } + + if (bc1far < 0) { + + // v1 lies outside the far plane, v2 inside + alpha1 = Math.max(alpha1, bc1far / (bc1far - bc2far)); + + } else if (bc2far < 0) { + + // v2 lies outside the far plane, v2 inside + alpha2 = Math.min(alpha2, bc1far / (bc1far - bc2far)); + + } + + if (alpha2 < alpha1) { + + // The line segment spans two boundaries, but is outside both of them. + // (This can't happen when we're only clipping against just near/far but good + // to leave the check here for future usage if other clip planes are added.) + return false; + + } else { + + // Update the s1 and s2 vertices to match the clipped line segment. + s1.lerp(s2, alpha1); + s2.lerp(s1, 1 - alpha2); + + return true; + + } + + } + + } + +}; -- 2.39.5