KhronosGroup / glTF-Blender-IO

Blender glTF 2.0 importer and exporter
https://docs.blender.org/manual/en/latest/addons/import_export/scene_gltf2.html
Apache License 2.0
1.48k stars 316 forks source link

__get_nla_tracks_obj: Invalid Index #2297

Open StellarGame opened 1 month ago

StellarGame commented 1 month ago

Hello, we have an asset we are exporting in blender 4.0 -> 4.2 where NLA tracks ticked is producing a bug in the function __get_nla_tracks_obj. Interestingly, the bug only manifests on the second call to the function during exporting and I'm not sure why it's being called a second time. The first pass appears to handle all the known animations of interest without error; hence the stack trace below shows the path to the second call that causes the bug:

image

The asset can't be shared at this moment, however playing with the code I can tell you that the length check on track.strips is 0 for the second pass causing current_exported_tracks, and consequently exported_tracks to be empty, finally creating an invalid index access when trying to get the track_names at the bottom of the function. You can also see I added a sloppy fix to just return empty lists on subsequent calls, though I'm not sure that's OK since I still have not reverse engineered what the second pass is designed to do:

Any help on what in our blender setup might be causing the second pass or a track with 0 strips is greatly appreciated! Thank you:


Ran = False # Added
def __get_nla_tracks_obj(obj_uuid: str, export_settings):
    # Added
    global Ran

    if (False == Ran):
        Ran = True
    else:
        return [], [], []
    # END Added

    obj = export_settings['vtree'].nodes[obj_uuid].blender_object

    if not obj.animation_data:
        return [], [], []

    if len(obj.animation_data.nla_tracks) == 0:
        return [], [], []

    exported_tracks = []
    current_exported_tracks = []

    for idx_track, track in enumerate(obj.animation_data.nla_tracks):
        print(track)

        if len(track.strips) == 0:
            #print("ADDED PRINT: track.strips = 0")
            continue

        stored_track = NLATrack(
            idx_track,
            track.strips[0].frame_start,
            track.strips[-1].frame_end,
            track.is_solo,
            track.mute
        )

        # Keep tracks where some blending together
        if any([strip.blend_type != 'REPLACE' for strip in track.strips]):
            # There is some blending. Keeping with previous track
            pass
        else:
            # The previous one(s) can go to the list, if any (not for first track)
            if len(current_exported_tracks) != 0:
                exported_tracks.append(current_exported_tracks)
                current_exported_tracks = []
        current_exported_tracks.append(stored_track)

    # End of loop. Keep the last one(s)
    exported_tracks.append(current_exported_tracks)

    track_names = [obj.animation_data.nla_tracks[tracks_group[0].idx].name for tracks_group in exported_tracks]
    on_types = ['OBJECT'] * len(track_names)
    return exported_tracks, track_names, on_types
julienduroure commented 1 month ago

Hello, Thanks for the report. It will be difficult to investigate without a test case. You can either

This function is called for each object of the scene, so you may have to look what object is impacted when the crash happen