예제 #1
0
    def create(self, dson_material, sg_node):
        self.material = pm.shadingNode('lambert', asShader=True)
        pm.rename(self.material,
                  'Mat_Viewport_%s' % mh.cleanup_node_name(self.name))
        self.material.attr('diffuse').set(1)

        material_type = self._get_dson_material_type(dson_material)

        self.set_attr_to_texture_with_color(self.material.attr('color'),
                                            self.images.get('diffuse'),
                                            self.channels['diffuse'],
                                            nodeName=self.name)

        if material_type == 'studio/material/uber_iray':
            # Refraction isn't really opacity, but we'll approximate it that way in the viewport.
            refraction_opacity = self.get_texture_with_alpha(
                self.images.get('Refraction Weight'),
                self.channels['Refraction Weight'],
                zero_is_invisible=True)

            transparency = self.get_texture_with_alpha(
                self.images.get('Cutout Opacity'),
                self.channels['Cutout Opacity'])

            # "Transparency" is a terrible way to represent opacity, because instead of just multiplying
            # values to combine them, you have to do 1-((1-t1)*(1-t2)).  That gives an ugly shader.
            # Cutout opacity and refraction are used for very different types of materials and I've never
            # seen them used together, so we cheat here and just add them.
            transparency = mh.math_op('add', transparency, refraction_opacity)
        else:
            transparency = self.get_texture_with_alpha(
                self.images.get('transparency'), self.channels['transparency'])

        # If transparency is constant, don't let it be 0.  Clamp it to 0.5, so it's not completely
        # invisible in the viewport.  In the real materials there are usually other things causing
        # it to be visible, like reflections or refraction.  Hack: don't do this for certain eye
        # materials, or it'll wash out eyes.
        allow_completely_transparent_shaders = [
            'EyeMoisture',
            'Cornea',
        ]
        allow_completely_transparent = any(
            s in str(dson_material)
            for s in allow_completely_transparent_shaders)
        if not isinstance(transparency,
                          pm.PyNode) and not allow_completely_transparent:
            transparency = min(transparency, 0.5)

        mh.set_or_connect(self.material.attr('transparency'), transparency)

        # Connect the material.  Force this connection, so if it's already connected to lambert1
        # we'll override it.
        self.material.attr('outColor').connect(sg_node.attr('surfaceShader'),
                                               f=True)
예제 #2
0
    def create(self, dson_material, sg_node):
        super(MaterialPlastic, self).create(dson_material, sg_node)

        material = pm.shadingNode('aiStandard', asShader=True)
        pm.rename(material, 'Mat_Arnold_%s' % mh.cleanup_node_name(self.name))

        # Don't apply transparency if there's no texture and transparency is 1 (transparency is really alpha).
        if self.channels['transparency'] < 1 or self.images.get('transparency'):
            self.set_attr_to_transparency(material.attr('opacity'), self.images.get('transparency'), self.channels['transparency'], zero_is_invisible=True)
            self.uses_transparency = True

        # Bump
        # Daz has a positive and negative bump value.  That's odd and I'm not sure if anyone actually uses this.
        # This isn't used for now.
        bump_negative = self.channels['bump_min']
        bump_positive = self.channels['bump_max']
        bump_scale = abs(bump_positive - bump_negative)
        if bump_scale > 0:
            self._set_normal_map(material, bump_texture=self.images.get('bump'), bump_scale=bump_scale * 0.5)

#        # reflectionStrength = self.channels['Reflection Strength']
#        # if reflectionStrength > 0:
#        #     pass
#
        # Specular (XXX untested)
        specular_strength = self.channels.get('specular_strength', 0)
        if specular_strength > 0:
            mh.set_or_connect(material.attr('Ks'), specular_strength)

            # We currently ignore any glossiness map.  You can set one, but Iray seems to ignore it.
            # self.set_attr_to_roughness_from_glossiness(self.images.get('glossiness'), self.channels['glossiness'], spec_layer.node.attr('roughness'))
            self.set_attr_to_roughness_from_glossiness(None, self.channels['glossiness'], material.attr('specularRoughness'))

            self.set_attr_to_texture_with_color(material.attr('KsColor'), self.images.get('specular'), self.channels['specular'])

        # Diffuse color.  We don't currently implement textures for diffuse strength.
        self.set_attr_to_texture_with_color(material.attr('color'), self.images.get('diffuse'), self.channels['diffuse'])
        self.set_attr_to_texture_with_color(material.attr('Kd'), self.images.get('diffuse_strength'), self.channels['diffuse_strength'], mode='r')

        # Unimplemented: ambient/ambient_strength, reflection/reflection_strength, refraction/refraction_strength/ior,
        # displacement/displacement_min/displacement_max, normal, u_offset/u_scale/v_offset/v_scale

        self._post_create(sg_node, material)
예제 #3
0
    def create(self, dson_material, sg_node):
        super(MaterialDazShader, self).create(dson_material, sg_node)

        material = pm.shadingNode('aiStandard', asShader=True)
        pm.rename(material, 'Mat_Arnold_%s' % mh.cleanup_node_name(self.name))

        diffuse_weight = 1

        # Bump:
        if self.channels['Bump Active']:
            # XXX: Normal maps?
            self._set_normal_map(material, bump_texture=self.images.get('Bump Strength'), bump_scale=self.channels.get('Bump Strength', 0) * 0.025)

        if self.channels['Opacity Active'] and (self.channels['transparency'] < 1 or self.images.get('transparency')):
            self.set_attr_to_transparency(material.attr('opacity'), self.images.get('transparency'), self.channels['transparency'], zero_is_invisible=True)
            self.uses_transparency = True

        # Specular (Primary)
        if self.channels['Specular Active']:
            self.set_attr_to_texture_with_color(material.attr('KsColor'), self.images.get('Specular Color'), self.channels['Specular Color'])
            self.set_attr_to_roughness_from_glossiness(self.images.get('Glossiness'), self.channels['Glossiness'], material.attr('specularRoughness'))

            specular_strength = self.channels['Specular Strength']
#            diffuse_weight = mh.math_op('sub', diffuse_weight, specular_strength)
            mh.set_or_connect(material.attr('Ks'), specular_strength)

        # Diffuse
        if self.channels['Diffuse Active'] and self.channels['Diffuse Strength'] > 0:
            self.set_attr_to_texture_with_color(material.attr('color'), self.images.get('diffuse'), self.channels['diffuse'])

            diffuse_weight = mh.math_op('mult', diffuse_weight, self.channels['Diffuse Strength'])
            mh.set_or_connect(material.attr('Kd'), diffuse_weight)

        self.set_attr_to_texture_with_color(material.attr('diffuseRoughness'), self.images.get('Diffuse Roughness'), self.channels['Diffuse Roughness'], mode='alpha')

        self._post_create(sg_node, material)
예제 #4
0
 def set_attr_to_roughness_from_glossiness(self, texture, value,
                                           output_attr):
     value = self.get_roughness_from_glossiness(texture, value)
     mh.set_or_connect(output_attr, value)
예제 #5
0
 def set_attr_to_transparency(self, output_attr, *args, **kwargs):
     texture_node = self.get_texture_with_alpha(*args, **kwargs)
     mh.set_or_connect(output_attr, texture_node)
예제 #6
0
 def set_attr_to_texture_with_color(self, output_attr, *args, **kwargs):
     texture_node = self.get_texture_with_color(*args, **kwargs)
     mh.set_or_connect(output_attr, texture_node)
예제 #7
0
    def create(self, dson_material, sg_node):
        super(MaterialDazBrick, self).create(dson_material, sg_node)

        material = pm.shadingNode('aiStandard', asShader=True)
        pm.rename(material, 'Mat_Arnold_%s' % mh.cleanup_node_name(self.name))

        # Don't apply transparency if there's no texture and transparency is 1 (transparency is really alpha).
        if self.channels['transparency'] < 1 or self.images.get('transparency'):
            self.set_attr_to_transparency(material.attr('opacity'), self.images.get('transparency'), self.channels['transparency'], zero_is_invisible=True)
            self.uses_transparency = True

        # Normals:
        #
        # This material has a positive and negative bump value.  That's odd and I'm not sure if anyone actually uses this.
        bump_negative = self.channels.get('Negative Bump', 0)
        bump_positive = self.channels.get('Positive Bump', 0)
        bump_scale = abs(bump_positive - bump_negative)
        bump_scale = mh.math_op('mult', bump_scale, self.channels['Bump Strength'])
        self._set_normal_map(material, normal_texture=self.images.get('Normal Map'), bump_texture=self.images.get('Bump Strength'), bump_scale=bump_scale*0.5)

#        # reflectionStrength = self.channels['Reflection Strength']
#        # if reflectionStrength > 0:
#        #     pass

        # Specular 1.
        specularStrength = self.channels.get('Specular Strength', 0)
        if specularStrength > 0:
            # XXX: calibrate
            material.attr('Ks').set(specularStrength * 0.075)

            # We currently ignore any glossiness map.  You can set one, but Iray seems to ignore it.
            # self.set_attr_to_roughness_from_glossiness(self.images.get('Glossiness'), self.channels['Glossiness'], material.attr('specularRoughness'))
            self.set_attr_to_roughness_from_glossiness(None, self.channels['Glossiness'], material.attr('specularRoughness'))

            self.set_attr_to_texture_with_color(material.attr('KsColor'), self.images.get('Specular Color'), self.channels['Specular Color'])
#
#        # Specular 2.  This is a phong specular.  This isn't very well tested.
#        specular2Strength = self.channels['value222']
#        if specular2Strength > 0:
#	    pass

        # This material allows Diffuse Strength to be greater than 1, but aiStandard doesn't,
        # so apply diffuse strength as part of the diffuse color instead.
        #diffuse_color = util.srgb_vector_to_linear(self.channels['diffuse'])
        #diffuse_color = mh.math_op('mult', diffuse_color, self.channels['Diffuse Strength'])
        #diffuse_texture_node = self.find_or_create_texture(path=self.images.get('diffuse'))
        #if diffuse_texture_node is not None:
        #    diffuse_color = mh.math_op('mult', diffuse_color, diffuse_texture_node.attr('outColor'))
        
        #mh.set_or_connect(material.attr('color'), diffuse_color)
        #mh.set_or_connect(material.attr('Kd'), 1)

        diffuse_color = self.get_texture_with_color(self.images.get('diffuse'), self.channels['diffuse'])
        mh.set_or_connect(material.attr('color'), diffuse_color)
        
        # Note that the diffuse strength isn't quite the same as a multiplier to the diffuse color,
        # since it's a layering weight and affected by other material layers.
        diffuse_strength = self.get_texture_with_color(self.images.get('Diffuse Strength'), self.channels['Diffuse Strength'], mode='alpha')
        if not isinstance(diffuse_strength, pm.PyNode):
            diffuse_strength = min(diffuse_strength, 1)
        mh.set_or_connect(material.attr('Kd'), diffuse_strength)

        # Subsurface
        #
        # "value23" is "Subsurface Off - On".
        # "Ambient Strength" is "Subsurface Strength" (huh?).
        # "Ambient Color" is "Subsurface Color"
        # Multiply these together to get the SSS weight, and to see if we need to set up SSS at
        # all.  "Subsurface Off - On" is probably 0 or 1 most of the time.
        #
        # This material's scatter is very different from ours, so we don't really try to emulate
        # it.  We just set up basic texture maps if available, so it's easier to turn it on manually.
        scatter_weight = self._scatter_weight()

        # If we have an ambient color (which is actually Subsurface Color), connect it to SSS color.
        # Otherwise, connect the diffuse color, since it's the usually what you want if you're turning
        # on simple SSS.
        self.set_attr_to_texture_with_color(material.attr('KsssColor'), self.images.get('Ambient Color'), [1,1,1])

        # Set a reasonable default for skin SSS.  This won't be used unless SSS is actually weighted on.
        material.attr('sssRadius').set((1, 0.5, 0.25))

        # XXX: Not implemented: ambient, displacement, opacity, reflection, shadows, tiling, velvet

        self._post_create(sg_node, material)
예제 #8
0
    def _create_metallic(self, dson_material, sg_node, metallic=False, top_weight=1):
        """
        Create a material for "PBM Metallicity",.

        If metallic is true, create a material for metallicity 1.  Otherwise, create metallicity 0.

        top_weight is the weight for this layer.  If we're not being added to a layeredShader this
        will be 1.  This is multiplied into the cutout opacity.
        """
        assert self.channels['Base Mixing'] == 0

        material = pm.shadingNode('aiStandard', asShader=True)

        cutout_opacity = self.get_texture_with_alpha(self.images.get('Cutout Opacity'), self.channels['Cutout Opacity'], mode='rgb', zero_is_invisible=True)
        if cutout_opacity != 1:
            # Tricky: We need to set uses_transparency if the final material will be transparent.  In this
            # case, that's only if the actual cutout opacity value isn't 1, not the value combined with
            # top_weight.  If top_weight is 0.25, then the other metallicity layer will have a top_weight
            # of 0.75 and the final material won't be transparent due to that.
            self.uses_transparency = True

        # Include the layer's weight in opacity.
        cutout_opacity = mh.math_op('mult', cutout_opacity, top_weight)
        mh.set_or_connect(material.attr('opacity'), cutout_opacity)

        # Turn off diffuse by default.  We'll turn it on later if we want it.
        mh.set_or_connect(material.attr('Kd'), 0)

        # Bump:
        bump_strength = self.channels.get('Bump Strength', 0)
        self._set_normal_map(material, normal_texture=self.images.get('Normal Map'), bump_texture=self.images.get('Bump Strength'), bump_scale=bump_strength*0.025)

        # Diffuse.  Set this even though in some cases we won't use it, so texture connections are available
        # during material tweaking.
        self.set_attr_to_texture_with_color(material.attr('color'), self.images.get('diffuse'), self.channels['diffuse'])
        self.set_attr_to_texture_with_color(material.attr('diffuseRoughness'), self.images.get('Diffuse Roughness'), self.channels['Diffuse Roughness'], mode='alpha')

        # Shared glossy settings
        #
        # Grr.  Arnold's specular behaves completely differently when it has a value of 0 than 0.001, and always
        # reflects a ton of light even with low weights.  This makes it act like a mirror, and makes texture
        # mapped roughness behave strangely.  Clamp roughness to 0.001 so this doesn't happen.
        roughness = self.get_texture_with_color(self.images.get('Glossy Roughness'), self.channels['Glossy Roughness'], mode='alpha')
        roughness = mh.math_op('clamp', roughness, 0.001, 1)
        mh.set_or_connect(material.attr('specularRoughness'), roughness)

        # Convert anisotropy from [0,1] to [0.5,1].  With this material, 0.5 is isotropic and values towards 0 and 1
        # are anisotropic in each axis.
        anisotropy = self.get_texture_with_color(self.images.get('Glossy Anisotropy'), self.channels['Glossy Anisotropy'], mode='r')
        anisotropy = mh.math_op('mult', anisotropy, 0.5)
        anisotropy = mh.math_op('add', anisotropy, 0.5)
        mh.set_or_connect(material.attr('specularAnisotropy'), anisotropy)

        self.set_attr_to_texture_with_color(material.attr('specularRotation'), self.images.get('Glossy Anisotropy Rotations'), self.channels['Glossy Anisotropy Rotations'], mode='alpha')
        material.attr('enableInternalReflections').set(0)

        # Always enable fresnel.  If we don't want fresnel, we'll just set Ksn to 1.  This has the same
        # effect on weighting, but avoids the weird side-effect of making the diffuse channel blending mode
        # change as if FresnelAffectDiff is false.
        material.attr('specularFresnel').set(1)
        material.attr('Ksn').set(1)

        # The top-level mixing is clamped: each layer is added in order with its weight, and once
        # we reach 100% no further layers are added.
        remaining_weight = 1

        # Refraction
        #
        # This is on top regardless of whether we're metallic or not.
        refraction_weight = self.get_texture_with_color(self.images.get('Refraction Weight'), self.channels['Refraction Weight'], mode='alpha')
        mh.set_or_connect(material.attr('Kt'), refraction_weight)

        material.attr('dispersionAbbe').set(self.channels['Abbe'])

        # Refraction Index in refraction isn't actually the IOR of refraction.  It's really the IOR for reflections
        # on top of refractions.  There seems to be no IOR built into refractions for this material.
        # material.attr('IOR').set(self.channels['Refraction Index'])

        if isinstance(refraction_weight, pm.PyNode) or refraction_weight > 0:
            self.uses_transparency = True

            # Only set these if we have any refraction, so we don't create connections to glossiness if we're not using it.
            #
            # If the Share Glossy Inputs setting is true, use the reflection settings for roughness/glossiness and color.
            # Note that we don't connect the roughness value, since the metallicity adjustments we make for
            # specular roughness shouldn't be made to refraction roughness (a non-metallic surface should have
            # rough reflections, but not rough refraction).
            share_glossy_inputs = self.channels['Share Glossy Inputs']
            diffuse_channel_name = 'Glossy Color' if share_glossy_inputs else 'Refraction Color'
            self.set_attr_to_texture_with_color(material.attr('KtColor'), self.images.get(diffuse_channel_name), self.channels[diffuse_channel_name])

            self.set_attr_to_texture_with_color(
                    material.attr('refractionRoughness'),
                    self.images.get('Glossy Roughness' if share_glossy_inputs else 'Refraction Roughness'),
                    self.channels['Glossy Roughness' if share_glossy_inputs else'Refraction Roughness'],
                    mode='alpha')

        # Subtract the weight used by refraction.  The remainder is the amount available for the remaining layers.
        remaining_weight = mh.math_op('sub', remaining_weight, refraction_weight)

        # Refraction reflections
        #
        # The top refraction layer has its own built-in reflections.  If you have Glossy Layered Weight on with
        # 100% refraction, this is the reflection you're seeing, not anything in the metallic or plastic layer.
        #
        # Unlike the other reflection layers, this one isn't multiplied by diffuse color.  This makes this tricky,
        # since we only have one main glossy layer to work with.  Currently we only implement this if refraction
        # is 100%, which means none of the other layers are visible.  If refraction is less than 100% or textured,
        # we won't set up this glossy layer (but you'll get the glossiness layers beneath it instead).
        glossy_layered_weight = self.get_texture_with_color(self.images.get('Glossy Layered Weight'), self.channels['Glossy Layered Weight'], mode='alpha')
        reflection_ior = self.channels['Refraction Index']
        if remaining_weight == 0 and glossy_layered_weight != 0 and reflection_ior != 1:
            facing_reflectance = _ior_to_schlick(reflection_ior)
            log.debug('%s using refraction reflections, %s %s', dson_material, reflection_ior, facing_reflectance)

            mh.set_or_connect(material.attr('Ksn'), facing_reflectance)
#            fresnel_ramp = schlick.create_ramp_for_schlick(facing_reflectance, max_points=16)
#            glossy_layered_weight = mh.math_op('mult', glossy_layered_weight, fresnel_ramp)
            mh.set_or_connect(material.attr('Ks'), glossy_layered_weight)

            return material

        # Metallic glossiness
        if metallic:
            # Disable diffuse for the metallic layer.  We're using up the rest of the layering weight, so there's
            # no diffuse underneath it.
            mh.set_or_connect(material.attr('Kd'), 0)

            # This is the metallic layer.  This layer is completely specular, minus any weight used up by refraction.
            # (The glossiness weight for the metallicity layer is the metallicity, and we're implementing metallicity 1.)
            mh.set_or_connect(material.attr('Ks'), remaining_weight)

            # When metallic, the fresnel reflectance is always 0.7.
            mh.set_or_connect(material.attr('Ksn'), 0.7)

            # top_coat_directional_normal_color for metallic seems to be the base color, and top_coat_directional_grazing_color
            # is white.  The IOR is 0.7 which gives a lot of reflectance at the facing angle anyway, so we just mix the base
            # color into the glossy color.
            glossy_color = self.get_texture_with_color(self.images.get('Glossy Color'), self.channels['Glossy Color'])
            diffuse_color = self.get_texture_with_color(self.images.get('diffuse'), self.channels['diffuse'])
            glossy_color_combined = mh.math_op('mult', diffuse_color, glossy_color)
            mh.set_or_connect(material.attr('KsColor'), glossy_color_combined)

            return material

        # This is the plastic layer.  None of the rest applies to the metallic layer.
        #
        # Backscatter (not sub-surface scatter)
        #
        # When this is enabled, an object can be lit from behind.  The usual example is paper.
        # In the original material, diffuse weight had backscatter weight subtracted.  The
        # backscatter layer probably includes diffuse, so this prevents it from being doubled.
        # Here, backscatter isn't a separate layer but just a weight on diffuse, so we just apply
        # the backscatter weight and don't subtract it from diffuse.
        #
        # Backscatter has color, roughness and anisotropy, but we don't have separate control over
        # that.  All we can do is set a weight.  We don't need to set uses_transparency here.
        self.set_attr_to_texture_with_color(material.attr('Kb'), self.images.get('Backscattering Weight'), self.channels['Backscattering Weight'], mode='alpha')

        # The remaining weight not taken up by refraction and scatter is shared by the glossy and diffuse
        # layer.  These two layers are mixed with reflection on top, using fresnel for weighting (Glossy Reflectivity)
        # multiplied by Glossy Layered Weight.  That is, if refraction is 10% and scatter is 15%, we have 75%
        # remaining.  That layer is weighted to glossiness with fresnel multiplied by Glossy Layered Weight.
        #
        # Glossy Reflectivity doesn't map 1:1 to Schlick reflectivity.  It's not clear what the
        # translation is.  Empirically, the MDL receives 0.28 for 1.0, 0.24 for 0.75, 0.20 for 0.5,
        # 0.14 for 0.25, and 0 for 0.  Just approximate it by scaling.
        glossy_reflectivity = self.get_texture_with_color(self.images.get('Glossy Reflectivity'), self.channels['Glossy Reflectivity'], mode='alpha')
        glossy_reflectivity = mh.math_op('mult', glossy_reflectivity, 0.25)
        mh.set_or_connect(material.attr('Ksn'), glossy_reflectivity)

        glossy_weight = mh.math_op('mult', glossy_layered_weight, remaining_weight)
        mh.set_or_connect(material.attr('Ks'), glossy_weight)

        # remaining_weight is the weight remaining for diffuse.  aiStandard will subtract the weight of
        # the specular (and reflective) layer, since FresnelAffectDiff is true.
        mh.set_or_connect(material.attr('Kd'), remaining_weight)

        # The dialectric layer sets its base to diffuse, which seems to effectively mix in the
        # diffuse color with the glossy color.
        glossy_color = self.get_texture_with_color(self.images.get('Glossy Color'), self.channels['Glossy Color'])
        diffuse_color = self.get_texture_with_color(self.images.get('diffuse'), self.channels['diffuse'])
        glossy_color_combined = mh.math_op('mult', diffuse_color, glossy_color)
        mh.set_or_connect(material.attr('KsColor'), glossy_color_combined)

        # The scatter component of diffuse isn't implemented.  The SSS model is complex: a translucency weight, reflectance
        # tint, translucency color, transmission color and lots of weights, deriving an absorbance coefficient and a scattering
        # coefficient.  It's hard to estimate what the results are in order to even emulate the overall basic color.  If
        # your material uses scatter, you'll need to set this up manually.
        #
        # If we have a SSS texture, hook that up to make it easier to turn this on manually.
        self.set_attr_to_texture_with_color(material.attr('KsssColor'), self.images.get('Translucency Color'), self.channels['Translucency Color'])

        # XXX: Thin film using the reflection layer?

        return material
예제 #9
0
    def _create_glossy_or_weighted(self, dson_material, sg_node):
        material = pm.shadingNode('aiStandard', asShader=True)

        diffuse_weight = 1

        opacity = self.channels['Cutout Opacity']
        if self.images.get('Cutout Opacity') or self.channels['Cutout Opacity'] != 1:
            self.set_attr_to_transparency(material.attr('opacity'), self.images.get('Cutout Opacity'), opacity, mode='rgb', zero_is_invisible=True)
            self.uses_transparency = True

        # Bump:
        bump_strength = self.channels.get('Bump Strength', 0)
        self._set_normal_map(material, normal_texture=self.images.get('Normal Map'), bump_texture=self.images.get('Bump Strength'), bump_scale=bump_strength*0.025)

        # Glossiness
                       
        # 0: PBR Metallicity/Roughness
        # 1: PBR Specular/Glossiness
        # 2: Weighted
        base_mixing = self.channels['Base Mixing']
        log.debug('%s: %s, %s', sg_node, dson_material, base_mixing)

        assert base_mixing in (1,2)
        if base_mixing == 1:
            # "Specular/Glossiness"
            #
            # Glossy Layered Weight has the same effect here as in the above mode, blending from diffuse to glossy.
            glossy_weight = self.get_texture_with_color(self.images.get('Glossy Layered Weight'), self.channels['Glossy Layered Weight'], mode='alpha')
            mh.set_or_connect(material.attr('Ks'), glossy_weight)
            diffuse_weight = mh.math_op('sub', diffuse_weight, glossy_weight)

            # In this mode, the diffuse color is not mixed into the glossy color: if you have 100% glossy
            # layered weight and a blue diffuse, the blue isn't visible at all.
            self.set_attr_to_texture_with_color(material.attr('KsColor'), self.images.get('Glossy Color'), self.channels['Glossy Color'])
            roughness = self.get_roughness_from_glossiness(self.images.get('Glossiness'), self.channels['Glossiness'])
        else:
            # "Weighted"
            #
            # This mode replaces Glossy Layered Weight with a Glossy Weight and Diffuse Weight.
            #
            # The docs say that it normalizes them, but that's wrong: if you set them both to 0.25 the result
            # is darker than if you set them both to 1.  If they were normalized, 0.25+0.25 would be normalized
            # to 0.5+0.5.  It actually only normalizes if the sum is greater than 1.  Note that most of the
            # time we don't have textures on both of these and all of this math is just done at setup time,
            # so this doesn't always create a complicated node network.
            #
            # This mode doesn't have fresnel reflections.
            unnormalized_glossy = self.get_texture_with_color(self.images.get('Glossy Weight'), self.channels['Glossy Weight'], mode='alpha')
            unnormalized_diffuse = self.get_texture_with_color(self.images.get('Diffuse Weight'), self.channels['Diffuse Weight'], mode='alpha')
            log.debug('... %s, %s', unnormalized_glossy, unnormalized_diffuse)

            total_weight = mh.math_op('add', unnormalized_glossy, unnormalized_diffuse)
            normalized_glossy = mh.math_op('div', unnormalized_glossy, total_weight)
            normalized_diffuse = mh.math_op('div', unnormalized_diffuse, total_weight)

            # If the total is less than one, use the original weight.  Otherwise, use the normalized weight.
            glossy_weight = mh.math_op('lt', total_weight, 1, unnormalized_glossy, normalized_glossy)
            assert diffuse_weight == 1 # should not have been changed yet
            diffuse_weight = mh.math_op('lt', total_weight, 1, unnormalized_diffuse, normalized_diffuse)

            mh.set_or_connect(material.attr('Ks'), glossy_weight)

            self.set_attr_to_texture_with_color(material.attr('KsColor'), self.images.get('Glossy Color'), self.channels['Glossy Color'])
            roughness = self.get_texture_with_color(self.images.get('Glossy Roughness'), self.channels['Glossy Roughness'], mode='alpha')

        # Grr.  Arnold's specular behaves completely differently when it has a value of 0 than 0.001, and always
        # reflects a ton of light even with low weights.  This makes it act like a mirror, and makes texture
        # mapped roughness behave strangely.  Clamp roughness to 0.001 so this doesn't happen.
        roughness = mh.math_op('clamp', roughness, 0.001, 1)
        mh.set_or_connect(material.attr('specularRoughness'), roughness)

        # Convert anisotropy from [0,1] to [0.5,1].  With this material, 0.5 is isotropic and values towards 0 and 1
        # are anisotropic in each axis.
        anisotropy = self.get_texture_with_color(self.images.get('Glossy Anisotropy'), self.channels['Glossy Anisotropy'], mode='r')
        anisotropy = mh.math_op('mult', anisotropy, 0.5)
        anisotropy = mh.math_op('add', anisotropy, 0.5)
        mh.set_or_connect(material.attr('specularAnisotropy'), anisotropy)

        self.set_attr_to_texture_with_color(material.attr('specularRotation'), self.images.get('Glossy Anisotropy Rotations'), self.channels['Glossy Anisotropy Rotations'], mode='alpha')

        # Refraction
        #
        # Refraction in this material is strange.  For example, if we're in weighted mode and the diffuse weight
        # is 1, refraction still makes the material transparent, but the refraction color isn't applied at all.
        refraction_weight = self.get_texture_with_color(self.images.get('Refraction Weight'), self.channels['Refraction Weight'], mode='alpha')
        diffuse_weight = mh.math_op('sub', diffuse_weight, refraction_weight)
        if isinstance(refraction_weight, pm.PyNode) or refraction_weight > 0:
            # Only set these if we have any refraction, so we don't create connections to glossiness if we're not using it.
            mh.set_or_connect(material.attr('Kt'), refraction_weight)
            self.uses_transparency = True

            # If the Share Glossy Inputs setting is true, use the reflection settings for roughness/glossiness and color.
            # Note that we don't connect the roughness value, since the metallicity adjustments we make for
            # specular roughness shouldn't be made to refraction roughness (a non-metallic surface should have
            # rough reflections, but not rough refraction).
            share_glossy_inputs = self.channels['Share Glossy Inputs']
            diffuse_channel_name = 'Glossy Color' if share_glossy_inputs else 'Refraction Color'
            self.set_attr_to_texture_with_color(material.attr('KtColor'), self.images.get(diffuse_channel_name), self.channels[diffuse_channel_name])

            # Use the base mixing mode to determine whether it uses Refraction Roughness or Refraction Glossiness.
            if base_mixing == 1:
                # "Specular/Glossiness"
                self.set_attr_to_roughness_from_glossiness(
                        self.images.get('Glossiness' if share_glossy_inputs else 'Refraction Glossiness'),
                        self.channels['Glossiness' if share_glossy_inputs else 'Refraction Glossiness'],
                        material.attr('refractionRoughness'))
            else:
                # "Weighted"
                self.set_attr_to_texture_with_color(
                        material.attr('refractionRoughness'),
                        self.images.get('Glossy Roughness' if share_glossy_inputs else 'Refraction Roughness'),
                        self.channels['Glossy Roughness' if share_glossy_inputs else'Refraction Roughness'],
                        mode='alpha')

        # XXX
        material.attr('IOR').set(self.channels['Refraction Index'])
        material.attr('dispersionAbbe').set(self.channels['Abbe'])

        # XXX: Backscatter

        # XXX: Thin film using the reflection layer?

        # Diffuse
        self.set_attr_to_texture_with_color(material.attr('color'), self.images.get('diffuse'), self.channels['diffuse'])
        self.set_attr_to_texture_with_color(material.attr('diffuseRoughness'), self.images.get('Diffuse Roughness'), self.channels['Diffuse Roughness'], mode='alpha')

        # Diffuse, glossy and transparency are additive.  Set the diffuse weight to the remainder after
        # subtracting the other parts.  A completely transparent or reflective object shouldn't have any
        # diffuse.  If refraction + glossy > 1, set diffuse to 0.
        diffuse_weight = mh.math_op('clamp', diffuse_weight, 0, 1)
        mh.set_or_connect(material.attr('Kd'), diffuse_weight)
        return material