Skip to content

Instantly share code, notes, and snippets.

@n-burk
Last active February 13, 2022 17:27
Show Gist options
  • Save n-burk/f9ed11d0342e3d1587353f39d824e60d to your computer and use it in GitHub Desktop.
Save n-burk/f9ed11d0342e3d1587353f39d824e60d to your computer and use it in GitHub Desktop.
Blender geo node post animation (local space) sculpt
##############################################
# Select mesh and run script, new collection is created
# and keyable floats are dropped on mesh's new geoNode
# sculpting in action
# https://drive.google.com/file/d/1jDXskWJ0mx_aJMjBoaeRhuHS8vBOhwxT/view?usp=sharing
#
##############################################
import bpy
def dupShapes(shape, sculptCollection):
filledFrame ='{}'.format(bpy.context.scene.frame_current).zfill(4)
#sculptshape
bpy.ops.object.duplicate({"object" : shape})
modShape = bpy.context.selected_objects[0]
for mod in modShape.modifiers:
bpy.ops.object.modifier_apply({"object" : modShape}, modifier=mod.name)
modShape.name = '{}.{}.sculptShape'.format(shape.name, filledFrame)
for collection in modShape.users_collection:
collection.objects.unlink(modShape)
bpy.ops.object.parent_clear()
sculptCollection.objects.link(modShape)
modShape.select_set(False)
modShape.parent = None
shape.select_set(True)
bpy.ops.object.duplicate({"object" : shape})
baseShape = bpy.context.selected_objects[0]
bpy.ops.object.select_all(action='DESELECT')
baseShape.select_set(True)
baseShape.name = '{}.{}.baseShape'.format(shape.name, filledFrame)
baseShape.parent = None
for mod in baseShape.modifiers:
bpy.ops.object.modifier_apply({"object" : baseShape} ,modifier=mod.name)
for collection in baseShape.users_collection:
collection.objects.unlink(baseShape)
bpy.ops.object.parent_clear()
sculptCollection.objects.link(baseShape)
baseShape.hide_viewport = True
baseShape.hide_render = True
modShape.hide_render = True
return [modShape, baseShape]
def sculptCollection():
found = False
for collection in bpy.data.collections:
if 'sculpt' in collection:
return collection
sculptCollection = bpy.data.collections.new('sculpt')
bpy.context.scene.collection.children.link(sculptCollection)
sculptCollection['sculpt'] = 1
return sculptCollection
def recursiveChainFinder(node, attrIndex):
foundNode = None
if node.inputs[attrIndex].links:
inNode = node.inputs[attrIndex].links[0].from_node
foundNode = recursiveChainFinder(inNode, attrIndex)
return foundNode
return node
def geomNodeSetup(shape, baseShape, modShape):
filledFrame ='{}'.format(bpy.context.scene.frame_current).zfill(4)
if not 'sculpt' in shape.data:
shape.modifiers.new('SculptFrame', 'NODES')
sculptMod = shape.modifiers['SculptFrame']
shape.data['sculpt'] = sculptMod.name
setPos = sculptMod.node_group.nodes.new('GeometryNodeSetPosition')
shape.data['setPos'] = setPos.name
inGeo = sculptMod.node_group.nodes['Group Input']
outGeo = sculptMod.node_group.nodes['Group Output']
sculptMod.node_group.links.new(inGeo.outputs['Geometry'], setPos.inputs['Geometry'])
sculptMod.node_group.links.new(setPos.outputs['Geometry'], outGeo.inputs['Geometry'])
sculptMod = shape.modifiers[shape.data['sculpt']]
setPos = sculptMod.node_group.nodes[shape.data['setPos']]
#grab old input
posInput = setPos.inputs['Offset'].links[0].from_node if setPos.inputs['Offset'].links else None
newVecAdd = sculptMod.node_group.nodes.new('ShaderNodeVectorMath')
frameMag = sculptMod.node_group.inputs.new("NodeSocketFloatFactor", "{}_sculptMag".format(filledFrame))
frameMag.min_value = 0.0
frameMag.max_value = 1.0
frameMag.default_value = 1.0
if posInput:
recursedInput = recursiveChainFinder(posInput, 0)
sculptMod.node_group.links.new(newVecAdd.outputs['Vector'], recursedInput.inputs[0])
else:
sculptMod.node_group.links.new(newVecAdd.outputs['Vector'], setPos.inputs['Offset'])
baseInfo = sculptMod.node_group.nodes.new('GeometryNodeObjectInfo')
baseInfo.inputs['Object'].default_value = baseShape
modInfo = sculptMod.node_group.nodes.new('GeometryNodeObjectInfo')
modInfo.inputs['Object'].default_value = modShape
posId = sculptMod.node_group.nodes.new('GeometryNodeInputPosition')
baseCapPos = sculptMod.node_group.nodes.new('GeometryNodeAttributeTransfer')
modCapPos = sculptMod.node_group.nodes.new('GeometryNodeAttributeTransfer')
sculptMod.node_group.links.new(baseInfo.outputs['Geometry'], baseCapPos.inputs['Target'])
sculptMod.node_group.links.new(modInfo.outputs['Geometry'], modCapPos.inputs['Target'])
baseCapPos.data_type = 'FLOAT_VECTOR'
modCapPos.data_type = 'FLOAT_VECTOR'
baseCapPos.mapping = 'INDEX'
modCapPos.mapping = 'INDEX'
sculptMod.node_group.links.new(posId.outputs['Position'], baseCapPos.inputs['Attribute'])
sculptMod.node_group.links.new(posId.outputs['Position'], modCapPos.inputs['Attribute'])
vecSub = sculptMod.node_group.nodes.new('ShaderNodeVectorMath')
vecSub.operation = 'SUBTRACT'
vecMag = sculptMod.node_group.nodes.new('ShaderNodeVectorMath')
vecMag.operation = 'MULTIPLY'
sculptMod.node_group.links.new(modCapPos.outputs['Attribute'], vecSub.inputs[0])
sculptMod.node_group.links.new(baseCapPos.outputs['Attribute'], vecSub.inputs[1])
sculptMod.node_group.links.new(vecSub.outputs['Vector'], vecMag.inputs[0])
sculptMod.node_group.links.new(sculptMod.node_group.nodes['Group Input'].outputs[frameMag.name], vecMag.inputs[1])
sculptMod.node_group.links.new(vecMag.outputs['Vector'], newVecAdd.inputs[1])
sel = bpy.context.selected_objects[0]
sculptCollection = sculptCollection()
sculptShapes = dupShapes(sel, sculptCollection)
geomNodeSetup(sel, sculptShapes[1], sculptShapes[0])
bpy.ops.object.select_all(action='DESELECT')
sel.select_set(True)
bpy.context.view_layer.objects.active = sel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment