- .animctrl state machine asset (params/transitions/blend trees, 1D+2D
directional blending) with Unity Animator-style editor panel: state
list, transitions, params, directional pad preview driving live
CrossFade on the selected entity
- Split animation evaluation out of SceneRenderer into
AnimationSystem/AnimationEvaluator; skin palettes with rest-pose
fallback and blend-weight epsilon filtering
- MaterialAsset (.material) with reload-safe preview tile pool
(DropMaterial clears dirty refs before instance destruction)
- EditorGizmoPass skeleton wires + viewport joint overlay; camera
projection rework (baked Vulkan Y-flip, ortho support)
- Editor/game build split via HELLOVULKAN_EDITOR option
The smooth brass case back of the pocket watch reflects large bright
sky regions of the Venice dawn environment at HDR values well above 1.
With the old threshold of 1.0 the entire metal surface passed the
bloom extract, and the blurred additive result washed it out to flat
white. Only genuinely hot highlights (HDR >= 3, near-white after ACES)
should bloom.
- Default bloomThreshold 1.0 -> 3.0; Stats slider range extended to 8
with a logarithmic scale
- Texture mip generation: transition the whole mip chain to
TRANSFER_DST up front; blitting into never-transitioned destination
mips triggered one validation warning per level
TAA subpixel jitter makes rasterized HDR specular highlights oscillate
per frame. The old RGB min/max neighborhood clamp re-clamped the
converged history against jitter-driven extremes every frame, leaving a
periodic residual that the bloom threshold then amplified into visible
glow pulsing on static models.
- TAA: replace the RGB AABB clamp with YCoCg variance clipping
(history clamped toward the stable neighborhood mean +/- 1.25 sigma,
intersected with the min/max box as a ghosting upper bound)
- Bloom extract: 5-tap cross prefilter before the threshold spreads
surviving 1px jitter energy over a ~4x4 footprint so the glow no
longer pulses
Root causes behind blurry/unphysical materials (4K pocket watch render
mushy, glass fully opaque, watch hands unreadable):
- Textures loaded via stbi had a single mip level: 4096x4096 albedo/MR
maps sampled with no LOD -> minification aliasing turned fine detail
(watch hands) into noise. Generate a full blit-based mip chain with
per-level layout transitions.
- samplerAnisotropy was never enabled on the device and every sampler
was created with anisotropy disabled: grazing-angle surfaces (watch
face, ground) collapsed into a blur. Enable the device feature and
16x anisotropic filtering on material texture samplers.
- glTF baseColorFactor was ignored: the glass material (alphaMode
BLEND, no baseColor texture, baseColorFactor.a = 0.3) fell back to
the default white texture with alpha = 1 and rendered fully opaque.
MaterialParams gains a baseColor vec4 (rgb tints albedo, a scales
the blend alpha) threaded through both PBR shaders.
- alphaMode MASK was treated as opaque: add a factors.w switch that
performs an alpha cutout test in the opaque shader.
- Fix material cache key dropping factors.z (separate-roughness
switch); it now encodes the full factors and baseColor vectors.
SetCursorCaptured / IsCursorCaptured / GetMouseDelta / ConsumeScrollDelta
now route through the Input facade, so the editor camera no longer takes
a Window& at all: Camera::Update and EditorCamera::Update lose their
window parameter and SandboxApp calls Update(dt) directly. App layer
now has zero Window/GLFW usage for input - everything goes through
Engine::Input.
New Engine/Core/Input.h/.cpp: static input facade with a Key/MouseButton
enum (values map to GLFW key codes internally) so scripts and game logic
no longer touch GLFW directly - window backend becomes swappable and the
facade is the surface to expose when embedding scripting later. Wired up
in Application's constructor; Camera (editor camera WASD/RMB/MMB), the
WASDMove script and SandboxApp key handling now query Input only.
Ctrl+P hotkey fix: the !m_Playing guard made the hotkey dead exactly
when it was needed - after pressing Play. Now toggles both ways.
Toolbar Pause button now reads "Resume" while paused (tooltip follows).
BuildFrame returned false (skip frame) when the game view had no
primary camera, skipping the WaitFence/ResetFence/submit chain - the
frame-slot fence never got signaled, so the next WaitFence on that
slot blocked forever. Reproduced instantly by playing an empty scene
with embedded PIE; latent in manual fullscreen preview too.
Game view now falls back to the editor camera instead of skipping the
frame, keeping the per-slot fence cadence intact. Viewport warning
text updated to mention the editor-view fallback.
Play no longer switches to fullscreen direct rendering: PIE now keeps
the editor UI alive and renders the primary camera into the Viewport
panel (new SceneRenderer::SetGameView flag; kDisplayDirect stays a
manual fullscreen preview for later release form). Editor camera stops
being driven during PIE, and viewport click-pick is gated off while
playing (clicks are game input now).
The Play/Stop buttons move out of the menu bar into a dedicated main
toolbar row below it (centered, UE Main Toolbar semantics): green Play
when idle; Pause (yellow, highlighted while paused) + Stop (orange)
while playing. Ctrl+P hotkey kept. Stats "Pause Simulation" merged
into the toolbar Pause; viewport overlay shows PAUSED accordingly.
Details: multi-parameter components (RigidBody/Joint/CharacterController/
Animation) now use SeparatorText category headers in UE Details style;
all parameter labels normalized to Pascal Case.
Stats: rearranged into View / Scene / Rendering / Physics / Performance
collapsing groups. Clear Color moved to Scene, Debug View to Rendering,
GPU Timings + Frame merged into Performance; bare top-level widgets and
the now-obsolete Environment group removed.
Skybox/SkyLight are now Actor-carried components instead of resident
global settings, matching UE where SkyAtmosphere/SkyLight/fog are
Actors added from Place Actors and configured in Details.
- New SkyboxComponent (active + envPath asset slot) and SkyLightComponent
(active + ambientColor); first-active entity wins per frame
- SceneRenderer collects env from components into FrameSceneData
(DrawSkybox/envPath/ambient); Renderer performs delta sync - env
reloads only when the resolved path changes, neutral fallback when
empty or unloadable
- App-side env plumbing removed: kEnvPaths table and getEnvIndex/
setEnvIndex callbacks deleted; StatsPanel keeps ClearColor + debug
view, env params move to Details
- Serializer: scene-level Settings stores ClearColor only; env
components serialize per entity; legacy v1 scenes (Environment header
+ Settings.AmbientColor/DrawSkybox) auto-migrate into an Environment
entity on load for visual parity
- Inspector: Skybox/Sky Light sections with .hdr asset slot dropdown
and Add Component entries; Outliner "+ Create" popup offers
Empty/Light/Camera/Environment
Panels renamed to UE semantics (docking identity change): Hierarchy ->
Outliner, Inspector -> Details, Asset Browser -> Content Browser.
Default DockBuilder layout now matches UE5 factory layout: Details
docked right (full height), Content Browser docked bottom spanning
left+center, Outliner top-left, Viewport center with Stats as a
side tab.
Legacy imgui.ini (old window names, no new names) is removed once at
Init so the new default rebuilds cleanly; fresh or new-style ini is
untouched.
2268fa5 added the dirty-flag deferred flush but left the immediate
save in place, so new GUID registrations wrote the registry twice
(once inline, once at the next FlushGUIDRegistry). Remove the inline
save so the deferred path is the only writer.
entt entity 0's integral bits are 0, colliding with the "no selection"
sentinel: u32 selected == 0 meant "clear selection", so the scene's
first-created entity could never be outlined. Replaced with
std::optional<u32> + FrameSceneData::selectedEntityValid.
Also restore per-batch instance contiguity broken by the batchMats
flattening (e038cb3): entities sharing a mesh scatter across the flat
instance array in collection order. ViewScratch now tracks per-batch
singly-linked lists and appendView aggregates each batch into a
contiguous range, preserving the flat-array performance intent.
Hierarchy click selection now routes through SelectEntity so outline
stays in sync when picking from the panel.
With GLFW_CURSOR_DISABLED the official GLFW backend keeps feeding
virtual unbounded cursor coords to ImGui each frame, so hover state
wanders across hidden panels while the physical cursor is invisible.
Append a "mouse left the window" pos event before ImGui::NewFrame
whenever the cursor is captured - ImGui treats the frame as mouseless.
UE's far clip is effectively unlimited and shadow distance is an
independent parameter. Split kCameraFar (main projection, 10km) from
kShadowDistance (cascade splits, unchanged 100m) so distant geometry
is no longer clipped while shadow quality stays bit-identical.
Main projection now references the shared constants instead of
duplicated literals.
Assimp flags every mesh-less scene as AI_SCENE_FLAGS_INCOMPLETE
(FBXConverter), so pure animation FBX never reached clip parsing -
the scene was discarded before ParseAnimations ran. Only treat null
scene / missing root node as hard failure; log INCOMPLETE as a
warning and continue. Final validity stays with ParsedModel::Valid().
Pure-animation FBX (no meshes, skeleton clips only) was rejected at
the cache layer: ParsedModel::Valid() required non-empty meshes, so
Model::FromParsed/LoadModel returned null and the asset never made it
into the cache. Animation assets are now first-class:
- ParsedModel::Valid() = meshes or clips non-empty; parse failures
(both empty) still rejected. Empty-mesh consumers already guarded
(rendering skips empty mesh loop, mesh collider falls back to Box)
- ParseFromDisk: fix null-deref when assimp returns a null scene
- AssetCache::Find(path) + AssetManager::GetModelByPath for panels
- Asset Browser hover card shows meshes/bones/clips for loaded models
- Inspector model line flags "animation-only" when meshes == 0
- Animation section: collapsible clip data table (name/duration/
channel count) to verify bone-name matching of attached clips