4
$\begingroup$

I've been able to assign an fcurve modifier to to fcurves in my scripts, but I can't figure out how to modify them once created.

here's what the info panel spits out when I do it manually:

bpy.ops.graph.fmodifier_add(type='LIMITS')                   #creates fcurve mod
bpy.data.node_groups["Shader Nodetree"].(null) = True        #"maximum y" set to true
bpy.data.node_groups["Shader Nodetree"].(null) = 0           #"maximum y" value set to 0
bpy.data.node_groups["Shader Nodetree"].(null) = True        #"use influence" set to true
bpy.data.node_groups["Shader Nodetree"].(null) = 0.5         #"use influence" set to 0.5

clearly this won't work in a script as is. what is the correct syntax for what I am doing?

EDIT:

I tried using zeffii's code, but I get an error from this line.

fcurve = ob.animation_data.action.fcurves[0]

error:

AttributeError: 'NoneType' object has no attribute 'action'

here is my code in it's entirety:

import bpy
import bmesh
import pprint


for i in range(1):

    bpy.ops.mesh.primitive_cube_add(radius=1, view_align=False, enter_editmode=False, location=(i-5,0,0))

    ob = bpy.context.active_object

    #sets origin to bottom of object
    bpy.context.scene.cursor_location = ob.location
    bpy.context.scene.cursor_location.y -= 1
    bpy.ops.object.origin_set(type='ORIGIN_CURSOR')

    ob.scale = (0.5, 1.0, 0.5)

    mat = bpy.data.materials.new(str(ob.name) + "mat")
    mat.diffuse_color = (1,0,0)

    mat.use_nodes = True

    nodes = mat.node_tree.nodes

    # clear all nodes to start clean
    for node in nodes:
        nodes.remove(node)

    # create emission node
    node_emission = nodes.new(type='ShaderNodeEmission')
    node_emission.inputs[0].default_value = (0,1,0,1)  # green RGBA
    node_emission.inputs[1].default_value = 0.9 # strength
    node_emission.location = 0,0

    # create output node
    node_output = nodes.new(type='ShaderNodeOutputMaterial')   
    node_output.location = 400,0

    # link nodes
    links = mat.node_tree.links
    link = links.new(node_emission.outputs[0], node_output.inputs[0])


    ob.data.materials.append(mat)

    node = bpy.context.active_object.active_material.node_tree.nodes['Emission']
    node.inputs[1].keyframe_insert(data_path="default_value",frame=1)

    bpy.context.area.type = 'GRAPH_EDITOR'    
    bpy.ops.graph.sound_bake(filepath=r'C:\Users\andga\Desktop\Garrett\Music\Knife Party\Singles & EP\2012 - Rage Valley\01-knife_party-rage_valley.mp3', attack = 0.0001, release = 1.1)

    # pick the fcurve to add to
    fcurve = ob.animation_data.action.fcurves[0]   #here is were the error comes from

    # add a modifier and get a reference.
    limits = fcurve.modifiers.new(type='LIMITS')
    limits.use_max_x = True
    limits.max_x = 0.0

    limits.use_influence = True
    limits.influence  = 0.5
$\endgroup$

2 Answers 2

9
$\begingroup$

Adding a modifier to an FCurve is much like adding a modifier to an Object, you first add it, then you adjust the properties.

Adding the Modifier + Setting properties

Here is shown how to add a modifier to an existing fcurve for an object, but it's very similar when the subject is a keyframed property of a shader tree.

import bpy

obj = bpy.data.objects['Plane']

# pick the fcurve to add to
fcurve = obj.animation_data.action.fcurves[0]

# add a modifier and get a reference.
limits = fcurve.modifiers.new(type='LIMITS')
limits.use_max_x = True
limits.max_x = 0.23
limits.use_influence = True
limits.influence = 0.5

The shadertree version

mat = bpy.data.materials['some_material']
fcurve =  mat.node_tree.animation_data.action.fcurves[0]

The attributes of the modifier can be found by

  • hovering over the UI elements and reading the tooltip
  • using the Python Console and use the auto-complete feature to dig through the list of attributes. (admittedly not very graceful)

Normally you don't need to set all possible properties of the modifier, the modifier will come with all its default values set (just like when you add via the GUI). The properties are named fairly logical and the above methods of finding the properties should be sufficient.

Picking the FCurve

Picking the FCurve from the UI is direct, but using a script to pick the FCurve will take some practice. Luckily there's a method to get fcurve by name and index.

# to get a reference to an fcurve for location, specifically the
# x component. index 0 is x.
action.fcurves.find('location', index=0)

enter image description here

or for materials with keyframed node properties

fcurves = mat.node_tree.animation_data.action.fcurves

# get the reference this way
fcurve = fcurves.find('nodes["Emission"].inputs[1].default_value')
# >> bpy.data.actions['Shader NodetreeAction']...FCurve

Figuring out what to use in the fcurves.find() method, can involve doing a little experimenting (a dry run) and first see what fcurves[your index].data_path returns. Or use the UI and read what the Active F-Curve says:

enter image description here

for the materials..

enter image description here

$\endgroup$
3
  • $\begingroup$ Thank you for such a detailed explanation! I'm still getting errors. check my edit in the original post. $\endgroup$
    – andgarrett
    Jan 31, 2016 at 21:31
  • $\begingroup$ @zeffii Finding the right fcurve is easier now, from 2.76 we have fcurves.find() so you can use action.fcurves.find('location', index =1) to get the curve for location.y $\endgroup$
    – sambler
    Feb 1, 2016 at 8:22
  • $\begingroup$ andgarret - it appears sambler has answered your modified question, the issue there is really picking which fcurve to add the modifier to.. --- @sambler nice to know this! $\endgroup$
    – zeffii
    Feb 1, 2016 at 9:06
4
$\begingroup$

The error AttributeError: 'NoneType' object has no attribute 'action' is saying that animation_data (which is the object you are trying to access the action property from) is resolving to None - meaning it does not exist, which is because you haven't animated any properties of the object.

In your situation, you are animating a material property which stores the animation data within the material not the object using the material.

The fcurve you are looking for can be found at

fcurve = mat.node_tree.animation_data.action.fcurves[0]
$\endgroup$
0

You must log in to answer this question.

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