11using namespace Tempest;
17 return uint64_t(decl.
ppsValue*1000.f);
23 uint64_t emitted0 = uint64_t(pps*
float(time0)/1000.f);
24 uint64_t emitted1 = uint64_t(pps*
float(time1)/1000.f);
25 return emitted1-emitted0;
28float PfxBucket::ParState::lifeTime()
const {
29 return 1.f-life/float(maxLife);
32std::mt19937 PfxBucket::rndEngine;
34bool PfxBucket::Draw::isEmpty()
const {
35 return (pfxGpu.byteSize()==0);
39 :decl(decl), parent(parent), visual(visual) {
41 uint64_t pps = uint64_t(std::ceil(
decl.
maxPps()));
42 uint64_t reserve = (lt*pps+1000-1)/1000;
43 blockSize = size_t(reserve);
48 auto& item = this->item[i];
63 auto& item = this->itemTrl[i];
83 return impl.size()==0;
86size_t PfxBucket::allocBlock() {
88 forceUpdate[i] =
true;
90 for(
size_t i=0;i<block.size();++i) {
91 if(!block[i].allocated) {
92 block[i].allocated =
true;
93 block[i].timeTotal = 0;
100 Block& b = block.back();
102 b.offset = particles.size();
105 particles.resize(particles.size()+blockSize);
106 pfxCpu .resize(particles.size());
108 for(
size_t i=0; i<blockSize; ++i)
109 particles[b.offset+i].life = 0;
110 return block.size()-1;
113void PfxBucket::freeBlock(
size_t& i) {
119 pfxCpu[b.offset].size = Vec3();
125PfxBucket::Block& PfxBucket::getBlock(ImplEmitter &e) {
126 if(e.block==
size_t(-1)) {
127 e.block = allocBlock();
128 auto& p = block[e.block];
131 return block[e.block];
134PfxBucket::Block& PfxBucket::getBlock(
PfxEmitter& e) {
135 return getBlock(impl[e.id]);
139 for(
size_t i=0; i<impl.size(); ++i) {
147 auto& e = impl.back();
148 e.block = size_t(-1);
151 forceUpdate[i] =
true;
153 return impl.size()-1;
158 if(v.block!=
size_t(-1)) {
159 auto& b = getBlock(v);
167 forceUpdate[i] =
true;
173bool PfxBucket::shrink() {
174 while(impl.size()>0) {
175 auto& b = impl.back();
180 while(block.size()>0) {
181 auto& b = block.back();
186 if(particles.size()!=block.size()*blockSize) {
187 particles.resize(block.size()*blockSize);
188 pfxCpu .resize(particles.size());
194float PfxBucket::randf() {
195 return float(rndEngine()%10000)/10000.f;
198float PfxBucket::randf(
float base,
float var) {
199 return (2.f*randf()-1.f)*var + base;
202void PfxBucket::init(PfxBucket::Block& block, ImplEmitter& emitter,
size_t particle) {
203 auto& p = particles[particle];
216 p.pos = Vec3(at,at,at);
221 p.pos = Vec3(randf()*2.f-1.f,
227 p.pos = Vec3(randf()*2.f-1.f,
235 float theta = float(2.0*M_PI)*randf();
236 float phi = std::acos(1.f - 2.f * randf());
237 p.pos = Vec3(std::sin(phi) * std::cos(theta),
238 std::sin(phi) * std::sin(theta),
246 float a = float(2.0*M_PI)*randf();
247 p.pos = Vec3(std::sin(a),
252 p.pos = p.pos*std::sqrt(randf());
257 auto mesh = (emitter.mesh!=
nullptr) ? emitter.mesh :
decl.shpMesh;
258 auto pose = (emitter.mesh!=
nullptr) ? emitter.pose : nullptr;
260 auto pos = mesh->randCoord(randf(),pose);
262 p.pos = emitter.direction[0]*pos.x +
263 emitter.direction[1]*pos.y +
264 emitter.direction[2]*pos.z;
294 float dy = 1.f - 2.f * randf();
295 float sn = std::sqrt(1-dy*dy);
296 float theta = float(2.0*M_PI)*randf();
297 float dx = sn * std::cos(theta);
298 float dz = sn * std::sin(theta);
300 p.dir = Vec3(dx,dy,dz);
313 float head = (90+randf(
decl.
dirAngleHead,dirAngleHeadVar))*
float(M_PI)/180.f;
316 float dx = std::cos(elev) * std::cos(head);
317 float dy = std::sin(elev);
318 float dz = std::cos(elev) * std::sin(head);
323 p.dir = emitter.direction[0]*dx +
324 emitter.direction[1]*dy +
325 emitter.direction[2]*dz;
329 p.dir = Vec3(dx,dy,dz);
336 Vec3 targetPos = emitter.pos;
343 if(emitter.targetNpc!=
nullptr) {
345 auto mt = emitter.targetNpc->transform();
346 mt.project(targetPos);
349 targetPos = emitter.pos;
354 p.dir += targetPos - (emitter.pos+p.pos);
359 p.pos += emitter.pos;
361 auto l = p.dir.length();
364 p.dir = p.dir*velocity/l;
368void PfxBucket::finalize(
size_t particle) {
369 particles[particle] = {};
370 pfxCpu [particle] = {};
373void PfxBucket::tick(Block& sys, ImplEmitter& emitter,
size_t particle, uint64_t dt) {
374 ParState& ps = particles[particle+sys.offset];
381 finalize(particle+sys.offset);
385 const float dtF = float(dt);
388 ps.life = uint16_t(ps.life-dt);
389 ps.pos += ps.dir*dtF;
393 tickTrail(ps,emitter,dt);
396void PfxBucket::tickTrail(ParState& ps, ImplEmitter& emitter, uint64_t dt) {
397 for(
auto& i:ps.trail)
402 tx.pos = ps.pos + emitter.pos;
else
405 if(ps.trail.size()==0) {
406 ps.trail.push_back(tx);
408 else if(ps.trail.back().pos!=tx.pos) {
409 bool extrude =
false;
410 if(
false && ps.trail.size()>1) {
411 auto u = tx.pos - ps.trail[ps.trail.size()-2].pos;
412 auto v = ps.trail.back().pos - ps.trail[ps.trail.size()-2].pos;
413 if(std::abs(Vec3::dotProduct(u,v)-u.length()*v.length()) < 0.001f)
417 ps.trail.back() = tx;
else
418 ps.trail.push_back(tx);
421 ps.trail.back().time = 0;
424 for(
size_t rm=0; rm<=ps.trail.size(); ++rm) {
425 if(rm==ps.trail.size() || ps.trail[rm].time<maxTrlTime) {
426 ps.trail.erase(ps.trail.begin(),ps.trail.begin()+
int(rm));
434 implTickDecals(dt,viewPos);
437 implTickCommon(dt,viewPos);
440void PfxBucket::implTickCommon(uint64_t dt,
const Vec3& viewPos) {
441 bool doShrink =
false;
442 for(
auto& emitter:impl) {
446 const auto dp = emitter.pos-viewPos;
451 auto& e = *emitter.next;
452 e.setPosition(emitter.pos.x,emitter.pos.y,emitter.pos.z);
454 e.setLooped(emitter.isLoop);
457 if(emitter.waitforNext>=dt)
458 emitter.waitforNext-=dt;
460 if(emitter.block!=
size_t(-1)) {
461 auto& p = getBlock(emitter);
463 for(
size_t i=0;i<blockSize;++i)
464 tick(p,emitter,i,dt);
465 if(p.count==0 && (emitter.st==
S_Fade || !nearby)) {
467 freeBlock(emitter.block);
476 if(emitter.st==
S_Active && nearby) {
477 auto& p = getBlock(emitter);
478 auto dE =
ppsDiff(
decl,emitter.isLoop,p.timeTotal,p.timeTotal+dt);
479 tickEmit(p,emitter,dE);
482 if(emitter.block!=
size_t(-1)) {
483 auto& p = getBlock(emitter);
492void PfxBucket::implTickDecals(uint64_t,
const Vec3&) {
493 for(
auto& emitter:impl) {
497 auto& p = getBlock(emitter);
500 tickEmit(p,emitter,1);
503 for(
size_t i=0; i<blockSize; ++i)
504 particles[p.offset+i].life = 0;
506 freeBlock(emitter.block);
512void PfxBucket::tickEmit(Block& p, ImplEmitter& emitter, uint64_t emited) {
514 for(
size_t id=1; emited>0; ++id) {
515 const size_t i =
id%blockSize;
516 ParState& ps = particles[i+p.offset];
520 init(p,emitter,i+p.offset);
547 for(
size_t pId=0; pId<blockSize; ++pId) {
548 ParState& ps = particles[pId+p.offset];
549 auto& px = pfxCpu [pId+p.offset];
556 const float a = ps.lifeTime();
557 const Vec3 cl = colorS*(1.f-a) + colorE*a;
558 const float clA = visAlphaStart*(1.f-a) + visAlphaEnd*a;
560 const float scale = 1.f*(1.f-a) + a*visSizeEndScale;
561 const float szX = visSizeStart.x*scale;
562 const float szY = visSizeStart.y*scale;
563 const float szZ = 0.1f*((szX+szY)*0.5f);
573 color.r = uint8_t(cl.x*clA);
574 color.g = uint8_t(cl.y*clA);
575 color.b = uint8_t(cl.z*clA);
576 color.a = uint8_t(255);
578 color.r = uint8_t(cl.x);
579 color.g = uint8_t(cl.y);
580 color.b = uint8_t(cl.z);
581 color.a = uint8_t(clA*255);
584 std::memcpy(&colorU32,&color,4);
585 buildBilboard(px,p,ps, colorU32, szX,szY,szZ);
590void PfxBucket::buildSsboTrails() {
594 trlCpu.reserve(trlCpu.size());
597 for(
auto& p:particles) {
603 float maxT = float(std::min(maxTrlTime,p.trail[0].time));
604 for(
size_t r=1; r<p.trail.size(); ++r) {
606 buildTrailSegment(st,p.trail[r-1],p.trail[r],maxT);
607 trlCpu.push_back(st);
612void PfxBucket::buildBilboard(PfxState& v,
const Block& p,
const ParState& ps,
const uint32_t color,
613 float szX,
float szY,
float szZ) {
615 v.pos = ps.pos + p.pos;
else
618 v.size = Vec3(szX,szY,szZ);
624 v.bits0 |= uint32_t(0) << 3;
629void PfxBucket::buildTrailSegment(PfxState& v,
const Trail& a,
const Trail& b,
float maxT) {
630 float tA = 1.f - float(a.time)/maxT;
631 float tB = 1.f - float(b.time)/maxT;
633 uint32_t clA = mkTrailColor(tA);
634 uint32_t clB = mkTrailColor(tB);
639 v.bits0 = uint32_t(1) << 3;
640 v.dir = b.pos - a.pos;
644uint32_t PfxBucket::mkTrailColor(
float clA)
const {
652 clA = std::max(0.f, std::min(clA, 1.f));
655 color.r = uint8_t(255*clA);
656 color.g = uint8_t(255*clA);
657 color.b = uint8_t(255*clA);
658 color.a = uint8_t(255);
664 color.a = uint8_t(clA*255);
667 std::memcpy(&cl,&color,
sizeof(cl));
674 if(item[fId].pMain!=
nullptr || item[fId].pShadow!=
nullptr) {
676 auto& ssbo = item[fId].pfxGpu;
678 auto heap =
decl.
isDecal() ? BufferHeap::Device : BufferHeap::Upload;
679 ssbo = device.ssbo(heap,pfxCpu);
683 forceUpdate[fId] =
false;
688 if(itemTrl[fId].pMain!=
nullptr || itemTrl[fId].pShadow!=
nullptr) {
689 auto& ssbo = itemTrl[fId].pfxGpu;
691 ssbo = device.ssbo(BufferHeap::Upload, trlCpu);
708 drawCommon(cmd, scene, item[fId], view, i,
false);
709 drawCommon(cmd, scene, itemTrl[fId], view, i,
true);
720void PfxBucket::drawCommon(Tempest::Encoder<Tempest::CommandBuffer>& cmd,
const SceneGlobals& scene,
const Draw& itm,
722 if(itm.pfxGpu.isEmpty())
728 const RenderPipeline* pso =
nullptr;
751 else if(mat.hasFrameAnimation()) {
752 auto frame = size_t((itm.timeShift+scene.
tickCount)/mat.texAniFPSInv);
753 auto t = mat.frames[frame%mat.frames.size()];
754 cmd.setBinding(L_Diffuse, *t);
757 cmd.setBinding(L_Diffuse, *mat.tex);
759 cmd.setBinding(L_Sampler, smp);
768 cmd.setBinding(L_SceneClr, *scene.
sceneColor, Sampler::bilinear(ClampMode::MirroredRepeat));
769 cmd.setBinding(L_GDepth, *scene.
sceneDepth, Sampler::nearest (ClampMode::MirroredRepeat));
772 cmd.setBinding(L_Scene, scene.
uboGlobal[view]);
773 cmd.setBinding(L_Pfx, itm.pfxGpu);
774 cmd.setPipeline(*pso);
775 cmd.draw(
nullptr, 0, 6, 0, itm.pfxGpu.byteSize()/
sizeof(PfxState));
const Tempest::Texture2d * tex
const Tempest::Texture2d * trlTexture
Tempest::Vec3 visTexColorStart
const ParticleFx * ppsCreateEm
Tempest::Vec3 visTexColorEnd
uint64_t maxLifetime() const
Tempest::Vec2 visSizeStart
Tempest::Vec3 shpOffsetVec
float ppsScale(uint64_t time) const
Orientation visOrientation
float shpScale(uint64_t time) const
Tempest::Vec3 dirModeTargetPos
void preFrameUpdate(const SceneGlobals &scene, uint8_t fId)
void tick(uint64_t dt, const Tempest::Vec3 &viewPos)
void drawGBuffer(Tempest::Encoder< Tempest::CommandBuffer > &cmd, const SceneGlobals &scene, uint8_t fId)
void freeEmitter(size_t &id)
PfxBucket(const ParticleFx &decl, PfxObjects &parent, const SceneGlobals &scene, VisualObjects &visual)
void drawShadow(Tempest::Encoder< Tempest::CommandBuffer > &cmd, const SceneGlobals &scene, uint8_t fId, int layer)
void drawTranslucent(Tempest::Encoder< Tempest::CommandBuffer > &cmd, const SceneGlobals &scene, uint8_t fId)
static constexpr const float viewRage
static const Tempest::Sampler & shadowSampler()
static Tempest::Device & device()
const Tempest::Texture2d * shadowMap[2]
Tempest::StorageBuffer uboGlobal[V_Count]
const Tempest::Texture2d * sceneColor
static bool isShadowView(VisCamera v)
const Tempest::Texture2d * sceneDepth
const Tempest::RenderPipeline * materialPipeline(const Material &desc, DrawCommands::Type t, PipelineType pt, bool bindless) const
static uint64_t ppsDiff(const ParticleFx &decl, bool loop, uint64_t time0, uint64_t time1)