From fda91877f9296cab63657410dd02a7aa154ae541 Mon Sep 17 00:00:00 2001 From: xeolabs Date: Mon, 4 Aug 2014 13:52:13 +0200 Subject: [PATCH] V4.0 Depth targets Color targets Multipass --- src/core/config.js | 2 +- src/core/display/chunks/cameraChunk.js | 20 +- src/core/display/chunks/clipsChunk.js | 6 +- src/core/display/chunks/colorbufChunk.js | 8 +- src/core/display/chunks/cubemapChunk.js | 8 +- src/core/display/chunks/depthTargetChunk.js | 35 ++ src/core/display/chunks/depthbufChunk.js | 14 +- src/core/display/chunks/drawChunk.js | 2 +- src/core/display/chunks/flagsChunk.js | 49 ++- src/core/display/chunks/framebufChunk.js | 38 -- src/core/display/chunks/geometryChunk.js | 14 +- src/core/display/chunks/lightsChunk.js | 4 +- src/core/display/chunks/listenersChunk.js | 4 +- src/core/display/chunks/lookAtChunk.js | 8 +- src/core/display/chunks/materialChunk.js | 2 +- src/core/display/chunks/nameChunk.js | 10 +- src/core/display/chunks/programChunk.js | 3 + src/core/display/chunks/renderTargetChunk.js | 46 +++ src/core/display/chunks/rendererChunk.js | 8 +- src/core/display/chunks/shaderChunk.js | 4 +- src/core/display/chunks/shaderParamsChunk.js | 4 +- src/core/display/chunks/styleChunk.js | 6 +- src/core/display/chunks/textureChunk.js | 8 +- src/core/display/chunks/viewChunk.js | 6 +- src/core/display/chunks/xformChunk.js | 8 +- src/core/display/display.js | 396 ++++++------------- src/core/display/programSourceFactory.js | 106 +++-- src/core/engine.js | 3 +- src/core/scene/colorTarget.js | 65 +++ src/core/scene/depthTarget.js | 65 +++ src/core/scene/framebuf.js | 62 --- src/core/scene/geometry.js | 37 +- src/core/scene/pass.js | 83 ++++ src/core/scene/shader.js | 4 +- src/core/scene/texture.js | 16 +- src/core/scene/textureMap.js | 35 +- src/core/scenejs.js | 23 ++ 37 files changed, 688 insertions(+), 524 deletions(-) create mode 100644 src/core/display/chunks/depthTargetChunk.js delete mode 100644 src/core/display/chunks/framebufChunk.js create mode 100644 src/core/display/chunks/renderTargetChunk.js create mode 100644 src/core/scene/colorTarget.js create mode 100644 src/core/scene/depthTarget.js delete mode 100644 src/core/scene/framebuf.js create mode 100644 src/core/scene/pass.js diff --git a/src/core/config.js b/src/core/config.js index af846352..47c745a2 100644 --- a/src/core/config.js +++ b/src/core/config.js @@ -58,7 +58,7 @@ var SceneJS_configsModule = new (function () { for (var i = 0; cfg && i < parts.length; i++) { cfg = cfg[parts[i]]; } - return cfg || {}; + return (cfg != undefined) ? cfg : {}; } }; diff --git a/src/core/display/chunks/cameraChunk.js b/src/core/display/chunks/cameraChunk.js index 5866b4f9..a0efe9e0 100644 --- a/src/core/display/chunks/cameraChunk.js +++ b/src/core/display/chunks/cameraChunk.js @@ -5,13 +5,15 @@ SceneJS_ChunkFactory.createChunkType({ build : function() { this._uPMatrixDraw = this.program.draw.getUniformLocation("SCENEJS_uPMatrix"); + this._uZNearDraw = this.program.draw.getUniformLocation("SCENEJS_uZNear"); + this._uZFarDraw = this.program.draw.getUniformLocation("SCENEJS_uZFar"); this._uPMatrixPick = this.program.pick.getUniformLocation("SCENEJS_uPMatrix"); this._uZNearPick = this.program.pick.getUniformLocation("SCENEJS_uZNear"); this._uZFarPick = this.program.pick.getUniformLocation("SCENEJS_uZFar"); }, - draw : function(ctx) { + draw : function(frameCtx) { if (this.core.dirty) { this.core.rebuild(); @@ -23,11 +25,19 @@ SceneJS_ChunkFactory.createChunkType({ gl.uniformMatrix4fv(this._uPMatrixDraw, gl.FALSE, this.core.mat); } - ctx.cameraMat = this.core.mat; // Query only in draw pass + if (this._uZNearDraw) { + gl.uniform1f(this._uZNearDraw, this.core.optics.near); + } + + if (this._uZFarDraw) { + gl.uniform1f(this._uZFarDraw, this.core.optics.far); + } + + frameCtx.cameraMat = this.core.mat; // Query only in draw pass }, - pick : function(ctx) { + pick : function(frameCtx) { var gl = this.program.gl; @@ -35,7 +45,7 @@ SceneJS_ChunkFactory.createChunkType({ gl.uniformMatrix4fv(this._uPMatrixPick, gl.FALSE, this.core.mat); } - if (ctx.rayPick) { // Z-pick pass: feed near and far clip planes into shader + if (frameCtx.rayPick) { // Z-pick pass: feed near and far clip planes into shader if (this._uZNearPick) { gl.uniform1f(this._uZNearPick, this.core.optics.near); @@ -46,6 +56,6 @@ SceneJS_ChunkFactory.createChunkType({ } } - ctx.cameraMat = this.core.mat; // Query only in draw pass + frameCtx.cameraMat = this.core.mat; // Query only in draw pass } }); \ No newline at end of file diff --git a/src/core/display/chunks/clipsChunk.js b/src/core/display/chunks/clipsChunk.js index ef9c3772..7cd8c084 100644 --- a/src/core/display/chunks/clipsChunk.js +++ b/src/core/display/chunks/clipsChunk.js @@ -30,9 +30,9 @@ SceneJS_ChunkFactory.createChunkType({ } }, - drawAndPick: function(ctx) { + drawAndPick: function(frameCtx) { - var vars = (ctx.pick) ? this._pick : this._draw; + var vars = (frameCtx.pick) ? this._pick : this._draw; var mode; var normalAndDist; @@ -42,7 +42,7 @@ SceneJS_ChunkFactory.createChunkType({ for (var i = 0, len = clips.length; i < len; i++) { - if (ctx.pick) { + if (frameCtx.pick) { mode = vars[i].uClipMode; normalAndDist = vars[i].uClipNormalAndDist; } else { diff --git a/src/core/display/chunks/colorbufChunk.js b/src/core/display/chunks/colorbufChunk.js index 7c7af11b..b39597c3 100644 --- a/src/core/display/chunks/colorbufChunk.js +++ b/src/core/display/chunks/colorbufChunk.js @@ -11,20 +11,20 @@ SceneJS_ChunkFactory.createChunkType({ build:function () { }, - drawAndPick:function (ctx) { + drawAndPick:function (frameCtx) { - if (!ctx.transparencyPass) { // Blending forced when rendering transparent bin + if (!frameCtx.transparent) { // Blending forced when rendering transparent bin var blendEnabled = this.core.blendEnabled; - if (ctx.blendEnabled != blendEnabled) { + if (frameCtx.blendEnabled != blendEnabled) { var gl = this.program.gl; if (blendEnabled) { gl.enable(gl.BLEND); } else { gl.disable(gl.BLEND); } - ctx.blendEnabled = blendEnabled; + frameCtx.blendEnabled = blendEnabled; } } } diff --git a/src/core/display/chunks/cubemapChunk.js b/src/core/display/chunks/cubemapChunk.js index c2148e4b..93c54f8b 100644 --- a/src/core/display/chunks/cubemapChunk.js +++ b/src/core/display/chunks/cubemapChunk.js @@ -17,7 +17,7 @@ SceneJS_ChunkFactory.createChunkType({ } }, - draw: function (ctx) { + draw: function (frameCtx) { var layers = this.core.layers; if (layers) { var layer; @@ -25,7 +25,7 @@ SceneJS_ChunkFactory.createChunkType({ for (var i = 0, len = layers.length; i < len; i++) { layer = layers[i]; if (this._uCubeMapSampler[i] && layer.texture) { - draw.bindTexture(this._uCubeMapSampler[i], layer.texture, ctx.textureUnit++); + draw.bindTexture(this._uCubeMapSampler[i], layer.texture, frameCtx.textureUnit++); if (this._uCubeMapIntensity[i]) { this._uCubeMapIntensity[i].setValue(layer.intensity); } @@ -33,8 +33,8 @@ SceneJS_ChunkFactory.createChunkType({ } } - if (ctx.textureUnit > 10) { // TODO: Find how many textures allowed - ctx.textureUnit = 0; + if (frameCtx.textureUnit > 10) { // TODO: Find how many textures allowed + frameCtx.textureUnit = 0; } } }); \ No newline at end of file diff --git a/src/core/display/chunks/depthTargetChunk.js b/src/core/display/chunks/depthTargetChunk.js new file mode 100644 index 00000000..7995c0f2 --- /dev/null +++ b/src/core/display/chunks/depthTargetChunk.js @@ -0,0 +1,35 @@ +/** + * Create display state chunk type for draw and pick render of depthTarget + */ +SceneJS_ChunkFactory.createChunkType({ + + type: "depthTarget", + + // Avoid reapplication of this chunk type after a program switch. + programGlobal: true, + + build: function () { + }, + + drawAndPick: function (frameCtx) { + + // Flush and unbind last render buffer + if (frameCtx.depthRenderBuf) { + var gl = this.program.gl; + gl.finish(); + frameCtx.depthRenderBuf.unbind(); + frameCtx.depthRenderBuf = null; + frameCtx.depthPass = false; + } + + // Bind this chunk's render buffer, if any + var renderBuf = this.core.renderBuf; + if (renderBuf) { + renderBuf.bind(); + var gl = this.program.gl; + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + frameCtx.depthRenderBuf = renderBuf; + frameCtx.depthPass = true; + } + } +}); \ No newline at end of file diff --git a/src/core/display/chunks/depthbufChunk.js b/src/core/display/chunks/depthbufChunk.js index b5e3bba7..83bbdf04 100644 --- a/src/core/display/chunks/depthbufChunk.js +++ b/src/core/display/chunks/depthbufChunk.js @@ -8,32 +8,32 @@ SceneJS_ChunkFactory.createChunkType({ // Avoid reapplication of a chunk after a program switch. programGlobal:true, - drawAndPick:function (ctx) { + drawAndPick:function (frameCtx) { var enabled = this.core.enabled; - if (ctx.depthbufEnabled != enabled) { + if (frameCtx.depthbufEnabled != enabled) { var gl = this.program.gl; if (enabled) { gl.enable(gl.DEPTH_TEST); } else { gl.disable(gl.DEPTH_TEST); } - ctx.depthbufEnabled = enabled; + frameCtx.depthbufEnabled = enabled; } var clearDepth = this.core.clearDepth; - if (ctx.clearDepth != clearDepth) { + if (frameCtx.clearDepth != clearDepth) { gl.clearDepth(clearDepth); - ctx.clearDepth = clearDepth; + frameCtx.clearDepth = clearDepth; } var depthFunc = this.core.depthFunc; - if (ctx.depthFunc != depthFunc) { + if (frameCtx.depthFunc != depthFunc) { gl.depthFunc(depthFunc); - ctx.depthFunc = depthFunc; + frameCtx.depthFunc = depthFunc; } } }); diff --git a/src/core/display/chunks/drawChunk.js b/src/core/display/chunks/drawChunk.js index a10598ee..6e1d900e 100644 --- a/src/core/display/chunks/drawChunk.js +++ b/src/core/display/chunks/drawChunk.js @@ -16,7 +16,7 @@ SceneJS_ChunkFactory.createChunkType({ build:function () {}, - drawAndPick:function (ctx) { + drawAndPick:function (frameCtx) { var gl = this.program.gl; diff --git a/src/core/display/chunks/flagsChunk.js b/src/core/display/chunks/flagsChunk.js index dbfbaaf3..33268632 100644 --- a/src/core/display/chunks/flagsChunk.js +++ b/src/core/display/chunks/flagsChunk.js @@ -3,9 +3,9 @@ */ SceneJS_ChunkFactory.createChunkType({ - type:"flags", + type: "flags", - build:function () { + build: function () { var draw = this.program.draw; @@ -22,43 +22,64 @@ SceneJS_ChunkFactory.createChunkType({ this._uClippingPick = pick.getUniformLocation("SCENEJS_uClipping"); }, - drawAndPick:function (ctx) { + drawAndPick: function (frameCtx) { var gl = this.program.gl; var backfaces = this.core.backfaces; - if (ctx.backfaces != backfaces) { + if (frameCtx.backfaces != backfaces) { if (backfaces) { gl.disable(gl.CULL_FACE); } else { gl.enable(gl.CULL_FACE); } - ctx.backfaces = backfaces; + frameCtx.backfaces = backfaces; } var frontface = this.core.frontface; - if (ctx.frontface != frontface) { + if (frameCtx.frontface != frontface) { if (frontface == "ccw") { gl.frontFace(gl.CCW); } else { gl.frontFace(gl.CW); } - ctx.frontface = frontface; + frameCtx.frontface = frontface; } - if (ctx.pick) { + var transparent = this.core.transparent; + + if (frameCtx.transparent != transparent) { + if (transparent) { + + // Entering a transparency bin + + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + frameCtx.blendEnabled = true; + + } else { + + // Leaving a transparency bin + + gl.disable(gl.BLEND); + frameCtx.blendEnabled = false; + } + frameCtx.transparent = transparent; + } + + if (frameCtx.pick) { gl.uniform1i(this._uClippingPick, this.core.clipping); } else { var drawUniforms = (this.core.backfaceTexturing ? 1 : 0) + - (this.core.backfaceLighting ? 2 : 0) + - (this.core.specular ? 4 : 0) + - (this.core.clipping ? 8 : 0) + - (this.core.ambient ? 16 : 0) + - (this.core.diffuse ? 32 : 0) + - (this.core.reflection ? 64 : 0); + (this.core.backfaceLighting ? 2 : 0) + + (this.core.specular ? 4 : 0) + + (this.core.clipping ? 8 : 0) + + (this.core.ambient ? 16 : 0) + + (this.core.diffuse ? 32 : 0) + + (this.core.reflection ? 64 : 0); if (this.program.drawUniformFlags != drawUniforms) { gl.uniform1i(this._uBackfaceTexturingDraw, this.core.backfaceTexturing); gl.uniform1i(this._uBackfaceLightingDraw, this.core.backfaceLighting); diff --git a/src/core/display/chunks/framebufChunk.js b/src/core/display/chunks/framebufChunk.js deleted file mode 100644 index adff7736..00000000 --- a/src/core/display/chunks/framebufChunk.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Create display state chunk type for draw and pick render of framebuf - */ -SceneJS_ChunkFactory.createChunkType({ - - type: "framebuf", - - // Avoid reapplication of a chunk after a program switch. - programGlobal:true, - - build: function() { - }, - - drawAndPick: function(ctx) { - - var gl = this.program.gl; - - if (ctx.framebuf) { - - gl.finish(); // Force framebuf to complete - - ctx.framebuf.unbind(); - - ctx.framebuf = null; - } - - var framebuf = this.core.framebuf; - - if (framebuf) { - - framebuf.bind(); - - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); - - ctx.framebuf = framebuf; // Must flush on cleanup - } - } -}); \ No newline at end of file diff --git a/src/core/display/chunks/geometryChunk.js b/src/core/display/chunks/geometryChunk.js index ce34e339..d459150b 100644 --- a/src/core/display/chunks/geometryChunk.js +++ b/src/core/display/chunks/geometryChunk.js @@ -89,12 +89,12 @@ SceneJS_ChunkFactory.createChunkType({ }, - draw:function (ctx) { + draw:function (frameCtx) { var doMorph = this.core.targets && this.core.targets.length; var cleanInterleavedBuf = this.core2.interleavedBuf && !this.core2.interleavedBuf.dirty; if (this.VAO) { - ctx.VAO.bindVertexArrayOES(this.VAO); + frameCtx.VAO.bindVertexArrayOES(this.VAO); if (doMorph) { if (this.VAOMorphKey1 == this.core.key1 && this.VAOMorphKey2 == this.core.key2) { this.setDrawMorphFactor(); @@ -103,11 +103,11 @@ SceneJS_ChunkFactory.createChunkType({ } else if (cleanInterleavedBuf || !this.VAOHasInterleavedBuf) { return; } - } else if (ctx.VAO) { + } else if (frameCtx.VAO) { // Start creating a new VAO by switching to the default VAO, which doesn't have attribs enabled. - ctx.VAO.bindVertexArrayOES(null); - this.VAO = ctx.VAO.createVertexArrayOES(); - ctx.VAO.bindVertexArrayOES(this.VAO); + frameCtx.VAO.bindVertexArrayOES(null); + this.VAO = frameCtx.VAO.createVertexArrayOES(); + frameCtx.VAO.bindVertexArrayOES(this.VAO); var gl = this.program.gl; } @@ -193,7 +193,7 @@ SceneJS_ChunkFactory.createChunkType({ }, - pick:function (ctx) { + pick:function (frameCtx) { if (this.core.targets && this.core.targets.length) { this.morphPick(); diff --git a/src/core/display/chunks/lightsChunk.js b/src/core/display/chunks/lightsChunk.js index c2a5fa6a..f7851aea 100644 --- a/src/core/display/chunks/lightsChunk.js +++ b/src/core/display/chunks/lightsChunk.js @@ -42,9 +42,9 @@ SceneJS_ChunkFactory.createChunkType({ } }, - draw:function (ctx) { + draw:function (frameCtx) { - if (ctx.dirty) { + if (frameCtx.dirty) { this.build(); } diff --git a/src/core/display/chunks/listenersChunk.js b/src/core/display/chunks/listenersChunk.js index 522eeebe..cb2707ae 100644 --- a/src/core/display/chunks/listenersChunk.js +++ b/src/core/display/chunks/listenersChunk.js @@ -11,10 +11,10 @@ SceneJS_ChunkFactory.createChunkType({ build : function() { }, - draw : function(ctx) { + draw : function(frameCtx) { var listeners = this.core.listeners; - var renderListenerCtx = ctx.renderListenerCtx; + var renderListenerCtx = frameCtx.renderListenerCtx; for (var i = listeners.length - 1; i >= 0; i--) { // Child listeners first if (listeners[i](renderListenerCtx) === true) { // Call listener with query facade object as scope diff --git a/src/core/display/chunks/lookAtChunk.js b/src/core/display/chunks/lookAtChunk.js index fdaa412e..91c13adf 100644 --- a/src/core/display/chunks/lookAtChunk.js +++ b/src/core/display/chunks/lookAtChunk.js @@ -14,7 +14,7 @@ SceneJS_ChunkFactory.createChunkType({ this._uvMatrixPick = this.program.pick.getUniformLocation("SCENEJS_uVMatrix"); }, - draw : function(ctx) { + draw : function(frameCtx) { if (this.core.dirty) { this.core.rebuild(); @@ -34,10 +34,10 @@ SceneJS_ChunkFactory.createChunkType({ gl.uniform3fv(this._uWorldEyeDraw, this.core.lookAt.eye); } - ctx.viewMat = this.core.mat; + frameCtx.viewMat = this.core.mat; }, - pick : function(ctx) { + pick : function(frameCtx) { var gl = this.program.gl; @@ -45,6 +45,6 @@ SceneJS_ChunkFactory.createChunkType({ gl.uniformMatrix4fv(this._uvMatrixPick, gl.FALSE, this.core.mat); } - ctx.viewMat = this.core.mat; + frameCtx.viewMat = this.core.mat; } }); \ No newline at end of file diff --git a/src/core/display/chunks/materialChunk.js b/src/core/display/chunks/materialChunk.js index 5b436512..791e7f0d 100644 --- a/src/core/display/chunks/materialChunk.js +++ b/src/core/display/chunks/materialChunk.js @@ -9,7 +9,7 @@ SceneJS_ChunkFactory.createChunkType({ var draw = this.program.draw; - this._uMaterialBaseColor = draw.getUniformLocation("SCENEJS_uMaterialBaseColor"); + this._uMaterialBaseColor = draw.getUniformLocation("SCENEJS_uMaterialColor"); this._uMaterialSpecularColor = draw.getUniformLocation("SCENEJS_uMaterialSpecularColor"); this._uMaterialSpecular = draw.getUniformLocation("SCENEJS_uMaterialSpecular"); this._uMaterialShine = draw.getUniformLocation("SCENEJS_uMaterialShine"); diff --git a/src/core/display/chunks/nameChunk.js b/src/core/display/chunks/nameChunk.js index 0cf666e4..a3bd7b83 100644 --- a/src/core/display/chunks/nameChunk.js +++ b/src/core/display/chunks/nameChunk.js @@ -9,15 +9,15 @@ SceneJS_ChunkFactory.createChunkType({ this._uPickColor = this.program.pick.getUniformLocation("SCENEJS_uPickColor"); }, - pick : function(ctx) { + pick : function(frameCtx) { if (this._uPickColor && this.core.name) { - ctx.pickNames[ctx.pickIndex++] = this.core; + frameCtx.pickNames[frameCtx.pickIndex++] = this.core; - var b = ctx.pickIndex >> 16 & 0xFF; - var g = ctx.pickIndex >> 8 & 0xFF; - var r = ctx.pickIndex & 0xFF; + var b = frameCtx.pickIndex >> 16 & 0xFF; + var g = frameCtx.pickIndex >> 8 & 0xFF; + var r = frameCtx.pickIndex & 0xFF; this.program.gl.uniform3fv(this._uPickColor, [r / 255, g / 255, b / 255]); } diff --git a/src/core/display/chunks/programChunk.js b/src/core/display/chunks/programChunk.js index 359ddec3..0e93c08c 100644 --- a/src/core/display/chunks/programChunk.js +++ b/src/core/display/chunks/programChunk.js @@ -3,6 +3,7 @@ SceneJS_ChunkFactory.createChunkType({ type: "program", build : function() { + this._depthMode = this.program.draw.getUniformLocation("SCENEJS_uDepthMode"); this._rayPickMode = this.program.pick.getUniformLocation("SCENEJS_uRayPickMode"); }, @@ -16,6 +17,8 @@ SceneJS_ChunkFactory.createChunkType({ var gl = this.program.gl; + gl.uniform1i(this._depthMode, frameCtx.depthMode); + if (!frameCtx.VAO) { for (var i = 0; i < 10; i++) { gl.disableVertexAttribArray(i); diff --git a/src/core/display/chunks/renderTargetChunk.js b/src/core/display/chunks/renderTargetChunk.js new file mode 100644 index 00000000..5958fe1a --- /dev/null +++ b/src/core/display/chunks/renderTargetChunk.js @@ -0,0 +1,46 @@ +/** + * Create display state chunk type for draw and pick render of renderTarget + */ +SceneJS_ChunkFactory.createChunkType({ + + type: "renderTarget", + + // Avoid reapplication of this chunk type after a program switch. + programGlobal: true, + + build: function () { + this._uDepthMode = this.program.draw.getUniformLocation("SCENEJS_uDepthMode"); + }, + + drawAndPick: function (frameCtx) { + + // Flush and unbind last render buffer + if (frameCtx.renderBuf) { + var gl = this.program.gl; + gl.finish(); + frameCtx.renderBuf.unbind(); + frameCtx.renderBuf = null; + } + + // Bind this chunk's render buffer, if any + + var target = frameCtx.targetList[frameCtx.targetIndex++]; + if (target) { + var renderBuf = target.renderBuf; + if (renderBuf) { + frameCtx.depthMode = (target.bufType == "depth"); + var gl = this.program.gl; + gl.uniform1i(this._uDepthMode, frameCtx.depthMode); + renderBuf.bind(); + if (!frameCtx.depthMode) { + if (frameCtx.blendEnabled) { // Enable blending + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + } + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + frameCtx.renderBuf = renderBuf; + } + } + } +}); \ No newline at end of file diff --git a/src/core/display/chunks/rendererChunk.js b/src/core/display/chunks/rendererChunk.js index 09b4575f..ea8218f8 100644 --- a/src/core/display/chunks/rendererChunk.js +++ b/src/core/display/chunks/rendererChunk.js @@ -8,15 +8,15 @@ SceneJS_ChunkFactory.createChunkType({ build : function() { }, - drawAndPick : function(ctx) { + drawAndPick : function(frameCtx) { if (this.core.props) { var gl = this.program.gl; - if (ctx.renderer) { - ctx.renderer.props.restoreProps(gl); - ctx.renderer = this.core; + if (frameCtx.renderer) { + frameCtx.renderer.props.restoreProps(gl); + frameCtx.renderer = this.core; } this.core.props.setProps(gl); diff --git a/src/core/display/chunks/shaderChunk.js b/src/core/display/chunks/shaderChunk.js index a2c962a4..bae3d6fa 100644 --- a/src/core/display/chunks/shaderChunk.js +++ b/src/core/display/chunks/shaderChunk.js @@ -8,13 +8,13 @@ SceneJS_ChunkFactory.createChunkType({ build : function() { }, - drawAndPick : function(ctx) { + drawAndPick : function(frameCtx) { var paramsStack = this.core.paramsStack; if (paramsStack) { - var program = ctx.pick ? this.program.pick : this.program.draw; + var program = frameCtx.pick ? this.program.pick : this.program.draw; var params; var name; diff --git a/src/core/display/chunks/shaderParamsChunk.js b/src/core/display/chunks/shaderParamsChunk.js index 40cbc876..f242901f 100644 --- a/src/core/display/chunks/shaderParamsChunk.js +++ b/src/core/display/chunks/shaderParamsChunk.js @@ -8,13 +8,13 @@ SceneJS_ChunkFactory.createChunkType({ build : function() { }, - drawAndPick: function(ctx) { + drawAndPick: function(frameCtx) { var paramsStack = this.core.paramsStack; if (paramsStack) { - var program = ctx.pick ? this.program.pick : this.program.draw; + var program = frameCtx.pick ? this.program.pick : this.program.draw; var params; var name; diff --git a/src/core/display/chunks/styleChunk.js b/src/core/display/chunks/styleChunk.js index ff2cca86..44ba78aa 100644 --- a/src/core/display/chunks/styleChunk.js +++ b/src/core/display/chunks/styleChunk.js @@ -8,14 +8,14 @@ SceneJS_ChunkFactory.createChunkType({ // Avoid reapplication of a chunk after a program switch. programGlobal:true, - drawAndPick:function (ctx) { + drawAndPick:function (frameCtx) { var lineWidth = this.core.lineWidth; - if (ctx.lineWidth != lineWidth) { + if (frameCtx.lineWidth != lineWidth) { var gl = this.program.gl; gl.lineWidth(lineWidth); - ctx.lineWidth = lineWidth; + frameCtx.lineWidth = lineWidth; } } }); diff --git a/src/core/display/chunks/textureChunk.js b/src/core/display/chunks/textureChunk.js index c534b355..29b76cf4 100644 --- a/src/core/display/chunks/textureChunk.js +++ b/src/core/display/chunks/textureChunk.js @@ -32,7 +32,7 @@ SceneJS_ChunkFactory.createChunkType({ } }, - draw : function(ctx) { + draw : function(frameCtx) { var layers = this.core.layers; @@ -47,7 +47,7 @@ SceneJS_ChunkFactory.createChunkType({ if (this._uTexSampler[i] && layer.texture) { // Lazy-loads - draw.bindTexture(this._uTexSampler[i], layer.texture, ctx.textureUnit++); + draw.bindTexture(this._uTexSampler[i], layer.texture, frameCtx.textureUnit++); if (layer._matrixDirty && layer.buildMatrix) { layer.buildMatrix.call(layer); @@ -67,8 +67,8 @@ SceneJS_ChunkFactory.createChunkType({ } } - if (ctx.textureUnit > 10) { // TODO: Find how many textures allowed - ctx.textureUnit = 0; + if (frameCtx.textureUnit > 10) { // TODO: Find how many textures allowed + frameCtx.textureUnit = 0; } } }); \ No newline at end of file diff --git a/src/core/display/chunks/viewChunk.js b/src/core/display/chunks/viewChunk.js index 6941704d..7d13c058 100644 --- a/src/core/display/chunks/viewChunk.js +++ b/src/core/display/chunks/viewChunk.js @@ -11,18 +11,18 @@ SceneJS_ChunkFactory.createChunkType({ build:function () { }, - drawAndPick:function (ctx) { + drawAndPick:function (frameCtx) { var scissorTestEnabled = this.core.scissorTestEnabled; - if (ctx.scissorTestEnabled != scissorTestEnabled) { + if (frameCtx.scissorTestEnabled != scissorTestEnabled) { var gl = this.program.gl; if (scissorTestEnabled) { gl.enable(gl.SCISSOR_TEST); } else { gl.disable(gl.SCISSOR_TEST); } - ctx.scissorTestEnabled = scissorTestEnabled; + frameCtx.scissorTestEnabled = scissorTestEnabled; } } }); diff --git a/src/core/display/chunks/xformChunk.js b/src/core/display/chunks/xformChunk.js index 6a0500a9..b62d7e8b 100644 --- a/src/core/display/chunks/xformChunk.js +++ b/src/core/display/chunks/xformChunk.js @@ -15,7 +15,7 @@ SceneJS_ChunkFactory.createChunkType({ this._uNormalMatLocationPick = pick.getUniformLocation("SCENEJS_uMNMatrix"); }, - draw : function(ctx) { + draw : function(frameCtx) { /* Rebuild core's matrix from matrices at cores on path up to root */ @@ -33,10 +33,10 @@ SceneJS_ChunkFactory.createChunkType({ gl.uniformMatrix4fv(this._uNormalMatLocationDraw, gl.FALSE, this.core.normalMat); } - ctx.modelMat = this.core.mat; + frameCtx.modelMat = this.core.mat; }, - pick : function(ctx) { + pick : function(frameCtx) { /* Rebuild core's matrix from matrices at cores on path up to root */ @@ -54,6 +54,6 @@ SceneJS_ChunkFactory.createChunkType({ gl.uniformMatrix4fv(this._uNormalMatLocationPick, gl.FALSE, this.core.normalMat); } - ctx.modelMat = this.core.mat; + frameCtx.modelMat = this.core.mat; } }); diff --git a/src/core/display/display.js b/src/core/display/display.js index 07c73eb9..87dbad50 100644 --- a/src/core/display/display.js +++ b/src/core/display/display.js @@ -59,8 +59,7 @@ * *

Draw List

*

The draw list is actually comprised of three lists of state chunks: a "pick" list to render a pick buffer - * for colour-indexed GPU picking, along with an "opaque" list and "transparent" list for normal image rendering. - * For normal rendering the opaque list is rendered, then blending is enabled and the transparent list is rendered. + * for colour-indexed GPU picking, along with an "draw" list for normal image rendering. * The chunks in these lists are held in the state-sorted order of their objects in #_objectList, with runs of * duplicate states removed, as mentioned.

* @@ -107,6 +106,12 @@ var SceneJS_Display = function (cfg) { */ this.layer = null; + /** + * Node state core for the last {@link SceneJS.Pass} visited during scene graph compilation traversal + * @type Object + */ + this.pass = null; + /** * Node state core for the last {@link SceneJS.Renderer} visited during scene graph compilation traversal * @type Object @@ -174,10 +179,10 @@ var SceneJS_Display = function (cfg) { this.projTransform = null; /** - * Node state core for the last {@link SceneJS.Framebuf} visited during scene graph compilation traversal + * Node state core for the last {@link SceneJS.ColorTarget} visited during scene graph compilation traversal * @type Object */ - this.framebuf = null; + this.renderTarget = null; /** * Node state core for the last {@link SceneJS.Clips} visited during scene graph compilation traversal @@ -255,22 +260,18 @@ var SceneJS_Display = function (cfg) { /* The "draw list", comprised collectively of three lists of state chunks belong to visible objects * within #_objectList: a "pick" list to render a pick buffer for colour-indexed GPU picking, along with an - * "opaque" list and "transparent" list for normal image rendering. For normal rendering the opaque list is - * rendered, then blending is enabled and the transparent list is rendered. The chunks in these lists - * are held in the state-sorted order of their objects in #_objectList, with runs of duplicate states removed. + * "draw" list for normal image rendering. The chunks in these lists are held in the state-sorted order of + * their objects in #_objectList, with runs of duplicate states removed. */ this._drawList = []; // State chunk list to render all objects this._drawListLen = 0; - this._opaqueDrawList = []; // State chunk list to render opaque objects - this._opaqueDrawListLen = 0; - - this._transparentDrawList = []; // State chunk list to render transparent objects - this._transparentDrawListLen = 0; - - this._pickDrawList = []; // State chunk list to render scene to pick buffer + this._pickDrawList = []; // State chunk list to render scene to pick buffer this._pickDrawListLen = 0; + this._targetList = []; + this._targetListLen = 0; + /* The frame context holds state shared across a single render of the draw list, along with any results of * the render, such as pick hits */ @@ -341,21 +342,15 @@ var SceneJS_Display = function (cfg) { * Reallocates WebGL resources for objects within this display */ SceneJS_Display.prototype.webglRestored = function () { - this._programFactory.webglRestored();// Reallocate programs - this._chunkFactory.webglRestored(); // Recache shader var locations - var gl = this._canvas.gl; - if (this.pickBuf) { this.pickBuf.webglRestored(gl); // Rebuild pick buffers } - if (this.rayPickBuf) { this.rayPickBuf.webglRestored(gl); } - this.imageDirty = true; // Need redraw }; @@ -371,13 +366,13 @@ SceneJS_Display.prototype.buildObject = function (objectId) { var object = this._objects[objectId]; if (!object) { // Create object - object = this._objects[objectId] = this._objectFactory.getObject(objectId); - this.objectListDirty = true; } + object.renderTarget = this.renderTarget; object.layer = this.layer; + object.pass = this.pass; object.texture = this.texture; object.cubemap = this.cubemap; object.geometry = this.geometry; @@ -399,21 +394,17 @@ SceneJS_Display.prototype.buildObject = function (objectId) { ]).join(";"); if (!object.program || hash != object.hash) { - - /* Get new program for object if no program or hash mismatch - */ - + // Get new program for object if no program or hash mismatch if (object.program) { this._programFactory.putProgram(object.program); } - object.program = this._programFactory.getProgram(hash, this); object.hash = hash; } //} - /* Build draw chunks for object - */ + // Build draw chunks for object + this._setChunk(object, 0, "program"); // Must be first this._setChunk(object, 1, "xform", this.modelTransform); this._setChunk(object, 2, "lookAt", this.viewTransform); @@ -430,7 +421,7 @@ SceneJS_Display.prototype.buildObject = function (objectId) { this._setChunk(object, 13, "material", this.material); this._setChunk(object, 14, "texture", this.texture); this._setChunk(object, 15, "cubemap", this.cubemap); - this._setChunk(object, 16, "framebuf", this.framebuf); + this._setChunk(object, 16, "renderTarget", this.renderTarget); this._setChunk(object, 17, "clips", this.clips); this._setChunk(object, 18, "geometry", this.morphGeometry, this.geometry); this._setChunk(object, 19, "listeners", this.renderListeners); // Must be after the above chunks @@ -446,15 +437,11 @@ SceneJS_Display.prototype._setChunk = function (object, order, chunkType, core, // Core supplied if (core.empty) { // Only set default cores for state types that have them - var oldChunk = object.chunks[order]; - if (oldChunk) { this._chunkFactory.putChunk(oldChunk); // Release previous chunk to pool } - object.chunks[order] = null; - return; } @@ -483,11 +470,9 @@ SceneJS_Display.prototype._setChunk = function (object, order, chunkType, core, var oldChunk = object.chunks[order]; if (oldChunk) { - if (oldChunk.id == chunkId) { // Avoid needless chunk reattachment return; } - this._chunkFactory.putChunk(oldChunk); // Release previous chunk to pool } @@ -520,22 +505,15 @@ SceneJS_Display.prototype._setAmbient = function (core) { * @param {String} objectId ID of object to remove */ SceneJS_Display.prototype.removeObject = function (objectId) { - var object = this._objects[objectId]; - if (!object) { return; } - this._programFactory.putProgram(object.program); - object.program = null; object.hash = null; - this._objectFactory.putObject(object); - delete this._objects[objectId]; - this.objectListDirty = true; }; @@ -555,40 +533,30 @@ SceneJS_Display.prototype.render = function (params) { params = params || {}; if (this.objectListDirty) { - this._buildObjectList(); // Build object render bin - this.objectListDirty = false; this.stateOrderDirty = true; // Now needs state ordering } if (this.stateOrderDirty) { - this._makeStateSortKeys(); // Compute state sort order - this.stateOrderDirty = false; this.stateSortDirty = true; // Now needs state sorting } if (this.stateSortDirty) { - this._stateSort(); // State sort the object render bin - this.stateSortDirty = false; this.drawListDirty = true; // Now needs new visible object bin } if (this.drawListDirty) { // Render visible list while building transparent list - this._buildDrawList(); - this.imageDirty = true; } if (this.imageDirty || params.force) { - - this._render({}); // Render, no pick - + this._doDrawList({}); // Render, no pick this.imageDirty = false; this.pickBufDirty = true; // Pick buff will now need rendering on next pick } @@ -607,28 +575,21 @@ SceneJS_Display.prototype._makeStateSortKeys = function () { // TODO: state sort var object; for (var i = 0, len = this._objectListLen; i < len; i++) { object = this._objectList[i]; - object.sortKey = object.program - ? (((object.layer.priority + 1) * 1000000) - + ((object.program.id + 1) * 1000) - + (object.texture.stateId)) - : -1; // Non-visual object (eg. sound) + if (!object.program) { + // Non-visual object (eg. sound) + object.sortKey = -1; + } else { + object.sortKey = + (object.pass.priority * 100000000) + + ((object.flags.transparent ? 1 : 0 ) * 10000000) + + ((object.layer.priority + 1) * 100000) + + ((object.program.id + 1) * 1000) + + object.texture.stateId + ; + } } }; -//SceneJS_Display.prototype._makeStateSortKeys = function () { // TODO: state sort for sound objects? -// var object; -// for (var i = 0, len = this._objectListLen; i < len; i++) { -// object = this._objectList[i]; -// object.sortKey = object.program -// ? (((object.layer.priority + 1) * 1000000000) -// + ((object.program.id + 1) * 1000000) -// + (object.texture.stateId * 10000) -// + (object.geometry.stateId)) -// // + i // Force stability among same-priority objects across multiple sorts -// : -1; // Non-visual object (eg. sound) -// } -//}; - SceneJS_Display.prototype._stateSort = function () { this._objectList.length = this._objectListLen; this._objectList.sort(this._stateSortObjects); @@ -643,15 +604,18 @@ SceneJS_Display.prototype._buildDrawList = function () { this._lastStateId = this._lastStateId || []; this._lastPickStateId = this._lastPickStateId || []; - for (var i = 0; i < 21; i++) { + for (var i = 0; i < 22; i++) { this._lastStateId[i] = null; this._lastPickStateId[i] = null; } this._drawListLen = 0; - this._opaqueDrawListLen = 0; this._pickDrawListLen = 0; - this._transparentDrawListLen = 0; + + var targetObjectLists = {}; + var targetListList = []; + + this._targetListLen = 0; var object; var tagMask; @@ -660,7 +624,6 @@ SceneJS_Display.prototype._buildDrawList = function () { var flags; var chunks; var chunk; - var transparent; var picking; if (this._tagSelector) { @@ -668,10 +631,8 @@ SceneJS_Display.prototype._buildDrawList = function () { tagRegex = this._tagSelector.regex; } - if (!this._xpBuf) { - this._xpBuf = []; - } - this._xpBufLen = 0; + this._objectDrawList = this._objectDrawList || []; + this._objectDrawListLen = 0; for (var i = 0, len = this._objectListLen; i < len; i++) { @@ -689,157 +650,103 @@ SceneJS_Display.prototype._buildDrawList = function () { continue; } - if (!object.layer.enabled) { // Skip disabled layers + // Cull objects in disabled layers + if (!object.layer.enabled) { continue; } - if (tagMask) { // Skip unmatched tags. No tag matching in visible bin prevent this being done on every frame. - + // Cull objects with unmatched tags + if (tagMask) { tagCore = object.tag; - if (tagCore.tag) { - if (tagCore.mask != tagMask) { // Scene tag mask was updated since last render tagCore.mask = tagMask; tagCore.matches = tagRegex.test(tagCore.tag); } - if (!tagCore.matches) { continue; } } } - transparent = flags.transparent; - - if (transparent) { - this._xpBuf[this._xpBufLen++] = object; - } - - /* Add object's chunks to appropriate chunk list - */ - - chunks = object.chunks; - - picking = flags.picking; - - for (var j = 0, lenj = chunks.length; j < lenj; j++) { - - chunk = chunks[j]; - - if (chunk) { - - // As we apply the state chunk lists we track the ID of most types of chunk in order - // to cull redundant re-applications of runs of the same chunk - except for those chunks with a - // 'unique' flag. We don't want to cull runs of draw chunks because they contain the GL - // drawElements calls which render the objects. - - // Chunk IDs are only considered unique within the same program. Therefore, whenever we do a - // program switch, we'll be applying all the different types of chunk again. - - if (!transparent && chunk.draw) { - if (chunk.unique || this._lastStateId[j] != chunk.id) { - this._opaqueDrawList[this._opaqueDrawListLen++] = chunk; - this._drawList[this._drawListLen++] = chunk; - this._lastStateId[j] = chunk.id; - } - } - - if (chunk.pick) { // Transparent objects are pickable - if (picking) { // Don't pick unpickable objects - if (chunk.unique || this._lastPickStateId[j] != chunk.id) { - this._pickDrawList[this._pickDrawListLen++] = chunk; - this._lastPickStateId[j] = chunk.id; - } - } + // Put objects with render targets into a bin for each target + if (object.renderTarget.targets) { + var targets = object.renderTarget.targets; + var target; + var coreId; + var list; + for (var j = 0, lenj = targets.length; j < lenj; j++) { + target = targets[j]; + coreId = target.coreId; + list = targetObjectLists[coreId]; + if (!list) { + list = []; + targetObjectLists[coreId] = list; + targetListList.push(list); + this._targetList[this._targetListLen++] = target; } + list.push(object); } + } else { + + // + this._objectDrawList[this._objectDrawListLen++] = object; } } - if (this._xpBufLen > 0) { - - for (var i = 0; i < this._lastStateId.length; i++) { - this._lastStateId[i] = null; + // Append chunks for objects within render targets first + for (var i = 0, len = targetListList.length; i < len; i++) { + var list = targetListList[i]; + for (var j = 0, lenj = list.length; j < lenj; j++) { + this._appendObjectToDrawLists(list[j]); } + } - for (var i = 0; i < this._xpBufLen; i++) { - - object = this._xpBuf[i]; - chunks = object.chunks; - - for (var j = 0, lenj = chunks.length; j < lenj; j++) { - - chunk = chunks[j]; - - if (chunk && chunk.draw) { - - if (chunk.unique || this._lastStateId[j] != chunk.id) { - this._transparentDrawList[this._transparentDrawListLen++] = chunk; - this._drawList[this._drawListLen++] = chunk; - this._lastStateId[j] = chunk.id; - } - } - } - } + // Append chunks for objects not in render targets + for (var i = 0, len = this._objectDrawListLen; i < len; i++) { + this._appendObjectToDrawLists(this._objectDrawList[i]); } this.drawListDirty = false; }; -SceneJS_Display.prototype._render = function (params) { - - if (params.dof) { - - // Multi-pass depth-of-field (DOF) render - - // DOF image - - var dofImageBuf = this.dofImageBuf; // Lazy-create pick buffer - if (!dofImageBuf) { - dofImageBuf = this.dofImageBuf = new SceneJS._webgl.RenderBuffer({ canvas: this._canvas }); - } - dofImageBuf.bind(); // Bind pick buffer - dofImageBuf.clear(); - this._doDrawList({}); - this._canvas.gl.finish(); - - // DOF depth - - var dofDepthBuf = this.dofDepthBuf; // Lazy-create pick buffer - if (!dofDepthBuf) { - dofDepthBuf = this.dofDepthBuf = new SceneJS._webgl.RenderBuffer({ canvas: this._canvas }); +SceneJS_Display.prototype._appendObjectToDrawLists = function(object) { + var chunks = object.chunks; + var picking = object.flags.picking; + var chunck; + for (var k = 0, lenk = chunks.length; k < lenk; k++) { + chunk = chunks[k]; + if (chunk) { + // As we apply the state chunk lists we track the ID of most types of chunk in order + // to cull redundant re-applications of runs of the same chunk - except for those chunks with a + // 'unique' flag. We don't want to cull runs of draw chunks because they contain the GL + // drawElements calls which render the objects. + // Chunk IDs are only considered unique within the same program. Therefore, whenever we do a + // program switch, we'll be applying all the different types of chunk again. + if (chunk.draw) { + if (chunk.unique || this._lastStateId[k] != chunk.id) { + this._drawList[this._drawListLen++] = chunk; + this._lastStateId[k] = chunk.id; + } + } + if (chunk.pick) { // Transparent objects are pickable + if (picking) { // Don't pick unpickable objects + if (chunk.unique || this._lastPickStateId[k] != chunk.id) { + this._pickDrawList[this._pickDrawListLen++] = chunk; + this._lastPickStateId[k] = chunk.id; + } + } + } } - dofDepthBuf.bind(); // Bind pick buffer - dofDepthBuf.clear(); - this._doDrawList({ - all: true - }); - this._canvas.gl.finish(); - - // DOF blur - // .. TODO - - // DOF image - // .. TODO - - } else { - - // Default render - - this._doDrawList({}); } }; SceneJS_Display.prototype.pick = function (params) { var canvas = this._canvas.canvas; - var hit = null; - var canvasX = params.canvasX; var canvasY = params.canvasY; - var pickBuf = this.pickBuf; // Lazy-create pick buffer if (!pickBuf) { @@ -852,15 +759,11 @@ SceneJS_Display.prototype.pick = function (params) { pickBuf.bind(); // Bind pick buffer if (this.pickBufDirty) { // Render pick buffer - pickBuf.clear(); - this._doDrawList({ pick: true }); - this._canvas.gl.finish(); - this.pickBufDirty = false; // Pick buffer up to date this.rayPickBufDirty = true; // Ray pick buffer now dirty } @@ -868,7 +771,6 @@ SceneJS_Display.prototype.pick = function (params) { var pix = pickBuf.read(canvasX, canvasY); // Read pick buffer var pickedObjectIndex = pix[0] + pix[1] * 256 + pix[2] * 65536; var pickIndex = (pickedObjectIndex >= 1) ? pickedObjectIndex - 1 : -1; - pickBuf.unbind(); // Unbind pick buffer var pickName = this._frameCtx.pickNames[pickIndex]; // Map pixel to name @@ -883,7 +785,6 @@ SceneJS_Display.prototype.pick = function (params) { }; if (params.rayPick) { // Ray pick to find position - var rayPickBuf = this.rayPickBuf; // Lazy-create Z-pick buffer if (!rayPickBuf) { rayPickBuf = this.rayPickBuf = new SceneJS._webgl.RenderBuffer({ canvas: this._canvas }); @@ -892,14 +793,11 @@ SceneJS_Display.prototype.pick = function (params) { rayPickBuf.bind(); if (this.rayPickBufDirty) { - rayPickBuf.clear(); - this._doDrawList({ pick: true, rayPick: true }); - this.rayPickBufDirty = false; } @@ -907,34 +805,24 @@ SceneJS_Display.prototype.pick = function (params) { rayPickBuf.unbind(); - /* Read normalised device Z coordinate, which will be - * in range of [0..1] with z=0 at front - */ + // Read normalised device Z coordinate, which will be + // in range of [0..1] with z=0 at front var screenZ = this._unpackDepth(pix); - var w = canvas.width; var h = canvas.height; - - /* Calculate clip space coordinates, which will be in range - * of x=[-1..1] and y=[-1..1], with y=(+1) at top - */ + // Calculate clip space coordinates, which will be in range + // of x=[-1..1] and y=[-1..1], with y=(+1) at top var x = (canvasX - w / 2) / (w / 2); // Calculate clip space coordinates var y = -(canvasY - h / 2) / (h / 2); - var projMat = this._frameCtx.cameraMat; var viewMat = this._frameCtx.viewMat; - var pvMat = SceneJS_math_mulMat4(projMat, viewMat, []); var pvMatInverse = SceneJS_math_inverseMat4(pvMat, []); - var world1 = SceneJS_math_transformVector4(pvMatInverse, [x, y, -1, 1]); world1 = SceneJS_math_mulVec4Scalar(world1, 1 / world1[3]); - var world2 = SceneJS_math_transformVector4(pvMatInverse, [x, y, 1, 1]); world2 = SceneJS_math_mulVec4Scalar(world2, 1 / world2[3]); - var dir = SceneJS_math_subVec3(world2, world1, []); - var vWorld = SceneJS_math_addVec3(world1, SceneJS_math_mulVec4Scalar(dir, screenZ, []), []); hit.worldPos = vWorld; @@ -952,35 +840,34 @@ SceneJS_Display.prototype._unpackDepth = function (depthZ) { SceneJS_Display.prototype._doDrawList = function (params) { - var frameCtx = this._frameCtx; // Reset rendering context - var gl = this._canvas.gl; - frameCtx.framebuf = null; + // Reset frame context + var frameCtx = this._frameCtx; + + frameCtx.targetList = this._targetList; + frameCtx.targetIndex = 0; + frameCtx.renderBuf = null; frameCtx.viewMat = null; frameCtx.modelMat = null; frameCtx.cameraMat = null; frameCtx.renderer = null; - frameCtx.depthbufEnabled = null; frameCtx.clearDepth = null; frameCtx.depthFunc = gl.LESS; - frameCtx.scissorTestEnabled = false; - frameCtx.blendEnabled = false; - frameCtx.backfaces = true; frameCtx.frontface = "ccw"; frameCtx.pick = !!params.pick; + frameCtx.rayPick = !!params.rayPick; + frameCtx.pickIndex = 0; + frameCtx.depthPass = false; frameCtx.textureUnit = 0; - frameCtx.lineWidth = 1; + frameCtx.transparent = false; - frameCtx.transparencyPass = false; - - // The extension needs to be re-queried in case the context was lost and - // has been recreated. + // The extension needs to be re-queried in case the context was lost and has been recreated. var VAO = gl.getExtension("OES_vertex_array_object"); if (VAO) { frameCtx.VAO = VAO; @@ -998,57 +885,26 @@ SceneJS_Display.prototype._doDrawList = function (params) { if (params.pick) { - // Render to pick buffer - - frameCtx.pickIndex = 0; - frameCtx.rayPick = !!params.rayPick; - - for (var i = 0, len = this._pickDrawListLen; i < len; i++) { // Push picking chunks + // Render for pick + for (var i = 0, len = this._pickDrawListLen; i < len; i++) { this._pickDrawList[i].pick(frameCtx); } - } else if (params.all) { - - // Render all + } else { + // Render for draw for (var i = 0, len = this._drawListLen; i < len; i++) { // Push opaque rendering chunks this._drawList[i].draw(frameCtx); } + } - } else { - - // Normal draw - - for (var i = 0, len = this._opaqueDrawListLen; i < len; i++) { // Push opaque rendering chunks - this._opaqueDrawList[i].draw(frameCtx); - } - - if (this._transparentDrawListLen > 0) { - - // Disables some types of state changes during - // transparency pass, such as blending disable - frameCtx.transparencyPass = true; - - // Enable blending - gl.enable(gl.BLEND); - frameCtx.blendEnabled = true; - - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - - for (var i = 0, len = this._transparentDrawListLen; i < len; i++) { // Push transparent rendering chunks - this._transparentDrawList[i].draw(frameCtx); - } - - // Disable blending - gl.disable(gl.BLEND); - frameCtx.blendEnabled = false; + gl.flush(); - frameCtx.transparencyPass = false; - } + if (frameCtx.renderBuf) { + gl.finish(); + frameCtx.renderBuf.unbind(); } - gl.flush(); // Flush GL - if (frameCtx.VAO) { frameCtx.VAO.bindVertexArrayOES(null); for (var i = 0; i < 10; i++) { diff --git a/src/core/display/programSourceFactory.js b/src/core/display/programSourceFactory.js index 6c2925f6..ac6a1b15 100644 --- a/src/core/display/programSourceFactory.js +++ b/src/core/display/programSourceFactory.js @@ -51,12 +51,20 @@ var SceneJS_ProgramSourceFactory = new (function () { var customFragmentShader = customShaders.fragment || {}; var fragmentHooks = customFragmentShader.hooks || {}; + // Do a full custom shader replacement if code supplied without hooks + if (customShaders.vertex + && customShaders.vertex.code + && customShaders.vertex.code != "" + && SceneJS._isEmpty(customShaders.vertex.hooks)) { + return [customShaders.vertex.code]; + } + var clipping = states.clips.clips.length > 0; var morphing = !!states.morphGeometry.targets; var normals = this._hasNormals(states); var src = [ - "precision mediump float;", + "precision highp float;", "attribute vec3 SCENEJS_aVertex;", "uniform mat4 SCENEJS_uMMatrix;", "uniform mat4 SCENEJS_uMNMatrix;", @@ -164,12 +172,20 @@ var SceneJS_ProgramSourceFactory = new (function () { var customFragmentShader = customShaders.fragment || {}; var fragmentHooks = customFragmentShader.hooks || {}; + // Do a full custom shader replacement if code supplied without hooks + if (customShaders.fragment + && customShaders.fragment.code + && customShaders.fragment.code != "" + && SceneJS._isEmpty(customShaders.fragment.hooks)) { + return [customShaders.fragment.code]; + } + var clipping = states.clips.clips.length > 0; var normals = this._hasNormals(states); var src = [ - "precision mediump float;" + "precision highp float;" ]; src.push("vec4 packDepth(const in float depth) {"); @@ -311,10 +327,12 @@ var SceneJS_ProgramSourceFactory = new (function () { var customShaders = states.shader.shaders || {}; - /* Do a full custom shader replacement if code supplied without hooks - */ - if (customShaders.vertex && customShaders.vertex.code && !customShaders.vertex.hooks) { - return customShaders.vertex.code; + // Do a full custom shader replacement if code supplied without hooks + if (customShaders.vertex + && customShaders.vertex.code + && customShaders.vertex.code != "" + && SceneJS._isEmpty(customShaders.vertex.hooks)) { + return [customShaders.vertex.code]; } var customVertexShader = customShaders.vertex || {}; @@ -329,7 +347,7 @@ var SceneJS_ProgramSourceFactory = new (function () { var morphing = !!states.morphGeometry.targets; var src = [ - "precision mediump float;" + "precision highp float;" ]; src.push("attribute vec3 SCENEJS_aVertex;"); // Model coordinates @@ -395,9 +413,9 @@ var SceneJS_ProgramSourceFactory = new (function () { src.push("varying vec4 SCENEJS_vWorldVertex;"); // Varying for fragment clip or world pos hook } - if (fragmentHooks.viewPos) { - src.push("varying vec4 SCENEJS_vViewVertex;"); // Varying for fragment view clip hook - } + // if (fragmentHooks.viewPos) { + src.push("varying vec4 SCENEJS_vViewVertex;"); // Varying for fragment view clip hook + // } if (texturing) { // Varyings for fragment texturing if (states.geometry.uvBuf) { @@ -483,9 +501,9 @@ var SceneJS_ProgramSourceFactory = new (function () { src.push(" SCENEJS_vWorldVertex = worldVertex;"); // Varying for fragment world clip or hooks } - if (fragmentHooks.viewPos) { - src.push(" SCENEJS_vViewVertex = viewVertex;"); // Varying for fragment hooks - } + // if (fragmentHooks.viewPos) { + src.push(" SCENEJS_vViewVertex = viewVertex;"); // Varying for fragment hooks + // } if (vertexHooks.projMatrix) { src.push("gl_Position = " + vertexHooks.projMatrix + "(SCENEJS_uPMatrix) * viewVertex;"); @@ -581,10 +599,12 @@ var SceneJS_ProgramSourceFactory = new (function () { var customShaders = states.shader.shaders || {}; - /* Do a full custom shader replacement if code supplied without hooks - */ - if (customShaders.fragment && customShaders.fragment.code && !customShaders.fragment.hooks) { - return customShaders.fragment.code; + // Do a full custom shader replacement if code supplied without hooks + if (customShaders.fragment + && customShaders.fragment.code + && customShaders.fragment.code != "" + && SceneJS._isEmpty(customShaders.fragment.hooks)) { + return [customShaders.fragment.code]; } var customFragmentShader = customShaders.fragment || {}; @@ -597,16 +617,20 @@ var SceneJS_ProgramSourceFactory = new (function () { var src = ["\n"]; - src.push("precision mediump float;"); + src.push("precision highp float;"); if (clipping || fragmentHooks.worldPos) { src.push("varying vec4 SCENEJS_vWorldVertex;"); // World-space vertex } - if (fragmentHooks.viewPos) { - src.push("varying vec4 SCENEJS_vViewVertex;"); // View-space vertex - } + // if (fragmentHooks.viewPos) { + src.push("varying vec4 SCENEJS_vViewVertex;"); // View-space vertex + // } + + src.push("uniform float SCENEJS_uZNear;"); // Used in Z-pick mode + src.push("uniform float SCENEJS_uZFar;"); // Used in Z-pick mode + /*----------------------------------------------------------------------------------- * Variables @@ -656,6 +680,8 @@ var SceneJS_ProgramSourceFactory = new (function () { src.push("uniform bool SCENEJS_uDiffuse;"); src.push("uniform bool SCENEJS_uReflection;"); + src.push("uniform bool SCENEJS_uDepthMode;"); + /* True when rendering transparency */ src.push("uniform bool SCENEJS_uTransparent;"); @@ -668,7 +694,7 @@ var SceneJS_ProgramSourceFactory = new (function () { src.push("uniform vec3 SCENEJS_uAmbientColor;"); // Scene ambient colour - taken from clear colour - src.push("uniform vec3 SCENEJS_uMaterialBaseColor;"); + src.push("uniform vec3 SCENEJS_uMaterialColor;"); src.push("uniform float SCENEJS_uMaterialAlpha;"); src.push("uniform float SCENEJS_uMaterialEmit;"); src.push("uniform vec3 SCENEJS_uMaterialSpecularColor;"); @@ -752,7 +778,7 @@ var SceneJS_ProgramSourceFactory = new (function () { if (states.geometry.colorBuf) { src.push(" vec3 color = SCENEJS_vColor.rgb;"); } else { - src.push(" vec3 color = SCENEJS_uMaterialBaseColor;") + src.push(" vec3 color = SCENEJS_uMaterialColor;") } src.push(" float alpha = SCENEJS_uMaterialAlpha;"); @@ -783,6 +809,7 @@ var SceneJS_ProgramSourceFactory = new (function () { if (normals) { src.push(" float attenuation = 1.0;"); src.push(" vec3 viewNormalVec = SCENEJS_vViewNormal;"); + src.push(" vec3 worldNormalVec = SCENEJS_vWorldNormal;"); } var layer; @@ -841,6 +868,10 @@ var SceneJS_ProgramSourceFactory = new (function () { } else if (layer.blendMode == "add") { src.push("alpha = ((1.0 - SCENEJS_uLayer" + i + "BlendFactor) * alpha) + (SCENEJS_uLayer" + i + "BlendFactor * texture2D(SCENEJS_uSampler" + i + ", vec2(textureCoord.x, 1.0 - textureCoord.y)).b);"); } + + //===================================================================== + // TODO: "mix" blendMode + //===================================================================== } /* Texture output @@ -879,7 +910,7 @@ var SceneJS_ProgramSourceFactory = new (function () { if (layer.applyTo == "normals" && normals) { src.push("vec3 bump = normalize(texture2D(SCENEJS_uSampler" + i + ", vec2(textureCoord.x, -textureCoord.y)).xyz * 2.0 - 1.0);"); - src.push("viewNormalVec *= -bump;"); + src.push("worldNormalVec = bump;"); } } if (normals) { @@ -889,7 +920,7 @@ var SceneJS_ProgramSourceFactory = new (function () { if (normals && cubeMapping) { src.push("if (SCENEJS_uReflection) {"); // Flag which can enable/disable reflection - src.push("vec3 envLookup = reflect(normalize(SCENEJS_vWorldEyeVec), normalize(SCENEJS_vWorldNormal));"); + src.push("vec3 envLookup = reflect(normalize(SCENEJS_vWorldEyeVec), normalize(worldNormalVec));"); src.push("envLookup.y = envLookup.y * -1.0;"); // Need to flip textures on Y-axis for some reason src.push("vec4 envColor;"); for (var i = 0, len = states.cubemap.layers.length; i < len; i++) { @@ -925,7 +956,7 @@ var SceneJS_ProgramSourceFactory = new (function () { if (light.mode == "point") { - src.push("dotN = max(dot(viewNormalVec, viewLightVec), 0.0);"); + src.push("dotN = max(dot(worldNormalVec, viewLightVec), 0.0);"); //src.push("if (dotN > 0.0) {"); @@ -945,7 +976,7 @@ var SceneJS_ProgramSourceFactory = new (function () { if (light.specular) { src.push("if (SCENEJS_uSpecularLighting) {"); src.push(" specularValue += specularColor * SCENEJS_uLightColor" + i + - " * specular * pow(max(dot(reflect(-viewLightVec, -viewNormalVec), vec3(0.0,0.0,1.0)), 0.0), shine) * attenuation;"); + " * specular * pow(max(dot(reflect(-viewLightVec, -worldNormalVec), vec3(0.0,0.0,1.0)), 0.0), shine) * attenuation;"); src.push("}"); } //src.push("}"); @@ -953,7 +984,7 @@ var SceneJS_ProgramSourceFactory = new (function () { if (light.mode == "dir") { - src.push("dotN = max(dot(viewNormalVec, viewLightVec), 0.0);"); + src.push("dotN = max(dot(worldNormalVec, viewLightVec), 0.0);"); //src.push("if (dotN > 0.0) {"); if (light.diffuse) { @@ -965,7 +996,7 @@ var SceneJS_ProgramSourceFactory = new (function () { if (light.specular) { src.push("if (SCENEJS_uSpecularLighting) {"); src.push(" specularValue += specularColor * SCENEJS_uLightColor" + i + - " * specular * pow(max(dot(reflect(-viewLightVec, -viewNormalVec), vec3(0.0,0.0,1.0)), 0.0), shine);"); + " * specular * pow(max(dot(reflect(-viewLightVec, -worldNormalVec), vec3(0.0,0.0,1.0)), 0.0), shine);"); src.push("}"); } // src.push("}"); @@ -984,11 +1015,24 @@ var SceneJS_ProgramSourceFactory = new (function () { if (fragmentHooks.pixelColor) { src.push("fragColor=" + fragmentHooks.pixelColor + "(fragColor);"); } - if (false && debugCfg.whitewash === true) { src.push(" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);"); } else { - src.push(" gl_FragColor = fragColor;"); + src.push(" if (SCENEJS_uDepthMode) {"); + src.push(" float depth = length(SCENEJS_vViewVertex) / (SCENEJS_uZFar - SCENEJS_uZNear);"); + src.push(" const vec4 bias = vec4(1.0 / 255.0,"); + src.push(" 1.0 / 255.0,"); + src.push(" 1.0 / 255.0,"); + src.push(" 0.0);"); + src.push(" float r = depth;"); + src.push(" float g = fract(r * 255.0);"); + src.push(" float b = fract(g * 255.0);"); + src.push(" float a = fract(b * 255.0);"); + src.push(" vec4 colour = vec4(r, g, b, a);"); + src.push(" gl_FragColor = colour - (colour.yzww * bias);"); + src.push(" } else {"); + src.push(" gl_FragColor = fragColor;"); + src.push(" };"); } src.push("}"); diff --git a/src/core/engine.js b/src/core/engine.js index 90424c93..8c53fb6a 100644 --- a/src/core/engine.js +++ b/src/core/engine.js @@ -40,7 +40,8 @@ var SceneJS_Engine = function (json, options) { */ this.display = new SceneJS_Display({ canvas:this.canvas, - transparent: json.transparent + transparent: json.transparent, + dof: json.dof }); /** diff --git a/src/core/scene/colorTarget.js b/src/core/scene/colorTarget.js new file mode 100644 index 00000000..fcfcf12b --- /dev/null +++ b/src/core/scene/colorTarget.js @@ -0,0 +1,65 @@ +new (function () { + + var defaultCore = { + type: "renderTarget", + stateId: SceneJS._baseStateId++, + targets: null + }; + + // Map of nodes to cores, for reallocation on WebGL context restore + var nodeCoreMap = {}; + + var coreStack = []; + var stackLen = 0; + + SceneJS_events.addListener( + SceneJS_events.SCENE_COMPILING, + function (params) { + params.engine.display.renderTarget = defaultCore; + stackLen = 0; + }); + + // Reallocate VBOs when context restored after loss + SceneJS_events.addListener( + SceneJS_events.WEBGL_CONTEXT_RESTORED, + function () { + for (var nodeId in nodeCoreMap) { + if (nodeCoreMap.hasOwnProperty(nodeId)) { + nodeCoreMap[nodeId]._buildNodeCore(); + } + } + }); + + SceneJS.ColorTarget = SceneJS_NodeFactory.createNodeType("colorTarget"); + + SceneJS.ColorTarget.prototype._init = function (params) { + nodeCoreMap[this._core.coreId] = this; + this._core.bufType = "color"; + this._buildNodeCore(); + }; + + SceneJS.ColorTarget.prototype._buildNodeCore = function () { + this._core.renderBuf = new SceneJS._webgl.RenderBuffer({ canvas: this._engine.canvas }); + }; + + SceneJS.ColorTarget.prototype._compile = function (ctx) { + if (!this.__core) { + this.__core = this._engine._coreFactory.getCore("renderTarget"); + } + var parentCore = this._engine.display.renderTarget; + if (!this._core.empty) { + this.__core.targets = (parentCore && parentCore.targets) ? parentCore.targets.concat([this._core]) : [this._core]; + } + coreStack[stackLen++] = this.__core; + this._engine.display.renderTarget = this.__core; + this._compileNodes(ctx); + this._engine.display.renderTarget = (--stackLen > 0) ? coreStack[stackLen - 1] : defaultCore; + }; + + + SceneJS.ColorTarget.prototype._destroy = function () { + if (this._core) { + delete nodeCoreMap[this._core.coreId]; + } + }; +})(); \ No newline at end of file diff --git a/src/core/scene/depthTarget.js b/src/core/scene/depthTarget.js new file mode 100644 index 00000000..615a17b0 --- /dev/null +++ b/src/core/scene/depthTarget.js @@ -0,0 +1,65 @@ +new (function () { + + var defaultCore = { + type: "renderTarget", + stateId: SceneJS._baseStateId++, + targets: null + }; + + // Map of nodes to cores, for reallocation on WebGL context restore + var nodeCoreMap = {}; + + var coreStack = []; + var stackLen = 0; + + SceneJS_events.addListener( + SceneJS_events.SCENE_COMPILING, + function (params) { + params.engine.display.renderTarget = defaultCore; + stackLen = 0; + }); + + // Reallocate VBOs when context restored after loss + SceneJS_events.addListener( + SceneJS_events.WEBGL_CONTEXT_RESTORED, + function () { + for (var nodeId in nodeCoreMap) { + if (nodeCoreMap.hasOwnProperty(nodeId)) { + nodeCoreMap[nodeId]._buildNodeCore(); + } + } + }); + + SceneJS.DepthTarget = SceneJS_NodeFactory.createNodeType("depthTarget"); + + SceneJS.DepthTarget.prototype._init = function (params) { + nodeCoreMap[this._core.coreId] = this; + this._core.bufType = "depth"; + this._buildNodeCore(); + }; + + SceneJS.DepthTarget.prototype._buildNodeCore = function () { + this._core.renderBuf = new SceneJS._webgl.RenderBuffer({ canvas: this._engine.canvas }); + }; + + SceneJS.DepthTarget.prototype._compile = function (ctx) { + if (!this.__core) { + this.__core = this._engine._coreFactory.getCore("renderTarget"); + } + var parentCore = this._engine.display.renderTarget; + if (!this._core.empty) { + this.__core.targets = (parentCore && parentCore.targets) ? parentCore.targets.concat([this._core]) : [this._core]; + } + coreStack[stackLen++] = this.__core; + this._engine.display.renderTarget = this.__core; + this._compileNodes(ctx); + this._engine.display.renderTarget = (--stackLen > 0) ? coreStack[stackLen - 1] : defaultCore; + }; + + + SceneJS.DepthTarget.prototype._destroy = function () { + if (this._core) { + delete nodeCoreMap[this._core.coreId]; + } + }; +})(); \ No newline at end of file diff --git a/src/core/scene/framebuf.js b/src/core/scene/framebuf.js deleted file mode 100644 index 3c89bc15..00000000 --- a/src/core/scene/framebuf.js +++ /dev/null @@ -1,62 +0,0 @@ -new (function () { - - var defaultCore = { - type: "framebuf", - stateId: SceneJS._baseStateId++, - framebuf: null - }; - - // Map of framebuf nodes to cores, for reallocation on WebGL context restore - var nodeCoreMap = {}; - - var coreStack = []; - var stackLen = 0; - - SceneJS_events.addListener( - SceneJS_events.SCENE_COMPILING, - function (params) { - params.engine.display.framebuf = defaultCore; - stackLen = 0; - }); - - SceneJS_events.addListener(// Reallocate VBOs when context restored after loss - SceneJS_events.WEBGL_CONTEXT_RESTORED, - function () { - for (var nodeId in nodeCoreMap) { - if (nodeCoreMap.hasOwnProperty(nodeId)) { - nodeCoreMap[nodeId]._buildNodeCore(); - } - } - }); - - /** - * @class Scene graph node which sets up a frame buffer to which the {@link SceneJS.Geometry} nodes in its subgraph will be rendered. - * The frame buffer may be referenced as an image source by successive {@link SceneJS.Texture} nodes. - * @extends SceneJS.Node - */ - SceneJS.Framebuf = SceneJS_NodeFactory.createNodeType("framebuf"); - - SceneJS.Framebuf.prototype._init = function () { - nodeCoreMap[this._core.coreId] = this; - this._buildNodeCore(); - }; - - SceneJS.Framebuf.prototype._buildNodeCore = function () { - if (!this._core) { - this._core = {}; - } - this._core.framebuf = new SceneJS._webgl.RenderBuffer({ canvas: this._engine.canvas }); - }; - - SceneJS.Framebuf.prototype._compile = function (ctx) { - this._engine.display.framebuf = coreStack[stackLen++] = this._core; - this._compileNodes(ctx); - this._engine.display.framebuf = (--stackLen > 0) ? coreStack[stackLen - 1] : defaultCore; - }; - - SceneJS.Framebuf.prototype._destroy = function () { - if (this._core) { - delete nodeCoreMap[this._core.coreId]; - } - }; -})(); \ No newline at end of file diff --git a/src/core/scene/geometry.js b/src/core/scene/geometry.js index f569fbf2..6ae302f4 100755 --- a/src/core/scene/geometry.js +++ b/src/core/scene/geometry.js @@ -28,10 +28,10 @@ new (function () { autoNormals: params.normals == "auto" }); - SceneJS.Geometry._buildNodeCore(this._engine.canvas.gl, this._core); + this._buildNodeCore(this._engine.canvas.gl, this._core); this._core.webglRestored = function () { - SceneJS.Geometry._buildNodeCore(self._engine.canvas.gl, self._core); + this._buildNodeCore(self._engine.canvas.gl, self._core); }; } @@ -199,20 +199,20 @@ new (function () { * In addition to initially allocating those, this is called to reallocate them after * WebGL context is regained after being lost. */ - SceneJS.Geometry._buildNodeCore = function (gl, core) { + SceneJS.Geometry.prototype._buildNodeCore = function (gl, core) { var usage = gl.STATIC_DRAW; //var usage = (!arrays.fixed) ? gl.STREAM_DRAW : gl.STATIC_DRAW; try { // TODO: Modify usage flags in accordance with how often geometry is evicted var arrays = core.arrays; - var canInterleave = true; + var canInterleave = (SceneJS.getConfigs("enableInterleaving") !== false); var dataLength = 0; var interleavedValues = 0; var interleavedArrays = []; var interleavedArrayStrides = []; - var prepareInterleaveBuffer = function (array, strideInElements) { + var prepareInterleaveBuffer = function(array, strideInElements) { if (dataLength == 0) { dataLength = array.length / strideInElements; } else if (array.length / strideInElements != dataLength) { @@ -225,27 +225,37 @@ new (function () { }; if (arrays.positions) { - core.interleavedPositionOffset = prepareInterleaveBuffer(arrays.positions, 3); + if (canInterleave) { + core.interleavedPositionOffset = prepareInterleaveBuffer(arrays.positions, 3); + } core.vertexBuf = new SceneJS._webgl.ArrayBuffer(gl, gl.ARRAY_BUFFER, arrays.positions, arrays.positions.length, 3, usage); } if (arrays.normals) { - core.interleavedNormalOffset = prepareInterleaveBuffer(arrays.normals, 3); + if (canInterleave) { + core.interleavedNormalOffset = prepareInterleaveBuffer(arrays.normals, 3); + } core.normalBuf = new SceneJS._webgl.ArrayBuffer(gl, gl.ARRAY_BUFFER, arrays.normals, arrays.normals.length, 3, usage); } if (arrays.uv) { - core.interleavedUVOffset = prepareInterleaveBuffer(arrays.uv, 2); + if (canInterleave) { + core.interleavedUVOffset = prepareInterleaveBuffer(arrays.uv, 2); + } core.uvBuf = new SceneJS._webgl.ArrayBuffer(gl, gl.ARRAY_BUFFER, arrays.uv, arrays.uv.length, 2, usage); } if (arrays.uv2) { - core.interleavedUV2Offset = prepareInterleaveBuffer(arrays.uv2, 2); + if (canInterleave) { + core.interleavedUV2Offset = prepareInterleaveBuffer(arrays.uv2, 2); + } core.uvBuf2 = new SceneJS._webgl.ArrayBuffer(gl, gl.ARRAY_BUFFER, arrays.uv2, arrays.uv2.length, 2, usage); } if (arrays.colors) { - core.interleavedColorOffset = prepareInterleaveBuffer(arrays.colors, 4); + if (canInterleave) { + core.interleavedColorOffset = prepareInterleaveBuffer(arrays.colors, 4); + } core.colorBuf = new SceneJS._webgl.ArrayBuffer(gl, gl.ARRAY_BUFFER, arrays.colors, arrays.colors.length, 4, usage); } @@ -458,6 +468,10 @@ new (function () { return this.primitive; }; + /** Returns the Model-space boundary of this geometry + * + * @returns {*} + */ SceneJS.Geometry.prototype.getBoundary = function () { if (this._boundary) { return this._boundary; @@ -631,5 +645,4 @@ new (function () { } }; -}) - (); +})(); diff --git a/src/core/scene/pass.js b/src/core/scene/pass.js new file mode 100644 index 00000000..2686987b --- /dev/null +++ b/src/core/scene/pass.js @@ -0,0 +1,83 @@ +(function() { + + /** + * The default state core singleton for {@link SceneJS.Pass} nodes + */ + var defaultCore = { + type: "pass", + stateId: SceneJS._baseStateId++, + priority: 0, + enabled: true + }; + + var coreStack = []; + var stackLen = 0; + + SceneJS_events.addListener( + SceneJS_events.SCENE_COMPILING, + function(params) { + params.engine.display.pass = defaultCore; + stackLen = 0; + }); + + /** + * @class Scene graph node which assigns the {@link SceneJS.Geometry}s within its subgraph to a prioritised render bin + * @extends SceneJS.Node + */ + SceneJS.Pass = SceneJS_NodeFactory.createNodeType("pass"); + + SceneJS.Pass.prototype._init = function(params) { + if (this._core.useCount == 1) { // This node defines the resource + this._core.priority = params.priority || 0; + this._core.enabled = params.enabled !== false; + } + }; + + SceneJS.Pass.prototype.setPriority = function(priority) { + priority = priority || 0; + if (this._core.priority != priority) { + this._core.priority = priority; + this._engine.display.stateOrderDirty = true; + } + }; + + SceneJS.Pass.prototype.getPriority = function() { + return this._core.priority; + }; + + SceneJS.Pass.prototype.setEnabled = function(enabled) { + enabled = !!enabled; + if (this._core.enabled != enabled) { + this._core.enabled = enabled; + this._engine.display.drawListDirty = true; + } + }; + + SceneJS.Pass.prototype.getEnabled = function() { + return this._core.enabled; + }; + + SceneJS.Pass.prototype.getEnabled = function() { + return this._core.enabled; + }; + + SceneJS.Pass.prototype.setClearDepth = function(clearDepth) { + clearDepth = clearDepth || 0; + if (this._core.clearDepth != clearDepth) { + this._core.clearDepth = clearDepth; + this._engine.display.drawListDirty = true; + } + }; + + SceneJS.Pass.prototype.getClearDepth = function() { + return this._core.clearDepth; + }; + + SceneJS.Pass.prototype._compile = function(ctx) { + this._engine.display.pass = coreStack[stackLen++] = this._core; + this._compileNodes(ctx); + this._engine.display.pass = (--stackLen > 0) ? coreStack[stackLen - 1] : defaultCore; + }; + +})(); + diff --git a/src/core/scene/shader.js b/src/core/scene/shader.js index 105256c3..7de5da5a 100644 --- a/src/core/scene/shader.js +++ b/src/core/scene/shader.js @@ -47,11 +47,11 @@ new (function() { shaders: { fragment: { - code: shaderFragmentCodeStack.slice(0, stackLen).join("\n"), + code: shaderFragmentCodeStack.slice(0, stackLen).join(""), hooks: combineMapStack(shaderFragmentHooksStack) }, vertex: { - code: shaderVertexCodeStack.slice(0, stackLen).join("\n"), + code: shaderVertexCodeStack.slice(0, stackLen).join(""), hooks: combineMapStack(shaderVertexHooksStack) } }, diff --git a/src/core/scene/texture.js b/src/core/scene/texture.js index 54246b94..0015f1d9 100644 --- a/src/core/scene/texture.js +++ b/src/core/scene/texture.js @@ -57,11 +57,11 @@ new (function () { layerParams = params.layers[i]; - if (!layerParams.source && !layerParams.uri && !layerParams.src && !layerParams.framebuf && !layerParams.video) { + if (!layerParams.source && !layerParams.uri && !layerParams.src && !layerParams.colorTarget && !layerParams.video) { throw SceneJS_error.fatalError( SceneJS.errors.NODE_CONFIG_EXPECTED, - "texture layer " + i + " has no uri, src, source, framebuf, video or canvasId specified"); + "texture layer " + i + " has no uri, src, source, colorTarget, video or canvasId specified"); } if (layerParams.applyFrom) { @@ -92,11 +92,11 @@ new (function () { } if (layerParams.blendMode) { - if (layerParams.blendMode != "add" && layerParams.blendMode != "multiply") { + if (layerParams.blendMode != "add" && layerParams.blendMode != "multiply" && layerParams.blendMode != "mix") { throw SceneJS_error.fatalError( SceneJS.errors.NODE_CONFIG_EXPECTED, "texture layer " + i + " blendMode value is unsupported - " + - "should be either 'add' or 'multiply'"); + "should be either 'add', 'multiply' or 'mix'"); } } @@ -120,10 +120,10 @@ new (function () { this._setLayerTransform(layerParams, layer); - if (layer.framebuf) { // Create from a framebuf node preceeding this texture in the scene graph - var targetNode = this._engine.findNode(layer.framebuf); - if (targetNode && targetNode.type == "framebuf") { - layer.texture = targetNode._core.framebuf.getTexture(); // TODO: what happens when the framebuf is destroyed? + if (layer.colorTarget) { // Create from a colorTarget node preceeding this texture in the scene graph + var targetNode = this._engine.findNode(layer.colorTarget); + if (targetNode && targetNode.type == "colorTarget") { + layer.texture = targetNode._core.colorTarget.getTexture(); // TODO: what happens when the colorTarget is destroyed? } } else { // Create from texture node's layer configs this._loadLayerTexture(gl, layer); diff --git a/src/core/scene/textureMap.js b/src/core/scene/textureMap.js index 78719a03..ddffa4ad 100644 --- a/src/core/scene/textureMap.js +++ b/src/core/scene/textureMap.js @@ -97,10 +97,10 @@ new (function () { this._core.image = params.image; this._initTexture(params.image); - } else if (params.framebuf) { // Render to this texture - this.getScene().getNode(params.framebuf, - function (framebuf) { - self.setFramebuf(framebuf); + } else if (params.target) { // Render to this texture + this.getScene().getNode(params.target, + function (target) { + self.setTarget(target); }); } @@ -111,12 +111,11 @@ new (function () { } else if (self._core.image) { self._initTexture(self._core.image); - } else if (self._core.framebuf) { - self.getScene().getNode(params.framebuf, - function (framebuf) { - self.setFramebuf(framebuf); - }); - self.setFramebuf(self._core.framebuf); + } else if (self._core.target) { +// self.getScene().getNode(params.target, +// function (target) { +// self.setTarget(self._core.target); +// }); } }; } @@ -248,27 +247,27 @@ new (function () { SceneJS.TextureMap.prototype.setSrc = function (src) { this._core.image = null; this._core.src = src; - this._core.framebuf = null; + this._core.target = null; this._loadTexture(src); }; SceneJS.TextureMap.prototype.setImage = function (image) { this._core.image = image; this._core.src = null; - this._core.framebuf = null; + this._core.target = null; this._initTexture(image); }; - SceneJS.TextureMap.prototype.setFramebuf = function (framebuf) { - if (!framebuf.type == "framebuf") { - console.log("Not a 'framebuf' node - ignoring"); + SceneJS.TextureMap.prototype.setTarget = function (target) { + if (target.type != "colorTarget" && target.type != "depthTarget") { + console.log("Target node type not compatible: " + target.type); return; } delete this._core.src; - this._core.framebuf = framebuf; + this._core.target = target; this._core.src = null; this._core.image = null; - this._core.texture = framebuf._core.framebuf.getTexture(); // TODO: what happens when the framebuf is destroyed? + this._core.texture = target._core.renderBuf.getTexture(); // TODO: what happens when the target is destroyed? this._engine.display.imageDirty = true; }; @@ -374,7 +373,7 @@ new (function () { SceneJS.TextureMap.prototype._destroy = function () { if (this._core.useCount == 1) { // Last core user - if (this._core.texture && !this._core.framebuf) { // Don't wipe out framebuf texture + if (this._core.texture && !this._core.target) { // Don't wipe out target texture this._core.texture.destroy(); this._core.texture = null; } diff --git a/src/core/scenejs.js b/src/core/scenejs.js index 272f8a74..5caad01d 100755 --- a/src/core/scenejs.js +++ b/src/core/scenejs.js @@ -284,6 +284,29 @@ var SceneJS = new (function () { return o2; }; + var hasOwnProperty = Object.prototype.hasOwnProperty; + + /** + * Tests is an object is empty + * @param obj + * @returns {boolean} + * @private + */ + this._isEmpty =function(obj) { + // null and undefined are "empty" + if (obj == null) return true; + // Assume if it has a length property with a non-zero value + // that that property is correct. + if (obj.length > 0) return false; + if (obj.length === 0) return true; + // Otherwise, does it have any properties of its own? + // Note that this doesn't handle + // toString and valueOf enumeration bugs in IE < 9 + for (var key in obj) { + if (hasOwnProperty.call(obj, key)) return false; + } + return true; + }; /** * Resets SceneJS, destroying all existing scenes