mantrakp's picture
Isolate reference inference in a dedicated ZeroGPU worker
5c331a4 verified
Raw
History Blame Contribute Delete
3.39 kB
"""Blender CPU review renders, including labeled component previews."""
import argparse
import math
from pathlib import Path
import sys
import bpy
from mathutils import Vector
sys.path.insert(0, str(Path(__file__).resolve().parent))
from render_settings import configure_cpu_render
parser = argparse.ArgumentParser()
parser.add_argument('--input', required=True)
parser.add_argument('--output', required=True)
parser.add_argument('--components', action='store_true')
args = parser.parse_args(sys.argv[sys.argv.index('--') + 1:])
folder = Path(args.output)
folder.mkdir(parents=True, exist_ok=True)
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
bpy.ops.import_scene.gltf(filepath=args.input)
meshes = [obj for obj in bpy.context.scene.objects if obj.type == 'MESH']
if not meshes:
raise RuntimeError('No mesh to render')
scene = bpy.context.scene
configure_cpu_render(scene)
scene.cycles.samples = 8 if scene.cycles.use_denoising else 32
scene.render.resolution_x = scene.render.resolution_y = 384
scene.render.resolution_percentage = 100
scene.render.image_settings.file_format = 'PNG'
scene.world.use_nodes = True
scene.world.node_tree.nodes['Background'].inputs['Color'].default_value = (.15, .17, .2, 1)
scene.world.node_tree.nodes['Background'].inputs['Strength'].default_value = .6
points = [obj.matrix_world @ Vector(corner) for obj in meshes for corner in obj.bound_box]
low = Vector([min(p[i] for p in points) for i in range(3)])
high = Vector([max(p[i] for p in points) for i in range(3)])
center, size = (low + high) / 2, max(high - low)
if not math.isfinite(size) or size <= 0:
raise RuntimeError('Invalid model bounds')
for name, xyz, energy in [('key', (3, -4, 5), 1000), ('fill', (-3, -2, 2), 600), ('rim', (2, 4, 4), 900)]:
data = bpy.data.lights.new(name, 'AREA')
data.energy = energy * size * size / 9
data.shape = 'DISK'
data.size = size * 1.5
obj = bpy.data.objects.new(name, data)
scene.collection.objects.link(obj)
obj.location = center + Vector(xyz) * size / 3
obj.rotation_euler = (center - obj.location).to_track_quat('-Z', 'Y').to_euler()
camera_data = bpy.data.cameras.new('Review')
camera = bpy.data.objects.new('Review', camera_data)
scene.collection.objects.link(camera)
scene.camera = camera
camera_data.type = 'ORTHO'
camera_data.clip_end = size * 100 + 100
def render(name, direction, target=center, scale=size):
camera_data.ortho_scale = scale * 1.3
camera.location = target + Vector(direction).normalized() * size * 3
camera.rotation_euler = (target - camera.location).to_track_quat('-Z', 'Y').to_euler()
scene.render.filepath = str(folder / (name + '.png'))
bpy.ops.render.render(write_still=True)
for name, direction in [('front', (0, -1, .1)), ('back', (0, 1, .1)), ('left', (-1, 0, .1)),
('right', (1, 0, .1)), ('top', (0, .001, 1)), ('quarter', (1, -1.4, .7))]:
render(name, direction)
if args.components:
for obj in meshes:
for other in meshes:
other.hide_render = other != obj
bounds = [obj.matrix_world @ Vector(corner) for corner in obj.bound_box]
minimum = Vector([min(p[i] for p in bounds) for i in range(3)])
maximum = Vector([max(p[i] for p in bounds) for i in range(3)])
render(obj.name, (1, -1.4, .7), (minimum + maximum) / 2, max(maximum - minimum))