-
-
Save rivetchip/bdbc5db09465a85c34935a6097e35c11 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bpy | |
import glob | |
import os | |
import os.path as path | |
import time | |
import uuid | |
from datetime import date | |
from math import radians | |
""" | |
Sources : | |
https://gist.github.com/Psyda/7b795a392d0f7ec8ff1a4345b08e17a3 | |
https://projects.blender.org/blender/blender/issues/93893 | |
https://github.com/Gorgious56/asset_browser_utilities/blob/master/core/library/tool.py#L12 | |
Launch : | |
/blender/app/blender --background --python /blender/assets/Kitbash3D/kitbash3d-create-assets.py | |
""" | |
# -------------------------------------------------------------------------------------------- | |
library_dirname = path.dirname(path.abspath(__file__)) | |
kit_catalog = {} | |
def main(): | |
today = str(date.today()) | |
blendcats_filepath = path.join(library_dirname, "blender_assets.cats.txt") | |
# clean previous assets catalogs | |
if path.exists(blendcats_filepath): | |
os.remove(blendcats_filepath) | |
if path.exists(blendcats_filepath + "~"): | |
os.remove(blendcats_filepath + "~") | |
# loop on subfolder only, not root files | |
for entry in os.scandir(library_dirname): | |
if not entry.name.startswith(".") and entry.is_dir(): | |
for file in glob.glob(path.join(library_dirname, entry.name, "**/*.blend"), recursive=True): | |
process_blender_file(file) | |
with open(blendcats_filepath, "a") as file: | |
file.write(f"# Generation date: {today}\n") | |
file.write("VERSION 1\n") | |
for catalog_name, catalog_uid in kit_catalog.items(): | |
catalog_hyphen = catalog_name.replace("/", "-") | |
file.write(f"{catalog_uid}:{catalog_name}:{catalog_hyphen}\n") | |
print("finish! quitting...") | |
bpy.ops.wm.quit_blender() | |
def process_blender_file(blend_filepath): | |
blend_dirname = path.dirname(blend_filepath) | |
# simulate open file from directory | |
os.chdir(blend_dirname) | |
bpy.ops.wm.open_mainfile(filepath=blend_filepath) | |
# create catalogs mappings | |
catalog_name = path.basename(blend_dirname) | |
if catalog_name not in kit_catalog: | |
kit_catalog[catalog_name] = str(uuid.uuid4()) | |
catalog_uid = kit_catalog[catalog_name] | |
# remove all others scenes | |
for scene in bpy.data.scenes: | |
if not scene.name.startswith("KB3D_"): | |
bpy.data.scenes.remove(scene, do_unlink=True) | |
# checks for missings files | |
missing_images = list_missing_blend_images() | |
if len(missing_images) > 0: | |
print("########## MISSING IMAGES: ##########") | |
for missing_img in missing_images: | |
print(f"# {missing_img}") | |
print("####################################") | |
# get current scene | |
scene = bpy.context.scene | |
# remove all cameras | |
for obj in scene.objects: | |
if obj.type == "CAMERA": | |
bpy.data.objects.remove(obj) | |
# remove all empties empty | |
for obj in scene.objects: | |
if obj.type == "EMPTY" and len(obj.children) == 0: | |
bpy.data.objects.remove(obj) | |
# current collections assets already done | |
collections_assets_names = [col.name for col in scene.collection.children] | |
# convert all empty to collection | |
for obj in scene.objects: | |
if obj.type == "EMPTY" and obj.name.startswith("KB3D_"): | |
obj.location = (0, 0, 0) | |
# wrap object to collection if not already | |
if obj.name not in collections_assets_names: | |
create_collection_from_object(scene, obj) | |
# add default camera/world for rendering | |
scene.camera = create_scene_camera(scene) | |
scene.world = create_scene_world(scene) | |
scene_apply_rendering_settings(scene) | |
# create assets from the new collections | |
for col in scene.collection.children: | |
if col.asset_data is None: | |
col.asset_mark() | |
col.asset_data.catalog_id = catalog_uid | |
if col.preview is None: | |
generate_asset_preview_from_collection(scene, col) | |
# cleanup preview datas and render | |
bpy.data.objects.remove(scene.camera) | |
bpy.data.worlds.remove(scene.world) | |
bpy.data.orphans_purge(do_recursive=True) | |
# if bpy.data.is_dirty: | |
time.sleep(1) | |
bpy.context.preferences.filepaths.save_version = 0 # No backup blends needed | |
bpy.ops.wm.save_as_mainfile(filepath=blend_filepath) | |
def scene_apply_rendering_settings(scene): | |
scene.render.engine = "CYCLES" | |
scene.cycles.device = "GPU" | |
scene.cycles.feature_set = "SUPPORTED" | |
scene.cycles.samples = 10 | |
scene.cycles.use_denoising = False | |
scene.cycles.use_auto_tile = False | |
scene.render.use_persistent_data = True | |
scene.render.resolution_x = 200 | |
scene.render.resolution_y = 200 | |
scene.render.image_settings.file_format = "PNG" | |
scene.render.image_settings.color_mode = "RGBA" | |
scene.render.image_settings.compression = 15 | |
scene.render.image_settings.color_management = "FOLLOW_SCENE" | |
scene.render.film_transparent = True | |
scene.display_settings.display_device = "sRGB" | |
scene.view_settings.view_transform = "Standard" | |
scene.view_settings.look = "Medium Contrast" | |
scene.view_settings.exposure = 0.00 | |
scene.view_settings.gamma = 1 | |
def create_scene_camera(scene): | |
camera = bpy.data.cameras.new("Camera") | |
camera_obj = bpy.data.objects.new("Camera Render", camera) | |
camera_obj.location = (0, 0, 0) | |
camera_obj.rotation_euler = (radians(75), radians(0), radians(45)) | |
scene.collection.objects.link(camera_obj) | |
return camera_obj | |
def create_scene_world(scene): | |
world = bpy.data.worlds.new("World Render") | |
world.use_nodes = True | |
node_tree = world.node_tree | |
node_tree.nodes.clear() | |
node_background = node_tree.nodes.new(type="ShaderNodeBackground") | |
node_background.inputs.get("Color").default_value = (1, 1, 1, 1) # white | |
node_background.inputs.get("Strength").default_value = 1 | |
node_output = node_tree.nodes.new(type="ShaderNodeOutputWorld") | |
node_tree.links.new(input=node_background.outputs.get("Background"), output=node_output.inputs.get("Surface")) | |
return world | |
def create_collection_from_object(scene, obj): | |
print("create_collection:", obj.name) | |
# create a collection and add it to scene main collection | |
new_collection = bpy.data.collections.new(obj.name) | |
scene.collection.children.link(new_collection) | |
# move all objects to collection | |
new_collection.objects.link(obj) | |
for child in obj.children: | |
new_collection.objects.link(child) | |
# unlink all children from previous parent | |
scene.collection.objects.unlink(obj) | |
for child in obj.children: | |
scene.collection.objects.unlink(child) | |
def generate_asset_preview_from_collection(scene, col): | |
print("generate_preview:", col.name) | |
# mark other collections as non-visible | |
for col2 in scene.collection.children: | |
col2.hide_render = (col2.name != col.name) | |
# center the camera acording to objects in collection | |
visible_objects = [obj.name for obj in col.all_objects.values()] | |
for obj in scene.objects: | |
obj.select_set(obj.name in visible_objects) | |
bpy.ops.view3d.camera_to_view_selected() | |
# finally, render | |
preview_filename = str(uuid.uuid4()) + ".png" | |
scene.render.filepath = path.join(bpy.app.tempdir, preview_filename) | |
bpy.ops.render.render(write_still=True) | |
while bpy.app.is_job_running("RENDER"): | |
print("Waiting for render...") | |
time.sleep(1) | |
with bpy.context.temp_override(id=col): | |
bpy.ops.ed.lib_id_load_custom_preview(filepath=str(scene.render.filepath)) | |
# re-set default visility values | |
for col2 in scene.collection.children: | |
col2.hide_render = False | |
def list_missing_blend_libs(): | |
return [bpy.path.abspath(lib.filepath) for lib in bpy.data.libraries | |
if not path.exists(bpy.path.abspath(lib.filepath))] | |
def list_missing_blend_images(): | |
return [bpy.path.abspath(img.filepath) for img in bpy.data.images | |
if img.source == "FILE" and img.packed_file is None and not path.exists(bpy.path.abspath(img.filepath))] | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment