Blender Hack Blog

オープンソースの総合3DCGソフトウェアのBlenderのコード解析や開発を記録していきます。

macOS: Visual Studio CodeでBlenderの開発環境を構築する

はじめに

Blender開発者によるライブコーディングを観ていると、Visual Studio Codeがとても便利そうでした。 そこで、macOSにてVisual Studio Code(以下VSCode)でBlenderの開発環境を構築してみます。

www.youtube.com

手順

こちらのマニュアルを参考にしました。 https://wiki.blender.org/wiki/Developer_Intro/Environment/Portable_CMake_VSCode

VS Codeのインストール

VSCodeを本家からダウンロードしてインストールします。 zipファイルを展開して、アプリケーションにコピーします。

code.visualstudio.com

拡張のインストール

本家のマニュアルに列挙されている拡張をインストールします。

フォーマッタの設定

Code > Preferences > Settingsを開きます。 検索フォームに「format」と入力し、「Editor: Default Formatter」の設定で、「ms-vscode.cpptools 」を選択します。

インテリセンスの設定

本家のマニュアルを読むと少し面倒な工程が必要そうです。 まずは、ビルドとデバッグをできるように整えたいので、ここではこの工程はスキップします。

Blenderのビルド

tasks.jsonに以下を設定し、ビルドを実行します。

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Build Blender",
            "type": "shell",
            "command": "make",
            "group": "build"
        }
    ]
}

Blenderデバッグ版のビルド

前のステップでBlenderをビルドするとbuild_darwinというディレクトリ以下に様々なファイルが作成されます。 build_darwin/CMakeCache.txtにあるCMAKE_BUILD_TYPEReleaseDebugに変更します。 その後、VSCodeからビルドを実行します。

CMAKE_BUILD_TYPE:STRING=Debug

デバッガの設定

VSCodeからデバッグできるように、launch.jsonを以下のように記述します。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(lldb) Launch Blender",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/../build_darwin/bin/Blender.app/Contents/MacOS/Blender",
            "args": [],               // You could place a .blend file path here to open it by default, or any other Blender args
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false, // This could be set to true if you prefer an external console for debugging
            "MIMode": "lldb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "Build Blender" // Optional; you can use if you want it to build before launching
        }
    ]
}

本家はLinux向けなので注意が必要です。 macOS向けに以下の修正を加えます。

ポイント
  • program: build_darwin以下に生成されるBlenderを指定
  • MIMode: lldbを指定

デバッグの実行

VSCodeで適当な場所にブレークポイントを設定します。 ここでは、Blenderを起動したら必ず実行される、void OVERLAY_extra_cache_init(OVERLAY_Data *vedata)の関数の先頭に設定することにします。

Runから「(lldb) Launch Blender」をクリックしてデバッグを実行します。 launch.jsonのpreLaunchTaskに「Build Blender」を指定したので、Blenderのビルド後、デバッガが起動します。

設定したブレークポイントで実行が停止します。 Xcodeのデバッガと遜色なくデバッグができます。

f:id:fixme:20210224122855p:plain
VSCodeデバッグに成功

実験: ワイヤーフレームの色を変更してみる

はじめに

今回は、Blenderワイヤーフレームの色をHackして変更してみます。 GLSL(OpenGL Shading Language)の知識が役に立ちました。

使用したコード

master (2.93.0)を使用しました。 https://developer.blender.org/rBd447bd3e4a9a793364b5f4951ad280fe0293d79e

ワイヤーフレームが描画される仕組み

頂点シェーダとフラグメントシェーダでモデルのワイヤーフレームが描画されます。 頂点シェーダで定義されているfinalColorワイヤーフレームの色がセットされて、フラグメントシェーダでfinalColorを参照して最終的な色(fragColor)にセットされます。

source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl

out vec4 finalColor;
[...]
void main()
{
[...]
  vec3 rim_col, wire_col;
  if (isObjectColor || isRandomColor) {
    wire_object_color_get(rim_col, wire_col);
  }
  else {
    wire_color_get(rim_col, wire_col);
  }

  facing = clamp(abs(facing), 0.0, 1.0);

  /* Do interpolation in a non-linear space to have a better visual result. */
  rim_col = pow(rim_col, vec3(1.0 / 2.2));
  wire_col = pow(wire_col, vec3(1.0 / 2.2));
  vec3 final_front_col = mix(rim_col, wire_col, 0.35);
  finalColor.rgb = mix(rim_col, final_front_col, facing);
  finalColor.rgb = pow(finalColor.rgb, vec3(2.2));
  finalColor.a = wireOpacity;
  finalColor.rgb *= wireOpacity;
}

source/blender/draw/engines/overlay/shaders/wireframe_frag.glsl

in vec4 finalColor;
[...]
void main()
{
[...]
  fragColor = finalColor;

ちなみに、wireframe_vert.glslwireframe_frag.glslはビルド時に、const char[]型のdatatoc_wireframe_vert_glsldatatoc_wireframe_frag_glslにそれぞれ変換されるようです。 そして、OVERLAY_shader_wireframe()でシェーダープログラムとしてロードされます。

source/blender/draw/engines/overlay/overlay_shader.c

GPUShader *OVERLAY_shader_wireframe(bool custom_bias)
{
  const DRWContextState *draw_ctx = DRW_context_state_get();
  const GPUShaderConfigData *sh_cfg = &GPU_shader_cfg_data[draw_ctx->sh_cfg];
  OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
  if (!sh_data->wireframe[custom_bias]) {
    sh_data->wireframe[custom_bias] = GPU_shader_create_from_arrays({
        .vert = (const char *[]){sh_cfg->lib,
                                 datatoc_common_view_lib_glsl,
                                 datatoc_common_globals_lib_glsl,
                                 datatoc_gpu_shader_common_obinfos_lib_glsl,
                                 datatoc_wireframe_vert_glsl,
                                 NULL},
        .frag = (const char *[]){datatoc_common_view_lib_glsl,
                                 datatoc_common_globals_lib_glsl,
                                 datatoc_wireframe_frag_glsl,
                                 NULL},
        .defs = (const char *[]){sh_cfg->def,
                                 custom_bias ? "#define CUSTOM_DEPTH_BIAS\n" : NULL,
                                 NULL},
    });
  }
  return sh_data->wireframe[custom_bias];
}

ワイヤーフレームの色をセットしている場所の特定

wire_color_get()を見ると、colorWireという変数が見つかりました。この変数にワイヤーフレームの色がセットされていそうです。

void wire_color_get(out vec3 rim_col, out vec3 wire_col)
{
[...]
  else if (is_selected && useColoring) {
    if (isTransform) {
      rim_col = colorTransform.rgb;
    }
    else if (is_active) {
      rim_col = colorActive.rgb;
    }
    else {
      rim_col = colorSelect.rgb;
    }
    wire_col = colorWire.rgb;
  }
  else {
    rim_col = colorWire.rgb;
    wire_col = colorBackground.rgb;
  }
}

colorWiregrepしてみると、以下のuniform変数として定義されていることが分かりました。

source/blender/draw/intern/shaders/common_globals_lib.glsl

/* keep in sync with GlobalsUboStorage */
layout(std140) uniform globalsBlock
{
  vec4 colorWire;
  vec4 colorWireEdit;
  vec4 colorActive;
  vec4 colorSelect;
[...]

もう少し探索を進めると、uniform変数にセットしていそうなコードが見つかりました。 UI_GetThemeColor4fv(TH_WIRE, gb->colorWire);にbreak pointをセットしてみます。 デバッガで確認すると、初回実行時には、ゼロ初期化されていて、DRW_globals_update(void)の呼び出し後は、 gb->colorWireに(0, 0, 0, 1)がセットされました。

source/blender/draw/intern/draw_common.c

void DRW_globals_update(void)
{
  GlobalsUboStorage *gb = &G_draw.block;

  UI_GetThemeColor4fv(TH_WIRE, gb->colorWire);
  UI_GetThemeColor4fv(TH_WIRE_EDIT, gb->colorWireEdit);
  UI_GetThemeColor4fv(TH_ACTIVE, gb->colorActive);
  UI_GetThemeColor4fv(TH_SELECT, gb->colorSelect);
[...]

コードの修正

colorWireは、float[4]でそれぞれの要素はRGBAに対応しているようです。 そこで、rgba = (1, 0, 0, 1)、つまり赤をセットしてみます。

diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index 132b5274517..db7501e6a94 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -53,7 +53,12 @@ void DRW_globals_update(void)
 {
   GlobalsUboStorage *gb = &G_draw.block;
 
-  UI_GetThemeColor4fv(TH_WIRE, gb->colorWire);
+  gb->colorWire[0] = 1.0;
+  gb->colorWire[1] = 0.0;
+  gb->colorWire[2] = 0.0;
+  gb->colorWire[3] = 1.0;
+
+  //UI_GetThemeColor4fv(TH_WIRE, gb->colorWire);
   UI_GetThemeColor4fv(TH_WIRE_EDIT, gb->colorWireEdit);
   UI_GetThemeColor4fv(TH_ACTIVE, gb->colorActive);
   UI_GetThemeColor4fv(TH_SELECT, gb->colorSelect);

ビルドと実行結果

ビルドして実行し、ワイヤーフレームで表示すると無事ワイヤーフレームの色が赤くなりました。

f:id:fixme:20210223110934p:plain
ワイヤーフレームの色が変更された

関連情報

ワイヤーフレームの不透明度をUIから変更できるようにする修正

⚙ D7622 Add An Opacity Slider to Overlay Wireframe

Blenderのはじめての不具合改修

はじめに

BlenderXcodeデバッグ実行した時にNull Pointer Exceptionが発生する不具合を発見しました。 軽微な不具合だったので、改修してパッチを送りました。その過程を簡単にメモに残しておきます。

使用したコード

master (af940c68cbed7e840d6ae58f2645ff12ed6abffb) 2.93.0を使用しました。

バグの詳細

環境変数BLENDER_SYSTEM_PYTHONを未設定の状態でBlenderを実行すると、Null Pointer Exceptionが発生します。 こちらに詳細をまとめて、パッチを送りました。 https://developer.blender.org/D10494

git loggit blameでバグが混入したコミットを特定しました。

パッチの作成

このページに書かれている、Arcanistをインストールしました。 https://wiki.blender.org/wiki/Tools/CodeReview

はじめて使用する際には、以下のコマンドを実行して、パッチの送付先のシステム認証を行う必要があります。

% arc install-certificate

以下のコマンドを実行するとコードのコミットとチケットの作成が行われます。

% arc diff

コミッターにレビューを依頼しました。

無事、修正が取り込まれました。 https://developer.blender.org/rB06212759bc7285a131c3ee811aec1ee240841c2c

commit 06212759bc7285a131c3ee811aec1ee240841c2c
Author: Campbell Barton <ideasman42@gmail.com>
Date:   Mon Feb 22 21:20:48 2021 +1100

    Fix missing NULL check on macOS when system-python isn't found
    
    Regression from cafd6b519c5f5c4b67d0dfe3d453cd4223b38716.
    
    D10494 by @ysano with edits.

Hello Blender Hack: bpy.app.ocio.supportedの値を変更してみる

はじめに

Blenderにごく簡単な修正をして、修正が反映されるのを確認する実験をしてみます。 修正自体は、何も面白くないですが、BlenderのHackingの第一歩としては意義があると思います。

使用したコード

master (af940c68cbed7e840d6ae58f2645ff12ed6abffb) 2.93.0を使用しました。

修正前の挙動の確認

Blenderの組み込みPythonシェルで以下を評価するとTrueが返りました。

>>> bpy.app.ocio.supported
True

挙動に対応するコード

source/blender/python/intern/bpy_app_ocio.cbpy.app.ocioをセットするコードが見つかりました。 ローカル変数posをインクリメントしながら、プロパティを順番にセットしているようです。 SetObjItem(PyBool_FromLong(1));bpy.app.ocio.supportedTrueを返すように実装していそうです。

static PyObject *make_ocio_info(void)
{
  PyObject *ocio_info;
  int pos = 0;

#ifdef WITH_OCIO
  int curversion;
#endif

  ocio_info = PyStructSequence_New(&BlenderAppOCIOType);
  if (ocio_info == NULL) {
    return NULL;
  }

#ifndef WITH_OCIO
#  define SetStrItem(str) PyStructSequence_SET_ITEM(ocio_info, pos++, PyUnicode_FromString(str))
#endif

#define SetObjItem(obj) PyStructSequence_SET_ITEM(ocio_info, pos++, obj)

#ifdef WITH_OCIO
  curversion = OCIO_getVersionHex();
  SetObjItem(PyBool_FromLong(1));
  SetObjItem(
      PyC_Tuple_Pack_I32(curversion >> 24, (curversion >> 16) % 256, (curversion >> 8) % 256));
  SetObjItem(PyUnicode_FromFormat(
      "%2d, %2d, %2d", curversion >> 24, (curversion >> 16) % 256, (curversion >> 8) % 256));
#else
  SetObjItem(PyBool_FromLong(0));
  SetObjItem(PyC_Tuple_Pack_I32(0, 0, 0));
  SetStrItem("Unknown");
#endif

  if (PyErr_Occurred()) {
    Py_CLEAR(ocio_info);
    return NULL;
  }

#undef SetStrItem
#undef SetObjItem

  return ocio_info;
}

コードの修正

そこで以下のように修正します。0をセットすることでFalseを返すようになるのではと考えました。

% git diff
diff --git a/source/blender/python/intern/bpy_app_ocio.c b/source/blender/python/intern/bpy_app_ocio.c
index 3a36e90018f..125f03e2a52 100644
--- a/source/blender/python/intern/bpy_app_ocio.c
+++ b/source/blender/python/intern/bpy_app_ocio.c
@@ -70,7 +70,7 @@ static PyObject *make_ocio_info(void)
 
 #ifdef WITH_OCIO
   curversion = OCIO_getVersionHex();
-  SetObjItem(PyBool_FromLong(1));
+  SetObjItem(PyBool_FromLong(0));
   SetObjItem(
       PyC_Tuple_Pack_I32(curversion >> 24, (curversion >> 16) % 256, (curversion >> 8) % 256));
   SetObjItem(PyUnicode_FromFormat(

修正結果の確認

修正後、Xcodeでビルドと実行します。

Blender組み込みのPythonシェルで確認すると、予想通りbpy.app.ocio.supportedFalseを返すようになりました。

f:id:fixme:20210222133733p:plain
修正結果の確認

macOS: XcodeでBlenderの開発環境を構築する

はじめに

今回はBlenderXcodeでビルドしてデバッグ実行するまでの工程を記録に残します。 公式マニュアルで不足していた部分を補足します。

手順

公式マニュアルを参考にしました。 https://wiki.blender.org/wiki/Building_Blender/Mac

Xcodeのインストール

App Storeから最新のXcodeをインストールしました。 時間がかかりますがしばらく放置します。

Homebrewのインストール

https://brew.sh

cmakeとsvnのインストール

% brew install cmake svn

ソースコードと依存外部ライブラリの取得

make updateのタイミングで外部依存ライブラリが取得されます。

% mkdir ~/blender-git
% cd ~/blender-git
% git clone https://git.blender.org/blender.git
% cd blender
% make update

Xcodeのプロジェクトファイルの生成

ビルドしたBlenderの配置先を作成し、ビルド時にそこに配置されるようにCMAKE_INSTALL_PREFIXを指定します。 cmakeが成功すると、build_xcode/Blender.xcodeprojが生成されます。

CMAKE_INSTALL_PREFIXを指定しないと、書き込み権限が無いため、ビルドに失敗しました。

% mkdir /Users/yoshinori_sano/build
% cd ~/blender-git/blender
% cmake . -B ../build_xcode/ -G "Xcode"  -DCMAKE_INSTALL_PREFIX=/Users/yoshinori_sano/build

Xcodeの設定

Blender.xcodeprojを開きます。

Automatically Create Schemesを選択します。

Active Schemeをinstallに設定します。

Product->Scheme->Edit Schemeを選択し、RunのexecutableにBlender.appを選択します。 Environmental Variablesに以下を設定します。 これらの環境変数をセットしないと、XcodeからBlenderを起動できません。

  • BLENDER_SYSTEM_PYTHON: /Users/yoshinori_sano/build/Blender.app/Contents/Resources/2.93/python
  • BLENDER_SYSTEM_DATAFILES: /Users/yoshinori_sano/build/Blender.app/Contents/Resources/2.93/datafiles
  • BLENDER_SYSTEM_SCRIPTS: /Users/yoshinori_sano/build/Blender.app/Contents/Resources/2.93/scripts

f:id:fixme:20210222125010p:plain
環境変数の設定

Choseをクリックして設定を保存します。

デバッグ実行

source/creator/creator.cmain()があるので、適当な場所にbreak pointをセットします。

Runボタンをクリックするとビルドが開始されます。 ビルドが正常に完了したら、Debug版のBlenderが起動します。

設定したbreak pointで実行が停止します。

実行を続行すると、BlenderのUIが表示されます。

f:id:fixme:20210222130300p:plain
Blender 2.93.0 Alphaの起動直後