How can I run a Python Script with bpy on a Flask Server?

197 Views Asked by At

I have a project that creates a 3D object with vertex colours and saves it as blenderObj.obj. I have a Python script using bpy called "blenderConverter.py" which modifies the 3D object, bakes its vertex colours into a texture image and exports both.

When I run blenderConverter.py in the IDE, it works fine, but when I try to run it in my Flask server, I get this error:

Traceback (most recent call last):
  File "*path*\lib\site-packages\flask\app.py", line 2190, in wsgi_app
    response = self.full_dispatch_request()
  File "*path*\lib\site-packages\flask\app.py", line 1486, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "*path*\lib\site-packages\flask\app.py", line 1484, in full_dispatch_request
    rv = self.dispatch_request()
  File "*path*\lib\site-packages\flask\app.py", line 1469, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "*path*\flaskServer.py", line 11, in start
    convert()
  File "C*path*\blenderConverter.py", line 11, in convert
    bpy.ops.wm.obj_import(files=files)
  File "*path*\lib\site-packages\bpy\3.5\scripts\modules\bpy\ops.py", line 113, in __call__
    ret = _op_call(self.idname_py(), None, kw)
RuntimeError: Operator bpy.ops.wm.obj_import.poll() failed, context is incorrect

flaskServer.py executes the convert() function of blenderConverter.py on a route.

blenderConverter.py looks like this:

import bpy


def convert():
    # Wipe Scene
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete()

    # Import .obj
    files = [{"name": "blenderObj.obj"}]
    bpy.ops.wm.obj_import(files=files)

    # Rotate and Scale .obj
    obj = bpy.context.selected_objects[0]
    obj.rotation_euler = (0, 0, 0)
    obj.scale = (5, 5, 5)

    # Add new Material to .obj
    bpy.ops.material.new()
    material = bpy.data.materials[-1]
    obj.data.materials.append(material)

    # Configure Material
    node_tree = material.node_tree
    bsdf = node_tree.nodes[0]
    color_attribute = node_tree.nodes.new(type="ShaderNodeVertexColor")
    color_attribute.layer_name = "Color"
    node_tree.links.new(color_attribute.outputs["Color"], bsdf.inputs["Base Color"])

    # Configure Bake
    scene = bpy.context.scene
    scene.render.engine = 'CYCLES'
    scene.cycles.device = 'GPU'
    scene.render.bake.use_pass_indirect = False
    scene.render.bake.use_pass_direct = False
    scene.cycles.bake_type = 'DIFFUSE'
    scene.render.bake.margin = 5

    # Prepare image
    image_texture = node_tree.nodes.new(type="ShaderNodeTexImage")
    image = bpy.data.images.new('bake', 1024, 1024)
    image_texture.image = image

    # Create UV Map
    bpy.ops.object.editmode_toggle()
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.uv.smart_project(angle_limit=66, island_margin=0.002)
    bpy.ops.object.editmode_toggle()

    # Bake
    node_tree.nodes.active = image_texture
    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.bake(type='DIFFUSE', save_mode='EXTERNAL')

    # Save image
    image.filepath_raw = '//dispatch//bake.png'
    image.file_format = "PNG"
    image.save()

    # Save .obj
    bpy.ops.export_scene.obj(filepath='dispatch/object.obj', use_materials=False)

Through some research I got it working by using os.system("blender -b --python blenderConverter.py") instead of calling convert() on the Flask server. But this does not seem to be a best practice?

I also found that you can build blender as a python module, but that did not solve my problem. It could be my fault if I build it wrong or something.

My goal is to run blenderConverter without a Blender instance in the background.

Since I am pretty new to Python or Flask or Blender, I do not know what best practices are or are not. Is the os.system() solution ok or should I try to find a way to use convert()? If anyone can help I would appreciate it.

I am happy to help if more context or information is needed.

0

There are 0 best solutions below