2018年5月28日月曜日

BlenderからPhotoshopを制御5 -選択形状をPhotoshopの3Dレイヤに

こちらのサイトでは今までもBlenderのスクリプトからPhotoshopを制御をする方法の解説を書いてきました

BlenderからPhotoshopを制御1-Blender-PyhtonとPhotoshop-JavaScriptについて
BlenderからPhotoshopを制御2-レンダリングした画像をPhotoshpで開く
BlenderからPhotoshopを制御3-画像をPhotoshpで開くアドオン作成
BlenderからPhotoshopを制御4 -選択UVからPhotoshpの選択範囲

今回、「Blenderの3D形状を簡単な操作でPhotoshopに読み込ませる方法はないかな?」
という話を見かけて作ってみました。
Photoshopの3Dレイヤの操作って通常のメニュー操作でやると手順が多いので煩わしいのですよね・・・


結果としては拍子抜けするほど簡単でした(笑
基本的な動作は BlenderからPhotoshopを制御3 からやってきたように
PhotoshopのjavascriptにBlenderから書き出したデータのパスを渡して処理させるといった感じになります

まず Photoshopのjavascript

#target photoshop;

//ファイルの読み込み
var obj_file =  File(arguments[0]);
//指定したファイルから3Dレイヤを作成
// =======================================================
var idaddthreeDLayerFromFile = stringIDToTypeID( "add3DLayerFromFile" );
    var desc10 = new ActionDescriptor();
    var idfileList = stringIDToTypeID( "fileList" );
        var list1 = new ActionList();
        list1.putPath( obj_file );
    desc10.putList( idfileList, list1 );
executeAction( idaddthreeDLayerFromFile, desc10, DialogModes.NO );

非常にシンプルですね

次に Blender側のPython

bl_info = {
    "name": "3D Object to Photoshop",
    "description": "選択形状をPhotoshopの3Dレイヤに",
    "author": "Yukimi",
    "version": (0,4),
    "blender": (2, 6, 0),
    "location": "object",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "http://yukimi-blend.blogspot.jp/",
    "category": "Import-Export"}
 
import bpy
from io_scene_obj import export_obj
from bpy_extras.io_utils import axis_conversion

import os
import subprocess
import time
import random


#実行するjavascriptの名前
js_name = "Add3DLayerFromFile.jsx"
#このスクリプトのあるディレクトリのパス
mydoc_dir = os.path.dirname(__file__)
#実行するスクリプトのパス
VB_Hub = os.path.abspath(os.path.join(mydoc_dir, "VB_Hub.vbs"))
jscript = os.path.abspath(os.path.join(mydoc_dir, js_name))
#Blenderの一時ファイルディレクトリを利用する場合
tmp_dir = bpy.context.user_preferences.filepaths.temporary_directory
#ファイルの書き出し先をデスクトップにしたい場合は↓をコメントアウト
#tmp_dir = os.path.join(os.getenv("HOMEDRIVE"), os.getenv("HOMEPATH") , "Desktop")

def obj_to_photoshop(context):
    #ファイル名の作成
    source_str = 'abcdefghijklmnopqrstuvwxyz'
    f_name = time.strftime("%y%m%d%H%M") + "".join([random.choice(source_str) for x in range(3)]) + ".obj"
    obj_path  = os.path.join(tmp_dir,f_name)
    #Objデータの出力
    scale = 1.0
    obj_export(scale, obj_path)
    #javascriptの実行
    subprocess.call(["CScript", VB_Hub, jscript, obj_path, "//nologo"])

def obj_export(scale, obj_path):
    if len(bpy.context.selected_objects) == 0: return()
    from mathutils import Matrix
    global_matrix = (Matrix.Scale(scale, 4) *
                     axis_conversion(to_forward= '-Z',
                                     to_up='Y',
                                     ).to_4x4())
    export_obj.save(bpy.context,
                         filepath      = obj_path,
                         global_matrix = global_matrix,
                         use_triangles = True,
                         use_selection = True,
                         path_mode     = 'STRIP')




###################################################
class DupulicateActionAtCurrentTime(bpy.types.Operator):
    '''selected 3D Object to Photoshop 3D Layer'''
    bl_idname = "action.obj_to_photoshop"
    bl_label = "3D Object to Photoshop 3D Layer"
    def execute(self, context):
         obj_to_photoshop(context)
         return {'FINISHED'}
# メニューの構築処理
def menu_func(self, context):
    self.layout.operator("action.obj_to_photoshop", 
           text="選択形状をPhotoshopへ" )
# アドオン有効化時の処理
def register():
    bpy.utils.register_module(__name__)
    bpy.types.VIEW3D_MT_object.prepend(menu_func)
# アドオン無効化時の処理
def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.VIEW3D_MT_object.remove(menu_func)
 
if __name__ == "__main__":
    register()
##########################################################
標準アドオンのWavefrontObj書き出しを利用してデータを書き出して
javascriptを動かすための中継ソフト VB_Hub.vbs に受け渡しています
上記の2つとのスクリプトとVB_Hub.vbsを入れたフォルダをaddonフォルダに作成するか、
ユーザー設定のアドオンタブの「ファイルからインストール」から下記のzipを読み込んでアドオンを有効にすることで使えます

Blender上で選択している形状をOBJで書き出す方式にしてます
多分Obj以外の形式でも同様にできるかと思いますが、今回はこの形式を利用しました。
アドオンとして利用しやすいように今回のzipファイルをここにアップしておきます

いかがでしょうか

2018年5月11日金曜日

選択した形状に関連するデータの名前をそろえる

Blenderでは個々のオブジェクトの名前の他
オブジェクトのポリゴンやマテリアル、テクスチャ、画像までがファイル内で名前をつけて管理されています。
映像等、レンダリング画像が最終出力になる場合には問題がないのですが、
ゲームのデータ等では名称を統一する必要があったりします

命名規則は様々ですが 今使ってる命名パターンでアドオンを作成してみました
bl_info = {
    "name": "リソースの名前を統一",
    "author": "Yukimi",
    "version": (3, 0),
    "blender": (2, 60, 0),
    "location": "プロパティ > オブジェクト",
    "description": "アクティブオブジェクトのデータ、マテリアルの名称を統一",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "Object"
}
import bpy
from bpy.props import StringProperty

class CalibratePanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "uniform name Panel"
    bl_idname = "OBJECT_uniform_name"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "object"
    
    def draw(self, context):
        
        layout = self.layout
        scene = context.scene
        obj = context.object
        row = layout.row()
        row.label(text="Active object is: " + obj.name)
        row = layout.row()
        row.operator("ab.get_f_name")
        row = layout.row()
        row.prop(scene, "uni_basename")
        row = layout.row()
        row.operator("ab.uniform_name")

#名称の変更
class RenameButton(bpy.types.Operator):
    bl_idname = "ab.uniform_name"
    bl_label = "名前の統一化"
    bl_options = {'REGISTER', 'UNDO'}
    def execute(self, context):
        scene = context.scene
        if not "uni_basename" in scene: pass
        ID = scene.uni_basename
        obj = context.object
        #オブジェクトの名前を揃える
        obj.name = ID + "_Obj"
        obj.data.name = ID + "_Mesh"
        if obj.data.uv_textures:
            obj.data.uv_textures.active.name = ID + "_UV" #アクティブなもののみ変更
        if obj.material_slots:
            for  i,mat_slot in enumerate(obj.material_slots):
                mat = mat_slot.material
                if not mat: continue
                mat.name= ID + "_Mtl_%02d" % i
                if mat.active_texture:
                    tex = mat.active_texture
                    tex.name = ID + "_Tex_%02d" % i
                    if tex.type == 'IMAGE' and tex.image:
                        tex.image.name = ID + "_Img_%02d" % i
        return{'FINISHED'}

#ファイル名を取得してテキスト入力の値を設定
class fpathButtonButton(bpy.types.Operator):
    bl_idname = "ab.get_f_name"
    bl_label = "ファイル名の取得"
    def execute(self, context):
        import os
        base_name = os.path.basename(bpy.context.blend_data.filepath)
        f_name = os.path.splitext(base_name)[0]
        context.scene.uni_basename = f_name
        return{'FINISHED'}

def init_props():
    scene = bpy.types.Scene
    scene.uni_basename = StringProperty(name = "揃える名前", default= "" )


#プロパティの削除
def crear_props():
    scene = bpy.types.Scene
    del uni_basename


def register():
    bpy.utils.register_module(__name__)
    init_props()


def unregister():
    bpy.utils.unregister_module(__name__)
    crear_props()


if __name__ == "__main__":
    register()

プロパティのオブジェクトタブに追加する形になっています
現状はUVはアクティブなもののみ末尾に_UVをつけ、マテリアルに通し番号をつけ、 テクスチャ、イメージ名はマテリアルの通し番号で命名するようにしています
名称の変更部分はシンプルなので 個々の案件によって調整できるかと思います
いかがでしょうか