Files
ViewDesignEngine/include/vde/brep/assembly_lod.h
T

268 lines
9.3 KiB
C++
Raw Normal View History

#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 <functional>
#include <optional>
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<LODNode>& 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 <typename Visitor>
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 <typename Visitor>
bool traverse_with_context(Visitor&& visitor) const {
return traverse_impl(&assembly_.root, Transform3D::Identity(), 0,
std::forward<Visitor>(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<LODNode> nodes_;
InstanceCache cache_;
core::AABB3D total_bounds_;
/// 收集所有零件节点
void collect_nodes(AssemblyNode* node, const Transform3D& parent_tf);
/// 带上下文的遍历实现
template <typename Visitor>
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>(visitor)))
return false;
}
return true;
}
};
} // namespace vde::brep