mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-12 08:11:10 +02:00
781 lines
20 KiB
C++
781 lines
20 KiB
C++
// BSE_Effect.cpp
|
|
//
|
|
|
|
#include "precompiled.h"
|
|
#include "BSE_Envelope.h"
|
|
#include "BSE_Particle.h"
|
|
#include "BSE.h"
|
|
#include "BSE_SpawnDomains.h"
|
|
|
|
//#include "../renderer/RenderCommon.h"
|
|
|
|
static ID_INLINE void BSE_BoundTriSurf(srfTriangles_t* tri) {
|
|
if (!tri) {
|
|
return;
|
|
}
|
|
if (tri->numVerts <= 0 || !tri->verts) {
|
|
tri->bounds.Clear();
|
|
return;
|
|
}
|
|
tri->bounds.Clear();
|
|
for (int i = 0; i < tri->numVerts; ++i) {
|
|
tri->bounds.AddPoint(tri->verts[i].xyz);
|
|
}
|
|
}
|
|
|
|
void rvBSE::Init(const rvDeclEffect* declEffect, renderEffect_s* parms, float time)
|
|
{
|
|
int v6; // esi
|
|
float* v7; // ecx
|
|
float* v8; // eax
|
|
double v9; // st3
|
|
double v10; // st5
|
|
float* v11; // esi
|
|
float* v12; // eax
|
|
int v13; // ecx
|
|
double v14; // st5
|
|
//float v15; // [esp+18h] [ebp-30h]
|
|
//float v16; // [esp+1Ch] [ebp-2Ch]
|
|
//float v17; // [esp+20h] [ebp-28h]
|
|
//float v18; // [esp+24h] [ebp-24h]
|
|
//float v19; // [esp+28h] [ebp-20h]
|
|
//float v20; // [esp+2Ch] [ebp-1Ch]
|
|
//float v21; // [esp+30h] [ebp-18h] BYREF
|
|
//float v22[5]; // [esp+34h] [ebp-14h] BYREF
|
|
float parmsa; // [esp+50h] [ebp+8h]
|
|
|
|
this->mStartTime = parms->startTime;
|
|
this->mDeclEffect = declEffect;
|
|
this->mLastTime = time;
|
|
this->mFlags = 0;
|
|
this->mDuration = 0.0;
|
|
this->mAttenuation = 1.0;
|
|
v6 = 2;
|
|
this->mCost = 0.0;
|
|
this->mFlags = parms->loop;
|
|
this->mCurrentLocalBounds[1].z = 0.0;
|
|
this->mCurrentLocalBounds[1].y = 0.0;
|
|
v7 = &this->mLastRenderBounds[0].z;
|
|
this->mCurrentLocalBounds[1].x = 0.0;
|
|
this->mCurrentLocalBounds[0].z = 0.0;
|
|
this->mCurrentLocalBounds[0].y = 0.0;
|
|
this->mCurrentLocalBounds[0].x = 0.0;
|
|
v8 = &this->mCurrentLocalBounds[0].z;
|
|
v9 = declEffect->mSize;
|
|
this->mCurrentLocalBounds[0].x = this->mCurrentLocalBounds[0].x - v9;
|
|
this->mCurrentLocalBounds[0].y = this->mCurrentLocalBounds[0].y - v9;
|
|
this->mCurrentLocalBounds[0].z = this->mCurrentLocalBounds[0].z - v9;
|
|
this->mCurrentLocalBounds[1].x = this->mCurrentLocalBounds[1].x + v9;
|
|
this->mCurrentLocalBounds[1].y = v9 + this->mCurrentLocalBounds[1].y;
|
|
this->mCurrentLocalBounds[1].z = v9 + this->mCurrentLocalBounds[1].z;
|
|
do
|
|
{
|
|
v10 = *(v8 - 2);
|
|
v8 += 3;
|
|
*(v7 - 2) = v10;
|
|
v7 += 3;
|
|
--v6;
|
|
*(v7 - 4) = *(v8 - 4);
|
|
*(v7 - 3) = *(v8 - 3);
|
|
} while (v6);
|
|
this->mGrownRenderBounds[0].z = 1.0e30;
|
|
this->mGrownRenderBounds[0].y = 1.0e30;
|
|
this->mGrownRenderBounds[0].x = 1.0e30;
|
|
parmsa = -1.0e30;
|
|
this->mGrownRenderBounds[1].z = parmsa;
|
|
this->mGrownRenderBounds[1].y = parmsa;
|
|
this->mGrownRenderBounds[1].x = parmsa;
|
|
this->mForcePush = 0;
|
|
this->mOriginalOrigin.x = parms->origin.x;
|
|
this->mOriginalOrigin.y = parms->origin.y;
|
|
this->mOriginalOrigin.z = parms->origin.z;
|
|
this->mOriginalEndOrigin.z = 0.0;
|
|
this->mOriginalEndOrigin.y = 0.0;
|
|
this->mOriginalEndOrigin.x = 0.0;
|
|
memcpy(&this->mOriginalAxis, &parms->axis, sizeof(this->mOriginalAxis));
|
|
//v18 = this->mOriginalOrigin.x + this->mCurrentLocalBounds.b[1].x;
|
|
//v19 = this->mCurrentLocalBounds[1].y + this->mOriginalOrigin.y;
|
|
//v11 = &v21;
|
|
//v20 = this->mCurrentLocalBounds[1].z + this->mOriginalOrigin.z;
|
|
//v12 = &this->mCurrentWorldBounds[0].y;
|
|
//v13 = 2;
|
|
//v15 = this->mOriginalOrigin.x + this->mCurrentLocalBounds.b[0].x;
|
|
//v16 = this->mCurrentLocalBounds[0].y + this->mOriginalOrigin.y;
|
|
//v17 = this->mCurrentLocalBounds[0].z + this->mOriginalOrigin.z;
|
|
//v21 = v15;
|
|
//v22[0] = v16;
|
|
//v22[1] = v17;
|
|
//v22[2] = v18;
|
|
//v22[3] = v19;
|
|
//v22[4] = v20;
|
|
//do
|
|
//{
|
|
// v14 = *v11;
|
|
// v11 += 3;
|
|
// *(v12 - 1) = v14;
|
|
// v12 += 3;
|
|
// --v13;
|
|
// *(v12 - 3) = *(float*)((char*)v12 + (char*)&v21 - (char*)&this->mCurrentWorldBounds - 12);
|
|
// *(v12 - 2) = *(float*)((char*)v12 + (char*)v22 - (char*)&this->mCurrentWorldBounds - 12);
|
|
//} while (v13);
|
|
if (parms->hasEndOrigin)
|
|
{
|
|
this->mFlags |= 2u;
|
|
this->mOriginalEndOrigin.x = parms->endOrigin.x;
|
|
this->mOriginalEndOrigin.y = parms->endOrigin.y;
|
|
this->mOriginalEndOrigin.z = parms->endOrigin.z;
|
|
this->mCurrentEndOrigin.x = parms->endOrigin.x;
|
|
this->mCurrentEndOrigin.y = parms->endOrigin.y;
|
|
this->mCurrentEndOrigin.z = parms->endOrigin.z;
|
|
}
|
|
this->mCurrentTime = time;
|
|
this->mCurrentOrigin.x = this->mOriginalOrigin.x;
|
|
this->mCurrentOrigin.y = this->mOriginalOrigin.y;
|
|
this->mCurrentOrigin.z = this->mOriginalOrigin.z;
|
|
this->mCurrentVelocity.z = 0.0;
|
|
this->mCurrentVelocity.y = 0.0;
|
|
this->mCurrentVelocity.x = 0.0;
|
|
this->mCurrentWorldBounds.Clear();
|
|
this->mCurrentWorldBounds.AddPoint(this->mCurrentOrigin + this->mCurrentLocalBounds[0]);
|
|
this->mCurrentWorldBounds.AddPoint(this->mCurrentOrigin + this->mCurrentLocalBounds[1]);
|
|
this->mSpriteSize.Zero();
|
|
UpdateFromOwner(parms, time, 1);
|
|
this->mReferenceSound = 0;
|
|
if (parms->referenceSoundHandle > 0) {
|
|
this->mReferenceSound = soundSystem->EmitterForIndex(SOUNDWORLD_GAME, parms->referenceSoundHandle);
|
|
}
|
|
UpdateSegments(time);
|
|
this->mOriginDistanceToCamera = 0.0;
|
|
this->mShortestDistanceToCamera = 0.0;
|
|
}
|
|
|
|
float rvBSE::GetAttenuation(rvSegmentTemplate* st) const
|
|
{
|
|
double result; // st7
|
|
float sta; // [esp+4h] [ebp+4h]
|
|
float stb; // [esp+4h] [ebp+4h]
|
|
float stc; // [esp+4h] [ebp+4h]
|
|
float std; // [esp+4h] [ebp+4h]
|
|
|
|
result = 0.0;
|
|
if (st->mAttenuation.x <= 0.0 && st->mAttenuation.y <= 0.0)
|
|
return this->mAttenuation;
|
|
sta = st->mAttenuation.x + 1.0;
|
|
if (sta > (double)this->mShortestDistanceToCamera)
|
|
return this->mAttenuation;
|
|
stb = st->mAttenuation.y - 1.0;
|
|
if (stb >= (double)this->mShortestDistanceToCamera)
|
|
{
|
|
stc = (this->mShortestDistanceToCamera - st->mAttenuation.x) / (st->mAttenuation.y - st->mAttenuation.x);
|
|
std = (1.0 - stc) * this->mAttenuation;
|
|
result = std;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
float rvBSE::GetOriginAttenuation(rvSegmentTemplate* st) const
|
|
{
|
|
float result = 0.0f;
|
|
|
|
if (st->mAttenuation.x <= 0.0f && st->mAttenuation.y <= 0.0f) {
|
|
return st->mScale * mAttenuation;
|
|
}
|
|
|
|
if (st->mAttenuation.x + 1.0f > mOriginDistanceToCamera) {
|
|
return st->mScale * mAttenuation;
|
|
}
|
|
|
|
if (st->mAttenuation.y - 1.0f >= mOriginDistanceToCamera) {
|
|
const float lerp = (mOriginDistanceToCamera - st->mAttenuation.x) / (st->mAttenuation.y - st->mAttenuation.x);
|
|
result = (1.0f - lerp) * st->mScale * mAttenuation;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void rvBSE::UpdateSoundEmitter(rvSegmentTemplate* st, rvSegment* seg)
|
|
{
|
|
if (!st || !seg || !mReferenceSound) {
|
|
return;
|
|
}
|
|
|
|
if (GetStopped()) {
|
|
// Stop looping sounds when the owning effect is stopped.
|
|
if (st->GetSoundLooping() && (seg->mFlags & 2) != 0) {
|
|
mReferenceSound->StopSound(static_cast<s_channelType>(seg->mSegmentTemplateHandle + SCHANNEL_ONE));
|
|
}
|
|
return;
|
|
}
|
|
|
|
soundShaderParms_t parms = {};
|
|
parms.volume = seg->mSoundVolume;
|
|
parms.frequencyShift = seg->mFreqShift;
|
|
|
|
// Keep the emitter spatialized at the current effect origin so sound
|
|
// tracks moving/attached effects correctly.
|
|
mReferenceSound->UpdateEmitter(mCurrentOrigin, mCurrentVelocity, 0, &parms);
|
|
}
|
|
const idVec3 rvBSE::GetInterpolatedOffset(float time) const
|
|
{
|
|
idVec3 result; // eax
|
|
float v5; // [esp+0h] [ebp-18h]
|
|
float v6; // [esp+4h] [ebp-14h]
|
|
float v7; // [esp+8h] [ebp-10h]
|
|
float v8; // [esp+Ch] [ebp-Ch]
|
|
float v9; // [esp+10h] [ebp-8h]
|
|
float v10; // [esp+14h] [ebp-4h]
|
|
float deltaTime; // [esp+1Ch] [ebp+4h]
|
|
float deltaTimea; // [esp+1Ch] [ebp+4h]
|
|
|
|
result.z = 0.0;
|
|
result.y = 0.0;
|
|
result.x = 0.0;
|
|
deltaTime = this->mCurrentTime - this->mLastTime;
|
|
if (deltaTime >= 0.0020000001)
|
|
{
|
|
deltaTimea = 1.0 - (time - this->mLastTime) / deltaTime;
|
|
v5 = this->mCurrentOrigin.x - this->mLastOrigin.x;
|
|
v6 = this->mCurrentOrigin.y - this->mLastOrigin.y;
|
|
v7 = this->mCurrentOrigin.z - this->mLastOrigin.z;
|
|
v8 = v5 * deltaTimea;
|
|
v9 = v6 * deltaTimea;
|
|
v10 = deltaTimea * v7;
|
|
result.x = v8;
|
|
result.y = v9;
|
|
result.z = v10;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void rvBSE::SetDuration(float time)
|
|
{
|
|
double v2; // st7
|
|
|
|
v2 = time;
|
|
if (time < 0.0)
|
|
{
|
|
v2 = this->mDeclEffect->mMinDuration;
|
|
LABEL_3:
|
|
this->mDuration = v2;
|
|
return;
|
|
}
|
|
if (this->mDuration < v2)
|
|
goto LABEL_3;
|
|
}
|
|
|
|
const char* rvBSE::GetDeclName()
|
|
{
|
|
return mDeclEffect->base->GetName();
|
|
}
|
|
|
|
void rvBSE::UpdateAttenuation()
|
|
{
|
|
if ((this->mDeclEffect->mFlags & ETFLAG_ATTENUATES) == 0) {
|
|
return;
|
|
}
|
|
|
|
idVec3 viewOrigin;
|
|
idMat3 viewAxis;
|
|
game->GetPlayerView(viewOrigin, viewAxis);
|
|
|
|
const float originDistance = (mCurrentOrigin - viewOrigin).LengthFast();
|
|
mOriginDistanceToCamera = idMath::ClampFloat(1.0f, 131072.0f, originDistance);
|
|
|
|
// Match vanilla BSE attenuation space conversion (uses current axis here).
|
|
const idVec3 localView = mCurrentAxis * (viewOrigin - mCurrentOrigin);
|
|
const float shortest = mCurrentLocalBounds.ShortestDistance(localView);
|
|
mShortestDistanceToCamera = idMath::ClampFloat(1.0f, 131072.0f, shortest);
|
|
}
|
|
|
|
void rvBSE::LoopInstant(float time)
|
|
{
|
|
rvBSE* v2; // esi
|
|
int v3; // edi
|
|
bool v4; // zf
|
|
bool v5; // sf
|
|
int v6; // ebx
|
|
double v7; // st7
|
|
|
|
v2 = this;
|
|
if (0.0 == this->mDuration)
|
|
{
|
|
v3 = 0;
|
|
v4 = this->mSegments.Num() == 0;
|
|
v5 = this->mSegments.Num() < 0;
|
|
this->mStartTime = this->mDeclEffect->mMaxDuration + 0.5 + this->mStartTime;
|
|
if (!v5 && !v4)
|
|
{
|
|
v6 = 0;
|
|
do
|
|
{
|
|
v2->mSegments[v6].ResetTime(v2, v2->mStartTime);
|
|
++v3;
|
|
++v6;
|
|
} while (v3 < v2->mSegments.Num());
|
|
}
|
|
if (bse_debug.GetInteger() == 2)
|
|
{
|
|
v7 = v2->mDeclEffect->mMaxDuration + 0.5;
|
|
common->Printf("BSE: Looping duration %g\n", v7);
|
|
}
|
|
++v2->mDeclEffect->mLoopCount;
|
|
}
|
|
}
|
|
|
|
void rvBSE::LoopLooping(float time)
|
|
{
|
|
int v3; // edi
|
|
bool v4; // cc
|
|
int v5; // ebx
|
|
|
|
if (0.0 != this->mDuration)
|
|
{
|
|
v3 = 0;
|
|
v4 = this->mSegments.Num() <= 0;
|
|
this->mStartTime = this->mStartTime + this->mDuration;
|
|
this->mDuration = 0.0;
|
|
if (!v4)
|
|
{
|
|
v5 = 0;
|
|
do
|
|
{
|
|
mSegments[v5].ResetTime(this, this->mStartTime);
|
|
++v3;
|
|
++v5;
|
|
} while (v3 < this->mSegments.Num());
|
|
}
|
|
if (bse_debug.GetInteger() == 2) {
|
|
common->Printf("BSE: Looping duration: %g", this->mDuration);
|
|
}
|
|
|
|
++this->mDeclEffect->mLoopCount;
|
|
}
|
|
}
|
|
bool rvBSE::Service(renderEffect_t* parms, float time, bool spawn, bool& forcePush)
|
|
{
|
|
renderEffect_s* v5; // ebp
|
|
int v7; // edi
|
|
int v8; // ebx
|
|
int v9; // edi
|
|
char v10; // bl
|
|
int v11; // ebp
|
|
bool v12; // zf
|
|
float spawna; // [esp+24h] [ebp+Ch]
|
|
float spawnb; // [esp+24h] [ebp+Ch]
|
|
float spawnc; // [esp+24h] [ebp+Ch]
|
|
float spawnd; // [esp+24h] [ebp+Ch]
|
|
|
|
v5 = parms;
|
|
UpdateFromOwner(parms, time, 0);
|
|
UpdateAttenuation();
|
|
if (spawn)
|
|
{
|
|
v7 = 0;
|
|
if (this->mSegments.Num() > 0)
|
|
{
|
|
v8 = 0;
|
|
do
|
|
{
|
|
spawna = 0.0f;
|
|
mSegments[v8].Check(this, time, spawna);
|
|
++v7;
|
|
++v8;
|
|
} while (v7 < this->mSegments.Num());
|
|
}
|
|
}
|
|
if ((this->mFlags & 8) == 0 && parms->loop)
|
|
{
|
|
spawnb = this->mDuration + this->mStartTime;
|
|
if (spawnb < (double)time)
|
|
LoopLooping(time);
|
|
}
|
|
v9 = 0;
|
|
v10 = 0;
|
|
if (this->mSegments.Num() > 0)
|
|
{
|
|
v11 = 0;
|
|
do
|
|
{
|
|
if (mSegments[v11].UpdateParticles(this, time))
|
|
v10 = 1;
|
|
++v9;
|
|
++v11;
|
|
} while (v9 < this->mSegments.Num());
|
|
v5 = parms;
|
|
}
|
|
this->mFlags &= 0xFFFFFFFB;
|
|
forcePush = this->mForcePush;
|
|
v12 = (this->mFlags & 8) == 0;
|
|
this->mForcePush = 0;
|
|
if (!v12)
|
|
return v10 == 0;
|
|
if (v5->loop)
|
|
{
|
|
spawnc = this->mDuration + this->mStartTime;
|
|
if (spawnc < (double)time)
|
|
LoopInstant(time);
|
|
if (v5->loop)
|
|
return 0;
|
|
}
|
|
spawnd = this->mDuration + this->mStartTime;
|
|
return spawnd < (double)time;
|
|
}
|
|
|
|
float rvBSE::EvaluateCost(int segment)
|
|
{
|
|
double result; // st7
|
|
int v4; // edi
|
|
int v5; // ebx
|
|
double v6; // st7
|
|
|
|
if (segment < 0)
|
|
{
|
|
v4 = 0;
|
|
this->mCost = 0.0;
|
|
if (this->mSegments.Num() > 0)
|
|
{
|
|
v5 = 0;
|
|
do
|
|
{
|
|
v6 = mDeclEffect->EvaluateCost(
|
|
this->mSegments[v5].mActiveCount,
|
|
segment);
|
|
++v4;
|
|
++v5;
|
|
this->mCost = v6 + this->mCost;
|
|
} while (v4 < this->mSegments.Num());
|
|
}
|
|
result = this->mCost;
|
|
}
|
|
else
|
|
{
|
|
this->mCost = mDeclEffect->EvaluateCost(
|
|
this->mSegments[segment].mActiveCount,
|
|
segment);
|
|
result = this->mCost;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void rvBSE::InitModel(idRenderModel* model)
|
|
{
|
|
int v3; // edi
|
|
int v4; // ebx
|
|
|
|
v3 = 0;
|
|
if (this->mSegments.Num() > 0)
|
|
{
|
|
v4 = 0;
|
|
do
|
|
{
|
|
mSegments[v4].AllocateSurface(this, model);
|
|
++v3;
|
|
++v4;
|
|
} while (v3 < this->mSegments.Num());
|
|
}
|
|
}
|
|
|
|
idRenderModel* rvBSE::Render(idRenderModel* model, const struct renderEffect_s* owner, const struct viewDef_s* view)
|
|
{
|
|
if (!bse_render.GetInteger() || !owner || !view) {
|
|
return NULL;
|
|
}
|
|
|
|
idRenderModelStatic* renderModel = dynamic_cast<idRenderModelStatic*>(model);
|
|
if (model && !renderModel) {
|
|
renderModelManager->FreeModel(model);
|
|
model = NULL;
|
|
}
|
|
|
|
if (!renderModel) {
|
|
model = renderModelManager->AllocModel();
|
|
renderModel = dynamic_cast<idRenderModelStatic*>(model);
|
|
if (!renderModel) {
|
|
if (model) {
|
|
renderModelManager->FreeModel(model);
|
|
}
|
|
common->Warning("^4BSE:^1 Unable to allocate runtime model for effect '%s'", mDeclEffect ? mDeclEffect->GetName() : "<unknown>");
|
|
return NULL;
|
|
}
|
|
renderModel->InitEmpty("_bse_runtime");
|
|
InitModel(renderModel);
|
|
}
|
|
else {
|
|
// Runtime particles mutate vertex/index data every frame.
|
|
// Invalidate vertex/index caches so new data is uploaded.
|
|
renderModel->FreeVertexCache();
|
|
for (int i = 0; i < renderModel->NumSurfaces(); ++i) {
|
|
const modelSurface_t* surf = renderModel->Surface(i);
|
|
if (!surf || !surf->geometry) {
|
|
continue;
|
|
}
|
|
srfTriangles_t* tri = surf->geometry;
|
|
if (tri->indexCache) {
|
|
tri->indexCache = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
mViewAxis = view->renderView.viewaxis;
|
|
mViewOrg = view->renderView.vieworg;
|
|
|
|
float time = view->floatTime;
|
|
for (int i = 0; i < mSegments.Num(); ++i) {
|
|
const bool active = mSegments[i].Active();
|
|
|
|
mSegments[i].ClearSurface(this, renderModel);
|
|
if (active) {
|
|
mSegments[i].Render(this, owner, renderModel, time);
|
|
mSegments[i].RenderTrail(this, owner, renderModel, time);
|
|
}
|
|
}
|
|
|
|
// BSE runtime geometry is rebuilt every frame and includes strip-like particle
|
|
// topology that can intentionally share points. Avoid full static-surface cleanup
|
|
// here and only update per-surface/model bounds for culling.
|
|
idBounds modelBounds;
|
|
modelBounds.Clear();
|
|
bool hasBounds = false;
|
|
for (int i = 0; i < renderModel->NumSurfaces(); ++i) {
|
|
const modelSurface_t* surf = renderModel->Surface(i);
|
|
if (!surf || !surf->geometry) {
|
|
continue;
|
|
}
|
|
|
|
srfTriangles_t* tri = surf->geometry;
|
|
if (tri->numVerts <= 0 || tri->numIndexes <= 0) {
|
|
tri->bounds.Clear();
|
|
continue;
|
|
}
|
|
|
|
BSE_BoundTriSurf(tri);
|
|
if (!hasBounds) {
|
|
modelBounds = tri->bounds;
|
|
hasBounds = true;
|
|
}
|
|
else {
|
|
modelBounds.AddBounds(tri->bounds);
|
|
}
|
|
}
|
|
|
|
if (!hasBounds) {
|
|
modelBounds.Zero();
|
|
}
|
|
renderModel->bounds = modelBounds;
|
|
DisplayDebugInfo(owner, view, renderModel->bounds);
|
|
mLastRenderBounds = modelBounds;
|
|
mGrownRenderBounds = mLastRenderBounds;
|
|
mGrownRenderBounds.ExpandSelf(20.0f);
|
|
mForcePush = true;
|
|
return renderModel;
|
|
}
|
|
|
|
void rvBSE::Destroy()
|
|
{
|
|
mSegments.Clear();
|
|
mReferenceSound = NULL;
|
|
}
|
|
|
|
|
|
void rvBSE::UpdateSegments(float time)
|
|
{
|
|
int v3; // ebx
|
|
rvSegment* v4; // eax
|
|
rvParticle** v5; // esi
|
|
bool v6; // cc
|
|
int v7; // ecx
|
|
int* v8; // eax
|
|
rvSegment* v9; // esi
|
|
rvSegment* v10; // ecx
|
|
int v11; // edx
|
|
int v12; // eax
|
|
rvParticle** v13; // esi
|
|
int v14; // esi
|
|
int v15; // edi
|
|
int v16; // esi
|
|
int v17; // edi
|
|
int v18; // esi
|
|
int v19; // edi
|
|
rvSegment* ptr; // [esp+14h] [ebp-14h]
|
|
|
|
v3 = this->mDeclEffect->mSegmentTemplates.Num();
|
|
if (v3 > 0)
|
|
{
|
|
if (v3 != this->mSegments.Size())
|
|
{
|
|
mSegments.SetNum(v3);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mSegments.Clear();
|
|
}
|
|
v14 = 0;
|
|
//this->mSegments.num = v3; // Not sure why this gets set twice? compiler optimization gone wrong?
|
|
if (v3 > 0)
|
|
{
|
|
v15 = 0;
|
|
do
|
|
mSegments[v15++].Init(this, this->mDeclEffect, v14++, time);
|
|
while (v14 < this->mSegments.Num());
|
|
}
|
|
v16 = 0;
|
|
if (this->mSegments.Num() > 0)
|
|
{
|
|
v17 = 0;
|
|
do
|
|
{
|
|
mSegments[v17].CalcCounts(this, time);
|
|
++v16;
|
|
++v17;
|
|
} while (v16 < this->mSegments.Num());
|
|
}
|
|
v18 = 0;
|
|
if (this->mSegments.Num() > 0)
|
|
{
|
|
v19 = 0;
|
|
do
|
|
{
|
|
|
|
mSegments[v19].InitParticles(this);
|
|
++v18;
|
|
++v19;
|
|
} while (v18 < this->mSegments.Num());
|
|
}
|
|
}
|
|
|
|
|
|
void rvBSE::DisplayDebugInfo(const struct renderEffect_s* parms, const struct viewDef_s* view, idBounds& bounds) {
|
|
if (!parms || !view || !view->renderWorld) {
|
|
return;
|
|
}
|
|
const int debugLevel = bse_debug.GetInteger();
|
|
if (!debugLevel && !bse_showBounds.GetBool()) {
|
|
return;
|
|
}
|
|
|
|
idRenderWorld* rw = view->renderWorld;
|
|
|
|
// Keep level-1 debug useful for logging without polluting gameplay visuals.
|
|
// Level 2 enables heavy on-screen BSE overlays.
|
|
if (debugLevel >= 2) {
|
|
const char* effectName = "<unknown>";
|
|
if (mDeclEffect && mDeclEffect->base) {
|
|
effectName = mDeclEffect->base->GetName();
|
|
}
|
|
|
|
idStr txt = va(
|
|
"(%g) (size %g) (bri %g)\n%s",
|
|
mCost,
|
|
mDeclEffect ? mDeclEffect->mSize : 0.0f,
|
|
parms->shaderParms[6],
|
|
effectName);
|
|
|
|
rw->DebugAxis(parms->origin, parms->axis);
|
|
rw->DrawText(txt.c_str(), parms->origin, 14.0f, colorCyan, view->renderView.viewaxis, 1, 0);
|
|
}
|
|
|
|
if (!bse_showBounds.GetBool()) {
|
|
return;
|
|
}
|
|
|
|
rw->DebugBounds(colorGreen, bounds, vec3_origin);
|
|
|
|
const idBox worldBox(mCurrentWorldBounds, parms->origin, parms->axis);
|
|
rw->DebugBox(colorBlue, worldBox);
|
|
|
|
if (!mCurrentWorldBounds.Contains(bounds)) {
|
|
rw->DebugBounds(colorRed, bounds, vec3_origin);
|
|
}
|
|
}
|
|
|
|
void rvBSE::UpdateFromOwner(renderEffect_s* parms, float time, bool init)
|
|
{
|
|
mLastTime = mCurrentTime;
|
|
mLastOrigin = mCurrentOrigin;
|
|
|
|
mCurrentTime = time;
|
|
mCurrentOrigin = parms->origin;
|
|
mCurrentAxis = parms->axis;
|
|
mCurrentAxisTransposed = mCurrentAxis.Transpose();
|
|
|
|
const float dt = mCurrentTime - mLastTime;
|
|
if (dt > 0.002f) {
|
|
mCurrentVelocity = (mCurrentOrigin - mLastOrigin) * (1.0f / dt);
|
|
}
|
|
// Vanilla keeps the last valid owner velocity when multiple updates land
|
|
// on the same timestamp; avoid zeroing in that case.
|
|
|
|
mGravity = parms->gravity;
|
|
mGravityDir = mGravity;
|
|
const float gravityLengthSqr = mGravityDir.LengthSqr();
|
|
if (gravityLengthSqr > 0.0f) {
|
|
mGravityDir *= idMath::InvSqrt(gravityLengthSqr);
|
|
}
|
|
|
|
SetLooping(parms->loop);
|
|
SetHasEndOrigin(parms->hasEndOrigin);
|
|
SetOrientateIdentity((mDeclEffect->mFlags & ETFLAG_ORIENTATE_IDENTITY) != 0);
|
|
SetFlag(parms->ambient, EFLAG_AMBIENT);
|
|
|
|
const idVec3 halfSize(mDeclEffect->mSize, mDeclEffect->mSize, mDeclEffect->mSize);
|
|
|
|
mCurrentWorldBounds.AddPoint(mCurrentOrigin + halfSize);
|
|
mCurrentWorldBounds.AddPoint(mCurrentOrigin - halfSize);
|
|
|
|
if (parms->hasEndOrigin && (mDeclEffect->mFlags & ETFLAG_USES_ENDORIGIN) != 0) {
|
|
const bool endOriginChanged = init || parms->endOrigin != mCurrentEndOrigin || mCurrentOrigin != mLastOrigin;
|
|
mCurrentEndOrigin = parms->endOrigin;
|
|
mCurrentWorldBounds.AddPoint(mCurrentEndOrigin + halfSize);
|
|
mCurrentWorldBounds.AddPoint(mCurrentEndOrigin - halfSize);
|
|
SetEndOriginChanged(endOriginChanged);
|
|
}
|
|
else {
|
|
mCurrentEndOrigin = parms->endOrigin;
|
|
SetEndOriginChanged(false);
|
|
}
|
|
|
|
mCurrentLocalBounds.Clear();
|
|
for (int ix = 0; ix < 2; ++ix) {
|
|
for (int iy = 0; iy < 2; ++iy) {
|
|
for (int iz = 0; iz < 2; ++iz) {
|
|
const idVec3 corner(
|
|
mCurrentWorldBounds[ix].x,
|
|
mCurrentWorldBounds[iy].y,
|
|
mCurrentWorldBounds[iz].z);
|
|
const idVec3 local = mCurrentAxisTransposed * (corner - mCurrentOrigin);
|
|
mCurrentLocalBounds.AddPoint(local);
|
|
}
|
|
}
|
|
}
|
|
|
|
mLightningAxis = mCurrentAxis;
|
|
if (GetHasEndOrigin()) {
|
|
idVec3 forward = mCurrentEndOrigin - mCurrentOrigin;
|
|
if (forward.Normalize() > BSE_TIME_EPSILON) {
|
|
idVec3 up = mCurrentAxis[2];
|
|
if (idMath::Fabs(forward * up) > 0.98f) {
|
|
up = mCurrentAxis[1];
|
|
}
|
|
|
|
idVec3 right = up.Cross(forward);
|
|
if (right.Normalize() > BSE_TIME_EPSILON) {
|
|
up = forward.Cross(right);
|
|
up.Normalize();
|
|
mLightningAxis[0] = forward;
|
|
mLightningAxis[1] = right;
|
|
mLightningAxis[2] = up;
|
|
}
|
|
}
|
|
}
|
|
|
|
mCurrentWindVector.Zero();
|
|
memcpy( mShaderParms, parms->shaderParms, sizeof( mShaderParms ) );
|
|
mTint.Set(parms->shaderParms[0], parms->shaderParms[1], parms->shaderParms[2], parms->shaderParms[3]);
|
|
mBrightness = parms->shaderParms[6];
|
|
mSpriteSize.x = parms->shaderParms[8];
|
|
mSpriteSize.y = parms->shaderParms[9];
|
|
mAttenuation = parms->attenuation;
|
|
mSuppressLightsInViewID = parms->suppressSurfaceInViewID;
|
|
}
|