ghost: capture local player transform from the body item, not the UBT hook
The self-check cube (skate3_ghost_draw_local) appeared on a nearby NPC because OnLocalSkaterTick read the world from the UpdateBoneTransforms hook's pack-owner entity, and UpdateBoneTransforms fires for NPC skaters too - the last writer won, often an NPC. Local-player capture now happens in OnFrameSceneBuilt (the same per-frame body-item heuristic the ghost models use): resolve the body item's OWNING entity via the ctx->entity store and read its world matrix (entity+416), falling back to the body root-bone translation. The UBT-based capture and hook call are removed.
This commit is contained in:
parent
d809bbc25a
commit
df86553b77
3 changed files with 32 additions and 25 deletions
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "skate3_native_ghost_net.h"
|
#include "skate3_native_ghost_net.h"
|
||||||
#include "skate3_native_ghost_relay.h"
|
#include "skate3_native_ghost_relay.h"
|
||||||
|
#include "skate3_native_entity.h"
|
||||||
#include "skate3_native_guest_read.h"
|
#include "skate3_native_guest_read.h"
|
||||||
#include "skate3_native_palette.h"
|
#include "skate3_native_palette.h"
|
||||||
|
|
||||||
|
|
@ -901,24 +902,7 @@ void GhostNetworkLoop() {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// ---- capture (sim thread; hook in skate3_native_render.cpp) ------------------
|
// ---- capture (per built frame; see OnFrameSceneBuilt) ----------------------
|
||||||
void OnLocalSkaterTick(uint8_t* base) {
|
|
||||||
if (!Enabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const uint32_t entity = skate3::native_palette::CurrentPackOwner();
|
|
||||||
if (entity == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
float world[16];
|
|
||||||
if (!ReadEntityWorld(base, entity, world)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::lock_guard<std::mutex> lock(g_mu);
|
|
||||||
std::memcpy(g_local_world, world, sizeof(g_local_world));
|
|
||||||
g_local_valid = true;
|
|
||||||
g_local_time = NowSec();
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Identity rows for a bone (column-vector affine [R|t]): the safe padding
|
// Identity rows for a bone (column-vector affine [R|t]): the safe padding
|
||||||
|
|
@ -988,6 +972,33 @@ void OnFrameSceneBuilt(uint8_t* base,
|
||||||
g_local_pose_count = rows;
|
g_local_pose_count = rows;
|
||||||
g_local_pose_valid = true;
|
g_local_pose_valid = true;
|
||||||
g_local_pose_time = NowSec();
|
g_local_pose_time = NowSec();
|
||||||
|
|
||||||
|
// Local player's world transform for the self-check cube / the wire
|
||||||
|
// pos/rot. Resolve the body item's OWNING ENTITY (ctx -> entity store)
|
||||||
|
// and read its world matrix - the reliable player signal. The previous
|
||||||
|
// UBT-hook capture could pick an NPC skater (UpdateBoneTransforms fires
|
||||||
|
// for them too), which put the cube on a nearby NPC.
|
||||||
|
float world[16];
|
||||||
|
bool have_world = false;
|
||||||
|
if (body->ctx != 0) {
|
||||||
|
skate3::native_entity::CtxInfo info;
|
||||||
|
if (skate3::native_entity::LookupCtx(body->ctx, &info) && info.entity != 0 &&
|
||||||
|
ReadEntityWorld(base, info.entity, world)) {
|
||||||
|
have_world = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!have_world) {
|
||||||
|
// Fallback: the body item's root bone translation (the player's pelvis
|
||||||
|
// world position); identity rotation is fine for a debug cube.
|
||||||
|
std::memset(world, 0, sizeof(world));
|
||||||
|
world[0] = world[5] = world[10] = world[15] = 1.0f;
|
||||||
|
world[12] = body->bones[3];
|
||||||
|
world[13] = body->bones[7];
|
||||||
|
world[14] = body->bones[11];
|
||||||
|
}
|
||||||
|
std::memcpy(g_local_world, world, sizeof(g_local_world));
|
||||||
|
g_local_valid = true;
|
||||||
|
g_local_time = NowSec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnsureNetworkThread() {
|
void EnsureNetworkThread() {
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,12 @@ struct FrameScene;
|
||||||
|
|
||||||
namespace skate3::native_ghost {
|
namespace skate3::native_ghost {
|
||||||
|
|
||||||
// ---- host-side capture (called from skate3_native_render.cpp) -------------
|
|
||||||
void OnLocalSkaterTick(uint8_t* base);
|
|
||||||
|
|
||||||
// ---- host-side pose capture (called from BuildFrameScene pre-publish) -----
|
// ---- host-side pose capture (called from BuildFrameScene pre-publish) -----
|
||||||
// Snapshot the local player's skater skeleton (the highest-bone-count
|
// Snapshot the local player's skater skeleton (the highest-bone-count
|
||||||
// skinned non-ropa character item near the camera) as the pose this client
|
// skinned non-ropa character item near the camera) as the pose this client
|
||||||
// broadcasts. Called every built frame; gated internally on the enable cvar.
|
// broadcasts, plus the local player's world transform (resolved from that
|
||||||
|
// item's owning entity). Called every built frame; gated internally on the
|
||||||
|
// enable cvar.
|
||||||
void OnFrameSceneBuilt(uint8_t* base, const skate3::native_scene::FrameScene& scene);
|
void OnFrameSceneBuilt(uint8_t* base, const skate3::native_scene::FrameScene& scene);
|
||||||
|
|
||||||
// ---- network thread lifecycle -------------------------------------------------
|
// ---- network thread lifecycle -------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -473,9 +473,6 @@ extern "C" REX_FUNC(sub_827A52C8) {
|
||||||
__imp__sub_827A52C8(ctx, base);
|
__imp__sub_827A52C8(ctx, base);
|
||||||
if (skate3::native_render::Enabled()) {
|
if (skate3::native_render::Enabled()) {
|
||||||
skate3::native_palette::OnBoneTransforms(base, parts, count);
|
skate3::native_palette::OnBoneTransforms(base, parts, count);
|
||||||
// Ghost multiplayer: capture the local player's world transform (the
|
|
||||||
// pack-owner entity at this point is the player's SkaterPresEntity).
|
|
||||||
skate3::native_ghost::OnLocalSkaterTick(base);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue