Skip to content
Snippets Groups Projects
render.py 277 KiB
Newer Older
  • Learn to ignore specific revisions
  •             if ob.pov.curveshape in {'prism'}:
                    tabWrite("rotate <90,0,0>\n")
    
                    tabWrite("scale y*-1\n")
    
        #################################################################
    
    Luca Bonavita's avatar
    Luca Bonavita committed
        def exportMeta(metas):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """write all POV blob primitives and Blender Metas to exported file """
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            # TODO - blenders 'motherball' naming is not supported.
    
            if comments and len(metas) >= 1:
    
                file.write("//--Blob objects--\n\n")
    
            # Get groups of metaballs by blender name prefix.
            meta_group = {}
            meta_elems = {}
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            for ob in metas:
    
                if not prefix in meta_group:
                    meta_group[prefix] = ob  # .data.threshold
    
                elems = [
                    (elem, ob)
                    for elem in ob.data.elements
                    if elem.type
                    in {'BALL', 'ELLIPSOID', 'CAPSULE', 'CUBE', 'PLANE'}
                ]
    
                if prefix in meta_elems:
                    meta_elems[prefix].extend(elems)
    
                    meta_elems[prefix] = elems
    
                    tabWrite("\n//dummy sphere to represent empty meta location\n")
    
                    tabWrite(
                        "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n"
                        % (ob.location.x, ob.location.y, ob.location.z)
                    )  # ob.name > povdataname)
    
                # other metaballs
                else:
    
                        if len(meta_elems[mg]) != 0:
                            tabWrite(
                                "blob{threshold %.4g // %s \n"
                                % (ob.data.threshold, mg)
                            )
    
                            for elems in meta_elems[mg]:
                                elem = elems[0]
                                loc = elem.co
                                stiffness = elem.stiffness
                                if elem.use_negative:
    
                                    stiffness = -stiffness
    
                                    tabWrite(
                                        "sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g "
                                        % (
                                            loc.x,
                                            loc.y,
                                            loc.z,
                                            elem.radius,
                                            stiffness,
                                        )
                                    )
                                    writeMatrix(
                                        global_matrix @ elems[1].matrix_world
                                    )
    
                                    tabWrite("}\n")
    
                                    tabWrite(
                                        "sphere{ <%.6g, %.6g, %.6g>,%.4g,%.4g "
                                        % (
                                            loc.x / elem.size_x,
                                            loc.y / elem.size_y,
                                            loc.z / elem.size_z,
                                            elem.radius,
                                            stiffness,
                                        )
                                    )
                                    tabWrite(
                                        "scale <%.6g, %.6g, %.6g>"
                                        % (elem.size_x, elem.size_y, elem.size_z)
                                    )
                                    writeMatrix(
                                        global_matrix @ elems[1].matrix_world
                                    )
    
                                    tabWrite("}\n")
    
                                elif elem.type == 'CAPSULE':
    
                                    tabWrite(
                                        "cylinder{ <%.6g, %.6g, %.6g>,<%.6g, %.6g, %.6g>,%.4g,%.4g "
                                        % (
                                            (loc.x - elem.size_x),
                                            (loc.y),
                                            (loc.z),
                                            (loc.x + elem.size_x),
                                            (loc.y),
                                            (loc.z),
                                            elem.radius,
                                            stiffness,
                                        )
                                    )
                                    # tabWrite("scale <%.6g, %.6g, %.6g>" % (elem.size_x, elem.size_y, elem.size_z))
                                    writeMatrix(
                                        global_matrix @ elems[1].matrix_world
                                    )
    
                                    tabWrite(
                                        "cylinder { -x*8, +x*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale  <1/4,1,1> scale <%.6g, %.6g, %.6g>\n"
                                        % (
                                            elem.radius * 2.0,
                                            stiffness / 4.0,
                                            loc.x,
                                            loc.y,
                                            loc.z,
                                            elem.size_x,
                                            elem.size_y,
                                            elem.size_z,
                                        )
                                    )
                                    writeMatrix(
                                        global_matrix @ elems[1].matrix_world
                                    )
    
                                    tabWrite(
                                        "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n"
                                        % (
                                            elem.radius * 2.0,
                                            stiffness / 4.0,
                                            loc.x,
                                            loc.y,
                                            loc.z,
                                            elem.size_x,
                                            elem.size_y,
                                            elem.size_z,
                                        )
                                    )
                                    writeMatrix(
                                        global_matrix @ elems[1].matrix_world
                                    )
    
                                    tabWrite("}\n")
    
                                    tabWrite(
                                        "cylinder { -z*8, +z*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1,1/4> scale <%.6g, %.6g, %.6g>\n"
                                        % (
                                            elem.radius * 2.0,
                                            stiffness / 4.0,
                                            loc.x,
                                            loc.y,
                                            loc.z,
                                            elem.size_x,
                                            elem.size_y,
                                            elem.size_z,
                                        )
                                    )
                                    writeMatrix(
                                        global_matrix @ elems[1].matrix_world
                                    )
    
                                    tabWrite(
                                        "cylinder { -x*8, +x*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale  <1/4,1,1> scale <%.6g, %.6g, %.6g>\n"
                                        % (
                                            elem.radius * 2.0,
                                            stiffness / 4.0,
                                            loc.x,
                                            loc.y,
                                            loc.z,
                                            elem.size_x,
                                            elem.size_y,
                                            elem.size_z,
                                        )
                                    )
                                    writeMatrix(
                                        global_matrix @ elems[1].matrix_world
                                    )
    
                                    tabWrite(
                                        "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n"
                                        % (
                                            elem.radius * 2.0,
                                            stiffness / 4.0,
                                            loc.x,
                                            loc.y,
                                            loc.z,
                                            elem.size_x,
                                            elem.size_y,
                                            elem.size_z,
                                        )
                                    )
                                    writeMatrix(
                                        global_matrix @ elems[1].matrix_world
                                    )
    
                                    tabWrite("}\n")
    
                                material = elems[1].data.materials[
                                    0
                                ]  # lame! - blender cant do enything else.
    
                            except:
                                material = None
                            if material:
                                diffuse_color = material.diffuse_color
    
                                if (
                                    material.use_transparency
                                    and material.transparency_method == 'RAYTRACE'
                                ):
                                    povFilter = (
                                        material.pov_raytrace_transparency.filter
                                        * (1.0 - material.alpha)
                                    )
    
                                    trans = (1.0 - material.pov.alpha) - povFilter
    
                                else:
                                    povFilter = 0.0
                                material_finish = materialNames[material.name]
    
                                tabWrite(
                                    "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n"
                                    % (
                                        diffuse_color[0],
                                        diffuse_color[1],
                                        diffuse_color[2],
                                        povFilter,
                                        trans,
                                    )
                                )
                                tabWrite(
                                    "finish{%s} " % safety(material_finish, Level=2)
                                )
    
                                tabWrite(
                                    "pigment{srgb 1} finish{%s} "
                                    % (safety(DEF_MAT_NAME, Level=2))
                                )
    
                                # writeObjectMaterial(material, elems[1])
                                tabWrite(
                                    "radiosity{importance %3g}\n"
                                    % ob.pov.importance_value
                                )
    
                                tabWrite("}\n\n")  # End of Metaball block
    
    Luca Bonavita's avatar
    Luca Bonavita committed
                meta = ob.data
    
    
                # important because no elements will break parsing.
    
                elements = [elem for elem in meta.elements if elem.type in {'BALL', 'ELLIPSOID'}]
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    tabWrite("blob {\n")
                    tabWrite("threshold %.4g\n" % meta.threshold)
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                        material = meta.materials[0]  # lame! - blender cant do enything else.
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    for elem in elements:
                        loc = elem.co
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                        stiffness = elem.stiffness
                        if elem.use_negative:
                            stiffness = - stiffness
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                        if elem.type == 'BALL':
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                            tabWrite("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" %
    
                                     (loc.x, loc.y, loc.z, elem.radius, stiffness))
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                            # After this wecould do something simple like...
    
                            # except we'll write the color
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                        elif elem.type == 'ELLIPSOID':
                            # location is modified by scale
    
                            tabWrite("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" %
                                     (loc.x / elem.size_x,
                                      loc.y / elem.size_y,
                                      loc.z / elem.size_z,
    
                            tabWrite("scale <%.6g, %.6g, %.6g> \n" %
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    if material:
                        diffuse_color = material.diffuse_color
    
                        if material.use_transparency and material.transparency_method == 'RAYTRACE':
    
                            povFilter = material.pov_raytrace_transparency.filter * (1.0 - material.alpha)
    
                            trans = (1.0 - material.pov.alpha) - povFilter
    
                            povFilter = 0.0
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                        material_finish = materialNames[material.name]
    
                        tabWrite("pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" %
    
                                 (diffuse_color[0], diffuse_color[1], diffuse_color[2],
                                  povFilter, trans))
    
                        tabWrite("finish {%s}\n" % safety(material_finish, Level=2))
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                        # Write the finish last.
                        tabWrite("finish {%s}\n" % (safety(DEF_MAT_NAME, Level=2)))
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    writeMatrix(global_matrix @ ob.matrix_world)
    
                    # Importance for radiosity sampling added here
    
                    # importance > ob.pov.importance_value
    
                    tabWrite("importance %3g \n" % importance)
                    tabWrite("}\n")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
    
    
                    tabWrite("}\n")  # End of Metaball block
    
                    if comments and len(metas) >= 1:
    
        def exportMeshes(scene, sel, csg):
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
            """write all meshes as POV mesh2{} syntax to exported file """
    
            #        obmatslist = []
            #        def hasUniqueMaterial():
            #            # Grab materials attached to object instances ...
            #            if hasattr(ob, 'material_slots'):
            #                for ms in ob.material_slots:
            #                    if ms.material is not None and ms.link == 'OBJECT':
            #                        if ms.material in obmatslist:
            #                            return False
            #                        else:
            #                            obmatslist.append(ms.material)
            #                            return True
            #        def hasObjectMaterial(ob):
            #            # Grab materials attached to object instances ...
            #            if hasattr(ob, 'material_slots'):
            #                for ms in ob.material_slots:
            #                    if ms.material is not None and ms.link == 'OBJECT':
            #                        # If there is at least one material slot linked to the object
            #                        # and not the data (mesh), always create a new, "private" data instance.
            #                        return True
            #            return False
    
            # For objects using local material(s) only!
    
            # This is a mapping between a tuple (dataname, materialnames, ...), and the POV dataname.
    
            # As only objects using:
            #     * The same data.
            #     * EXACTLY the same materials, in EXACTLY the same sockets.
    
            # ... can share a same instance in POV export.
    
            def checkObjectMaterials(ob, name, dataname):
                if hasattr(ob, 'material_slots'):
                    has_local_mats = False
                    key = [dataname]
                    for ms in ob.material_slots:
    
                        if ms.material is not None:
    
                            key.append(ms.material.name)
                            if ms.link == 'OBJECT' and not has_local_mats:
                                has_local_mats = True
                        else:
    
                            # Even if the slot is empty, it is important to grab it...
    
                            key.append("")
                    if has_local_mats:
                        # If this object uses local material(s), lets find if another object
                        # using the same data and exactly the same list of materials
    
                        # (in the same slots) has already been processed...
    
                        # Note that here also, we use object name as new, unique dataname for Pov.
    
                        key = tuple(key)  # Lists are not hashable...
    
                        if key not in obmats2data:
                            obmats2data[key] = name
                        return obmats2data[key]
                return None
    
            data_ref = {}
    
            def store(scene, ob, name, dataname, matrix):
                # The Object needs to be written at least once but if its data is
                # already in data_ref this has already been done.
    
                # This func returns the "povray" name of the data, or None
    
                # if no writing is needed.
                if ob.is_modified(scene, 'RENDER'):
                    # Data modified.
                    # Create unique entry in data_ref by using object name
                    # (always unique in Blender) as data name.
                    data_ref[name] = [(name, MatrixAsPovString(matrix))]
                    return name
                # Here, we replace dataname by the value returned by checkObjectMaterials, only if
                # it is not evaluated to False (i.e. only if the object uses some local material(s)).
                dataname = checkObjectMaterials(ob, name, dataname) or dataname
                if dataname in data_ref:
                    # Data already known, just add the object instance.
                    data_ref[dataname].append((name, MatrixAsPovString(matrix)))
                    # No need to write data
                    return None
                else:
                    # Data not yet processed, create a new entry in data_ref.
                    data_ref[dataname] = [(name, MatrixAsPovString(matrix))]
                    return dataname
    
            def exportSmoke(smoke_obj_name):
    
                # if LuxManager.CurrentScene.name == 'preview':
                # return 1, 1, 1, 1.0
                # else:
    
                flowtype = -1
                smoke_obj = bpy.data.objects[smoke_obj_name]
                domain = None
    
                # Search smoke domain target for smoke modifiers
                for mod in smoke_obj.modifiers:
                    if mod.name == 'Smoke':
                        if mod.smoke_type == 'FLOW':
                            if mod.flow_settings.smoke_flow_type == 'BOTH':
                                flowtype = 2
                            else:
                                if mod.flow_settings.smoke_flow_type == 'SMOKE':
                                    flowtype = 0
                                else:
                                    if mod.flow_settings.smoke_flow_type == 'FIRE':
                                        flowtype = 1
    
                        if mod.smoke_type == 'DOMAIN':
                            domain = smoke_obj
                            smoke_modifier = mod
    
                eps = 0.000001
                if domain is not None:
    
                    # if bpy.app.version[0] >= 2 and bpy.app.version[1] >= 71:
    
                    # Blender version 2.71 supports direct access to smoke data structure
                    set = mod.domain_settings
                    channeldata = []
                    for v in set.density_grid:
                        channeldata.append(v.real)
                        print(v.real)
                    ## Usage en voxel texture:
                    # channeldata = []
                    # if channel == 'density':
    
                    # for v in set.density_grid:
                    # channeldata.append(v.real)
    
    
                    # if channel == 'fire':
    
                    # for v in set.flame_grid:
                    # channeldata.append(v.real)
    
    
                    resolution = set.resolution_max
                    big_res = []
                    big_res.append(set.domain_resolution[0])
                    big_res.append(set.domain_resolution[1])
                    big_res.append(set.domain_resolution[2])
    
                    if set.use_high_resolution:
                        big_res[0] = big_res[0] * (set.amplify + 1)
                        big_res[1] = big_res[1] * (set.amplify + 1)
                        big_res[2] = big_res[2] * (set.amplify + 1)
                    # else:
    
                    # p = []
                    ##gather smoke domain settings
                    # BBox = domain.bound_box
                    # p.append([BBox[0][0], BBox[0][1], BBox[0][2]])
                    # p.append([BBox[6][0], BBox[6][1], BBox[6][2]])
                    # set = mod.domain_settings
                    # resolution = set.resolution_max
                    # smokecache = set.point_cache
                    # ret = read_cache(smokecache, set.use_high_resolution, set.amplify + 1, flowtype)
                    # res_x = ret[0]
                    # res_y = ret[1]
                    # res_z = ret[2]
                    # density = ret[3]
                    # fire = ret[4]
    
                    # if res_x * res_y * res_z > 0:
                    ##new cache format
                    # big_res = []
                    # big_res.append(res_x)
                    # big_res.append(res_y)
                    # big_res.append(res_z)
                    # else:
                    # max = domain.dimensions[0]
                    # if (max - domain.dimensions[1]) < -eps:
                    # max = domain.dimensions[1]
    
                    # if (max - domain.dimensions[2]) < -eps:
                    # max = domain.dimensions[2]
    
                    # big_res = [int(round(resolution * domain.dimensions[0] / max, 0)),
                    # int(round(resolution * domain.dimensions[1] / max, 0)),
                    # int(round(resolution * domain.dimensions[2] / max, 0))]
    
                    # if set.use_high_resolution:
                    # big_res = [big_res[0] * (set.amplify + 1), big_res[1] * (set.amplify + 1),
                    # big_res[2] * (set.amplify + 1)]
    
                    # if channel == 'density':
                    # channeldata = density
    
                    # if channel == 'fire':
                    # channeldata = fire
    
                    # sc_fr = '%s/%s/%s/%05d' % (efutil.export_path, efutil.scene_filename(), bpy.context.scene.name, bpy.context.scene.frame_current)
                    #               if not os.path.exists( sc_fr ):
                    #                   os.makedirs(sc_fr)
                    #
                    #               smoke_filename = '%s.smoke' % bpy.path.clean_name(domain.name)
                    #               smoke_path = '/'.join([sc_fr, smoke_filename])
                    #
                    #               with open(smoke_path, 'wb') as smoke_file:
                    #                   # Binary densitygrid file format
                    #                   #
                    #                   # File header
                    #                   smoke_file.write(b'SMOKE')        #magic number
                    #                   smoke_file.write(struct.pack('<I', big_res[0]))
                    #                   smoke_file.write(struct.pack('<I', big_res[1]))
                    #                   smoke_file.write(struct.pack('<I', big_res[2]))
                    # Density data
                    #                   smoke_file.write(struct.pack('<%df'%len(channeldata), *channeldata))
                    #
                    #               LuxLog('Binary SMOKE file written: %s' % (smoke_path))
    
                    # return big_res[0], big_res[1], big_res[2], channeldata
    
                    mydf3 = df3.df3(big_res[0], big_res[1], big_res[2])
    
                    sim_sizeX, sim_sizeY, sim_sizeZ = mydf3.size()
                    for x in range(sim_sizeX):
                        for y in range(sim_sizeY):
                            for z in range(sim_sizeZ):
    
                                mydf3.set(
                                    x,
                                    y,
                                    z,
                                    channeldata[
                                        ((z * sim_sizeY + y) * sim_sizeX + x)
                                    ],
                                )
    
    
                    mydf3.exportDF3(smokePath)
                    print('Binary smoke.df3 file written in preview directory')
                    if comments:
                        file.write("\n//--Smoke--\n\n")
    
    
                    # Note: We start with a default unit cube.
                    #       This is mandatory to read correctly df3 data - otherwise we could just directly use bbox
                    #       coordinates from the start, and avoid scale/translate operations at the end...
                    file.write("box{<0,0,0>, <1,1,1>\n")
    
                    file.write("    pigment{ rgbt 1 }\n")
                    file.write("    hollow\n")
                    file.write("    interior{ //---------------------\n")
                    file.write("        media{ method 3\n")
    
                    file.write(
                        "               emission <1,1,1>*1\n"
                    )  # 0>1 for dark smoke to white vapour
    
                    file.write("               scattering{ 1, // Type\n")
    
                    file.write("                  <1,1,1>*0.1\n")
    
                    file.write("                } // end scattering\n")
    
                    file.write(
                        "                density{density_file df3 \"%s\"\n"
                        % (smokePath)
                    )
    
                    file.write("                        color_map {\n")
                    file.write("                        [0.00 rgb 0]\n")
                    file.write("                        [0.05 rgb 0]\n")
                    file.write("                        [0.20 rgb 0.2]\n")
                    file.write("                        [0.30 rgb 0.6]\n")
                    file.write("                        [0.40 rgb 1]\n")
                    file.write("                        [1.00 rgb 1]\n")
                    file.write("                       } // end color_map\n")
                    file.write("               } // end of density\n")
    
                    file.write(
                        "               samples %i // higher = more precise\n"
                        % resolution
                    )
                    file.write(
                        "         } // end of media --------------------------\n"
                    )
    
                    file.write("    } // end of interior\n")
    
    
                    # START OF TRANSFORMATIONS
    
                    # Size to consider here are bbox dimensions (i.e. still in object space, *before* applying
                    # loc/rot/scale and other transformations (like parent stuff), aka matrix_world).
                    bbox = smoke_obj.bound_box
    
                    dim = [
                        abs(bbox[6][0] - bbox[0][0]),
                        abs(bbox[6][1] - bbox[0][1]),
                        abs(bbox[6][2] - bbox[0][2]),
                    ]
    
    
                    # We scale our cube to get its final size and shapes but still in *object* space (same as Blender's bbox).
                    file.write("scale<%.6g,%.6g,%.6g>\n" % (dim[0], dim[1], dim[2]))
    
                    # We offset our cube such that (0,0,0) coordinate matches Blender's object center.
    
                    file.write(
                        "translate<%.6g,%.6g,%.6g>\n"
                        % (bbox[0][0], bbox[0][1], bbox[0][2])
                    )
    
    
                    # We apply object's transformations to get final loc/rot/size in world space!
                    # Note: we could combine the two previous transformations with this matrix directly...
    
                    writeMatrix(global_matrix @ smoke_obj.matrix_world)
    
    
                    # END OF TRANSFORMATIONS
    
    
                    file.write("}\n")
    
                    # file.write("               interpolate 1\n")
                    # file.write("               frequency 0\n")
                    # file.write("   }\n")
                    # file.write("}\n")
    
    Luca Bonavita's avatar
    Luca Bonavita committed
            for ob in sel:
    
                # subtract original from the count of their instances as were not counted before 2.8
    
                if not (ob.is_instancer and ob.original != ob):
                    ob_num += 1
    
                    # XXX I moved all those checks here, as there is no need to compute names
                    #     for object we won't export here!
    
                    if ob.type in {
                        'LIGHT',
                        'CAMERA',  #'EMPTY', #empties can bear dupligroups
                        'META',
                        'ARMATURE',
                        'LATTICE',
                    }:
    
                    for mod in ob.modifiers:
                        if mod and hasattr(mod, 'smoke_type'):
    
                            smokeFlag = True
                            if mod.smoke_type == 'DOMAIN':
    
                                exportSmoke(ob.name)
    
                            break  # don't render domain mesh or flow emitter mesh, skip to next object.
    
                    if not smokeFlag:
                        # Export Hair
                        renderEmitter = True
                        if hasattr(ob, 'particle_systems'):
                            renderEmitter = False
    
                            if ob.show_instancer_for_render:
    
                                renderEmitter = True
    
                            for pSys in ob.particle_systems:
    
                                for mod in [
                                    m
                                    for m in ob.modifiers
                                    if (m is not None)
                                    and (m.type == 'PARTICLE_SYSTEM')
                                ]:
                                    if (
                                        (pSys.settings.render_type == 'PATH')
                                        and mod.show_render
                                        and (pSys.name == mod.particle_system.name)
                                    ):
    
                                        tstart = time.time()
    
                                        texturedHair = 0
                                        if (
                                            ob.material_slots[
                                                pSys.settings.material - 1
                                            ].material
                                            and ob.active_material is not None
                                        ):
                                            pmaterial = ob.material_slots[
                                                pSys.settings.material - 1
                                            ].material
    
                                            for th in pmaterial.texture_slots:
                                                if th and th.use:
    
                                                    if (
                                                        (
                                                            th.texture.type
                                                            == 'IMAGE'
                                                            and th.texture.image
                                                        )
                                                        or th.texture.type
                                                        != 'IMAGE'
                                                    ):
    
                                                        if th.use_map_color_diffuse:
    
                                            if pmaterial.strand.use_blender_units:
    
                                                strandStart = (
                                                    pmaterial.strand.root_size
                                                )
                                                strandEnd = (
                                                    pmaterial.strand.tip_size
                                                )
    
                                                strandShape = pmaterial.strand.shape
                                            else:  # Blender unit conversion
    
                                                strandStart = (
                                                    pmaterial.strand.root_size
                                                    / 200.0
                                                )
                                                strandEnd = (
                                                    pmaterial.strand.tip_size
                                                    / 200.0
                                                )
    
                                                strandShape = pmaterial.strand.shape
                                        else:
    
                                            pmaterial = (
                                                "default"
                                            )  # No material assigned in blender, use default one
    
                                            strandStart = 0.01
                                            strandEnd = 0.01
                                            strandShape = 0.0
                                        # Set the number of particles to render count rather than 3d view display
    
                                        # pSys.set_resolution(scene, ob, 'RENDER') # DEPRECATED
    
                                        # When you render, the entire dependency graph will be
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                        # evaluated at render resolution, including the particles.
    
                                        # In the viewport it will be at viewport resolution.
                                        # So there is no need fo render engines to use this function anymore,
    
    Maurice Raybaud's avatar
    Maurice Raybaud committed
                                        # it's automatic now.
    
                                        steps = pSys.settings.display_step
    
                                        steps = (
                                            3 ** steps
                                        )  # or (power of 2 rather than 3) + 1 # Formerly : len(particle.hair_keys)
    
                                        totalNumberOfHairs = (
                                            pSys.settings.count
                                            + pSys.settings.rendered_child_count
                                        )
                                        # hairCounter = 0
                                        file.write(
                                            '#declare HairArray = array[%i] {\n'
                                            % totalNumberOfHairs
                                        )
    
                                        for pindex in range(0, totalNumberOfHairs):
    
    
                                            # if particle.is_exist and particle.is_visible:
                                            # hairCounter += 1
                                            # controlPointCounter = 0
                                            # Each hair is represented as a separate sphere_sweep in POV-Ray.
    
                                            file.write('sphere_sweep{')
                                            if pSys.settings.use_hair_bspline:
                                                file.write('b_spline ')
                                                file.write(
                                                    '%i,\n' % (steps + 2)
                                                )  # +2 because the first point needs tripling to be more than a handle in POV
                                            else:
                                                file.write('linear_spline ')
                                                file.write('%i,\n' % (steps))
                                            # changing world coordinates to object local coordinates by multiplying with inverted matrix
                                            initCo = ob.matrix_world.inverted() @ (
                                                pSys.co_hair(
                                                    ob, particle_no=pindex, step=0
                                                )
                                            )
                                            if (
                                                ob.material_slots[
                                                    pSys.settings.material - 1
                                                ].material
                                                and ob.active_material is not None
                                            ):
                                                pmaterial = ob.material_slots[
                                                    pSys.settings.material - 1
                                                ].material
                                                for th in pmaterial.texture_slots:
                                                    if (
                                                        th
                                                        and th.use
                                                        and th.use_map_color_diffuse
                                                    ):
                                                        # treat POV textures as bitmaps
                                                        if (
                                                            th.texture.type
                                                            == 'IMAGE'
                                                            and th.texture.image
                                                            and th.texture_coords
                                                            == 'UV'
                                                            and ob.data.uv_textures
                                                            is not None
                                                        ):  # or (th.texture.pov.tex_pattern_type != 'emulator' and th.texture_coords == 'UV' and ob.data.uv_textures is not None):
                                                            image = th.texture.image
                                                            image_width = image.size[
                                                                0
                                                            ]
                                                            image_height = image.size[
                                                                1
                                                            ]
                                                            image_pixels = image.pixels[
                                                                :
                                                            ]
                                                            uv_co = pSys.uv_on_emitter(
                                                                mod,
                                                                pSys.particles[
                                                                    pindex
                                                                ],
                                                                pindex,
                                                                0,
                                                            )
                                                            x_co = round(
                                                                uv_co[0]
                                                                * (image_width - 1)
                                                            )
                                                            y_co = round(
                                                                uv_co[1]
                                                                * (image_height - 1)
                                                            )
                                                            pixelnumber = (
                                                                image_width * y_co
                                                            ) + x_co
                                                            r = image_pixels[
                                                                pixelnumber * 4
                                                            ]
                                                            g = image_pixels[
                                                                pixelnumber * 4 + 1
                                                            ]
                                                            b = image_pixels[
                                                                pixelnumber * 4 + 2
                                                            ]
                                                            a = image_pixels[
                                                                pixelnumber * 4 + 3
                                                            ]
                                                            initColor = (r, g, b, a)
                                                        else:
                                                            # only overwrite variable for each competing texture for now
                                                            initColor = th.texture.evaluate(
                                                                (
                                                                    initCo[0],
                                                                    initCo[1],
                                                                    initCo[2],
                                                                )
                                                            )
                                            for step in range(0, steps):
                                                co = ob.matrix_world.inverted() @ (
                                                    pSys.co_hair(
                                                        ob,
                                                        particle_no=pindex,
                                                        step=step,
                                                    )
                                                )
                                                # for controlPoint in particle.hair_keys:
                                                if pSys.settings.clump_factor != 0:
                                                    hDiameter = (
                                                        pSys.settings.clump_factor
                                                        / 200.0
                                                        * random.uniform(0.5, 1)
                                                    )
                                                elif step == 0:
                                                    hDiameter = strandStart
    
                                                    hDiameter += (
                                                        strandEnd - strandStart
                                                    ) / (
                                                        pSys.settings.display_step
                                                        + 1
                                                    )  # XXX +1 or not?
                                                if (
                                                    step == 0
                                                    and pSys.settings.use_hair_bspline
                                                ):
                                                    # Write three times the first point to compensate pov Bezier handling
                                                    file.write(
                                                        '<%.6g,%.6g,%.6g>,%.7g,\n'
                                                        % (
                                                            co[0],
                                                            co[1],
                                                            co[2],
                                                            abs(hDiameter),
                                                        )
                                                    )
                                                    file.write(
                                                        '<%.6g,%.6g,%.6g>,%.7g,\n'
                                                        % (
                                                            co[0],
                                                            co[1],
                                                            co[2],
                                                            abs(hDiameter),
                                                        )
                                                    )
                                                    # file.write('<%.6g,%.6g,%.6g>,%.7g' % (particle.location[0], particle.location[1], particle.location[2], abs(hDiameter))) # Useless because particle location is the tip, not the root.
                                                    # file.write(',\n')
                                                # controlPointCounter += 1
                                                # totalNumberOfHairs += len(pSys.particles)# len(particle.hair_keys)
    
                                                # Each control point is written out, along with the radius of the
                                                # hair at that point.
                                                file.write(
                                                    '<%.6g,%.6g,%.6g>,%.7g'
                                                    % (
                                                        co[0],
                                                        co[1],
                                                        co[2],
                                                        abs(hDiameter),
                                                    )
                                                )
    
                                                # All coordinates except the last need a following comma.
    
                                                if step != steps - 1:
    
                                                    file.write(',\n')
                                                else:
    
                                                    if texturedHair:
                                                        # Write pigment and alpha (between Pov and Blender alpha 0 and 1 are reversed)
                                                        file.write(
                                                            '\npigment{ color srgbf < %.3g, %.3g, %.3g, %.3g> }\n'
                                                            % (
                                                                initColor[0],
                                                                initColor[1],
                                                                initColor[2],
                                                                1.0 - initColor[3],
                                                            )
                                                        )
                                                    # End the sphere_sweep declaration for this hair
                                                    file.write('}\n')
    
                                            # All but the final sphere_sweep (each array element) needs a terminating comma.
                                            if pindex != totalNumberOfHairs:
                                                file.write(',\n')
                                            else:
                                                file.write('\n')
    
                                        # End the array declaration.
    
                                        file.write('}\n')
    
                                        if not texturedHair:
                                            # Pick up the hair material diffuse color and create a default POV-Ray hair texture.
    
                                            file.write('#ifndef (HairTexture)\n')
    
                                            file.write(
                                                '  #declare HairTexture = texture {\n'
                                            )
                                            file.write(
                                                '    pigment {srgbt <%s,%s,%s,%s>}\n'
                                                % (
                                                    pmaterial.diffuse_color[0],
                                                    pmaterial.diffuse_color[1],
                                                    pmaterial.diffuse_color[2],
                                                    (
                                                        pmaterial.strand.width_fade
                                                        + 0.05
                                                    ),
                                                )
                                            )
    
                                            file.write('  }\n')
                                            file.write('#end\n')
                                            file.write('\n')
    
                                        # Dynamically create a union of the hairstrands (or a subset of them).
                                        # By default use every hairstrand, commented line is for hand tweaking test renders.
    
                                        file.write(
                                            '//Increasing HairStep divides the amount of hair for test renders.\n'
                                        )
                                        file.write(
                                            '#ifndef(HairStep) #declare HairStep = 1; #end\n'
                                        )
    
                                        file.write('union{\n')
                                        file.write('  #local I = 0;\n')
    
                                        file.write(
                                            '  #while (I < %i)\n'
                                            % totalNumberOfHairs
                                        )
    
                                        file.write('    object {HairArray[I]')
                                        if not texturedHair:
                                            file.write(' texture{HairTexture}\n')
                                        else:
                                            file.write('\n')
                                        # Translucency of the hair:
                                        file.write('        hollow\n')
                                        file.write('        double_illuminate\n')
                                        file.write('        interior {\n')
                                        file.write('            ior 1.45\n')
                                        file.write('            media {\n')
    
                                        file.write(
                                            '                scattering { 1, 10*<0.73, 0.35, 0.15> /*extinction 0*/ }\n'
                                        )
                                        file.write(
                                            '                absorption 10/<0.83, 0.75, 0.15>\n'
                                        )
    
                                        file.write('                samples 1\n')
                                        file.write('                method 2\n')
    
                                        file.write(
                                            '                density {cylindrical\n'
                                        )
                                        file.write(
                                            '                    color_map {\n'
                                        )
                                        file.write(
                                            '                        [0.0 rgb <0.83, 0.45, 0.35>]\n'
                                        )
                                        file.write(
                                            '                        [0.5 rgb <0.8, 0.8, 0.4>]\n'
                                        )
                                        file.write(
                                            '                        [1.0 rgb <1,1,1>]\n'
                                        )
    
                                        file.write('                    }\n')
                                        file.write('                }\n')
                                        file.write('            }\n')
                                        file.write('        }\n')
                                        file.write('    }\n')
    
                                        file.write('    #local I = I + HairStep;\n')
                                        file.write('  #end\n')
    
                                        writeMatrix(global_matrix @ ob.matrix_world)
    
                                        file.write('}')
    
                                        print(
                                            'Totals hairstrands written: %i'
                                            % totalNumberOfHairs