Jump to content
  • Advertisement
Sign in to follow this  

Simulating hair physics

This topic is 2342 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I'd like to ask some help and suggestions about simulating hair physics (so basically pulling hair vertices around, and smoothly disposing the adjacent ones).

Please take a look at the embedded video (which shows an application tha can do it): the hair displayed there is made of 15 vertices; once the user drags the active vertices (those inside the circular cursor area), all the hair vertices are moved and repositioned accordingly. To notice that hair length remain the same during the process.

Thanks for any help.

[media]
[/media]

Share this post


Link to post
Share on other sites
Advertisement
Sorry, I had troubles embedding the YT movie, now it's there.

Well, probably "simulating physics" was not really appropriate, since there is no gravity or dynamic forces that I need to apply to that hair. Nonetheless, I need to pull out vertices just as you would with a normal hair... Edited by Alessandro

Share this post


Link to post
Share on other sites
You could probably use the same method used here (though you could simplify it a lot since you won't be simulating full 3D bodies, just hair). Just make a "rope" of vertices, each one attached to the one before and after it, set a min/max distance between each pair of vertices, and then use Verlet integration and a simple constraint solver to handle the constraints. You could add gravity or not, it's up to you.

Share this post


Link to post
Share on other sites

You could probably use the same method used here (though you could simplify it a lot since you won't be simulating full 3D bodies, just hair). Just make a "rope" of vertices, each one attached to the one before and after it, set a min/max distance between each pair of vertices, and then use Verlet integration and a simple constraint solver to handle the constraints. You could add gravity or not, it's up to you.


I already have a "rope" of vertices, all attached to each other. I've read the paper and honestly didn't understand much of it.
Would it be possible to get some pseudo-code that would show how, for example, having a rope of 5 vertices, move those around while maintaining a fixed distance between each vertex?

Share this post


Link to post
Share on other sites
Sure. Note that this code doesn't use a Verlet integrator because I'm not doing anything with gravity, and I'm not giving any momentum/velocity to the vertices. I appologize if some of the variable names aren't the clearest; rename them if it helps you understand things better. Most of this comes from page two of that article I linked. It's been awhile since I've done any of this and thought it might be a fun challenge. Yes, it's 2D, but the concepts are exactly the same for 3D.

#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>

#include <iostream>
#include <cmath>

// Some settings. Adjust these to play around
float distance = 20.0f;
int numVerts = 20;
float circleRadius = 50.0f;
int numIterations = 10;

std::vector<sf::Vector2f> verts;
sf::Vector2f mousePos;

int main(int argc, char* argv[])
{
sf::RenderWindow renderWindow(sf::VideoMode(800, 600), "Meh title");

// Initialize the list of vertices
for (int i = 0; i < numVerts; ++i)
{
sf::Vector2f v(renderWindow.GetWidth() / 2, i * distance);
verts.push_back(v);
}

std::clock_t lastUpdate = std::clock();
while (renderWindow.IsOpened())
{
// ~60 FPS
if (std::clock() - lastUpdate >= 1000 / 60)
{
lastUpdate = std::clock();

// Capture mouse position
sf::Vector2i mp = sf::Mouse::GetPosition(renderWindow);
mousePos.x = mp.x;
mousePos.y = mp.y;

renderWindow.Clear();

// THIS IS WHERE YOU WANT TO START PAYING ATTENTION
// This section just uses a couple of simple constraint solvers. The first one forces vertices inside the circle's mouse to outside it.
// The second one forces the root to stay at a fixed location. The third one forces the vertices to be a fixed distance from each other.
// More iterations in the constraint solver means greater accuracy. The cost is speed, of course.
for (int iterationCounter = 0; iterationCounter < numIterations; ++iterationCounter)
{
// First constraint: Interact with the circle
for (std::size_t i = 0; i < verts.size(); ++i)
{
sf::Vector2f dif = verts - mousePos;
if (dif.x * dif.x + dif.y * dif.y < circleRadius * circleRadius)
{
float length = std::sqrt(dif.x * dif.x + dif.y * dif.y);
float dist = circleRadius - length;
dif /= length;
dif *= dist;
verts += dif;
}
}

// Second constraint: Fix the root
verts[0] = sf::Vector2f(renderWindow.GetWidth() / 2, 0);

// Third constraint: Fix the distance constraint of the verts
for (std::size_t i = 0; i < verts.size() - 1; ++i)
{
sf::Vector2f dif = verts - verts[i + 1];
if (dif.x * dif.x + dif.y * dif.y != distance * distance)
{
float length = std::sqrt(dif.x * dif.x + dif.y * dif.y);
float dist = distance - length;
dif /= length;
dif *= dist * 0.5f; // each vert gets half the distance
verts += dif;
verts[i + 1] -= dif;
}
}
}
// YOU CAN STOP PAYING ATTENTION HERE

// Draw the line/rope/hair and the vertices
for (std::size_t i = 0; i < verts.size() - 1; ++i)
{
sf::Shape line = sf::Shape::Line(verts, verts[i + 1], 1.0f, sf::Color::Red);
renderWindow.Draw(line);
sf::Shape circle = sf::Shape::Circle(verts, 2.0f, sf::Color::Yellow, 1.0f, sf::Color::Yellow);
circle.EnableFill(false);
renderWindow.Draw(circle);
}

// Draw the circle around the mouse
sf::Shape circle = sf::Shape::Circle(mousePos, circleRadius, sf::Color::Green, 1.0f, sf::Color::Green);
circle.EnableFill(false);
renderWindow.Draw(circle);

renderWindow.Display();
}

sf::Event event;
if (renderWindow.PollEvent(event))
{
if (event.Type == sf::Event::Closed)
{
renderWindow.Close();
}
}
}
}


[edit]

Do source tags no longer do automatic syntax highlighting? And how do I specify the language in source tags?

Share this post


Link to post
Share on other sites
Dear CornsTalks, thanks so much for this code you provided; very much appreciated as I believe you spent quite some time on it. Thanks again, I'm now going to study it and report :P


Sure. Note that this code doesn't use a Verlet integrator because I'm not doing anything with gravity, and I'm not giving any momentum/velocity to the vertices. I appologize if some of the variable names aren't the clearest; rename them if it helps you understand things better. Most of this comes from page two of that article I linked. It's been awhile since I've done any of this and thought it might be a fun challenge. Yes, it's 2D, but the concepts are exactly the same for 3D.

#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>

#include <iostream>
#include <cmath>

// Some settings. Adjust these to play around
float distance = 20.0f;
int numVerts = 20;
float circleRadius = 50.0f;
int numIterations = 10;

std::vector<sf::Vector2f> verts;
sf::Vector2f mousePos;

int main(int argc, char* argv[])
{
sf::RenderWindow renderWindow(sf::VideoMode(800, 600), "Meh title");

// Initialize the list of vertices
for (int i = 0; i < numVerts; ++i)
{
sf::Vector2f v(renderWindow.GetWidth() / 2, i * distance);
verts.push_back(v);
}

std::clock_t lastUpdate = std::clock();
while (renderWindow.IsOpened())
{
// ~60 FPS
if (std::clock() - lastUpdate >= 1000 / 60)
{
lastUpdate = std::clock();

// Capture mouse position
sf::Vector2i mp = sf::Mouse::GetPosition(renderWindow);
mousePos.x = mp.x;
mousePos.y = mp.y;

renderWindow.Clear();

// THIS IS WHERE YOU WANT TO START PAYING ATTENTION
// This section just uses a couple of simple constraint solvers. The first one forces vertices inside the circle's mouse to outside it.
// The second one forces the root to stay at a fixed location. The third one forces the vertices to be a fixed distance from each other.
// More iterations in the constraint solver means greater accuracy. The cost is speed, of course.
for (int iterationCounter = 0; iterationCounter < numIterations; ++iterationCounter)
{
// First constraint: Interact with the circle
for (std::size_t i = 0; i < verts.size(); ++i)
{
sf::Vector2f dif = verts - mousePos;
if (dif.x * dif.x + dif.y * dif.y < circleRadius * circleRadius)
{
float length = std::sqrt(dif.x * dif.x + dif.y * dif.y);
float dist = circleRadius - length;
dif /= length;
dif *= dist;
verts += dif;
}
}

// Second constraint: Fix the root
verts[0] = sf::Vector2f(renderWindow.GetWidth() / 2, 0);

// Third constraint: Fix the distance constraint of the verts
for (std::size_t i = 0; i < verts.size() - 1; ++i)
{
sf::Vector2f dif = verts - verts[i + 1];
if (dif.x * dif.x + dif.y * dif.y != distance * distance)
{
float length = std::sqrt(dif.x * dif.x + dif.y * dif.y);
float dist = distance - length;
dif /= length;
dif *= dist * 0.5f; // each vert gets half the distance
verts += dif;
verts[i + 1] -= dif;
}
}
}
// YOU CAN STOP PAYING ATTENTION HERE

// Draw the line/rope/hair and the vertices
for (std::size_t i = 0; i < verts.size() - 1; ++i)
{
sf::Shape line = sf::Shape::Line(verts, verts[i + 1], 1.0f, sf::Color::Red);
renderWindow.Draw(line);
sf::Shape circle = sf::Shape::Circle(verts, 2.0f, sf::Color::Yellow, 1.0f, sf::Color::Yellow);
circle.EnableFill(false);
renderWindow.Draw(circle);
}

// Draw the circle around the mouse
sf::Shape circle = sf::Shape::Circle(mousePos, circleRadius, sf::Color::Green, 1.0f, sf::Color::Green);
circle.EnableFill(false);
renderWindow.Draw(circle);

renderWindow.Display();
}

sf::Event event;
if (renderWindow.PollEvent(event))
{
if (event.Type == sf::Event::Closed)
{
renderWindow.Close();
}
}
}
}


[edit]

Do source tags no longer do automatic syntax highlighting? And how do I specify the language in source tags?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!