import bpy
import numpy as np

scene = bpy.context.scene
ter = bpy.data.objects["Ground"]
me = ter.data

# add second attribute holding SED in R channel
ca = me.color_attributes.get("mask")
n_v = len(me.vertices)
data = np.empty(n_v*4, dtype=np.float32)
ca.data.foreach_get("color", data)
sed = data[3::4].copy()

ca2 = me.color_attributes.new(name="mask2", type='FLOAT_COLOR', domain='POINT')
d2 = np.zeros(n_v*4, dtype=np.float32)
d2[0::4] = sed
ca2.data.foreach_set("color", d2)
me.update()

# rebuild material
mat = bpy.data.materials["GroundMat"]
nt = mat.node_tree; nt.nodes.clear()
out = nt.nodes.new('ShaderNodeOutputMaterial'); out.location=(700,0)
bsdf = nt.nodes.new('ShaderNodeBsdfPrincipled'); bsdf.location=(420,120)
nt.links.new(bsdf.outputs['BSDF'], out.inputs['Surface'])

attr  = nt.nodes.new('ShaderNodeAttribute'); attr.attribute_name="mask"; attr.location=(-760,180)
attr2 = nt.nodes.new('ShaderNodeAttribute'); attr2.attribute_name="mask2"; attr2.location=(-760,-140)
sep   = nt.nodes.new('ShaderNodeSeparateColor'); sep.location=(-580,180)
sep2  = nt.nodes.new('ShaderNodeSeparateColor'); sep2.location=(-580,-140)
nt.links.new(attr.outputs['Color'], sep.inputs['Color'])
nt.links.new(attr2.outputs['Color'], sep2.inputs['Color'])

mix = nt.nodes.new('ShaderNodeMixRGB'); mix.blend_type='MIX'; mix.location=(-380,220)
mix.inputs['Color1'].default_value=(0.088,0.135,0.045,1)   # dark grass
mix.inputs['Color2'].default_value=(0.205,0.235,0.075,1)   # dry grass
nt.links.new(sep.outputs['Green'], mix.inputs['Fac'])

m2 = nt.nodes.new('ShaderNodeMixRGB'); m2.blend_type='MIX'; m2.location=(-200,220)
m2.inputs['Color1'].default_value=(0,0,0,1)
m2.inputs['Color2'].default_value=(0.44,0.365,0.245,1)     # sand
nt.links.new(mix.outputs['Color'], m2.inputs['Color1'])
nt.links.new(sep.outputs['Blue'], m2.inputs['Fac'])

m3 = nt.nodes.new('ShaderNodeMixRGB'); m3.blend_type='MIX'; m3.location=(-20,220)
m3.inputs['Color2'].default_value=(0.185,0.125,0.075,1)    # dirt path
nt.links.new(m2.outputs['Color'], m3.inputs['Color1'])
nt.links.new(sep.outputs['Red'], m3.inputs['Fac'])

m4 = nt.nodes.new('ShaderNodeMixRGB'); m4.blend_type='MIX'; m4.location=(160,220)
m4.inputs['Color2'].default_value=(0.075,0.072,0.055,1)    # pond sediment
nt.links.new(m3.outputs['Color'], m4.inputs['Color1'])
nt.links.new(sep2.outputs['Red'], m4.inputs['Fac'])
nt.links.new(m4.outputs['Color'], bsdf.inputs['Base Color'])

texco = nt.nodes.new('ShaderNodeTexCoord'); texco.location=(-760,-320)
noiz = nt.nodes.new('ShaderNodeTexNoise'); noiz.location=(-560,-320)
noiz.inputs['Scale'].default_value=0.35; noiz.inputs['Detail'].default_value=6
rampv = nt.nodes.new('ShaderNodeMapRange'); rampv.location=(-360,-320)
rampv.inputs['From Min'].default_value=0.35; rampv.inputs['From Max'].default_value=0.72
rampv.inputs['To Min'].default_value=0.72; rampv.inputs['To Max'].default_value=0.95
nt.links.new(texco.outputs['Object'], noiz.inputs['Vector'])
nt.links.new(noiz.outputs['Fac'], rampv.inputs['Value'])
nt.links.new(rampv.outputs['Result'], bsdf.inputs['Roughness'])

bumpn = nt.nodes.new('ShaderNodeTexNoise'); bumpn.location=(-560,-520)
bumpn.inputs['Scale'].default_value=2.2; bumpn.inputs['Detail'].default_value=8
bmp = nt.nodes.new('ShaderNodeBump'); bmp.location=(100,-480)
bmp.inputs['Strength'].default_value=0.22; bmp.inputs['Distance'].default_value=0.03
nt.links.new(texco.outputs['Object'], bumpn.inputs['Vector'])
nt.links.new(bumpn.outputs['Fac'], bmp.inputs['Height'])
nt.links.new(bmp.outputs['Normal'], bsdf.inputs['Normal'])

result = {"ok": True}