2025年6月18日水曜日

本当に久々の Blender スクリプト

いやー本当にギリギリまで追い込まれないと知的な行動ができない子なんですよ、自分。
以前ちょこっとだけ、顔のモデリングをはじめました…と書いていたのですが…。

瞳を作成
瞳を作成するに当たって通常どうやってモデリングするでしょうか?
一般的にはUV球にテクスチャを描いて貼り付けると思います。
又、黒目のように円一周の模様が同じパーツなら、切り分けたピザの様に一部だけテクスチャを描いて、ぐるっと一周コピーして使用するのでは無いでしょうか?
が、残念ながら私はテクスチャを描く技術がありません。
普通ならココで絵を描く技術を磨くところですが、残念ながら私にはやる気と時間が(年齢的にも)ありません。
よって、全て細かいメッシュで表現しようとしているのですが、流石にまるまる全てをモデリングすると労力がかかりすぎます。

そこで眼球の右下部分だけをモデリングして、それをコピー接合していきます。
…左上に当たる座標に3Dカーソルを当てる→複製→縦横にマイナス拡縮を行って反転→自動吸着を行う…とやっていてけば確かに手動でも可能ですが、流石に面倒すぎる。

※ぶちゃけた事を書けば
眼球を作ると言うより、幾何学模様があるカラーコンタクトを大量に作って重ねるという感覚の方が近く、これら(カラーコンタクト)を1個作る毎に毎回この処理をやってるのは面倒すぎるのです。

そこで、手動操作を「スクリプト作成」で書き出して繋ぎ合わせたのが下のコードになります

コード開始

import bpy
import bmesh

while bpy.context.mode == 'EDIT_MESH':

    bFirst=True
    min_x=0.0
    min_z=0.0
    max_x=0.0
    max_z=0.0

    #### get center point ####

    me = bpy.context.object.data
    bm = bmesh.from_edit_mesh(me)

    for v in bm.verts:
        if v.select == False:
            continue
        if bFirst:
            min_x=v.co.x
            max_x=v.co.x
            min_z=v.co.z
            max_z=v.co.z
            bFirst=False
        if min_x>v.co.x:
            min_x=v.co.x
        if max_x<v.co.x:
            max_x=v.co.x
        if min_z>v.co.z:
            min_z=v.co.z
        if max_z<v.co.z:
            max_z=v.co.z

    bm.free()

    if bFirst == True:
        break

    center_x = min_x
    center_z = max_z
    width = max_x-min_x
    height = max_z-min_z

    #### dupricate ####

    old_pivot = bpy.context.scene.tool_settings.transform_pivot_point
    bpy.context.scene.tool_settings.transform_pivot_point = 'MEDIAN_POINT'

    old_merge = bpy.context.scene.tool_settings.use_mesh_automerge

    #### 1 ####
    bpy.context.scene.tool_settings.use_mesh_automerge = False
    bpy.ops.mesh.duplicate_move(
        MESH_OT_duplicate={"mode":1}, 
        TRANSFORM_OT_translate={"value":(-width, 0, 0)}
    )
    bpy.context.scene.tool_settings.use_mesh_automerge = True
    bpy.ops.transform.resize(value=(-1, 1, 1))

    #### 2 ####
    bpy.context.scene.tool_settings.use_mesh_automerge = False
    bpy.ops.mesh.duplicate_move(
        MESH_OT_duplicate={"mode":1}, 
        TRANSFORM_OT_translate={"value":(0, 0, height)}
    )
    bpy.context.scene.tool_settings.use_mesh_automerge = True
    bpy.ops.transform.resize(value=(1, 1, -1))
        
    #### 3 ####
    bpy.context.scene.tool_settings.use_mesh_automerge = False
    bpy.ops.mesh.duplicate_move(
        MESH_OT_duplicate={"mode":1}, 
        TRANSFORM_OT_translate={"value":(width, 0, 0)}
    )
    bpy.context.scene.tool_settings.use_mesh_automerge = True
    bpy.ops.transform.resize(value=(-1, 1, 1))

    bpy.context.scene.tool_settings.transform_pivot_point = old_pivot
    bpy.context.scene.tool_settings.use_mesh_automerge = old_merge
    
    break

コード終わり

一応解説

#### get center point ####
バウンディングボックスの算出

#### dupricate ####
ぐるっとコピー。コピーして反転しての繰り返し。反転する前に頂点同士がくっつくと不味いので、自動頂点吸着のフラグ操作が忙しくなっている。


はい、大分久しぶりでした。こんなんでも1週間くらいかかったような…
いや、コードが動き始めてからは2日くらいでしたが調べるのに時間かかりました。

速度を出すなら bmesh で全ての処理を完結させるべきだとは解るのですが、瞳に使う頂点、辺、面、法線…を全て網羅するコードを無事駆動させる自信が無かったので、「早い手動」でいいかなと。
あと、今回はじめて「スクリプト作成」を見ながらスクリプトを作ったのですが…フラグを弄ろうとして、わざわざ設定したのに反映されない場面が多々ありました。
なんでフラグ設定に従ってくれなかったのか?おかげでどのフラグがどういった意味を持っているのか判らないと言う部分がありまして、結局は動く分だけでどうにかしたのが今回のコードになります。

また、今回のスクリプトですが、例によって開発に使った機材は…以前購入した Latitude3120 です。Celeron N5100 マシン。
何年か前にスクリプトを書いた際にも書きましたが、こういう物は気付いた時に枕元で作るのが一番進みます。スペックではないのですよ。

まあ、久々に趣味で頭を使ったのでそれなりに満足ではあります。今後はコレを使ってモデリングを進めねば。

0 件のコメント:

コメントを投稿