Exemplo n.º 1
0
def packeddisk_wrangler(gdp,
                        paramset=None,
                        properties=None,
                        override_node=None):
    """Outputs "ply" 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
    """
    alpha_paramset = mesh_alpha_texs(properties)
    for prim in gdp.prims():
        shape_paramset = ParamSet(paramset)
        shape_paramset |= prim_override(prim, override_node)
        filename = prim.intrinsicValue("filename")
        if not filename:
            continue
        if os.path.splitext(filename)[1].lower() != ".ply":
            continue
        shape_paramset.replace(PBRTParam("string", "filename", filename))
        shape_paramset.update(alpha_paramset)
        with api.TransformBlock():
            xform = prim_transform(prim)
            api.ConcatTransform(xform)
            api.Shape("plymesh", shape_paramset)
    return
Exemplo n.º 2
0
def mesh_wrangler(gdp, paramset=None, properties=None, override_node=None):
    """Outputs meshes (trianglemesh or loopsubdiv) depending on properties

    If the pbrt_rendersubd property is set and true, a loopsubdiv shape will
    be generated, otherwise a trianglemesh

    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 = {}

    mesh_paramset = ParamSet(paramset)

    # Triangle Meshes in PBRT uses "vertices" to denote positions.
    # These are similar to Houdini "points". Since the PBRT verts
    # are shared between primitives if hard edges or "vertex normals"
    # (Houdini-ese) are required then need to unique the points so
    # so each point can have its own normal.
    # To support this, if any of the triangle mesh params (N, uv, S)
    # are vertex attributes, then we'll uniquify the points.

    # We can only deal with triangles, where Houdini is a bit more
    # general, so we'll need to tesselate

    mesh_gdp = scene_state.tesselate_geo(gdp)
    gdp.clear()

    if not mesh_gdp:
        return None

    # If subdivs are turned on, instead of running the
    # trianglemesh wrangler, use the loop subdiv one instead
    shape = "trianglemesh"
    if "pbrt_rendersubd" in properties:
        if properties["pbrt_rendersubd"].Value[0]:
            shape = "loopsubdiv"

    if shape == "loopsubdiv":
        wrangler_paramset = loopsubdiv_params(mesh_gdp)
        if "levels" in properties:
            wrangler_paramset.replace(properties["levels"].to_pbrt())
    else:
        computeN = True
        if "pbrt_computeN" in properties:
            computeN = properties["pbrt_computeN"].Value[0]
        wrangler_paramset = trianglemesh_params(mesh_gdp, computeN)
        alpha_paramset = mesh_alpha_texs(properties)
        wrangler_paramset.update(alpha_paramset)

    mesh_paramset.update(wrangler_paramset)

    api.Shape(shape, mesh_paramset)

    return None
Exemplo n.º 3
0
def process_full_pt_instance_medium(instance_info, medium_type):
    gdp = instance_info.gdp

    if medium_type not in ("interior", "exterior"):
        return None, None

    medium_attrib_h = gdp.attribute("geo:point", "pbrt_" + medium_type)
    if medium_attrib_h < 0:
        return None, None

    medium = gdp.value(medium_attrib_h, instance_info.number)[0]

    # an empty string is valid here as it means no medium
    if medium == "":
        return medium, ParamSet()

    suffix = ":%s:%i" % (instance_info.source, instance_info.number)
    medium_node = BaseNode.from_node(medium)
    medium_node.path_suffix = suffix

    if not (medium_node and medium_node.directive == "medium"):
        return None, None

    medium_paramset = ParamSet(medium_node.paramset)

    # Look for attributes to create an override dictionary
    pt_attribs = gdp.globalValue("geo:pointattribs")
    overrides = {}
    for attrib in pt_attribs:
        if not attrib.startswith("{}_".format(medium_type)):
            continue
        if attrib in ("pbrt_interior", "pbrt_exterior"):
            continue
        parm_name = attrib.split("_", 1)[1]
        attrib_h = gdp.attribute("geo:point", attrib)
        if attrib_h < 0:
            continue
        val = gdp.value(attrib_h, instance_info.number)
        overrides[parm_name] = val
    if overrides:
        medium_paramset.update(medium_node.override_paramset(overrides))

    # We might be outputing a named medium even if its not going to be needed
    # as in the case of instancing volume prims
    api.MakeNamedMedium(
        medium_node.full_name, medium_node.directive_type, medium_paramset
    )

    return medium_node.full_name, medium_paramset
Exemplo n.º 4
0
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
Exemplo n.º 5
0
def smoke_prim_wrangler(prims, paramset=None, properties=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

    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()

        name = '%s[%i]' % (properties['object:soppath'].Value[0],
                           prim.number())
        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(name, 'heterogeneous', smoke_paramset)
            api.Material('none')
            api.MediumInterface(name, exterior)
            # Pad this slightly?
            bounds_to_api_box([-1, 1, -1, 1, -1, 1])
    return
Exemplo n.º 6
0
def output_geo(soppath, now, properties=None):
    """Output the geometry by calling the appropriate wrangler

    Geometry is partitioned into subparts based on the shop_materialpath
    and material_override prim attributes.

    Args:
        soppath (str): oppath to SOP
        properties (dict, None): Dictionary of SohoParms
                                 (Optional, defaults to None)
    Returns:
        None
    """
    # split by material
    # split by material override #
    # split by geo type
    # NOTE: We won't be splitting based on medium interior/exterior
    #       those will be left as a object level assignment only.
    #       Note, that in the case of Houdini Volumes they will look
    #       for the appropriate medium parameters as prim vars

    if properties is None:
        properties = {}

    ignore_materials = False
    if 'pbrt_ignorematerials' in properties:
        ignore_materials = properties['pbrt_ignorematerials'].Value[0]

    # PBRT allows setting Material parameters on the Shapes in order to
    #       override a material's settings.  (Shapes get checked first)
    #       This paramset will be for holding those overrides and passing
    #       them down to the actual shape api calls.
    material_paramset = ParamSet()

    # We need the soppath to come along and since we are creating new
    # hou.Geometry() we'll lose the original sop connection so we need
    # to stash it here.

    node = hou.node(soppath)
    if node is None or node.type().category() != hou.sopNodeTypeCategory():
        return

    input_gdp = node.geometry()
    if input_gdp is None:
        return
    gdp = hou.Geometry()
    gdp.merge(input_gdp.freeze())

    # Partition based on materials
    global_material = None
    if not ignore_materials:
        try:
            global_material = gdp.stringAttribValue('shop_materialpath')
        except hou.OperationFailed:
            pass

    attrib_h = gdp.findPrimAttrib('shop_materialpath')
    if attrib_h is not None and not ignore_materials:
        material_gdps = partition_by_attrib(gdp, attrib_h)
    else:
        material_gdps = {global_material: gdp}

    global_override = None
    if not ignore_materials:
        try:
            global_override = gdp.stringAttribValue('material_override')
        except hou.OperationFailed:
            pass

    # Further partition based on material overrides
    has_prim_overrides = bool(
        not ignore_materials
        and gdp.findPrimAttrib('material_override') is not None)

    for material in material_gdps:
        if material:
            api.AttributeBegin()
            api.NamedMaterial(material)

        material_gdp = material_gdps[material]
        #api.Comment('%s %i' % (material_gdp,len(material_gdp.prims())))

        if has_prim_overrides:
            attrib_h = material_gdp.findPrimAttrib('material_override')
            override_gdps = partition_by_attrib(material_gdp, attrib_h)
            # Clean up post partition
            material_gdp.clear()
        else:
            override_gdps = {global_override: material_gdp}

        for override in override_gdps:
            override_gdp = override_gdps[override]
            #api.Comment(' %s %i' % (override_gdp, len(override_gdp.prims())))

            shape_gdps = partition_by_attrib(override_gdp,
                                             'typename',
                                             intrinsic=True)
            override_gdp.clear()

            for shape in shape_gdps:
                material_paramset = ParamSet()

                if override and material:
                    material_paramset.update(
                        override_to_paramset(material, override))

                shape_gdp = shape_gdps[shape]
                #api.Comment('  %s %i' % (shape_gdp, len(shape_gdp.prims())))

                shape_wrangler = shape_wranglers.get(shape, not_supported)
                if shape_wrangler:
                    shape_wrangler(shape_gdp, material_paramset, properties)
                shape_gdp.clear()

        if material:
            api.AttributeEnd()
    return
Exemplo n.º 7
0
def wrangle_shading_network(
    node_path,
    name_prefix="",
    name_suffix="",
    use_named=True,
    exported_nodes=None,
    overrides=None,
    node_cache=None,
    param_cache=None,
):

    if node_path in scene_state.invalid_shading_nodes:
        return

    # Depth first, as textures/materials need to be
    # defined before they are referenced

    # Use this to track if a node has been output or not.
    # if the exported_nodes is None, we use the global scene_state
    # otherwise we use the one passed in. This is useful for outputing
    # named materials within a nested Attribute Block.
    if exported_nodes is None:
        exported_nodes = scene_state.shading_nodes

    # NOTE: We prefix and suffix names here so that there are not collisions when
    #       using full point instancing. There is some possible redundancy as the same
    #       network maybe recreated multiple times under different names if the
    #       overrides are the same. A possible optimization for export and PBRT is to
    #       do a prepass and build the networks before and keep a map to the pre-built
    #       networks. For now we'll brute force it.
    presufed_node_path = name_prefix + node_path + name_suffix
    if presufed_node_path in exported_nodes:
        return

    if isinstance(node_cache, dict):
        if node_path not in node_cache:
            hnode = hou.node(node_path)
            node_cache[node_path] = BaseNode.from_node(hnode)
        node = node_cache[node_path]
    else:
        hnode = hou.node(node_path)
        node = BaseNode.from_node(hnode)

    if node is None:
        api.Comment("Skipping %s since its not a Material or Texture node" %
                    node_path)
        scene_state.invalid_shading_nodes.add(node_path)
        return
    else:
        exported_nodes.add(presufed_node_path)

    node.path_suffix = name_suffix
    node.path_prefix = name_prefix

    # Material or Texture?
    if node.directive == "material":
        api_call = api.MakeNamedMaterial if use_named else api.Material
    elif node.directive == "texture":
        api_call = api.Texture
    else:
        return

    if isinstance(param_cache, dict):
        if node_path not in param_cache:
            param_cache[node_path] = node.paramset
        paramset = ParamSet(param_cache[node_path])
        paramset.update(node.override_paramset(overrides))
    else:
        paramset = node.paramset_with_overrides(overrides)

    for node_input in node.inputs():
        wrangle_shading_network(
            node_input,
            name_prefix=name_prefix,
            name_suffix=name_suffix,
            use_named=use_named,
            exported_nodes=exported_nodes,
            overrides=overrides,
            param_cache=param_cache,
        )

    colorspace = node.colorspace
    if colorspace is not None:
        api.AttributeBegin()
        api.ColorSpace(colorspace)

    coord_sys = node.coord_sys
    if coord_sys:
        api.AttributeBegin()
        api.Transform(coord_sys)

    if api_call == api.Material:
        api_call(node.directive_type, paramset)
    else:
        api_call(node.full_name, node.output_type, node.directive_type,
                 paramset)

    if coord_sys:
        api.AttributeEnd()

    if colorspace is not None:
        api.AttributeEnd()

    if api_call == api.MakeNamedMaterial:
        print()

    return