feat: Add KoikatsuBlenderPipeline addon with import/export functionality
- Add core addon files (__init__.py, KKPanel.py, preferences.py, common.py) - Add import pipeline with armature, mesh, and material modification modules - Add export pipeline with material baking and FBX preparation utilities - Add material combiner tool for texture atlas generation and optimization - Add extras utilities for animation, rigging, and asset management - Add bone orientation data from better_fbx for accurate skeletal structure - Add comprehensive documentation and wiki with multi-language support (EN, JP, ZH) - Add animation library retargeting lists for ARP and Rokoko motion capture - Add Rigify integration scripts for advanced rigging workflows - Add shader file (KK Shader V8.0.blend) for material rendering - Add manifest and license files for addon distribution - Add changelog documenting version history and improvements - Initialize complete Blender addon project for Koikatsu character import/export
This commit is contained in:
114
extras/matcombsetup.py
Normal file
114
extras/matcombsetup.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import bpy
|
||||
from ..interface.dictionary_en import t
|
||||
from .. import common as c
|
||||
|
||||
class mat_comb_setup(bpy.types.Operator):
|
||||
bl_idname = "kkbp.matcombsetup"
|
||||
bl_label = "Material combiner setup"
|
||||
bl_description = t('mat_comb_tt')
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
def execute(self, context):
|
||||
def recurLayerCollection(layerColl, collName):
|
||||
found = None
|
||||
if (layerColl.name == collName):
|
||||
return layerColl
|
||||
for layer in layerColl.children:
|
||||
found = recurLayerCollection(layer, collName)
|
||||
if found:
|
||||
return found
|
||||
|
||||
def remove_orphan_data():
|
||||
#revert the image back from the atlas file to the baked file
|
||||
for mat in bpy.data.materials:
|
||||
if mat.name[-4:] == '-ORG':
|
||||
simplified_name = mat.name[:-4]
|
||||
if bpy.data.materials.get(simplified_name):
|
||||
simplified_mat = bpy.data.materials[simplified_name]
|
||||
for bake_type in ['light', 'dark', 'normal']:
|
||||
simplified_mat.node_tree.nodes['textures'].node_tree.nodes[bake_type].image = bpy.data.images.get(simplified_name + ' ' + bake_type + '.png')
|
||||
#delete orphan data
|
||||
for cat in [bpy.data.armatures, bpy.data.objects, bpy.data.meshes, bpy.data.materials, bpy.data.images, bpy.data.node_groups]:
|
||||
for block in cat:
|
||||
if block.users == 0:
|
||||
cat.remove(block)
|
||||
|
||||
if bpy.data.collections.get(c.get_name() + ' atlas'):
|
||||
c.kklog('deleting previous collection "Model with atlas" and regenerating atlas model...')
|
||||
def del_collection(coll):
|
||||
for c in coll.children:
|
||||
del_collection(c)
|
||||
bpy.data.collections.remove(coll,do_unlink=True)
|
||||
del_collection(bpy.data.collections[c.get_name() + ' atlas'])
|
||||
remove_orphan_data()
|
||||
#show the original collection again
|
||||
layer_collection = bpy.context.view_layer.layer_collection
|
||||
layerColl = recurLayerCollection(layer_collection, 'Scene Collection')
|
||||
bpy.context.view_layer.active_layer_collection = layerColl
|
||||
bpy.context.scene.view_layers[0].active_layer_collection.children[0].exclude = False
|
||||
|
||||
#Change the Active LayerCollection to 'My Collection'
|
||||
layer_collection = bpy.context.view_layer.layer_collection
|
||||
layerColl = recurLayerCollection(layer_collection, c.get_name())
|
||||
bpy.context.view_layer.active_layer_collection = layerColl
|
||||
|
||||
# https://blender.stackexchange.com/questions/157828/how-to-duplicate-a-certain-collection-using-python
|
||||
from collections import defaultdict
|
||||
def copy_objects(from_col, to_col, linked, dupe_lut):
|
||||
for o in from_col.objects:
|
||||
dupe = o.copy()
|
||||
if not linked and o.data:
|
||||
dupe.data = dupe.data.copy()
|
||||
to_col.objects.link(dupe)
|
||||
dupe_lut[o] = dupe
|
||||
def copy(parent, collection, linked=False):
|
||||
dupe_lut = defaultdict(lambda : None)
|
||||
def _copy(parent, collection, linked=False):
|
||||
if collection.name == 'Bone Widgets':
|
||||
return
|
||||
cc = bpy.data.collections.new(collection.name)
|
||||
copy_objects(collection, cc, linked, dupe_lut)
|
||||
for c in collection.children:
|
||||
_copy(cc, c, linked)
|
||||
parent.children.link(cc)
|
||||
_copy(parent, collection, linked)
|
||||
for o, dupe in tuple(dupe_lut.items()):
|
||||
parent = dupe_lut[o.parent]
|
||||
if parent:
|
||||
dupe.parent = parent
|
||||
|
||||
context = bpy.context
|
||||
scene = context.scene
|
||||
col = context.collection
|
||||
assert(col is not scene.collection)
|
||||
copy(scene.collection, col)
|
||||
|
||||
#setup materials for the combiner script
|
||||
for obj in [o for o in bpy.data.collections[c.get_name() + '.001'].all_objects if not o.hide_get() and o.type == 'MESH']:
|
||||
for mat in [mat_slot.material for mat_slot in obj.material_slots if mat_slot.material.get('simple')]:
|
||||
nodes = mat.node_tree.nodes
|
||||
links = mat.node_tree.links
|
||||
emissive_node = nodes.new('ShaderNodeEmission')
|
||||
image_node = nodes.new('ShaderNodeTexImage')
|
||||
links.new(emissive_node.inputs[0], image_node.outputs[0])
|
||||
image_node.image = nodes['textures'].node_tree.nodes['light'].image
|
||||
context.view_layer.objects.active = obj
|
||||
bpy.ops.object.material_slot_remove_unused()
|
||||
|
||||
#update the modifiers
|
||||
for mod in obj.modifiers:
|
||||
if mod.type == 'ARMATURE':
|
||||
#fix the armature modifier to use the copied aramture
|
||||
copied_armature = [o for o in bpy.data.collections[c.get_name() + '.001'].all_objects if o.type == 'ARMATURE'][0]
|
||||
mod.object = copied_armature
|
||||
elif mod.type == 'SOLIDIFY':
|
||||
#disable the outline on the atlased object because I don't feel like fixing it
|
||||
obj.modifiers['Outline Modifier'].show_render = False
|
||||
obj.modifiers['Outline Modifier'].show_viewport = False
|
||||
elif mod.type == 'UV_WARP':
|
||||
#fix the UV warp modifier to use the copied armature
|
||||
copied_armature = [o for o in bpy.data.collections[c.get_name() + '.001'].all_objects if o.type == 'ARMATURE'][0]
|
||||
mod.object_from = copied_armature
|
||||
mod.object_to = copied_armature
|
||||
|
||||
return {'FINISHED'}
|
||||
Reference in New Issue
Block a user