2
$\begingroup$

I want to write an add-on script for a custom format. I have 99% of the boilerplate by finding a similar format's export and modifying it heavily.

Here is a snippet from my current script.

    #open the file for writing
    file = open(filepath, "wb")

    #get script name from the filepath - may want to check if this is blank at some point int he future?
    base_name = os.path.basename(filepath)
    script_name = os.path.splitext(base_name)[0]

    #branch the output here, depending on what the user wants to export
    #model output
    model_output_string = '';
    model_type_num = 4;
    vert_count = 0;
    #build the model into a string, then write that to the file later
    #start writing the mesh data to the file
    model_type_num = 4;
    if len(mesh.uv_textures) > 0:
    #if the model has a UV map
            uv_layer = mesh.tessface_uv_textures.active
            for face in mesh.tessfaces:
                    faceUV = uv_layer.data[face.index]
                    i=0
                    for index in face.vertices:
                            if len(face.vertices) == 3:
                                    vert = mesh.vertices[index]
                                    model_output_string += '%s     %s     %s     ' % (integer_to_string(vert.co.x), integer_to_string(vert.co.y), integer_to_string(vert.co.z))
                                    model_output_string += '%s     %s\n' % (integer_to_string(faceUV.uv[i][0]), integer_to_string(1-faceUV.uv[i][1]))
                                    vert_count+=1
                                    i+=1
    else:
    #else if the model has no UV map
            uv_layer = mesh.tessface_uv_textures.active
            for face in mesh.tessfaces:
                    for index in face.vertices:
                            if len(face.vertices) == 3:
                                    vert = mesh.vertices[index]
                                    model_output_string += '%s     %s     %s     ' % (integer_to_string(vert.co.x), integer_to_string(vert.co.y), integer_to_string(vert.co.z))
                                    model_output_string += ';     ;\n'
                                    vert_count+=1
    writeString(file, str(vert_count) + '\n')
    writeString(file, model_output_string)
    #end the model output
    #output the file and close it for editing
    file.flush()
    file.close()

Currently it takes every triangle in the scene and generates a file with the following format:

<number of triangles>
<triangle1.vertex1.x> <triangle1.vertex1.y> <triangle1.vertex1.z> <triangle1.vertex1.u> <triangle1.vertex1.v>
<triangle1.vertex2.x> <triangle1.vertex2.y> <triangle1.vertex2.z> <triangle1.vertex2.u> <triangle1.vertex2.v>
<triangle1.vertex3.x> <triangle1.vertex3.y> <triangle1.vertex3.z> <triangle1.vertex3.u> <triangle1.vertex3.v>
.
.
.
<triangleN.vertexN.x> <triangleN.vertexN.y> <triangleN.vertexN.z> <triangleN.vertexN.z> <triangleN.vertexN.v>

This part works fine. The issue is that I also need to save the texture data as well.

Each triangle has a texture, at least, assuming the model being saved is textured. We'll go under the assumption that the models are textured when being saved. It will make my work easier. The format I want to save it in is of the following format. The format dictates the need for two files.

The geometry file:

<number of triangles>
<number of texture batches>
<triangles within batch 1> <texture id of batch 1>
<triangles within batch 2> <texture id of batch 2>
.
.
.
<triangles within batch N> <texture id of batch N>
<triangle1.vertex1.x> <triangle1.vertex1.y> <triangle1.vertex1.z> <triangle1.vertex1.u> <triangle1.vertex1.v>
<triangle1.vertex2.x> <triangle1.vertex2.y> <triangle1.vertex2.z> <triangle1.vertex2.u> <triangle1.vertex2.v>
<triangle1.vertex3.x> <triangle1.vertex3.y> <triangle1.vertex3.z> <triangle1.vertex3.u> <triangle1.vertex3.v>
.
.
.
<triangleN.vertexN.x> <triangleN.vertexN.y> <triangleN.vertexN.z> <triangleN.vertexN.z> <triangleN.vertexN.v>

The texture file:

<number of textures>
<texture filename 1>
<texture filename 2>
.
.
.
<texture filename N>

I have no clue on how to get the filename of the texture for each triangle. What I would like is to be able to take each triangle as I iterate through them and put the filename of the texture associated with that triangle into an array. I can then take that array myself and make sure none of the texture ids are duplicated and that any sequential faces with the same texture get into the same branch. A later "upgrade" might be to take the triangles and sort them into batches so that no texture id is recycled, but that can also be done in the program they are being loaded into (and it would be way easier at that end then in the python model exporter).

I keep hearing something about texture slots material, but honestly I have no clue what that means at all other than that in computer graphics the material dictates data regarding the physical roughness and smoothness of some surface (aka polygon aka triangle). I do not need anything from the material other than the filename, so a solution that does not make it convenient to go back and add other properties to this export is perfectly acceptable. There will not be any support added later for more material stuff. The software this is being loaded into does not support such data, PERIOD.

For those wondering I'm on blender v2.79 as reported by the software. If a convenient solution requires an upgrade, that is perfectly acceptable. I am only on 2.79 because I don't use blender really at all and I just haven't yet bothered to go download the new version to get feature upgrades I'll probably never use.

I've also heard that supposedly this code does not grab everything in the scene. I'm not familiar enough to know whether this is the case or not. If this is the case please let know in the comments. I can make a separate question to look into getting that corrected (assuming I can't fix it myself).

$\endgroup$
  • $\begingroup$ @brockmann But the file format has zero relevance at all??? I only want to know how to access whatever texture corresponds to each face or mesh or whatever textures are applied to... $\endgroup$ – user64742 Dec 12 '19 at 10:59
  • $\begingroup$ So I wrote that comment after waking up in the middle and choosing to check this site. The hilarious result occurred then of me having a wacky dream where this post got closed and three answers it received deleted due to me posting this page once in a discord thread and it then getting closed for reason of “spam”. Because apparently sharing in an external site once counts as an egregious offense. I felt like sharing this since it too weird to pass up. Please don’t vote to close as spam. XD $\endgroup$ – user64742 Dec 12 '19 at 15:23
  • $\begingroup$ Have you looked at the inside of a .obj file (and corresponding .mtl file)? It's almost exactly like the format you've described here, just with xyz on separate lines from uv. The texture filenames are in "materials", and materials are assigned to faces. $\endgroup$ – emackey Dec 13 '19 at 21:10
  • $\begingroup$ @emackey I have seen obj files somewhat, but I’m not sure what good one would do here. There’s definitely no stipulation in my situation at least that anything I’m loading into Blender is in an obj format to start with. Is there any way to view the obj export source code like on a github somewhere? If so, someone explaining how in the world blender is generating the obj material file might work as an answer. $\endgroup$ – user64742 Dec 14 '19 at 1:00
  • $\begingroup$ @emackey I think the script that the asker attempted here might be able to get me a clue as to how to do what I desire. The more trivial problem now is that I don't know how to run their script file. Do you happen to know by any chance? blender.stackexchange.com/questions/80773/… $\endgroup$ – user64742 Dec 14 '19 at 5:10
4
+100
$\begingroup$

There is no such a single file for material in Blender. Material is assigned to face for sure. And Material may contain diffuse color texture and other texture if present.

In the list object.data.polygons, you should find an entry for material index call face.material_index. Then you will need to find what texture you want to export to the file. Again, Blender and all other 3D software use their own format to manage the material. You will need to write your own converter to make it possible to export to file or text in your case.

Also, a similar github project to export obj can be found here

$\endgroup$
  • $\begingroup$ I go into blender. I have a cube. I select it. I go to uv edit on the bottom pane. I change to edit mode on the cube. I drag a texture into the white box that appears. The cube now has a texture. How do I access this via code? There is no material_slot. Blender claims there are 0 material slots??? And surely Blender has a default way of storing the data internally. $\endgroup$ – user64742 Dec 14 '19 at 20:25
  • 1
    $\begingroup$ I suspect I'm not doing something properly in my usage of blender. I may need to edit my question to explain how I'm using it better. $\endgroup$ – user64742 Dec 14 '19 at 21:58
  • $\begingroup$ @TheGreatDuck blender.stackexchange.com/questions/75816/… $\endgroup$ – Hikariztw Dec 14 '19 at 23:19
  • $\begingroup$ Interestingly enough, if I take whatever model I'm interested in and export to obj and then successfully import it back in the script I'm about to post here works. This does imply that there's something I could do to improve stuff, but it will at least work for the moment. I'm still going to accept your answer (unless even better appears) since you are definitely correct that blender can and will use different material storage classes depending on exactly what you are doing - clearly the weird situation with the obj export I just mentioned is evidence enough of that on its own. $\endgroup$ – user64742 Dec 16 '19 at 5:24
  • $\begingroup$ Enjoy the bounty. You've earned it! $\endgroup$ – user64742 Dec 19 '19 at 3:13
1
$\begingroup$

What I have determined is that even within my own editing process there is definitively no single answer on where to get the texture.

There are two means by which texture data is being supplied to any models I might be viewing or editing or what-have-you. The first method is that I directly drag and drop an image file into the uv editing window with a mesh selected. For whatever reason I cannot locate the exact mechanism by which the texture is then accessible to other scripts such as the obj export (which can and does export the texture filename into its material file). This is acceptable, because the obj export works with it.

The other method is to load an obj file with a material file attached to it. With the obj file loaded in the following script appears to print the texture filename for every triangle in the scene. I tested with 5-6 obj models both from me exporting a mesh made by me via dragging and dropping textures and from me loading old obj files I have received from other people.

import bpy

texture_list = []
for obj in bpy.context.scene.objects:
    if obj.type == 'MESH':
        for f in obj.data.polygons:
            mat = obj.material_slots[f.material_index].material
            # Iterate over all the current material's texture slots
            for t in mat.texture_slots:
                # If this is an image texture, with an active image append its name to the list
                if t and t.texture.type == 'IMAGE' and t.texture.image:
                    texture_list.append( t.texture.image.name )
print(texture_list)
print(len(texture_list))

The script above is an amalgam of the scripts given in the answers to this question:

How to get the name of image of "Image Texture" with python?

I wouldn't say I wouldn't say I understand fully how my script works. Basically I took the component of the one (accepted answer's) script of "loop through the objects face data" and merged it with the other script's component of compatible material examination.

This can in turn be restructured to be merged with my scripts current loop. I might post an update later to show the completed thing, but the actual difficulty of "how in the world do I get the texture data?!?!" has been resolved - architectural organization/integration of code is trivial.

I will not be accepting this answer though as it definitely leaves a bad taste in my mouth. I definitely do not like the idea of obj model export somehow working where mine fails and me having to try and accommodate for this, but it is at least viable enough to be worth sharing with anyone having similar pains. I don't condone hacks, but at least this works...

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.