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$
11
  • $\begingroup$ Can you include the text file so we can help encoding it to an animation? $\endgroup$
    – Bert VdB
    Commented May 26, 2017 at 11:51
  • $\begingroup$ Thanks of your interest in my question. I added a link to the file. $\endgroup$
    – Mzidare
    Commented May 26, 2017 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$ Commented May 29, 2017 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
    Commented May 30, 2017 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
    Commented May 30, 2017 at 16:27

1 Answer 1

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$
5
  • $\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
    Commented May 30, 2017 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
    Commented May 31, 2017 at 12:45
  • $\begingroup$ The script works flawlessly. Thank You so much @brockmann $\endgroup$
    – Mzidare
    Commented Jun 1, 2017 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
    Commented Jun 1, 2017 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
    Commented Jun 1, 2017 at 8:41

You must log in to answer this question.

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