Add enhanced house presentation, client film and walkable Unreal prototype
This commit is contained in:
77
unreal/SamfordHomestead/Content/Python/build_homestead.py
Normal file
77
unreal/SamfordHomestead/Content/Python/build_homestead.py
Normal file
@@ -0,0 +1,77 @@
|
||||
import unreal as u,json,math,time
|
||||
from pathlib import Path
|
||||
ROOT=Path(__file__).resolve().parents[3];DATA=json.loads((ROOT/'transfer/homestead-manifest.json').read_text());LIB=u.EditorAssetLibrary;MAT=u.MaterialEditingLibrary;TOOLS=u.AssetToolsHelpers.get_asset_tools();ACT=u.get_editor_subsystem(u.EditorActorSubsystem);LEVEL=u.get_editor_subsystem(u.LevelEditorSubsystem)
|
||||
u.EditorPythonScripting.set_keep_python_script_alive(True)
|
||||
def prop(o,k,v):o.set_editor_property(k,v)
|
||||
def log(s):u.log('HOMESTEAD: '+s)
|
||||
def imp(file,dest,options=None):
|
||||
t=u.AssetImportTask()
|
||||
for k,v in dict(filename=str(file),destination_path=dest,automated=True,replace_existing=True,save=True).items():prop(t,k,v)
|
||||
if options:prop(t,'options',options)
|
||||
TOOLS.import_asset_tasks([t]);return [LIB.load_asset(p) for p in t.imported_object_paths]
|
||||
def scalar(m,n,out):
|
||||
a=MAT.create_material_expression(m,u.MaterialExpressionConstant);prop(a,'r',n);MAT.connect_material_property(a,'',out)
|
||||
def material(name,r):
|
||||
path='/Game/Homestead/Materials';m=LIB.load_asset(path+'/'+name) or TOOLS.create_asset(name,path,u.Material,u.MaterialFactoryNew());MAT.delete_all_material_expressions(m);prop(m,'two_sided',True)
|
||||
if r.get('base_color_texture'):
|
||||
tex=imp(ROOT/'transfer'/r['base_color_texture'],'/Game/Homestead/Textures')[0];a=MAT.create_material_expression(m,u.MaterialExpressionTextureSample);prop(a,'texture',tex);out='RGB'
|
||||
else:
|
||||
a=MAT.create_material_expression(m,u.MaterialExpressionConstant3Vector);prop(a,'constant',u.LinearColor(*r['base_color']));out=''
|
||||
MAT.connect_material_property(a,out,u.MaterialProperty.MP_BASE_COLOR)
|
||||
if r.get('normal_texture'):
|
||||
tex=imp(ROOT/'transfer'/r['normal_texture'],'/Game/Homestead/Textures')[0];prop(tex,'compression_settings',u.TextureCompressionSettings.TC_NORMALMAP);prop(tex,'srgb',False);prop(tex,'flip_green_channel',True);LIB.save_loaded_asset(tex);a=MAT.create_material_expression(m,u.MaterialExpressionTextureSample);prop(a,'texture',tex);prop(a,'sampler_type',u.MaterialSamplerType.SAMPLERTYPE_NORMAL);MAT.connect_material_property(a,'RGB',u.MaterialProperty.MP_NORMAL)
|
||||
scalar(m,r['roughness'],u.MaterialProperty.MP_ROUGHNESS);scalar(m,r['metallic'],u.MaterialProperty.MP_METALLIC)
|
||||
if r.get('transmission',0)>0 or 'translucent footprint' in r['source']:
|
||||
prop(m,'blend_mode',u.BlendMode.BLEND_TRANSLUCENT);scalar(m,.13,u.MaterialProperty.MP_OPACITY)
|
||||
if r.get('emission',0)>0:
|
||||
a=MAT.create_material_expression(m,u.MaterialExpressionConstant3Vector);prop(a,'constant',u.LinearColor(*[v*r['emission'] for v in r['base_color'][:3]],1));MAT.connect_material_property(a,'',u.MaterialProperty.MP_EMISSIVE_COLOR)
|
||||
MAT.recompile_material(m);LIB.save_loaded_asset(m);return m
|
||||
materials={n:(LIB.load_asset('/Game/Homestead/Materials/'+n) if LIB.does_asset_exist('/Game/Homestead/Materials/'+n) else material(n,r)) for n,r in DATA['materials'].items()};log('Materials saved')
|
||||
opts=u.FbxImportUI()
|
||||
for k,v in dict(import_mesh=True,import_materials=False,import_textures=False,import_as_skeletal=False,import_animations=False,mesh_type_to_import=u.FBXImportType.FBXIT_STATIC_MESH).items():prop(opts,k,v)
|
||||
for k,v in dict(combine_meshes=False,auto_generate_collision=False,generate_lightmap_u_vs=False,convert_scene=True,force_front_x_axis=False).items():prop(opts.static_mesh_import_data,k,v)
|
||||
if len(LIB.list_assets('/Game/Homestead/Meshes',recursive=True))!=len(DATA['objects']):imp(ROOT/'transfer/SamfordHomestead.fbx','/Game/Homestead/Meshes',opts)
|
||||
meshes=[LIB.load_asset(p) for p in LIB.list_assets('/Game/Homestead/Meshes',recursive=True)];meshes=[m for m in meshes if isinstance(m,u.StaticMesh)];assert len(meshes)==len(DATA['objects']),(len(meshes),len(DATA['objects']))
|
||||
LEVEL.new_level('/Game/Homestead/SamfordHomestead');records={r['name']:r for r in DATA['objects']};collision_meshes=0
|
||||
for mesh in meshes:
|
||||
rec=records[mesh.get_name().removeprefix('SamfordHomestead_')];mesh.set_material(0,materials[rec['material']]);body=mesh.get_editor_property('body_setup')
|
||||
if rec['collision']:
|
||||
prop(body,'collision_trace_flag',u.CollisionTraceFlag.CTF_USE_COMPLEX_AS_SIMPLE);prop(body,'double_sided_geometry',True);u.get_editor_subsystem(u.StaticMeshEditorSubsystem).enable_section_collision(mesh,True,0,0);collision_meshes+=1
|
||||
actor=ACT.spawn_actor_from_class(u.StaticMeshActor,u.Vector(0,0,0));actor.static_mesh_component.set_static_mesh(mesh);actor.set_actor_label(mesh.get_name());actor.set_folder_path(rec['category']);actor.static_mesh_component.set_collision_profile_name('BlockAll' if rec['collision'] else 'NoCollision');LIB.save_loaded_asset(mesh)
|
||||
# Check known main-floor origin and all coordinate units from FBX bounds.
|
||||
main=next(m for m in meshes if '01_Living' in m.get_name() and 'concrete' in records[m.get_name().removeprefix('SamfordHomestead_')]['material'].lower());b=main.get_bounds();assert abs(b.origin.x)<5 and abs(b.origin.y)<5 and 50<b.origin.z<150,str(b)
|
||||
def pos(v):return u.Vector(v[0]*100,-v[1]*100,v[2]*100)
|
||||
def spawn(cls,label,xyz,rot=None):
|
||||
a=ACT.spawn_actor_from_class(cls,pos(xyz),rot or u.Rotator(0,0,0));a.set_actor_label(label);return a
|
||||
sun=spawn(u.DirectionalLight,'Warm northeast morning',(12,14,20),u.Rotator(-32,-140,0));sun.light_component.set_mobility(u.ComponentMobility.MOVABLE);sun.light_component.set_intensity(4200);sun.light_component.set_light_color(u.LinearColor(1,.85,.67,1));prop(sun.light_component,'light_source_angle',2.0);prop(sun.light_component,'atmosphere_sun_light',True)
|
||||
spawn(u.SkyAtmosphere,'Samford open sky',(0,0,0));sky=spawn(u.SkyLight,'Soft sky fill',(0,0,6));sky.light_component.set_mobility(u.ComponentMobility.MOVABLE);sky.light_component.set_intensity(1.4);prop(sky.light_component,'real_time_capture',True)
|
||||
fog=spawn(u.ExponentialHeightFog,'Subtle valley atmosphere',(0,0,-6));prop(fog.component,'fog_density',.006)
|
||||
post=spawn(u.PostProcessVolume,'Natural architectural exposure',(0,0,0));prop(post,'unbound',True);p=post.settings
|
||||
for k,v in dict(override_auto_exposure_min_brightness=True,override_auto_exposure_max_brightness=True,auto_exposure_min_brightness=6.0,auto_exposure_max_brightness=6.0,override_auto_exposure_bias=True,auto_exposure_bias=0,override_bloom_intensity=True,bloom_intensity=.12,override_vignette_intensity=True,vignette_intensity=.12).items():prop(p,k,v)
|
||||
prop(post,'settings',p)
|
||||
for x in [-4.37,-3.22]:
|
||||
a=spawn(u.PointLight,'Warm kitchen pendant',(x,.1,3.14));a.light_component.set_mobility(u.ComponentMobility.MOVABLE);a.light_component.set_intensity(90);a.light_component.set_light_color(u.LinearColor(1,.70,.40,1));prop(a.light_component,'attenuation_radius',220)
|
||||
# Blueprint defaults use the bundled architectural walking component, no game weapons or avatar.
|
||||
f=u.BlueprintFactory();prop(f,'parent_class',u.ArchVisCharacter);bp=LIB.load_asset('/Game/Homestead/Walkthrough/BP_HomesteadWalker') if LIB.does_asset_exist('/Game/Homestead/Walkthrough/BP_HomesteadWalker') else TOOLS.create_asset('BP_HomesteadWalker','/Game/Homestead/Walkthrough',u.Blueprint,f);cl=bp.generated_class();default=u.get_default_object(cl);default.capsule_component.set_capsule_size(27,90);prop(default,'base_eye_height',78);prop(default.character_movement,'max_step_height',36);prop(default.character_movement,'walking_speed',165);LIB.save_loaded_asset(bp)
|
||||
f=u.BlueprintFactory();prop(f,'parent_class',u.GameModeBase);gm=LIB.load_asset('/Game/Homestead/Walkthrough/BP_HomesteadGameMode') if LIB.does_asset_exist('/Game/Homestead/Walkthrough/BP_HomesteadGameMode') else TOOLS.create_asset('BP_HomesteadGameMode','/Game/Homestead/Walkthrough',u.Blueprint,f);prop(u.get_default_object(gm.generated_class()),'default_pawn_class',cl);LIB.save_loaded_asset(gm)
|
||||
world=u.get_editor_subsystem(u.UnrealEditorSubsystem).get_editor_world();prop(world.get_world_settings(),'default_game_mode',gm.generated_class())
|
||||
start=spawn(u.PlayerStart,'START HERE - verandah WASD and mouse',(0,4.55,2.03),u.Rotator(0,90,0))
|
||||
# Presentation cameras also remain available in the editor.
|
||||
shots=[('Overview',(35,43,30),(0,-4,1),38),('Verandah',(6,10,3.4),(0,1.4,2),32),('Kitchen',(1.5,2.4,2.85),(-3,-1,2),25),('Garden Buddha',(-7.5,18.2,2.6),(-12.4,15.35,.6),49)]
|
||||
cameras=[]
|
||||
for name,xyz,target,lens in shots:
|
||||
cp=pos(xyz);cam=ACT.spawn_actor_from_class(u.CameraActor,cp,u.MathLibrary.find_look_at_rotation(cp,pos(target)));cam.set_actor_label('CAM - '+name);cam.camera_component.set_field_of_view(math.degrees(2*math.atan(36/(2*lens))));prop(cam.camera_component,'aspect_ratio',16/9);cameras.append(cam)
|
||||
u.EditorLevelLibrary.set_level_viewport_camera_info(cameras[1].get_actor_location(),cameras[1].get_actor_rotation());u.AutomationLibrary.set_editor_viewport_view_mode(u.ViewModeIndex.VMI_LIT)
|
||||
LEVEL.save_current_level();LIB.save_directory('/Game/Homestead',only_if_is_dirty=True,recursive=True)
|
||||
report={'meshes':len(meshes),'materials':len(materials),'collision_meshes':collision_meshes,'first_person_class':str(cl),'spawn_blender_metres':[0,4.55,2.03],'level_saved':True,'movement_test':'pending','render':'pending'}
|
||||
(ROOT/'unreal-build-checks.json').write_text(json.dumps(report,indent=2));log('Level saved, warming shaders for screenshots')
|
||||
state={'started':time.monotonic(),'task':None,'index':0}
|
||||
def tick(dt):
|
||||
if time.monotonic()-state['started']<35:return
|
||||
if state['task'] is None:
|
||||
u.AutomationLibrary.finish_loading_before_screenshot();index=state['index'];state['task']=u.AutomationLibrary.take_high_res_screenshot(1920,1080,str(ROOT/'renders'/('unreal-'+['verandah','overview'][index]+'.png')),camera=cameras[[1,0][index]],delay=3)
|
||||
elif state['task'].is_task_done():
|
||||
state['index']+=1;state['task']=None
|
||||
if state['index']==2:
|
||||
u.unregister_slate_post_tick_callback(handle);log('UNREAL_HOMESTEAD_READY');u.SystemLibrary.quit_editor()
|
||||
handle=u.register_slate_post_tick_callback(tick)
|
||||
Reference in New Issue
Block a user