Release 0.9.5 texture and material refinement
This commit is contained in:
1 parent
fb3da52438
commit
4bc6390e9f
28 files changed
+160
-78
No files matched your search
+45
-15
@@ -81,15 +81,16 @@ const SHOTS_PORTRAIT := [
|
||||
]
|
||||
## Stage list: shift time selects the phase, `venue` switches the scenario first.
|
||||
const STAGES := [
|
||||
{"name": "day", "elapsed": 0.0, "venue": "", "shots": SHOTS_DAY, "night": false},
|
||||
{"name": "surfaces", "elapsed": 0.0, "venue": "", "shots": SHOTS_SURFACES, "night": false},
|
||||
{"name": "surfaces_joke", "elapsed": 0.0, "venue": "joke_shop", "shots": SHOTS_SURFACES_JOKE, "night": false},
|
||||
# Portrait next, while the day crowd is still on the street.
|
||||
{"name": "portrait", "elapsed": 0.0, "venue": "", "shots": SHOTS_PORTRAIT,
|
||||
{"name": "day", "elapsed": 0.0, "venue": "hollywood_building", "shots": SHOTS_DAY, "night": false},
|
||||
{"name": "surfaces", "elapsed": 0.0, "venue": "hollywood_building", "shots": SHOTS_SURFACES, "night": false},
|
||||
# Portrait before the venue switch: re-staging a close-up after a rebuild is
|
||||
# not reliable, and this keeps the day crowd on Hollywood.
|
||||
{"name": "portrait", "elapsed": 0.0, "venue": "hollywood_building", "shots": SHOTS_PORTRAIT,
|
||||
"night": false},
|
||||
{"name": "evening", "elapsed": 620.0, "venue": "", "shots": SHOTS_EVENING, "night": true},
|
||||
{"name": "late", "elapsed": 760.0, "venue": "", "shots": SHOTS_LATE, "night": true},
|
||||
{"name": "evening", "elapsed": 620.0, "venue": "hollywood_building", "shots": SHOTS_EVENING, "night": true},
|
||||
{"name": "late", "elapsed": 760.0, "venue": "hollywood_building", "shots": SHOTS_LATE, "night": true},
|
||||
{"name": "joke_shop_day", "elapsed": 0.0, "venue": "joke_shop", "shots": SHOTS_JOKE_DAY, "night": false},
|
||||
{"name": "surfaces_joke", "elapsed": 0.0, "venue": "joke_shop", "shots": SHOTS_SURFACES_JOKE, "night": false},
|
||||
{"name": "joke_shop", "elapsed": 700.0, "venue": "joke_shop", "shots": SHOTS_JOKE, "night": true},
|
||||
{"name": "studio", "elapsed": 700.0, "venue": "tv_studio", "shots": SHOTS_STUDIO, "night": true},
|
||||
]
|
||||
@@ -180,6 +181,7 @@ func _process(delta: float) -> bool:
|
||||
return false
|
||||
|
||||
func _begin_stage() -> void:
|
||||
_clear_capture_pause()
|
||||
_stage += 1
|
||||
while _stage < STAGES.size() and not _only_stages.is_empty() \
|
||||
and not _only_stages.has(str(STAGES[_stage]["name"])):
|
||||
@@ -209,6 +211,7 @@ func _begin_stage() -> void:
|
||||
print("capture_realism: stage %s at %d ms" % [stage["name"], Time.get_ticks_msec()])
|
||||
|
||||
func _run_queue(delta: float) -> void:
|
||||
_clear_capture_pause()
|
||||
if _queue.is_empty():
|
||||
_phase = "stage"
|
||||
return
|
||||
@@ -241,9 +244,18 @@ func _run_queue(delta: float) -> void:
|
||||
_queue.pop_front()
|
||||
_shot_frames = 0
|
||||
|
||||
## Capture staging must not inherit a pause overlay from the previous stage or a
|
||||
## window-focus change; a paused frame hides the very surfaces the pass audits.
|
||||
func _clear_capture_pause() -> void:
|
||||
if gm == null:
|
||||
return
|
||||
var pause = gm.get("pause_overlay")
|
||||
if pause != null and pause.visible:
|
||||
gm.call("_close_pause")
|
||||
|
||||
## Frame a nearby NPC from the front, with the sun behind the camera so the face
|
||||
## is lit. The subject's own yaw is re-written every frame by its FSM, so the
|
||||
## portrait turns the visual `Rig` child instead and stands on the sunlit side.
|
||||
## is lit. The subject's FSM and idle head-turn are frozen so the still keeps the
|
||||
## face toward the lens; photo/film poses are skipped because their props cover it.
|
||||
func _stage_portrait(delta: float, angle_deg: float = 0.0) -> bool:
|
||||
var guard_pos: Vector3 = gm.guard.global_position
|
||||
var light := _sun_direction()
|
||||
@@ -254,7 +266,17 @@ func _stage_portrait(delta: float, angle_deg: float = 0.0) -> bool:
|
||||
var npc := node as Node3D
|
||||
if npc == null or _faces_covered(npc):
|
||||
continue
|
||||
if _portrait_type >= 0 and int(_profile_of(npc).get("npc_type")) != _portrait_type:
|
||||
var profile = _profile_of(npc)
|
||||
if profile == null:
|
||||
continue
|
||||
if _portrait_type >= 0 and int(profile.get("npc_type")) != _portrait_type:
|
||||
continue
|
||||
# A film/photo pose holds a camera or phone in front of the face; keep the
|
||||
# default portrait on an idle person so the texture pass sees skin.
|
||||
var pose := ""
|
||||
if npc.has_method("theater_pose"):
|
||||
pose = str(npc.call("theater_pose"))
|
||||
if _portrait_type < 0 and pose in ["photo", "film", "perform"]:
|
||||
continue
|
||||
var distance: float = npc.global_position.distance_to(guard_pos)
|
||||
if distance > 25.0:
|
||||
@@ -280,9 +302,10 @@ func _stage_portrait(delta: float, angle_deg: float = 0.0) -> bool:
|
||||
continue
|
||||
other.set_physics_process(false)
|
||||
other.global_position += Vector3(0.0, 0.0, 140.0)
|
||||
# Freeze the subject: its FSM re-writes the yaw every physics tick, which
|
||||
# would turn it away from the camera again.
|
||||
# Freeze the subject: its FSM re-writes the yaw every physics tick, and the
|
||||
# idle head-turn would still look away after the body is aimed at the camera.
|
||||
best.set_physics_process(false)
|
||||
best.set_process(false)
|
||||
# Clear standing spot on the sidewalk, away from the queue: the close-up is a
|
||||
# character still, not a slice of the scene, and the crowd would block it.
|
||||
var spot := Vector3(0.0, 0.0, 0.0)
|
||||
@@ -290,6 +313,11 @@ func _stage_portrait(delta: float, angle_deg: float = 0.0) -> bool:
|
||||
spot = gm.guard.get("spawn_position")
|
||||
best.global_position = spot + _portrait_spot
|
||||
best.rotation.y = 0.0
|
||||
var subject_head := best.get_node_or_null("Rig/Head") as Node3D
|
||||
if subject_head == null:
|
||||
subject_head = best.find_child("Head", true, false) as Node3D
|
||||
if subject_head != null:
|
||||
subject_head.rotation.y = 0.0
|
||||
# Stand between the subject and the sun, so the face is front-lit.
|
||||
var to_light := -light
|
||||
to_light = to_light.rotated(Vector3.UP, deg_to_rad(angle_deg))
|
||||
@@ -305,10 +333,12 @@ func _stage_portrait(delta: float, angle_deg: float = 0.0) -> bool:
|
||||
# Face the camera: from the subject, the guard lies in the -look direction.
|
||||
# Use the drawn rig so an FSM yaw on the root cannot fight the pose.
|
||||
var rig := best.find_child("Rig", true, false) as Node3D
|
||||
# NPC art faces +Z; yaw the root toward the guard so the face, not the
|
||||
# back, is under the camera.
|
||||
var face := atan2(-look.x, -look.z)
|
||||
best.rotation.y = face
|
||||
if rig != null:
|
||||
rig.rotation.y = atan2(-look.x, -look.z)
|
||||
else:
|
||||
best.rotation.y = atan2(-look.x, -look.z)
|
||||
rig.rotation.y = 0.0
|
||||
|
||||
return true
|
||||
|
||||
|
||||
@@ -42,10 +42,19 @@ func _process(delta: float) -> bool:
|
||||
if frame >= WARMUP_FRAMES + SAMPLE_FRAMES:
|
||||
var average := sample_ms / SAMPLE_FRAMES
|
||||
var meshes := 0
|
||||
var triangles := 0
|
||||
for avatar in avatars:
|
||||
meshes += avatar.find_children("*", "MeshInstance3D", true, false).size()
|
||||
print("NPC budget: %d avatars, %d meshes, avg frame %.2f ms (budget %.0f ms)" % [
|
||||
COUNT, meshes, average, BUDGET_MS])
|
||||
for node in avatar.find_children("*", "MeshInstance3D", true, false):
|
||||
var mi := node as MeshInstance3D
|
||||
if mi == null or mi.mesh == null:
|
||||
continue
|
||||
meshes += 1
|
||||
for surface in mi.mesh.get_surface_count():
|
||||
var arrays := mi.mesh.surface_get_arrays(surface)
|
||||
var indices: PackedInt32Array = arrays[Mesh.ARRAY_INDEX]
|
||||
triangles += indices.size() / 3
|
||||
print("NPC budget: %d avatars, %d meshes, %d triangles, avg frame %.2f ms (budget %.0f ms)" % [
|
||||
COUNT, meshes, triangles, average, BUDGET_MS])
|
||||
if average > BUDGET_MS:
|
||||
printerr("SMOKE FAIL: frame time %.1f ms exceeds the %.0f ms budget" % [
|
||||
average, BUDGET_MS])
|
||||
|
||||
Reference in new issue
Block a user