//===========================================================================// // File: btl4vid.hpp // // Project: BattleTech Brick: Video Renderer Manager // // Contents: BTL4VideoRenderer -- the BattleTech L4 out-the-window 3D WORLD // // renderer manager. Builds the per-entity renderable tree for the // // main view each time an interesting entity becomes visible: // // the player mech + its weapons/effects/reticle, terrain, and // // other movers, walking the jointed-mover segment table and the // // subsystem roster and submitting renderables to the scene. // //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 02/13/95 CPB Initial coding. // //---------------------------------------------------------------------------// // Copyright (C) 1994-1996, Virtual World Entertainment, Inc. // // PROPRIETARY and CONFIDENTIAL // //===========================================================================// // // RECONSTRUCTED from the shipped binary (BTL4OPT.EXE). NO header survived. // Class/member/method names are taken from the embedded assert path // "d:\tesla\bt\bt_l4\BTL4VID.CPP", the embedded class name string // "BTL4VideoRenderer::Material name ..." (@0051d6f8), and the direct // Red Planet analogue RP_L4/RPL4VID.cpp (RPL4VideoRenderer) plus the surviving // MUNGA headers MUNGA_L4/L4VIDEO.HPP and MUNGA_L4/L4VIDRND.HPP. Each method // cites its originating @ADDR. // // PORTING NOTES (reconstruction -> WinTesla engine) // * BTL4VideoRenderer is the BattleTech analogue of RPL4VideoRenderer; both // subclass the concrete L4 manager DPLRenderer (L4VIDEO.HPP). Deriving // from DPLRenderer supplies SetFogStyle/AddDynamicRenderable, the fog // colour/plane fields, dplMainView/dplMainZone/dplDeathZone and the // vehicleReticle slot the recovered code touches. // * The 1996 BattleTech build drove a pre-DPL renderable hierarchy whose // joint renderables parent on a dpl_DCS* (rather than the WinTesla // HierarchicalDrawComponent* parent). That hierarchy did NOT survive in // source, so the BT-specific renderables it constructs are declared here // (BTRootRenderable / BTHingeRenderable / ... ) with the signatures the // recovered code actually calls. They are distinct from the modern // L4VIDRND.HPP renderables of the same role (which is why the targeting // reticle is BTReticleRenderable, NOT the engine's ReticleRenderable -- // the BT object is 0x358 bytes and carries up to 10 AddWeapon pip markers). // #if !defined(BTL4VID_HPP) # define BTL4VID_HPP // // Forward declaration so MUNGA_L4/l4vidrnd.h (which references DPLRenderer in // HierarchicalDrawComponent before l4video.h defines it) parses cleanly // regardless of include order. // class DPLRenderer; #if !defined(L4VIDEO_HPP) # include // DPLRenderer / VideoRenderer / INTERSECT_ALL #endif #if !defined(L4VIDRND_HPP) # include // VideoComponent / the *Renderable hierarchy #endif #if !defined(RETICLE_HPP) # include #endif #if !defined(STATE_HPP) # include // StateIndicator #endif #if !defined(JMOVER_HPP) # include // JointedMover #endif #if !defined(JOINT_HPP) # include // Joint / JointSubsystem / Hinge #endif #if !defined(SEGMENT_HPP) # include // EntitySegment #endif #if !defined(SUBSYSTM_HPP) # include // Subsystem #endif #if !defined(NAMELIST_HPP) # include // NameList / NameList::Entry #endif #include // per-mech render-tree bookkeeping (RemakeEntity) class Entity; class Mission; class ResourceDescription; class NotationFile; // // BT "scene" handle. In the 1996 build the world-view renderables were // built against an opaque scene root (the analogue of RP's dpl_ZONE pair); // it is used pointer-only in this module, so a forward declaration suffices. // class Scene; // //===========================================================================// // Legacy 2D / material-callback DPL entry points //===========================================================================// // // The dpl2d_ display-list layer and the material-name-substitution callback // hook were part of libDPL (the IG-board driver) and were NOT carried into the // WinTesla engine. The recovered reticle builder and material setup call them // directly; declare them here so the translation unit compiles (resolved at // link time against the libDPL shim). // dpl2d_DISPLAY* dpl2d_NewDisplayList(); void dpl2d_Begin(dpl2d_DISPLAY *list, int mode); void dpl2d_SetColor(dpl2d_DISPLAY *list, Scalar red, Scalar green, Scalar blue); void dpl2d_Circle(dpl2d_DISPLAY *list, Scalar x, Scalar y, Scalar radius, int fill); void dpl2d_PushMatrix(dpl2d_DISPLAY *list); void dpl2d_PopMatrix(dpl2d_DISPLAY *list); void dpl2d_MoveTo(dpl2d_DISPLAY *list, Scalar x, Scalar y); void dpl2d_End(dpl2d_DISPLAY *list); void dpl2d_Compile(dpl2d_DISPLAY *list); // // The FULL recovered API (phase-02: every recorder named by its debug string; // opcode map in phases/phase-02-dpl2d-reticle.md). The Push/Move/Pop trio // above is a legacy alias of OpenPolypoint/AddPoint/ClosePolypoint. // void dpl2d_OpenPolypoint(dpl2d_DISPLAY *list); // opcode 2 void dpl2d_ClosePolypoint(dpl2d_DISPLAY *list); // opcode 3 void dpl2d_OpenPolyline(dpl2d_DISPLAY *list); // opcode 4 (closed loop) void dpl2d_ClosePolyline(dpl2d_DISPLAY *list); // opcode 5 void dpl2d_OpenLines(dpl2d_DISPLAY *list); // opcode 6 (open strip) void dpl2d_CloseLines(dpl2d_DISPLAY *list); // opcode 7 void dpl2d_AddPoint(dpl2d_DISPLAY *list, Scalar x, Scalar y); // opcode 8 void dpl2d_SetLineWidth(dpl2d_DISPLAY *list, Scalar width); // opcode 0x15 void dpl2d_PushState(dpl2d_DISPLAY *list); // opcode 0x12 void dpl2d_PopState(dpl2d_DISPLAY *list); // opcode 0x13 void dpl2d_SetMatrix(dpl2d_DISPLAY *list, const Scalar *six); // opcode 0x10 (2x3) void dpl2d_ConcatMatrix(dpl2d_DISPLAY *list, const Scalar *six); // opcode 0x11 void dpl2d_CallList(dpl2d_DISPLAY *list, dpl2d_DISPLAY *callee); // nested glyphs void dpl2d_FullScreenClip(dpl2d_DISPLAY *list); void dpl_SetMaterialNameCallback(char *(*callback)(char *source)); // //===========================================================================// // BattleTech renderable hierarchy (pre-DPL) //===========================================================================// // // Common base: every BT world renderable carries the dpl_DCS / dpl_INSTANCE it // creates and offers the graphical (Add) / control (Connect) hook-up the world // container uses. Modelled on VideoComponent so a BTRootRenderable can be // returned as the VideoComponent* mech root. // class BTRenderableBase: public VideoComponent { public: BTRenderableBase( Entity *entity, VideoExecutionType execution_type = DynamicVideoExecutionType) : VideoComponent(entity, execution_type), myDCS(0), myInstance(0) {} dpl_DCS* GetDCS() { return myDCS; } dpl_INSTANCE* GetInstance() { return myInstance; } // // Graphical / control hook-up. These hide VideoComponent::Add/Connect // (which take VideoComponent*) so the world container can accept any // renderable in the BT hierarchy by its common HierarchicalDrawComponent // base. // void Add(HierarchicalDrawComponent *child) { if (child) addChild(child); } void Connect(HierarchicalDrawComponent *child) { if (child) addChild(child); } protected: dpl_DCS *myDCS; dpl_INSTANCE *myInstance; }; // // World container renderable (FUN_00455de4, alloc 0x60). Holds the death / // world DCS roots for one entity's tree. // class BTWorldContainerRenderable: public BTRenderableBase { public: BTWorldContainerRenderable( Entity *entity, int execution_type, int in_death_zone, Scene *scene); }; // // Root DCS object renderable (FUN_00453578, alloc 0x64). // class BTRootRenderable: public BTRenderableBase { public: BTRootRenderable( Entity *entity, int execution_type, d3d_OBJECT *graphical_object, Scene *scene, int intersect_mode, uint32 intersect_mask); }; // // Static / jointed child DCS renderables. // class BTDCSObjectRenderable: public BTRenderableBase { public: BTDCSObjectRenderable( // FUN_0045848c, alloc 0x50 Entity *entity, Scene *scene, d3d_OBJECT *graphical_object, int execution_type, uint32 intersect_mask, LinearMatrix *offset_matrix, dpl_DCS *parent_DCS); }; class BTHingeRenderable: public BTRenderableBase { public: BTHingeRenderable( // FUN_004537e8, alloc 0x78 Entity *entity, int execution_type, d3d_OBJECT *graphical_object, Scene *scene, int exec2, uint32 intersect_mask, dpl_DCS *parent_DCS, LinearMatrix *offset_matrix, const Hinge *my_hinge); }; class BTBallJointRenderable: public BTRenderableBase { public: BTBallJointRenderable( // FUN_004539b4, alloc 0x7c Entity *entity, int execution_type, d3d_OBJECT *graphical_object, Scene *scene, int exec2, uint32 intersect_mask, dpl_DCS *parent_DCS, LinearMatrix *offset_matrix, const EulerAngles *my_euler); }; class BTBallTranslateJointRenderable: public BTRenderableBase { public: BTBallTranslateJointRenderable( // FUN_00453ac4, alloc 0x8c Entity *entity, int execution_type, d3d_OBJECT *graphical_object, Scene *scene, int exec2, uint32 intersect_mask, dpl_DCS *parent_DCS, LinearMatrix *offset_matrix, const EulerAngles *my_euler, const Point3D *my_translation); }; // // Inside-view eyepoint (POV/eye) renderable (FUN_004579a8, alloc 0x5c). // class BTEyeRenderable: public BTRenderableBase { public: BTEyeRenderable( Entity *entity, Scene *scene, LinearMatrix *offset_matrix, dpl_DCS *parent_DCS, dpl_VIEW *this_view, EulerAngles *eyepoint_rotation); }; // // Death / explosion effect renderable (FUN_00453f18, alloc 0x48). // class BTDeathEffectRenderable: public BTRenderableBase { public: BTDeathEffectRenderable( Entity *entity, int execution_type, dpl_VIEW *this_view, Scene *scene, dpl_DCS *parent_DCS, StateIndicator *death_state, int death_control_state); }; // // Marker (timestamp/beacon) watcher renderable (FUN_00458c58, alloc 0x120). // class BTMarkerWatcherRenderable: public BTRenderableBase { public: BTMarkerWatcherRenderable( Entity *entity, int execution_type, dpl_VIEW *this_view, dpl_DCS *parent_DCS); }; // // Drop-zone translocation effect renderable (FUN_00458d2c, alloc 0x40). // // THE "BLUE WARP" (task #52): the engine analog is POVTranslocateRenderable // (L4VIDRND.cpp:1749), which loads tsphere.bgf and runs a collapse-on-death / // expand-on-respawn sphere keyed on the entity's SimulationState dial vs a // control state. This was a no-op stub; now reconstructed. The sphere // COLLAPSES (scale 100->1 over 1.3s) when the watched player enters the // control state (DropZoneAcquired), holds, then EXPANDS (1->150 over 1.0s) // when it leaves (VehicleTranslocated) -- revealing the reborn mech. Rotates // throughout. Drawn (direct-draw, like the weapon beams) by // BTDrawTranslocationSpheres from the render loop. // class BTTranslocationRenderable: public BTRenderableBase { public: BTTranslocationRenderable( Entity *entity, int execution_type, dpl_VIEW *this_view, StateIndicator *effect_trigger, Point3D *drop_zone, int effect_control_state); ~BTTranslocationRenderable(); // Sphere state machine (public so the file-static render walk in // btl4vid.cpp can step + draw each active effect). Mirrors // POVTranslocateRenderable's IdleState/InitialCollapse/WaitForReincarnate/ // ExpandReveal. enum { TLoc_Idle = 0, TLoc_Collapse, TLoc_Wait, TLoc_Expand }; Entity *myWatchedEntity; // the player this sphere belongs to (dedupe key) StateIndicator *myTrigger; // the SimulationState dial we watch Point3D *myDropZone; // world position to render the sphere at unsigned myControlState; // state value that starts the collapse int mySphereState; // TLoc_* float myTimer; // seconds elapsed in the current timed phase float myRotateY; // accumulated spin bool mySphereVisible; }; // // Player POV mission start/end fade renderable (FUN_00454394, alloc 0x50). // (BT-local twin of the engine's POVStartEndRenderable; the BT ctor takes the // main-view handle plus the world/death zones explicitly.) // class BTPOVStartEndRenderable: public BTRenderableBase { public: BTPOVStartEndRenderable( Entity *entity, int execution_type, dpl_VIEW *this_view, dpl_ZONE *main_zone, dpl_ZONE *death_zone, StateIndicator *effect_trigger, float red_fog, float green_fog, float blue_fog, float near_fog, float far_fog, int start_mission_state, int end_mission_state); }; // // Coolant / reservoir / tracer reservoir effect renderable (FUN_00456a68, // alloc 0x138). // class BTTracerEffectRenderable: public BTRenderableBase { public: BTTracerEffectRenderable( Entity *entity, int execution_type, void *state_attr, int mode, dpl_ZONE *zone, dpl_DCS *parent_DCS, LinearMatrix *offset_matrix); }; // // Emitter beam renderable (FUN_004593c0, alloc 0x80). // class BTEmitterBeamRenderable: public BTRenderableBase { public: BTEmitterBeamRenderable( Entity *entity, int execution_type, d3d_OBJECT *graphical_object, Scene *scene, int exec2, uint32 intersect_mask, dpl_DCS *parent_DCS, LinearMatrix *offset_matrix, void *sim_state, int flag, void *beam_scale, void *beam_orient); }; // // PPC beam renderable (FUN_004590d8, alloc 0x88). // class BTPPCBeamRenderable: public BTRenderableBase { public: BTPPCBeamRenderable( Entity *entity, int execution_type, d3d_OBJECT *graphical_object, Scene *scene, int exec2, uint32 intersect_mask, dpl_DCS *parent_DCS, LinearMatrix *offset_matrix, void *sim_state, int flag, void *beam_scale, void *beam_orient, float beam_width); }; // // Searchlight LightOn -> spotlight connector (FUN_0045612c, alloc 0x2c). // class BTLightConnection: public BTRenderableBase { public: BTLightConnection( Entity *entity, int execution_type, dpl_INSTANCE *spot_instance, int flag, int *light_on); }; // //############################################################################# // BTReticleRenderable (BattleTech targeting reticle) //############################################################################# // // File-private to btl4vid.cpp. Parallels the engine ReticleRenderable // (L4VIDRND.HPP) but the BT object is 0x358 bytes and carries up to 10 weapon // pip / range markers built by AddWeapon. Constructed @004cc40c for the inside // view; weapon markers are appended by MakeMechRenderables via AddWeapon. // // header layout actually touched (this+...): // +0x38 weaponCount (cap 10 -- "Tried to display too many weapons") // ... parallel per-weapon arrays (see members below) // reticle geometry: minRange@0x230, maxRange@0x22c, rangeScale@0x234, // originX@0x1fc, originY@0x200, scaleY@0x204, biasX@0x208 // class BTReticleRenderable: public VideoRenderable { public: // // PORT ctor. The binary ctor (@004cc40c, 15 args) received the mech's // TargetReticle attribute + 8 HUD-subsystem attributes; its CALIBRATION // however is its own hardcoded constants (0.35/0.25/0.5/0.016/0.008 -- // the pip-row origin/scale and the two tick-ladder frames), which this // port ctor reproduces directly. The one live input the glyphs consume // is the target RANGE (drives the range-scale caret); it binds to a // Scalar the targeting step updates. The 3D marker chain + the // PNAME1-8.bgf pip meshes (the floating 3D target designator) are a // separate deferred piece -- see phases/phase-02-dpl2d-reticle.md. // BTReticleRenderable( // @004cc40c (port signature) Entity *entity, Scalar *range_attr); ~BTReticleRenderable(); // // Per-frame draw (the binary Execute @004cdcf0 is in an UN-EXPORTED // gap; the dynamics here are [T3]: draw the master reticle, then each // weapon's lit pip when its within-range flag is up, else the dark // ring). Called from the render loop, cockpit view only. // void Draw(struct IDirect3DDevice9 *device); int WeaponCount() const { return weaponCount; } // // Append a weapon range/pip marker to the reticle. @004cdac0 // Parameter order/count is the binary's. The RECOVERED Execute // (@004cdcf0, task #37) fixed the two state attrs' semantics: // param_9 = weapon attr 0x1c, the FIRE-CYCLE state: == const2 (2) // -> LOADED (lit pip A); == const3 (3) -> charging (dark // ring B). Port source: rechargeLevel (>= 1.0 == loaded). // param_12 = weapon attr 1, the DAMAGE state: == const1 (1) // -> Destroyed (the pip is HIDDEN entirely). // weapon_mode is a GROUP BIT (Front=1/Rear=2/Left=4/Right=8) tested // against the Reticle element mask's low nibble. // void AddWeapon( Scalar weapon_range, // param_2 int pip_position, // param_3 int *within_range_value, // param_4 TargetWithinRange int extended_range, // param_5 PipExtendedRange Scalar pip_red, // param_6..8 PipColor Scalar pip_green, Scalar pip_blue, const int *cycle_ready, // param_9 attr 0x1C WeaponState (== 2 loaded) int const2, // param_10 (2 = loaded) int const3, // param_11 (3 = charging) int *sim_state_value, // param_12 weapon attr 1 int const1, // param_13 (1 = destroyed) int weapon_mode); // param_14 (group bit) protected: // // Per-weapon parallel arrays (cap 10). Slot mapping is the verified // store order of AddWeapon @004cdac0 (part_014.c:4827-4837) [T1]; // semantics RECOVERED from the disassembled Execute @004cdcf0 (task // #37 [T1]): the caches are the change-detect state driving the pip // list rebuild; the constants are the fire-cycle/damage enum values. // int weaponCount; // +0x38 int weaponMode[10]; // +0x3c group bit (F/R/L/R) int stateConst3[10]; // +0x64 3 = charging (dark) int stateConst2[10]; // +0x8c 2 = loaded (lit) int stateConst1[10]; // +0xb4 1 = destroyed (hidden) int alarmCache[10]; // +0xdc port: LOADED flag cache int simStateCache[10]; // +0x104 port: DESTROYED flag cache const int *cycleReady[10]; // +0x130 attr 0x1C WeaponState (== stateConst2 => loaded) int *simStateAttr[10]; // +0x158 damage state (attr 1) int *withinRangePtr[10]; // +0x18c param_4 (stored; Execute int withinRangeCache[10]; // +0x1b4 never reads it -- T1) dpl2d_DISPLAY *pipDisplayListB[10]; // +0x288 dpl2d_DISPLAY *pipDisplayListA[10]; // +0x2b0 // // Calibrated reticle geometry (the ctor's authentic constants). // Scalar originX; // +0x1fc [0x7f] = 0.35 Scalar originY; // +0x200 [0x80] = 0.25 Scalar scaleY; // +0x204 [0x81] = 0.5 Scalar biasX; // +0x208 [0x82] = 0.016 Scalar maxRange; // +0x22c [0x8b] = 1200 Scalar minRange; // +0x230 [0x8c] = 0 Scalar rangeScale; // +0x234 [0x8f] // // The reticle's own display lists (the ctor's [0x97..0xa1, 0xb6..0xba] // set, as transcribed) + the live range binding. // Scalar *rangeAttr2; // [0x89] the target-range source dpl2d_DISPLAY *masterList; // [0x98] the composed reticle dpl2d_DISPLAY *simpleXList; // [0x99] the minimal cross (PrimaryHudOn off) dpl2d_DISPLAY *aimDotList; // [0x9a] centre aim dot dpl2d_DISPLAY *rangeCaretR; // [0x9b] right-ladder caret dpl2d_DISPLAY *rangeCaretB; // [0x9c] bottom-ladder carets dpl2d_DISPLAY *headingList; // [0x9d] heading sub-list dpl2d_DISPLAY *bottomAnchor; // [0x9e] bottom-ladder anchor dpl2d_DISPLAY *leftArrow; // [0x9f] left range arrow dpl2d_DISPLAY *rightArrow; // [0xa0] right range arrow dpl2d_DISPLAY *crossList; // [0xa1] the dotted cross dpl2d_DISPLAY *subB6; // [0xb6] the composed weapon pips dpl2d_DISPLAY *subB7; // [0xb7] the lock-ring slot dpl2d_DISPLAY *subB8; // [0xb8] plain ring (simple-X mode) dpl2d_DISPLAY *subB9; // [0xb9] ring + cross (the LOCK ring) dpl2d_DISPLAY *subBA; // [0xba] the threat-direction trail // Recovered-Execute dynamic state (task #37) int pipsBuilt; // subB6 composed at least once int lockShown; // subB7 holds the ring (this[0x1dc]) float lockSpinDeg; // the ring spin angle (this[0x240]) }; // // The live reticle draw hook (called from the engine render loop after the // 3D scene; draws only in the cockpit view). // extern void BTDrawReticle(struct IDirect3DDevice9 *device); // //############################################################################# // BTL4VideoRenderer //############################################################################# // // @ vtable: BattleTech L4 video renderer manager subclass. Base is the MUNGA // L4 renderer manager (DPLRenderer, l4video.hpp). Parallels RPL4VideoRenderer. // class BTL4VideoRenderer: public DPLRenderer { public: BTL4VideoRenderer( RendererRate calibration_rate, RendererComplexity calibration_complexity, RendererPriority calibration_priority, InterestType interest_type, InterestDepth depth_calibration ); ~BTL4VideoRenderer(); Logical TestInstance() const; // // The per-zone EFFECT DISPATCHER (@004d097c, coverage-audit discovery // 2026-07-13) -- the missing target of the engine's whole authentic // StartEntityEffect chain (MechDeathHandler class-5 -> RendererManager // -> Renderer::StartEntityEffectMessageHandler [T0, applies the // ExplosionResourceTable graphic-state remap] -> THIS virtual). // Resolves the zone's SEGMENT socket, walks the effect resource's // VideoModel (type 10), and starts each >=1000 effect ATTACHED to the // segment (the smoke follows the walking mech); <1000 warns // (byte-faithful to the binary's gate). StopAll kills every effect // tagged to the entity (the respawn cleanup, @004d0c14 analog). // virtual void StartEntityEffectImplementation( Entity *entity, DamageZone *damage_zone, ResourceDescription::ResourceID resource_ID); // @004d097c virtual void StopAllEntityEffectsImplementation(Entity *entity); // @004d0c14 // // Material substitution (mirrors RPL4VideoRenderer). // void SetupMaterialSubstitutionList(Entity *entity); // @004d0cc0 void TearDownMaterialSubstitutionList(); // @004d11e8 // // RemakeEntity (damage-model swap). The 1996 binary drives a per-entity // render state machine (Make / RemakeEntity / DestroyEntity -- the state // name strings survive at .rdata 0x4e3f20). Only "Make" was ported (the // tree is built ONCE at entity creation, with fixed d3d_OBJECTs). // RemakeEntity is the model REFRESH: when a damage zone's graphic state // changes (a segment becomes Destroyed/Gone), re-pick each segment's video- // object variant by its zone graphic state (EntitySegment::GetVideoObjectName // is keyed by {skeleton, graphic_state}) and swap it onto the already-built // joint renderable IN PLACE (HierarchicalDrawComponent::Execute re-reads // graphicalObject each frame). No teardown: the component dtor does not // cascade to children (L4VIDRND.cpp:104) so a rebuild would leak -- the // authentic behaviour is an in-place mesh swap. // void RemakeEntityRenderables(Entity *entity); // The HEAL direction of RemakeEntity: on respawn (Mech::Reset) drop the // wreck hulk/debris + restore every body segment to its now-intact mesh // (the wreck swap, SwapToWreck, is one-way; this reverses it). void RebuildMechRenderables(Entity *entity); // Select/load each body segment's displayed mesh for the given view // (inside=SkeletonType_A + '_cop' suppression / outside=full skeleton), // honoring live damage graphic state. Shared by SetViewInside (V-toggle) // and RebuildMechRenderables (respawn) so the cockpit view stays consistent // across death/respawn. Returns the count of shown meshes. int ApplyViewSkeleton(Entity *viewpoint, int inside); protected: // // Per-mech render-tree bookkeeping so RemakeEntityRenderables can find each // segment's joint renderable (the dcs_array in MakeMechRenderables is local // and freed). Keyed by Entity*; one entry per built mech. segRenderable // maps a segment SLOT (EntitySegment::GetIndex) to its draw component; // segGState is the graphic state last applied to that slot so a swap only // reloads geometry on an actual state change. // struct MechRenderTree { int skeletonType; // EntitySegment::SkeletonType used at build int viewSkeleton; // the skeleton currently DISPLAYED (view toggle) HierarchicalDrawComponent *rootRenderable;// the tree root (wreck hulk parent) int wrecked; // 1 = swapped to the dbr hulk DPLStaticChildRenderable *wreckHulk; // the dbr piece (sinks) DPLStaticChildRenderable *wreckDebris; // the ldbr scatter (sinks) DPLStaticChildRenderable *wreckFlames; // flamebig card (billboards, sinks slower) float wreckAge; // seconds since the swap int wreckRevealed; // 1 once the 0.25s reveal delay elapsed d3d_OBJECT *wreckHulkObj; // pieces held hidden until the reveal d3d_OBJECT *wreckDebrisObj; d3d_OBJECT *wreckFlamesObj; std::map segRenderable; // slot -> joint renderable std::map segGState; // slot -> last applied graphic state }; std::map mMechRenderTrees; public: // // The wreck's quadratic SINK (the 1996 script's burial: FUN_00456410 // computes offsetY = rate * t^2; the hulk's authored rate is -0.025 -> // the ~7-unit hulk is fully buried ~17s after the kill). Ticked per // frame from the dead mech's UpdateDeathState. Returns 0 once the // wreck is fully buried (the caller stops the wreck-smoke re-arm), // 1 while anything is still visible. // int TickWreck(Entity *victim, float dt); // // VIEW TOGGLE: the player's mech builds BOTH cameras -- the authentic // cockpit eyepoint (DPLEyeRenderable at 'siteeyepoint', the pod's only // view) and the port's external chase camera (a usability addition for // the windowed build). SetViewInside switches the renderer's live // camera between them (V key via the BTSetViewInside bridge). // void SetViewInside(int inside); protected: DPLEyeRenderable *mEyeCockpit; // the authentic cockpit eyepoint DPLEyeRenderable *mEyeChase; // the external chase camera int mViewInside; // the pilot's chosen view (V/state); // survives renderable REBUILDS (the // chase-eye build used to stomp // mCamera back to chase every remake) public: // // The death-wreck swap (ExplosionScripts effect 104, reconstructed): the // victim's whole visual becomes its burning hulk -- hide every segment // mesh and hang "dbr.bgf" (gendbr.bgf fallback) on the tree root. // The 1996 script hardcoded thrdbr.bgf (a dev shortcut; every mech ships // its own *DBR hulk). If the victim's tree isn't built yet, the swap is // remembered and applied when MakeMechRenderables builds it. // void SwapToWreck(Entity *victim); protected: // // Renderer-manager overrides // void LoadMissionImplementation(Mission *mission); void MakeEntityRenderables( // @004d0774 Entity *this_entity, ResourceDescription *model_resource, ViewFrom type); HierarchicalDrawComponent* MakeMechRenderables( // @004cef28 Entity *entity, ResourceDescription *model_resource, ViewFrom type); // //-------------------------------------------------------------------- // BT renderer helpers (recovered). //-------------------------------------------------------------------- // Scene* GetScene(); // FUN_0045a724 dpl_VIEW* GetMainView() { return dplMainView; } // this[0x124] dpl_ZONE* GetMainZone() { return dplMainZone; } // this[300] d3d_OBJECT* LoadObject(const char *object_name); // FUN_00498448 void AttachToEyeDCS(dpl_DCS *root_dcs, LinearMatrix &local_to_world); // FUN_00489cec void AddDynamicRenderable(VideoComponent *renderable, Entity *entity); // FUN_0045a994 protected: // // vtvCount / vtvsExpected analogues (RPL4VideoRenderer). // int mechsExpected, mechCount; // // True once the linked (player) entity exists, so its root DCS is // hooked onto the eye. ([0xb0] in the recovered layout.) // Logical linkedEntityPresent; // // Effect zones touched by the recovered code. // dpl_ZONE *scene_zone, // [0x318] coolant / reservoir effects *tracer_zone; // [0x2e4] projectile tracers }; // // Sim-side bridge to RemakeEntityRenderables. MechDeathHandler (sim TU) calls // this when a mech's damage graphic state changes; defined in btl4vid.cpp, it // reaches the live renderer via l4_application->GetVideoRenderer(). A free // function so the sim TU needs no renderer header (just this extern). // extern void BTRemakeMechModel(Entity *entity); // // Render bridge for the death-wreck swap (effect 104): called by the engine's // ExplosionClassID dispatch with the explosion's entityHit (the dead mech). // extern void BTSwapMechToWreck(Entity *victim); // // Per-frame wreck sink tick (sim -> render bridge; called by the dead mech's // UpdateDeathState with the frame dt). Returns 0 once the wreck is fully // buried -- the caller stops re-arming the wreck smoke. // extern int BTWreckSinkTick(Entity *victim, float dt); // // View-mode bridge (the V key): 1 = the authentic cockpit eyepoint, // 0 = the external chase camera. // extern void BTSetViewInside(int inside); #endif // BTL4VID_HPP //===========================================================================//