Cockpit view live: V toggle, camera gate, real EyepointRotation, inside skeleton

- V toggles the authentic cockpit eyepoint <-> the chase camera.  Both eyes
  coexist; DPLEyeRenderable::Execute now writes the VIEW only when it IS
  mCamera (unconditional writes let the last-executed eye stomp the toggle).
- EyepointRotation is a real zeroed EulerAngles member on Mech (was bound to
  the shared junk attrPad -- the eye composes this attribute into the view
  EVERY frame, so the cockpit camera was rotated by garbage: the canted
  horizon, then the black screen).  The eye-slew systems write it later.
- The cockpit eye mounts at the eyepoint's REST position with a clean upright
  forward basis on the tree root (the live joint chain fed it the site tilt +
  torso pose; authentic pitch/yaw is the deferred gyro eye chain).
- The inside view swaps the player to the INSIDE skeleton mesh set
  (SkeletonType_A): 19 body segments hide, exactly one mesh remains --
  blx_cop.bgf, the authentic cockpit canopy shell around the eyepoint.  It
  currently renders as a black enclosure (the black-screen report), so it is
  HIDDEN pending its interior/punch-material rendering (BT_INSIDE_COCKPIT=1
  shows it for that work).  Damage gstates respected in both directions;
  RemakeEntity keys off the DISPLAYED skeleton.
- Dev: BT_START_INSIDE=1 starts in the cockpit view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-08 19:23:07 -05:00
co-authored by Claude Fable 5
parent c2f70f6348
commit 56f02b2176
6 changed files with 186 additions and 11 deletions
+140 -8
View File
@@ -302,6 +302,7 @@ HierarchicalDrawComponent*
MechRenderTree &render_tree = mMechRenderTrees[entity];
render_tree = MechRenderTree();
render_tree.skeletonType = (int)skeletonType;
render_tree.viewSkeleton = (int)skeletonType;
render_tree.rootRenderable = this_root;
render_tree.wrecked = 0;
if (getenv("BT_DEATH_LOG"))
@@ -363,17 +364,37 @@ HierarchicalDrawComponent*
//
if (segment->IsSiteSegment() /* [0x10] */ != 0)
{
if (type == insideEntity &&
// The authentic COCKPIT EYEPOINT. Built for the true inside view
// AND for the player's chase build (buildDebugChaseCamera), so the
// V-key toggle can switch to it -- the pod's only view was this
// eyepoint; the chase camera is the port's usability addition.
if ((type == insideEntity || buildDebugChaseCamera) &&
strcmp((const char *)segment->GetName() /* [0x11c] */, "siteeyepoint") == 0) // @0051d290
{
EulerAngles *eye_rot =
(EulerAngles *)entity->GetAttributePointer("EyepointRotation"); // @0051d29d
//
// DPLEyeRenderable installs itself as the renderer's camera
// (mCamera) so the main view tracks the cockpit eyepoint.
// Mount the eye at the eyepoint's REST position but with a clean
// UPRIGHT forward basis, parented on the tree ROOT: riding the
// live joint chain fed the camera the site's authored tilt + the
// torso pose (a permanently canted horizon). The authentic pitch
// /yaw came from the gyro-driven eye-joint chain (deferred); until
// that lands, the cockpit view = eyepoint position + mech yaw.
//
mCamera = new DPLEyeRenderable(
entity, offset_matrix, parent_DCS, eye_rot);
AffineMatrix restToEntity = segment->GetSegmentToEntity();
Point3D eyePos;
eyePos = restToEntity; // rest translation (W row)
LinearMatrix eyeBasis(True);
eyeBasis(0,0) = -1.0f; eyeBasis(0,1) = 0.0f; eyeBasis(0,2) = 0.0f; // X row
eyeBasis(1,0) = 0.0f; eyeBasis(1,1) = 1.0f; eyeBasis(1,2) = 0.0f; // Y row (up)
eyeBasis(2,0) = 0.0f; eyeBasis(2,1) = 0.0f; eyeBasis(2,2) = -1.0f; // Z row (look = mech forward)
eyeBasis(3,0) = (Scalar)eyePos.x;
eyeBasis(3,1) = (Scalar)eyePos.y;
eyeBasis(3,2) = (Scalar)eyePos.z;
mEyeCockpit = new DPLEyeRenderable(
entity, eyeBasis, this_root, eye_rot);
if (type == insideEntity) // true inside build: it IS the camera
mCamera = mEyeCockpit;
dbg_eye = 1;
}
continue;
@@ -504,6 +525,11 @@ HierarchicalDrawComponent*
SwapToWreck(entity);
}
// DEV: BT_START_INSIDE=1 begins in the cockpit view (also exercises the
// inside-skeleton swap headlessly).
if (buildDebugChaseCamera && getenv("BT_START_INSIDE"))
SetViewInside(1);
//
// TODO(bring-up): inside-view targeting reticle (BTReticleRenderable +
// AddWeapon pips) and the per-subsystem weapon/effect renderables (PPC/
@@ -572,10 +598,12 @@ HierarchicalDrawComponent*
debugOffset(2,0) = zx; debugOffset(2,1) = zy; debugOffset(2,2) = zz; // Z row (look)
debugOffset(3,0) = camPx; debugOffset(3,1) = camPy; debugOffset(3,2) = camPz; // W row (pos)
mCamera = new DPLEyeRenderable(entity, debugOffset, this_root, NULL);
mEyeChase = new DPLEyeRenderable(entity, debugOffset, this_root, NULL);
mCamera = mEyeChase; // default view: chase (V toggles cockpit)
DEBUG_STREAM << "[BTrender] external debug chase camera installed at ("
<< camPx << "," << camPy << "," << camPz << ") looking at ("
<< tgtX << "," << tgtY << "," << tgtZ << ")\n" << std::flush;
<< tgtX << "," << tgtY << "," << tgtZ << ") -- V toggles the cockpit eyepoint"
<< (mEyeCockpit ? "" : " (COCKPIT EYE MISSING)") << "\n" << std::flush;
}
return this_root;
@@ -614,7 +642,7 @@ void
JointedMover *jointed_mover = (JointedMover *)entity;
EntitySegment::SkeletonType skeletonType =
(EntitySegment::SkeletonType)render_tree.skeletonType;
(EntitySegment::SkeletonType)render_tree.viewSkeleton; // the DISPLAYED set
EntitySegment::SegmentTableIterator segment_iterator(jointed_mover->segmentTable);
EntitySegment *segment;
@@ -1233,6 +1261,110 @@ BTL4VideoRenderer::BTL4VideoRenderer(
DPLRenderer(::GetActiveWindow(), 800, 600, false, interest_type, depth_calibration)
{
Check_Pointer(this);
mEyeCockpit = 0;
mEyeChase = 0;
}
//
// The V-key view toggle: switch the live camera between the authentic cockpit
// eyepoint and the port's external chase camera. A missing eye (e.g. the
// cockpit eye on a mech with no siteeyepoint) leaves the current view.
//
void
BTL4VideoRenderer::SetViewInside(int inside)
{
if (inside && mEyeCockpit != 0)
mCamera = mEyeCockpit;
else if (!inside && mEyeChase != 0)
mCamera = mEyeChase;
//
// Swap the player's DISPLAYED skeleton with the view: the INSIDE view uses
// the inside-skeleton mesh set (SkeletonType_A -- most body segments have
// no inside mesh, so the pilot isn't wrapped in his own torso textures;
// the authentic pod view worked exactly this way), the chase view restores
// the full outside set. Damage graphic states are respected per segment.
//
Entity *viewpoint = (application != 0) ? application->GetViewpointEntity() : 0;
std::map<Entity*, MechRenderTree>::iterator tree_it =
mMechRenderTrees.find(viewpoint);
if (tree_it != mMechRenderTrees.end() && !tree_it->second.wrecked)
{
MechRenderTree &render_tree = tree_it->second;
render_tree.viewSkeleton = inside
? (int)EntitySegment::SkeletonType_A
: render_tree.skeletonType;
JointedMover *jm = (JointedMover *)viewpoint;
EntitySegment::SegmentTableIterator it(jm->segmentTable);
EntitySegment *segment;
int shown = 0, hidden = 0;
while ((segment = it.ReadAndNext()) != NULL)
{
if (segment->IsSiteSegment() != 0)
continue;
int slot = segment->GetIndex();
std::map<int, HierarchicalDrawComponent*>::iterator r =
render_tree.segRenderable.find(slot);
if (r == render_tree.segRenderable.end() || r->second == NULL)
continue;
int gstate = 0;
std::map<int, int>::iterator g = render_tree.segGState.find(slot);
if (g != render_tree.segGState.end())
gstate = g->second;
CString *nm = segment->GetVideoObjectName(
(EntitySegment::SkeletonType)render_tree.viewSkeleton,
(Enumeration)gstate);
// The cockpit interior shell (blx_cop -- the canopy frame around the
// eyepoint) currently renders as a black enclosure; keep it hidden
// until its interior rendering is sorted (BT_INSIDE_COCKPIT=1 shows
// it for that investigation).
if (inside && nm != NULL && strstr((const char *)*nm, "_cop") != NULL
&& !getenv("BT_INSIDE_COCKPIT"))
nm = NULL;
d3d_OBJECT *obj = NULL;
if (nm != NULL)
{
char filename[44];
strcpy(filename, (const char *)*nm);
int len = (int)strlen(filename);
if (len >= 4)
filename[len - 4] = '\0';
strcat(filename, ".bgf");
obj = d3d_OBJECT::LoadObject(GetDevice(), filename);
}
r->second->SetDrawObj(obj);
if (obj)
{
++shown;
DEBUG_STREAM << "[view] shown: seg '"
<< (const char *)segment->GetName() << "' mesh '"
<< (nm ? (const char *)*nm : "?") << "'\n" << std::flush;
}
else ++hidden;
}
DEBUG_STREAM << "[view] skeleton "
<< (inside ? "A (inside)" : "N (outside)") << ": "
<< shown << " segment mesh(es) shown, " << hidden
<< " hidden\n" << std::flush;
}
DEBUG_STREAM << "[view] " << (inside ? "COCKPIT eyepoint" : "external chase")
<< (mCamera == mEyeCockpit ? " (cockpit live)" : " (chase live)")
<< "\n" << std::flush;
}
//
// Sim-side bridge (the mech4 keyboard poll drives it).
//
void BTSetViewInside(int inside)
{
if (application == NULL)
return;
BTL4VideoRenderer *renderer =
(BTL4VideoRenderer *)application->GetVideoRenderer();
if (renderer != NULL)
renderer->SetViewInside(inside);
}
BTL4VideoRenderer::~BTL4VideoRenderer()