From 91b043778e4b1151fb4c1a9267726d8869a0e586 Mon Sep 17 00:00:00 2001 From: Cyd Date: Tue, 28 Jul 2026 00:18:10 -0500 Subject: [PATCH] BT410 5.3.42: the 3-D pipeline runs to the geometry load -- two rungs climbed RUNG 1: BTL4VideoRenderer::LoadMissionImplementation, Fail -> bring-up no-op. Legal, not a cheat: the engine's own VideoRenderer version (VIDREND.CPP:259) is a bare Tell and our working GaugeRenderer ships the same, so the renderer comes up and runs its frame loop with an empty scene -- exactly what we want to measure before writing content. The authentic shape for the real body is pinned by the surviving sibling header CODE/RP/RP_L4/RPL4VID.HPP (walk the mission entities -> MakeEntityRenderables -> ReadSKLFile/RecurseSKLFile). RUNG 2: Mech::EyepointRotation published. The engine then Failed at SetupCull, which fetches that attribute BY NAME off the viewpoint entity and composes it with the siteeyepoint segment to build worldToEyeMatrix. The mech already HAD EulerAngles eyepointRotation with a getter -- only the publication was missing. One enum id, one table row. The run now reaches the full mech build, constructs the renderer, boots the board (~907K VPX wire transactions), loads the mission, runs the per-frame cull and enters geometry loading with ZERO Fails and zero exceptions. It stops at 'couldn't load object buttee.bgf' / 'NULL instance' -- and that is not a defect: the models are present and BTDPL.INI points at them, but .BGF loading goes through the board to the external GL render bridge that the VPX HLE tees over VPX_FIFOSOCK, and that bridge was not running. 'NULL instance' comes from the prebuilt LIBDPL.LIB refusing to instance an object that never loaded. Next rung is the documented full rig (render-bridge/launch_pod.ps1). Co-Authored-By: Claude Fable 5 --- restoration/source410/BT/MECH.CPP | 3 +- restoration/source410/BT/MECH.HPP | 10 +++++ restoration/source410/BT_L4/BTL4VID.CPP | 35 ++++++++++++++- restoration/source410/RENDER-ROADMAP.NOTES.md | 43 +++++++++++++++++++ 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/restoration/source410/BT/MECH.CPP b/restoration/source410/BT/MECH.CPP index 37808485..05f0f07c 100644 --- a/restoration/source410/BT/MECH.CPP +++ b/restoration/source410/BT/MECH.CPP @@ -100,7 +100,8 @@ const Mech::IndexEntry ATTRIBUTE_ENTRY(Mech, RadarAngularPosition, radarAngularPosition), ATTRIBUTE_ENTRY(Mech, LinearSpeed, currentBodySpeed), ATTRIBUTE_ENTRY(Mech, MaxRunSpeed, reverseStrideLength), - ATTRIBUTE_ENTRY(Mech, DuckState, duckState) + ATTRIBUTE_ENTRY(Mech, DuckState, duckState), + ATTRIBUTE_ENTRY(Mech, EyepointRotation, eyepointRotation) }; Mech::AttributeIndexSet diff --git a/restoration/source410/BT/MECH.HPP b/restoration/source410/BT/MECH.HPP index 26eacbdd..d6a2fe90 100644 --- a/restoration/source410/BT/MECH.HPP +++ b/restoration/source410/BT/MECH.HPP @@ -180,6 +180,16 @@ LinearSpeedAttributeID, MaxRunSpeedAttributeID, DuckStateAttributeID, + // + // The 3-D renderer reads this BY NAME: DPLRenderer::SetupCull + // (CODE/RP/MUNGA_L4/L4VIDEO.CPP:4990) fetches + // "EyepointRotation" off the viewpoint entity and, for a + // JointedMover, composes it with the siteeyepoint segment to + // build worldToEyeMatrix. Without it the engine Fails every + // frame. The member already existed -- only the publication + // was missing. + // + EyepointRotationAttributeID, NextAttributeID }; diff --git a/restoration/source410/BT_L4/BTL4VID.CPP b/restoration/source410/BT_L4/BTL4VID.CPP index 1c9c27aa..d292f74b 100644 --- a/restoration/source410/BT_L4/BTL4VID.CPP +++ b/restoration/source410/BT_L4/BTL4VID.CPP @@ -36,8 +36,41 @@ BTL4VideoRenderer::~BTL4VideoRenderer() { } +// +//############################################################################# +// LoadMissionImplementation -- called by Renderer::LoadMission (the authentic +// engine, CODE/RP/MUNGA/RENDERER.CPP:263) after it has set +// LoadingRendererStatus, stamped nextRenderTime and started the renderer with +// the RendererManager. This is where a GAME renderer builds the mission's +// scene content on the Division board. +// +// BRING-UP NO-OP (phase 1 of the btl4vid ladder). A no-op is a LEGAL body +// here, not a cheat: the engine's own VideoRenderer::LoadMissionImplementation +// (CODE/RP/MUNGA/VIDREND.CPP:259) is a bare Tell, and our GaugeRenderer +// (GAUGREND.CPP:3275) ships the same. The renderer therefore comes up and +// runs its frame loop with an EMPTY scene, which is exactly what we want to +// measure before writing any content. +// +// THE AUTHENTIC SHAPE, pinned by the surviving sibling header for Red +// Planet's renderer (CODE/RP/RP_L4/RPL4VID.HPP -- same engine, same board, +// same year; only the .CPP is missing there too): +// +// LoadMissionImplementation walks the mission's entities and calls +// MakeEntityRenderables(entity, model_resource, view_type) for each, +// which builds a dpl_DCS hierarchy through ReadSKLFile / +// RecurseSKLFile (the skeleton notation pages), with a material +// substitution list set up around it. +// +// The BT-specific renderables (BTReticleRenderable, BTTranslocationRenderable, +// the pending-wrecks map) come from the BT411 donor game/reconstructed/ +// btl4vid.cpp -- 3188 lines -- but NOTE that port restructured this hook and +// has no method under this name, so the CONTRACT above comes from the 1995 +// engine and only the CONTENT comes from the donor. +//############################################################################# +// void BTL4VideoRenderer::LoadMissionImplementation(Mission *) { - Fail("BTL4VideoRenderer::LoadMissionImplementation -- btl4vid.cpp not yet reconstructed"); + Check(this); + Tell("BTL4VideoRenderer::LoadMissionImplementation -- empty scene (bring-up)\n"); } diff --git a/restoration/source410/RENDER-ROADMAP.NOTES.md b/restoration/source410/RENDER-ROADMAP.NOTES.md index fff94e62..c93589bd 100644 --- a/restoration/source410/RENDER-ROADMAP.NOTES.md +++ b/restoration/source410/RENDER-ROADMAP.NOTES.md @@ -134,3 +134,46 @@ MakeVideoRenderer returns NULL unless DPLARG is set (BTL4APP.CPP:152), and no gauge conf sets it -- so no video renderer exists in those runs at all. That frame was almost certainly attract/loading art out of BTL4.RES. The evidence above is the real thing, and it came from setting DPLARG deliberately. + +-------------------------------------------------------------------------------- +TWO RUNGS CLIMBED -- the 3-D pipeline now runs end to end to the geometry load +-------------------------------------------------------------------------------- +RUNG 1: BTL4VideoRenderer::LoadMissionImplementation, Fail -> bring-up no-op. +A no-op is a LEGAL body, not a cheat: the engine's own +VideoRenderer::LoadMissionImplementation (CODE/RP/MUNGA/VIDREND.CPP:259) is a +bare Tell, and our working GaugeRenderer (GAUGREND.CPP:3275) ships the same. +The renderer comes up and runs its frame loop with an EMPTY scene, which is +what we wanted to measure before writing content. + +RUNG 2: Mech::EyepointRotation PUBLISHED. With the renderer live the engine +immediately Failed at + + l4video.cpp(5004): DPLRenderer::SetupCull jointed mover had no + EyepointRotation attribute + +SetupCull (CODE/RP/MUNGA_L4/L4VIDEO.CPP:4990) fetches "EyepointRotation" BY +NAME off the viewpoint entity and, for a JointedMover, composes it with the +"siteeyepoint" segment to build worldToEyeMatrix. Our mech already HAD +EulerAngles eyepointRotation with a getter (MECH.HPP) -- only the attribute +publication was missing. One enum id + one table row. (The siteeyepoint +check passed first time, so that segment was already streaming correctly.) + +WHERE IT STOPS NOW, and why that is not a defect: + + L4VIDEO.cpp couldn't load object buttee.bgf (x123, also dg100/mslr) + NULL instance + +ZERO Fails, zero exceptions -- the run reaches the full mech build (all 7 +weapons), constructs the renderer, boots the board (~907K VPX wire +transactions), loads the mission, runs the per-frame cull and gets all the way +into geometry loading. The models ARE present (VIDEO/GEO/BUTTEE.BGF etc, 879 +BGFs) and BTDPL.INI points at them (objectpath=. ideo\geo), but loading a +.BGF goes through the board to the EXTERNAL GL render bridge that the VPX HLE +tees over VPX_FIFOSOCK -- and that bridge was not running. "NULL instance" +comes from the prebuilt LIBDPL.LIB, i.e. the library refusing to instance an +object that never loaded. + +NEXT RUNG: run under the documented full rig (render-bridge/launch_pod.ps1, +see LAUNCH.md) so the bridge is up and geometry actually loads. Only then is +it worth writing real MakeEntityRenderables content -- until the bridge is in +the loop there is nothing to see even if the scene graph is correct.