"""Rasterize existing Hanja strokes into the stone's own albedo; no letter geometry exported.""" import bpy, numpy as np, math from pathlib import Path s=bpy.data.scenes['Summit slab and marker'];bpy.context.window.scene=s body=bpy.data.objects['Photo matched summit boulder'];root=bpy.data.objects['summit-marker'];mesh=body.data if body.get('paintedInscription'):raise RuntimeError('Already painted; edit the texture rather than baking it twice.') mat=mesh.materials[0];nodes=mat.node_tree.nodes;links=mat.node_tree.links bs=next(n for n in nodes if n.type=='BSDF_PRINCIPLED');old_color=bs.inputs['Base Color'].links[0].from_socket old_uv=nodes.new('ShaderNodeUVMap');old_uv.uv_map='Granite grain UV' for n in list(nodes): if n.type=='TEX_IMAGE':links.new(old_uv.outputs['UV'],n.inputs['Vector']) atlas=mesh.uv_layers.new(name='Painted monument atlas') # Separate front and rear islands; preserve the existing stone geometry and normals. xmin=-1.3;xspan=2.6;zmin=-.2;zspan=2.3 half=len(mesh.vertices)//2 for f in mesh.polygons: rear=sum(1 for i in f.vertices if i>=half)>1 for li in f.loop_indices: v=mesh.vertices[mesh.loops[li].vertex_index].co atlas.data[li].uv=((.52 if rear else .02)+.46*(v.x-xmin)/xspan,.02+.96*(v.z-zmin)/zspan) mesh.uv_layers.active=atlas for layer in mesh.uv_layers:layer.active_render=(layer==atlas) N=2048;mask=np.zeros((N,N),dtype=np.float32) letters=[o for o in root.children if o.name.startswith('Carved ')] for o in letters: o.data.calc_loop_triangles() for tri in o.data.loop_triangles: p=[o.matrix_basis@o.data.vertices[i].co for i in tri.vertices] xy=np.array([[(.02+.46*(v.x-xmin)/xspan)*(N-1),(.02+.96*(v.z-zmin)/zspan)*(N-1)] for v in p]) lo=np.maximum(0,np.floor(xy.min(axis=0)).astype(int));hi=np.minimum(N-1,np.ceil(xy.max(axis=0)).astype(int)) if np.any(hi=0)&(v>=0)&(u+v<=1) mask[lo[1]:hi[1]+1,lo[0]:hi[0]+1]=np.maximum(mask[lo[1]:hi[1]+1,lo[0]:hi[0]+1],hit) # Slight pigment wear lets mineral grain remain visible through the painted strokes. rng=np.random.default_rng(91);mask*=rng.uniform(.88,1,mask.shape) rgba=np.ones((N,N,4),dtype=np.float32);rgba[:,:,:3]=mask[:,:,None] im=bpy.data.images.new('Hanja pigment mask',width=N,height=N,alpha=False);im.pixels.foreach_set(rgba.ravel());im.update() tex=nodes.new('ShaderNodeTexImage');tex.image=im atlas_node=nodes.new('ShaderNodeUVMap');atlas_node.uv_map=atlas.name;links.new(atlas_node.outputs['UV'],tex.inputs['Vector']) mix=nodes.new('ShaderNodeMixRGB');links.new(tex.outputs['Color'],mix.inputs[0]);links.new(old_color,mix.inputs[1]);mix.inputs[2].default_value=(.012,.014,.010,1);links.new(mix.outputs[0],bs.inputs['Base Color']) image=bpy.data.images.new('Painted Hanja on granite',width=N,height=N,alpha=False) target=nodes.new('ShaderNodeTexImage');target.image=image;nodes.active=target for o in s.objects:o.select_set(False) body.select_set(True);bpy.context.view_layer.objects.active=body s.render.bake.use_pass_direct=False;s.render.bake.use_pass_indirect=False;s.render.bake.use_pass_color=True;s.render.bake.margin=8 bpy.ops.object.bake(type=next(i.identifier for i in bpy.ops.object.bake.get_rna_type().properties['type'].enum_items if i.identifier=='DIFFUSE')) image.filepath_raw='D:/project/sunrise/public/terrain/materials/marker-painted-color.png' image.file_format=next(i.identifier for i in image.bl_rna.properties['file_format'].enum_items if i.identifier=='PNG');image.save();image.pack() links.new(atlas_node.outputs['UV'],target.inputs['Vector']);links.new(target.outputs['Color'],bs.inputs['Base Color']) for o in letters: world=o.matrix_world.copy();o.parent=None;o.matrix_world=world;o.hide_render=True;o.hide_set(True);o.name='Paint source - '+o.name body['paintedInscription']=True side=mat.copy();side.name='Unpainted stone edge' edge_bs=next(n for n in side.node_tree.nodes if n.type=='BSDF_PRINCIPLED') scan=next(n for n in side.node_tree.nodes if n.type=='TEX_IMAGE' and n.image and n.image.name.startswith('rock-color')) for socket in [edge_bs.inputs['Base Color'],edge_bs.inputs['Normal']]: for link in list(socket.links):side.node_tree.links.remove(link) edge_bs.inputs['Base Color'].default_value=(.23,.215,.18,1);mesh.materials.append(side) for p in mesh.polygons: if any(i=half for i in p.vertices):p.material_index=1 s.render.filepath='D:/project/sunrise/assets/summit/blender-plateau-study/marker-painted.png' bpy.ops.render.render(write_still=True) print('Painted',int(np.count_nonzero(mask)),'texels. Export hierarchy contains only the stone; glyph meshes retained hidden as paint sources.')