#include "model/animation/modelanimation_moveables.h" #include "model/animation/modelanimation_segments.h" namespace animation { ModelAnimationMoveableOrientation::ModelAnimationMoveableOrientation(std::shared_ptr submodel, const angles& defaultOrient) : m_submodel(std::move(submodel)), m_defaultPosOrient(defaultOrient) { } void ModelAnimationMoveableOrientation::update(polymodel_instance* pmi, const SCP_vector& args) { if(args.size() < 3){ Error(LOCATION,"Tried updating moveable orientation with too few (%d of 3) arguments!", (int) args.size()); return; } auto& anim = m_instances[pmi->id].animation; try { auto p = std::any_cast(args[0]), h = std::any_cast(args[1]), b = std::any_cast(args[2]); anim->forceRecalculate(pmi); auto& setOrientation = *std::static_pointer_cast(anim->m_animation); setOrientation.m_targetAngle = angles{fl_radians((float)p), fl_radians((float)b), fl_radians((float)h)}; anim->start(pmi, ModelAnimationDirection::FWD); } catch (const std::bad_any_cast& e){ Error(LOCATION, "Argument error trying to update orientation moveable: %s", e.what()); } } void ModelAnimationMoveableOrientation::initialize(ModelAnimationSet* parentSet, polymodel_instance* pmi) { auto& anim = m_instances[pmi->id].animation; anim = std::make_shared(false, false, true, parentSet); anim->setAnimation(std::make_shared(parentSet->getSubmodel(m_submodel), m_defaultPosOrient, ModelAnimationCoordinateRelation::RELATIVE_COORDS)); anim->start(pmi,ModelAnimationDirection::FWD); } std::shared_ptr ModelAnimationMoveableOrientation::parser() { required_string("+Angle:"); angles angle; stuff_angles_deg_phb(&angle); auto submodel = ModelAnimationParseHelper::parseSubmodel(); if(submodel == nullptr) error_display(1, "Could not create moveable! Moveable Orientation has no target submodel!"); return std::make_shared(std::move(submodel), angle); } ModelAnimationMoveableRotation::ModelAnimationMoveableRotation(std::shared_ptr submodel, const angles& defaultOrient, const angles& velocity, const std::optional& acceleration) : m_submodel(std::move(submodel)), m_defaultPosOrient(defaultOrient), m_velocity(velocity), m_acceleration(acceleration) { } void ModelAnimationMoveableRotation::update(polymodel_instance* pmi, const SCP_vector& args) { if(args.size() < 3){ Error(LOCATION,"Tried updating moveable rotation with too few (%d of 3) arguments!", (int) args.size()); return; } auto& anim = m_instances[pmi->id].animation; try { auto p = std::any_cast(args[0]), h = std::any_cast(args[1]), b = std::any_cast(args[2]); auto& sequence = *std::static_pointer_cast(anim->m_animation); auto& setOrientation = *std::static_pointer_cast(sequence.m_segments[0]); auto& rotation = *std::static_pointer_cast(sequence.m_segments[1]); ModelAnimationSubmodelBuffer buffer; buffer[rotation.m_submodel].data.orientation = IDENTITY_MATRIX; //Will now only contain the delta of the rotation anim->m_animation->calculateAnimation(buffer, anim->getTime(pmi->id), pmi->id); anim->forceRecalculate(pmi); setOrientation.m_targetOrientation = buffer[rotation.m_submodel].data.orientation; rotation.m_targetAngle = angles{fl_radians((float)p), fl_radians((float)b), fl_radians((float)h)}; anim->start(pmi, ModelAnimationDirection::FWD); } catch (const std::bad_any_cast& e){ Error(LOCATION, "Argument error trying to update rotation moveable: %s", e.what()); } } void ModelAnimationMoveableRotation::initialize(ModelAnimationSet* parentSet, polymodel_instance* pmi) { auto& anim = m_instances[pmi->id].animation; anim = std::make_shared(false, false, true, parentSet); matrix orient; vm_angles_2_matrix(&orient, &m_defaultPosOrient); auto submodel = parentSet->getSubmodel(m_submodel); auto sequence = std::make_shared(); sequence->addSegment(std::make_shared(submodel, orient, ModelAnimationCoordinateRelation::RELATIVE_COORDS)); sequence->addSegment(std::make_shared(std::move(submodel), m_defaultPosOrient, m_velocity, std::nullopt, m_acceleration, ModelAnimationCoordinateRelation::ABSOLUTE_COORDS)); anim->setAnimation(sequence); anim->start(pmi,ModelAnimationDirection::FWD); } std::shared_ptr ModelAnimationMoveableRotation::parser() { required_string("+Angle:"); angles angle; stuff_angles_deg_phb(&angle); required_string("+Velocity:"); angles velocity; stuff_angles_deg_phb(&velocity); std::optional acceleration; if (optional_string("+Acceleration:")) { angles parse; stuff_angles_deg_phb(&parse); acceleration = parse; } auto submodel = ModelAnimationParseHelper::parseSubmodel(); if(submodel == nullptr) error_display(1, "Could not create moveable! Moveable Rotation has no target submodel!"); return std::make_shared(std::move(submodel), angle, velocity, acceleration); } ModelAnimationMoveableTranslation::ModelAnimationMoveableTranslation(std::shared_ptr submodel, const vec3d& defaultOffset, const vec3d& velocity, const std::optional& acceleration) : m_submodel(std::move(submodel)), m_defaultOffset(defaultOffset), m_velocity(velocity), m_acceleration(acceleration) { } void ModelAnimationMoveableTranslation::update(polymodel_instance* pmi, const SCP_vector& args) { if(args.size() < 3){ Error(LOCATION,"Tried updating moveable translation with too few (%d of 3) arguments!", (int) args.size()); return; } auto& anim = m_instances[pmi->id].animation; try { auto x = std::any_cast(args[0]), y = std::any_cast(args[1]), z = std::any_cast(args[2]); auto& sequence = *std::static_pointer_cast(anim->m_animation); auto& setOffset = *std::static_pointer_cast(sequence.m_segments[0]); auto& translation = *std::static_pointer_cast(sequence.m_segments[1]); ModelAnimationSubmodelBuffer buffer; buffer[translation.m_submodel].data.position = ZERO_VECTOR; //Will now only contain the delta of the translation anim->m_animation->calculateAnimation(buffer, anim->getTime(pmi->id), pmi->id); anim->forceRecalculate(pmi); setOffset.m_target = buffer[translation.m_submodel].data.position; translation.m_target = vec3d{{{x * 0.01f, y * 0.01f, z * 0.01f}}}; anim->start(pmi, ModelAnimationDirection::FWD); } catch (const std::bad_any_cast& e){ Error(LOCATION, "Argument error trying to update translation moveable: %s", e.what()); } } void ModelAnimationMoveableTranslation::initialize(ModelAnimationSet* parentSet, polymodel_instance* pmi) { auto& anim = m_instances[pmi->id].animation; anim = std::make_shared(false, false, true, parentSet); auto submodel = parentSet->getSubmodel(m_submodel); auto sequence = std::make_shared(); sequence->addSegment(std::make_shared(submodel, m_defaultOffset, ModelAnimationCoordinateRelation::RELATIVE_COORDS)); sequence->addSegment(std::make_shared(std::move(submodel), m_defaultOffset, m_velocity, std::nullopt, m_acceleration, ModelAnimationSegmentTranslation::CoordinateSystem::COORDS_PARENT, ModelAnimationCoordinateRelation::ABSOLUTE_COORDS)); anim->setAnimation(sequence); anim->start(pmi,ModelAnimationDirection::FWD); } std::shared_ptr ModelAnimationMoveableTranslation::parser() { required_string("+Target:"); vec3d angle; stuff_vec3d(&angle); required_string("+Velocity:"); vec3d velocity; stuff_vec3d(&velocity); std::optional acceleration; if (optional_string("+Acceleration:")) { vec3d parse; stuff_vec3d(&parse); acceleration = parse; } auto submodel = ModelAnimationParseHelper::parseSubmodel(); if(submodel == nullptr) error_display(1, "Could not create moveable! Moveable Translation has no target submodel!"); return std::make_shared(std::move(submodel), angle, velocity, acceleration); } ModelAnimationMoveableAxisRotation::ModelAnimationMoveableAxisRotation(std::shared_ptr submodel, const float& velocity, const std::optional& acceleration, const vec3d& axis) : m_submodel(std::move(submodel)), m_velocity(velocity), m_acceleration(acceleration), m_axis(axis) { } void ModelAnimationMoveableAxisRotation::update(polymodel_instance* pmi, const SCP_vector& args) { if(args.empty()){ Error(LOCATION,"Tried updating moveable axis rotation with too few (%d of 1) arguments!", (int) args.size()); return; } auto& anim = m_instances[pmi->id].animation; try { auto ang = std::any_cast(args[0]); auto& sequence = *std::static_pointer_cast(anim->m_animation); auto& setOrientation = *std::static_pointer_cast(sequence.m_segments[0]); auto& rotation = *std::static_pointer_cast(sequence.m_segments[1]); ModelAnimationSubmodelBuffer buffer; buffer[rotation.m_submodel].data.orientation = IDENTITY_MATRIX; //Will now only contain the delta of the rotation sequence.m_segments[1]->calculateAnimation(buffer, anim->getTime(pmi->id), pmi->id); anim->forceRecalculate(pmi); matrix startOrient = *setOrientation.m_targetOrientation; matrix newOrient; //Apply rotation to old default state vm_matrix_x_matrix(&newOrient, &startOrient, &buffer[rotation.m_submodel].data.orientation); setOrientation.m_targetOrientation = newOrient; float currentAngle; vm_closest_angle_to_matrix(&newOrient, &m_axis, ¤tAngle); //CurrentAngle is 0 to 2Pi, convert to -pi to pi if (currentAngle > PI) currentAngle -= PI2; rotation.m_targetAngle = fl_radians((float)ang) - currentAngle; anim->start(pmi, ModelAnimationDirection::FWD); } catch(const std::bad_any_cast& e){ Error(LOCATION, "Argument error trying to update rotation moveable: %s", e.what()); } } void ModelAnimationMoveableAxisRotation::initialize(ModelAnimationSet* parentSet, polymodel_instance* pmi) { auto& anim = m_instances[pmi->id].animation; anim = std::make_shared(false, false, true, parentSet); auto submodel = parentSet->getSubmodel(m_submodel); auto sequence = std::make_shared(); sequence->addSegment(std::make_shared(submodel, vmd_identity_matrix, ModelAnimationCoordinateRelation::RELATIVE_COORDS)); sequence->addSegment(std::make_shared(std::move(submodel), 0.0f, m_velocity, std::nullopt, m_acceleration, m_axis)); anim->setAnimation(sequence); anim->start(pmi,ModelAnimationDirection::FWD); } std::shared_ptr ModelAnimationMoveableAxisRotation::parser() { required_string("+Axis:"); vec3d axis; stuff_vec3d(&axis); required_string("+Velocity:"); float velocity; stuff_float(&velocity); velocity = fl_radians(velocity); std::optional acceleration; if (optional_string("+Acceleration:")) { float parse; stuff_float(&parse); acceleration = fl_radians(parse); } auto submodel = ModelAnimationParseHelper::parseSubmodel(); if(submodel == nullptr) error_display(1, "Could not create moveable! Moveable Axis Rotation has no target submodel!"); return std::make_shared(std::move(submodel), velocity, acceleration, axis); } ModelAnimationMoveableIK::ModelAnimationMoveableIK(std::vector chain, float time) : m_time(time), m_chain(std::move(chain)) { } void ModelAnimationMoveableIK::update(polymodel_instance* pmi, const SCP_vector& args) { if(args.size() < 3){ Error(LOCATION,"Tried updating moveable IK with too few (%d of 3) arguments!", (int) args.size()); return; } auto& anim = m_instances[pmi->id].animation; try { auto x = std::any_cast(args[0]), y = std::any_cast(args[1]), z = std::any_cast(args[2]); std::optional orient; if(args.size() >= 6 && args[3].type() == typeid(int)) { auto p = std::any_cast(args[3]), h = std::any_cast(args[4]), b = std::any_cast(args[5]); matrix rot; angles ang{ fl_radians(p), fl_radians(b), fl_radians(h) }; vm_angles_2_matrix(&rot, &ang); orient = rot; } auto& sequence = *std::static_pointer_cast(anim->m_animation); auto& setOrientationParallel = *std::static_pointer_cast(sequence.m_segments[0]); auto& ik = *std::static_pointer_cast(sequence.m_segments[1]); ModelAnimationSubmodelBuffer buffer; for(const auto& segment : setOrientationParallel.m_segments){ auto& orientation = *std::static_pointer_cast(segment); buffer[orientation.m_submodel].data.orientation = IDENTITY_MATRIX; } //Will now only contain the delta of the IK anim->m_animation->calculateAnimation(buffer, anim->getTime(pmi->id), pmi->id); anim->forceRecalculate(pmi); for(const auto& segment : setOrientationParallel.m_segments){ auto& orientation = *std::static_pointer_cast(segment); orientation.m_targetOrientation = buffer[orientation.m_submodel].data.orientation; } ik.m_targetRotation = orient; ik.m_targetPosition = vec3d{{{x * 0.01f, y * 0.01f, z * 0.01f}}}; anim->start(pmi, ModelAnimationDirection::FWD); } catch(const std::bad_any_cast& e){ Error(LOCATION, "Argument error trying to update rotation moveable: %s", e.what()); } } void ModelAnimationMoveableIK::initialize(ModelAnimationSet* parentSet, polymodel_instance* pmi) { auto& anim = m_instances[pmi->id].animation; anim = std::make_shared(false, false, true, parentSet); auto sequence = std::make_shared(); auto parallelSetOrient = std::make_shared(); for(const auto& link : m_chain) { auto submodel = parentSet->getSubmodel(link.submodel); parallelSetOrient->addSegment(std::make_shared(submodel, vmd_identity_matrix, ModelAnimationCoordinateRelation::RELATIVE_COORDS)); } sequence->addSegment(parallelSetOrient); auto parallelIK = std::make_shared(); vec3d startPos = ZERO_VECTOR; for(size_t i = 1; i < m_chain.size(); ++i) { startPos += parentSet->getSubmodel(m_chain[i].submodel)->findSubmodel(pmi).second->offset; } auto segment = std::make_shared(startPos, std::nullopt); segment->m_segment = parallelIK; for(const auto& link : m_chain) { auto submodel = parentSet->getSubmodel(link.submodel); auto rotation = std::make_shared(submodel, std::optional({0,0,0}), std::nullopt, m_time, link.acceleration, ModelAnimationCoordinateRelation::ABSOLUTE_COORDS); parallelIK->addSegment(rotation); segment->m_chain.push_back({submodel, link.constraint, std::move(rotation)}); } sequence->addSegment(segment); anim->setAnimation(sequence); anim->start(pmi,ModelAnimationDirection::FWD); } std::shared_ptr ModelAnimationMoveableIK::parser() { required_string("+Time:"); float time; stuff_float(&time); std::vector chain; while (optional_string("$Chain Link:")) { auto submodel = ModelAnimationParseHelper::parseSubmodel(); std::optional acceleration; if (optional_string("+Acceleration:")) { angles accel; stuff_angles_deg_phb(&accel); acceleration = accel; } std::shared_ptr constraint; if (optional_string("+Constraint:")) { int type = required_string_one_of(2, "Window", "Hinge"); switch (type) { case 0: { //Window angles window; required_string("Window"); required_string("+Window Size:"); stuff_angles_deg_phb(&window); constraint = std::make_shared(window); break; } case 1: { //Hinge vec3d axis; required_string("Hinge"); required_string("+Axis:"); stuff_vec3d(&axis); vm_vec_normalize(&axis); constraint = std::make_shared(axis); break; } default: UNREACHABLE("IK constraint of unknown type specified!"); break; } } else constraint = std::make_shared(); chain.push_back({std::move(submodel), std::move(constraint), acceleration}); } return std::make_shared(std::move(chain), time); } }