Monster Evolution in Qt: Episode 2 (Attack of the SquirrelFish)

To make our wonderful JavaScript world a bit more attractive, I have decided to continue this monster series in a much more pleasant adventure. So herewith semiofficially I changed the first episode's title to The QtScript Menace. Make sure you read it, and readers, here is the second episode: Attack of the SquirrelFish.

Basically it is still the same little Qt program which can run Dean's Monster Evolution demo. However, while last time we utilized QtScript to execute the monster script, here we use QtWebKit. This is still different that running the demo directly in a QtWebKit-based web browser. Specifically, we just use the JavaScript engine in QtWebKit, but not the web page, frame, view, including its canvas implementation. So here lies the answer to those who often wonder "Can I run JavaScript code without running any visual widget at all?". The answer: yes.

The trick involves two parts: passing something from your C++ code to the script word and getting something back from the script world to C++. Both of them are not of concern if we use QtScript, mainly because QtScript offers bridging functionalities via QScriptValue, QScriptEngine::newQObject, QScriptEngine::newFunction and so on. With QtWebKit, available at our disposal are two key functions: QWebFrame::addToJavaScriptWindowObject and QWebFrame::evaluateJavaScript.

As usual, the example is in the git repository, check webmonster subdirectory. Rather than using git, you are of course free to download the archive (find the snapshot link). Qt 4.5 is recommended to have the blazing-fast JavaScript. If it is slow on your box, try using OpenGL graphics system. If you don't fancy C++, run the PyQt version thanks to David Boddie.

Like in the first episode, the program prepares some hooks and fake implementations of Canvas (enough to render what the script wants), downloads monster.js, then executes it. You'd soon notice the similarities with the QtScript version. Basically a function called drawPolygons will be called back at the end of every frame. This function is a slot of our main widget, QtWebKit binding knows this and will invoke the slot, even converting all the polygon buffer object to QVariantList.

Performance should be better than running it with QtScript only. On typical laptops these days, 10 fps is not unreachable. In general, it should run roughly as fast, if not faster, compared to running the demo in a QtWebKit-based browser. If you bother to run your profiler, one of the bottleneck is converting JavaScript objects to QVariant. This is also a reason why JavaScriptCore-based QtScript makes sense: we just need to return everything as a QScriptValue (which wraps any JavaScript objects nicely) and hence get away with excessive heap allocations as well as value conversions.

In a related note, if you are confused when analyzing the minified monster.js, take a look at pre3d. It is the JavaScript-based 3-D engine behind the monster demo, released recently by its author Dean under the BSD license. Grab the code while it's still hot and surpise us with your creativity!

Stay tuned for the third and the last episode in this Monsterwalker family saga. As with any good story, there we will switch to the dark side. I have the title ready, it is secured in a safe deposit box somewhere in Zurich. Guess the title right, and I owe you a drink :-)

Another little FAQ:

Q: Does this mean QtWebKit canvas will be improved?
A: You did not bother to read The QtScript Menace, did not you?

Q: What is this SquirrelFish?
A: Codename for the JavaScript engine that powers WebKit. See what I wrote before for details.

Q: Shouldn't you start with A New Hope instead?
A: myName.contains("Lucas") returns false.

Blog Topics: