4
$\begingroup$

I have a text file with coordinates.In each line are positions of x, y, and z, for 9 objects. How could I for each line make Blender get coordinate from current line and create a keyframe for bone movement with it, using python? I was trying to solve it by my self, but for some strange reason, when I try to use file.read() and file.readlines() in the same script second one stops working. Thanks in advance for any answers and useful hints. Here is the file: https://www.dropbox.com/s/1eqf0zun2x5s89c/text3.txt?dl=0

First set of numbers is x of the first object second y and third z. Fourth is x of second object and so on. letters "e", "+" sings and 000 or 0001 after the plus signs are irrelevant and should be omitted.

$\endgroup$
  • $\begingroup$ Can you include the text file so we can help encoding it to an animation? $\endgroup$ – Bert VdB May 26 '17 at 11:51
  • $\begingroup$ Thanks of your interest in my question. I added a link to the file. $\endgroup$ – Mzidare May 26 '17 at 13:40
  • 1
    $\begingroup$ Why would you need to use both file.read and file.readlines for this case? If you need help scripting, post a MCVE. $\endgroup$ – Colonel Thirty Two May 29 '17 at 19:10
  • $\begingroup$ The e and the number following is not irrelevant. It is e-notation. Eg 1000 == 1.0e+003 Also suggest posting your script or a part thereof to determine a) the cause of your hassles, b) whether it is just a python issue and c) what you have tried so far. $\endgroup$ – batFINGER May 30 '17 at 13:00
  • $\begingroup$ Yes @brockmann it's 9 - thanks for noticing - corrected. The rig looks like separated bones (you can imagine them as not connected points in space). Each bone has to influence a mesh in it's nearest proximity(but this I'll take care of by hand, no need to include this part in the script). The mesh at this point will be just some subdivaded plane. The ultimate goal is to feed Blender with the text file and this way creating an animation of this bones. $\endgroup$ – Mzidare May 30 '17 at 16:27
3
+200
$\begingroup$

Hardest part is formatting the data (IMHO). I'd probably read the values and create a dictionary of location lists per object, which allows to structure and access the values in a reasonable way:

dict = {
    object-1 : [
       [row1-X, row1-Y, row1-Z], # Frame 1
       [row2-X, row2-Y, row2-Z], # Frame 2
       [row3-X, row3-Y, row3-Z], # Frame 3
       ...
    ], 
    object-2 : [
       [row1-X, row1-Y, row1-Z], # Frame 1
       [row2-X, row2-Y, row2-Z], # Frame 2
       [row3-X, row3-Y, row3-Z], # Frame 3
       ...
    ],
    ...
}

Reads the data and creates the dictionary

import bpy

file_data = [] # create an empty list
animation_data = {} # create an empty dictionary

# read the file
with open(<PATH-TO-FILE>) as f:
    content = f.readlines()
    for line in content:
        # store the line as list in file_data
        file_data.append(line.split())

# create location keyframe dictonary
for line in file_data:
    try: # try converting strings to floats
        floats = [float(i) for i in line] 
        # create composite list of this line
        composite = [floats[x:x+3] for x in range(0, len(floats),3)]
        # store it in the dict to organize the values
        for c, item in enumerate(composite): 
            #key = "object" +  str(c)
            animation_data.setdefault(c, []).append(item)
    except ValueError:
        print ("Incorrect data")

Adds one cube per dict item to the scene as well as the keys, starting at frame 1

if animation_data:
    for i in animation_data:
        bpy.ops.mesh.primitive_cube_add()
        # get the newly created cube
        obj = bpy.context.object
        # get keyframe location values
        obj_keyframes = animation_data.get(i)
        # start animation at frame 1
        for c, loc in enumerate(obj_keyframes, 1): 
            bpy.context.scene.frame_set(c) # set frame
            # move the cube and insert a keyframe
            obj.location = (loc[0], loc[1], loc[2])
            obj.keyframe_insert(data_path="location", index=-1) 

It's basically the same for loose bones of an armature

if animation_data:
    # get the selected object
    selected_obj = bpy.context.object
    if selected_obj.type == "ARMATURE":
        # continue if number of bones match the data
        if len(selected_obj.data.bones) == len(animation_data):
            for i in animation_data:
                # get the values from the dict
                obj_keyframes = animation_data.get(i)
                for c, loc in enumerate(obj_keyframes, 1):
                    bpy.context.scene.frame_set(c) # set the frame
                    # move the bone and insert a keyframe
                    selected_obj.pose.bones[i].location = (loc[0], loc[1], loc[2])
                    selected_obj.pose.bones[i].keyframe_insert(data_path="location", index=-1)
        else:
            print ("Data does not match number of bones")
$\endgroup$
  • $\begingroup$ Thank you, I got already one more gyrus in my brain thanks to well explained peace of code of yours. My goal though is not to create new objects in scene, but to apply positions from the text file to already existing bones, which are in the same armature, but are not parented or connected to each other. $\endgroup$ – Mzidare May 30 '17 at 16:46
  • $\begingroup$ I hope you get the idea. Tried my best to explain the code in comments. Just let me know if there is something you do not understand @Mzidare $\endgroup$ – brockmann May 31 '17 at 12:45
  • $\begingroup$ The script works flawlessly. Thank You so much @brockmann $\endgroup$ – Mzidare Jun 1 '17 at 6:45
  • $\begingroup$ One more question though if you don't mind. How should I go about changing decimal place of the coordinates, to make movement more or less noticeable? $\endgroup$ – Mzidare Jun 1 '17 at 7:31
  • $\begingroup$ No problem @Mzidare, you are welcome. I'm not sure what your actual goal is but at a first glance, simple math within the floats list comprehension should do the trick floats = [ ( float(i) / 10 ) for i in line ] ...? $\endgroup$ – brockmann Jun 1 '17 at 8:41

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.