# Blender experiments The [pcb2blender](https://github.com/30350n/pcb2blender) plug-in allows importing KiCad PCBs in Blender. It has two parts: - Exporter: A KiCad 6 action plug-in to export the PCB - Importer: A Blender 3.4 + Python 10 plug-in for Blender A user called @Haschtl asked for VRML export in issue #349. Its objetive was to convert KiCad PCBs into [GLTF](https://es.wikipedia.org/wiki/GlTF), a portable 3D format, to be displayed in web pages. This format can be used to render good quality 3D models in web pages, here is an [example](https://github.khronos.org/glTF-Sample-Viewer-Release/). @Haschtl has its own [fork](https://github.com/haschtl/pcb2blender) where he is experimenting with some changes to the original plug-in in order to make the 3D models easier to render, at the cost of some quality. As Blender is scriptable generating the files for the Blender plug-in opens various interesting uses. So I started to investigate it. Currently (2023/01/08) the current KiBot `dev` branch can generate files for both plug-ins (the original and the fork). This document is just some information about what I experimented. ## Blender requirements This is a really complex thing @30350n coded the pluig-in for the last version of Blender (3.4.x), the current Blender for Debian stable is 2.83 and for testing/unstable 3.3.x. To make things worst he coded it using Python 3.10 syntax (Python 3.9 is the one in Debian stable). Even removing the Python 3.10 extensions I found that Blender 2.83 can't handle the VRML files as expected. Using Blender 3.3.5 from `bookworm` (current testing) is enough. I tried to setup docker images to test the import plug-in. ### Linux Server images This project has Blender [images](https://hub.docker.com/r/linuxserver/blender). The current latest was 3.4.1. The images are easy to use ... well ... once you figure out how they think. You must first pull the image: ``` docker pull lscr.io/linuxserver/blender:latest ``` This is a 1.82 GB docker image. Once you have the image you must create a directory to store Blender config, lets say: ``` mkdir ~/blender_docker_config/ ``` Now you can run the image using something like this: ``` export USER_ID=$(id -u) export GROUP_ID=$(id -g) export TZ=$(cat /etc/timezone) docker run -d \ --name=blender \ -e PUID=$USER_ID \ -e PGID=$GROUP_ID \ -e TZ=$TZ \ -p 3000:3000 \ -v /home/$USER/blender_docker_config/:/config \ --device /dev/dri:/dev/dri \ --restart unless-stopped \ lscr.io/linuxserver/blender:latest ``` This creates a container that will be **always** running on your system, unless you explicitly run `docker stop blender`. The container runs in background (-d). To access to the GUI you can just open `http://localhost:3000` in your browser. This looks really simple, but at least in my case, using a 4k display, Blender is really hard to use. I tried to use it connecting to my DISPLAY, but couldn't. The image uses some bogus user and has some particular setup. The documentation for these images is [here](https://docs.linuxserver.io/), and the Dockerfile is available at [GitHub](https://github.com/linuxserver/docker-blender). Note that in order to install the plug-in you need `pip`, which **isn't** included in the image. You must get and run the `get-pip.py` script from the [pip documentation](https://pip.pypa.io/en/stable/installation/). ### rd-blender-docker These images are available at [GitHub](https://github.com/nytimes/rd-blender-docker). The current version was 3.3.1, which seems to be good enough for the plug-in. The images comes in two flavors: with and without support for GPU render. I tried the GPU version, but I don't have the needed proprietary drivers installed. The GPU image is 1.69 GB and you can get it pulling: ``` docker pull nytimes/blender:latest ``` This image is much more simple and I was able to run it with good performance. I just run it using my HOME directory for it, like this: ``` export USER_ID=$(id -u) export GROUP_ID=$(id -g) export TZ=$(cat /etc/timezone) docker run -it --rm \ --user $USER_ID:$GROUP_ID \ --env TZ=$TZ \ --env DISPLAY=$DISPLAY \ --env NO_AT_BRIDGE=1 \ --workdir="/home/$USER" \ --volume="/tmp/.X11-unix:/tmp/.X11-unix" \ --volume="/etc/group:/etc/group:ro" \ --volume="/home/$USER:/home/$USER:rw" \ --volume="/etc/passwd:/etc/passwd:ro" \ --volume="/etc/shadow:/etc/shadow:ro" \ --volume="/home/$USER:/home/$USER:rw" \ --device /dev/dri:/dev/dri \ nytimes/blender:latest blender ``` Note that this maps all the system users into the docker image. As this connects to the X display using your authorization and as we map the DRI device the speed is good. The version without GPU support is 1.43 GB. Note that this image contains `pip`. The bad thing about these images is that they are created installing the Blender tarball from [here](https://mirror.clarkson.edu/blender/release/), making it bad to integrate with other tools. ### Blender for Debian I tried creating a docker image using Debian. It needs bookworm (testing) to get Blender 3.3.1. I used: ``` FROM debian:bookworm-slim MAINTAINER Salvador E. Tropea LABEL Description="Minimal Blender image based on Debian" RUN apt-get -y update && \ apt-get -y install --no-install-recommends blender && \ apt-get -y install --no-install-recommends python3-pip python3-pil && \ apt-get -y install --no-install-recommends python3-numpy && \ apt-get -y autoremove && \ rm -rf /var/lib/apt/lists/* \ rm -rf /usr/share/icons/Adwaita/ ``` It works ... but I found Blender is compiled without the Intel denoiser. So renders using Cycle renderer fails, you have to disable the denoiser. I don't know why Blender enables something not compiled. Anyways, without the denoiser a 1 minute render could take an hour. Basically: this package is pointless. ### Blender for Ubuntu I tried using lunar, to get 3.3.1. I used: ``` FROM ubuntu:lunar MAINTAINER Salvador E. Tropea LABEL Description="Minimal Blender image based on Ubuntu" RUN apt-get -y update && \ apt-get -y install --no-install-recommends blender && \ apt-get -y install --no-install-recommends python3-pip python3-pil python3-numpy && \ apt-get -y autoremove && \ rm -rf /var/lib/apt/lists/* \ rm -rf /usr/share/icons/Adwaita/ ``` And got *blender: symbol lookup error: blender: undefined symbol: _ZN7openvdb4v9_16points14AttributeArrayC2ERKS2_RKN3tbb6detail2d118unique_scoped_lockINS7_10spin_mutexEEE* Looks like at the moment the package is broken. ### Custom solution From the experiments I conclude: - Support for Blender in Debian is weak (broken) - Workable images uses the tarball So I decided to integrate it with my current Debian stable + KiCad images #### Installing Blender It must be installed from the tarball, but I don't like it on */bin* like in rd-blender-docker images. So I installed it on `/usr/bin` ```docker RUN apt-get -y update && \ apt-get -y install --no-install-recommends xz-utils wget && \ wget https://mirrors.ocf.berkeley.edu/blender/release/Blender3.4/blender-3.4.1-linux-x64.tar.xz && \ tar xvf blender-3.4.1-linux-x64.tar.xz --strip-components=1 -C /usr/bin/ && \ rm blender-3.4.1-linux-x64.tar.xz && \ apt-get -y remove wget xz-utils && \ apt-get -y autoremove && \ rm -rf /var/lib/apt/lists/* ``` It provides an usable Blender, a lot of static stuff, some system X libs and a few custom libs: ```shell $ ldd `which blender` linux-vdso.so.1 (0x00007ffd2bfd0000) librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007fcbb9943000) libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fcbb993e000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fcbb991c000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fcbb9916000) libX11.so.6 => /usr/lib/x86_64-linux-gnu/libX11.so.6 (0x00007fcbb97d3000) libXrender.so.1 => /usr/lib/x86_64-linux-gnu/libXrender.so.1 (0x00007fcbb95c9000) libXxf86vm.so.1 => /usr/lib/x86_64-linux-gnu/libXxf86vm.so.1 (0x00007fcbb93c1000) libXfixes.so.3 => /usr/lib/x86_64-linux-gnu/libXfixes.so.3 (0x00007fcbb93b9000) libXi.so.6 => /usr/lib/x86_64-linux-gnu/libXi.so.6 (0x00007fcbb93a7000) libxkbcommon.so.0 => /usr/lib/x86_64-linux-gnu/libxkbcommon.so.0 (0x00007fcbb9364000) libcycles_kernel_oneapi_aot.so => /usr/bin/lib/libcycles_kernel_oneapi_aot.so (0x00007fcbb4268000) libsycl.so.6 => /usr/bin/lib/libsycl.so.6 (0x00007fcbb3e20000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fcbb3cda000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcbb3b05000) /lib64/ld-linux-x86-64.so.2 (0x00007fcbb9959000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fcbb3aeb000) libxcb.so.1 => /usr/lib/x86_64-linux-gnu/libxcb.so.1 (0x00007fcbb3ac0000) libXext.so.6 => /usr/lib/x86_64-linux-gnu/libXext.so.6 (0x00007fcbb3aab000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fcbb38dc000) libXau.so.6 => /usr/lib/x86_64-linux-gnu/libXau.so.6 (0x00007fcbb38d7000) libXdmcp.so.6 => /usr/lib/x86_64-linux-gnu/libXdmcp.so.6 (0x00007fcbb36d1000) libbsd.so.0 => /usr/lib/x86_64-linux-gnu/libbsd.so.0 (0x00007fcbb36ba000) libmd.so.0 => /usr/lib/x86_64-linux-gnu/libmd.so.0 (0x00007fcbb36ad000) ``` #### Installing plug-in dependencies They must be installed in the Python copy embeded in Blender. We must install `pip`, I don't understand why the Blender people doesn't include it. After installing `pip` we need `pillow` and `skia-python`. ```docker RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \ /usr/bin/3.4/python/bin/python3.10 get-pip.py && \ /usr/bin/3.4/python/bin/python3.10 -m pip install pillow && \ /usr/bin/3.4/python/bin/python3.10 -m pip install skia-python ``` #### Installing the plug-in We need to install it, but the normal installation is on the user's home, quite pointless here. Is amazing how these mature tools doens't provide clean mechanisms for system level plug-ins. In order to install the plug-in we need to use a Python script, like this: ```python import bpy # Register the addon and enable it bpy.context.preferences.filepaths.script_directory = '/usr/bin/3.4/scripts/' bpy.ops.preferences.addon_install(filepath='./pcb2blender_importer_2-1.zip', target='PREFS') bpy.ops.preferences.addon_enable(module='pcb2blender_importer') bpy.ops.wm.save_userpref() ``` This configures the script directory as */usr/bin/3.4/scripts/*, which is the place where the bundled plug-ins are installed. Then we install the plu-in using the previous dir as target. We finally enable the plug-in and save the user config. This save the config for the root. This is ok when using CI/CD, we use root as user. It isn't really good when we want to run the Blender inside the image for a regular user. In this case the user must enable the plug-in manually. Ridiculous, and very Windows style. The docker part is: (enable_pcb2blender.py is the previous Python code) ```docker RUN curl -L https://github.com/30350n/pcb2blender/releases/download/v2.1-k6.0-b3.4/pcb2blender_importer_2-1.zip -o pcb2blender_importer_2-1.zip && \ blender --background --python /usr/bin/enable_pcb2blender.py && \ rm pcb2blender_importer_2-1.zip ``` #### All together ```docker FROM setsoft/kicad_auto_test:ki6 MAINTAINER Salvador E. Tropea LABEL Description="KiBot full + Blender on Debian" # Install Blender RUN apt-get -y update && \ apt-get -y install --no-install-recommends xz-utils wget && \ wget https://mirrors.ocf.berkeley.edu/blender/release/Blender3.4/blender-3.4.1-linux-x64.tar.xz && \ tar xvf blender-3.4.1-linux-x64.tar.xz --strip-components=1 -C /usr/bin/ && \ rm blender-3.4.1-linux-x64.tar.xz && \ apt-get -y remove wget xz-utils && \ apt-get -y autoremove && \ rm -rf /var/lib/apt/lists/* # Install plug-in deps: pillow and skia-python (pip, numpy, etc.) RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \ /usr/bin/3.4/python/bin/python3.10 get-pip.py && \ /usr/bin/3.4/python/bin/python3.10 -m pip install pillow && \ /usr/bin/3.4/python/bin/python3.10 -m pip install skia-python # Install the plug-in COPY enable_pcb2blender.py /usr/bin/ RUN curl -L https://github.com/30350n/pcb2blender/releases/download/v2.1-k6.0-b3.4/pcb2blender_importer_2-1.zip -o pcb2blender_importer_2-1.zip && \ blender --background --python /usr/bin/enable_pcb2blender.py && \ rm pcb2blender_importer_2-1.zip ``` Built with: ```shell #!/bin/sh docker build -f Dockerfile -t setsoft/kicad_auto_test_blender:latest . ``` Using `enable_pcb2blender.py`: ```python import bpy # Register the addon and enable it bpy.context.preferences.filepaths.script_directory = '/usr/bin/3.4/scripts/' bpy.ops.preferences.addon_install(filepath='./pcb2blender_importer_2-1.zip', target='PREFS') bpy.ops.preferences.addon_enable(module='pcb2blender_importer') bpy.ops.wm.save_userpref() ``` We can then run Blender 3.4.1 on the host using: ```shell #!/bin/bash export USER_ID=$(id -u) export GROUP_ID=$(id -g) export TZ=$(cat /etc/timezone) docker run -it --rm \ --user $USER_ID:$GROUP_ID \ --env TZ=$TZ \ --env DISPLAY=$DISPLAY \ --env NO_AT_BRIDGE=1 \ --workdir="/home/$USER" \ --volume="/tmp/.X11-unix:/tmp/.X11-unix" \ --volume="/etc/group:/etc/group:ro" \ --volume="/home/$USER:/home/$USER:rw" \ --volume="/etc/passwd:/etc/passwd:ro" \ --volume="/etc/shadow:/etc/shadow:ro" \ --volume="/home/$USER:/home/$USER:rw" \ --device /dev/dri:/dev/dri \ setsoft/kicad_auto_test_blender:latest blender ``` Remmeber you must enable the plug-in manually in Edit | Preferences | Add-ons searching for `pcb2blender`. This is persistent because we mounted the user home. The config will be stored in `~/.config/blender/3.4/`. The resulting image is huge. About 2.77 GB, the tarball adds 943 MB and the Python dependencies 167 MB. About 1.1 GB added. The price of installing things in a dirty way. #### Numpy issues I'm not sure why but skia pulls numpy (isn't it part of Blender bundles?) The problem is that it pulls 1.24.1 and the glTF importer uses `numpy.bool`, deprecated in 1.20 and changed to `numpy.bool_`. So I ended forcing 1.23.1 ## Using Blender from the docker image To pretend this is the system level Blender I have: ```shell #!/bin/bash export USER_ID=$(id -u) export GROUP_ID=$(id -g) docker run --rm \ --user $USER_ID:$GROUP_ID \ --env DISPLAY=$DISPLAY \ --env NO_AT_BRIDGE=1 \ --workdir=$(pwd) \ --volume="/tmp:/tmp" \ --volume="/etc/group:/etc/group:ro" \ --volume="/etc/gshadow:/etc/gshadow:ro" \ --volume="/etc/timezone:/etc/timezone:ro" \ --volume="/home/$USER:/home/$USER:rw" \ --volume="/etc/passwd:/etc/passwd:ro" \ --volume="/etc/shadow:/etc/shadow:ro" \ --volume="/home/$USER:/home/$USER:rw" \ --device /dev/dri:/dev/dri \ setsoft/kicad_auto_test_blender:latest nice blender "$@" ``` Named *blender* in my path (~/bin/blender). In this way running `blender` gives me Blender 3.4.1, and not 2.83.5 (from the system).