Import system for C++ modules
  November 4th, 2009 by rdb

I’ve just checked in support for a shiny new ‘panda3d’ module that better organizes the C++ classes of Panda3D. This feature has been requested for a while now as many were annoyed with the long, unorganized and imports through pandac.PandaModules. Basically, it now allows you to import Panda3D classes similar to this:

from panda3d.egg import EggData
from panda3d.ode import OdeJoint
from panda3d.core import *

The current system

There are a number of Panda3D dynamic libraries that contain a Python module, for example libp3direct, libpandaexpress, libpanda, libpandaegg and libpandaode. You can directly import them from Python by importing them by their library name, and thus access the wrapped classes and functions that this library exposes to CPython.

However, not all of the functions for those classes are implemented in C++. There are a bunch of functions that are implemented in Python that provide extra functionality to the C++ classes. Those methods are just convenience methods to make your life easier, or exist to make an interface more Pythonic. Some of these methods have already been deprecated in favor of implementing it on the C++ side (or in the wrapper generator tool, interrogate.)

These extension functions are defined in pandac/libnameModules.py, where ‘libname’ is the name of the library in question. Once you import pandac/PandaModules.py, the Python code is imported from all of Panda3D’s libraries, and the extension functions are added to the classes.
The extension functions are not the only reason why the ‘pandac’ tree exists. I’ve just said that you can directly import a dynamic library from Python, but that is not entirely true – it is not possible anymore, as of Python 2.5, on Mac and Windows. (On Windows, you can still import it by renaming the dll into pyd, but that trick doesn’t work for Mac OSX dylibs.)

Therefore, we need to manually import the library by locating it first and then directly loading it via the load_dynamic function in Python’s imp module. The code to do this has been added to ‘pandac’, as that’s the place where the Panda3D libraries are usually imported.

The new system

In the new system, instead of importing the class in question from pandac.PandaModules, you will import the class from a submodule of the ‘panda3d’ module.
We did not group the classes by their source directory, as was proposed. Organizing the ‘panda3d’ module in such a way that the sub-packages represent source directories would make it needlessly complicated.
Instead, we chose to group classes by the C++ dynamic library. The name of the submodule is defined by the name of the library without the “libpanda” or “libp3″ prefix. For instance, libpandaegg maps to panda3d.egg, and libp3direct maps to panda3d.direct.

There is one exception for the libpanda and libpandaexpress libraries, they are merged into one module, panda3d.core. We chose to do this because libpanda and libpandaexpress contain the Python wrappers for the core of Panda3D – you will need these libraries to do anything useful with Panda3D. Furthermore, the distinction between libpanda and libpandaexpress is arbitrary and not meaningful to the developer.
When more libraries are added later, such as libpandaphysx, and libpandaai, we could simply add those to the list.

As for the direct tree, we have left it in it’s place. It has been suggested that it be merged into the panda3d module, but we decided not to do it, as it would become needlessly complicated and confusing. Furthermore, I think there is a benefit in keeping Panda3D’s Python modules and C++ modules separated.
We do acknowledge the unfortunate naming of the direct tree, and we might change that for a future release, but that would be a major change.

The new import system is all bundled into one file, panda3d.py. This monstrosity of Python black magic does everything needed – it locates the Panda3D libraries, adds their location to the system’s library path, registers phony modules, and dynamically loads the required libraries when somebody starts using a class from it.
As for the Python extension functions, those are not supported in the new system. The reason for this is that we are trying to phase it out, and try to move most functions to the C++ side of Panda3D.

Another cool feature is that the ‘panda3d’ module implements a lazy-loading system for it’s libraries. That means that the libraries will only get loaded when you actually use one of it’s classes. This has the advantage that libraries you don’t use will not get loaded, which can result in a slight speed and memory gain.

Rationale

Now, why did we do it? Besides the fact that it has been requested various times, there are a few benefits to it. First of all, it is better organized, allows people to type it faster and more easily, and it leads to cleaner code. Furthermore, you don’t import the libraries you don’t need, leading to slightly faster import times, and slightly less memory usage. But even more importantly, because we need it for the plugin/runtime system.

Uh, what, why? I’ll explain. When you distribute a game that can be used with the Panda3D runtime (be it the browser plugin, or be it the standalone runtime), you pack it into a .p3d file. That .p3d file will indicate which version of Panda3D it is built for. When someone runs a .p3d file through the runtime, the runtime downloads a small ultra-optimized build of Panda3D to run the provided .p3d package.

This Panda3D package that is downloaded by the runtime must be as small as possible. Because the entire Panda3D build will contain big components that not every game will use, we have split those into separate packages. For instance, a “panda3d” package containing the core, an “egg” package containing libpandaegg (usually, a packed game will only contain .bam files, so this is usually not needed), libpandaode, a “models” package containing the default models, one package per audio library, etc. The .p3d file can indicate which packages it depends on, so that the download size is kept to a minimum.

That gives a problem with the old pandac.PandaModules-style import system, though. First of all, pandac.PandaModules imports every single library, while it is not guaranteed that all libraries are on the system (as the .p3d file may not need some of them). We had to put hacky ImportError exception handlers in PandaModules.py to work around that.

Furthermore, with the old system, we have no control anymore over the imported classes. Why we would need that? Well, the game has the ability to download and install more components of Panda3D while it is running. With the new system, when for example the “egg” component is installed, we can easily add a hook into the ‘panda3d.egg’ module to be automatically updated with the new installed libpandaegg library. This allows game developers to keep the pre-download time to a minimum by installing packages on demand (for example, if only a part of the game uses ODE physics, the developer can choose to have the ‘ode’ package installed whenever the end-user chooses to run that part of the game.)

How it affects you

If you’re still awake after reading all that, you might be wondering what will happen to all the existing code. Will this break anything? The answer is, no. Right now, it’s just a small Python file that I added, which allows you to import Panda3D classes using a different convention. This is still experimental, so the ‘pandac’ tree is still around, and not even deprecated. When, perhaps after the 1.7 release series, the new system has been thoroughly tested and proved to work better, we might switch the Python code and sample programs to the new system, and recommend people to use it.
But if you don’t feel like switching, don’t worry. As we care about backward compatibility, and because most of the code is heavily dependent upon the old structure, the ‘pandac’ structure will probably be around for a loooong time.

Examples

Last but not least, some example imports showing you a bit how it works:

from panda3d.egg import EggData
 
from panda3d import ode
joint = ode.OdeJoint
 
from panda3d.core import *
tex = Texture()
tex.read(Filename("img.png"))
 
import panda3d.direct
ival = panda3d.direct.CInterval()
  November 4th, 2009 by rdb

  The Browser plugin and its side-benefits
  August 23rd, 2009 by drwr

Most people know that there’s a project underway to build a browser plugin for Panda3D. I’ve been working hard on that project for the past several weeks, and now it’s nearing completion, though there’s still more work to go. We’ll be targeting 1.7.0, or at least 1.7.1, with the first release of this effort.

I thought this would be a good time to clarify what, precisely, Panda will be getting as a result of all this.

Browser plugin

First, there’s the obvious: you’ll be able to run your Panda3D application as an embedded window in a browser. Simply embedding a Panda3D window in a browser is technically very easy; we had this working within a couple of days of starting the project. We can run on any major browser on any supported Panda3D platform: IE on Windows, or Firefox on Windows, Mac, and Linux, not to mention Safari, Chrome, Opera, and probably some of the more fiddly browsers that I haven’t tested yet. On every browser, and on every platform, it uses the full hardware capabilities of your graphics card, and runs at full frame rate; there’s no need to be limited to software rendering. (There may be some small performance impact on certain platforms; but still, it’s way faster than software rendering.)

But then there’s the bigger question of how do you get your code onto a web page in the first place?

P3D files

To solve that, we need a tool to package up your application into a convenient downloadable file. We’ve had the rudiments of such a tool for a while now, but now it’s much more fleshed out. It works sort of like packpanda does, but instead of building an exe file, it packs your application into a p3d file. Think of this like Flash’s compiled swf file. Now, you can embed your p3d file in a web page with a few lines of HTML syntax, very similar to the way you embed a Flash swf file, and there’s your Panda3D application playing on a web page!

The p3d file contains everything you need to run your application. So why should we limit ourselves to running the p3d file only on a web browser? Let’s not. We will also distribute a standalone executable called panda3d.exe (or just panda3d on Mac and Linux), which can be used to run any p3d file on your desktop, no browser required.

Panda3D runtime

Look what’s happened! Suddenly, packpanda is no longer needed. Even if you never plan to embed your application on a web page, you can pack it into a p3d file, and distribute that file instead of the executable that packpanda would have produced. The p3d file is better than the exe file, because (a) it’s a lot smaller, containing your application only, and not another copy of Panda, and (b) it’s platform-independent (provided your application is written in pure Python).

Anyone who receives your p3d file can just double-click on the icon, and if they have installed the Panda3D runtime, no matter what OS they are running, it will launch and run with their local copy of panda3d.exe. If you want to distribute it to people who might not already have the Panda3D runtime, you can either direct them a link on panda3d.org to install it, or (if you are distributing a CD-Rom title, for instance) you can ship a copy of the Panda3D runtime along with the game and install it automatically. This is similar to the way games distribute the DirectX runtime from Microsoft. Users who receive your game on CD never need to know they are running Panda3D, even though they’re still running your game from a p3d file.

iPhone integration

It’s worth mentioning in passing, too, that when this browser plugin effort is done and I get a chance to revisit the iPhone support, then I’ll be able to write this Panda3D runtime for iPhone too, which will play the very same p3d files that play on your desktop. (Realistically, you’ll still need to tune the game to make it work well on the iPhone. Rendering performance and interface requirements are very different on the iPhone than they are on your PC. But the underlying technology will be the same, and some simple games may work unchanged on both platforms.) Unfortunately, Apple has funny rules about apps that run other apps, so this Panda3D runtime application won’t be permitted on a non-jailbroken iPhone. To stay within the legitimate channels, we’ll also provide a way to compile a p3d file into a true, completely standalone iPhone app that can be distributed via the App Store and installed onto any iPhone. (This will also require you to pay Apple’s $100 developer fee, of course.)

Web security

Now, back to the browser plugin, and the p3d files. I haven’t yet talked about security: one of the things that Flash and Java are particularly good at (and ActiveX is particularly bad at) is securely running untrusted code on a web page. All three of these technologies allow a program on a web page to immediately start running as soon as you visit the page. This has obvious implications for hackers installing malware onto your computer: while Flash and Java both go through hoops to make it impossible to do this, ActiveX basically opens up your whole computer to whatever program the web page wants to run. To deal with the fallout from this, Microsoft has had to gradually close down the automatic running of ActiveX applications, and now by default prompts you before running any ActiveX program for the first time.

Clearly, we’d rather be like Flash and Java in this regard than like ActiveX. Unfortunately, it turns out that Python doesn’t really lend itself to running untrusted code securely. Unlike Flash’s ActionScript, or Java, Python was never designed from the beginning to be a secure language, and it’s ridiculously full of holes. It’s nearly impossible to prevent Python code from doing pretty much whatever it likes with the computer’s hardware. Really. There are people on the internet who think Python can be made secure (I was once one of them), and a few people who think they’ve done it, but for the most part they’re mistaken.

There are a couple of possible solutions to this. For instance, we could run the Python interpreter entirely on a virtual machine. That’s hard, for a number of reasons, but it may get easier in the future, as the technology matures. Zope has a secure python that I think may actually achieve security, but it’s also hard to use, and I’m not sure what effect it has on runtime performance. Also, maybe IronPython holds promise, since that’s basically Python running within an already-secure environment; but again this is a major change from the CPython we’ve all come to know and love.

In lieu of any of those, I’ll be implementing something like the ActiveX security model, as lame as it is. What this means to you is that you will have to sign your p3d files when you put them on a website. (”Signing” a file means marking it with a unique key that identifies you as the developer. It’s OK if it’s anonymous; the only point is that it’s unique.) You can sign all of your p3d files with the same key. When the Panda3D web plugin comes across a p3d file signed with a new key it doesn’t recognize, it will offer the user a choice of whether to approve that key before it runs the application. If the user approves the key–meaning he gives you permission to run your code on his machine–then it will run the p3d file, and it will remember the user’s approval for future p3d files signed with the same key. This is basically the same kind of approval the user has to give for downloading an exe, so it doesn’t really interfere with distribution of applications, though it does make it a little clumsy if you want to have your p3d file play automatically the first time someone comes to your website.

There are other possibilities for different security models down the road, but I think this will address the issue adequately for the short term.

JavaScript integration

Back to the good stuff. Running an app in a little box on a web page is fun for a while, but it doesn’t really buy you much unless it can interact with the web page around it.

To put this in context: Python and JavaScript are very similar languages; both support late binding, dynamic object creation, and duck typing. Panda is already pretty good at wrapping C++ objects for Python’s benefit; compared to that monstrosity, wrapping JavaScript’s objects is a warm summer breeze. So why not do it properly, and fully expose the entire JavaScript object space to direct manipulation by Python?

So, we do. The Panda3D plugin allows you to write Python code that looks like “document.getElementById(’form’).greeting.value = ‘hello’”, which is of course changing a form value on the web page, using the JavaScript DOM model. But you’re writing it in Python. In fact, anything that JavaScript can do, your Python program can do too. And the reverse is true as well: we allow you to expose the Python objects of your choice to JavaScript, to allow the web page to directly poke values into your application, or even call your Python methods!

This means you can easily integrate your Panda3D web program with any other web programs, written in JavaScript or any other web-supported language. For instance, I’ve provided a demo page that illustrates Panda3D interacting with Flash on the same page together, sending objects back and forth between Panda and Flash. When the plugin is more finished, I’ll provide this demo page and others for general inspection.

Package tool

There’s still more involved here. Packaging up your application into a single p3d file is all well and good for small applications, but what about really big apps like MMO’s, to which Panda3D owes its original raison d’être? Your users aren’t going to want to download a 200 MB p3d file every time they want to play. So, we need a system for packaging up smaller pieces of code, and downloading them individually, and/or sharing these pieces among different applications.

Enter the general package system. The same tool that can create p3d files can also build general packages: collections of Python code, compiled extension modules, and models, that aren’t an application per se, but can be requested by an application at runtime and downloaded on demand. Now when your app gets big, you have two choices: you can bundle it into one monolithic p3d file, and let the user download it all up front; or you can create one small p3d file that the user downloads first to get into the game–which might be no more than the title screen–and then you can strategically download the rest of the code in its various packages while the user continues to play the part(s) you’ve downloaded.

The package system will also automatically integrate with Panda’s built-in patchfile mechanism, allowing you to provide updates without forcing your users to completely re-download the entire application.

Also, we can use this same package system to provide several extension libraries, like pygame or wx, things which aren’t strictly part of Panda but which many applications will want to take advantage of.

You can also, of course, load models and such directly from an arbitrary URL. But you can do that today, using the vfs-mount-url Config.prc setting; that’s not a new feature of Panda (though it does take on new relevance in a web-based setting).

Panda3D versions

We haven’t talked about different versions of Panda yet. Here’s where it gets good: each p3d file knows the version of Panda3D that was used to produce it, and will only attempt to run with that same version of Panda. But the Panda3D runtime is itself version-independent. When you run a particular p3d file, the Panda3D runtime will figure out what version of Panda is required. If that version isn’t already installed on the user’s machine, it will go and download it first. It’s the job of the Panda3D runtime to manage these different versions of Panda, and to keep them all separate from each other; and it does this automatically, without the user even needing to know that it’s happening. In fact, you can even run two different versions of Panda at the same time on the same web page.

This means that once you, or any user, has installed the Panda3D runtime, you don’t need to worry about updating it when a new version of Panda comes along. It automatically updates itself as needed. This is true for the standalone panda3d.exe as well as for the browser plugin. (Every once in a while there may be an update to the runtime itself, which will require an explicit update by the user. But basic upgrades of Panda3D or any of its packages won’t require this.)

Note that the Panda3D runtime will cache all of these downloaded packages on the user’s hard disk. There has to be a limit to the amount of space Panda consumes, so the runtime will have to manage this space and automatically remove old, unused packages and versions. But the space used can be quite large by web browser standards (disks today are large enough to store hundreds of megabytes tucked away in a corner), and there’s no reason to cram all of this in the web browser’s relatively tiny cache directory. Even still, if you need more space for your app than Panda normally provides, you can take it–remember, the user has given your app permission. :)

We’ll also provide a webpage that the user can visit to manage his/her Panda3D installation, monitoring cache files and such, and tweaking global config files to his/her liking.

  August 23rd, 2009 by drwr

  New Shader System
  August 11th, 2009 by rdb

Hi all,

You’ll be pleased to know that I’ve made some big changes to the shader system for the upcoming 1.7.0 release. The new features include:

  • GLSL support (OpenGL only,  of course) and also GLSL ES (OpenGL ES only)
  • Support for separate vertex/fragment/etc programs
  • Support for structs in Cg shaders
  • Geometry shaders for Cg and GLSL (for Cg, this was provided by Schell Games)

And some more stuff. In the future, we hope to have support for CgFX as well. Maybe for 1.8 or so. Oh well, in the meantime you can watch this cute panda explode using a geometry shader.

  August 11th, 2009 by rdb