<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8301333049390472977</id><updated>2012-02-01T19:40:30.627-08:00</updated><title type='text'>pyppet project</title><subtitle type='html'>Machinima,
digital puppetry,
real-time audio composer,
programmable physics</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>56</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-6236749501033577726</id><published>2012-02-01T19:22:00.000-08:00</published><updated>2012-02-01T19:40:30.636-08:00</updated><title type='text'>Progressive Baking</title><content type='html'>&lt;iframe width="620" height="315" src="http://www.youtube.com/embed/BuTy9WDLPcE" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;Texture maps are progressively downloaded from the Blender integrated server.  Baking happens on demand and is fully automated.  Client side javascript code adapts the texture request to best fit the given shader.  Supported texture layers: diffuse, AO, specular intensity and displacement.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Hardware Displacement Mapping&lt;/h3&gt;&lt;br /&gt;&lt;iframe width="620" height="349" src="http://www.youtube.com/embed/muHeGpzNqS0?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;The base mesh (without subdivision) is sent to the client.  The client then applies subdivision and recalculates the tangents.  The displacement map is progressively downloaded starting at 64x64 and stopping at 512x512 resolution.  The displacement happens in hardware using GLSL shader model 3.0.  &lt;i&gt;Code recycled from the Three.js examples.&lt;/i&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-6236749501033577726?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/6236749501033577726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2012/02/progressive-baking.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6236749501033577726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6236749501033577726'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2012/02/progressive-baking.html' title='Progressive Baking'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/BuTy9WDLPcE/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-4554812352380942987</id><published>2012-01-24T02:46:00.000-08:00</published><updated>2012-01-24T02:52:00.552-08:00</updated><title type='text'>Pyppet - WebGL Streaming</title><content type='html'>&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/UDIn8T4H7xE?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;This blog post was supposed to be about streaming data from Blender into the Unity Flash-based web-client.  I gave Unity a try under Wine-1.2.2 only to find that Unity is not fully compatible with Wine, making it unusable.  Why are the Unity developers not testing their software with Wine, or doing the smart thing and offering native builds for Linux?  The case was closed on Unity when I &lt;a href="http://sebleedelisle.com/2011/07/no-molehill-on-linux/"&gt;read&lt;/a&gt; that Adobe is in fact stupid enough to not support hardware 3D in the new Flash player on Linux.  Bad move Unity!&lt;br /&gt;&lt;br /&gt;Moving on, I gave GLGE a try, but quickly gave up, becoming disappointed by the API and not easily finding a way to dynamically set bone transforms and vertex positions.  For a split micro-second I thought maybe I should install MS Windows and give Unity another try.  Then I remembered another WebGL engine called &lt;a href="https://github.com/mrdoob/three.js/"&gt;Three.js.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Just going through the examples of Three.js should blow you away, the API is so simple yet so powerful, and the library is a tiny 380KB.  No wonder the Three.js community is so active.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Streaming&lt;/h3&gt;&lt;br /&gt;For streaming the data the only real option is Websockets, and the best Python3 library for Websockets I could find is &lt;a href="https://github.com/kanaka/websockify"&gt;Websockify&lt;/a&gt; by Joel Martin.  I was a bit confused at first by the code in Websockify, because it includes some extra scripts for running TCP proxies and rebinding ports (written in C).  However, the only file required from Websockify is "websocket.py" which is pure python and requires no compiled modules.  To get Websockify working inside Blender I only had to overload the start_server method not to use Multiprocessing.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Mission1 - Support Blender Modifiers&lt;/h4&gt;&lt;br /&gt;Three.js supports skeleton and vertex morph animation; and while this is great, it sucks not having Blender's mesh deform modifiers like: Cast, Curve, Displace, Hook, Lattice, MeshDeform, ShrinkWrap, SimpleDeform, Smooth, Warp, and Wave.  These modifiers are highly useful for animation, and not easy to reproduce with only bones.&lt;br /&gt;&lt;br /&gt;A workaround for not having these modifiers client-side, is to stream mesh data from Blender using Websockets into Three.js and dynamically update the WebGL mesh at 30fps.  To make the streaming optimal only the base mesh (subdivision level 0) is streamed, and then on the client side the Catmull-Clark subdivision surface is regenerated each frame.  The vertex positions are also rounded up to 3, cutting the byte stream almost in half.  The result is that in order to stream the default monkey head (500 vertices) to the web-client creates a load of 350KB per second.  This is still using ASCII Json for transport, so hopefully in the future a binary stream or other packing method could cut this down even more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-4554812352380942987?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/4554812352380942987/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2012/01/pyppet-webgl-streaming.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4554812352380942987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4554812352380942987'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2012/01/pyppet-webgl-streaming.html' title='Pyppet - WebGL Streaming'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/UDIn8T4H7xE/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-780157372598586834</id><published>2012-01-05T20:29:00.000-08:00</published><updated>2012-01-08T03:42:37.877-08:00</updated><title type='text'>Pyppet2 - Audio Analysis</title><content type='html'>&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/3mWN6xL9WQ4?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;Prototype for real-time musical performance at live events, concerts.  TODO: OSC integration to support pro music hardware, sample mixer integrated with physics system (collisions trigger sounds), particles, multiple full screen windows and camera switching driver input.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blenderartists.org/forum/showthread.php?241290-Real-time-Audio-Analysis-Microphone-Input-and-FluidSynth"&gt;Blenderartists thread&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pyppet.googlecode.com/files/pyppet-1.9.3b.tar.bz2"&gt;pyppet 1.9.3b source code&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;Update&lt;/h2&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/pyppet/wiki/InstallationGuide"&gt;linux install guide&lt;/a&gt;&lt;br /&gt; &lt;h5&gt;feature requests approved:&lt;/h5&gt;&lt;br /&gt; &lt;ul&gt;&lt;br /&gt;   &lt;li&gt;"assign multiple movement and rotation controls to one OSC input"&lt;/li&gt;&lt;br /&gt;   &lt;li&gt;"adjust scale, and attack/release function" (callbacks)&lt;/li&gt;&lt;br /&gt; &lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-780157372598586834?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/780157372598586834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2012/01/pyppet2-audio-analysis.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/780157372598586834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/780157372598586834'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2012/01/pyppet2-audio-analysis.html' title='Pyppet2 - Audio Analysis'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/3mWN6xL9WQ4/default.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8254016177998702551</id><published>2012-01-05T02:23:00.000-08:00</published><updated>2012-01-05T02:41:33.048-08:00</updated><title type='text'>Pyppet2 - Biped Solver - Part2</title><content type='html'>&lt;iframe width="620" height="349" src="http://www.youtube.com/embed/XR0mdYJ2JqA?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt; def update(self, context):&lt;br /&gt;  AbstractArmature.update(self,context)&lt;br /&gt;&lt;br /&gt;  step_left = step_right = False&lt;br /&gt;&lt;br /&gt;  if not self.left_foot_loc:&lt;br /&gt;   self.left_foot_loc = self.left_foot.shadow_parent.location&lt;br /&gt;   step_left = True&lt;br /&gt;  if not self.right_foot_loc:&lt;br /&gt;   self.right_foot_loc = self.right_foot.shadow_parent.location&lt;br /&gt;   step_right = True&lt;br /&gt;&lt;br /&gt;  x,y,z = self.chest.get_velocity_local()&lt;br /&gt;&lt;br /&gt;  sideways = None&lt;br /&gt;  sideways_rate = abs( x )&lt;br /&gt;  if x &lt; -2: sideways = 'RIGHT'&lt;br /&gt;  elif x &gt; 2: sideways = 'LEFT'&lt;br /&gt;&lt;br /&gt;  moving = None&lt;br /&gt;  motion_rate = abs( y )&lt;br /&gt;  if y &lt; -0.5: moving = 'FORWARD'&lt;br /&gt;  elif y &gt; 0.5: moving = 'BACKWARD'&lt;br /&gt;&lt;br /&gt;  loc,rot,scl = self.pelvis.shadow.matrix_world.decompose()&lt;br /&gt;  euler = rot.to_euler()&lt;br /&gt;  tilt = sum( [abs(math.degrees(euler.x)), abs(math.degrees(euler.y))] ) / 2.0  # 0-45&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  x,y,z = self.pelvis.get_location()&lt;br /&gt;  current_pelvis_height = z&lt;br /&gt;  falling = current_pelvis_height &lt; self.pelvis.rest_height * (1.0-self.standing_height_threshold)&lt;br /&gt;&lt;br /&gt;  hx,hy,hz = self.head.get_location()&lt;br /&gt;  x = (x+hx)/2.0&lt;br /&gt;  y = (y+hy)/2.0&lt;br /&gt;  ob = self.pelvis.shadow&lt;br /&gt;  ob.location = (x,y,-0.5)&lt;br /&gt;  loc,rot,scale = ob.matrix_world.decompose()&lt;br /&gt;  euler = rot.to_euler()&lt;br /&gt;&lt;br /&gt;  heading = math.degrees( euler.z )&lt;br /&gt;  spin = self.prev_heading - heading&lt;br /&gt;  self.prev_heading = heading&lt;br /&gt;  turning = None&lt;br /&gt;  turning_rate =  abs(spin) #/ 360.0&lt;br /&gt;  if abs(spin) &lt; 300: # ignore euler flip&lt;br /&gt;   if spin &lt; -1.0: turning = 'LEFT'&lt;br /&gt;   elif spin &gt; 1.0: turning = 'RIGHT'&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  if turning == 'LEFT':&lt;br /&gt;   if moving == 'BACKWARD':&lt;br /&gt;    self.left_foot.shadow.location.x = -(motion_rate * 0.25)&lt;br /&gt;    self.right_foot.shadow.location.x = -0.5&lt;br /&gt;&lt;br /&gt;   elif moving == 'FORWARD':&lt;br /&gt;    self.left_foot.shadow.location.x = 0.1&lt;br /&gt;    self.right_foot.shadow.location.x = 0.2&lt;br /&gt;    if motion_rate &gt; 2:&lt;br /&gt;     if random() &gt; 0.8:&lt;br /&gt;      step_right = True&lt;br /&gt;      self.left_foot.shadow.location.x = -(motion_rate * 0.25)&lt;br /&gt;     self.right_foot.shadow.location.x = motion_rate * 0.25&lt;br /&gt;&lt;br /&gt;   if not step_right and random() &gt; 0.2:&lt;br /&gt;    if random() &gt; 0.1: step_left = True&lt;br /&gt;    else: step_right = True&lt;br /&gt;&lt;br /&gt;  elif turning == 'RIGHT':&lt;br /&gt;   if moving == 'BACKWARD':&lt;br /&gt;    self.right_foot.shadow.location.x = -(motion_rate * 0.25)&lt;br /&gt;    self.left_foot.shadow.location.x = -0.5&lt;br /&gt;&lt;br /&gt;   elif moving == 'FORWARD':&lt;br /&gt;    self.right_foot.shadow.location.x = 0.1&lt;br /&gt;    self.left_foot.shadow.location.x = 0.2&lt;br /&gt;    if motion_rate &gt; 2:&lt;br /&gt;     if random() &gt; 0.8:&lt;br /&gt;      step_left = True&lt;br /&gt;      self.right_foot.shadow.location.x = -(motion_rate * 0.25)&lt;br /&gt;     self.left_foot.shadow.location.x = motion_rate * 0.25&lt;br /&gt;&lt;br /&gt;   if not step_left and random() &gt; 0.2:&lt;br /&gt;    if random() &gt; 0.1: step_right = True&lt;br /&gt;    else: step_left = True&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  hand_swing_targets = []&lt;br /&gt;  v = self.left_hand.biped_solver['swing-target'].location&lt;br /&gt;  hand_swing_targets.append( v )&lt;br /&gt;  v.x = -( self.left_foot.biped_solver['target'].location.x )&lt;br /&gt;&lt;br /&gt;  v = self.right_hand.biped_solver['swing-target'].location&lt;br /&gt;  hand_swing_targets.append( v )&lt;br /&gt;  v.x = -( self.right_foot.biped_solver['target'].location.x )&lt;br /&gt;&lt;br /&gt;  v = self.left_foot.biped_solver['target'].location&lt;br /&gt;  if v.x &lt; 0:  # if foot moving backward only pull on heel/foot&lt;br /&gt;   self.left_toe.biped_solver['TARGET'].weight = 0.0&lt;br /&gt;  elif v.x &gt; 0: # if foot moving forward only pull on toe&lt;br /&gt;   self.left_foot.biped_solver['TARGET'].weight = 0.0&lt;br /&gt;&lt;br /&gt;  v = self.right_foot.biped_solver['target'].location&lt;br /&gt;  if v.x &lt; 0:  # if foot moving backward only pull on heel/foot&lt;br /&gt;   self.right_toe.biped_solver['TARGET'].weight = 0.0&lt;br /&gt;  elif v.x &gt; 0: # if foot moving forward only pull on toe&lt;br /&gt;   self.right_foot.biped_solver['TARGET'].weight = 0.0&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  if moving == 'BACKWARD': # hands forward if moving backwards&lt;br /&gt;   for v in hand_swing_targets: v.x += 0.1&lt;br /&gt;&lt;br /&gt;  if step_left:&lt;br /&gt;   rad = euler.z - math.radians(90+self.primary_heading)&lt;br /&gt;   cx = math.sin( -rad )&lt;br /&gt;   cy = math.cos( -rad )&lt;br /&gt;   v = self.left_foot.shadow_parent.location&lt;br /&gt;   v.x = x+cx&lt;br /&gt;   v.y = y+cy&lt;br /&gt;   v.z = .0&lt;br /&gt;   self.left_foot_loc = v&lt;br /&gt;  if step_right:&lt;br /&gt;   rad = euler.z + math.radians(90+self.primary_heading)&lt;br /&gt;   cx = math.sin( -rad )&lt;br /&gt;   cy = math.cos( -rad )&lt;br /&gt;   v = self.right_foot.shadow_parent.location&lt;br /&gt;   v.x = x+cx&lt;br /&gt;   v.y = y+cy&lt;br /&gt;   v.z = .0&lt;br /&gt;   self.right_foot_loc = v&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;  #################### falling ####################&lt;br /&gt;  if falling:&lt;br /&gt;&lt;br /&gt;   if current_pelvis_height &lt; 0.2:&lt;br /&gt;    for foot in (self.left_foot, self.right_foot):&lt;br /&gt;     target = foot.biped_solver[ 'TARGET:pelvis' ]&lt;br /&gt;     if target.weight &lt; 50: target.weight += 1.0&lt;br /&gt;&lt;br /&gt;     foot.add_local_torque( -30, 0, 0 )&lt;br /&gt;&lt;br /&gt;   else:&lt;br /&gt;    for foot in (self.left_foot, self.right_foot):&lt;br /&gt;     target = foot.biped_solver[ 'TARGET:pelvis' ]&lt;br /&gt;     target.weight *= 0.9&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   for target in self.foot_solver_targets: # reduce foot step force&lt;br /&gt;    target.weight *= 0.9&lt;br /&gt;&lt;br /&gt;   #for target in self.hand_solver_targets: # increase hand plant force&lt;br /&gt;   # if target.weight &lt; self.when_falling_hand_target_goal_weight:&lt;br /&gt;   #  target.weight += 1&lt;br /&gt;&lt;br /&gt;   for hand in (self.left_hand, self.right_hand):&lt;br /&gt;    self.head.add_local_torque( -self.when_falling_head_curl, 0, 0 )&lt;br /&gt;    u = self.when_falling_pull_hands_down_by_tilt_factor * tilt&lt;br /&gt;    hand.add_force( 0,0, -u )&lt;br /&gt;&lt;br /&gt;    x,y,z = hand.get_location()&lt;br /&gt;    if z &lt; 0.1:&lt;br /&gt;     self.head.add_force( &lt;br /&gt;      0,&lt;br /&gt;      0, &lt;br /&gt;      tilt * self.when_falling_and_hands_down_lift_head_by_tilt_factor&lt;br /&gt;     )&lt;br /&gt;     hand.add_local_force( 0, -10, 0 )&lt;br /&gt;    else:&lt;br /&gt;     hand.add_local_force( 0, 3, 0 )&lt;br /&gt;&lt;br /&gt;  else: # standing&lt;br /&gt;&lt;br /&gt;   for foot in (self.left_foot, self.right_foot):&lt;br /&gt;    target = foot.biped_solver[ 'TARGET:pelvis' ]&lt;br /&gt;    target.weight *= 0.9&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   for target in self.foot_solver_targets:&lt;br /&gt;    if target.weight &lt; self.when_standing_foot_target_goal_weight:&lt;br /&gt;     target.weight += 1&lt;br /&gt;&lt;br /&gt;   #for target in self.hand_solver_targets: # reduce hand plant force&lt;br /&gt;   # target.weight *= 0.9&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   head_lift = self.when_standing_head_lift&lt;br /&gt;   for toe in ( self.left_toe, self.right_toe ):&lt;br /&gt;    x,y,z = toe.get_location()&lt;br /&gt;    if z &lt; 0.1:&lt;br /&gt;     self.head.add_force( 0,0, head_lift*0.5 )&lt;br /&gt;&lt;br /&gt;   ## lift feet ##&lt;br /&gt;   foot = self.left_foot&lt;br /&gt;   v1 = foot.get_location().copy()&lt;br /&gt;   if v1.z &lt; 0.1: self.head.add_force( 0,0, head_lift*0.5 )&lt;br /&gt;&lt;br /&gt;   v2 = self.left_foot_loc.copy()&lt;br /&gt;   v1.z = .0; v2.z = .0&lt;br /&gt;   dist = (v1 - v2).length&lt;br /&gt;   if dist &gt; 0.5:&lt;br /&gt;    foot.add_force( 0, 0, self.when_standing_foot_step_far_lift)&lt;br /&gt;   elif dist &lt; 0.25:&lt;br /&gt;    foot.add_force( 0, 0, -self.when_standing_foot_step_near_pull)&lt;br /&gt;&lt;br /&gt;   foot = self.right_foot&lt;br /&gt;   v1 = foot.get_location().copy()&lt;br /&gt;   if v1.z &lt; 0.1: self.head.add_force( 0,0, head_lift*0.5 )&lt;br /&gt;&lt;br /&gt;   v2 = self.right_foot_loc.copy()&lt;br /&gt;   v1.z = .0; v2.z = .0&lt;br /&gt;   dist = (v1 - v2).length&lt;br /&gt;   if dist &gt; 0.5:&lt;br /&gt;    foot.add_force( 0, 0, self.when_standing_foot_step_far_lift)&lt;br /&gt;   elif dist &lt; 0.25:&lt;br /&gt;    foot.add_force( 0, 0, -self.when_standing_foot_step_near_pull)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8254016177998702551?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8254016177998702551/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2012/01/pyppet2-biped-solver-part2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8254016177998702551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8254016177998702551'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2012/01/pyppet2-biped-solver-part2.html' title='Pyppet2 - Biped Solver - Part2'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/XR0mdYJ2JqA/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-6698414802176892307</id><published>2011-12-16T16:59:00.000-08:00</published><updated>2011-12-18T20:42:50.575-08:00</updated><title type='text'>Pyppet2 - Biped Solver - Part1</title><content type='html'>&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/oZzFEr68bpE?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pyppet.googlecode.com/files/pyppet-1.9.2f.tar.bz2"&gt;http://pyppet.googlecode.com/files/pyppet-1.9.2f.tar.bz2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt; def update(self, context):&lt;br /&gt;  Ragdoll.update(self,context)&lt;br /&gt;&lt;br /&gt;  loc,rot,scl = self.pelvis.shadow.matrix_world.decompose()&lt;br /&gt;  euler = rot.to_euler()&lt;br /&gt;  tilt = sum([abs(math.degrees(euler.x)),abs(math.degrees(euler.y))])/2.0&lt;br /&gt;&lt;br /&gt;  x1,y1,z1 = self.pelvis.get_location()&lt;br /&gt;  current_pelvis_height = z1&lt;br /&gt;  x2,y2,z2 = self.head.get_location()&lt;br /&gt;  x = (x1+x2)/2.0&lt;br /&gt;  y = (y1+y2)/2.0&lt;br /&gt;  ob = self.pelvis.shadow&lt;br /&gt;  ob.location = (x,y,0)&lt;br /&gt;  loc,rot,scale = ob.matrix_world.decompose()&lt;br /&gt;  euler = rot.to_euler()&lt;br /&gt;&lt;br /&gt;  rad = euler.z - math.radians(90)&lt;br /&gt;  cx = math.sin( -rad )&lt;br /&gt;  cy = math.cos( -rad )&lt;br /&gt;  if not self.left_foot_loc or random() &gt; 0.9:&lt;br /&gt;   v = self.left_foot.shadow.location&lt;br /&gt;   v.x = x+cx&lt;br /&gt;   v.y = y+cy&lt;br /&gt;   v.z = .0&lt;br /&gt;   self.left_foot_loc = v&lt;br /&gt;&lt;br /&gt;  rad = euler.z + math.radians(90)&lt;br /&gt;  cx = math.sin( -rad )&lt;br /&gt;  cy = math.cos( -rad )&lt;br /&gt;  if not self.right_foot_loc or random() &gt; 0.9:&lt;br /&gt;   v = self.right_foot.shadow.location&lt;br /&gt;   v.x = x+cx&lt;br /&gt;   v.y = y+cy&lt;br /&gt;   v.z = .0&lt;br /&gt;   self.right_foot_loc = v&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Updates foot targets, and measures tilt of head/pelvis offset.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&lt;br /&gt;  ## falling ##&lt;br /&gt;  if current_pelvis_height &lt; self.pelvis.rest_height * (1.0-self.standing_height_threshold):&lt;br /&gt;&lt;br /&gt;   for target in self.foot_solver_targets: # reduce foot step force&lt;br /&gt;    target.weight *= 0.9&lt;br /&gt;&lt;br /&gt;   for target in self.hand_solver_targets: # increase hand plant force&lt;br /&gt;    if target.weight &lt; self.when_falling_hand_target_goal_weight:&lt;br /&gt;     target.weight += 1&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   for hand in (self.left_hand, self.right_hand):&lt;br /&gt;&lt;br /&gt;    self.head.add_local_torque( -self.when_falling_head_curl, 0, 0 )&lt;br /&gt;&lt;br /&gt;    u = self.when_falling_pull_hands_down_by_tilt_factor * tilt&lt;br /&gt;    hand.add_force( 0,0, -u )&lt;br /&gt;&lt;br /&gt;    x,y,z = hand.get_location()&lt;br /&gt;    if z &lt; 0.1:&lt;br /&gt;     self.head.add_force( &lt;br /&gt;      0,&lt;br /&gt;      0, &lt;br /&gt;      tilt * self.when_falling_and_hands_down_lift_head_by_tilt_factor&lt;br /&gt;     )&lt;br /&gt;     hand.add_local_force( 0, -10, 0 )&lt;br /&gt;    else:&lt;br /&gt;     hand.add_local_force( 0, 3, 0 )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If falling pull hands down to break fall, then try to sit up.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;&lt;br /&gt;  else: # standing&lt;br /&gt;   for target in self.foot_solver_targets:&lt;br /&gt;    if target.weight &lt; self.when_standing_foot_target_goal_weight:&lt;br /&gt;     target.weight += 1&lt;br /&gt;&lt;br /&gt;   for target in self.hand_solver_targets: # reduce hand plant force&lt;br /&gt;    target.weight *= 0.9&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   ## lift feet ##&lt;br /&gt;   head_lift = self.when_standing_head_lift&lt;br /&gt;&lt;br /&gt;   foot = self.left_foot&lt;br /&gt;   v1 = foot.get_location().copy()&lt;br /&gt;   if v1.z &lt; 0.1: self.head.add_force( 0,0, head_lift )&lt;br /&gt;   v2 = self.left_foot_loc.copy()&lt;br /&gt;   v1.z = .0; v2.z = .0&lt;br /&gt;   dist = (v1 - v2).length&lt;br /&gt;   if dist &gt; 0.5:&lt;br /&gt;    foot.add_force( 0, 0, self.when_standing_foot_step_far_lift)&lt;br /&gt;    #self.pelvis.add_force( 0,0, -head_lift*0.25 )&lt;br /&gt;   elif dist &lt; 0.25:&lt;br /&gt;    foot.add_force( 0, 0, -self.when_standing_foot_step_near_pull)&lt;br /&gt;    #self.head.add_force( 0,0, head_lift )&lt;br /&gt;&lt;br /&gt;   foot = self.right_foot&lt;br /&gt;   v1 = foot.get_location().copy()&lt;br /&gt;   if v1.z &lt; 0.1: self.head.add_force( 0,0, head_lift )&lt;br /&gt;   v2 = self.right_foot_loc.copy()&lt;br /&gt;   v1.z = .0; v2.z = .0&lt;br /&gt;   dist = (v1 - v2).length&lt;br /&gt;   if dist &gt; 0.5:&lt;br /&gt;    foot.add_force( 0, 0, self.when_standing_foot_step_far_lift)&lt;br /&gt;    #self.pelvis.add_force( 0,0, -head_lift*0.25 )&lt;br /&gt;   elif dist &lt; 0.25:&lt;br /&gt;    foot.add_force( 0, 0, -self.when_standing_foot_step_near_pull)&lt;br /&gt;    #self.head.add_force( 0,0, head_lift )&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If standing take step to next foot target, and lift head if foot is touching the ground.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-6698414802176892307?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/6698414802176892307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/12/pyppet2-biped-solver-part1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6698414802176892307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6698414802176892307'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/12/pyppet2-biped-solver-part1.html' title='Pyppet2 - Biped Solver - Part1'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/oZzFEr68bpE/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-3587472437336573005</id><published>2011-12-10T02:34:00.000-08:00</published><updated>2011-12-10T17:27:17.357-08:00</updated><title type='text'>pyppet2 - breakable ragdoll</title><content type='html'>&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/0taOswhiwJE?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;Using ODE joint feedback the stress of each joint can be measured, if over the user set threshold Pyppet will break the joint.  TODO: add options to weaken/damage joints by reducing their ERP.&lt;br /&gt;&lt;br /&gt;This video also shows a Stretch-to generated rig.  (not selecting the stretch-to option creates a normal IK based rig)&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Joint Damage&lt;/h4&gt;&lt;br /&gt;&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/0BmULKu87Jc?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;This morning I experimented with joint damage, reducing the ERP and raising the CFM on a joint when it is under stress.  This can produce some interesting effects like a weakening joint.  Another effect is to add a constant pull downwards when a joint is near breaking.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-3587472437336573005?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/3587472437336573005/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/12/pyppet2-breakable-ragdoll.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3587472437336573005'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3587472437336573005'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/12/pyppet2-breakable-ragdoll.html' title='pyppet2 - breakable ragdoll'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/0taOswhiwJE/default.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-9212580464531476841</id><published>2011-12-06T03:37:00.000-08:00</published><updated>2011-12-06T03:41:20.787-08:00</updated><title type='text'>RPythonic 0.4.4</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-X7fIjvM7mqg/Tt3_SiSheCI/AAAAAAAAAL4/DDxqCp1vJJk/s1600/pypy-opencv-gtk3.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 269px; height: 400px;" src="http://3.bp.blogspot.com/-X7fIjvM7mqg/Tt3_SiSheCI/AAAAAAAAAL4/DDxqCp1vJJk/s400/pypy-opencv-gtk3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5682978998718658594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-EZGXVeWSb0c/Tt3_SV-TpGI/AAAAAAAAALs/0eMk7-Fiq9A/s1600/pypy-kinect-gtk3.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 375px; height: 400px;" src="http://4.bp.blogspot.com/-EZGXVeWSb0c/Tt3_SV-TpGI/AAAAAAAAALs/0eMk7-Fiq9A/s400/pypy-kinect-gtk3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5682978995412640866" /&gt;&lt;/a&gt;&lt;br /&gt; Rpythonic has reached another major milestone and is now able to generate PyPy compatible ctypes bindings to C libraries.  The two screen shots show OpenCV, Gtk3, and libfreenect running under PyPy 1.7.&lt;br /&gt; The Rpythonic &lt;a href="http://rpythonic.googlecode.com/files/rpythonic-0.4.4.tar.bz2"&gt;download&lt;/a&gt; contains pre-generated wrappers for the following libraries:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;SDL&lt;/li&gt;&lt;br /&gt; &lt;li&gt;OpenAL&lt;/li&gt;&lt;br /&gt; &lt;li&gt;OpenCV&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Emokit&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Fluidsynth&lt;/li&gt;&lt;br /&gt; &lt;li&gt;GTK3&lt;/li&gt;&lt;br /&gt; &lt;li&gt;libfreenect&lt;/li&gt;&lt;br /&gt; &lt;li&gt;ODE&lt;/li&gt;&lt;br /&gt; &lt;li&gt;OpenGL&lt;/li&gt;&lt;br /&gt; &lt;li&gt;OpenJPEG&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Wiiuse&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-9212580464531476841?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/9212580464531476841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/12/rpythonic-044.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/9212580464531476841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/9212580464531476841'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/12/rpythonic-044.html' title='RPythonic 0.4.4'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-X7fIjvM7mqg/Tt3_SiSheCI/AAAAAAAAAL4/DDxqCp1vJJk/s72-c/pypy-opencv-gtk3.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-3889996207023972923</id><published>2011-12-05T05:53:00.000-08:00</published><updated>2011-12-09T05:01:39.730-08:00</updated><title type='text'>Pyppet2</title><content type='html'>&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/z0nV4Pf_0gU?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Changes from Pyppet1 to Pyppet2:&lt;/h4&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;ported from Blender2.49 to Blender2.6&lt;/li&gt;&lt;br /&gt; &lt;li&gt;WMD replaced with R.Pavlik's enhanced Wiiuse&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Pygame replaced with ctypes-SDL&lt;/li&gt;&lt;br /&gt; &lt;li&gt;PyGTK2 replaced with ctypes-GTK3&lt;/li&gt;&lt;br /&gt; &lt;li&gt;PyODE replaced with ctypes-ODE&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/C3UnyrMnk3g?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;New in Pyppet2:&lt;/h4&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;Kinect streaming using libfreenect&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Web-camera streaming using OpenCV&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Drag'n'Drop device config&lt;/li&gt;&lt;br /&gt; &lt;li&gt;Multi-threaded&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;ODE Joint Physics&lt;/h4&gt;&lt;br /&gt;&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/XVrYUKjkous?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pyppet.googlecode.com/files/pyppet-1.9.0.tar.bz2"&gt;http://pyppet.googlecode.com/files/pyppet-1.9.0.tar.bz2&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-3889996207023972923?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/3889996207023972923/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/12/pyppet2.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3889996207023972923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3889996207023972923'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/12/pyppet2.html' title='Pyppet2'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/z0nV4Pf_0gU/default.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-5009109041711929887</id><published>2011-12-01T01:51:00.000-08:00</published><updated>2011-12-01T21:55:33.134-08:00</updated><title type='text'>Pyppet2 Update</title><content type='html'>&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/xg_30ZZ4sZQ?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Mission1: Integrate Blender and GTK3&lt;/h3&gt;&lt;br /&gt; The first requirement of this project was to integrate Blender and GTK3 without any compiled Python modules, or modifications to the Blender C source code.  To acheive this, only Python and ctypes are used.  The GTK3 ctypes wrappers were generated by Rpythonic.  GObject Introspection is not used or required.&lt;br /&gt; Rpythonic is also used to generate wrappers to the Blender C API.  This allows us to control the Blender main-loop from Python and integrate it with our own custom main-loop.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;XEmbed:&lt;/h4&gt;&lt;br /&gt; The XEmbed protocol is used to embed Blender's window into a GtkSocket.  The Blender window is placed on a GtkFixed canvas as the bottom layer.  Gtk widgets can be drawn over-top of the Blender window by wrapping them in a GtkEventBox.  The Properties sub-window is replaced with a Gtk widget by checking the location and size of the Area-&gt;Region object each main loop iteration.  Bpy provides the width and height of this struct, but not its location in window-space.  The ctypes wrapper to Blender (libblender) is used to get the location:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; # the pointer of any bpy object can be read with "as_pointer()" #&lt;br /&gt; addr = reg.as_pointer()&lt;br /&gt; ptr = ctypes.POINTER(ctypes.c_void_p).from_address( addr )&lt;br /&gt; creg = libblender.ARegion( pointer=ctypes.pointer(ptr), cast=True )&lt;br /&gt; rect = creg.winrct # blender window space&lt;br /&gt; print( rect.xmin, rect.xmax, rect.ymin, rect.ymax )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Mission2: Integrate Blender and Webcamera Streaming&lt;/h3&gt;&lt;br /&gt; OpenCV provides interesting effects and an interface for reading data from a web-camera using the highgui.QueryFrame function.  QueryFrame is slow and blocks until the next frame is ready, not something that we can allow to slow down the main loop.  Python multi-threading works very well when combined with ctypes, where the blocking call will release the GIL and allow other threads to continue.  We only need to lock and release around the call that writes the final image data to a pixel buffer that Gtk displays.&lt;br /&gt; To display the webcam buffer as a texture in the Blender viewport, OpenGL is used directly.  I first tried BGL, but it seems that the call to glTexImage2D requires a BGL.Buffer object which wraps a python list.  Converting the raw image data to a python list would be another speed hit.  Instead, I used a pure ctypes wrapper to OpenGL where raw data pointers can be used directly.  In order to get OpenGL over ctypes to work in Blender I found that the library can not be loaded from an external DLL, the magic trick is to call &lt;i&gt;ctypes.CDLL("")&lt;/i&gt; with an empty string which forces ctypes to load the library from the current process.&lt;br /&gt; The main loop checks the 'webcam' bpy Image each frame to see if its OpenGL "bindcode" is active, meaning that Blender has cached the image to be displayed in the view.  Using the bindcode the texture can be updated dynamically.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  img = bpy.data.images['webcam']&lt;br /&gt;  if img.bindcode:&lt;br /&gt;   bind = img.bindcode&lt;br /&gt;   glBindTexture(GL_TEXTURE_2D, bind)&lt;br /&gt;   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)&lt;br /&gt;   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)&lt;br /&gt;   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)&lt;br /&gt;   ptr = self.webcam.preview_image.imageData&lt;br /&gt;   glTexImage2D(&lt;br /&gt;    GL_TEXTURE_2D, 0, GL_RGB, 320, 240, 0, &lt;br /&gt;    GL_RGB, GL_UNSIGNED_BYTE, ptr&lt;br /&gt;   )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pyppet.googlecode.com/files/pyppet-dec2-2011.tar.bz2"&gt;http://pyppet.googlecode.com/files/pyppet-dec2-2011.tar.bz2&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-5009109041711929887?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/5009109041711929887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/12/pyppet2-update.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5009109041711929887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5009109041711929887'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/12/pyppet2-update.html' title='Pyppet2 Update'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/xg_30ZZ4sZQ/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-6471526200180409402</id><published>2011-11-18T22:43:00.000-08:00</published><updated>2011-11-18T23:01:58.333-08:00</updated><title type='text'>bpyengine</title><content type='html'>&lt;iframe width="640" height="360" src="http://www.youtube.com/embed/szi48Q9Ave8" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/bpyengine/"&gt;http://code.google.com/p/bpyengine/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blenderartists.org/forum/showthread.php?237174-networked-blender-realtime-sync"&gt;thread on blenderartists.org&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-6471526200180409402?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/6471526200180409402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/11/bpyengine.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6471526200180409402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6471526200180409402'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/11/bpyengine.html' title='bpyengine'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/szi48Q9Ave8/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-7677875876851246594</id><published>2011-11-14T23:26:00.000-08:00</published><updated>2011-11-15T18:07:39.992-08:00</updated><title type='text'>SplineIK Face-Rig Template V1</title><content type='html'>&lt;iframe width="640" height="360" src="http://www.youtube.com/embed/oTGjo9Ug9Yw" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href="http://www.blendswap.com/3D-models/characters/bretts-face-rig-v1/"&gt;download&lt;/a&gt;&lt;/h3&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-7677875876851246594?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/7677875876851246594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/11/splineik-face-rig-template-v1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7677875876851246594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7677875876851246594'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/11/splineik-face-rig-template-v1.html' title='SplineIK Face-Rig Template V1'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/oTGjo9Ug9Yw/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8109469218815715936</id><published>2011-09-18T22:51:00.001-07:00</published><updated>2011-10-24T01:12:31.735-07:00</updated><title type='text'>blender2ogre 0.5.x</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-A1_EVv36Kso/Tp7nWpWFfiI/AAAAAAAAALE/auom91hiRxc/s1600/b2ogre-skull-normals.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 243px;" src="http://4.bp.blogspot.com/-A1_EVv36Kso/Tp7nWpWFfiI/AAAAAAAAALE/auom91hiRxc/s400/b2ogre-skull-normals.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5665219757520289314" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.blendswap.com/3D-models/misc-objects/craneo/"&gt;Craneo by Daniel FR Gordillo&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blender2ogre.googlecode.com/files/blender2ogre-0.5.5.zip"&gt;http://blender2ogre.googlecode.com/files/blender2ogre-0.5.5.zip&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Getting Started Tutorial Blender2Ogre-0.5.5 and RealXtend Tundra&lt;/h3&gt;&lt;br /&gt;&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/3EpwEsB0_kk?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;iframe width="425" height="349" src="http://www.youtube.com/embed/5oVM0Lmeb68?hl=en&amp;fs=1" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;h4&gt;&lt;a href="http://www.openspace3d.com/"&gt;Basic Mesh Export Tutorial - by OpenSpace3D.com&lt;/a&gt;&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/hhoFkwiDwO8?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/hhoFkwiDwO8?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8109469218815715936?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8109469218815715936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/09/blender2ogre-05x.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8109469218815715936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8109469218815715936'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/09/blender2ogre-05x.html' title='blender2ogre 0.5.x'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-A1_EVv36Kso/Tp7nWpWFfiI/AAAAAAAAALE/auom91hiRxc/s72-c/b2ogre-skull-normals.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-2179727675494975825</id><published>2011-07-06T08:18:00.001-07:00</published><updated>2011-07-13T20:22:42.631-07:00</updated><title type='text'>C++ Wrapper Generator - Part1</title><content type='html'>There are several ways to wrap C++ and integrate it with Python, have a look on &lt;a href="http://stackoverflow.com/questions/1492755/python-c-binding-library-comparison"&gt;stackoverflow&lt;/a&gt;.  Many wrapper generators rely on &lt;a href="http://www.gccxml.org/"&gt;GccXML&lt;/a&gt;, but the project has been dead for years and has trouble parsing some newer C++.  Another option is &lt;a href="http://www.swig.org/"&gt;Swig&lt;/a&gt;, still active, but appears to require hand written wrapper code, and lacks support for things like nested classes.  Clang was yet another option, it was able to output its parse tree as XML, but this feature was recently broken.  So after much searching I finally found &lt;a href="http://sourceforge.net/projects/cppheaderparser/"&gt;CppHeaderParser&lt;/a&gt; by Jashua Cloutier on SourceForge, the source code is all contained in a single file and very easy to understand.  Since then (back in March), in my free time, I have been hacking away on CppHeaderParser; adding support for parsing more complex C++, resolving typedef's, nested classes, etc.  For anyone else interesting in building a wrapper generator on top of it, you will be happy to hear it remains less than 2,000 lines of code, and very easy to modify to fit your particular needs.  It is already being used successfully by the &lt;a href="https://github.com/kripken/emscripten/"&gt;Emscripten&lt;/a&gt; project to generate a C wrapper and Javascript bindings, &lt;a href="http://syntensity.blogspot.com/2011/07/emscripten-14.html"&gt;kripken's blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.3.8.tar.bz2"&gt;RPythonic-0.3.8&lt;/a&gt; uses CppHeaderParser-2.0 as a backend to parse the C++ code and generate a C wrapper and Python-ctypes bindings.  There is going to be a speed hit calling C++ through a C API over ctypes.  Future work will solve the speed problem by generation of RPython (RFFI) bindings so that RPython can call into C++ code.&lt;br /&gt;&lt;br /&gt;Work on both CppHeaderParser and the wrapper generator will continue, and should stablize in the next few releases.  Basic features that are working now include: operators (==, +=, -=, etc.), enums, typedefs, classes and nested classes, class properties, structs, arrays, doxygen, and C compatible types.  The major missing feature is wrapping of templates.&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;download all wrapper generator samples &lt;a href="http://rpythonic.googlecode.com/files/C%2B%2B_wrapper_generator_examples_july8.tar.bz2"&gt;here&lt;/a&gt;&lt;/h5&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;C Wrapper Sample - OgrePaged &lt;i&gt;Method Call&lt;/i&gt;&lt;/h4&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;/* Forests::TreeLoader3D.addTree */&lt;br /&gt;/** \brief Adds an entity to the scene with the specified location, rotation, and scale.&lt;br /&gt; \param entity The entity to be added to the scene.&lt;br /&gt; \param position The desired position of the tree&lt;br /&gt; \param yaw The desired rotation around the vertical axis in degrees&lt;br /&gt; \param scale The desired scale of the entity&lt;br /&gt;&lt;br /&gt; While TreeLoader3D allows you to provide full 3-dimensional x/y/z coordinates,&lt;br /&gt; you are restricted to only yaw rotation, and only uniform scale. &lt;br /&gt; &lt;br /&gt; \warning By default, scale values may not exceed 2.0. If you need to use higher scale&lt;br /&gt; values than 2.0, use setMaximumScale() to reconfigure the maximum. */&lt;br /&gt;/*void addTree ( Ogre : : Entity * entity , __const__ Ogre : : Vector3 &amp; position , Ogre : : Degree yaw = Ogre : : Degree ( 0 ) , Ogre : : Real scale = 1 0f , void * userData = __null ) ;&lt;br /&gt; returns_fundamental: True&lt;br /&gt; returns_pointer: 0&lt;br /&gt; returns_reference: False&lt;br /&gt; returns: void&lt;br /&gt; returns_class: False&lt;br /&gt;*/&lt;br /&gt;void  TreeLoader3D_addTree( void* object, void* arg0, void* arg1, void* arg2 )  {&lt;br /&gt; ((Forests::TreeLoader3D*)object)-&gt;addTree(&lt;br /&gt;  //type: Ogre::Entity *, raw_type: Ogre::Entity, pointer: 1, ,&lt;br /&gt;  (Ogre::Entity*)arg0,&lt;br /&gt;  //type: const Ogre::Vector3 &amp;, raw_type: Ogre::Vector3, constant: 1, reference: 1, ,&lt;br /&gt;  (Ogre::Vector3&amp;)arg1,&lt;br /&gt;  //type: Ogre::Degree, raw_type: Ogre::Degree, default: Ogre : : Degree ( 0, ,&lt;br /&gt;  (Ogre::Degree&amp;)arg2 );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;C Wrapper Sample - BulletPhysics - &lt;i&gt;Set Property&lt;/i&gt;&lt;/h4&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;/* --------------- class btVector3 --------------- */&lt;br /&gt;/* &lt;abstract: False&gt; */&lt;br /&gt;/**@brief btVector3 can be used to represent 3D points and vectors.&lt;br /&gt;* It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user&lt;br /&gt;* Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers&lt;br /&gt;*/&lt;br /&gt;/* ---------properties--------- */&lt;br /&gt;//type: btScalar, raw_type: btScalar, typedefs: 1, fundamental: True, array: 4, ctypes_type: ctypes.c_float, &lt;br /&gt;void btVector3_m_floats__property_set__( void* object,btScalar  arg[4] ) { &lt;br /&gt; ((btVector3*)object)-&gt;m_floats[ 0 ] = arg[ 0 ]; &lt;br /&gt; ((btVector3*)object)-&gt;m_floats[ 1 ] = arg[ 1 ]; &lt;br /&gt; ((btVector3*)object)-&gt;m_floats[ 2 ] = arg[ 2 ]; &lt;br /&gt; ((btVector3*)object)-&gt;m_floats[ 3 ] = arg[ 3 ];  } &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;C Wrapper Sample - BulletPhysics - &lt;i&gt;Operator Overloading&lt;/i&gt;&lt;/h4&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;/**@brief Scale the vector&lt;br /&gt;* @param s Scale factor */&lt;br /&gt;/*} inline btVector3 &amp; operator * = ( __const__ btScalar &amp; s ) {&lt;br /&gt; returns_fundamental: False&lt;br /&gt; returns_pointer: 0&lt;br /&gt; returns_reference: True&lt;br /&gt; returns: btVector3&lt;br /&gt; returns_class: True&lt;br /&gt;*/&lt;br /&gt;void*  btVector3___operator____imult__( void* object, const btScalar &amp; arg0 )  {&lt;br /&gt; return (void*)(&amp; (((btVector3&amp;)object)*=(arg0)) );&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Pass1: Generate C Wrapper&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;flatten all method calls to functions&lt;/li&gt;&lt;br /&gt; &lt;li&gt;the instance is passed as the first argument&lt;/li&gt;&lt;br /&gt; &lt;li&gt;all instances are passed as void pointers&lt;/li&gt;&lt;br /&gt; &lt;li&gt;C compatible types are passed directly&lt;/li&gt;&lt;br /&gt; &lt;li&gt;objects returned are cast to void pointers&lt;/li&gt;&lt;br /&gt; &lt;li&gt;create wrapper functions for operators&lt;/li&gt;&lt;br /&gt; &lt;li&gt;create wrapper functions for get/set properties&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Pass2: Generate Python-ctypes Wrapper&lt;/h3&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;create a Python class for each C++ class&lt;/li&gt;&lt;br /&gt; &lt;li&gt;__init__ calls the constructor C wrapper&lt;/li&gt;&lt;br /&gt; &lt;li&gt;each method calls the C wrapper function&lt;/li&gt;&lt;br /&gt; &lt;li&gt;methods that return instances are passed to the matching python class&lt;/li&gt;&lt;br /&gt; &lt;li&gt;__del__ calls the destructor&lt;/li&gt;&lt;br /&gt; &lt;li&gt;operators overload: __mult__, __add__, etc..&lt;/li&gt;&lt;br /&gt; &lt;li&gt;__getattr__, __setattr__ call the get/set wrapper functions&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-2179727675494975825?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/2179727675494975825/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/07/c-wrapper-generator-part1.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/2179727675494975825'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/2179727675494975825'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/07/c-wrapper-generator-part1.html' title='C++ Wrapper Generator - Part1'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8746937730244892574</id><published>2011-06-10T03:15:00.000-07:00</published><updated>2011-06-10T03:34:58.464-07:00</updated><title type='text'>Pycon 2011 Singapore</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-bZrX6_O8jtA/TfHxRD6BmiI/AAAAAAAAAGM/q90mWl9V8po/s1600/brett-at-pycon2011-small.jpg"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 200px; height: 118px;" src="http://1.bp.blogspot.com/-bZrX6_O8jtA/TfHxRD6BmiI/AAAAAAAAAGM/q90mWl9V8po/s200/brett-at-pycon2011-small.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5616535485716470306" /&gt;&lt;/a&gt;&lt;br /&gt;Thanks to Maurice Ling and &lt;a href="http://apac.pycon.org/"&gt;PyCon APAC&lt;/a&gt; I was able to attend today and give my talk about RPython and using it for creating games for Android.  The latest source code for RPythonic can be downloaded &lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.3.7.tar.bz2"&gt;here,&lt;/a&gt; this is the version I used in the demo.  I was surprised to see almost a quarter of the audience was familiar with RPython, and of course everyone knew about Cython.  The screen recording was not working, so there is no webcast of my talk, but for those who could not attend, the slides are attached in this post.&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-unxdErrWk6I/TfHxxCwfzPI/AAAAAAAAAGU/1IEyX_jFC_w/s1600/Screenshot.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 199px;" src="http://3.bp.blogspot.com/-unxdErrWk6I/TfHxxCwfzPI/AAAAAAAAAGU/1IEyX_jFC_w/s320/Screenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536035163884786" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-ukyzSC7YnHA/TfHxxUHcNWI/AAAAAAAAAGc/roatqHHO4Mk/s1600/Screenshot-1.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 186px;" src="http://1.bp.blogspot.com/-ukyzSC7YnHA/TfHxxUHcNWI/AAAAAAAAAGc/roatqHHO4Mk/s320/Screenshot-1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536039823521122" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/--BH-_QMTw9U/TfHxyNYr_MI/AAAAAAAAAGk/8vrFjrf-aS8/s1600/Screenshot-2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 153px;" src="http://3.bp.blogspot.com/--BH-_QMTw9U/TfHxyNYr_MI/AAAAAAAAAGk/8vrFjrf-aS8/s320/Screenshot-2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536055196679362" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/--vpH49qRfjU/TfHxyVyOrII/AAAAAAAAAGs/7plGY5q5U4M/s1600/Screenshot-3.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 161px;" src="http://3.bp.blogspot.com/--vpH49qRfjU/TfHxyVyOrII/AAAAAAAAAGs/7plGY5q5U4M/s320/Screenshot-3.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536057451293826" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-mRr8yQYy51A/TfHxywjbQ4I/AAAAAAAAAG0/bcTbId24aCA/s1600/Screenshot-4.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 256px;" src="http://2.bp.blogspot.com/-mRr8yQYy51A/TfHxywjbQ4I/AAAAAAAAAG0/bcTbId24aCA/s320/Screenshot-4.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536064636961666" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-z1YeEqM7Rwk/TfHyiyB6lzI/AAAAAAAAAG8/S0D_1_hifj8/s1600/Screenshot-5.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 214px;" src="http://1.bp.blogspot.com/-z1YeEqM7Rwk/TfHyiyB6lzI/AAAAAAAAAG8/S0D_1_hifj8/s320/Screenshot-5.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536889667000114" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-wvFY4ipObNs/TfHyjGIn1JI/AAAAAAAAAHE/lLnH8_HPmDI/s1600/Screenshot-6.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 214px;" src="http://3.bp.blogspot.com/-wvFY4ipObNs/TfHyjGIn1JI/AAAAAAAAAHE/lLnH8_HPmDI/s320/Screenshot-6.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536895063839890" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-iXQCSDEhu3o/TfHyjRQkX0I/AAAAAAAAAHM/trP-ZJeqOeU/s1600/Screenshot-7.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 230px;" src="http://1.bp.blogspot.com/-iXQCSDEhu3o/TfHyjRQkX0I/AAAAAAAAAHM/trP-ZJeqOeU/s320/Screenshot-7.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536898049957698" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-GsapW1BAMco/TfHyjtxeJLI/AAAAAAAAAHU/Tlg_CwOYGW4/s1600/Screenshot-8.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 233px;" src="http://4.bp.blogspot.com/-GsapW1BAMco/TfHyjtxeJLI/AAAAAAAAAHU/Tlg_CwOYGW4/s320/Screenshot-8.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536905704154290" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-oVoPWCvKERU/TfHykGDmzUI/AAAAAAAAAHc/iNJ-9cMoh5M/s1600/Screenshot-9.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 213px;" src="http://2.bp.blogspot.com/-oVoPWCvKERU/TfHykGDmzUI/AAAAAAAAAHc/iNJ-9cMoh5M/s320/Screenshot-9.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616536912222670146" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-kclx9Uhcbzs/TfHzFFsMhjI/AAAAAAAAAHk/fkTk1UKhCo4/s1600/Screenshot-10.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 210px;" src="http://4.bp.blogspot.com/-kclx9Uhcbzs/TfHzFFsMhjI/AAAAAAAAAHk/fkTk1UKhCo4/s320/Screenshot-10.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616537479060162098" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-2FjJyK3v9YM/TfHzFaVMvEI/AAAAAAAAAHs/rXxehIq9T9I/s1600/Screenshot-11.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 198px;" src="http://1.bp.blogspot.com/-2FjJyK3v9YM/TfHzFaVMvEI/AAAAAAAAAHs/rXxehIq9T9I/s320/Screenshot-11.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5616537484600851522" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8746937730244892574?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8746937730244892574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/06/pycon-2011-singapore.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8746937730244892574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8746937730244892574'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/06/pycon-2011-singapore.html' title='Pycon 2011 Singapore'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-bZrX6_O8jtA/TfHxRD6BmiI/AAAAAAAAAGM/q90mWl9V8po/s72-c/brett-at-pycon2011-small.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8340161855927558596</id><published>2011-05-19T06:07:00.000-07:00</published><updated>2011-05-19T06:08:30.733-07:00</updated><title type='text'>ODE-ctypes in Blender</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/wi64EhDXD8I?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/wi64EhDXD8I?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;&lt;a href="http://rpythonic.googlecode.com/files/Active-Physics-Blender-release1.zip"&gt;http://rpythonic.googlecode.com/files/Active-Physics-Blender-release1.zip&lt;/a&gt;&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;I never understood why the Bullet physics API is not exposed in the main Blender Python API, and instead its use is restricted to the BGE.  Most users wanting to use physics are either doing digital puppetry, or prototyping game play to export to some other game engine.  In either case we need something more flexible than the BGE for good results.  Luckly with ctypes and ODE, realtime physics can run within the main Blender Python API (bpy) and give us greater flexablity.&lt;br /&gt;&lt;br /&gt;This addon is still an experiment, the primary goal is to test the new object-oriented ctypes ODE wrappers and make sure they work well.  I plan to come back to the addon later on and improve it, and hopefully adding interfaces to cool devices like Kinect, and support for ragdoll rigging.&lt;br /&gt;&lt;br /&gt;The ctypes-ODE wrappers are now much easier to use being object-oriented, rather than previous versions which were mostly flat wrappers around the C API of ODE.  This is done by three methods: 1. assuming that any function where the first argument is a pointer to a structure is a method on that struct, 2. allowing dynamic lookup of functions on structs via __getattr__, 3. and allowing RPythonic to insert portions of manually written wrapper code into the generated wrapper.  The result is a wrapper that is almost fully compatible with the original hand-written PyODE.  The difference is this wrapper is much easier to install because ctypes dynamically loads the shared library, and therefore we can run within Blender's embeded Python without any need for a local installation of Python on the end users machine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8340161855927558596?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8340161855927558596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/05/ode-ctypes-in-blender.html#comment-form' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8340161855927558596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8340161855927558596'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/05/ode-ctypes-in-blender.html' title='ODE-ctypes in Blender'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-4345123881764748778</id><published>2011-05-12T04:44:00.000-07:00</published><updated>2011-05-13T13:24:27.530-07:00</updated><title type='text'>Kinect Blender Addon</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/8Bl1RMg-TCs?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/8Bl1RMg-TCs?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://rpythonic.googlecode.com/files/BlenderAddons-kinect-test1.zip"&gt;source code&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This addon is just a first test, more to come soon...&lt;br /&gt;&lt;br /&gt;Requires libfreenect and opencv shared libraries are on your PATH (copy them to blender's root directory).  So for Windows this would be: libfreenect_sync.dll, libcv.dll, libhighgui.dll.  Linux users can just apt-get the packages as normal.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-4345123881764748778?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/4345123881764748778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/05/kinect-blender-addon.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4345123881764748778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4345123881764748778'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/05/kinect-blender-addon.html' title='Kinect Blender Addon'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-230667809926359155</id><published>2011-05-10T06:42:00.000-07:00</published><updated>2011-05-10T20:08:58.382-07:00</updated><title type='text'>Kinect Hand Tracking</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-BqoJa6bFNms/Tcn9treXbyI/AAAAAAAAAFc/n-4a1FAnEt4/s1600/tripplethreaded-vs-dualthreaded.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 170px;" src="http://1.bp.blogspot.com/-BqoJa6bFNms/Tcn9treXbyI/AAAAAAAAAFc/n-4a1FAnEt4/s400/tripplethreaded-vs-dualthreaded.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5605290172445323042" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The OpenCV-ctypes bindings may have a high overhead per-call because of the thick broiler-plate hiding ctypes, and ctypes itself using calling via libffi.  However, given that quad-core CPU's are now standard; ctypes escaping the GIL can more than make up for the overhead, and gain higher performance than the official compiled OpenCV bindings.&lt;br /&gt;&lt;br /&gt;The first Kinect/OpenCV test is dual threaded, with libfreenect and OpenCV each running in their own threads.  In the graph to the right we can see the CPU usage, the blue and green cores are doing most of the work, and are out of phase - meanwhile the red and orange cores are not being used much.  The main Python thread runs 70million operations in 30 seconds, and the frame rate display is low, with only about 100 updates.&lt;br /&gt;&lt;br /&gt;Much of the work OpenCV is doing can be done in two passes, so it becomes easy to split the code into two threads.  The tripple-threaded version seen to the right showed much better CPU usage.  Blue and green cores are now running in-phase, without the blue core spiking.  The red and orange cores now do much more work, and are only slightly out of phase.  The main Python thread runs 50million operations in 30 seconds, lower than the dual threaded version, but the result is a four times faster display update - with 400 updates.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Hand Detection&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/8pKS4cGWN4o?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/8pKS4cGWN4o?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;HAAR wavelets are by far the most popular way of detecting hands or other features, the problem is speed, the Haar cascade can easily take a single core to 100% usage, and this is not suitable if we plan to run this hand detector within another program like Blender or RealXtend and maintain good performance.&lt;br /&gt;&lt;br /&gt;Another method that is faster is to check for convexity defects of contours, this &lt;a href="http://www.andol.info/hci/1459.htm"&gt;blog&lt;/a&gt; by Andol has a good overview of the techniques.  Using the heuristic &lt;i&gt;4 or more defects is hand&lt;/i&gt;, and simply checking for defects among the many contour passes will yield false-positives from noise.  The first trick is to filter out this noise on the contour with extreme polygon reduction, using the function cv.ApproxPoly with a factor of 20-30.0 or more.  This reduces the head to a few triangles, while keeping the star-shape of the hand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-230667809926359155?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/230667809926359155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/05/kinect-hand-tracking.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/230667809926359155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/230667809926359155'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/05/kinect-hand-tracking.html' title='Kinect Hand Tracking'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-BqoJa6bFNms/Tcn9treXbyI/AAAAAAAAAFc/n-4a1FAnEt4/s72-c/tripplethreaded-vs-dualthreaded.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-1172273600405937307</id><published>2011-05-07T23:35:00.000-07:00</published><updated>2011-05-07T23:40:13.109-07:00</updated><title type='text'>Kinect and OpenCV</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/6XAexYQV32s?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/6XAexYQV32s?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;This research project was made possible by Adminotech.  Kinect and OpenCV make a great combination, and now getting started is even easier with this pure-ctypes versions of both Libfreenect and OpenCV2.  Python bindings already existed for both Libfreenect and OpenCV, but these must be compiled and linked to a given Python version.  This is a pain for developers who need to support multiple platforms and multiple Python versions.  Using pure ctypes bindings, only the shared library is needed per-platform, which can even be found pre-compiled.  The other benefit of ctypes is threading and escaping the GIL, both Freenect and OpenCV can each run in their own threads.&lt;br /&gt;&lt;br /&gt;The current aim of this research is to track the hands with as little CPU resources as possible, and to maintain 30fps read rate from the Kinect device.  Streaming both video and depth from the Kinect can have serious effects on performance, so we will need a technique that can work with just the depth buffer.  OpenCV is fast enough to sweep through about 12 passes of the depth buffer with increasing thresholds and hold 30fps.  This is a good starting point since the contour of the hand is still clearly visible in at least one of the sweeps.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;RPythonic Smart Wrappers&lt;/h4&gt;&lt;br /&gt;Another goal of the project was to make it easy for Python developers who are already familiar with the PyOpenCV API to migrate to the ctypes API.  RPythonic now generates much smarter ctypes bindings which hide &lt;b&gt;almost all&lt;/b&gt; of the &lt;i&gt;ctypes-broiler-plate&lt;/i&gt; so that migration is simply a matter of changing the import header.  Callback functions can now be directly passed to wrapped C function, and they will automatically be converted to a PyCFuncPtrType.  Also lists and tuples are now valid arguments to wrapped C functions, if the argument type expects an array the tuple or list will be automatically converted.  Or, if the argument expects some Structure, the list or tuple is used to initialze the Structure.&lt;br /&gt;&lt;br /&gt;There are still some differences with the hand-made PyOpenCV and this automatically generated ctypes version.  These mainly concern C functions that are passed a pointer that is then filled with some result, instead of returning a pointer to the result.  The hand-made PyOpenCV wrappers deal with this properly and return a pointer without the user having to create it first and pass it to the function.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pastebin.com/Tc0JaSZE"&gt;sample on pastebin&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-1172273600405937307?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/1172273600405937307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/05/kinect-and-opencv.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/1172273600405937307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/1172273600405937307'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/05/kinect-and-opencv.html' title='Kinect and OpenCV'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-2306924300493510758</id><published>2011-05-04T01:49:00.000-07:00</published><updated>2011-05-04T01:58:45.052-07:00</updated><title type='text'>RPython and iOS</title><content type='html'>&lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.3.3.tar.bz2"&gt;RPythonic 0.3.3&lt;/a&gt; now can wrap the standard C libraries of iOS, the following RFFI wrappers are included for:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt; &lt;li&gt;OpenGLES&lt;/li&gt;&lt;br /&gt; &lt;li&gt;OpenAL&lt;/li&gt;&lt;br /&gt; &lt;li&gt;QuartzCore&lt;/li&gt;&lt;br /&gt; &lt;li&gt;CoreFoundation&lt;/li&gt;&lt;br /&gt; &lt;li&gt;CoreGraphics&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Compiling RPython on iOS remains untested while I do not have an iPhone, but in theory it is not a problem because RPython is translated into standard ANSI C.&lt;br /&gt;&lt;br /&gt;The Android wrappers have also been updated, and numerous bugs were fixed with ctypes wrapper generation.  Most of the ctypes examples have not yet been updated to using the new RPythonic-API, only the simple-gtk demo, and the Kinect demo seen below.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/--k10V6EdXZs/TcETb0mg32I/AAAAAAAAAFU/0G6YMjJGlqs/s1600/kinect-sdl-ctypes-test.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 218px;" src="http://3.bp.blogspot.com/--k10V6EdXZs/TcETb0mg32I/AAAAAAAAAFU/0G6YMjJGlqs/s320/kinect-sdl-ctypes-test.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5602780780123184994" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-2306924300493510758?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/2306924300493510758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/05/rpython-and-ios.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/2306924300493510758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/2306924300493510758'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/05/rpython-and-ios.html' title='RPython and iOS'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/--k10V6EdXZs/TcETb0mg32I/AAAAAAAAAFU/0G6YMjJGlqs/s72-c/kinect-sdl-ctypes-test.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-771791110713011590</id><published>2011-04-30T19:32:00.000-07:00</published><updated>2011-04-30T19:35:21.897-07:00</updated><title type='text'>RPython to Javascript</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-tNA_EcDf7tU/TbzGb2BcDjI/AAAAAAAAAFM/wDpMpYpAX0I/s1600/rpython-javascript.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 258px;" src="http://2.bp.blogspot.com/-tNA_EcDf7tU/TbzGb2BcDjI/AAAAAAAAAFM/wDpMpYpAX0I/s400/rpython-javascript.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5601570218202369586" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.3.1.tar.bz2"&gt;RPythonic 0.3.1&lt;/a&gt; now integrates with the fantastic &lt;a href="https://github.com/kripken/emscripten"&gt;Emscripten&lt;/a&gt; project to allow conversion of RPython into Javascript.  PyPy itself had an experimental backend for Javascript, but this was recently removed because no one was maintaining it.  Emscripten is more useful in any case because it will allow for the translation of C libraries used within RPython/RFFI.  The PyPy backend would have been limited to only translating RPython code.&lt;br /&gt;&lt;br /&gt;Translation begins with passing the RPython entry-point to rpy.compile().  Compile will then take care of translating to C, passing to Emmaken and LLVM, disassemble the LLVM binary, optimize with Emscripten's remove-dead-functions, pass that to emscripten.py to generate the javascript, and finally optimize the javascript with Closure.  The result is 6,000 lines of javascript, most of which is RPython's reference counting garbage collector.&lt;br /&gt;&lt;br /&gt;To make RPythonic easy to install several things are now included:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt; prebuilt d8 (google v8 javascript interpreter)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt; Emscripten configured for Ubuntu 11.04&lt;/li&gt;&lt;br /&gt;  &lt;li&gt; Closure&lt;/li&gt;&lt;br /&gt;  &lt;li&gt; PyCparser&lt;/li&gt;&lt;br /&gt;  &lt;li&gt; CppHeaderParser&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-771791110713011590?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/771791110713011590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/04/rpython-to-javascript.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/771791110713011590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/771791110713011590'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/04/rpython-to-javascript.html' title='RPython to Javascript'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-tNA_EcDf7tU/TbzGb2BcDjI/AAAAAAAAAFM/wDpMpYpAX0I/s72-c/rpython-javascript.png' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-3272674054108030550</id><published>2011-04-25T00:29:00.000-07:00</published><updated>2011-04-25T00:38:41.039-07:00</updated><title type='text'>Stackless Compiled Modules</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-wRNRL6NDiLs/TbUk-KzeoZI/AAAAAAAAAEE/XbCMJjwXTJc/s1600/rpython-ctypes-stackless.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 249px;" src="http://2.bp.blogspot.com/-wRNRL6NDiLs/TbUk-KzeoZI/AAAAAAAAAEE/XbCMJjwXTJc/s400/rpython-ctypes-stackless.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5599422362175840658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.3.0.tar.bz2"&gt;RPythonic 0.3.0&lt;/a&gt; now includes an example of using stackless in RPython loaded into Python over ctypes.  The stackless mainloop can be called from a Python thread, the GIL is released so it is non-blocking.  Values from a shared-object can be read in a thread-safe manner.  The next step is to allow setting values from Python in a thread-safe way using locks.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;&lt;a href="http://pastebin.com/hsatRnWR"&gt;stackless test1&lt;/a&gt;&lt;/h4&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-3272674054108030550?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/3272674054108030550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/04/stackless-compiled-modules.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3272674054108030550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3272674054108030550'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/04/stackless-compiled-modules.html' title='Stackless Compiled Modules'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-wRNRL6NDiLs/TbUk-KzeoZI/AAAAAAAAAEE/XbCMJjwXTJc/s72-c/rpython-ctypes-stackless.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-7347315476203584182</id><published>2011-04-22T23:25:00.000-07:00</published><updated>2011-07-06T08:04:51.919-07:00</updated><title type='text'>RPython Shared Objects</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-eUCCJN8t8PI/TbJyAOJfMOI/AAAAAAAAAD8/YvSuZrwNu-E/s1600/rpython-ctypes-shared-objects.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 229px;" src="http://4.bp.blogspot.com/-eUCCJN8t8PI/TbJyAOJfMOI/AAAAAAAAAD8/YvSuZrwNu-E/s400/rpython-ctypes-shared-objects.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5598662634898403554" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.2.9.tar.bz2"&gt;RPythonic 0.2.9&lt;/a&gt; contains initial support for sharing object instances between Python and a RPython compiled module.  Attributes are read only, so for now you must define setter functions for class attributes you need to set from Python.  An interesting side-effect of having read only attributes is thread-safety.  It appears that it is safe to read attributes from the main thread even when the same object is having its attributes updated from another thread calling RPython functions.&lt;br /&gt;&lt;br /&gt;&lt;hr/&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;rpy = rpythonic.RPython()&lt;br /&gt;&lt;br /&gt;@rpy.object&lt;br /&gt;class MyRpyObject(object):&lt;br /&gt; def __init__(self, x=.0, y=.0, z=.0):&lt;br /&gt;  self.x = x; self.y = y; self.z = z&lt;br /&gt; def add( self, x=.0,y=.0,z=.0 ): self.x += x; self.y += y; self.z += z&lt;br /&gt; def sum( self ): return self.x + self.y + self.z&lt;br /&gt; def show( self ): print '&lt;%s %s %s&gt;' %(self.x, self.y, self.z)&lt;br /&gt;&lt;br /&gt;rpy.cache('test2', refresh=0) &lt;br /&gt;&lt;br /&gt;o = MyRpyObject()&lt;br /&gt;print o.x&lt;br /&gt;o.add( 1,2,3 )&lt;br /&gt;print o.x&lt;br /&gt;o.show()&lt;br /&gt;&lt;br /&gt;GO = True&lt;br /&gt;def ctypes_thread():&lt;br /&gt; print( 'thread start' )&lt;br /&gt; while GO:&lt;br /&gt;  o.add( 0.1, 0.2, 0.3 )&lt;br /&gt; print( 'thread exit' )&lt;br /&gt;&lt;br /&gt;import thread, time&lt;br /&gt;thread.start_new_thread( ctypes_thread, () )&lt;br /&gt;&lt;br /&gt;for i in range(1000*10):&lt;br /&gt; print o.x, o.y, o.z   # getting values from _pointer.contents is thread safe?  seems like it!&lt;br /&gt; #o.show()      # not thread safe&lt;br /&gt;&lt;br /&gt;GO = False&lt;br /&gt;print('test exit')&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-7347315476203584182?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/7347315476203584182/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/04/rpython-shared-objects.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7347315476203584182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7347315476203584182'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/04/rpython-shared-objects.html' title='RPython Shared Objects'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-eUCCJN8t8PI/TbJyAOJfMOI/AAAAAAAAAD8/YvSuZrwNu-E/s72-c/rpython-ctypes-shared-objects.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-2614353154497573526</id><published>2011-04-18T04:28:00.000-07:00</published><updated>2011-04-18T19:17:11.233-07:00</updated><title type='text'>RPython modules for CPython</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-XYV8T7IbQiM/TazwKQE8u4I/AAAAAAAAAD0/QUWsS2z3fWY/s1600/rpy-cpy-ext-mod2.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 220px;" src="http://4.bp.blogspot.com/-XYV8T7IbQiM/TazwKQE8u4I/AAAAAAAAAD0/QUWsS2z3fWY/s400/rpy-cpy-ext-mod2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5597112495819766658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It is now possible to use RPythonic to write CPython extension modules in RPython, RPythonic will work as the front-end that uses the PyPy translation toolchain to generate C code, compile it with GCC, package it in a cache directory with generated ctypes wrappers, and finally replace the decorated functions in-place in CPython.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;&lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.2.8-refactored.tar.bz2"&gt;RPythonic 0.2.8&lt;/a&gt;&lt;/h4&gt;&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;RPython CPython Module API&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt; import os, sys&lt;br /&gt; sys.path.append('..')&lt;br /&gt; import rpythonic&lt;br /&gt; rpythonic.set_cache( '../cache' )&lt;br /&gt; rpythonic.set_pypy_root( '../../pypy' )&lt;br /&gt; ################################&lt;br /&gt; rpy = rpythonic.RPython()&lt;br /&gt; @rpy.bind()     # declare arg types is optional if,&lt;br /&gt; def add( a=1, b=1000 ):   # keyword defaults are given&lt;br /&gt;  return a+b&lt;br /&gt; @rpy.bind(a=float, b=float)&lt;br /&gt; def sub( a, b ):&lt;br /&gt;  return a-b&lt;br /&gt; rpy.cache('test1')    # only compiles if cache is dirty&lt;br /&gt; ########### now functions are using compiled version ###########&lt;br /&gt; print add( 99, 88 )&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Module Compiling and Caching&lt;/h3&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-gJf60KdwsdM/TazwKIPpaUI/AAAAAAAAADs/05b8Wd2m4PA/s1600/rpy-cpy-ext-mod1.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 262px;" src="http://4.bp.blogspot.com/-gJf60KdwsdM/TazwKIPpaUI/AAAAAAAAADs/05b8Wd2m4PA/s400/rpy-cpy-ext-mod1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5597112493717154114" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-2614353154497573526?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/2614353154497573526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/04/rpython-modules-for-cpython.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/2614353154497573526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/2614353154497573526'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/04/rpython-modules-for-cpython.html' title='RPython modules for CPython'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-XYV8T7IbQiM/TazwKQE8u4I/AAAAAAAAAD0/QUWsS2z3fWY/s72-c/rpy-cpy-ext-mod2.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-3546039017139470364</id><published>2011-02-10T23:28:00.000-08:00</published><updated>2011-02-11T00:33:30.049-08:00</updated><title type='text'>pyvnc realxtend addon</title><content type='html'>&lt;iframe title="YouTube video player" width="640" height="360" src="http://www.youtube.com/embed/rbPj8HSBS5U" frameborder="0" allowfullscreen&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;The old &lt;a href="http://code.google.com/p/python-vnc-viewer/"&gt;Python-VNC-Viewer&lt;/a&gt; project went to great effort to gain the best performance, even optimizing the image bit-depth, direct buffer copies into SDL surfaces, and grouped blitting - but it still remains unuseably slow.  The readme file even contains this: &lt;i&gt;"What is it good for? Nothing ;-) Use the original VNC viewer for better performance."&lt;/i&gt;  This got me thinking, maybe I should give up, sounds like Python can't handle this problem?&lt;br /&gt;&lt;br /&gt;At first I considered using twistedmatrix and leveraging the work already done by Python-VNC-Viewer.  I knew that using ctypes and going direct to libvncserver would have much better performance; but not as much as I expected - at least at first.  It turns out twisted is infact pretty fast, even compared to a compiled C library.  Testing single threaded ctypes-libvnc vs single threaded twisted-vnc, I found that ctypes-libvnc is only modestly faster - not many times faster.  The bottleneck is in reading bytes from the server, the VNC (rfb) protocol sends lots of data despite being jpeg compressed and chunked.  The slowness comes from single-threading, and having to block when reading these bytes.  Thats the end of the story for twisted, since its threads are under the GIL.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The GIL and Ctypes (our Friends):&lt;/h3&gt;&lt;br /&gt;I often read people bashing the Python and the GIL (global interpreter lock), and that Python lacks true support for threads.  In fact Python does have threads, its just that they lock every time you touch a variable, at first this sounds like bad news.  However, what is not being point out more often, is that ctypes automatically releases the GIL when calling into C.  The GIL now becomes our friend because it reduces the work needed to make things thread-safe.  Making PyVNC thread-safe was trivial.  The final results show that dual-threaded ctypes-vnc is many times faster than single-threaded twisted-vnc.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Test Machine 2ghz pent4&lt;br /&gt; python-vnc-viewer single threaded (twistedmatrix)&lt;br /&gt;  average refresh 0.6 seconds&lt;br /&gt; pyvnc dual threaded (ctypes libvnc)&lt;br /&gt;  average refresh 0.07 seconds&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Naali (Tundra) Addon:&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;1. get this &lt;a href="http://pastebin.com/63T8Lhzf"&gt;addon script&lt;/a&gt; and save it as addon-installer.py&lt;br /&gt;2. download &lt;a href="http://pyvnc.googlecode.com/files/pyvnc.addon"&gt;addon xml file&lt;/a&gt; and save it some where&lt;br /&gt;3. run: 'python addon-installer.py pyvnc.addon /path/to/naali/bin/pymodules'&lt;br /&gt;&lt;br /&gt;The script will then take care of downloading the modules required for your platform, now when you start Tundra the vnc-server and vnc-client windows should appear.  If the addon script does not work for you for some reason, you can get the latest source for pyvnc on google code &lt;a href="http://code.google.com/p/pyvnc/"&gt;here.&lt;/a&gt;  Note you can run pyvnc standalone, use the addon script just give a different path to save it, then run it by: 'python pyvnc.py' (as server), 'python pyvnc.py --client' (as client).&lt;br /&gt;&lt;br /&gt;Special thanks to Adminotech LTD who sponsored this project, and everybody in IRC #realxtend-dev who helped me in understanding Naali and Qt.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-3546039017139470364?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/3546039017139470364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/02/pyvnc-realxtend-addon.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3546039017139470364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3546039017139470364'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/02/pyvnc-realxtend-addon.html' title='pyvnc realxtend addon'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/rbPj8HSBS5U/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-4902939709531398248</id><published>2011-01-29T00:55:00.000-08:00</published><updated>2011-01-29T00:57:40.330-08:00</updated><title type='text'>ctypeslibs and rffilibs</title><content type='html'>&lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.2.2.tar.gz"&gt;RPythonic-0.2.2&lt;/a&gt; now includes several pre-generated wrappers, see the list of libraries below.  The wrappers come in both formats: ctypes and rffi.  The main issue in using RPython to write complex standalone programs is it can not connect with external libraries like Python can.  This problem is now in the process of going away using generated bindings.  There are still problems with the wrapper generation, so this is still a work in progress.&lt;br /&gt; . OpenGL&lt;br /&gt; . GTK&lt;br /&gt; . libpng&lt;br /&gt; . ODE (Open Dynamics Engine)&lt;br /&gt; . SDL&lt;br /&gt; . audiofile&lt;br /&gt; . openjpeg&lt;br /&gt; . tiffio&lt;br /&gt;&lt;br /&gt;Once wrapper generation is effective for binding to both C and C++, the last hurdle to make RPython easier to use is to lift some of the restrictions below.  Some of these restrictions will never go away, afterall it is Restricted-Python.&lt;br /&gt;&lt;br /&gt;&lt;h2&gt;RPython is more or less defined as:&lt;/h2&gt;&lt;br /&gt;&lt;br /&gt;RPython rule1: All rules can be broken if you can meta-program your way around them&lt;br /&gt;( example: using function decorators to rewrite functions to comply with the rules below )&lt;br /&gt;&lt;br /&gt;RPython rule2: All globals and class-level attributes are constants. &lt;br /&gt;       . PyPy considers globals to always be constant, including class- &lt;br /&gt;level attributes. Instead use singleton instances to store global changeable state.&lt;br /&gt;&lt;br /&gt;RPython rule3: Tuples may contain mixed types, but it should be avoided. &lt;br /&gt;       . PyPy can not use a loop to iterate over mixed tuples &lt;br /&gt; ( because to iterate over a tuple it must be cast to a list - ie. for x in list(mytuple) )&lt;br /&gt;&lt;br /&gt;RPython rule4: Dicts and lists must contain compatible types (homogeneous). &lt;br /&gt;       . ShedSkin and PyPy common rule &lt;br /&gt;&lt;br /&gt;RPython rule5: No reflection (getattr, hasattr, etc..) &lt;br /&gt;       . PyPy allows for limited getattr, hasattr.., but only if the string is 'concrete.'&lt;br /&gt;&lt;br /&gt;RPython rule6: No runtime evaluation. &lt;br /&gt;       . Neither ShedSkin or PyPy support eval or exec &lt;br /&gt;&lt;br /&gt;RPython rule7: No **keywordargs &lt;br /&gt;       . Neither ShedSkin or PyPy support **kw &lt;br /&gt;&lt;br /&gt;RPython rule 8:  Within a scope, a variable must not change type; however,&lt;br /&gt;`None` can be used as a place holder and intermixed with any type, but NOT with ints or floats.&lt;br /&gt;&lt;br /&gt;RPython rule 9: For instances to be of a compatible type, they must inherit from a common base class. &lt;br /&gt;       . ShedSkin and PyPy requirement &lt;br /&gt;&lt;br /&gt;RPython rule 10: For compatible instances stored in the same list, but &lt;br /&gt;of different subclasses, to call methods with incompatible signatures &lt;br /&gt;or access attributes unique to the subclass, the type must be asserted &lt;br /&gt;first: &lt;br /&gt;       assert isinstance(a,MySubClass) &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;RPython rule 11: The *assert isinstance* statement acts like a cast in &lt;br /&gt;C/C++, turning `SomeObject` into the real subclass instance.  This &lt;br /&gt;solves problems like in rule10, and in other cases where an instance &lt;br /&gt;of uncertian type must be passed to a function that expects a certian &lt;br /&gt;type. &lt;br /&gt;&lt;br /&gt;RPython rule 12: No overloading, except for __init__ and __del__. &lt;br /&gt;&lt;br /&gt;RPython rule 13: Attribute variables should be of consistent type in &lt;br /&gt;all subclasses.  Do not create a subclass that redefines the type of &lt;br /&gt;an attribute. &lt;br /&gt;&lt;br /&gt;RPython rule 14: The calling and return signature of a function must &lt;br /&gt;not change.  The types of function arguments must always be the same &lt;br /&gt;for each function call.  Only `None` can be intermixed but not with ints or floats.  &lt;br /&gt;Function returns must always have the same type. &lt;br /&gt;&lt;br /&gt;RPython rule 15: Its not recommended to define subclasses with like- &lt;br /&gt;named functions that have different signatures.  Casting by `assert &lt;br /&gt;isinstance` is the work around that allows this rule to be bent.&lt;br /&gt;In addition when doing this you may need to follow rule16 below.&lt;br /&gt;&lt;br /&gt;RPython rule 16: The root base class should define dummy functions,&lt;br /&gt;if multiple subclasses will overload them those functions with&lt;br /&gt;incompatible signatures.&lt;br /&gt;       . Prevents method `demotion` error in PyPy &lt;br /&gt;&lt;br /&gt;RPython Extra Quirks:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; 1. a string of multiple characters is not the same as a string of a single character,&lt;br /&gt; some functions will only accept a single character string; Examples:&lt;br /&gt;  this is valid:&lt;br /&gt;   a = 'hello world'&lt;br /&gt;   a.split(' ')&lt;br /&gt;   a.split('x')&lt;br /&gt;  this is invalid:&lt;br /&gt;   a = 'hello world'&lt;br /&gt;   a.split()&lt;br /&gt;   a.split('world')&lt;br /&gt;&lt;br /&gt; 2. some builtin functions require an argument where normally they do not, Example:&lt;br /&gt;  this is valid:&lt;br /&gt;   a = ['x','y','z']&lt;br /&gt;   a.pop( len(a)-1 ) # pop by index, pop the last item&lt;br /&gt;  this is invalid:&lt;br /&gt;   a = ['x','y','z']&lt;br /&gt;   a.pop() # pop requires an index so this fails&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-4902939709531398248?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/4902939709531398248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/01/ctypeslibs-and-rffilibs.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4902939709531398248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4902939709531398248'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/01/ctypeslibs-and-rffilibs.html' title='ctypeslibs and rffilibs'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8316198609640740946</id><published>2011-01-22T00:58:00.000-08:00</published><updated>2011-01-22T01:07:08.524-08:00</updated><title type='text'>Wrapping The Android NDK</title><content type='html'>The latest RPythonic, &lt;a href="http://rpythonic.googlecode.com/files/RPythonic-0.2.1.tar.bz2"&gt;here,&lt;/a&gt; can generate &lt;a href="http://codespeak.net/pypy/dist/pypy/doc/rffi.html"&gt;rffi&lt;/a&gt; bindings for almost the entire Android NDK.  Using these bindings compiled RPython can call C functions, like OpenGLES display functions.  The randroid-compile.py script is updated to call &lt;a href="http://ant.apache.org/"&gt;ant&lt;/a&gt; and automatically build the .apk file for you in debug mode (unsigned).  Some issues remain like native-app-glue integration, and parsing errors from &lt;a href="http://code.google.com/p/pycparser/"&gt;pycparser&lt;/a&gt; that prevent some of the NDK from being wrapped.  The following are wrapped:&lt;br /&gt;&lt;b&gt;&lt;br /&gt; . android/api_level&lt;br /&gt; . android/bitmap&lt;br /&gt; . android/input&lt;br /&gt; . android/keycodes&lt;br /&gt; . android/looper&lt;br /&gt; . android/native_activity&lt;br /&gt; . android/native_window&lt;br /&gt; . android/native_window_jni&lt;br /&gt; . android/obb&lt;br /&gt; . android/rect&lt;br /&gt; . android/storage_manager&lt;br /&gt; . EGL/egl&lt;br /&gt; . EGL/eglplatform&lt;br /&gt; . GLES/gl&lt;br /&gt; . GLES/glplatform&lt;br /&gt; . GLES2/gl2&lt;br /&gt; . GLES2/gl2platform&lt;br /&gt; . SLES/OpenSLES&lt;br /&gt; . SLES/OpenSLES_AndroidConfiguration&lt;br /&gt; . SLES/OpenSLES_Platform&lt;br /&gt;&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8316198609640740946?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8316198609640740946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/01/wrapping-android-ndk.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8316198609640740946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8316198609640740946'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/01/wrapping-android-ndk.html' title='Wrapping The Android NDK'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-9177564579747237633</id><published>2011-01-18T02:25:00.000-08:00</published><updated>2011-01-29T19:38:58.955-08:00</updated><title type='text'>Android and RPython</title><content type='html'>Wrapping the ndk-build tool and auto generating the Android.mk file is the simplest way to get RPython code to compile using the Android NDK.  Note this only works on 32bit linux at the moment.&lt;br /&gt;&lt;br /&gt;The PyPy translation toolchain dumps several C source and header files when calling translation.source_c(), and these are not returned by the function; instead they must be collected from translation.driver.cbuilder.targetdir and ..cbuilder.extrafiles.  These files are then copied to a new project, in a 'jni' subdirectory, and the Android.mk make-file is generated.  All C files except for testing_1.c should be built as static libraries.  Android.mk builds each one using "include $(BUILD_STATIC_LIBRARY)".  Finally the shared library setup to include all the static libraries using "LOCAL_STATIC_LIBRARIES := structimpl nonfuncnodes implement etc..."&lt;br /&gt;&lt;br /&gt;To jump in a try out translating and compiling some RPython code:&lt;br /&gt;1. download and setup the Android SDK and NDK, &lt;br /&gt;2. get the PyPy source code,&lt;br /&gt;3. get randroid-compile.py from the latest &lt;a href="http://code.google.com/p/rpythonic/"&gt;RPythonic&lt;/a&gt;, follow the README file for where to place the PyPy source code and Android SDK/NDK, &lt;br /&gt;4. modify PyPy's source code - small changes see below*&lt;br /&gt;5. run randroid-compile.py my_rpython_script.py&lt;br /&gt;&lt;br /&gt;*4. The PyPy C backend contains some handwritten C code, found under pypy/translator/c/src.  This will have to be changed just slightly to allow for minor differences between Android-Linux and GNU/Linux.  &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    1. " &amp;&amp; !defined(ANDROID)"&lt;br /&gt;    (add to the end of line 49 in debug_print.h)&lt;br /&gt;&lt;br /&gt;    2. "# ifdef ANDROID&lt;br /&gt;  decimal_point = ".";&lt;br /&gt;  decimal_point_len = 1;&lt;br /&gt;        # else&lt;br /&gt;  locale_data = localeconv();&lt;br /&gt;  decimal_point = locale_data-&gt;decimal_point;&lt;br /&gt;  decimal_point_len = strlen(decimal_point);&lt;br /&gt;        # endif&lt;br /&gt;    (replace lines 43, and 112 of ll_strtod.h)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;&lt;br /&gt;or you can download these patched versions &lt;a href="http://pastebin.com/0cRd8Pjv"&gt;[1]&lt;/a&gt; &lt;a href="http://pastebin.com/agViHgg8"&gt;[2]&lt;/a&gt; and overwrite them in pypy/translator/c/src/&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;5. run "python rpy-android.py"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-9177564579747237633?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/9177564579747237633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/01/android-and-rpython.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/9177564579747237633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/9177564579747237633'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/01/android-and-rpython.html' title='Android and RPython'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8694414025597901333</id><published>2011-01-06T01:37:00.000-08:00</published><updated>2011-01-06T01:54:21.730-08:00</updated><title type='text'>ctypes-pygtk in blender25</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Z42nrTY4F64/TSWONEOF4VI/AAAAAAAAADc/ginOzn0Chlk/s1600/ctypes-gtk-blender-addon.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 217px;" src="http://1.bp.blogspot.com/_Z42nrTY4F64/TSWONEOF4VI/AAAAAAAAADc/ginOzn0Chlk/s400/ctypes-gtk-blender-addon.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5559005670181036370" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href="http://pastebin.com/cBj0SJUr"&gt;addon source code&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;The latest RPythonic can generate ctypes bindings for GTK that are also Python3 and Blender compatible.  Currently Blender lacks a way to call a python function at a timed interval, this would be a show stopper for the old PyGTK bindings where gtk_main is a blocking function.  Luckily ctypes releases the GIL when a C function is called, as detailed by Christopher Swenson, &lt;a href="http://www.caswenson.com/past/2009/6/13/bypassing_the_python_gil_with_ctypes/"&gt;here&lt;/a&gt;, if the call is wrapped in a python thread, then gtk_main can run in its own non-blocking thread while Blender continues to update smoothly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8694414025597901333?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8694414025597901333/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2011/01/ctypes-pygtk-in-blender25.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8694414025597901333'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8694414025597901333'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2011/01/ctypes-pygtk-in-blender25.html' title='ctypes-pygtk in blender25'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Z42nrTY4F64/TSWONEOF4VI/AAAAAAAAADc/ginOzn0Chlk/s72-c/ctypes-gtk-blender-addon.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-3581717889979362849</id><published>2010-12-15T22:30:00.000-08:00</published><updated>2010-12-15T23:43:47.431-08:00</updated><title type='text'>Blender Ogre Collision Panel</title><content type='html'>&lt;object width="640" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/3SK-ApfP6wQ?fs=1&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/3SK-ApfP6wQ?fs=1&amp;amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Triangle count may not be much of an issue for rendering, but it still remains a bottle neck when doing collisions even with fast libraries like Bullet.  The exporter uses blender-game-engine (BGE) collision primitive types; or a second proxy-mesh can be parented underneath the display mesh, and then the proxy is substituted as the triangle mesh for collision.  Collision primitives are of course the fastest option.  By default Blender does not display the bounding type when a BGE collision primitive is choosen, so the collision panel seen here will do the extra work and display the correct bounding type so that the user can get visual feedback of their changes.  If the collision type 'Triangle Mesh' is chosen, the object is set to show wire-frame over shaded.  (note there is a bug in blender 'Capsule' type may not show)&lt;br /&gt;&lt;br /&gt;Setting a proxy-mesh for triangle collision can be done manually by the user, but this is error prone, so the interface automates this process by doing the following: copies object, parents, locks translation, renames to 'collision', and then applies a decimate modifier.  The decimate modifier is then controlled from the parents panel.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-3581717889979362849?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/3581717889979362849/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/12/blender-ogre-collision-panel.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3581717889979362849'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3581717889979362849'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/12/blender-ogre-collision-panel.html' title='Blender Ogre Collision Panel'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-3617461071608935388</id><published>2010-12-10T19:42:00.000-08:00</published><updated>2010-12-10T19:54:32.615-08:00</updated><title type='text'>Ogre Texture and Material Panels</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Z42nrTY4F64/TQLzk6-rQ4I/AAAAAAAAADQ/UWEmwJXQB9k/s1600/ogre-simple-mat.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 216px; height: 400px;" src="http://3.bp.blogspot.com/_Z42nrTY4F64/TQLzk6-rQ4I/AAAAAAAAADQ/UWEmwJXQB9k/s400/ogre-simple-mat.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5549265506506851202" /&gt;&lt;/a&gt;&lt;br /&gt;When dealing with a hijacked interface, often the user will get confused which options are supported by the exporter, and which ones are not.  To solve this problem two custom panels have been added to the Material and Texture tabs under Properties that restrict the UI to only what is supported.  These extra panels also streamline the workflow for the user, and provides some additional error checking to make sure they have entered valid options.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Ogre Texture Units have very nice options for texture animation.  Only the most basic options are exposed here: scrolling and rotate.  Another animation option Wave-xform allows for any combination of cyclic animation to position, rotation or scaling.  Unfortunately, Wave-xform can not be included because, at this time, Blender does not support 'Custom-Properties' for texture-slot objects.  If your an Ogre/Blender user, and want Wave-xform texture support, you can lobby for it in "blendercoders" on IRC freenode by talking directly with the Blender developers there.  Custom-Property support for texture-slots requires a change at the C-level of Blender, and it can not be done from this addon script alone.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_Z42nrTY4F64/TQLzk6j4UoI/AAAAAAAAADI/TW-EoUlXxe8/s1600/ogre-simple-tex.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 348px; height: 400px;" src="http://4.bp.blogspot.com/_Z42nrTY4F64/TQLzk6j4UoI/AAAAAAAAADI/TW-EoUlXxe8/s400/ogre-simple-tex.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5549265506394460802" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-3617461071608935388?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/3617461071608935388/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/12/ogre-texture-and-material-panels.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3617461071608935388'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3617461071608935388'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/12/ogre-texture-and-material-panels.html' title='Ogre Texture and Material Panels'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Z42nrTY4F64/TQLzk6-rQ4I/AAAAAAAAADQ/UWEmwJXQB9k/s72-c/ogre-simple-mat.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-59895482741917803</id><published>2010-12-09T01:36:00.000-08:00</published><updated>2010-12-12T18:49:36.429-08:00</updated><title type='text'>OgreDotScene + BGE</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_Z42nrTY4F64/TQCjqMT_cDI/AAAAAAAAADA/ZCPkIsDhjmA/s1600/BGE-hijacked-actuators.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 399px; height: 400px;" src="http://2.bp.blogspot.com/_Z42nrTY4F64/TQCjqMT_cDI/AAAAAAAAADA/ZCPkIsDhjmA/s400/BGE-hijacked-actuators.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5548614686175359026" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Blender contains a fully functional game engine (BGE) that is highly useful for learning the concepts of game programming by breaking it down into three simple parts: Sensor, Controller, and Actuator.  An Ogre based game engine will likely have similar concepts in its internal API and game logic scripting.  Without a custom interface to define game logic, very often game designers may have to resort to having programmers implement their ideas in purely handwritten script.  This is prone to breakage because object names then end up being hard-coded.  Not only does this lead to non-reusable code, its also a slow process.  Why should we have to resort to this when Blender already contains a very rich interface for game logic?  By hijacking a subset of the BGE interface we can make this workflow between game designer and game programmer much better.&lt;br /&gt;&lt;br /&gt;The OgreDocScene format can easily be extened to include extra game logic data.  While the BGE contains some features that can not be easily mapped to other game engines, there are many are highly useful generic features we can exploit, including many of the Sensors and Actuators.  Blender uses the paradigm of: 1. Sensor -&gt; 2. Controller -&gt; 3. Actuator.  In pseudo-code, this can be thought of as: 1. on-event -&gt; 2. conditional logic -&gt; 3. do-action.  The designer is most often concerned with the on-events (the Sensors), and the do-actions (the Actuators); and the BGE interface provides a clear way for defining and editing those.  Its a harder task to provide a good interface for the conditional logic (Controller), that is flexible enough to fit everyones different Ogre engine and requirements, so that is outside the scope of this exporter at this time.  A programmer will still be required to fill the gap between Sensor and Actuator, but hopefully his work is greatly reduced and can write more generic/reuseable code.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Z42nrTY4F64/TQCjp4O3BgI/AAAAAAAAAC4/0uPRJgP8d8c/s1600/BGE-hijacked-sensors.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 390px; height: 400px;" src="http://1.bp.blogspot.com/_Z42nrTY4F64/TQCjp4O3BgI/AAAAAAAAAC4/0uPRJgP8d8c/s400/BGE-hijacked-sensors.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5548614680785126914" /&gt;&lt;/a&gt;&lt;br /&gt;The rules for which Sensors trigger which Actuators is left undefined, as explained above we are hijacking the BGE interface not trying to export and reimplement everything.  BGE Controllers and all links are ignored by the exporter, so whats the best way to define Sensor/Actuator relationships?  One convention that seems logical is to group Sensors and Actuators by name.  More complex syntax could be used in Sensor/Actuators names, or they could be completely ignored and instead all the mapping is done by the game programmer using other rules.  This issue is not easily solved so designers and the engine programmers will have to decide upon their own conventions, there is no one size fits all solution.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pastebin.com/MnycmYXj"&gt;example xml&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-59895482741917803?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/59895482741917803/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/12/ogredotscene-bge.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/59895482741917803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/59895482741917803'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/12/ogredotscene-bge.html' title='OgreDotScene + BGE'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_Z42nrTY4F64/TQCjqMT_cDI/AAAAAAAAADA/ZCPkIsDhjmA/s72-c/BGE-hijacked-actuators.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8514222743631983534</id><published>2010-11-24T00:42:00.000-08:00</published><updated>2010-12-02T18:23:44.931-08:00</updated><title type='text'>Ogre Shaders and Blender Library Linking</title><content type='html'>Ogre offers some very advanced and complex ways to implement shaders.  This must be carefully considered by an experienced programmer who is dealing with the high-level shading code.  One particular issue is shadows and deforming mesh requires a shader that can easily be broken by an artist who changes the wrong options, or adds options that invalidate the shader.  These issues can mostly be solved by using Blender's library linking, because linked data beomes read-only - it in effect provides a layer of control by the shader-programmer over the artist.  Done in the right way, these restrictions will not hinder the artist either from experimenting and extending the shader's usefullness.  In addition, library linking allows automatic-push of updates downstream to artists; so whenever the shader-programmer updates their shader code and updates his own master .blend with new shader options, the change is propagated to all .blends linking to it.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/blender2ogre/wiki/OgreShadersBlenderLibraryLinking"&gt;more info on the wiki&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ogre3d.org/forums/viewtopic.php?f=4&amp;t=61485"&gt;ogre forum topic&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/xYJOzWg1sJA?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/xYJOzWg1sJA?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8514222743631983534?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8514222743631983534/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/11/ogre-shaders-and-blender-library.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8514222743631983534'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8514222743631983534'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/11/ogre-shaders-and-blender-library.html' title='Ogre Shaders and Blender Library Linking'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-5318199381071868915</id><published>2010-11-20T01:29:00.000-08:00</published><updated>2010-11-20T01:53:11.626-08:00</updated><title type='text'>Ogre Shader Nodes</title><content type='html'>Continuing my work with Evocativi on RealXtend and the Ogre exporter, at last I have reached the final milestone - supporting all of Ogre-Dot-Material options from within Blender.  This was the hardest of all milestones because of the extensive number of features that Ogre-Dot-Material contains.  Another tricky part was digging into the shader nodes RNA in Blender, which is still developing.  You can get the latest source on google code, &lt;a href="http://code.google.com/p/blender2ogre/"&gt;here.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Sn2Zhh72emk?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/Sn2Zhh72emk?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-5318199381071868915?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/5318199381071868915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/11/ogre-shader-nodes.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5318199381071868915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5318199381071868915'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/11/ogre-shader-nodes.html' title='Ogre Shader Nodes'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-104808236724699269</id><published>2010-11-14T22:51:00.000-08:00</published><updated>2011-08-08T08:34:30.984-07:00</updated><title type='text'>Blender Python Operator API</title><content type='html'>I recently started working for Evocativi to help migrate the existing python tools to blender25, and develop new tools for game development and integration of blender with RealXtend.  RealXtend is a scaleable Ogre-rendering based game engine for making online virtual worlds.  My work begins with porting the Ogre exporter by Michael Reimpel from blender24 to blender25.  This has turned out to be harder than I first expected, partly because of the new operator API in blender25 and its unique design.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Getting Started&lt;/h3&gt;&lt;br /&gt;The first thing to be confused by is that the __init__ function is not used when defining a custom operator.  Instead you will define class-level attributes using the types in &lt;a href="http://www.blender.org/documentation/250PythonDoc/bpy.props.html"&gt;bpy.props&lt;/a&gt;: BoolProperty, StringProperty, FloatProperty, IntProperty, etc.  The second thing to be confused by is that the value returned by bpy.props.*Property is always a tuple with the first item a pointer to the property function just called, and the second item a dictionary that contains the arguments you passed to the property function. Only when the operator is invoked or executed is the class-level attribute changed from a tuple into the expected type.  This appears not to be clearly documented in the Blender wiki on operators, &lt;a href="http://wiki.blender.org/index.php/Dev:2.5/Source/Architecture/Operators"&gt;found here.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;bpy.types&lt;/h3&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Z42nrTY4F64/TODYm8-PfFI/AAAAAAAAACw/FeEV03mkn1g/s1600/info_menu.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 140px;" src="http://1.bp.blogspot.com/_Z42nrTY4F64/TODYm8-PfFI/AAAAAAAAACw/FeEV03mkn1g/s400/info_menu.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5539665705379527762" /&gt;&lt;/a&gt;&lt;br /&gt;Another point of confusion is that each and every widget and operator that is defined (including addons) dynamically appear in the bpy.types listing.  Care must be taken to give your operator a unique class name, and a unique 'bl_idname'.  By not giving a unique name you can override blender's default behavior or interface.  For example lets override the INFO menu with some more useful buttons and features that a game designer user will likely always want to have quick access to.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;class INFO_HT_header(bpy.types.Header):&lt;br /&gt;    bl_space_type = 'INFO'&lt;br /&gt;    def draw(self, context):&lt;br /&gt;        layout = self.layout&lt;br /&gt;        wm = context.window_manager&lt;br /&gt;        window = context.window&lt;br /&gt;        scene = context.scene&lt;br /&gt;        rd = scene.render&lt;br /&gt;        layout.operator("wm.window_fullscreen_toggle", icon='FULLSCREEN_ENTER', text="")&lt;br /&gt;        #layout.operator("ogre_export", text="Ogre")&lt;br /&gt;        row = layout.row(align=True)&lt;br /&gt;        sub = row.row(align=True)&lt;br /&gt;        sub.menu("INFO_MT_instances")&lt;br /&gt;        sub.menu("INFO_MT_groups")&lt;br /&gt;        sub.menu("INFO_MT_actors")&lt;br /&gt;        sub.menu("INFO_MT_dynamics")&lt;br /&gt;&lt;br /&gt;        if True: #context.area.show_menus:&lt;br /&gt;            sub.menu("INFO_MT_file")&lt;br /&gt;            sub.menu("INFO_MT_add")&lt;br /&gt;            if rd.use_game_engine: sub.menu("INFO_MT_game")&lt;br /&gt;            else: sub.menu("INFO_MT_render")&lt;br /&gt;        layout.separator()&lt;br /&gt;        if window.screen.show_fullscreen:&lt;br /&gt;            layout.operator("screen.back_to_previous", icon='SCREEN_BACK', text="Back to Previous")&lt;br /&gt;            layout.separator()&lt;br /&gt;        else:&lt;br /&gt;            layout.template_ID(context.window, "screen", new="screen.new", unlink="screen.delete")&lt;br /&gt;&lt;br /&gt;        layout.separator()&lt;br /&gt;        layout.template_running_jobs()&lt;br /&gt;        layout.template_reports_banner()&lt;br /&gt;&lt;br /&gt;        layout.separator()&lt;br /&gt;        if rd.has_multiple_engines:&lt;br /&gt;            layout.prop(rd, "engine", text="")&lt;br /&gt;        layout.template_header()&lt;br /&gt;        if context.area.show_menus:&lt;br /&gt;            layout.template_ID(context.screen, "scene", new="scene.new", unlink="scene.delete")&lt;br /&gt;            layout.label(text=scene.statistics())&lt;br /&gt;            layout.menu( "INFO_MT_help" )&lt;br /&gt;        else:&lt;br /&gt;            screen = context.screen&lt;br /&gt;            row = layout.row(align=True)&lt;br /&gt;            row.operator("screen.frame_jump", text="", icon='REW').end = False&lt;br /&gt;            row.operator("screen.keyframe_jump", text="", icon='PREV_KEYFRAME').next = False&lt;br /&gt;            if not screen.is_animation_playing:&lt;br /&gt;                row.operator("screen.animation_play", text="", icon='PLAY_REVERSE').reverse = True&lt;br /&gt;                row.operator("screen.animation_play", text="", icon='PLAY')&lt;br /&gt;            else:&lt;br /&gt;                sub = row.row()&lt;br /&gt;                sub.scale_x = 2.0&lt;br /&gt;                sub.operator("screen.animation_play", text="", icon='PAUSE')&lt;br /&gt;            row.operator("screen.keyframe_jump", text="", icon='NEXT_KEYFRAME').next = True&lt;br /&gt;            row.operator("screen.frame_jump", text="", icon='FF').end = True&lt;br /&gt;&lt;br /&gt;            row = layout.row(align=True)&lt;br /&gt;            if not scene.use_preview_range:&lt;br /&gt;                row.prop(scene, "frame_start", text="Start")&lt;br /&gt;                row.prop(scene, "frame_end", text="End")&lt;br /&gt;            else:&lt;br /&gt;                row.prop(scene, "frame_preview_start", text="Start")&lt;br /&gt;                row.prop(scene, "frame_preview_end", text="End")&lt;br /&gt;&lt;br /&gt;            layout.prop(scene, "frame_current", text="")&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def gather_instances():&lt;br /&gt; instances = {}&lt;br /&gt; for ob in bpy.data.objects:&lt;br /&gt;  if ob.data and ob.data.users &gt; 1:&lt;br /&gt;   if ob.data not in instances: instances[ ob.data ] = []&lt;br /&gt;   instances[ ob.data ].append( ob )&lt;br /&gt; return instances&lt;br /&gt;&lt;br /&gt;def select_instances( context, name ):&lt;br /&gt; for ob in bpy.data.objects: ob.select = False&lt;br /&gt; ob = bpy.data.objects[ name ]&lt;br /&gt; if ob.data:&lt;br /&gt;  inst = gather_instances()&lt;br /&gt;  for ob in inst[ ob.data ]: ob.select = True&lt;br /&gt;  bpy.context.scene.objects.active = ob&lt;br /&gt;&lt;br /&gt;def select_group( context, name, options={} ):&lt;br /&gt; for ob in bpy.data.objects: ob.select = False&lt;br /&gt; for grp in bpy.data.groups:&lt;br /&gt;  if grp.name == name:&lt;br /&gt;   bpy.context.scene.objects.active = grp.objects[0]&lt;br /&gt;   for ob in grp.objects: ob.select = True&lt;br /&gt;&lt;br /&gt;class INFO_MT_instances(bpy.types.Menu):&lt;br /&gt; bl_label = "Instances"&lt;br /&gt; def draw(self, context):&lt;br /&gt;  layout = self.layout&lt;br /&gt;  inst = gather_instances()&lt;br /&gt;  for data in inst:&lt;br /&gt;   ob = inst[data][0]&lt;br /&gt;   op = layout.operator("select_instances", text=ob.name) # operator has no variable for button name?&lt;br /&gt;   op.mystring = ob.name&lt;br /&gt;  layout.separator()&lt;br /&gt;&lt;br /&gt;class INFO_MT_instance(bpy.types.Operator):                &lt;br /&gt; '''select instance group'''                                                     &lt;br /&gt; bl_idname = "select_instances"                                             &lt;br /&gt; bl_label = "Select Instance Group"                        &lt;br /&gt; bl_options = {'REGISTER', 'UNDO'}     &lt;br /&gt; mystring= StringProperty(name="MyString", description="...", maxlen=1024, default="my string")&lt;br /&gt; @classmethod&lt;br /&gt; def poll(cls, context): return True&lt;br /&gt; def invoke(self, context, event):&lt;br /&gt;  print( 'invoke select_instances op', event )&lt;br /&gt;  select_instances( context, self.mystring )&lt;br /&gt;  return {'FINISHED'}&lt;br /&gt;&lt;br /&gt;class INFO_MT_groups(bpy.types.Menu):&lt;br /&gt; bl_label = "Groups"&lt;br /&gt; def draw(self, context):&lt;br /&gt;  layout = self.layout&lt;br /&gt;  for group in bpy.data.groups:&lt;br /&gt;   op = layout.operator("select_group", text=group.name) # operator no variable for button name?&lt;br /&gt;   op.mystring = group.name&lt;br /&gt;  layout.separator()&lt;br /&gt;&lt;br /&gt;class INFO_MT_group(bpy.types.Operator):                  &lt;br /&gt; '''select group'''       &lt;br /&gt; bl_idname = "select_group"                                      &lt;br /&gt; bl_label = "Select Group"        &lt;br /&gt; bl_options = {'REGISTER', 'UNDO'}   &lt;br /&gt; mystring= StringProperty(name="MyString", description="...", maxlen=1024, default="my string")&lt;br /&gt; @classmethod                                                      &lt;br /&gt; def poll(cls, context): return True&lt;br /&gt; def invoke(self, context, event):&lt;br /&gt;  select_group( context, self.mystring )&lt;br /&gt;  return {'FINISHED'}&lt;br /&gt;&lt;br /&gt;class INFO_MT_actors(bpy.types.Menu):&lt;br /&gt; bl_label = "Actors"&lt;br /&gt; def draw(self, context):&lt;br /&gt;  layout = self.layout&lt;br /&gt;  for ob in bpy.data.objects:&lt;br /&gt;   if ob.game.use_actor:&lt;br /&gt;    op = layout.operator("select_actor", text=ob.name)&lt;br /&gt;    op.mystring = ob.name&lt;br /&gt;  layout.separator()&lt;br /&gt;&lt;br /&gt;class INFO_MT_actor(bpy.types.Operator):                &lt;br /&gt; '''select actor'''                                                     &lt;br /&gt; bl_idname = "select_actor"                                             &lt;br /&gt; bl_label = "Select Actor"                                             &lt;br /&gt; bl_options = {'REGISTER', 'UNDO'}                              # Options for this panel type&lt;br /&gt; mystring= StringProperty(name="MyString", description="...", maxlen=1024, default="my string")&lt;br /&gt; @classmethod&lt;br /&gt; def poll(cls, context): return True&lt;br /&gt; def invoke(self, context, event):&lt;br /&gt;  bpy.data.objects[self.mystring].select = True&lt;br /&gt;  return {'FINISHED'}&lt;br /&gt;&lt;br /&gt;class INFO_MT_dynamics(bpy.types.Menu):&lt;br /&gt; bl_label = "Dynamics"&lt;br /&gt; def draw(self, context):&lt;br /&gt;  layout = self.layout&lt;br /&gt;  for ob in bpy.data.objects:&lt;br /&gt;   if ob.game.physics_type in 'DYNAMIC SOFT_BODY RIGID_BODY'.split():&lt;br /&gt;    op = layout.operator("select_dynamic", text=ob.name)&lt;br /&gt;    op.mystring = ob.name&lt;br /&gt;  layout.separator()&lt;br /&gt;&lt;br /&gt;class INFO_MT_dynamic(bpy.types.Operator):                &lt;br /&gt; '''select dynamic'''                                                     &lt;br /&gt; bl_idname = "select_dynamic"                                             &lt;br /&gt; bl_label = "Select Dynamic"                                             &lt;br /&gt; bl_options = {'REGISTER', 'UNDO'}                              # Options for this panel type&lt;br /&gt; mystring= StringProperty(name="MyString", description="...", maxlen=1024, default="my string")&lt;br /&gt; @classmethod&lt;br /&gt; def poll(cls, context): return True&lt;br /&gt; def invoke(self, context, event):&lt;br /&gt;  bpy.data.objects[self.mystring].select = True&lt;br /&gt;  return {'FINISHED'}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-104808236724699269?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/104808236724699269/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/11/blender-python-operator-api.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/104808236724699269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/104808236724699269'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/11/blender-python-operator-api.html' title='Blender Python Operator API'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Z42nrTY4F64/TODYm8-PfFI/AAAAAAAAACw/FeEV03mkn1g/s72-c/info_menu.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-4791080220204720794</id><published>2010-10-04T19:04:00.000-07:00</published><updated>2010-10-04T21:24:03.975-07:00</updated><title type='text'>Inside The Blender C API - Part2</title><content type='html'>A good high level overview of Blender's window and event management is on the official wiki, &lt;a href="http://wiki.blender.org/index.php/Dev:2.5/Source/Architecture/Window_Manager"&gt;here&lt;/a&gt;.  Digging deeper into the source code, the main function of creator.c the function call WM_keymap_init(C) setups basic mouse and keyboard handling.  Looking inside this function the first call is to CTX_wm_manager(C) which returns a pointer to the wmWindowManager struct contained by C (the context struct).  Inside WM_keymap_init, a new wmKeyConfig struct is created by WM_keyconfig_new, this is then passed to: wm_window_keymap, ED_spacetypes_keymap, and WM_keyconfig_userdef.  Finally the wmKeyConfig is assigned to the wmWindowManager struct as `defaultconf`.&lt;br /&gt;&lt;br /&gt;The recent recoding overhaul of Blender25 has removed much of the use of muteable globals to store changeable state, and has been replaced by a better system of a single context struct (C) that is passed around as a function argument.  It is well documented, &lt;a href="http://wiki.blender.org/index.php/Dev:2.5/Source/Architecture/Context"&gt;here&lt;/a&gt;. The context struct C, is of the type bContext and defined in:&lt;br /&gt; blender/source/blender/blenkernel/BKE_context.h&lt;br /&gt; blender/source/blender/blenkernel/intern/context.c&lt;br /&gt;&lt;br /&gt;C has a very simple top-level structure, it contains: a thread index, window manager struct, data context struct, and eval struct.  The window manager struct contains: a manager, window, screen, area, region, menu, and store.  The data context struct contains the scene and python context.&lt;br /&gt;&lt;h4&gt;blenkernel note:&lt;/h4&gt;&lt;br /&gt;Many functions defined in blender/source/blender/blendkernel are prefixed with BKE, but not all.  CTX_create and CTX_wm_manager are two examples of blendkernel functions that do not start with BKE.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;DNA_windowmanager_types.h&lt;/h3&gt;&lt;br /&gt;The wmWindowManager struct and wmKeyConfig are defined in blender/source/blender/makesdna/DNA_windowmanager_types.h.  Other important window manager related structs also define here are: wmWindow and wmOperator.  The wmWindow struct contains a pointer to a wmEvent struct named `eventstate`, and a list named `queue` that contains all events.  Two other event lists inside wmWindow are: `handlers` and `modalhandlers`.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;WM_types.h and wm_event_types.h&lt;/h3&gt;&lt;br /&gt;Both of these headers are in: blender/source/blender/windowmanager/.  wmEvent and other window event related things are defined here, the structs are in WM_types.h and the enums are in wm_event_types.h.  Below is the definition of wmEvent:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* each event should have full modifier state */&lt;br /&gt;/* event comes from eventmanager and from keymap */&lt;br /&gt;typedef struct wmEvent {&lt;br /&gt; struct wmEvent *next, *prev;&lt;br /&gt; short type;   /* event code itself (short, is also in keymap) */&lt;br /&gt; short val;   /* press, release, scrollvalue */&lt;br /&gt; short x, y;   /* mouse pointer position, screen coord */&lt;br /&gt; short mval[2];  /* region mouse position, name convention pre 2.5 :) */&lt;br /&gt; short unicode;  /* future, ghost? */&lt;br /&gt; char ascii;   /* from ghost */&lt;br /&gt; char pad;&lt;br /&gt; /* previous state */&lt;br /&gt; short prevtype;&lt;br /&gt; short prevval;&lt;br /&gt; short prevx, prevy;&lt;br /&gt; double prevclicktime;&lt;br /&gt; short prevclickx, prevclicky;&lt;br /&gt; /* modifier states */&lt;br /&gt; short shift, ctrl, alt, oskey; /* oskey is apple or windowskey, value denotes order of pressed */&lt;br /&gt; short keymodifier;    /* rawkey modifier */&lt;br /&gt; short pad1;&lt;br /&gt; /* keymap item, set by handler (weak?) */&lt;br /&gt; const char *keymap_idname;&lt;br /&gt; /* custom data */&lt;br /&gt; short custom;  /* custom data type, stylus, 6dof, see wm_event_types.h */&lt;br /&gt; short customdatafree;&lt;br /&gt; int pad2;&lt;br /&gt; void *customdata; /* ascii, unicode, mouse coords, angles, vectors, dragdrop info */&lt;br /&gt;} wmEvent;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-4791080220204720794?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/4791080220204720794/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/10/inside-blender-c-api-part2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4791080220204720794'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4791080220204720794'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/10/inside-blender-c-api-part2.html' title='Inside The Blender C API - Part2'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-7424351795781156802</id><published>2010-10-04T01:12:00.000-07:00</published><updated>2010-10-04T20:19:51.472-07:00</updated><title type='text'>Inside Blender</title><content type='html'>After so many years of Blender being open source, it is rather shocking to see the Blender C API still remains mostly undocumented.  &lt;a href="http://wiki.blender.org/index.php/Dev:2.5/Source/Architecture"&gt;The official wiki&lt;/a&gt; has some documents which provide a good high level overview, but very few get into the details of the source code. Blender is a large project, certainly difficult to document, it consists of: 839 C files (.c), 1398 C header files (.h), 618 C++ files (.cpp), and 38 C++ header files (.hpp).  Total lines of code is 1,253,126.  Blender includes source from other external projects (like bulletphysics), so after substracting them the total lines of code is 1,097,349.  &lt;br /&gt;&lt;br /&gt;Looking at the recent svn commit activity, Blender is being developed by a relatively small number of developers for such a high profile project.  Only nine developers have recent checkins of twenty or more files.  With most of the load carried by Campbell Barton.&lt;br /&gt;&lt;br /&gt;SVN Check-Ins:   (snapshot of source files, not total commits this year)&lt;br /&gt; campbellbarton 1081&lt;br /&gt; nexyon 156&lt;br /&gt; blendix 75&lt;br /&gt; gsrb3d 47&lt;br /&gt; jesterking 31&lt;br /&gt; broken 24&lt;br /&gt; nazgul 20&lt;br /&gt; dfelinto 20&lt;br /&gt; aligorith 20&lt;br /&gt;&lt;br /&gt;What is shying away new developers?  Documentation must be a major factor, it is very hard to jump into a code base of one million lines of code with only high level documentation.  Another factor stems from Blender's odd opposition to using a standardized GUI toolkit, and instead opting for its own internal GUI system written in OpenGL with an event system that interfaces directly with the OS.  Developers familiar with common place GUI toolkits like GTK or QT will find it hard to adapt to Blender's GUI system.&lt;br /&gt;&lt;br /&gt;Recently with the major advancement of Blender2.5, the GUI system was given a major overhaul, and now much of it exists in Python.  However, this Python API has its limitations (not exposing the Tree/List-View for example), and not compatible with anything else like PyGTK or PyQT.  Blender has taken the approach of `embed not extend`, which is counter to the Python mantra of `extend not embed`; and this has led to a Blender Python API that is very hard to integrate with the rest of the greater Python eco-system.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Inside The Blender C API: - Part 1&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;Diving into the source code you will find that the `main` function is in: blender/source/creator/creator.c&lt;br /&gt;This mostly sets things up and calls the mainloop, which is actually WM_main found in: blender/source/blender/windowmanager/intern/wm.c&lt;br /&gt;Blender uses the directory convention: `intern` and `extern`, which apparently defines which part of a library should be hidden or exposed to other libraries.  Another convention (not always followed) is functions that begin with capital letters are exposed to other libraries.&lt;br /&gt;WM_main only calls four functions in its while loop:&lt;br /&gt;  /* get events from ghost, handle window events, add to window queues */&lt;br /&gt;  wm_window_process_events(C); &lt;br /&gt;  /* per window, all events to the window, screen, area and region handlers */&lt;br /&gt;  wm_event_do_handlers(C);&lt;br /&gt;  /* events have left notes about changes, we handle and cache it */&lt;br /&gt;  wm_event_do_notifiers(C);  &lt;br /&gt;  /* execute cached changes draw */&lt;br /&gt;  wm_draw_update(C);&lt;br /&gt;&lt;br /&gt;These functions are defined in:&lt;br /&gt; blender/source/blender/windowmanager/WM_types.h &lt;br /&gt; blender/source/blender/windowmanager/wm_window.h &lt;br /&gt; blender/source/blender/windowmanager/wm_event_system.h &lt;br /&gt; blender/source/blender/windowmanager/wm_draw.h&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Libblender - Getting Started:&lt;/h3&gt;&lt;br /&gt;Before trying the ctypes wrapper for libblender, you will need to compile blender as a shared library - on Windows this is a 'dll' file, and on Linux this is a 'so' file.  First modify 'blender/CMakeLists.txt' and turn off both the game engine and python.  Then edit 'blender/source/creator/CMakeLists.txt' and change:&lt;br /&gt;ADD_EXECUTABLE(blender ${EXETYPE} ${EXESRC})&lt;br /&gt;to:&lt;br /&gt;ADD_LIBRARY(blender SHARED ${EXETYPE} ${EXESRC})&lt;br /&gt;Finally, run cmake.  If you have never used cmake to build blender, you need to create a new build directory outside of the blender source tree.  On linux you would then run:&lt;br /&gt;cmake -G "Unix Makefiles" ../blender&lt;br /&gt;make&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://pastebin.com/fhcnpmN3"&gt;lastest&lt;/a&gt; ctypes wrapper for libblender exposes enough of the C API to control the main loop.  Converting the `main` and `WM_main` into ctypes looks like following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import os, sys, time&lt;br /&gt;from ctypes_libblender import *&lt;br /&gt;from ctypes import *&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt; _argv = ''&lt;br /&gt; for arg in sys.argv: _argv += arg + ' '&lt;br /&gt; argc = len(sys.argv)&lt;br /&gt; argv = ctypes.pointer(ctypes.c_char_p(_argv))&lt;br /&gt; C = CTX_create()  # bContext pointer&lt;br /&gt; BLI_threadapi_init()&lt;br /&gt; RNA_init()&lt;br /&gt; RE_engines_init()&lt;br /&gt; pluginapi_force_ref();&lt;br /&gt; init_nodesystem();&lt;br /&gt; initglobals()&lt;br /&gt; IMB_init()&lt;br /&gt; syshandle = SYS_GetSystem()&lt;br /&gt; GEN_init_messaging_system()&lt;br /&gt; ba = BLI_argsInit(argc, argv)&lt;br /&gt; setupArguments(C, ba, syshandle);&lt;br /&gt; BLI_argsParse(ba, 1, None, None);  # required, segfaults without this&lt;br /&gt; sound_init_once(); &lt;br /&gt; init_def_material();&lt;br /&gt; print ('WM_init...'); time.sleep(1)&lt;br /&gt; WM_init(C, argc, argv);       # some less random crash happening here&lt;br /&gt; BLI_where_is_temp( c_char_p('/tmp'), 1 )&lt;br /&gt; CTX_py_init_set(C, 1);&lt;br /&gt; WM_keymap_init(C);  # (this segfaults if BLI_where_is_temp is not called above)&lt;br /&gt; time.sleep(1) # can segfault if we don't wait for the window to appear?&lt;br /&gt; while True:&lt;br /&gt;  wm_window_process_events(C); &lt;br /&gt;  wm_event_do_handlers(C);&lt;br /&gt;  wm_event_do_notifiers(C);&lt;br /&gt;  wm_draw_update(C);&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-7424351795781156802?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/7424351795781156802/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/10/inside-blender.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7424351795781156802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7424351795781156802'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/10/inside-blender.html' title='Inside Blender'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-7918900170522785020</id><published>2010-10-03T00:48:00.000-07:00</published><updated>2010-10-03T00:57:32.567-07:00</updated><title type='text'>ctypes libblender</title><content type='html'>Get the first working version of the &lt;a href="http://rpythonic.googlecode.com/files/ctypes_libblender.py"&gt;ctypes version of libblender.py&lt;/a&gt; precompiled for 64bit ubuntu, &lt;a href="http://rpythonic.googlecode.com/files/rpythonic-0.1.5.tar.gz"&gt;here.&lt;/a&gt;  A large chunk of the blender C API is exposed, over 7,000 lines of generated ctypes code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-7918900170522785020?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/7918900170522785020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/10/ctypes-libblender.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7918900170522785020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7918900170522785020'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/10/ctypes-libblender.html' title='ctypes libblender'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-3035076122381759263</id><published>2010-09-30T02:57:00.000-07:00</published><updated>2010-09-30T03:00:45.410-07:00</updated><title type='text'>Testing RPythonic on Blender25</title><content type='html'>The main entry point into the Blender C API is `creator.c`.  This file after CPP has processed it and included all the imports, is about 5,000 lines long.  Running RPythonic on it generates: about 1,000 lines of ctypes wrapper code, and the same for rffi.  Pointers are not working well yet, so the generated code is not functional.  The approach is working well so far, and it should be possible to wrap the entire Blender C API this way.  How much can be translated to RPython remains to be seen.  Work in progress:&lt;br /&gt; &lt;a href="http://pastebin.com/tDZxvt54"&gt;generated rffi&lt;/a&gt;&lt;br /&gt; &lt;a href="http://pastebin.com/7V69giaM"&gt;generated ctypes&lt;/a&gt;&lt;br /&gt; &lt;a href="http://pastebin.com/EZLht07S"&gt;generated rpython&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-3035076122381759263?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/3035076122381759263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/09/testing-rpythonic-on-blender25.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3035076122381759263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3035076122381759263'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/09/testing-rpythonic-on-blender25.html' title='Testing RPythonic on Blender25'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-724770779427735094</id><published>2010-09-28T18:43:00.000-07:00</published><updated>2010-09-30T03:14:57.651-07:00</updated><title type='text'>RPythonic</title><content type='html'>RPythonic takes in C code and outputs three different formats: 1. pure RPython code, 2. ctypes wrappers, and 3. rffi wrappers.  Using the PyPy translation toolchain the pure RPython code can be translated back to C, or C# and Java (allowing your C code to be executed by .Net or Java runtimes).  The generated ctypes wrapper can be used from CPython.  The rffi wrappers allow you to use C code from RPython.  RPythonic is built using PycParser, a powerful C parser written by Eli Bendersky written in Python.&lt;br /&gt;&lt;br /&gt;Why not Pygccxml?&lt;br /&gt;Pygccxml is made up of about 50 files and several thousands of lines of code, totalling about 1MB of code; and relies on the external program Gccxml.  Gccxml is old and development has stalled.  Another automatic binding generator Pybindgen uses Pygccxml, and the interface between Pybindgen and Pygccxml alone is over two thousands lines of code, not pretty!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/pycparser/"&gt;PycParser&lt;/a&gt; is super clean, 10 files, less than 150KB of code; is pure Python and relies on no external programs.  While Pygccxml can parse C/C++ code, it can not parse the function bodies.   On the other hand, PycParser can fully parse C and function bodies, but not C++.  For RPython and ctypes wrappers we are only interested in C; and because PycParser can fully parse C, translation of C to RPython is also possible at the same time the bindings are generated.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/rpythonic/"&gt;RPythonic on google code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-724770779427735094?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/724770779427735094/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/09/rpythonic.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/724770779427735094'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/724770779427735094'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/09/rpythonic.html' title='RPythonic'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-3623156685955690451</id><published>2010-09-18T02:36:00.000-07:00</published><updated>2010-09-18T02:37:43.978-07:00</updated><title type='text'>OpenCV connected to Blender25</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/r3DwdfCGnkI?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/r3DwdfCGnkI?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://pastebin.com/9fc0ArfA"&gt;source code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-3623156685955690451?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/3623156685955690451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/09/opencv-connected-to-blender25.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3623156685955690451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/3623156685955690451'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/09/opencv-connected-to-blender25.html' title='OpenCV connected to Blender25'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-5599269241512858370</id><published>2010-09-16T18:12:00.000-07:00</published><updated>2010-09-16T18:17:55.251-07:00</updated><title type='text'>Multiprocessing</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_Z42nrTY4F64/TJLBs_x4A7I/AAAAAAAAACk/E1fTTTUEvfM/s1600/multiprocessing-opencv-pygtk.png"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 400px; height: 224px;" src="http://4.bp.blogspot.com/_Z42nrTY4F64/TJLBs_x4A7I/AAAAAAAAACk/E1fTTTUEvfM/s400/multiprocessing-opencv-pygtk.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5517685472261309362" /&gt;&lt;/a&gt;&lt;br /&gt;OpenCV is multithreaded, and you can set the number of threads it uses by cvSetNumThreads(N).  The haar cascades used for face and other feature detection will fully utilize all the threads you give it.  Using glib.timeout_add( miliseconds, self.loop ) even with 4 threads you will still get unacceptable lag doing haar detection on a 320x240 image; the GUI will become unresponsive because glib will not update its own events if the timeout is always fully loaded.  &lt;br /&gt;&lt;br /&gt;The solution is spliting the process in two so that pygtk can have its own core and update without being blocked by the OpenCV/Pygame process.  One approach for this is to use the subprocess module, and serialize data from the GUI to the OpenCV/Pygame process over a pipe - but this is rather slow.  The fastest approach available in Python is the multiprocessing module that uses ctypes wrapped sharedmemory.  If sharedmemory is only read from the OpenCV/Pygame process then there will be no data copy overhead, giving us the maximum possible speed. (copy-on-write is implemented in hardware and supported by modern Linux kernels.)&lt;br /&gt;&lt;br /&gt;Ctypes restricts what types can be shared, so its not possible to share a dictionary or instance.  Luckly ctypes has a simple interface for using C-level structs, and all the data from the GUI can easily be reformatted from using a dictionary to using structs in an array.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;_cfg_ubytes =  'active alpha blur athresh_block_size thresh_min thresh_max'.split()&lt;br /&gt;_cfg_ubytes += 'FXstencil FXblur FXsobel FXathresh FXthresh FXdetect'.split()&lt;br /&gt;class LayerConfig( ctypes.Structure ):&lt;br /&gt; _fields_ = [ ('colorspace',ctypes.c_int) ]&lt;br /&gt; for tag in _cfg_ubytes: _fields_.append( (tag, ctypes.c_ubyte) )&lt;br /&gt; del tag&lt;br /&gt;mysharedmemory = multiprocessing.sharedctypes.Array( LayerConfig, [ (cv.CV_BGR2RGB,) ], lock=False )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://pastebin.com/hHDPYU1h"&gt;source code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-5599269241512858370?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/5599269241512858370/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/09/multiprocessing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5599269241512858370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5599269241512858370'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/09/multiprocessing.html' title='Multiprocessing'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_Z42nrTY4F64/TJLBs_x4A7I/AAAAAAAAACk/E1fTTTUEvfM/s72-c/multiprocessing-opencv-pygtk.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-514564222980141831</id><published>2010-08-28T23:19:00.000-07:00</published><updated>2010-08-28T23:21:50.526-07:00</updated><title type='text'>RPython FFTW</title><content type='html'>Numpy fast fourier transforms use FFTPack (written in Fortran) with optimized C glue code to Python.  FFTPack standalone has been benched marked against FFTW, and FFTW wins by far.  FFTW is even faster than GPU accelerated CUDA FFT for realtime sample sizes up to 16K.  PyFFTW by Schroeder and Peterson is a ctypes binding to FFTW; in the case of small sample sizes typical for realtime analysis (1024), PyFFTW does not offer any speed up over Numpy's native fft, infact is appears to be 3x slower.  Note that if dealing with much larger sample sizes PyFFTW would then beat Numpy FFTPack, especially since sample sizes over one million can then benifit from FFTW threading.&lt;br /&gt;&lt;br /&gt;This RPython binding to FFTW is faster than both, about 8x faster than Numpy, and 24x faster than ctypes PyFFTW.  Get the code &lt;a href="http://pastebin.com/PzhTgt3h"&gt;here&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-514564222980141831?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/514564222980141831/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-fftw.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/514564222980141831'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/514564222980141831'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-fftw.html' title='RPython FFTW'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-7820294037781265928</id><published>2010-08-22T19:28:00.000-07:00</published><updated>2010-08-22T19:33:14.320-07:00</updated><title type='text'>iTaSC Pole Vector Free Rigging</title><content type='html'>Blender now has a very powerful set of IK solvers: Spline IK, and simulation based IK (iTaSC).  When iTaSC is used in `Simulation` mode it becomes very difficult to make an IK chain flip.  This could greatly simplify the rigging and animation process if we can remove the need for a pole-vector object to control knee/elbow tilt.  One of the problems in using a pole-vector is that it imposes a non-ideal flipping point, an arm or leg will have a large range of possible motion, flipping then becomes unavoidable past 180 degrees.  Animators may have to resort to scripting to control the pole-vector or bone roll directly, but these hacks complicate animation blending in NLA or are cumbersome to use and setup.&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/FqTAN19lMR0&amp;amp;hl=en&amp;amp;fs=1"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/FqTAN19lMR0&amp;amp;hl=en&amp;amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Using iTaSC no hacks are required to reach almost 360 degrees of rotation.  Simply by using two IK constraints, one for position and the other for rotation, is all that is needed for full elbow or knee tilt while keeping the optimal flipping point at 360 degrees.  The interface may not lead us directly to this simple solution, the old pole-vector option remains available even though its not very useful combined with iTaSC.  The correct steps are as follows:&lt;br /&gt;1. From the Armature panel -&gt; iTaSC options -&gt; select `Simulation` mode (keep all the default options)&lt;br /&gt;2. Go into pose-mode, create a position IK for the ankle, do not assign a pole-vector target&lt;br /&gt;3. Pick the thigh bone, create another IK constraint, pick the ankle IK handle as the target&lt;br /&gt;4. Set the constraint to use rotation, and only in the Y, and turn off position.&lt;br /&gt;5. If your model faces -Y (the default in Blender) your leg will have flipped, rotate the IK handle 180 on the Y.&lt;br /&gt;6. Test the leg by moving the handle, you should see it flip when moving forward and back,&lt;br /&gt; this is because the X rotation of the handle will define where the monopole is located that iTaSC will flip around,&lt;br /&gt; by relocating the monopole to 180 degrees from the leg's natural rest position,&lt;br /&gt; we gain almost 360 degrees of flip-free movement.&lt;br /&gt;7. Adjust the rotation of the handle in X to minimize the flipping,&lt;br /&gt; some where between 45 to 90 degrees should give the best results.&lt;br /&gt;8. After you have found the optimal rotation in X of the handle, go back to the constraint options and set the weight to 0.5,&lt;br /&gt; this helps keep the response of the IK chain smooth when nearing 360 degrees.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Torso and Shoulder Setup:&lt;/span&gt;&lt;br /&gt;Shoulder rigging and weighting is another area where many techniques have been tried, but most of these hacks are hard to setup, so it becomes a serious bottleneck for creating digital characters.  The interaction of all the ribs, muscles, clavicle and shoulder-blades is far too complex to be modeled by a simple single chain spine with arms branching directly from it.  The following approach instead uses IK to model the shoulder blades and stretch-to constraints to model the shoulders and chest.  This setup can only work with bone-to-bone constraints because of the way Blender computes its DAG node graph, in other words you will get cyclic dependency errors trying to use bone-to-empty constraints.&lt;br /&gt;&lt;br /&gt;Notes:&lt;br /&gt;. Even if bone tips are perfectly snapped to the heads of their stretch-to targets, they will still move slightly when the constraint is applied.&lt;br /&gt;. IK bone stretch scales opposite of stretch-to constraint.  IK stretch gets larger as distance grows, while stretch-to maintains volume and thins.&lt;br /&gt;. Stretch-to constraint when using a bone target allows for slider control between head and tail.  IK lacks this option.&lt;br /&gt;. iTaSC treats bone stiffness slightly different from legacy IK, stiffness of 0.99 is considered absolute and locked.&lt;br /&gt;. iTaSC bone stretch is disabled if two IK constraints have overlapping bone influence.&lt;br /&gt;. iTaSC not as compatible with other constraints as the legacy IK was. (locked-track was compatible with legacy IK)&lt;br /&gt;. iTaSC has its own set of internal IK types: copy-pose and limit distance, with more comming in the future!&lt;br /&gt;. Too many bones in an IK chain with stiffness of 0.99 can lead to jerking and unstable response.&lt;br /&gt;. Bone shake due to overlapping IK is usally solved by lowering the constraint weights, good ranges are from 0.5 - 1.0&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-7820294037781265928?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/7820294037781265928/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/itasc-pole-vector-free-rigging.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7820294037781265928'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/7820294037781265928'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/itasc-pole-vector-free-rigging.html' title='iTaSC Pole Vector Free Rigging'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-6694202008694280676</id><published>2010-08-15T23:00:00.000-07:00</published><updated>2010-08-15T23:05:32.189-07:00</updated><title type='text'>RPython callbacks from C</title><content type='html'>It is possible to define an RPython function that is passed to a C function as a callback argument.  The RPython function will then be invoked from C whenever an event causes it to trigger.  Pyaudio never implemented the PortAudio async API, because it requires callback functionality and that may take a great amount of effort from the Python C API.  From RPython is rather easy once we know the steps:&lt;br /&gt;  1. import llhelper from pypy.rpython.annlowlevel&lt;br /&gt;  2. define the function signature (argument types, return type) using lltype.FuncType&lt;br /&gt;  3. create a pointer to the signature lltype.Ptr( mysignature )&lt;br /&gt;  4. pass the pointer as an argument to  rffi.llexternal when defining the wrapper&lt;br /&gt;  5. within the entrypoint use llhelper:&lt;br /&gt;      mycallback = llhelper(lltype.Ptr( mysignature ), my_rpython_function )&lt;br /&gt;  6. pass mycallback as an argument to the external C function&lt;br /&gt;&lt;br /&gt;This excerpt is from RpyPortAudio 0.3:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mult16bits = 2**15&lt;br /&gt;def stream_callback( ibuf, obuf, frameCount, info, flags, user ):&lt;br /&gt; t = time.time()&lt;br /&gt; for i in range(512):&lt;br /&gt;  raw = ibuf[i]&lt;br /&gt;  x=rffi.cast(lltype.Signed, raw)&lt;br /&gt;  samp = math.sin( t + (float(i)/512.0) ) * mult16bits&lt;br /&gt;  samp = int(samp) + x&lt;br /&gt;  obuf[i] = rffi.cast(rffi.INT, samp)&lt;br /&gt; return rffi.cast(rffi.INT, 0)  # 0=continue, 1=complete, 2=abort&lt;br /&gt;&lt;br /&gt;StreamCallbackTimeInfoPtr.TO.become( StreamCallbackTimeInfo )&lt;br /&gt;stream_cb_signature = lltype.FuncType([RBufferPtr, RBufferPtr, rffi.INT, StreamCallbackTimeInfoPtr, rffi.INT, rffi.VOIDP], rffi.INT)&lt;br /&gt;stream_callback_ptr = lltype.Ptr( stream_cb_signature )&lt;br /&gt;&lt;br /&gt;OpenDefaultStream = rffi.llexternal( 'Pa_OpenDefaultStream', &lt;br /&gt; [&lt;br /&gt;  StreamRefPtr,      # PaStream** &lt;br /&gt;  rffi.INT,          # numInputChannels&lt;br /&gt;  rffi.INT,          # numOutputChannels&lt;br /&gt;  rffi.INT,          # sampleFormat&lt;br /&gt;  rffi.DOUBLE,        # double sampleRate&lt;br /&gt;  rffi.INT,          # unsigned long framesPerBuffer&lt;br /&gt;  stream_callback_ptr,     #PaStreamCallback *streamCallback&lt;br /&gt;  rffi.VOIDP,        #void *userData&lt;br /&gt; ], &lt;br /&gt; rffi.INT,          # return&lt;br /&gt; compilation_info=eci,&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;def entrypoint():&lt;br /&gt; streamptr = lltype.malloc(StreamRefPtr.TO, 1,  flavor='raw')  # must have length 1&lt;br /&gt; userdata = lltype.nullptr(rffi.VOIDP.TO)&lt;br /&gt; callback = llhelper(lltype.Ptr( stream_cb_signature ), stream_callback)&lt;br /&gt; ok = OpenDefaultStream( streamptr, 2, 2, Int16, 22050.0, 512, callback, userdata )&lt;br /&gt; stream = streamptr[0]&lt;br /&gt; startok = StartStream( stream )&lt;br /&gt; time.sleep(10.0)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-6694202008694280676?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/6694202008694280676/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-callbacks-from-c.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6694202008694280676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6694202008694280676'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-callbacks-from-c.html' title='RPython callbacks from C'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8587409646967631558</id><published>2010-08-15T17:58:00.000-07:00</published><updated>2010-08-15T18:10:13.692-07:00</updated><title type='text'>RPython FluidSynth Bindings</title><content type='html'>PyFluidSynth by Nathan Whitehead and Bart Spaans is a ctypes binding to &lt;a href="http://sourceforge.net/apps/trac/fluidsynth/"&gt;FluidSynth&lt;/a&gt;, a software synthesizer that can read SoundFont2 files and generate music in real-time.  In terms of lines of code, this RPython binding using rffi is not much larger than the ctypes wrapper, less than 100 extra lines.  However, in terms of performance the RPython binding is about 10x faster than the ctypes binding.  FluidSynth v1.1.1 is required, the version that is in the Ubuntu Karmic repos is out of date and contains a bug, so you will need to download and compile the latest FluidSynth. &lt;a href="http://code.google.com/p/rpyfluidsynth/"&gt;RPyFluidSynth on google code.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;One issue that came up creating this wrapper, if a struct is only declared in the header and not defined (or perhaps empty), then rffi_platform.Struct is not used.  Instead the pointer is told to become a lltype.Struct directly.&lt;br /&gt; _ptr = lltype.Ptr(lltype.ForwardReference())&lt;br /&gt; _ptr.TO.become( lltype.Struct(name) )&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8587409646967631558?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8587409646967631558/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-fluidsynth-bindings.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8587409646967631558'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8587409646967631558'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-fluidsynth-bindings.html' title='RPython FluidSynth Bindings'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8258063373964302038</id><published>2010-08-13T05:51:00.000-07:00</published><updated>2010-08-13T05:53:44.206-07:00</updated><title type='text'>RPython PortAudio Bindings</title><content type='html'>The &lt;a href="http://people.csail.mit.edu/hubert/pyaudio/"&gt;PyAudio&lt;/a&gt; package for Python2.x written by Hubert Pham, wraps PortAudio directly using the Python C API, this method is extremely verbose but gives much better performance than a ctypes based wrapper, and for audio performance is a big factor.  Hubert's wrapper is no less than 2,500 lines of hand written C, and about 500 lines of Python code that provides a nice pythonic interface ontop of the C API.  How does RPython compare?  Nearly the entire PortAudio library can be wrapped in RPython in under 200 lines of code, the only thing missing here are: error codes, a few device options, and a nice pythonic API.  As far as speed, my guess it is as good or better.  Using rffi and lltypes is very low level so it might take more lines of code to encapsulate the binding into a higher level pythonic API, but this extra work is more than offset by not having to write any C.  You can get rpyportaudio on google code &lt;a href="http://code.google.com/p/rpyportaudio/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8258063373964302038?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8258063373964302038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-portaudio-bindings.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8258063373964302038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8258063373964302038'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-portaudio-bindings.html' title='RPython PortAudio Bindings'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-4057246911357253489</id><published>2010-08-11T20:04:00.000-07:00</published><updated>2010-08-11T20:15:49.429-07:00</updated><title type='text'>Interfacing RPython with C</title><content type='html'>The documentation on how to interface RPython with C is rather limited, the &lt;a href="http://codespeak.net/pypy/dist/pypy/doc/extending.html"&gt;extending doc&lt;/a&gt; mentions that `MixedModules` using rffi is the most advanced method available.  The &lt;a href="http://codespeak.net/pypy/dist/pypy/doc/rffi.html"&gt;rffi document&lt;/a&gt; only provides a quick glance at how to use rffi.llexternal.  The best place to get started is looking at the source code in rlib/rsdl/RSDL.py which binds RPython to SDL.  What should be clear after reading RSDL.py is that PyPy provides a very direct and easy to understand interface for C; that should also provide the highest possible performance. &lt;br /&gt;&lt;br /&gt;The source code in rffi_platform.py (found in pypy/rpython/tool) is very interesting, it generates dynamic C code that it compiles to get information about the C code we are trying to wrap, for example if you had used the wrong name in a struct it will generate an error.  Using rffi_platform different C types can be defined, such as structs, constant integers; these are then put into a special container class called CConfig, which is then parsed by rffi_platform.configure(CConfig) and wrappers returned.  After we have the wrappers we must tell any pointers we had previously used in the CConfig that they `become` those objects, MyPointer.TO.become(MyStruct).&lt;br /&gt;&lt;br /&gt;RSDL that is included in PyPy is incomplete and lacks wrappers for Joystick, the code below wraps SDL Joystick.  Full source code is available &lt;a href="http://pastebin.com/LRPfGZHr"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;eci = get_rsdl_compilation_info()&lt;br /&gt;## wrapper for rffi.llexternal just to shorten the call&lt;br /&gt;def external(name, args, result): return rffi.llexternal(name, args, result, compilation_info=eci)&lt;br /&gt;&lt;br /&gt;JoystickPtr = lltype.Ptr(lltype.ForwardReference())&lt;br /&gt;JoyAxisEventPtr = lltype.Ptr(lltype.ForwardReference())&lt;br /&gt;JoyBallEventPtr = lltype.Ptr(lltype.ForwardReference())&lt;br /&gt;JoyButtonEventPtr = lltype.Ptr(lltype.ForwardReference())&lt;br /&gt;JoyHatEventPtr = lltype.Ptr(lltype.ForwardReference())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class CConfig:&lt;br /&gt; _compilation_info_ = eci&lt;br /&gt; Joystick = platform.Struct('SDL_JoyAxisEvent', [])  # just and ID, struct contains nothing&lt;br /&gt; # rsdl/constants.py already defines SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, etc..&lt;br /&gt; JoyAxisEvent = platform.Struct('SDL_JoyAxisEvent',&lt;br /&gt;   [('type', rffi.INT),&lt;br /&gt;   ('which', rffi.INT),&lt;br /&gt;   ('axis', rffi.INT),&lt;br /&gt;   ('value', rffi.INT)])&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CONSTS = 'INIT_JOYSTICK QUERY ENABLE IGNORE PRESSED RELEASED'&lt;br /&gt;for name in CONSTS.split():&lt;br /&gt; name = name.strip()&lt;br /&gt; if name:&lt;br /&gt;  ci = platform.ConstantInteger('SDL_%s' %name)&lt;br /&gt;  setattr( CConfig, name, ci )&lt;br /&gt;globals().update(platform.configure(CConfig))&lt;br /&gt;&lt;br /&gt;JoystickPtr.TO.become(Joystick)&lt;br /&gt;JoyAxisEventPtr.TO.become(JoyAxisEvent)&lt;br /&gt;&lt;br /&gt;JoystickUpdate = external('SDL_JoystickUpdate', [], lltype.Void)&lt;br /&gt;NumJoysticks = external('SDL_NumJoysticks', [], rffi.INT)&lt;br /&gt;## CCHARP seems to stand for C char pointer ##&lt;br /&gt;JoystickName = external('SDL_JoystickName', [rffi.INT], rffi.CCHARP)&lt;br /&gt;JoystickOpen = external('SDL_JoystickOpen', [rffi.INT], JoystickPtr)&lt;br /&gt;JoystickOpened = external('SDL_JoystickOpened', [rffi.INT], rffi.INT)&lt;br /&gt;&lt;br /&gt;JoystickEventState = external('SDL_JoystickEventState', [rffi.INT], rffi.INT)&lt;br /&gt;&lt;br /&gt;def handle_event( etype, event ):&lt;br /&gt; if etype == RSDL.JOYAXISMOTION:&lt;br /&gt;  p = rffi.cast( JoyAxisEventPtr, event )&lt;br /&gt;  axis = rffi.getintfield(p, 'c_axis')&lt;br /&gt;  value = rffi.getintfield(p, 'c_value')&lt;br /&gt;  print 'axis: %s value: %s' %(axis, value)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def poll(loops=1000):&lt;br /&gt; event = lltype.malloc(RSDL.Event, flavor='raw')&lt;br /&gt; try:&lt;br /&gt;  i = 1&lt;br /&gt;  while i &lt; ok =" RSDL.PollEvent(event);" ok =" rffi.cast(lltype.Signed,"&gt;= 0&lt;br /&gt;   if ok &gt; 0: c_type = rffi.getintfield(event, 'c_type'); handle_event( c_type, event )&lt;br /&gt;   time.sleep(0.01)&lt;br /&gt;   i += 1&lt;br /&gt; finally: lltype.free(event, flavor='raw')&lt;br /&gt;&lt;br /&gt;def test():&lt;br /&gt; assert RSDL.Init(INIT_JOYSTICK | RSDL.INIT_VIDEO ) &gt;= 0&lt;br /&gt; num = NumJoysticks(); print 'number of joysticks/gamepads: %s' %num&lt;br /&gt; JoystickEventState( RSDL.ENABLE )&lt;br /&gt; if num:&lt;br /&gt;  joy = JoystickOpen( 0 )&lt;br /&gt;  numaxes = JoystickNumAxes( joy ); print 'number of axes: %s' %numaxes&lt;br /&gt;  numbut = JoystickNumButtons( joy ); print 'number of buttons: %s' %numbut&lt;br /&gt;  poll()&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt; from pypy.translator.interactive import Translation&lt;br /&gt; t = Translation( test )&lt;br /&gt; t.annotate(); t.rtype()&lt;br /&gt; entrypoint = t.compile_c()&lt;br /&gt; entrypoint()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-4057246911357253489?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/4057246911357253489/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/interfacing-rpython-with-c.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4057246911357253489'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4057246911357253489'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/interfacing-rpython-with-c.html' title='Interfacing RPython with C'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-8709563875867465185</id><published>2010-08-10T18:10:00.000-07:00</published><updated>2010-08-10T18:23:40.494-07:00</updated><title type='text'>RPython Open Sound Control</title><content type='html'>RPython OSC is now on google code &lt;a href="http://code.google.com/p/rpyosc/"&gt;here.&lt;/a&gt;  In an idealized test (without socket read/write) it performs about 10x faster than the pure python version of OSC by &lt;a href="https://trac.v2.nl/wiki/pyOSC"&gt;V2Labs.&lt;/a&gt;  PyOSC is fast because it uses CPython's struct module which is written in C, but still there is about a 10x cost in calling a C function from Python, which is eliminated by RPython.  RpyOSC uses the same API as PyOSC where it can, the differences are noted in the source code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-8709563875867465185?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/8709563875867465185/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-open-sound-control.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8709563875867465185'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/8709563875867465185'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-open-sound-control.html' title='RPython Open Sound Control'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-5343974326515280919</id><published>2010-08-10T17:59:00.000-07:00</published><updated>2010-08-10T18:02:02.068-07:00</updated><title type='text'>RPython Struct</title><content type='html'>There is no drop in replacement for the struct module in RPython.  In Rlib there is a "rstruct" folder, but only "unpack" is implemented with a warning in the header that it is incomplete; what it aparently lacks is proper endian support.  There was never any pure RPython example made for how to implement "pack", there is however one example how to implement pack/unpack for the interpreter level.  Looking at those examples it is clear we need to use the FormatIterator found in rlib/rstruct.  The runpack example uses meta-programming to generate tuples for returning different types, but this seems like overkill, at least it is in the case of OSC where we know excatly what types we are dealing with.  The two classes below (RStructUnpacker and RStructPacker) are simple and restricted RPython replacements for the struct module.  They can not be used as drop in replacements; the Packer expects to be packing the same type, and the Unpacker does not return what it has unpacked and instead sorts it into lists by type.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class RStructUnpacker( pypy.rlib.rstruct.formatiterator.FormatIterator):&lt;br /&gt; def __init__( self, fmt, data ):&lt;br /&gt;  self.strings = []&lt;br /&gt;  self.ints = []&lt;br /&gt;  self.floats = []&lt;br /&gt;  self.longs = []&lt;br /&gt;  self.input = data&lt;br /&gt;  self.inputpos = 0&lt;br /&gt;  self.interpret( fmt )&lt;br /&gt;&lt;br /&gt; def operate(self, fmtdesc, repetitions):&lt;br /&gt;  if fmtdesc.needcount:&lt;br /&gt;   fmtdesc.unpack(self, repetitions)&lt;br /&gt;  else:&lt;br /&gt;   for i in range(repetitions):&lt;br /&gt;    fmtdesc.unpack(self)&lt;br /&gt; operate._annspecialcase_ = 'specialize:arg(1)'&lt;br /&gt; _operate_is_specialized_ = True&lt;br /&gt;&lt;br /&gt; def align(self, mask):&lt;br /&gt;  self.inputpos = (self.inputpos + mask) &amp; ~mask&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; def read(self, count):&lt;br /&gt;  end = self.inputpos + count&lt;br /&gt;  if end &gt; len(self.input): raise SyntaxError&lt;br /&gt;  s = self.input[self.inputpos : end]&lt;br /&gt;  self.inputpos = end&lt;br /&gt;  return s&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; @specialize.argtype(1)&lt;br /&gt; def appendobj( self, ob ):&lt;br /&gt;  if isinstance(ob, str): self.strings.append( ob )&lt;br /&gt;  elif isinstance(ob, int): self.ints.append( ob )&lt;br /&gt;  elif isinstance(ob, float): self.floats.append( ob )&lt;br /&gt;  #elif isinstance(ob, long): self.longs.append( ob )  isinstance(ob, long) not RPython, how do we check for long?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class RStructPacker(pypy.rlib.rstruct.formatiterator.FormatIterator):&lt;br /&gt; # not a drop in replacement for struct.pack, but as close as RPython can get (easily).&lt;br /&gt; def __init__(self, fmt, strings=[], ints=[], floats=[], longs=[], unicodes=[], uints=[] ):&lt;br /&gt;  self.strings = strings&lt;br /&gt;  self.ints = ints&lt;br /&gt;  self.floats = floats&lt;br /&gt;  self.longs = longs&lt;br /&gt;  self.unicodes = unicodes&lt;br /&gt;  self.uints = uints&lt;br /&gt;  self.args_index = 0&lt;br /&gt;  self.result = []      # list of characters&lt;br /&gt;  self.interpret( fmt )&lt;br /&gt;&lt;br /&gt; def operate(self, fmtdesc, repetitions):&lt;br /&gt;  if fmtdesc.needcount:&lt;br /&gt;   fmtdesc.pack(self, repetitions)&lt;br /&gt;  else:&lt;br /&gt;   for i in range(repetitions):&lt;br /&gt;    fmtdesc.pack(self)&lt;br /&gt; operate._annspecialcase_ = 'specialize:arg(1)'&lt;br /&gt; _operate_is_specialized_ = True&lt;br /&gt;&lt;br /&gt; def align(self, mask):&lt;br /&gt;  pad = (-len(self.result)) &amp; mask&lt;br /&gt;  for i in range(pad):&lt;br /&gt;   self.result.append('\x00')&lt;br /&gt;&lt;br /&gt; def finished(self): pass&lt;br /&gt;&lt;br /&gt; def accept_str_arg(self):&lt;br /&gt;  assert self.strings&lt;br /&gt;  a = self.strings[ self.args_index ]&lt;br /&gt;  self.args_index += 1&lt;br /&gt;  return a&lt;br /&gt; def accept_int_arg(self):&lt;br /&gt;  assert self.ints&lt;br /&gt;  a = self.ints[ self.args_index ]&lt;br /&gt;  self.args_index += 1&lt;br /&gt;  return a&lt;br /&gt; def accept_float_arg(self):&lt;br /&gt;  assert self.floats&lt;br /&gt;  a = self.floats[ self.args_index ]&lt;br /&gt;  self.args_index += 1&lt;br /&gt;  return a&lt;br /&gt; def accept_unicode_arg(self):&lt;br /&gt;  assert self.unicodes&lt;br /&gt;  a = self.unicodes[ self.args_index ]&lt;br /&gt;  self.args_index += 1&lt;br /&gt;  return a&lt;br /&gt; def accept_uint_arg(self):&lt;br /&gt;  assert self.uints&lt;br /&gt;  a = self.uints[ self.args_index ]&lt;br /&gt;  self.args_index += 1&lt;br /&gt;  return a&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-5343974326515280919?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/5343974326515280919/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-struct.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5343974326515280919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5343974326515280919'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-struct.html' title='RPython Struct'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-1427335114099251133</id><published>2010-08-07T19:35:00.000-07:00</published><updated>2011-07-11T04:55:53.882-07:00</updated><title type='text'>Meta-Programming Part2: rbpy Internals</title><content type='html'>&lt;span style="font-weight: bold; font-family: arial;"&gt;RNA Introspection&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: arial;"&gt;Blender's bpy API is generated by RNA introspection; and this information is still preserved at the Python-level, each object either has a bl_rna attribute or a get_rna() function that contains all the C-level introspection information.  Knowing the names and types of all function arguments and their returns allows for more efficient remote wrappers that do less data marshaling because they know what type the server is sending them.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: arial;"&gt;Blender objects are not passed directly to the class/function wrapper generator; instead, they are first converted into an intermediate class during introspection because some special-case hacks must be applied.  The intermediate class "genRNA" is then passed to the wrapper generator that packages the information and gets it ready for pickling.  Pickling saves the user from having to run introspection every time they run their script, and also is the only workaround for transferring data from Python3 back to Python2 (Blender uses Python3 while RPython is a subset of Python2).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: arial;"&gt;When rbpy is launched again as a subprocess this time from Python2, it loads the introspection pickle and generates the final remote wrapper classes.  The __init__ of all objects uses *args so it can take any number of arguments or types.  Normally this is not RPython compatible because a tuple can not be iterated over, to workaround this we use additional meta-programming to unroll the *args in a RPython safe manner, the technique is detailed &lt;/span&gt;&lt;a style="font-family: arial;" href="http://pyppet.blogspot.com/2010/08/meta-programming-in-rpython.html"&gt;here.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: arial;"&gt;Below is the function that does the job of RNA introspection from a live blender object and generation of the intermediate class.&lt;/span&gt;&lt;br /&gt;&lt;pre style="font-family: arial;" class="prettyprint"&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;def reflect_RNA_instance( ob, classpath=None ):&lt;br /&gt; class genRNA(object):&lt;br /&gt;  if isinstance(ob,bpy.types.Object): type = ob.type&lt;br /&gt; genRNA.__name__ = sn = rpython_shadow_name( ob, classpath )&lt;br /&gt; genRNA._class_name_ = sn&lt;br /&gt; if classpath: genRNA._bpy_class_name_ = classpath&lt;br /&gt; else: genRNA._bpy_class_name_ = ob.__class__.__module__+'.'+ob.__class__.__name__&lt;br /&gt;&lt;br /&gt; if ob.__class__.__name__ == 'bpy_ops_submodule': # bpy.ops..&lt;br /&gt;  for opname in dir(ob):&lt;br /&gt;   func = getattr( ob, opname )&lt;br /&gt;   rna = func.get_rna()&lt;br /&gt;   args = []&lt;br /&gt;   for prop in rna.bl_rna.properties:&lt;br /&gt;    if prop.identifier == 'rna_type': continue&lt;br /&gt;    args.append( reflect_RNA_prop( prop ) )&lt;br /&gt;   d = lambda:()&lt;br /&gt;   d._argument_info = args&lt;br /&gt;   d._return_info = None&lt;br /&gt;   setattr( genRNA, opname, d )&lt;br /&gt;&lt;br /&gt; elif ob.__class__.__name__=='bpy_prop_collection': # bpy.data..&lt;br /&gt;  genRNA.length = 0  # these should not be a special case and all GET funcs should assign, can setattr work with nonconcretes?&lt;br /&gt;  genRNA.GET_length = d = lambda:(); d._return_info = int&lt;br /&gt;  d = lambda:(); d._return_info = (str,)&lt;br /&gt;  setattr( genRNA, 'keys', d )&lt;br /&gt;  d = lambda:(); d._return_info = object&lt;br /&gt;  setattr( genRNA, 'get', d )&lt;br /&gt;  d = lambda:(); d._return_info = object&lt;br /&gt;  setattr( genRNA, 'new', d )&lt;br /&gt;  d = lambda:(); d._return_info = (object,)&lt;br /&gt;  setattr( genRNA, 'values', d )&lt;br /&gt;&lt;br /&gt; elif ob.__class__.__name__ == 'vector':&lt;br /&gt;  genRNA.vec = [.0,.0,.0] # specialcase, use v.vec = v.to_tuple()&lt;br /&gt;  genRNA.angle = d = lambda:(); d._return_info = float&lt;br /&gt;  genRNA.copy = d = lambda:(); d._return_info = object&lt;br /&gt;  genRNA.cross = d = lambda:(); d._return_info = object&lt;br /&gt;  genRNA.difference = d = lambda:(); d._return_info = object&lt;br /&gt;  genRNA.dot = d = lambda:(); d._return_info = float&lt;br /&gt;  genRNA.lerp = d = lambda:(); d._return_info = object&lt;br /&gt;  genRNA.negate = d = lambda:(); d._return_info = object&lt;br /&gt;  genRNA.normalize = d = lambda:(); d._return_info = object&lt;br /&gt;  genRNA.project = d = lambda:(); d._return_info = object&lt;br /&gt;  genRNA.reflect = d = lambda:(); d._return_info = object&lt;br /&gt;  genRNA.to_tuple = d = lambda:(); d._return_info = (float,)&lt;br /&gt;  for p in 'length magnitude x y z w xyz'.split():&lt;br /&gt;   dummy = lambda:(); dummy._return_info = []; dummy._argument_info = float&lt;br /&gt;   setattr( genRNA, 'SET_%s' %p, dummy )&lt;br /&gt;   d = lambda:(); d._return_info = float; d._argument_info = None&lt;br /&gt;   setattr( genRNA, 'GET_%s' %p, d )&lt;br /&gt;&lt;br /&gt; else:&lt;br /&gt;  for prop in ob.bl_rna.properties:&lt;br /&gt;   #if not prop.is_readonly:&lt;br /&gt;   attr = getattr(ob, prop.identifier)&lt;br /&gt;   if hasattr( genRNA, prop.identifier ) and prop.identifier != 'type':&lt;br /&gt;    print( 'this should never happen', prop.identifier )&lt;br /&gt;    raise SyntaxError&lt;br /&gt;   dummy = lambda:()&lt;br /&gt;   dummy._argument_info = ai = reflect_RNA_prop( prop )&lt;br /&gt;   dummy._return_info = [] # do not return from a SET&lt;br /&gt;   setattr( genRNA, 'SET_%s' %prop.identifier, dummy )&lt;br /&gt;   d = lambda:()&lt;br /&gt;   d._argument_info = None&lt;br /&gt;   d._return_info = ri = reflect_RNA_prop( prop, attr )&lt;br /&gt;   setattr( genRNA, 'GET_%s' %prop.identifier, d )&lt;br /&gt;   #if prop.identifier == 'location': raise&lt;br /&gt;&lt;br /&gt;  for bfunc in ob.bl_rna.functions:&lt;br /&gt;   if hasattr( genRNA, bfunc.identifier ) or bfunc.identifier=='select': # what is this select bug? TODO check&lt;br /&gt;    print( 'this should never happen', bfunc.identifier )&lt;br /&gt;    raise SyntaxError&lt;br /&gt;   args = []&lt;br /&gt;   returns = []&lt;br /&gt;   for prop in bfunc.parameters:&lt;br /&gt;    if prop.use_output: returns.append( reflect_RNA_prop( prop ) )&lt;br /&gt;    else: args.append( reflect_RNA_prop( prop ) )&lt;br /&gt;   d2 = lambda:()&lt;br /&gt;   d2._argument_info = args&lt;br /&gt;   d2._return_info = returns&lt;br /&gt;   setattr( genRNA, bfunc.identifier, d2 )&lt;br /&gt; return genRNA&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-1427335114099251133?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/1427335114099251133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/meta-programming-part2-rbpy-internals.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/1427335114099251133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/1427335114099251133'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/meta-programming-part2-rbpy-internals.html' title='Meta-Programming Part2: rbpy Internals'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-6913558137917634550</id><published>2010-08-07T00:41:00.000-07:00</published><updated>2010-08-07T01:00:14.426-07:00</updated><title type='text'>rbpy - Remote Blender Proxy Objects</title><content type='html'>&lt;span style="font-family:arial;"&gt;The rbpy API &lt;a href="http://code.google.com/p/rbpy/"&gt;(now on google code)&lt;/a&gt; wraps Blender's own bpy API in a way that is optimized for remote access and speed, this means that existing scripts must be adapted to run remotely.  Passing entire objects over a pipe or socket is very expensive, so this API tends to be very verbose and explicit about what data is read and written per object.  The API is also RPython compatible, so compiling to C is an option, this is ideal for programs that do heavy computation but may only need occasionally talk to Blender.  It is more work to write RPython compliant code, but we can first prototype our program quickly in normal Python and later add the typing information required for RPython, if maximum speed is required.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Below is Blender's native API.  This example only does the most basic thing, move a few vertices on the default cube, and change the camera lens setting:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre  style="font-family:arial;"&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;def bpy_example():&lt;br /&gt;for ob in bpy.data.objects:&lt;br /&gt; if ob.type=='MESH':&lt;br /&gt;  if ob.data.verts:&lt;br /&gt;   for i,vert in enumerate(ob.data.verts):&lt;br /&gt;    vert.co.x += i*0.5&lt;br /&gt; elif ob.type=='CAMERA':&lt;br /&gt;  ob.data.lens = 80.0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:arial;" &gt;The Remote API&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt; 1. no nested modules: bpy.data.objects becomes bpy_data_objects&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt; 2. no dictionary or indexing or iterable objects, objects['Cube'] becomes objects.get('Cube')&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt; 3. no dynamic attributes, ob.location becomes ob.GET_location and ob.SET_location&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt; 4. functions that return objects are always wrapped by a container&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  a) if not RPython access single objects by con.value, and for a list con.all&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  b) if you are doing RPython you access the object from the container by lists with special tag names&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;The code below does the same thing as above but is written in the remote API.  Note that it is not yet RPython compatible, but runs fine in normal Python.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre  style="font-family:arial;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;def remote_example():&lt;br /&gt;objects = bpy_data_objects()&lt;br /&gt;container = objects.values()  # objects is not iterable, so instead we call values()&lt;br /&gt;for o in container.meshes:&lt;br /&gt; mesh = o.GET_data().value # untyped (not RPython)&lt;br /&gt; verts = mesh.GET_verts().value&lt;br /&gt; # we can not use len() on this object, GET_length is a special case for collections&lt;br /&gt; n = verts.GET_length()&lt;br /&gt; if n:&lt;br /&gt;  for i,vert in enumerate( verts.values().all ):&lt;br /&gt;   debug( str(vert) )&lt;br /&gt;   co = vert.GET_co().value&lt;br /&gt;   co.vec = co.to_tuple()  # co.vec is local not remote, note to_tuple returns a list not a tuple&lt;br /&gt;   co.vec[0] += float(i) * 0.5&lt;br /&gt;   co.SET_x( co.vec[0] )&lt;br /&gt;for cam in container.cameras:&lt;br /&gt; c = cam.GET_data().value&lt;br /&gt; c.SET_lens( 80.0 )&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:arial;" &gt;RPython Static Typing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;If you try to compile the above code you will get this error:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  &lt;span style="font-size:85%;"&gt;File "pypy/pypy/annotation/unaryop.py", line 581, in next&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:arial;font-size:85%;"&gt;    s_item = itr.s_container.getanyitem()&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:arial;font-size:85%;"&gt;AttributeError': 'SomeObject' object has no attribute 'getanyitem'&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:arial;font-size:85%;"&gt; .. v1 = next(v0)&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:arial;font-size:85%;"&gt; .. '(rbpy:693)remote_example'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;The code can not be translated because the dynamic variables are untyped, to make it RPython we only need to change the way we access it from the container that automatically types what it contains and sorts each type into their own list.  This by no means makes all of your code RPython, that is still up to you, it just means the API is RPython compatible.  A good place to getting started with RPython is to read &lt;/span&gt;&lt;a style="font-family: arial;" href="http://pyppet.blogspot.com/2010/08/rpython-part3-inheritance-rules.html"&gt;how the container works.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Below is the RPython version:&lt;/span&gt;&lt;br /&gt;&lt;pre  style="font-family:arial;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;def remote_rpy_example():&lt;br /&gt;objects = bpy_data_objects()&lt;br /&gt;container = objects.values()&lt;br /&gt;for o in container.meshes:&lt;br /&gt; mesh = o.GET_data().meshes_data[0]&lt;br /&gt; verts = mesh.GET_verts().collections[0]&lt;br /&gt; n = verts.GET_length()&lt;br /&gt; if n:&lt;br /&gt;  vcon = verts.values()&lt;br /&gt;  for i,vert in enumerate( vcon.verts ):&lt;br /&gt;   co = vert.GET_co().vectors[0]&lt;br /&gt;   co.vec = co.to_tuple()&lt;br /&gt;   co.vec[0] += float(i) * 0.5&lt;br /&gt;   co.SET_x( co.vec[0] )&lt;br /&gt;&lt;br /&gt;for cam in container.cameras:&lt;br /&gt; c = cam.GET_data().cameras_data[0]&lt;br /&gt; c.SET_lens( 80.0 )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-6913558137917634550?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/6913558137917634550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rbpy-remote-blender-proxy-objects.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6913558137917634550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6913558137917634550'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rbpy-remote-blender-proxy-objects.html' title='rbpy - Remote Blender Proxy Objects'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-6779626681544317263</id><published>2010-08-05T18:28:00.000-07:00</published><updated>2010-08-05T18:37:07.171-07:00</updated><title type='text'>RPython - Part3: Inheritance Rules</title><content type='html'>&lt;span style="font-family:arial;"&gt;`The inheritance rules are as follows: the union of two SomeInstance annotations is the SomeInstance of the &lt;span style="font-weight: bold;"&gt;most precise common base class&lt;/span&gt;. If an attribute is considered (i.e. read or written) through a SomeInstance of a parent class, then we assume that all subclasses also have the same attribute, and that the same annotation applies to them all (so code like return self.x in a method of a parent class forces the parent class and all its subclasses to have an attribute x, whose annotation is general enough to contain all the values that all the subclasses might want to store in x). However, distinct subclasses can have attributes of the same names with different, unrelated annotations if they are not used in a general way through the parent class.`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;The above quote from the PyPy documentation is critical in understanding how to write proper RPython code, it is not found in the coding guide subsection on RPython, but instead in the &lt;a href="http://codespeak.net/pypy/trunk/pypy/doc/translation.html#user-defined-classes-and-instances"&gt;translation document&lt;/a&gt;, don't miss the subsection on the &lt;a href="http://codespeak.net/pypy/trunk/pypy/doc/translation.html#the-annotation-pass"&gt;annotation pass&lt;/a&gt;, which helps explain what "someobjectness" is all about - basically you want zero percent "someobjectness" when you translate.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:arial;" &gt;Most Precise Common Base Class?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;What excatly is SomeInstance and the most precise common base class?  Above we saw that like-named methods with different signatures can be called if their type is proven by assertion first.  Without this assertion, only methods with matching signatures can be called.  SomeInstance is created when a function returns any number of different subclasses, the calling block is then unsure of which it is dealing with, and the flow graph will warn you it can not follow the call.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Assertion is block scoped flowing downstream, you can not call another function that asserts the type (below external_assert) and then expect the flow graph to know the type from the calling block.  It is not valid either to assert the type after wards, even if you do not reassign the variable within that scope.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre  style="font-family:arial;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;def external_assert( ob ):&lt;br /&gt; if ob.__class__ is TA: assert isinstance(ob, TA)&lt;br /&gt; elif ob.__class__ is TB: assert isinstance(ob, TB)&lt;br /&gt;&lt;br /&gt;def entrypoint_external_assert_fails( outside ):&lt;br /&gt; ta = TA()&lt;br /&gt; tb = TB()&lt;br /&gt; con = Container( ta, tb )&lt;br /&gt; a,klass = con.get(outside)&lt;br /&gt; a.whoami()&lt;br /&gt; if a.__class__ is TA:&lt;br /&gt;  external_assert( a )&lt;br /&gt;  a.diff_num_args( 1,2 )&lt;br /&gt; elif a.__class__ is TB:&lt;br /&gt;  external_assert( a )&lt;br /&gt;  a.diff_num_args( 1,2,3 )&lt;br /&gt;&lt;br /&gt; ## asserting after the function calls wont help either ##&lt;br /&gt; if a.__class__ is TA: assert isinstance(a, TA)&lt;br /&gt; elif a.__class__ is TB: assert isinstance(a, TB)&lt;br /&gt; print 'assert by func test complete'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;SomeInstance can be passed through numerous functions and at any later point be typed (by assertion) to convert it back into its real subclass and then saved into a list unique for that type.  Later when recalled from the list and the previously asserted instance remains its proper subclass.  The flow graph is smart enough to know that after the assertion, subclass X was put into this list, and all code that comes after references that list will get the correct subclass.  Note that if we put another subclass into the list, they all revert into SomeInstance.  This downstream-flow-style of typing is likely to confuse programmers coming from statically typed languages (like C++) where types are always declared first; its also likely to confuse Python programmers who are not used to having to worry about typing.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:arial;" &gt;The Casting Container:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;The class CastContainer encapsulates casting of objects and storing them in a list unique to their type.  Note that CastContainer expects all objects to all share the same base class if they pass through the &lt;span style="font-weight: bold;"&gt;add&lt;/span&gt; method.  If however you need objects of differing base classes, simply define a new &lt;span style="font-weight: bold;"&gt;add&lt;/span&gt; method with a different name, or change &lt;span style="font-weight: bold;"&gt;add&lt;/span&gt; to accept keyword arguments that default to None and represent all known base classes.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: arial;"&gt;&lt;br /&gt;class CastContainer(object):&lt;br /&gt; def __init__(self):&lt;br /&gt;  self.SomeInstances = []&lt;br /&gt;  self.type1 = []&lt;br /&gt;  self.type2 = []&lt;br /&gt;&lt;br /&gt; def add( self, o ):     # this is safer&lt;br /&gt;  self.SomeInstances.append( o )&lt;br /&gt;  if o.__class__ is TA:&lt;br /&gt;   assert isinstance(o,TA)&lt;br /&gt;   self.type1.append( o )&lt;br /&gt;   #getattr( self, TA.tag ).append( o )  # this works to&lt;br /&gt;  else:&lt;br /&gt;   assert isinstance(o,TB)&lt;br /&gt;   self.type2.append( o )&lt;br /&gt;&lt;br /&gt; def call_all( self, func, *args ):  &lt;br /&gt;  if args:&lt;br /&gt;   for a in self.SomeInstances: getattr(a,func)( args )&lt;br /&gt;  else:&lt;br /&gt;   for a in self.SomeInstances: getattr(a,func)()&lt;br /&gt;&lt;br /&gt;def entrypoint_castcontainer( outside ):&lt;br /&gt; ta = TA()&lt;br /&gt; tb = TB()&lt;br /&gt; con = CastContainer()&lt;br /&gt; con.add( ta )&lt;br /&gt; con.add( tb )&lt;br /&gt; a = con.type1[0]&lt;br /&gt; b = con.type2[0]&lt;br /&gt; a.diff_num_args( 1,2 )&lt;br /&gt; b.diff_num_args( 1,2,3 )&lt;br /&gt; con.call_all( 'whoami' )&lt;br /&gt; print 'this also works'&lt;br /&gt; for a in con.SomeInstances: a.whoami()&lt;br /&gt; print 'presorting test'&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-6779626681544317263?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/6779626681544317263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-part3-inheritance-rules.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6779626681544317263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6779626681544317263'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-part3-inheritance-rules.html' title='RPython - Part3: Inheritance Rules'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-4298068002367891996</id><published>2010-08-05T01:26:00.000-07:00</published><updated>2010-08-05T01:38:10.113-07:00</updated><title type='text'>RPython Part2</title><content type='html'>&lt;span style="font-family: arial;"&gt;There are at least two cases where runtime variables can be problematic in RPython with subclasses that have methods with the same name, and 1. return different types, or 2. like-named methods taking different number of arguments - in other words having a different signature.  The solution requires not only a if/else block, but also a assert statement that proves type.  Note that proving the type in the container's get function is of no help.  If the variable &lt;span style="font-weight: bold;"&gt;outside&lt;/span&gt; is known and concrete at translation time then everything works, if however it is passed in from CPython (as it is here) or read from a pipe or socket it is no longer concrete and translation fails. &lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;pre style="font-family: arial;"&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;br /&gt;class T(object): pass&lt;br /&gt;&lt;br /&gt;class TA( T ):&lt;br /&gt; def whoami(self): print 'i am instance of TA'&lt;br /&gt; def incompatible_return( self ): return 100&lt;br /&gt; def diff_args( self, a,b,c ): print 'TA: ', a, b, c&lt;br /&gt; def diff_num_args( self, a,b ): print 'TA: ', a, b&lt;br /&gt;&lt;br /&gt;class TB( T ):&lt;br /&gt; def whoami(self): print 'i am instance of TB'&lt;br /&gt; def incompatible_return(self): return 'string'&lt;br /&gt; def diff_args( self, x='1', y='y', z='z' ): print 'TB: ', x, y, z&lt;br /&gt; def diff_num_args( self, a,b,c ): print 'TA: ', a, b, c&lt;br /&gt;&lt;br /&gt;class Container(object):&lt;br /&gt; def __init__(self, *args):&lt;br /&gt;  self.items = []&lt;br /&gt;  for item in list(args): self.items.append( item )&lt;br /&gt; def get( self, index ):&lt;br /&gt;  a = self.items[index]&lt;br /&gt;  if index==0:&lt;br /&gt;   assert isinstance(a, TA)&lt;br /&gt;   return a, TA&lt;br /&gt;  else:&lt;br /&gt;   assert isinstance(a, TB)&lt;br /&gt;   return a, TB&lt;br /&gt;&lt;br /&gt;#File "pypy/pypy/translator/c/node.py", line 998, in _python_c_name&lt;br /&gt;# Exception: don't know how to simply render py object: 100&lt;br /&gt;def entrypoint_incompatible_fails( outside ):&lt;br /&gt; ta = TA()&lt;br /&gt; tb = TB()&lt;br /&gt; con = Container( ta, tb )&lt;br /&gt; a,klass = con.get(outside)&lt;br /&gt; assert isinstance(a, klass)  # too bad this wont work&lt;br /&gt; b = a.incompatible_return()&lt;br /&gt; print b&lt;br /&gt;&lt;br /&gt;## this works, so SomeObject subclasses method calls are fine as long as the number of args it the same and the return is the same type.&lt;br /&gt;def entrypoint_diffargs_works( outside ):&lt;br /&gt; ta = TA()&lt;br /&gt; tb = TB()&lt;br /&gt; con = Container( ta, tb )&lt;br /&gt; a,klass = con.get(outside)&lt;br /&gt; a.diff_args( 1,2,3 )&lt;br /&gt; a.whoami()&lt;br /&gt;&lt;br /&gt;## assert is required ##&lt;br /&gt;def entrypoint_branching_fails( outside ):&lt;br /&gt; ta = TA()&lt;br /&gt; tb = TB()&lt;br /&gt; con = Container( ta, tb )&lt;br /&gt; a,klass = con.get(outside)&lt;br /&gt; if a.__class__ is TA:&lt;br /&gt;  a.diff_num_args( 1,2 )&lt;br /&gt; elif a.__class__ is TB:&lt;br /&gt;  a.diff_num_args( 1,2,3 )&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def entrypoint_branching_works( outside ):&lt;br /&gt; ta = TA()&lt;br /&gt; tb = TB()&lt;br /&gt; con = Container( ta, tb )&lt;br /&gt; a,klass = con.get(outside)&lt;br /&gt; if a.__class__ is TA:&lt;br /&gt;  assert isinstance( a, TA )  # this is required&lt;br /&gt;  a.diff_num_args( 1,2 )&lt;br /&gt; elif a.__class__ is TB:&lt;br /&gt;  assert isinstance( a, TB )  # this is required&lt;br /&gt;  a.diff_num_args( 1,2,3 )&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PATH2PYPY = 'pypy'&lt;br /&gt;sys.path.append(PATH2PYPY)  # assumes you have pypy dist in a subfolder, you may need to rename this pypy-trunk&lt;br /&gt;&lt;br /&gt;def tests( outside ):&lt;br /&gt; entrypoint_branching_works(outside)&lt;br /&gt;&lt;br /&gt;from pypy.translator.interactive import Translation&lt;br /&gt;t = Translation( tests )&lt;br /&gt;t.annotate([int]); t.rtype()&lt;br /&gt;f = t.compile_c()&lt;br /&gt;f(0)&lt;br /&gt;f(1)&lt;br /&gt;print( 'end of program' )&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-4298068002367891996?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/4298068002367891996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-part2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4298068002367891996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/4298068002367891996'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/rpython-part2.html' title='RPython Part2'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-1017562444699059888</id><published>2010-08-03T01:50:00.000-07:00</published><updated>2010-08-03T02:40:23.000-07:00</updated><title type='text'>Meta-Programming in RPython</title><content type='html'>&lt;span style=";font-family:arial;font-size:130%;"  &gt;What is Restricted Python?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Its hard to find a good definition, mainly because there is no &lt;a href="http://codespeak.net/pypy/trunk/pypy/doc/coding-guide.html#rpython-definition-not"&gt;formal definition&lt;/a&gt;.  RPython was born as a side effect of the PyPy interpreter needing a way to compile itself.  Since then it has become a &lt;a href="http://code.google.com/p/rpython/"&gt;general purpose language&lt;/a&gt;, but apparently has not been written about much other than a few academic papers &lt;a href="http://www.disi.unige.it/person/AnconaD/papers/Recent_abstracts.html#AACM-DLS07"&gt;[1]&lt;/a&gt;, &lt;a href="http://www.iam.unibe.ch/%7Everwaest/pygirl.pdf"&gt;[2]&lt;/a&gt;.  Meta-programming is RPython most exciting feature, more on that below.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Language Restrictions/Definition:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:arial;"&gt;    1. Lists and Dicts must contain a compatible type.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        (objects with a common base class are compatible)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    2. Function arguments types must not be changed after the first call, each call must be consistent with the others in the ordering of types.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        (this also applies to function returns)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    3. Subclasses must redefine functions that interact with an attribute they have made a new type.*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    4. String and char are different, any string of length 1 becomes a char.  (some builtin functions accept only a char)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    5. Tuples may have incompatible types, but can not be iterated over, yet. (the error messages suggest the iteration limitation is going away)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    6. Only __init__ and __del__ are allowed, so no custom attribute access or operator overloading.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    7. Globals are considered constants (use singletons for global changeable state)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    8. No runtime definition of functions or classes.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    9. Slicing can not use negative indexes except for [:-1]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    10. Some builtin functions requires literals, like getattr, for example:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;          valid:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;                getattr(o,'x')&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;          invalid:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;                arg = 'x'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;                getattr( o, arg )&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;There are a few other rules and exceptions not listed above, but those are small and the RPython translator/compiler is going to give you clear errors when you violate them.  Getting started with RPython it is easy to make a mistake somewhere and violate rules 1-3, the errors you will get from breaking them are a bit cryptic as well:&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:arial;"&gt;    a. Exception: don't know how to simply render py object: &lt;type&gt;&lt;/type&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    b. pypy.rpython.error.TyperError: don't know how to convert from &lt;instancerepr&gt; to &lt;pyobjrepr&gt;&lt;/pyobjrepr&gt;&lt;/instancerepr&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    c. AttributeError: no field 'll_newlist'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    d. pypy.rpython.error.TyperError: reading attribute '__init__': no common base class for&lt;/span&gt; [type]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;*Rule 3 is unusual and easy to overlook, it means we must be careful with functions defined in our base class that interact with attributes we have redefined in subclasses that are of a new type.  In other words, a function defined in the base class expects its the attributes of self to always have those fixed types,  but if the subclasses changes the type, the function breaks.  In unrestricted Python we would normally want to put as many shared functions in the base class as possible to avoid code duplication, rule 3 turns this upside down.  Consider the following where we use wrapper objects for generic types and note that we have put the unwrap function in each of the subclasses.&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;class Type1(object): pass&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class Type2(object): pass&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class Abstract(object):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def __init__(self):        # be careful with init, do not try to define defaults for what will have different types in subclasses!&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            #self.x = 'never define what x is in the base class'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            self.y = 'this is safe'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def failure(self): self.x = 1.0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class O(object):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def __init__(self, arg='string'): self.a = arg&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class X( Abstract ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def __init__(self):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            Abstract.__init__(self)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            self.x = Type1()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def unwrap(self): return self.x&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class Y( Abstract ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def __init__(self):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            Abstract.__init__(self)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            self.x = Type2()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def unwrap(self): return self.x&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class Z( Abstract ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def __init__(self):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            Abstract.__init__(self)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            self.x = 10.0&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def unwrap(self): return self.x&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class W( Abstract ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def __init__(self):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            Abstract.__init__(self)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            self.x = O&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def unwrap(self): return self.x&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def new_instance(self, arg): return self.x(arg)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;def test_subclasses_fails():&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      c = Abstract()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      c.failure()    # this breaks because the type can not be known from the base class&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      a = X()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      b = Y()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;def test_subclasses_works():&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  ab=Abstract()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  a = X()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  b = Y()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  c = Z()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  lst = [a,b]    # sharing the same base class means they can co-exist in a list&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  for item in lst: print item&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print a.x&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print b.x&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print c.x&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  o = W().x()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print o&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print a.unwrap()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print b.unwrap()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  o2 = W().new_instance( 'hello world' )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print o2.a&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print 'test shared complete'&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-family:arial;"&gt;Another case that can easily lead to rule breaking is when dealing with *args which is a tuple,  and since tuples can store incompatibles, we may be tempted to use it to pass variable amounts of incompatible types; but they can not be iterated over, and casting args to a list will not work either because lists must contain only compatible types.  Tuple indexing must also be done with a literal constant as well, so for loop will fail:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;def func( *args ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      for i in range(len(args)):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            a = args[i]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Above fails, so the only way to get at items in args is to index each one with a literal like this, not pretty!:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;n = len(args)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;if n &gt; 1: args[0]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;if n &gt; 2: args[1]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;if n &gt; 3: args[2]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-family:arial;"&gt;Meta-Programming&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;If we have to code that way, then it seems like we should give up trying to pass variable numbers of incompatible types.  However, unlike many other languages which are parsed only from source code, RPython uses live objects and introspection; and before compiling we have the chance to generate classes, modify functions, create new globals, and more, all from unrestricted Python.  Using meta-programming we can easily bend some of the restrictions of RPython, in this case we are going to unroll the loop and inline valid RPython code.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;STAR_TYPES = 'bool int float str tuple list Meta ID'.split()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;ARGUMENT_SEP = '~'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class ID(object):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;      def __init__(self,value='undefined'): self.value = value&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class Meta(object): pass&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;class SimplePack( Meta ):        # would be useful to turn this into a minipickler&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_head(self):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    self.ID = '0'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    self._init_string = ''&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_bool(self, v ): self._init_string += '%s%s' %(v, ARGUMENT_SEP)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_int(self, v ): self._init_string += '%s%s' %(v, ARGUMENT_SEP)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_float(self, v ): self._init_string += '%s%s' %(v, ARGUMENT_SEP)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_str(self, v ): self._init_string += '"%s"%s' %(v, ARGUMENT_SEP)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_tuple(self, v ): self._init_string += '%s%s' %(v, ARGUMENT_SEP)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_list(self, v ): self._init_string += '%s%s' %(v, ARGUMENT_SEP)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_Meta(self, v ): self._init_string += '@%s%s' %(v.ID, ARGUMENT_SEP); print 'got meta'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  def init_ID(self, v ): self.ID = v.value; print 'got id'&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-family:arial;"&gt;The SimplePack subclass above is going to handle each possible type that may be passed in *args from our generated function.  gen_star_func and gen_switch_block are going to create the unrolled function for us, that can be very large depending on what maxitems is set to as we will see below. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;def gen_switch_block( prefix, indent, index ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  indent += '  '&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;     r = ''&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;     for type in STAR_TYPES:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;           if not r:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;                 r += '%sif isinstance( args[%s], %s ): self.%s_%s(args[%s])\n' %(indent,index,type, prefix,type,index)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;           else:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;                 r += '%selif isinstance( args[%s], %s ): self.%s_%s(args[%s])\n' %(indent,index,type, prefix,type,index)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  r += '%selse: print "unknown type", args[%s]\n' %(indent,index)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  return r&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;def gen_star_func(  name, handler_prefix='init', head=None, tail=None, maxitems=16 ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  ident = '  '&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  if head: body = '  self.%s\n' %head&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  else: body = ''&lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;br /&gt;  body += '  n = len(args)\n'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  for i in range( maxitems ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;         body += '%sif n &gt; %s:\n%s' %(ident, i, gen_switch_block(handler_prefix, ident,i) )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;         ident += '  '&lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;br /&gt;  if tail: body += '\n  ' + tail&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  e = 'def star_func(self, *args):\n' + body&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  print e&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  exec( e )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  star_func.func_name = name&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;  return star_func&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-family:arial;"&gt;The output of gen_star_func simply checks for each possible type, and forwards it to a handler.  In terms of speed there should not be a big expense, since the next switch block is only executed if the length of *args is greater, we lose some memory but it is reasonable as long as maxitems is not too high.  As you can see below the output of gen_star_func greatly helps keeps our code maintainable not having to copy and paste into every function where we want to use *args and variable types.&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;def star_func(self, *args):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    self.init_head()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    n = len(args)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    if n &gt; 0:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        if isinstance( args[0], bool ): self.init_bool(args[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        elif isinstance( args[0], int ): self.init_int(args[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        elif isinstance( args[0], float ): self.init_float(args[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        elif isinstance( args[0], str ): self.init_str(args[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        elif isinstance( args[0], tuple ): self.init_tuple(args[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        elif isinstance( args[0], list ): self.init_list(args[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        elif isinstance( args[0], Meta ): self.init_Meta(args[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        elif isinstance( args[0], ID ): self.init_ID(args[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        else: print "unknown type", args[0]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        if n &gt; 1:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            if isinstance( args[1], bool ): self.init_bool(args[1])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            elif isinstance( args[1], int ): self.init_int(args[1])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            elif isinstance( args[1], float ): self.init_float(args[1])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            elif isinstance( args[1], str ): self.init_str(args[1])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            elif isinstance( args[1], tuple ): self.init_tuple(args[1])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            elif isinstance( args[1], list ): self.init_list(args[1])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            elif isinstance( args[1], Meta ): self.init_Meta(args[1])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            elif isinstance( args[1], ID ): self.init_ID(args[1])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;       else: print "unknown type", args[1]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;            if n &gt; 2:&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;                 &lt;/span&gt;&lt;span style="font-family:arial;"&gt;...(to maxitems)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;def gen_class( name, base=Meta, items={} ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    cls = types.ClassType(str(name), bases=(base,), dict=items)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    globals().update( {name:cls} )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    return cls&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;def gen_metas( names ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    generated = []&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    init = gen_star_func( '__init__', handler_prefix='init', head='init_head()' )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    for name in names.split():&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        items = {'__init__':init}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        cls = gen_class( name, base=SimplePack, items=items )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;        generated.append( cls )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    return generated&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;GEN_METAS = gen_metas( 'Meta1 Meta2 Meta3' )&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;def test_meta():&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    m1 = Meta1()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    myid = ID('myid')&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    print myid&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    m2 = Meta2('a', 'b', 1.0, 'string', m1, myid )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    print m2._init_string&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    m3 = Meta3( m1, m2 )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    print m3._init_string&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-family:arial;"&gt;The test above test_meta takes the variable argument types and packs them into a string that could be sent over a pipe or socket for communication with another process.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;To run these tests you need to download the pypy source code, and set PATH2PYPY to where pypy is.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Below the tests function is the pypy entry point, to run the different tests above uncomment test_*&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;You can download the source code for this whole demo &lt;a href="http://pastebin.com/1kQTpktJ"&gt;here&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:arial;"&gt;PATH2PYPY = 'pypy'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;sys.path.append(PATH2PYPY)        # assumes you have pypy dist in a subfolder, you may need to rename this pypy-trunk&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;def tests( outside ):&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    #test_subclasses_fails()    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    #test_subclasses_works()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;    test_meta()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;from pypy.translator.interactive import Translation&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;t = Translation( tests )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;t.annotate([str]); t.rtype()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;f = t.compile_c()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;f('from the outside')&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;print( 'end of program' )&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-1017562444699059888?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/1017562444699059888/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2010/08/meta-programming-in-rpython.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/1017562444699059888'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/1017562444699059888'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2010/08/meta-programming-in-rpython.html' title='Meta-Programming in RPython'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-5196323185133896455</id><published>2009-07-19T09:34:00.000-07:00</published><updated>2010-06-16T19:01:43.750-07:00</updated><title type='text'>Beta2:  Physics, Video Playback, Wiimote</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/dwjTZHJ4LNI&amp;hl=en&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/dwjTZHJ4LNI&amp;hl=en&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_Z42nrTY4F64/SmQNCLhTosI/AAAAAAAAABI/fhQkYfXwLug/s1600-h/pyppet-xp.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:right;cursor:pointer; cursor:hand;width: 400px; height: 240px;" src="http://2.bp.blogspot.com/_Z42nrTY4F64/SmQNCLhTosI/AAAAAAAAABI/fhQkYfXwLug/s400/pyppet-xp.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5360423787581121218" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Above: Pyppet running under WinXP&lt;br /&gt;&lt;br /&gt;&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube.com/v/2IcGOFSRO9Y&amp;hl=en&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/2IcGOFSRO9Y&amp;hl=en&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-5196323185133896455?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/5196323185133896455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2009/07/new-beta-test-physics-and-video.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5196323185133896455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/5196323185133896455'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2009/07/new-beta-test-physics-and-video.html' title='Beta2:  Physics, Video Playback, Wiimote'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_Z42nrTY4F64/SmQNCLhTosI/AAAAAAAAABI/fhQkYfXwLug/s72-c/pyppet-xp.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8301333049390472977.post-6899744282089636082</id><published>2009-04-09T00:18:00.000-07:00</published><updated>2009-08-10T21:11:06.215-07:00</updated><title type='text'>Pyppet  Alpha Test</title><content type='html'>&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/P3g63sdYtig&amp;hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/P3g63sdYtig&amp;hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8301333049390472977-6899744282089636082?l=pyppet.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pyppet.blogspot.com/feeds/6899744282089636082/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://pyppet.blogspot.com/2009/04/pyppet-10-beta.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6899744282089636082'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8301333049390472977/posts/default/6899744282089636082'/><link rel='alternate' type='text/html' href='http://pyppet.blogspot.com/2009/04/pyppet-10-beta.html' title='Pyppet  Alpha Test'/><author><name>Hart</name><uri>http://www.blogger.com/profile/14287416587600419675</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://2.bp.blogspot.com/-33JSna-Iqpw/Tbj12JDTetI/AAAAAAAAAEs/_hC9MWA0JKw/s220/skelonly.jpg'/></author><thr:total>0</thr:total></entry></feed>
