// Copyright (C) 2007 Id Software, Inc. // #include "precompiled.h" #include "BSE_Envelope.h" #include "BSE_Particle.h" #include "BSE_SpawnDomains.h" #include "BSE.h" #include #include namespace { ID_INLINE float SpawnRandom01() { return rvRandom::flrand(0.0f, 1.0f); } ID_INLINE float SpawnLerp(float a, float b, float t) { return a + (b - a) * t; } ID_INLINE void SpawnGetNormalInternal(idVec3* normal, const idVec3& point, const idVec3* centre) { if (!normal) { return; } if (centre) { *normal = point - *centre; } else { *normal = point; } const float lenSqr = normal->LengthSqr(); if (lenSqr > 1e-6f) { normal->NormalizeFast(); } } ID_INLINE idVec3 SpawnBoxPoint(const rvParticleParms& parms) { return idVec3( rvRandom::flrand(parms.mMins.x, parms.mMaxs.x), rvRandom::flrand(parms.mMins.y, parms.mMaxs.y), rvRandom::flrand(parms.mMins.z, parms.mMaxs.z)); } ID_INLINE idVec3 SpawnSurfaceBoxPoint(const rvParticleParms& parms) { idVec3 result = SpawnBoxPoint(parms); switch (rvRandom::irand(0, 5)) { case 0: result.x = parms.mMins.x; break; case 1: result.x = parms.mMaxs.x; break; case 2: result.y = parms.mMins.y; break; case 3: result.y = parms.mMaxs.y; break; case 4: result.z = parms.mMins.z; break; default: result.z = parms.mMaxs.z; break; } return result; } ID_INLINE idVec3 SpawnSpherePoint(const rvParticleParms& parms, bool surfaceOnly) { const idVec3 centre = (parms.mMins + parms.mMaxs) * 0.5f; const idVec3 radius = (parms.mMaxs - parms.mMins) * 0.5f; const float z = rvRandom::flrand(-1.0f, 1.0f); const float phi = rvRandom::flrand(0.0f, idMath::TWO_PI); const float radial = surfaceOnly ? 1.0f : SpawnRandom01(); const float ring = idMath::Sqrt(Max(0.0f, 1.0f - z * z)); idVec3 dir(ring * idMath::Cos(phi), ring * idMath::Sin(phi), z); return idVec3( centre.x + dir.x * radius.x * radial, centre.y + dir.y * radius.y * radial, centre.z + dir.z * radius.z * radial); } ID_INLINE idVec3 SpawnCylinderPoint(const rvParticleParms& parms, bool surfaceOnly) { const float t = SpawnRandom01(); const float phi = rvRandom::flrand(0.0f, idMath::TWO_PI); const float radial = surfaceOnly ? 1.0f : SpawnRandom01(); const float x = SpawnLerp(parms.mMins.x, parms.mMaxs.x, t); const float cy = 0.5f * (parms.mMins.y + parms.mMaxs.y); const float cz = 0.5f * (parms.mMins.z + parms.mMaxs.z); const float ry = 0.5f * (parms.mMaxs.y - parms.mMins.y); const float rz = 0.5f * (parms.mMaxs.z - parms.mMins.z); return idVec3(x, cy + idMath::Cos(phi) * ry * radial, cz + idMath::Sin(phi) * rz * radial); } } TSpawnFunc rvParticleParms::spawnFunctions[SPF_COUNT] = { /*00*/ &SpawnStub, /*01*/ &SpawnNone1, /*02*/ &SpawnNone2, /*03*/ &SpawnNone3, /*04*/ &SpawnStub, /*05*/ &SpawnOne1, /*06*/ &SpawnOne2, /*07*/ &SpawnOne3, /*08*/ &SpawnStub, /*09*/ &SpawnPoint1, /*10*/ &SpawnPoint2, /*11*/ &SpawnPoint3, /*12*/ &SpawnStub, /*13*/ &SpawnLinear1, /*14*/ &SpawnLinear2, /*15*/ &SpawnLinear3, /*16*/ &SpawnStub, /*17*/ &SpawnBox1, /*18*/ &SpawnBox2, /*19*/ &SpawnBox3, /*20*/ &SpawnStub, /*21*/ &SpawnSurfaceBox1, /*22*/ &SpawnSurfaceBox2, /*23*/ &SpawnSurfaceBox3, /*24*/ &SpawnStub, /*25*/ &SpawnBox1, /*26*/ &SpawnSphere2, /*27*/ &SpawnSphere3, /*28*/ &SpawnStub, /*29*/ &SpawnSurfaceBox1, /*30*/ &SpawnSurfaceSphere2, /*31*/ &SpawnSurfaceSphere3, /*32*/ &SpawnStub, /*33*/ &SpawnBox1, /*34*/ &SpawnSphere2, /*35*/ &SpawnCylinder3, /*36*/ &SpawnStub, /*37*/ &SpawnSurfaceBox1, /*38*/ &SpawnSurfaceSphere2, /*39*/ &SpawnSurfaceCylinder3, /*40*/ &SpawnStub, /*41*/ &SpawnStub, /*42*/ &SpawnSpiral2, /*43*/ &SpawnSpiral3, /*44*/ &SpawnStub, /*45*/ &SpawnStub, /*46*/ &SpawnStub, /*47*/ &SpawnModel3, }; void sdModelInfo::CalculateSurfRemap(void) { for (int i = 0; i < NUM_SURF_REMAP; ++i) { surfRemap[i] = 0; } if (!model || model->NumSurfaces() <= 0) { return; } int totalTris = 0; for (int i = 0; i < model->NumSurfaces(); ++i) { const modelSurface_t* surf = model->Surface(i); if (!surf || !surf->geometry) { continue; } totalTris += surf->geometry->numIndexes / 3; } if (totalTris <= 0) { return; } int slot = 0; for (int i = 0; i < model->NumSurfaces() && slot < NUM_SURF_REMAP; ++i) { const modelSurface_t* surf = model->Surface(i); if (!surf || !surf->geometry) { continue; } const int tris = surf->geometry->numIndexes / 3; const int count = Max(1, idMath::FtoiFast((float)NUM_SURF_REMAP * ((float)tris / (float)totalTris) + 0.5f)); for (int j = 0; j < count && slot < NUM_SURF_REMAP; ++j) { surfRemap[slot++] = i; } } while (slot < NUM_SURF_REMAP) { surfRemap[slot] = surfRemap[slot > 0 ? slot - 1 : 0]; ++slot; } } bool rvParticleParms::Compare(const rvParticleParms& comp) const { if (mSpawnType != comp.mSpawnType || mFlags != comp.mFlags) { return false; } if ((mModelInfo == NULL) != (comp.mModelInfo == NULL)) { return false; } if (mModelInfo && comp.mModelInfo && mModelInfo->model != comp.mModelInfo->model) { return false; } const float eps = 0.001f; if (!mMins.Compare(comp.mMins, eps) || !mMaxs.Compare(comp.mMaxs, eps)) { return false; } return idMath::Fabs(mRange - comp.mRange) <= eps; } void rvParticleParms::HandleRelativeParms(float* death, float* init, int count) { if ((mFlags & PPFLAG_RELATIVE) == 0 || death == NULL || init == NULL) { return; } for (int i = 0; i < count; ++i) { death[i] += init[i]; } } void rvParticleParms::GetMinsMaxs(idVec3& mins, idVec3& maxs) { mins.Zero(); maxs.Zero(); switch (mSpawnType & ~0x3) { case SPF_ONE_0: mins.Set(1.0f, 1.0f, 1.0f); maxs = mins; break; case SPF_POINT_0: mins = mMins; maxs = mMins; break; case SPF_LINEAR_0: case SPF_BOX_0: case SPF_SURFACE_BOX_0: case SPF_SPHERE_0: case SPF_SURFACE_SPHERE_0: case SPF_CYLINDER_0: case SPF_SURFACE_CYLINDER_0: case SPF_SPIRAL_0: case SPF_MODEL_0: mins = mMins; maxs = mMaxs; break; default: break; } } void SpawnStub(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { if (result) { result[0] = 0.0f; result[1] = 0.0f; result[2] = 0.0f; } if (normal) { normal->Set(0.0f, 0.0f, 1.0f); } } void SpawnNone1(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = 0.0f; } void SpawnNone2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = 0.0f; result[1] = 0.0f; } void SpawnNone3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out.Zero(); SpawnGetNormalInternal(normal, out, centre); } void SpawnOne1(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = 1.0f; } void SpawnOne2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = 1.0f; result[1] = 1.0f; } void SpawnOne3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out.Set(1.0f, 1.0f, 1.0f); SpawnGetNormalInternal(normal, out, centre); } void SpawnPoint1(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = parms.mMins.x; } void SpawnPoint2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = parms.mMins.x; result[1] = parms.mMins.y; } void SpawnPoint3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out = parms.mMins; SpawnGetNormalInternal(normal, out, centre); } void SpawnLinear1(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = SpawnLerp(parms.mMins.x, parms.mMaxs.x, SpawnRandom01()); } void SpawnLinear2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { const float t = (parms.mFlags & PPFLAG_LINEARSPACING) ? result[0] : SpawnRandom01(); result[0] = SpawnLerp(parms.mMins.x, parms.mMaxs.x, t); result[1] = SpawnLerp(parms.mMins.y, parms.mMaxs.y, t); } void SpawnLinear3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); const float t = (parms.mFlags & PPFLAG_LINEARSPACING) ? out.x : SpawnRandom01(); out.x = SpawnLerp(parms.mMins.x, parms.mMaxs.x, t); out.y = SpawnLerp(parms.mMins.y, parms.mMaxs.y, t); out.z = SpawnLerp(parms.mMins.z, parms.mMaxs.z, t); SpawnGetNormalInternal(normal, out, centre); } void SpawnBox1(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = rvRandom::flrand(parms.mMins.x, parms.mMaxs.x); } void SpawnBox2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = rvRandom::flrand(parms.mMins.x, parms.mMaxs.x); result[1] = rvRandom::flrand(parms.mMins.y, parms.mMaxs.y); } void SpawnBox3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out = SpawnBoxPoint(parms); SpawnGetNormalInternal(normal, out, centre); } void SpawnSurfaceBox1(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = rvRandom::irand(0, 1) ? parms.mMins.x : parms.mMaxs.x; } void SpawnSurfaceBox2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3 sample = SpawnSurfaceBoxPoint(parms); result[0] = sample.x; result[1] = sample.y; } void SpawnSurfaceBox3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out = SpawnSurfaceBoxPoint(parms); SpawnGetNormalInternal(normal, out, centre); } void SpawnSphere1(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = SpawnSpherePoint(parms, false).x; } void SpawnSphere2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3 p = SpawnSpherePoint(parms, false); result[0] = p.x; result[1] = p.y; } void SpawnSphere3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out = SpawnSpherePoint(parms, false); SpawnGetNormalInternal(normal, out, centre); } void SpawnSurfaceSphere1(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { result[0] = SpawnSpherePoint(parms, true).x; } void SpawnSurfaceSphere2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3 p = SpawnSpherePoint(parms, true); result[0] = p.x; result[1] = p.y; } void SpawnSurfaceSphere3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out = SpawnSpherePoint(parms, true); SpawnGetNormalInternal(normal, out, centre); } void SpawnCylinder3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out = SpawnCylinderPoint(parms, false); SpawnGetNormalInternal(normal, out, centre); } void SpawnSurfaceCylinder3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); out = SpawnCylinderPoint(parms, true); SpawnGetNormalInternal(normal, out, centre); } void SpawnSpiral2(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { if (parms.mFlags & PPFLAG_LINEARSPACING) { result[0] = SpawnLerp(parms.mMins.x, parms.mMaxs.x, result[0]); } else { result[0] = rvRandom::flrand(parms.mMins.x, parms.mMaxs.x); } const float range = (idMath::Fabs(parms.mRange) > BSE_TIME_EPSILON) ? parms.mRange : 1.0f; const float theta = idMath::TWO_PI * (result[0] / range); result[1] = idMath::Cos(theta) * rvRandom::flrand(parms.mMins.y, parms.mMaxs.y); } void SpawnSpiral3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); if (parms.mFlags & PPFLAG_LINEARSPACING) { out.x = SpawnLerp(parms.mMins.x, parms.mMaxs.x, out.x); } else { out.x = rvRandom::flrand(parms.mMins.x, parms.mMaxs.x); } const float range = (idMath::Fabs(parms.mRange) > BSE_TIME_EPSILON) ? parms.mRange : 1.0f; const float theta = idMath::TWO_PI * (out.x / range); const float c = idMath::Cos(theta); const float s = idMath::Sin(theta); const float ry = rvRandom::flrand(parms.mMins.y, parms.mMaxs.y); const float rz = rvRandom::flrand(parms.mMins.z, parms.mMaxs.z); out.y = c * ry - s * rz; out.z = c * rz + s * ry; if (normal) { normal->x = centre ? 0.0f : out.x; normal->y = out.y; normal->z = out.z; const float lenSqr = normal->LengthSqr(); if (lenSqr > 1e-6f) { normal->NormalizeFast(); } else { normal->Set(0.0f, 0.0f, 1.0f); } } } void SpawnModel3(float* result, const rvParticleParms& parms, idVec3* normal, const idVec3* centre) { idVec3& out = *reinterpret_cast(result); if (parms.mModelInfo && parms.mModelInfo->model) { idBounds bounds = parms.mModelInfo->model->Bounds(NULL); out.x = rvRandom::flrand(bounds[0].x, bounds[1].x); out.y = rvRandom::flrand(bounds[0].y, bounds[1].y); out.z = rvRandom::flrand(bounds[0].z, bounds[1].z); } else { out = SpawnBoxPoint(parms); } SpawnGetNormalInternal(normal, out, centre); }