I have encountered a unknown to me anomaly in Javascript coding: I completely don't know why, but there are some functions that a compiler just won't return from, and it just 'sits' there, even though after the '}' bracket there are next lines of code.
Look at following example - I have a main function which contains calls to functions like initShaders(), initBuffers() or drawScene() one after another. And the inside of troublesome initShaders() looks like this:
<!DOCTYPE html>
<html>
<canvas id = 'c' width = '500' height = '500'></canvas>
<script src ='js/webgl-utils.js'></script>
<script src ='js/webgl-debug.js'></script>
<!-- MAIN WEBGL SCRIPT -->
<script>
var gl;
var offset;
function initWebGL() {
// Create space and try to create context for WebGL
var c = document.getElementById('c');
gl = null;
gl = c.getContext('experimental-webgl');
// Check if browser supports WebGL
if (!gl) {
alert("Unable to initialize WebGL. Your browser may not support it.");
}
// Continue only if WebGL is available and working
else {
// Javascript console debug tool
gl = WebGLDebugUtils.makeDebugContext(gl, throwOnGLError, logAndValidate);
offset = [1, 1];
// Choose background color
gl.clearColor(0, 0, 0, 1);
initShaders();
initBuffers();
drawScene();
}
}
var program;
function initShaders() {
program = gl.createProgram();
// Call this function when loadProgram function will load shaders
function initProgram(loadedProgram) {
program = loadedProgram;
gl.useProgram(program);
/*-- Specify ATTRIBUTES locations --*/
// Specify locations of variables passed to shader program
program.vertexPosAttrib = gl.getAttribLocation(program, 'aVertexPosition');
// Enable this attribute as an array
gl.enableVertexAttribArray(program.vertexPosAttrib);
program.offsetUniform = gl.getUniformLocation(program, 'uOffset');
// Set the format of an in-data
gl.vertexAttribPointer(program.vertexPosAttrib, vertexPosBuffer.itemSize, gl.FLOAT, false, 0 ,0);
/*-- Specify UNIFORMS locations --*/
// TODO exit this function...
};
// Load the shaders from the file
loadProgram('shaders/vshader-00.txt', 'shaders/fshader-00.txt', program, initProgram);
}
var vertexPosBuffer;
function initBuffers() {
vertexPosBuffer = generateQuad();
}
function drawScene() {
// Clear the scene before rendering
gl.clear(gl.COLOR_BUFFER_BIT);
/*-- Pass UNIFORMS to shader program --*/
gl.uniform2f(program.offsetUniform, offset[0], offset[1]);
// Draw some triangles with predefined amount of vertices
gl.drawArrays(gl.TRIANGLE_STRIP, 0, vertexPosBuffer.numItems);
}
initWebGL();
</script>
</html>
The thing is that after the command 'gl.vertexAttribPointer(...)' it just do nothing, while it should exit this function, go after loadProgram function and keep going. It works fine if I put initBuffers() at the end of loadProgram() and then call drawScene() inside innitBuffers(). But this is just unpractical. What is the cause of this issue?
EDIT: PS program variable has been globally declared earlier.
EDIT2: Pasted the whole code from the main file for better clarity.
In JavaScript, most things are non-blocking. the loadProgram call will return immediately. However by then the program is not loaded yet. When it is loaded, the callback initProgram will be called.
JavaScript is event driven and therefore the control flow may be a bit harder to follow.
If this is running in a browser, you may want to start a timer at the end of initProgram which should then call your 'main' function when it is fired. This mechanism would be somewhat similar to what is happening with loadProgram which calls initShaders when it is done loading the program.
Related
I need to show two animations using WebGL on the page. Do I need to instantiate multiple shaders, or is there a way to reuse one shader? They are using the same program (so not entirely different animations). They both need to react to mouse events.
something like this
window.onload = function() {
main('canvas1');
main('canvas2');
}
function main(element) {
// Get A WebGL context
var canvas = document.getElementById(element);
var gl = getWebGLContext(canvas);
if (!gl) {
return;
}
// setup GLSL program
vertexShader = createShaderFromScriptElement(gl, "2d-vertex-shader");
fragmentShader = createShaderFromScriptElement(gl, "2d-fragment-shader");
program = createProgram(gl, [vertexShader, fragmentShader]);
gl.useProgram(program);
...
}
I don't understand the derogaratory remarks. I asked a theoretical question about webgl, not particular code.
I ran this code and it works. So now all I need to know is if I can use mouse events on both. I'd be surprised if that didn't work.
This was already answered by someone else:
Is it possible to have two WebGL contexts on the same page?
WebGL cannot share resources over multiple contexts. But you can create an object with its own gl variable, and then have multiple contexts on the page. The only limit I see now is that somehow it's intensive for the system and it won't allow more than 16 concurrent contexts.
This one of the phaser examples, what Im trying to do is to load the image again when it reach the end o the frame. Can somebody explain how to do this
var game = new Phaser.Game(1500, 200, Phaser.AUTO, 'phaser-example', { preload: preload, create: create });
function preload() {
// You can fill the preloader with as many assets as your game requires
// Here we are loading an image. The first parameter is the unique
// string by which we'll identify the image later in our code.
// The second parameter is the URL of the image (relative)
game.load.image('Car', 'car.jpg');
}
function create() {
// This creates a simple sprite that is using our loaded image and
// displays it on-screen
// and assign it to a variable
var image = game.add.sprite(0, 0, 'Car');
game.physics.enable(image, Phaser.Physics.ARCADE);
image.body.velocity.x=750;
if (image>game.width)
{
game.load.image('Car', 'car.jpg');
var image = game.add.sprite(0, 0, 'Car');
}
}
I assume the above is just pseudo code because there are lots of little errors. But there are a number of issues with this approach.
If you know what the new image needs to be in advance, then preload it up front and then use loadTexture to apply it:
function preload() {
game.load.image('Car', 'car.jpg');
game.load.image('Pumpkin', 'pumpkin.png');
}
...
function update() {
if (car.x > game.width) {
car.loadTexture('Pumpkin');
}
}
Also some other things to watch out for:
1) Only change the texture if it's not already set (in the example above it will run loadTexture over and over, unless you reset the car.x coordinate)
2) If you can't preload the image up front, then you can load it during play. Look at the "Loader Events" example code for details.
3) You need to check the sprite position in update and not create (as it won't have moved at all by that point).
I'm using glfx.js to edit my image but when I'm trying to get that image's data using the toDataURL() function I get a blank image (width the same size as the original image).
The strange thing is that in Chrome the script works perfect.
What I want to mention is that the image is loaded in canvas using the onload event:
img.onload = function(){
try {
canvas = fx.canvas();
} catch (e) {
alert(e);
return;
}
// convert the image to a texture
texture = canvas.texture(img);
// draw and update canvas
canvas.draw(texture).update();
// replace the image with the canvas
img.parentNode.insertBefore(canvas, img);
img.parentNode.removeChild(img);
}
Also my image's path is on the same domain;
The problem (in Firefox) is when i hit the save button. Chrome returns the expected result but Firefox return this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA7YAAAIWCAYAAABjkRHCAAAHxklEQVR4nO3BMQEAAADCoPVPbQZ/oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
... [ lots of A s ] ...
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAzwD6aAABkwvPRgAAAABJRU5ErkJggg==
What could cause this result and how can I fix it?
Most likely there's some async event between the time you draw to the canvas and the time you call toDataURL. By default the canvas is cleared after every composite. Either prevent the canvas from being cleared by creating the WebGL context with preserveDrawingBuffer: true as in
var gl = canvas.getContext("webgl", {preserveDrawingBuffer: true});
or make sure toDataURL is called before exiting whatever event you're using to render. For example if you do this
function render() {
drawScene();
requestAnimationFrame(render);
}
render();
And somewhere else do this
someElement.addEventListener('click', function() {
var data = someCanvas.toDataURL();
}, false);
Those 2 events, the animation frame, and the click are not in sync and the canvas may be cleared between calling them. Note: The canvas won't appear cleared as it's double buffered but the buffer toDataURL and other commands that effect that buffer are looking at is cleared.
The solution is either use preserveDrawingBuffer or make your call to toDataURL inside the same event as rendering. For example
var captureFrame = false;
function render() {
drawScene();
if (captureFrame) {
captureFrame = false;
var data = someCanvas.toDataURL();
...
}
requestAnimationFrame(render);
}
render();
someElement.addEventListener('click', function() {
captureFrame = true;
}, false);
What's the point of preserveDrawingBuffer: false which is the default? It can be significantly faster, especially on mobile to not have to preserve the drawing buffer. Another way to look at it is the browser needs 2 copies of your canvas. The one you're drawing to and the one it's displaying. It has 2 ways to deal with these 2 buffers. (A) double buffer. Let you draw to one, display the other, swap the buffers when you're done rendering which is inferred from exiting any event that issued draw commands (B) Copy the contents of the buffer you're drawing to do the buffer that's being displayed. Swapping is much faster than copying. So, swapping is the default. It's up to the browser what actually happens. The only requirement is that if preserveDrawingBuffer is false that the drawing buffer get cleared after a composite (which is yet another async event and therefore unpredictable) if preserveDrawingBuffer is true then it must copy so that the drawingbuffer's contents is preserved.
Note that once a canvas has a context it will always have the same context. So in other words let's say you change the code that initializes the WebGL context but you still want to set preserveDrawingBuffer: true
There are at least 2 ways.
find the canvas first, get a context on it
since the code later will end up with the same context.
<script>
document.querySelector('#somecanvasid').getContext(
'webgl', {preserveDrawingBuffer: true});
</script>
<script src="script/that/will/use/somecanvasid.js"></script>
Because you've already created a context for that canvas whatever script comes after will get the same context.
augment getContext
<script>
HTMLCanvasElement.prototype.getContext = function(origFn) {
return function(type, attributes) {
if (type === 'webgl') {
attributes = Object.assign({}, attributes, {
preserveDrawingBuffer: true,
});
}
return origFn.call(this, type, attributes);
};
}(HTMLCanvasElement.prototype.getContext);
</script>
<script src="script/that/will/use/webgl.js"></script>
In this case any webgl context created after augmenting the getContext will have preserveDrawingBuffer set to true.
Requirement:
Now: Draw on a Canvas, and hit Save (store Canvas state/drawing offline - but NOT as image).
Later: Open up the Canvas with previously saved drawing showing, and continue to draw again.
For drawing we normally use code as follows:
canvas = document.getElementById('can');
ctx = canvas.getContext("2d");
...
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
....
In order to restore Canvas state later - exporting to Image does not help.
I want to restore the Canvas to it's original state to continue editing the drawing at a later date.
I guess, the Canvas context has to be exported and stored offline - how?
Your best shot here is to use a Proxy that will both store the draw commands and perform the drawings.
Since the browser support for Proxy is very bad (only FF as of today), you'll have to build the Proxy yourself, either by using nosuchmethod, or by building a new brand new WatchedContext Class out of the Context2D.
I took the last solution (WatchedContext Class) for this short demo :
function WatchedContext(hostedCtx) {
this.commands= [];
Context2dPrototype = CanvasRenderingContext2D.prototype;
for (var p in Context2dPrototype ) {
this[p] = function(methodName) {
return function() {
this.commands.push(methodName, arguments);
return Context2dPrototype[methodName].apply(hostedCtx, arguments);
}
}(p);
}
this.replay=function() {
for (var i=0; i<this.commands.length; i+=2) {
var com = this.commands[i];
var args = this.commands[i+1];
Context2dPrototype[com].apply(hostedCtx, args);
}
}
}
Obviously you might need some other method (start/stop recording, clear, ...)
Just a small example of use :
var cv = document.getElementById('cv');
var ctx=cv.getContext('2d');
var watchedContext=new WatchedContext(ctx);
// do some drawings on the watched context
// --> they are performed also on the real context
watchedContext.beginPath();
watchedContext.moveTo(10, 10);
watchedContext.lineTo(100, 100);
watchedContext.stroke();
// clear context (not using the watched context to avoid recording)
ctx.clearRect(0,0,100,1000);
// replay what was recorded
watchedContext.replay();
You can see here :
http://jsbin.com/gehixavebe/2/edit?js,output
That the replay does work, and the line is re-drawn as a result of replaying the stored commands.
For storing offline you can either store the commands locally using localStorage or store them remotely on a server an use AJAX calls or similar.
I'm using some javascript to allow users to dynamically load a sketch on click to a canvas element using:
Processing.loadSketchFromSources('canvas_id', ['sketch.pde']);
If I call Processing.loadSketchFromSources(...) a second (or third...) time, it loads a second (or third...) .pde file onto the canvas, which is what I would expect.
I'd like for the user to be able to click another link to load a different sketch, effectively unloading the previous one. Is there a method I can call (or a technique I can use) to check if Processing has another sketch running, and if so, tell it to unload it first?
Is there some sort of Processing.unloadSketch() method I'm overlooking? I could simply drop the canvas DOM object and recreate it, but that (1) seems like using a hammer when I need a needle, and (2) it results in a screen-flicker that I'd like to avoid.
I'm no JS expert, but I've done my best to look through the processing.js source to see what other functions may exist, but I'm hitting a wall. I thought perhaps I could look at Processing.Sketches.length to see if something is loaded already, but simply pop'ing it off the array doesn't seem to work (didn't think it would).
I'm using ProcessingJS 1.3.6.
In case someone else comes looking for the solution, here's what I did that worked. Note that this was placed inside a closure (not included here for brevity) -- hence the this.launch = function(), blah blah blah... YMMV.
/**
* Launches a specific sketch. Assumes files are stored in
* the ./sketches subdirectory, and your canvas is named g_sketch_canvas
* #param {String} item The name of the file (no extension)
* #param {Array} sketchlist Array of sketches to choose from
* #returns true
* #type Boolean
*/
this.launch = function (item, sketchlist) {
var cvs = document.getElementById('g_sketch_canvas'),
ctx = cvs.getContext('2d');
if ($.inArray(item, sketchlist) !== -1) {
// Unload the Processing script
if (Processing.instances.length > 0) {
// There should only be one, so no need to loop
Processing.instances[0].exit();
// If you may have more than one, then use this loop:
for (i=0; i < Processing.instances.length; (i++)) {
// Processing.instances[i].exit();
//}
}
// Clear the context
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, cvs.width, cvs.height);
// Now, load the new Processing script
Processing.loadSketchFromSources(cvs, ['sketches/' + item + '.pde']);
}
return true;
};
I'm not familiar with Processing.js, but the example code from the site has this:
var canvas = document.getElementById("canvas1");
// attaching the sketchProc function to the canvas
var p = new Processing(canvas, sketchProc);
// p.exit(); to detach it
So in your case, you'll want to keep a handle to the first instance when you create it:
var p1 = Processing.loadSketchFromSources('canvas_id', ['sketch.pde']);
When you're ready to "unload" and load a new sketch, I'm guessing (but don't know) that you'll need to clear the canvas yourself:
p1.exit();
var canvas = document.getElementById('canvas_id');
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
// Or context.fillRect(...) with white, or whatever clearing it means to you
Then, from the sound of things, you're free to attach another sketch:
var p2 = Processing.loadSketchFromSources('canvas_id', ['sketch2.pde']);
Again, I'm not actually familiar with that library, but this appears straightforward from the documentation.
As of processing.js 1.4.8, Andrew's accepted answer (and the other answers I've found in here) do not seem to work anymore.
This is what worked for me:
var pjs = Processing.getInstanceById('pjs');
if (typeof pjs !== "undefined") {
pjs.exit();
}
var canvas = document.getElementById('pjs')
new Processing(canvas, scriptText);
where pjs is the id of the canvas element where the scrips is being run.