home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / site-packages / cgkit / tool.py < prev    next >
Encoding:
Python Source  |  2007-01-11  |  13.7 KB  |  420 lines

  1. # ***** BEGIN LICENSE BLOCK *****
  2. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. #
  4. # The contents of this file are subject to the Mozilla Public License Version
  5. # 1.1 (the "License"); you may not use this file except in compliance with
  6. # the License. You may obtain a copy of the License at
  7. # http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS IS" basis,
  10. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. # for the specific language governing rights and limitations under the
  12. # License.
  13. #
  14. # The Original Code is the Python Computer Graphics Kit.
  15. #
  16. # The Initial Developer of the Original Code is Matthias Baas.
  17. # Portions created by the Initial Developer are Copyright (C) 2004
  18. # the Initial Developer. All Rights Reserved.
  19. #
  20. # Contributor(s):
  21. #
  22. # Alternatively, the contents of this file may be used under the terms of
  23. # either the GNU General Public License Version 2 or later (the "GPL"), or
  24. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  25. # in which case the provisions of the GPL or the LGPL are applicable instead
  26. # of those above. If you wish to allow use of your version of this file only
  27. # under the terms of either the GPL or the LGPL, and not to allow others to
  28. # use your version of this file under the terms of the MPL, indicate your
  29. # decision by deleting the provisions above and replace them with the notice
  30. # and other provisions required by the GPL or the LGPL. If you do not delete
  31. # the provisions above, a recipient may use your version of this file under
  32. # the terms of any one of the MPL, the GPL or the LGPL.
  33. #
  34. # ***** END LICENSE BLOCK *****
  35. # $Id: tool.py,v 1.6 2005/03/31 17:20:01 mbaas Exp $
  36.  
  37. ## \file tool.py
  38. ## Contains the Tool base class.
  39.  
  40. import os, os.path, sys, optparse, re
  41. import pluginmanager
  42. from eventmanager import eventManager
  43. from scene import getScene
  44. from targetcamera import TargetCamera
  45. from Interfaces import *
  46. from cmds import *
  47. import application
  48.  
  49.  
  50. # Tool
  51. class Tool:
  52.     """Base class for command tools that process a cgkit scene.
  53.  
  54.     Derived classes have to implement the init() and action() method.
  55.     The options are parsed in the constructor. To start the tool, the
  56.     run() method has to be called. This method does the following:
  57.  
  58.     - Call init()
  59.     - Check if input files are given
  60.     - Load plugins
  61.     - Load input files
  62.     - Get or create a camera
  63.     - Call action()
  64.  
  65.     Attributes:
  66.  
  67.     - time_start
  68.     - time_end
  69.     - frame_start
  70.     - frame_end
  71.  
  72.     The global timer will be initialized with the frame rate and the
  73.     start time.
  74.     """
  75.  
  76.     def __init__(self, defaultoptionvar=None):
  77.         """Constructor.
  78.  
  79.         defaultoptionvar is the name of an environment variable that
  80.         contains the default options.
  81.         """
  82.  
  83.         # Create the option parser, ...
  84.         self.optparser = self.createOptionParser()
  85.         # set the options...
  86.         self.setOptions(self.optparser)
  87.  
  88.         scene = getScene()
  89.         timer = scene.timer()
  90.  
  91.         # Parse the default options
  92.         defaultopts = None
  93.         if defaultoptionvar!=None:
  94.             if defaultoptionvar in os.environ:
  95.                 optstring = os.environ[defaultoptionvar]
  96.                 args = optstring.split()
  97.                 defaultopts, dummy = self.optparser.parse_args(args)
  98.                 
  99.         # ...and parse the command line. The options are in "options"
  100.         self.options, self.args = self.optparser.parse_args(values=defaultopts)
  101.  
  102.         # Print default options
  103.         if self.options.verbose:
  104.             print "Python",sys.version
  105.             if defaultoptionvar!=None:
  106.                 if defaultoptionvar in os.environ:
  107.                     print "Default options in %s: %s"%(defaultoptionvar, os.environ[defaultoptionvar])
  108.                 else:
  109.                     print "Environment variable %s not set."%defaultoptionvar
  110.  
  111.         # Determine screen resolution...
  112.         self._no_resolution_specified = False
  113.         if self.options.width==None:
  114.             self.options.width = 640
  115.             self._no_resolution_specified = True
  116.         if self.options.height==None:
  117.             self.options.height = int(self.options.width*3.0/4)
  118.  
  119.         # Set the global application object to this tool class
  120.         application._app = self
  121.  
  122.     # init
  123.     def init(self):
  124.         pass
  125.  
  126.     # action
  127.     def action(self):
  128.         pass
  129.  
  130.  
  131.     # createOptionParser
  132.     def createOptionParser(self):
  133.         """Create and OptionParser instance.
  134.  
  135.         \return Option parser.
  136.         """
  137.         usage = "usage: %prog [options] inputfiles"
  138.         return optparse.OptionParser(usage)
  139.  
  140.     # setOptions
  141.     def setOptions(self, optparser):
  142.         """Set the options.
  143.  
  144.         \param optparser (\c OptionParser) Option parser
  145.         """
  146.         optparser.add_option("-f", "--fps", type="float", default=30,
  147.                              help="Frame rate")
  148.         optparser.add_option("-W", "--width", type="int", default=None,
  149.                              help="Screen width")
  150.         optparser.add_option("-H", "--height", type="int", default=None,
  151.                              help="Screen height")
  152.         optparser.add_option("-p", "--plugin", action="append", default=[],
  153.                              help="Load a plugin file or directory")
  154.         optparser.add_option("-v", "--verbose", action="store_true", default=False,
  155.                              help="Output info messages")
  156.         optparser.add_option("-c", "--camera", metavar="NAME",
  157.                              help="Select a camera")
  158.         optparser.add_option("-b", "--begin", metavar="TIME",
  159.                              help="Specify time or frame where to begin")
  160.         optparser.add_option("-e", "--end", metavar="TIME",
  161.                              help="Specify time or frame where to end")
  162.         optparser.add_option("-U", "--up", metavar="AXIS",
  163.                              help="Specify the up axis ('y' or 'z')")
  164.         optparser.add_option("-O", "--option", action="append", default=[],
  165.                              help="Add a global scene option")
  166.         optparser.add_option("-t", "--set-time", action="store_true", default=False,
  167.                              help="Set starting time directly instead of stepping there")
  168.  
  169.     # run
  170.     def run(self):
  171.         """Run the tool.
  172.  
  173.         This method calls the init() and action() method.
  174.         """
  175.  
  176.         # Custom initialization
  177.         self.init()
  178.              
  179.         # No input file given? Then print the help page and exit
  180.         if len(self.args)==0:
  181.             self.optparser.print_help()
  182.             return
  183.  
  184.         # Load plugins
  185.         self.loadPlugins()
  186.  
  187.         # Load the input files...
  188.         for filename in self.args:
  189.             if self.options.verbose:
  190.                 print 'Loading "%s"...'%filename
  191.             load(filename)
  192.  
  193.         # Convert global settings into command line options
  194.         self.setOptionsFromGlobals()
  195.  
  196.         self.cam = self.getCamera()
  197.  
  198.         self.action()
  199.  
  200.         getScene().clear()
  201.  
  202.     # setOptionsFromGlobals
  203.     def setOptionsFromGlobals(self):
  204.         """Convert global settings into command line options.
  205.  
  206.         This method is called by the run() method after the input
  207.         files have been read. The method inspects the global settings
  208.         and modifies the command line options as appropriate.
  209.         """
  210.         
  211.         scene = getScene()
  212.         timer = scene.timer()
  213.  
  214.         # Set additional global options
  215.         for s in self.options.option:
  216.             f = s.split("=")
  217.             name = f[0]
  218.             val = "=".join(f[1:])
  219.             scene.setGlobal(name, val)
  220.  
  221.         # Set framerate
  222.         self.options.fps = scene.getGlobal("fps", self.options.fps)
  223.  
  224.         # Set resolution
  225.         if self._no_resolution_specified:
  226.             w,h,a = self.getResolution()
  227.             self.options.width = w
  228.             self.options.height = h
  229.             self.options.aspect = a
  230.  
  231.         # Camera
  232.         self.options.camera = scene.getGlobal("camera", self.options.camera)
  233.  
  234.         # Set up direction
  235.         if self.options.up!=None:
  236.             up = self.options.up.lower()
  237.             if up=="y":
  238.                 scene.up = (0,1,0)
  239.             elif up=="z":
  240.                 scene.up = (0,0,1)
  241.             else:
  242.                 raise ValueError, "Invalid 'up' direction: '%s' (should be 'y' or 'z')"%self.options.up
  243.  
  244.         # Set the frame rate
  245.         timer.fps = self.options.fps
  246.  
  247.         # Determine end time
  248.         self.time_end = None
  249.         self.frame_end = None
  250.         if self.options.end!=None:
  251.             val, unit = self._parseTimeStr(self.options.end)
  252.             if unit=="f":
  253.                 scene.timer().frame = val
  254.             else:
  255.                 scene.timer().time = val
  256.             self.time_end = timer.time
  257.             self.frame_end = timer.frame
  258.  
  259.         # Determine start time (which will be set on the timer)
  260.         self.time_start = 0.0
  261.         self.frame_start = 0.0
  262.         if self.options.begin!=None:
  263.             val, unit = self._parseTimeStr(self.options.begin)
  264.             if unit=="f":
  265.                 scene.timer().frame = val
  266.             else:
  267.                 scene.timer().time = val
  268.             self.time_start = timer.time
  269.             self.frame_start = timer.frame
  270.  
  271.         # Set the timer to the start time...
  272.         # (either by stepping there or by setting it directly)
  273.         if self.options.set_time:
  274.             # Set the time directly
  275.             timer.time = self.time_start
  276.         else:
  277.             # Step to the start time (so that any simulation, etc. is properly
  278.             # done)
  279.             timer.time = 0
  280.             while self.time_start-timer.time>1E-5:
  281.                 timer.step()
  282.  
  283.         if self.time_end==None:
  284.             te = "<inf>"
  285.             fe = "<inf>"
  286.         else:
  287.             te = "%1.2fs"%self.time_end
  288.             fe = "%d"%self.frame_end
  289.         if self.options.verbose:
  290.             print "Time range: %1.2fs - %s (frames %d - %s)"%(self.time_start, te, self.frame_start, fe)
  291.  
  292.  
  293.     # loadPlugins
  294.     def loadPlugins(self):
  295.         """Load plugins.
  296.  
  297.         The plugins specified in the environment variable CGKIT_PLUGIN_PATH
  298.         and in the command line are loaded.
  299.         """
  300.         
  301.         s = os.environ.get("CGKIT_PLUGIN_PATH", "")
  302.         pluginsvar = splitPaths(s)
  303.         
  304.         lst = getattr(self.options, "plugin", [])
  305.         s = ";".join(lst)
  306.         s = s.replace(",", ";")
  307.         pluginsopt = splitPaths(s)
  308.  
  309.         plugins = pluginsvar + pluginsopt
  310.  
  311.         descs = pluginmanager.importPlugins(plugins)
  312.         for desc in descs:
  313.             if desc.status!=pluginmanager.STATUS_OK:
  314.                 sys.stderr.write(70*"-"+"\n")
  315.                 sys.stderr.write('ERROR: Loading plugin "%s" failed:\n'%os.path.basename(desc.filename))
  316.                 sys.stderr.write("\n"+desc.traceback)
  317.                 sys.stderr.write(70*"-"+"\n")
  318.  
  319.     # getCamera
  320.     def getCamera(self):
  321.         """Get or create a camera object.
  322.         """
  323.         
  324.         scene = getScene()
  325.  
  326.         cname = self.options.camera
  327.         
  328.         # Search for a camera...
  329.         cam = None
  330.         for obj in scene.walkWorld():
  331.             prots = obj.protocols()
  332.             if ICamera in prots:
  333.                 if obj.name==cname or cname==None :
  334.                     cam = obj
  335.                     break
  336.  
  337.         if cname!=None and cam==None:
  338.             raise ValueError, 'Camera "%s" not found.'%cname
  339.  
  340.         # No camera? Then create a default camera...
  341.         if cam==None:
  342.             if self.options.verbose:
  343.                 print "No camera set, using a default camera."
  344.             bbmin, bbmax = scene.boundingBox().getBounds()
  345.             dif = bbmax-bbmin
  346.             b1 = scene.up.ortho()
  347.             b2 = scene.up.cross(b1)
  348.             pos = dif.length()*(0.5*b1+b2) + (bbmax.z+0.5*dif.z)*scene.up
  349.             if abs(dif.z)<0.0001:
  350.                 pos += 0.8*dif.length()*scene.up
  351.             cam = TargetCamera(pos = pos,
  352.                                target = 0.5*(bbmin+bbmax)-0.2*(dif.z*scene.up),
  353.                                fov = 50)
  354.         else:
  355.             if self.options.verbose:
  356.                 print "Camera:",cam.name
  357.  
  358.         return cam
  359.  
  360.     # getResolution
  361.     def getResolution(self):
  362.         """Read the global resolution setting.
  363.  
  364.         Returns a tuple (width, height, pixel aspect)
  365.         """
  366.         res = getScene().getGlobal("resolution", (640,480))
  367.         try:
  368.             if len(res)==2:
  369.                 w,h = res
  370.                 aspect = 1
  371.             elif len(res)==3:
  372.                 w,h,aspect = res
  373.             else:
  374.                 raise Exception
  375.         except:
  376.             print >>sys.stderr, "Error: Invalid resolution setting:",res
  377.             w,h,aspect = 640,480,1
  378.         return w,h,aspect
  379.  
  380.     # translateKeyWordOpt
  381.     def translateKeyWordOpt(self, opt, dic, msg):
  382.         """Translates an option value from keyword to internal value.
  383.  
  384.         opt is the value of the option as specified by the user.
  385.         dic is a dictionary that translates keywords to internal values.
  386.         msg is an error message that will be passed to the ValueError
  387.         exception. msg must contain the sub string "%s" that will be
  388.         replaced with the invalid option value.
  389.         """
  390.  
  391.         if opt==None:
  392.             opt2 = None
  393.         else:
  394.             opt2 = opt.lower()
  395.             
  396.         if dic.has_key(opt2):
  397.             return dic[opt2]
  398.         else:
  399.             raise ValueError, msg%opt
  400.  
  401.         
  402.     ## protected:
  403.  
  404.     # _parseTimeStr
  405.     def _parseTimeStr(self, s):
  406.         """Split a string containing a time value into the value and unit.
  407.  
  408.         Returns a tuple (value, unit).
  409.         """
  410.         m = re.search("[a-z]*$", s)
  411.         unit = s[m.start():]
  412.         val = float(s[:m.start()])
  413.         if unit=="":
  414.             unit="f"
  415.         if unit not in ["f", "s"]:
  416.             raise ValueError, "Unknown time unit: '%s'"%unit
  417.         return val,unit
  418.     
  419.  
  420.