Titta Heikkala

Porting Three.js Code to Canvas3D

Published Friday June 5th, 2015
8 Comments on Porting Three.js Code to Canvas3D
Posted in Qt Canvas3D | Tags: , , ,

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?

Do you like this? Share it
Share on LinkedInGoogle+Share on FacebookTweet about this on Twitter

Posted in Qt Canvas3D | Tags: , , ,

8 comments

Scorp1us says:

Very cool. I don’t do 3D (yet) but will in a few months. I hope all JS libraries are as easy to use in QML as this.

Titta Heikkala Titta Heikkala says:

You could check out the Qt documentation related to integrating QML and JavaScript: http://doc.qt.io/qt-5/qtqml-javascript-topic.html

kang says:

how to avoid flickering update when window not active and to get paint again when window activated, this seems bugs on classic theme windows 7 .

Titta Heikkala Titta Heikkala says:

I’m sorry to hear you are seeing flickering with the example. I’ve tested the ported application with Windows 7 and Classic theme but I did not see the flickering issue there. It might be that the display driver is causing it for you.
The paintGL() method is called in response to the paintGL signal and this happens continuously. So there’s no need to trigger the painting explicitly.

kang says:

Thanks for kind answer, I was using Intel HD graphics driver version 9.17.10.2867 and using angle build mingw x64

spise says:

On Android (Nexus 7 2012) animation-cloth example running 11-12 fps in Web-browser (both Chrome and Firefox) and only 5-6 fps in Qt 5.5 application.

Pasi Keränen Pasi Keränen says:

We’ve not benchmarked the implementation that much. We’ve profiled it though and made sure Canvas3D methods are not at the top of the methods using CPU time. One thing to check is that you are building a release version. We’ve observed quite big performance difference between debug and release builds of Canvas3D and Canvas3D apps on some platforms. You can also try using QtQuick Compiler, that can help a little bit with the performance, especially the startup time.

Neoreturn says:

It looks promising ! I can’t wait for a final version.
There is still a lot of work to do to integrate 3rd party Js libraries seamlessly. For example i tried to integrate PouchDb library (to be used with the QML Sqlite localstorage ) so i could remove couchbaselite ( which is somehow boilerplate) from my project.
But I didn’t succeeded so I kept Couchbaselite XD

Commenting closed.

Get started today with Qt Download now