Basic WebGL and Three setup

You need to import WebGL bindings and Three.js in your script:

```
import {WebGLContext} from 'webgl'
import * as THREE from 'three/src/Three.js'
```

You then create a WebGL context (in this example, on a texture but you can also use the primary display):

```
let width=1280;
let height=720;
let gl = new WebGLContext(width, height);
```

You can also pass a WebGLContextAttributes object to the constructor:

```
let gl = new WebGLContext(width, height, {depth: false});
```

We do not cover here how to setup the output PID of your filter, see the [WebGL HowTo](webgl).


Three.js requires a console object, which is not available in GPAC JS. Let's create one:

```

let console = {};
console.loglevel=0;
console._do_log = function() {
  var msg = '';
  for (let i=0; i<arguments.length; i++) {
    if (i) msg += ' ';
    msg += '' + arguments[i];
  }
  print(this.loglevel, msg);
  this.loglevel=0;
}
console.error = function() { this.loglevel = 3; this._do_log.apply(this, arguments); };
console.warn = function() { this.loglevel = 2; this._do_log.apply(this, arguments); };
console.log = function() { this.loglevel = -1; this._do_log.apply(this, arguments); };
console.info = function() { this.loglevel = 1; this._do_log.apply(this, arguments); };
console.debug = function() { this.loglevel = 0; this._do_log.apply(this, arguments); };
globalThis.console=console;
```

Note that we need to export the console object to `globalThis` for Three.js to see it.


As previously explained, GPAC WebGL does not run in a DOM context, there is no such thing as a canvas element. 
Luckily for us, Three.js can initialize a WebGL renderer from a pre-existing canvas object and GL context.
We just need to create a basic canvas object that Three.js can interact with, and load the WebGL renderer:

```
let fake_canvas={};
fake_canvas.style={};
fake_canvas.addEventListener = function(evt_name, callback, use_capture) { print('CanvasAddEventListener: ' + evt_name); };

renderer = new THREE.WebGLRenderer( {canvas: fake_canvas, context: gl, width: width, height: height } );
renderer.setSize(width, height);
print('Three.js init !');

```

Congratulations, you now have Three.js ready to go ! Let's define a simple scene, a cube and light:

```
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(40, width / height, .5, 1000);
camera.position.set(3, 3, 3);
camera.lookAt(0, 0, 0);


const light = new THREE.DirectionalLight(0xFFFFFF, 1);
light.position.set(5, 10, 2);
scene.add(light);
scene.add(light.target);

let cube = new THREE.Mesh(
            new THREE.BoxGeometry(1, 1, 1),
            new THREE.MeshLambertMaterial({
                color: 0xFF0000
            }))

scene.add(cube);

``` 


We now need to render the scene. As GPAC WebGL runs outside of DOM, there is no `requestAnimationFrame` available. The rendering will happen in your filter process function.
Given that your filter is likely not the only one using the underlying OpenGL context, you will need to reset the state of the Three renderer before each draw call. 

The following shows animating the cube:

```
let cts=0
filter.process = function()
{
	if (filter.frame_pending) return GF_OK;
	//we MUST activate the context
	gl.activate(true);

	//let's rotate our cube
	cube.rotation.y = Math.PI * 2 * (cts%50)/50;  

	//we MUST reset Three.js renderer state
	renderer.state.reset();
	
	//and render
	renderer.render(scene, camera);
	
	//we are done, flush OpenGL pipeline and deactivate context
	gl.flush();
	gl.activate(false);

	//create packet from webgl framebuffer
	let opck = pid.new_packet(gl, () => { filter.frame_pending=false; } );
	this.frame_pending = true;
	opck.cts = cts;
	cts += 1;
	opck.send();
	return GF_OK;
}
```

And that's it !


The complete code for this example is [here](examples/three/ex1.js).