0
$\begingroup$

I have a script that modifies position and rotation of some objects as well as individual verticies. For each frame I render PNG and then I insert them to video sequence to render the final video. The problem is that I have 6000 frames and the individual image files are quite large. Is there a way to append the frames as they are created in the for loop to single video file? As I understand it I can't keyframe individual verticies and therefore can't create only keyframes in the for loop and render the video afterwards

Here is the code:

import bpy
import csv

FILE = 'Data/20200712.csv'
OUTPUT = 'Other/Output/'

#
fp = bpy.context.scene.render.filepath # get existing output path
bpy.context.scene.render.image_settings.file_format = 'PNG' # set output format to .png

arrow = bpy.data.objects['Arrow']
target = bpy.data.objects['Target']

#find which vertex of pyramid is moving
bottom = bpy.data.objects['Cover']
for i, vert in enumerate(bottom.data.vertices):
    if vert.co[2]>0.5:
        ind = i

with open(filename, newline='') as data:
    reader = csv.DictReader(data)
    counter = 0
    for row in reader:
        if row['A'] is not None:
            frame_num = int(row['SampleTime'])
            
            x = float(row['hB'])            #load value saved as hB
                    
            y = float(row['hA'])            #load value saved as hA
            
            arrow.rotation_euler = (-y/3, x/3, 0)
            
            target.location = (row['gestTarget0'], row['gestTarget1'], 0.9)
            
            bottom.data.vertices[ind].co[0] = x/5
            bottom.data.vertices[ind].co[1] = y/5
            
            bpy.context.scene.frame_set(frame_num)
            #arrow.keyframe_insert(data_path="location", frame=frame_num)
            arrow.keyframe_insert(data_path='rotation_euler', frame=frame_num)
            
            target.keyframe_insert(data_path="location", frame=frame_num)
            
            bpy.context.scene.frame_current = frame_num
            bpy.context.scene.render.filepath = OUTPUT + str(frame_num)
            bpy.ops.render.render(write_still=True) # render still
            
#        counter += 1
#        
#        if counter>200:
#            break

bpy.context.scene.render.filepath = fp #restore filepath
```
$\endgroup$
5
  • 2
    $\begingroup$ Consider posting your script. $\endgroup$
    – batFINGER
    Commented Aug 20, 2020 at 9:27
  • $\begingroup$ Did each frame need to base on last rendered frame? Isn't that a image sequence clip? Wouldn't shape key been use in that situation? The bold text in your question sounds like a generator. $\endgroup$
    – HikariTW
    Commented Aug 20, 2020 at 9:44
  • $\begingroup$ @batFINGER I put the script into the original submission $\endgroup$
    – halal
    Commented Aug 24, 2020 at 10:47
  • $\begingroup$ @HikariTW I am not really sure about your questions. Each frame is with the same objects but different positions in the frame. Currently I export each frame to an image and then create image sequence clip, but because I have to create 6000images first, the whole folder is really large. $\endgroup$
    – halal
    Commented Aug 24, 2020 at 10:49
  • $\begingroup$ Application Handlers (bpy.app.handlers) — Blender Python API, with handler, you can manipulate the object when rendering animation. $\endgroup$
    – HikariTW
    Commented Aug 24, 2020 at 12:09

2 Answers 2

0
$\begingroup$

Not sure if handler make your requirement but I thought this should work:

  1. Collect your data into a list or dict(I use dict)
  2. According to the scene.current_frame, set the corresponding value to those object in data.
  3. Make the process a handler and register it to bpy.app
  4. Render your animation normally using video format
import bpy
import csv

FILE = filename = 'T:/data.csv'
OUTPUT = 'Other/Output/'

arrow = bpy.data.objects['Arrow']
target = bpy.data.objects['Target']
bottom = bpy.data.objects['Cover']  # find which vertex of pyramid is moving

ind = -1
for i, vert in enumerate(bottom.data.vertices):
    if vert.co[2] > 0.5:
        ind = i
        break  # Should be break here?
assert ind != -1, "Should find a vertice index here."

with open(filename, newline='') as data:  # Is filename defined before?
    reader = csv.DictReader(data)
    counter = 0
    # Create a dictionary, key is the frame number.
    data_frame = {
        int(row['SampleTime']): {
            'x': float(row['hB']),
            'y': float(row['hA']),
            'arrow.rot': (-float(row['hA']) / 3, float(row['hB']) / 3, 0),
            'target.loc': (float(row['gestTarget0']), float(row['gestTarget1']), 0.9)
        }
        for row in reader
        if row.get('A', None) is not None
    }

def obj_handler(scene):
    fn = scene.frame_current
    try:
        arrow.rotation_euler = data_frame[fn]['arrow.rot']
        target.location = data_frame[fn]['target.loc']
        bottom.data.vertices[ind].co.x = data_frame[fn]['x'] / 5
        bottom.data.vertices[ind].co.y = data_frame[fn]['y'] / 5
    except KeyError:
        print(f"Frame number ({fn}) is not in Sample Time.")
        pass
    except:
        raise

bpy.app.handlers.frame_change_pre.append(obj_handler)

# Render Animation

The data used:

enter image description here

The scene move correctly according to data fed:

enter image description here

$\endgroup$
3
  • $\begingroup$ I am not sure if Blender video encoder use some method to dynamic flush image to the encoder and release them when the chunk is written. In the worst case, it will still write a cache image file into your disk, or it will require 6000 image memory space which can be large. $\endgroup$
    – HikariTW
    Commented Aug 24, 2020 at 14:06
  • $\begingroup$ And when rendering with Blender, you might need to check "Lock Interface" to prevent crash since handler is changing data that interface is accessing. $\endgroup$
    – HikariTW
    Commented Aug 25, 2020 at 1:40
  • 1
    $\begingroup$ I was finally able to test it and it works very well. To summarize my understanding of it: 1) I load the whole csv into dict w SampleTime being the main key. 2) Then I define object handler function that gets current frame num and sets the positions, rotations etc of all objects according to the dict specified in step 1). If there is any other mapping than 1:1 between frame num and sample time I specify it here. 3) frame_change_pre handler appends the specified function to all other functions that get called normally and whenever there is a frame change all functions are executed $\endgroup$
    – halal
    Commented Aug 28, 2020 at 17:49
0
$\begingroup$

By default blender don't allow to animate in editmode(i.e keyframing vertices properties). So to over come this problem you need to enable AnimAll addon at "preference->addon -> AnimAll". This creates a new tab in right side panel of "3d port view". Go-to this panel unmark select only check box and Mark points check box. Hope this help you.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .