From 284545b6285273d79a609d7d2c95e6f4c376b31c Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 22 Nov 2025 21:27:06 -0600 Subject: [PATCH 1/5] xDecal: Match full method --- src/SB/Core/x/xDecal.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/SB/Core/x/xDecal.h b/src/SB/Core/x/xDecal.h index f6343e3f1..4ea98f23d 100644 --- a/src/SB/Core/x/xDecal.h +++ b/src/SB/Core/x/xDecal.h @@ -105,6 +105,11 @@ struct xDecalEmitter void debug_update(F32 dt) { } + + bool full() const + { + return units.full(); + } }; void xDecalInit(); From 7c117bac0a80b679a27d8aa507230642b9ff2fa7 Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 22 Nov 2025 21:27:19 -0600 Subject: [PATCH 2/5] xDecal: Clean up iterator loop --- src/SB/Core/x/xDecal.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/SB/Core/x/xDecal.cpp b/src/SB/Core/x/xDecal.cpp index 2367b569d..10038c3f6 100644 --- a/src/SB/Core/x/xDecal.cpp +++ b/src/SB/Core/x/xDecal.cpp @@ -239,7 +239,7 @@ void xDecalEmitter::update(F32 dt) this->curve_index = 0; static_queue::iterator it = this->units.begin(); - for (; it != this->units.end(); ++it) + while (it != this->units.end()) { unit_data& unit = *it; unit.age += dage; @@ -264,6 +264,8 @@ void xDecalEmitter::update(F32 dt) } get_render_data(unit, scale, pool.color[0], pool.mat[0], pool.uv[0], pool.uv[1]); + + ++it; } pool.flush(); From 4a3bc5e19c7f80886f2ff7a071960d3e800beb22 Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 22 Nov 2025 21:27:32 -0600 Subject: [PATCH 3/5] xMath3: Add xMat3x3LookVec3 signature to header --- src/SB/Core/x/xMath3.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SB/Core/x/xMath3.h b/src/SB/Core/x/xMath3.h index 8918ca533..3ece8eae3 100644 --- a/src/SB/Core/x/xMath3.h +++ b/src/SB/Core/x/xMath3.h @@ -152,6 +152,7 @@ void xQuatConj(xQuat* o, const xQuat* q); void xQuatCopy(xQuat*, const xQuat*); void xMat3x3LookAt(xMat3x3* m, const xVec3* pos, const xVec3* at); F32 xMat3x3LookVec(xMat3x3* m, const xVec3* at); +F32 xMat3x3LookVec3(xMat3x3& m, const xVec3& at); void xBoxInitBoundOBB(xBox* o, const xBox* b, const xMat4x3* m); void xBoxUnion(xBox& a, const xBox& b, const xBox& c); void xMat3x3Scale(xMat3x3* m, const xVec3* s); From c441c19fbd21e7621b9e01aa8aee03566568b9f7 Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 22 Nov 2025 21:27:49 -0600 Subject: [PATCH 4/5] xLaserBolt: Header updates and matches --- src/SB/Core/x/xLaserBolt.h | 77 +++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/src/SB/Core/x/xLaserBolt.h b/src/SB/Core/x/xLaserBolt.h index 9d5703aba..d0b6690cb 100644 --- a/src/SB/Core/x/xLaserBolt.h +++ b/src/SB/Core/x/xLaserBolt.h @@ -7,6 +7,7 @@ #include "xMath2.h" #include "xMath3.h" #include "xParEmitter.h" +#include "xVec3.h" #include #include @@ -121,7 +122,11 @@ struct xLaserBoltEmitter RxObjSpace3DVertex* get_vert_buffer(S32& dat); void apply_damage(bolt& b); void reset_fx(fx_when_enum when); - + void update_fx(bolt& b, F32 prev_dist, F32 dt); + void emit_particle(effect_data& effect, bolt& b, F32 from_dist, F32 to_dist, F32 dt); + void emit_decal(effect_data& effect, bolt& b, F32 from_dist, F32 to_dist, F32 dt); + void emit_decal_dist(effect_data& effect, bolt& b, F32 from_dist, F32 to_dist, F32 dt); +; U32 visible() const; void debug_init(const char* texture_name) @@ -129,10 +134,80 @@ struct xLaserBoltEmitter } + void emit_fx(effect_data& effect, bolt& b, F32 from_dist, F32 to_dist, F32 dt) + { + switch (effect.type) + { + case FX_TYPE_PARTICLE: + emit_particle(effect, b, from_dist, to_dist, dt); + break; + case FX_TYPE_DECAL: + emit_decal(effect, b, from_dist, to_dist, dt); + break; + case FX_TYPE_DECAL_DIST: + emit_decal_dist(effect, b, from_dist, to_dist, dt); + break; + case FX_TYPE_CALLBACK: + effect.callback.fp(b, effect.callback.context); + break; + } + } + + void perturb_dir(xVec3& dir, F32 rand_angle) + { + xVec3 temp = { 0.0f, 0.0f, 0.0f }; + xMat3x3 mat; + + temp.x = (xurand() - 0.5f) * rand_angle; + temp.y = (xurand() - 0.5f) * rand_angle; + temp.z = (xurand() - 0.5f) * rand_angle; + + xMat3x3Euler(&mat, &temp); + xMat3x3LMulVec(&dir, &mat, &dir); + } + + void update(bolt& b, F32 dt) + { + b.prev_dist = b.dist; + b.dist += cfg.vel * dt; + } + + void debug_update(F32 dt) + { + + } + + void flush_verts(RxObjSpace3DVertex* verts, S32 num_verts) + { + RwIm3DTransform(verts, num_verts, NULL, 0x19); + RwIm3DRenderPrimitive(rwPRIMTYPETRILIST); + RwIm3DEnd(); + } + + void debug_render() + { + + } + void debug_refresh_effects(fx_when_enum when) { } + + void log_collide_statics(bool) + { + + } + + void log_collide_dynamics(bool) + { + + } + + void set_bolt_verts(RxObjSpace3DVertex* vert, const xVec3& pointA, const xVec3& pointB, U8, const xVec3&) + { + + } }; #endif From 63289b68ca8ab380a56e8a054a588b7157d70b70 Mon Sep 17 00:00:00 2001 From: Josh Sanchez Date: Sat, 22 Nov 2025 21:28:12 -0600 Subject: [PATCH 5/5] xLaserBolt: Additional match progress --- src/SB/Core/x/xLaserBolt.cpp | 330 ++++++++++++++++++++++++++++++++++- 1 file changed, 325 insertions(+), 5 deletions(-) diff --git a/src/SB/Core/x/xLaserBolt.cpp b/src/SB/Core/x/xLaserBolt.cpp index 4427c0dd6..db8e7cf63 100644 --- a/src/SB/Core/x/xLaserBolt.cpp +++ b/src/SB/Core/x/xLaserBolt.cpp @@ -6,6 +6,7 @@ #include "zBase.h" #include "zNPCTypeCommon.h" #include "zEntDestructObj.h" +#include "containers.h" #include @@ -47,6 +48,29 @@ void xLaserBoltEmitter::set_texture(RwRaster* raster) void xLaserBoltEmitter::reset() { + static_queue::iterator it = this->bolts.begin(); + while (it != this->bolts.end()) + { + bolt& b = *it; + + effect_data* itfx = this->fx[6]; + effect_data* endfx = itfx + this->fxsize[6]; + while (itfx != endfx) + { + emit_fx(*itfx, b, b.hit_dist, b.hit_dist, (1.0f / 60.0f)); + itfx++; + } + + ++it; + } + + this->bolts.clear(); + start_collide = 0; + + for (S32 i = FX_WHEN_LAUNCH; i < MAX_FX_WHEN; i++) + { + reset_fx((fx_when_enum)i); + } } void xLaserBoltEmitter::refresh_config() @@ -63,6 +87,99 @@ void xLaserBoltEmitter::refresh_config() this->ialpha = alpha; } +void xLaserBoltEmitter::emit(const xVec3& loc, const xVec3& dir) +{ + if (this->bolts.full()) + { + this->bolts.pop_back(); + } + + bolt& b = this->bolts.push_front(); + b.origin = b.loc = loc; + b.dir = dir; + b.dist = b.prev_dist = b.prev_check_dist = 0.0f; + b.hit_dist = cfg.kill_dist; + b.hit_ent = NULL; + b.emitted = 0.0f; + b.context = NULL; + + if (cfg.rand_ang > 0.0f) + { + perturb_dir(b.dir, cfg.rand_ang); + } + + if (cfg.hit_interval > 0) + { + pre_collide(b); + } + + start_collide++; + + effect_data* fx = this->fx[0]; + effect_data* fx_end = fx + this->fxsize[0]; + while (fx != fx_end) + { + emit_fx(*fx, b, 0.0f, 0.0f, (1.0f / 60.0f)); + fx++; + } +} + +void xLaserBoltEmitter::update(F32 dt) +{ + debug_update(dt); + + if (start_collide) + { + + } + else + { + + } + + static_queue::iterator it; + while (it != this->bolts.end()) + { + bolt& b = *it; + + update(b, dt); + + U8 collided; + F32 prev_dist; + effect_data* itfx; + effect_data* endfx; + // effect_data* itfx; + // effect_data* endfx; + } +} + +void xLaserBoltEmitter::render() +{ + debug_render(); + + RxObjSpace3DVertex* vert; + RxObjSpace3DVertex* v = get_vert_buffer(*(S32*)&vert); + + RwRenderStateSet(rwRENDERSTATETEXTURERASTER, bolt_raster); + + static_queue::iterator it = bolts.begin(); + while (it != bolts.end()) + { + if ((S32)(v - (v - vert)) < 6) + { + flush_verts(v, (S32)(v - (v - vert))); + } + + render(*it, v); + ++it; + } + + if (v != vert) + { + flush_verts(v, vert - v); + } +} + void xLaserBoltEmitter::attach_effects(fx_when_enum when, effect_data* fx, size_t fxsize) { this->fx[when] = fx; @@ -71,6 +188,56 @@ void xLaserBoltEmitter::attach_effects(fx_when_enum when, effect_data* fx, size_ debug_refresh_effects(when); } +void xLaserBoltEmitter::pre_collide(bolt& b) +{ + xVec3 origin; + xVec3 dir; + xCollis coll; + + origin = b.origin; + dir = b.dir; + xRayHitsSceneFlags(globals.sceneCur, (xRay3*)&origin, &coll, XENT_COLLTYPE_PLYR, 0x22); + + if (coll.flags & 0x1) + { + b.hit_dist = coll.dist; + b.hit_norm = coll.norm; + } + + log_collide_statics(coll.flags & 0x1); +} + +RxObjSpace3DVertex* xLaserBoltEmitter::render(bolt& b, RxObjSpace3DVertex *vert) +{ + F32 dist0 = b.prev_dist - this->cfg.length; + if (dist0 < 0.0f) + { + dist0 = 0.0f; + } + + F32 dist1 = b.dist; + if (dist1 <= b.hit_dist) + { + dist1 = dist0; + } + + if (dist0 < dist1) + { + return vert; + } + + xVec3 loc0 = b.origin + b.dir * dist0; + xVec3 loc1 = b.dir * dist0; + // xMat4x3 &cam_mat; + xVec3 dir; + xVec3 right; + xVec3 half_right; + + + set_bolt_verts(vert, loc0, loc1, 0xFF, half_right); + return vert; +} + RxObjSpace3DVertex* xLaserBoltEmitter::get_vert_buffer(S32& dat) { dat = (U32)0x1e0; @@ -94,14 +261,167 @@ void xLaserBoltEmitter::apply_damage(xLaserBoltEmitter::bolt& b) } } -// WIP. void xLaserBoltEmitter::reset_fx(fx_when_enum when) { - for (U32 i = 0; i < sizeof(fx); i++) - { - U32* sizePtr = &this->fxsize[when]; - effect_data** effect = &this->fx[when]; + effect_data* cur_fx = this->fx[when]; + effect_data* fx_end = cur_fx + this->fxsize[when]; + while (cur_fx != fx_end) + { + cur_fx->irate = cur_fx->rate > 0.0f ? 1.0f / cur_fx->rate : 0.0f; + cur_fx++; + } +} +void xLaserBoltEmitter::update_fx(bolt& b, F32 prev_dist, F32 dt) +{ + F32 tail_dist = b.dist - this->cfg.length; + if (b.dist < this->cfg.length) + { + effect_data* itfx = this->fx[2]; + effect_data* endfx = itfx + this->fxsize[2]; + while (itfx != endfx) + { + emit_fx(*itfx, b, 0.0f, 0.0f, dt); + itfx++; + } + } + else if (tail_dist < b.hit_dist) + { + F32 from_dist = prev_dist - this->cfg.length; + effect_data* itfx = this->fx[5]; + effect_data* endfx = itfx + this->fxsize[5]; + if (from_dist < 0.0f) + { + from_dist = 0.0f; + } + + while (itfx != endfx) + { + emit_fx(*itfx, b, from_dist, tail_dist, dt); + itfx++; + } + } + + if (b.dist < b.hit_dist) + { + effect_data* itfx = this->fx[4]; + effect_data* endfx = itfx + this->fxsize[4]; + while (itfx != endfx) + { + emit_fx(*itfx, b, prev_dist, b.dist, dt); + itfx++; + } + } + else if (tail_dist < b.hit_dist) + { + effect_data* itfx = this->fx[3]; + effect_data* endfx = itfx + this->fxsize[3]; + while (itfx != endfx) + { + emit_fx(*itfx, b, b.hit_dist, b.hit_dist, dt); + itfx++; + } + } +} + +void xLaserBoltEmitter::emit_particle(effect_data& effect, bolt& b, F32 from_dist, F32 to_dist, F32 dt) +{ + xParEmitter& pe = *effect.par; + xParEmitterAsset& pea = *pe.tasset; + F32 velmag = pea.vel.y; + + switch(effect.orient) + { + case FX_ORIENT_PATH: + pea.vel = b.dir * velmag; + break; + case FX_ORIENT_IPATH: + pea.vel = b.dir * -velmag; + break; + case FX_ORIENT_HIT_NORM: + pea.vel = b.hit_norm * velmag; + break; + case FX_ORIENT_HIT_REFLECT: + break; + } + + if (pea.emit_type == eParEmitterLine) + { + pea.e_line.pos1 = b.origin + b.dir * from_dist; + pea.e_line.pos2 = b.origin + b.dir * to_dist; + xParEmitterEmit(&pe, dt); + } + else + { + xVec3 oldloc = pea.pos; + pea.pos += b.origin + b.dir * to_dist; + + xParEmitterEmit(&pe, dt); + pea.pos = oldloc; + } +} + +void xLaserBoltEmitter::emit_decal(effect_data& effect, bolt& b, F32 from_dist, F32 to_dist, F32 dt) +{ + xMat4x3 mat; + + switch (effect.orient) + { + case FX_ORIENT_DEFAULT: + case FX_ORIENT_PATH: + case FX_ORIENT_IPATH: + xMat3x3LookVec3(mat, b.dir); + break; + case FX_ORIENT_HIT_NORM: + xMat3x3LookVec3(mat, b.hit_norm); + break; + case FX_ORIENT_HIT_REFLECT: + break; + } + + mat.pos = b.origin + b.dir * to_dist; + effect.decal->emit(mat, -1); +} + +void xLaserBoltEmitter::emit_decal_dist(effect_data& effect, bolt& b, F32 from_dist, F32 to_dist, F32 dt) +{ + F32 start_dist = to_dist - from_dist; + b.emitted = effect.rate * start_dist + b.emitted; + + S32 total = effect.rate * start_dist + b.emitted; + b.emitted -= total; + + if (total <= 0) + { + return; + } + + xMat4x3 mat; + switch (effect.orient) + { + case FX_ORIENT_DEFAULT: + case FX_ORIENT_PATH: + case FX_ORIENT_IPATH: + xMat3x3LookVec3(mat, b.dir); + break; + case FX_ORIENT_HIT_NORM: + xMat3x3LookVec3(mat, b.hit_norm); + break; + case FX_ORIENT_HIT_REFLECT: + break; + } + + xVec3 dloc = b.dir * effect.irate; + mat.pos = b.origin + b.dir * start_dist; + for (S32 i = 0; i < total; i++) + { + if (((xDecalEmitter*)effect.par)->full()) + { + break; + } + + ((xDecalEmitter*)effect.par)->emit(mat, -1); + mat.pos += dloc; } }