예제 #1
0
    def __add_floor(self):
        rotate_transform = Transform.rotate(Vector(-1, 0, 0), 90)
        scale_transform = Transform.scale(Vector(100, 100, 100))
        translate_transform = Transform.translate(
            Vector(0.0, self.floor_height, 0.0))
        total_transform = translate_transform * scale_transform\
                * rotate_transform

        if self.scene.active_view.background == "d":
            reflectance = Spectrum(0.05)
        elif self.scene.active_view.background == "l":
            reflectance = Spectrum(0.5)
        else:
            reflectance = Spectrum(0.0)

        floor = self.plgr.create({
            "type": "rectangle",
            "toWorld": total_transform,
            "bsdf": {
                "type": "roughdiffuse",
                "diffuseReflectance": reflectance,
                "alpha": 0.5
            }
        })
        self.mitsuba_scene.addChild(floor)
예제 #2
0
    def __add_lights(self):
        #TODO: load lights from scene
        front_light = self.plgr.create({
            "type": "sphere",
            "center": Point(3.0, 6.0, 4.0),
            "radius": 2.5,
            "emitter": {
                "type": "area",
                "radiance": Spectrum(10.0),
                "samplingWeight": 10.0
            }
        })

        side_light = self.plgr.create({
            "type": "point",
            "position": Point(4.0, 4.0, -1.0),
            "intensity": Spectrum(5.0)
        })

        back_light = self.plgr.create({
            "type": "point",
            "position": Point(-0, 5.0, -1),
            "intensity": Spectrum(5.0)
        })

        self.mitsuba_scene.addChild(front_light)
예제 #3
0
    def __add_primitives(self, active_view, parent_transform=None):
        if len(active_view.subviews) > 0:
            for view in active_view.subviews:
                if parent_transform is None:
                    view_transform = self.__get_view_transform(active_view)
                else:
                    view_transform = parent_transform * self.__get_view_transform(
                        active_view)
                self.__add_primitives(view, view_transform)
            return

        old_active_view = self.scene.active_view
        self.scene.active_view = active_view
        scale = active_view.scale
        normalize_transform = self.__get_normalize_transform(active_view)
        view_transform = self.__get_view_transform(active_view)
        if parent_transform is not None:
            view_transform = parent_transform * view_transform
        glob_transform = self.__get_glob_transform()
        total_transform = glob_transform * view_transform * normalize_transform

        primitives = self.scene.active_view.primitives
        for shape in primitives:
            if shape.color[3] <= 0.0: continue
            color = {
                "type": "plastic",
                "diffuseReflectance": Spectrum(shape.color[:3].tolist())
            }
            if shape.color[3] < 1.0:
                color = {
                    "type": "mask",
                    "opacity": Spectrum(active_view.alpha),
                    "bsdf": color
                }
            if isinstance(shape, Cylinder):
                if shape.radius <= 0.0: continue
                setting = self.__add_cylinder(shape)
                setting["bsdf"] = color
                setting["toWorld"] = total_transform
            elif isinstance(shape, Cone):
                if shape.radius <= 0.0: continue
                setting = self.__add_cone(shape)
                setting["bsdf"] = color
                setting["toWorld"] = total_transform * setting["toWorld"]
            elif isinstance(shape, Sphere):
                if shape.radius <= 0.0: continue
                # Due to weird behavior in Mitsuba, all transformation is
                # applied directly on radius and center variable.
                setting = self.__add_sphere(shape)
                setting["radius"] *= scale
                setting["center"] = total_transform * setting["center"]
                setting["bsdf"] = color
            else:
                raise NotImplementedError(
                    "Unknown primitive: {}".format(shape))

            mitsuba_primative = self.plgr.create(setting)
            self.mitsuba_scene.addChild(mitsuba_primative)
        self.scene.active_view = old_active_view
예제 #4
0
def create_spectrum_from_rgb(r: int, g: int, b: int) -> Spectrum:
    """
    Create a Mitsuba Spectrum from r, g, and b values
    :param r: Red component
    :param g: Green component
    :param b: Blue component
    :return: A Mitsuba Spectrum
    """
    assert (0 <= r <= 255 and 0 <= g <= 255 and 0 <= b <= 255), "Provide integer rgb values in the range 0-255"
    spectrum = Spectrum()
    spectrum.fromSRGB(float(r) / 255., float(g) / 255., float(b) / 255.)

    return spectrum
예제 #5
0
    def __get_material_setting(self, active_view):
        setting = {}
        if self.with_texture_coordinates:
            diffuse_color = {
                "type": "checkerboard",
                "color0": Spectrum([1.0, 1.0, 1.0]),
                "color1": Spectrum([0.5, 0.5, 0.5]),
                "flipTexCoords": False,
            }
        else:
            if self.with_colors:
                diffuse_color = {"type": "vertexcolors"}
            else:
                diffuse_color = Spectrum(0.2)

        setting["bsdf"] = {
            "type": "roughplastic",
            "distribution": "beckmann",
            "alpha": 0.2,
            "diffuseReflectance": diffuse_color,
        }
        setting["bsdf"] = {
            "type": "twosided",
            "bsdf": setting["bsdf"]
        }
        if self.with_alpha:
            setting["bsdf"] = {
                "type": "mask",
                "opacity": Spectrum(active_view.alpha),
                "bsdf": setting["bsdf"]
            }
        if not self.with_colors and \
                not self.with_alpha and \
                not self.with_texture_coordinates:
            setting["subsurface"] = {
                "type": "dipole",
                "material": "Skimmilk",
                "scale": 0.5
            }
        elif self.with_texture_coordinates:
            setting["bsdf"]["bsdf"]["nonlinear"] = True
        elif not self.with_alpha and not self.with_texture_coordinates:
            setting["subsurface"] = {
                "type": "dipole",
                "material": "sprite",
                "scale": 0.5
            }
        return setting
예제 #6
0
    def __get_material_setting(self, active_view):
        setting = {};
        if self.with_wire_frame:
            diffuse_color = {
                    "type": "wireframe",
                    "edgeColor": Spectrum(0.0),
                    "lineWidth": active_view.line_width,
                    };
            if self.with_uniform_colors:
                diffuse_color["interiorColor"] =\
                        Spectrum(active_view.vertex_colors[0][0].tolist()[0:3]);
            #elif self.with_colors:
            #    diffuse_color["interiorColor"] = { "type": "vertexcolors" }
        else:
            if self.with_colors:
                diffuse_color = { "type": "vertexcolors" }
            else:
                diffuse_color = Spectrum(0.2);

        setting["bsdf"] = {
                "type": "roughplastic",
                "distribution": "beckmann",
                "alpha": 0.2,
                "diffuseReflectance": diffuse_color
                };
        setting["bsdf"] = {
                "type": "twosided",
                "bsdf": setting["bsdf"]
                };
        if self.with_alpha:
            setting["bsdf"] = {
                    "type": "mask",
                    "opacity": Spectrum(active_view.alpha),
                    "bsdf": setting["bsdf"]
                    };
        if not self.with_colors and not self.with_alpha and not self.with_wire_frame:
            setting["subsurface"] = {
                    "type": "dipole",
                    "material": "Skimmilk",
                    "scale": 0.5
                    };
        elif not self.with_alpha:
            setting["subsurface"] = {
                    "type": "dipole",
                    "material": "sprite",
                    "scale": 0.5
                    };
        return setting;
예제 #7
0
    def estimate(self, in_pos, im, props, sigma_n, active):
        """
        Estimate output position and absorption with VAE
        Notice that the types of arguments are in pytorch (tensor) except sigma_n, but the ones of returns are in mitsuba (Vector3f, Float)
        Args:
            in_pos: Incident position in local mesh coordinates, and the type of this is tensor
            im: Height map around incident position ranging to multiple of sigma_n.
                This map can be generated by clip_scaled_map() from data_handler, and the type is tensor
            props: Medium properties vector including (and following this order)
                   - effective albedo
                   - g
                   - eta
                   - incident angle (xyz)
                   - max height
                   , and the type of this argument is tensor
            sigma_n: Standard deviation of the range of medium scattering.
                     In this method, this value is used as scale factor of coordinates in vae
            active: Boolean mask which indicates whether a ray is applied VAE or not

        Return:
            recon_pos: estimated outgoing position (Vector3f)
            recon_abs: estimated absorption probability (Spectrum)
        """

        n_sample, _, _, _ = im.shape
        pos = Vector3f(in_pos)
        abs_prob = Float().zero(n_sample)

        self.model.eval()

        with torch.no_grad():
            # Feature conversion
            feature = self.model.feature_conversion(
                im.to(self.device, dtype=torch.float),
                props.to(self.device, dtype=torch.float))

            # Sample latent variable from normal distribution
            z = torch.randn(n_sample, 4).to(self.device, dtype=torch.float)

            # Decode and get reconstructed position and absorption
            recon_pos, recon_abs = self.model.decode(feature, z)

        # Convert from tensor to Vector3f and Float
        recon_pos = Vector3f(recon_pos)
        abs_prob = Spectrum(recon_abs.view(1, -1).squeeze())

        # Reconstruct real scale position in mesh local coordinates
        pos += ek.select(active, sigma_n * recon_pos, 0)

        return pos, abs_prob
예제 #8
0
    def __add_primitives(self, active_view):
        if len(active_view.subviews) > 0:
            for view in active_view.subviews:
                self.__add_primitives(view);
            return;

        old_active_view = self.scene.active_view;
        self.scene.active_view = active_view;
        scale = active_view.scale;
        normalize_transform = self.__get_normalize_transform(active_view);
        view_transform = self.__get_view_transform(active_view);
        glob_transform = self.__get_glob_transform();
        total_transform = glob_transform * normalize_transform * view_transform;

        primitives = self.scene.active_view.primitives;
        for shape in primitives:
            if shape.color[3] <= 0.0: continue;
            color = {
                    "type": "plastic",
                    "diffuseReflectance": Spectrum(shape.color[:3].tolist())
                    };
            if isinstance(shape, Cylinder):
                if shape.radius <= 0.0: continue;
                setting = self.__add_cylinder(shape);
                setting["bsdf"] = color;
                setting["toWorld"] = total_transform
            elif isinstance(shape, Cone):
                if shape.radius <= 0.0: continue;
                setting = self.__add_cone(shape);
                setting["bsdf"] = color;
                setting["toWorld"] = total_transform * setting["toWorld"];
            elif isinstance(shape, Sphere):
                if shape.radius <= 0.0: continue;
                # Due to weird behavior in Mitsuba, all transformation is
                # applied directly on radius and center variable.
                setting = self.__add_sphere(shape);
                setting["radius"] *= scale;
                setting["center"] = total_transform * setting["center"];
                setting["bsdf"] = color;
            else:
                raise NotImplementedError("Unknown primitive: {}".format(shape));

            mitsuba_primative = self.plgr.create(setting);
            self.mitsuba_scene.addChild(mitsuba_primative);
        self.scene.active_view = old_active_view;
예제 #9
0
def test_incoming_flux(variant_scalar_rgb, radiance):
    """
    We test the recorded power density of the irradiance meter, by creating a simple scene:
    The irradiance meter is attached to a sphere with unit radius at the coordinate origin
    surrounded by a constant environment emitter.
    We sample a number of rays and average their contribution to the cumulative power
    density.
    We expect the average value to be \\pi * L with L the radiance of the constant
    emitter.
    """
    from mitsuba.core import Spectrum, ScalarVector3f
    from mitsuba.core.xml import load_dict

    scene_dict = {
        "type": "scene",
        "sensor": sensor_shape_dict(1, ScalarVector3f(0, 0, 0)),
        "emitter": constant_emitter_dict(radiance)
    }

    scene = load_dict(scene_dict)
    sensor = scene.sensors()[0]

    power_density_cum = 0.0
    num_samples = 100

    wav_samples = np.random.rand(num_samples)
    pos_samples = np.random.rand(num_samples, 2)
    dir_samples = np.random.rand(num_samples, 2)

    for i in range(100):
        ray, weight = sensor.sample_ray_differential(
            0.0, wav_samples[i], pos_samples[i], dir_samples[i])

        intersection = scene.ray_intersect(ray)
        power_density_cum += weight * \
            intersection.emitter(scene).eval(intersection)
    power_density_avg = power_density_cum / float(num_samples)

    assert ek.allclose(power_density_avg, Spectrum(ek.pi * radiance))
예제 #10
0
def test_incoming_flux(variant_scalar_rgb, radiance):
    """
    We test the recorded power density of the irradiance meter, by creating a simple scene:
    The irradiance meter is attached to a sphere with unit radius at the coordinate origin
    surrounded by a constant environment emitter.
    We sample a number of rays and average their contribution to the cumulative power
    density.
    We expect the average value to be \\pi * L with L the radiance of the constant
    emitter.
    """
    from mitsuba.core import Spectrum
    from mitsuba.core.xml import load_string

    sensor_xml = f"""
    <shape version='0.1.0' type="sphere">
        <float name="radius" value="1"/>
        <transform name="to_world">
            <translate x="0" y="0" z="0"/>
        </transform>
        <sensor type="irradiancemeter">
            <film type="hdrfilm">
                <integer name="width" value="1"/>
                <integer name="height" value="1"/>
            </film>
        </sensor>
    </shape>
    """

    emitter_xml = f"""
    <emitter type="constant">
        <spectrum name="radiance" type='uniform'>
        	<float name="value" value="{radiance}"/>
    	</spectrum>
    </emitter>
    """

    scene_xml = f"""
        <scene version="0.1.0">
            {sensor_xml}
            {emitter_xml}
        </scene>
    """
    scene = load_string(scene_xml)
    sensor = scene.sensors()[0]

    power_density_cum = 0.0
    num_samples = 100

    wav_samples = np.random.rand(num_samples)
    pos_samples = np.random.rand(num_samples, 2)
    dir_samples = np.random.rand(num_samples, 2)

    for i in range(100):
        ray, weight = sensor.sample_ray_differential(0.0, wav_samples[i],
                                                     pos_samples[i],
                                                     dir_samples[i])

        intersection = scene.ray_intersect(ray)
        power_density_cum += weight * intersection.emitter(scene).eval(
            intersection)
    power_density_avg = power_density_cum / float(num_samples)

    assert ek.allclose(power_density_avg, Spectrum(ek.pi * radiance))
 def spectrum_from_stokes(v):
     res = Spectrum(0.0)
     for i in range(4):
         res[i, 0] = v[i]
     return res
예제 #12
0
def render_sample(scene, sampler, rays, bdata, heightmap_pybind, bssrdf=None):
    """
    Sample RTE
    TODO: Support multi channel sampling

    Args:
        scene: Target scene object
        sampler: Sampler object for random number
        rays: Given rays for sampling
        bdata: BSSRDF Data object
        heightmap_pybind: Object for getting height map around incident position.
                          Refer src/librender/python/heightmap.cpp

    Returns:
        result: Sampling RTE result
        valid_rays: Mask data whether rays are valid or not
        scatter: Scatter components of Sampling RTE result
        non_scatter: Non scatter components of Sampling RTE result
        invalid_sample: Sampling RTE result with invalid sampled data by VAEBSSRDF
    """

    eta = Float(1.0)
    emission_weight = Float(1.0)
    throughput = Spectrum(1.0)
    result = Spectrum(0.0)
    scatter = Spectrum(0.0)
    non_scatter = Spectrum(0.0)
    invalid_sample = Spectrum(0.0)
    active = True
    is_bssrdf = False

    ##### First interaction #####
    si = scene.ray_intersect(rays, active)
    active = si.is_valid() & active
    valid_rays = si.is_valid()

    emitter = si.emitter(scene, active)

    depth = 0

    # Set channel
    # At and after evaluating BSSRDF, a ray consider only this one channel
    n_channels = 3
    channel = UInt32(
        ek.min(sampler.next_1d(active) * n_channels, n_channels - 1))

    d_out_local = Vector3f().zero()
    d_out_pdf = Float(0)

    sss = Mask(False)

    while (True):
        depth += 1
        if config.aovs and depth == 2:
            sss = is_bssrdf

        ##### Interaction with emitters #####
        emission_val = emission_weight * throughput * Emitter.eval_vec(
            emitter, si, active)

        result += ek.select(active, emission_val, Spectrum(0.0))
        invalid_sample += ek.select(active, emission_val, Spectrum(0.0))
        scatter += ek.select(active & sss, emission_val, Spectrum(0.0))
        non_scatter += ek.select(active & ~sss, emission_val, Spectrum(0.0))

        active = active & si.is_valid()

        # Process russian roulette
        if depth > config.rr_depth:
            q = ek.min(ek.hmax(throughput) * ek.sqr(eta), 0.95)
            active = active & (sampler.next_1d(active) < q)
            throughput *= ek.rcp(q)

        # Stop if the number of bouces exceeds the given limit bounce, or
        # all rays are invalid. latter check is done only when the limit
        # bounce is infinite
        if depth >= config.max_depth:
            break

        ##### Emitter sampling #####
        bsdf = si.bsdf(rays)
        ctx = BSDFContext()

        active_e = active & has_flag(BSDF.flags_vec(bsdf), BSDFFlags.Smooth)
        ds, emitter_val = scene.sample_emitter_direction(
            si, sampler.next_2d(active_e), True, active_e)
        active_e &= ek.neq(ds.pdf, 0.0)

        # Query the BSDF for that emitter-sampled direction
        wo = si.to_local(ds.d)
        bsdf_val = BSDF.eval_vec(bsdf, ctx, si, wo, active_e)
        # Determine density of sampling that same direction using BSDF sampling
        bsdf_pdf = BSDF.pdf_vec(bsdf, ctx, si, wo, active_e)

        mis = ek.select(ds.delta, Float(1), mis_weight(ds.pdf, bsdf_pdf))

        emission_val = mis * throughput * bsdf_val * emitter_val

        result += ek.select(active, emission_val, Spectrum(0.0))
        invalid_sample += ek.select(active, emission_val, Spectrum(0.0))
        scatter += ek.select(active & sss, emission_val, Spectrum(0.0))
        non_scatter += ek.select(active & ~sss, emission_val, Spectrum(0.0))

        ##### BSDF sampling #####
        bs, bsdf_val = BSDF.sample_vec(bsdf, ctx, si, sampler.next_1d(active),
                                       sampler.next_2d(active), active)

        ##### BSSRDF replacing #####
        if (config.enable_bssrdf):
            # Replace bsdf samples by ones of BSSRDF
            bs.wo = ek.select(is_bssrdf, d_out_local, bs.wo)
            bs.pdf = ek.select(is_bssrdf, d_out_pdf, bs.pdf)
            bs.sampled_component = ek.select(is_bssrdf, UInt32(1),
                                             bs.sampled_component)
            bs.sampled_type = ek.select(is_bssrdf,
                                        UInt32(+BSDFFlags.DeltaTransmission),
                                        bs.sampled_type)
        ############################

        throughput *= ek.select(is_bssrdf, Float(1.0), bsdf_val)
        active &= ek.any(ek.neq(throughput, 0))

        eta *= bs.eta

        # Intersect the BSDF ray against the scene geometry
        rays = RayDifferential3f(si.spawn_ray(si.to_world(bs.wo)))
        si_bsdf = scene.ray_intersect(rays, active)

        ##### Checking BSSRDF #####
        if (config.enable_bssrdf):
            # Whether the BSDF is BSS   RDF or not?
            is_bssrdf = (active
                         & has_flag(BSDF.flags_vec(bsdf), BSDFFlags.BSSRDF)
                         & (Frame3f.cos_theta(bs.wo) < Float(0.0))
                         & (Frame3f.cos_theta(si.wi) > Float(0.0)))

            # Decide whether we should use 0-scattering or multiple scattering
            is_zero_scatter = utils_render.check_zero_scatter(
                sampler, si_bsdf, bs, channel, is_bssrdf)
            is_bssrdf = is_bssrdf & ~is_zero_scatter

            throughput *= ek.select(is_bssrdf, ek.sqr(bs.eta), Float(1.0))
        ###########################

        ###### Process for BSSRDF ######
        if (config.enable_bssrdf and not ek.none(is_bssrdf)):
            # Get projected samples from BSSRDF
            projected_si, project_suc, abs_prob = bssrdf.sample_bssrdf(
                scene, bsdf, bs, si, bdata, heightmap_pybind, channel,
                is_bssrdf)

            if config.visualize_invalid_sample and (depth <= 1):
                active = active & (~is_bssrdf | project_suc)
                invalid_sample += ek.select((is_bssrdf & (~project_suc)),
                                            Spectrum([100, 0, 0]),
                                            Spectrum(0.0))

            # Sample outgoing direction from projected position
            d_out_local, d_out_pdf = utils_render.resample_wo(
                sampler, is_bssrdf)
            # Apply absorption probability
            throughput *= ek.select(is_bssrdf,
                                    Spectrum(1) - abs_prob, Spectrum(1))
            # Replace interactions by sampled ones from BSSRDF
            si_bsdf = SurfaceInteraction3f().masked_si(si_bsdf, projected_si,
                                                       is_bssrdf)
        ################################

        # Determine probability of having sampled that same
        # direction using emitter sampling
        emitter = si_bsdf.emitter(scene, active)
        ds = DirectionSample3f(si_bsdf, si)
        ds.object = emitter

        delta = has_flag(bs.sampled_type, BSDFFlags.Delta)
        emitter_pdf = ek.select(delta, Float(0.0),
                                scene.pdf_emitter_direction(si, ds))
        emission_weight = mis_weight(bs.pdf, emitter_pdf)

        si = si_bsdf

    return result, valid_rays, scatter, non_scatter, invalid_sample
예제 #13
0
            def spectrum(self, value, mode=''):
                if not mode:
                    mode = self.color_mode

                spec = None

                if isinstance(value, (dict)):
                    if 'type' in value:
                        if value['type'] in {'rgb', 'srgb', 'spectrum'}:
                            spec = self.spectrum(value['value'], value['type'])

                        elif value['type'] == 'blackbody':
                            spec = Spectrum()
                            spec.fromContinuousSpectrum(BlackBodySpectrum(value['temperature']))
                            spec.clampNegative()
                            spec = spec * value['scale']

                elif isinstance(value, (float, int)):
                    spec = Spectrum(value)

                elif isinstance(value, (str)):
                    contspec = InterpolatedSpectrum(self.get_export_path(value))
                    spec = Spectrum()
                    spec.fromContinuousSpectrum(contspec)
                    spec.clampNegative()

                else:
                    try:
                        items = list(value)

                        for i in items:
                            if not isinstance(i, (float, int, tuple)):
                                raise Exception('Error: spectrum list contains an unknown type')

                    except:
                        items = None

                    if items:
                        totitems = len(items)

                        if isinstance(items[0], (float, int)):
                            if totitems == 3 or totitems == 4:
                                spec = Spectrum()

                                if mode == 'srgb':
                                    spec.fromSRGB(items[0], items[1], items[2])

                                else:
                                    spec.fromLinearRGB(items[0], items[1], items[2])

                            elif totitems == 1:
                                spec = Spectrum(items[0])

                            else:
                                MtsLog('Expected spectrum items to be 1, 3 or 4, got %d.' % len(items), type(items), items)

                        else:
                            spec = Spectrum()
                            contspec = InterpolatedSpectrum()

                            for spd in items:
                                (wlen, val) = spd
                                contspec.append(wlen, val)

                            spec.fromContinuousSpectrum(contspec)
                            spec.clampNegative()

                    else:
                        MtsLog('Unknown spectrum type.', type(value), value)

                if spec is None:
                    spec = Spectrum(0.0)

                return spec
예제 #14
0
            def spectrum(self, value, mode=''):
                if not mode:
                    mode = self.color_mode

                spec = None

                if isinstance(value, (dict)):
                    if 'type' in value:
                        if value['type'] in {'rgb', 'srgb', 'spectrum'}:
                            spec = self.spectrum(value['value'], value['type'])

                        elif value['type'] == 'blackbody':
                            spec = Spectrum()
                            spec.fromContinuousSpectrum(
                                BlackBodySpectrum(value['temperature']))
                            spec.clampNegative()
                            spec = spec * value['scale']

                elif isinstance(value, (float, int)):
                    spec = Spectrum(value)

                elif isinstance(value, (str)):
                    contspec = InterpolatedSpectrum(
                        self.get_export_path(value))
                    spec = Spectrum()
                    spec.fromContinuousSpectrum(contspec)
                    spec.clampNegative()

                else:
                    try:
                        items = list(value)

                        for i in items:
                            if not isinstance(i, (float, int, tuple)):
                                raise Exception(
                                    'Error: spectrum list contains an unknown type'
                                )

                    except:
                        items = None

                    if items:
                        totitems = len(items)

                        if isinstance(items[0], (float, int)):
                            if totitems == 3 or totitems == 4:
                                spec = Spectrum()

                                if mode == 'srgb':
                                    spec.fromSRGB(items[0], items[1], items[2])

                                else:
                                    spec.fromLinearRGB(items[0], items[1],
                                                       items[2])

                            elif totitems == 1:
                                spec = Spectrum(items[0])

                            else:
                                MtsLog(
                                    'Expected spectrum items to be 1, 3 or 4, got %d.'
                                    % len(items), type(items), items)

                        else:
                            spec = Spectrum()
                            contspec = InterpolatedSpectrum()

                            for spd in items:
                                (wlen, val) = spd
                                contspec.append(wlen, val)

                            spec.fromContinuousSpectrum(contspec)
                            spec.clampNegative()

                    else:
                        MtsLog('Unknown spectrum type.', type(value), value)

                if spec is None:
                    spec = Spectrum(0.0)

                return spec