碎片形状随机化:唯一代键替代命中点量化缓存键(每次命中重新随机 Voronoi),配套碎片模型生命周期回收(销毁次帧卸载 + OnDestroy 兜底)
This commit is contained in:
@@ -25,9 +25,9 @@
|
||||
// OnUpdate:碎片簿记——body 建好(下一帧 Step 对账,同 Bullet
|
||||
// OnCreate 施速的时序契约)即统一施加初速;寿命到期统一 Destroy;
|
||||
// 总量上限防泄漏
|
||||
// 碎片模型缓存驻留(与场景模型同策略;同代碎片共享缓存 ref,不
|
||||
// 能 per-fragment 卸载,门面亦无按路径卸载——TODO 归资产系统
|
||||
// 侧线:AssetCache 伪资产 LRU/引用计数)
|
||||
// 碎片模型生命周期:每代唯一缓存键(形状每次随机)+ 碎片销毁的
|
||||
// 下一帧 UnloadModelByPath 回收(Destroy 帧尾延迟,本帧渲染收集
|
||||
// 仍引用模型,同帧卸载 = 渲染悬空)+ OnDestroy 兜底
|
||||
//
|
||||
// 占位局限(真碎片资产接入后本路径退役):碎片网格顶点按墙世界
|
||||
// 尺寸烘焙、碎片实体复用墙 world pos/rot(scale 恒 1,防 ConvexHull
|
||||
@@ -71,6 +71,11 @@ namespace {
|
||||
return static_cast<ECS::Entity>(bits);
|
||||
}
|
||||
|
||||
// 碎片模型代序号(全局递增):每代碎片一组唯一缓存键 → 每次
|
||||
// 命中都重新随机生成形状(命中点量化键会让同位置重复命中
|
||||
// 复用首次几何——缓存去重把新几何丢弃,碎片就"固定"了)
|
||||
u32 s_GenSeq = 0;
|
||||
|
||||
// 实体句柄 → bits(EntityBits() 只覆盖自身绑定实体,碎片簿记
|
||||
// 需要任意实体的同款换算)
|
||||
u32 BitsOf(const ECS::EntityHandle& e) {
|
||||
@@ -248,10 +253,27 @@ namespace {
|
||||
}
|
||||
|
||||
void OnUpdate(f32 dt) override {
|
||||
ApplyPendingUnload();
|
||||
ApplyPendingVelocities();
|
||||
AgeAndCleanup(dt);
|
||||
}
|
||||
|
||||
// 墙销毁(PIE stop 换血 / 手动删除):碎片实体由场景清亡处理,
|
||||
// 独占碎片模型全部回收(模型不被快照恢复的场景引用——唯一代键)
|
||||
void OnDestroy() override {
|
||||
auto& assets = App().GetAssets();
|
||||
for (const Fragment& f : m_Fragments) {
|
||||
if (!f.modelPath.empty()) {
|
||||
assets.UnloadModelByPath(f.modelPath);
|
||||
}
|
||||
}
|
||||
m_Fragments.clear();
|
||||
for (const std::string& p : m_PendingUnload) {
|
||||
assets.UnloadModelByPath(p);
|
||||
}
|
||||
m_PendingUnload.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
// ---- 触发碎裂:藏墙 + Voronoi 切分 + spawn 碎片群 ----
|
||||
void Fire(const glm::vec3& hitPoint, const glm::vec3& hitNormal) {
|
||||
@@ -311,16 +333,11 @@ namespace {
|
||||
if (seeds.size() < 4) return; // 墙面太小采样失败:不碎
|
||||
|
||||
// ---- 每种子 → cell → 棱柱 mesh → ParsedModel ----
|
||||
// 缓存键量化命中点(0.25m)+ 墙尺寸:同条件重复命中复用
|
||||
// 同一批模型缓存(Load 去重,loader 不再执行)
|
||||
auto q = [](f32 v) { return int(std::round(v * 4.0f)); };
|
||||
// 唯一代键(见 s_GenSeq 注释):形状每次随机;模型回收走
|
||||
// Fragment.modelPath + 待卸载队列(ApplyPendingUnload)
|
||||
const std::string keyBase =
|
||||
"procedural://vorfrag/" + std::to_string(q(wallSize.x)) +
|
||||
'x' + std::to_string(q(wallSize.y)) +
|
||||
'x' + std::to_string(q(wallSize.z)) +
|
||||
'/' + std::to_string(count) +
|
||||
'/' + std::to_string(q(uvHit.x)) +
|
||||
'/' + std::to_string(q(uvHit.y)) + '/';
|
||||
"procedural://vorfrag/" +
|
||||
std::to_string(s_GenSeq++) + '/';
|
||||
// 砖色 baseColor(glTF baseColorFactor 语义:rgb 乘进 albedo)
|
||||
const glm::vec4 brick{140.0f / 255.0f, 92.0f / 255.0f,
|
||||
72.0f / 255.0f, 1.0f};
|
||||
@@ -328,14 +345,17 @@ namespace {
|
||||
auto& assets = App().GetAssets();
|
||||
std::vector<Engine::ModelRef> refs;
|
||||
std::vector<glm::vec3> centroids; // 碎片质心(世界系,初速径向用)
|
||||
std::vector<std::string> paths; // 模型缓存键(回收用,与 refs 对齐)
|
||||
refs.reserve(seeds.size());
|
||||
centroids.reserve(seeds.size());
|
||||
paths.reserve(seeds.size());
|
||||
for (size_t s = 0; s < seeds.size(); ++s) {
|
||||
const std::vector<glm::vec2> cell =
|
||||
VoronoiCell(seeds[s], seeds, hw, hh);
|
||||
if (cell.size() < 3) {
|
||||
refs.push_back({}); // 退化 cell:占位,spawn 跳过
|
||||
centroids.emplace_back();
|
||||
paths.emplace_back();
|
||||
continue;
|
||||
}
|
||||
PrismMesh pm = CellToPrism(cell, hz);
|
||||
@@ -359,6 +379,7 @@ namespace {
|
||||
const Engine::ModelRef ref =
|
||||
assets.LoadModel(parsed.path, std::move(parsed));
|
||||
refs.push_back(ref);
|
||||
paths.push_back(parsed.path);
|
||||
}
|
||||
|
||||
// ---- spawn 碎片实体:pos/rot 复用墙位姿(mesh 已真尺寸,
|
||||
@@ -412,17 +433,32 @@ namespace {
|
||||
0.0f};
|
||||
|
||||
// 总量上限:超限立即回收最老碎片(防泄漏的兜底闸;
|
||||
// 寿命清理才是常态回收)
|
||||
// 寿命清理才是常态回收)。模型延迟一帧卸载
|
||||
const u32 bits = BitsOf(frag);
|
||||
if (m_Fragments.size() >=
|
||||
static_cast<size_t>(m_MaxFragments)) {
|
||||
Destroy(EntityFromBits(m_Fragments.front().bits));
|
||||
if (!m_Fragments.front().modelPath.empty()) {
|
||||
m_PendingUnload.push_back(
|
||||
std::move(m_Fragments.front().modelPath));
|
||||
}
|
||||
m_Fragments.erase(m_Fragments.begin());
|
||||
}
|
||||
m_Fragments.push_back({bits, vel, 0.0f});
|
||||
m_Fragments.push_back(
|
||||
Fragment{bits, vel, 0.0f, false, paths[s]});
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 模型回收:延迟一帧执行(Destroy 是帧尾延迟,碎片实体
|
||||
// 本帧渲染收集仍会引用模型;下一帧实体已灭,卸载才安全)----
|
||||
void ApplyPendingUnload() {
|
||||
auto& assets = App().GetAssets();
|
||||
for (const std::string& p : m_PendingUnload) {
|
||||
assets.UnloadModelByPath(p);
|
||||
}
|
||||
m_PendingUnload.clear();
|
||||
}
|
||||
|
||||
// ---- 施速待办:body 建好(bodyValid)即注入,勿重复 ----
|
||||
void ApplyPendingVelocities() {
|
||||
auto& registry = App().GetScene().GetRegistry();
|
||||
@@ -431,6 +467,11 @@ namespace {
|
||||
if (it->applied) { ++it; continue; }
|
||||
const ECS::Entity e = EntityFromBits(it->bits);
|
||||
if (!registry.valid(e)) { // 实体没了(外部销毁)
|
||||
// 模型是独占资源(一代一键),实体没了模型也回收
|
||||
if (!it->modelPath.empty()) {
|
||||
m_PendingUnload.push_back(
|
||||
std::move(it->modelPath));
|
||||
}
|
||||
it = m_Fragments.erase(it);
|
||||
continue;
|
||||
}
|
||||
@@ -456,6 +497,10 @@ namespace {
|
||||
if (registry.valid(e)) {
|
||||
Destroy(e);
|
||||
}
|
||||
// 模型延迟一帧卸载(本帧渲染收集还引用它)
|
||||
if (!it->modelPath.empty()) {
|
||||
m_PendingUnload.push_back(std::move(it->modelPath));
|
||||
}
|
||||
it = m_Fragments.erase(it);
|
||||
}
|
||||
}
|
||||
@@ -470,6 +515,7 @@ namespace {
|
||||
glm::vec3 velocity{0.0f};// 待施初速(body 建好后注入)
|
||||
f32 age = 0.0f; // 存活秒数(寿命清理用)
|
||||
bool applied = false; // 初速已注入
|
||||
std::string modelPath; // 独占模型缓存键(回收时按路径卸载)
|
||||
};
|
||||
|
||||
int m_Count = 30;
|
||||
@@ -485,6 +531,9 @@ namespace {
|
||||
|
||||
bool m_Fired = false; // 一次性触发保护
|
||||
std::vector<Fragment> m_Fragments; // 活碎片簿记
|
||||
// 待卸载模型路径(碎片销毁的下一帧执行——Destroy 帧尾延迟,
|
||||
// 本帧渲染收集仍引用模型,同帧卸载会渲染悬空指针)
|
||||
std::vector<std::string> m_PendingUnload;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user