Posts Tagged ‘Bullet’

July 2018 Development Update

Sunday, August 26th, 2018 by fireclaw

Despite the vacation period, the developers have not remained idle in July. Here is an update on some of the new developments.

Collision shapes

While the internal collision system provides many useful tests between various collision solids, the CollisionTube solid (representing a capsule shape) in particular was only really useful as an “into” collision shape. Many of you have requested for more tests to be added so that it can also be used as a “from” shape, since many see it as a good fit for use with character bodies. Earlier, we had already added tube-into-plane and tube-into-sphere tests. We have now also extended this to include tube-into-tube tests and tube-into-box tests. We have also added a line-into-box test to complete the suite of CollisionBox tests.

For those who are using Bullet physics instead of the internal collision system, we have also extended the ability to convert collision solids from the Panda3D representation to the Bullet representation to include CollisionTube and CollisionPlane as well. These solids can now be easily converted to a BulletCapsuleShape and BulletPlaneShape, respectively. This way you can add these shapes directly to your .egg models and load them into your application without needing custom code to convert them to Bullet shapes.

Depth buffer precision

As most Panda3D programmers will know, two important variables to define when configuring a camera in any game are the “near” and “far” distances. These determine the range of the depth buffer; objects at the near distance have a value in the depth buffer of 0.0, whereas objects at the far plane have a value of 1.0. As such, they also determine the drawing range: objects that fall outside this range cannot be rendered. This is fundamental to how perspective rendering works in graphics APIs.

As it happens, because of the way the projection matrix is defined, it is actually possible to set the “far” distance to infinity. Panda3D added support for this a while ago already. Because of the reciprocal relationship between the distance to the camera and the generated depth value, the near distance is far more critical to the depth precision than the far distance. If it is too low, then objects in the distance will start to flicker as the differences in depth values between different objects becomes 0; the video card can no longer tell the difference between their respective distances and gets confused about which surface to render in front of the other. This is usually known as “Z-fighting”.

This is a real problem in games that require a very large drawing distance, while still needing to render objects close to the camera. There are a few ways to deal with this.

One way people usually try to resolve this is by increasing the precision of the depth buffer. Instead of the default 24 bits of depth precision, we can request a floating-point depth buffer, which has 32 bits of depth precision. However, since 32-bit floating-point numbers still have a 24-bit mantissa, this does not actually improve the precision by that much. Furthermore, due to the exponential nature of floating-point numbers, most precision is actually concentrated near 0.0, whereas we actually need precision in the distance.

As it turns out, there is a really easy way to solve this: just invert the depth range! By setting the near distance to infinity, and the far distance to our desired near distance, we get an inverted depth range whereby a value of 1.0 is close to the camera and 0.0 is infinitely far away. This turns out to radically improve the precision of the depth buffer, as further explained by this NVIDIA article, since the exponential precision curve of the floating-point numbers now complements the inverse precision curve of the depth buffer. We also need to swap the depth comparison function so that objects that are behind other objects won’t appear in front of them instead.

There is one snag, though. While the technique above works quite well in DirectX and Vulkan, where the depth is defined to range from 0.0 to 1.0, OpenGL actually uses a depth range of -1.0 to 1.0. Since floating-point numbers are most precise near 0.0, this actually puts all our precision uselessly in the middle of the depth range:

This is not very helpful, since we want to improve depth precision in the distance. Fortunately, the OpenGL authors have remedied this in OpenGL 4.5 (and with the GL_ARB_clip_control extension for earlier versions), where it is possible to configure OpenGL to use a depth range of 0.0 to 1.0. This is accomplished by setting the gl-depth-zero-to-one configuration variable to `true`. There are plans to make this the default Panda3D convention in order to improve the precision of projection matrix calculation inside Panda3D as well.

All the functionality needed to accomplish this is now available in the development builds. If you wish to play with this technique, check out this forum thread to see what you need to do.

Double precision vertices in shaders

For those who need the greatest level of numerical precision in their simulations, it has been possible to compile Panda3D with double-precision support. This makes Panda3D perform all transformation calculations with 64-bit precision instead of the default 32-bit precision at a slight performance cost. However, by default, all the vertex information of the models are still uploaded as 32-bit single-precision numbers, since only recent video cards natively support operations on 64-bit precision numbers. By setting the vertices-float64 variable, the vertex information is uploaded to the GPU as double-precision.

This worked well for the fixed-function pipeline, but was not supported when using shaders, or when using an OpenGL 3.2+ core-only profile. This has now been remedied; it is possible to use double-precision vertex inputs in your shaders, and Panda3D will happily support this in the default shaders when vertices-float64 is set.

Interrogate additions

The system we use to provide Python bindings for Panda3D’s C++ codebase now has limited support for exposing C++11 enum classes to Python 2 as well by emulating support for Python 3 enums. This enables Panda3D developers (and any other users of Interrogate) to use C++11 enum classes in order to better wrap enumerations in the Panda3D API.

Multi-threading

We have continued to improve the thread safety of the engine in order to make it easier to use the multi-threaded rendering pipeline. Mutex lock have been added to the X11 window code, which enables certain window calls to be safely made from the App thread. Furthermore, a bug was fixed that caused a crash when taking a screenshot from a thread other than the draw thread.

June 2018 Development Update

Tuesday, July 31st, 2018 by fireclaw

Due to the vacation period, this post is somewhat delayed, but the wait is finally at an end. Here is the new update with a selection of the developments in June.

OpenGL on macOS

At the Apple WWDC in June of this year, Apple formally announced the deprecation of the OpenGL graphics API, in favor of an Apple-only graphics API called Metal. This move puzzled many as a lot of software is relying on OpenGL for macOS support, including Panda3D, and investing significant resources into developing a whole new rendering back-end to support just a relatively small segment of the market is hard to justify.

While it seems likely that—despite the deprecation notice—Apple will continue to include OpenGL support as a part of macOS, we will need to start looking at another approach for maintaining first-class support for high-end graphics on macOS. It would be nice if there were one next-gen graphics API that would be well-supported on all platforms going forward.

Enter MoltenVK. This is an implementation of the cross-platform Vulkan API for macOS and iOS, implemented as a wrapper layer on top of Apple’s Metal API. It has recently been open sourced by the Khronos group, in an effort to make Vulkan available on every operating system. Despite being a wrapper layer, it was still found by Valve to have increased performance benefits over plain OpenGL. This will let us focus our efforts on implementing Vulkan and thereby support all platforms.

Accordingly, we have increased the priority towards developing a Vulkan renderer, and it has made several strides forward in the past months. It is still not quite able to render more than the simplest of sample programs, however. We will keep you updated as developments unfold.

Mouselook smoothness improved

In order to implement camera movement using the mouse, many applications use move_pointer to reset the cursor to the center of the window every frame. However, when the frame rate gets low, the mouse movement can become choppy. This is usually only an issue occurring on Windows as other platforms support the M_relative mouse mode, which obviates the need to reset the mouse cursor. For all those who can’t or don’t use this mode, a significant improvement in smoothing this movement has now been worked out.
In previous versions, small movements that occurred between the last iteration of the event loop and the call to move_pointer could have been ignored, causing the mouselook to become choppy. This has been fixed by changing the get_pointer() method to always obtain the latest mouse cursor position from the operating system, resulting in more accurate mouse tracking. In the future, we will further improve on this by emulating relative mouse mode on Windows and perhaps even adding a new interface for handling mouselook.

Grayscale and grayscale-alpha support with FFmpeg

Panda3D’s implementation of FFmpeg now gained the ability to load videos that only contain grayscale colours more efficiently, greatly reducing the memory usage compared to the previous approach of converting all videos to full RGB. This is also extended to videos that have a grayscale channel as well as an alpha channel for transparency. Do note, however, that not all video formats support grayscale pixel formats.

Removing ‘using namespace std’ from Panda3D headers

C++ users, take note: besides the changes to add better C++11 support, we have also eliminated the bad practice of having using namespace std; in the public Panda3D headers. This prevents the entire std namespace from being pulled in and causing namespace clashes. If your codebase relied on being able to use standard types without an explicit std:: prefix, you may need to add using namespace std to your own headers. It would be even better to fully qualify your references to standard types and functions with an std:: prefix or—where appropriate—pull in specific types with individual using std::x; statements.

Bullet Vehicle chassis-to-wheel synchronisation

Using Bullet’s BulletVehicle class to simulate car physics had a problem with syncing the wheels to the chassis if the wheels were parented to the the vehicle’s RigidBodyNode. The wheel models would always be one frame ahead of the chassis model, resulting in visible artifacts as the wheels would appear to move away from the chassis. Especially if the vehicle accelerates to a significant speed, the wheels may appear to drift quite far from the vehicle. This bug was fixed thanks to a contributor.

February 2018 Development Update

Tuesday, March 13th, 2018 by rdb

We again bring you some of the highlights of the new developments in the past month. This is however not an exhaustive list of changes, which can be obtained from the commit logs in the GitHub repository.

Android developments

We’re happy to announce that the Android port has made great strides forward in the past weeks. Most importantly, there is now a complete Python interpreter added to the Panda3D app, so that Python applications can be run directly from the Android device. Many of the sample programs now run as well as they do on the desktop.

It is now even possible to compile Panda3D entirely on an Android device, using an app that provides a Linux-like terminal environment. One free and open-source example is termux, which provides a package manager that gives access to an impressive suite of software, including the compilers needed to compile Panda3D.

Furthermore, using a bash script that we provide, it is also possible to launch Panda3D from termux and pipe the output from the interpreter app back to the terminal, so that you can develop Panda apps on Android the same way as you would on the desktop.

Eventually, we intend to make it possible to use the new deploy-ng system to produce an Android package from any operating system, making it easy to deploy your application to both desktop and mobile platforms.

In a separate effort, a commercial port of Panda3D to Android has been released on the app store, called Cub3D. This is an impressively complete development environment for Panda3D apps, and can be used today to develop and run Panda apps on Android. (This software is developed by a third-party company that is not affiliated with the Panda3D team.)

Roaming Ralph running on an Android tablet

Roaming Ralph running on an Android tablet

Ball in Maze running on an Android watch with Cub3D

Ball in Maze running on an Android watch with Cub3D



CMake improvements

Behind the scenes, we have been working on adding support for the CMake build system on the cmake branch. CMake is a popular cross-platform build system that is rapidly becoming the de facto standard for open-source projects, and it is eventually intended to replace our own makepanda build script. This will make it easier to build Panda from source and to customize the build process. Last month, the CMake port saw some updates and improvements, bringing us closer to this goal.

Bullet thread safety

Users of the Bullet physics engine were previously encountering crashes when using Bullet in conjunction with the multithreaded render pipeline. This is caused by the fact that the Bullet library is not thread safe. To remedy this, we have added locking in the Panda3D wrapper for Bullet that protects the Bullet library from access by multiple threads, which makes it safe to use the Bullet integration in a multithreaded program.

November 2017 Development Update

Monday, December 4th, 2017 by fireclaw

Hello and welcome to our first official informative post in this series of Panda3D development blog posts. We’ll showcase newly added features as well as behind-the-scenes information and other changes in November, 2017.

Note that this is just an overview highlighting a few of the bigger features that were added this month. It is by no means a comprehensive list of all the features and bugfixes that were added. For a full list of changes, check out the git commit log.

What happened in the last month

Unit testing

Panda3D got initial test coverage utilizing the mature and full-featured PyTest toolset. This means that core features of the engine will always be tested for their intended functionality and bugs in those places will be caught earlier, helping to keep the engine in an overall good and stable state. Currently there is basic test coverage for NodePath, serializability of Bullet shapes, various interrogate features and some display configurations, but we plan to add more in the future.

Coroutines and async/await

The most exciting change this month is perhaps the support for coroutines and async/await. This is a new syntax introduced in Python 3.5 to simplify asynchronous programming. It can be hard to understand at first, but the possibilities are quite interesting.

Simply put, a “coroutine” is a function that can be temporarily suspended, pending an asynchronous operation, and continued at a later time. For example, if you use a coroutine to set up your scene, you can temporarily suspend it while a model is being loaded asynchronously, so that other operations can continue without blocking the rendering loop.

Asynchronous operations were already possible in Panda using callbacks, but these get messy quite quickly when dealing with multiple operations. The async/await syntax introduced in Python 3.5 makes it significantly easier to write code that involves a series of asynchronous operations.

Here is a side-by-side example of loading a scene using the new approach. (Use of classes is omitted for brevity.) Note that coroutines need to be added to the task manager to make use of them.

# With coroutines

async def loadScene():
    text = OnscreenText("Loading…")

    # Suspend the coroutine while waiting for it to load
    scene = await loader.loadModel("scene"blocking=False)
    scene.reparentTo(render)

    text.hide()

taskMgr.add(loadScene())
# With callbacks

def loadScene():
    global text
    text = OnscreenText("Loading…")
    loader.loadModel("scene"callback=loadSceneCallback)

def loadSceneCallback(scene):
    scene.reparentTo(render)
    text.hide()

loadScene()

The way this works is that loadModel, when passing blocking=False, will schedule the load operation to happen in another thread, and immediately return a ‘future’ object. This is a special object (called a ‘promise’ in some languages) that represents an operation that is not yet complete, but will later return a result. The ‘await’ keyword is what suspends the operation of the coroutine until the future is complete. At a later time, the task manager notices that the future is done, and will resume execution of the loadScene function.

The ‘await’ keyword isn’t only supported when loading models. Any task has been made ‘awaitable’, meaning you can suspend a task waiting for another task to complete. You can also ‘await’ various other operations that can occur in the background. At present, that includes:

  • any task or asynchronous loader request
  • messenger.future('event') (waits for the given event to be thrown)
  • interval.start() (waits for the interval to complete)
  • win.triggerCopy() (waits for the copy operation to be complete)
  • texture.prepare() and shader.prepare() (waits for the resource to be prepared)

Coroutines need to be added to the task manager, but they can also be used as event handlers. Here is an example of an asynchronous event handler that uses ‘await’ to time how long the spacebar was held, without needing to store external state:

async def on_press_space():
    # Record the time at which the space key was pressed.
    begin = time.time()

    # Suspend the method until the space-up event is detected.
    await messenger.future("space-up")

    # Now measure how much time it took.
    duration = time.time() - begin
    print("spacebar was held for {} seconds".format(duration))

base.accept("space", on_press_space)

This is still an experimental feature, and we’d love to get your feedback on how we can further improve support for asynchronous programming in Panda3D.

Bullet physics

With the help of the community, all Bullet Physics engine shapes supported by Panda3D have now been made serializable. This means all Bullet structures can now be written to a .bam file, paving the way for future model conversion tools to implement support for Bullet physics.

Interrogate property interfaces

It has been a long-requested feature to enable property interfaces in the Python bindings, as a way to access members instead of the traditional getters and setters. This has been developed further by refining the support for sequence and mapping properties. For example, instead of the get_tag() and set_tag() methods, NodePath now also exposes its tags through a tags property that behaves like a Python dictionary, implementing many standard dictionary methods such as update() and keys(). The same applies to properties that expose collections, providing a predictable, standard and aesthetic interface to manipulate the API.

Released games section

In this section we’ll give you an overview of released and updated game titles that have been written with Panda3D

New game releases

The Traveling Fox

Updated games

Kitsunetsuki
YORG