60 lines
5.1 KiB
Python
60 lines
5.1 KiB
Python
"""Release-art model renders. Uses existing meshes, no image generation."""
|
|
import bpy, bmesh, json, math
|
|
from pathlib import Path
|
|
from mathutils import Vector
|
|
|
|
work=Path(__file__).parent
|
|
out=work.parent/'outputs/FunctionalTurrets-release-art'
|
|
out.mkdir(parents=True,exist_ok=True)
|
|
def linear(x):return x/12.92 if x<=0.04045 else ((x+0.055)/1.055)**2.4
|
|
def rgba(hex):return tuple(linear(int(hex[i:i+2],16)/255) for i in (0,2,4))+(1,)
|
|
for kind in ['lav','ferret']:
|
|
bpy.ops.wm.open_mainfile(filepath=str(work/('lav_turret_rig.blend' if kind=='lav' else 'ferret_turret_rig.blend')))
|
|
rig=bpy.data.objects['KLT_LAVSkeleton' if kind=='lav' else 'KFT_FerretSkeleton']
|
|
rig.animation_data_clear()
|
|
for bone in rig.pose.bones:bone.matrix_basis.identity()
|
|
upper=bpy.data.objects['KLT_LAVTurret' if kind=='lav' else 'KFT_FerretTurret']
|
|
keep=({'KLT_LAVTurret','KLT_LAVHatch','KLT_LAVViewports','KLT_LAVRadio','KLT_LAVInterior'} if kind=='lav' else {'KFT_FerretTurret','KFT_FerretHatch'})
|
|
for obj in list(bpy.context.scene.objects):
|
|
if obj.type=='MESH' and obj.name not in keep:bpy.data.objects.remove(obj,do_unlink=True)
|
|
group=upper.vertex_groups['body_bone'].index
|
|
fixed={v.index for v in upper.data.vertices if any(g.group==group and g.weight>.99 for g in v.groups)}
|
|
faces={p.index for p in upper.data.polygons if all(i in fixed for i in p.vertices)}
|
|
assert len(faces)==(52 if kind=='lav' else 16)
|
|
bm=bmesh.new();bm.from_mesh(upper.data);bm.faces.ensure_lookup_table()
|
|
bmesh.ops.delete(bm,geom=[f for f in bm.faces if f.index in faces],context='FACES');bm.to_mesh(upper.data);bm.free()
|
|
mat=bpy.data.materials.new('HRSys distressed coral');mat.use_nodes=True
|
|
nodes=mat.node_tree.nodes;links=mat.node_tree.links;bsdf=nodes.get('Principled BSDF')
|
|
noise=nodes.new('ShaderNodeTexNoise');noise.inputs['Scale'].default_value=18;noise.inputs['Detail'].default_value=3.5;noise.inputs['Roughness'].default_value=.75
|
|
coords=nodes.new('ShaderNodeTexCoord');links.new(coords.outputs['Generated'],noise.inputs['Vector'])
|
|
ramp=nodes.new('ShaderNodeValToRGB');ramp.color_ramp.elements[0].position=.22;ramp.color_ramp.elements[0].color=rgba('3e3330')
|
|
ramp.color_ramp.elements[1].position=.46;ramp.color_ramp.elements[1].color=rgba('e46060')
|
|
links.new(noise.outputs['Fac'],ramp.inputs[0]);links.new(ramp.outputs['Color'],bsdf.inputs['Base Color'])
|
|
bsdf.inputs['Roughness'].default_value=.84;bsdf.inputs['Metallic'].default_value=.08
|
|
bump=nodes.new('ShaderNodeBump');bump.inputs['Strength'].default_value=.10;bump.inputs['Distance'].default_value=.025;links.new(noise.outputs['Fac'],bump.inputs['Height']);links.new(bump.outputs['Normal'],bsdf.inputs['Normal'])
|
|
for obj in bpy.context.scene.objects:
|
|
if obj.type=='MESH':obj.data.materials.clear();obj.data.materials.append(mat)
|
|
scene=bpy.context.scene;scene.render.engine='CYCLES';scene.cycles.samples=96;scene.cycles.use_denoising=True
|
|
scene.render.film_transparent=True;scene.render.image_settings.file_format='PNG';scene.render.image_settings.color_mode='RGBA';scene.render.image_settings.color_depth='8'
|
|
scene.view_settings.view_transform='Standard';scene.view_settings.look='None';scene.view_settings.exposure=-.15
|
|
scene.world.use_nodes=True;scene.world.node_tree.nodes['Background'].inputs[0].default_value=(.72,.77,.72,1);scene.world.node_tree.nodes['Background'].inputs[1].default_value=.32
|
|
scene.render.resolution_x=1800;scene.render.resolution_y=1800;scene.render.resolution_percentage=100
|
|
bpy.context.view_layer.update()
|
|
points=[o.matrix_world@Vector(c) for o in scene.objects if o.type=='MESH' for c in o.bound_box]
|
|
low=Vector(tuple(min(v[a] for v in points) for a in range(3)));high=Vector(tuple(max(v[a] for v in points) for a in range(3)));centre=(low+high)/2
|
|
size=max(high-low)
|
|
direction=Vector((.70,1,.67)) if kind=='lav' else Vector((-.8,1,.70))
|
|
bpy.ops.object.camera_add(location=centre+direction.normalized()*size*2.5)
|
|
cam=bpy.context.object;cam.rotation_euler=(centre-cam.location).to_track_quat('-Z','Y').to_euler();cam.data.type='ORTHO';scene.camera=cam
|
|
rot=cam.rotation_euler.to_matrix();projected=[rot.transposed()@(v-centre) for v in points]
|
|
span=[max(p[a] for p in projected)-min(p[a] for p in projected) for a in range(2)]
|
|
midpoint=Vector(tuple((max(p[a] for p in projected)+min(p[a] for p in projected))/2 for a in range(2))+(0,))
|
|
cam.location+=rot@midpoint;cam.data.ortho_scale=max(span)*1.08
|
|
def light(name,position,energy,scale):
|
|
data=bpy.data.lights.new(name,'AREA');data.energy=energy*size*size;data.size=scale*size
|
|
obj=bpy.data.objects.new(name,data);scene.collection.objects.link(obj);obj.location=centre+Vector(position)*size;obj.rotation_euler=(centre-obj.location).to_track_quat('-Z','Y').to_euler()
|
|
light('Warm key',(-.6,.8,1.3),10,.65);light('Soft fill',(.8,.3,.5),3,.8);light('Rim',(.5,-1,1.1),13,.5)
|
|
scene.render.filepath=str(out/f'{kind}-turret-render.png');bpy.ops.render.render(write_still=True)
|
|
bpy.ops.wm.save_as_mainfile(filepath=str(out/f'{kind}-release-render.blend'))
|
|
print('RELEASE_RENDER',kind,out)
|