extends Node func settle() -> void: for frame in 4: await get_tree().process_frame await RenderingServer.frame_post_draw func difference(a: Image, b: Image, center: Vector2) -> float: var total := 0.0 var samples := 0 for y in range(int(center.y)-55,int(center.y)+55,2): for x in range(int(center.x)-55,int(center.x)+55,2): if x < 0 or y < 0 or x >= a.get_width() or y >= a.get_height(): continue var first := a.get_pixel(x,y) var next := b.get_pixel(x,y) total += absf(first.r-next.r)+absf(first.g-next.g)+absf(first.b-next.b) samples += 3 return total/maxi(1,samples) func _ready() -> void: var sheet := Image.create(1280,1080,false,Image.FORMAT_RGBA8) var max_pan_change := 0.0 var progress: Resource = GameState.captain.expedition for biome in 6: var profile: Dictionary = preload("res://scripts/planets/planet_profiles.gd").generate(99,biome,"Złoże / biom %d" % (biome+1)) profile.temperature = 75 if biome == 4 else (-35 if biome == 5 else 20) progress.lander_hp = 100 assert(progress.begin_visit(profile)) progress.visit_seed = 200+biome var surface := Control.new() surface.set_script(preload("res://scripts/planets/surface_screen.gd")) surface.captain = GameState.captain surface.progress = progress add_child(surface) surface.set_process(false) surface.set_physics_process(false) surface.lander.hide() surface._surface_viewport.get_node("SurfaceAtmosphere").hide() var target := Vector2.INF var chosen: Sprite2D for poi in surface.world.data.pois: if poi.kind != "deposit": continue var sprite: Sprite2D = surface.world._poi_sprites[poi.id] assert(sprite.texture.resource_path == "res://assets/planets/mineral_deposit.png") assert(sprite.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS) assert(sprite.texture.get_image().has_mipmaps()) assert(sprite.material.shader.resource_path.ends_with("mineral_deposit.gdshader")) assert(not sprite.material.shader.code.contains("TIME")) if poi.position.length_squared() < target.length_squared(): target = poi.position chosen = sprite assert(chosen != null) surface.world.refresh_pois(target) surface.camera.position_smoothing_enabled = false surface.camera.position = target surface.camera.force_update_scroll() await settle() var origin: Vector2 = chosen.get_global_transform_with_canvas().origin/surface.SURFACE_RENDER_SCALE var initial: Image = get_viewport().get_texture().get_image() initial.save_png("res://artifacts/mineral_deposit_biome_%d.png" % biome) var small := initial.duplicate() as Image small.convert(Image.FORMAT_RGBA8) small.resize(640,360,Image.INTERPOLATE_LANCZOS) sheet.blit_rect(small,Rect2i(0,0,640,360),Vector2i(biome%2*640,biome/2*360)) if biome == 0: await settle() assert(difference(initial,get_viewport().get_texture().get_image(),origin) < 0.0001,"Stationary ore must not flicker") var previous := initial for frame in 12: surface.camera.position = target+Vector2((frame+1)*0.25,0) surface.camera.force_update_scroll() await settle() var current: Image = get_viewport().get_texture().get_image() var change := difference(previous,current,origin) max_pan_change = maxf(max_pan_change,change) assert(change < 0.035,"Subpixel camera motion should remain smoothly filtered") previous = current surface.camera.position = target surface.camera.force_update_scroll() await settle() assert(difference(initial,get_viewport().get_texture().get_image(),origin) < 0.0001,"Returning to the same camera position restores the same pixels") progress.finish_visit(GameState.captain,false) surface.queue_free() await get_tree().process_frame sheet.save_png("res://artifacts/mineral_deposit_biomes.png") print("MINERAL DEPOSIT PASS: six biome renders, real ore instead of icon, mipmaps, alpha/soil blending, no stationary flicker, smooth subpixel panning, identical pixels after camera return; max mean pan delta=",max_pan_change) get_tree().quit()