Files

106 lines
8.1 KiB
Python

import bpy, math, os, json
from mathutils import Vector, Matrix
from math import sin, cos, pi
OUT=os.path.dirname(os.path.abspath(__file__)); s=bpy.context.scene
detail=bpy.data.collections['DETAIL - full geometry for renders']
col=bpy.data.collections.new('32 Bedroom - Eight Sleep blackout and air conditioning');detail.children.link(col)
COL={'sleep':col};active='sleep'
src=open(OUT+'/../samford-modular/build_scene.py').read();exec(src[src.index('def mesh('):src.index('timber=textured(')])
cubefs=[tuple(reversed(f)) for f in cubefs]
wood=bpy.data.materials['Unpainted hardwood - long X grain'];steel=bpy.data.materials['Reusable dark steel bolts and shoes']
linen=bpy.data.materials['Undyed flax linen'];stone=bpy.data.materials['Split sandstone facing - mineral grain']
black=textured('Eight Sleep charcoal cover textile',(.027,.029,.033),(.065,.067,.071),(110,110,110),.91,.001)
hubmat=mat('Eight Sleep hub graphite casing',(.042,.045,.049),.48)
white=mat('Quiet AC warm white casing',(.79,.78,.73),.38)
lining=mat('Opaque blackout curtain lining',(.095,.09,.08),1)
curtainmat=textured('Blackout curtain oatmeal woven face',(.27,.24,.18),(.48,.43,.34),(100,100,20),.98,.002)
F=1.12;bx=-12.5;by=-5
cover=box('EIGHT SLEEP - fitted temperature regulating cover',(bx,by,1.727),(1.91,2.16,.065),black,.035)
cover['Product']='Eight Sleep Pod cover and bedside Hub; approximate concept geometry, not a manufacturer CAD model.'
box('Eight Sleep fitted cover north skirt',(bx,-3.914,1.65),(1.89,.022,.15),black,.018)
for x in [-13.454,-11.546]:box('Eight Sleep fitted cover side skirt',(x,by,1.65),(.025,2.10,.15),black,.014)
box('Bed folded undyed linen throw',(bx,-4.24,1.79),(1.83,.59,.07),linen,.025)
hx,hy=-13.97,-5.60
box('EIGHT SLEEP - bedside cooling Hub',(hx,hy,F+.245),(.29,.41,.47),hubmat,.045)
box('Eight Sleep hub top lid',(hx,hy,F+.485),(.255,.365,.014),black,.028)
box('Eight Sleep hub front intake',(hx,hy+.207,F+.24),(.22,.008,.32),black,.006)
for z in range(18):box('Hub ventilation slot',(hx,hy+.213,F+.09+z*.016),(.205,.004,.004),steel,.001)
for x in [hx-.10,hx+.10]:
for y in [hy-.14,hy+.14]:box('Hub rubber isolating foot',(x,y,F+.014),(.033,.04,.028),black,.004)
# Twin hoses stay near the bed head and clear of the circulation route.
for dx in [-.02,.02]:
points=[(hx+dx,hy-.19,F+.19),(hx+dx,-6.21,F+.14),(-13.40+dx,-6.21,F+.16),(-13.40+dx,-6.04,1.68)]
for a,b in zip(points,points[1:]):rod('Eight Sleep water hose',a,b,.012,black,sides=12)
labeldata=bpy.data.curves.new('Eight Sleep wordmark','FONT');labeldata.body='EIGHT SLEEP';labeldata.size=.032;labeldata.align_x='CENTER';labeldata.materials.append(white)
label=bpy.data.objects.new('Eight Sleep Hub label',labeldata);col.objects.link(label);label.location=(hx,hy+.218,F+.405);label.rotation_euler=(pi/2,0,pi)
# Pleated curtains: two real opaque layers; shape keys expose the closed position.
box('Blackout curtain ceiling track',(-12.65,-2.69,4.22),(5.45,.06,.055),steel,.009)
box('Blackout curtain timber pelmet',(-12.65,-2.73,4.245),(5.5,.23,.20),wood,.01)
curtains=[]
for side,edge,sign in [('left',-15.35,1),('right',-9.95,-1)]:
vs=[];fs=[];steps=64
for z in [1.14,4.18]:
for i in range(steps+1):
u=i/steps;vs.append((edge+sign*.82*u,-2.68+.065*cos(u*2*pi*8),z))
for i in range(steps):fs.append((i,i+1,steps+i+2,steps+i+1))
o=mesh('BLACKOUT CURTAIN - '+side,vs,fs,curtainmat);o.data.materials.append(lining)
o.shape_key_add(name='Basis - open for viewing');closed=o.shape_key_add(name='Closed - blackout')
for i,v in enumerate(closed.data):v.co.x=edge+sign*2.73*(i%(steps+1))/steps
closed.value=0.0;o.active_shape_key_index=0
m=o.modifiers.new('Opaque blackout lining','SOLIDIFY');m.thickness=.007;m.material_offset=1
o['How to close']='Object Data Properties > Shape Keys > Closed - blackout: set Value to 1 on both panels. DETAIL collection must be enabled for editing.'
curtains.append(o)
# Indoor split head on the west wall near the foot of the bed.
ax,ay,az=-15.57,-3.48,3.67
box('AIR CONDITIONER - bedroom indoor split head',(ax,ay,az),(.23,.92,.30),white,.055)
box('Air conditioner dark outlet',(ax+.118,ay,az-.09),(.014,.79,.065),black,.008)
for z in [az-.11,az-.075]:box('Air conditioner adjustable louvre',(ax+.13,ay,z),(.07,.78,.012),white,.003)
box('Air conditioner status panel',(ax+.12,ay-.29,az+.045),(.009,.10,.035),hubmat,.003)
box('AC remote on bedroom wall',(-15.67,-4.18,2.33),(.038,.065,.15),white,.008)
box('AC remote display',(-15.647,-4.18,2.35),(.009,.048,.045),hubmat,.002)
# External condenser on isolation feet, behind the sleeping pavilion.
cx,cy,cz=-14.70,-8.12,.79
box('AC outdoor unit stable pad',(cx,cy,.39),(1.03,.67,.10),stone,.015)
box('AIR CONDITIONER - outdoor condenser',(cx,cy,cz),(.88,.39,.64),white,.035)
for x in [cx-.30,cx+.30]:box('AC outdoor vibration mount',(x,cy,.46),(.085,.32,.04),black,.005)
rod('AC outdoor circular fan grille',(cx-.12,cy-.202,cz),(cx-.12,cy-.215,cz),.24,black,sides=48)
for i in range(11):
x=cx-.34+i*.044
half=math.sqrt(max(0,.235**2-(x-(cx-.12))**2))
rod('Condenser grille bar',(x,cy-.22,cz-half),(x,cy-.22,cz+half),.0045,steel,sides=6)
for z in range(9):box('Condenser service ventilation',(cx+.30,cy-.20,cz-.20+z*.048),(.14,.008,.011),steel,.002)
pipepoints=[(-15.94,-3.48,3.65),(-15.94,-7.72,3.65),(-15.94,-7.72,.8),(-15.05,-7.72,.8),(-15.05,-7.92,.8)]
for a,b in zip(pipepoints,pipepoints[1:]):beam('AC accessible insulated service trunking',a,b,.075,.065,white)
rod('AC condensate drain down exterior',(-15.94,-3.5,3.52),(-15.94,-3.5,.5),.013,white,sides=10)
s['Bedroom sleep setup']='Eight Sleep Pod-style fitted cover with ventilated bedside Hub, lined blackout curtains with closed shape keys, indoor split AC and outdoor condenser. Concept placement; final product clearances and HVAC sizing are unresolved.'
# Preserve fast tour performance with one display proxy for the new static pieces.
verts=[];faces=[];indices=[];materials=[];mapping={}
for o in col.objects:
if o.type!='MESH':continue
remap=[]
for m in o.data.materials:
if m not in mapping:mapping[m]=len(materials);materials.append(m)
remap.append(mapping[m])
world=Matrix.LocRotScale(o.location.copy(),o.rotation_euler.to_quaternion(),o.scale.copy());off=len(verts)
verts.extend(tuple(world@v.co) for v in o.data.vertices)
for p in o.data.polygons:faces.append(tuple(off+i for i in p.vertices));indices.append(remap[p.material_index])
me=bpy.data.meshes.new('Bedroom sleep equipment lightweight display');me.from_pydata(verts,[],faces);me.update()
for m in materials:me.materials.append(m)
me.polygons.foreach_set('material_index',indices)
proxy=bpy.data.objects.new('FAST | Bedroom sleep equipment',me);bpy.data.collections['00 FAST TOUR - lightweight viewport'].objects.link(proxy)
# Refine the existing bedroom chapter without changing the two-minute tour length.
cam=bpy.data.objects['PLAY TOUR camera']
for frame,loc,target in [(841,(-10.70,-6.95,3.30),(-13.10,-4.60,1.97)),(960,(-10.95,-6.73,3.20),(-13.10,-4.52,1.97))]:
s.frame_set(frame);cam.location=loc;cam.rotation_euler=(Vector(target)-cam.location).to_track_quat('-Z','Y').to_euler();cam.keyframe_insert('location',frame=frame);cam.keyframe_insert('rotation_euler',frame=frame);cam.data.lens=20;cam.data.keyframe_insert('lens',frame=frame)
s.frame_set(841);s.camera=cam
report={'eight_sleep_cover':1,'eight_sleep_hub':1,'blackout_curtain_panels':2,'curtain_closed_shape_keys':2,'indoor_ac':1,'outdoor_condenser':1,'tour_chapters':24,'tour_seconds':120,'new_fast_display_meshes':1}
json.dump(report,open(OUT+'/bedroom-sleep-checks.json','w'),indent=2)
bpy.ops.wm.save_as_mainfile(filepath=OUT+'/samford-solarpunk-bedroom-tour.blend')
for o in bpy.data.collections['22 PLAY - guided camera tour'].objects:
if o.type=='FONT':o.hide_render=True
s.render.resolution_percentage=60;s.cycles.samples=32;s.cycles.use_denoising=True
prefs=bpy.context.preferences.addons['cycles'].preferences;prefs.compute_device_type='METAL';prefs.get_devices()
for d in prefs.devices:d.use=d.type=='METAL'
s.cycles.device='GPU';s.render.filepath=OUT+'/bedroom-sleep-setup.png';bpy.ops.render.render(write_still=True)
print('BEDROOM_SLEEP_READY '+json.dumps(report),flush=True)