def wrangle_accelerator(obj, wrangler, now): node = wrangle_node_parm(obj, "accelerator_node", now) if node is not None: return node.type_and_paramset parm_selection = {"accelerator": SohoPBRT("accelerator", "string", ["bvh"], False)} parms = obj.evaluate(parm_selection, now) accelerator_name = parms["accelerator"].Value[0] if accelerator_name == "bvh": parm_selection = { "maxnodeprims": SohoPBRT("maxnodeprims", "integer", [4], True), "splitmethod": SohoPBRT("splitmethod", "string", ["sah"], True), } else: parm_selection = { "intersectcost": SohoPBRT("intersectcost", "integer", [80], True), "traversalcostcost": SohoPBRT("traversalcost", "integer", [1], True), "emptybonus": SohoPBRT("emptybonus", "float", [0.2], True), "maxprims": SohoPBRT("maxprims", "integer", [1], True), "kdtree_maxdepth": SohoPBRT( "kdtree_maxdepth", "integer", [1], True, key="maxdepth" ), } parms = obj.evaluate(parm_selection, now) paramset = ParamSet() for parm in parms: paramset.add(parms[parm].to_pbrt()) return (accelerator_name, paramset)
def wrangle_filter(obj, wrangler, now): node = wrangle_node_parm(obj, "filter_node", now) if node is not None: return node.type_and_paramset parm_selection = { "filter": SohoPBRT("filter", "string", ["gaussian"], False), "filter_width": SohoPBRT("filter_width", "float", [2.0, 2.0], False), "alpha": SohoPBRT("gauss_alpha", "float", [2.0], True, key="alpha"), "B": SohoPBRT("mitchell_B", "float", [0.333333], True, key="B"), "C": SohoPBRT("mitchell_C", "float", [0.333333], True, key="C"), "tau": SohoPBRT("sinc_tau", "float", [3], True, key="tau"), } parms = obj.evaluate(parm_selection, now) filter_name = parms["filter"].Value[0] paramset = ParamSet() xwidth = parms["filter_width"].Value[0] ywidth = parms["filter_width"].Value[1] paramset.add(PBRTParam("float", "xwidth", xwidth)) paramset.add(PBRTParam("float", "ywidth", ywidth)) if filter_name == "gaussian" and "alpha" in parms: paramset.add(parms["alpha"].to_pbrt()) if filter_name == "mitchell" and "B" in parms: paramset.add(parms["B"].to_pbrt()) if filter_name == "mitchell" and "C" in parms: paramset.add(parms["C"].to_pbrt()) if filter_name == "sinc" and "tau" in parms: paramset.add(parms["tau"].to_pbrt()) return (filter_name, paramset)
def override_to_paramset(material, override_str): """Convert a material override to a ParamSet Args: material (str): An oppath to a material node override_str (str): A string with the overrides (material_override) Returns: ParamSet of the override params """ paramset = ParamSet() override = eval(override_str) if not override or not material: return paramset node = hou.node(material) if not node: return paramset processed_parms = set() for parm_name in override: parm = node.parm(parm_name) if parm is None: continue parm_tuple = parm.tuple() if parm_tuple.name() in processed_parms: continue value = [override[x.name()] for x in parm_tuple] pbrt_param = pbrt_param_from_ref(parm_tuple, value) paramset.add(pbrt_param) return paramset
def wrangle_filter(obj, wrangler, now): node = wrangle_node_parm(obj, 'filter_node', now) if node is not None: return node.type_and_paramset parm_selection = { 'filter' : SohoPBRT('filter', 'string', ['gaussian'], False), 'filter_width' : SohoPBRT('filter_width', 'float', [2.0, 2.0], False), 'alpha' : SohoPBRT('gauss_alpha', 'float', [2.0], True, key='alpha'), 'B' : SohoPBRT('mitchell_B', 'float', [0.333333], True, key='B'), 'C' : SohoPBRT('mitchell_C', 'float', [0.333333], True, key='C'), 'tau' : SohoPBRT('sinc_tau', 'float', [3], True, key='tau'), } parms = obj.evaluate(parm_selection, now) filter_name = parms['filter'].Value[0] paramset = ParamSet() xwidth = parms['filter_width'].Value[0] ywidth = parms['filter_width'].Value[1] paramset.add(PBRTParam('float', 'xwidth', xwidth)) paramset.add(PBRTParam('float', 'ywidth', ywidth)) if filter_name == 'gaussian' and 'alpha' in parms: paramset.add(parms['alpha'].to_pbrt()) if filter_name == 'mitchell' and 'mitchell_B' in parms: paramset.add(parms['B'].to_pbrt()) if filter_name == 'mitchell' and 'mitchell_C' in parms: paramset.add(parms['C'].to_pbrt()) if filter_name == 'sinc' and 'tau' in parms: paramset.add(parms['tau'].to_pbrt()) return (filter_name, paramset)
def heightfield_prim_wrangler(prims, paramset=None, properties=None, override_node=None): """Outputs a "heightfield" Shapes for the input geometry Args: prims (list of hou.Prims): Input prims paramset (ParamSet): Any base params to add to the shape. (Optional) properties (dict): Dictionary of SohoParms (Optional) Returns: None """ for prim in prims: resolution = prim.resolution() # If the z resolution is not 1 then this really isn't a heightfield if resolution[2] != 1: continue hf_paramset = ParamSet() # Similar to trianglemesh, this is more compact fast, however it might # be a memory issue and a generator per voxel or per slice might be a # better approach. voxeldata = array.array("f") voxeldata.fromstring(prim.allVoxelsAsString()) with api.TransformBlock(): xform = prim_transform(prim) xform = hou.Matrix4(xform) srt = xform.explode() # Here we need to split up the xform mainly so we can manipulate # the scale. In particular Houdini's prim xforms maintain a # rotation matrix but the z scale is ignored. So here we are # setting it directly to 1 as the "Pz" (or voxeldata) will always # be the exact height, no scales are applied to the prim xform. # We also need to scale up heightfield since in Houdini by default # the size is -1,-1,-1 to 1,1,1 where in pbrt its 0,0,0 to 1,1,1 api.Translate(*srt["translate"]) rot = srt["rotate"] if rot.z(): api.Rotate(rot[2], 0, 0, 1) if rot.y(): api.Rotate(rot[1], 0, 1, 0) if rot.x(): api.Rotate(rot[0], 1, 0, 0) api.Scale(srt["scale"][0] * 2.0, srt["scale"][1] * 2.0, 1.0) api.Translate(-0.5, -0.5, 0) hf_paramset.add(PBRTParam("integer", "nu", resolution[0])) hf_paramset.add(PBRTParam("integer", "nv", resolution[1])) hf_paramset.add(PBRTParam("float", "Pz", voxeldata)) hf_paramset |= paramset hf_paramset |= prim_override(prim, override_node) api.Shape("heightfield", hf_paramset) return
def tube_wrangler(gdp, paramset=None, properties=None): """Outputs "cone" or "cylinder" Shapes for the input geometry Args: gdp (hou.Geometry): Input geo paramset (ParamSet): Any base params to add to the shape. (Optional) properties (dict): Dictionary of SohoParms (Optional) Returns: None """ for prim in gdp.prims(): shape_paramset = ParamSet(paramset) with api.TransformBlock(): xform = prim_transform(prim) taper = prim.intrinsicValue('tubetaper') # workaround, see TODO below in the else: pass if not (taper == 0 or taper == 1): api.Comment( 'Skipping tube, prim # %i, with non-conforming taper of %f' % (prim.number(), taper)) continue closed = prim.intrinsicValue('closed') api.ConcatTransform(xform) api.Rotate(-90, 1, 0, 0) if taper == 0: shape = 'cone' api.Translate(0, 0, -0.5) elif taper == 1: shape = 'cylinder' shape_paramset.add(PBRTParam('float', 'zmin', -0.5)) shape_paramset.add(PBRTParam('float', 'zmax', 0.5)) else: # TODO support hyperboloid, however pbrt currently # has no ends of trouble with this shape type # crashes or hangs pass with api.TransformBlock(): # Flip in Y so parameteric UV's match Houdini's api.Scale(1, -1, 1) api.Shape(shape, shape_paramset) if closed: disk_paramset = ParamSet(paramset) if shape == 'cylinder': disk_paramset.add(PBRTParam('float', 'height', 0.5)) api.Shape('disk', disk_paramset) disk_paramset.add(PBRTParam('float', 'height', -0.5)) api.Shape('disk', disk_paramset) else: disk_paramset.add(PBRTParam('float', 'height', 0)) api.Shape('disk', disk_paramset) return
def mesh_alpha_texs(properties): if not properties: return paramset = ParamSet() for prop in ("alpha", "shadowalpha"): if prop not in properties: continue tex = properties[prop].Value[0] if not tex: continue paramset.add(PBRTParam("texture", prop, tex)) return paramset
def wrangle_sampler(obj, wrangler, now): node = wrangle_node_parm(obj, "sampler_node", now) if node is not None: return node.type_and_paramset parm_selection = { "sampler": SohoPBRT("sampler", "string", ["halton"], False), "pixelsamples": SohoPBRT("pixelsamples", "integer", [16], False), "jitter": SohoPBRT("jitter", "bool", [1], False), "samples": SohoPBRT("samples", "integer", [4, 4], False), "dimensions": SohoPBRT("dimensions", "integer", [4], False), } parms = obj.evaluate(parm_selection, now) sampler_name = parms["sampler"].Value[0] paramset = ParamSet() if sampler_name == "stratified": xsamples = parms["samples"].Value[0] ysamples = parms["samples"].Value[1] paramset.add(PBRTParam("integer", "xsamples", xsamples)) paramset.add(PBRTParam("integer", "ysamples", ysamples)) paramset.add(parms["jitter"].to_pbrt()) paramset.add(parms["dimensions"].to_pbrt()) else: paramset.add(parms["pixelsamples"].to_pbrt()) return (sampler_name, paramset)
def wrangle_sampler(obj, wrangler, now): node = wrangle_node_parm(obj, 'sampler_node', now) if node is not None: return node.type_and_paramset parm_selection = { 'sampler' : SohoPBRT('sampler', 'string', ['halton'], False), 'pixelsamples' : SohoPBRT('pixelsamples', 'integer', [16], False), 'jitter' : SohoPBRT('jitter', 'bool', [1], False), 'samples' : SohoPBRT('samples', 'integer', [4, 4], False), 'dimensions' : SohoPBRT('dimensions', 'integer', [4], False), } parms = obj.evaluate(parm_selection, now) sampler_name = parms['sampler'].Value[0] paramset = ParamSet() if sampler_name == 'stratified': xsamples = parms['samples'].Value[0] ysamples = parms['samples'].Value[1] paramset.add(PBRTParam('integer', 'xsamples', xsamples)) paramset.add(PBRTParam('integer', 'ysamples', ysamples)) paramset.add(parms['jitter'].to_pbrt()) paramset.add(parms['dimensions'].to_pbrt()) else: paramset.add(parms['pixelsamples'].to_pbrt()) return (sampler_name, paramset)
def wrangle_sampler(obj, wrangler, now): node = wrangle_node_parm(obj, "sampler_node", now) if node is not None: return node.type_and_paramset parm_selection = { "sampler": SohoPBRT("sampler", "string", ["pmj02bn"], False), "pixelsamples": SohoPBRT("pixelsamples", "integer", [16], False), "randomization": SohoPBRT("randomization", "string", ["fastowen"], True), "jitter": SohoPBRT("jitter", "bool", [1], True), "samples": SohoPBRT("samples", "integer", [4, 4], False), } parms = obj.evaluate(parm_selection, now) sampler_name = parms["sampler"].Value[0] paramset = ParamSet() if sampler_name == "stratified": xsamples = parms["samples"].Value[0] ysamples = parms["samples"].Value[1] paramset.add(PBRTParam("integer", "xsamples", xsamples)) paramset.add(PBRTParam("integer", "ysamples", ysamples)) if "jitter" in parms: paramset.add(parms["jitter"].to_pbrt()) else: if (sampler_name in ("sobol", "paddedsobol", "zsobol", "halton") and "randomization" in parms): # NOTE: If the halton sampler is picked, it is not compatible with the # randomization "fastowen". paramset.add(parms["randomization"].to_pbrt()) paramset.add(parms["pixelsamples"].to_pbrt()) return (sampler_name, paramset)
def bounds_to_api_box(b): """Output a trianglemesh Shape of box based on the input bounds""" paramset = ParamSet() paramset.add( PBRTParam('point', 'P', [ b[1], b[2], b[5], b[0], b[2], b[5], b[1], b[3], b[5], b[0], b[3], b[5], b[0], b[2], b[4], b[1], b[2], b[4], b[0], b[3], b[4], b[1], b[3], b[4] ])) paramset.add( PBRTParam('integer', 'indices', [ 0, 3, 1, 0, 2, 3, 4, 7, 5, 4, 6, 7, 6, 2, 7, 6, 3, 2, 5, 1, 4, 5, 0, 1, 5, 2, 0, 5, 7, 2, 1, 6, 4, 1, 3, 6 ])) api.Shape('trianglemesh', paramset)
def override_to_paramset(material, override_str): """Convert a material override to a ParamSet Args: material (str): An oppath to a material node override_str (str): A string with the overrides (material_override) Returns: ParamSet of the override params """ paramset = ParamSet() override = eval(override_str) if not override or not material: return paramset node = hou.node(material) if not node: return paramset for override_name in override: # There can be two style of "overrides" one is a straight parm override # which is similar to what Houdini does. The other style of override is # for the spectrum type parms. Since spectrum parms can be of different # types and the Material Overrides only support "rgb" we are limited # in the types of spectrum overrides we can do. To work around this we'll # support a different style, override_name:spectrum_type. If the parm name # ends in one of the "rgb/color" types then we'll handle it differently. try: parm_name, spectrum_type = override_name.split(':', 1) parm_tuple = node.parmTuple(parm_name) except ValueError: spectrum_type = None parm_name = override_name parm = node.parm(parm_name) if parm is None: continue parm_tuple = parm.tuple() if spectrum_type is None: value = [override[x.name()] for x in parm_tuple] pbrt_param = pbrt_param_from_ref(parm_tuple, value) elif spectrum_type in ('spectrum', 'xyz', 'blackbody'): pbrt_param = PBRTParam(spectrum_type, parm_name, override[override_name]) else: raise ValueError('Unable to wrangle override name: %s' % override_name) paramset.add(pbrt_param) return paramset
def wrangle_film(obj, wrangler, now): node = wrangle_node_parm(obj, 'film_node', now) if node is not None: return node.type_and_paramset paramset = ParamSet() parm_selection = { 'filename' : SohoPBRT('filename', 'string', ['pbrt.exr'], False), 'maxsampleluminance' : SohoPBRT('maxsampleluminance', 'float', [1e38], True), 'diagonal' : SohoPBRT('diagonal', 'float', [35], True), } parms = obj.evaluate(parm_selection, now) for parm_name, parm in parms.iteritems(): paramset.add(parm.to_pbrt()) parm_selection = { 'res' : SohoPBRT('res', 'integer', [1280, 720], False), } parms = obj.evaluate(parm_selection, now) paramset.add(PBRTParam('integer', 'xresolution', parms['res'].Value[0])) paramset.add(PBRTParam('integer', 'yresolution', parms['res'].Value[1])) crop_region = obj.getCameraCropWindow(wrangler, now) if crop_region != [0.0, 1.0, 0.0, 1.0]: paramset.add(PBRTParam('float', 'cropwindow', crop_region)) return ('image', paramset)
def wrangle_film(obj, wrangler, now): node = wrangle_node_parm(obj, "film_node", now) if node is not None: return node.type_and_paramset paramset = ParamSet() parm_selection = { "filename": SohoPBRT("filename", "string", ["pbrt.exr"], False), "maxsampleluminance": SohoPBRT("maxsampleluminance", "float", [1e38], True), "diagonal": SohoPBRT("diagonal", "float", [35], True), } parms = obj.evaluate(parm_selection, now) for parm_name, parm in parms.iteritems(): paramset.add(parm.to_pbrt()) parm_selection = {"res": SohoPBRT("res", "integer", [1280, 720], False)} parms = obj.evaluate(parm_selection, now) paramset.add(PBRTParam("integer", "xresolution", parms["res"].Value[0])) paramset.add(PBRTParam("integer", "yresolution", parms["res"].Value[1])) crop_region = obj.getCameraCropWindow(wrangler, now) if crop_region != [0.0, 1.0, 0.0, 1.0]: paramset.add(PBRTParam("float", "cropwindow", crop_region)) return ("image", paramset)
def loopsubdiv_params(mesh_gdp): """Generates a ParamSet for a loopsubdiv The following attributes are checked for - P (point), built-in attribute Args: mesh_gdp (hou.Geometry): Input geo Returns: ParamSet of the attributes on the geometry """ mesh_paramset = ParamSet() P = array.array("f") P.fromstring(mesh_gdp.pointFloatAttribValuesAsString("P")) indices = vtx_attrib_gen(mesh_gdp, None) mesh_paramset.add(PBRTParam("integer", "indices", indices)) mesh_paramset.add(PBRTParam("point", "P", P)) return mesh_paramset
def wrangle_accelerator(obj, wrangler, now): node = wrangle_node_parm(obj, 'accelerator_node', now) if node is not None: return node.type_and_paramset parm_selection = { 'accelerator' : SohoPBRT('accelerator', 'string', ['bvh'], False), } parms = obj.evaluate(parm_selection, now) accelerator_name = parms['accelerator'].Value[0] if accelerator_name == 'bvh': parm_selection = { 'maxnodeprims' : SohoPBRT('maxnodeprims', 'integer', [4], True), 'splitmethod' : SohoPBRT('splitmethod', 'string', ['sah'], True), } else: parm_selection = { 'intersectcost' : SohoPBRT('intersectcost', 'integer', [80], True), 'traversalcostcost' : SohoPBRT('traversalcost', 'integer', [1], True), 'emptybonus' : SohoPBRT('emptybonus', 'float', [0.2], True), 'maxprims' : SohoPBRT('maxprims', 'integer', [1], True), 'kdtree_maxdepth' : SohoPBRT('kdtree_maxdepth', 'integer', [1], True, key='maxdepth') } parms = obj.evaluate(parm_selection, now) paramset = ParamSet() for parm in parms: paramset.add(parms[parm].to_pbrt()) return (accelerator_name, paramset)
def wrangle_film(obj, wrangler, now): node = wrangle_node_parm(obj, "film_node", now) if node is not None: return node.type_and_paramset paramset = ParamSet() parm_selection = { "filename": SohoPBRT("filename", "string", ["pbrt.exr"], False), "maxcomponentvalue": SohoPBRT("maxcomponentvalue", "float", [1e38], True), "diagonal": SohoPBRT("diagonal", "float", [35], True), "savefp16": SohoPBRT("savefp16", "bool", [1], True), } parms = obj.evaluate(parm_selection, now) for parm in parms.values(): paramset.add(parm.to_pbrt()) parm_selection = { "film": SohoPBRT("film", "string", ["rgb"], False), "res": SohoPBRT("res", "integer", [1280, 720], False), } parms = obj.evaluate(parm_selection, now) film_name = parms["film"].Value[0] paramset.add(PBRTParam("integer", "xresolution", parms["res"].Value[0])) paramset.add(PBRTParam("integer", "yresolution", parms["res"].Value[1])) crop_region = obj.getCameraCropWindow(wrangler, now) if crop_region != [0.0, 1.0, 0.0, 1.0]: paramset.add(PBRTParam("float", "cropwindow", crop_region)) parm_selection = { "iso": SohoPBRT("iso", "float", [100], True), "whitebalance": SohoPBRT("whitebalance", "float", [0], True), "sensor": SohoPBRT("sensor", "string", ["cie1931"], True), } if film_name == "spectral": parm_selection["buckets"] = SohoPBRT("buckets", "integer", [16], True) parms = obj.evaluate(parm_selection, now) for parm in parms.values(): paramset.add(parm.to_pbrt()) return (film_name, paramset)
def nurbs_wrangler(gdp, paramset=None, properties=None, override_node=None): """Outputs a "nurbs" Shape for input geometry The following attributes are checked for - P (point), built-in attribute Args: gdp (hou.Geometry): Input geo paramset (ParamSet): Any base params to add to the shape. (Optional) properties (dict): Dictionary of SohoParms (Optional) Returns: None """ # TODO: - Figure out how the Pw attribute works in Houdini # has_Pw = False if gdp.findPointAttrib('Pw') is None else True has_Pw = False # TODO - Figure out how to query [uv]_extent in hou # u_extent_h = gdp.attribute('geo:prim', 'geo:ubasisextent') # v_extent_h = gdp.attribute('geo:prim', 'geo:vbasisextent') for prim in gdp.prims(): nurbs_paramset = ParamSet() row = prim.intrinsicValue("nu") col = prim.intrinsicValue("nv") u_order = prim.intrinsicValue("uorder") v_order = prim.intrinsicValue("vorder") u_wrap = prim.intrinsicValue("uwrap") v_wrap = prim.intrinsicValue("vwrap") u_knots = prim.intrinsicValue("uknots") v_knots = prim.intrinsicValue("vknots") if u_wrap: row += u_order - 1 if v_wrap: col += v_order - 1 nurbs_paramset.add(PBRTParam("integer", "nu", row)) nurbs_paramset.add(PBRTParam("integer", "nv", col)) nurbs_paramset.add(PBRTParam("integer", "uorder", u_order)) nurbs_paramset.add(PBRTParam("integer", "vorder", v_order)) nurbs_paramset.add(PBRTParam("float", "uknots", u_knots)) nurbs_paramset.add(PBRTParam("float", "vknots", v_knots)) # NOTE: Currently not sure how these are set within Houdini # but they are queryable # The Platonic SOP, Teapot -> Convert to NURBS can make these. # nurbs_paramset.add(PBRTParam('float', 'u0', u_extent[0])) # nurbs_paramset.add(PBRTParam('float', 'v0', v_extent[0])) # nurbs_paramset.add(PBRTParam('float', 'u1', u_extent[1])) # nurbs_paramset.add(PBRTParam('float', 'v1', v_extent[1])) # if row + u_order != len(u_knots): # api.Comment('Invalid U') # if col + v_order != len(v_knots): # api.Comment('Invalid V') P = [] for v in xrange(col): for u in xrange(row): vtx = prim.vertex(u % prim.numCols(), v % prim.numRows()) pt = vtx.point() P.append(pt.attribValue("P")) if not has_Pw: nurbs_paramset.add(PBRTParam("point", "P", P)) else: # TODO: While the pbrt scene file looks right, the render # is a bit odd. Scaled up geo? Not what I was expecting. # Perhaps compare to RMan. w = prim_pt2vtx_attrib_gen(prim, "Pw") Pw = itertools.izip(P, w) nurbs_paramset.add(PBRTParam("float", "Pw", Pw)) nurbs_paramset |= paramset nurbs_paramset |= prim_override(prim, override_node) api.Shape("nurbs", nurbs_paramset)
def wrangle_integrator(obj, wrangler, now): node = wrangle_node_parm(obj, 'integrator_node', now) if node is not None: return node.type_and_paramset parm_selection = { 'integrator' : SohoPBRT('integrator', 'string', ['path'], False), 'maxdepth' : SohoPBRT('maxdepth', 'integer', [5], False), 'rrthreshold' : SohoPBRT('rrthreshold', 'float', [1], True), 'lightsamplestrategy' : SohoPBRT('lightsamplestrategy', 'string', ['spatial'], True), 'visualizestrategies' : SohoPBRT('visualizestrategies', 'toggle', [False], True), 'visualizeweights' : SohoPBRT('visualizeweights', 'toggle', [False], True), 'iterations' : SohoPBRT('iterations', 'integer', [64], True), 'photonsperiterations' : SohoPBRT('photonsperiterations', 'integer', [-1], True), 'imagewritefrequency' : SohoPBRT('imagewritefrequency', 'integer', [2.14748e+09], True), 'radius' : SohoPBRT('radius', 'float', [1], True), 'bootstrapsamples' : SohoPBRT('bootstrapsamples', 'integer', [100000], True), 'chains' : SohoPBRT('chains', 'integer', [1000], True), 'mutationsperpixel' : SohoPBRT('mutataionsperpixel', 'integer', [100], True), 'largestepprobability' : SohoPBRT('largestepprobability', 'float', [0.3], True), 'sigma' : SohoPBRT('sigma', 'float', [0.01], True), 'strategy' : SohoPBRT('strategy', 'string', ['all'], True), 'nsamples' : SohoPBRT('nsamples', 'integer', ['64'], True), 'cossample' : SohoPBRT('cossample', 'toggle', [True], True), } integrator_parms = { 'ao' : ['nsamples', 'cossample'], 'path' : ['maxdepth', 'rrthreshold', 'lightsamplestrategy'], 'bdpt' : ['maxdepth', 'rrthreshold', 'lightsamplestrategy', 'visualizestrategies', 'visualizeweights'], 'mlt' : ['maxdepth', 'bootstrapsamples', 'chains', 'mutationsperpixel', 'largestepprobability', 'sigma'], 'sppm' : ['maxdepth', 'iterations', 'photonsperiteration', 'imagewritefrequency', 'radius'], 'whitted' : ['maxdepth'], 'volpath' : ['maxdepth', 'rrthreshold', 'lightsamplestrategy'], 'directlighting' : ['maxdepth', 'strategy'], } parms = obj.evaluate(parm_selection, now) integrator_name = parms['integrator'].Value[0] paramset = ParamSet() for parm_name in integrator_parms[integrator_name]: if parm_name not in parms: continue paramset.add(parms[parm_name].to_pbrt()) return (integrator_name, paramset)
def curve_wrangler(gdp, paramset=None, properties=None): """Outputs a "curve" Shape for input geometry The following attributes are checked for - P (point), built-in attribute width (vertex/point/prim), float N (vertex/point), float[3] curvetype (prim), string (overrides the property pbrt_curvetype) Args: gdp (hou.Geometry): Input geo paramset (ParamSet): Any base params to add to the shape. (Optional) properties (dict): Dictionary of SohoParms (Optional) Returns: None """ if paramset is None: paramset = ParamSet() if properties is None: properties = {} curve_type = None if 'pbrt_curvetype' in properties: curve_type = properties['pbrt_curvetype'].Value[0] paramset.add(PBRTParam('string', 'type', curve_type)) if 'splitdepth' in properties: paramset.add(properties['splitdepth'].to_pbrt()) has_vtx_width = False if gdp.findVertexAttrib('width') is None else True has_pt_width = False if gdp.findPointAttrib('width') is None else True has_prim_width = False if gdp.findPrimAttrib('width') is None else True has_curvetype = False if gdp.findPrimAttrib('curvetype') is None else True has_vtx_N = False if gdp.findVertexAttrib('N') is None else True has_pt_N = False if gdp.findPointAttrib('N') is None else True for prim in gdp.prims(): curve_paramset = ParamSet() prim_curve_type = curve_type # Closed curve surfaces are not supported if prim.intrinsicValue('closed'): continue order = prim.intrinsicValue('order') degree = order - 1 # PBRT only supports degree 2 or 3 curves # TODO: We could possibly convert the curves to a format that # pbrt supports but for now we'll expect the user to have # a curve basis which is supported # https://www.codeproject.com/Articles/996281/NURBS-crve-made-easy if degree not in (2, 3): continue curve_paramset.add(PBRTParam('integer', 'degree', degree)) if prim.intrinsicValue('typename') == 'BezierCurve': basis = 'bezier' else: basis = 'bspline' curve_paramset.add(PBRTParam('string', 'basis', [basis])) P = prim_pt2vtx_attrib_gen(prim) curve_paramset.add(PBRTParam('point', 'P', P)) if has_curvetype: prim_val = prim.attribValue('curvetype') prim_curve_type = prim_val if prim_val else curve_type if prim_curve_type is not None: curve_paramset.add(PBRTParam('string', 'type', [prim_curve_type])) if prim_curve_type == 'ribbon': if has_vtx_N or has_pt_N: N = (prim.attribValueAt('N', u) for u in prim.intrinsicValue('knots')) else: # If ribbon, normals must exist # TODO: Let pbrt error? Or put default values? N = [ (0, 0, 1), ] * len(prim.intrinsicValue('knots')) if N is not None: curve_paramset.add(PBRTParam('normal', 'N', N)) if has_vtx_width: curve_paramset.add( PBRTParam('float', 'width0', prim.vertex(0).attribValue('width'))) curve_paramset.add( PBRTParam('float', 'width1', prim.vertex(-1).attribValue('width'))) elif has_pt_width: curve_paramset.add( PBRTParam('float', 'width0', prim.vertex(0).point().attribValue('width'))) curve_paramset.add( PBRTParam('float', 'width1', prim.vertex(-1).point().attribValue('width'))) elif has_prim_width: curve_paramset.add( PBRTParam('float', 'width', prim.attribValue('width'))) else: # Houdini's default matches a width of 0.05 curve_paramset.add(PBRTParam('float', 'width', 0.05)) curve_paramset |= paramset api.Shape('curve', curve_paramset) return
def tube_wrangler(gdp, paramset=None, properties=None, override_node=None): """Outputs "cone" or "cylinder" Shapes for the input geometry Args: gdp (hou.Geometry): Input geo paramset (ParamSet): Any base params to add to the shape. (Optional) properties (dict): Dictionary of SohoParms (Optional) Returns: None """ for prim in gdp.prims(): shape_paramset = ParamSet(paramset) shape_paramset |= prim_override(prim, override_node) with api.TransformBlock(): side_paramset = ParamSet(shape_paramset) xform = prim_transform(prim) taper = prim.intrinsicValue("tubetaper") # workaround, see TODO below in the else: pass if not (taper == 0 or taper == 1): api.Comment( "Skipping tube, prim # %i, with non-conforming taper of %f" % (prim.number(), taper)) continue closed = prim.intrinsicValue("closed") api.ConcatTransform(xform) api.Rotate(-90, 1, 0, 0) if taper == 0: shape = "cone" api.Translate(0, 0, -0.5) elif taper == 1: shape = "cylinder" side_paramset.add(PBRTParam("float", "zmin", -0.5)) side_paramset.add(PBRTParam("float", "zmax", 0.5)) else: # TODO support hyperboloid, however pbrt currently # has no ends of trouble with this shape type # crashes or hangs api.Comment("Hyperboloid skipped due to PBRT instability") with api.AttributeBlock(): api.ReverseOrientation() # Flip in Y so parameteric UV's match Houdini's api.Scale(1, -1, 1) api.Shape(shape, side_paramset) if closed: disk_paramset = ParamSet(shape_paramset) if shape == "cylinder": disk_paramset.add(PBRTParam("float", "height", 0.5)) api.Shape("disk", disk_paramset) disk_paramset.replace(PBRTParam("float", "height", -0.5)) with api.AttributeBlock(): api.ReverseOrientation() api.Shape("disk", disk_paramset) else: with api.AttributeBlock(): api.ReverseOrientation() api.Shape("disk", disk_paramset) return
def trianglemesh_params(mesh_gdp, computeN=True): """Generates a ParamSet for a trianglemesh The following attributes are checked for - P (point), built-in attribute N (vertex/point), float[3] uv (vertex/point), float[3] S (vertex/point), float[3] faceIndices (prim), integer, used for ptex Args: mesh_gdp (hou.Geometry): Input geo computeN (bool): Whether to auto-compute normals if they don't exist Defaults to True Returns: ParamSet of the attributes on the geometry """ mesh_paramset = ParamSet() unique_points = False # Required P_attrib = mesh_gdp.findPointAttrib("P") # Optional N_attrib = mesh_gdp.findVertexAttrib("N") if N_attrib is None: N_attrib = mesh_gdp.findPointAttrib("N") # If there are no vertex or point normals and we need to compute # them with a SopVerb if N_attrib is None and computeN: normal_verb = hou.sopNodeTypeCategory().nodeVerb("normal") normal_verb.setParms({"type": 0}) normals_gdp = hou.Geometry() normal_verb.execute(normals_gdp, [mesh_gdp]) mesh_gdp.clear() del mesh_gdp mesh_gdp = normals_gdp N_attrib = mesh_gdp.findPointAttrib("N") uv_attrib = mesh_gdp.findVertexAttrib("uv") if uv_attrib is None: uv_attrib = mesh_gdp.findPointAttrib("uv") S_attrib = mesh_gdp.findVertexAttrib("S") if S_attrib is None: S_attrib = mesh_gdp.findPointAttrib("S") faceIndices_attrib = mesh_gdp.findPrimAttrib("faceIndices") # TODO: If uv's don't exist, check for 'st', we'll assume uvs are a float[3] # in Houdini and st are a float[2], or we could just auto-convert as # needed. # We need to unique the points if any of the handles # to vtx attributes exists. for attrib in (N_attrib, uv_attrib, S_attrib): if attrib is None: continue if attrib.type() == hou.attribType.Vertex: unique_points = True break S = None uv = None N = None faceIndices = None if faceIndices_attrib is not None: faceIndices = array.array("i") faceIndices.fromstring( mesh_gdp.primIntAttribValuesAsString("faceIndices")) # We will unique points (verts in PBRT) if any of the attributes are # per vertex instead of per point. if unique_points: P = vtx_attrib_gen(mesh_gdp, P_attrib) indices = linear_vtx_gen(mesh_gdp) if N_attrib is not None: N = vtx_attrib_gen(mesh_gdp, N_attrib) if uv_attrib is not None: uv = vtx_attrib_gen(mesh_gdp, uv_attrib) if S_attrib is not None: S = vtx_attrib_gen(mesh_gdp, S_attrib) else: # NOTE: We are using arrays here for very fast access since we can # fetch all the values at once compactly, while faster, this # will take more RAM than a generator approach. If this becomes # and issue we can change it. P = array.array("f") P.fromstring(mesh_gdp.pointFloatAttribValuesAsString("P")) indices = vtx_attrib_gen(mesh_gdp, None) if N_attrib is not None: N = array.array("f") N.fromstring(mesh_gdp.pointFloatAttribValuesAsString("N")) if S_attrib is not None: S = array.array("f") S.fromstring(mesh_gdp.pointFloatAttribValuesAsString("S")) if uv_attrib is not None: uv = pt_attrib_gen(mesh_gdp, uv_attrib) mesh_paramset.add(PBRTParam("integer", "indices", indices)) mesh_paramset.add(PBRTParam("point", "P", P)) if N is not None: mesh_paramset.add(PBRTParam("normal", "N", N)) if S is not None: mesh_paramset.add(PBRTParam("vector", "S", S)) if faceIndices is not None: mesh_paramset.add(PBRTParam("integer", "faceIndices", faceIndices)) if uv is not None: # Houdini's uvs are stored as 3 floats, but pbrt only needs two # We'll use a generator comprehension to strip off the extra # float. uv2 = (x[0:2] for x in uv) mesh_paramset.add(PBRTParam("float", "uv", uv2)) return mesh_paramset
def wrangle_light(light, wrangler, now): # NOTE: Lights do not support motion blur so we disable it when # outputs the xforms node = wrangle_node_parm(light, "light_node", now) parm_selection = { "light_wrangler": SohoPBRT("light_wrangler", "string", [""], False), "light_color": SohoPBRT("light_color", "float", [1, 1, 1], False), "light_intensity": SohoPBRT("light_intensity", "float", [1], False), "light_exposure": SohoPBRT("light_exposure", "float", [0], False), } parms = light.evaluate(parm_selection, now) light_wrangler = parms["light_wrangler"].Value[0] paramset = ParamSet() paramset.add(_to_light_scale(parms)) if light_wrangler == "HoudiniEnvLight": env_map = [] paramset.add(PBRTParam("rgb", "L", parms["light_color"].Value)) if light.evalString("env_map", now, env_map): paramset.add(PBRTParam("string", "mapname", env_map)) output_xform(light, now, no_motionblur=True) api.Scale(1, 1, -1) api.Rotate(90, 0, 0, 1) api.Rotate(90, 0, 1, 0) _light_api_wrapper("infinite", paramset, node) return elif light_wrangler != "HoudiniLight": api.Comment("This light type, %s, is unsupported" % light_wrangler) return # We are dealing with a standard HoudiniLight type. light_type = light.wrangleString(wrangler, "light_type", now, ["point"])[0] if light_type in ("sphere", "disk", "grid", "tube", "geo"): single_sided = light.wrangleInt(wrangler, "singlesided", now, [0])[0] visible = light.wrangleInt(wrangler, "light_contribprimary", now, [0])[0] size = light.wrangleFloat(wrangler, "areasize", now, [1, 1]) paramset.add(PBRTParam("rgb", "L", parms["light_color"].Value)) paramset.add(PBRTParam("bool", "twosided", [not single_sided])) # TODO, Possibly get the xform's scale and scale the geo, not the light. # (for example, further multiplying down the radius) xform = get_transform(light, now) xform_to_api_srt(xform, scale=False) _light_api_wrapper("diffuse", paramset, node) api.AttributeBegin() # PBRT only supports uniform scales for non-mesh area lights # this is in part due to explicit light's area scaling factor. if light_type in ("grid", "geo"): api.Scale(size[0], size[1], size[0]) # The visibility only applies to hits on the non-emissive side of the light. # the emissive side will still be rendered if not visible: api.Material("none") if light_type == "sphere": # We apply the scale to the radius instead of using a api.Scale api.Shape("sphere", [PBRTParam("float", "radius", 0.5 * size[0])]) elif light_type == "tube": api.Rotate(90, 0, 1, 0) api.Shape( "cylinder", [ PBRTParam("float", "radius", 0.075 * size[1]), PBRTParam("float", "zmin", -0.5 * size[0]), PBRTParam("float", "zmax", 0.5 * size[0]), ], ) elif light_type == "disk": # After pbrt-v3 commit #2f0852ce api.ReverseOrientation() is needed, # prior that it was a api.Scale(1,1,-1) # (see issue #183 in pbrt-v3) api.ReverseOrientation() api.Shape("disk", [PBRTParam("float", "radius", 0.5 * size[0])]) elif light_type == "grid": api.Shape( "trianglemesh", [ PBRTParam("integer", "indices", [0, 3, 1, 0, 2, 3]), PBRTParam( "point", "P", [-0.5, -0.5, 0, 0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, 0.5, 0], ), ], ) elif light_type == "geo": areageo_parm = hou.node(light.getName()).parm("areageometry") if not areageo_parm: api.Comment('No "areageometry" parm on light') return area_geo_node = areageo_parm.evalAsNode() if not area_geo_node: api.Comment("Skipping, no geometry object specified") return obj = soho.getObject(area_geo_node.path()) api.Comment("Light geo from %s" % obj.getName()) # TODO: The area light scale ('areasize') happens *after* the wrangle_obj's # xform when 'intothisobject' is enabled. into_this_obj = light.wrangleInt(wrangler, "intothisobject", now, [0])[0] ignore_xform = not into_this_obj wrangle_obj(obj, None, now, ignore_xform=ignore_xform) api.AttributeEnd() return cone_enable = light.wrangleInt(wrangler, "coneenable", now, [0])[0] projmap = light.wrangleString(wrangler, "projmap", now, [""])[0] areamap = light.wrangleString(wrangler, "areamap", now, [""])[0] api_calls = [] api_calls.append(_apiclosure(output_xform, light, now, no_motionblur=True)) api_calls.append(_apiclosure(api.Scale, 1, 1, -1)) api_calls.append(_apiclosure(api.Scale, 1, -1, 1)) if light_type == "point": paramset.add(PBRTParam("rgb", "I", parms["light_color"].Value)) if areamap: light_name = "goniometric" paramset.add(PBRTParam("string", "mapname", [areamap])) api_calls = [] api_calls.append(_apiclosure(output_xform, light, now, no_motionblur=True)) api_calls.append(_apiclosure(api.Scale, 1, -1, 1)) api_calls.append(_apiclosure(api.Rotate, 90, 0, 1, 0)) elif not cone_enable: light_name = "point" else: conedelta = light.wrangleFloat(wrangler, "conedelta", now, [10])[0] coneangle = light.wrangleFloat(wrangler, "coneangle", now, [45])[0] if projmap: light_name = "projection" paramset.add(PBRTParam("float", "fov", [coneangle])) paramset.add(PBRTParam("string", "mapname", [projmap])) else: light_name = "spot" coneangle *= 0.5 coneangle += conedelta paramset.add(PBRTParam("float", "coneangle", [coneangle])) paramset.add(PBRTParam("float", "conedeltaangle", [conedelta])) elif light_type == "distant": light_name = light_type paramset.add(PBRTParam("rgb", "L", parms["light_color"].Value)) else: api.Comment("Light Type, %s, not supported" % light_type) return for api_call in api_calls: api_call() _light_api_wrapper(light_name, paramset, node) return
def wrangle_camera(obj, wrangler, now): node = wrangle_node_parm(obj, "camera_node", now) if node is not None: output_cam_xform(obj, node.directive_type, now) return node.type_and_paramset paramset = ParamSet() window = obj.getCameraScreenWindow(wrangler, now) parm_selection = { "projection": SohoPBRT("projection", "string", ["perspective"], False), "focal": SohoPBRT("focal", "float", [50], False), "focalunits": SohoPBRT("focalunits", "string", ["mm"], False), "aperture": SohoPBRT("aperture", "float", [41.4214], False), "orthowidth": SohoPBRT("orthowidth", "float", [2], False), "res": SohoPBRT("res", "integer", [1280, 720], False), "aspect": SohoPBRT("aspect", "float", [1], False), "fstop": SohoPBRT("fstop", "float", [5.6], False), "focaldistance": SohoPBRT("focus", "float", [5], False, key="focaldistance"), "pbrt_dof": SohoPBRT("pbrt_dof", "integer", [0], False), } parms = obj.evaluate(parm_selection, now) aspect = parms["aspect"].Value[0] aspectfix = aspect * float(parms["res"].Value[0]) / float(parms["res"].Value[1]) projection = parms["projection"].Value[0] if parms["pbrt_dof"].Value[0]: paramset.add(parms["focaldistance"].to_pbrt()) # to convert from f-stop to lens radius # FStop = FocalLength / (Radius * 2) # Radius = FocalLength/(FStop * 2) focal = parms["focal"].Value[0] fstop = parms["fstop"].Value[0] units = parms["focalunits"].Value[0] focal = soho.houdiniUnitLength(focal, units) lens_radius = focal / (fstop * 2.0) paramset.add(PBRTParam("float", "lensradius", lens_radius)) if projection == "perspective": projection_name = "perspective" focal = parms["focal"].Value[0] aperture = parms["aperture"].Value[0] fov = 2.0 * focal / aperture fov = 2.0 * math.degrees(math.atan2(1.0, fov)) paramset.add(PBRTParam("float", "fov", [fov])) screen = [ (window[0] - 0.5) * 2.0, (window[1] - 0.5) * 2.0, (window[2] - 0.5) * 2.0 / aspectfix, (window[3] - 0.5) * 2.0 / aspectfix, ] paramset.add(PBRTParam("float", "screenwindow", screen)) elif projection == "ortho": projection_name = "orthographic" width = parms["orthowidth"].Value[0] screen = [ (window[0] - 0.5) * width, (window[1] - 0.5) * width, (window[2] - 0.5) * width / aspectfix, (window[3] - 0.5) * width / aspectfix, ] paramset.add(PBRTParam("float", "screenwindow", screen)) elif projection == "sphere": projection_name = "environment" else: soho.error("Camera projection setting of %s not supported by PBRT" % projection) output_cam_xform(obj, projection_name, now) return (projection_name, paramset)
def bounds_to_api_box(b): """Output a trianglemesh Shape of box based on the input bounds""" paramset = ParamSet() paramset.add( PBRTParam( "point", "P", [ b[1], b[2], b[5], b[0], b[2], b[5], b[1], b[3], b[5], b[0], b[3], b[5], b[0], b[2], b[4], b[1], b[2], b[4], b[0], b[3], b[4], b[1], b[3], b[4], ], )) paramset.add( PBRTParam( "integer", "indices", [ 0, 3, 1, 0, 2, 3, 4, 7, 5, 4, 6, 7, 6, 2, 7, 6, 3, 2, 5, 1, 4, 5, 0, 1, 5, 2, 0, 5, 7, 2, 1, 6, 4, 1, 3, 6, ], )) api.Shape("trianglemesh", paramset)
def wrangle_integrator(obj, wrangler, now): node = wrangle_node_parm(obj, "integrator_node", now) if node is not None: return node.type_and_paramset parm_selection = { "integrator": SohoPBRT("integrator", "string", ["path"], False), "maxdepth": SohoPBRT("maxdepth", "integer", [5], False), "rrthreshold": SohoPBRT("rrthreshold", "float", [1], True), "lightsamplestrategy": SohoPBRT( "lightsamplestrategy", "string", ["spatial"], True ), "visualizestrategies": SohoPBRT("visualizestrategies", "toggle", [False], True), "visualizeweights": SohoPBRT("visualizeweights", "toggle", [False], True), "iterations": SohoPBRT("iterations", "integer", [64], True), "photonsperiterations": SohoPBRT("photonsperiterations", "integer", [-1], True), "imagewritefrequency": SohoPBRT( "imagewritefrequency", "integer", [2.14748e09], True ), "radius": SohoPBRT("radius", "float", [1], True), "bootstrapsamples": SohoPBRT("bootstrapsamples", "integer", [100000], True), "chains": SohoPBRT("chains", "integer", [1000], True), "mutationsperpixel": SohoPBRT("mutataionsperpixel", "integer", [100], True), "largestepprobability": SohoPBRT("largestepprobability", "float", [0.3], True), "sigma": SohoPBRT("sigma", "float", [0.01], True), "strategy": SohoPBRT("strategy", "string", ["all"], True), "nsamples": SohoPBRT("nsamples", "integer", ["64"], True), "cossample": SohoPBRT("cossample", "toggle", [True], True), } integrator_parms = { "ao": ["nsamples", "cossample"], "path": ["maxdepth", "rrthreshold", "lightsamplestrategy"], "bdpt": [ "maxdepth", "rrthreshold", "lightsamplestrategy", "visualizestrategies", "visualizeweights", ], "mlt": [ "maxdepth", "bootstrapsamples", "chains", "mutationsperpixel", "largestepprobability", "sigma", ], "sppm": [ "maxdepth", "iterations", "photonsperiteration", "imagewritefrequency", "radius", ], "whitted": ["maxdepth"], "volpath": ["maxdepth", "rrthreshold", "lightsamplestrategy"], "directlighting": ["maxdepth", "strategy"], } parms = obj.evaluate(parm_selection, now) integrator_name = parms["integrator"].Value[0] paramset = ParamSet() for parm_name in integrator_parms[integrator_name]: if parm_name not in parms: continue paramset.add(parms[parm_name].to_pbrt()) return (integrator_name, paramset)
def smoke_prim_wrangler(prims, paramset=None, properties=None, override_node=None): """Outputs a "heterogeneous" Medium and bounding Shape for the input geometry The following attributes are checked for via medium_prim_paramset() - (See pbrt_medium node for what each parm does) pbrt_interior (prim), string preset (prim), string g (prim), float scale (prim), float[3] sigma_a (prim), float[3] sigma_s (prim), float[3] Args: prims (list of hou.Prims): Input prims paramset (ParamSet): Any base params to add to the shape. (Optional) properties (dict): Dictionary of SohoParms (Optional) Returns: None """ # NOTE: Overlapping heterogeneous volumes don't currently # appear to be supported, although this may be an issue # with the Medium interface order? Visually it appears one # object is blocking the other. # NOTE: Not all samplers support heterogeneous volumes. Determine which # ones do, (and verify this is accurate). if properties is None: properties = {} if "pbrt_ignorevolumes" in properties and properties[ "pbrt_ignorevolumes"].Value[0]: api.Comment("Ignoring volumes because pbrt_ignorevolumes is enabled") return medium_paramset = ParamSet() if "pbrt_interior" in properties: interior = BaseNode.from_node(properties["pbrt_interior"].Value[0]) if interior is not None and interior.directive_type == "pbrt_medium": medium_paramset |= interior.paramset # These are special overrides that come from full point instancing. # It allows "per point" medium values to be "stamped" out to volume prims. interior_paramset = properties.get(".interior_overrides") if interior_paramset is not None: medium_paramset.update(interior_paramset) medium_suffix = "" instance_info = properties.get(".instance_info") if instance_info is not None: medium_suffix = ":%s[%i]" % (instance_info.source, instance_info.number) exterior = None if "pbrt_exterior" in properties: exterior = properties["pbrt_exterior"].Value[0] exterior = "" if exterior is None else exterior for prim in prims: smoke_paramset = ParamSet() medium_name = "%s[%i]%s" % ( properties["object:soppath"].Value[0], prim.number(), medium_suffix, ) resolution = prim.resolution() # TODO: Benchmark this vs other methods like fetching volumeSlices voxeldata = array.array("f") voxeldata.fromstring(prim.allVoxelsAsString()) smoke_paramset.add(PBRTParam("integer", "nx", resolution[0])) smoke_paramset.add(PBRTParam("integer", "ny", resolution[1])) smoke_paramset.add(PBRTParam("integer", "nz", resolution[2])) smoke_paramset.add(PBRTParam("point", "p0", [-1, -1, -1])) smoke_paramset.add(PBRTParam("point", "p1", [1, 1, 1])) smoke_paramset.add(PBRTParam("float", "density", voxeldata)) medium_prim_overrides = medium_prim_paramset(prim, medium_paramset) smoke_paramset.update(medium_prim_overrides) smoke_paramset |= paramset # By default we'll set a sigma_a and sigma_s to be more Houdini-like # however the object's pbrt_interior, or prim's pbrt_interior # or prim attribs will override these. if (PBRTParam("color", "sigma_a") not in smoke_paramset and PBRTParam( "color", "sigma_s") not in smoke_paramset) and PBRTParam( "string", "preset") not in smoke_paramset: smoke_paramset.add(PBRTParam("color", "sigma_a", [1, 1, 1])) smoke_paramset.add(PBRTParam("color", "sigma_s", [1, 1, 1])) with api.AttributeBlock(): xform = prim_transform(prim) api.ConcatTransform(xform) api.MakeNamedMedium(medium_name, "heterogeneous", smoke_paramset) api.Material("none") api.MediumInterface(medium_name, exterior) # Pad this slightly? bounds_to_api_box([-1, 1, -1, 1, -1, 1]) return
def wrangle_camera(obj, wrangler, now): node = wrangle_node_parm(obj, 'camera_node', now) if node is not None: output_cam_xform(obj, node.directive_type, now) return node.type_and_paramset paramset = ParamSet() window = obj.getCameraScreenWindow(wrangler, now) parm_selection = { 'projection' : SohoPBRT('projection', 'string', ['perspective'], False), 'focal' : SohoPBRT('focal', 'float', [50], False), 'focalunits' : SohoPBRT('focalunits', 'string', ['mm'], False), 'aperture' : SohoPBRT('aperture', 'float', [41.4214], False), 'orthowidth' : SohoPBRT('orthowidth', 'float', [2], False), 'res' : SohoPBRT('res', 'integer', [1280, 720], False), 'aspect' : SohoPBRT('aspect', 'float', [1], False), 'fstop' : SohoPBRT('fstop', 'float', [5.6], False), 'focaldistance' : SohoPBRT('focus', 'float', [5], False, key='focaldistance'), 'pbrt_dof' : SohoPBRT('pbrt_dof', 'integer', [0], False), } parms = obj.evaluate(parm_selection, now) aspect = parms['aspect'].Value[0] aspectfix = aspect * float(parms['res'].Value[0]) / float(parms['res'].Value[1]) projection = parms['projection'].Value[0] if parms['pbrt_dof'].Value[0]: paramset.add(parms['focaldistance'].to_pbrt()) # to convert from f-stop to lens radius # FStop = FocalLength / (Radius * 2) # Radius = FocalLength/(FStop * 2) focal = parms['focal'].Value[0] fstop = parms['fstop'].Value[0] units = parms['focalunits'].Value[0] focal = soho.houdiniUnitLength(focal, units) lens_radius = focal/(fstop*2.0) paramset.add(PBRTParam('float', 'lensradius', lens_radius)) if projection == 'perspective': projection_name = 'perspective' focal = parms['focal'].Value[0] aperture = parms['aperture'].Value[0] fov = 2.0 * focal / aperture fov = 2.0 * math.degrees(math.atan2(1.0, fov)) paramset.add(PBRTParam('float', 'fov', [fov])) screen = [(window[0] - 0.5) * 2.0, (window[1] - 0.5) * 2.0, (window[2] - 0.5) * 2.0 / aspectfix, (window[3] - 0.5) * 2.0 / aspectfix] paramset.add(PBRTParam('float', 'screenwindow', screen)) elif projection == 'ortho': projection_name = 'orthographic' width = parms['orthowidth'].Value[0] screen = [(window[0] - 0.5) * width, (window[1] - 0.5) * width, (window[2] - 0.5) * width / aspectfix, (window[3] - 0.5) * width / aspectfix] paramset.add(PBRTParam('float', 'screenwindow', screen)) elif projection == 'sphere': projection_name = 'environment' else: soho.error('Camera projection setting of %s not supported by PBRT' % projection) output_cam_xform(obj, projection_name, now) return (projection_name, paramset)
def wrangle_light(light, wrangler, now): # NOTE: Lights do not support motion blur so we disable it when # outputs the xforms node = wrangle_node_parm(light, 'light_node', now) parm_selection = { 'light_wrangler' : SohoPBRT('light_wrangler', 'string', [''], False), 'light_color' : SohoPBRT('light_color', 'float', [1, 1, 1], False), 'light_intensity' : SohoPBRT('light_intensity', 'float', [1], False), 'light_exposure' : SohoPBRT('light_exposure', 'float', [0], False), } parms = light.evaluate(parm_selection, now) light_wrangler = parms['light_wrangler'].Value[0] paramset = ParamSet() paramset.add(_to_light_scale(parms)) if light_wrangler == 'HoudiniEnvLight': env_map = [] paramset.add(PBRTParam('rgb', 'L', parms['light_color'].Value)) if light.evalString('env_map', now, env_map): paramset.add(PBRTParam('string', 'mapname', env_map)) output_xform(light, now, no_motionblur=True) api.Scale(1, 1, -1) api.Rotate(90, 0, 0, 1) api.Rotate(90, 0, 1, 0) _light_api_wrapper('infinite', paramset, node) return elif light_wrangler != 'HoudiniLight': api.Comment('This light type, %s, is unsupported' % light_wrangler) return # We are dealing with a standard HoudiniLight type. light_type = light.wrangleString(wrangler, 'light_type', now, ['point'])[0] if light_type in ('sphere', 'disk', 'grid', 'tube', 'geo'): single_sided = light.wrangleInt(wrangler, 'singlesided', now, [0])[0] visible = light.wrangleInt(wrangler, 'light_contribprimary', now, [0])[0] size = light.wrangleFloat(wrangler, 'areasize', now, [1, 1]) paramset.add(PBRTParam('rgb', 'L', parms['light_color'].Value)) paramset.add(PBRTParam('bool', 'twosided', [not single_sided])) # TODO, Possibly get the xform's scale and scale the geo, not the light. # (for example, further multiplying down the radius) xform = get_transform(light, now) xform_to_api_srt(xform, scale=False) _light_api_wrapper('diffuse', paramset, node) api.AttributeBegin() # PBRT only supports uniform scales for non-mesh area lights # this is in part due to explicit light's area scaling factor. if light_type in ('grid', 'geo'): api.Scale(size[0], size[1], size[0]) # The visibility only applies to hits on the non-emissive side of the light. # the emissive side will still be rendered if not visible: api.Material('none') if light_type == 'sphere': # We apply the scale to the radius instead of using a api.Scale api.Shape('sphere', [PBRTParam('float', 'radius', 0.5*size[0])]) elif light_type == 'tube': api.Rotate(90, 0, 1, 0) api.Shape('cylinder', [PBRTParam('float', 'radius', 0.075*size[1]), PBRTParam('float', 'zmin', -0.5*size[0]), PBRTParam('float', 'zmax', 0.5*size[0])]) elif light_type == 'disk': # A bug was introduced with Issue #154 which requires a -z scale # on disk area lights # See issue #183 # api.Scale(1,1,-1) api.Shape('disk', [PBRTParam('float', 'radius', 0.5*size[0])]) elif light_type == 'grid': api.Shape('trianglemesh', [PBRTParam('integer', 'indices', [0, 3, 1, 0, 2, 3]), PBRTParam('point', 'P', [-0.5, -0.5, 0, 0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, 0.5, 0])]) elif light_type == 'geo': areageo_parm = hou.node(light.getName()).parm('areageometry') if not areageo_parm: api.Comment('No "areageometry" parm on light') return area_geo_node = areageo_parm.evalAsNode() if not area_geo_node: api.Comment('Skipping, no geometry object specified') return obj = soho.getObject(area_geo_node.path()) api.Comment('Light geo from %s' % obj.getName()) # TODO: The area light scale ('areasize') happens *after* the wrangle_obj's xform # when 'intothisobject' is enabled. into_this_obj = light.wrangleInt(wrangler, 'intothisobject', now, [0])[0] ignore_xform = not into_this_obj wrangle_obj(obj, None, now, ignore_xform=ignore_xform) api.AttributeEnd() return cone_enable = light.wrangleInt(wrangler, 'coneenable', now, [0])[0] projmap = light.wrangleString(wrangler, 'projmap', now, [''])[0] areamap = light.wrangleString(wrangler, 'areamap', now, [''])[0] api_calls = [] api_calls.append(_apiclosure(output_xform, light, now, no_motionblur=True)) api_calls.append(_apiclosure(api.Scale, 1, 1, -1)) api_calls.append(_apiclosure(api.Scale, 1, -1, 1)) if light_type == 'point': paramset.add(PBRTParam('rgb', 'I', parms['light_color'].Value)) if areamap: light_name = 'goniometric' paramset.add(PBRTParam('string', 'mapname', [areamap])) api_calls = [] api_calls.append(_apiclosure(output_xform, light, now, no_motionblur=True)) api_calls.append(_apiclosure(api.Scale, 1, -1, 1)) api_calls.append(_apiclosure(api.Rotate, 90, 0, 1, 0)) elif not cone_enable: light_name = 'point' else: conedelta = light.wrangleFloat(wrangler, 'conedelta', now, [10])[0] coneangle = light.wrangleFloat(wrangler, 'coneangle', now, [45])[0] if projmap: light_name = 'projection' paramset.add(PBRTParam('float', 'fov', [coneangle])) paramset.add(PBRTParam('string', 'mapname', [projmap])) else: light_name = 'spot' coneangle *= 0.5 coneangle += conedelta paramset.add(PBRTParam('float', 'coneangle', [coneangle])) paramset.add(PBRTParam('float', 'conedeltaangle', [conedelta])) elif light_type == 'distant': light_name = light_type paramset.add(PBRTParam('rgb', 'L', parms['light_color'].Value)) else: api.Comment('Light Type, %s, not supported' % light_type) return for api_call in api_calls: api_call() _light_api_wrapper(light_name, paramset, node) return
def curve_wrangler(gdp, paramset=None, properties=None, override_node=None): """Outputs a "curve" Shape for input geometry The following attributes are checked for - P (point), built-in attribute width (vertex/point/prim), float N (vertex/point), float[3] curvetype (prim), string (overrides the property pbrt_curvetype) Args: gdp (hou.Geometry): Input geo paramset (ParamSet): Any base params to add to the shape. (Optional) properties (dict): Dictionary of SohoParms (Optional) Returns: None """ if properties is None: properties = {} shape_paramset = ParamSet(paramset) curve_type = None if "pbrt_curvetype" in properties: curve_type = properties["pbrt_curvetype"].Value[0] shape_paramset.add(PBRTParam("string", "type", curve_type)) if "splitdepth" in properties: shape_paramset.add(properties["splitdepth"].to_pbrt()) gdp = _convert_nurbs_to_bezier(gdp) has_vtx_width = False if gdp.findVertexAttrib("width") is None else True has_pt_width = False if gdp.findPointAttrib("width") is None else True has_prim_width = False if gdp.findPrimAttrib("width") is None else True has_prim_width01 = False if (gdp.findPrimAttrib("width0") is not None and gdp.findPrimAttrib("width1") is not None): has_prim_width01 = True has_curvetype = False if gdp.findPrimAttrib("curvetype") is None else True has_vtx_N = False if gdp.findVertexAttrib("N") is None else True has_pt_N = False if gdp.findPointAttrib("N") is None else True for prim in gdp.prims(): curve_paramset = ParamSet() prim_curve_type = curve_type # Closed curve surfaces are not supported if prim.intrinsicValue("closed"): continue order = prim.intrinsicValue("order") degree = order - 1 # PBRT only supports degree 2 or 3 curves # TODO: We could possibly convert the curves to a format that # pbrt supports but for now we'll expect the user to have # a curve basis which is supported # https://www.codeproject.com/Articles/996281/NURBS-crve-made-easy if degree not in (2, 3): continue curve_paramset.add(PBRTParam("integer", "degree", degree)) if prim.intrinsicValue("typename") == "BezierCurve": basis = "bezier" else: # We should not see these as they are being converted to BezierCurves basis = "bspline" curve_paramset.add(PBRTParam("string", "basis", [basis])) # SPEED consideration, run inline: # P = prim_pt2vtx_attrib_gen(prim) P = [pt.attribValue("P") for pt in prim.points()] curve_paramset.add(PBRTParam("point", "P", P)) if has_curvetype: prim_val = prim.attribValue("curvetype") prim_curve_type = prim_val if prim_val else curve_type if prim_curve_type is not None: curve_paramset.add(PBRTParam("string", "type", [prim_curve_type])) if prim_curve_type == "ribbon": if has_vtx_N or has_pt_N: N = (prim.attribValueAt("N", u) for u in prim.intrinsicValue("knots")) else: # If ribbon, normals must exist # TODO: Let pbrt error? Or put default values? N = [(0, 0, 1)] * len(prim.intrinsicValue("knots")) if N is not None: curve_paramset.add(PBRTParam("normal", "N", N)) if has_vtx_width: curve_paramset.add( PBRTParam("float", "width0", prim.vertex(0).attribValue("width"))) curve_paramset.add( PBRTParam("float", "width1", prim.vertex(-1).attribValue("width"))) elif has_pt_width: curve_paramset.add( PBRTParam("float", "width0", prim.vertex(0).point().attribValue("width"))) curve_paramset.add( PBRTParam("float", "width1", prim.vertex(-1).point().attribValue("width"))) elif has_prim_width01: curve_paramset.add( PBRTParam("float", "width0", prim.attribValue("width0"))) curve_paramset.add( PBRTParam("float", "width1", prim.attribValue("width1"))) elif has_prim_width: curve_paramset.add( PBRTParam("float", "width", prim.attribValue("width"))) else: # Houdini's default matches a width of 0.05 curve_paramset.add(PBRTParam("float", "width", 0.05)) curve_paramset |= shape_paramset curve_paramset |= prim_override(prim, override_node) api.Shape("curve", curve_paramset) return