port 2V2-d.2: Granny runtime on libgr2, main character model animates
Implement the Granny SDK 2.11 API the client uses on top of libgr2 (platform/EterGrnLib/GrannyRuntime.cpp, shim granny.h declared from the SDK header): file sections, type-definition vertex conversion, material textures by Usage, mesh/bone bindings, deformer, control clock/loop/ease and weighted SRT sampling. Port EterGrnLib (LODController, Material, Mesh, Model, ModelInstance*, Motion, Thing, ThingInstance, Util) plus EterLib GrpObjectInstance/GrpCollisionObject/CollisionData verbatim and drop their platform skeletons and pending stand-ins; register the gr2 resource factory; vertex/index buffers are CPU-side until the 2V2-e render adapter. app.UpdateGame/RenderGame now run 40250's bodies, reaching the CPythonApplication members through their singletons (the platform has no app object); CPythonGraphic joins GameSingletons in member order, and GetMousePosition reads the host cursor. port.login_flow asserts the main instance's PART_MAIN model has meshes, its diffuse image loaded and the WAIT motion moves the bones between frames; offline and live pass, port_gate macos PASS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5266b00c6e
commit
668f48d6ec
+57
-41
@@ -98,11 +98,47 @@ bool File::Impl::extract_animation(Ref anim_obj, Ref anim_type, const Skeleton*
|
||||
out.name = rd_str(member_slot(anim_obj, *m));
|
||||
if (const TypeMember* m = find_member(ams, "Duration"))
|
||||
out.duration = rd_f32(member_slot(anim_obj, *m));
|
||||
if (const TypeMember* m = find_member(ams, "TimeStep"))
|
||||
out.time_step = rd_f32(member_slot(anim_obj, *m));
|
||||
if (const TypeMember* m = find_member(ams, "Oversampling"))
|
||||
out.oversampling = rd_f32(member_slot(anim_obj, *m));
|
||||
|
||||
const TypeMember* mTG = find_member(ams, "TrackGroups");
|
||||
if (!mTG) return true;
|
||||
auto groups = read_array_of_refs(anim_obj, *mTG);
|
||||
out.track_group_count = int32_t(groups.size());
|
||||
const auto& tgms = parse_type(mTG->ref_type);
|
||||
|
||||
// TrackGroups[0].TextTracks: RefToArray text_track{String Name; RefToArray Entries(
|
||||
// text_track_entry{Real32 TimeStamp; String Text})}(motion event 用)
|
||||
if (!groups.empty() && groups[0].valid())
|
||||
if (const TypeMember* mTx = find_member(tgms, "TextTracks")) {
|
||||
Ref xb; uint32_t xc = 0, xs = 0;
|
||||
if (read_ref_to_array(groups[0], *mTx, xb, xc, xs) && xc && xs) {
|
||||
const auto& xms = parse_type(mTx->ref_type);
|
||||
const TypeMember* mXN = find_member(xms, "Name");
|
||||
const TypeMember* mXE = find_member(xms, "Entries");
|
||||
for (uint32_t i = 0; i < xc && i < 4096; ++i) {
|
||||
Ref x = {xb.section, xb.offset + i * xs};
|
||||
TextTrack tt;
|
||||
if (mXN) tt.name = rd_str(member_slot(x, *mXN));
|
||||
Ref eb; uint32_t ec = 0, es = 0;
|
||||
if (mXE && read_ref_to_array(x, *mXE, eb, ec, es) && ec && es) {
|
||||
const auto& ems = parse_type(mXE->ref_type);
|
||||
const TypeMember* mTS = find_member(ems, "TimeStamp");
|
||||
const TypeMember* mTT = find_member(ems, "Text");
|
||||
for (uint32_t k = 0; k < ec && k < 65536; ++k) {
|
||||
Ref e = {eb.section, eb.offset + k * es};
|
||||
TextTrackEntry te;
|
||||
if (mTS) te.time_stamp = rd_f32(member_slot(e, *mTS));
|
||||
if (mTT) te.text = rd_str(member_slot(e, *mTT));
|
||||
tt.entries.push_back(std::move(te));
|
||||
}
|
||||
}
|
||||
out.text_tracks.push_back(std::move(tt));
|
||||
}
|
||||
}
|
||||
}
|
||||
const TypeMember* mTT = find_member(tgms, "TransformTracks");
|
||||
if (!mTT) return true;
|
||||
|
||||
@@ -225,49 +261,29 @@ void Curve::eval(float t, float* out) const {
|
||||
}
|
||||
}
|
||||
|
||||
// t 处每个 track 的局部 4x4(BuildCompositeTransform4x4 语义:C 上 3x3 = transpose(R3*SS3),
|
||||
// 平移在 elem 12..14)。position 缺省 (0,0,0),orientation 缺省单位四元数,scaleshear 缺省 I。
|
||||
void Animation::sample_local(float t, std::vector<Mat4>& out) const {
|
||||
out.assign(tracks.size(), Mat4{1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1});
|
||||
for (size_t ti = 0; ti < tracks.size(); ++ti) {
|
||||
const BoneTrack& tr = tracks[ti];
|
||||
float pos[3] = {0, 0, 0};
|
||||
float ori[4] = {0, 0, 0, 1};
|
||||
float ss[9] = {1,0,0, 0,1,0, 0,0,1};
|
||||
if (!tr.position.empty()) tr.position.eval(t, pos);
|
||||
if (!tr.orientation.empty()) tr.orientation.eval(t, ori);
|
||||
bool have_ss = false;
|
||||
if (!tr.scale_shear.empty()) {
|
||||
if (tr.scale_shear.dim == 9) { tr.scale_shear.eval(t, ss); have_ss = true; }
|
||||
else if (tr.scale_shear.dim == 3) {
|
||||
float s[3]; tr.scale_shear.eval(t, s);
|
||||
ss[0] = s[0]; ss[4] = s[1]; ss[8] = s[2]; have_ss = true;
|
||||
}
|
||||
// t 处的 SRT。position 缺省 (0,0,0),orientation 缺省单位四元数,scaleshear 缺省 I。
|
||||
void BoneTrack::sample(float t, Transform& out) const {
|
||||
out = Transform{};
|
||||
if (!position.empty()) { position.eval(t, out.position); out.flags |= 0x1; }
|
||||
if (!orientation.empty()) { orientation.eval(t, out.orientation); out.flags |= 0x2; }
|
||||
if (!scale_shear.empty()) {
|
||||
if (scale_shear.dim == 9) { scale_shear.eval(t, out.scale_shear); out.flags |= 0x4; }
|
||||
else if (scale_shear.dim == 3) {
|
||||
float s[3]; scale_shear.eval(t, s);
|
||||
out.scale_shear[0] = s[0]; out.scale_shear[4] = s[1]; out.scale_shear[8] = s[2];
|
||||
out.flags |= 0x4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// R3 (行主序) from quat
|
||||
const float xx = ori[0]*ori[0], yy = ori[1]*ori[1], zz = ori[2]*ori[2];
|
||||
const float xy = ori[0]*ori[1], xz = ori[0]*ori[2], yz = ori[1]*ori[2];
|
||||
const float wx = ori[3]*ori[0], wy = ori[3]*ori[1], wz = ori[3]*ori[2];
|
||||
float r3[9] = {
|
||||
1 - 2*(yy+zz), 2*(xy-wz), 2*(xz+wy),
|
||||
2*(xy+wz), 1 - 2*(xx+zz), 2*(yz-wx),
|
||||
2*(xz-wy), 2*(yz+wx), 1 - 2*(xx+yy)
|
||||
};
|
||||
float u3[9];
|
||||
if (have_ss) {
|
||||
for (int r = 0; r < 3; ++r)
|
||||
for (int c = 0; c < 3; ++c)
|
||||
u3[r*3+c] = r3[r*3+0]*ss[0*3+c] + r3[r*3+1]*ss[1*3+c] + r3[r*3+2]*ss[2*3+c];
|
||||
} else {
|
||||
for (int k = 0; k < 9; ++k) u3[k] = r3[k];
|
||||
}
|
||||
Mat4& m = out[ti];
|
||||
m = Mat4{};
|
||||
m[0] = u3[0]; m[1] = u3[3]; m[2] = u3[6]; m[3] = 0;
|
||||
m[4] = u3[1]; m[5] = u3[4]; m[6] = u3[7]; m[7] = 0;
|
||||
m[8] = u3[2]; m[9] = u3[5]; m[10] = u3[8]; m[11] = 0;
|
||||
m[12] = pos[0]; m[13] = pos[1]; m[14] = pos[2]; m[15] = 1;
|
||||
// t 处每个 track 的局部 4x4(BuildCompositeTransform4x4 语义:C 上 3x3 = transpose(R3*SS3),
|
||||
// 平移在 elem 12..14)。
|
||||
void Animation::sample_local(float t, std::vector<Mat4>& out) const {
|
||||
out.resize(tracks.size());
|
||||
Transform x;
|
||||
for (size_t ti = 0; ti < tracks.size(); ++ti) {
|
||||
tracks[ti].sample(t, x);
|
||||
out[ti] = compose(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user