cloth2

diff src/cloth.cc @ 0:ef0c22554406

cloth sim test, initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 11 Jan 2016 16:51:16 +0200
parents
children dc15b741486c
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/cloth.cc	Mon Jan 11 16:51:16 2016 +0200
     1.3 @@ -0,0 +1,173 @@
     1.4 +#include <stdio.h>
     1.5 +#include <float.h>
     1.6 +#include "cloth.h"
     1.7 +#include "object.h"
     1.8 +#include "opengl.h"
     1.9 +
    1.10 +Cloth::Cloth()
    1.11 +{
    1.12 +	mass = 1.0;
    1.13 +	elast = 0.8;
    1.14 +	spring_k = 1.0;
    1.15 +	grav = Vector3(0, -9, 0);
    1.16 +}
    1.17 +
    1.18 +void Cloth::create_rect(int x, int y, float width, float height)
    1.19 +{
    1.20 +	int num = x * y;
    1.21 +	float dx = width / (float)x;
    1.22 +	float dy = height / (float)y;
    1.23 +
    1.24 +	masses.resize(num);
    1.25 +	conn.resize(num);
    1.26 +	for(int i=0; i<num; i++) {
    1.27 +		conn[i].resize(num);
    1.28 +	}
    1.29 +
    1.30 +	int idx = 0;
    1.31 +	for(int i=0; i<y; i++) {
    1.32 +		for(int j=0; j<x; j++) {
    1.33 +			// position the mass
    1.34 +			masses[idx].pos.x = (float)j * dx - width / 2.0;
    1.35 +			masses[idx].pos.y = 0;
    1.36 +			masses[idx].pos.z = (float)i * dy - height / 2.0;
    1.37 +			masses[idx].fixed = i == 0 && (j == 0 || j == x - 1);	// XXX debug hack
    1.38 +			++idx;
    1.39 +		}
    1.40 +	}
    1.41 +
    1.42 +	idx = 0;
    1.43 +	for(int i=0; i<y; i++) {
    1.44 +		for(int j=0; j<x; j++) {
    1.45 +			// not connected to itself
    1.46 +			conn[idx][idx] = -1;
    1.47 +
    1.48 +			// connect node to its neighbors
    1.49 +			for(int ni=0; ni<5; ni++) {
    1.50 +				for(int nj=0; nj<5; nj++) {
    1.51 +					int nx = j + nj - 2;
    1.52 +					int ny = i + ni - 2;
    1.53 +
    1.54 +					if(!nx && !ny) continue;
    1.55 +					if(nx < 0 || nx >= x || ny < 0 || ny >= y) continue;
    1.56 +
    1.57 +					int nidx = ny * x + nx;
    1.58 +					float dist = length(masses[idx].pos - masses[nidx].pos);
    1.59 +
    1.60 +					conn[idx][nidx] = conn[nidx][idx] = dist;
    1.61 +				}
    1.62 +			}
    1.63 +
    1.64 +			++idx;
    1.65 +		}
    1.66 +	}
    1.67 +}
    1.68 +
    1.69 +void Cloth::transform(const Matrix4x4 &xform)
    1.70 +{
    1.71 +	for(size_t i=0; i<masses.size(); i++) {
    1.72 +		masses[i].pos = masses[i].pos * xform;
    1.73 +	}
    1.74 +}
    1.75 +
    1.76 +void Cloth::add_collider(Object *o)
    1.77 +{
    1.78 +	colliders.push_back(o);
    1.79 +}
    1.80 +
    1.81 +void Cloth::step(float dt)
    1.82 +{
    1.83 +	int num_masses = (int)masses.size();
    1.84 +
    1.85 +	for(int i=0; i<num_masses; i++) {
    1.86 +		if(masses[i].fixed) continue;
    1.87 +
    1.88 +		Vector3 newpos = masses[i].pos + masses[i].vel * dt;
    1.89 +
    1.90 +		Vector3 force = grav + forces;
    1.91 +
    1.92 +		for(int j=0; j<num_masses; j++) {
    1.93 +			if(conn[i][j] <= 0.0) continue;	// skip unconnected
    1.94 +
    1.95 +			Vector3 dir = masses[j].pos - masses[i].pos;
    1.96 +			float dist = length(dir);
    1.97 +
    1.98 +			// apply an impulse based on the displacement from the original state
    1.99 +			force += dir * (dist - conn[i][j]) * spring_k;
   1.100 +		}
   1.101 +
   1.102 +		// update the velocity by integrating the accumulation of all forces
   1.103 +		masses[i].vel += force * dt;
   1.104 +
   1.105 +		Ray ray;
   1.106 +		ray.origin = masses[i].pos;
   1.107 +		ray.dir = newpos - masses[i].pos;
   1.108 +
   1.109 +		HitPoint pt;
   1.110 +		if(find_collision(ray, &pt)) {
   1.111 +			newpos = pt.pos;
   1.112 +			masses[i].vel = reflect(masses[i].vel, pt.normal) * elast;	// TODO collider elasticity
   1.113 +		}
   1.114 +		masses[i].pos = newpos;
   1.115 +	}
   1.116 +}
   1.117 +
   1.118 +bool Cloth::find_collision(const Ray &ray, HitPoint *hit) const
   1.119 +{
   1.120 +	HitPoint nearest;
   1.121 +	nearest.obj = 0;
   1.122 +	nearest.t = FLT_MAX;
   1.123 +
   1.124 +	for(size_t i=0; i<colliders.size(); i++) {
   1.125 +		if(colliders[i]->intersect(ray, hit) && hit->t < nearest.t) {
   1.126 +			nearest = *hit;
   1.127 +		}
   1.128 +	}
   1.129 +
   1.130 +	if(nearest.obj) {
   1.131 +		*hit = nearest;
   1.132 +		return true;
   1.133 +	}
   1.134 +	return false;
   1.135 +}
   1.136 +
   1.137 +void Cloth::draw() const
   1.138 +{
   1.139 +	glPushAttrib(GL_ENABLE_BIT);
   1.140 +
   1.141 +	glPointSize(7.0);
   1.142 +	glEnable(GL_POINT_SMOOTH);
   1.143 +
   1.144 +	glEnable(GL_BLEND);
   1.145 +	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   1.146 +
   1.147 +	// draw masses
   1.148 +	glBegin(GL_POINTS);
   1.149 +	for(size_t i=0; i<masses.size(); i++) {
   1.150 +		if(masses[i].fixed) {
   1.151 +			glColor3f(1, 0, 0);
   1.152 +		} else {
   1.153 +			glColor3f(1, 0.4, 0.2);
   1.154 +		}
   1.155 +		glVertex3f(masses[i].pos.x, masses[i].pos.y, masses[i].pos.z);
   1.156 +	}
   1.157 +	glEnd();
   1.158 +
   1.159 +	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
   1.160 +
   1.161 +	// draw neighbor connection lines
   1.162 +	glBegin(GL_LINES);
   1.163 +	glColor4f(0, 1, 0, 0.25);
   1.164 +
   1.165 +	for(size_t i=0; i<masses.size(); i++) {
   1.166 +		for(size_t j=0; j<masses.size(); j++) {
   1.167 +			if(i == j || conn[i][j] <= 0.0) continue;
   1.168 +
   1.169 +			glVertex3f(masses[i].pos.x, masses[i].pos.y, masses[i].pos.z);
   1.170 +			glVertex3f(masses[j].pos.x, masses[j].pos.y, masses[j].pos.z);
   1.171 +		}
   1.172 +	}
   1.173 +	glEnd();
   1.174 +
   1.175 +	glPopAttrib();
   1.176 +}