OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
pfxbucket.cpp
Go to the documentation of this file.
1#include "pfxbucket.h"
2
3#include <cassert>
4
6#include "graphics/shaders.h"
7#include "pfxobjects.h"
8#include "particlefx.h"
9#include "world/objects/npc.h"
10
11using namespace Tempest;
12
13static uint64_t ppsDiff(const ParticleFx& decl, bool loop, uint64_t time0, uint64_t time1) {
14 if(time1<=time0)
15 return 0;
16 if(decl.prefferedTime==0 && time0==0 && !loop)
17 return uint64_t(decl.ppsValue*1000.f);
18 if(!loop) {
19 time0 = std::min(decl.prefferedTime,time0);
20 time1 = std::min(decl.prefferedTime,time1);
21 }
22 const float pps = decl.ppsScale(time1)*decl.ppsValue;
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;
26 }
27
28float PfxBucket::ParState::lifeTime() const {
29 return 1.f-life/float(maxLife);
30 }
31
32std::mt19937 PfxBucket::rndEngine;
33
34bool PfxBucket::Draw::isEmpty() const {
35 return (pfxGpu.byteSize()==0);
36 }
37
38PfxBucket::PfxBucket(const ParticleFx &decl, PfxObjects& parent, const SceneGlobals& scene, VisualObjects& visual)
39 :decl(decl), parent(parent), visual(visual) {
40 uint64_t lt = decl.maxLifetime();
41 uint64_t pps = uint64_t(std::ceil(decl.maxPps()));
42 uint64_t reserve = (lt*pps+1000-1)/1000;
43 blockSize = size_t(reserve);
44 if(blockSize==0)
45 blockSize=1;
46
47 for(size_t i=0; i<Resources::MaxFramesInFlight; ++i) {
48 auto& item = this->item[i];
49 if(decl.visMaterial.tex==nullptr)
50 continue;
51
54 }
55
56 if(decl.hasTrails()) {
57 maxTrlTime = uint64_t(decl.trlFadeSpeed*1000.f);
58
60 mat.tex = decl.trlTexture;
61
62 for(size_t i=0; i<Resources::MaxFramesInFlight; ++i) {
63 auto& item = this->itemTrl[i];
64 if(mat.tex==nullptr)
65 continue;
66
69 }
70 }
71 }
72
75
76bool PfxBucket::isEmpty() const {
77 for(size_t i=0; i<Resources::MaxFramesInFlight; ++i) {
78 if(!item[i].isEmpty())
79 return false;
80 //if(!itemTrl[i].isEmpty())
81 // return false;
82 }
83 return impl.size()==0;
84 }
85
86size_t PfxBucket::allocBlock() {
87 for(size_t i=0; i<Resources::MaxFramesInFlight; ++i)
88 forceUpdate[i] = true;
89
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;
94 return i;
95 }
96 }
97
98 block.emplace_back();
99
100 Block& b = block.back();
101 b.allocated = true;
102 b.offset = particles.size();
103 b.timeTotal = 0;
104
105 particles.resize(particles.size()+blockSize);
106 pfxCpu .resize(particles.size());
107
108 for(size_t i=0; i<blockSize; ++i)
109 particles[b.offset+i].life = 0;
110 return block.size()-1;
111 }
112
113void PfxBucket::freeBlock(size_t& i) {
114 if(i==size_t(-1))
115 return;
116 auto& b = block[i];
117 assert(b.count==0);
118
119 pfxCpu[b.offset].size = Vec3();
120
121 b.allocated = false;
122 i = size_t(-1);
123 }
124
125PfxBucket::Block& PfxBucket::getBlock(ImplEmitter &e) {
126 if(e.block==size_t(-1)) {
127 e.block = allocBlock();
128 auto& p = block[e.block];
129 p.pos = e.pos;
130 }
131 return block[e.block];
132 }
133
134PfxBucket::Block& PfxBucket::getBlock(PfxEmitter& e) {
135 return getBlock(impl[e.id]);
136 }
137
139 for(size_t i=0; i<impl.size(); ++i) {
140 auto& b = impl[i];
141 if(b.st==S_Free) {
142 b.st = S_Inactive;
143 return i;
144 }
145 }
146 impl.emplace_back();
147 auto& e = impl.back();
148 e.block = size_t(-1); // no backup memory
149 e.st = S_Inactive;
150 for(size_t i=0; i<Resources::MaxFramesInFlight; ++i)
151 forceUpdate[i] = true;
152
153 return impl.size()-1;
154 }
155
156void PfxBucket::freeEmitter(size_t& id) {
157 auto& v = impl[id];
158 if(v.block!=size_t(-1)) {
159 auto& b = getBlock(v);
160 v.st = b.count==0 ? S_Free : S_Fade;
161 if(b.count==0)
162 freeBlock(v.block);
163 } else {
164 v.st = S_Free;
165 }
166 for(size_t i=0; i<Resources::MaxFramesInFlight; ++i)
167 forceUpdate[i] = true;
168 v.next.reset();
169 id = size_t(-1);
170 shrink();
171 }
172
173bool PfxBucket::shrink() {
174 while(impl.size()>0) {
175 auto& b = impl.back();
176 if(b.st!=S_Free)
177 break;
178 impl.pop_back();
179 }
180 while(block.size()>0) {
181 auto& b = block.back();
182 if(b.allocated)
183 break;
184 block.pop_back();
185 }
186 if(particles.size()!=block.size()*blockSize) {
187 particles.resize(block.size()*blockSize);
188 pfxCpu .resize(particles.size());
189 return true;
190 }
191 return false;
192 }
193
194float PfxBucket::randf() {
195 return float(rndEngine()%10000)/10000.f;
196 }
197
198float PfxBucket::randf(float base, float var) {
199 return (2.f*randf()-1.f)*var + base;
200 }
201
202void PfxBucket::init(PfxBucket::Block& block, ImplEmitter& emitter, size_t particle) {
203 auto& p = particles[particle];
204
205 p.life = uint16_t(randf(decl.lspPartAvg,decl.lspPartVar));
206 p.maxLife = p.life;
207
208 // TODO: pfx.shpDistribType, pfx.shpDistribWalkSpeed;
209 switch(decl.shpType) {
211 p.pos = Vec3();
212 break;
213 }
215 float at = randf();
216 p.pos = Vec3(at,at,at);
217 break;
218 }
220 if(decl.shpIsVolume) {
221 p.pos = Vec3(randf()*2.f-1.f,
222 randf()*2.f-1.f,
223 randf()*2.f-1.f);
224 p.pos*=0.5;
225 } else {
226 // TODO
227 p.pos = Vec3(randf()*2.f-1.f,
228 randf()*2.f-1.f,
229 randf()*2.f-1.f);
230 p.pos*=0.5;
231 }
232 break;
233 }
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),
239 std::cos(phi));
240 //p.pos*=0.5;
241 if(decl.shpIsVolume)
242 p.pos*=randf();
243 break;
244 }
246 float a = float(2.0*M_PI)*randf();
247 p.pos = Vec3(std::sin(a),
248 0,
249 std::cos(a));
250 //p.pos*=0.5;
251 if(decl.shpIsVolume)
252 p.pos = p.pos*std::sqrt(randf());
253 break;
254 }
256 p.pos = Vec3();
257 auto mesh = (emitter.mesh!=nullptr) ? emitter.mesh : decl.shpMesh;
258 auto pose = (emitter.mesh!=nullptr) ? emitter.pose : nullptr;
259 if(mesh!=nullptr) {
260 auto pos = mesh->randCoord(randf(),pose);
261 pos -= emitter.pos;
262 p.pos = emitter.direction[0]*pos.x +
263 emitter.direction[1]*pos.y +
264 emitter.direction[2]*pos.z;
265 }
266 break;
267 }
268 }
269
272 Vec3 dim = decl.shpDim*decl.shpScale(block.timeTotal);
273 p.pos.x*=dim.x;
274 p.pos.y*=dim.y;
275 p.pos.z*=dim.z;
276 }
277
278 switch(decl.shpFOR) {
281 p.pos += emitter.direction[0]*decl.shpOffsetVec.x +
282 emitter.direction[1]*decl.shpOffsetVec.y +
283 emitter.direction[2]*decl.shpOffsetVec.z;
284 break;
285 }
287 p.pos += decl.shpOffsetVec;
288 break;
289 }
290 }
291
292 switch(decl.dirMode) {
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);
299
300 p.dir = Vec3(dx,dy,dz);
301 break;
302 }
304 float dirAngleHeadVar = decl.dirAngleHeadVar;
305 float dirAngleElevVar = decl.dirAngleElevVar;
306
307 // HACK: STARGATE_PARTICLES
308 if(decl.dirAngleHeadVar>=180)
309 dirAngleHeadVar = 0;
310 if(decl.dirAngleElevVar>=180 )
311 dirAngleElevVar = 0;
312
313 float head = (90+randf(decl.dirAngleHead,dirAngleHeadVar))*float(M_PI)/180.f;
314 float elev = ( randf(decl.dirAngleElev,dirAngleElevVar))*float(M_PI)/180.f;
315
316 float dx = std::cos(elev) * std::cos(head);
317 float dy = std::sin(elev);
318 float dz = std::cos(elev) * std::sin(head);
319
320 switch(decl.dirFOR) {
323 p.dir = emitter.direction[0]*dx +
324 emitter.direction[1]*dy +
325 emitter.direction[2]*dz;
326 break;
327 }
329 p.dir = Vec3(dx,dy,dz);
330 break;
331 }
332 }
333 break;
334 }
336 Vec3 targetPos = emitter.pos;
337 switch(decl.dirModeTargetFOR) {
339 targetPos = decl.dirModeTargetPos;
340 break;
343 if(emitter.targetNpc!=nullptr) {
344 // MFX_WINDFIST_CAST
345 auto mt = emitter.targetNpc->transform();
346 mt.project(targetPos);
347 } else {
348 // IRRLICHT_DIE
349 targetPos = emitter.pos;
350 }
351 break;
352 }
353 }
354 p.dir += targetPos - (emitter.pos+p.pos);
355 break;
356 }
357
359 p.pos += emitter.pos;
360
361 auto l = p.dir.length();
362 if(l!=0.f) {
363 float velocity = randf(decl.velAvg,decl.velVar);
364 p.dir = p.dir*velocity/l;
365 }
366 }
367
368void PfxBucket::finalize(size_t particle) {
369 particles[particle] = {};
370 pfxCpu [particle] = {};
371 }
372
373void PfxBucket::tick(Block& sys, ImplEmitter& emitter, size_t particle, uint64_t dt) {
374 ParState& ps = particles[particle+sys.offset];
375 if(ps.life==0)
376 return;
377
378 if(ps.life<=dt){
379 ps.life = 0;
380 sys.count--;
381 finalize(particle+sys.offset);
382 return;
383 }
384
385 const float dtF = float(dt);
386
387 // eval particle
388 ps.life = uint16_t(ps.life-dt);
389 ps.pos += ps.dir*dtF;
390 ps.dir += decl.flyGravity*dtF;
391
392 if(maxTrlTime!=0)
393 tickTrail(ps,emitter,dt);
394 }
395
396void PfxBucket::tickTrail(ParState& ps, ImplEmitter& emitter, uint64_t dt) {
397 for(auto& i:ps.trail)
398 i.time+=dt;
399
400 Trail tx;
402 tx.pos = ps.pos + emitter.pos; else
403 tx.pos = ps.pos;
404
405 if(ps.trail.size()==0) {
406 ps.trail.push_back(tx);
407 }
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)
414 extrude = true;
415 }
416 if(extrude)
417 ps.trail.back() = tx; else
418 ps.trail.push_back(tx);
419 }
420 else {
421 ps.trail.back().time = 0;
422 }
423
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));
427 break;
428 }
429 }
430 }
431
432void PfxBucket::tick(uint64_t dt, const Vec3& viewPos) {
433 if(decl.isDecal()) {
434 implTickDecals(dt,viewPos);
435 return;
436 }
437 implTickCommon(dt,viewPos);
438 }
439
440void PfxBucket::implTickCommon(uint64_t dt, const Vec3& viewPos) {
441 bool doShrink = false;
442 for(auto& emitter:impl) {
443 if(emitter.st==S_Free)
444 continue;
445
446 const auto dp = emitter.pos-viewPos;
447 const bool nearby = (dp.quadLength()<PfxObjects::viewRage*PfxObjects::viewRage);
448
449 if(emitter.next==nullptr && decl.ppsCreateEm!=nullptr && emitter.waitforNext<dt && emitter.st==S_Active) {
450 emitter.next.reset(new PfxEmitter(parent,decl.ppsCreateEm));
451 auto& e = *emitter.next;
452 e.setPosition(emitter.pos.x,emitter.pos.y,emitter.pos.z);
453 e.setActive(true);
454 e.setLooped(emitter.isLoop);
455 }
456
457 if(emitter.waitforNext>=dt)
458 emitter.waitforNext-=dt;
459
460 if(emitter.block!=size_t(-1)) {
461 auto& p = getBlock(emitter);
462 if(p.count>0) {
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)) {
466 // free mem
467 freeBlock(emitter.block);
468 if(emitter.st==S_Fade)
469 emitter.st = S_Free;
470 doShrink = true;
471 continue;
472 }
473 }
474 }
475
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);
480 }
481
482 if(emitter.block!=size_t(-1)) {
483 auto& p = getBlock(emitter);
484 p.timeTotal+=dt;
485 }
486 }
487
488 if(doShrink)
489 shrink();
490 }
491
492void PfxBucket::implTickDecals(uint64_t, const Vec3&) {
493 for(auto& emitter:impl) {
494 if(emitter.st==S_Free)
495 continue;
496
497 auto& p = getBlock(emitter);
498 if(emitter.st==S_Active || emitter.st==S_Inactive) {
499 if(p.count==0)
500 tickEmit(p,emitter,1);
501 } else
502 if(emitter.st==S_Fade) {
503 for(size_t i=0; i<blockSize; ++i)
504 particles[p.offset+i].life = 0;
505 p.count = 0;
506 freeBlock(emitter.block);
507 emitter.st = S_Free;
508 }
509 }
510 }
511
512void PfxBucket::tickEmit(Block& p, ImplEmitter& emitter, uint64_t emited) {
513 size_t lastI = 0;
514 for(size_t id=1; emited>0; ++id) {
515 const size_t i = id%blockSize;
516 ParState& ps = particles[i+p.offset];
517 if(ps.life==0) { // free slot
518 --emited;
519 lastI = i;
520 init(p,emitter,i+p.offset);
521 if(ps.life==0)
522 continue;
523 p.count++;
524 } else {
525 // out of slots
526 if(lastI==i)
527 return;
528 }
529 }
530 }
531
533 buildSsboTrails();
534
535 auto colorS = decl.visTexColorStart;
536 auto colorE = decl.visTexColorEnd;
537 auto visSizeStart = decl.visSizeStart;
538 auto visSizeEndScale = decl.visSizeEndScale;
539 auto visAlphaStart = decl.visAlphaStart;
540 auto visAlphaEnd = decl.visAlphaEnd;
541 auto visAlphaFunc = decl.visMaterial.alpha;
542
543 for(auto& p:block) {
544 if(p.count==0)
545 continue;
546
547 for(size_t pId=0; pId<blockSize; ++pId) {
548 ParState& ps = particles[pId+p.offset];
549 auto& px = pfxCpu [pId+p.offset];
550
551 if(ps.life==0) {
552 px.size = Vec3();
553 continue;
554 }
555
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;
559
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);
564
565 struct Color {
566 uint8_t r=255;
567 uint8_t g=255;
568 uint8_t b=255;
569 uint8_t a=255;
570 } color;
571
572 if(visAlphaFunc==Material::AlphaFunc::AdditiveLight) {
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);
577 } else {
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);
582 }
583 uint32_t colorU32;
584 std::memcpy(&colorU32,&color,4);
585 buildBilboard(px,p,ps, colorU32, szX,szY,szZ);
586 }
587 }
588 }
589
590void PfxBucket::buildSsboTrails() {
591 if(!decl.hasTrails())
592 return;
593
594 trlCpu.reserve(trlCpu.size());
595 trlCpu.clear();
596
597 for(auto& p:particles) {
598 if(p.life==0)
599 continue;
600 if(p.trail.size()<2)
601 continue;
602
603 float maxT = float(std::min(maxTrlTime,p.trail[0].time));
604 for(size_t r=1; r<p.trail.size(); ++r) {
605 PfxState st;
606 buildTrailSegment(st,p.trail[r-1],p.trail[r],maxT);
607 trlCpu.push_back(st);
608 }
609 }
610 }
611
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
616 v.pos = ps.pos;
617
618 v.size = Vec3(szX,szY,szZ);
619 v.color = color;
620 v.bits0 = 0;
621 v.bits0 |= uint32_t(decl.visZBias ? 1 : 0);
622 v.bits0 |= uint32_t(decl.visTexIsQuadPoly ? 1 : 0) << 1;
623 v.bits0 |= uint32_t(decl.visYawAlign ? 1 : 0) << 2;
624 v.bits0 |= uint32_t(0) << 3; // TODO: trails
625 v.bits0 |= uint32_t(decl.visOrientation) << 4;
626 v.dir = ps.dir;
627 }
628
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;
632
633 uint32_t clA = mkTrailColor(tA);
634 uint32_t clB = mkTrailColor(tB);
635
636 v.pos = a.pos;
637 v.color = clA;
638 v.size = Vec3(decl.trlWidth,tA,tB);
639 v.bits0 = uint32_t(1) << 3;
640 v.dir = b.pos - a.pos;
641 v.colorB = clB;
642 }
643
644uint32_t PfxBucket::mkTrailColor(float clA) const {
645 struct Color {
646 uint8_t r=255;
647 uint8_t g=255;
648 uint8_t b=255;
649 uint8_t a=255;
650 } color;
651
652 clA = std::max(0.f, std::min(clA, 1.f));
653
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);
659 }
661 color.r = 255;
662 color.g = 255;
663 color.b = 255;
664 color.a = uint8_t(clA*255);
665 }
666 uint32_t cl;
667 std::memcpy(&cl,&color,sizeof(cl));
668 return cl;
669 }
670
671void PfxBucket::preFrameUpdate(const SceneGlobals& scene, uint8_t fId) {
672 auto& device = Resources::device();
673
674 if(item[fId].pMain!=nullptr || item[fId].pShadow!=nullptr) {
675 // hidden staging under the hood
676 auto& ssbo = item[fId].pfxGpu;
677 if(pfxCpu.size()*sizeof(PfxBucket::PfxState)!=ssbo.byteSize()) {
678 auto heap = decl.isDecal() ? BufferHeap::Device : BufferHeap::Upload;
679 ssbo = device.ssbo(heap,pfxCpu);
680 } else {
681 if(!decl.isDecal() || forceUpdate[fId]) {
682 ssbo.update(pfxCpu);
683 forceUpdate[fId] = false;
684 }
685 }
686 }
687
688 if(itemTrl[fId].pMain!=nullptr || itemTrl[fId].pShadow!=nullptr) {
689 auto& ssbo = itemTrl[fId].pfxGpu;
690 if(trlCpu.size()*sizeof(PfxBucket::PfxState)!=ssbo.byteSize()) {
691 ssbo = device.ssbo(BufferHeap::Upload, trlCpu);
692 } else {
693 ssbo.update(trlCpu);
694 }
695 }
696 }
697
698void PfxBucket::drawGBuffer(Tempest::Encoder<Tempest::CommandBuffer>& cmd, const SceneGlobals& scene, uint8_t fId) {
699 for(auto i:{Material::Solid, Material::AlphaTest}) {
700 drawCommon(cmd, scene, item[fId], SceneGlobals::V_Main, i, false);
701 drawCommon(cmd, scene, itemTrl[fId], SceneGlobals::V_Main, i, true);
702 }
703 }
704
705void PfxBucket::drawShadow(Tempest::Encoder<Tempest::CommandBuffer>& cmd, const SceneGlobals& scene, uint8_t fId, int layer) {
707 for(auto i:{Material::Solid, Material::AlphaTest}) {
708 drawCommon(cmd, scene, item[fId], view, i, false);
709 drawCommon(cmd, scene, itemTrl[fId], view, i, true);
710 }
711 }
712
713void PfxBucket::drawTranslucent(Tempest::Encoder<Tempest::CommandBuffer>& cmd, const SceneGlobals& scene, uint8_t fId) {
715 drawCommon(cmd, scene, item[fId], SceneGlobals::V_Main, i, false);
716 drawCommon(cmd, scene, itemTrl[fId], SceneGlobals::V_Main, i, true);
717 }
718 }
719
720void PfxBucket::drawCommon(Tempest::Encoder<Tempest::CommandBuffer>& cmd, const SceneGlobals& scene, const Draw& itm,
721 SceneGlobals::VisCamera view, Material::AlphaFunc func, bool trl) {
722 if(itm.pfxGpu.isEmpty())
723 return;
724
725 if(decl.visMaterial.alpha!=func)
726 return;
727
728 const RenderPipeline* pso = nullptr;
729 switch(view) {
733 pso = itm.pShadow;
734 break;
736 pso = itm.pMain;
737 break;
740 break;
741 }
742 if(pso==nullptr)
743 return;
744
745 auto& mat = decl.visMaterial;
746 if(view==SceneGlobals::V_Main || mat.isTextureInShadowPass()) {
747 auto smp = SceneGlobals::isShadowView(SceneGlobals::VisCamera(view)) ? Sampler::trillinear() : Sampler::anisotrophy();
748 if(trl) {
749 cmd.setBinding(L_Diffuse, *decl.trlTexture);
750 }
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);
755 }
756 else {
757 cmd.setBinding(L_Diffuse, *mat.tex);
758 }
759 cmd.setBinding(L_Sampler, smp);
760 }
761
762 if(view==SceneGlobals::V_Main && mat.isShadowmapRequired()) {
763 cmd.setBinding(L_Shadow0, *scene.shadowMap[0],Resources::shadowSampler());
764 cmd.setBinding(L_Shadow1, *scene.shadowMap[1],Resources::shadowSampler());
765 }
766
767 if(view==SceneGlobals::V_Main && mat.isSceneInfoRequired()) {
768 cmd.setBinding(L_SceneClr, *scene.sceneColor, Sampler::bilinear(ClampMode::MirroredRepeat));
769 cmd.setBinding(L_GDepth, *scene.sceneDepth, Sampler::nearest (ClampMode::MirroredRepeat));
770 }
771
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));
776 }
@ AdditiveLight
Definition material.h:25
@ Multiply2
Definition material.h:23
@ Multiply
Definition material.h:22
@ Transparent
Definition material.h:24
@ AlphaTest
Definition material.h:19
const Tempest::Texture2d * tex
Definition material.h:28
AlphaFunc alpha
Definition material.h:30
bool visTexIsQuadPoly
Definition particlefx.h:100
Frame shpFOR
Definition particlefx.h:67
bool useEmittersFOR
Definition particlefx.h:123
const Tempest::Texture2d * trlTexture
Definition particlefx.h:112
float maxPps() const
float dirAngleHead
Definition particlefx.h:85
bool visZBias
Definition particlefx.h:110
float lspPartAvg
Definition particlefx.h:92
float dirAngleElevVar
Definition particlefx.h:88
Tempest::Vec3 visTexColorStart
Definition particlefx.h:103
const ParticleFx * ppsCreateEm
Definition particlefx.h:63
Material visMaterial
Definition particlefx.h:98
float visAlphaStart
Definition particlefx.h:107
uint64_t prefferedTime
Definition particlefx.h:128
EmitterType shpType
Definition particlefx.h:66
Frame dirFOR
Definition particlefx.h:82
Tempest::Vec3 visTexColorEnd
Definition particlefx.h:104
float ppsValue
Definition particlefx.h:58
float dirAngleHeadVar
Definition particlefx.h:86
Frame dirModeTargetFOR
Definition particlefx.h:83
Tempest::Vec3 flyGravity
Definition particlefx.h:95
bool isDecal() const
float visSizeEndScale
Definition particlefx.h:106
bool shpIsVolume
Definition particlefx.h:71
uint64_t maxLifetime() const
Tempest::Vec3 shpDim
Definition particlefx.h:72
bool hasTrails() const
Tempest::Vec2 visSizeStart
Definition particlefx.h:105
float trlWidth
Definition particlefx.h:114
float velVar
Definition particlefx.h:91
Tempest::Vec3 shpOffsetVec
Definition particlefx.h:68
float ppsScale(uint64_t time) const
Orientation visOrientation
Definition particlefx.h:99
bool visYawAlign
Definition particlefx.h:109
float shpScale(uint64_t time) const
float velAvg
Definition particlefx.h:90
float trlFadeSpeed
Definition particlefx.h:113
float visAlphaEnd
Definition particlefx.h:108
float lspPartVar
Definition particlefx.h:93
Tempest::Vec3 dirModeTargetPos
Definition particlefx.h:84
float dirAngleElev
Definition particlefx.h:87
bool isEmpty() const
Definition pfxbucket.cpp:76
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 buildSsbo()
void freeEmitter(size_t &id)
PfxObjects & parent
Definition pfxbucket.h:54
PfxBucket(const ParticleFx &decl, PfxObjects &parent, const SceneGlobals &scene, VisualObjects &visual)
Definition pfxbucket.cpp:38
const ParticleFx & decl
Definition pfxbucket.h:53
void drawShadow(Tempest::Encoder< Tempest::CommandBuffer > &cmd, const SceneGlobals &scene, uint8_t fId, int layer)
size_t allocEmitter()
void drawTranslucent(Tempest::Encoder< Tempest::CommandBuffer > &cmd, const SceneGlobals &scene, uint8_t fId)
friend class PfxEmitter
Definition pfxbucket.h:170
static constexpr const float viewRage
Definition pfxobjects.h:22
@ MaxFramesInFlight
Definition resources.h:48
static const Tempest::Sampler & shadowSampler()
static Tempest::Device & device()
Definition resources.h:83
const Tempest::Texture2d * shadowMap[2]
Tempest::StorageBuffer uboGlobal[V_Count]
const Tempest::Texture2d * sceneColor
static bool isShadowView(VisCamera v)
uint64_t tickCount
const Tempest::Texture2d * sceneDepth
static Shaders & inst()
Definition shaders.cpp:39
const Tempest::RenderPipeline * materialPipeline(const Material &desc, DrawCommands::Type t, PipelineType pt, bool bindless) const
Definition shaders.cpp:338
@ T_Main
Definition shaders.h:24
@ T_Shadow
Definition shaders.h:22
static uint64_t ppsDiff(const ParticleFx &decl, bool loop, uint64_t time0, uint64_t time1)
Definition pfxbucket.cpp:13