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 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-28 00:18:10 -05:00
co-authored by Claude Fable 5
parent 1b204de6d3
commit 91b043778e
4 changed files with 89 additions and 2 deletions
+2 -1
View File
@@ -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
+10
View File
@@ -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
};
+34 -1
View File
@@ -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");
}
@@ -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.