"""Shape the left hollow and deep cleft from the supplied oblique photos. Restores a saved mesh baseline before every run so edits never accumulate. The front UV atlas and inscription remain attached to their original vertices. """ import bpy, math, json from mathutils import Vector from pathlib import Path ROOT = Path('D:/project/sunrise') spec = json.loads((ROOT/'scripts/constants/marker-shape.json').read_text()) scene = bpy.data.scenes['Summit slab and marker'] bpy.context.window.scene = scene body = bpy.data.objects['Photo matched summit boulder'] root = bpy.data.objects['summit-marker'] baseline = bpy.data.meshes.get('Marker before asymmetric hollow') if baseline is None: baseline = body.data.copy() baseline.name = 'Marker before asymmetric hollow' baseline.use_fake_user = True mesh = body.data for vertex, source in zip(mesh.vertices,baseline.vertices): x,y,z = source.co front = max(0,min(1,(.15-y)/.5)) bowl = math.exp(-((x+.55)/.34)**2-((z-.86)/.67)**4) # A narrow, slanting cleft between the sunken left bowl and inscribed slab. cleft = math.exp(-((x+.18-.12*z)/.065)**2)*math.exp(-((z-.86)/.88)**4) shoulder = math.exp(-((x+.44)/.48)**2)*max(0,min(1,(z-.8)/.7)) vertex.co = (x-.13*shoulder+spec['rightFaceOffset']*max(0,min(1,(x+.12)/.6)), y+front*(spec['leftBowlDepth']*bowl+spec['cleftDepth']*cleft), z+spec['leftShoulderLift']*shoulder) mesh.update() root.scale = spec['scale'] root['blockedRadius'] = round(max(math.hypot(v.co.x*root.scale.x,v.co.y*root.scale.y) for v in mesh.vertices)+.12,2) body['asymmetricHollow'] = True # Close the local fissure under the enlarged stone using the shared terrain mesh. # This surface is exported for both rendering and walking; no separate pedestal. bpy.context.view_layer.update() slab = bpy.data.objects['SLAB_SURFACE'] ground_baseline = bpy.data.meshes.get('Slab before marker seating') if ground_baseline is None: ground_baseline = slab.data.copy() ground_baseline.name = 'Slab before marker seating' ground_baseline.use_fake_user = True base = min((body.matrix_world@v.co).z for v in mesh.vertices) for vertex,source in zip(slab.data.vertices,ground_baseline.vertices): vertex.co = source.co d = math.hypot(vertex.co.x-root.location.x,vertex.co.y-root.location.y) if d >= spec['bedrockRadius']+spec['bedrockBlend']: continue t = max(0,min(1,(d-spec['bedrockRadius'])/spec['bedrockBlend'])) weight = 1-t*t*(3-2*t) target = base+spec['bedrockOverlap']+.025*math.sin(vertex.co.x*3+vertex.co.y*2) if vertex.co.z < target: vertex.co.z += (target-vertex.co.z)*weight slab.data.update() scene.camera.location = root.location+Vector((1.7,-10,3.5)) scene.camera.rotation_euler = (root.location+Vector((0,0,1.5))-scene.camera.location).to_track_quat('-Z','Y').to_euler() scene.camera.data.lens = 55 print('Asymmetric left hollow sculpted; scale',spec['scale'],'blocked radius',root['blockedRadius'])