#include "MassSpringSystemSimulator.h" #include MassSpringSystemSimulator::MassSpringSystemSimulator() { m_iTestCase = 0; m_fMass = 10; m_fStiffness = 40; m_iIntegrator = EULER; } const char* MassSpringSystemSimulator::getTestCasesStr() { return "Demo1,Demo2,Demo3,Demo4"; } void MassSpringSystemSimulator::initUI(DrawingUtilitiesClass* DUC) { this->DUC = DUC; switch (m_iTestCase) { case 0: break; case 1: break; case 2: break; default: break; } } void MassSpringSystemSimulator::reset() { m_mouse.x = m_mouse.y = 0; m_trackmouse.x = m_trackmouse.y = 0; m_oldtrackmouse.x = m_oldtrackmouse.y = 0; springs.clear(); masspoints.clear(); } void MassSpringSystemSimulator::drawFrame(ID3D11DeviceContext* pd3dImmediateContext) { for (size_t i = 0; i < springs.size(); i++) { auto sp = springs.at(i); if (!sp.isValid()) { springs.erase(springs.begin() + i); continue; } auto mp1 = sp.mp1.lock(); auto mp2 = sp.mp2.lock(); DUC->setUpLighting(Vec3(), 0.4 * Vec3(1, 1, 1), 100, 0.6 * Vec3(0.97, 0.86, 1)); DUC->drawSphere(mp1->position, Vec3(0.01)); DUC->drawSphere(mp2->position, Vec3(0.01)); DUC->beginLine(); DUC->drawLine(mp1->position, Vec3(1,0,0), mp2->position, Vec3(0,1,0)); DUC->endLine(); } } void MassSpringSystemSimulator::notifyCaseChanged(int testCase) { m_iTestCase = testCase; system("cls"); reset(); switch (m_iTestCase) { case 0: { cout << "Demo 1 !\n"; setMass(10); setStiffness(40); int first = addMassPoint(Vec3(0, 0, 0), Vec3(-1, 0, 0), true); int second = addMassPoint(Vec3(0, 2, 0), Vec3(1, 0, 0), true); addSpring(first, second, 1); cout << "\t -- INITIAL --\n"; printSpring(springs.at(0)); cout << "--------------------------------------------------" << std::endl; //calculate Euler for one step and print results setIntegrator(EULER); cout << "\n\n\t -- EULER RESULT --\n"; simulateTimestep(1); printSpring(springs.at(0)); cout << "--------------------------------------------------" << std::endl; reset(); first = addMassPoint(Vec3(0, 0, 0), Vec3(-1, 0, 0), true); second = addMassPoint(Vec3(0, 2, 0), Vec3(1, 0, 0), true); addSpring(first, second, 1); //calculate Midpoint for one step and print results setIntegrator(MIDPOINT); cout << "\n\n\t -- MIDPOINT RESULT --\n"; simulateTimestep(0.005); printSpring(springs.at(0)); break; } case 1: { cout << "Demo 2 !\n"; reset(); int first = addMassPoint(Vec3(0, 0, 0), Vec3(-1, 0, 0), true); int second = addMassPoint(Vec3(0, 2, 0), Vec3(1, 0, 0), true); addSpring(first, second, 1.0); cout << "\t -- INITIAL --\n"; printSpring(springs.at(0)); cout << "--------------------------------------------------" << std::endl; //calculate Euler for a timestep of 0.005 and print results setIntegrator(EULER); cout << "\n\n\t -- EULER RESULT--\n"; simulateTimestep(0.005); printSpring(springs.at(0)); break; } case 2: { cout << "Demo 3 !\n"; reset(); int first = addMassPoint(Vec3(0, 0, 0), Vec3(-1, 0, 0), true); int second = addMassPoint(Vec3(0, 2, 0), Vec3(1, 0, 0), true); addSpring(first, second, 1.0); cout << "\t -- INITIAL --\n"; printSpring(springs.at(0)); cout << "--------------------------------------------------" << std::endl; //calculate Midpoint for a timestep of 0.005 and print results setIntegrator(MIDPOINT); cout << "\n\n\t -- MIDPOINT RESULT --\n"; simulateTimestep(0.005); printSpring(springs.at(0)); break; } case 3: { cout << "Demo 4 !\n"; break; } default: break; } } void MassSpringSystemSimulator::externalForcesCalculations(float timeElapsed) { Point2D mouseDiff; mouseDiff.x = m_trackmouse.x - m_oldtrackmouse.x; mouseDiff.y = m_trackmouse.y - m_oldtrackmouse.y; if (mouseDiff.x != 0 || mouseDiff.y != 0) { Mat4 worldViewInv = Mat4(DUC->g_camera.GetWorldMatrix() * DUC->g_camera.GetViewMatrix()); worldViewInv = worldViewInv.inverse(); Vec3 inputView = Vec3((float)mouseDiff.x, (float)-mouseDiff.y, 0); Vec3 inputWorld = worldViewInv.transformVectorNormal(inputView); // find a proper scale! float inputScale = 0.001f; inputWorld = inputWorld * inputScale; //m_vfMovableObjectPos = m_vfMovableObjectFinalPos + inputWorld; } else { //m_vfMovableObjectFinalPos = m_vfMovableObjectPos; } } void MassSpringSystemSimulator::simulateTimestep(float timeStep) { //update current setup for each frame for (size_t i = 0; i < springs.size(); i++) { auto sp = springs.at(i); if (!sp.isValid()) { springs.erase(springs.begin() + i); continue; } auto mp1 = sp.mp1.lock(); auto mp2 = sp.mp2.lock(); if (m_iIntegrator == EULER) { Euler(*mp1.get(), *mp2.get(), sp, timeStep); } else if (m_iIntegrator == MIDPOINT) { //TODO: Add Midpoint } else if (m_iIntegrator == LEAPFROG) { //TODO: Add Leapfrog } } } void MassSpringSystemSimulator::onClick(int x, int y) { m_trackmouse.x = x; m_trackmouse.y = y; } void MassSpringSystemSimulator::onMouse(int x, int y) { m_oldtrackmouse.x = x; m_oldtrackmouse.y = y; m_trackmouse.x = x; m_trackmouse.y = y; } void MassSpringSystemSimulator::setMass(float mass) { m_fMass = mass; } void MassSpringSystemSimulator::setStiffness(float stiffness) { m_fStiffness = stiffness; } void MassSpringSystemSimulator::setDampingFactor(float damping) { m_fDamping = damping; } int MassSpringSystemSimulator::addMassPoint(Vec3 position, Vec3 Velocity, bool isFixed) { MassPoint masspoint; masspoint.position = position; masspoint.velocity = Velocity; masspoint.isFixed = isFixed; masspoints.push_back(std::make_shared(masspoint)); return masspoints.size() - 1; } void MassSpringSystemSimulator::addSpring(int masspoint1, int masspoint2, float initialLength) { auto mp1 = masspoints.at(masspoint1); auto mp2 = masspoints.at(masspoint2); Spring spring; spring.mp1 = mp1; spring.mp2 = mp2; spring.initialLength = initialLength; springs.push_back(spring); } int MassSpringSystemSimulator::getNumberOfMassPoints() { return masspoints.size(); } int MassSpringSystemSimulator::getNumberOfSprings() { return springs.size(); } Vec3 MassSpringSystemSimulator::getPositionOfMassPoint(int index) { auto mp = masspoints.at(index); return mp->position; } Vec3 MassSpringSystemSimulator::getVelocityOfMassPoint(int index) { auto mp = masspoints.at(index); return mp->velocity; } void MassSpringSystemSimulator::applyExternalForce(Vec3 force) { } Vec3 MassSpringSystemSimulator::calcualtePositionTimestepEuler(Vec3 oldPosition, float timestep, Vec3 velocity) { return oldPosition + timestep * velocity; } Vec3 MassSpringSystemSimulator::calcualteVelocityTimestepEuler(Vec3 oldVelocity, float timestep, Vec3 acceleration) { return oldVelocity + acceleration * timestep; } Vec3 MassSpringSystemSimulator::calculateAcceleration(Vec3 force, float mass) { return force / mass; } void MassSpringSystemSimulator::Euler(MassPoint& masspoint1, MassPoint& masspoint2, Spring& spring, float timestep) { //take old position and send to calculatePositionTimestepEuler auto PosVector = masspoint1.position - masspoint2.position; auto lengthVector = sqrt(PosVector.x * PosVector.x + PosVector.y * PosVector.y + PosVector.z * PosVector.z); auto normalized = PosVector / lengthVector; // Actual Calculation // Force of spring is -k * (l - L) * normalizedVector [for P2 we can take -F1) auto force = -m_fStiffness * (lengthVector - spring.initialLength) * normalized; auto foreP2 = -1 * force; auto veloc = calcualteVelocityTimestepEuler(masspoint1.velocity, timestep, calculateAcceleration(force, 10.)); auto pos = calcualtePositionTimestepEuler(masspoint1.position, timestep, veloc); auto veloc2 = calcualteVelocityTimestepEuler(masspoint2.velocity, timestep, calculateAcceleration(foreP2, 10.)); auto pos2 = calcualtePositionTimestepEuler(masspoint2.position, timestep, veloc2); // Update Positions and Velocity masspoint1.position = pos; masspoint1.velocity = veloc; masspoint2.position = pos2; masspoint2.velocity = veloc2; } void MassSpringSystemSimulator::printSpring(const Spring& spring) { auto mp1 = spring.mp1.lock(); auto mp2 = spring.mp2.lock(); printf("Masspoint 1:\nPosition: %s \nVelocity: %s\n\n", mp1->position.toString().c_str(), mp1->velocity.toString().c_str()); printf("Masspoint 2:\nPosition: %s \nVelocity: %s\n", mp2->position.toString().c_str(), mp2->velocity.toString().c_str()); }