Private
Public Access
Initiales Halloween-Projekt
This commit is contained in:
commit
e071fbad2a
36 files changed
+2220
No files matched your search
@@ -0,0 +1,132 @@
|
||||
"""Improve the existing concept scene; render still examples only."""
|
||||
import bpy, math, random, os
|
||||
from mathutils import Vector
|
||||
ROOT=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
OUT=os.path.join(ROOT,'preview','v3');os.makedirs(OUT,exist_ok=True)
|
||||
bpy.ops.wm.open_mainfile(filepath=os.path.join(ROOT,'preview','halloween_preview.blend'))
|
||||
s=bpy.context.scene;s.eevee.taa_render_samples=32
|
||||
random.seed(104)
|
||||
def material(name,c,rough=.8):
|
||||
m=bpy.data.materials.new(name);m.use_nodes=True
|
||||
p=m.node_tree.nodes.get('Principled BSDF');p.inputs['Base Color'].default_value=(*c,1);p.inputs['Roughness'].default_value=rough
|
||||
return m
|
||||
leafm=[material('Dry oak leaves '+str(i),c) for i,c in enumerate([(.20,.055,.012),(.32,.14,.032),(.10,.042,.013),(.40,.23,.075),(.16,.08,.025)])]
|
||||
shroud=bpy.data.objects['Breathing burial shroud'];base=shroud.data.shape_keys.key_blocks['Rest'];breath=shroud.data.shape_keys.key_blocks['Breath']
|
||||
# A real opening at the hand's emergence point, rather than intersecting cloth.
|
||||
import bmesh
|
||||
bm=bmesh.new();bm.from_mesh(shroud.data)
|
||||
for face in list(bm.faces):
|
||||
c=face.calc_center_median()
|
||||
if abs(c.x)<.16 and abs(c.y)<.18:bm.faces.remove(face)
|
||||
bm.to_mesh(shroud.data);bm.free()
|
||||
# Leaf litter follows the animated cloth with its own synchronized shape key.
|
||||
verts=[];faces=[];indices=[];lifts=[]
|
||||
def surface(x,y):
|
||||
u=(x+.8)/1.6*28;v=(y+1.35)/2.7*44;i=min(27,max(0,int(u)));j=min(43,max(0,int(v)));a=u-i;b=v-j
|
||||
zs=[base.data[k].co.z for k in [j*29+i,j*29+i+1,(j+1)*29+i,(j+1)*29+i+1]]
|
||||
return zs[0]*(1-a)*(1-b)+zs[1]*a*(1-b)+zs[2]*(1-a)*b+zs[3]*a*b
|
||||
for n in range(950):
|
||||
x=random.uniform(-.77,.77);y=random.uniform(-1.32,1.32)
|
||||
if x*x+y*y<.24**2:continue
|
||||
angle=random.random()*math.tau;size=random.uniform(.045,.105);start=len(verts)
|
||||
z=surface(x,y)+random.uniform(.012,.045)
|
||||
coords=[(0,0,.010)]
|
||||
for k in range(14):
|
||||
a=k*math.tau/14;r=1 if k%2==0 else .65
|
||||
coords.append((math.cos(a)*size*r,math.sin(a)*size*.55*r,random.uniform(-.004,.014)))
|
||||
lift=.23*math.exp(-((x/.50)**2+(y/.85)**2))
|
||||
for xx,yy,zz in coords:
|
||||
verts.append((x+xx*math.cos(angle)-yy*math.sin(angle),y+xx*math.sin(angle)+yy*math.cos(angle),z+zz));lifts.append(lift)
|
||||
idx=random.randrange(len(leafm))
|
||||
for k in range(14):faces.append((start,start+1+k,start+1+(k+1)%14));indices.append(idx)
|
||||
me=bpy.data.meshes.new('Curled oak leaf litter');me.from_pydata(verts,[],faces);me.update()
|
||||
leaves=bpy.data.objects.new('Leaves following breathing surface',me);s.collection.objects.link(leaves)
|
||||
for m in leafm:me.materials.append(m)
|
||||
for p,idx in zip(me.polygons,indices):p.material_index=idx
|
||||
leaves.shape_key_add(name='Rest');lk=leaves.shape_key_add(name='Breath')
|
||||
for v,lift in zip(lk.data,lifts):v.co.z+=lift
|
||||
for frame in range(1,481):
|
||||
s.frame_set(frame);lk.value=breath.value;lk.keyframe_insert('value',frame=frame)
|
||||
# A layer covering the middle opening bursts upward with the emerging hand.
|
||||
for n in range(110):
|
||||
angle=random.uniform(0,math.tau);radius=.23*math.sqrt(random.random())
|
||||
x=radius*math.cos(angle);y=radius*math.sin(angle);z0=surface(x,y)+.035
|
||||
size=random.uniform(.055,.10);vs=[(0,0,.009)]
|
||||
for k in range(14):
|
||||
theta=k*math.tau/14;r=1 if k%2==0 else .65
|
||||
vs.append((size*r*math.cos(theta),size*.55*r*math.sin(theta),random.uniform(-.004,.012)))
|
||||
me=bpy.data.meshes.new('Flying curled leaf');me.from_pydata(vs,[],[(0,k+1,(k+1)%14+1) for k in range(14)]);me.update()
|
||||
obj=bpy.data.objects.new('Burst leaf %03d'%n,me);s.collection.objects.link(obj);obj.data.materials.append(random.choice(leafm))
|
||||
delay=random.uniform(0,.11);speed=random.uniform(.35,.85);up=random.uniform(2.0,3.0);spin=random.uniform(-7,7);initial=random.uniform(0,math.tau)
|
||||
for frame in [1,200,310]+list(range(311,371))+[480]:
|
||||
s.frame_set(frame);t=max(0,(frame-313)/24-delay)
|
||||
flight=min(t,2*up/5.4);direction=angle+.8*flight
|
||||
xx=x+speed*flight*math.cos(direction);yy=y+speed*flight*math.sin(direction)
|
||||
zz=z0+up*flight-2.7*flight*flight
|
||||
if t==0:zz+=.23*math.exp(-((x/.50)**2+(y/.85)**2))*breath.value
|
||||
else:zz=max(.025,zz)
|
||||
obj.location=(xx,yy,zz);obj.rotation_euler=(spin*flight,.7*spin*flight,initial+spin*flight)
|
||||
obj.keyframe_insert('location',frame=frame);obj.keyframe_insert('rotation_euler',frame=frame)
|
||||
for fc in obj.animation_data.action.fcurves:
|
||||
for key in fc.keyframe_points:key.interpolation='LINEAR'
|
||||
hand=bpy.data.objects['Emerging hand'];hand.animation_data_clear();hand.location=(0,0,-.65)
|
||||
for frame,z in [(1,-.65),(310,-.65),(314,-.35),(322,.15),(340,.15),(365,-.65),(480,-.65)]:
|
||||
hand.location.z=z;hand.keyframe_insert('location',frame=frame)
|
||||
|
||||
# Dimmer porch and blue moonlight, red glow beneath the grave.
|
||||
bpy.data.lights['Moon'].energy=220;bpy.data.lights['Porch'].energy=95;bpy.data.lights['Grave blue'].energy=95
|
||||
red=bpy.data.lights.new('Hidden red glow','POINT');red.energy=32;red.color=(1,.018,.004);red.shadow_soft_size=.28
|
||||
ob=bpy.data.objects.new('Hidden red glow',red);s.collection.objects.link(ob);ob.location=(0,-.3,.6)
|
||||
# Bare branches create recognisable long, eerie shadows.
|
||||
bark=material('Dark tree bark',(.025,.018,.014))
|
||||
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
|
||||
for x,y in [(-3.3,2.5),(-4,-1.5)]:
|
||||
rod('Bare tree trunk',(x,y,0),(x+.1,y,3.8),.11,bark)
|
||||
for i in range(8):
|
||||
h=random.uniform(1.7,3.4);tip=(x+random.uniform(-1.5,1.5),y+random.uniform(-.8,.8),h+random.uniform(.3,1))
|
||||
rod('Bare branch',(x,y,h),tip,.035,bark)
|
||||
rod('Branch twig',tip,(tip[0]+.4,tip[1]-.2,tip[2]+.4),.012,bark)
|
||||
# Thin ground mist; faces remain readable.
|
||||
fog=bpy.data.materials.new('Thin ground mist');fog.use_nodes=True;nodes=fog.node_tree.nodes;nodes.clear();out=nodes.new('ShaderNodeOutputMaterial');vol=nodes.new('ShaderNodeVolumePrincipled');vol.inputs['Density'].default_value=.025;vol.inputs['Color'].default_value=(.45,.55,.65,1);fog.node_tree.links.new(vol.outputs['Volume'],out.inputs['Volume'])
|
||||
bpy.ops.mesh.primitive_cube_add(size=1,location=(0,0,.22));ob=bpy.context.object;ob.name='Low garden mist';ob.scale=(10,10,.40);ob.data.materials.append(fog)
|
||||
black=bpy.data.materials['Dark fabric']
|
||||
# Replace floating boots with jointed, stepping legs and swinging sleeves.
|
||||
roots=[bpy.data.objects[n] for n in ['Adult A','Adult B','Child witch','Child vampire']]
|
||||
for root,h in zip(roots,[1.78,1.68,1.05,1.2]):
|
||||
for ob in list(root.children):
|
||||
if ob.name.startswith('Boot'):bpy.data.objects.remove(ob,do_unlink=True)
|
||||
gate='((frame>=95 and frame<165) or (frame>=311 and frame<=344))'
|
||||
for side in [-1,1]:
|
||||
phase=0 if side<0 else math.pi
|
||||
hip=bpy.data.objects.new(root.name+' leg pivot '+str(side),None);s.collection.objects.link(hip);hip.parent=root;hip.location=(side*h*.075,0,h*.40)
|
||||
upper=rod('Trouser thigh',(0,0,0),(0,0,-h*.17),h*.041,black,hip)
|
||||
knee=bpy.data.objects.new(root.name+' knee '+str(side),None);s.collection.objects.link(knee);knee.parent=hip;knee.location=(0,0,-h*.17)
|
||||
rod('Trouser shin',(0,0,0),(0,0,-h*.16),h*.036,black,knee)
|
||||
bpy.ops.mesh.primitive_uv_sphere_add(segments=16,ring_count=8,location=(0,.025,-h*.19));boot=bpy.context.object;boot.name='Animated boot';boot.parent=knee;boot.scale=(h*.055,h*.10,h*.045);boot.data.materials.append(black)
|
||||
hip.driver_add('rotation_euler',0).driver.expression=f'0.40*sin(frame*0.34+{phase}) if {gate} else 0'
|
||||
knee.driver_add('rotation_euler',0).driver.expression=f'-0.40*max(0,-sin(frame*0.34+{phase})) if {gate} else 0'
|
||||
for idx,ob in enumerate([o for o in root.children if o.name.startswith('Sleeve')]):
|
||||
ob.driver_add('rotation_euler',0).driver.expression=f'0.20*sin(frame*0.34+{idx*math.pi}) if {gate} else 0.1'
|
||||
# Keep the family at the grave for the fictional fright scene.
|
||||
root.animation_data_clear()
|
||||
child=root.name.startswith('Child');idx=roots.index(root)
|
||||
x=[3.25,3.9,2.10,2.82][idx];y=[.05,.8,-.55,-1.0][idx]
|
||||
positions=[(1,(x,-9-idx*.35,0)),(95,(x,-5-idx*.25,0)),(165,(x,y,0)),(310,(x,y,0)),(325,(x+.20,y-.30,0)),(345,(x+.22,y-.40,0)),(480,(x+.22,y-.4,0))]
|
||||
for f,pos in positions:root.location=(pos[0],pos[1],pos[2]+.065);root.keyframe_insert('location',frame=f)
|
||||
for f,ang in [(1,0),(165,-.15),(310,-.15),(321,.18 if child else .07),(345,0),(480,0)]:root.rotation_euler.z=ang;root.keyframe_insert('rotation_euler',frame=f)
|
||||
root.driver_add('rotation_euler',0).driver.expression='-0.14 if 316<=frame<=328 else 0'
|
||||
# Camera remains with the family; no later jump to an adults-only sequence.
|
||||
cam=s.camera;cam.animation_data_clear();cam.data.lens=40
|
||||
for f,loc,target in [(1,(7.5,-9,4),(.7,0,.6)),(165,(6.8,-7.6,3.3),(.7,0,.65)),(310,(6.8,-7.6,3.3),(.7,0,.65)),(320,(6.6,-7.5,3.15),(.7,0,.65)),(480,(7.5,-9,4),(.7,0,.6))]:
|
||||
cam.location=loc;cam.rotation_euler=(Vector(target)-cam.location).to_track_quat('-Z','Y').to_euler();cam.keyframe_insert('location',frame=f);cam.keyframe_insert('rotation_euler',frame=f)
|
||||
# Warm reveal is brief and darker than the previous white flash.
|
||||
flash=bpy.data.lights['Brief reveal'];flash.animation_data_clear();flash.color=(1,.24,.07)
|
||||
for f,power in [(1,0),(310,0),(316,350),(322,65),(332,140),(340,0),(480,0)]:flash.energy=power;flash.keyframe_insert('energy',frame=f)
|
||||
s.frame_set(200);s.render.image_settings.file_format='PNG';s.render.filepath=os.path.join(OUT,'example_')
|
||||
bpy.ops.file.pack_all();bpy.ops.wm.save_as_mainfile(filepath=os.path.join(OUT,'halloween_examples.blend'))
|
||||
for frame in [310,322,334]:
|
||||
s.frame_set(frame);s.render.filepath=os.path.join(OUT,'example_%03d.png'%frame);bpy.ops.render.render(write_still=True)
|
||||
print('V3 EXAMPLES COMPLETE — no video rendered')
|
||||
Reference in new issue
Block a user