#pragma once /** * @file assembly_lod.h * @brief 大装配 LOD(细节层次)系统 * * 为装配体节点提供三级 LOD 支持,基于视距自动切换。 * 集成 InstanceCache 实现几何共享,减少大装配的内存占用。 * * LOD 级别: * - Full: 完整 B-Rep(精确几何) * - Simplified: QEM 简化网格(快速渲染) * - BoundingBox: AABB 包围盒(远距离/不可见) * * @ingroup brep */ #include "vde/brep/assembly.h" #include "vde/brep/large_assembly.h" #include "vde/core/aabb.h" #include #include namespace vde::brep { /// 将局部空间 AABB 变换到世界空间 [[nodiscard]] core::AABB3D compute_transformed_bounds( const core::AABB3D& local_bb, const Transform3D& tf); // ═══════════════════════════════════════════════════════════ // LODLevel // ═══════════════════════════════════════════════════════════ /// 细节层次枚举 enum class LODLevel { Full, ///< 完整 B-Rep 模型(精确几何) Simplified, ///< QEM 简化后的三角网格 BoundingBox ///< 仅 AABB 包围盒(远距离/剔除测试) }; /// LOD 级别转可读字符串 [[nodiscard]] inline const char* to_string(LODLevel level) { switch (level) { case LODLevel::Full: return "Full"; case LODLevel::Simplified: return "Simplified"; case LODLevel::BoundingBox: return "BoundingBox"; } return "Unknown"; } // ═══════════════════════════════════════════════════════════ // LODNode — per-node LOD state // ═══════════════════════════════════════════════════════════ /// 每个装配节点的 LOD 状态 struct LODNode { AssemblyNode* node = nullptr; ///< 对应的装配节点 core::AABB3D world_bounds; ///< 世界空间包围盒(缓存) LODLevel current_level = LODLevel::Full; ///< 当前 LOD 级别 bool manual_override = false; ///< 是否手动设置了级别 }; // ═══════════════════════════════════════════════════════════ // LODConfig // ═══════════════════════════════════════════════════════════ /// LOD 系统配置参数 struct LODConfig { double near_plane = 50.0; ///< 近距离阈值:距离 < near_plane → Full double far_plane = 500.0; ///< 远距离阈值:距离 > far_plane → BoundingBox double screen_size_threshold = 0.02; ///< 屏幕空间尺寸比阈值 int simplify_num_levels = 3; ///< QEM 简化级别数 bool enable_auto_lod = true; ///< 是否启用自动 LOD 切换 }; // ═══════════════════════════════════════════════════════════ // AssemblyLOD // ═══════════════════════════════════════════════════════════ /** * @brief 大装配 LOD 管理器 * * 管理装配体所有节点的 LOD 级别。支持: * - 自动视距驱动 LOD 选择 * - 手动设置节点 LOD 级别 * - 按 LOD 级别遍历装配体 * - 集成 InstanceCache 共享几何 * * @code{.cpp} * Assembly assy("robot"); * // ... 添加零件 ... * AssemblyLOD lod(assy); * lod.set_config(LODConfig{ .near_plane = 100, .far_plane = 1000 }); * * // 自动选择 LOD * lod.compute_lod(camera_distance, screen_size); * * // 按 LOD 遍历 * lod.traverse_assembly([&](const LODNode& n) { * switch (n.current_level) { * case LODLevel::Full: render_brep(n); break; * case LODLevel::Simplified: render_simplified(n); break; * case LODLevel::BoundingBox: render_bbox(n); break; * } * }); * @endcode * * @ingroup brep */ class AssemblyLOD { public: /** * @brief 从装配体构建 LOD 管理器 * * 收集所有零件节点,预计算世界空间 AABB, * 通过 InstanceCache 注册可共享的几何。 * * @param assembly 装配体引用 */ explicit AssemblyLOD(Assembly& assembly); /// 获取配置 [[nodiscard]] const LODConfig& config() const { return config_; } /// 设置配置 void set_config(const LODConfig& cfg); /** * @brief 获取所有 LOD 节点(扁平化列表) * @return LODNode 列表引用 */ [[nodiscard]] const std::vector& nodes() const { return nodes_; } /** * @brief 根据视距和屏幕尺寸自动选择 LOD 级别 * * 选择规则: * - distance > far_plane → BoundingBox * - distance < near_plane → Full * - near_plane ≤ distance ≤ far_plane → Simplified * * @param camera_distance 相机到装配体中心的距离 * @param screen_size 屏幕空间尺寸比(节点投影 / 屏幕尺寸) */ void compute_lod(double camera_distance, double screen_size); /** * @brief 自动计算单个节点的 LOD 级别 * * @param node LOD 节点(含预计算的 world_bounds) * @param camera_distance 相机到装配体中心的距离 * @param screen_size 屏幕空间尺寸比 * @return 推荐的 LOD 级别 */ [[nodiscard]] LODLevel compute_lod_for_node( const LODNode& node, double camera_distance, double screen_size) const; /** * @brief 手动设置某节点的 LOD 级别 * * 手动设置后,该节点将忽略自动 LOD 选择直到调用 clear_manual_override()。 * * @param node LOD 节点(通过 nodes() 获取) * @param level 要设置的 LOD 级别 */ void set_lod_level(LODNode& node, LODLevel level); /** * @brief 清除手动 LOD 设置 * * @param node 要清除手动覆盖的节点 */ void clear_manual_override(LODNode& node); /// 清除所有节点的手动 LOD 设置 void clear_all_manual_overrides(); /** * @brief 按 LOD 级别遍历装配体 * * 对每个零件节点调用 visitor,传入其 LODNode 状态。 * * @param visitor 回调函数:void(const LODNode&) */ template void traverse_assembly(Visitor&& visitor) const { for (const auto& node : nodes_) { visitor(node); } } /** * @brief 按 LOD 遍历(带装配层级上下文) * * 遍历装配体树,对每个零件节点调用 visitor。 * 传入 LODNode、父变换矩阵、深度。 * * @param visitor 回调:bool(LODNode&, const Transform3D& parent_tf, int depth) * 返回 false 可提前终止遍历 */ template bool traverse_with_context(Visitor&& visitor) const { return traverse_impl(&assembly_.root, Transform3D::Identity(), 0, std::forward(visitor)); } /// 获取 InstanceCache [[nodiscard]] InstanceCache& cache() { return cache_; } [[nodiscard]] const InstanceCache& cache() const { return cache_; } /// 零件总数 [[nodiscard]] size_t part_count() const { return nodes_.size(); } /// 各级别统计 struct LODStats { size_t full_count = 0; size_t simplified_count = 0; size_t bbox_count = 0; size_t manual_count = 0; }; /// 获取当前 LOD 统计信息 [[nodiscard]] LODStats statistics() const; /// 获取装配体总包围盒 [[nodiscard]] core::AABB3D total_bounds() const { return total_bounds_; } /// 更新所有节点的世界空间包围盒(变换改变后调用) void update_bounds(); private: Assembly& assembly_; LODConfig config_; std::vector nodes_; InstanceCache cache_; core::AABB3D total_bounds_; /// 收集所有零件节点 void collect_nodes(AssemblyNode* node, const Transform3D& parent_tf); /// 带上下文的遍历实现 template bool traverse_impl(AssemblyNode* node, const Transform3D& parent_tf, int depth, Visitor&& visitor) const { Transform3D world = parent_tf * node->local_transform; if (node->is_part()) { // 查找对应的 LODNode for (auto& ln : nodes_) { if (ln.node == node) { if (!visitor(ln, world, depth)) return false; break; } } } for (const auto& child : node->children) { if (!traverse_impl(child.get(), world, depth + 1, std::forward(visitor))) return false; } return true; } }; } // namespace vde::brep