Porting Three.js Code to Canvas3D

You might have heard of our new Qt Canvas3D module that comes with Qt 5.5: Introducing Qt Canvas3D. In this post we will go through the steps needed to port your three.js content to Canvas3D. We will cover how you can create your own Canvas3D project with Qt Creator and what are the main things to take into account when porting your code. To make it more exciting, all this will be shown via a well-known three.js example.

Creating a Canvas3D project

The easiest way to start is to use the QtCanvas3D Application with Three.js wizard with Qt Creator. The wizard template will be included in Qt Creator 3.5 and onwards. If you are using an older version of Qt Creator, you can find the wizard template in the Qt folder under 5.5/Src/qtcanvas3d/creatortemplates folder. The template needs to be copied to the correct location for Qt Creator to detect it. More details about the correct location can be found from Qt Creator documentation: Locating Wizards

After you have the wizard template in place you can start the New File or Project wizard. The QtCanvas3D Application with Three.js template can be found under Applications. Just give your project a descriptive name, select the kits used for the project, add it to version control if needed, and finish the wizard. Now you have a basic Canvas3D project created. The project includes a pre-defined set of files to run a Canvas3D application including the three.js. The default implementation draws a cube so that you can verify that everything is working correctly.

Porting HTML Content to JS

Next we are going to go through the steps needed to port the original HTML content into the created project. As an example we will use the Simple Cloth Simulation from three.js. We will use here a port of three.js on top of Canvas3D.

clothsimulation

The three.js library and the other JavaScript file needed by the example are added to the resource file automatically if the project was created with the wizard. Otherwise you will need to add the files to the project's resource (.qrc) file.

The QML file created by the wizard has a Canvas3D element that already has the basic methods you will need to initialize, paint, and resize the canvas. The Canvas3D object is passed to these methods as a parameter. We will later show how the buttons to toggle different properties are created and positioned on the QML side. However, first we will go to the JavaScript side to port the actual code from the HTML file in the Simple Cloth Simulation has.

Let’s start with the initializeGL() function in our Canvas3D project. Basically this method is the place where you put the code you had in your init() function in HTML. With Canvas3D we don’t use the document object so code related to that can be skipped. Also the code related to stats is something we don’t need here. In the HTML you have used window to get the size. Code related to that should be changed so that the canvas object passed to the function is used. For example, the camera creation in the original example was done like this:

camera = new THREE.PerspectiveCamera( 30,
            window.innerWidth / window.innerHeight,
            1,
            10000 );

With Canvas3D the camera should be created in the following way:

camera = new THREE.PerspectiveCamera( 30,
            canvas.width / canvas.height,
            1,
            10000 );

There are some textures used in the example. The texture files need to be added to the project’s resource file. After this they can be loaded using the reference to the resource:

var clothTexture = THREE.ImageUtils.loadTexture(
            'qrc:/textures/patterns/circuit_pattern.png' );

The original HTML example has some shaders defined with the help of the document. We have created a WebGL Shader Library that basically has the exact same code as the original shaders did.

var clothShader = THREE.ShaderLib[ "cloth" ];
var vertexShader = clothShader.vertexShader;
var fragmentShader = clothShader.fragmentShader;

Last but not least, we replace WebGLRenderer with Canvas3DRenderer in the initializeGL() function.

renderer = new THREE.Canvas3DRenderer(
            { canvas: canvas,
              antialias: true,
              devicePixelRatio: canvas.devicePixelRatio });

The JavaScript file has an implementation for the onResizeGL() function. The default implementation there will work fine for Canvas3D projects, but you may want to do some changes based on the HTML files. In the example we ported, the implementation of the template was sufficient.

function resizeGL(canvas) {
    camera.aspect = canvas.width / canvas.height;
    camera.updateProjectionMatrix();
    renderer.setPixelRatio(canvas.devicePixelRatio);
    renderer.setSize( canvas.width, canvas.height );
}

The content of the animate() function you have in HTML can be ported to the paintGL() function with Canvas3D. You no longer need to request animation frame because Canvas3D takes care of that.

The full port of the HTML to JavaScript for Simple Cloth Simulation example can be found here: animation-cloth.js.

Adding controls to the QML side

For the Simple Cloth Simulation example we have added some buttons to the QML to do the toggling. All the buttons are positioned on the top of the window. For example, for toggling the wind we have created a button in the following way:

Button {
    id: windButton
    anchors.left: cameraButton.right
    width: parent.width / 4
    text: "Toggle Wind"
    onClicked: GLCode.toggleWind()
}

The JavaScript code was imported as GLCode for our ported application. The toggleWind() function from JavaScript code is called when the button is pressed. This function has the implementation that was defined in the hyperlink on the original HTML.

Another way to do this would be to define custom properties in your QML types, for example on the Canvas3D element. That object is already passed to the JavaScript, so you can just modify the value of the property on button press. In the way we have done the amount of needed porting effort has been reduced by reusing the existing methods from the HTML implementation.

The fully ported Simple Cloth Simulation example you can find here: ported animation-cloth example.

Mouse and Key Event Handling

The example we have covered here doesn’t have any mouse or key event handling. If you need these event handlers you can do it, for example, by adding a ControlEventSource to your Canvas3D type. ControlEventSource offers an API that is compatible to key and mouse event registration in HTML.

ControlEventSource {
    anchors.fill: parent
    focus: true
    id: eventSource
}

This needs to be passed to the JavaScript on initialization:

onInitializeGL: {
    GLCode.initializeGL(canvas3d, eventSource);
}

Then in the JavaScript initializeGL() function you should connect the desired signals to the corresponding functions. For example, for mouse and key down events the code would look like this:

eventSource.mouseDown.connect(onDocumentMouseDown);
eventSource.keyDown.connect(onDocumentKeyDown);

For the mouse event you will get the position of the pointer. For the key event you get the event and from it you can check which key was pressed using the key names used by Qt (Qt::Key).

Try it out

There’s a set of three.js examples that we’ve already ported on top of Canvas3D. You can find these examples here: qt-examples. Why not take a look at those and then give a try to port your own code?


Blog Topics:

Comments