2012年12月28日金曜日

blender - QUMARION用可動モデルの作り方

QUMARION用可動モデルの作り方(モデラー向け)
ついにBlender向け自作FBXインポータ/エクスポータが半分完成したので、記念に。

注意:こちらは簡単なほうじゃなくて難しいほうの作り方です。モデリングできる人向け。


まずミクさんを用意します。


用意しました。こちらは、ボーンとか入ってないけど、MMD用のボーンを入れるために腕がハの字になっているミクさん。まだ修理中だったため、色々とおかしいです。靴下とか。

次に、別のレイヤーに某所で配布されているモデルを、
Another Blender FBX Importer/Exporter
で読み込みます。使用許諾的にはは出来たものをうpればOKのようです。


読み込みました。

この女子学生から、骨だけ頂きます。


ミクさん小さいです。いや、骨がでかいです。仕方ないので、ミクさんを(47メートルくらい)巨大化させます。



巨大化させました。しかし腕がT字では無くハの字になっているため、何とかしかければいけません。
ここで、骨のほうをハの字にしてはいけません。 骨を回転させてしまうと、これまでの苦労が水の泡です。あくまで骨は女子学生のものを使います。 面倒ですが、メッシュのほうをT時にします。



薄々気付いていましたが、うちのミクは手が短すぎるようです。女学生は逆にちょっと長い気がするので、間くらいまで腕を伸ばします。
同時に、ポーズモードでボーンを選択してSキーで拡大縮小させることで、全く回転させずにボーンの長さだけ調節します。



はい、重要です。これが今回最も重要です。QUMARIONが何もせずに反応するボーンは可動モデルなんとかガイドに書いてあるボーン名と、ボーンの軸の角度がピッタリあってることが必要なようです。
(角度は書いてないし、書いてあったとしてもBlenderでは調節できないのでどうしようもない。)
このように長さだけ調節することで、軸の角度を全く変えずにボーンを編集することができるのです。
長さをとことん変えた後は、Ctrl+Aで、Apply Pose as Rest Poseを行って、エディットモードのボーンも同じ状態にしてください。
で、まぁあとは作るだけです。 作って作って作りまくって、最後にFBXで出力します。

出力するときは必ず、
Another Blender FBX Importer/Exporter
で、次のオプションを有効にして出力してください。

export imported node propertiesは、Blender上のボーンの位置/角度情報などを一切無視して、読み込んだままの位置/角度情報を出力します。
fit node lengthは、Blender上のボーンの位置/角度情報などを無視する場合でも、ボーンの長さのみ、反映させるというオプションです。

これら2つを有効にすることで、読み込んだ女学生と同様にQUMARIONで設定なしに動かせるFBXが出来上がります。


と書いたものの、作りながらやってるのでまだ出来てません。出来次第、完成画像載せます。
あと、エクスポータのほうで重要な行を1行コメントアウトしてしまっていのたで、明日修正します…

2012年12月4日火曜日

blender - Another Autodesk FBX Importer(3)


今までBlenderには技術的かつ宗教的な理由で、
FBXのまともなプラグインは存在しなかったので、
既に我ながらかなり満足しているが、
ついでに行くとこまで行くよ!

インポート時のボーンの軸を維持したまま、
ポーズモードでスケールをかけて、軸に沿ってボーンの長さを変え、
それを出力することに成功した。


画像左のように、軸がてきとーなBlenderのリグでも、インポートしたときの軸情報を覚えていて、
かつ長さを変更した場合は、エクスポート時に軸に沿った方向に長さを変更するので、
右のように、QUMARION対応状態でボーン長さ調整ができる。
この機能は、QUMARIONだけでなく、ゲーム用とかDCCツール用のモデルを扱ってる人には、かなり有効なのではないだろうか。

こんなことしなくても、ボーンなしのメッシュに、モーションシェルとかいうのを入れるとさくっとQUMARIONで動くんだけど、

Unityとか他のソフトでも使えるというのは大きい。
Unityとか他のソフトでも使えるというのは大きい。

あとGUIとメニュー付けたらいつでもリリースできる感じだけど、
Blender 2.65がリリース間近とのことなので、それ出るまで待つかも。


2012年11月30日金曜日

blender - Another Autodesk FBX Importer(2)

Blenderはボーンの軸の+Yがtail方向で、変更できないので、インポートしてエクスポートすると多くの場合再現不能と思う。
しかもZupだ。仮に変換できるとしても一筋縄でいかない。

公式のエクスポータみたいにローカル軸を気にせず適当に90度回転させれば、位置だけは合うんだけど、軸がどうやっても合わない。 rollとかで多少いじれるけど、rollは軸周りの回転しかできない。軸が逆向きだったりするとどうしようもない。


そこで、読み込み時のローカルのTrans/Rot/ScaleをCustomPropertiesに保存しておくようにした。
これをエクスポートのときにオプションによってはそのまま出力する。



これを行うことで、Blender上での軸や、編集結果を無視して、確実に軸を変更せずに出力できるので、Unityとかのゲーム向けモデルとか、QUMARION向けモデルとかで便利なはずである。

この問題が一番の難関だったけど、上記方法で上手くいったため、多分速ければ今週末くらいにインポータ・エクスポータともに割とまともなものを公開できると思います。

2012年11月29日木曜日

blender - Another Autodesk FBX Importer(1)

FBX Importer作り始めました。

とりあえずなんか読み込める程度のテスト版が出来たので

テスト公開してしまいます。

バイナリのFBXファイル一応読めます。 バイナリのFBXファイル一応読めます。

現在の対応状況:頂点、法線、UV、マテリアル、テクスチャ、ウェイト、ボーン
今後の予定:ボーンの軸の改良、頂点カラー、シェイプ、カメラ、多角形ポリゴン
DownloadHere!

あ、スーパー人柱バージョンだからね!勘違いしないでよねっ!
http://blenderfbx.render.jp/


エクスポータもあわせて年内に出すよ!

etc - QUMARION to Blender(1)

QUMARIONのSDKが学術目的または評価目的で使用するユーザ向けに、 無償で公開されました!

ただしC++用です!


早速Pythonで使えるか、Blenderで使えるかを評価するためハックを始めましょう!

この技術情報は、SDKを入手する前に知り得た情報であるので、公開しても問題ないであろうと思いますが、問題あった場合は直ちに公開停止されることでしょう。



Blenderから使うには、Pythonからアクセスできねばなりません。
Pythonから使うには、Pythonのctypeを使ってネイティブのDLLにアクセスするとか、
Boost.Pythonを使ってラップしたDLLを作る(最近覚え中)などがあります。

では、早速dependencywalkerでDLLを開きます。


開きました。これはなにやらシンプルな記述の関数がならんでいますね。
これはctypeでさくっといけそうな感じです。
しかしこれはちょっとシンプルすぎて使えません。右クリックして「Undecorate C++ Functions」のチェックを外しましょう

外しました。


これです!これが必要らしいのです。ctypeとかいうやつには。

早速QmPdkDll.dllがある場所でコンソールでPythonを開き、以下のように打ち込みます。



なんとかInitとかいう初期化っぽいのを呼んでみました。
「0」が返ってきました。ちょっと成功したのか分からんですね。
まぁそういうときはヘッダがあるのでヘッダファイルを見ます。
すると、どうやら成功しているようです。


といった感じでゴリゴリ書いていきます。
折角なのでクラス化しておきましょう。


class QmPdk:

 def __init__(self):
  """
   The constructor.
  """
  qmpdk = CDLL("QmPdkDll.dll")
  self.QmPdkCalibratePose = \
   getattr(qmpdk, "?QmPdkCalibratePose@@YAHH@Z")
  self.QmPdkCharacterCreate = \
   getattr(qmpdk, "?QmPdkCharacterCreate@@YAHPAHH0PAPBD@Z")
  self.QmPdkCharacterDestroy = \
   getattr(qmpdk, "?QmPdkCharacterDestroy@@YAHH@Z")
  self.QmPdkCharacterGetHandle = \
   getattr(qmpdk, "?QmPdkCharacterGetHandle@@YAHHPAH@Z")
  self.QmPdkCharacterGetLocalMatrix = \
   getattr(qmpdk, "?QmPdkCharacterGetLocalMatrix@@YAHHHQAY03M@Z")
  self.QmPdkCharacterGetName = \
   getattr(qmpdk, "?QmPdkCharacterGetName@@YAHHHPADH0H@Z")
  self.QmPdkCharacterGetNumOfHandle = \
   getattr(qmpdk, "?QmPdkCharacterGetNumOfHandle@@YAHPAH@Z")
  self.QmPdkCharacterGetQumaHandle = \
   getattr(qmpdk, "?QmPdkCharacterGetQumaHandle@@YAHHPAH@Z")
  self.QmPdkCharacterGetRotate = \
   getattr(qmpdk, "?QmPdkCharacterGetRotate@@YAHHHQAY03M@Z")
  self.QmPdkCharacterMemorizeInitialPose = \
   getattr(qmpdk, "?QmPdkCharacterMemorizeInitialPose@@YAHH@Z")
  self.QmPdkCharacterRecallInitialPose = \
   getattr(qmpdk, "?QmPdkCharacterRecallInitialPose@@YAHH@Z")
  self.QmPdkCharacterSetLocalMatrix = \
   getattr(qmpdk, "?QmPdkCharacterSetLocalMatrix@@YAHHHQAY03M@Z")
  self.QmPdkCharacterSetRotate = \
   getattr(qmpdk, "?QmPdkCharacterSetRotate@@YAHHHQAY03M@Z")
  self.QmPdkCopyPose = \
   getattr(qmpdk, "?QmPdkCopyPose@@YAHH@Z")
  self.QmPdkEnable = \
   getattr(qmpdk, "?QmPdkEnable@@YAHHH@Z")
  self.QmPdkEnableAccelerometer = \
   getattr(qmpdk, "?QmPdkEnableAccelerometer@@YAHHH@Z")
  self.QmPdkFinal = \
   getattr(qmpdk, "?QmPdkFinal@@YAHXZ")
  self.QmPdkGetVersionStr = \
   getattr(qmpdk, "?QmPdkGetVersionStr@@YAHPADPAH@Z")
  self.QmPdkInit = \
   getattr(qmpdk, "?QmPdkInit@@YAHXZ")
  self.QmPdkIsEnable = \
   getattr(qmpdk, "?QmPdkIsEnable@@YAHHPAH@Z")
  self.QmPdkLoadCalibrationDataFile = \
   getattr(qmpdk, "?QmPdkLoadCalibrationDataFile@@YAHHPBD@Z")
  self.QmPdkNnbCreateGroup = \
   getattr(qmpdk, "?QmPdkNnbCreateGroup@@YAHHHPAH@Z")
  self.QmPdkNnbIsValid = \
   getattr(qmpdk, "?QmPdkNnbIsValid@@YAHHPAH@Z")
  self.QmPdkNnbLoadFromFile = \
   getattr(qmpdk, "?QmPdkNnbLoadFromFile@@YAHHPBD0@Z")
  self.QmPdkNnbLoadFromMem = \
   getattr(qmpdk, "?QmPdkNnbLoadFromMem@@YAHHPBDH@Z")
  self.QmPdkNnbOpenMappingWindow =\
   getattr(qmpdk, "?QmPdkNnbOpenMappingWindow@@YAHHI@Z")
  self.QmPdkNnbSaveToFile = \
   getattr(qmpdk, "?QmPdkNnbSaveToFile@@YAHHHPBD@Z")
  self.QmPdkNnbSaveToMem = \
   getattr(qmpdk, "?QmPdkNnbSaveToMem@@YAHHHPADPAH@Z")
  self.QmPdkNnbSetGroup = \
   getattr(qmpdk, "?QmPdkNnbSetGroup@@YAHHHHPAHH0@Z")
  self.QmPdkQumaAttachInitPoseModel = \
   getattr(qmpdk, "?QmPdkQumaAttachInitPoseModel@@YAHHH@Z")
  self.QmPdkQumaDetachModel = \
   getattr(qmpdk, "?QmPdkQumaDetachModel@@YAHH@Z")
  self.QmPdkQumaGetButtonState = \
   getattr(qmpdk, "?QmPdkQumaGetButtonState@@YAHHHPAH@Z")
  self.QmPdkQumaGetHandle = \
   getattr(qmpdk, "?QmPdkQumaGetHandle@@YAHHPAH@Z")
  self.QmPdkQumaGetNumOfHandle = \
   getattr(qmpdk, "?QmPdkQumaGetNumOfHandle@@YAHPAH@Z")
  self.QmPdkQumaIsPoseChanged =\
   getattr(qmpdk, "?QmPdkQumaIsPoseChanged@@YAHHPAH@Z")
  self.QmPdkSaveCalibrationDataFile = \
   getattr(qmpdk, "?QmPdkSaveCalibrationDataFile@@YAHHPBD@Z")


ひたすらコピーペーストしていくだけの簡単かつ面倒な作業です。 まぁこれ↑実はSDK公開前にせこせこmaya用についてたDLLをハックしてたやつなので、今の関数と違うかもしれませんが、それは(2)とか(3)とかで直していきます。ではまた後日!

2012年7月31日火曜日

etc - L-System適当メモ



教科書:


The Algorithmic Beauty of Plants
http://algorithmicbotany.org/papers/#abop

上記書籍の簡易解説pdf.
http://natcomp.liacs.nl/CSA/slides/CSA7.pdf

以下は読みながら適当にメモったものであり、
内容の正当性については保証できないので、本気出す人は上記書籍を参照してください。



---------------------------------------------------------


DOL-System(deterministic and context-free,L-System)

→コンテキストは自由に与えることが出来、コンテキストごとの結果は一意に定まる(ランダムに結果が変動したりしない)ようなL-System

---------------------------------------------------------

方法1:上から書いていく方法

ω(axiom)=初期状態(n=0)
n=何回実行したか。ωにp1~pNを全てm回適用したらmである。
p1=rule1
p2=rule2
p3=rule3



pN=ruleN

方法2:turtleインタプリタ。亀が2次元で動く
方法1の定義に加え、亀用に以下の定義を導入

δ(angle)=回る角度
F=前に距離dだけ一歩進む、移動中描画する
f=前に距離dだけ一歩進む、移動中描画しない
+=左にδ回転
-=右にδ回転


---------------------------------------------------------

DOL-Systemの合成

高い木を作るには、ランダムな変更が必要であるが、
プログラムでは配列や構造体操作でL-Systemが表現される
というインタフェース上の問題がある。

1990年以前高い木をモデリングするには非常に制限された方法であるが、
turtleインタプリタに2つの操作モードを導入する方法が考案された。
edge rewritingnode rewritingである。(グラフ理論に由来する用語らしい)



Edge RewritingとNode Rewritingについては、
http://www.selcukergen.net/ncca_lsystems_research/lsystems.html
の図が分かりやすい






(読み終わるまで続く)

2012年6月28日木曜日

blender - 2.6 - pythonで3DViewにお絵かきin3Dその2

ray_castとかいう関数があったので、その1で得たベクトルを適当に加工して突っ込んでみたところ、
少し怪しいけど、だいたいいけてる。

この調子で複数オブジェクトにも対応してみたい。

    def unproject_to_object(self, context, x, y):
        px = x - self.viewport[0]
        py = y - self.viewport[1]
        
        coord = (px, py)
        region = context.region
        region_3d = context.space_data.region_3d
        
        vec = bpy_extras.view3d_utils.region_2d_to_vector_3d(\
                region, region_3d, coord)
                
        start = bpy_extras.view3d_utils.region_2d_to_location_3d(\
                region, region_3d, coord, vec * -100)
        
        end = bpy_extras.view3d_utils.region_2d_to_location_3d(\
                region, region_3d, coord, vec * 100)
        
        if context.object != None:
            start = context.object.matrix_world.inverted() * start
            end = context.object.matrix_world.inverted() * end
            loc, nor, index = context.object.ray_cast(start, end)
            return (loc.x, loc.y, loc.z)

        return None

blender - 2.6 - pythonで3DViewにお絵かきin3Dその1

画面と平行な適当な3D座標を返してくれる関数を発見したので、
フリーハンドで2Dに描いたものを渡してやって、ご覧のとおり。

glUnProjectのほうは上手くいかない…。
昔はBpyWindowにmouseViewRayとかいう関数があったらしいので、それ参考にするか…

まぁ現状でも結構満足している。これにAn algorithm for automatically fitting digitized curves実装したらどの程度速度出るだろうか…。
# Blender 3DViewport Line Drawer
# author: uimac
# data: 2012/6/28

import bpy
import bgl
import bpy_extras
from bpy_extras import *
import mathutils
from mathutils import *

class LineDrawer:
    
    pre_x = 0
    pre_y = 0
    is_mouse_pressed = 0
    
    # [(x0, y0), (x1, y1), ...]
    point_list = []
    
    # [(world_x_0, world_y_0, world_z_0), ...]
    point_list_3d = []
    
    # [ point_list_1, point_list_2, ... ]
    stroke_list = []
    
    # [ point_list_3d_1, point_list_3d_2, ... ]
    stroke_list_3d = []
    
    viewport = None
    
    line_object = None
    line_mesh = None
    
    def __init__(self):
        self.point_list = []
        self.stroke_list = []
        self.point_list_3d = []
        self.stroke_list_3d = []
        self.viewport = None
        self.pre_x = 0
        self.pre_y = 0
        
    def init_buffers(self, context):
        if self.viewport == None:
            self.viewport = bgl.Buffer(bgl.GL_INT, 4)
            
        if self.line_object == None:
            self.line_mesh = bpy.data.meshes.new("line_mesh")
            self.line_object = bpy.data.objects.new("line", self.line_mesh)
            self.line_object.data = self.line_mesh
            context.scene.objects.link(self.line_object) 
            
    def dispose(self):
        self.__init__()
    
    def mouse(self, context, event):
        x = event.mouse_x
        y = event.mouse_y
                
        if event.type == 'LEFTMOUSE':
            if event.value == 'PRESS':
                self.is_mouse_pressed = True
                self.pre_x = x
                self.pre_y = y
                
            elif event.value == 'RELEASE':
                self.is_mouse_pressed = False
                self.stroke_list.append(self.point_list)
                self.stroke_list_3d.append(self.point_list_3d)
                self.point_list = []
                self.point_list_3d = []
                self.convert_stroke_to_mesh()
                
        if event.type == 'MOUSEMOVE':
            if self.is_mouse_pressed:
                point_list = self.point_list
                point_list.append((x,y))
                
                # tekitou hokan
                pointlen = len(point_list)
                if pointlen >= 3:
                    last = pointlen - 1
                    p0 = point_list[last-2]
                    p1 = point_list[last-1]
                    p2 = point_list[last]
                    p = ( \
                    (((p0[0] + p2[0]) >> 1) + p1[0]) >> 1,
                    (((p0[1] + p2[1]) >> 1) + p1[1]) >> 1)
                    
                    self.point_list[last-1] = p
                    p3d = self.unproject_parallel(context, p[0], p[1])
                    self.point_list_3d.append(p3d)
                
        self.pre_x = x
        self.pre_y = y

    def unproject_parallel(self, context, x, y):
        px = x - self.viewport[0]
        py = y - self.viewport[1]
        
        coord = (px, py)
        region = context.region
        region_3d = context.space_data.region_3d
        
        vec = bpy_extras.view3d_utils.region_2d_to_vector_3d(\
                region, region_3d, coord)
                
        loc = bpy_extras.view3d_utils.region_2d_to_location_3d(\
                region, region_3d, coord, vec)
        
        return (loc.x, loc.y, loc.z)

    def draw_line(self, x0, y0, x1, y1):
        bgl.glBegin(bgl.GL_LINES)
        bgl.glVertex2i(x0,y0)
        bgl.glVertex2i(x1,y1)
        bgl.glEnd()
    
    def draw_lines(self, context, event, viewport):
        
        if len(self.stroke_list) > 0:
            for stroke in self.stroke_list:
                bgl.glBegin(bgl.GL_LINE_STRIP)
                for point in stroke:
                    x1 = point[0] - viewport[0]
                    y1 = point[1] - viewport[1]
                    bgl.glVertex2i(x1,y1)
                bgl.glEnd()
        
        bgl.glBegin(bgl.GL_LINE_STRIP)
        
        for point in self.point_list:
            x1 = point[0] - viewport[0]
            y1 = point[1] - viewport[1]
            bgl.glVertex2i(x1,y1)
            
        bgl.glEnd()

    def convert_stroke_to_mesh(self):
        verts = []
        edges = []
        
        if len(self.stroke_list_3d) > 0:

            for stroke_index, stroke in enumerate(self.stroke_list_3d):
                voffset = len(self.line_mesh.vertices)
                eoffset = len(self.line_mesh.edges)
                vlen = len(stroke)
                if vlen <= 1:
                    continue
                self.line_mesh.vertices.add(vlen)
                self.line_mesh.edges.add(vlen)
                for index, point in enumerate(stroke):
                    vindex = voffset+index
                    eindex = eoffset+index-1
                    verts.append(vindex)
                    self.line_mesh.vertices[vindex].co = point
                    if index == 0:
                        self.line_mesh.edges[eindex].vertices = [-1, -1]
                    else:
                        eindex = eoffset+index-1
                        edges.append(eindex)
                        self.line_mesh.edges[eindex].vertices = [eindex, eindex+1]

            self.line_mesh.update()
            self.stroke_list_3d = []
            self.stroke_list = []
    
    def draw_lines_3d(self, context, event, viewport):
        
        bgl.glColor4f(0.0, 0.0, 1.0, 1)    
    
        if len(self.stroke_list_3d) > 0:
            for stroke in self.stroke_list_3d:
                bgl.glBegin(bgl.GL_LINE_STRIP)
                for point in stroke:
                    bgl.glVertex3f(point[0],point[1],point[2])
                bgl.glEnd()
    
        bgl.glBegin(bgl.GL_LINE_STRIP)
        
        for point in self.point_list_3d:
            #print(point[0], point[1], point[2])
            bgl.glVertex3f(point[0], point[1], point[2])
            
        bgl.glEnd()
    
        bgl.glColor4f(0.0, 0.0, 0.0, 1)
        
    def draw(self, context, event):
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glColor4f(0.0, 0.0, 0.0, 1)
        bgl.glLineWidth(1.5)
    
        self.init_buffers(context)
        bgl.glGetIntegerv(bgl.GL_VIEWPORT, self.viewport)
            
        # draw!
        self.draw_lines_3d(context, event, self.viewport)
        self.draw_lines(context, event, self.viewport)

        # restore opengl defaults
        bgl.glLineWidth(1)
        bgl.glDisable(bgl.GL_BLEND)
        bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
        

class DrawerRegister(bpy.types.Operator):
    bl_idname = "uimac.drawer"
    bl_label = "uimac Drawer"
    
    drawer = LineDrawer()
    handle = None

    def modal(self, context, event):
        if context.area:
            context.area.tag_redraw()
        
        self.drawer.mouse(context, event)
        
        if event.type in ('ESC'):
            if self.handle:
                self.drawer.dispose()
                context.region.callback_remove(self.handle)
            return {'CANCELLED'}

        return {"PASS_THROUGH"}

    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            self.cursor_on_handle = 'None'
            context.window_manager.modal_handler_add(self)
            
            #bpy.ops.object.mode_set(mode='OBJECT')
            
            self.handle = context.region.callback_add(\
                self.drawer.draw, (context, event), 'POST_PIXEL')
      
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "Image View not found, cannot run operator")
            return {'CANCELLED'}

def register():
    bpy.utils.register_class(DrawerRegister)
def unregister():
    bpy.utils.unregister_class(DrawerRegister)
    
if __name__ == '__main__':
    register()

2012年6月10日日曜日

blender - 2.6 - pythonで3DViewにお絵かき


実行方法
1. TextEditorにスクリプトを貼り付ける
2. RunScriptボタンを押す
3. 3DViewでスペースキーを押して、uimacで検索
4. uimac Drawer というのが出てくるので選択する
5, 描く

Escキーで終了です。
# Blender 3DViewport Line Drawer
# author: uimac
# date: 2012/6/10
# ver: 0.001dot

import bpy
import bgl

class LineDrawer:
    
    pre_x = 0
    pre_y = 0
    is_mouse_pressed = 0
    
    # [(x0, y0), (x1, y1), ...]
    point_list = []
    
    # [ point_list_1, point_list_2, ... ]
    stroke_list = []
    
    viewport = None
    
    def dispose(self):
        self.point_list = []
        self.stroke_list = []
        self.viewport = None
        self.pre_x = 0
        self.pre_y = 0
        
    def mouse(self, context, event):
        x = event.mouse_x
        y = event.mouse_y
                
        if event.type == 'LEFTMOUSE':
            if event.value == 'PRESS':
                self.is_mouse_pressed = True
                self.pre_x = x
                self.pre_y = y
                
            elif event.value == 'RELEASE':
                self.is_mouse_pressed = False
                self.stroke_list.append(self.point_list)
                self.point_list = []
                
        if event.type == 'MOUSEMOVE':
            if self.is_mouse_pressed:
                point_list = self.point_list
                point_list.append((x,y))
                
                # tekitou hokan
                pointlen = len(point_list)
                if pointlen >= 3:
                    last = pointlen - 1
                    p0 = point_list[last-2]
                    p1 = point_list[last-1]
                    p2 = point_list[last]
                    point_list[last-1] = ( \
                    (((p0[0] + p2[0]) >> 1) + p1[0]) >> 1,
                    (((p0[1] + p2[1]) >> 1) + p1[1]) >> 1)

        self.pre_x = x
        self.pre_y = y

    def draw_line(self, x0, y0, x1, y1):
        bgl.glBegin(bgl.GL_LINES)
        bgl.glVertex2i(x0,y0)
        bgl.glVertex2i(x1,y1)
        bgl.glEnd()
    
    def draw_lines(self, context, event, viewport):
        
        if len(self.stroke_list) > 0:
            for stroke in self.stroke_list:
                bgl.glBegin(bgl.GL_LINE_STRIP)
                for point in stroke:
                    x1 = point[0] - viewport[0]
                    y1 = point[1] - viewport[1]
                    bgl.glVertex2i(x1,y1)
                bgl.glEnd()
        
        bgl.glBegin(bgl.GL_LINE_STRIP)
        
                
        for point in self.point_list:
            x1 = point[0] - viewport[0]
            y1 = point[1] - viewport[1]
            #print(point[0], point[1])
            #print(viewport)
            bgl.glVertex2i(x1,y1)
            
        bgl.glEnd()
        
    def draw(self, context, event):
        bgl.glEnable(bgl.GL_BLEND)
        bgl.glColor4f(0.0, 0.0, 0.0, 1)
        bgl.glLineWidth(1.5)
    
        if self.viewport == None:
            self.viewport = bgl.Buffer(bgl.GL_INT, 4)
            bgl.glGetIntegerv(bgl.GL_VIEWPORT, self.viewport)
            
        # draw!
        self.draw_lines(context, event, self.viewport)
    
        # restore opengl defaults
        bgl.glLineWidth(1)
        bgl.glDisable(bgl.GL_BLEND)
        bgl.glColor4f(0.0, 0.0, 0.0, 1.0)
        

class DrawerRegister(bpy.types.Operator):
    bl_idname = "uimac.drawer"
    bl_label = "uimac Drawer"
    
    drawer = LineDrawer()
    handle = None

    def modal(self, context, event):
        if context.area:
            context.area.tag_redraw()
        
        self.drawer.mouse(context, event)
        
        if event.type in ('ESC'):
            if self.handle:
                self.drawer.dispose()
                context.region.callback_remove(self.handle)
            return {'CANCELLED'}

        return {"PASS_THROUGH"}

    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            self.cursor_on_handle = 'None'
            context.window_manager.modal_handler_add(self)
            
            self.handle = context.region.callback_add(\
                self.drawer.draw, (context, event), 'POST_PIXEL')
                
            return {'RUNNING_MODAL'}
        else:
            self.report({'WARNING'}, "Image View not found, cannot run operator")
            return {'CANCELLED'}

def register():
    bpy.utils.register_class(DrawerRegister)
def unregister():
    bpy.utils.unregister_class(DrawerRegister)
    
if __name__ == '__main__':
    register()

2012年6月9日土曜日

blender - 2.6 - pythonで何か作る

いつもどこにあるか忘れるのでメモ: Blender/Python Documentation 2.63.8 http://www.blender.org/documentation/blender_python_api_2_63_8/

2012年2月29日水曜日

blender - 2.6 - pythonで面の面積(2)

前回で面積が出せました。

面積が分かると色々できますね!

とりあえず小さい順にソートして、小さいほうから選択してみると何か分かるかもしれませんね。

試しにミクさんの顔の面を、面積を小さいほうから150個くらい選択してみましょう


import bpy

context = bpy.context
context.tool_settings.mesh_select_mode = [True, False, False]

N = 150

if (context.active_object != None):
object = bpy.context.active_object

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

area_dict = {}

if (object.type == "MESH"):
faces = object.data.faces
vertices = object.data.vertices
for f in faces:
area_dict[f] = f.area

count = 0
for f, area in sorted(area_dict.items(), key=lambda x:x[1]):
if count < N:
for vi in f.vertices:
vertices[vi].select = True
count += 1

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


途中、見かけないlambdaというやつがありますが、ぶっちゃけよく分かりません。
ソートするにはでググって出てきたのをそのまま使った程度です。

そして結果!


おお!おおおおお!これは何か分かりそうですね!(続く)

blender - 2.6 - pythonで面の面積

前回のを改造して面の面積を求めて見ます。

三角形の面積というと、
2Dでは、底辺x高さ÷2
3Dでは、外積の大きさ÷2
です。

BlenderはOpenGLで面の頂点番号もOpenGLと同じく、反時計回りです。

0
|
|
1---2

といった感じで三角形になっています。
よって、選択した三角形の面積を表示するなら次のようになります。


import bpy

context = bpy.context

if (context.active_object != None):
object = bpy.context.active_object

if (object.type == "MESH"):
faces = object.data.faces
vertices = object.data.vertices
for f in faces:
if (f.select):
vi = f.vertices
a = vertices[vi[2]].co - vertices[vi[1]].co
b = vertices[vi[0]].co - vertices[vi[1]].co
c = a.cross(b)
print(c.length / 2)



で、これをもっと簡単にして、さらに四角形にも対応させると、次のようになります。


import bpy

context = bpy.context

if (context.active_object != None):
object = bpy.context.active_object

if (object.type == "MESH"):
faces = object.data.faces
vertices = object.data.vertices
for f in faces:
if (f.select):
print(f.area)


さすがBlender、計算するまでもないですね!
(続く)

2012年2月26日日曜日

blender - pixivで開催されたUnityのためのCGとか勉強会に行ってきました

pixivで開催されたUnityのためのCGとか勉強会(第4回)に行ってきました。

実はこの勉強会、第1回にも参加させていただいたのですが、そのときは会場は大久保のルノアールでした。
いつのまにか会場がpixivになっていて、ずっと参加したいなーと思っていたのですが、ついに参加できました。

さて、pixivは、色々と凄いところでした。
入り口、うわさには聞いていたピクシブたんチャリ!




そして大量の壁絵。



中も撮りわすれたのですが、色々と凄いです!色々と凄いです。
pixivさん最高です!


遅れて到着したため、みんなもくもくと作業をしていたので、ちょっと外れたところの人が少なめの席に行っ
たら、なんということでしょう、ピクシブさんの中の人な席でした。

とりあえずBlender2.62の新機能を試したりしていたのですが、やはりネットブックだと厳しい。
そして皆やはりMac率が高いっ!MBA!MBA!
色々PC環境の準備が足りてなくて駄目な感じだったので、途中から参加者とか中の人と話をする方向でうろうろしていました。twitterなどで気になっていた方にも無事会え、前会った方にも会え、有意義な時間を過ごせました。

がっ、全く成果が出なかったため帰宅後に1人反省会を兼ねてBlender。
2.6になってなかなか付いていけてないですが、shapeがマシになったとか、booleanが良くなったとか、remeshが付いたとか、cyclesとか凄いことになってるんで、そろそろ本気出さないとヤバイですね!
というわけで、本日の成果。
2.6になってpythonのapi覚えなおさないといけなくてめんどかったです。

shapekeyで参照元の頂点と位置が違う部分を選択するスクリプト



import bpy

context = bpy.context

if (context != None):
if (context.active_object != None):
object = bpy.context.active_object

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

verts = []

if (object.type == "MESH"):
vertices = object.data.vertices
for v in vertices:
v.select = False
verts.append(v)

if (object.active_shape_key != None):
shape_key = object.active_shape_key
base = shape_key.relative_key
for i, v in enumerate(shape_key.data):
if (v.co != base.data[i].co):
verts[i].select = True

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


これは何かというと、↓シェイプキーを選択して実行すると、こんな感じにシェイプの参照元と位置が違う頂点を選択してくれます。


これはBlender2.49で作った駄目なシェイプキーなので、口のシェイプのつもりだったのですが、
このように要らないところが動いてしまっています。
さらにその後にベースのメッシュをいじってしまったので、もう大変です。

しかし、最近のBlenderには、wキーを押したときに、"Blend from shape"という機能が付きました。

ちょっと動いてしまったところに、これを適用すると…



このように、口の周り以外の余計に動いてしまった部分を元に戻せます。


昔DirectXに持っていくときに、この辺でなかなか死んだ記憶があります。
たしか、目と口をシェイプでつけていて、重くならないように動く頂点だけ抽出しようとしたのですが、動く部分だけ切り出したときに頂点が点在すると面にならないので、vertexgroupオプションでshapekeyの範囲を絞って頂点を切り出すのですが、vertexgroup範囲外の微妙に動いている頂点をなんとかしようとpropagateしたらもうグチャグチャです。まぁこの場合、切り出すのが一番難しかったんですが…

2012年1月9日月曜日

etc - ミップマップ

ShaderX2より。

テクスチャ座標の、スクリーン座標系と同次座標系での関係を次のように表す


u' = \frac{\partial u'}{\partial x}x + \frac{\partial u'}{\partial y}y + uo'
v' = \frac{\partial v'}{\partial x}x + \frac{\partial v'}{\partial y}y + vo'

ここで
u', v' は同次座標系でのてくテクスチャ座標
uo, vo はスクリーン座標の原点(x = 0, y = 0)
x, y はスクリーン座標

テクスチャのx方向の圧縮は、

mx= \sqrt{(\frac{\partial u}{\partial x})^2 + (\frac{\partial v}{\partial x})^2}


u, vを同時座標u', v'で表すと、

mx= \sqrt{(\frac{(\partial{\frac{u'}{w}})}{\partial x})^2 + (\frac{(\partial{\frac{v'}{w}})}{\partial x})^2}


続く