2
$\begingroup$

I baked a very heavy simulation that toke like 1 and an half hour, but I forgot that simulation had to start from frame 100 because, before starting, there should be some camera movements, but I forgot it and now simulation starts from frame 0 and I haven't time. Should I bake it again from frame 100 or is there a way I can start the animation from frame -100?

$\endgroup$
9
  • $\begingroup$ Does this answer your question? Selecting Negetive frames in timeline $\endgroup$ – Markus von Broady Jun 2 at 12:31
  • $\begingroup$ It does something similar, but I mean rendering from frame -100, not executing physics from -100 $\endgroup$ – Arisyn ILY Jun 2 at 12:34
  • $\begingroup$ Sorry, I thought you actually could render from negative frame, but you only can preview from negative frames by clicking the clock button left to the start frame field. What you can do is render a still image, so you actually could write a Python script to render negative frames 🤔 $\endgroup$ – Markus von Broady Jun 2 at 12:45
  • 1
    $\begingroup$ @MarkusvonBroady I think there is an option somewhere to allow negative frames, but meanwhile, why not try and move the cache forward instead? I’m pretty sure there is a way to do that. $\endgroup$ – TheLabCat Jun 2 at 12:52
  • $\begingroup$ @ZargulTheWizard according to this, you can't render multiple negative frames at once, using Blender's vanilla interface: developer.blender.org/T42151 $\endgroup$ – Markus von Broady Jun 2 at 13:02
2
$\begingroup$

Update: the previous version actually didn't work (because I forgot the most important part, frame_set()), sorry for any confusions caused.

Enable negative frames in your project: Selecting Negative frames in timeline, then click on the stopwatch ⏱ icon to be able to preview those frames in viewport: , then add necessary animations and use the script below.

import bpy

start = bpy.context.scene.frame_preview_start  # or just input the starting frame manually here
end = -1  # or maybe you want bpy.context.scene.frame_end or frame_preview_end
renders_path = "/tmp/"
filename = "{}.png" 
negative_char = "_"  # minus sign sorts poorly together with positive numbers

class Negative_Render(bpy.types.Operator):
    """Render negative frames without hanging the interface
    adapted code from https://blender.stackexchange.com/q/71454/60486 """
    bl_idname = "render.negative"
    bl_label = "Render negative frames"

    # Define some variables to register
    _timer = None
    current_frame = start
    last_frame = end
    stop = None
    rendering = None

    # Define the handler functions. I use pre and post to know if Blender "is rendering"
    def pre(self, scene, context=None):
        self.rendering = True

    def post(self, scene, context=None):
        self.current_frame += 1
        self.rendering = False

    def cancelled(self, scene, context=None):
        self.stop = True
        
    def add_handlers(self, context):
        bpy.app.handlers.render_pre.append(self.pre)
        bpy.app.handlers.render_post.append(self.post)
        bpy.app.handlers.render_cancel.append(self.cancelled)

        # The timer gets created and the modal handler is added to the window manager
        self._timer = context.window_manager.event_timer_add(0.1, window=context.window)
        context.window_manager.modal_handler_add(self)
    
    def remove_handlers(self, context):
        bpy.app.handlers.render_pre.remove(self.pre)
        bpy.app.handlers.render_post.remove(self.post)
        bpy.app.handlers.render_cancel.remove(self.cancelled)
        context.window_manager.event_timer_remove(self._timer)

    def execute(self, context):
        # Define the variables during execution. This allows
        # to define when called from a button
        self.stop = False
        self.rendering = False
        self.add_handlers(context)
        return {"RUNNING_MODAL"}

    def modal(self, context, event):
        if event.type == 'TIMER': # This event is signaled every 100 ms
                                  # and will start the render if available
            if self.stop:
                # Cancelled:
                self.remove_handlers(context)
                return {"CANCELLED"}
            if self.current_frame > end:
                # Finished:
                self.remove_handlers(context)
                return {"FINISHED"}
            if self.rendering is False: # Nothing is currently rendering.
                                          # Proceed to render.
                sc = context.scene
                name = negative_char if self.current_frame < 0 else ""
                name += f"{abs(self.current_frame):03d}"
                sc.render.filepath = renders_path + filename.format(name)
                sc.frame_set(self.current_frame)
                bpy.ops.render.render("INVOKE_DEFAULT", write_still=True)

        return {"PASS_THROUGH"}
        # This is very important! If we used "RUNNING_MODAL", this new modal function
        # would prevent the use of the X button to cancel rendering, because this
        # button is managed by the modal function of the render operator,
        # not this new operator!

def register():
    bpy.utils.register_class(Negative_Render)

def unregister():
    bpy.utils.unregister_class(Negative_Render)

if __name__ == "__main__":
    register()
    bpy.ops.render.negative() # Test call

I adapted the script from:

Is it possible to make a sequence of renders and give the user the option to cancel the process through the UI at any given time?

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.