Repository navigation
Track Lab challenge, boss and hub levels; Modern camera wall and hit fixes; textured caustics and low-pass water spray - #93
Conversation
…ed vertex colours A .blend shared or moved keeps naming an absolute model path that is no longer there; the recorded file is now looked for beside the .blend first. Vertex Paint edits on the baked layer reach the export, and meshes without a baked layer use their active colour layer.
…ls, tie the anchor to the racer during hits
Keeps Track Lab, the modern camera rework, the auto boot character fix and the water caustics/low-pass layers. The projection probe takes main's parallel draw buffer, which fixes the same D3D stride limit. The low-pass and caustics patches still have to be re-authored on main's new lava/waterfall chain (WaterDrawParams is now a full 2048 bytes).
…hain WaterDrawParams is a full 2048 bytes on main, so the per-craft clearance no longer gets its own field: it lives in the last two shoreline vectors (WATER_CRAFT_AIR), which water draws never fill and lava never reaches (it uses segments 0..15). Clearance is written only for water draws. Patches regenerated by the compose scripts; shader math and constants unchanged.
Regenerate the low-pass and caustics water layers on main's updated stability/waterfall patches (context only, same changes), and keep the camera Shake copy that matches the removed hit shake.
main's pattern guard refuses new first-party patch layers, so the caustics changes now ride on the existing baselined dkr-lava-glow.patch instead of a separate dkr-caustics.patch, and the texture moves out of patches/ to runtime-recomp/assets/water. The low-pass layer is regenerated on top of it. The composed RT64 tree is byte-identical to the previous chain.
Custom boss levels skip the retail boss intro lookup, which ran past the intro table for a custom level id. Hub EXITs can lead to another installed .dkrmap through manifest levelLinks, resolved after the object map is inflated. Tracks can carry up to 127 checkpoints. Shadow clipping on dense custom floors now respects the retail array sizes instead of overflowing them. A plain runtime configure links again through a retail pass-through for dkr_analogue_driving when the overlay does not define it.
The addon gets a Mode Checklist panel for battle, banana, egg, boss and hub levels, an enabled AI Node Graph panel, mode validation and a "Leads to" destination for hub exits that is exported as manifest levelLinks. Adding an AI branch no longer fails on the wrong class.
game_main.cpp and rt64_renderer.cpp call the native crash context directly since v1.0.15, but only the release overlay linked DKRCrashReports into DKRPortGame, so a configure without the overlay failed with two unresolved externals.
main's pattern guard refuses new first-party patch layers and string-replace pipelines, so the low-pass changes now ride on the baselined dkr-lava-glow.patch (as the caustics did in 80e4611) and dkr-lowpass.patch and compose-water-lowpass.py are removed. The folded patch applies with the same CMake flags and composes an RT64 tree byte-identical to the previous lava-glow + low-pass chain.
Twelve first-party files evolved by the Track Lab, modern camera and enhanced-water work are bases of the v1.0.7 source capsule. Like the existing drifted bases, their pins are now checked against the historical blob restore_capsule.py reads, which still matches.
swhitt
left a comment
There was a problem hiding this comment.
The shadow budget hooks and the boss intro skip are pretty nicely reasoned against the decomp.
The camera commit override is the one I'm most nervous about, mostly because the wall changes haven't been played yet.
| // than metres in one frame. | ||
| if (g_pending_closest > 0.0) { | ||
| const double cleared = distance_between(dkr_cam_position(rdram, camera), g_pending_pivot); | ||
| if ((cleared < g_pending_closest * 0.95 || g_pending_sliding) && |
There was a problem hiding this comment.
This puts back modern_cam's eye after the retail clearance pass already decided it was in a wall. So while sliding (or under closest) the drawn eye sits wherever modern_cam had it, which can be behind the wall, and segment_at below then picks the segment for that spot. I think that trades "inside the racer" for "inside the wall" for the length of the slide.
Since the wall changes are still unplayed, it'd be good to try a tight wall on a retail track with this before merge.
| return bits; | ||
| } | ||
| #if defined(_MSC_VER) | ||
| #pragma comment(linker, "/alternatename:dkr_analogue_driving=dkr_analogue_driving_retail") |
There was a problem hiding this comment.
/alternatename is MSVC-only, so a plain configure on clang (macOS/Linux) still won't link. Maybe also mark the pass-through __attribute__((weak)) named dkr_analogue_driving under #else?
| } | ||
| const std::int32_t value = read_word(rdram, at); | ||
| if ((value & kRaceVisitedAndCleared) != kRaceVisitedAndCleared) { | ||
| write_word(rdram, at, value | kRaceVisitedAndCleared); |
There was a problem hiding this comment.
lend_course_clear takes RACE_CLEARED back before the save, but this sets visited|cleared and never clears them. save_data.c packs those bits for every RACETYPE_BOSS level up to levelCount.
Does a custom boss level's flag end up in the EEPROM after a race?
| cache.frame.previous[i]=current; | ||
| cache.objects[i]=object; | ||
| } | ||
| // Bounded calibration trace for user-run low-pass playtests. |
There was a problem hiding this comment.
Nit: this trace is always on in release. Maybe gate it behind the water trace level like the camera ones?
| if str(o.get(scene.PROP_ID)) == level_types.WEAPON_BALLOON] | ||
| chests = [] | ||
| if bananas: | ||
| chests = [(int(o.get("playerID", 1)) - 1, o) for o in objects |
There was a problem hiding this comment.
Not masked here, but mode_checklist.owner() does (playerID - 1) & 3. A chest with playerID 0 shows up as racer 3 in the checklist, but here it's -1 and destination_tags skips it - the Bank nodes row stays MISSING and blocks export. Maybe just use mode_checklist.owner(CHEST, ...) here.
| }, | ||
| { | ||
| "function": "allocate_object_pools", | ||
| "vram": "0x8000C008", |
There was a problem hiding this comment.
Checked this against the decomp. gTrackCheckpoints is the only thing sized by MAX_CHECKPOINTS, and the slti at checkpoint_update_all is the only bound on the load, so I think these two patches cover it. 127 also keeps everything inside the s8 fields.
Worth calling out: allocate_object_pools runs once at boot, so the extra ~3 KB is taken for every level, retail included, and every allocation after it moves. Online already refuses mismatched builds, so that's probably fine. It might be good to add a line to the CHANGELOG saying this build can't play online with older ones.
A follow-up to #57, merged with main at v1.0.15. Needs both owners' sign-off on the checkpoint change below (cross-play).
Track Lab: challenge, boss and hub levels from a .dkrmap
Until now a
.dkrmapcould only be a race track. All five other level kinds now play from one. Battle, bananas, eggs, boss and hub were each checked in game with a retail-clone header and with a track built from scratch.level_loadswaps an unvisited boss level for its intro scene. It finds that scene by scanningASSET_MISC_67with no bound, so a custom level id read past the table and loaded junk (frozen camera).skip_custom_boss_introincustom_tracks_hooks.cppmarks the course as visited first. After the race the game returns to the hub the player came from.levelLinks({slot, offset, track}), lets a hub EXIT lead to another installed.dkrmap. The hook sits aftergzip_inflateintrack_spawn_objectsand patches the exit'sdestinationMapId. Hub → custom boss → hub was played through.instructionPatchesin both recomp policies (allocate_object_pools,checkpoint_update_all). The limit is 127 becausenextCheckpointis ans8. A 120-checkpoint oval races, and the AI completes laps on it.D_8011B120[32],D_8011C238[32],D_8011B330[120], which it uses as 128 entries). This crashed Paulista, an egg arena. Two hooks,dkr_shadow_clip_budgetanddkr_shadow_polygon_budget, now keep it within those sizes. The fix rests on the crash dump: the crash later stopped reproducing even on the old build.levelLinks.docs/CUSTOM_TRACKS.mddocuments the level kinds andlevelLinks. Tests are incustom_tracks_tests.cpp.Modern camera: hits and walls
modern_cam:collisionMinDist(1.6 m) keeps the eye at least that far from the pivot.collisionLiftMaxlets the boom rise over the kart instead.collisionPullSharpness) instead of jumping 100+ units in one frame.riseLag,slopeMax).LoadCameraTuningkept the ini open, so the v1 → v2 migration from Modern camera keeps up through corners and hits; vehicle choice and a preview flight for custom tracks in Track Select #57 was never saved. It is now.modern_camera_dkr_tests.cpp:WallNeverPutsTheEyeInTheRacer,WallLiftReturns,ClimbingKeepsTheCameraAbove,WallCutSlides,HitKeepsTheBoomIn.Enhanced water: caustics and low-pass spray
runtime-recomp/assets/water/WaterCaustics.png.WaterDrawParamsis already at the 2048-byte stride, so each craft's clearance lives in the last two shoreline vectors (WATER_CRAFT_AIR). Water draws never fill those vectors, and lava uses only 0..15; astatic_assertguards this.dkr-lava-glow.patch, anddkr-caustics.patch/dkr-lowpass.patchand their compose scripts are deleted. The folded patch applies with the same CMake flags and composes an RT64 tree byte-identical to the old chain.Fixes
Unadmitted custom presentation resource. This happens because auto boot skips character select, where the presentation is initialised.prepare_presentationnow initialises it on demand. It allocates nothing in the guest, so the heap order is unchanged.bakedlayer now reaches the export; before, it was display-only and lost. A moved or shared.blendfinds its base model beside itself.DKRCrashReportsis now linked intoDKRPortGame.dkr_analogue_drivinggets a retail pass-through: an MSVC/alternatenameinruntime_code_mods.cpp, used only when the overlay does not define it.DRIFTED_BASESincheck_pinned_bytes.py, so they are checked against their pinned Git blob, which still matches.Pattern checks
python scripts/check.py --suite guard,toolingpasses. The pattern guard reports 0 new findings: the low-pass layer is folded, and no newpatches/<name>/or compose scripts are added.revision_addresses.hppand the recomp policies.netplay/legacy_identity.hppdoes not exist yet, so nothing was spliced anywhere else. See Risk.Testing
DKRPortGame(build/dkr-runtime-rt64) after the v1.0.15 merge: Egg Arena and Boss Circuit boot and race.WATER_CRAFT_AIR.tools/blender/run_tests.py) pass.Risk
instructionPatcheschange the guest's object-pool layout. Online already refuses mismatched builds, but this is a simulation-affecting change, and AGENTS.md asks for both owners' approval. If you'd rather not take it now, dropping the two checkpoint entries from the recomp policies brings back 60 checkpoints. Custom tracks with more than 60 checkpoints would then need re-exporting./alternatenamepass-through fordkr_analogue_drivingis MSVC-only and only matters for configures without the online-performance overlay. Say if you'd prefer it handled differently.