Blender

第1回 Blenderソースコードリーディング 準備:Blenderをビルドする

酔っ払った夜中のツイートの流れでウッカリ全世界が熱望してきた“第1回 Blenderソースコードリーディング”というのをやることになったので、まずは準備として Blender のビルド環境を用意したメモを残します。

参加者の皆さんは当日コードのチェックアウトをしているとそれだけで一日おわってしまうので、この記事を参考にひとまずビルドして Blender が実行できるようになるまでは環境を整えておくと当日ダッシュで濃~い話ができるんじゃないかなと思います。

今回はまず入り口からということで比較的内部のドキュメントがまとまっている 2.6 のコードを参考にするために環境を構築してみました。Windows7/VS2008 で用意しています。

コードの取得

clone

https://developer.blender.org/diffusion/B/

存在するドキュメントは 2.6 の時のものなので、この頃のコードを用いて環境を構築する。リポジトリを見ると 2.6 の次に Bug fix 版として 2.6a があったので、そちらを使用することにする。

git clone git://git.blender.org/blender.git
cd blender
git submodule update --init --recursive
git submodule foreach git checkout master
git submodule foreach git pull --rebase origin master

リリース時につけられる tag もここで確認できるので、必要なバージョンのコードを抽出する際は参考にする。

コードのチェックアウトはものすごく時間がかかるので注意。

git.exe clone      --progress -v  "git://git.blender.org/blender.git" "D:\chiyama\Documents\Projects\OpenSource\blender\blender"

Cloning into 'D:\chiyama\Documents\Projects\OpenSource\blender\blender'...
remote: Counting objects: 797630, done.
Receiving objects: 100% (797630/797630), 249.43 MiB | 78.00 KiB/s, done.
Resolving deltas: 100% (698733/698733), done.
remote: Compressing objects: 100% (98387/98387), done.
remote: Total 797630 (delta 698733), reused 794130 (delta 695726)
Checking connectivity... done.
Checking out files: 100% (7749/7749), done.

Success (10762719 ms @ 2014/10/13 16:25:46)

約三時間(!!)

Blender 2.6a のコードを取得

clone 後、v2.6a タグのついた部分をチェックアウト。

https://developer.blender.org/diffusion/B/browse/master/;v2.60a

※今回は clone してから checkout してしまったので二度手間になってしまった。 git clone -b (branch name) というのがあるようなのでそちらを使った方が楽かも。

外部ライブラリを取得する

http://wiki.blender.org/index.php/Dev:Doc/Building_Blender/Windows/msvc/CMake

VS2008 を使ってビルドするので以下を実行

svn checkout https://svn.blender.org/svnroot/bf-blender/trunk/lib/win64 ../lib/win64

チェックアウト先は (リポジトリ)/../lib/win64 と、リポジトリと並列な場所にする必要がある

最新のものでは Python のバージョンが異なるので、これも当時のものを使用する必要がある。リポジトリを見ると Rev.60366 が該当するリビジョンらしい(ので、チェックアウト時にリビジョンを指定する方がよい)。

ビルド

ソースコードから Blender をビルドする

http://wiki.blender.org/index.php/Dev:JA/Doc/Building_Blender

ビルドシステムは SCons と CMake が選べる。私は CMake を選択。
あと、Building Blender on Windowsを見ると SCons から CMake に 移行してるっぽい?

プロジェクト生成

CMake GUI で VS2008 用プロジェクトを生成。バイナリ出力先は (リポジトリ)/../build に設定。インストール先も指定できるが、2.6a のタグ付きのところから持ってきたせいか、これは使用されないっぽい。

WITH_INTERNATIONAL がオンになっているとLibIntl for Windowsが必要っぽい。本質とは関係ないしメンドイので今回はオフ。

最後のリンクでも OpenEXR 関係のライブラリが無いと言われてコケるので、プロジェクトの設定を修正。CMakeLists.txt のここ?

if(WITH_IMAGE_OPENEXR)
        if(MSVC90)
                set(MSVC_LIB _vs2008)
                set(MSVC_INC)
        elseif(MSVC10)
                set(MSVC_LIB _vs2010)
                set(MSVC_INC _vs2010)
        else()
                set(MSVC_LIB msvc)
                set(MSVC_INC)
        endif()
        set(OPENEXR ${LIBDIR}/openexr)

ここのバージョン判別部分をゴッソリ削って

if(WITH_IMAGE_OPENEXR)
        set(OPENEXR ${LIBDIR}/openexr)

こうする。

コード修正

OpenEXR のパス周りで幾つかエラーが出るので修正する。

3>..\..\..\..\..\..\blender\source\blender\imbuf\intern\openexr\openexr_api.cpp(66) : fatal error C1083: include ファイルを開けません。'half.h': No such file or directory
>..\..\..\..\..\..\blender\source\blender\imbuf\intern\openexr\openexr_api.cpp(67) : fatal error C1083: include ファイルを開けません。'IlmImf/ImfVersion.h': No such file or directory

何故か IlmImf ディレクトリが指定されているので、 OpenEXR にかきかえる

インストール

INSTALL プロジェクトがあるので、これをビルドするとインストールされる。
インストール後、 blender.exe を実行して Blender が起動すれば成功。

未分類

Py3dsMax で undo

Py3dsMax で undo の登録をする方法が以外とどこにも書いてなかったのでメモ。
Py3dsMax モジュールには普段使う mxs モジュール以外にもいくつか登録されているものがあって、そのひとつに undo を管理するための関数があります。

import Py3dsMax
from Py3dsMax import mxs

Py3dsMax.undoOn()
n = mxs.selection[0]
c = mxs.prs()
print n.controller
n.controller = c
print n.controller
Py3dsMax.undoOff(u'test')

結論だけ見てしまえば何てことはない、これだけです。ただ、undoOn/undoOff の使い方はコードを見ないとわからないんですよね(汗

Blender, CG, コンピュータ

オープンソースソフトウェアの醍醐味

最近はエイプリルフールが世間にガッツリ定着してしまって、どこもかしこも嘘だらけでつまんないので逆張りしてガチネタで攻めます。天邪鬼天邪鬼。

ここ一、二年、一般的な CG 屋さん達からも Blender が注目を集めてきていています。最近はそこに Softimage の開発終了のニュースがあったために更に注目を浴びることになっています。

それはそれでいいことですが、Blender を使うということに対して何かいろいろ的外れな言及が多い(というか、そればっか)ので一度自分の意見をまとめてみました。


一番違和感を覚えるのが “無料=お金がかからない” という紹介です。これはもう完全に的外れです。タダだから使うというのは、情熱と時間が有り余って、でもお金がない学生さんならともかく、いい大人(しかもプロ)がそれ基準でものごとを決めるのは “本気!?” と思ってしまいます。正直、発想が貧乏臭いです。

はっきり断言しておくと、Blender を使うことでお金がかからないとか安く作品を作ることができるということは全くありません。自分達が割くリソースの内容と、その投入先が市販ソフトウェアを使う場合と異なるというだけです。そもそもソフトウェア代なんて人件費に比べたら誤差みたいなもので、それが高いというのは(ry

繰り返すと、投資の質と投資先が異なるというだけです。

市販ソフトウェアは既にある程度実績があって商品としてパッケージングされたものをお金を払って使うものです。それに対し、オープンソースソフトウェア(特に発展途上なもの)はそこにあるものを自分の力で使えるようにするという努力が必要になります。

例えて言うなら、市販ソフトウェアを使うのがコンビニ弁当を買って食べるということだとするとオープンソースソフトウェアは山や海に行って、自分で食材を集めて調理するようなものです。材料そのものにはお金はかからないかもしれないですが、そのかわりに現地に行く労力や食材を育てたり狩りをするためのスキルが必要になります。

そのためのスキルを身につけるには相応以上のコストを払う必要があります。Blender を使おうかと言っている人で、そういったコストを払う覚悟がある人はどれくらいいるんでしょうか?かなり疑問です。

また、無料だからという理由で飛びつくのも、それを吹聴するのも自分の首を締める行為にしかなりません。無料で制作ができる=自分たちの仕事をするためにかかるコストが安いですよというメッセージに受け取られ、結果的に映像制作をするということの価値を下げることに繋がります。それがみんなが求めている未来なのでしょうか?

大事なのは、”無料だから使う”ではなく、”使いたい使い方があるから使う”、そういう発想で道具を選ぶことです。これは極々当たり前のことですね。でも、”無料”の二文字の前にはそういう判断ができなくなる人が多いようです。

そして、Blender を始めとしたオープンソースツールを使用すること、ひいてはオープンソースソフトウェアのコミュニティに参加することの醍醐味はもっと別のところにあります。

開発力があればオープンソースソフトの開発に携って、自分達の力でソフトウェアをより良くすることができます。これは最もコアなかかわり方と言えます。

開発力が無い人は指をくわえて見ているだけか?というとそんなことはありません。
自分たちが欲しいと思ったら、自分で足りない部分を補っていけばいいのです。日本語マニュアルが欲しかったら自分が翻訳してみる、アーティストであれば自分の作った作品を公開してみる。これもコミュニティに参加するためのアプローチの一つです。
極端な話、インストールが難しいソフトウェアであればインストールマニュアルを作るというのも貴重な(そして、とても感謝される)貢献になります。

こういった、自分ができるアクションを起こすことで世界中の Blender チームの一員になるとができ、また、世界中の Blender チームを自分たちの一員にできるのです。そうなれば、世界中の開発者、研究者、アーティストと協力して新しい価値を生み出すことができるようになります。

そのためのコストは決して小さいものではありませんが、それ以上に得られるものがあります。

「ウチは CG をつくる会社だから関係ないよ」という意見もあるかもしれません。でも、考えてください。コミュニティの一員になることで、特定のベンダの呪縛から逃れることができます。SIGGRAPH で発表された、あの会社の使っていた技術がすぐに手元に届きます。しかも、開発者と直にディスカッションできます。自分が欲しいと思った表現方法を提案して賛同者を募ることもできますし、資金援助することで自分達が使っているツールを直接的により良くすることもできます。

これらの可能性を、今掛けているのと同等かそれ以下のコストで実現できる可能性を秘めているのが Blender であり、オープンソースコミュニティなのです。

無料だから使うっていう貧乏臭い発想より、こっちの方が断然ワクワクしませんか?

自分たちの手で未来を切り拓きましょうよ。

3ds max, Programming

Py3dsMaxでmaxの特殊な引数への対応

max script で各種操作を行う際によく目にする、# 付きのパラメータ(named parameter? name type?)を Py3dsMax で扱うためのメモ。

本家のドキュメントの Type Conversion でも簡単には触れられているのですが、 “testing” という文字列を #testing という名前としての値に変換するには max script では


"testing" as name
#testing

とし、同様のことを Py3dsmax で行うには


>>> mxs.pyhelper.namify("testing")
#testing

とします。ここまでは OK。で、ここからが疑問で、関数への引数として渡すには? ということが書いてないです。どうすればいいんでしょう?

答えとしては単純で、namify された値をそのまま渡せばいけました。これに気づくまで、わざわざ max script のコードを生成してそれを実行してましたよ。。。。。

たとえば、確認ダイアログを出さないでシーンをリセットする場合。


noPrompt = mxs.pyhelper.namify('noPrompt')
mxs.resetMaxFile(noPrompt)

こんな感じです。メンドクサイですけど、しかたないですね、、、

コンピュータ

iTunesのデータをNASに置きたい

新しい iTunes が OSX 10.8 でサポートしていないということで Windows で iTunes を使えるようにするついでに NAS 上に iPhone のバックアップデータやらライブラリやらを置くようにしたのでその作業メモ。

Twitter であーでもないこーでもないとグダグダ呟いていたらいろいと御助言頂けた @takmiz 君、@kaoru さん、@cokeraita さん、@AItoiI さんたちには大感謝です!!

実作業

基本は、”放牧82日~iTunesのバックアップ保存場所変更方法(windows vista)~” の記事をそのまま実行しただけですw

ちょっと違うのが、元記事ではドライブ内のパスにリンクをはっているものを NAS 上のパスに直接リンクをはってしまったところです。こうすることで、NAS 上に iTunes のデータを置くことができます。私のようにメイン PC がノートで、SSD を使っているために容量に余裕が無い人なんかはこのような形で運用できると結構嬉しいのではないでしょうか。

iTunes で厄介なのが、iPhone などのバックアップデータを置く場所と音楽などのライブラリを置く場所が別々になっていることです。しかも一見するとどこにあるのか全くわかりません。普段意識させないのはいいんですが、完全に隠されると困ります 🙁

とグチは置いておいて、ググってファイルのありかを見つけたら二つともリンクをはります。

iTunes ディレクトリのリンクをはる

mklink /d C:\Users\chiyama\Music \\NAS01\home\chiyama\Music\iTunes

Backup ディレクトリのリンクをはる

mklink /d “C:\Users\chiyama\AppData\Roaming\Apple Computer\MobileSync\Backup” “\\NAS01\home\chiyama\Library\Application Support\MobileSync\Backup”

この二つでOK。ユーザー名とかパスは適宜読み替えてください。

mklink は何故か普通のコマンドとはオプションが逆で、

mklink /d “リンク先” “リンク元”

となっています。このコマンドを作った人のセンスを疑いますね 🙁

3ds max, Programming

Py3dsMaxでKrakatoa PRTローダを操作する

Py3dsMax 関連でドハマりしたのでメモ。

Py3dsMax であるクラスに関連するオブジェクトを取得するのは、maxScript にある getClassInstances() 関数を使えば取得できます。が、どうやらモノによってはこれで取得したオブジェクトだと、親クラスで定義されているプロパティにアクセスできないようです。


from Py3dsMax import mxs 
for n in mxs.getClassInstances(mxs.KrakatoaPRTLoader): 
    print n.name

これは一見 OK そうですが、実はダメです。なーぜーーーーー!?

PRT Loader を選択した状態で selection から拾う、


from Py3dsMax import mxs 
for n in mxs.selection: 
    print n.name

これならイケるんですね。ぐぬぬぬ。何が違うのか全く不明です。

ちなみにどちらの例でも fileList のように KrakatoaPRTLoaderクラスで定義されているプロパティはアクセスできます。

でまあ、いろいろ試行錯誤した結果こんな感じに。ここで得られた objs なら、 show するなり何なりで通常のオブジェクトと同じように操作できます。


from Py3dsMax import mxs

objs = []
for n in mxs.getClassInstances(mxs.KrakatoaPRTLoader): 
    objs.append('selectMore ' + mxs.exprForMAXObject(n))

s = '''undo on
(
clearSelection()
%s
)''' % '\n'.join(objs)

mxs.execute(s)
objs = mxs.selection
mxs.execute('max undo')

for o in objs:
    print o.name

良い子はマネしちゃいけない、極悪非道なコードです。あまりにも酷すぎます。

全く、なんなんでしょうね、、、、、

未分類

パノラマ雲台製作中

IBL 素材作成のためにパノラマ機材を購入してパノラマ野郎化した勢いで自動雲台まで作りたくなって、ここ数カ月、細々と設計やら部品調達やらを進めていたモロモロが少しづつ形になってきたのでお披露目。一番のネックだったフレーム部分ができて仮組みしてみました。

制御制御部分は Raspberry Pi を使って、パノラマ撮影+多段撮影による HDR パノラマ撮影まで対応する予定です。サーボとカメラを制御するためのソフトウェア側も大体メドはついているので、あとはパッケージングをどうするかというところです。

Programming

ネットワークファイルへの stat() は、嘘をつかれることがあるよ!!

ネットワークファイル周りは結構地雷がたくさんあって、これも今年私がドハマりしたものです。
私が確認しているのはWindows7+Windows Server の組み合わせのみで XP などでは起きない症状ですが、ネットワーク上にファイルを作成/削除してもどこかでキャッシュが聞いているようで、数秒間はstat() が嘘情報を返すというものがあります。


import os
import time
import sys
import ctypes


def CreateHardLink(src, dest):
    if os.path.exists(dest):
        print 'file exist'
        raise FileExistError

    CreateHardLinkW = ctypes.windll.kernel32.CreateHardLinkW
    CreateHardLinkW.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int]

    ret = CreateHardLinkW(dest, src, 0)
    if ret ==0:
        print ctypes.FormatError()
        print src
        print dest

        raise PlatformError

def clearData():
    for i in range(10):
        f = os.path.join(d, r'%s\%03d.txt' % (d,i))
        if os.path.exists(f):
            os.remove(f)

def testCreate(path):
    s = os.path.join(path, r'src.txt')
    open(s, 'w').close()

    for i in range(10):
        print ('create %d' % (i))

        f = os.path.join(path, r'%s\%03d.txt' % (path,i))

        CreateHardLink(s, f)

        count = 0
        while not os.path.exists(f):
            print ('file not found %d %d' % (i, count))
            time.sleep(0.1)
            count += 1

testCreate(sys.argv[1])
C:\temp>python linkPerformanceTest.py \\Nas01\PJ\tmp
create 0
file not found 0 0
file not found 0 1
file not found 0 2
file not found 0 3
file not found 0 4
file not found 0 5
file not found 0 6

(以下略)

これで不思議なのは、Explorer ではきちんとファイルがあると認識されていることなんですね。どうやったら正確な情報を得られるのか。。。謎です。

※。。。と書いている途中で、もしかしたらAPI 経由で取ればいけるのかも?と思えてきました。後から追試してみます。

この症状はネットを漁ってみると Windos に限って起こるわけではなく、NFS ベースの Linux システムでもオプションによっては起こるようです。ただ、各種対応策を取っても解決できなかったのでちょっと特殊な理由なのかもしれません。

こういうの、地味に困るんですよね、、、ある程度時間が経ってから確認するときちんと動くのに、ちょっとシビアなタイミング(テスト環境では4.5秒以内)でアクセスすると発動する地雷なのです。トホホホホ。。。。。

Programming

ネットワーク上のファイルのハードリンクのカウントは正確に取得できないことがあるみたいだよ!!

NTFSで一つのファイルに張れるハードリンクは1023個までだよ!! の続き記事です。

1023 個って数字な時点で SAN 値ダダ下がり、もう一歩でソウルジェム真っ黒!!ってところまで来てたものの、自分の用途ならば限界超えてリンクを張るなら一つファイルをコピーしてハードリンク先を半分そっちに割り振ればいいやと思っていたので、そのための検証をしていたらここでもドハマりしたのでこれ以上不幸な(ry

UN*X の場合、ファイルにハードリンクされた数は stat で取得できます。例えば、Python のドキュメントだとこのあたり。ところが、こいつは Windows では正しく動きません。 OKOK。これは想定内。これくらいで動じてたらソウルジェムが幾つあっても足りない。

Windows にはこれらの情報を得るための API 、GetFileInformationByHandle() があります。ハードリンク数を取得するのはこちらが正解。これを使ってリンク数を取得するテストプログラムは以下のとおり

import os
import sys
import stat
import ctypes

import win32file

def CreateHardLink(src, dest):
    CreateHardLinkW = ctypes.windll.kernel32.CreateHardLinkW
    CreateHardLinkW.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int]

    ret = CreateHardLinkW(dest, src, 0)
    if ret ==0:
        print ctypes.FormatError()
        print src
        print dest

        raise

    return ret

def getNumHardLink(fileName):
    hFile = win32file.CreateFile (
        fileName,
        win32file.GENERIC_READ,
        win32file.FILE_SHARE_READ,
        None,
        win32file.OPEN_EXISTING,
        0,
        None
        )

    (attributes,
     created_at, accessed_at, written_at,
     volume,
     file_hi, file_lo,
     n_links,
     index_hi, index_lo
     ) = win32file.GetFileInformationByHandle (hFile)

    hFile.Close()

    return n_links

d = sys.argv[1]
print d
src = os.path.join(d, 'src.txt')
dest = os.path.join(d, 'dest.txt')

if not os.path.exists(src):
    open(src, 'w').close()

CreateHardLink(src, dest)

s = os.stat(src)
if s.st_nlink == 2:
    print 'OK by stat'
else:
    print ('invalid link count by stat : %d' % s.st_nlink)

n_links = getNumHardLink(src)
if n_links == 2:
    print 'OK by API'
else:
    print ('invalid link count by API : %d' % n_links)

比較のために stat でも情報を拾っています。

C:\temp>python countLink.py c:\temp
c:\temp
invalid link count by stat : 0
OK by API

ちゃんと動いています。

だがしかし!!ここで一筋縄でいかないのが Windows 様。ネットワーク上のファイルシステムを指定してみると。。。

C:\temp>python countLink.py \\nas01\PJ\test
\\nas01\PJ\test
invalid link count by stat : 0
invalid link count by API : 1

え。。。。

え。。。。。

はい、ちゃんと値が取れません。ちなみに、これもサーバーによるらしく某所ではこの症状が起きてドハマりしました。
ちなみに Windows7+QNAP(Linux) ではきちんと動いているっぽいです。

たまたま外れのサーバーだったのか、それとも Windows ベースのものだったからなのか。。。どなたか他の環境で検証していただけないでしょうか。
この数字が取れないと本当に困るんですけど。。。。

これ以外でハードリンクされているかどうかを確認するには、先ほどの GetFileInformationByHandle() でファイルの ID が取れるので、
それを比較するしかなさそうな予感がします。。。。

Programming

NTFSで一つのファイルに張れるハードリンクは1023個までだよ!!

こんなところにこんな罠があるとは夢にも思わず、ガッツリやられたのでこれ以上不幸な人を生み出さないためにメモ。

Windows の NTFS で作るハードリンクは、一つのファイルに対して 1023 個が MAX のようです(!!)
恐ろしいことに、これ、日本語で書かれたドキュメントが見つからないんですね、、、みんなそこまで使ってないんでしょうか?

一番詳しく書かれているっぽいのが英語の Wikipedia。

Hard link

The maximum number of hard links to a single file is limited by the size of the reference counter: with NTFS this is limited to 1023 because a 10 bit field is used for this purpose.

NTFSでは、ハードリンクを管理するための領域として 10bit しか確保してないから 1023 個しかリンクできないよ♪ってことらしいです。おおう、じーざす!!

本当かどうか、テストしてみました。

import os
import ctypes

def CreateHardLink(src, dest):
    CreateHardLinkW = ctypes.windll.kernel32.CreateHardLinkW
    CreateHardLinkW.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int]

    ret = CreateHardLinkW(dest, src, 0)
    if ret ==0:
        print ctypes.FormatError()
        print src
        print dest

        raise

    return ret

src = r'c:\temp\src.txt'
if not os.path.exists(src):
    open(src, 'w').close()

for i in range(2048):
    print i
    CreateHardLink(src, os.path.join(r'c:\temp', '%04d.txt' % i))

C:\temp>python testLink.py
0
1
2
3
4
5
6
7
8
9
10

(中略)

1020
1021
1022
1023
ファイル システムでサポートされている以上の数のリンクをファイルに作成しようとしました。
c:\temp\src.txt
c:\temp\1023.txt
Traceback (most recent call last):
File “testLink.py”, line 24, in
CreateHardLink(src, os.path.join(r’c:\temp’, ‘%04d.txt’ % i))
File “testLink.py”, line 14, in CreateHardLink
raise
TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType

C:\temp>

本当でした。。。

ちなみに、この制限は NTFS によるものなので Linux ベースのファイルサーバーに対して同様なことをしてもエラーにならないみたいです。
私の環境(Windows7+QNAP)では、2048 個のリンクを張ることもできました。

Windows でハードリンクを張るっていうことがかなり少ないのでハマることはあまりないのかもしれませんが、こういうことが想定されるシステムではちゃんとした OS で動いているファイルサーバーを用意した方がいいかもしれません。