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.
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()
Shawn
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!