M0' scaffold: Godot 4.7 + GDExtension + libgr2 wired up (macOS)
- extension/: godot-cpp submodule (master, API 4.7), Metin2Model node registered, links xrender-poc/libgr2 via sibling-dir reference. - project/: Godot 4.7 project — orbit camera + DirectionalLight3D (shadows) + WorldEnvironment/procedural sky + placeholder cube; main.gd probes the extension and can run libgr2 on a real .gr2 (MTGODOT_PROBE_GR2). - build.sh one-shot build into project/bin/. - docs/GODOT-POC-PLAN.md (phased: Phase 1 macOS -> M0'/M1/M2/M2.5). Verified on this machine: [mtgodot] Metin2Model registered OK — libgr2 linked probe_gr2(warrior_cheongrin.gr2) -> gr2 OK: format_version=6 sections=8 Metal Forward+ renders the scene (test/golden/m0-scaffold.png). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WaHYEY9rwLWt21PULiYjeJ
This commit is contained in:
+16
@@ -0,0 +1,16 @@
|
||||
# build
|
||||
/build/
|
||||
/cmake-build-*/
|
||||
compile_commands.json
|
||||
|
||||
# godot
|
||||
/project/.godot/
|
||||
/project/bin/*.dylib
|
||||
/project/bin/*.dSYM/
|
||||
/project/bin/*.so
|
||||
/project/bin/*.dll
|
||||
/project/bin/*.wasm
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.tmp
|
||||
@@ -0,0 +1,4 @@
|
||||
[submodule "extension/godot-cpp"]
|
||||
path = extension/godot-cpp
|
||||
url = https://github.com/godotengine/godot-cpp.git
|
||||
branch = master
|
||||
@@ -0,0 +1,21 @@
|
||||
# mtgodot-poc — top level
|
||||
# Phase 1 (macOS) scaffolding. See docs/GODOT-POC-PLAN.md §M0'.
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(mtgodot_poc CXX C)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# Phase 1 is macOS-only; fail early anywhere else so nobody wastes time.
|
||||
if(NOT APPLE)
|
||||
message(FATAL_ERROR "mtgodot-poc Phase 1 targets macOS only (see docs/GODOT-POC-PLAN.md §00).")
|
||||
endif()
|
||||
|
||||
add_subdirectory(extension)
|
||||
@@ -0,0 +1,111 @@
|
||||
# mtgodot-poc
|
||||
|
||||
「**Godot 4 + 自研资源 loader**」跨平台方案的渲染 Demo(route ①)。
|
||||
用一个 C++ GDExtension 复用 [`xrender-poc`](../xrender-poc) 的 `libgr2`,把 Metin2 的
|
||||
`.gr2` 骨骼资源渲染进 Godot,用 Godot 内置渲染器。
|
||||
|
||||
与 `xrender-poc`(自研引擎 + bgfx RHI)是**两条并行的跨平台基座候选**,共用 `libgr2`。
|
||||
|
||||
判定分两阶段:
|
||||
|
||||
| 阶段 | 平台 | 里程碑 | 产出 |
|
||||
|---|---|---|---|
|
||||
| **Phase 1**(当前) | macOS | M0' · M1 · M2 · M2.5 | 中期评审(不下 go/no-go) |
|
||||
| Phase 2 | iOS · Android | M3 | go / no-go |
|
||||
|
||||
完整计划见 [`docs/GODOT-POC-PLAN.md`](docs/GODOT-POC-PLAN.md)。
|
||||
|
||||
---
|
||||
|
||||
## 现状
|
||||
|
||||
- [~] **M0'** — 脚手架。GDExtension 骨架(`Metin2Model` 空节点)+ `libgr2` 静态库集成 + Godot 4.7 工程(orbit 相机 + 方向光阴影 + sky + 占位 cube)。macOS only。
|
||||
|
||||
---
|
||||
|
||||
## 环境
|
||||
|
||||
| 组件 | 版本 / 位置 | 说明 |
|
||||
|---|---|---|
|
||||
| Godot | **4.7.1**(`/opt/homebrew/bin/godot`,Homebrew cask) | 编辑器 + headless |
|
||||
| godot-cpp | submodule `extension/godot-cpp` @ `master`(pin `101ae38`) | master 默认 targets Godot 4.7 API(`GODOTCPP_DEFAULT_API_VERSION=4.7`);无 `4.6/4.7` 分支,只有 bundled `extension_api-4-7.json` |
|
||||
| Xcode | 26.4.1 | macOS 构建 |
|
||||
| CMake | 已装;**不需要 SCons** | godot-cpp 走 CMake 路径 |
|
||||
| libgr2 | `../xrender-poc/libgr2`(sibling-dir 引用) | 见下「libgr2 依赖」 |
|
||||
|
||||
**导出模板**(M0' T0.4 / 真正 export 才需要):编辑器内 `Editor → Manage Export Templates → Download`,或 `godot --headless --install-export-templates`。~600MB,版本须与编辑器一致(4.7.1)。
|
||||
|
||||
---
|
||||
|
||||
## 构建
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive # 拉 godot-cpp(首次)
|
||||
./build.sh # Debug;产物 → project/bin/libmtgodot.macos.template_debug.dylib
|
||||
./build.sh Release # template_release
|
||||
```
|
||||
|
||||
首次会编译 godot-cpp(几分钟)。
|
||||
|
||||
## 运行
|
||||
|
||||
```bash
|
||||
# 编辑器打开
|
||||
godot -e --path project
|
||||
|
||||
# headless 冒烟测试(验证 GDExtension 加载 + 类注册)
|
||||
godot --headless --path project --quit-after 3
|
||||
|
||||
# 跑一个真实 gr2 过 libgr2(可选)
|
||||
MTGODOT_PROBE_GR2="$PWD/../m2dev-client-main/assets/PC/ymir work/pc/warrior/warrior_cheongrin.gr2" \
|
||||
godot --headless --path project --quit-after 3
|
||||
```
|
||||
|
||||
控制台出现 `[mtgodot] Metin2Model registered OK — libgr2 linked; ...` = M0' 的 T0.1/T0.2 通过。
|
||||
|
||||
窗口内:拖拽 = 轨道,滚轮 = 缩放,`F2` = 截图(→ `user://`)。
|
||||
|
||||
---
|
||||
|
||||
## libgr2 依赖
|
||||
|
||||
M0' 直接用 **sibling-directory 引用**:`extension/CMakeLists.txt` 里
|
||||
`XRENDER_POC_DIR` 默认 `../../xrender-poc`,`add_subdirectory` 进 `libgr2`。
|
||||
两个 repo 都在 `.../mt/` 下时开箱即用。
|
||||
|
||||
换机 / CI 时二选一:
|
||||
- `git submodule add <xrender-poc url> third_party/xrender-poc` 后传 `-DXRENDER_POC_DIR=third_party/xrender-poc`
|
||||
- vendor 一份 `libgr2/` 进本仓库
|
||||
|
||||
`formats/`(msa/msm)到 **M2** 才需要,届时同样方式接入。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
```
|
||||
mtgodot-poc/
|
||||
CMakeLists.txt 顶层(macOS only 守卫)
|
||||
build.sh 便捷构建
|
||||
extension/
|
||||
CMakeLists.txt mtgodot SHARED + godot-cpp + libgr2
|
||||
godot-cpp/ submodule @ master
|
||||
src/
|
||||
register_types.{h,cpp} GDExtension 入口,注册 Metin2Model
|
||||
metin2_model.{h,cpp} M0' 占位节点(证明类注册 + libgr2 链接)
|
||||
project/ Godot 4.7 工程
|
||||
project.godot
|
||||
main.tscn 根 Node3D + main.gd
|
||||
main.gd M0' harness:建 env/light/cam/cube + 探测扩展
|
||||
assets/.gdignore 让 Godot 不 import .gr2/.dds
|
||||
bin/mtgodot.gdextension 扩展描述符(dylib 构建产物落这里)
|
||||
export_presets.cfg macOS preset
|
||||
docs/
|
||||
GODOT-POC-PLAN.md 开发计划(M0'–M4 + 风险 + 验收)
|
||||
steps/ (按需)
|
||||
test/golden/ 对拍截图(M1 起)
|
||||
```
|
||||
|
||||
## 授权
|
||||
|
||||
`libgr2` 全自研,不含 Granny SDK 代码。本仓库内部研究用途,不对外公开。
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the mtgodot GDExtension into project/bin/. macOS only (Phase 1).
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
CONFIG="${1:-Debug}" # Debug | Release
|
||||
JOBS="${JOBS:-$(sysctl -n hw.ncpu)}"
|
||||
|
||||
if [ ! -f extension/godot-cpp/CMakeLists.txt ]; then
|
||||
echo "godot-cpp submodule missing — run: git submodule update --init --recursive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE="$CONFIG"
|
||||
cmake --build build --config "$CONFIG" -j "$JOBS"
|
||||
|
||||
echo
|
||||
echo "Built libs in project/bin/:"
|
||||
ls -la project/bin/*.dylib 2>/dev/null || echo " (none — check build output above)"
|
||||
@@ -0,0 +1,298 @@
|
||||
# Godot 渲染 Demo — 开发计划
|
||||
|
||||
> 内部研究方案,不对外公开。
|
||||
> 与 [`PLAN.md`](./PLAN.md) 是**两条并行的跨平台基座候选**:本文件评估「Godot 4 + 自研资源 loader」,`PLAN.md` 评估「自研引擎 + bgfx RHI」。
|
||||
> 复用 [`../libgr2`](../libgr2)、[`../formats`](../formats) 与 [`../oracle`](../oracle) 的真值数据。
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| 状态 | PoC 规划 |
|
||||
| 技术栈 | Godot **4.7.1** stable · GDExtension(`godot-cpp` `4.7` 分支)· C++20 |
|
||||
| 目标平台 | **Phase 1:macOS**(当前)→ Phase 2:iOS · Android |
|
||||
| 判定周期 | Phase 1 ≈ 3.5–5 周(单人)→ **中期评审**(不下结论);Phase 2 后 → **go / no-go** |
|
||||
| 复用 | `libgr2`(gr2 v6 读取器)· `formats/`(textscript · msa · msm)· `oracle/` golden · bgfx demo 截图 |
|
||||
| 自研 | `Metin2Model` GDExtension 节点(gr2 → `Skeleton3D` + `ArrayMesh` + 材质) |
|
||||
|
||||
---
|
||||
|
||||
## 00 · 阶段划分
|
||||
|
||||
| 阶段 | 平台 | 里程碑 | 回答什么 | 产出 |
|
||||
|---|---|---|---|---|
|
||||
| **Phase 1** | macOS | M0' · M1 · M2 · **M2.5** | ① gr2 → Godot 场景对象能否正确映射;② 动画运行时(采样 / retarget / 事件)能否移植;③ **Metin2 观感能否在 Godot 里还原** | 中期评审报告:桥接层工作量实测 + 观感对比 + 剩余风险 |
|
||||
| **Phase 2** | iOS · Android | M3(·M4 可选) | ④ 跨平台三端一致;⑤ 移动端性能 / 生命周期 | **go / no-go 结论** |
|
||||
|
||||
**关键澄清(写给评审)**:Godot 在 macOS 上渲染一个蒙皮 gr2 **几乎不可能失败**。Phase 1 不是用来判断"该不该押 Godot"的——那个只有 Phase 2 能答。Phase 1 的价值是把**平台无关**的 GDExtension 工程(占本方案工程量的大头)做完,并确认视觉保真度可达,从而让 Phase 2 只剩"移动端 + 性能"这一个真未知。
|
||||
|
||||
**进入 Phase 2 的前置**:M2.5 门禁通过,且中期评审判定"桥接层工作量与观感均可接受"。否则在此转回 bgfx route,损失仅为 GDExtension 桥接层(≈ glTF importer 量级),`libgr2` 完整保留。
|
||||
|
||||
---
|
||||
|
||||
## 01 · 目标与判定标准
|
||||
|
||||
### Phase 1 要证明的三件事(macOS)
|
||||
|
||||
1. **gr2 → Godot 场景对象** — `libgr2` 读出的骨架 / 蒙皮网格 / 权重能否正确映射到 `Skeleton3D` + `ArrayMesh` + `Skin`,覆盖多种骨架(不止 warrior)。
|
||||
2. **动画正确** — 每帧从 `libgr2::sample_pose` 驱动 `Skeleton3D`,结果与 bgfx demo / Granny oracle 逐帧一致;跨文件 retarget、motion event 成立。
|
||||
3. **观感可还原** — 多 texture-stage 材质、alpha test、顶点色、additive 混合、fog、方向光阴影在 Godot 里做出来,与原客户端目视接近。
|
||||
|
||||
### Phase 2 要证明的两件事(移动端,本轮不做)
|
||||
|
||||
4. **跨平台一致** — 同一 GDExtension + Godot 工程在 iOS 真机 + Android 真机(Vulkan + Compatibility)渲染与桌面一致。
|
||||
5. **性能 / 生命周期达标** — 中端机单角色 / 多角色帧率达标,后台 / 恢复稳,Godot 节点开销可控。
|
||||
|
||||
### 明确排除(两个阶段都不做)
|
||||
|
||||
SpeedTree 植被、地形(Metin2 户外地图)、UI / Python 脚本层、eterpack(Phase 1 用散文件)、GPU 蒙皮优化、LOD 策略、多角色 AI、网络、玩法。
|
||||
|
||||
> 地形与 `.mse` 特效在 Phase 1 也不做,但 **M2.5 会做通用 `ShaderMaterial` + 混合模式的地基**,为后续正式移植时的地形 / 特效渲染铺路。
|
||||
|
||||
### 判定口径
|
||||
|
||||
- **M2.5 门禁通过 + 中期评审"可接受"** = 进入 Phase 2。
|
||||
- **门禁失败区分两类**(同 `PLAN.md` §01 口径):
|
||||
- **工程性延期** — 材质差色、某曲线子类型没实现、某 API 用错。有明确修法,记下继续。
|
||||
- **方案性死路** — 例如 Godot 节点 / 骨骼模型无法表达 Metin2 的多部件挂点体系、或多 stage 固定管线在 `ShaderMaterial` 里无法近似到可接受、或每帧写骨骼在合理规模下 Mac 上就已经卡。这才触发"转回 bgfx route"。
|
||||
- **最终交付(Phase 2 末)** 一页纸对比结论:与 bgfx route 在 **工作量 / 性能 / 视觉保真度 / 长期维护成本** 四轴上的取舍。
|
||||
|
||||
### 与 bgfx route 的关系
|
||||
|
||||
| | bgfx route(`PLAN.md`) | Godot route(本文件) |
|
||||
|---|---|---|
|
||||
| 渲染器 | 自研(bgfx 薄封装 + `.sc` 着色器) | Godot 内置渲染器 |
|
||||
| 平台壳 / 输入 / 音频 / 打包 | 自己拼(SDL3 / 薄壳 + 各库) | Godot 全包 + 编辑器 |
|
||||
| gr2 解析 | `libgr2` | `libgr2`(同一份) |
|
||||
| 动画运行时 | 自研(复用 EterGrnLib 逻辑) | 映射到 `Skeleton3D` / `AnimationMixer` |
|
||||
| 阴影 / 环境 / sky | 自研 | Godot 内置(M2.5 直接用) |
|
||||
| 长期 | 自己养一个引擎 | 跟随 Godot 版本(GDExtension ABI 绑 minor) |
|
||||
|
||||
**`libgr2` 在两条路里都是核心,完整复用。**
|
||||
|
||||
---
|
||||
|
||||
## 02 · 架构总览
|
||||
|
||||
```
|
||||
mtgodot-poc/
|
||||
extension/ GDExtension(C++,godot-cpp 4.7)
|
||||
src/
|
||||
register_types.cpp 注册 Metin2Model / Metin2AnimPlayer
|
||||
metin2_model.{h,cpp} load_gr2 → Skeleton3D + ArrayMesh + Skin + material
|
||||
metin2_anim.{h,cpp} 每帧 libgr2::sample_pose → Skeleton3D bone pose;跨文件 retarget;motion event
|
||||
gr2_bridge.{h,cpp} libgr2 POD 视图 → Godot 数组打包;basis + 单位换算(集中一处)
|
||||
dds_loader.{h,cpp} DDS → Godot Image(散文件路径用;res:// 里的 .dds 交 Godot 原生导入)
|
||||
m2_material.{h,cpp} texture-stage 描述 → ShaderMaterial(M2.5)
|
||||
libgr2/ submodule → ../xrender-poc/libgr2
|
||||
formats/ submodule → ../xrender-poc/formats(msa/msm)
|
||||
SConstruct / CMakeLists godot-cpp 构建(Phase 1 只出 macOS arm64)
|
||||
project/ Godot 4.7.1 demo 工程
|
||||
main.tscn orbit 相机 + DirectionalLight3D(+shadow) + WorldEnvironment(sky) + Metin2Model
|
||||
main.gd 加载资产、切模型 / 动画、截图、性能 HUD
|
||||
assets/ warrior + 另 2 种骨架的 gr2/dds
|
||||
.gdignore ← 让 Godot 不去 import .gr2(见下)
|
||||
export_presets.cfg macOS(Phase 1)
|
||||
test/
|
||||
golden/ godot-macos-*.png
|
||||
compare.py 与 ../xrender-poc bgfx demo / oracle 对拍
|
||||
docs/
|
||||
GODOT-POC-PLAN.md 本文件
|
||||
steps/ 按需拆
|
||||
```
|
||||
|
||||
### 分层(Phase 1)
|
||||
|
||||
| 层 | 内容 |
|
||||
|---|---|
|
||||
| 平台 / 窗口 / 输入 / 打包 | **Godot**(macOS) |
|
||||
| 渲染器 | **Godot 内置** Forward+(macOS / Metal) |
|
||||
| 资源运行时 | **`Metin2Model` GDExtension**:gr2 → `Skeleton3D` / `ArrayMesh` / `Skin` / `ShaderMaterial` |
|
||||
| 动画 | 每帧 `libgr2::sample_pose` 写 `Skeleton3D`(**B 方案**);A/B 决策**推迟到 Phase 2**(Mac 上信号不足),M2 只做一次 50 角色压力采样留档 |
|
||||
| 解析 | **`libgr2`(复用)** · `formats/`(msa/msm,复用) · DDS |
|
||||
| 数学 / 坐标 | Godot `Transform3D` / `Basis`;gr2 art-tool basis → Godot 右手 Y-up + 单位缩放,在 `gr2_bridge` 一次性处理,约定见 §03 |
|
||||
|
||||
### 两个实操约定(Phase 1 T1 前定死,写进 `gr2_bridge` 注释)
|
||||
|
||||
- **`.gr2` 不进 import 流程**:`project/assets/` 放 `.gdignore`,`.gr2` 由 `Metin2Model` 在运行时用绝对路径 / `user://` 直接读;`.dds` 可留在 `res://` 交 Godot 原生导入,或也走运行时 `dds_loader`(二选一,T1.3 决定并记录)。
|
||||
- **单位缩放**:定 `M2_TO_GODOT = 0.01`(Metin2 约 100 unit ≈ 1 Godot m),相机 near/far、`DirectionalLight3D` 阴影范围按此设。若与资产实测不符,在 T1.1 调整并记录。
|
||||
|
||||
---
|
||||
|
||||
## 03 · 技术选型
|
||||
|
||||
| 项 | 选择 | 理由 |
|
||||
|---|---|---|
|
||||
| 引擎版本 | **Godot 4.7.1 stable**(本机已装),锁死不随手升 | GDExtension ABI 绑 minor;升级作独立任务 |
|
||||
| 扩展机制 | `godot-cpp` **`4.7` 分支**,GDExtension(非模块编译) | 用官方导出模板,不自定义 Godot 构建 |
|
||||
| 构建工具 | `godot-cpp` 的 **CMake 路径**(本机已有 cmake;不装 SCons)或 `brew install scons` 二选一 | Phase 1 只出 macOS arm64,摩擦最小 |
|
||||
| gr2 解析 | `xrender-poc/libgr2` 静态库嵌入 extension | 与 bgfx route 同一份,已过 9166 fuzz + oracle |
|
||||
| 动画方案 | **B(每帧写骨骼)**;A(烘 `Animation`)**决策推迟到 Phase 2** | B 直接复用 `libgr2` 采样、最保真;A 的动机(多角色 CPU)在 Mac 上压不出来 |
|
||||
| 骨骼权重 | `ArrayMesh` 4 权重优先;>4 影响则 `ARRAY_FLAG_USE_8_BONE_WEIGHTS` | M1 T1.2 早验,三种骨架都查 |
|
||||
| 贴图 | 见 §02 实操约定(`.dds` 原生导入 vs 运行时 `dds_loader`,T1.3 定) | 两条都留 |
|
||||
| 材质 | **自定义 `ShaderMaterial`**(`light()` 自定义着色贴 Metin2 观感);M1 先 `StandardMaterial3D` 上屏,M2.5 换 | 多 stage 只做近似,列差距清单 |
|
||||
| 渲染器 | Forward+(macOS) | Phase 1 不涉及 Compatibility |
|
||||
| 阴影 / sky | Godot 内置 `DirectionalLight3D` shadow + `WorldEnvironment` procedural sky,M2.5 接入 | 成本近零,Mac 观感必要 |
|
||||
| 资产来源 | 从 `m2dev-client-main/assets` 拷 **warrior + 另 2 种骨架** 的 gr2/dds 到 `project/assets/`,散文件 | 不接 eterpack(Phase 2 / M4) |
|
||||
| 真值源 | `xrender-poc/oracle` golden + bgfx demo 同机位 / pose / t 截图 | 复用现成对拍基建 |
|
||||
| 仓库 | 新建 `mtgodot-poc/`,`libgr2` / `formats` submodule 指向 `xrender-poc` | 隔离,不污染 bgfx route |
|
||||
|
||||
---
|
||||
|
||||
## 04 · 里程碑
|
||||
|
||||
### M0' · 脚手架(macOS,1–2 天)
|
||||
|
||||
**目标**:GDExtension 骨架 + `libgr2` 集成 + Godot 工程能跑,仅 macOS。
|
||||
|
||||
| 任务 | 完成判据 |
|
||||
|---|---|
|
||||
| T0.1 `godot-cpp` 4.7 GDExtension 骨架,注册空节点 `Metin2Model`(继承 `Node3D`) | Godot 4.7.1 编辑器里能添加该节点、无报错 |
|
||||
| T0.2 构建集成 `libgr2` 静态库(CMake 或 SCons),仅 macOS arm64 | `libgr2` 符号进 extension,`.dylib` 加载成功 |
|
||||
| T0.3 Godot 工程骨架:`main.tscn`(orbit 相机 + `DirectionalLight3D` + `WorldEnvironment` + 空 `Metin2Model`),`main.gd`(orbit 控制 + 截图键) | 编辑器运行出带 sky 的场景 + 一个 cube,鼠标可轨道 |
|
||||
| T0.4 `project/assets/.gdignore`;`export_presets.cfg` 加 macOS preset | 导出一次 macOS `.app`,空场景能跑 |
|
||||
|
||||
**门禁 M0'**:macOS 上跑起「Godot 场景 + sky + cube + orbit 相机」,导出的 `.app` 也能跑。
|
||||
|
||||
---
|
||||
|
||||
### M1 · 静态渲染(1–1.5 周)
|
||||
|
||||
**目标**:warrior + 另 2 种骨架的 bind pose + 贴图在 Godot 里正确显示,多部件组装。
|
||||
|
||||
对标 [`steps/M1-static-render.md`](./steps/M1-static-render.md) T2–T5。
|
||||
|
||||
| 任务 | 完成判据 |
|
||||
|---|---|
|
||||
| T1.1 `gr2_bridge`:`libgr2::Skeleton` → `Skeleton3D`(bone 名 / parent / rest `Transform3D`);basis + 单位缩放在此处理,约定写进注释 | 骨架层级树与 `gr2dump --members` 一致;三种骨架的骨骼数都正确 |
|
||||
| T1.2 `libgr2::Mesh` → `ArrayMesh`(`ARRAY_VERTEX/NORMAL/TANGENT/TEX_UV/BONES/WEIGHTS`)+ `Skin`(bind 矩阵);处理 >4 权重 | 白模轮廓 = Blender 里同模型;无破面;三种骨架都过 |
|
||||
| T1.3 贴图路径决策(`.dds` 原生导入 vs 运行时 `dds_loader`),实现选定方案;warrior 贴图(实测 512×512 DXT3 5mip) | UV 无错位 / 无镜像;决策写进 `extension/README.md` |
|
||||
| T1.4 材质:`StandardMaterial3D`(albedo 贴图 + unshaded 先看轮廓),可切白模 | 贴图目视正确 |
|
||||
| T1.5 多部件(body / weapon / hair)组装,武器挂 `BoneAttachment3D` | 部件数正确、武器跟手骨 |
|
||||
| T1.6 `test/compare.py`:与 bgfx demo 同机位截图 | 几何一致(允许色彩空间 / 光照差异) |
|
||||
|
||||
**门禁 M1**:warrior + 2 种骨架 bind pose 下与 bgfx demo 同机位截图**几何像素级接近**;顶点数、部件数、骨骼数与 oracle dump 一致。
|
||||
|
||||
---
|
||||
|
||||
### M2 · 骨骼动画(1.5–2 周)
|
||||
|
||||
**目标**:从 GDExtension 每帧驱动 `Skeleton3D` 播动画,含跨文件 retarget 与 motion event。
|
||||
|
||||
对标 [`steps/M2-anim-skinning.md`](./steps/M2-anim-skinning.md)。
|
||||
|
||||
| 任务 | 完成判据 |
|
||||
|---|---|
|
||||
| T2.1 `Metin2AnimPlayer`:`_process` 里 `libgr2::sample_pose(t)` → 写 `Skeleton3D` 每骨 local `Transform3D` | 单个动画连续播放、不抖 |
|
||||
| T2.2 跨文件 retarget:骨架 gr2 + 独立动画 gr2 / `.msa` 按骨名对齐(必要时 `RetargetModifier3D` / 骨名映射表) | warrior 骨架播 `dance_1`(独立文件)姿势正确 |
|
||||
| T2.3 motion event:`.msa`/`.msm` 里的帧事件(放音 / 特效锚点 / 震屏)解析并派发(Phase 1 只需回调打点,不接实际特效) | 事件在正确帧触发,日志可见 |
|
||||
| T2.4 `main.gd`:idle + 一个动作,按键 / HUD 按钮切换 | 切换无爆姿势 |
|
||||
| T2.5 逐帧对拍:同 `t` 的 `Skeleton3D` 世界骨骼矩阵 vs bgfx demo / oracle | 差值在坐标系 / 单位换算误差内(沿用 M2 的 ~6.5e-5 量级口径);全程无 NaN |
|
||||
| T2.6 全量采样自检:181 个 warrior 动画逐个 load + sample | 0 崩溃、0 NaN |
|
||||
| T2.7 留档:50 角色同播动画的帧时间 / `_process` 耗时采样(**不作门禁**,给 Phase 2 A/B 决策用) | 数据写进 `test/godot-macos-stress.json` |
|
||||
|
||||
**门禁 M2**:181 动画全部加载采样无 NaN;选定动作与 bgfx demo 同 `t` 视觉一致、数值在换算误差内;motion event 触发帧正确。
|
||||
|
||||
---
|
||||
|
||||
### M2.5 · 材质与观感保真(1–1.5 周)★ Phase 1 的核心门禁
|
||||
|
||||
**目标**:把 Metin2 的固定管线材质在 `ShaderMaterial` 里近似出来,让 Mac 版渲染"看起来做好了"。
|
||||
|
||||
| 任务 | 完成判据 |
|
||||
|---|---|
|
||||
| T2.5.1 `m2_material`:Metin2 材质描述(texture-stage op / arg / alpha op)→ 自定义 `ShaderMaterial`(Godot shading language,`light()` 自定义) | 单 stage `MODULATE(TEXTURE, DIFFUSE)` = `tex * vertex_color` 与 bgfx demo `texture_stage.sh` 输出一致 |
|
||||
| T2.5.2 多 stage 近似:`MODULATE2X` / `ADD` / `BLENDDIFFUSEALPHA` 等常见组合 | warrior 全套贴图正确显示,与原客户端目视接近 |
|
||||
| T2.5.3 alpha test(`D3DCMP_*`)+ alpha blend + additive(装备发光) | 半透明 / 发光部件正确,排序无明显错误 |
|
||||
| T2.5.4 顶点色 + fog(线性 / exp2) | 与 bgfx demo 对拍一致 |
|
||||
| T2.5.5 接 Godot `DirectionalLight3D` 阴影 + `WorldEnvironment`(procedural sky + ambient) | 角色有投影、受环境光;整体画面完整 |
|
||||
| T2.5.6 差距清单:列出无法 1:1 还原的项(排序 corner case、特定 blend op、mip / 各向异性差异) | 写进中期评审报告 |
|
||||
|
||||
**门禁 M2.5**:warrior 完整着装 + 动画,在 Godot 里与原客户端 / bgfx demo **目视接近**(结构化对比 + 直方图,不做像素比);差距清单里没有"方案性死路"项。
|
||||
|
||||
---
|
||||
|
||||
### M3 ·(Phase 2,本轮不做)iOS + Android + 性能
|
||||
|
||||
移动端真机、三端一致、性能、生命周期、Compatibility 渲染器、每帧写骨骼的 A/B 决策 —— **go / no-go 结论在此产生**。详见后续 `steps/M3-mobile.md`(届时再写)。
|
||||
|
||||
### M4 ·(Phase 2 可选拉伸)
|
||||
|
||||
接真实 eterpack、试水一个 `.mse` 特效、多 texture-stage 精确对拍。
|
||||
|
||||
---
|
||||
|
||||
## 05 · 工作量(Phase 1,macOS)
|
||||
|
||||
| 里程碑 | 单人 | 2 人并行 |
|
||||
|---|---|---|
|
||||
| M0' | 1–2 天 | 1 天 |
|
||||
| M1 | 1–1.5 周 | 1 周(网格 / 骨架分工) |
|
||||
| M2 | 1.5–2 周 | 1–1.5 周(动画 / 对拍分工) |
|
||||
| M2.5 | 1–1.5 周 | 1 周(shader / 材质映射分工) |
|
||||
| **Phase 1 合计** | **≈ 3.5–5 周** | **≈ 2.5–3.5 周** |
|
||||
|
||||
前置依赖:`xrender-poc` 的 `libgr2`(已完成)、`oracle` golden(M2 对拍要)、bgfx demo(M1/M2/M2.5 截图对拍要,已可跑)、Godot 4.7.1 export templates(需在编辑器内下载)。
|
||||
|
||||
---
|
||||
|
||||
## 06 · 风险登记册
|
||||
|
||||
### Phase 1(macOS)
|
||||
|
||||
| 风险 | 触发信号 | 缓解 / 退路 |
|
||||
|---|---|---|
|
||||
| `godot-cpp` + `libgr2` 构建集成摩擦 | M0' 链接 / 加载失败 | 只出 macOS arm64,摩擦面最小;CMake 路径优先,SCons 备选 |
|
||||
| `.gr2` 被 Godot import 流程扫到报错 | 打开工程时一堆 import 错误 | `project/assets/.gdignore`;`.gr2` 走运行时绝对路径 / `user://`,T1 前定死 |
|
||||
| gr2 basis / 单位换算到 Godot(左手 → 右手 Y-up + 缩放) | M1 模型躺 / 镜像 / 骨骼反向 / 大小离谱 | `gr2_bridge` 一次性处理,定 `M2_TO_GODOT`,用朝向明确资产手校,写注释 |
|
||||
| `Skeleton3D` 骨骼数上限 / 8 权重路径行为 | M1 T1.2 蒙皮爆开 / 部位错位 | 三种骨架都查 `gr2dump`;>4 权重走 8-bone flag |
|
||||
| **多 stage 固定管线材质无法在 `ShaderMaterial` 近似到可接受** | M2.5 T2.5.2/3 关键装备观感明显错 | **这是 Phase 1 主要方案性风险**;差距清单区分"可延期"vs"死路",后者触发转回 bgfx route |
|
||||
| 半透明 / additive 排序 corner case | M2.5 T2.5.3 发光部件穿插 | 记入差距清单;Godot 的 `render_priority` / `depth_draw` 调,不追求 100% |
|
||||
| motion event 语义(`.msa`/`.msm` 帧事件类型) | M2 T2.3 事件类型没覆盖全 | Phase 1 只需打点回调,实际特效 Phase 2;未覆盖类型列清单 |
|
||||
|
||||
### Phase 2(移动端,本轮不激活,仅登记)
|
||||
|
||||
| 风险 | 缓解 / 退路 |
|
||||
|---|---|
|
||||
| 每帧 GDExtension 写骨骼的 CPU 成本(多角色) | M2 T2.7 已留档;转 A 方案(烘 `Animation` + `AnimationMixer`) |
|
||||
| Godot Compatibility(GLES3) 老安卓 shader 兼容 | M2.5 的 `ShaderMaterial` 写保守,避开 Compatibility 不支持特性 |
|
||||
| Godot 节点开销(大场景规模) | `MultiMesh` / `RenderingServer` 直调兜底 |
|
||||
| GDExtension ABI 绑 Godot minor | 锁 4.7.1,升级作独立任务 |
|
||||
| iOS 交叉编译 `libgr2` 静态库 | 提前出 iOS `.a` 预编译产物 |
|
||||
|
||||
---
|
||||
|
||||
## 07 · 验证方法论
|
||||
|
||||
- **真值源**:`xrender-poc/oracle` 的 Granny golden 数据 + `xrender-poc` bgfx demo(同机位 / pose / `t` 截图与数值 dump,Metal 后端在同一台 Mac 上跑)。
|
||||
- **每个门禁**存 `test/golden/godot-macos-<case>.png` + 骨骼矩阵 / 顶点 dump,跑 `test/compare.py` 与 bgfx route 对拍。
|
||||
- **资产集**:`xrender-poc/test/assets.list` 里的 warrior 相关文件 + 另选 2 种骨架(记入本仓库 `test/assets.list`)。
|
||||
- **对拍容差**:几何 / 骨骼矩阵按坐标系 + 单位换算误差内严格比;颜色 / 光照因渲染器不同**不做像素比**,只做结构化目视 + 直方图。
|
||||
|
||||
---
|
||||
|
||||
## 08 · 交付物
|
||||
|
||||
### Phase 1(本轮)
|
||||
|
||||
| 产物 | 位置 |
|
||||
|---|---|
|
||||
| `mtgodot-poc/` 仓库:GDExtension 源 + Godot 4.7.1 demo 工程 | 新仓库 |
|
||||
| macOS 导出配置 | `project/export_presets.cfg` |
|
||||
| 与 bgfx demo 的几何 / 动画对拍报告 | `test/godot-snapshot.json` |
|
||||
| 材质差距清单 | 中期评审报告内 |
|
||||
| 50 角色压力采样(供 Phase 2 A/B 决策) | `test/godot-macos-stress.json` |
|
||||
| **中期评审报告** | 桥接层工作量实测 + 观感对比截图 + 剩余风险 + "是否进入 Phase 2"建议 |
|
||||
|
||||
### Phase 2(后续)
|
||||
|
||||
| 产物 |
|
||||
|---|
|
||||
| iOS `.app` + Android APK/AAB + 真机性能 / 生命周期报告 |
|
||||
| **go/no-go 对比结论(一页纸)**:与 bgfx route 在工作量 / 性能 / 视觉保真度 / 长期维护四轴对比,给出客户端跨平台基座选型建议 |
|
||||
|
||||
---
|
||||
|
||||
## 选型变更记录
|
||||
|
||||
- 2026-08-29 · 范围收敛为 **Phase 1 = macOS only**;新增 **M2.5 材质与观感保真** 作为 Phase 1 核心门禁;动画 A/B 决策推迟到 Phase 2;Godot 版本钉死 4.7.1 / godot-cpp 4.7。
|
||||
- *(待 M1 T1.3 填写:贴图走 `.dds` 原生导入 还是运行时 `dds_loader`)*
|
||||
- *(待 M2.5 T2.5.6 填写:材质无法 1:1 还原项清单)*
|
||||
@@ -0,0 +1,37 @@
|
||||
# mtgodot GDExtension — M0' skeleton (registers Metin2Model, links libgr2).
|
||||
|
||||
# --- godot-cpp (submodule @ master) ---
|
||||
# master has no per-minor branch; it ships bundled extension_api-4-*.json.
|
||||
# Pin to 4.7 to match the installed editor (4.7.1; patch-level ABI is compatible).
|
||||
set(GODOTCPP_API_VERSION "4.7" CACHE STRING "Target Godot API version" FORCE)
|
||||
add_subdirectory(godot-cpp)
|
||||
|
||||
# --- libgr2 from the sibling xrender-poc repo ---
|
||||
# M0': plain sibling-directory reference (both repos live under .../mt/).
|
||||
# For a portable checkout, either git-submodule xrender-poc or vendor libgr2,
|
||||
# then pass -DXRENDER_POC_DIR=/path/to/xrender-poc.
|
||||
set(XRENDER_POC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../xrender-poc"
|
||||
CACHE PATH "Path to the xrender-poc repo (provides libgr2)")
|
||||
if(NOT EXISTS "${XRENDER_POC_DIR}/libgr2/CMakeLists.txt")
|
||||
message(FATAL_ERROR
|
||||
"libgr2 not found at '${XRENDER_POC_DIR}/libgr2'. "
|
||||
"Pass -DXRENDER_POC_DIR=/path/to/xrender-poc")
|
||||
endif()
|
||||
add_subdirectory("${XRENDER_POC_DIR}/libgr2" "${CMAKE_CURRENT_BINARY_DIR}/libgr2")
|
||||
|
||||
# --- the extension shared library ---
|
||||
add_library(mtgodot SHARED
|
||||
src/register_types.cpp
|
||||
src/metin2_model.cpp
|
||||
)
|
||||
target_compile_features(mtgodot PRIVATE cxx_std_20)
|
||||
target_link_libraries(mtgodot PRIVATE godot::cpp xrender::libgr2)
|
||||
|
||||
# Drop the dylib straight into the Godot project where the .gdextension expects it:
|
||||
# project/bin/libmtgodot.macos.template_debug.dylib (Debug)
|
||||
# project/bin/libmtgodot.macos.template_release.dylib (Release)
|
||||
set_target_properties(mtgodot PROPERTIES
|
||||
PREFIX "lib"
|
||||
OUTPUT_NAME "mtgodot.macos.$<IF:$<CONFIG:Release>,template_release,template_debug>"
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/project/bin"
|
||||
)
|
||||
Submodule
+1
Submodule extension/godot-cpp added at 101ae38034
@@ -0,0 +1,39 @@
|
||||
#include "metin2_model.h"
|
||||
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
#include <godot_cpp/variant/utility_functions.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <gr2/gr2.h> // libgr2 (xrender-poc)
|
||||
|
||||
using namespace godot;
|
||||
|
||||
namespace mtgodot {
|
||||
|
||||
Metin2Model::Metin2Model() = default;
|
||||
Metin2Model::~Metin2Model() = default;
|
||||
|
||||
void Metin2Model::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("libgr2_info"), &Metin2Model::libgr2_info);
|
||||
ClassDB::bind_method(D_METHOD("probe_gr2", "path"), &Metin2Model::probe_gr2);
|
||||
}
|
||||
|
||||
String Metin2Model::libgr2_info() const {
|
||||
// Forces a reference to a libgr2 symbol so the link is real, not dead-stripped.
|
||||
return String("libgr2 linked; gr2::Magic enum size=") + itos((int)sizeof(gr2::Magic));
|
||||
}
|
||||
|
||||
String Metin2Model::probe_gr2(const String &path) const {
|
||||
gr2::LoadError err;
|
||||
const std::string p(path.utf8().get_data());
|
||||
auto file = gr2::File::load_path(p, &err);
|
||||
if (!file) {
|
||||
return vformat("gr2 load FAILED [%s]: %s",
|
||||
String(err.stage.c_str()), String(err.message.c_str()));
|
||||
}
|
||||
return vformat("gr2 OK: format_version=%d sections=%d",
|
||||
(int)file->format_version(), (int)file->sections().size());
|
||||
}
|
||||
|
||||
} // namespace mtgodot
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/classes/node3d.hpp>
|
||||
#include <godot_cpp/variant/string.hpp>
|
||||
|
||||
namespace mtgodot {
|
||||
|
||||
// M0' placeholder node.
|
||||
//
|
||||
// Purpose at this milestone: prove the GDExtension registers a class the Godot
|
||||
// editor can instantiate, and that libgr2 is linked in and callable from here.
|
||||
// M1 replaces the body with real Skeleton3D / ArrayMesh construction
|
||||
// (see docs/GODOT-POC-PLAN.md §M1).
|
||||
class Metin2Model : public godot::Node3D {
|
||||
GDCLASS(Metin2Model, godot::Node3D)
|
||||
|
||||
public:
|
||||
Metin2Model();
|
||||
~Metin2Model() override;
|
||||
|
||||
// One-line tag proving libgr2 symbols resolved at link time.
|
||||
godot::String libgr2_info() const;
|
||||
|
||||
// Parse a .gr2 with libgr2 and report a couple of counts (no scene build yet).
|
||||
godot::String probe_gr2(const godot::String &path) const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
};
|
||||
|
||||
} // namespace mtgodot
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "register_types.h"
|
||||
|
||||
#include <gdextension_interface.h>
|
||||
#include <godot_cpp/core/defs.hpp>
|
||||
#include <godot_cpp/godot.hpp>
|
||||
|
||||
#include "metin2_model.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
void initialize_mtgodot_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
GDREGISTER_CLASS(mtgodot::Metin2Model);
|
||||
}
|
||||
|
||||
void uninitialize_mtgodot_module(ModuleInitializationLevel p_level) {
|
||||
(void)p_level;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
GDExtensionBool GDE_EXPORT mtgodot_library_init(
|
||||
GDExtensionInterfaceGetProcAddress p_get_proc_address,
|
||||
const GDExtensionClassLibraryPtr p_library,
|
||||
GDExtensionInitialization *r_initialization) {
|
||||
godot::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization);
|
||||
init_obj.register_initializer(initialize_mtgodot_module);
|
||||
init_obj.register_terminator(uninitialize_mtgodot_module);
|
||||
init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
|
||||
return init_obj.init();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/core/class_db.hpp>
|
||||
|
||||
void initialize_mtgodot_module(godot::ModuleInitializationLevel p_level);
|
||||
void uninitialize_mtgodot_module(godot::ModuleInitializationLevel p_level);
|
||||
@@ -0,0 +1,10 @@
|
||||
[configuration]
|
||||
|
||||
entry_symbol = "mtgodot_library_init"
|
||||
compatibility_minimum = "4.7"
|
||||
reloadable = true
|
||||
|
||||
[libraries]
|
||||
|
||||
macos.debug = "res://bin/libmtgodot.macos.template_debug.dylib"
|
||||
macos.release = "res://bin/libmtgodot.macos.template_release.dylib"
|
||||
@@ -0,0 +1 @@
|
||||
uid://hc7fxxrjvpr8
|
||||
@@ -0,0 +1,61 @@
|
||||
[preset.0]
|
||||
|
||||
name="macOS"
|
||||
platform="macOS"
|
||||
runnable=true
|
||||
advanced_options=false
|
||||
dedicated_server=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
exclude_filter=""
|
||||
export_path="../build/export/mtgodot-poc.app"
|
||||
patches=PackedStringArray()
|
||||
encryption_include_filters=""
|
||||
encryption_exclude_filters=""
|
||||
seed=0
|
||||
encrypt_pck=false
|
||||
encrypt_directory=false
|
||||
script_export_mode=2
|
||||
|
||||
[preset.0.options]
|
||||
|
||||
export/distribution_type=1
|
||||
binary_format/architecture="arm64"
|
||||
custom_template/debug=""
|
||||
custom_template/release=""
|
||||
debug/export_console_wrapper=1
|
||||
application/icon=""
|
||||
application/icon_interpolation=4
|
||||
application/bundle_identifier="org.internal.mtgodotpoc"
|
||||
application/signature=""
|
||||
application/app_category="Games"
|
||||
application/short_version="1.0"
|
||||
application/version="1.0"
|
||||
application/copyright=""
|
||||
display/high_res=true
|
||||
xcode/platform_build="14C18"
|
||||
xcode/sdk_version="13.1"
|
||||
xcode/sdk_path="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
|
||||
xcode/sdk_name="macosx13.1"
|
||||
codesign/codesign=0
|
||||
codesign/identity=""
|
||||
codesign/certificate_file=""
|
||||
codesign/certificate_password=""
|
||||
codesign/entitlements/allow_jit_code_execution=false
|
||||
codesign/entitlements/allow_unsigned_executable_memory=false
|
||||
codesign/entitlements/allow_dyld_environment_variables=false
|
||||
codesign/entitlements/disable_library_validation=true
|
||||
notarization/notarization=0
|
||||
privacy/microphone_usage_description=""
|
||||
privacy/camera_usage_description=""
|
||||
privacy/location_usage_description=""
|
||||
privacy/address_book_usage_description=""
|
||||
privacy/calendar_usage_description=""
|
||||
privacy/photos_library_usage_description=""
|
||||
privacy/desktop_folder_usage_description=""
|
||||
privacy/documents_folder_usage_description=""
|
||||
privacy/downloads_folder_usage_description=""
|
||||
privacy/network_volumes_usage_description=""
|
||||
privacy/removable_volumes_usage_description=""
|
||||
ssh_remote_deploy/enabled=false
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
extends Node3D
|
||||
## mtgodot-poc — M0' harness.
|
||||
##
|
||||
## Builds the minimal scene the plan asks for (orbit camera + DirectionalLight3D
|
||||
## with shadows + WorldEnvironment/sky + placeholder cube + an empty Metin2Model),
|
||||
## then reports whether the GDExtension loaded.
|
||||
##
|
||||
## M1 will replace the cube with Metin2Model.load_gr2(...) output.
|
||||
|
||||
const ORBIT_SPEED := 0.01
|
||||
const ZOOM_STEP := 0.3
|
||||
const PITCH_LIMIT := 1.45
|
||||
|
||||
var _cam: Camera3D
|
||||
var _yaw := 0.6
|
||||
var _pitch := 0.45
|
||||
var _dist := 4.0
|
||||
var _target := Vector3(0.0, 1.0, 0.0)
|
||||
var _dragging := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_build_environment()
|
||||
_build_sun()
|
||||
_build_camera()
|
||||
_build_placeholder()
|
||||
_probe_extension()
|
||||
print("[mtgodot] M0' harness ready — drag: orbit, wheel: zoom, F2: screenshot")
|
||||
# Batch capture: MTGODOT_AUTOSHOT=/abs/path.png godot --path project
|
||||
var autoshot := OS.get_environment("MTGODOT_AUTOSHOT")
|
||||
if autoshot != "":
|
||||
await get_tree().create_timer(0.8).timeout
|
||||
var img := get_viewport().get_texture().get_image()
|
||||
img.save_png(autoshot)
|
||||
print("[mtgodot] autoshot -> ", autoshot)
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
func _build_environment() -> void:
|
||||
var we := WorldEnvironment.new()
|
||||
var env := Environment.new()
|
||||
var sky := Sky.new()
|
||||
sky.sky_material = ProceduralSkyMaterial.new()
|
||||
env.background_mode = Environment.BG_SKY
|
||||
env.sky = sky
|
||||
env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
|
||||
env.tonemap_mode = Environment.TONE_MAPPER_FILMIC
|
||||
we.environment = env
|
||||
add_child(we)
|
||||
|
||||
|
||||
func _build_sun() -> void:
|
||||
var sun := DirectionalLight3D.new()
|
||||
sun.rotation_degrees = Vector3(-45.0, -35.0, 0.0)
|
||||
sun.shadow_enabled = true
|
||||
add_child(sun)
|
||||
|
||||
|
||||
func _build_camera() -> void:
|
||||
_cam = Camera3D.new()
|
||||
_cam.current = true
|
||||
_cam.fov = 55.0
|
||||
add_child(_cam)
|
||||
_update_camera()
|
||||
|
||||
|
||||
func _build_placeholder() -> void:
|
||||
var cube := MeshInstance3D.new()
|
||||
cube.name = "Placeholder"
|
||||
cube.mesh = BoxMesh.new()
|
||||
cube.position = _target
|
||||
add_child(cube)
|
||||
|
||||
|
||||
func _probe_extension() -> void:
|
||||
if not ClassDB.class_exists("Metin2Model"):
|
||||
push_error("[mtgodot] Metin2Model NOT registered — GDExtension failed to load")
|
||||
return
|
||||
var m := ClassDB.instantiate("Metin2Model") as Node3D
|
||||
m.name = "Metin2Model"
|
||||
add_child(m)
|
||||
print("[mtgodot] Metin2Model registered OK — ", m.call("libgr2_info"))
|
||||
# Optional: point MTGODOT_PROBE_GR2 at a real .gr2 to exercise libgr2 end to end.
|
||||
var probe := OS.get_environment("MTGODOT_PROBE_GR2")
|
||||
if probe != "":
|
||||
print("[mtgodot] probe_gr2(", probe, ") -> ", m.call("probe_gr2", probe))
|
||||
|
||||
|
||||
func _update_camera() -> void:
|
||||
var offset := Vector3(
|
||||
cos(_pitch) * sin(_yaw),
|
||||
sin(_pitch),
|
||||
cos(_pitch) * cos(_yaw)) * _dist
|
||||
_cam.position = _target + offset
|
||||
_cam.look_at(_target, Vector3.UP)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
var mb := event as InputEventMouseButton
|
||||
match mb.button_index:
|
||||
MOUSE_BUTTON_LEFT:
|
||||
_dragging = mb.pressed
|
||||
MOUSE_BUTTON_WHEEL_UP:
|
||||
_dist = maxf(0.5, _dist - ZOOM_STEP)
|
||||
_update_camera()
|
||||
MOUSE_BUTTON_WHEEL_DOWN:
|
||||
_dist = minf(50.0, _dist + ZOOM_STEP)
|
||||
_update_camera()
|
||||
elif event is InputEventMouseMotion and _dragging:
|
||||
var mm := event as InputEventMouseMotion
|
||||
_yaw -= mm.relative.x * ORBIT_SPEED
|
||||
_pitch = clampf(_pitch + mm.relative.y * ORBIT_SPEED, -PITCH_LIMIT, PITCH_LIMIT)
|
||||
_update_camera()
|
||||
elif event is InputEventKey:
|
||||
var k := event as InputEventKey
|
||||
if k.pressed and k.keycode == KEY_F2:
|
||||
_screenshot()
|
||||
|
||||
|
||||
func _screenshot() -> void:
|
||||
var img := get_viewport().get_texture().get_image()
|
||||
var path := "user://shot_%d.png" % Time.get_ticks_msec()
|
||||
img.save_png(path)
|
||||
print("[mtgodot] screenshot -> ", ProjectSettings.globalize_path(path))
|
||||
@@ -0,0 +1 @@
|
||||
uid://clyxqop8cvbbe
|
||||
@@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://main.gd" id="1_main"]
|
||||
|
||||
[node name="Main" type="Node3D"]
|
||||
script = ExtResource("1_main")
|
||||
@@ -0,0 +1,19 @@
|
||||
; Godot 4.7 project — mtgodot-poc Phase 1 (macOS)
|
||||
; See ../docs/GODOT-POC-PLAN.md
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="mtgodot-poc"
|
||||
run/main_scene="res://main.tscn"
|
||||
config/features=PackedStringArray("4.7", "Forward Plus")
|
||||
|
||||
[debug]
|
||||
|
||||
gdscript/warnings/untyped_declaration=1
|
||||
|
||||
[rendering]
|
||||
|
||||
renderer/rendering_method="forward_plus"
|
||||
anti_aliasing/quality/msaa_3d=2
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 176 KiB |
Reference in New Issue
Block a user