rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <float.h>
|
nuclear@0
|
3 #include "cloth.h"
|
nuclear@0
|
4 #include "object.h"
|
nuclear@0
|
5 #include "opengl.h"
|
nuclear@0
|
6
|
nuclear@0
|
7 Cloth::Cloth()
|
nuclear@0
|
8 {
|
nuclear@0
|
9 mass = 1.0;
|
nuclear@0
|
10 elast = 0.8;
|
nuclear@0
|
11 spring_k = 1.0;
|
nuclear@0
|
12 grav = Vector3(0, -9, 0);
|
nuclear@0
|
13 }
|
nuclear@0
|
14
|
nuclear@0
|
15 void Cloth::create_rect(int x, int y, float width, float height)
|
nuclear@0
|
16 {
|
nuclear@0
|
17 int num = x * y;
|
nuclear@0
|
18 float dx = width / (float)x;
|
nuclear@0
|
19 float dy = height / (float)y;
|
nuclear@0
|
20
|
nuclear@0
|
21 masses.resize(num);
|
nuclear@0
|
22 conn.resize(num);
|
nuclear@0
|
23 for(int i=0; i<num; i++) {
|
nuclear@0
|
24 conn[i].resize(num);
|
nuclear@0
|
25 }
|
nuclear@0
|
26
|
nuclear@0
|
27 int idx = 0;
|
nuclear@0
|
28 for(int i=0; i<y; i++) {
|
nuclear@0
|
29 for(int j=0; j<x; j++) {
|
nuclear@0
|
30 // position the mass
|
nuclear@0
|
31 masses[idx].pos.x = (float)j * dx - width / 2.0;
|
nuclear@0
|
32 masses[idx].pos.y = 0;
|
nuclear@0
|
33 masses[idx].pos.z = (float)i * dy - height / 2.0;
|
nuclear@0
|
34 masses[idx].fixed = i == 0 && (j == 0 || j == x - 1); // XXX debug hack
|
nuclear@0
|
35 ++idx;
|
nuclear@0
|
36 }
|
nuclear@0
|
37 }
|
nuclear@0
|
38
|
nuclear@0
|
39 idx = 0;
|
nuclear@0
|
40 for(int i=0; i<y; i++) {
|
nuclear@0
|
41 for(int j=0; j<x; j++) {
|
nuclear@0
|
42 // not connected to itself
|
nuclear@0
|
43 conn[idx][idx] = -1;
|
nuclear@0
|
44
|
nuclear@0
|
45 // connect node to its neighbors
|
nuclear@0
|
46 for(int ni=0; ni<5; ni++) {
|
nuclear@0
|
47 for(int nj=0; nj<5; nj++) {
|
nuclear@0
|
48 int nx = j + nj - 2;
|
nuclear@0
|
49 int ny = i + ni - 2;
|
nuclear@0
|
50
|
nuclear@0
|
51 if(!nx && !ny) continue;
|
nuclear@0
|
52 if(nx < 0 || nx >= x || ny < 0 || ny >= y) continue;
|
nuclear@0
|
53
|
nuclear@0
|
54 int nidx = ny * x + nx;
|
nuclear@0
|
55 float dist = length(masses[idx].pos - masses[nidx].pos);
|
nuclear@0
|
56
|
nuclear@0
|
57 conn[idx][nidx] = conn[nidx][idx] = dist;
|
nuclear@0
|
58 }
|
nuclear@0
|
59 }
|
nuclear@0
|
60
|
nuclear@0
|
61 ++idx;
|
nuclear@0
|
62 }
|
nuclear@0
|
63 }
|
nuclear@0
|
64 }
|
nuclear@0
|
65
|
nuclear@0
|
66 void Cloth::transform(const Matrix4x4 &xform)
|
nuclear@0
|
67 {
|
nuclear@0
|
68 for(size_t i=0; i<masses.size(); i++) {
|
nuclear@0
|
69 masses[i].pos = masses[i].pos * xform;
|
nuclear@0
|
70 }
|
nuclear@0
|
71 }
|
nuclear@0
|
72
|
nuclear@0
|
73 void Cloth::add_collider(Object *o)
|
nuclear@0
|
74 {
|
nuclear@0
|
75 colliders.push_back(o);
|
nuclear@0
|
76 }
|
nuclear@0
|
77
|
nuclear@0
|
78 void Cloth::step(float dt)
|
nuclear@0
|
79 {
|
nuclear@0
|
80 int num_masses = (int)masses.size();
|
nuclear@0
|
81
|
nuclear@0
|
82 for(int i=0; i<num_masses; i++) {
|
nuclear@0
|
83 if(masses[i].fixed) continue;
|
nuclear@0
|
84
|
nuclear@0
|
85 Vector3 newpos = masses[i].pos + masses[i].vel * dt;
|
nuclear@0
|
86
|
nuclear@0
|
87 Vector3 force = grav + forces;
|
nuclear@0
|
88
|
nuclear@0
|
89 for(int j=0; j<num_masses; j++) {
|
nuclear@0
|
90 if(conn[i][j] <= 0.0) continue; // skip unconnected
|
nuclear@0
|
91
|
nuclear@0
|
92 Vector3 dir = masses[j].pos - masses[i].pos;
|
nuclear@0
|
93 float dist = length(dir);
|
nuclear@0
|
94
|
nuclear@0
|
95 // apply an impulse based on the displacement from the original state
|
nuclear@0
|
96 force += dir * (dist - conn[i][j]) * spring_k;
|
nuclear@0
|
97 }
|
nuclear@0
|
98
|
nuclear@0
|
99 // update the velocity by integrating the accumulation of all forces
|
nuclear@0
|
100 masses[i].vel += force * dt;
|
nuclear@0
|
101
|
nuclear@0
|
102 Ray ray;
|
nuclear@0
|
103 ray.origin = masses[i].pos;
|
nuclear@0
|
104 ray.dir = newpos - masses[i].pos;
|
nuclear@0
|
105
|
nuclear@0
|
106 HitPoint pt;
|
nuclear@0
|
107 if(find_collision(ray, &pt)) {
|
nuclear@0
|
108 newpos = pt.pos;
|
nuclear@0
|
109 masses[i].vel = reflect(masses[i].vel, pt.normal) * elast; // TODO collider elasticity
|
nuclear@0
|
110 }
|
nuclear@0
|
111 masses[i].pos = newpos;
|
nuclear@0
|
112 }
|
nuclear@0
|
113 }
|
nuclear@0
|
114
|
nuclear@0
|
115 bool Cloth::find_collision(const Ray &ray, HitPoint *hit) const
|
nuclear@0
|
116 {
|
nuclear@0
|
117 HitPoint nearest;
|
nuclear@0
|
118 nearest.obj = 0;
|
nuclear@0
|
119 nearest.t = FLT_MAX;
|
nuclear@0
|
120
|
nuclear@0
|
121 for(size_t i=0; i<colliders.size(); i++) {
|
nuclear@0
|
122 if(colliders[i]->intersect(ray, hit) && hit->t < nearest.t) {
|
nuclear@0
|
123 nearest = *hit;
|
nuclear@0
|
124 }
|
nuclear@0
|
125 }
|
nuclear@0
|
126
|
nuclear@0
|
127 if(nearest.obj) {
|
nuclear@0
|
128 *hit = nearest;
|
nuclear@0
|
129 return true;
|
nuclear@0
|
130 }
|
nuclear@0
|
131 return false;
|
nuclear@0
|
132 }
|
nuclear@0
|
133
|
nuclear@0
|
134 void Cloth::draw() const
|
nuclear@0
|
135 {
|
nuclear@0
|
136 glPushAttrib(GL_ENABLE_BIT);
|
nuclear@0
|
137
|
nuclear@0
|
138 glPointSize(7.0);
|
nuclear@0
|
139 glEnable(GL_POINT_SMOOTH);
|
nuclear@0
|
140
|
nuclear@0
|
141 glEnable(GL_BLEND);
|
nuclear@0
|
142 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
nuclear@0
|
143
|
nuclear@0
|
144 // draw masses
|
nuclear@0
|
145 glBegin(GL_POINTS);
|
nuclear@0
|
146 for(size_t i=0; i<masses.size(); i++) {
|
nuclear@0
|
147 if(masses[i].fixed) {
|
nuclear@0
|
148 glColor3f(1, 0, 0);
|
nuclear@0
|
149 } else {
|
nuclear@0
|
150 glColor3f(1, 0.4, 0.2);
|
nuclear@0
|
151 }
|
nuclear@0
|
152 glVertex3f(masses[i].pos.x, masses[i].pos.y, masses[i].pos.z);
|
nuclear@0
|
153 }
|
nuclear@0
|
154 glEnd();
|
nuclear@0
|
155
|
nuclear@0
|
156 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
nuclear@0
|
157
|
nuclear@0
|
158 // draw neighbor connection lines
|
nuclear@0
|
159 glBegin(GL_LINES);
|
nuclear@0
|
160 glColor4f(0, 1, 0, 0.25);
|
nuclear@0
|
161
|
nuclear@0
|
162 for(size_t i=0; i<masses.size(); i++) {
|
nuclear@0
|
163 for(size_t j=0; j<masses.size(); j++) {
|
nuclear@0
|
164 if(i == j || conn[i][j] <= 0.0) continue;
|
nuclear@0
|
165
|
nuclear@0
|
166 glVertex3f(masses[i].pos.x, masses[i].pos.y, masses[i].pos.z);
|
nuclear@0
|
167 glVertex3f(masses[j].pos.x, masses[j].pos.y, masses[j].pos.z);
|
nuclear@0
|
168 }
|
nuclear@0
|
169 }
|
nuclear@0
|
170 glEnd();
|
nuclear@0
|
171
|
nuclear@0
|
172 glPopAttrib();
|
nuclear@0
|
173 }
|