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@1
|
12 damping = 0.01;
|
nuclear@0
|
13 grav = Vector3(0, -9, 0);
|
nuclear@0
|
14 }
|
nuclear@0
|
15
|
nuclear@0
|
16 void Cloth::create_rect(int x, int y, float width, float height)
|
nuclear@0
|
17 {
|
nuclear@0
|
18 int num = x * y;
|
nuclear@0
|
19 float dx = width / (float)x;
|
nuclear@0
|
20 float dy = height / (float)y;
|
nuclear@0
|
21
|
nuclear@0
|
22 masses.resize(num);
|
nuclear@0
|
23 conn.resize(num);
|
nuclear@0
|
24 for(int i=0; i<num; i++) {
|
nuclear@0
|
25 conn[i].resize(num);
|
nuclear@0
|
26 }
|
nuclear@0
|
27
|
nuclear@0
|
28 int idx = 0;
|
nuclear@0
|
29 for(int i=0; i<y; i++) {
|
nuclear@0
|
30 for(int j=0; j<x; j++) {
|
nuclear@0
|
31 // position the mass
|
nuclear@0
|
32 masses[idx].pos.x = (float)j * dx - width / 2.0;
|
nuclear@0
|
33 masses[idx].pos.y = 0;
|
nuclear@0
|
34 masses[idx].pos.z = (float)i * dy - height / 2.0;
|
nuclear@1
|
35 masses[idx].fixed = i == 0; // XXX debug hack
|
nuclear@0
|
36 ++idx;
|
nuclear@0
|
37 }
|
nuclear@0
|
38 }
|
nuclear@0
|
39
|
nuclear@0
|
40 idx = 0;
|
nuclear@0
|
41 for(int i=0; i<y; i++) {
|
nuclear@0
|
42 for(int j=0; j<x; j++) {
|
nuclear@0
|
43 // not connected to itself
|
nuclear@0
|
44 conn[idx][idx] = -1;
|
nuclear@0
|
45
|
nuclear@0
|
46 // connect node to its neighbors
|
nuclear@1
|
47 for(int ni=0; ni<3; ni++) {
|
nuclear@1
|
48 for(int nj=0; nj<3; nj++) {
|
nuclear@1
|
49 int nx = j + nj - 1;
|
nuclear@1
|
50 int ny = i + ni - 1;
|
nuclear@0
|
51
|
nuclear@0
|
52 if(!nx && !ny) continue;
|
nuclear@0
|
53 if(nx < 0 || nx >= x || ny < 0 || ny >= y) continue;
|
nuclear@0
|
54
|
nuclear@0
|
55 int nidx = ny * x + nx;
|
nuclear@0
|
56 float dist = length(masses[idx].pos - masses[nidx].pos);
|
nuclear@0
|
57
|
nuclear@0
|
58 conn[idx][nidx] = conn[nidx][idx] = dist;
|
nuclear@0
|
59 }
|
nuclear@0
|
60 }
|
nuclear@0
|
61
|
nuclear@0
|
62 ++idx;
|
nuclear@0
|
63 }
|
nuclear@0
|
64 }
|
nuclear@0
|
65 }
|
nuclear@0
|
66
|
nuclear@1
|
67 void Cloth::set_mass(float m)
|
nuclear@1
|
68 {
|
nuclear@1
|
69 mass = m;
|
nuclear@1
|
70 }
|
nuclear@1
|
71
|
nuclear@1
|
72 void Cloth::set_elasticity(float e)
|
nuclear@1
|
73 {
|
nuclear@1
|
74 elast = e;
|
nuclear@1
|
75 }
|
nuclear@1
|
76
|
nuclear@1
|
77 void Cloth::set_spring_constant(float k)
|
nuclear@1
|
78 {
|
nuclear@1
|
79 spring_k = k;
|
nuclear@1
|
80 }
|
nuclear@1
|
81
|
nuclear@1
|
82 void Cloth::set_damping(float d)
|
nuclear@1
|
83 {
|
nuclear@1
|
84 damping = d;
|
nuclear@1
|
85 }
|
nuclear@1
|
86
|
nuclear@1
|
87 void Cloth::set_gravity(const Vector3 &g)
|
nuclear@1
|
88 {
|
nuclear@1
|
89 grav = g;
|
nuclear@1
|
90 }
|
nuclear@1
|
91
|
nuclear@1
|
92 void Cloth::set_force(const Vector3 &f)
|
nuclear@1
|
93 {
|
nuclear@1
|
94 forces = f;
|
nuclear@1
|
95 }
|
nuclear@1
|
96
|
nuclear@0
|
97 void Cloth::transform(const Matrix4x4 &xform)
|
nuclear@0
|
98 {
|
nuclear@0
|
99 for(size_t i=0; i<masses.size(); i++) {
|
nuclear@0
|
100 masses[i].pos = masses[i].pos * xform;
|
nuclear@0
|
101 }
|
nuclear@0
|
102 }
|
nuclear@0
|
103
|
nuclear@0
|
104 void Cloth::add_collider(Object *o)
|
nuclear@0
|
105 {
|
nuclear@0
|
106 colliders.push_back(o);
|
nuclear@0
|
107 }
|
nuclear@0
|
108
|
nuclear@0
|
109 void Cloth::step(float dt)
|
nuclear@0
|
110 {
|
nuclear@0
|
111 int num_masses = (int)masses.size();
|
nuclear@0
|
112
|
nuclear@1
|
113 Vector3 *newp = new Vector3[num_masses];
|
nuclear@1
|
114
|
nuclear@0
|
115 for(int i=0; i<num_masses; i++) {
|
nuclear@1
|
116 if(masses[i].fixed) {
|
nuclear@1
|
117 newp[i] = masses[i].pos;
|
nuclear@1
|
118 continue;
|
nuclear@1
|
119 }
|
nuclear@0
|
120
|
nuclear@0
|
121 Vector3 force = grav + forces;
|
nuclear@0
|
122
|
nuclear@0
|
123 for(int j=0; j<num_masses; j++) {
|
nuclear@1
|
124 if(i == j || conn[i][j] <= 0.0) {
|
nuclear@1
|
125 continue; // skip unconnected
|
nuclear@1
|
126 }
|
nuclear@0
|
127
|
nuclear@0
|
128 Vector3 dir = masses[j].pos - masses[i].pos;
|
nuclear@0
|
129 float dist = length(dir);
|
nuclear@1
|
130 float disp = dist - conn[i][j];
|
nuclear@1
|
131 if(fabs(disp) < 1e-5) disp = 0.0f;
|
nuclear@0
|
132
|
nuclear@0
|
133 // apply an impulse based on the displacement from the original state
|
nuclear@1
|
134 force += dir * ((disp * spring_k) / dist * 0.5);
|
nuclear@0
|
135 }
|
nuclear@0
|
136
|
nuclear@0
|
137 // update the velocity by integrating the accumulation of all forces
|
nuclear@1
|
138 Vector3 accel = force / mass;
|
nuclear@1
|
139 masses[i].vel = masses[i].vel * (1.0 - damping) + accel * dt;
|
nuclear@1
|
140
|
nuclear@1
|
141 Vector3 newpos = masses[i].pos + masses[i].vel * dt;
|
nuclear@0
|
142
|
nuclear@0
|
143 Ray ray;
|
nuclear@0
|
144 ray.origin = masses[i].pos;
|
nuclear@0
|
145 ray.dir = newpos - masses[i].pos;
|
nuclear@0
|
146
|
nuclear@0
|
147 HitPoint pt;
|
nuclear@0
|
148 if(find_collision(ray, &pt)) {
|
nuclear@0
|
149 newpos = pt.pos;
|
nuclear@0
|
150 masses[i].vel = reflect(masses[i].vel, pt.normal) * elast; // TODO collider elasticity
|
nuclear@0
|
151 }
|
nuclear@1
|
152 //masses[i].pos = newpos;
|
nuclear@1
|
153 newp[i] = newpos;
|
nuclear@0
|
154 }
|
nuclear@1
|
155
|
nuclear@1
|
156 for(int i=0; i<num_masses; i++) {
|
nuclear@1
|
157 masses[i].pos = newp[i];
|
nuclear@1
|
158 }
|
nuclear@1
|
159
|
nuclear@1
|
160 delete [] newp;
|
nuclear@0
|
161 }
|
nuclear@0
|
162
|
nuclear@0
|
163 bool Cloth::find_collision(const Ray &ray, HitPoint *hit) const
|
nuclear@0
|
164 {
|
nuclear@0
|
165 HitPoint nearest;
|
nuclear@0
|
166 nearest.obj = 0;
|
nuclear@0
|
167 nearest.t = FLT_MAX;
|
nuclear@0
|
168
|
nuclear@0
|
169 for(size_t i=0; i<colliders.size(); i++) {
|
nuclear@0
|
170 if(colliders[i]->intersect(ray, hit) && hit->t < nearest.t) {
|
nuclear@0
|
171 nearest = *hit;
|
nuclear@0
|
172 }
|
nuclear@0
|
173 }
|
nuclear@0
|
174
|
nuclear@0
|
175 if(nearest.obj) {
|
nuclear@0
|
176 *hit = nearest;
|
nuclear@0
|
177 return true;
|
nuclear@0
|
178 }
|
nuclear@0
|
179 return false;
|
nuclear@0
|
180 }
|
nuclear@0
|
181
|
nuclear@0
|
182 void Cloth::draw() const
|
nuclear@0
|
183 {
|
nuclear@0
|
184 glPushAttrib(GL_ENABLE_BIT);
|
nuclear@0
|
185
|
nuclear@0
|
186 glPointSize(7.0);
|
nuclear@0
|
187 glEnable(GL_POINT_SMOOTH);
|
nuclear@0
|
188
|
nuclear@0
|
189 glEnable(GL_BLEND);
|
nuclear@0
|
190 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
nuclear@0
|
191
|
nuclear@0
|
192 // draw masses
|
nuclear@0
|
193 glBegin(GL_POINTS);
|
nuclear@0
|
194 for(size_t i=0; i<masses.size(); i++) {
|
nuclear@0
|
195 if(masses[i].fixed) {
|
nuclear@0
|
196 glColor3f(1, 0, 0);
|
nuclear@0
|
197 } else {
|
nuclear@0
|
198 glColor3f(1, 0.4, 0.2);
|
nuclear@0
|
199 }
|
nuclear@0
|
200 glVertex3f(masses[i].pos.x, masses[i].pos.y, masses[i].pos.z);
|
nuclear@0
|
201 }
|
nuclear@0
|
202 glEnd();
|
nuclear@0
|
203
|
nuclear@0
|
204 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
nuclear@0
|
205
|
nuclear@0
|
206 // draw neighbor connection lines
|
nuclear@0
|
207 glBegin(GL_LINES);
|
nuclear@0
|
208 glColor4f(0, 1, 0, 0.25);
|
nuclear@0
|
209
|
nuclear@0
|
210 for(size_t i=0; i<masses.size(); i++) {
|
nuclear@0
|
211 for(size_t j=0; j<masses.size(); j++) {
|
nuclear@0
|
212 if(i == j || conn[i][j] <= 0.0) continue;
|
nuclear@0
|
213
|
nuclear@0
|
214 glVertex3f(masses[i].pos.x, masses[i].pos.y, masses[i].pos.z);
|
nuclear@0
|
215 glVertex3f(masses[j].pos.x, masses[j].pos.y, masses[j].pos.z);
|
nuclear@0
|
216 }
|
nuclear@0
|
217 }
|
nuclear@0
|
218 glEnd();
|
nuclear@0
|
219
|
nuclear@0
|
220 glPopAttrib();
|
nuclear@0
|
221 }
|