saumwacht-godot/assets-source/phase11/bake_cutwood.py
Martin Stickelbroeck c2b447610c Baseline: Saumwacht 0.15.0 (Phase 20A) vor dem Umbau zu Champions of Luneraria
Unveränderter Ausgangsstand inklusive Masterprompt und Konzeptbildern
unter assets/. .godot/ und docs/screenshots/ bleiben laut .gitignore
unversioniert; letztere sind unter ../backups gesichert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:00:36 +02:00

32 lines
2.3 KiB
Python

"""Native 4K PBR maps from an authored annual-ring surface; no bitmap upscaling.
Blender writes portable RGB PNGs. Colour is explicitly encoded as sRGB; the
normal and roughness images contain linear data. All maps share the same UVs.
"""
from pathlib import Path
import bpy,numpy as np,json
P=Path(__file__).resolve().parents[2];out=P/'assets/materials/phase11';out.mkdir(exist_ok=True)
N=4096
axis=np.linspace(-1,1,N,dtype=np.float32);x=axis[None,:];y=axis[:,None]
r=np.sqrt(x*x+y*y);a=np.arctan2(y,x)
phase=r*12+.13*np.sin(a*3+r*9)+.07*np.sin(a*7-r*8)
edge=np.abs(np.sin(phase*np.pi));line=np.clip((.27-edge)/.15,0,1);line=line*line*(3-2*line)
fibres=np.sin(a*113+r*38)*np.sin(r*290+a*5)
split=np.zeros((N,N),dtype=np.float32)
for k in range(3):
ca=.36+k*2.12;d=np.abs(np.arctan2(np.sin(a-ca-.012*np.sin(r*24)),np.cos(a-ca-.012*np.sin(r*24))))
split=np.maximum(split,np.clip((.014-d)/.011,0,1)*np.clip((r-(.48+.1*k))/(.91-.48-.1*k),0,1))
height=.54-.17*line-.35*split+.018*fibres
rgba=np.ones((N,N,4),dtype=np.float32)
def save(name,data,srgb=False):
if data.ndim==2:data=data[:,:,None]
if srgb:data=np.where(data<=.0031308,12.92*data,1.055*np.maximum(data,0)**(1/2.4)-.055)
rgba[:,:,:3]=np.clip(data,0,1)
im=bpy.data.images.new(name,width=N,height=N,alpha=False,float_buffer=False)
im.colorspace_settings.name='Non-Color';im.pixels.foreach_set(rgba.ravel());im.filepath_raw=str(out/(name+'.png'));im.file_format='PNG';im.save();bpy.data.images.remove(im)
colour=(1.2-.64*line)*(1+.07*fibres)*(1-.8*split)
save('ringborke-cut-albedo-4k',colour[:,:,None]*np.array([.265,.147,.066],dtype=np.float32),True)
gy,gx=np.gradient(height);normal=np.dstack((-gx*42,-gy*42,np.ones_like(height)));normal/=np.linalg.norm(normal,axis=2)[:,:,None]
save('ringborke-cut-normal-4k',normal*.5+.5)
save('ringborke-cut-roughness-4k',.78+.14*line+.06*split)
(P/'assets-source/phase11/material-provenance.json').write_text(json.dumps({'method':'Original analytic Blender/Python surface bake','size':[N,N],'upscaled':False,'albedo_encoding':'sRGB','normal_encoding':'linear, tangent space +Y','roughness_encoding':'linear','generator':'assets-source/phase11/bake_cutwood.py','inputs':'Annual ring field, warped fibres and radial growth checks; no external images'},indent=2)+'\n')
print('CUTWOOD_BAKE_PASS: three native 4096px material maps',flush=True)