diff --git a/code/model/animation/modelanimation.cpp b/code/model/animation/modelanimation.cpp index 38e89d742ba..bbd4f008dac 100644 --- a/code/model/animation/modelanimation.cpp +++ b/code/model/animation/modelanimation.cpp @@ -1816,6 +1816,7 @@ namespace animation { {"$Axis Rotation:", ModelAnimationSegmentAxisRotation::parser}, {"$Translation:", ModelAnimationSegmentTranslation::parser}, {"$Sound During:", ModelAnimationSegmentSoundDuring::parser}, + {"$Particles During:", ModelAnimationSegmentParticlesDuring::parser}, {"$Inverse Kinematics:", ModelAnimationSegmentIK::parser} }; diff --git a/code/model/animation/modelanimation_segments.cpp b/code/model/animation/modelanimation_segments.cpp index c5085ee4dc2..f2a48ed634a 100644 --- a/code/model/animation/modelanimation_segments.cpp +++ b/code/model/animation/modelanimation_segments.cpp @@ -3,6 +3,10 @@ #include #include "render/3d.h" +#include "particle/ParticleManager.h" +#include "particle/hosts/EffectHostObject.h" +#include "particle/hosts/EffectHostSubmodel.h" +#include "particle/hosts/EffectHostVector.h" namespace animation { @@ -1287,6 +1291,8 @@ namespace animation { instance.interruptableSound = false; instance.currentlyPlaying = sound_handle::invalid(); } + + m_segment->forceStopAnimation(pmi_id); } void ModelAnimationSegmentSoundDuring::playLoopSnd(polymodel_instance* pmi) { @@ -1381,6 +1387,108 @@ namespace animation { } + ModelAnimationSegmentParticlesDuring::ModelAnimationSegmentParticlesDuring(std::shared_ptr segment, particle::ParticleEffectHandle effect, float atTime, std::shared_ptr submodel, std::optional position, std::optional orientation) : + m_segment(std::move(segment)), m_submodel(std::move(submodel)), m_position(std::move(position)), m_orientation(std::move(orientation)), m_effect(effect), m_atTime(atTime) { } + + ModelAnimationSegment* ModelAnimationSegmentParticlesDuring::copy() const { + auto newCopy = new ModelAnimationSegmentParticlesDuring(*this); + newCopy->m_segment = std::shared_ptr(newCopy->m_segment->copy()); + return newCopy; + } + + void ModelAnimationSegmentParticlesDuring::recalculate(ModelAnimationSubmodelBuffer& base, ModelAnimationSubmodelBuffer& currentAnimDelta, polymodel_instance* pmi) { + m_segment->recalculate(base, currentAnimDelta, pmi); + m_duration[pmi->id] = m_segment->getDuration(pmi->id); + } + + void ModelAnimationSegmentParticlesDuring::calculateAnimation(ModelAnimationSubmodelBuffer& base, float time, int pmi_id) const { + m_segment->calculateAnimation(base, time, pmi_id); + } + + void ModelAnimationSegmentParticlesDuring::executeAnimation(const ModelAnimationSubmodelBuffer& state, float timeboundLower, float timeboundUpper, ModelAnimationDirection direction, int pmi_id) { + float atTime = fminf(fmaxf(m_atTime, 0.0f), m_duration.at(pmi_id)); + if (timeboundLower <= atTime && atTime <= timeboundUpper) { + createParticleSource(model_get_instance(pmi_id)); + } + m_segment->executeAnimation(state, timeboundLower, timeboundUpper, direction, pmi_id); + } + + void ModelAnimationSegmentParticlesDuring::exchangeSubmodelPointers(ModelAnimationSet& replaceWith) { + m_segment->exchangeSubmodelPointers(replaceWith); + } + + void ModelAnimationSegmentParticlesDuring::forceStopAnimation(int pmi_id) { + m_segment->forceStopAnimation(pmi_id); + } + + void ModelAnimationSegmentParticlesDuring::createParticleSource(polymodel_instance* pmi) const { + if (!m_effect.isValid()) + return; + + auto source = particle::ParticleManager::get()->createSource(m_effect); + if (!source) + return; + + matrix orient = m_orientation.value_or(vmd_identity_matrix); + vec3d pos = m_position.value_or(vmd_zero_vector); + + std::unique_ptr host; + + if (m_submodel != nullptr && pmi->objnum >= 0) { + auto submodel = m_submodel->findSubmodel(pmi); + if (submodel.first != nullptr) { + host = std::make_unique(&Objects[pmi->objnum], static_cast(submodel.first - pmi->submodel), pos, orient); + } + } + + if (!host && pmi->objnum >= 0) { + host = std::make_unique(&Objects[pmi->objnum], pos, orient); + } + + if (!host) { + host = std::make_unique(pos, orient, vmd_zero_vector); + } + + source->setHost(std::move(host)); + source->finishCreation(); + } + + std::shared_ptr ModelAnimationSegmentParticlesDuring::parser(ModelAnimationParseHelper* data) { + auto submodel = ModelAnimationParseHelper::parseSubmodel(); + if (!submodel) { + if (data->parentSubmodel) + submodel = data->parentSubmodel; + } + + required_string("+Effect:"); + auto effect = particle::util::parseEffect(data->m_animationName); + + required_string("+At Time:"); + float atTime = 0.0f; + stuff_float(&atTime); + + std::optional position = std::nullopt; + if (optional_string("+Position:")) { + vec3d parse; + stuff_vec3d(&parse); + position = std::move(parse); + } + + std::optional orientation = std::nullopt; + if (optional_string("+Orientation:")) { + angles angle; + stuff_angles_deg_phb(&angle); + matrix mat; + vm_angles_2_matrix(&mat, &angle); + orientation = std::move(mat); + } + + auto segment = std::make_shared(data->parseSegment(), effect, atTime, submodel, position, orientation); + + return segment; + } + + ModelAnimationSegmentIK::ModelAnimationSegmentIK(const vec3d& targetPosition, const std::optional& targetRotation) : m_targetPosition(targetPosition), m_targetRotation(targetRotation) { } diff --git a/code/model/animation/modelanimation_segments.h b/code/model/animation/modelanimation_segments.h index efa8217b064..7996e905864 100644 --- a/code/model/animation/modelanimation_segments.h +++ b/code/model/animation/modelanimation_segments.h @@ -2,6 +2,7 @@ #include "math/ik_solver.h" #include "model/animation/modelanimation.h" +#include "particle/particle.h" namespace animation { @@ -290,6 +291,34 @@ namespace animation { }; + class ModelAnimationSegmentParticlesDuring : public ModelAnimationSegment { + std::shared_ptr m_segment; + + std::shared_ptr m_submodel; + std::optional m_position; + std::optional m_orientation; + + //configurables: + public: + particle::ParticleEffectHandle m_effect; + float m_atTime; + + private: + ModelAnimationSegment* copy() const override; + void recalculate(ModelAnimationSubmodelBuffer& base, ModelAnimationSubmodelBuffer& currentAnimDelta, polymodel_instance* pmi) override; + void calculateAnimation(ModelAnimationSubmodelBuffer& base, float time, int pmi_id) const override; + void executeAnimation(const ModelAnimationSubmodelBuffer& state, float timeboundLower, float timeboundUpper, ModelAnimationDirection direction, int pmi_id) override; + void exchangeSubmodelPointers(ModelAnimationSet& replaceWith) override; + void forceStopAnimation(int pmi_id) override; + + void createParticleSource(polymodel_instance* pmi) const; + + public: + static std::shared_ptr parser(ModelAnimationParseHelper* data); + ModelAnimationSegmentParticlesDuring(std::shared_ptr segment, particle::ParticleEffectHandle effect, float atTime, std::shared_ptr submodel = nullptr, std::optional position = std::nullopt, std::optional orientation = std::nullopt); + + }; + class ModelAnimationSegmentIK : public ModelAnimationSegment { struct instance_data {