Files
sunrise/scripts/sculpt-marker-sides.py
T

61 lines
3.5 KiB
Python

"""Sculpt a weathered, thick side and rear while preserving the painted front."""
import bpy,bmesh,math
from mathutils import Vector
from mathutils.noise import noise_vector
s=bpy.data.scenes['Summit slab and marker'];bpy.context.window.scene=s
body=bpy.data.objects['Photo matched summit boulder'];m=body.data
if body.get('weatheredSides'):raise RuntimeError('Side study already sculpted; edit it in place.')
backup=m.copy();backup.name='Monument before side sculpt';backup.use_fake_user=True
half=len(m.vertices)//2
# A fuller foot and a rear face that retreats under the narrow shoulder.
for v in m.vertices:
if v.index>=half:
v.co.y+=.15+.20*max(0,1-v.co.z/2.1)
for p in m.polygons:
if all(i>=half for i in p.vertices):p.material_index=1
bm=bmesh.new();bm.from_mesh(m)
edges={e for f in bm.faces if f.material_index==1 and any(v.co.y<0 for v in f.verts) for e in f.edges}
bmesh.ops.subdivide_edges(bm,edges=list(edges),cuts=9,use_grid_fill=True)
bm.normal_update()
for v in bm.verts:
# Only change the connecting rock wall, with zero displacement at the painted face.
if not all(f.material_index==1 for f in v.link_faces):continue
x,y,z=v.co
if y<-.48+.13*x+.05*z+.03:continue
noise=noise_vector(Vector((x*5,y*6,z*5)))[0]
bulge=.055*noise+.035*math.sin(z*9+y*4)
# Shallow oblique joints break the broad plane; no repeated vertical striping.
seam=.055*math.exp(-((z-.64-.32*y)/.045)**2)+.035*math.exp(-((z-1.35+.22*y)/.055)**2)
amount=bulge-seam
if abs(v.normal.x)>.25:v.co.x+=v.normal.x*amount
if v.normal.y>.3:v.co.y+=v.normal.y*amount
v.co.z+=v.normal.z*amount*.5
bmesh.ops.recalc_face_normals(bm,faces=list(bm.faces));bm.to_mesh(m);bm.free();m.update()
# Side and rear each receive spatially scaled UVs instead of collapsed front projections.
uv=m.uv_layers['Granite grain UV']
for p in m.polygons:
if p.material_index!=1:continue
n=p.normal
for li in p.loop_indices:
v=m.vertices[m.loops[li].vertex_index].co
if abs(n.z)>.65:coord=(v.x*.9,v.y*.9)
elif abs(n.x)>abs(n.y):coord=(v.y*.9,v.z*.9)
else:coord=(v.x*.9,v.z*.9)
uv.data[li].uv=coord
p.use_smooth=True
side=m.materials[1];nodes=side.node_tree.nodes;links=side.node_tree.links
bs=next(n for n in nodes if n.type=='BSDF_PRINCIPLED')
scan=next(n for n in nodes if n.type=='TEX_IMAGE' and n.image and n.image.name.startswith('rock-color'))
normal=next(n for n in nodes if n.type=='TEX_IMAGE' and n.image and n.image.name.startswith('rock-normal'))
uvnode=next(n for n in nodes if n.type=='UVMAP' and n.uv_map=='Granite grain UV')
links.new(uvnode.outputs['UV'],scan.inputs['Vector']);links.new(uvnode.outputs['UV'],normal.inputs['Vector'])
links.new(scan.outputs['Color'],bs.inputs['Base Color'])
nm=next(n for n in nodes if n.type=='NORMAL_MAP');nm.uv_map='Granite grain UV';nm.inputs['Strength'].default_value=.48
links.new(normal.outputs['Color'],nm.inputs['Color']);links.new(nm.outputs['Normal'],bs.inputs['Normal'])
bs.inputs['Roughness'].default_value=.96
body['weatheredSides']=True
r=bpy.data.objects['summit-marker'];s.camera.location=r.location+Vector((4.6,-4.8,2.6));s.camera.rotation_euler=(r.location+Vector((0,.1,.9))-s.camera.location).to_track_quat('-Z','Y').to_euler();s.camera.data.lens=55
s.render.filepath='D:/project/sunrise/assets/summit/blender-plateau-study/marker-weathered-side.png'
bpy.ops.render.render(write_still=True)
print('Sculpted sides:',len(m.vertices),'vertices;',len(m.polygons),'faces. Front paint UVs preserved; sides and rear retextured.')