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)