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:
shenlei
2026-09-23 19:43:27 +09:00
co-authored by Claude Opus 5.5
parent 5266b00c6e
commit 668f48d6ec
74 changed files with 11404 additions and 2356 deletions
+31
View File
@@ -47,6 +47,10 @@
#include "UserInterface/StdAfx.h"
#include "UserInterface/PythonCharacterManager.h"
#include "UserInterface/InstanceBase.h"
#include "EterGrnLib/StdAfx.h"
#include "EterGrnLib/LODController.h"
#include "EterGrnLib/ModelInstance.h"
#include "EterGrnLib/Model.h"
#include "port_login_flow_server.h"
#include <memory>
@@ -315,6 +319,33 @@ int main(int argc, char** argv)
CHECK(main_instance != nullptr);
CHECK(live || (main_instance && main_instance->GetRace() == 0));
CHECK(count_syserr("TPacketGCCharacterAdditionalInfo") == 0);
// (批次 2V2-d.2) SetRace/SetShape loaded the body .gr2 through the Granny runtime into PART_MAIN, and
// game.GameWindow's OnUpdate/OnRender (app.UpdateGame/RenderGame) play the WAIT motion: the
// sampled bone matrices change between frames.
CGrannyLODController* lod = main_instance ?
main_instance->GetGraphicThingInstanceRef().GetLODControllerPointer(CRaceData::PART_MAIN) : nullptr;
CGrannyModelInstance* model_instance = lod ? lod->GetModelInstance() : nullptr;
CGrannyModel* model = model_instance ? model_instance->GetModel() : nullptr;
CHECK(model && model->GetMeshCount() > 0);
// The material's diffuse map resolved through GrannyGetMaterialTextureByType to a loaded image.
CGrannyMaterialPalette* palette = model ? const_cast<CGrannyMaterialPalette*>(&model->GetMaterialPalette()) : nullptr;
CHECK(palette && palette->GetMaterialCount() > 0 && palette->GetMaterialRef(0).GetImagePointer(0));
const int bone_count = model ? model->GetGrannyModelPointer()->Skeleton->BoneCount : 0;
CHECK(bone_count > 1);
auto bones = [&] {
std::vector<float> out;
for (int i = 0; i < bone_count; ++i)
{
const float* m = model_instance->GetBoneMatrixPointer(i);
out.insert(out.end(), m, m + 16);
}
return out;
};
if (bone_count > 1)
{
const std::vector<float> before = bones();
CHECK(pump_until(3, [&] { return bones() != before; }));
}
}
if (server)
{