Trying to update Blender ASE exporter for Quake 3 / Doom 3 to 2.80 and failing. Help is needed

I have an addon that works fine under 2.79b, but I am migrated to 2.80 and the author of the addon is nowhere to be found

I followed instructions for upgrading 2.7x addons to 2.80 and hit a wall :confused:

Here is the error I am getting:

and here is my partially converted addon:
https://pastebin.com/meLtJrA5

I am not really sure what’s going on as I am not really a programmer :confused:

Thanks beforehand

UPDATE:

ASE file format specs https://wiki.beyondunreal.com/Legacy:ASE_File_Format

Updated add-on: https://pastebin.com/xPV85fQz (it seems to be working, but still has some quirks)

Hi, you don’t need to register, unregister all classes, only those bellow :

The Blender Python api allows integration for:

bpy.types.Panel
bpy.types.Menu
bpy.types.Operator
bpy.types.PropertyGroup
bpy.types.KeyingSet
bpy.types.RenderEngine

Extract from https://docs.blender.org/api/blender2.8/info_overview.html.

Thanks @Elreenys

Add-on: https://pastebin.com/iMN0HYzG

Now it’s loading fine, but when used, it doesn’t show the UI dialog it used to have. If I simply export mesh as-is it gives out bunch of errors (seems like related to UI and materials):

As far as materials, all I need is the name. I don’t think the ASE file format stores anything like “specular hardness” :confused:

UPDATE: I think I got a rid of that “specular_hardness” error. Now I just need some boost figuring out UI stuff.

Add-on: https://pastebin.com/9ifj2g0q

Alright, I think I fixed all those issues and now I am getting this:

There seems to be an on going porting effort of ASE export for Blender 2.8x on Github, in case you haven’t seen it. I haven’t tested it myself yet, though, but maybe it helps you.

Yep and not only I can’t even load that add-on, it exports empty ASE file. In fact, that add-on is based on older version of original 2.7x add-on. Mine is based on most recent version that was designed to work with Quake 3 / Doom 3 and it was never publicly released.

I think I got it working now:

https://pastebin.com/sxsg63Lh

It exports ASE, but some stuff is exported wrong. One thing - it doesn’t apply modifiers (it did in 2.79). It also somehow miscalculates MESH_NUMTVFACES :confused:

This is the reference output ASE file (from 2.79b):

This is 2.80 scene (same as in 2.79):

http://pasteall.org/blend/index.php?id=52443

In the "Essentials: I only use Export Copy option for now. Applying all transforms checkboxes needs to be checked. Scale = 1.

As far as I know, when you get mesh data from an object and you want to have modifiers applied, you now need to get the depsgraph and then the evaluated object. Have a look at this thread and here for more info.

Thanks, although it’s a bit over my head for the moment :confused:

This is more pressing issue: self.numtvfaces = len( object.data.uv_layer_stencil.data ) returns a way higher number than it should be :confused:

The scene is basically a UV mapped triangulated cube. So 2.79’s add-on writes into ASE file the following:

*MESH_NUMTVFACES 12

However, same add-on for 2.80 writes into ASE file this:

*MESH_NUMTVFACES 36

I am not sure why :confused:

Not the answer you’re looking for, but you might want to consider creating your mesh in 2.8 and then bring that into a build that doesn’t break the exporter script. It’s not that bad; I still have 2.49b laying around just in case I ever want to go back to working with .nif files someday.

That’s the last resort :slight_smile: I’d rather have working add-on for 2.80.

Hmm… It looks like the issue with *MESH_NUMTVFACES might be related to missing vertex colors. To test, I added vertex color to the mesh and Blender gave me some hell on export. I fixed whatever I could, but then turned out that tessface_vertex_colors is depreciated.

Here is the chunk of code with that stuff:

 # Blender 2.63+
    bpy.ops.object.mode_set( mode = 'OBJECT' )
    object.data.calc_loop_triangles()

    for tri in object.data.loop_triangles:
        temp = object.data.tessface_vertex_colors[0].data[tri.index].color1
        self.vertlist.append( cCVert( self.index, temp ) )
        temp = object.data.tessface_vertex_colors[0].data[tri.index].color2
        self.vertlist.append( cCVert( self.index + 1, temp ) )
        temp = object.data.tessface_vertex_colors[0].data[tri.index].color3
        self.vertlist.append( cCVert( self.index + 2, temp ) )
        self.index += 3

    self.length = len( self.vertlist )

How do I convert it to 2.80 digestible code ?

Never mind, I think this should do:

    bpy.ops.object.mode_set( mode = 'OBJECT' )
    object.data.calc_loop_triangles()

    for face in object.data.loop_triangles:
        temp = object.data.vertex_colors[0].data[face.index + 1].color
        self.vertlist.append( cCVert( self.index, temp ) )
        temp = object.data.vertex_colors[0].data[face.index + 2].color
        self.vertlist.append( cCVert( self.index + 1, temp ) )
        temp = object.data.vertex_colors[0].data[face.index + 3].color
        self.vertlist.append( cCVert( self.index + 2, temp ) )
        self.index += 3

    self.length = len( self.vertlist )

Although that didn’t fix the issue with *MESH_NUMTVFACES

Updated version of the add-on:

https://pastebin.com/X5jJcRdn

I think I know what’s going on here. I fixed *MESH_NUMTVFACES per ASE specs.

*MESH_NUMTVERTEX is 36 because somehow every triangle on the UV map is treated as separate isle. So adjacent vertices aren’t accounted for as 1 vertex. I don’t really know whether it’s how it is, or if 2 adjacent vertices should be counted as 1.

This is the code that does all this mambo-jumbo:

class cTVertlist:
def __init__( self, object ):
    self.vertlist = []

    # update tessface
    mesh = bpy.context.object.data
    mesh.update()
    uv_layers_count = len( mesh.uv_layers )
    mesh.calc_loop_triangles()

    for index, face in enumerate( mesh.loop_triangles ):
        if len( mesh.uv_layers ) == 0:
            raise cError( "cError:  No UV texture data for " + object.name )
        else:
            temp = cTVert( ( index * 3 ), mesh.uv_layers[mesh.uv_layers.active_index].data[face.index].uv )
            self.vertlist.append( temp )
            temp = cTVert( ( index * 3 ) + 1, mesh.uv_layers[mesh.uv_layers.active_index].data[face.index + 1].uv )
            self.vertlist.append( temp )
            temp = cTVert( ( index * 3 ) + 2, mesh.uv_layers[mesh.uv_layers.active_index].data[face.index + 2].uv )
            self.vertlist.append( temp )

    self.length = len( self.vertlist )

def dump( self ):
    temp = ''
    for x in self.vertlist:
        temp += str( x )
    return temp

def __repr__( self ):
    return '''{{\n{0}\t\t}}'''.format( self.dump() )
class cTVert:
def __init__( self, index, coord ):
    self.index = index
    self.u = aseFloat( coord[0] )
    self.v = aseFloat( coord[1] )

def __repr__( self ):
    return '''\t\t\t*MESH_TVERT {0} {1} {2} 0.0000\n'''.format( self.index, self.u, self.v )

New version of the add-on: https://pastebin.com/198HRXAj

UPDATE: Apparently it’s how it works - no shared vertices in UV space.

Alright, here is the next question. This code below used to work in 2.79 (afaik). If “triangulate” and “export copy” options were selected, then copy of the mesh would be triangulated and exported, and mesh in the scene would stay intact. Currently in 2.80 that’s not the case - mesh in the scene gets triangulated and exported, and remains triangulated :frowning:

Here is the code in question:

      #Copy object by creating a new mesh from it, applying modifiers
			
            if (self.option_copy):
                orig_mesh = object.data
                meshes = [bpy.data.meshes.new_from_object(object, preserve_all_data_layers=True)]

                if self.option_split:
                    import bmesh

                    object.data = meshes[0]
                    bpy.ops.object.mode_set(mode = 'EDIT')
                    meshes = []

                    bm = bmesh.from_edit_mesh(object.data)
                    for midx, mslot in enumerate(object.material_slots):
                        mat = mslot.material
                        if mat:
                            bm_new = bm.copy()
                            faces = None
                            for f in bm_new.faces:
                                if f.material_index == midx:
                                    # type=201 = material
                                    faces = set(bmesh.ops.similar_faces(bm_new, faces=[f], type=201)['faces'])
                                    faces = [f for f in bm_new.faces if f not in faces]
                                    break
                            if faces:
                                for f in faces:
                                    bm_new.faces.remove(f)
                                new_mesh = orig_mesh.copy()
                                bm_new.to_mesh(new_mesh)
                                meshes.append(new_mesh)

                    bpy.ops.object.mode_set(mode = 'OBJECT')
                    old_mesh = object.data
                    object.data = orig_mesh
                    bpy.data.meshes.remove(old_mesh)

                for mesh in meshes:
                    object.data = mesh
                    aseGeom += str(cGeomObject(object))

                object.data = orig_mesh
                for mesh in meshes:
                    bpy.data.meshes.remove(mesh)
            else:
                aseGeom = str( cGeomObject( object ) )

Why isn’t it working in 2.80 ?

Apparently “triangulate” option was applied before anything else. The new plan is to use modifier instead and then make copy of the object with modifier, apply it, export the copy and then delete the copy.

I can’t wrap my head around that depsgraph thing and while the code below applies modifier and deletes the object/mesh, it doesn’t do it on the copy. It just does it to the original object/mesh leaving me with empty scene at the end:

 for object in bpy.context.selected_objects:
        if object.type == 'MESH':
            bpy.context.view_layer.objects.active = object
            object.select_set(True)
         
           if (self.option_copy):
                orig_mesh = object.data
                depsgraph = context.evaluated_depsgraph_get()
                object_eval = object.evaluated_get(depsgraph)                    
                meshes = [bpy.data.meshes.new_from_object(object_eval, preserve_all_data_layers=True)]
                bpy.ops.object.modifier_apply(apply_as='DATA', modifier="Triangulate")
         
                for mesh in meshes:
                    object_eval.data = mesh
                    aseGeom += str(cGeomObject(object_eval))

                object.data = mesh
                for mesh in meshes:
                    bpy.data.meshes.remove(object.data)
           else:
                aseGeom = str( cGeomObject( object ) )

Can someone please help me to get a copy of the object and leave original one in the scene intact ?

Most recent version (2.6.1) of the add-on: https://pastebin.com/xPV85fQz

A good soul (credited in the add-on) helped adding non-destructive export functionality!

I have not tested exporting multiple selected objects and I temporarily disabled “split per material” option.