Start a new topic

Remove texture from palette leaves texture assigned to faces..

Am looking for an 'easy' way to remove texture indices from database after the texture image is removed from the palette.  Do I really need to walk the hierarchy myself looking for faces, then looking for the specific texture layer, then deleting the ID, or how should this be done?



Thanks!


Hi, One easy way is to use the New Attribute Search tool to select all the faces that use the texture index that was removed and changing them all to -1

Thanks Chris!


I would like to do this via a python script :)

Ah, I just noticed that this was the API forum, not the Creator forum. My mistake.


Unfortunately, walking the hierarchy is the only way. This is pretty simple though,


Just a few lines of code:

 

indexToRemove = 13
db = mgGetCurrentDb ()

def RemoveTextureIndex (db, parent, rec, i):
   if mgGetCode(rec) == fltPolygon:
      ok, code, curIndex = mgGetAttList (rec, fltPolyTexture)
      if curIndex == i:
         mgSetAttList(rec, fltPolyTexture, -1)
   return MG_TRUE

mgWalk (db, None, RemoveTextureIndex, indexToRemove, MWALK_ON)

 

Very nice - thank you!!!

Keep in mind that this only looks for the index in the base texture layer. It can be improved by looking through the other 7 layers. Also this does not look though extended materials for the index to remove it from them. The last thing that could improve this would be to loop through all the polygons, meshes and extended materials, then checking every index to see if that index exists in the palette. If it does not, then remove the index.

Excellent points, thank you!  Esp. since I'll be needing to process layer 2

;)


Thx

So it looks like I need something like this?

 

if mgGetCode(rec) == fltPolygon:
    numAttr, \
        code0, index0, \
        code1, index1, \
        code2, index2)

    if curIndex == i:
        if code0 == fltPolyTexture:
            # remove but print 1st
            print("mgSetAttList(rec, fltPolyTexture, -1)")
        elif code1 == fltLayerTexture1:
           # remove but print 1st
            print("mgSetAttList(rec, fltLayerTexture1, -1)")
        elif code2 == fltLayerTexture2:
           # remove but print 1st
            print("mgSetAttList(rec, fltLayerTexture2, -1)")

 

Well I am close, but the script does not remove the texture index from faces as I expect on layers other than the base layer.  So far my test is using textures on layer 0 and 1, but the script has more added to cover some edge cases.  When I look at the flt file post process, the layer IDs are still assigned, so I am doing something wrong but I don't see what.  

 

#############################################################################
#
# 10.12.2023 DSRI Shawn Allen
# Python 3.7.6 script to replace texture references in flt model file
#
# convention: file.abc replaced with file.png if file.png exists in TileTx
#
# saving absolute paths since TileTx is a fixed location on dev system
#
# Requirements:
# OpenFlight API
#
#############################################################################

from ast import mod
import sys
import os
from mgapilib import *

# must use raw string for valid path construction; also works in Creator
# as a manual paste, and Windows with a dir command
k_Tx_Path = r'd:\nads\ProjectData\TileTx'

# list of texture IDs to remove from faces
IDs_to_remove = []

# file modified flag
modified = False

if not os.path.isdir(k_Tx_Path):
    print('Error: Unable to access texture folder')
    exit()

# untested yet
def RemoveTextureIndex (db, parent, rec, i):
    global modified

    print("texture index?", i)

    if mgGetCode(rec) == fltPolygon:
        
        ok, code0, index0, \
            code1, index1, \
            code2, index2, \
            code3, index3 = \
            mgGetAttList(rec, \
            fltPolyTexture, \
            fltLayerTexture1, \
            fltLayerTexture2, \
            fltLayerTexture3)
        
        if index0 == i:
            # remove from flt here
            mgSetAttList(rec, fltPolyTexture, -1)
            modified = True
        elif index1 == i:
            mgSetAttList(rec, fltLayerTexture1, -1)
            modified = True
        elif index2 == i:
            mgSetAttList(rec, fltLayerTexture2, -1)
            modified = True
        elif index3 == i:
            mgSetAttList(rec, fltLayerTexture3, -1)
            modified = True
        # return MG_TRUE
        return modified

def seek_in_list(search_term, file_tuples):
    matching_elements = []

    #print('Searching for:',search_term)
    search_term_basename = os.path.basename(search_term)

    for x in file_tuples:
        file_name = os.path.basename(x[0])
        file_ID = x[-1]
        #print(f'dbug: {search_term.lower()} == {file_name.lower()}')
        #print('##########')

        if search_term_basename.lower() == file_name.lower():
            print('FOUND:',search_term_basename)
            matching_elements.append((search_term, file_ID))

    return matching_elements

def replace_texture(db, texture_ID, new_texture_name):
    # replace original file name.xyz with name.png
    # not checking for rgba yet, but that is a condition that
    # should be considered

    global modified
    
    ok = mgReplaceTexture(db, texture_ID, new_texture_name)
    
    if ok == MG_FALSE:
        print('Error replacing texture:', mgGetName(index))
        return None
    else:
        modified = True
        return modified

def remove_texture(db, index):
    global modified

    # alpha textures have multiple maps, need to remove the unnecessary maps
    # when the .rgba (_TMA.png) file is used - no need to keep 2nd alpha
    #
    # currently file is removed from palette but reference remains on faces

    ok = mgDeleteTexture(db, index)
    # ok = print('removing texture')
    
    if ok == MG_FALSE:
        print('Error removing texture:', mgGetName(index))
        return None
    else:
        modified = True
        return modified


def process_textures(db, Tx_Files):
    global k_Tx_Path
    

    def rename_texture(orig_tex_name):
        global IDs_to_remove

        name_elems = os.path.split(orig_tex_name)
        tmp_file_name = name_elems[-1].split('.')[0]

        if '_TM' in tmp_file_name and not orig_tex_name.lower().endswith('.rgba'):
            base_name = tmp_file_name.replace('_TM', '_TMA')
        else:
            base_name = tmp_file_name.replace(' ', '_')

        new_tx_file_name = os.path.join(k_Tx_Path, (base_name + new_extension))

        if not os.path.exists(new_tx_file_name):
            print('File not found:', new_tx_file_name)
        else:
            return new_tx_file_name

    for m in Tx_Files:
        tex_ID = m[-1]
        orig_tex_name = m[0]

        new_extension = '.png'

        if orig_tex_name.lower().endswith(new_extension):
            continue

        elif '_TM' in orig_tex_name:
            if orig_tex_name.lower().endswith('.rgba'):
                # Handle _TM.rgba files
                print('Removing file:', orig_tex_name)

                IDs_to_remove.append(tex_ID)
                
                remove_texture(db, tex_ID)
            else:
                # Handle _TM files
                # print('Potential double file:', orig_tex_name)

                new_tx_file_name = rename_texture(orig_tex_name)
                if new_tx_file_name:
                    replace_texture(db, tex_ID, new_tx_file_name)
        else:
            new_tx_file_name = rename_texture(orig_tex_name)
            if new_tx_file_name:
                replace_texture(db, tex_ID, new_tx_file_name)

    return None

def main():
    if len(sys.argv) < 2:
        print("\nUsage: ", sys.argv[0], " <input_flt_filename> \n")
        print("   Reads database: <input_db_filename>")

        print("\n")
        return

    # disable generic OFAPI messages (otherwise they echo)
    ######################################################

    mgSetMessagesEnabled (MMSG_STATUS, MG_FALSE)
    mgSetMessagesEnabled (MMSG_WARNING, MG_FALSE)
    mgSetMessagesEnabled (MMSG_ERROR, MG_FALSE)
    mgInit(None, None)

    db = mgOpenDb(sys.argv[1])
    if db == None:
        msgbuf = mgGetLastError()
        print(msgbuf, "\n")
        mgExit()
        return

    # set texture file path here; unclear what happens to no path palette;
    # mixed case not supported by FLT, palette must be all one mode (absolute, relative, none)
    #
    # we are using absolute paths on modified files based on src TileTx folder location
    ok = mgTextureSetSavePathType(db, MSPT_ABSOLUTE)

    if ok == MG_FALSE:
        print('Error setting database path type!')

    Tx_Files = []

    # inventory flt file for textures
    found_tx, index, name = mgGetFirstTexture(db)

    while (found_tx):
           #print ('{0} : {1}'.format(index, name))
           if not any (name == x[0] for x in Tx_Files):
               Tx_Files.append((name, index))
           found_tx,index,name = mgGetNextTexture (db)
    
    # this way file_TM.rgb will always be processed before file_TM.rgba
    Tx_Files.sort()

    process_textures(db, Tx_Files)

    print('Checking for IDs', len(IDs_to_remove))

    if (len(IDs_to_remove) > 0):
        # checking: 2 in file c.flt
        # remove indices from faces

        for item in IDs_to_remove:
            indexToRemove = item
            mgWalk (db, None, RemoveTextureIndex, indexToRemove, MWALK_ON)

    if modified:
        ok = mgWriteDb (db)

        if ok == MG_FALSE:
            print('Error writing database!', db)

    mgCloseDb(db)

    mgExit()

if __name__ == '__main__':
    main()

 

There is no reason for the from ast import mod line, not sure how that ended up in the script, but it works without that so..


Did not have any bearing on removing the layer texture references unfortunately.


Thx

Sorry for the delay, I took a look and I see a major problem: The walk function is returning the modified bool. The walk function return value is used to determine if the walk should continue. If you return MG_FALSE in a walk, the entire walk ends and no more nodes will be visited. If you return MG_TRUE, then the walk will continue as usual. In essence, this return allows you to kill the walk when you are sure that your processing is done. In your situation, you cannot know when processing is done because you are looking for all occurrences, not just the first one.

image


Kudos for wading through that pile of code!


I am processing everything, I think, but am not handling the set attribute -1 on the layer 1 texture.  I can successfully remap all textures on layer 0.  So I am trimming the code to a minimum test case closer to the original suggestion and then I'll see, hopefully, what's going on with mgSetAttList.  


Thanks, again!

I see another issue. You are looking for the texture at layer0 with an if, but the rest you use an elif. This will cause layers to be skipped if a previous layer uses the index. Try changing them all to if statements instead. That will remove all indices regardless of if they were on other layers as well.

Aha!  Thanks very much!

This code seems to run, but I don't know why the setAttribute -1 failed.

 

import sys
import os
from mgapilib import *

used_textures = set()
modified = False

mgInit (None, None)

def RemoveTextureIndex (db, parent, rec, i):
   
   global modified

   print('Removing texture id?',i)

   if mgGetCode(rec) == fltPolygon:
      ok, code, curIndex = mgGetAttList (rec, fltPolyTexture)

      print(i,curIndex, type(i), type(curIndex))
   
      if curIndex == i:
         print('found tx match:',i)
         # mgSetAttList(rec, fltPolyTexture, -1)
         ok = mgDeleteTexture(db, curIndex)
         print(ok)
         modified = True
         print('dbug modified:',modified)

   return MG_TRUE

def ListTextures (db, parent, rec, i):
    # print('Get texture')

    global used_textures

    if mgGetCode(rec) == fltPolygon:
      ok, code, curIndex = mgGetAttList (rec, fltPolyTexture)
      # print('Tx index:',curIndex)

      used_textures.add(curIndex)

      t_name = mgGetTextureName(db, curIndex)
      # print('Tx:', t_name)
    
    return MG_TRUE

if len(sys.argv) < 2:
    print("\nUsage: ", sys.argv[0], " <input_flt_filename> \n")
    print("   Reads database: <input_db_filename>")

    print("\n")
    exit()

db = mgOpenDb(sys.argv[1])
# print(sys.argv[1])
if db == None:
    msgbuf = mgGetLastError()
    print(msgbuf, "\n")
    mgExit()
    exit()


if len(sys.argv) > 2:
    indexToRemove = int(sys.argv[-1])
    print('Removing index:', indexToRemove)

    mgWalk (db, None, RemoveTextureIndex, indexToRemove, MWALK_ON)

else:

    print('No texture index provided!')
    mgWalk (db, None, ListTextures, None, MWALK_ON)

if len(used_textures) > 0:    
    print('== Texture IDs in file ==')
    for x in used_textures:
        print(x)

print('modified?',modified)

if modified:
    ok = mgWriteDb (db)

    if ok == MG_FALSE:
        print('Error writing database!', db)

    else:
        print('closing modified db')

    mgCloseDb(db)

    mgExit()


 

Login to post a comment