ShaderOp.com

ShaderOp.com

Hi there. I have moved to a new website at Mhmmd.org. I'm no longer updating this one, but I'm keeping it around so the Internet wouldn't break. See you there.

Cubic Bezier Curve Node for Softimage ICE

Bezier curves are handy little things, especially the cubic variety. A while back I was playing around with ICE rigging in Autodesk Softimage, and I found myself in need of a cubic Bezier curve compound. I initially built one using the native ICE nodes, but although the tree was rather simple and consisted of nothing more than a few math nodes, the compound was simply too slow to be usable. So I set out to build a custom C++ ICE node to do the job.

Here it is in action:

SO_CubicBezierCurve in action

SO_CubicBezierCurve in action

 

Here’s what it looks like in the ICE tree:

SO_CubicBezierCurve node in an ICE tree

SO_CubicBezierCurve node in an ICE tree

 

Here’s the formal description from the source code:

 SO Cubic Bezier Curve is an ICE node for Autodesk Softimage 2011 SAP and above that computes a position on a Bezier curve, where the curve is defined by four input control points (P0, P1, P2, P3), and the distance along the curve is controlled using a parametric input parameter “S.”

It’s possible to replicate this functionality using nothing more than the basic ICE math nodes, but my tests showed that the performance in that case is unacceptably slow; typical results were 500 milliseconds per evaluation for the native math nodes compared to 20 milliseconds for the C++ implementation.

And finally, the binaries:

And a simple test scene:

And the source code:

I’m unable to provide a Linux build since I don’t have access to Softimage on that platform, but building from the source code provided above should be simple.

Sorry for being rather terse and short on the details, but I imagine this plug-in to be of interest to technical directors for the most part, and I’m assuming that the information provided here is good enough for that audience. But do contact me if you need any help.

A final thought

It’s expected for a custom C++ ICE node to perform better than an equivalent ICE compound, but the one order of magnitude performance advantage is a bit puzzling. The only thing I can conclude is that there’s a certain fixed overhead incurred during the evaluation of every ICE node. And this overhead adds up quickly when evaluating many simple, interconnected ICE nodes.

tl;dr: A single, monolithic ICE node >> a complex ICE compound.

Glossy Reflection and Refraction Shaders Updated for 3Delight for Softimage 3.0.9

A while back I wrote a couple of custom shaders for 3Delight for Softimage, in which I had to rely on a few hacks to get 3Delight to see the RenderMan shader implementation files that I was using. And it worked just fine for a while.

Version 3.0.9 of 3Delight for Softimage was released a little while ago, and it introduced a better, more robust way to distribute customs shaders. But that also meant that my hacks no longer worked.

Fortunately it turned out that the fix was easy to implement. So without further ado, here’s the improved, 3.0.9-compatible glossy reflection and refraction shaders for 3Delight for Softimage:

SO_3DelightShaders_v0.3.xsiaddon.zip

Someone also asked me if I could provide a demo scene that shows these shaders in action. And I’m glad to oblige:

CornellBox-3Delight-Glossy-Refraction.scn.zip

Enjoy.

Softimage Icons For Your Windows 7 Taskbar

I usually keep two versions of Softimage installed on my computer: the current version and whatever version that preceded it. I also keep the shortcuts to both versions pinned to my Windows 7 taskbar for easy access. But since both of them use the same icon, it usually takes me a fraction of a second to decide which version I want to launch.

So to make things easier, I made a few icons to help me tell them apart. Here’s what my taskbar looks like at the moment:

Softimage Windows 7 Taskbar icons

I’m not sure how many microseconds I’ll be saving every day because of those shortcuts, but at least it looks better than two identical icons next to each other.

Here are the icons for Softimage 2010, 2011.5, and 2012 in case someone else wants to use them:

SI2010.ico

SI2011SAP.ico

SI2012.ico

Enjoy!

Using wxPython with Autodesk Softimage

Every now and then someone on the Softimage mailing list would ask a question about using wxPython or PyQT with Autodesk Softimage, and such questions usually go unaddressed or receive a vague answer at best. So I’ll try to answer that question once and for all in this post.

Softimage already has a robust set of built-in user interface controls that should be adequate for most scenarios, but sometimes it would make sense to build a richer UI, like in the case of integrating external pipeline tools. And since Python is something of a de facto standard in the VFX industry, wxPython and PyQT are often the libraries of choice for such tasks.

The only problem is… Well, you better see for yourself. This is what happened when I tried to import the wxPython module in the script editor inside Softimage:

An instant, albeit orderly crash to desktop.

Here’s the issue as far as I understand it: On Windows operating systems, every process that has a window (i.e. any none-console application) also has a message loop, and each process can only have one message loop. The problem with PyQT and wxPython is that they will try to create their own message loops inside the Softimage process, which somehow leads to the whole process shutting down. I don’t know Linux that well, but the same issue should arise there.

It turns out that the solution to this is to run wxPython or PyQT on their own separate threads, so that both message loops will run in isolation from each other.

Apparently Houdini 9.5 had the same thing happening with PyQT, and its documentation contains a code sample that addresses this issue. All I had to do was repackage it inside a Softimage plug-in, although I used wxPython instead of PyQT because I’m more familiar with it. But it should be trivial to change the code to use PyQT instead.

So, without further ado, here’s the code:

# By Mohammad "ShaderOp" Abdulfatah
#
# This little sample demonstrates how to use wxPython to display
# a dialog box inside Autodesk Softimage. It attached an item
# to the "Window" menu named "WxSceneInfo", which executes a
# a command that shows a wxPython dialog box with a labed and
# two buttons. One button adpats the label with the number
# of top-level objects in the current scne, and the other creates
# a polygonal cube.
#
# Modifying this code to work wiht PyQT should be trivial.
#
# For questions, comments, and feedback, you can reach me
# through my website at http://shaderop.com/contact/
 
import win32com.client
from win32com.client import constants
import threading
 
# This will be used to ensure that only one copy of the dialog
# is running.
is_dialog_running_event = threading.Event()
 
def XSILoadPlugin(in_reg):
  in_reg.Author = "Mohammad Abdulfatah"
  in_reg.Name = "WxSceneInfoPlugin"
  in_reg.Major = 1
  in_reg.Minor = 0
 
  in_reg.RegisterCommand("WxSceneInfo","WxSceneInfo")
  in_reg.RegisterMenu(constants.siMenuMainWindowID,
    "WxSceneInfo_Menu",
    False,
    False)
 
  #RegistrationInsertionPoint - do not remove this line
 
  return True
 
def XSIUnloadPlugin(in_reg):
  strPluginName = in_reg.Name
  return True
 
def WxSceneInfo_Init(in_ctxt):
  oCmd = in_ctxt.Source
  oCmd.Description = "Wx Scene Info"
  oCmd.ReturnValue = True
 
  return True
 
def WxSceneInfo_Execute():
  # only launch the dialog if it's not already running.
  if is_dialog_running_event.is_set() == False:
    __queueCommand(runDialog)
  else:
    # It's probably a good idea to somehow notify the user that
    # the dialog is already running.
    pass
 
  return True
 
def WxSceneInfo_Menu_Init( in_ctxt ):
  oMenu = in_ctxt.Source
  oMenu.AddCommandItem("WxSceneInfo","WxSceneInfo")
  return True
 
def runDialog():
  # Any attempt to import the wx module on the main application thread
  # will cause Softiamge to crash and burn, but since this method runs
  # on a separate thread and has its own message loop, importing wx
  # will work here.
  import wx	
 
  # Now go to town
  class MyFrame(wx.Frame):
    def __init__(self, title):
      wx.Frame.__init__(self,
        None,
        size=(400,300),
        style=wx.DEFAULT_FRAME_STYLE,
        title=title)
 
      # set background color to SI's signature desert gray.
      self.SetBackgroundColour(wx.Color(171, 168, 166))
      self.Bind(wx.EVT_CLOSE, self.OnClose)
 
      self.objectCountLabel = wx.StaticText(self,
        label="Number of objects in scene:",
        pos = (10, 10),
        size = (300, -1))
      self.refreshButton = wx.Button(self, label = "Refresh",
                                     pos = (310, 10))
      self.Bind(wx.EVT_BUTTON, self.OnRefresh, self.refreshButton)
 
      self.createCubeButton = wx.Button(self,
                                      label="Create cube",
                                      pos=(310, 50))
      self.Bind(wx.EVT_BUTTON, self.OnCreateCube, self.createCubeButton)
 
    def OnRefresh(self, event):
      count = Application.ActiveSceneRoot.Children.Count
      label = "Number of objects in scene: {0}".format(count)
      self.objectCountLabel.SetLabel(label)
 
    def OnCreateCube(self, event):
      Application.ActiveSceneRoot.AddGeometry('Cube', 'MeshSurface')
 
    def OnClose(self, event):
      self.Destroy()
      # IMPORTANT: Must release the event once the windows is closed,
      # else the dialog won't launch again until after SI is restarted
      global is_dialog_running_event
      is_dialog_running_event.clear()
 
  app = __getApplication()
  frame = MyFrame("Hello World")
  frame.Show(True)
  is_dialog_running_event.set()
  app.MainLoop()
 
# Following code is copied almost verbatim from Houdini 9.5 sample
# code at http://www.sidefx.com/docs/houdini9.5/hom/cookbook/pyqt/
 
__command_queue = []
__command_queue_lock = threading.RLock()
__command_queue_event = threading.Event()
 
__wx_thread = None
 
def __queueCommand(callable, arguments=()):
  global __wx_thread
 
  if __wx_thread is None:
    __wx_thread = threading.Thread(target=__wxThreadMain,
                                   name="wxThread")
    __wx_thread.start()
 
  __command_queue_lock.acquire()
  __command_queue.append((callable, arguments))
  __command_queue_lock.release()
 
  __command_queue_event.set()
 
def __wxThreadMain():
  while True:
    __command_queue_event.wait()
    __command_queue_lock.acquire()
    command = __command_queue.pop()
    __command_queue_event.clear()
    __command_queue_lock.release()
 
    command[0].__call__(*command[1])
 
__wx_app = None
 
def __getApplication():
  import wx
  global __wx_app
  if __wx_app is None:
    __wx_app = wx.App(False)
  return __wx_app

Here’s what it looks like inside Softimage:

And here’s the link to the Github gist:

https://gist.github.com/911265

Hope this helps.

One Awesome Spam Comment

This one really pushes the envelope:

Attractive section of content. I just stumbled upon your website and in accession capital to assert that I get in fact enjoyed account your blog posts. Anyway I’ll be subscribing to your augment and even I achievement you access consistently rapidly.

I hope they’re not selling dictionaries.

Older posts
Powered by: