Files
mtgodot-poc/libgr2/include/gr2/gr2.h
T
shenandClaude Sonnet 5 47baf6c0c6 Metin2 game client (P0–P11) + mobile asset pipeline
Networked client on the existing Godot 4.7 + libgr2 renderer:
- net: m2dev wire protocol (libsodium KX + XChaCha20), auth/select/game
  phases, EntityStore world model, ~all GC/CG headers. char create/delete,
  private shop / mall / cube, SHOP_GC_START_EX, guild, party (+ CG_PARTY_SET_STATE),
  quests, dragon soul, refine, safebox, exchange.
- UI: in-game windows migrated 1:1 from the reference uiscript/root .py —
  char status (/stat), inventory+equipment, select-item ([SELECT_ITEM] quest
  token), system-option + game-option + ESC system menu, private-shop 39-grid,
  party info board, shop tabs, atlas, minimap, quickbar, chat, …
- EterGrnLib polish: GR2 material blend/two-sided, LOD crossfade, motion-event
  dispatch, contact shadow, ray-AABB picking, weapon grip pre-transform.

Portable asset IO (A1) — all extension/libgr2/formats/mtproto reads routed
through godot::FileAccess (res:// PCK works on iOS/Android); standalone-lib
*_path() kept for the non-Godot CTests. AssetResolver + PropertyRegistry
switched to a baked index (bake_asset_index.gd) instead of std::filesystem.

Mobile builds: build-{android,ios}.sh, export-android.sh, pack-assets.sh,
gen-debug-keystore.sh. Assets ship as a zip mounted at runtime by
project/asset_pack.gd (adb push now; HTTP download is a drop-in later).

ctest 10/10, 34 GDScript suites, macOS/iOS/Android all build.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013EJxkHiNKS4kybHS3XKyAJ
2026-08-31 20:02:12 +09:00

98 lines
4.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// libgr2 —— gr2 v6 只读读取器(demo 子集)。
// 施工文档:docs/steps/M0-gr2-reader.md
#pragma once
#include "types.h"
#include <cstdint>
#include <cstddef>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <vector>
namespace gr2 {
enum class Magic { Unknown, Bit32_LE, Bit32_BE, Bit64_LE, Bit64_BE };
struct LoadError {
std::string stage; // "header" / "section" / "typetree" / "fileinfo" / ...
std::string message;
};
// T3:通用类型树遍历(gr2dump 打印成员用;不硬编码 struct 布局)
struct MemberDump { std::string name; int member_type; int array_width; };
class File;
std::vector<MemberDump> dump_root_members(const File&);
// 材质视图(类型树遍历 root.Materials → granny_material)。
// 只读文件名,不解码贴图。diffuse_texture 取 Usage 含 "diffuse" 的 map 的
// 贴图,退而取第一张;all_textures 是从该材质可达的全部 FromFileName。
struct MaterialInfo {
std::string name;
std::string diffuse_texture;
std::vector<std::string> all_textures;
// Metin2/EterGrnLib 渲染态推断(gr2 不带 D3DRS,靠名字 + map 数 + ExtendedData):
int map_count = 0; // granny_material.MapCount(可达子材质的 Texture 数)
bool alpha_blend = false;// Name 以 "Blend" 开头且 map_count>1EterGrnLib Material.cpp:233
bool two_sided = false; // 名字含 2side/twoside/double/leaf/tree/ivy/fenceExtendedData "Two-sided" 的近似)
};
std::vector<MaterialInfo> dump_materials(const File&);
// 已解析、fixup 后的文件。析构释放所有 section 缓冲。
class File {
public:
static std::optional<File> load(const uint8_t* bytes, size_t len, LoadError* err);
static std::optional<File> load_path(const std::string& path, LoadError* err);
// T1
Magic magic() const { return magic_; }
uint32_t format_version()const { return version_; } // 期望 6
uint32_t total_size() const { return total_size_; }
const std::vector<SectionInfo>& sections() const { return sections_; }
// T4 —— 完整汇总(内部会按需触发 T5/T6/T7a)
const FileInfo& file_info() const { return info_; }
// 展开后的 section 字节(T2 产出)。空 section 返回空 span。调试 / 校验用。
std::span<const uint8_t> section_bytes(size_t i) const;
// T5 —— bind pose 自洽检查(不依赖 oracle)。返回 max|world_bind·invBind I|
// 阈值策略见 M0-gr2-reader.md T5noise_floor 前用 1e-3
double bind_pose_self_check(int skeleton = 0) const;
// M2 便捷封装(同文件内的 skeleton + animation)。跨文件(model gr2 + anim gr2
// 用自由函数 gr2::sample_pose(skeleton, animation, t, ...)。anim/skeleton 是 file_info() 索引。
bool sample_pose(int anim, float t, int skeleton,
std::vector<Mat4>& world_pose, std::vector<Mat4>& skin) const;
bool bind_pose(int skeleton, std::vector<Mat4>& world_pose, std::vector<Mat4>& skin) const;
private:
File() = default;
Magic magic_ = Magic::Unknown;
uint32_t version_ = 0;
uint32_t total_size_ = 0;
std::vector<SectionInfo> sections_;
FileInfo info_;
// 内部:section 缓冲、root object 指针、类型树 —— 放 .cpp 的 Impl
struct Impl;
std::shared_ptr<Impl> impl_;
friend std::vector<MemberDump> dump_root_members(const File&);
friend std::vector<MaterialInfo> dump_materials(const File&);
};
// ── M2:姿势采样(model 的 skeleton + anim 的 tracks 可来自不同 gr2)──────
// Granny 语义、行主序、平移在 elem 12..14
// world[i] = Composite(local[i]) · (parent<0 ? sk.initial_placement : world[parent])
// skin[i] = sk.bones[i].inverse_world · world[i]
// tracks 按骨骼名匹配 sk;无匹配的骨骼用 sk 的 bind LocalTransform。
void sample_pose(const Skeleton& sk, const Animation& an, float t,
std::vector<Mat4>& world, std::vector<Mat4>& skin);
void bind_pose(const Skeleton& sk, std::vector<Mat4>& world, std::vector<Mat4>& skin);
// 打印辅助
const char* magic_name(Magic);
const char* compression_name(uint32_t section_format); // 0=none 1=Oodle0 2=Oodle1
} // namespace gr2