Ejemplo n.º 1
0
def on_renderassetloadfailed():
    """Called when a render asset load failed."""
    current_time = time.time()
    options = PluginOptions()
    # Reset the file modification time of the Base FBX to trigger an import.
    if options.fbx_abs_path:
        options.fbx_modification_timestamp = current_time
Ejemplo n.º 2
0
def render_asset_only_import(fbx_path):
    "A simple import of the FBX as the render asset, without tracking files."
    _fail_if_no_ogrefbx()
    try:
        with FxHelperLibrary.Progress("Importing FBX...") as progress:

            progress.update(0.05)

            # Convert the base FBX to a render asset.  If this fails, the
            # exception we will caught before any work is done from the calls
            # below.  The goal is to warn about the failure without changing
            # anything.
            import_render_asset(fbx_path, progress)
            progress.update(0.7)

             # Set the render asset in the actor.
            update_render_asset(fbx_path)
            progress.update(1.0)

            # set the fbx_export_input_file console variable
            FxStudio.setConsoleVariable('fbx_export_input_file', fbx_path)

            # Make sure we aren't tracking files.
            options = PluginOptions()
            options.clear()
    except (RuntimeError, FBXImportError) as e:
        FxStudio.warn(': '.join(['FBXImporter', str(e)]))
Ejemplo n.º 3
0
def on_renderassetloadfailed():
    """Called when a render asset load failed."""
    current_time = time.time()
    options = PluginOptions()
    # Reset the file modification time of the Base FBX to trigger an import.
    if options.fbx_abs_path:
        options.fbx_modification_timestamp = current_time
Ejemplo n.º 4
0
def render_asset_only_import(fbx_path):
    "A simple import of the FBX as the render asset, without tracking files."
    _fail_if_no_ogrefbx()
    try:
        with FxHelperLibrary.Progress("Importing FBX...") as progress:

            progress.update(0.05)

            # Convert the base FBX to a render asset.  If this fails, the
            # exception we will caught before any work is done from the calls
            # below.  The goal is to warn about the failure without changing
            # anything.
            import_render_asset(fbx_path, progress)
            progress.update(0.7)

            # Set the render asset in the actor.
            update_render_asset(fbx_path)
            progress.update(1.0)

            # set the fbx_export_input_file console variable
            FxStudio.setConsoleVariable('fbx_export_input_file', fbx_path)

            # Make sure we aren't tracking files.
            options = PluginOptions()
            options.clear()
    except (RuntimeError, FBXImportError) as e:
        FxStudio.warn(': '.join(['FBXImporter', str(e)]))
Ejemplo n.º 5
0
def _update_from_relative_paths():
    """Updates the absolute paths stored in the plugin options using the
    relative paths and the current clientspec, but only if the new absolute
    paths are found. Returns true if anything was updated, false if nothing was
    updated."""
    options = PluginOptions()
    current_time = time.time()
    updated = False
    if not os.path.exists(options.fbx_abs_path) and not options.fbx_rel_path == None:
        abs_fbx_path = os.path.abspath(os.path.join(FxStudio.getDirectory(
            'clientspec_root'), options.fbx_rel_path))
        if os.path.exists(abs_fbx_path):
            FxStudio.msg("Updating absolute FBX path from relative: {0} to {1}"
                .format(options.fbx_abs_path, abs_fbx_path))
            options.fbx_abs_path = abs_fbx_path
            options.fbx_modification_timestamp = current_time
            updated = True

    if not os.path.exists(options.betf_abs_path) and not options.betf_rel_path == None:
        abs_betf_path = os.path.abspath(os.path.join(FxStudio.getDirectory(
            'clientspec_root'), options.betf_rel_path))
        if os.path.exists(abs_betf_path):
            FxStudio.msg("Updating absolute BETF path from relative: {0} to {1}"
                .format(options.betf_abs_path, abs_betf_path))
            options.betf_abs_path = abs_betf_path
            options.betf_modification_timestamp = current_time
            updated = True
    return updated
Ejemplo n.º 6
0
def remove_animation(fbx_path, fbx_anim_path):
    """Removes a single animation from the render asset. """
    options = PluginOptions()
    render_asset_name = get_render_asset_name(fbx_path)
    # Remove the animation.
    ogre_fbx_proxy = OgreFBXProxy()
    ogre_fbx_proxy.remove_animation(fbx_anim_path, render_asset_name)
    # Remove the animation from the cache.
    options.remove_fbx_anim(fbx_anim_path)
Ejemplo n.º 7
0
def remove_animation(fbx_path, fbx_anim_path):
    """Removes a single animation from the render asset. """
    options = PluginOptions()
    render_asset_name = get_render_asset_name(fbx_path)
    # Remove the animation.
    ogre_fbx_proxy = OgreFBXProxy()
    ogre_fbx_proxy.remove_animation(fbx_anim_path, render_asset_name)
    # Remove the animation from the cache.
    options.remove_fbx_anim(fbx_anim_path)
Ejemplo n.º 8
0
def _create_single_animation_from(fbx_anim_path, render_asset_name):
    """Creates a single animation from the fbx_anim_path. """
    #print '_create_single_animation_from(fbx_anim_path="{0}", render_asset_name="{1}")'.format(fbx_anim_path, render_asset_name)
    options = PluginOptions()
    # Import the animation.
    ogre_fbx_proxy = OgreFBXProxy()
    ogre_fbx_proxy.import_animation(fbx_anim_path, render_asset_name,
        is_pose_anim(fbx_anim_path))
    # Cache the animation path and its modification time.
    options.add_fbx_anim(fbx_anim_path, os.path.getmtime(fbx_anim_path))
Ejemplo n.º 9
0
def _create_single_animation_from(fbx_anim_path, render_asset_name):
    """Creates a single animation from the fbx_anim_path. """
    #print '_create_single_animation_from(fbx_anim_path="{0}", render_asset_name="{1}")'.format(fbx_anim_path, render_asset_name)
    options = PluginOptions()
    # Import the animation.
    ogre_fbx_proxy = OgreFBXProxy()
    ogre_fbx_proxy.import_animation(fbx_anim_path, render_asset_name,
                                    is_pose_anim(fbx_anim_path))
    # Cache the animation path and its modification time.
    options.add_fbx_anim(fbx_anim_path, os.path.getmtime(fbx_anim_path))
Ejemplo n.º 10
0
    def _parse_log(self):
        """Extracts clip data from the ANIMATION={} json section of the
        log output. Saves the clip data to the options.

        """
        with open(self._filename, 'r') as f:
            log_contents = f.read()
            match = re.search(r'ANIMATION\=(\{.*\})', log_contents)
            if match:
                self.clip_data = json.loads(match.group(0).split('=')[1])

                options = PluginOptions()
                if 'fps' in self.clip_data:
                    options.framerate = self.clip_data['fps']
                if 'keys' in self.clip_data:
                    options.keyframes = self.clip_data['keys']
Ejemplo n.º 11
0
    def _parse_log(self):
        """Extracts clip data from the ANIMATION={} json section of the
        log output. Saves the clip data to the options.

        """
        with open(self._filename, 'r') as f:
            log_contents = f.read()
            match = re.search(r'ANIMATION\=(\{.*\})', log_contents)
            if match:
                self.clip_data = json.loads(match.group(0).split('=')[1])

                options = PluginOptions()
                if 'fps' in self.clip_data:
                    options.framerate = self.clip_data['fps']
                if 'keys' in self.clip_data:
                    options.keyframes = self.clip_data['keys']
Ejemplo n.º 12
0
    def on_ok(self, event):
        fbx_abs_path = self.base_fbx_path.GetValue()
        betf_abs_path = self.batch_export_text_path.GetValue()

        if len(fbx_abs_path) > 0:
            if not os.path.exists(fbx_abs_path):
                FxStudio.displayMessageBox("The base FBX file does not exist.",
                                           "error")
                return
        else:
            fbx_abs_path = None

        if len(betf_abs_path) > 0:
            if not os.path.exists(betf_abs_path):
                FxStudio.displayMessageBox(
                    "The batch export text file does not exist.", "error")
                return

        # Set the options and update the timestamps so that the files will
        # register as changed.
        options = PluginOptions()

        current_time = time.time()

        old_fbx_abs_path = options.fbx_abs_path
        old_betf_abs_path = options.betf_abs_path

        if fbx_abs_path is not None:
            if os.path.abspath(old_fbx_abs_path) != os.path.abspath(
                    fbx_abs_path):
                options.fbx_abs_path = fbx_abs_path
                options.fbx_rel_path = make_relative_to_clientspec_root(
                    fbx_abs_path)
                options.fbx_modification_timestamp = current_time
        else:
            options.fbx_abs_path = None
            options.fbx_rel_path = None

        if os.path.abspath(old_betf_abs_path) != os.path.abspath(
                betf_abs_path):
            options.betf_abs_path = betf_abs_path
            options.betf_rel_path = make_relative_to_clientspec_root(
                betf_abs_path)
            options.betf_modification_timestamp = current_time

        self.EndModal(wx.ID_OK)
Ejemplo n.º 13
0
 def on_browse_for_batch_export_text(self, event):
     options = PluginOptions()
     betf = FxStudio.displayFileOpenDialog(
         msg=_BATCH_EXPORT_TEXT_FILE_LABEL,
         default_path=os.path.dirname(options.betf_abs_path),
         wildcard=
         'Batch Export Text Files (*.txt)|*.txt|All Files (*.*)|*.*',
         file_must_exist=True)
     if betf is not None:
         self.batch_export_text_path.SetValue(betf)
Ejemplo n.º 14
0
 def on_browse_for_base_fbx(self, event):
     options = PluginOptions()
     initial_fbx_abs_path = ''
     if options.fbx_abs_path is not None:
         initial_fbx_abs_path = os.path.dirname(options.fbx_abs_path)
     base_fbx = FxStudio.displayFileOpenDialog(
         msg=_BASE_FBX_FILE_LABEL,
         default_path=initial_fbx_abs_path,
         wildcard='FBX Files (*.fbx)|*.fbx|All Files (*.*)|*.*',
         file_must_exist=True)
     if base_fbx is not None:
         self.base_fbx_path.SetValue(base_fbx)
Ejemplo n.º 15
0
    def __init__(self, fbx_path):
        plugin_options = PluginOptions()
        self.disk_animations = set(find_anim_fbxs_for(fbx_path))
        self.cached_animations = plugin_options.get_fbx_anims_as_set()

        # the added and removed animations are the set differences.
        self.added_animations = self.disk_animations - self.cached_animations
        self.removed_animations = self.cached_animations - self.disk_animations

        # static animations (those that are both on disk and in our cache)
        # are the intersection of the two sets.
        self.static_animations = self.disk_animations & self.cached_animations

        # static animations can change if their modification time is more
        # recent than what we have cached.
        self.changed_animations = set([x for x in self.static_animations if
            os.path.getmtime(x) != plugin_options.get_fbx_anim_timestamp(x)])

        # the set of updated animations is the union of those that were
        # added and those that were changed.
        self.added_or_changed_animations = self.added_animations | self.changed_animations
Ejemplo n.º 16
0
def _update_from_relative_paths():
    """Updates the absolute paths stored in the plugin options using the
    relative paths and the current clientspec, but only if the new absolute
    paths are found. Returns true if anything was updated, false if nothing was
    updated."""
    options = PluginOptions()
    current_time = time.time()
    updated = False
    if not os.path.exists(
            options.fbx_abs_path) and not options.fbx_rel_path == None:
        abs_fbx_path = os.path.abspath(
            os.path.join(FxStudio.getDirectory('clientspec_root'),
                         options.fbx_rel_path))
        if os.path.exists(abs_fbx_path):
            FxStudio.msg(
                "Updating absolute FBX path from relative: {0} to {1}".format(
                    options.fbx_abs_path, abs_fbx_path))
            options.fbx_abs_path = abs_fbx_path
            options.fbx_modification_timestamp = current_time
            updated = True

    if not os.path.exists(
            options.betf_abs_path) and not options.betf_rel_path == None:
        abs_betf_path = os.path.abspath(
            os.path.join(FxStudio.getDirectory('clientspec_root'),
                         options.betf_rel_path))
        if os.path.exists(abs_betf_path):
            FxStudio.msg(
                "Updating absolute BETF path from relative: {0} to {1}".format(
                    options.betf_abs_path, abs_betf_path))
            options.betf_abs_path = abs_betf_path
            options.betf_modification_timestamp = current_time
            updated = True
    return updated
Ejemplo n.º 17
0
def _get_betf_path_from_user():
    """Returns the batch export path. Caches the path in the dictionary. """
    options = PluginOptions()
    betf_path = FxStudio.displayFileOpenDialog(
        'Select batch export text file (if necessary)',
        os.path.dirname(options.betf_abs_path),
        os.path.basename(options.betf_abs_path), '',
        'Text files (*.txt)|*.txt')

    if betf_path:
        if len(betf_path) > 0:
            return betf_path
    return ""
Ejemplo n.º 18
0
    def __init__(self, fbx_path):
        plugin_options = PluginOptions()
        self.disk_animations = set(find_anim_fbxs_for(fbx_path))
        self.cached_animations = plugin_options.get_fbx_anims_as_set()

        # the added and removed animations are the set differences.
        self.added_animations = self.disk_animations - self.cached_animations
        self.removed_animations = self.cached_animations - self.disk_animations

        # static animations (those that are both on disk and in our cache)
        # are the intersection of the two sets.
        self.static_animations = self.disk_animations & self.cached_animations

        # static animations can change if their modification time is more
        # recent than what we have cached.
        self.changed_animations = set([
            x for x in self.static_animations
            if os.path.getmtime(x) != plugin_options.get_fbx_anim_timestamp(x)
        ])

        # the set of updated animations is the union of those that were
        # added and those that were changed.
        self.added_or_changed_animations = self.added_animations | self.changed_animations
Ejemplo n.º 19
0
    def on_ok(self, event):
        fbx_abs_path = self.base_fbx_path.GetValue()
        betf_abs_path = self.batch_export_text_path.GetValue()

        if len(fbx_abs_path) > 0:
            if not os.path.exists(fbx_abs_path):
                FxStudio.displayMessageBox("The base FBX file does not exist.", "error")
                return
        else:
            fbx_abs_path = None

        if len(betf_abs_path) > 0:
            if not os.path.exists(betf_abs_path):
                FxStudio.displayMessageBox("The batch export text file does not exist.", "error")
                return

        # Set the options and update the timestamps so that the files will
        # register as changed.
        options = PluginOptions()

        current_time = time.time()

        old_fbx_abs_path = options.fbx_abs_path
        old_betf_abs_path = options.betf_abs_path

        if fbx_abs_path is not None:
            if os.path.abspath(old_fbx_abs_path) != os.path.abspath(fbx_abs_path):
                options.fbx_abs_path = fbx_abs_path
                options.fbx_rel_path = make_relative_to_clientspec_root(fbx_abs_path)
                options.fbx_modification_timestamp = current_time
        else:
            options.fbx_abs_path = None
            options.fbx_rel_path = None

        if os.path.abspath(old_betf_abs_path) != os.path.abspath(betf_abs_path):
            options.betf_abs_path = betf_abs_path
            options.betf_rel_path = make_relative_to_clientspec_root(betf_abs_path)
            options.betf_modification_timestamp = current_time

        self.EndModal(wx.ID_OK)
Ejemplo n.º 20
0
def update_poses(progress=None):
    """Creates or updates the poses in an actor's face graph. """
    options = PluginOptions()
    pose_names = _get_pose_names(options.betf_abs_path, options.keyframes)
    frame_rate = options.framerate
    with PreviewAnimation(config.POSE_ANIM_NAME):
        # Set the rest pose in FaceFX Studio.
        if progress:
            progress.update(0.44)
        FxStudio.setCurrentTime(0.0)
        commands.set_rest_pose()

        # Create the bone poses in FaceFX Studio.
        if len(pose_names) > 0:
            if progress:
                progress.update(0.48)
            _create_poses(pose_names, frame_rate)

    commands.flush_undo_redo_buffers()

    return pose_names is not None
Ejemplo n.º 21
0
    def __init__(self):
        wx.Dialog.__init__(self, parent=FxStudio.getMainWindow(),\
            title=_OPTIONS_DIALOG_TITLE, size=(500, 215), style=wx.CAPTION)

        options = PluginOptions()

        self.vertical_sizer = wx.BoxSizer(orient=wx.VERTICAL)

        self.static_box = wx.StaticBox(self, label='Options')

        self.static_box_sizer = wx.StaticBoxSizer(self.static_box, wx.VERTICAL)

        base_fbx_label = wx.StaticText(self, label=_BASE_FBX_FILE_LABEL)

        self.static_box_sizer.AddWindow(base_fbx_label,
                                        0,
                                        border=5,
                                        flag=wx.ALL)

        self.base_fbx_horizontal_sizer = wx.BoxSizer(orient=wx.HORIZONTAL)

        initial_fbx_abs_path = ''

        if options.fbx_abs_path is not None:
            initial_fbx_abs_path = options.fbx_abs_path

        self.base_fbx_path = wx.TextCtrl(self, value=initial_fbx_abs_path)

        self.base_fbx_horizontal_sizer.AddWindow(self.base_fbx_path, 1, border=5,\
            flag=wx.ALL | wx.GROW)

        self.base_fbx_browse_button = wx.Button(
            self, id=_BROWSE_FOR_BASE_FBX_BUTTON_ID, label='Browse...')

        self.base_fbx_browse_button.Bind(wx.EVT_BUTTON, self.on_browse_for_base_fbx,\
            id=_BROWSE_FOR_BASE_FBX_BUTTON_ID)

        self.base_fbx_horizontal_sizer.AddWindow(self.base_fbx_browse_button, 0, border=5,\
            flag=wx.ALL | wx.GROW | wx.ALIGN_RIGHT)

        self.static_box_sizer.AddWindow(self.base_fbx_horizontal_sizer, 0, border=0,\
            flag=wx.ALL | wx.GROW)

        batch_export_text_label = wx.StaticText(
            self, label=_BATCH_EXPORT_TEXT_FILE_LABEL)

        self.static_box_sizer.AddWindow(batch_export_text_label,
                                        0,
                                        border=5,
                                        flag=wx.ALL)

        self.batch_export_text_horizontal_sizer = wx.BoxSizer(
            orient=wx.HORIZONTAL)

        self.batch_export_text_path = wx.TextCtrl(self,
                                                  value=options.betf_abs_path)

        self.batch_export_text_horizontal_sizer.AddWindow(self.batch_export_text_path, 1, border=5,\
            flag=wx.ALL | wx.GROW)

        self.batch_export_text_browse_button = wx.Button(
            self,
            id=_BROWSE_FOR_BATCH_EXPORT_TEXT_BUTTON_ID,
            label='Browse...')

        self.batch_export_text_browse_button.Bind(wx.EVT_BUTTON, self.on_browse_for_batch_export_text,\
            id=_BROWSE_FOR_BATCH_EXPORT_TEXT_BUTTON_ID)

        self.batch_export_text_horizontal_sizer.AddWindow(self.batch_export_text_browse_button, 0, border=5,\
            flag=wx.ALL | wx.ALIGN_RIGHT)

        self.static_box_sizer.AddWindow(self.batch_export_text_horizontal_sizer,\
            0, border=0, flag=wx.ALL | wx.GROW)

        self.vertical_sizer.AddWindow(self.static_box_sizer,
                                      0,
                                      border=5,
                                      flag=wx.ALL | wx.GROW)

        self.button_horizontal_sizer = wx.BoxSizer(orient=wx.HORIZONTAL)

        self.button_horizontal_sizer.AddStretchSpacer()

        self.ok_button = wx.Button(self, id=wx.ID_OK, label='OK')

        self.ok_button.Bind(wx.EVT_BUTTON, self.on_ok, id=wx.ID_OK)

        self.button_horizontal_sizer.AddWindow(self.ok_button, 0, border=5,\
            flag=wx.ALL | wx.GROW)

        self.cancel_button = wx.Button(self, id=wx.ID_CANCEL, label='Cancel')

        self.cancel_button.Bind(wx.EVT_BUTTON, self.on_cancel, id=wx.ID_CANCEL)

        self.button_horizontal_sizer.AddWindow(self.cancel_button, 0, border=5,\
            flag=wx.ALL | wx.GROW)

        self.vertical_sizer.AddWindow(self.button_horizontal_sizer, 0, border=5,\
            flag=wx.ALL | wx.GROW)

        self.SetSizer(self.vertical_sizer)
        self.SetAutoLayout(True)
        self.Layout()
Ejemplo n.º 22
0
def full_import_fbx(fbx_path, batch_export_path=None):
    """Imports an FBX file from a path.

    parameters:

    fbx_path -- Fully-qualified path to an FBX file.
    batch_export_path [optional] -- If parameter is not provided, a dialog will
        be displayed asking the user to browse for the batch export text file.
        If the path is known, the dialog can be bypassed by providing the path
        as a parameter to this function. If you only wish to import a render
        asset without importing bone poses, passing the empty string as the path
        will do the trick.

    side effects:

    - If no actor is loaded in Studio, a new actor is created.
    - The render asset is changed to the imported FBX assets.
    - If a batch export text file is selected, the bone poses are loaded from
    the FBX file.

    """
    try:
        # Prerequisites
        _fail_if_no_ogrefbx()
        _ensure_exists_actor()
        _ensure_exists_importer_cache()

        if batch_export_path is None:
            batch_export_path = _get_betf_path_from_user()

        # Cache the FBX and BETF path
        options = PluginOptions()

        options.betf_abs_path = batch_export_path
        options.betf_rel_path = make_relative_to_clientspec_root(batch_export_path)

        # Set the fbx_export_input_file console variable to the fbx file.
        FxStudio.setConsoleVariable('fbx_export_input_file', fbx_path)

        with FxHelperLibrary.Progress("Importing FBX...") as progress:
            progress.update(0.05)

            # Convert the base FBX to a render asset.
            import_render_asset(fbx_path, progress)

            # Update the options.  This will enable automatic updating
            options.fbx_abs_path = fbx_path
            options.fbx_rel_path = make_relative_to_clientspec_root(fbx_path)

            # Find any animations for the base FBX and add them to the render asset.
            _find_and_create_animations_for(fbx_path)
            progress.update(0.25)

            # Set the render asset in the actor.
            update_render_asset(fbx_path)
            progress.update(0.3)

            # Create FaceFX animations for the imported Ogre body animations.
            create_ogre_anims()

            if update_poses(progress):
                rig_new_import()
                progress.update(0.97)

            with FxHelperLibrary.Unattended():
                # Sync to the default template to get the color map and workspaces.
                commands.sync_template(config.DEFAULT_TEMPLATE_PATH, flags='-workspaces -colormap')

            commands.flush_undo_redo_buffers()

            progress.update(1.0)

        # Cache the correct timestamps
        if os.path.exists(batch_export_path):
            options.betf_modification_timestamp = os.path.getmtime(batch_export_path)
        options.fbx_modification_timestamp = os.path.getmtime(fbx_path)

    except (RuntimeError, FBXImportError) as e:
        options.clear()
        commands.flush_undo_redo_buffers()
        FxStudio.warn(': '.join(['FBXImporter', str(e)]))
Ejemplo n.º 23
0
def full_import_fbx(fbx_path, batch_export_path=None):
    """Imports an FBX file from a path.

    parameters:

    fbx_path -- Fully-qualified path to an FBX file.
    batch_export_path [optional] -- If parameter is not provided, a dialog will
        be displayed asking the user to browse for the batch export text file.
        If the path is known, the dialog can be bypassed by providing the path
        as a parameter to this function. If you only wish to import a render
        asset without importing bone poses, passing the empty string as the path
        will do the trick.

    side effects:

    - If no actor is loaded in Studio, a new actor is created.
    - The render asset is changed to the imported FBX assets.
    - If a batch export text file is selected, the bone poses are loaded from
    the FBX file.

    """
    try:
        # Prerequisites
        _fail_if_no_ogrefbx()
        _ensure_exists_actor()
        _ensure_exists_importer_cache()

        if batch_export_path is None:
            batch_export_path = _get_betf_path_from_user()

        # Cache the FBX and BETF path
        options = PluginOptions()

        options.betf_abs_path = batch_export_path
        options.betf_rel_path = make_relative_to_clientspec_root(
            batch_export_path)

        # Set the fbx_export_input_file console variable to the fbx file.
        FxStudio.setConsoleVariable('fbx_export_input_file', fbx_path)

        with FxHelperLibrary.Progress("Importing FBX...") as progress:
            progress.update(0.05)

            # Convert the base FBX to a render asset.
            import_render_asset(fbx_path, progress)

            # Update the options.  This will enable automatic updating
            options.fbx_abs_path = fbx_path
            options.fbx_rel_path = make_relative_to_clientspec_root(fbx_path)

            # Find any animations for the base FBX and add them to the render asset.
            _find_and_create_animations_for(fbx_path)
            progress.update(0.25)

            # Set the render asset in the actor.
            update_render_asset(fbx_path)
            progress.update(0.3)

            # Create FaceFX animations for the imported Ogre body animations.
            create_ogre_anims()

            if update_poses(progress):
                rig_new_import()
                progress.update(0.97)

            with FxHelperLibrary.Unattended():
                # Sync to the default template to get the color map and workspaces.
                commands.sync_template(config.DEFAULT_TEMPLATE_PATH,
                                       flags='-workspaces -colormap')

            commands.flush_undo_redo_buffers()

            progress.update(1.0)

        # Cache the correct timestamps
        if os.path.exists(batch_export_path):
            options.betf_modification_timestamp = os.path.getmtime(
                batch_export_path)
        options.fbx_modification_timestamp = os.path.getmtime(fbx_path)

    except (RuntimeError, FBXImportError) as e:
        options.clear()
        commands.flush_undo_redo_buffers()
        FxStudio.warn(': '.join(['FBXImporter', str(e)]))
Ejemplo n.º 24
0
def on_idle():
    """Called when Studio is idle. Check for updates to monitored files. """
    if not FxStudio.isActorLoaded():
        return

    # If the notification dialog is currently visible return immediately.
    if is_notification_dialog_visible():
        return

    try:
        options = PluginOptions()
        fbx_path = options.fbx_abs_path

        if fbx_path:
            if not os.path.exists(fbx_path):
                if _update_from_relative_paths():
                    # update the FBX path from the new options.
                    fbx_path = options.fbx_abs_path
            if not os.path.exists(fbx_path):
                if not fbx_path in _missing_paths:
                    _missing_paths[fbx_path] = fbx_path
                    FxStudio.warn(
                        'Linked FBX file not found: {0}'.format(fbx_path))
                return
            fbx_anims = FBXAnims(fbx_path)
            #print 'disk', fbx_anims.disk_animations
            #print 'cached', fbx_anims.cached_animations
            #print 'added', fbx_anims.added_animations
            #print 'removed', fbx_anims.removed_animations
            #print 'changed', fbx_anims.changed_animations

            if _is_anything_modified(options, fbx_anims):
                import_render_asset = False
                update_render_asset = False

                selection = wx.ID_YES

                remembered_selection = get_remembered_selection()

                if remembered_selection is not None:
                    selection = remembered_selection
                else:
                    selection = show_notification_dialog()

                if selection == wx.ID_NO:
                    # Update the timestamps on anything that changed to prevent
                    # this notification from popping up again... also run the
                    # render asset update but NOT the actor update.

                    # This has the side effect of updating the base fbx file
                    # timestamp.
                    _is_base_fbx_modified(options)

                    # This has the side effect of updating the batch export
                    # text file timestamp.
                    _is_betf_modified(options)

                    # Remove all animations from the render asset that have been
                    # removed from the local disk.
                    for path in fbx_anims.removed_animations:
                        FBXImporter.remove_animation(fbx_path, path)
                        update_render_asset = True

                    # If the pose animation was removed, import the render asset
                    # so that the system will know the poses should come from the
                    # base fbx file instead.
                    if fbx_anims.is_pose_anim_removed():
                        import_render_asset = True
                        update_render_asset = True

                    if import_render_asset:
                        with FxHelperLibrary.Progress(
                                "Synchronizing...") as progress:
                            progress.update(0.05)
                            # If a pose animation exists, the poses will come from the
                            # external animation. Otherwise, the poses will come from the
                            # base fbx.
                            poses_are_in_base_fbx = not fbx_anims.pose_anim_exists(
                            )
                            progress.update(0.1)
                            # Import the render asset. We ask the importer to only touch
                            # the internal animation if the poses will come from it.
                            FBXImporter.import_render_asset(
                                fbx_path,
                                export_internal_animation=poses_are_in_base_fbx
                            )
                            # We need to reimport all other animations without modifying
                            # their timestamps.
                            _reimport_animations(fbx_path, fbx_anims, progress)
                            progress.update(1.0)
                            # Poses may need to be updated as a result of this but
                            # the user specifically selected no when we asked for
                            # permission to do so.

                    if fbx_anims.is_pose_anim_modified():
                        # [Re]import the pose animation.
                        FBXImporter.import_animation(
                            fbx_path, fbx_anims.get_pose_anim_path())
                        update_render_asset = True

                    if fbx_anims.is_other_anim_modified():
                        # [Re]import all of the other animations.
                        for path in fbx_anims.get_updated_animations_ex_pose_anim(
                        ):
                            FBXImporter.import_animation(fbx_path, path)
                        update_render_asset = True

                    # Update the render asset if necessary to get the changes to the
                    # base fbx or the animations shown in Studio.
                    if update_render_asset:
                        FBXImporter.update_render_asset(fbx_path)

                    return
                # wx.ID_CANCEL indicates that the user pressed the options
                # button.
                elif selection == wx.ID_CANCEL:
                    show_options_dialog(wx.CommandEvent())
                    # Don't do anything else.
                    return

                update_poses = False
                add_new_ogre_anims = False

                # Remove all animations from the render asset that have been
                # removed from the local disk. Do this first so the poses can come
                # in from the basefbx in case the user removed the pose animation.
                for path in fbx_anims.removed_animations:
                    FBXImporter.remove_animation(fbx_path, path)
                    FBXImporter.remove_ogre_anim(
                        get_animation_name_from_path(path))
                    update_render_asset = True

                # If the pose animation was removed, import the render asset
                # so that the system will know the poses should come from the
                # base fbx file instead.
                if fbx_anims.is_pose_anim_removed():
                    import_render_asset = True
                    update_render_asset = True

                if _is_base_fbx_modified(options):
                    FxStudio.msg('{0} changed. Reimporting.'.format(fbx_path))
                    import_render_asset = True
                    update_render_asset = True

                if import_render_asset:
                    with FxHelperLibrary.Progress(
                            "Synchronizing...") as progress:
                        progress.update(0.05)
                        # If a pose animation exists, the poses will come from the
                        # external animation. Otherwise, the poses will come from the
                        # base fbx.
                        poses_are_in_base_fbx = not fbx_anims.pose_anim_exists(
                        )
                        progress.update(0.1)
                        # Import the render asset. We ask the importer to only touch
                        # the internal animation if the poses will come from it.
                        FBXImporter.import_render_asset(
                            fbx_path,
                            export_internal_animation=poses_are_in_base_fbx)
                        # We need to reimport all other animations without modifying
                        # their timestamps.
                        _reimport_animations(fbx_path, fbx_anims, progress)
                        progress.update(1.0)
                        # Poses will need to be updated as a result of this step iff
                        # the poses came from *this* fbx.
                        if poses_are_in_base_fbx:
                            update_poses = True

                if _is_betf_modified(options):
                    FxStudio.msg('{0} changed. Reimporting.'.format(
                        options.betf_abs_path))
                    # If the batch export text file changed, we'll need to update
                    # the poses regardless of where they came from.
                    update_poses = True

                if fbx_anims.is_pose_anim_modified():
                    # [Re]import the pose animation. There is logic internal to
                    # this process to only update our internal cache if the
                    # animation is the pose animation.
                    FBXImporter.import_animation(
                        fbx_path, fbx_anims.get_pose_anim_path())
                    # Because the pose animation changed, the poses will need to be
                    # updated.
                    update_poses = True
                    update_render_asset = True
                    add_new_ogre_anims = True

                if fbx_anims.is_other_anim_modified():
                    # [Re]import all of the other animations. No caching occurs,
                    # because it is guaranteed that none of the animations in this
                    # list are the pose animation.
                    for path in fbx_anims.get_updated_animations_ex_pose_anim(
                    ):
                        FBXImporter.import_animation(fbx_path, path)
                    update_render_asset = True
                    add_new_ogre_anims = True

                # Update the render asset if necessary to get the changes to the
                # base fbx or the animations shown in Studio.
                if update_render_asset:
                    FBXImporter.update_render_asset(fbx_path)

                # At this point, we can update poses and it should take into
                # account all changes to the render asset.
                if update_poses:
                    FBXImporter.update_poses()

                if add_new_ogre_anims:
                    FBXImporter.create_ogre_anims()

    except (FBXImportError, os.error) as e:
        FxStudio.warn(': '.join(['FBXImporter', str(e)]))