Showing posts with label Unreal Engine. Show all posts
Showing posts with label Unreal Engine. Show all posts

Thursday, October 16, 2025

Unreal Engine control rig blendshape doodle using Blender blendshapes

i'm still very new to Unreal Engine's control rig but thought this doodle may be helpful for learning.  there may be better ways to implement.

here is an example of driving 4 blendshapes (the blendshapes were created in Blender and exported to fbx so they could be imported into Unreal Engine):


here is a picture of the control rig graph for a simpler example driving 2 blendshapes.  similar logic can be used by copying and pasting these nodes and changing up the blendshape names and driving transformation channel to get it to work for 4 blendshapes.

here is a picture of the control rig graph that has the various nodes used to drive 2 blendshapes.


basically starting left to right:

on far left is the animator control that will be used to drive the blendshapes.

next it uses a 'Greater' node to decide if the controls transform z channel is positive or negative. here the z axis of the control will be used to drive 2 blendshapes.  if the z translate axis is positive it uses its value after clamping it to a number between 0 and 1 to drive the first blendshape which is set in the 'set curve value' node. if the control's z is negative it first multiplies its value by -1 to get a positive number then it also eventually clamps its value and sends it to the second blendshape 'set curve value' node. (some of the float multiplies was to tweak the sensitivity of how much the animator control moves is enough to turn on blendshapes).

Hope this is helpful.

Happy Sketching!


also i highly recommend checking out JC's YouTube channel for learning about control rig.

inspired by,

JC's '3D Education with JC' YouTube channel's 'Build a control rig from scratch' video

Wednesday, May 28, 2025

learning python scripting in Unreal Engine for Artists

i'm still very much a beginner in python scripting in Unreal Engine but thought these personal work python snippets i made may be helpful for learning.  there may be bugs and better ways to code them so please modify/use at your own risk.


import unreal

import logging

logger = logging.getLogger(__name__)

logging.basicConfig(level=logging.DEBUG)


print("yaaay running a script")


def doIt():

#addCamera()

#addBoneActor()

#translateActor("TestCylinder", translateY=0.0)


#getCameras()

#getCameraPosition()


#translateExistingCamera()

#debug()

#setMaterialAllStaticMeshes(materialName="/Engine/VREditor/UI/FrameMaterial.FrameMaterial")

#setMaterialAllStaticMeshes()


#debug()

#printKeyFramesControl()


#???translateControlRig()

#createFkControl()

#applyMaterial()


#createMaterial()

#addCubeToLevelSequence()


#printStaticActorsInOutliner()


#printStaticActorsInFocusedLevelSequence()


def debug():

#print("material" in dir(unreal.StaticMeshActor))

print(dir(unreal.MovieSceneScriptingFloatChannel))



def printStaticActorsInOutliner():

print("yaaay printing stuff")

pass


actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_all_level_actors()

meshActors = [act.get_actor_label() for act in actors if isinstance(act, unreal.StaticMeshActor)]


print(meshActors)




def printStaticActorsInFocusedLevelSequence():

print("yaay static actors in focused level sequence")

levelSequence = unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence()


print(levelSequence, type(levelSequence))

#LogPython: <Object '/Game/TestA_LS.TestA_LS' (0x000008A280176680) Class 'LevelSequence'> <class 'LevelSequence'>


#get static mesh actors in this level sequence

#help(levelSequence)


staticMeshActors = []


#need to use binding

rootBindings = levelSequence.get_bindings()

for binding in rootBindings:

print(type(binding)) #LogPython: <class 'MovieSceneBindingProxy'>

actorName = binding.get_name()

print(actorName)


bindingClass = binding.get_possessed_object_class()

print(bindingClass)

print(type(bindingClass))

print(bindingClass.get_name())

#only store static meshes

if not bindingClass.get_name() == "StaticMeshActor":

continue

staticMeshActors.append(actorName)

print(staticMeshActors)

return staticMeshActors


def addCubeToLevelSequence():

#add a cube static mesh to an existing level sequence


#change these to where to find level sequence

folderName = '/Game/testSequences'

levelSequenceName = 'testShot_a'

#change this to what name of cube static mesh is in outliner

meshName = "Cube" 

levelSequence = unreal.load_asset("{0}/{1}".format(folderName, levelSequenceName))


print(type(levelSequence)) #LevelSequence


#find static mesh actor

actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_all_level_actors()

meshActors = [act for act in actors if isinstance(act, unreal.StaticMeshActor) and act.get_actor_label() == meshName]


if not meshActors:

return


meshActor = meshActors[0]

unreal.log(type(meshActor)) #StaticMeshActor


#add static mesh actor to level sequence

levelSequenceSystem = unreal.get_editor_subsystem(unreal.LevelSequenceEditorSubsystem)

bindings = levelSequenceSystem.add_actors([meshActor])


#inspired by dev dot epicgames dot com : Python Scripting in Sequencer






def createMaterial():

#create a green material

#create base material

matName = "testGreenMat" #name of material

location = "/Game/testMaterials" #assumes this already exists. it is where to save material


assetTools = unreal.AssetToolsHelpers.get_asset_tools()

material = assetTools.create_asset(matName, 

location,

unreal.Material,

unreal.MaterialFactoryNew()) #returns material object


#material = unreal.load_asset("/Game/testMaterials/testGreenMat")


#create constant 3 vector

constantNode = unreal.MaterialEditingLibrary.create_material_expression(material,

unreal.MaterialExpressionConstant3Vector,

-400, 

200)


#change to green constant and plug it into base color

constantNode.constant = unreal.LinearColor(0,1,0)


unreal.MaterialEditingLibrary.connect_material_property(constantNode,

"",

unreal.MaterialProperty.MP_BASE_COLOR)

#save

unreal.MaterialEditingLibrary.recompile_material(material)


#inspired by TomCic on Unreal Engine developer forum: forums dot unrealengine dot com




def applyMaterial(materialName = "/Game/testMaterials/testMatYellow"):

#apply an existing material to selected static mesh

material = unreal.load_asset(materialName)

print(type(material))


actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_selected_level_actors()


if not actors:

return


mesh = actors[0]

if not isinstance(mesh, unreal.StaticMeshActor):

unreal.log("requires a selected static mesh actor")

return


meshComponent = mesh.get_component_by_class(unreal.StaticMeshComponent)

unreal.log("meshComponent:{}".format(meshComponent))

meshAsset = meshComponent.get_editor_property("static_mesh")

unreal.log("meshAsset:{}".format(meshAsset))

unreal.log("type meshAsset")

unreal.log(type(meshAsset)) #StaticMesh

meshAsset.set_material(0, material) #using 0 material index




def createFkControl():

#creating fk control on existing control rig

rigs = unreal.ControlRigBlueprint.get_currently_open_rig_blueprints()


print("open rigs:",rigs)

#LogPython: open rigs: ["/Script/ControlRigDeveloper.ControlRigBlueprint'/Game/simple_leg_scene_CtrlRig.simple_leg_scene_CtrlRig'"]


#print(type(rigs[0]))  # <class 'ControlRigBlueprint'>


#controller = rigs[0].get_controller()

#print(controller)

#print(type(controller)) #<class 'RigVMController'>


controller = rigs[0].get_hierarchy_controller()

#print(controller)

#print(type(controller)) #<class 'RigHierarchyController'>



controlSetting = unreal.RigControlSettings()

controller.add_control('yaay_ctrl', 

unreal.RigElementKey(type=unreal.RigElementType.BONE, name='upLeg'), 

controlSetting, 

unreal.RigHierarchy.make_control_value_from_euler_transform(unreal.EulerTransform(location=[0.000000,0.000000,0.000000],rotation=[0.000000,-0.000000,0.000000],scale=[1.000000,1.000000,1.000000]))

)





#levelSequence = unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence()#get_focused_level_sequence()#"animMap"

#rigs = unreal.ControlRigSequencerLibrary.get_control_rigs(levelSequence)

#print("rigs:",rigs)

#print(type(rigs[0]))

#rigs[0] #ControlRigSequencerBindingProxy

#print(type(rigs[0].control_rig)) #ControlRig

#controlRig = rigs[0].control_rig


#print("controlRig")

#print(controlRig)

#print("levelSequence", levelSequence)


def translateControlRig():

#???

levelSequence = unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence()#get_focused_level_sequence()#"animMap"

rigs = unreal.ControlRigSequencerLibrary.get_control_rigs(levelSequence)

#print(type(rigs[0]))

#rigs[0] #ControlRigSequencerBindingProxy

#print(type(rigs[0].control_rig)) #ControlRig

controlRig = rigs[0].control_rig


print("controlRig")

print(controlRig)

print("levelSequence", levelSequence)

print(controlRig.current_control_selection())

worldTransform = unreal.ControlRigSequencerLibrary.get_control_rig_world_transform(levelSequence, controlRig, "Bone_ctrl", unreal.FrameNumber(1))

#print("world transform")

#print(worldTransform)

curTransform = unreal.ControlRigSequencerLibrary.get_local_control_rig_euler_transform(levelSequence, controlRig, "Bone_ctrl", unreal.FrameNumber(0)) #depends on value of control rig set to euler transform

print(curTransform)

unreal.ControlRigSequencerLibrary.set_local_control_rig_euler_transform(levelSequence, controlRig, "Bone_ctrl", unreal.FrameNumber(1), unreal.EulerTransform(location=[0, 0, 21]), set_key=False)

#curTransform = unreal.ControlRigSequencerLibrary.get_local_control_rig_euler_transform(levelSequence, controlRig, "Bone_ctrl", unreal.FrameNumber(0)) #depends on value of control rig set to euler transform

#print(curTransform)




def printKeyFramesControl():

#print all keyframes on a control of a control rig

logger.info("yaay - printing keyframes")


controlRigName = "cube_skinned_b_CtrlRig"

controlName = "Bone_ctrl"

attr = "Location.Y"

levelSequence = unreal.LevelSequenceEditorBlueprintLibrary.get_current_level_sequence()#get_focused_level_sequence()#"animMap"

rigs = unreal.ControlRigSequencerLibrary.get_control_rigs(levelSequence)


#print(rigs)

rig = rigs[0].control_rig

print(rig)

#<Object '/Game/cube_skinned_b_CtrlRig_Take1.cube_skinned_b_CtrlRig_Take1:MovieScene_0.MovieSceneControlRigParameterTrack_0.cube_skinned_b_CtrlRig' (0x000005BC19285400) Class 'cube_skinned_b_CtrlRig_C'>


rigTrack = rigs[0].track

print("rigTrack")

print(rigTrack)


keyedSection = rigTrack.get_section_to_key()

print("keyedSection")

print(keyedSection)


channelObjs = keyedSection.get_all_channels()#get_channel(channelName)

for channelObj in channelObjs:

print(channelObj)

print(type(channelObj))

logger.info(channelObj.get_path_name())

keys = channelObj.get_keys()

print("keys >>>")

print(keys)


for key in keys:

keyTime = key.get_time().frame_number.value


keyValue = key.get_value()


print(keyTime, keyValue)




def setMaterialAllStaticMeshes(materialName="/Engine/MaterialTemplates/Gradients/Gradient_Linear.Gradient_Linear"):

meshes = getStaticMeshes()

print(meshes)

material = unreal.load_asset(materialName)

print(material)

print(type(material))


for mesh in meshes:

meshComponent = mesh.get_component_by_class(unreal.StaticMeshComponent)

logger.info("meshComponent:{}".format(meshComponent))

meshAsset = meshComponent.get_editor_property("static_mesh")

logger.info("meshAsset:{}".format(meshAsset))

meshAsset.set_material(0, material)

def getStaticMeshes():

actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_all_level_actors()


#print(actors)

result = [x for x in actors if isinstance(x, unreal.StaticMeshActor)]


return(result)



def translateExistingCamera():

logger.info("translate a camera...")


cameras = getCameras() or []

if not cameras:

return


camera = cameras[0]


location = camera.get_actor_location()

logger.info("before cam position {}".format(location))


newLocation = unreal.Vector(location.x, location.y+100, location.z)

camera.set_actor_location(newLocation, False, False) #position, sweep, teleport


location = camera.get_actor_location()

logger.info("after cam position {}".format(location))



def getCameraPosition():

cameras = getCameras() or []

if not cameras:

return


camera = cameras[0]


location = camera.get_actor_location()

logger.info("cam position {}".format(location))


def getCameras():

actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_all_level_actors()


#print(actors)

result = [x for x in actors if isinstance(x, unreal.CineCameraActor)]

print(result)

#for a in actors:

# print(type(a))

return result


def addCamera():

logging.info("adding camera")


#add camera actor

#CineCameraActor #can get this by manually creating

actorClass = unreal.CineCameraActor

cylinderActor = unreal.EditorLevelLibrary.spawn_actor_from_class(actorClass, unreal.Vector(0,0,20), unreal.Rotator(0,0,0)) #class, translation, rotation


#add actor to level sequence

levelSystem = unreal.get_editor_subsystem(unreal.LevelSequenceEditorSubsystem) #?one per level sequence

levelSystem.add_actors([cylinderActor])



def addBoneActor():

print("add bone actor")

#loading a skeletal mesh

mesh = unreal.load_object(name="/Game/cube_skinned_b", outer=None)

factory = unreal.ControlRigBlueprintFactory

rig = factory.create_control_rig_from_skeletal_mesh_or_skeleton(selected_object=mesh)


hierarchy = rig.hierarchy

elements = hierarchy.get_all_keys()

for e in elements:

print(e)


    #LogPython: <Struct 'RigElementKey' (0x0000052EDB1D92E0) {type: Bone, name: "Bone"}>

#LogPython: <Struct 'RigElementKey' (0x0000052EDB1D63F0) {type: Bone, name: "Bone_end"}>

"""

    hierarchyCtrl = rig.get_hiearchy_controller()

    #add bone

    newBone = hierarchyCtrl.add_bone(name="testBone", parent=unreal.RigElementKey(), transform=unreal.Transform())


    #add an end bone to bone at a given offset

    newLoc = unreal.Transform(location=[0, 0, 5]) #xyz  offset

    endBone = hierarchyCtrl.add_bone(name="testEndBone", parent=newBone, transform=newLoc)

    """


def translateActor(actorName, translateY=5.0):

#shows info on finding an actor by name. and setting an actor location attribute

#unreal.Actor python docs

actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_all_level_actors()


actor = [x for x in actors if x.get_actor_label() == actorName]

if not actor:

return

actor = actor[0]


print(actor)


curLoc = actor.get_actor_location()

newLoc = unreal.Vector(curLoc.x, translateY, curLoc.z)

actor.set_actor_location(newLoc, False, False) #position, sweep, teleport



"""

def addCylinderActor():

#add an actor to scene

print("addCylinderActor")


cylinderMesh = unreal.load_asset('/Engine/BasicShapes/Cylinder') #to get path - can create a sphere and in ui gui can browse to its folder location

#actorClass = unreal.StaticMeshActor

cylinderActor = unreal.EditorLevelLibrary.spawn_actor_from_object(cylinderMesh, unreal.Vector(0,0,10), unreal.Rotator(0,0,0)) #object, translation, rotation

"""


"""

def addCylinderActor_v0():

#add an actor to scene

print("addCylinderActor")


cylinderMesh = unreal.load_asset('/Engine/BasicShapes/Cylinder') #to get path - can create a sphere and in ui gui can browse to its folder location

actorClass = unreal.StaticMeshActor

cylinderActor = unreal.EditorLevelLibrary.spawn_actor_from_class(actorClass, unreal.Vector(0,0,70), unreal.Rotator(0,0,0)) #class, translation, rotation

"""


"""

def printActors():

#print actors in scene


actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_all_level_actors()

print(actors)


def printSelectedActors():

actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_selected_level_actors()

print(actors)

print(len(actors))

unreal.log("printSelectedActors")

#print(type(actors[0]))

#LogPython: <class 'StaticMeshActor'>


#staticMeshComponent = actors[0].get_editor_property("static_mesh_component")

#print(type(staticMeshComponent))

#LogPython: <class 'StaticMeshComponent'>


def translateUpSelectedActors(amount=5):

actorSystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)

actors = actorSystem.get_selected_level_actors()

for actor in actors:

#from unreal.Actor

actor.add_actor_world_offset((0,0,amount), False, False)


"""


#printActors()

#printSelectedActors()

#translateUpSelectedActors(amount=20)

#addCylinderActor()


doIt()


#py "C:\Users\Nathaniel\Documents\unreal_learning\dev_area\topics.py"


#inspired by

#https://forums.unrealengine.com/t/how-to-create-static-mesh-actors-in-level-using-python-script/473670/2



Thanks for looking


inspired by,

Matt Lake MattLakeTA

matthewlake dot net