pKrime/brignet

Blender 3.1 introduces Python 3.10 API which rules out PyTorch 1.8.1 dependencies

PierceLBrooks opened this issue · 7 comments

The version 3.1 release patch notes for Blender show the internal distribution of Python has been bumped up to 3.10:
https://wiki.blender.org/wiki/Reference/Release_Notes/3.1/Python_API#Python_3.10

This causes problems for resolving dependencies upon installation through the pip package manager using the provided wheel module listing, which only offers support for the necessary CUDA versions up to Python 3.9:
https://download.pytorch.org/whl/torch_stable.html

Here is some terminal output from an installation attempt through the Blender UI to illustrate this:

Read prefs: C:\Users\Pierce\AppData\Roaming\Blender Foundation\Blender\3.2\config\userpref.blend
Reloading external rigs...
Reloading external metarigs...
adding C:\Users\Pierce\AppData\Roaming\Blender Foundation\Blender\3.2\scripts\addons\brignet\_additional_modules\Lib
adding C:\Users\Pierce\AppData\Roaming\Blender Foundation\Blender\3.2\scripts\addons\brignet\_additional_modules\Lib\site-packages
C:\Users\Pierce\AppData\Roaming\Blender Foundation\Blender\3.2\scripts\addons\brignet\_additional_modules\Lib\site-packages\win32 not a directory, skipping
C:\Users\Pierce\AppData\Roaming\Blender Foundation\Blender\3.2\scripts\addons\brignet\_additional_modules\Lib\site-packages\win32\lib not a directory, skipping
C:\Users\Pierce\AppData\Roaming\Blender Foundation\Blender\3.2\scripts\addons\brignet\_additional_modules\DLLs not a directory, skipping
C:\Users\Pierce\AppData\Roaming\Blender Foundation\Blender\3.2\scripts\addons\brignet\_additional_modules\Lib\site-packages\Pythonwin not a directory, skipping
installing torch
Looking in links: https://download.pytorch.org/whl/torch_stable.html
ERROR: Could not find a version that satisfies the requirement torch==1.8.1+cu102 (from versions: 1.11.0, 1.11.0+cpu, 1.11.0+cu113, 1.11.0+cu115, 1.12.0, 1.12.0+cpu, 1.12.0+cu113, 1.12.0+cu116, 1.12.1, 1.12.1+cpu, 1.12.1+cu113, 1.12.1+cu116)
ERROR: No matching distribution found for torch==1.8.1+cu102

I think the add-on should somehow break free from those dependencies, but as a quickfix I might add a torch version parameter in the preferences

At least in the case of support for CUDA 10.2, the only version of torch that offers a Python 3.10 wheel build is 1.12.0, and even then only for linux platforms:
https://pytorch-geometric.com/whl/torch-1.12.0+cu102.html

FYI I also tried being sly about it and editing the find link flag for the virtual environment's PIP package installation invokations ( https://github.com/pKrime/brignet/blob/v0.1-alpha/setup_utils/venv_utils.py#L260 ) to an edited local copy of the wheel build directory page that pointed to an appropriate local wheel build, which somehow still did not seem to work sadly.

fire commented

I had some issues compiling sparse torch on Windows for python 3.10

Something in the C++ extension compiling is failing.

     C:\Users\ernes\micromamba\envs\brignet\lib\site-packages\torch\include\pybind11\cast.h(624): error: too few arguments for template template parameter "Tuple"
                detected during instantiation of class "pybind11::detail::tuple_caster<Tuple, Ts...> [with Tuple=std::pair, Ts=<T1, T2>]"
      (721): here

      C:\Users\ernes\micromamba\envs\brignet\lib\site-packages\torch\include\pybind11\cast.h(717): error: too few arguments for template template parameter "Tuple"
                detected during instantiation of class "pybind11::detail::tuple_caster<Tuple, Ts...> [with Tuple=std::pair, Ts=<T1, T2>]"
      (721): here

The rest of the dependencies seems to go through for brignet

This still remains an issue for Blender 4.0+ builds.

fire commented

I've been using https://github.com/V-Sekai/blender-rignet as my fork with Blender 4 if this helps anyone.

If you can generate bone information in RigNet, you could run the following script from blender to create the model. Just set obj_path and skel_path for your files. Tested with blender 4.2.0.

import bpy

class ArmatureGenerator(object):
    def __init__(self, info, mesh=None):
        self._info = info
        self._mesh = mesh

    def generate(self, matrix=None):
        basename = self._mesh.name if self._mesh else ""
        arm_data = bpy.data.armatures.new(basename + "_armature")
        arm_obj = bpy.data.objects.new('brignet_rig', arm_data)

        bpy.context.collection.objects.link(arm_obj)
        bpy.context.view_layer.objects.active = arm_obj
        bpy.ops.object.mode_set(mode='EDIT')

        this_level = [self._info.root]
        hier_level = 1
        while this_level:
            next_level = []
            for p_node in this_level:
                pos = p_node.pos
                parent = p_node.parent.name if p_node.parent is not None else None

                e_bone = arm_data.edit_bones.new(p_node.name)
                if self._mesh and e_bone.name not in self._mesh.vertex_groups:
                    self._mesh.vertex_groups.new(name=e_bone.name)

                e_bone.head.x, e_bone.head.z, e_bone.head.y = pos[0], pos[2], pos[1]

                if parent:
                    e_bone.parent = arm_data.edit_bones[parent]
                    if e_bone.parent.tail == e_bone.head:
                        e_bone.use_connect = True

                if len(p_node.children) == 1:
                    pos = p_node.children[0].pos
                    e_bone.tail.x, e_bone.tail.z, e_bone.tail.y = pos[0], pos[2], pos[1]
                elif len(p_node.children) > 1:
                    x_offset = [abs(c_node.pos[0] - pos[0]) for c_node in p_node.children]

                    idx = x_offset.index(min(x_offset))
                    pos = p_node.children[idx].pos
                    e_bone.tail.x, e_bone.tail.z, e_bone.tail.y = pos[0], pos[2], pos[1]

                elif e_bone.parent:
                    offset = e_bone.head - e_bone.parent.head
                    e_bone.tail = e_bone.head + offset / 2
                else:
                    e_bone.tail.x, e_bone.tail.z, e_bone.tail.y = pos[0], pos[2], pos[1]
                    e_bone.tail.y += .1

                for c_node in p_node.children:
                    next_level.append(c_node)

            this_level = next_level
            hier_level += 1

        if matrix:
            arm_data.transform(matrix)

        bpy.ops.object.mode_set(mode='POSE')

        if self._mesh:
            for v_skin in self._info.joint_skin:
                v_idx = int(v_skin.pop(0))

                for i in range(0, len(v_skin), 2):
                    self._mesh.vertex_groups[v_skin[i]].add([v_idx], float(v_skin[i + 1]), 'REPLACE')

            arm_obj.matrix_world = self._mesh.matrix_world
            mod = self._mesh.modifiers.new('rignet', 'ARMATURE')
            mod.object = arm_obj

        return arm_obj

class Node(object):
    def __init__(self, name, pos):
        self.name = name
        self.pos = pos


class TreeNode(Node):
    def __init__(self, name, pos):
        super(TreeNode, self).__init__(name, pos)
        self.children = []
        self.parent = None

class Info:
    def __init__(self, filename):
        self.joint_pos = {}
        self.joint_skin = []
        self.root = None
        self.load(filename)

    def load(self, filename):
        with open(filename, 'r') as f_txt:
            lines = f_txt.readlines()
        for line in lines:
            word = line.split()
            if word[0] == 'joints':
                self.joint_pos[word[1]] = [float(word[2]), float(word[3]), float(word[4])]
            elif word[0] == 'root':
                root_pos = self.joint_pos[word[1]]
                self.root = TreeNode(word[1], (root_pos[0], root_pos[1], root_pos[2]))
            elif word[0] == 'skin':
                skin_item = word[1:]
                self.joint_skin.append(skin_item)
        self.loadHierarchy_recur(self.root, lines, self.joint_pos)

    def loadHierarchy_recur(self, node, lines, joint_pos):
        for li in lines:
            if li.split()[0] == 'hier' and li.split()[1] == node.name:
                pos = joint_pos[li.split()[2]]
                ch_node = TreeNode(li.split()[2], tuple(pos))
                node.children.append(ch_node)
                ch_node.parent = node
                self.loadHierarchy_recur(ch_node, lines, joint_pos)

obj_path = '..._ori.obj'

skel_path = '..._ori_rig.txt'

bpy.ops.wm.obj_import(filepath=obj_path)
mesh_obj = bpy.context.selected_objects[0]

skel_info = Info(filename=skel_path)

ArmatureGenerator(skel_info, mesh_obj).generate()