Files
EVEngine/src/engine/devtools/SceneInspect.hpp
T
西风逍遥游 34d1fdb63b refactor(modules): clear remaining back-edges and split the largest TUs (#127)
* chore(ci): add clang-format gate, CODEOWNERS and agent collaboration rules

* refactor(modules): clear remaining back-edges and split the largest TUs

Three known dependency back-edges are gone (module_depgraph.py KNOWN_BACK_EDGES
is now empty; --check passes with 0 violations, 0 cycles):

- window -> graphics: window queries the new IWindowSurfaceHost capability
  (common/WindowSurfaceHost.h) instead of holding a Graphics pointer;
  Window::setGraphics was removed and all call sites updated.
- window -> image: Window::setIcon was replaced by setIconRGBA(const uint8_t*,
  w, h); the window no longer decodes ImageData.
- scene -> graphics: pickScreenAt / collectFrustumIdsAt implementations moved
  to graphics/ScenePicking.cpp (graphics -> scene is a legal downward edge);
  scene now depends only on spatial.

TU splits (pure moves, verified identical method sets and brace balance):

- vulkan/Graphics.cpp (5118 lines) -> Graphics.cpp (lifecycle), GraphicsPipeline,
  Graphics2D, Graphics3D, GraphicsMesh + shared GraphicsInternal.h.
- scene/Scene.cpp -> Scene.cpp (script layer / host lifecycle / serialization)
  + SceneNodes.cpp + shared SceneInternal.h.

Verified with a real build (MSVC 19.51, prebuilt third-party): full build green;
window.* and Scene.* suites pass, including scene picking/culling and GPU cases.

* refactor(graphics): narrow Graphics interfaces and clear public-header leaks

Step 4 — interface segregation:
- New lean interfaces: IGraphics2D, IGraphics3D, IResourceFactory, IPostFX
  (pure virtuals, forward-declared types only; graphics/Color.h extracted from
  Canvas.h so modules that only need the color type stop pulling Canvas.h).
- Graphics now implements all four facets; consumers can depend on the narrow
  interface they use instead of the 129-method god class.
- Migrated voxel (VoxelWorld::drawVisible -> IGraphics3D*) and model3d
  (ModelRenderer::buildRenderable(s) -> IResourceFactory&).
- Graphics.h slims from 24 sub-header includes to a handful of small type
  headers + forward declarations; the destructor moved out-of-line so
  unique_ptr effect members are destroyed where types are complete.

Step 5 — header leaks (module_depgraph.py '*' marks: 14 -> 7):
- map/TileLayer.h, particles/ParticleEmitter.h, avatar/AvatarInstance.h,
  animation/SpineAnim.h, map/TileSystem.h, model3d/ModelData.h,
  model3d/ModelRenderer.h, audio/Audio.h + Source.h, image/ImageData.h +
  Image.h, dialogue/Dialogue.h, procgen Palette/OutputSpec/TextureRecipe/
  PbrMaterial -> forward declarations, includes moved to .cpp.
- voxel/VoxelWorld.h: terrain sampler Pimpl'd (unique_ptr + out-of-line
  destructor), removing the procgen header dependency.
- gpgpu/vulkan backend headers stop pulling graphics/vulkan/Graphics.h.
- Remaining '*' marks are deliberate: tiny POD/enum headers (GridConfig,
  MapObject, BlendMode, Color), an internal backend header
  (GraphicsInternal.h -> filesystem), and lean interface references.

Verified with a full clean build (MSVC) and tests: window/Scene/voxel/
RenderSystem/Quad/shader/TextureSampler/resourceLifetime pass; broad suite
only shows the pre-existing PHYSFS-init filesystem-environment failures.

* feat(resources): revive ResourceManager as unified IAssetReloader-backed cache

* fix(resources): leak ResourceManager singleton to avoid exit-time teardown crashes

Cached CPU resources may own third-party handles (FreeType faces, Assimp
scenes, image decode handlers) whose libraries are torn down at process
exit in an unspecified TU order; destroying cached entries from the
singleton destructor can segfault at exit (seen on CI Windows in
model3d.materialApi.objMtl). Keep the singleton alive until OS reclaim;
explicit unload()/clear() still release entries during the run.

* refactor(devtools): consume business modules through capability interfaces

DevTools / cmdline no longer include business-module headers, so trimming
scene / physics / procgen / particles / audio / ui / graphics no longer
requires disabling devtools, and devtools can be switched off with
-DEVENGINE_MODULE_DEVTOOLS=OFF.

New capability interfaces in common/:
- ISceneQuery (scene) - host/node inspection + mutation + transform sync
- IRenderCapture (graphics) - render status, PNG capture, camera control,
  visible-entity snapshot, entity-ID mask, G-buffer PNGs
- IPhysicsQuery (physics) - 2D world lifecycle + ray casts
- IProcgenQuery (procgen) - recipe listing, map/mesh generation
- IParticlesQuery (particles) - emitter status + one-shot emit
- IAudioQuery (audio) - volume / stop-all
- IEditorHost (ui) - declarative editor host (used by cmdline too)

Providers register in their module constructors. DevTools rewires McpServer
(render/scene/procgen/physics/particles/audio/host tools), SceneInspect
(now delegates to ISceneQuery + IRenderCapture) and RenderVision (uses
IRenderCapture::capturePngDataUrl); cmdline/Mcp.cpp uses IEditorHost.

manifest: devtools DEPS graphics scene physics procgen particles audio ui
image event filesystem -> event; cmdline DEPS filesystem ui -> filesystem.

Verified: full build green; devtools.mcp.* tests pass; broad suite shows
only the pre-existing PHYSFS-init filesystem-environment failures.

* style: apply clang-format to changed lines after dev merge

* fix(webgpu): include RenderControl/TextureSampler explicitly after Graphics.h slimming

* fix(webgpu): provide newFont/print stubs to satisfy IGraphics2D/IResourceFactory vtables

* fix(common): make ProcgenQuery.h self-sufficient (cstdint/utility for GCC)
2026-08-20 05:53:33 +08:00

125 lines
5.0 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.
#pragma once
#include "common/Export.h"
#include <glm/glm.hpp>
#include <string>
#include <vector>
namespace eve {
class IRenderCapture;
class ISceneQuery;
} // namespace eve
namespace eve::dev {
/**
* @brief 场景巡检工具集(MCP 底层数据底座)。
*
* 提供原子化的相机控制 + 渲染帧捕获 + 可见实体三维几何快照,保证图像与
* 3D 几何数据严格同步(同一次相机位姿下抓帧并导出配套几何 JSON)。
*
* 本模块不依赖任何脚本状态,直接读取引擎的 Graphics / Camera3D / Scene
* 运行时模块,因此可被 MCP 工具、C++ 测试或调试面板复用。
*
* 桌面端专用(EVDevTools 的一部分)。
*/
struct EVENGINE_API InspectView {
std::string name; // "along_road" | "bird_eye" | "corner_close" | "vista"
std::string kind; // 中文说明:沿路平视 / 高空俯拍 / 拐角特写 / 远景视角
glm::vec3 eye{0.f};
glm::vec3 target{0.f};
float fovYDeg = 60.f;
};
/** @brief 一次原子快照的结果:PNG 渲染帧 + 配套几何 JSON。 */
struct EVENGINE_API InspectCapture {
bool ok = false;
std::string error;
std::string pngPath; // 渲染帧 PNG 的磁盘路径(color buffer)
std::string jsonPath; // 配套三维几何快照 JSON 的磁盘路径
int width = 0;
int height = 0;
int entityCount = 0;
// 可选附加缓冲区(按需生成)。
std::string depthPngPath; // depth buffer PNG(GBuffer 线性深度)
std::string normalPngPath; // normal buffer PNG(GBuffer 世界法线)
std::string idPngPath; // 渲染 ID mask PNG(逐像素实体 ID 颜色)
std::string idJsonPath; // 渲染 ID mask 的 id→实体 映射 JSON
std::vector<std::string> unsupported; // 请求但当前后端不支持的缓冲区
};
class EVENGINE_API SceneInspect {
public:
static SceneInspect& instance();
SceneInspect(const SceneInspect&) = delete;
SceneInspect& operator=(const SceneInspect&) = delete;
// ---------- 多机位自动采样 ----------
/**
* @brief 围绕 center 自动生成一组标准化巡检机位:
* - along_road 沿路平视:低机位、贴近目标、视线水平略前倾
* - bird_eye 高空俯拍:目标正上方垂直俯拍
* - corner_close 拐角特写:贴近目标、斜向特写、较窄 FOV
* - vista 远景视角:抬高拉远、广角俯览
* fov 为基准 FOV(度),各机位在此基础上微调。
*/
static std::vector<InspectView> generateViews(const glm::vec3& center, float fov = 60.f);
// ---------- 相机位姿 ----------
/**
* @brief 以 pos + 欧拉角 rot 设置相机位姿。
* rot = [yawDeg, pitchDeg](与 firstperson 约定一致,Y-up)。
* fov <= 0 时保持当前 FOV。
*/
bool setCameraPose(const glm::vec3& eye, const glm::vec3& rotYawPitch, float fov = 0.f);
bool setCameraView(const InspectView& v);
// ---------- 原子快照(渲染帧 + 几何 JSON) ----------
/**
* @brief 锁定当前相机 → 捕获渲染帧 PNG → 立刻导出配套可见实体几何 JSON。
* 两者共享同一相机位姿,杜绝图片与实体数据错位。
* outDir 为空时使用缓存目录。返回文件路径与图像尺寸。
* buffers 可选值:"color"(默认, 帧) | "depth" | "normal" | "id"(渲染 ID mask)
* | "shadow"(当前后端不支持, 记为 unsupported)。
*/
InspectCapture capture(const std::string& outDir = {}, const std::string& tag = "frame",
const std::vector<std::string>& buffers = {});
// ---------- 可见实体三维几何快照 ----------
/**
* @brief 生成当前视锥内可见实体的结构化 JSON:
* { camera, viewport, entities:[ { id, asset, world_aabb:{min,max},
* screen_bbox:{x,y,w,h} } ] }
* 位姿可用 camera 指定,否则回退到当前激活相机。
*/
std::string visibleEntitiesJson(const glm::vec3* eye = nullptr,
const glm::vec3* target = nullptr, float fov = 0.f);
/** @brief 当前激活相机位姿 JSON(eye/target/fov/viewport)。 */
std::string currentPoseJson();
// ---------- 快照持久化 / 临时缓存 ----------
void setCacheDir(std::string dir) { cacheDir_ = std::move(dir); }
const std::string& cacheDir() const { return cacheDir_; }
/** @brief 默认缓存目录:<gameRoot>/eve_inspect_cache。 */
std::string defaultCacheDir() const;
/** @brief 最近一次快照的 PNG / JSON 路径(供后续读取)。 */
const std::string& lastPngPath() const { return lastPngPath_; }
const std::string& lastJsonPath() const { return lastJsonPath_; }
private:
SceneInspect() = default;
eve::IRenderCapture* captureIface();
eve::ISceneQuery* scene();
bool ensureCamera();
std::string cacheDir_;
std::string lastPngPath_;
std::string lastJsonPath_;
};
} // namespace eve::dev