"""Original tileable reed and leather maps; registered analytic height normals, CC0."""
from pathlib import Path
import json, numpy as np
from PIL import Image
ROOT=Path(__file__).parent
doc=json.loads((ROOT/'materials.json').read_text())
def save(name,col,height,ao,rough,tile,provenance):
 n=height.shape[0]
 # PNG rows run opposite glTF V with flipY=false; invert row derivative for +Y normals.
 dx=(np.roll(height,-1,1)-np.roll(height,1,1))*n/(2*tile)
 dy=-(np.roll(height,-1,0)-np.roll(height,1,0))*n/(2*tile)
 normal=np.stack([-dx,-dy,np.ones_like(dx)],2);normal/=np.linalg.norm(normal,axis=2)[:,:,None]
 orm=np.stack([np.broadcast_to(ao,height.shape),np.broadcast_to(rough,height.shape),np.zeros_like(height)],2)
 p=ROOT/'materials'/name;p.mkdir(parents=True,exist_ok=True)
 for kind,a in [('BaseColor',col),('Normal',normal*.5+.5),('ORM',orm)]:
  Image.fromarray(np.uint8(np.clip(a,0,1)*255)).save(p/(kind+'.png'))
 doc['materials'][name]={'maps':{k:f'materials/{name}/{k}.png' for k in ['BaseColor','Normal','ORM']},'resolution':n,'tile_size_metres':tile,'wrap':'REPEAT','normal_convention':'OpenGL +Y','provenance':provenance}
n=1024;y,x=np.mgrid[0:n,0:n]/n;rng=np.random.default_rng(187)
# Nonuniform, slightly bent reeds. Periodic strand identity, weathering and broken tips.
phase=x*142+.23*np.sin(2*np.pi*y)+.10*np.sin(2*np.pi*(y*5+x*3))
idx=np.floor(phase).astype(int)%142;f=phase%1
colors=rng.uniform(.63,1.33,142)[idx]
ends=rng.uniform(0,1,142)[idx]
lengths=.5+.5*np.cos(2*np.pi*(y-ends))
reed=np.sin(np.pi*f)**.65
splits=np.exp(-((f-.13)/.07)**2)
weather=.5+.5*np.sin(2*np.pi*(3*x+2*y)+.8*np.sin(2*np.pi*5*x))
bundle=.5+.5*np.cos(2*np.pi*11*x+.25*np.sin(2*np.pi*y))
fibre=.5+.5*np.sin(2*np.pi*(x*407+.1*np.sin(2*np.pi*7*y)))
height=.0017*reed+.0020*bundle+.00020*fibre-.0003*splits
value=(.48+.45*reed)*colors*(.81+.19*lengths)*(.84+.16*weather)
col=value[:,:,None]*np.array([.47,.423,.351])[None,None,:]
col+=((weather-.5)*.026)[:,:,None]*np.array([.5,.8,1.0])
save('Thatch_reed',col,height,.65+.35*reed,.95,1,'Original periodic varied reed strands, weathered grey-brown albedo and registered analytic height normal, CC0')
doc['materials']['Thatch_reed'].update(physical_repeat_metres=[1,1],grain_axis='V follows roof downslope')
# Staggered hollow stem ends: irregular cells, 4-7 mm diameter at 0.4 m repeat.
cells=36;row=np.floor(y*cells).astype(int)
u=(x*cells+.27*np.sin(row*2*np.pi*17/cells))%1-.5;v=(y*cells)%1-.5
cell=np.floor(x*cells+.27*np.sin(row*2*np.pi*17/cells)).astype(int)%cells
seed=(np.sin(cell*12.9898+row*78.233)*43758.5453)%1
rad=np.sqrt(((u+.065*np.sin(seed*23))/(.34+.12*seed))**2+((v+.045*np.cos(seed*19))/(.32+.10*seed))**2)
wall=np.exp(-((rad-.68)/.19)**2);core=np.exp(-(rad/.40)**2)
ends=(.43+.37*wall-.23*core)*(.78+.30*seed)
col=ends[:,:,None]*np.array([.62,.525,.38])[None,None,:]
save('Thatch_reed_cut',col,.0011*wall-.0006*core,.63+.35*wall,.98,.4,'Original periodic hollow reed stem ends with registered normals, CC0')
# Dedicated leather keeps the shared recolourable Sign_paint shelf unchanged.
n=256;y,x=np.mgrid[0:n,0:n]/n
pores=(.5+.5*np.sin(2*np.pi*x*67)*np.cos(2*np.pi*y*71))
col=(.85+.15*pores)[:,:,None]*np.array([.24,.125,.065])[None,None,:]
save('Leather_brown',col,.00008*pores,1,.78,.35,'Original dark brown leather fine grain, CC0')
for d in doc['materials'].values():d.update(base=d['maps']['BaseColor'],normal=d['maps']['Normal'],orm=d['maps']['ORM'],tile=d.get('tile_size_metres',1),roughness=1,metalness=1)
(ROOT/'materials.json').write_text(json.dumps(doc,indent=2))
