import bpy, math, random, os, sys, wave, struct from mathutils import Vector random.seed(18) ROOT=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) OUT=os.path.join(ROOT,'preview') bpy.ops.object.select_all(action='SELECT'); bpy.ops.object.delete(use_global=False) s=bpy.context.scene s.render.engine='BLENDER_EEVEE'; s.eevee.taa_render_samples=8; s.eevee.use_gtao=True; s.eevee.gtao_distance=3; s.eevee.use_soft_shadows=True s.render.resolution_x=1280;s.render.resolution_y=720;s.render.resolution_percentage=100 s.render.fps=24;s.frame_start=1;s.frame_end=480 s.world.color=(.003,.003,.003) s.view_settings.view_transform='AgX' def mat(name,color,rough=.7,noise=False): m=bpy.data.materials.new(name);m.diffuse_color=(*color,1);m.use_nodes=True p=m.node_tree.nodes.get('Principled BSDF');p.inputs['Base Color'].default_value=(*color,1);p.inputs['Roughness'].default_value=rough if noise: n=m.node_tree.nodes.new('ShaderNodeTexNoise');n.inputs['Scale'].default_value=24 b=m.node_tree.nodes.new('ShaderNodeBump');b.inputs['Strength'].default_value=.3;b.inputs['Distance'].default_value=.08 m.node_tree.links.new(n.outputs['Fac'],b.inputs['Height']);m.node_tree.links.new(b.outputs['Normal'],p.inputs['Normal']) return m soil=mat('Damp garden soil',(.07,.045,.025),noise=True);stone=mat('Weathered limestone',(.28,.29,.27),noise=True) cloth=mat('Burial cloth',(.15,.12,.09),noise=True);brick=mat('Rendered house wall',(.24,.19,.14),noise=True) bone=mat('Old ivory',(.55,.48,.32));black=mat('Dark fabric',(.015,.02,.025),noise=True) leafm=[mat('Autumn leaf '+str(i),c) for i,c in enumerate([(.21,.065,.014),(.30,.15,.03),(.12,.055,.018)])] def cube(name,loc,scale,m,bevel=0): bpy.ops.mesh.primitive_cube_add(size=1,location=loc);o=bpy.context.object;o.name=name;o.scale=scale bpy.ops.object.transform_apply(location=False,rotation=False,scale=True) if m:o.data.materials.append(m) if bevel: mod=o.modifiers.new('Soft edges','BEVEL');mod.width=bevel;mod.segments=3;o.data.use_auto_smooth=True;o.modifiers.new('Normals','WEIGHTED_NORMAL') return o def uv(name,loc,scale,m): bpy.ops.mesh.primitive_uv_sphere_add(segments=16,ring_count=10,location=loc);o=bpy.context.object;o.name=name;o.scale=scale;o.data.materials.append(m) for p in o.data.polygons:p.use_smooth=True return o def rod(name,a,b,r,m,parent=None): d=Vector(b)-Vector(a);bpy.ops.mesh.primitive_cylinder_add(vertices=10,radius=r,depth=d.length,location=(Vector(a)+Vector(b))/2) o=bpy.context.object;o.name=name;o.rotation_euler=d.to_track_quat('Z','Y').to_euler();o.data.materials.append(m) if parent:o.parent=parent return o cube('Ground',(0,0,-.12),(16,16,.2),soil) cube('Walkway',(2.6,0,.01),(1.5,12,.09),stone,.03) for y in range(-6,7):cube('Paving joint',(2.6,y,.065),(1.5,.017,.005),black) cube('House',(0,6,2),(12,.3,4),brick) for x in [-3,0,3]: cube('Window frame',(x,5.80,2.2),(1.6,.15,1.8),black,.04) glass=mat('Warm window '+str(x),(.5,.22,.045));p=glass.node_tree.nodes.get('Principled BSDF');p.inputs['Emission Color'].default_value=(1,.35,.08,1);p.inputs['Emission Strength'].default_value=.8 cube('Lit window',(x,5.70,2.2),(1.4,.04,1.6),glass) cube('Window divider',(x,5.65,2.2),(.05,.06,1.7),black) for x in [-5,5]: for y in range(-4,6):cube('Fence post',(x,y,.65),(.09,.09,1.3),stone,.015) cube('Fence rail',(x,.5,.9),(.07,10,.08),stone) # Grave border and headstone for x in [-.9,.9]:cube('Grave side',(x,0,.14),(.15,2.9,.28),stone,.04) for y in [-1.45,1.45]:cube('Grave end',(0,y,.14),(1.95,.15,.28),stone,.04) cube('Headstone base',(0,1.65,.25),(1.1,.45,.3),stone,.06) cube('Headstone',(0,1.65,.85),(.85,.22,1.1),stone,.10) uv('Rounded headstone',(0,1.65,1.35),(.425,.11,.3),stone) def text(body,loc,size): bpy.ops.object.text_add(location=loc,rotation=(math.pi/2,0,0));o=bpy.context.object;o.data.body=body;o.data.align_x='CENTER';o.data.size=size;o.data.extrude=.002;o.data.materials.append(black) text('R I P',(0,1.525,1.05),.19);text('STILL BREATHING',(0,1.525,.72),.065) # Fabric mesh with animated breathing shape verts=[];faces=[];nx=28;ny=44 for j in range(ny+1): y=-1.35+2.7*j/ny for i in range(nx+1): x=-.80+1.6*i/nx;z=.19+.035*math.sin(i*1.3+j*.7)*math.sin(math.pi*i/nx)*math.sin(math.pi*j/ny);verts.append((x,y,z)) for j in range(ny): for i in range(nx): a=j*(nx+1)+i;faces.append((a,a+1,a+nx+2,a+nx+1)) mesh=bpy.data.meshes.new('Cloth grid');mesh.from_pydata(verts,[],faces);mesh.update() o=bpy.data.objects.new('Breathing burial shroud',mesh);s.collection.objects.link(o);o.data.materials.append(cloth) for p in mesh.polygons:p.use_smooth=True o.shape_key_add(name='Rest');breath=o.shape_key_add(name='Breath') for v in breath.data:v.co.z+=.23*math.exp(-((v.co.x/.50)**2+(v.co.y/.85)**2)) for f,val in [(1,.1),(30,1),(65,0),(100,.85),(120,.15),(150,0),(175,.45),(210,0),(240,.1),(241,0),(310,0),(316,1.8),(322,.1),(332,1.2),(345,0),(380,.1),(415,.8),(450,0),(480,.35)]:breath.value=val;breath.keyframe_insert('value',frame=f) for i in range(220): x=random.uniform(-4,4);y=random.uniform(-4,4) if abs(x)<1 and abs(y)<1.5:continue me=bpy.data.meshes.new('Leaf');me.from_pydata([(-.10,0,0),(-.045,-.04,0),(0,-.065,.01),(.07,-.025,0),(.11,0,0),(.04,.045,0),(-.02,.055,.008)],[],[(0,1,2,3,4,5,6)]);me.update() l=bpy.data.objects.new('Fallen leaf',me);s.collection.objects.link(l);l.location=(x,y,.025);l.data.materials.append(random.choice(leafm));l.rotation_euler.z=random.random()*6.28 # Grass tufts, shrubs, and Halloween pumpkins grassmat=mat('Dry grass',(.055,.065,.024)) for i in range(180): x=random.uniform(-4.5,1.6);y=random.uniform(-4,4) if abs(x)<1.1 and abs(y)<1.8:continue rod('Grass blade',(x,y,0),(x+.025,y+.015,random.uniform(.07,.17)),.003,grassmat) for x,y in [(-2,1),(-3,3),(4,4)]: for j in range(8):uv('Garden shrub',(x+random.uniform(-.3,.3),y+random.uniform(-.3,.3),random.uniform(.2,.5)),(.3,.3,.3),grassmat) for x,y in [(-1.5,-1.7),(-1.8,-1.4),(.8,2.1)]: pumpkin=uv('Ribbed pumpkin',(x,y,.18),(.23,.23,.18),leafm[0]) for v in pumpkin.data.vertices: ang=math.atan2(v.co.y,v.co.x);fac=1+.055*math.cos(ang*10);v.co.x*=fac;v.co.y*=fac rod('Pumpkin stem',(x,y,.32),(x+.02,y,.42),.024,soil) # Cloaked costumed visitors, seen from behind/side purple=mat('Purple wool costume',(.11,.025,.13),noise=True);red=mat('Burgundy costume',(.18,.018,.022),noise=True) def person(name,h,color,start,end,child=False): root=bpy.data.objects.new(name,None);s.collection.objects.link(root) # Folded tapered cloak vs=[];fs=[];rings=14;seg=32 for j in range(rings): z=h*(.18+.60*j/(rings-1));r=h*(.24-.12*j/(rings-1)) for k in range(seg): ang=k*2*math.pi/seg;rr=r*(1+.085*math.sin(ang*9+j*.25));vs.append((rr*math.cos(ang),rr*.65*math.sin(ang),z)) for j in range(rings-1): for k in range(seg):a=j*seg+k;b=j*seg+(k+1)%seg;fs.append((a,b,b+seg,a+seg)) me=bpy.data.meshes.new(name+' costume');me.from_pydata(vs,[],fs);me.update();ob=bpy.data.objects.new(name+' cloak',me);s.collection.objects.link(ob);ob.parent=root;ob.data.materials.append(color) for p in me.polygons:p.use_smooth=True for x in [-h*.08,h*.08]: ob=uv('Boot',(x,-.04,h*.08),(h*.065,h*.10,h*.09),black);ob.parent=root drv=ob.driver_add('location',2).driver;drv.expression=f'{h*.08} + (max(0,sin(frame*0.32+{0 if x<0 else math.pi}))*{h*.035} if 12017 else .006*lp for begin,end in [(7.1,8.3),(13,13.45)]: if begin