Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Sec3Engine/demos/ParticleDemo.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
13 changes: 13 additions & 0 deletions Sec3Engine/demos/SEC3DEMO.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
}
Comment on lines +522 to +526

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fail-fast check leaves demo in inconsistent state

When WEBGL_draw_buffers is unavailable, setupScene now returns early here (return at line 527), but main() still calls setKeyInputs, SEC3.renderer.init(), etc. (lines 203‑221) assuming scene, interactor, and lights were created. In browsers where the extension is missing, this path now triggers runtime exceptions (e.g., interactor.onKeyDown or renderer init using scene) instead of a graceful exit. The fail-fast should either halt the rest of main() or propagate a failure signal so the remaining initialization is skipped.

Useful? React with 👍 / 👎.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex I think without drawbuffers the move is to fail gracefully. Let's check how common it would be that they wouldn't be available these days.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

  • Made setupScene return explicit success status for WebGL context acquisition, extension availability, and FBO initialization so failures exit early with clear messaging. demos/SEC3DEMO.jsL506-L636
  • Updated main to skip key/input setup and renderer initialization when scene setup fails, preventing runtime errors when WEBGL_draw_buffers is missing. demos/SEC3DEMO.jsL203-L220

Testing

  • ⚠️ Not run (not requested).

View task →

return;
}

gl.viewport( 0, 0, canvas.width, canvas.height );
gl.viewportWidth = canvas.width;
Expand Down
123 changes: 88 additions & 35 deletions Sec3Engine/js/core/ParticleSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down Expand Up @@ -96,21 +117,28 @@ 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);
}

/*
* 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());
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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]);
Expand Down
16 changes: 13 additions & 3 deletions Sec3Engine/js/core/camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

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();
};
18 changes: 15 additions & 3 deletions Sec3Engine/js/core/shader-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down
24 changes: 17 additions & 7 deletions particleDemo.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,29 @@
<title>Sec3 Particle Demo</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<!-- glMatrix version 2.2.0 -->
<script type="text/javascript" src="/Sec3Engine/js/math/gl-matrix.js"></script>
<script src="Sec3Engine/js/core/ParticleSystem.js" type="text/javascript"></script>
<script type="text/javascript" src="/Sec3Engine/js/math/gl-matrix.js"></script>
<script src="Sec3Engine/js/core/SceneObject.js" type="text/javascript"></script>
<script src="Sec3Engine/js/core/projector.js" type="text/javascript"></script>
<script src="Sec3Engine/js/core/PerspProjector.js" type="text/javascript"></script>
<script src="Sec3Engine/js/core/ParticleSystem.js" type="text/javascript"></script>
<script src="Sec3Engine/js/core/shader-util.js" type ="text/javascript"></script>
<script src="Sec3Engine/js/core/webgl-util.js" type ="text/javascript"></script>
<script src="Sec3Engine/js/core/camera.js" type ="text/javascript"></script>
<script src="Sec3Engine/js/core/cameraInteractor.js" type ="text/javascript"></script>
<script src="Sec3Engine/js/core/particleInteractor.js" type ="text/javascript"></script>
<script src="Sec3Engine/js/core/texture.js" type ="text/javascript"></script>
<script src="Sec3Engine/js/core/extensions.js" type ="text/javascript"></script>
<script src="Sec3Engine/js/core/geometry.js" type ="text/javascript"></script>
<script src="/Sec3Engine/js/math/UI.js" type="text/javascript"></script>
<script src="/Sec3Engine/js/math/math.js" type="text/javascript"></script>
<script src="ParticleSystem/ParticleDemo.js" type="text/javascript"></script>
<script src="Sec3Engine/js/core/extensions.js" type ="text/javascript"></script>
<script src="Sec3Engine/js/core/geometry.js" type ="text/javascript"></script>
<script src="/Sec3Engine/js/math/UI.js" type="text/javascript"></script>
<script src="/Sec3Engine/js/math/math.js" type="text/javascript"></script>
<script type="text/javascript">
// Legacy demos expect the engine API under the SEC3ENGINE name.
// Merge any helpers attached to the old namespace (for example
// ParticleInteractor) and expose the combined API under both names.
SEC3ENGINE = Object.assign(SEC3, SEC3ENGINE || {});
SEC3 = SEC3ENGINE;
</script>
<script src="Sec3Engine/demos/ParticleDemo.js" type="text/javascript"></script>

</head>

Expand Down