Package kawa_scripts

kawa_scripts is an addon and package of useful methods for Blender 2.8x+ made by Kawashirov. It's designed to use mostly form Python interactive console or from Python scripts.

Basically this addon provides powerful tool for making fully-automated script for preparing, finalizing and baking lots of raw parts (Objects, Meshes, Modifiers, Materials) into few export-ready assets.

Besides internal API for building, there is a few useful operators, especially for editing Shape Keys (see kawa_scripts.shapekeys) and applying Modifiers (see kawa_scripts.modifiers).

Documentation is not yet finished, sorry. My English is not very good, also sorry.

Downloads, Installation and Contacts

You can find everything related to this on a Github page.

kawa_scripts distributed as Blender addon. Addon should work on all verisons of Blender from 2.80, but 2.9x is recommended.

You can find ready-to-install zipped addon on Releases page.

To install you should go Edit > Preferences > Add-ons, click "Install" and select downloaded .zip archive.

addons_page.png

There is no any auto-updaters, if you want to upgrade kawa_scripts, you should remove old version and install fresh one.

After installation, you can use all features programmatically without UI by import kawa_sripts in Python Console or in .py text assets.

You can contact me here:

Please follow and support me on ko-fi too ❤

SupportMe_blue@60.png

Features

TODO ¯_(ツ)_/¯

How to use. Example of VRChat world: Russian Apartament: Хрущевка

My world "Russian Apartament: Хрущевка" for VRChat uses this addon for baking large hierarchy of objects and 400+ materials into few combined objects and single 4K x 4K PBR atlas!

And every time I change something in my project I don't need to set up anything: just running building script bake2.py, waiting a few minutes, and my .fbx + .png atlases are ready to be put into Unity project.

You can use this building script as reference. Most of the features in kawa_scripts I implemented for my projects, especially this one.

There is some utility functions in bake2.py, but main are make_working_hierarchy and make_atlas.

First one, make_working_hierarchy

Does the following:

  • Duplicates entire hierarchy of raw Objects into separate scene.
  • Applies proper objects naming to duplicated hierarchy.
  • Instantiates all collections (with proper objects naming).
  • Makes single-user objects
  • Converts non-mesh (Curves) objects into mesh-objects
  • Instantiates all material slots (Object-overrides)
  • Applies all modifiers of Objects.
  • Applies all transform scales
  • Combines some parts of hierarchy with a lots of small objects into few large objects.

All these complex operations made using BaseInstantiator and BaseMeshCombiner.

For example this complex hierarchy:

combiner_example_before.png

flattens to this:

combiner_example_after.png

Second one, make_atlas

Packs most of the materials into large texture atlas: KhrushchyovkaAtlas-D+A.png

(Image scaled down to 2k because of hosting limits.)

There is almost full support of PBR layers such as Normals, Metallic, Smoothness, Emission. Atlas is baked using Blender's bake features, so you can build own complex PBR materials using Cycles shader nodes and bake them together with other materials into same texture atlas.

Expand source code
# Kawashirov's Scripts (c) 2019 by Sergey V. Kawashirov
#
# Kawashirov's Scripts is licensed under a
# Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
#
# You should have received a copy of the license along with this
# work.  If not, see <http://creativecommons.org/licenses/by-nc-sa/3.0/>.
#
#

"""
`kawa_scripts` is an addon and package of useful methods for **Blender 2.8x+**
made by Kawashirov.
It's designed to use mostly form Python interactive console or from Python scripts.

Basically this addon provides powerful tool for making fully-automated script
for preparing, finalizing and baking lots of raw parts (Objects, Meshes, Modifiers, Materials)
into few export-ready assets.

Besides internal API for building, there is a few useful operators,
especially for editing Shape Keys (see `shapekeys`) and applying Modifiers (see `modifiers`).

**Documentation is not yet finished, sorry.**
**My English is not very good, also sorry.**

.. include:: ./documentation.md
"""

from collections import OrderedDict as _OrderedDict

import typing as _typing
if _typing.TYPE_CHECKING:
        from types import ModuleType
        from typing import Dict
        # Эти итак заимпортированы через __import__ но PyCharm их не видит
        from . import shapekeys, commons, modifiers, vertex_groups

bl_info = {
        "name": "Kawashirov's Scripts",
        "author": "Sergey V. Kawashirov",
        "description": "Kawashirov's Scripts for Unity and VRChat content creation",
        "location": "There is no UI. Use it from scripts or console by `import kawa_scripts` (or whatever)",
        "wiki_url": "",
        "version": (2021, 8, 14),
        "blender": (2, 83, 0),
        "category": "Object",
}
addon_name = __name__

if "bpy" in locals() and "_modules_loaded" in locals():
        from importlib import reload

        print("Reloading Kawashirov's Scripts...")
        for key, mod in list(_modules_loaded.items()):
                print("Reloading {0}...".format(repr(mod)))
                _modules_loaded[key] = reload(mod)
        del reload
        print("Reloaded Kawashirov's Scripts!")

_modules = [
        "_internals",
        "atlas_baker",
        "combiner",
        "commons",
        "instantiator",
        "modifiers",
        "reporter",
        "shader_nodes",
        "shapekeys",
        "tex_size_finder",
        "uv",
        "vertex_groups",
]

import bpy

__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = _OrderedDict()  # type: Dict[str, ModuleType]
for _mod_name in _modules:
        _modules_loaded[_mod_name] = _namespace.get(_mod_name)
del _namespace


from ._internals import log


def _shape_key_edit_mode_context_menu(self, context):
        self.layout.separator()  # EDIT-mode select
        self.layout.operator(shapekeys.OperatorSelectVerticesAffectedByShapeKey.bl_idname, icon='VERTEXSEL')
        self.layout.separator()  # EDIT-mode revert
        self.layout.operator(shapekeys.OperatorRevertSelectedInActiveToBasis.bl_idname, icon='KEY_DEHLT')
        self.layout.operator(shapekeys.OperatorRevertSelectedInAllToBasis.bl_idname, icon='KEY_DEHLT')
        self.layout.separator()  # EDIT-mode apply
        self.layout.operator(shapekeys.OperatorApplySelectedInActiveToBasis.bl_idname, icon='KEYINGSET')
        self.layout.operator(shapekeys.OperatorApplySelectedInActiveToAll.bl_idname, icon='KEYINGSET')


def _shape_key_object_mode_context_menu(self, context):
        self.layout.separator()  # OBJECT-mode apply
        self.layout.operator(shapekeys.OperatorApplyActiveToBasis.bl_idname, icon='KEYINGSET')
        self.layout.operator(shapekeys.OperatorApplyActiveToAll.bl_idname, icon='KEYINGSET')
        self.layout.separator()  # OBJECT-mode clean
        self.layout.operator(shapekeys.OperatorCleanupActive.bl_idname, icon='KEY_DEHLT')
        self.layout.operator(shapekeys.OperatorCleanupAll.bl_idname, icon='KEY_DEHLT')
        self.layout.operator(shapekeys.OperatorRemoveEmpty.bl_idname, icon='KEY_DEHLT')


# # #


class _MESH_MT_shape_key_context_kawa_sub_menu(bpy.types.Menu):
        bl_label = "Kawashirov"
        bl_idname = "MESH_MT_shape_key_context_kawa_sub_menu"

        def draw(self, context):
                _shape_key_edit_mode_context_menu(self, context)
                _shape_key_object_mode_context_menu(self, context)


def _MESH_MT_shape_key_context_menu(self, context):
        self.layout.menu(_MESH_MT_shape_key_context_kawa_sub_menu.bl_idname)


# # #


def _MESH_MT_vertex_group_context_menu(self, context):
        self.layout.separator()
        self.layout.operator(vertex_groups.OperatorRemoveEmpty.bl_idname, icon='X')


# # #


class _VIEW3D_MT_object_kawa_sub_menu(bpy.types.Menu):
        bl_label = "Kawashirov"
        bl_idname = "VIEW3D_MT_object_kawa_sub_menu"

        def draw(self, context):
                self.layout.separator()  # transforms
                self.layout.operator(commons.KawaApplyParentInverseMatrices.bl_idname, icon='ORIENTATION_LOCAL')
                self.layout.separator()  # modifiers
                self.layout.operator(modifiers.KawaApplyDeformModifierHighPrecision.bl_idname, icon='MODIFIER')
                self.layout.operator(modifiers.KawaApplyAllModifiersHighPrecision.bl_idname, icon='MODIFIER')
                self.layout.operator(modifiers.KawaApplyArmatureToMeshesHighPrecision.bl_idname, icon='ARMATURE_DATA')
                _shape_key_object_mode_context_menu(self, context)
                self.layout.separator()  # vertex groups
                self.layout.operator(vertex_groups.OperatorRemoveEmpty.bl_idname, icon='X')


def _VIEW3D_MT_object(self, context):
        self.layout.menu(_VIEW3D_MT_object_kawa_sub_menu.bl_idname)


# # #

class _VIEW3D_MT_edit_mesh_kawa_sub_menu(bpy.types.Menu):
        bl_label = "Kawashirov"
        bl_idname = "VIEW3D_MT_edit_mesh_kawa_sub_menu"
        
        def draw(self, context):
                _shape_key_edit_mode_context_menu(self, context)


def _VIEW3D_MT_edit_mesh_vertices(self, context):
        self.layout.menu(_VIEW3D_MT_edit_mesh_kawa_sub_menu.bl_idname)


def _VIEW3D_MT_edit_mesh_context_menu(self, context):
        self.layout.menu(_VIEW3D_MT_edit_mesh_kawa_sub_menu.bl_idname)


# # #


def register():
        print("Hello from {0}!".format(__name__))
        import logging
        from datetime import datetime
        
        log.py_log.setLevel(logging.DEBUG)
        if len(log.py_log.handlers) < 1:
                import tempfile
                print("Updating kawa_scripts log handler!")
                log_file = tempfile.gettempdir() + '/' + datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + '-kawa.log'
                log_formatter = logging.Formatter(fmt='[%(asctime)s][%(levelname)s] %(message)s')
                log_handler = logging.FileHandler(log_file, mode='w', encoding='utf-8', delay=False)
                log_handler.setFormatter(log_formatter)
                log.py_log.addHandler(log_handler)
                log.info("Log handler updated!")
        
        from bpy.utils import register_class
        for mod in _modules_loaded.values():
                if mod and hasattr(mod, 'classes'):
                        for cls in mod.classes:
                                register_class(cls)
        
        bpy.utils.register_class(_MESH_MT_shape_key_context_kawa_sub_menu)
        bpy.utils.register_class(_VIEW3D_MT_object_kawa_sub_menu)
        bpy.utils.register_class(_VIEW3D_MT_edit_mesh_kawa_sub_menu)
        
        bpy.types.VIEW3D_MT_object.append(_VIEW3D_MT_object)
        bpy.types.VIEW3D_MT_object_context_menu.append(_VIEW3D_MT_object)
        bpy.types.VIEW3D_MT_edit_mesh_context_menu.append(_VIEW3D_MT_edit_mesh_context_menu)
        bpy.types.VIEW3D_MT_edit_mesh_vertices.append(_VIEW3D_MT_edit_mesh_vertices)
        bpy.types.MESH_MT_shape_key_context_menu.append(_MESH_MT_shape_key_context_menu)
        bpy.types.MESH_MT_vertex_group_context_menu.append(_MESH_MT_vertex_group_context_menu)
        
        log.info("Hello from {0} once again!".format(__name__))


def unregister():
        from bpy.utils import unregister_class

        bpy.types.VIEW3D_MT_object.remove(_VIEW3D_MT_object)
        bpy.types.VIEW3D_MT_object_context_menu.remove(_VIEW3D_MT_object)
        bpy.types.VIEW3D_MT_edit_mesh_context_menu.remove(_VIEW3D_MT_edit_mesh_context_menu)
        bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(_VIEW3D_MT_edit_mesh_vertices)
        bpy.types.MESH_MT_shape_key_context_menu.remove(_MESH_MT_shape_key_context_menu)
        bpy.types.MESH_MT_vertex_group_context_menu.remove(_MESH_MT_vertex_group_context_menu)
        
        bpy.utils.unregister_class(_MESH_MT_shape_key_context_kawa_sub_menu)
        bpy.utils.unregister_class(_VIEW3D_MT_object_kawa_sub_menu)
        bpy.utils.unregister_class(_VIEW3D_MT_edit_mesh_kawa_sub_menu)
        
        for mod in reversed(_modules_loaded.values()):
                if hasattr(mod, 'classes'):
                        for cls in reversed(mod.classes):
                                if cls.is_registered:
                                        unregister_class(cls)
        
        log.info("Goodbye from {0}...".format(__name__))


__pdoc__ = dict()
__pdoc__['register'] = False
__pdoc__['unregister'] = False

Sub-modules

kawa_scripts.atlas_baker

Tool for baking a lots of PBR materials on a lots of Objects into single texture atlas. See BaseAtlasBaker.

kawa_scripts.combiner

Tool for combining few objects into single one. See BaseMeshCombiner.

kawa_scripts.commons
kawa_scripts.instantiator
kawa_scripts.modifiers
kawa_scripts.reporter
kawa_scripts.shader_nodes
kawa_scripts.shapekeys

Useful tools for Shape Keys …

kawa_scripts.tex_size_finder

Tool for figuring out a texture size of material. See TexSizeFinder.

kawa_scripts.uv

Useful tools for UV Layers

kawa_scripts.vertex_groups

Useful tools for Vertex Groups