Friday 5 February 2010

Asynchronous Hell...

I've just been on a week long hunt for a method of getting data from live feeds into Blender Game Engine. Not to taxing a task you might think...





I put together a simple example for a workshop we've just run. I wrote a python script in Blender - I grabbed the XML data feed from the Arch-OS system. used 'lxml' to grab the value of bms_.WindVane - the wind vane on the top of Portland Square. The idea was to have the camera in Blender align itself with with the wind vane in 'real-time'

Problem is, that querying the XML script takes from 0.5 to 4 seconds - and the 3D refresh cycle was blocking for that amount of time. I had a fiddle with threads, which didn't really yield solution. I asked around on #blendercoders, and was pointed in the direction of Python process, multiprocess and subprocess modules - the latter of which I'm quite familiar with - I've been using it to kick-off movie playback on remote machines using our AMX Panel.

I now understand process, multiprocess and subprocess, and threading a lot more now - but still didn't get any closer. The issue is the communication between the threads or processes, even if using 'queues', is the part that blocks the 3D.

I thought It might be a case of needing to run the slower processes in another Python instance...made sense - but it seems even the sockets used for communication either block, or if set to 'non-blocking' go out of scope...I think a similar happens if 'join()' is ommited.

I did finally get it to work. OSC.py came to my rescue. I figured that CLAM use OSC (and SpatDIF) - to loosely couple their audio backend to BGE - I've done it with Panda3D + CLAM, and BGE + CLAM with 'Yo Frankie'. It did work...but only in combination with a subtle python script I found for Blender:

if not own['connected']:
#print "Hallo blender Welt"
own['connected'] = True

GameLogic.socket = socket.socket( socket.AF_INET, socket. SOCK_DGRAM )
GameLogic.socket.bind(('', 1234))
GameLogic.socket.setblocking(0)
GameLogic.socket.settimeout(0.02)


The difference here is that they've created a persistent, non-blocking socket - which I assume is acting as some kind of buffer for the OSC messages...