Host-driven navigation across the three SDKs - #3
Merged
Merged
Conversation
A host that hands the SDK a camera position has no way to know whether the collider has a floor under it, and a walker placed where it has none refuses every step: the collider bounds the world, so the walk is over before it starts. findStandingSpot searches outward from the middle of the collider for a surface with room on every side and returns the eye position to stand at. ColliderBuilder and GlbEncoder produce a collider from a world, and splat_collider writes one out, so a scene no longer needs a hand-authored mesh to be walkable. The LOD validator capped hierarchies at 32 levels. The grid builder floors its finest level at a 2^20 fraction of the extent, which at the default base of 1.5 spans about 35, so a real capture built a tree its own validator rejected and the world failed to upload. The cap now bounds a corrupt file only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The frame rate counted frames the renderer submitted, so a scene the display never caught up with still read as smooth. StatsPublisher now takes display times when the backend has them and reports presented fps, a p95 interval, a 1% low and dropped frames; without them it falls back to submitted frames and says so. WalkCamera resolves a spawn the collider cannot support, so a world captured about an arbitrary origin starts walkable, and it carries the host's character settings into the controller it builds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The iOS view owned a two-finger walk gesture, so an app that wanted its own control fought the SDK for touches and got a walk nobody could aim. The view now handles looking only, through a gesture recognizer that coexists with a host's own, and walking arrives as velocity from whatever control the app draws. Character settings and collider loading are part of the same surface. Metal reports each drawable's presented time, so the stats a HUD reads count frames the display showed rather than frames submitted, and the LOD selector applies lodErrorPixels and lodSplatLimit live, thinning detail evenly when a cut will not fit rather than stopping wherever traversal ran out of room. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The package drew no navigation of its own and offered none: an app could mount a view and look at a world, but walking, the collider, the character and the render policy had no prop or command behind them. They do now, with world, collider, policy and camera events reported back, and SplatKitBuilder resolves a quality preset against the limits the engine reports in onCapabilities. Phases and presets are const objects with derived types rather than bare string unions, so the adapters, the codegen spec and the tests all compare against the same names. Two defects the example turned up: SKRenderPolicy was left indeterminate, and the lodSplatLimit the props do not carry starved the selector to whatever was on the stack, so a world rendered or vanished by chance. And the component view never seeded _props with its own props type, which a debug build asserts on, so the app aborted at the first commit while release builds ran. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Navigation belongs to the app, so the example apps own it: the React Native example draws a thumb stick and pushes velocity straight to the view, and both it and the iOS dev app show a stats card and a quality picker built from the events the SDK reports. The React Native example resolves react and react-native to its own copy by redirecting where resolution starts, rather than rewriting requests to absolute paths, which skipped the package exports map every subpath import needs. Two copies of react-native means two view config registries, and a component registered in one is invisible to the renderer reading the other. Its world request carries the budgets it was built against, so the world reloads once when the engine reports limits that move them, and never otherwise. This example installs the package from the monorepo; point it back at the published version before release. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Credits the PlayCanvas splat-transform collision passes the collider builder ports. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Capabilities reach the host with the engine a world load creates, so the first request is built against a guess. A guess above what the adapter accepts is rejected before any engine exists, which means no capabilities event and no way for the host to learn what it got wrong: the example asked for a 4M LOD budget, Android accepts 2.2M, and the app sat on the error for good. conservativeCapabilities is the narrowest limits any shipped backend reports, and a test holds them inside the ranges the Android adapter validates against. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two fingers walked the scene, which left an app no way to place its own control and no way to aim the walk it produced. The view now looks only, one pointer leading the whole drag, and takes walk velocity, character settings and a collider from the host. JoystickView stays as a sample the example app draws, not as the SDK's answer to navigation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The adapter sent every timing as unavailable, so a host HUD on Android could never show a frame rate or a GPU time while the engine was measuring both and logging them. Zero is the SDK's own "not measured" for these fields: the GPU and sort times are zero without timestamp queries, and the frame time is zero while nothing redraws. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Android goes to 0.1.0-alpha08 with walking handed to the host, iOS to 0.1.0-alpha.4 and React Native to 0.1.0-alpha.2. The SwiftPM binary target and the npm package's prepack fetch both pin the new XCFramework by checksum. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The joystick is gone from all three SDKs. A view looks only; the host supplies walk velocity, character settings and a collider, and draws whatever control it wants. The sample joysticks live in the example apps.
What changed
setWalkVelocity,setLook, acolliderprop with load events, character settings and a motion toggle.conservativeCapabilitiesso a first world request carries limits no adapter rejects. Capabilities only arrive with the engine, so a request rejected before the engine exists left a host stuck for good; a test holds the values inside the ranges the Android adapter validates.SplatKitViewComponentViewnever seeded_props, which trips an assertRCTViewComponentViewonly makes in Debug builds.Releases cut from this branch
io.github.xget7:splatkit-android:0.1.0-alpha08v0.1.0-alpha.4@splatkit/react-native@0.1.0-alpha.2(npm tagnext)Verification
Physical devices, not emulators. iPhone (A19 Pro): 47-67 fps on a 6M-splat capture, spawn resolves to the collider centre, 0 quality-limited refinements at 0.50 px LOD error. Xiaomi MI 9 (Adreno 640, Android 11): same world and same spawn position, 454,316 collider triangles, walk mode active, GPU-bound at 7 fps on the Max preset.
splat_core 169 pass / 3 skipped, engine 206 pass, React Native package 35 pass, codegen green on both platforms, C++ lint clean.
🤖 Generated with Claude Code