diff --git a/Sec3Engine/demos/ParticleDemo.js b/Sec3Engine/demos/ParticleDemo.js index 2b57493..11e99ca 100644 --- a/Sec3Engine/demos/ParticleDemo.js +++ b/Sec3Engine/demos/ParticleDemo.js @@ -279,10 +279,11 @@ function startDemo() { SEC3ENGINE.run(gl); } -function webGLStart() { + function webGLStart() { - canvas = document.getElementById("glcanvas"); - initGL(canvas); + canvas = document.getElementById("glcanvas"); + SEC3.canvas = canvas; + initGL(canvas); camera = SEC3ENGINE.createCamera(CAMERA_TRACKING_TYPE); camera.goHome([0.0, 0.0, 10.0]); diff --git a/Sec3Engine/demos/SEC3DEMO.js b/Sec3Engine/demos/SEC3DEMO.js index 0c489ca..ae70a9f 100644 --- a/Sec3Engine/demos/SEC3DEMO.js +++ b/Sec3Engine/demos/SEC3DEMO.js @@ -513,6 +513,19 @@ var setupScene = function(canvasId, messageId ) { console.log("Bad GL Context!"); return; } + + // The renderer relies on multiple render targets, which require the + // WEBGL_draw_buffers extension in WebGL 1. Prefer to fail fast when the + // extension is unavailable (for example, when the browser has reduced GPU + // support enabled) rather than letting the pipeline fail later with vague + // WebGL errors. + if (!gl.getExtension("WEBGL_draw_buffers")) { + console.warn("WEBGL_draw_buffers extension unavailable; rendering will fail."); + if (msg) { + msg.innerText = "WEBGL_draw_buffers is required for SEC3DEMO."; + } + return; + } gl.viewport( 0, 0, canvas.width, canvas.height ); gl.viewportWidth = canvas.width; diff --git a/Sec3Engine/js/core/ParticleSystem.js b/Sec3Engine/js/core/ParticleSystem.js index e15338d..752609a 100644 --- a/Sec3Engine/js/core/ParticleSystem.js +++ b/Sec3Engine/js/core/ParticleSystem.js @@ -43,18 +43,39 @@ SEC3.createParticleSystem = function(specs) { var renderProgram; var stepProgram; - var interactor = {}; - interactor.attractor = [ -13.0, 6.0, 1.0, 1.0 ]; - var self = {}; + var interactor = {}; + interactor.attractor = [ -13.0, 6.0, 1.0, 1.0 ]; + var self = {}; + + // Helpers to safely access optional scene and camera globals used by legacy demos. + var getActiveCamera = function() { + if (typeof scene !== "undefined" && scene.getCamera) { + return scene.getCamera(); + } + if (typeof camera !== "undefined") { + return camera; + } + return null; + }; + + var getActiveLight = function() { + if (typeof scene !== "undefined" && scene.getLight && scene.getNumLights && scene.getNumLights() > 0) { + return scene.getLight(0); + } + return null; + }; //----------------------------------------------------------------METHODS: - var update = function() { + var update = function() { - stepParticles(); - updateShadowMap(scene.getLight(0)); - }; + stepParticles(); + var activeLight = getActiveLight(); + if (activeLight) { + updateShadowMap(activeLight); + } + }; var draw = function( light ) { renderParticles( light ); @@ -96,11 +117,14 @@ SEC3.createParticleSystem = function(specs) { gl.vertexAttribPointer(stepProgram.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(stepProgram.aVertexPosition); - var center = vec3.clone(scene.getCamera().getPosition()); - var offset = vec3.clone(camera.normal); - vec3.scale(offset, offset, -6.0); - vec3.add(center, center, offset); - gl.uniform4f(stepProgram.uAttractor, center[0], center[1], center[2], 0.4 ); + var activeCamera = getActiveCamera(); + if (activeCamera) { + var center = vec3.clone(activeCamera.getPosition()); + var offset = vec3.clone(activeCamera.normal); + vec3.scale(offset, offset, -6.0); + vec3.add(center, center, offset); + gl.uniform4f(stepProgram.uAttractor, center[0], center[1], center[2], 0.4 ); + } gl.drawArrays(gl.TRIANGLES, 0, 6); } @@ -108,9 +132,13 @@ SEC3.createParticleSystem = function(specs) { /* * draws current scene into shadow map */ - var updateShadowMap = function ( light ) { + var updateShadowMap = function ( light ) { + + if (!light || !light.cascadeFramebuffers || !light.cascadeFramebuffers[0]) { + return; + } - var fbo = light.cascadeFramebuffers[0]; + var fbo = light.cascadeFramebuffers[0]; gl.colorMask(false,false,false,false); gl.useProgram(self.shadowProgram.ref()); gl.viewport(0, 0, fbo.getWidth(), fbo.getHeight()); @@ -139,30 +167,49 @@ SEC3.createParticleSystem = function(specs) { /* * Draws all particles in system */ - var renderParticles = function ( light ) { + var renderParticles = function ( light ) { + + light = light || getActiveLight(); + if (!light || !light.cascadeFramebuffers || !light.cascadeFramebuffers[0]) { + return; + } gl.useProgram(self.renderProgram.ref()); gl.viewport(0, 0, SEC3.canvas.width, SEC3.canvas.height ); - gl.uniform3fv(renderProgram.uLightPosition, light.getPosition()); - gl.uniform3fv(renderProgram.uCPosLoc, scene.getCamera().getPosition()); - - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, positionTextures[srcIndex]); - gl.uniform1i( renderProgram.uParticlePositions, 0); - - gl.activeTexture( gl.TEXTURE1 ); - gl.bindTexture( gl.TEXTURE_2D, SEC3.gBuffer.depthTexture() ); - gl.uniform1i( renderProgram.uGDepthLoc, 1 ); - - gl.activeTexture(gl.TEXTURE2); - gl.bindTexture(gl.TEXTURE_2D, light.cascadeFramebuffers[0].depthTexture() ); - gl.uniform1i(renderProgram.uShadowMap, 2); - - gl.uniformMatrix4fv(renderProgram.uShadowMapTransform, false, light.getMVP() ); - gl.uniformMatrix4fv(renderProgram.uCameraTransform, false, scene.getCamera().getMVP()); - gl.uniform3fv(renderProgram.uLightPosition, light.getPosition() ); + if (light) { + gl.uniform3fv(renderProgram.uLightPosition, light.getPosition()); + } + var activeCamera = getActiveCamera(); + if (activeCamera) { + gl.uniform3fv(renderProgram.uCPosLoc, activeCamera.getPosition()); + } + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, positionTextures[srcIndex]); + gl.uniform1i( renderProgram.uParticlePositions, 0); + + if (SEC3.gBuffer && SEC3.gBuffer.depthTexture) { + gl.activeTexture( gl.TEXTURE1 ); + gl.bindTexture( gl.TEXTURE_2D, SEC3.gBuffer.depthTexture() ); + gl.uniform1i( renderProgram.uGDepthLoc, 1 ); + } + + if (light) { + gl.activeTexture(gl.TEXTURE2); + gl.bindTexture(gl.TEXTURE_2D, light.cascadeFramebuffers[0].depthTexture() ); + gl.uniform1i(renderProgram.uShadowMap, 2); + + gl.uniformMatrix4fv(renderProgram.uShadowMapTransform, false, light.getMVP() ); + } + var activeCamera = getActiveCamera(); + if (activeCamera) { + gl.uniformMatrix4fv(renderProgram.uCameraTransform, false, activeCamera.getMVP()); + } + if (light) { + gl.uniform3fv(renderProgram.uLightPosition, light.getPosition() ); + } //bind the default frame buffer, disable depth testing and enable alpha blending finalFBO.bind(gl); @@ -293,8 +340,14 @@ SEC3.createParticleSystem = function(specs) { renderProgram.uShadowMultiply = gl.getUniformLocation(renderProgram.ref(), "uShadowMultiply"); renderProgram.uScale = gl.getUniformLocation(renderProgram.ref(), "uScale"); gl.useProgram(renderProgram.ref()); - gl.uniformMatrix4fv(renderProgram.uShadowMapTransform, false, scene.getLight(0).getMVP()); - gl.uniformMatrix4fv(renderProgram.uCameraTransform, false, scene.getCamera().getMVP()); + var activeLight = getActiveLight(); + if (activeLight) { + gl.uniformMatrix4fv(renderProgram.uShadowMapTransform, false, activeLight.getMVP()); + } + var activeCamera = getActiveCamera(); + if (activeCamera) { + gl.uniformMatrix4fv(renderProgram.uCameraTransform, false, activeCamera.getMVP()); + } gl.uniform2fv( renderProgram.uScreenSizeLoc, vec2.fromValues(SEC3.canvas.width, SEC3.canvas.height )); gl.uniform1f(renderProgram.uLuminence, self.luminence); gl.uniform1f(renderProgram.uAlpha, self.RGBA[3]); diff --git a/Sec3Engine/js/core/camera.js b/Sec3Engine/js/core/camera.js index e2050c1..5576b73 100644 --- a/Sec3Engine/js/core/camera.js +++ b/Sec3Engine/js/core/camera.js @@ -6,10 +6,20 @@ //SEC3 is a core function interface var SEC3 = SEC3 || {}; +// Camera type constants expected by legacy demos. +var CAMERA_EXPLORING_TYPE = 0; +var CAMERA_TRACKING_TYPE = 1; + SEC3.Camera = function(){ - + SEC3.PerspProjector.call( this ); }; - -SEC3.Camera.prototype = Object.create( SEC3.PerspProjector.prototype ); \ No newline at end of file + +SEC3.Camera.prototype = Object.create( SEC3.PerspProjector.prototype ); + +// Convenience factory used by demo pages. +SEC3.createCamera = function(/* type */){ + // Only one camera type is implemented; ignore the requested type for now. + return new SEC3.Camera(); +}; diff --git a/Sec3Engine/js/core/shader-util.js b/Sec3Engine/js/core/shader-util.js index 06c0877..93d8a27 100644 --- a/Sec3Engine/js/core/shader-util.js +++ b/Sec3Engine/js/core/shader-util.js @@ -16,10 +16,22 @@ SEC3.createShaderProgram = function(){ var program = null; //shader program var callbackFunArray = []; - function loadShaderFile( gl, fileName, shader, prefix ){ + function loadShaderFile( gl, fileName, shader, prefix ){ + var drawBuffersExt = gl.getExtension("WEBGL_draw_buffers"); prefix = prefix || ""; - prefix = "#extension GL_EXT_draw_buffers: enable\nprecision highp float; \n" + prefix; - var request = new XMLHttpRequest(); + var basePrefix = "precision highp float; \n"; + + if (drawBuffersExt) { + // Only ask GLSL to enable the extension when it is available; some + // browsers (notably mobile Safari) disable it when GPU capability is + // reduced, and requesting it regardless can cause vague shader link + // failures instead of falling back to the supported feature set. + prefix = "#extension GL_EXT_draw_buffers: enable\n" + basePrefix + prefix; + } else { + prefix = basePrefix + prefix; + } + + var request = new XMLHttpRequest(); //Register a callback function // diff --git a/particleDemo.html b/particleDemo.html index b6a4c0b..7935290 100644 --- a/particleDemo.html +++ b/particleDemo.html @@ -6,19 +6,29 @@ Sec3 Particle Demo - - + + + + + - - - - - + + + + + +