CM3D2 Converter.cm3d2_data
CM3D2/COM3D2用のデータ構造を扱うデータクラス
1"""CM3D2/COM3D2用のデータ構造を扱うデータクラス""" 2import bpy 3import copy 4import struct 5from . import common 6from . import compat 7from .translations.pgettext_functions import * 8 9SHADER_NAMES_CM3D2 = [ 10 'CM3D2/Toony_Lighted', 11 'CM3D2/Toony_Lighted_Hair', 12 'CM3D2/Toony_Lighted_Trans', 13 'CM3D2/Toony_Lighted_Trans_NoZ', 14 'CM3D2/Toony_Lighted_Outline', 15 'CM3D2/Toony_Lighted_Outline_Trans', 16 'CM3D2/Toony_Lighted_Hair_Outline', 17 'CM3D2/Lighted_Trans', 18 'CM3D2/Lighted', 19 'Unlit/Texture', 20 'Unlit/Transparent', 21 'CM3D2/Mosaic', 22 'CM3D2/Man', 23 'Diffuse', 24 'Transparent/Diffuse', 25 'CM3D2_Debug/Debug_CM3D2_Normal2Color', 26] 27SHADER_NAMES_COM3D2 = [ 28 'CM3D2/Toony_Lighted', 29 'CM3D2/Toony_Lighted_Hair', 30 'CM3D2/Toony_Lighted_Trans', 31 'CM3D2/Toony_Lighted_Trans_NoZ', 32 'CM3D2/Toony_Lighted_Trans_NoZTest', 33 'CM3D2/Toony_Lighted_Outline', 34 'CM3D2/Toony_Lighted_Outline_Tex', 35 'CM3D2/Toony_Lighted_Hair_Outline', 36 # 'CM3D2/Toony_Lighted_Hair_Outline_Tex', 37 'CM3D2/Toony_Lighted_Outline_Trans', 38 'CM3D2/Toony_Lighted_Cutout_AtC', 39 'CM3D2/Lighted_Cutout_AtC', 40 'CM3D2/Lighted_Trans', 41 'CM3D2/Lighted', 42 'Unlit/Texture', 43 'Unlit/Transparent', 44 'CM3D2/Mosaic', 45 'CM3D2/Man', 46 'Diffuse', 47 'Transparent/Diffuse', 48 'CM3D2_Debug/Debug_CM3D2_Normal2Color', 49] 50TOON_TEXES = [ 51 'NoTex', 'ToonBlueA1', 'ToonBlueA2', 'ToonBrownA1', 'ToonGrayA1', 52 'ToonGreenA1', 'ToonGreenA2', 'ToonGreenA3', 53 'ToonOrangeA1', 54 'ToonPinkA1', 'ToonPinkA2', 'ToonPurpleA1', 55 'ToonRedA1', 'ToonRedA2', 56 'ToonRedmmm1', 'ToonRedmm1', 'ToonRedm1', 57 'ToonYellowA1', 'ToonYellowA2', 'ToonYellowA3', 'ToonYellowA4', 58 'ToonFace', # 'ToonFace002', 59 'ToonSkin', # 'ToonSkin002', 60 'ToonBlackA1', 61 'ToonFace_shadow', 62 'ToonDress_shadow', 63 'ToonSkin_Shadow', 64 'ToonBlackMM1', 'ToonBlackM1', 'ToonGrayMM1', 'ToonGrayM1', 65 'ToonPurpleMM1', 'ToonPurpleM1', 66 'ToonSilverA1', 67 'ToonDressMM_Shadow', 'ToonDressM_Shadow', 68] 69PROP_DESC = { 70 '_MainTex': ["面の色を決定するテクスチャを指定。", "普段テスクチャと呼んでいるものは基本コレです。", "テクスチャパスは適当でも動きます。", "しかし、テクスチャ名はきちんと決めましょう。"], 71 '_ToonRamp': ["暗い部分に乗算するグラデーション画像を指定します。"], 72 '_ShadowTex': ["陰部分の面の色を決定するテクスチャを指定。", "「_ShadowRateToon」で範囲を指定します。"], 73 '_ShadowRateToon': ["「_ShadowTex」を有効にする部分を指定します。", "黒色で有効、白色で無効。"], 74 '_OutlineTex': ["アウトラインを表現するためのテクスチャを指定。(未確認)"], 75 '_OutlineToonRamp': ["_OutlineTexの暗い部分に乗算するグラデーション画像を指定します。(未確認)"], 76 '_Color': ["面の色を指定。", "白色で無効。基本的に白色で良いでしょう。"], 77 '_ShadowColor': ["影の色を指定。白色で無効。", "別の物体に遮られてできた「影」の色です。"], 78 '_RimColor': ["リムライトの色を指定。", "リムライトとは縁にできる光の反射のことです。"], 79 '_OutlineColor': ["輪郭線の色を指定。", "黒にするか、テクスチャの明度を", "落としたものを指定するとより良いでしょう。"], 80 '_Shininess': ["スペキュラーの強さを指定。0.0~1.0で指定。", "スペキュラーとは面の角度と光源の角度によって", "できるハイライトのことです。", "金属、皮、ガラスなどに使うと良いでしょう。"], 81 '_OutlineWidth': ["輪郭線の太さを指定。", "0.002は太め、0.001は細め。"], 82 '_RimPower': ["リムライトの強さを指定。", "この値は10以上なことも多いです。", "0に近い値だと正常に表示されません。"], 83 '_RimShift': ["リムライトの幅を指定。", "0.0~1.0で指定。0.5でもかなり強い。"], 84 '_RenderTex': ["モザイクシェーダーにある設定値。", "特に設定の必要なし。"], 85 '_FloatValue1': ["モザイクの粗さ"], 86 '_Cutoff': ["アルファのカットオフ値。", "アルファ値がこの値より大きい部分だけがレンダリングされる"], 87 # '_Cutout': ["アルファのカットオフ値。", "アルファ値がこの値より大きい部分だけがレンダリングされる"], 88 '_ZTest': ["デプステストの実行方法を指定する。"] 89} 90PROPS = { 91 '_MainTex': { 92 'type': 'tex', 93 'desc': ["面の色を決定するテクスチャを指定。", "普段テスクチャと呼んでいるものは基本コレです。", "テクスチャパスは適当でも動きます。", "しかし、テクスチャ名はきちんと決めましょう。"], 94 }, 95 '_ToonRamp': { 96 'type': 'tex', 97 'desc': ["暗い部分に乗算するグラデーション画像を指定します。"], 98 }, 99 '_ShadowTex': { 100 'type': 'tex', 101 'desc': ["陰部分の面の色を決定するテクスチャを指定。", "「_ShadowRateToon」で範囲を指定します。"], 102 }, 103 '_ShadowRateToon': { 104 'type': 'tex', 105 'desc': ["「_ShadowTex」を有効にする部分を指定します。", "黒色で有効、白色で無効。"], 106 }, 107 '_OutlineTex': { 108 'type': 'tex', 109 'desc': ["アウトラインを表現するためのテクスチャを指定。"], 110 }, 111 '_OutlineToonRamp': { 112 'type': 'tex', 113 'desc': ["_OutlineTexの暗い部分に乗算するグラデーション画像を指定します。"], 114 }, 115 '_RenderTex': { 116 'type': 'tex', 117 'desc': ["モザイクシェーダーにある設定値。", "特に設定の必要なし。"], 118 }, 119 '_Color': { 120 'type': 'col', 121 'desc': ["面の色を指定。", "白色で無効。基本的に白色で良いでしょう。"], 122 }, 123 '_ShadowColor': { 124 'type': 'col', 125 'desc': ["影の色を指定。白色で無効。", "別の物体に遮られてできた「影」の色です。"], 126 }, 127 '_RimColor': { 128 'type': 'col', 129 'desc': ["リムライトの色を指定。", "リムライトとは縁にできる光の反射のことです。"], 130 }, 131 '_OutlineColor': { 132 'type': 'col', 133 'desc': ["輪郭線の色を指定。", "黒にするか、テクスチャの明度を", "落としたものを指定するとより良いでしょう。"], 134 }, 135 '_Shininess': { 136 'type': 'f', 137 'desc': ["スペキュラーの強さを指定。0.0~1.0で指定。", "スペキュラーとは面の角度と光源の角度によって", "できるハイライトのことです。", "金属、皮、ガラスなどに使うと良いでしょう。"], 138 'presets': [0, 0.1, 0.5, 1, 5], 139 # 'default': 0, 'step': 1, 'precision': 2, 140 # 'min': -100, 'soft_min': -100, 141 # 'max': 100, 'soft_max': 100, 142 }, 143 '_OutlineWidth': { 144 'type': 'f', 145 'desc': ["輪郭線の太さを指定。", "0.002は太め、0.001は細め。"], 146 'presets': [0.0001, 0.001, 0.0015, 0.002], 147 'dispExact': True, 148 # 'default': 0, 'step': 0.001, 'precision': 4, 149 # 'min': 0, 'soft_min': 1, 150 # 'max': 0, 'soft_max': 1, 151 }, 152 '_RimPower': { 153 'type': 'f', 154 'desc': ["リムライトの強さを指定。", "この値は10以上なことも多いです。", "0に近い値だと正常に表示されません。"], 155 'presets': [0, 25, 50, 100], # 1, 10, 20, 30 156 # 'default': 0, 'step': 1, 'precision': 2, 157 # 'min': -100, 'soft_min': -100, 158 # 'max': 100, 'soft_max': 100, 159 }, 160 '_RimShift': { 161 'type': 'f', 162 'desc': ["リムライトの幅を指定。", "0.0~1.0で指定。0.5でもかなり強い。"], 163 'presets': [0, 0.25, 0.5, 0.75, 1], # 0.0, 0.25, 0.5, 0.75, 1.0 164 # 'default': 0, 'step': 1, 'precision': 2, 165 # 'min': -100, 'soft_min': -100, 166 # 'max': 100, 'soft_max': 100, 167 }, 168 '_FloatValue1': { 169 'type': 'f', 170 'desc': ["モザイクの粗さ"], 171 'presets': [0, 100, 200], 172 # 'default': 0, 'step': 1, 'precision': 2, 173 # 'min': -100, 'soft_min': -100, 174 # 'max': 100, 'soft_max': 100, 175 }, 176 '_Cutoff': { 177 'type': 'f', 178 'desc': ["アルファのカットオフ値。", "アルファ値がこの値より大きい部分だけがレンダリングされる"], 179 'presets': [0, 0.1, 0.5, 1, 5], 180 # 'default': 0, 'step': 1, 'precision': 2, 181 # 'min': -100, 'soft_min': -100, 182 # 'max': 100, 'soft_max': 100, 183 }, 184 # '_Cutout': ["アルファのカットオフ値。", "アルファ値がこの値より大きい部分だけがレンダリングされる"], 185 '_ZTest': { 186 'type': 'f', 187 'desc': ["デプステストの実行方法を指定する。"], 188 'disableSlider': True, 189 'preset_enums': [ 190 (0, "Disabled:0"), (1, "Never:1"), (2, "Less:2"), (3, "Equal:3"), (4, "LessEqual:4"), 191 (5, "Greater:5"), (6, "NotEqual:6"), (7, "GreaterEqual:7"), (8, "Always:8") 192 ], 193 }, 194 '_FloatValue2': { 195 'type': 'f', 196 'presets': [-15, 0, 1, 15], 197 # 'default': 0, 'step': 1, 'precision': 2, 198 # 'min': -100, 'soft_min': -100, 199 # 'max': 100, 'soft_max': 100, 200 }, 201 '_FloatValue3': { 202 'type': 'f', 203 'presets': [0, 0.1, 0.5, 1], 204 # 'default': 0, 'step': 1, 'precision': 2, 205 # 'min': -100, 'soft_min': -100, 206 # 'max': 100, 'soft_max': 100, 207 }, 208 '_ZTest2': { 209 'type': 'f', 210 'disableSlider': True, 211 'preset_enums': [(0, "0"), (1, "1")], 212 # 'default': 0, 'step': 1, 'precision': 2, 213 # 'min': -100, 'soft_min': -100, 214 # 'max': 100, 'soft_max': 100, 215 }, 216 '_ZTest2Alpha': { 217 'type': 'f', 218 'presets': [0, 0.8, 1], 219 # 'default': 0, 'step': 1, 'precision': 2, 220 # 'min': -100, 'soft_min': -100, 221 # 'max': 100, 'soft_max': 100, 222 } 223} 224 225 226class DataHandler: 227 _instance = None 228 229 def __new__(cls): 230 if cls._instance is None: 231 cls._instance = super().__new__(cls) 232 return cls._instance 233 234 @classmethod 235 def instance(cls): 236 if cls._instance is None: 237 cls._instance = DataHandler() 238 239 return cls._instance 240 241 def __init__(self): 242 diffuse = { 243 'type_name': "リアル", 244 'icon': 'BRUSH_CLAY_STRIPS', 245 'shader2': 'Legacy Shaders__Diffuse', 246 'tex_list': ['_MainTex'], 247 'col_list': ['_Color'], 248 'f_list': [], 249 } 250 trans_diffuse = { 251 'type_name': "リアル 透過", 252 'icon': 'BRUSH_TEXFILL', 253 'shader2': 'Legacy Shaders__Transparent__Diffuse', 254 'tex_list': ['_MainTex'], 255 'col_list': ['_Color'], 256 'f_list': [], 257 } 258 259 self.shader_dict = { 260 'CM3D2/Toony_Lighted': { 261 'type_name': "トゥーン", 262 'icon': compat.icon('SHADING_SOLID'), 263 'shader2': 'CM3D2__Toony_Lighted', 264 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 265 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 266 'f_list': ['_Shininess', '_RimPower', '_RimShift'] 267 }, 268 'CM3D2/Toony_Lighted_Hair': { 269 'type_name': "トゥーン 髪", 270 'icon': 'PARTICLEMODE', 271 'shader2': 'CM3D2__Toony_Lighted_Hair', 272 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'], 273 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 274 'f_list': ['_Shininess', '_RimPower', '_RimShift', '_HiRate', '_HiPow'] 275 }, 276 'CM3D2/Toony_Lighted_Trans': { 277 'type_name': "トゥーン 透過", 278 'icon': compat.icon('SHADING_WIRE'), 279 'shader2': 'CM3D2__Toony_Lighted_Trans', 280 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 281 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 282 'f_list': ['_Shininess', '_Cutoff', '_RimPower', '_RimShift'], 283 }, 284 'CM3D2/Toony_Lighted_Trans_NoZ': { 285 'type_name': "トゥーン 透過 NoZ", 286 'icon': 'DRIVER', 287 'shader2': 'CM3D2__Toony_Lighted_Trans_NoZ', 288 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 289 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 290 'f_list': ['_Shininess', '_RimPower', '_RimShift'], 291 }, 292 'CM3D2/Toony_Lighted_Trans_NoZTest': { 293 'type_name': "トゥーン 透過 NoZTest", 294 'icon': 'ANIM_DATA', 295 'shader2': 'CM3D2__Toony_Lighted_Trans_NoZTest', 296 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 297 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 298 'f_list': ['_Shininess', '_RimPower', '_RimShift', '_ZTest', '_ZTest2', '_ZTest2Alpha'], 299 }, 300 'CM3D2/Toony_Lighted_Outline': { 301 'type_name': "トゥーン 輪郭線", 302 'icon': 'ANTIALIASED', 303 'shader2': 'CM3D2__Toony_Lighted_Outline', 304 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 305 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 306 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'], 307 }, 308 'CM3D2/Toony_Lighted_Outline_Tex': { 309 'type_name': "トゥーン 輪郭線 Tex", 310 'icon': 'MATSPHERE', 311 'shader2': 'CM3D2__Toony_Lighted_Outline_Tex', 312 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_OutlineTex', '_OutlineToonRamp'], 313 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 314 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'], 315 }, 316 'CM3D2/Toony_Lighted_Hair_Outline': { 317 'type_name': "トゥーン 輪郭線 髪", 318 'icon': 'PARTICLEMODE', 319 'shader2': 'CM3D2__Toony_Lighted_Hair_Outline', 320 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'], 321 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 322 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift', '_HiRate', '_HiPow'], 323 }, 324 # 'CM3D2/Toony_Lighted_Hair_Outline_Tex': { 325 # 'type_name': "トゥーン 輪郭線 Tex 髪", 326 # 'icon': 'PARTICLEMODE', 327 # 'shader2': 'CM3D2__Toony_Lighted_Hair_Outline_Tex', 328 # 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'], 329 # 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 330 # 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift', '_HiRate', '_HiPow'], 331 # }, 332 'CM3D2/Toony_Lighted_Outline_Trans': { 333 'type_name': "トゥーン 輪郭線 透過", 334 'icon': 'PROP_OFF', 335 'shader2': 'CM3D2__Toony_Lighted_Outline_Trans', 336 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 337 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 338 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'], 339 }, 340 'CM3D2/Toony_Lighted_Cutout_AtC': { 341 'type_name': "トゥーン Cutout", 342 'icon': 'IPO_BACK', 343 'shader2': 'CM3D2__Toony_Lighted_Cutout_AtC', 344 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 345 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 346 'f_list': ['_Shininess', '_RimPower', '_RimShift', '_Cutoff'], 347 }, 348 'CM3D2/Lighted_Trans': { 349 'type_name': "トゥーン無し 透過", 350 'icon': compat.icon('VIS_SEL_01'), 351 'shader2': 'CM3D2__Lighted_Trans', 352 'tex_list': ['_MainTex'], 353 'col_list': ['_Color', '_ShadowColor'], 354 'f_list': ['_Shininess'], 355 }, 356 'CM3D2/Lighted': { 357 'type_name': "トゥーン無し", 358 'icon': compat.icon('VIS_SEL_11'), 359 'shader2': 'CM3D2__Lighted', 360 'tex_list': ['_MainTex'], 361 'col_list': ['_Color', '_ShadowColor'], 362 'f_list': ['_Shininess'], 363 }, 364 'CM3D2/Lighted_Cutout_AtC': { 365 'type_name': "トゥーン無し Cutout", 366 'icon': 'IPO_BACK', 367 'shader2': 'CM3D2__Lighted_Cutout_AtC', 368 'tex_list': ['_MainTex'], 369 'col_list': ['_Color', '_ShadowColor'], 370 'f_list': ['_Shininess', '_Cutoff'], 371 }, 372 'Unlit/Texture': { 373 'type_name': "発光", 374 'icon': 'PARTICLES', 375 'shader2': 'Unlit__Texture', 376 'tex_list': ['_MainTex'], 377 'col_list': [], # ['_Color'], 378 'f_list': [], 379 }, 380 'Unlit/Transparent': { 381 'type_name': "発光 透過", 382 'icon': 'MOD_PARTICLES', 383 'shader2': 'Unlit__Texture', 384 'tex_list': ['_MainTex'], 385 'col_list': [], # ['_Color'], 386 'f_list': [], 387 }, 388 'CM3D2/Mosaic': { 389 'type_name': "モザイク", 390 'icon': 'ALIASED', 391 'shader2': 'CM3D2__Mosaic', 392 'tex_list': ['_RenderTex'], 393 'col_list': [], 394 'f_list': ['_FloatValue1'], 395 }, 396 'CM3D2/Man': { 397 'type_name': "ご主人様", 398 'icon': 'ARMATURE_DATA', 399 'shader2': 'CM3D2__Man', 400 'tex_list': [], 401 'col_list': ['_Color'], 402 'f_list': ['_FloatValue2', '_FloatValue3'], 403 }, 404 'Diffuse': diffuse, 405 'Legacy Shaders/Diffuse': diffuse, 406 'Transparent/Diffuse': trans_diffuse, 407 'Legacy Shaders/Transparent/Diffuse': trans_diffuse, 408 'CM3D2_Debug/Debug_CM3D2_Normal2Color': { 409 'type_name': "法線", 410 'icon': compat.icon('NORMALS_VERTEX'), 411 'shader2': 'CM3D2_Debug__Debug_CM3D2_Normal2Color', 412 'tex_list': [], 413 'col_list': ['_Color'], # , '_RimColor', '_OutlineColor', '_SpecColor'], 414 'f_list': [] # ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'], 415 }, 416 } 417 418 @classmethod 419 def create_shader_items(cls) -> list: 420 _inst = cls.instance() 421 items = [] 422 idx = 0 423 for name in SHADER_NAMES_CM3D2: 424 item = _inst.shader_dict.get(name) 425 if item: 426 items.append((name, item['type_name'], '', item['icon'], idx)) 427 idx += 1 428 return items 429 430 @classmethod 431 def create_comshader_items(cls) -> list: 432 _inst = cls.instance() 433 items = [] 434 idx = 0 435 for name in SHADER_NAMES_COM3D2: 436 item = _inst.shader_dict.get(name) 437 if item: 438 items.append((name, item['type_name'], '', item['icon'], idx)) 439 idx += 1 440 return items 441 442 @classmethod 443 def get_shader_prop(cls, name): 444 _inst = cls.instance() 445 shader_prop = _inst.shader_dict.get(name) 446 if shader_prop: 447 return shader_prop 448 449 return {'type_name': '不明', 'icon': 'NONE'} 450 451Handler = DataHandler.instance() 452 453 454class Material(): 455 """マテリアルデータクラス""" 456 def __init__(self): 457 self.version = 1000 458 self.name1 = None 459 self.name2 = None 460 self.shader1 = None 461 self.shader2 = None 462 463 self.tex_list = [] # prop_name, (tex_name, tex_path, trans[2], scale[2]) 464 self.col_list = [] # prop_name, col[4] 465 self.f_list = [] # prop_name, f 466 self.range_list = [] # prop_name, col[4] 467 468 self.custom_list = dict() 469 470 def sort(self): 471 self.tex_list = sorted(self.tex_list, key=lambda item: item[0]) 472 self.col_list = sorted(self.col_list, key=lambda item: item[0]) 473 self.f_list = sorted(self.f_list, key=lambda item: item[0]) 474 475 @property 476 def name(self): 477 return self.name2 or self.name1 478 479 def read(self, reader, read_header=True): 480 if read_header: 481 header = common.read_str(reader) 482 if header != 'CM3D2_MATERIAL': 483 raise common.CM3D2ImportException(f_tip_("mateファイルではありません。ヘッダ:{}", header)) 484 self.version = struct.unpack('<i', reader.read(4))[0] 485 self.name1 = common.read_str(reader) 486 self.name2 = common.read_str(reader) 487 488 self.shader1 = common.read_str(reader) 489 self.shader2 = common.read_str(reader) 490 491 peeked = reader.peek()[0] 492 print(f"type({peeked}) = {type(peeked)}") 493 if self.version >= 2102 and (peeked in (0, 1)): # CR Edit Mode 494 cr_unknown_float_count = struct.unpack('<B', reader.read(1))[0] 495 for i in range(cr_unknown_float_count): 496 # CR TODO 497 self.custom_list[f'cr_unknown_float:{i:03d}'] = struct.unpack('<f', reader.read(4)) 498 499 for i in range(99999): 500 prop_type = common.read_str(reader) 501 if prop_type == 'tex': 502 prop_name = common.read_str(reader) 503 sub_type = common.read_str(reader) 504 if sub_type == 'tex2d': 505 tex_name = common.read_str(reader) 506 tex_path = common.read_str(reader) 507 offset = struct.unpack('<2f', reader.read(4 * 2)) 508 scale = struct.unpack('<2f', reader.read(4 * 2)) 509 tex_item = [prop_name, tex_name, tex_path, offset, scale] 510 else: 511 tex_item = [prop_name] 512 self.tex_list.append(tex_item) 513 514 elif prop_type == 'col': 515 prop_name = common.read_str(reader) 516 col = struct.unpack('<4f', reader.read(4 * 4)) 517 self.col_list.append([prop_name, col]) 518 519 elif prop_type == 'f' or prop_type == 'range': # 'range' from CR Edit 520 prop_name = common.read_str(reader) 521 f = struct.unpack('<f', reader.read(4))[0] 522 self.f_list.append([prop_name, f]) 523 524 # CR TODO 525 elif prop_type == 'keyword': 526 prop_name = common.read_str(reader) 527 keyword_f = struct.unpack('<f', reader.read(4))[0] 528 print(keyword_f, type(keyword_f)) 529 self.custom_list.setdefault('keyword', dict())[prop_name] = keyword_f #.append([prop_name, keyword_f]) 530 531 # CR TODO 532 elif prop_type == '_ALPHAPREMULTIPLY_ON': 533 alpha_bool = struct.unpack('<?', reader.read(1))[0] 534 self.custom_list['_ALPHAPREMULTIPLY_ON'] = alpha_bool 535 536 elif prop_type == 'end': 537 break 538 else: 539 raise common.CM3D2ImportException(f_tip_("Materialプロパティに未知の設定値タイプ({prop})が見つかりました。", prop=prop_type)) 540 541 def write(self, writer, write_header=True): 542 if write_header: 543 common.write_str(writer, 'CM3D2_MATERIAL') 544 writer.write(struct.pack('<i', self.version)) 545 common.write_str(writer, self.name1) 546 547 common.write_str(writer, self.name2) 548 common.write_str(writer, self.shader1) 549 common.write_str(writer, self.shader2) 550 551 for tex_item in self.tex_list: 552 common.write_str(writer, 'tex') 553 common.write_str(writer, tex_item[0]) # prop_name 554 555 if len(tex_item) < 2: 556 common.write_str(writer, 'null') 557 else: 558 common.write_str(writer, 'tex2d') 559 common.write_str(writer, tex_item[1]) # tex_name 560 common.write_str(writer, tex_item[2]) # tex_path 561 trans = tex_item[3] 562 writer.write(struct.pack('<2f', trans[0], trans[1])) 563 scale = tex_item[4] 564 writer.write(struct.pack('<2f', scale[0], scale[1])) 565 566 for col_item in self.col_list: 567 common.write_str(writer, 'col') 568 common.write_str(writer, col_item[0]) # prop_name 569 570 col = col_item[1] 571 writer.write(struct.pack('<4f', col[0], col[1], col[2], col[3])) 572 573 for f_item in self.f_list: 574 common.write_str(writer, 'f') 575 common.write_str(writer, f_item[0]) # prop_name 576 577 writer.write(struct.pack('<f', f_item[1])) 578 579 common.write_str(writer, 'end') 580 581 def to_text(self): 582 output_text = str(self.version) + "\n" 583 output_text += self.name1 + "\n" 584 output_text += self.name2 + "\n" 585 output_text += self.shader1 + "\n" 586 output_text += self.shader2 + "\n" 587 output_text += "\n" 588 589 for tex_item in self.tex_list: 590 output_text += 'tex\n' 591 output_text += "\t" + tex_item[0] + "\n" # prop_name 592 593 if len(tex_item) < 2: 594 output_text += '\tnull\n' 595 else: 596 output_text += '\ttex2d\n' 597 output_text += "\t" + tex_item[1] + "\n" # tex_name 598 output_text += "\t" + tex_item[2] + "\n" # tex_path 599 trans = tex_item[3] 600 scale = tex_item[4] 601 output_text += "\t" + " ".join([str(trans[0]), str(trans[1]), str(scale[0]), str(scale[1])]) + "\n" 602 603 for col_item in self.col_list: 604 output_text += 'col\n' 605 output_text += "\t" + col_item[0] + "\n" # prop_name 606 col = col_item[1] 607 output_text += "\t" + " ".join([str(col[0]), str(col[1]), str(col[2]), str(col[3])]) + "\n" # prop_name 608 609 for f_item in self.f_list: 610 output_text += 'f\n' 611 output_text += "\t" + f_item[0] + "\n" # prop_name 612 f = f_item[1] 613 output_text += "\t" + str(f) + "\n" 614 615 return output_text 616 617 def to_json(self): 618 import json 619 return json.dumps(self.__dict__, ensure_ascii=False, indent=2) 620 621 def from_dict(self, data): 622 self.name1 = data['name1'] 623 self.name2 = data['name2'] 624 self.version = data['version'] 625 self.shader1 = data['shader1'] 626 self.shader2 = data['shader2'] 627 628 self.tex_list = data['tex_list'] # prop_name, (tex_name, tex_path, trans[2], scale[2]) 629 self.col_list = data['col_list'] # prop_name, col[4] 630 self.f_list = data['f_list'] # prop_name, f 631 632 633class MaterialHandler: 634 635 @classmethod 636 def parse_tex_node(cls, node, remove_serial=True): 637 node_name = common.remove_serial_number(node.name, remove_serial) 638 tex_item = [node_name] 639 640 try: 641 img = node.image 642 except: 643 raise common.CM3D2ImportException('Materialプロパティのtexタイプの設定値取得に失敗しました。') 644 645 if img: 646 tex_name = common.remove_serial_number(img.name, remove_serial) 647 tex_name = common.re_png.sub(r'\1', tex_name) # 拡張子を除外 648 tex_item.append(tex_name) 649 650 if 'cm3d2_path' in img: 651 path = img['cm3d2_path'] 652 else: 653 path = bpy.path.abspath(img.filepath) 654 path = common.to_cm3d2path(path) 655 656 tex_item.append(path) 657 tex_map = node.texture_mapping 658 tex_trans = tex_map.translation[:2] 659 tex_scale = tex_map.scale[:2] 660 tex_item.append(tex_trans) 661 tex_item.append(tex_scale) 662 return tex_item 663 664 @classmethod 665 def parse_col_node(cls, node, remove_serial=True): 666 node_name = common.remove_serial_number(node.name, remove_serial) 667 col = node.outputs[0].default_value 668 return [node_name, col[:4]] 669 670 @classmethod 671 def parse_f_node(cls, node, remove_serial=True): 672 node_name = common.remove_serial_number(node.name, remove_serial) 673 f = node.outputs[0].default_value 674 return [node_name, f] 675 676 @classmethod 677 def read(cls, reader, read_header=True, version=None): 678 if not read_header and version == None: 679 raise ValueError(f_tip_("The argument 'version' is required when 'read_header' is False for MaterialHandler.read()")) 680 mat_data = Material() 681 if version: 682 mat_data.version = version 683 mat_data.read(reader, read_header) 684 685 return mat_data 686 687 @classmethod 688 def get_shader_prop_dynamic(cls, mate, ): 689 lists = { 'VALUE':'f_list', 'RGB':'col_list', 'TEX_IMAGE':'tex_list' } 690 shader_prop = copy.deepcopy( DataHandler.get_shader_prop(mate.get('shader1')) ) 691 for node in mate.node_tree.nodes: 692 if node.name[0] != '_': 693 continue 694 list_name = lists.get(node.type) 695 if not list_name: 696 continue 697 prop_list = shader_prop.get(list_name) 698 if not prop_list: 699 prop_list = list() 700 shader_prop[list_name] = prop_list 701 if node.name in prop_list: 702 continue 703 prop_list.append(node.name) 704 return shader_prop 705 706 @classmethod 707 def parse_mate(cls, mate, remove_serial=True): 708 mat_data = Material() 709 710 mate_name = common.remove_serial_number(mate.name, remove_serial) 711 mat_data.name1 = mate_name.lower() 712 mat_data.name2 = mate_name 713 mat_data.shader1 = mate['shader1'] 714 mat_data.shader2 = mate['shader2'] 715 716 nodes = mate.node_tree.nodes 717 shader_prop = cls.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(mat_data.shader1) 718 if shader_prop: 719 for node_name in shader_prop['tex_list']: 720 node = nodes.get(node_name) 721 if node and node.type == 'TEX_IMAGE': 722 tex_item = cls.parse_tex_node(node, remove_serial) 723 mat_data.tex_list.append(tex_item) 724 725 for node_name in shader_prop['col_list']: 726 node = nodes.get(node_name) 727 if node and node.type == 'RGB': 728 col_item = cls.parse_col_node(node, remove_serial) 729 mat_data.col_list.append(col_item) 730 731 for node_name in shader_prop['f_list']: 732 node = nodes.get(node_name) 733 if node and node.type == 'VALUE': 734 f_item = cls.parse_f_node(node, remove_serial) 735 mat_data.f_list.append(f_item) 736 737 #for node in nodes: 738 # if not node.name.startswith('_'): 739 # continue 740 # 741 # node_type = node.type 742 # if node_type == 'TEX_IMAGE': 743 # tex_item = cls.parse_tex_node(node, remove_serial) 744 # mat_data.tex_list.append(tex_item) 745 # elif node_type == 'RGB': 746 # col_item = cls.parse_col_node(node, remove_serial) 747 # mat_data.col_list.append(col_item) 748 # 749 # elif node_type == 'VALUE': 750 # f_item = cls.parse_f_node(node, remove_serial) 751 # mat_data.f_list.append(f_item) 752 753 return mat_data 754 755 @classmethod 756 def parse_mate_old(cls, mate, remove_serial=True): 757 """material parser for blender-2.7x""" 758 mat_data = Material() 759 760 mate_name = common.remove_serial_number(mate.name, remove_serial) 761 mat_data.name1 = mate_name.lower() 762 mat_data.name2 = mate_name 763 mat_data.shader1 = mate['shader1'] 764 mat_data.shader2 = mate['shader2'] 765 766 for tindex, tslot in enumerate(mate.texture_slots): 767 if not tslot: 768 continue 769 770 tex = tslot.texture 771 node_name = common.remove_serial_number(tex.name, remove_serial) 772 # node_name = tslot.name 773 if mate.use_textures[tindex]: 774 tex_item = [node_name] 775 try: 776 img = tex.image 777 except: 778 raise Exception('Materialプロパティのtexタイプの設定値取得に失敗しました。') 779 780 if img: 781 tex_name = common.remove_serial_number(img.name, remove_serial) 782 tex_name = common.re_png.sub(r'\1', tex_name) # 拡張子を除外 783 tex_item.append(tex_name) 784 785 if 'cm3d2_path' in img: 786 path = img['cm3d2_path'] 787 else: 788 path = bpy.path.abspath(img.filepath) 789 path = common.to_cm3d2path(path) 790 791 tex_item.append(path) 792 tex_trans = tex.offset[:2] 793 tex_scale = tex.scale[:2] 794 tex_item.append(tex_trans) 795 tex_item.append(tex_scale) 796 797 mat_data.tex_list.append(tex_item) 798 elif tslot.use_rgb_to_intensity: 799 col = tex.color[:3] + [tslot.diffuse_color_factor] 800 mat_data.col_list.append([node_name, col[:4]]) 801 else: 802 f = tslot.diffuse_color_factor 803 mat_data.f_list.append([node_name, f]) 804 805 return mat_data 806 807 @classmethod 808 def parse_text(cls, text): 809 mat_data = Material() 810 lines = text.split('\n') 811 812 mat_data.version = int(lines[0]) 813 mat_data.name1 = lines[1] 814 mat_data.name2 = lines[2] 815 mat_data.shader1 = lines[3] 816 mat_data.shader2 = lines[4] 817 818 line_seek = 5 819 while line_seek < len(lines): 820 node_type = common.line_trim(lines[line_seek]) 821 if not node_type: 822 line_seek += 1 823 continue 824 if node_type == 'tex': 825 prop_name = common.line_trim(lines[line_seek + 1]) 826 sub_type = common.line_trim(lines[line_seek + 2]) 827 if sub_type == 'tex2d': 828 line_seek += 3 829 tex_name = common.line_trim(lines[line_seek]) 830 tex_path = common.line_trim(lines[line_seek + 1]) 831 tex_map = common.line_trim(lines[line_seek + 2]).split(' ') 832 for map_datum in range(len(tex_map)): 833 tex_map[map_datum] = float(tex_map[map_datum]) 834 mat_data.tex_list.append([prop_name, tex_name, tex_path, tex_map[:2], tex_map[2:]]) 835 else: 836 mat_data.tex_list.append([prop_name]) 837 838 line_seek += 3 839 840 elif node_type == 'col': 841 prop_name = common.line_trim(lines[line_seek + 1]) 842 tex_map = common.line_trim(lines[line_seek + 2]).split(' ') 843 for map_datum in range(len(tex_map)): 844 tex_map[map_datum] = float(tex_map[map_datum]) 845 846 mat_data.col_list.append([prop_name, tex_map[:]]) 847 line_seek += 3 848 849 elif node_type == 'f': 850 prop_name = common.line_trim(lines[line_seek + 1]) 851 val = float(common.line_trim(lines[line_seek + 2])) 852 853 mat_data.f_list.append([prop_name, val]) 854 line_seek += 3 855 else: 856 raise Exception('未知の設定値タイプが見つかりました。') 857 858 return mat_data 859 860 @classmethod 861 def parse_json(cls, text): 862 import json 863 mat_data = Material() 864 mat_data.from_dict(json.loads(text)) 865 866 return mat_data 867 868 @classmethod 869 def apply_to(cls, override, mate, mat_data, replace_tex=True): 870 mate['shader1'] = mat_data.shader1 871 mate['shader2'] = mat_data.shader2 872 873 if mate.use_nodes is False: 874 mate.use_nodes = True 875 876 nodes = mate.node_tree.nodes 877 nodes.clear() 878 # OUTPUT_MATERIAL, BSDF_PRINCIPLEDは消さない 879 #if len(nodes) > 2: 880 # clear_nodes(nodes) 881 882 for tex_item in mat_data.tex_list: 883 prop_name = tex_item[0] 884 885 if len(tex_item) < 2: 886 common.create_tex(override, mate, prop_name) 887 else: 888 tex_name = tex_item[1] 889 tex_path = tex_item[2] 890 tex_map = tex_item[3] + tex_item[4] 891 common.create_tex(override, mate, prop_name, tex_name, tex_path, tex_path, tex_map, replace_tex) 892 893 for col_item in mat_data.col_list: 894 prop_name = col_item[0] 895 col = col_item[1] 896 common.create_col(override, mate, prop_name, col) 897 898 for item in mat_data.f_list: 899 prop_name = item[0] 900 f = item[1] 901 common.create_float(override, mate, prop_name, f) 902 903 for key, value in mat_data.custom_list.items(): 904 mate[key] = value 905 if type(value) == bool: 906 rna_ui = mate.get('_RNA_UI', dict()) 907 rna_ui[key] = { 908 "default" : value, 909 "min" : 0 , 910 "max" : 1 , 911 "soft_min" : 0 , 912 "soft_max" : 1 , 913 } 914 mate['_RNA_UI'] = rna_ui 915 916 917 918 align_nodes(mate) 919 920 @classmethod 921 def apply_to_old(cls, override, mate, mat_data, replace_tex=True, decorate=True, skip_same_prop=True): 922 ob = override['active_object'] 923 me = ob.data 924 925 mate['shader1'] = mat_data.shader1 926 mate['shader2'] = mat_data.shader2 927 928 slot_index = 0 929 olds_slots = {} 930 read_texes = set() if skip_same_prop else None 931 932 for item in mat_data.tex_list: 933 prop_name = item[0] 934 if read_texes: 935 if prop_name in read_texes: 936 continue 937 read_texes.add(prop_name) 938 939 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'IMAGE') 940 slot.use_rgb_to_intensity = False 941 mate.use_textures[slot_index] = True 942 943 if len(item) > 4: 944 tex = slot.texture 945 tex_name = item[1] 946 tex_path = item[2] 947 if tex_name in read_texes: 948 continue 949 950 if tex_name != common.remove_serial_number(tex.image.name): 951 tex.image.name = tex_name 952 tex.image['cm3d2_path'] = tex_path 953 tex.image.filepath = tex.image['cm3d2_path'] 954 955 slot.offset = item[3] 956 slot.scale = item[4] 957 if replace_tex: 958 if common.replace_cm3d2_tex(tex.image, reload_path=True) and prop_name == '_MainTex': 959 for face in me.polygons: 960 if face.material_index == ob.active_material_index: 961 me.uv_textures.active.data[face.index].image = tex.image 962 slot_index += 1 963 964 for item in mat_data.col_list: 965 prop_name = item[0] 966 col = item[1] 967 968 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND') 969 970 mate.use_textures[slot_index] = False 971 slot.use_rgb_to_intensity = True 972 slot.color = col[:3] 973 slot.diffuse_color_factor = col[3] 974 975 slot_index += 1 976 977 for item in mat_data.f_list: 978 prop_name = item[0] 979 f = item[1] 980 981 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND') 982 983 mate.use_textures[slot_index] = False 984 slot.use_rgb_to_intensity = False 985 slot.diffuse_color_factor = f 986 987 slot_index += 1 988 989 # 存在しないスロットをクリア 990 for item_index in range(slot_index, len(mate.texture_slots)): 991 if mate.texture_slots[item_index]: 992 mate.texture_slots.clear(item_index) 993 994 # プレビューへの反映 995 for slot in mate.texture_slots: 996 if slot: 997 common.set_texture_color(slot) 998 999 if decorate: 1000 common.decorate_material(mate, decorate, me, ob.active_material_index) 1001 1002 @staticmethod 1003 def search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, tex_type): 1004 tex = None 1005 slot_item = mate.texture_slots[slot_index] 1006 slot_name = slot_item.name if slot_item else '' 1007 1008 slot_name = common.remove_serial_number(slot_name) 1009 # 指定スロットが同名であればそのスロットをそのまま利用する 1010 if prop_name == slot_name: 1011 slot = slot_item 1012 else: 1013 # スロット名が異なり、既にスロットがある場合はキャッシュに格納 1014 if slot_item: 1015 olds_slots[slot_name] = slot_item 1016 slot = mate.texture_slots.create(slot_index) 1017 1018 if prop_name in olds_slots: 1019 tex = olds_slots.pop(prop_name).texture 1020 else: 1021 for item_index in range(slot_index + 1, len(mate.texture_slots)): 1022 slot_item = mate.texture_slots[item_index] 1023 if slot_item is None: 1024 break 1025 if prop_name == common.remove_serial_number(slot_item.name): 1026 tex = slot_item.texture 1027 break 1028 if tex is None: 1029 tex = override['blend_data'].textures.new(prop_name, tex_type) 1030 slot.texture = tex 1031 return slot 1032 1033 1034def clear_nodes(nodes): 1035 for node in nodes: 1036 if node.type not in ['VALUE', 'RGB', 'TEX_IMAGE']: 1037 nodes.remove(node) 1038 1039 1040def align_nodes(mate): 1041 nodes = mate.node_tree.nodes 1042 # Principled BSDFがある前提での整列 1043 bsdf = nodes.get('Principled BSDF') 1044 base_location = (10, 300) 1045 if bsdf: 1046 main_tex = nodes.get('_MainTex') 1047 if main_tex: 1048 mate.node_tree.links.new(bsdf.inputs['Base Color'], main_tex.outputs['Color']) 1049 mate.node_tree.links.new(bsdf.inputs['Alpha'], main_tex.outputs['Alpha']) 1050 shininess = nodes.get('_Shininess') 1051 if shininess: 1052 mate.node_tree.links.new(bsdf.inputs['Specular'], shininess.outputs[0]) 1053 base_location = bsdf.location 1054 1055 shader_name = mate.get('shader1') 1056 if shader_name: 1057 location_x = base_location[0] - 400 1058 location_y = base_location[1] + 60 1059 shader_prop = MaterialHandler.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(shader_name) 1060 node_list = shader_prop.get('tex_list') 1061 if node_list: 1062 for node_name in node_list: 1063 node = nodes.get(node_name) 1064 if node: 1065 node.location = (location_x, location_y) 1066 node.hide = True 1067 location_y -= 60 1068 1069 col_list = shader_prop.get('col_list') 1070 if col_list: 1071 for node_name in col_list: 1072 node = nodes.get(node_name) 1073 if node: 1074 node.location = (location_x, location_y) 1075 location_y -= 200 1076 1077 f_list = shader_prop.get('f_list') 1078 if f_list: 1079 for node_name in f_list: 1080 node = nodes.get(node_name) 1081 if node: 1082 node.location = (location_x, location_y) 1083 location_y -= 90
SHADER_NAMES_CM3D2 =
['CM3D2/Toony_Lighted', 'CM3D2/Toony_Lighted_Hair', 'CM3D2/Toony_Lighted_Trans', 'CM3D2/Toony_Lighted_Trans_NoZ', 'CM3D2/Toony_Lighted_Outline', 'CM3D2/Toony_Lighted_Outline_Trans', 'CM3D2/Toony_Lighted_Hair_Outline', 'CM3D2/Lighted_Trans', 'CM3D2/Lighted', 'Unlit/Texture', 'Unlit/Transparent', 'CM3D2/Mosaic', 'CM3D2/Man', 'Diffuse', 'Transparent/Diffuse', 'CM3D2_Debug/Debug_CM3D2_Normal2Color']
SHADER_NAMES_COM3D2 =
['CM3D2/Toony_Lighted', 'CM3D2/Toony_Lighted_Hair', 'CM3D2/Toony_Lighted_Trans', 'CM3D2/Toony_Lighted_Trans_NoZ', 'CM3D2/Toony_Lighted_Trans_NoZTest', 'CM3D2/Toony_Lighted_Outline', 'CM3D2/Toony_Lighted_Outline_Tex', 'CM3D2/Toony_Lighted_Hair_Outline', 'CM3D2/Toony_Lighted_Outline_Trans', 'CM3D2/Toony_Lighted_Cutout_AtC', 'CM3D2/Lighted_Cutout_AtC', 'CM3D2/Lighted_Trans', 'CM3D2/Lighted', 'Unlit/Texture', 'Unlit/Transparent', 'CM3D2/Mosaic', 'CM3D2/Man', 'Diffuse', 'Transparent/Diffuse', 'CM3D2_Debug/Debug_CM3D2_Normal2Color']
TOON_TEXES =
['NoTex', 'ToonBlueA1', 'ToonBlueA2', 'ToonBrownA1', 'ToonGrayA1', 'ToonGreenA1', 'ToonGreenA2', 'ToonGreenA3', 'ToonOrangeA1', 'ToonPinkA1', 'ToonPinkA2', 'ToonPurpleA1', 'ToonRedA1', 'ToonRedA2', 'ToonRedmmm1', 'ToonRedmm1', 'ToonRedm1', 'ToonYellowA1', 'ToonYellowA2', 'ToonYellowA3', 'ToonYellowA4', 'ToonFace', 'ToonSkin', 'ToonBlackA1', 'ToonFace_shadow', 'ToonDress_shadow', 'ToonSkin_Shadow', 'ToonBlackMM1', 'ToonBlackM1', 'ToonGrayMM1', 'ToonGrayM1', 'ToonPurpleMM1', 'ToonPurpleM1', 'ToonSilverA1', 'ToonDressMM_Shadow', 'ToonDressM_Shadow']
PROP_DESC =
{'_MainTex': ['面の色を決定するテクスチャを指定。', '普段テスクチャと呼んでいるものは基本コレです。', 'テクスチャパスは適当でも動きます。', 'しかし、テクスチャ名はきちんと決めましょう。'], '_ToonRamp': ['暗い部分に乗算するグラデーション画像を指定します。'], '_ShadowTex': ['陰部分の面の色を決定するテクスチャを指定。', '「_ShadowRateToon」で範囲を指定します。'], '_ShadowRateToon': ['「_ShadowTex」を有効にする部分を指定します。', '黒色で有効、白色で無効。'], '_OutlineTex': ['アウトラインを表現するためのテクスチャを指定。(未確認)'], '_OutlineToonRamp': ['_OutlineTexの暗い部分に乗算するグラデーション画像を指定します。(未確認)'], '_Color': ['面の色を指定。', '白色で無効。基本的に白色で良いでしょう。'], '_ShadowColor': ['影の色を指定。白色で無効。', '別の物体に遮られてできた「影」の色です。'], '_RimColor': ['リムライトの色を指定。', 'リムライトとは縁にできる光の反射のことです。'], '_OutlineColor': ['輪郭線の色を指定。', '黒にするか、テクスチャの明度を', '落としたものを指定するとより良いでしょう。'], '_Shininess': ['スペキュラーの強さを指定。0.0~1.0で指定。', 'スペキュラーとは面の角度と光源の角度によって', 'できるハイライトのことです。', '金属、皮、ガラスなどに使うと良いでしょう。'], '_OutlineWidth': ['輪郭線の太さを指定。', '0.002は太め、0.001は細め。'], '_RimPower': ['リムライトの強さを指定。', 'この値は10以上なことも多いです。', '0に近い値だと正常に表示されません。'], '_RimShift': ['リムライトの幅を指定。', '0.0~1.0で指定。0.5でもかなり強い。'], '_RenderTex': ['モザイクシェーダーにある設定値。', '特に設定の必要なし。'], '_FloatValue1': ['モザイクの粗さ'], '_Cutoff': ['アルファのカットオフ値。', 'アルファ値がこの値より大きい部分だけがレンダリングされる'], '_ZTest': ['デプステストの実行方法を指定する。']}
PROPS =
{'_MainTex': {'type': 'tex', 'desc': ['面の色を決定するテクスチャを指定。', '普段テスクチャと呼んでいるものは基本コレです。', 'テクスチャパスは適当でも動きます。', 'しかし、テクスチャ名はきちんと決めましょう。']}, '_ToonRamp': {'type': 'tex', 'desc': ['暗い部分に乗算するグラデーション画像を指定します。']}, '_ShadowTex': {'type': 'tex', 'desc': ['陰部分の面の色を決定するテクスチャを指定。', '「_ShadowRateToon」で範囲を指定します。']}, '_ShadowRateToon': {'type': 'tex', 'desc': ['「_ShadowTex」を有効にする部分を指定します。', '黒色で有効、白色で無効。']}, '_OutlineTex': {'type': 'tex', 'desc': ['アウトラインを表現するためのテクスチャを指定。']}, '_OutlineToonRamp': {'type': 'tex', 'desc': ['_OutlineTexの暗い部分に乗算するグラデーション画像を指定します。']}, '_RenderTex': {'type': 'tex', 'desc': ['モザイクシェーダーにある設定値。', '特に設定の必要なし。']}, '_Color': {'type': 'col', 'desc': ['面の色を指定。', '白色で無効。基本的に白色で良いでしょう。']}, '_ShadowColor': {'type': 'col', 'desc': ['影の色を指定。白色で無効。', '別の物体に遮られてできた「影」の色です。']}, '_RimColor': {'type': 'col', 'desc': ['リムライトの色を指定。', 'リムライトとは縁にできる光の反射のことです。']}, '_OutlineColor': {'type': 'col', 'desc': ['輪郭線の色を指定。', '黒にするか、テクスチャの明度を', '落としたものを指定するとより良いでしょう。']}, '_Shininess': {'type': 'f', 'desc': ['スペキュラーの強さを指定。0.0~1.0で指定。', 'スペキュラーとは面の角度と光源の角度によって', 'できるハイライトのことです。', '金属、皮、ガラスなどに使うと良いでしょう。'], 'presets': [0, 0.1, 0.5, 1, 5]}, '_OutlineWidth': {'type': 'f', 'desc': ['輪郭線の太さを指定。', '0.002は太め、0.001は細め。'], 'presets': [0.0001, 0.001, 0.0015, 0.002], 'dispExact': True}, '_RimPower': {'type': 'f', 'desc': ['リムライトの強さを指定。', 'この値は10以上なことも多いです。', '0に近い値だと正常に表示されません。'], 'presets': [0, 25, 50, 100]}, '_RimShift': {'type': 'f', 'desc': ['リムライトの幅を指定。', '0.0~1.0で指定。0.5でもかなり強い。'], 'presets': [0, 0.25, 0.5, 0.75, 1]}, '_FloatValue1': {'type': 'f', 'desc': ['モザイクの粗さ'], 'presets': [0, 100, 200]}, '_Cutoff': {'type': 'f', 'desc': ['アルファのカットオフ値。', 'アルファ値がこの値より大きい部分だけがレンダリングされる'], 'presets': [0, 0.1, 0.5, 1, 5]}, '_ZTest': {'type': 'f', 'desc': ['デプステストの実行方法を指定する。'], 'disableSlider': True, 'preset_enums': [(0, 'Disabled:0'), (1, 'Never:1'), (2, 'Less:2'), (3, 'Equal:3'), (4, 'LessEqual:4'), (5, 'Greater:5'), (6, 'NotEqual:6'), (7, 'GreaterEqual:7'), (8, 'Always:8')]}, '_FloatValue2': {'type': 'f', 'presets': [-15, 0, 1, 15]}, '_FloatValue3': {'type': 'f', 'presets': [0, 0.1, 0.5, 1]}, '_ZTest2': {'type': 'f', 'disableSlider': True, 'preset_enums': [(0, '0'), (1, '1')]}, '_ZTest2Alpha': {'type': 'f', 'presets': [0, 0.8, 1]}}
class
DataHandler:
227class DataHandler: 228 _instance = None 229 230 def __new__(cls): 231 if cls._instance is None: 232 cls._instance = super().__new__(cls) 233 return cls._instance 234 235 @classmethod 236 def instance(cls): 237 if cls._instance is None: 238 cls._instance = DataHandler() 239 240 return cls._instance 241 242 def __init__(self): 243 diffuse = { 244 'type_name': "リアル", 245 'icon': 'BRUSH_CLAY_STRIPS', 246 'shader2': 'Legacy Shaders__Diffuse', 247 'tex_list': ['_MainTex'], 248 'col_list': ['_Color'], 249 'f_list': [], 250 } 251 trans_diffuse = { 252 'type_name': "リアル 透過", 253 'icon': 'BRUSH_TEXFILL', 254 'shader2': 'Legacy Shaders__Transparent__Diffuse', 255 'tex_list': ['_MainTex'], 256 'col_list': ['_Color'], 257 'f_list': [], 258 } 259 260 self.shader_dict = { 261 'CM3D2/Toony_Lighted': { 262 'type_name': "トゥーン", 263 'icon': compat.icon('SHADING_SOLID'), 264 'shader2': 'CM3D2__Toony_Lighted', 265 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 266 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 267 'f_list': ['_Shininess', '_RimPower', '_RimShift'] 268 }, 269 'CM3D2/Toony_Lighted_Hair': { 270 'type_name': "トゥーン 髪", 271 'icon': 'PARTICLEMODE', 272 'shader2': 'CM3D2__Toony_Lighted_Hair', 273 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'], 274 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 275 'f_list': ['_Shininess', '_RimPower', '_RimShift', '_HiRate', '_HiPow'] 276 }, 277 'CM3D2/Toony_Lighted_Trans': { 278 'type_name': "トゥーン 透過", 279 'icon': compat.icon('SHADING_WIRE'), 280 'shader2': 'CM3D2__Toony_Lighted_Trans', 281 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 282 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 283 'f_list': ['_Shininess', '_Cutoff', '_RimPower', '_RimShift'], 284 }, 285 'CM3D2/Toony_Lighted_Trans_NoZ': { 286 'type_name': "トゥーン 透過 NoZ", 287 'icon': 'DRIVER', 288 'shader2': 'CM3D2__Toony_Lighted_Trans_NoZ', 289 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 290 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 291 'f_list': ['_Shininess', '_RimPower', '_RimShift'], 292 }, 293 'CM3D2/Toony_Lighted_Trans_NoZTest': { 294 'type_name': "トゥーン 透過 NoZTest", 295 'icon': 'ANIM_DATA', 296 'shader2': 'CM3D2__Toony_Lighted_Trans_NoZTest', 297 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 298 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 299 'f_list': ['_Shininess', '_RimPower', '_RimShift', '_ZTest', '_ZTest2', '_ZTest2Alpha'], 300 }, 301 'CM3D2/Toony_Lighted_Outline': { 302 'type_name': "トゥーン 輪郭線", 303 'icon': 'ANTIALIASED', 304 'shader2': 'CM3D2__Toony_Lighted_Outline', 305 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 306 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 307 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'], 308 }, 309 'CM3D2/Toony_Lighted_Outline_Tex': { 310 'type_name': "トゥーン 輪郭線 Tex", 311 'icon': 'MATSPHERE', 312 'shader2': 'CM3D2__Toony_Lighted_Outline_Tex', 313 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_OutlineTex', '_OutlineToonRamp'], 314 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 315 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'], 316 }, 317 'CM3D2/Toony_Lighted_Hair_Outline': { 318 'type_name': "トゥーン 輪郭線 髪", 319 'icon': 'PARTICLEMODE', 320 'shader2': 'CM3D2__Toony_Lighted_Hair_Outline', 321 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'], 322 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 323 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift', '_HiRate', '_HiPow'], 324 }, 325 # 'CM3D2/Toony_Lighted_Hair_Outline_Tex': { 326 # 'type_name': "トゥーン 輪郭線 Tex 髪", 327 # 'icon': 'PARTICLEMODE', 328 # 'shader2': 'CM3D2__Toony_Lighted_Hair_Outline_Tex', 329 # 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon', '_HiTex'], 330 # 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 331 # 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift', '_HiRate', '_HiPow'], 332 # }, 333 'CM3D2/Toony_Lighted_Outline_Trans': { 334 'type_name': "トゥーン 輪郭線 透過", 335 'icon': 'PROP_OFF', 336 'shader2': 'CM3D2__Toony_Lighted_Outline_Trans', 337 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 338 'col_list': ['_Color', '_ShadowColor', '_RimColor', '_OutlineColor'], 339 'f_list': ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'], 340 }, 341 'CM3D2/Toony_Lighted_Cutout_AtC': { 342 'type_name': "トゥーン Cutout", 343 'icon': 'IPO_BACK', 344 'shader2': 'CM3D2__Toony_Lighted_Cutout_AtC', 345 'tex_list': ['_MainTex', '_ToonRamp', '_ShadowTex', '_ShadowRateToon'], 346 'col_list': ['_Color', '_ShadowColor', '_RimColor'], 347 'f_list': ['_Shininess', '_RimPower', '_RimShift', '_Cutoff'], 348 }, 349 'CM3D2/Lighted_Trans': { 350 'type_name': "トゥーン無し 透過", 351 'icon': compat.icon('VIS_SEL_01'), 352 'shader2': 'CM3D2__Lighted_Trans', 353 'tex_list': ['_MainTex'], 354 'col_list': ['_Color', '_ShadowColor'], 355 'f_list': ['_Shininess'], 356 }, 357 'CM3D2/Lighted': { 358 'type_name': "トゥーン無し", 359 'icon': compat.icon('VIS_SEL_11'), 360 'shader2': 'CM3D2__Lighted', 361 'tex_list': ['_MainTex'], 362 'col_list': ['_Color', '_ShadowColor'], 363 'f_list': ['_Shininess'], 364 }, 365 'CM3D2/Lighted_Cutout_AtC': { 366 'type_name': "トゥーン無し Cutout", 367 'icon': 'IPO_BACK', 368 'shader2': 'CM3D2__Lighted_Cutout_AtC', 369 'tex_list': ['_MainTex'], 370 'col_list': ['_Color', '_ShadowColor'], 371 'f_list': ['_Shininess', '_Cutoff'], 372 }, 373 'Unlit/Texture': { 374 'type_name': "発光", 375 'icon': 'PARTICLES', 376 'shader2': 'Unlit__Texture', 377 'tex_list': ['_MainTex'], 378 'col_list': [], # ['_Color'], 379 'f_list': [], 380 }, 381 'Unlit/Transparent': { 382 'type_name': "発光 透過", 383 'icon': 'MOD_PARTICLES', 384 'shader2': 'Unlit__Texture', 385 'tex_list': ['_MainTex'], 386 'col_list': [], # ['_Color'], 387 'f_list': [], 388 }, 389 'CM3D2/Mosaic': { 390 'type_name': "モザイク", 391 'icon': 'ALIASED', 392 'shader2': 'CM3D2__Mosaic', 393 'tex_list': ['_RenderTex'], 394 'col_list': [], 395 'f_list': ['_FloatValue1'], 396 }, 397 'CM3D2/Man': { 398 'type_name': "ご主人様", 399 'icon': 'ARMATURE_DATA', 400 'shader2': 'CM3D2__Man', 401 'tex_list': [], 402 'col_list': ['_Color'], 403 'f_list': ['_FloatValue2', '_FloatValue3'], 404 }, 405 'Diffuse': diffuse, 406 'Legacy Shaders/Diffuse': diffuse, 407 'Transparent/Diffuse': trans_diffuse, 408 'Legacy Shaders/Transparent/Diffuse': trans_diffuse, 409 'CM3D2_Debug/Debug_CM3D2_Normal2Color': { 410 'type_name': "法線", 411 'icon': compat.icon('NORMALS_VERTEX'), 412 'shader2': 'CM3D2_Debug__Debug_CM3D2_Normal2Color', 413 'tex_list': [], 414 'col_list': ['_Color'], # , '_RimColor', '_OutlineColor', '_SpecColor'], 415 'f_list': [] # ['_Shininess', '_OutlineWidth', '_RimPower', '_RimShift'], 416 }, 417 } 418 419 @classmethod 420 def create_shader_items(cls) -> list: 421 _inst = cls.instance() 422 items = [] 423 idx = 0 424 for name in SHADER_NAMES_CM3D2: 425 item = _inst.shader_dict.get(name) 426 if item: 427 items.append((name, item['type_name'], '', item['icon'], idx)) 428 idx += 1 429 return items 430 431 @classmethod 432 def create_comshader_items(cls) -> list: 433 _inst = cls.instance() 434 items = [] 435 idx = 0 436 for name in SHADER_NAMES_COM3D2: 437 item = _inst.shader_dict.get(name) 438 if item: 439 items.append((name, item['type_name'], '', item['icon'], idx)) 440 idx += 1 441 return items 442 443 @classmethod 444 def get_shader_prop(cls, name): 445 _inst = cls.instance() 446 shader_prop = _inst.shader_dict.get(name) 447 if shader_prop: 448 return shader_prop 449 450 return {'type_name': '不明', 'icon': 'NONE'}
@classmethod
def
create_comshader_items(cls) -> list:
431 @classmethod 432 def create_comshader_items(cls) -> list: 433 _inst = cls.instance() 434 items = [] 435 idx = 0 436 for name in SHADER_NAMES_COM3D2: 437 item = _inst.shader_dict.get(name) 438 if item: 439 items.append((name, item['type_name'], '', item['icon'], idx)) 440 idx += 1 441 return items
Handler =
<CM3D2 Converter.cm3d2_data.DataHandler object>
class
Material:
455class Material(): 456 """マテリアルデータクラス""" 457 def __init__(self): 458 self.version = 1000 459 self.name1 = None 460 self.name2 = None 461 self.shader1 = None 462 self.shader2 = None 463 464 self.tex_list = [] # prop_name, (tex_name, tex_path, trans[2], scale[2]) 465 self.col_list = [] # prop_name, col[4] 466 self.f_list = [] # prop_name, f 467 self.range_list = [] # prop_name, col[4] 468 469 self.custom_list = dict() 470 471 def sort(self): 472 self.tex_list = sorted(self.tex_list, key=lambda item: item[0]) 473 self.col_list = sorted(self.col_list, key=lambda item: item[0]) 474 self.f_list = sorted(self.f_list, key=lambda item: item[0]) 475 476 @property 477 def name(self): 478 return self.name2 or self.name1 479 480 def read(self, reader, read_header=True): 481 if read_header: 482 header = common.read_str(reader) 483 if header != 'CM3D2_MATERIAL': 484 raise common.CM3D2ImportException(f_tip_("mateファイルではありません。ヘッダ:{}", header)) 485 self.version = struct.unpack('<i', reader.read(4))[0] 486 self.name1 = common.read_str(reader) 487 self.name2 = common.read_str(reader) 488 489 self.shader1 = common.read_str(reader) 490 self.shader2 = common.read_str(reader) 491 492 peeked = reader.peek()[0] 493 print(f"type({peeked}) = {type(peeked)}") 494 if self.version >= 2102 and (peeked in (0, 1)): # CR Edit Mode 495 cr_unknown_float_count = struct.unpack('<B', reader.read(1))[0] 496 for i in range(cr_unknown_float_count): 497 # CR TODO 498 self.custom_list[f'cr_unknown_float:{i:03d}'] = struct.unpack('<f', reader.read(4)) 499 500 for i in range(99999): 501 prop_type = common.read_str(reader) 502 if prop_type == 'tex': 503 prop_name = common.read_str(reader) 504 sub_type = common.read_str(reader) 505 if sub_type == 'tex2d': 506 tex_name = common.read_str(reader) 507 tex_path = common.read_str(reader) 508 offset = struct.unpack('<2f', reader.read(4 * 2)) 509 scale = struct.unpack('<2f', reader.read(4 * 2)) 510 tex_item = [prop_name, tex_name, tex_path, offset, scale] 511 else: 512 tex_item = [prop_name] 513 self.tex_list.append(tex_item) 514 515 elif prop_type == 'col': 516 prop_name = common.read_str(reader) 517 col = struct.unpack('<4f', reader.read(4 * 4)) 518 self.col_list.append([prop_name, col]) 519 520 elif prop_type == 'f' or prop_type == 'range': # 'range' from CR Edit 521 prop_name = common.read_str(reader) 522 f = struct.unpack('<f', reader.read(4))[0] 523 self.f_list.append([prop_name, f]) 524 525 # CR TODO 526 elif prop_type == 'keyword': 527 prop_name = common.read_str(reader) 528 keyword_f = struct.unpack('<f', reader.read(4))[0] 529 print(keyword_f, type(keyword_f)) 530 self.custom_list.setdefault('keyword', dict())[prop_name] = keyword_f #.append([prop_name, keyword_f]) 531 532 # CR TODO 533 elif prop_type == '_ALPHAPREMULTIPLY_ON': 534 alpha_bool = struct.unpack('<?', reader.read(1))[0] 535 self.custom_list['_ALPHAPREMULTIPLY_ON'] = alpha_bool 536 537 elif prop_type == 'end': 538 break 539 else: 540 raise common.CM3D2ImportException(f_tip_("Materialプロパティに未知の設定値タイプ({prop})が見つかりました。", prop=prop_type)) 541 542 def write(self, writer, write_header=True): 543 if write_header: 544 common.write_str(writer, 'CM3D2_MATERIAL') 545 writer.write(struct.pack('<i', self.version)) 546 common.write_str(writer, self.name1) 547 548 common.write_str(writer, self.name2) 549 common.write_str(writer, self.shader1) 550 common.write_str(writer, self.shader2) 551 552 for tex_item in self.tex_list: 553 common.write_str(writer, 'tex') 554 common.write_str(writer, tex_item[0]) # prop_name 555 556 if len(tex_item) < 2: 557 common.write_str(writer, 'null') 558 else: 559 common.write_str(writer, 'tex2d') 560 common.write_str(writer, tex_item[1]) # tex_name 561 common.write_str(writer, tex_item[2]) # tex_path 562 trans = tex_item[3] 563 writer.write(struct.pack('<2f', trans[0], trans[1])) 564 scale = tex_item[4] 565 writer.write(struct.pack('<2f', scale[0], scale[1])) 566 567 for col_item in self.col_list: 568 common.write_str(writer, 'col') 569 common.write_str(writer, col_item[0]) # prop_name 570 571 col = col_item[1] 572 writer.write(struct.pack('<4f', col[0], col[1], col[2], col[3])) 573 574 for f_item in self.f_list: 575 common.write_str(writer, 'f') 576 common.write_str(writer, f_item[0]) # prop_name 577 578 writer.write(struct.pack('<f', f_item[1])) 579 580 common.write_str(writer, 'end') 581 582 def to_text(self): 583 output_text = str(self.version) + "\n" 584 output_text += self.name1 + "\n" 585 output_text += self.name2 + "\n" 586 output_text += self.shader1 + "\n" 587 output_text += self.shader2 + "\n" 588 output_text += "\n" 589 590 for tex_item in self.tex_list: 591 output_text += 'tex\n' 592 output_text += "\t" + tex_item[0] + "\n" # prop_name 593 594 if len(tex_item) < 2: 595 output_text += '\tnull\n' 596 else: 597 output_text += '\ttex2d\n' 598 output_text += "\t" + tex_item[1] + "\n" # tex_name 599 output_text += "\t" + tex_item[2] + "\n" # tex_path 600 trans = tex_item[3] 601 scale = tex_item[4] 602 output_text += "\t" + " ".join([str(trans[0]), str(trans[1]), str(scale[0]), str(scale[1])]) + "\n" 603 604 for col_item in self.col_list: 605 output_text += 'col\n' 606 output_text += "\t" + col_item[0] + "\n" # prop_name 607 col = col_item[1] 608 output_text += "\t" + " ".join([str(col[0]), str(col[1]), str(col[2]), str(col[3])]) + "\n" # prop_name 609 610 for f_item in self.f_list: 611 output_text += 'f\n' 612 output_text += "\t" + f_item[0] + "\n" # prop_name 613 f = f_item[1] 614 output_text += "\t" + str(f) + "\n" 615 616 return output_text 617 618 def to_json(self): 619 import json 620 return json.dumps(self.__dict__, ensure_ascii=False, indent=2) 621 622 def from_dict(self, data): 623 self.name1 = data['name1'] 624 self.name2 = data['name2'] 625 self.version = data['version'] 626 self.shader1 = data['shader1'] 627 self.shader2 = data['shader2'] 628 629 self.tex_list = data['tex_list'] # prop_name, (tex_name, tex_path, trans[2], scale[2]) 630 self.col_list = data['col_list'] # prop_name, col[4] 631 self.f_list = data['f_list'] # prop_name, f
マテリアルデータクラス
def
read(self, reader, read_header=True):
480 def read(self, reader, read_header=True): 481 if read_header: 482 header = common.read_str(reader) 483 if header != 'CM3D2_MATERIAL': 484 raise common.CM3D2ImportException(f_tip_("mateファイルではありません。ヘッダ:{}", header)) 485 self.version = struct.unpack('<i', reader.read(4))[0] 486 self.name1 = common.read_str(reader) 487 self.name2 = common.read_str(reader) 488 489 self.shader1 = common.read_str(reader) 490 self.shader2 = common.read_str(reader) 491 492 peeked = reader.peek()[0] 493 print(f"type({peeked}) = {type(peeked)}") 494 if self.version >= 2102 and (peeked in (0, 1)): # CR Edit Mode 495 cr_unknown_float_count = struct.unpack('<B', reader.read(1))[0] 496 for i in range(cr_unknown_float_count): 497 # CR TODO 498 self.custom_list[f'cr_unknown_float:{i:03d}'] = struct.unpack('<f', reader.read(4)) 499 500 for i in range(99999): 501 prop_type = common.read_str(reader) 502 if prop_type == 'tex': 503 prop_name = common.read_str(reader) 504 sub_type = common.read_str(reader) 505 if sub_type == 'tex2d': 506 tex_name = common.read_str(reader) 507 tex_path = common.read_str(reader) 508 offset = struct.unpack('<2f', reader.read(4 * 2)) 509 scale = struct.unpack('<2f', reader.read(4 * 2)) 510 tex_item = [prop_name, tex_name, tex_path, offset, scale] 511 else: 512 tex_item = [prop_name] 513 self.tex_list.append(tex_item) 514 515 elif prop_type == 'col': 516 prop_name = common.read_str(reader) 517 col = struct.unpack('<4f', reader.read(4 * 4)) 518 self.col_list.append([prop_name, col]) 519 520 elif prop_type == 'f' or prop_type == 'range': # 'range' from CR Edit 521 prop_name = common.read_str(reader) 522 f = struct.unpack('<f', reader.read(4))[0] 523 self.f_list.append([prop_name, f]) 524 525 # CR TODO 526 elif prop_type == 'keyword': 527 prop_name = common.read_str(reader) 528 keyword_f = struct.unpack('<f', reader.read(4))[0] 529 print(keyword_f, type(keyword_f)) 530 self.custom_list.setdefault('keyword', dict())[prop_name] = keyword_f #.append([prop_name, keyword_f]) 531 532 # CR TODO 533 elif prop_type == '_ALPHAPREMULTIPLY_ON': 534 alpha_bool = struct.unpack('<?', reader.read(1))[0] 535 self.custom_list['_ALPHAPREMULTIPLY_ON'] = alpha_bool 536 537 elif prop_type == 'end': 538 break 539 else: 540 raise common.CM3D2ImportException(f_tip_("Materialプロパティに未知の設定値タイプ({prop})が見つかりました。", prop=prop_type))
def
write(self, writer, write_header=True):
542 def write(self, writer, write_header=True): 543 if write_header: 544 common.write_str(writer, 'CM3D2_MATERIAL') 545 writer.write(struct.pack('<i', self.version)) 546 common.write_str(writer, self.name1) 547 548 common.write_str(writer, self.name2) 549 common.write_str(writer, self.shader1) 550 common.write_str(writer, self.shader2) 551 552 for tex_item in self.tex_list: 553 common.write_str(writer, 'tex') 554 common.write_str(writer, tex_item[0]) # prop_name 555 556 if len(tex_item) < 2: 557 common.write_str(writer, 'null') 558 else: 559 common.write_str(writer, 'tex2d') 560 common.write_str(writer, tex_item[1]) # tex_name 561 common.write_str(writer, tex_item[2]) # tex_path 562 trans = tex_item[3] 563 writer.write(struct.pack('<2f', trans[0], trans[1])) 564 scale = tex_item[4] 565 writer.write(struct.pack('<2f', scale[0], scale[1])) 566 567 for col_item in self.col_list: 568 common.write_str(writer, 'col') 569 common.write_str(writer, col_item[0]) # prop_name 570 571 col = col_item[1] 572 writer.write(struct.pack('<4f', col[0], col[1], col[2], col[3])) 573 574 for f_item in self.f_list: 575 common.write_str(writer, 'f') 576 common.write_str(writer, f_item[0]) # prop_name 577 578 writer.write(struct.pack('<f', f_item[1])) 579 580 common.write_str(writer, 'end')
def
to_text(self):
582 def to_text(self): 583 output_text = str(self.version) + "\n" 584 output_text += self.name1 + "\n" 585 output_text += self.name2 + "\n" 586 output_text += self.shader1 + "\n" 587 output_text += self.shader2 + "\n" 588 output_text += "\n" 589 590 for tex_item in self.tex_list: 591 output_text += 'tex\n' 592 output_text += "\t" + tex_item[0] + "\n" # prop_name 593 594 if len(tex_item) < 2: 595 output_text += '\tnull\n' 596 else: 597 output_text += '\ttex2d\n' 598 output_text += "\t" + tex_item[1] + "\n" # tex_name 599 output_text += "\t" + tex_item[2] + "\n" # tex_path 600 trans = tex_item[3] 601 scale = tex_item[4] 602 output_text += "\t" + " ".join([str(trans[0]), str(trans[1]), str(scale[0]), str(scale[1])]) + "\n" 603 604 for col_item in self.col_list: 605 output_text += 'col\n' 606 output_text += "\t" + col_item[0] + "\n" # prop_name 607 col = col_item[1] 608 output_text += "\t" + " ".join([str(col[0]), str(col[1]), str(col[2]), str(col[3])]) + "\n" # prop_name 609 610 for f_item in self.f_list: 611 output_text += 'f\n' 612 output_text += "\t" + f_item[0] + "\n" # prop_name 613 f = f_item[1] 614 output_text += "\t" + str(f) + "\n" 615 616 return output_text
def
from_dict(self, data):
622 def from_dict(self, data): 623 self.name1 = data['name1'] 624 self.name2 = data['name2'] 625 self.version = data['version'] 626 self.shader1 = data['shader1'] 627 self.shader2 = data['shader2'] 628 629 self.tex_list = data['tex_list'] # prop_name, (tex_name, tex_path, trans[2], scale[2]) 630 self.col_list = data['col_list'] # prop_name, col[4] 631 self.f_list = data['f_list'] # prop_name, f
class
MaterialHandler:
634class MaterialHandler: 635 636 @classmethod 637 def parse_tex_node(cls, node, remove_serial=True): 638 node_name = common.remove_serial_number(node.name, remove_serial) 639 tex_item = [node_name] 640 641 try: 642 img = node.image 643 except: 644 raise common.CM3D2ImportException('Materialプロパティのtexタイプの設定値取得に失敗しました。') 645 646 if img: 647 tex_name = common.remove_serial_number(img.name, remove_serial) 648 tex_name = common.re_png.sub(r'\1', tex_name) # 拡張子を除外 649 tex_item.append(tex_name) 650 651 if 'cm3d2_path' in img: 652 path = img['cm3d2_path'] 653 else: 654 path = bpy.path.abspath(img.filepath) 655 path = common.to_cm3d2path(path) 656 657 tex_item.append(path) 658 tex_map = node.texture_mapping 659 tex_trans = tex_map.translation[:2] 660 tex_scale = tex_map.scale[:2] 661 tex_item.append(tex_trans) 662 tex_item.append(tex_scale) 663 return tex_item 664 665 @classmethod 666 def parse_col_node(cls, node, remove_serial=True): 667 node_name = common.remove_serial_number(node.name, remove_serial) 668 col = node.outputs[0].default_value 669 return [node_name, col[:4]] 670 671 @classmethod 672 def parse_f_node(cls, node, remove_serial=True): 673 node_name = common.remove_serial_number(node.name, remove_serial) 674 f = node.outputs[0].default_value 675 return [node_name, f] 676 677 @classmethod 678 def read(cls, reader, read_header=True, version=None): 679 if not read_header and version == None: 680 raise ValueError(f_tip_("The argument 'version' is required when 'read_header' is False for MaterialHandler.read()")) 681 mat_data = Material() 682 if version: 683 mat_data.version = version 684 mat_data.read(reader, read_header) 685 686 return mat_data 687 688 @classmethod 689 def get_shader_prop_dynamic(cls, mate, ): 690 lists = { 'VALUE':'f_list', 'RGB':'col_list', 'TEX_IMAGE':'tex_list' } 691 shader_prop = copy.deepcopy( DataHandler.get_shader_prop(mate.get('shader1')) ) 692 for node in mate.node_tree.nodes: 693 if node.name[0] != '_': 694 continue 695 list_name = lists.get(node.type) 696 if not list_name: 697 continue 698 prop_list = shader_prop.get(list_name) 699 if not prop_list: 700 prop_list = list() 701 shader_prop[list_name] = prop_list 702 if node.name in prop_list: 703 continue 704 prop_list.append(node.name) 705 return shader_prop 706 707 @classmethod 708 def parse_mate(cls, mate, remove_serial=True): 709 mat_data = Material() 710 711 mate_name = common.remove_serial_number(mate.name, remove_serial) 712 mat_data.name1 = mate_name.lower() 713 mat_data.name2 = mate_name 714 mat_data.shader1 = mate['shader1'] 715 mat_data.shader2 = mate['shader2'] 716 717 nodes = mate.node_tree.nodes 718 shader_prop = cls.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(mat_data.shader1) 719 if shader_prop: 720 for node_name in shader_prop['tex_list']: 721 node = nodes.get(node_name) 722 if node and node.type == 'TEX_IMAGE': 723 tex_item = cls.parse_tex_node(node, remove_serial) 724 mat_data.tex_list.append(tex_item) 725 726 for node_name in shader_prop['col_list']: 727 node = nodes.get(node_name) 728 if node and node.type == 'RGB': 729 col_item = cls.parse_col_node(node, remove_serial) 730 mat_data.col_list.append(col_item) 731 732 for node_name in shader_prop['f_list']: 733 node = nodes.get(node_name) 734 if node and node.type == 'VALUE': 735 f_item = cls.parse_f_node(node, remove_serial) 736 mat_data.f_list.append(f_item) 737 738 #for node in nodes: 739 # if not node.name.startswith('_'): 740 # continue 741 # 742 # node_type = node.type 743 # if node_type == 'TEX_IMAGE': 744 # tex_item = cls.parse_tex_node(node, remove_serial) 745 # mat_data.tex_list.append(tex_item) 746 # elif node_type == 'RGB': 747 # col_item = cls.parse_col_node(node, remove_serial) 748 # mat_data.col_list.append(col_item) 749 # 750 # elif node_type == 'VALUE': 751 # f_item = cls.parse_f_node(node, remove_serial) 752 # mat_data.f_list.append(f_item) 753 754 return mat_data 755 756 @classmethod 757 def parse_mate_old(cls, mate, remove_serial=True): 758 """material parser for blender-2.7x""" 759 mat_data = Material() 760 761 mate_name = common.remove_serial_number(mate.name, remove_serial) 762 mat_data.name1 = mate_name.lower() 763 mat_data.name2 = mate_name 764 mat_data.shader1 = mate['shader1'] 765 mat_data.shader2 = mate['shader2'] 766 767 for tindex, tslot in enumerate(mate.texture_slots): 768 if not tslot: 769 continue 770 771 tex = tslot.texture 772 node_name = common.remove_serial_number(tex.name, remove_serial) 773 # node_name = tslot.name 774 if mate.use_textures[tindex]: 775 tex_item = [node_name] 776 try: 777 img = tex.image 778 except: 779 raise Exception('Materialプロパティのtexタイプの設定値取得に失敗しました。') 780 781 if img: 782 tex_name = common.remove_serial_number(img.name, remove_serial) 783 tex_name = common.re_png.sub(r'\1', tex_name) # 拡張子を除外 784 tex_item.append(tex_name) 785 786 if 'cm3d2_path' in img: 787 path = img['cm3d2_path'] 788 else: 789 path = bpy.path.abspath(img.filepath) 790 path = common.to_cm3d2path(path) 791 792 tex_item.append(path) 793 tex_trans = tex.offset[:2] 794 tex_scale = tex.scale[:2] 795 tex_item.append(tex_trans) 796 tex_item.append(tex_scale) 797 798 mat_data.tex_list.append(tex_item) 799 elif tslot.use_rgb_to_intensity: 800 col = tex.color[:3] + [tslot.diffuse_color_factor] 801 mat_data.col_list.append([node_name, col[:4]]) 802 else: 803 f = tslot.diffuse_color_factor 804 mat_data.f_list.append([node_name, f]) 805 806 return mat_data 807 808 @classmethod 809 def parse_text(cls, text): 810 mat_data = Material() 811 lines = text.split('\n') 812 813 mat_data.version = int(lines[0]) 814 mat_data.name1 = lines[1] 815 mat_data.name2 = lines[2] 816 mat_data.shader1 = lines[3] 817 mat_data.shader2 = lines[4] 818 819 line_seek = 5 820 while line_seek < len(lines): 821 node_type = common.line_trim(lines[line_seek]) 822 if not node_type: 823 line_seek += 1 824 continue 825 if node_type == 'tex': 826 prop_name = common.line_trim(lines[line_seek + 1]) 827 sub_type = common.line_trim(lines[line_seek + 2]) 828 if sub_type == 'tex2d': 829 line_seek += 3 830 tex_name = common.line_trim(lines[line_seek]) 831 tex_path = common.line_trim(lines[line_seek + 1]) 832 tex_map = common.line_trim(lines[line_seek + 2]).split(' ') 833 for map_datum in range(len(tex_map)): 834 tex_map[map_datum] = float(tex_map[map_datum]) 835 mat_data.tex_list.append([prop_name, tex_name, tex_path, tex_map[:2], tex_map[2:]]) 836 else: 837 mat_data.tex_list.append([prop_name]) 838 839 line_seek += 3 840 841 elif node_type == 'col': 842 prop_name = common.line_trim(lines[line_seek + 1]) 843 tex_map = common.line_trim(lines[line_seek + 2]).split(' ') 844 for map_datum in range(len(tex_map)): 845 tex_map[map_datum] = float(tex_map[map_datum]) 846 847 mat_data.col_list.append([prop_name, tex_map[:]]) 848 line_seek += 3 849 850 elif node_type == 'f': 851 prop_name = common.line_trim(lines[line_seek + 1]) 852 val = float(common.line_trim(lines[line_seek + 2])) 853 854 mat_data.f_list.append([prop_name, val]) 855 line_seek += 3 856 else: 857 raise Exception('未知の設定値タイプが見つかりました。') 858 859 return mat_data 860 861 @classmethod 862 def parse_json(cls, text): 863 import json 864 mat_data = Material() 865 mat_data.from_dict(json.loads(text)) 866 867 return mat_data 868 869 @classmethod 870 def apply_to(cls, override, mate, mat_data, replace_tex=True): 871 mate['shader1'] = mat_data.shader1 872 mate['shader2'] = mat_data.shader2 873 874 if mate.use_nodes is False: 875 mate.use_nodes = True 876 877 nodes = mate.node_tree.nodes 878 nodes.clear() 879 # OUTPUT_MATERIAL, BSDF_PRINCIPLEDは消さない 880 #if len(nodes) > 2: 881 # clear_nodes(nodes) 882 883 for tex_item in mat_data.tex_list: 884 prop_name = tex_item[0] 885 886 if len(tex_item) < 2: 887 common.create_tex(override, mate, prop_name) 888 else: 889 tex_name = tex_item[1] 890 tex_path = tex_item[2] 891 tex_map = tex_item[3] + tex_item[4] 892 common.create_tex(override, mate, prop_name, tex_name, tex_path, tex_path, tex_map, replace_tex) 893 894 for col_item in mat_data.col_list: 895 prop_name = col_item[0] 896 col = col_item[1] 897 common.create_col(override, mate, prop_name, col) 898 899 for item in mat_data.f_list: 900 prop_name = item[0] 901 f = item[1] 902 common.create_float(override, mate, prop_name, f) 903 904 for key, value in mat_data.custom_list.items(): 905 mate[key] = value 906 if type(value) == bool: 907 rna_ui = mate.get('_RNA_UI', dict()) 908 rna_ui[key] = { 909 "default" : value, 910 "min" : 0 , 911 "max" : 1 , 912 "soft_min" : 0 , 913 "soft_max" : 1 , 914 } 915 mate['_RNA_UI'] = rna_ui 916 917 918 919 align_nodes(mate) 920 921 @classmethod 922 def apply_to_old(cls, override, mate, mat_data, replace_tex=True, decorate=True, skip_same_prop=True): 923 ob = override['active_object'] 924 me = ob.data 925 926 mate['shader1'] = mat_data.shader1 927 mate['shader2'] = mat_data.shader2 928 929 slot_index = 0 930 olds_slots = {} 931 read_texes = set() if skip_same_prop else None 932 933 for item in mat_data.tex_list: 934 prop_name = item[0] 935 if read_texes: 936 if prop_name in read_texes: 937 continue 938 read_texes.add(prop_name) 939 940 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'IMAGE') 941 slot.use_rgb_to_intensity = False 942 mate.use_textures[slot_index] = True 943 944 if len(item) > 4: 945 tex = slot.texture 946 tex_name = item[1] 947 tex_path = item[2] 948 if tex_name in read_texes: 949 continue 950 951 if tex_name != common.remove_serial_number(tex.image.name): 952 tex.image.name = tex_name 953 tex.image['cm3d2_path'] = tex_path 954 tex.image.filepath = tex.image['cm3d2_path'] 955 956 slot.offset = item[3] 957 slot.scale = item[4] 958 if replace_tex: 959 if common.replace_cm3d2_tex(tex.image, reload_path=True) and prop_name == '_MainTex': 960 for face in me.polygons: 961 if face.material_index == ob.active_material_index: 962 me.uv_textures.active.data[face.index].image = tex.image 963 slot_index += 1 964 965 for item in mat_data.col_list: 966 prop_name = item[0] 967 col = item[1] 968 969 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND') 970 971 mate.use_textures[slot_index] = False 972 slot.use_rgb_to_intensity = True 973 slot.color = col[:3] 974 slot.diffuse_color_factor = col[3] 975 976 slot_index += 1 977 978 for item in mat_data.f_list: 979 prop_name = item[0] 980 f = item[1] 981 982 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND') 983 984 mate.use_textures[slot_index] = False 985 slot.use_rgb_to_intensity = False 986 slot.diffuse_color_factor = f 987 988 slot_index += 1 989 990 # 存在しないスロットをクリア 991 for item_index in range(slot_index, len(mate.texture_slots)): 992 if mate.texture_slots[item_index]: 993 mate.texture_slots.clear(item_index) 994 995 # プレビューへの反映 996 for slot in mate.texture_slots: 997 if slot: 998 common.set_texture_color(slot) 999 1000 if decorate: 1001 common.decorate_material(mate, decorate, me, ob.active_material_index) 1002 1003 @staticmethod 1004 def search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, tex_type): 1005 tex = None 1006 slot_item = mate.texture_slots[slot_index] 1007 slot_name = slot_item.name if slot_item else '' 1008 1009 slot_name = common.remove_serial_number(slot_name) 1010 # 指定スロットが同名であればそのスロットをそのまま利用する 1011 if prop_name == slot_name: 1012 slot = slot_item 1013 else: 1014 # スロット名が異なり、既にスロットがある場合はキャッシュに格納 1015 if slot_item: 1016 olds_slots[slot_name] = slot_item 1017 slot = mate.texture_slots.create(slot_index) 1018 1019 if prop_name in olds_slots: 1020 tex = olds_slots.pop(prop_name).texture 1021 else: 1022 for item_index in range(slot_index + 1, len(mate.texture_slots)): 1023 slot_item = mate.texture_slots[item_index] 1024 if slot_item is None: 1025 break 1026 if prop_name == common.remove_serial_number(slot_item.name): 1027 tex = slot_item.texture 1028 break 1029 if tex is None: 1030 tex = override['blend_data'].textures.new(prop_name, tex_type) 1031 slot.texture = tex 1032 return slot
@classmethod
def
parse_tex_node(cls, node, remove_serial=True):
636 @classmethod 637 def parse_tex_node(cls, node, remove_serial=True): 638 node_name = common.remove_serial_number(node.name, remove_serial) 639 tex_item = [node_name] 640 641 try: 642 img = node.image 643 except: 644 raise common.CM3D2ImportException('Materialプロパティのtexタイプの設定値取得に失敗しました。') 645 646 if img: 647 tex_name = common.remove_serial_number(img.name, remove_serial) 648 tex_name = common.re_png.sub(r'\1', tex_name) # 拡張子を除外 649 tex_item.append(tex_name) 650 651 if 'cm3d2_path' in img: 652 path = img['cm3d2_path'] 653 else: 654 path = bpy.path.abspath(img.filepath) 655 path = common.to_cm3d2path(path) 656 657 tex_item.append(path) 658 tex_map = node.texture_mapping 659 tex_trans = tex_map.translation[:2] 660 tex_scale = tex_map.scale[:2] 661 tex_item.append(tex_trans) 662 tex_item.append(tex_scale) 663 return tex_item
@classmethod
def
read(cls, reader, read_header=True, version=None):
677 @classmethod 678 def read(cls, reader, read_header=True, version=None): 679 if not read_header and version == None: 680 raise ValueError(f_tip_("The argument 'version' is required when 'read_header' is False for MaterialHandler.read()")) 681 mat_data = Material() 682 if version: 683 mat_data.version = version 684 mat_data.read(reader, read_header) 685 686 return mat_data
@classmethod
def
get_shader_prop_dynamic(cls, mate):
688 @classmethod 689 def get_shader_prop_dynamic(cls, mate, ): 690 lists = { 'VALUE':'f_list', 'RGB':'col_list', 'TEX_IMAGE':'tex_list' } 691 shader_prop = copy.deepcopy( DataHandler.get_shader_prop(mate.get('shader1')) ) 692 for node in mate.node_tree.nodes: 693 if node.name[0] != '_': 694 continue 695 list_name = lists.get(node.type) 696 if not list_name: 697 continue 698 prop_list = shader_prop.get(list_name) 699 if not prop_list: 700 prop_list = list() 701 shader_prop[list_name] = prop_list 702 if node.name in prop_list: 703 continue 704 prop_list.append(node.name) 705 return shader_prop
@classmethod
def
parse_mate(cls, mate, remove_serial=True):
707 @classmethod 708 def parse_mate(cls, mate, remove_serial=True): 709 mat_data = Material() 710 711 mate_name = common.remove_serial_number(mate.name, remove_serial) 712 mat_data.name1 = mate_name.lower() 713 mat_data.name2 = mate_name 714 mat_data.shader1 = mate['shader1'] 715 mat_data.shader2 = mate['shader2'] 716 717 nodes = mate.node_tree.nodes 718 shader_prop = cls.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(mat_data.shader1) 719 if shader_prop: 720 for node_name in shader_prop['tex_list']: 721 node = nodes.get(node_name) 722 if node and node.type == 'TEX_IMAGE': 723 tex_item = cls.parse_tex_node(node, remove_serial) 724 mat_data.tex_list.append(tex_item) 725 726 for node_name in shader_prop['col_list']: 727 node = nodes.get(node_name) 728 if node and node.type == 'RGB': 729 col_item = cls.parse_col_node(node, remove_serial) 730 mat_data.col_list.append(col_item) 731 732 for node_name in shader_prop['f_list']: 733 node = nodes.get(node_name) 734 if node and node.type == 'VALUE': 735 f_item = cls.parse_f_node(node, remove_serial) 736 mat_data.f_list.append(f_item) 737 738 #for node in nodes: 739 # if not node.name.startswith('_'): 740 # continue 741 # 742 # node_type = node.type 743 # if node_type == 'TEX_IMAGE': 744 # tex_item = cls.parse_tex_node(node, remove_serial) 745 # mat_data.tex_list.append(tex_item) 746 # elif node_type == 'RGB': 747 # col_item = cls.parse_col_node(node, remove_serial) 748 # mat_data.col_list.append(col_item) 749 # 750 # elif node_type == 'VALUE': 751 # f_item = cls.parse_f_node(node, remove_serial) 752 # mat_data.f_list.append(f_item) 753 754 return mat_data
@classmethod
def
parse_mate_old(cls, mate, remove_serial=True):
756 @classmethod 757 def parse_mate_old(cls, mate, remove_serial=True): 758 """material parser for blender-2.7x""" 759 mat_data = Material() 760 761 mate_name = common.remove_serial_number(mate.name, remove_serial) 762 mat_data.name1 = mate_name.lower() 763 mat_data.name2 = mate_name 764 mat_data.shader1 = mate['shader1'] 765 mat_data.shader2 = mate['shader2'] 766 767 for tindex, tslot in enumerate(mate.texture_slots): 768 if not tslot: 769 continue 770 771 tex = tslot.texture 772 node_name = common.remove_serial_number(tex.name, remove_serial) 773 # node_name = tslot.name 774 if mate.use_textures[tindex]: 775 tex_item = [node_name] 776 try: 777 img = tex.image 778 except: 779 raise Exception('Materialプロパティのtexタイプの設定値取得に失敗しました。') 780 781 if img: 782 tex_name = common.remove_serial_number(img.name, remove_serial) 783 tex_name = common.re_png.sub(r'\1', tex_name) # 拡張子を除外 784 tex_item.append(tex_name) 785 786 if 'cm3d2_path' in img: 787 path = img['cm3d2_path'] 788 else: 789 path = bpy.path.abspath(img.filepath) 790 path = common.to_cm3d2path(path) 791 792 tex_item.append(path) 793 tex_trans = tex.offset[:2] 794 tex_scale = tex.scale[:2] 795 tex_item.append(tex_trans) 796 tex_item.append(tex_scale) 797 798 mat_data.tex_list.append(tex_item) 799 elif tslot.use_rgb_to_intensity: 800 col = tex.color[:3] + [tslot.diffuse_color_factor] 801 mat_data.col_list.append([node_name, col[:4]]) 802 else: 803 f = tslot.diffuse_color_factor 804 mat_data.f_list.append([node_name, f]) 805 806 return mat_data
material parser for blender-2.7x
@classmethod
def
parse_text(cls, text):
808 @classmethod 809 def parse_text(cls, text): 810 mat_data = Material() 811 lines = text.split('\n') 812 813 mat_data.version = int(lines[0]) 814 mat_data.name1 = lines[1] 815 mat_data.name2 = lines[2] 816 mat_data.shader1 = lines[3] 817 mat_data.shader2 = lines[4] 818 819 line_seek = 5 820 while line_seek < len(lines): 821 node_type = common.line_trim(lines[line_seek]) 822 if not node_type: 823 line_seek += 1 824 continue 825 if node_type == 'tex': 826 prop_name = common.line_trim(lines[line_seek + 1]) 827 sub_type = common.line_trim(lines[line_seek + 2]) 828 if sub_type == 'tex2d': 829 line_seek += 3 830 tex_name = common.line_trim(lines[line_seek]) 831 tex_path = common.line_trim(lines[line_seek + 1]) 832 tex_map = common.line_trim(lines[line_seek + 2]).split(' ') 833 for map_datum in range(len(tex_map)): 834 tex_map[map_datum] = float(tex_map[map_datum]) 835 mat_data.tex_list.append([prop_name, tex_name, tex_path, tex_map[:2], tex_map[2:]]) 836 else: 837 mat_data.tex_list.append([prop_name]) 838 839 line_seek += 3 840 841 elif node_type == 'col': 842 prop_name = common.line_trim(lines[line_seek + 1]) 843 tex_map = common.line_trim(lines[line_seek + 2]).split(' ') 844 for map_datum in range(len(tex_map)): 845 tex_map[map_datum] = float(tex_map[map_datum]) 846 847 mat_data.col_list.append([prop_name, tex_map[:]]) 848 line_seek += 3 849 850 elif node_type == 'f': 851 prop_name = common.line_trim(lines[line_seek + 1]) 852 val = float(common.line_trim(lines[line_seek + 2])) 853 854 mat_data.f_list.append([prop_name, val]) 855 line_seek += 3 856 else: 857 raise Exception('未知の設定値タイプが見つかりました。') 858 859 return mat_data
@classmethod
def
apply_to(cls, override, mate, mat_data, replace_tex=True):
869 @classmethod 870 def apply_to(cls, override, mate, mat_data, replace_tex=True): 871 mate['shader1'] = mat_data.shader1 872 mate['shader2'] = mat_data.shader2 873 874 if mate.use_nodes is False: 875 mate.use_nodes = True 876 877 nodes = mate.node_tree.nodes 878 nodes.clear() 879 # OUTPUT_MATERIAL, BSDF_PRINCIPLEDは消さない 880 #if len(nodes) > 2: 881 # clear_nodes(nodes) 882 883 for tex_item in mat_data.tex_list: 884 prop_name = tex_item[0] 885 886 if len(tex_item) < 2: 887 common.create_tex(override, mate, prop_name) 888 else: 889 tex_name = tex_item[1] 890 tex_path = tex_item[2] 891 tex_map = tex_item[3] + tex_item[4] 892 common.create_tex(override, mate, prop_name, tex_name, tex_path, tex_path, tex_map, replace_tex) 893 894 for col_item in mat_data.col_list: 895 prop_name = col_item[0] 896 col = col_item[1] 897 common.create_col(override, mate, prop_name, col) 898 899 for item in mat_data.f_list: 900 prop_name = item[0] 901 f = item[1] 902 common.create_float(override, mate, prop_name, f) 903 904 for key, value in mat_data.custom_list.items(): 905 mate[key] = value 906 if type(value) == bool: 907 rna_ui = mate.get('_RNA_UI', dict()) 908 rna_ui[key] = { 909 "default" : value, 910 "min" : 0 , 911 "max" : 1 , 912 "soft_min" : 0 , 913 "soft_max" : 1 , 914 } 915 mate['_RNA_UI'] = rna_ui 916 917 918 919 align_nodes(mate)
@classmethod
def
apply_to_old( cls, override, mate, mat_data, replace_tex=True, decorate=True, skip_same_prop=True):
921 @classmethod 922 def apply_to_old(cls, override, mate, mat_data, replace_tex=True, decorate=True, skip_same_prop=True): 923 ob = override['active_object'] 924 me = ob.data 925 926 mate['shader1'] = mat_data.shader1 927 mate['shader2'] = mat_data.shader2 928 929 slot_index = 0 930 olds_slots = {} 931 read_texes = set() if skip_same_prop else None 932 933 for item in mat_data.tex_list: 934 prop_name = item[0] 935 if read_texes: 936 if prop_name in read_texes: 937 continue 938 read_texes.add(prop_name) 939 940 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'IMAGE') 941 slot.use_rgb_to_intensity = False 942 mate.use_textures[slot_index] = True 943 944 if len(item) > 4: 945 tex = slot.texture 946 tex_name = item[1] 947 tex_path = item[2] 948 if tex_name in read_texes: 949 continue 950 951 if tex_name != common.remove_serial_number(tex.image.name): 952 tex.image.name = tex_name 953 tex.image['cm3d2_path'] = tex_path 954 tex.image.filepath = tex.image['cm3d2_path'] 955 956 slot.offset = item[3] 957 slot.scale = item[4] 958 if replace_tex: 959 if common.replace_cm3d2_tex(tex.image, reload_path=True) and prop_name == '_MainTex': 960 for face in me.polygons: 961 if face.material_index == ob.active_material_index: 962 me.uv_textures.active.data[face.index].image = tex.image 963 slot_index += 1 964 965 for item in mat_data.col_list: 966 prop_name = item[0] 967 col = item[1] 968 969 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND') 970 971 mate.use_textures[slot_index] = False 972 slot.use_rgb_to_intensity = True 973 slot.color = col[:3] 974 slot.diffuse_color_factor = col[3] 975 976 slot_index += 1 977 978 for item in mat_data.f_list: 979 prop_name = item[0] 980 f = item[1] 981 982 slot = search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, 'BLEND') 983 984 mate.use_textures[slot_index] = False 985 slot.use_rgb_to_intensity = False 986 slot.diffuse_color_factor = f 987 988 slot_index += 1 989 990 # 存在しないスロットをクリア 991 for item_index in range(slot_index, len(mate.texture_slots)): 992 if mate.texture_slots[item_index]: 993 mate.texture_slots.clear(item_index) 994 995 # プレビューへの反映 996 for slot in mate.texture_slots: 997 if slot: 998 common.set_texture_color(slot) 999 1000 if decorate: 1001 common.decorate_material(mate, decorate, me, ob.active_material_index)
@staticmethod
def
search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, tex_type):
1003 @staticmethod 1004 def search_or_create_slot(override, mate, olds_slots, slot_index, prop_name, tex_type): 1005 tex = None 1006 slot_item = mate.texture_slots[slot_index] 1007 slot_name = slot_item.name if slot_item else '' 1008 1009 slot_name = common.remove_serial_number(slot_name) 1010 # 指定スロットが同名であればそのスロットをそのまま利用する 1011 if prop_name == slot_name: 1012 slot = slot_item 1013 else: 1014 # スロット名が異なり、既にスロットがある場合はキャッシュに格納 1015 if slot_item: 1016 olds_slots[slot_name] = slot_item 1017 slot = mate.texture_slots.create(slot_index) 1018 1019 if prop_name in olds_slots: 1020 tex = olds_slots.pop(prop_name).texture 1021 else: 1022 for item_index in range(slot_index + 1, len(mate.texture_slots)): 1023 slot_item = mate.texture_slots[item_index] 1024 if slot_item is None: 1025 break 1026 if prop_name == common.remove_serial_number(slot_item.name): 1027 tex = slot_item.texture 1028 break 1029 if tex is None: 1030 tex = override['blend_data'].textures.new(prop_name, tex_type) 1031 slot.texture = tex 1032 return slot
def
clear_nodes(nodes):
def
align_nodes(mate):
1041def align_nodes(mate): 1042 nodes = mate.node_tree.nodes 1043 # Principled BSDFがある前提での整列 1044 bsdf = nodes.get('Principled BSDF') 1045 base_location = (10, 300) 1046 if bsdf: 1047 main_tex = nodes.get('_MainTex') 1048 if main_tex: 1049 mate.node_tree.links.new(bsdf.inputs['Base Color'], main_tex.outputs['Color']) 1050 mate.node_tree.links.new(bsdf.inputs['Alpha'], main_tex.outputs['Alpha']) 1051 shininess = nodes.get('_Shininess') 1052 if shininess: 1053 mate.node_tree.links.new(bsdf.inputs['Specular'], shininess.outputs[0]) 1054 base_location = bsdf.location 1055 1056 shader_name = mate.get('shader1') 1057 if shader_name: 1058 location_x = base_location[0] - 400 1059 location_y = base_location[1] + 60 1060 shader_prop = MaterialHandler.get_shader_prop_dynamic(mate) #DataHandler.get_shader_prop(shader_name) 1061 node_list = shader_prop.get('tex_list') 1062 if node_list: 1063 for node_name in node_list: 1064 node = nodes.get(node_name) 1065 if node: 1066 node.location = (location_x, location_y) 1067 node.hide = True 1068 location_y -= 60 1069 1070 col_list = shader_prop.get('col_list') 1071 if col_list: 1072 for node_name in col_list: 1073 node = nodes.get(node_name) 1074 if node: 1075 node.location = (location_x, location_y) 1076 location_y -= 200 1077 1078 f_list = shader_prop.get('f_list') 1079 if f_list: 1080 for node_name in f_list: 1081 node = nodes.get(node_name) 1082 if node: 1083 node.location = (location_x, location_y) 1084 location_y -= 90