BT410 5.3.69: the cockpit eye sits in the canopy -- site handling in the walk

RecurseSKLFile now walks "site=" children.  Sites get no draw component --
which is why the walk's 26 nodes matched the real capture all along: sites
were never DCS nodes -- and siteeyepoint spawns the cockpit camera with the
donor's exact construction (bt411 btl4vid.cpp:462, decomp FUN_004579a8):
offset = the site's own local rest transform, parent = the site's PARENT
joint's DCS, not the hull root.  World orientation, torso twist and gait all
arrive through chain composition.  The root-DCS eye remains only as the
zero-construction fallback for skeletons with no siteeyepoint.

Measured on the live pod with the RIO streaming: [eye] cockpit eye on
'jointeye', 26 nodes / 19 objects / 1 eye, and the bridge camera sits at
Y = hull + 7.7 -- the exact chain sum jointlocal 5.29 + hip 0.37 + torso 0.31
+ jointeye 1.69.  canopy_eye1.png is the pilot's view, the mech's own nose
wedge visible below the sightline.  Own-mech cage handling is the next layer.

Also: eye_count threading through ReadSKLFile/RecurseSKLFile, and the [skl]
summary now reports it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-29 15:18:27 -05:00
co-authored by Claude Fable 5
parent 8231bee58c
commit d4f105f42d
8 changed files with 2159 additions and 150 deletions
+120 -11
View File
@@ -125,10 +125,12 @@ dpl_DCS *
Entity *entity,
dpl_DCS *parent_dcs,
const char *skeleton_filename,
ViewFrom view_type)
ViewFrom view_type,
int *eye_count)
{
Check(this);
Check_Pointer(skeleton_filename);
Check_Pointer(eye_count);
char
path[256];
@@ -167,11 +169,12 @@ dpl_DCS *
dpl_DCS
*root = RecurseSKLFile(
entity, parent_dcs, skeleton, "ROOT", 0, view_type,
zone, &node_count, &object_count);
zone, &node_count, &object_count, eye_count);
DEBUG_STREAM << "[skl] " << path
<< " -> " << node_count << " nodes, "
<< object_count << " objects\n" << flush;
<< object_count << " objects, "
<< *eye_count << " eye\n" << flush;
//
// The engine guards this the same way (L4VIDEO.CPP, DPLReadEnvironment):
@@ -199,7 +202,8 @@ dpl_DCS *
ViewFrom view_type,
dpl_ZONE *zone,
int *node_count,
int *object_count)
int *object_count,
int *eye_count)
{
Check(this);
Check(skeleton);
@@ -348,7 +352,7 @@ dpl_DCS *
{
RecurseSKLFile(
entity, dcs, skeleton, child_page, recursion_depth + 1,
view_type, zone, node_count, object_count);
view_type, zone, node_count, object_count, eye_count);
}
}
@@ -356,6 +360,100 @@ dpl_DCS *
delete children;
}
//
// "site=" children. Sites get NO draw component of their own -- the
// real pod's capture decodes exactly 26 DCS bodies for this skeleton,
// which is joints+root only, and this walk matched that count precisely
// BECAUSE it ignored sites. One site spawns hardware instead of
// geometry: siteeyepoint is the COCKPIT CAMERA.
//
// The authentic construction comes from the donor's decompile
// (bt411 btl4vid.cpp:462, decomp FUN_004579a8): the eye's offset matrix
// is the SITE'S OWN local rest transform, and the eye is parented on the
// site's PARENT segment's draw component -- this page's DCS -- NOT the
// hull root. World orientation and all live motion (torso twist, gait)
// then come from the parent-chain composition for free. For MAD.SKL the
// chain is jointtorso -> jointeye (trany +1.687, tranz -1.318) ->
// siteeyepoint (identity), which puts the eye in the canopy where the
// cockpit sits, instead of at the hull origin staring through the torso
// panels.
//
if (view_type == insideEntity)
{
NameList
*sites = skeleton->MakeEntryList(page_name, "site");
if (sites != NULL)
{
Register_Object(sites);
NameList::Entry
*site_entry;
for (site_entry = sites->GetFirstEntry();
site_entry != NULL;
site_entry = site_entry->GetNextEntry())
{
const char
*site_page = (const char *)site_entry->dataReference;
if (
site_page != NULL &&
strcmp(site_page, "siteeyepoint") == 0 &&
skeleton->PageExists(site_page)
)
{
Scalar
site_tx = 0.0f, site_ty = 0.0f, site_tz = 0.0f,
site_pitch = 0.0f, site_yaw = 0.0f, site_roll = 0.0f;
skeleton->GetEntry(site_page, "tranx", &site_tx);
skeleton->GetEntry(site_page, "trany", &site_ty);
skeleton->GetEntry(site_page, "tranz", &site_tz);
skeleton->GetEntry(site_page, "pitch", &site_pitch);
skeleton->GetEntry(site_page, "yaw", &site_yaw);
skeleton->GetEntry(site_page, "roll", &site_roll);
//
// Default ctor identities; the EulerAngles assignment
// writes ONLY the 3x3 rotation (AFFNMTRX.CPP:179), so
// the identity's zero translation survives it and the
// site translation goes in after.
//
LinearMatrix
site_offset;
site_offset =
EulerAngles(site_pitch, site_yaw, site_roll);
site_offset(3,0) = site_tx;
site_offset(3,1) = site_ty;
site_offset(3,2) = site_tz;
EulerAngles
*eyepoint_rotation =
(EulerAngles *)entity->GetAttributePointer(
"EyepointRotation");
DPLEyeRenderable
*cockpit_eye = new DPLEyeRenderable(
entity,
dplMainZone,
site_offset,
dcs,
dplMainView,
eyepoint_rotation);
Register_Object(cockpit_eye);
++(*eye_count);
if (getenv("BT_MER_LOG"))
{
DEBUG_STREAM << " [eye] cockpit eye on '"
<< page_name << "' offset=("
<< site_tx << "," << site_ty << ","
<< site_tz << ")\n" << flush;
}
}
}
Unregister_Object(sites);
delete sites;
}
}
return dcs;
}
@@ -506,17 +604,28 @@ void
//
// 2. The skeleton hangs UNDER the root DCS -- so the whole
// model rides the entity's localToWorld instead of
// being parked at the world origin.
// being parked at the world origin. The walk also
// builds the COCKPIT EYE when it meets the
// siteeyepoint site (donor construction: offset =
// the site's local rest, parent = the site's parent
// joint DCS -- so the eye sits in the canopy and
// rides torso twist through chain composition).
//
int
eye_count = 0;
ReadSKLFile(entity, root_DCS,
video_object->GetObjectFilename(), view_type);
video_object->GetObjectFilename(), view_type,
&eye_count);
//
// 3. Inside view: the eyepoint, driven off the same root
// DCS plus the mech's published EyepointRotation --
// this is what the camera follows.
// 3. FALLBACK eyepoint only. A skeleton without a
// siteeyepoint still needs a camera for the inside
// view, and the hull root with a zero offset is the
// engine's own zero-construction default
// (L4VIDEO.CPP:4849). For MAD.SKL this no longer
// runs -- the walk builds the real cockpit eye.
//
if (view_type == insideEntity)
if (view_type == insideEntity && eye_count == 0)
{
EulerAngles
*eyepoint_rotation =
+4 -2
View File
@@ -78,7 +78,8 @@
Entity *entity,
dpl_DCS *parent_dcs,
const char *skeleton_filename,
ViewFrom view_type);
ViewFrom view_type,
int *eye_count);
dpl_DCS *
RecurseSKLFile(
@@ -90,7 +91,8 @@
ViewFrom view_type,
dpl_ZONE *zone,
int *node_count,
int *object_count);
int *object_count,
int *eye_count);
protected:
int reserved[16];
@@ -1447,3 +1447,27 @@ same streamed table our code hands to archive code, name what will not
resolve) turned a two-day intermittent heap-corruption hunt into a one-run
lookup. The streamed RES tables are BINDING CONTRACTS on our attribute
enums; every subsystem the RES maps should get the same audit treatment.
--------------------------------------------------------------------------------
THE COCKPIT EYE SITS IN THE CANOPY -- SITE HANDLING IN THE SKELETON WALK
--------------------------------------------------------------------------------
The eye no longer rides the hull root. RecurseSKLFile now walks "site="
children (sites get NO draw component -- which is WHY the walk's 26 nodes
matched the real capture: sites were never DCS nodes), and siteeyepoint
spawns the cockpit camera with the donor's exact construction (bt411
btl4vid.cpp:462, decomp FUN_004579a8):
offset = the SITE's own local rest transform (siteeyepoint = identity)
parent = the site's PARENT joint's DCS (jointeye, NOT the root)
so world orientation, torso twist and gait all arrive through chain
composition, with no per-mech forward-axis assumption. The old root-DCS eye
remains only as the zero-construction fallback for a skeleton with no
siteeyepoint (the engine's own default, L4VIDEO.CPP:4849).
MEASURED on the live pod (RIO streaming, post-5.3.68): [eye] cockpit eye on
'jointeye', [skl] 26 nodes / 19 objects / 1 eye, and the bridge camera sits at
Y = hull + 7.7 -- the exact chain sum jointlocal 5.29 + hip 0.37 + torso 0.31
+ jointeye 1.69 = 7.66. canopy_eye1.png: the pilot's view, with the MadCat's
own nose wedge visible below the sightline -- own-mech cage handling
(COCKPIT-CAGE-NOTES.md punch cutouts) is the known next layer.