diff --git a/Core/GameEngine/Include/GameClient/ParticleSys.h b/Core/GameEngine/Include/GameClient/ParticleSys.h index 4bf120e332d..d894d8b0ebb 100644 --- a/Core/GameEngine/Include/GameClient/ParticleSys.h +++ b/Core/GameEngine/Include/GameClient/ParticleSys.h @@ -431,7 +431,14 @@ class ParticleSystemInfo : public Snapshot m_emissionVolume; ///< the dimensions of the emission volume Bool m_isEmissionVolumeHollow; ///< if true, only create particles at boundary of volume - Bool m_isGroundAligned; ///< if true, align with the ground. if false, then do the normal billboarding. + + enum ParticleAlignmentType CPP_11(: Int) + { + PARTICLE_ALIGNMENT_BILLBOARD = 0, + PARTICLE_ALIGNMENT_XYPLANAR, + PARTICLE_ALIGNMENT_TYPE_COUNT + }; + ParticleAlignmentType m_particleAlignment; ///< align particles toward the camera or with the XY plane. Bool m_isEmitAboveGroundOnly; ///< if true, only emit particles when the system is above ground. Bool m_isParticleUpTowardsEmitter; ///< if true, align the up direction to be towards the emitter. @@ -495,6 +502,12 @@ static const char *const ParticlePriorityNames[] = }; static_assert(ARRAY_SIZE(ParticlePriorityNames) == NUM_PARTICLE_PRIORITIES + 1, "Incorrect array size"); +static const char *const GroundAlignmentTypeNames[] = +{ + "No", "Yes", nullptr +}; +static_assert(ARRAY_SIZE(GroundAlignmentTypeNames) == ParticleSystemInfo::PARTICLE_ALIGNMENT_TYPE_COUNT + 1, "Incorrect array size"); + static const char *const WindMotionNames[] = { "NONE", "Unused", "PingPong", "Circular", nullptr @@ -614,7 +627,7 @@ class ParticleSystem : public MemoryPoolObject, Bool isUsingVolumeParticles() const { return m_particleType == VOLUME_PARTICLE; } UnsignedInt getVolumeParticleDepth() const { return m_volumeParticleDepth; } - Bool shouldBillboard() const { return !m_isGroundAligned; } + Bool shouldBillboard() const { return m_particleAlignment == PARTICLE_ALIGNMENT_BILLBOARD; } ParticleShaderType getShaderType() const { return m_shaderType; } diff --git a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp index 0d697e89fe7..9fab714f490 100644 --- a/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp +++ b/Core/GameEngine/Source/GameClient/System/ParticleSys.cpp @@ -752,7 +752,7 @@ void Particle::loadPostProcess() ParticleSystemInfo::ParticleSystemInfo() { m_priority = PARTICLE_PRIORITY_LOWEST; - m_isGroundAligned = false; + m_particleAlignment = PARTICLE_ALIGNMENT_BILLBOARD; m_isEmitAboveGroundOnly = false; m_isParticleUpTowardsEmitter = false; @@ -808,14 +808,20 @@ void ParticleSystemInfo::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: TheSuperHackers @refactor Serialize particle alignment as an enum instead of a boolean. + */ // ------------------------------------------------------------------------------------------------ void ParticleSystemInfo::xfer( Xfer *xfer ) { Int i; // version +#if RETAIL_COMPATIBLE_XFER_SAVE XferVersion currentVersion = 1; +#else + XferVersion currentVersion = 2; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1001,8 +1007,19 @@ void ParticleSystemInfo::xfer( Xfer *xfer ) // is emission volume hollow xfer->xferBool( &m_isEmissionVolumeHollow ); - // is ground aligned - xfer->xferBool( &m_isGroundAligned ); + // particle alignment + if (version <= 1) + { + // TheSuperHackers @info Preserve save compatibility by mapping all non-billboard alignments to ground-aligned particles. + Bool groundAligned = m_particleAlignment > PARTICLE_ALIGNMENT_BILLBOARD; + xfer->xferBool( &groundAligned ); + if (xfer->getXferMode() == XFER_LOAD) + m_particleAlignment = groundAligned ? PARTICLE_ALIGNMENT_XYPLANAR : PARTICLE_ALIGNMENT_BILLBOARD; + } + else + { + xfer->xferUser( &m_particleAlignment, sizeof( ParticleAlignmentType ) ); + } // emit above ground only xfer->xferBool( &m_isEmitAboveGroundOnly ); @@ -1168,7 +1185,7 @@ ParticleSystem::ParticleSystem( const ParticleSystemTemplate *sysTemplate, m_emissionVolume = sysTemplate->m_emissionVolume; m_isEmissionVolumeHollow = sysTemplate->m_isEmissionVolumeHollow; - m_isGroundAligned = sysTemplate->m_isGroundAligned; + m_particleAlignment = sysTemplate->m_particleAlignment; m_isEmitAboveGroundOnly = sysTemplate->m_isEmitAboveGroundOnly; m_isParticleUpTowardsEmitter = sysTemplate->m_isParticleUpTowardsEmitter; @@ -1749,7 +1766,7 @@ Particle *ParticleSystem::createParticle( const ParticleInfo *info, TheGameLODManager->isParticleSkipped()) ) return nullptr; - if ( getParticleCount() > 0 && priority == AREA_EFFECT && m_isGroundAligned && TheParticleSystemManager->getFieldParticleCount() > (UnsignedInt)TheGlobalData->m_maxFieldParticleCount ) + if ( getParticleCount() > 0 && priority == AREA_EFFECT && !shouldBillboard() && TheParticleSystemManager->getFieldParticleCount() > (UnsignedInt)TheGlobalData->m_maxFieldParticleCount ) return nullptr; // ALWAYS_RENDER particles are exempt from all count limits, and are always created, regardless of LOD issues. @@ -2756,7 +2773,7 @@ const FieldParse ParticleSystemTemplate::m_fieldParseTable[] = { "VolCylinderLength", INI::parseReal, nullptr, offsetof( ParticleSystemTemplate, m_emissionVolume.cylinder.length ) }, { "IsHollow", INI::parseBool, nullptr, offsetof( ParticleSystemTemplate, m_isEmissionVolumeHollow ) }, - { "IsGroundAligned", INI::parseBool, nullptr, offsetof( ParticleSystemTemplate, m_isGroundAligned ) }, + { "IsGroundAligned", INI::parseIndexList, GroundAlignmentTypeNames, offsetof( ParticleSystemTemplate, m_particleAlignment ) }, { "IsEmitAboveGroundOnly", INI::parseBool, nullptr, offsetof( ParticleSystemTemplate, m_isEmitAboveGroundOnly) }, { "IsParticleUpTowardsEmitter", INI::parseBool, nullptr, offsetof( ParticleSystemTemplate, m_isParticleUpTowardsEmitter) }, diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp index 9ceadaa2894..c890104fe5a 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DParticleSys.cpp @@ -247,7 +247,7 @@ void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo) pos = p->getPosition(); psize = p->getSize(); - m_fieldParticleCount += ( sys->getPriority() == AREA_EFFECT && sys->m_isGroundAligned != FALSE ); + m_fieldParticleCount += ( sys->getPriority() == AREA_EFFECT && !sys->shouldBillboard() ); //@todo lorenzen sez: use pointer arithmetic for these arrays personalities[pointCount] = p->getPersonality(); diff --git a/Core/Tools/ParticleEditor/ParticleEditorDialog.cpp b/Core/Tools/ParticleEditor/ParticleEditorDialog.cpp index 256bf78938b..a2fe76fb132 100644 --- a/Core/Tools/ParticleEditor/ParticleEditorDialog.cpp +++ b/Core/Tools/ParticleEditor/ParticleEditorDialog.cpp @@ -1092,7 +1092,7 @@ void DebugWindowDialog::getSwitchFromSystem( IN SwitchType switchType, OUT Bool& { case ST_HOLLOW: switchVal = m_particleSystem->m_isEmissionVolumeHollow; break; case ST_ONESHOT: switchVal = m_particleSystem->m_isOneShot; break; - case ST_ALIGNXY: switchVal = m_particleSystem->m_isGroundAligned; break; + case ST_ALIGNXY: switchVal = m_particleSystem->m_particleAlignment == ParticleSystemInfo::PARTICLE_ALIGNMENT_XYPLANAR; break; case ST_EMITABOVEGROUNDONLY: switchVal = m_particleSystem->m_isEmitAboveGroundOnly; break; case ST_PARTICLEUPTOWARDSEMITTER: switchVal = m_particleSystem->m_isParticleUpTowardsEmitter; break; }; @@ -1108,7 +1108,7 @@ void DebugWindowDialog::updateSwitchToSystem( IN SwitchType switchType, IN const { case ST_HOLLOW: m_particleSystem->m_isEmissionVolumeHollow = switchVal; break; case ST_ONESHOT: m_particleSystem->m_isOneShot = switchVal; break; - case ST_ALIGNXY: m_particleSystem->m_isGroundAligned = switchVal; break; + case ST_ALIGNXY: m_particleSystem->m_particleAlignment = switchVal ? ParticleSystemInfo::PARTICLE_ALIGNMENT_XYPLANAR : ParticleSystemInfo::PARTICLE_ALIGNMENT_BILLBOARD; break; case ST_EMITABOVEGROUNDONLY: m_particleSystem->m_isEmitAboveGroundOnly = switchVal; break; case ST_PARTICLEUPTOWARDSEMITTER: m_particleSystem->m_isParticleUpTowardsEmitter = switchVal; break; }; diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp index f1f23211fdc..9e5a4cd606f 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp @@ -9073,7 +9073,7 @@ static const std::string F_VOLSPHERERAD = "VolSphereRadius"; static const std::string F_VOLCYLRAD = "VolCylinderRadius"; static const std::string F_VOLCYLLEN = "VolCylinderLength"; static const std::string F_ISHOLLOW = "IsHollow"; -static const std::string F_ISXYPLANAR = "IsGroundAligned"; +static const std::string F_PARTICLEALIGNMENT = "IsGroundAligned"; static const std::string F_ISEMITABOVEGROUNDONLY = "IsEmitAboveGroundOnly"; static const std::string F_ISPARTICLEUPTOWARDSEMITTER @@ -9375,7 +9375,7 @@ void _writeSingleParticleSystem( File *out, ParticleSystemTemplate *templ ) } thisEntry.append(SEP_HEAD).append(F_ISHOLLOW).append(EQ_WITH_SPACES).append((templ->m_isEmissionVolumeHollow ? STR_TRUE : STR_FALSE)).append(SEP_EOL); - thisEntry.append(SEP_HEAD).append(F_ISXYPLANAR).append(EQ_WITH_SPACES).append((templ->m_isGroundAligned ? STR_TRUE : STR_FALSE)).append(SEP_EOL); + thisEntry.append(SEP_HEAD).append(F_PARTICLEALIGNMENT).append(EQ_WITH_SPACES).append(GroundAlignmentTypeNames[templ->m_particleAlignment]).append(SEP_EOL); thisEntry.append(SEP_HEAD).append(F_ISEMITABOVEGROUNDONLY).append(EQ_WITH_SPACES).append((templ->m_isEmitAboveGroundOnly ? STR_TRUE : STR_FALSE)).append(SEP_EOL); thisEntry.append(SEP_HEAD).append(F_ISPARTICLEUPTOWARDSEMITTER).append(EQ_WITH_SPACES).append((templ->m_isParticleUpTowardsEmitter ? STR_TRUE : STR_FALSE)).append(SEP_EOL); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp index 9ee0ee728f4..26368e6ec06 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/ScriptEngine.cpp @@ -9776,7 +9776,7 @@ static const std::string F_VOLSPHERERAD = "VolSphereRadius"; static const std::string F_VOLCYLRAD = "VolCylinderRadius"; static const std::string F_VOLCYLLEN = "VolCylinderLength"; static const std::string F_ISHOLLOW = "IsHollow"; -static const std::string F_ISXYPLANAR = "IsGroundAligned"; +static const std::string F_PARTICLEALIGNMENT = "IsGroundAligned"; static const std::string F_ISEMITABOVEGROUNDONLY = "IsEmitAboveGroundOnly"; static const std::string F_ISPARTICLEUPTOWARDSEMITTER @@ -10078,7 +10078,7 @@ void _writeSingleParticleSystem( File *out, ParticleSystemTemplate *templ ) } thisEntry.append(SEP_HEAD).append(F_ISHOLLOW).append(EQ_WITH_SPACES).append((templ->m_isEmissionVolumeHollow ? STR_TRUE : STR_FALSE)).append(SEP_EOL); - thisEntry.append(SEP_HEAD).append(F_ISXYPLANAR).append(EQ_WITH_SPACES).append((templ->m_isGroundAligned ? STR_TRUE : STR_FALSE)).append(SEP_EOL); + thisEntry.append(SEP_HEAD).append(F_PARTICLEALIGNMENT).append(EQ_WITH_SPACES).append(GroundAlignmentTypeNames[templ->m_particleAlignment]).append(SEP_EOL); thisEntry.append(SEP_HEAD).append(F_ISEMITABOVEGROUNDONLY).append(EQ_WITH_SPACES).append((templ->m_isEmitAboveGroundOnly ? STR_TRUE : STR_FALSE)).append(SEP_EOL); thisEntry.append(SEP_HEAD).append(F_ISPARTICLEUPTOWARDSEMITTER).append(EQ_WITH_SPACES).append((templ->m_isParticleUpTowardsEmitter ? STR_TRUE : STR_FALSE)).append(SEP_EOL);