OSDN Git Service

c8dc3895352175e4fb1cda7f04497da42758a6a3
[mikumikustudio/MikuMikuStudio.git] / src / com / jmex / effects / cloth / ClothPatch.java
1 /*
2  * Copyright (c) 2003-2009 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 
17  *   may be used to endorse or promote products derived from this software 
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 package com.jmex.effects.cloth;
34
35 import java.io.IOException;
36 import java.nio.FloatBuffer;
37
38 import com.jme.math.Vector2f;
39 import com.jme.math.Vector3f;
40 import com.jme.math.spring.SpringPoint;
41 import com.jme.math.spring.SpringPointForce;
42 import com.jme.math.spring.SpringSystem;
43 import com.jme.scene.TexCoords;
44 import com.jme.scene.TriMesh;
45 import com.jme.util.export.InputCapsule;
46 import com.jme.util.export.JMEExporter;
47 import com.jme.util.export.JMEImporter;
48 import com.jme.util.export.OutputCapsule;
49 import com.jme.util.geom.BufferUtils;
50
51 /**
52  * <code>ClothPatch</code> is a rectangular trimesh representing a piece of
53  * Cloth. It is backed up by and shares verts and normals with a SpringSystem.
54  * 
55  * @author Joshua Slack
56  * @version $Id: ClothPatch.java,v 1.12 2006/06/21 20:33:13 nca Exp $
57  */
58 public class ClothPatch extends TriMesh {
59     private static final long serialVersionUID = 1L;
60
61     /** width, number of nodes wide on x axis. */
62     protected int clothNodesX;
63     /** height, number of nodes high on y axis. */
64     protected int clothNodesY;
65     /** The initial spring length of structural springs. */
66     protected float springLength;
67     /** The underlying SpringSystem for this ClothPatch. */
68     protected SpringSystem system;
69     /** Internal time watch used to track time since last update. */
70     protected float sinceLast = 0;
71     /**
72      * Dilation factor to multiply elapsed time by for use in updating
73      * underlying system.
74      */
75     protected float timeDilation = 1.0f;
76
77     // Temp vars used to eliminates object creation
78     private Vector3f tNorm = new Vector3f();
79     private Vector3f tempV1 = new Vector3f(), tempV2 = new Vector3f(),
80             tempV3 = new Vector3f();
81
82     public ClothPatch() {
83     }
84
85     /**
86      * Public constructor.
87      * 
88      * @param name
89      *            String
90      * @param nodesX
91      *            number of nodes wide this cloth will be.
92      * @param nodesY
93      *            number of nodes high this cloth will be.
94      * @param springLength
95      *            distance between each node
96      * @param nodeMass
97      *            mass of an individual node in this Cloth.
98      */
99     public ClothPatch(String name, int nodesX, int nodesY, float springLength,
100             float nodeMass) {
101         super(name);
102         clothNodesX = nodesX;
103         clothNodesY = nodesY;
104         this.springLength = springLength;
105
106         setVertexCount(clothNodesY * clothNodesX);
107         setVertexBuffer(BufferUtils.createVector3Buffer(getVertexCount()));
108         setNormalBuffer(BufferUtils.createVector3Buffer(getVertexCount()));
109         getTextureCoords().set(0,
110                 new TexCoords(BufferUtils.createVector2Buffer(getVertexCount())));
111
112         setTriangleQuantity((clothNodesX - 1) * (clothNodesY - 1) * 2);
113         setIndexBuffer(BufferUtils.createIntBuffer(3 * getTriangleCount()));
114
115         initCloth(nodeMass);
116     }
117
118     public ClothPatch(String name, int nodesX, int nodesY, Vector3f upperLeft,
119             Vector3f lowerLeft, Vector3f lowerRight, Vector3f upperRight,
120             float nodeMass) {
121         super(name);
122         clothNodesX = nodesX;
123         clothNodesY = nodesY;
124         // this.springLength = springLength;
125
126         setVertexCount(clothNodesY * clothNodesX);
127         setVertexBuffer(BufferUtils.createVector3Buffer(getVertexCount()));
128         setNormalBuffer(BufferUtils.createVector3Buffer(getVertexCount()));
129         getTextureCoords().set(0,
130                 new TexCoords(BufferUtils.createVector2Buffer(getVertexCount())));
131
132         setTriangleQuantity((clothNodesX - 1) * (clothNodesY - 1) * 2);
133         setIndexBuffer(BufferUtils.createIntBuffer(3 * getTriangleCount()));
134
135         initCloth(nodeMass, upperLeft, lowerLeft, lowerRight, upperRight);
136     }
137
138     /**
139      * Add an external force to the underlying SpringSystem.
140      * 
141      * @param force
142      *            SpringPointForce
143      */
144     public void addForce(SpringPointForce force) {
145         system.addForce(force);
146     }
147
148     /**
149      * Remove a force from the underlying SpringSystem.
150      * 
151      * @param force
152      *            SpringPointForce
153      * @return true if found and removed.
154      */
155     public boolean removeForce(SpringPointForce force) {
156         return system.removeForce(force);
157     }
158
159     /**
160      * Update the normals in the system.
161      */
162     public void updateNormals() {
163         // zero out the normals
164         getNormalBuffer().clear();
165         for (int i = getNormalBuffer().capacity(); --i >= 0;)
166             getNormalBuffer().put(0);
167         // go through each triangle and add the tri norm to it's corner's norms
168         int i1, i2, i3;
169         getIndexBuffer().rewind();
170         for (int i = 0, iMax = getIndexBuffer().capacity(); i < iMax; i += 3) {
171             // grab triangle normal
172             i1 = getIndexBuffer().get();
173             i2 = getIndexBuffer().get();
174             i3 = getIndexBuffer().get();
175             getTriangleNormal(i1, i2, i3, tNorm);
176             BufferUtils.addInBuffer(tNorm, getNormalBuffer(), i1);
177             BufferUtils.addInBuffer(tNorm, getNormalBuffer(), i2);
178             BufferUtils.addInBuffer(tNorm, getNormalBuffer(), i3);
179         }
180         // normalize
181         for (int i = getVertexCount(); --i >= 0;)
182             BufferUtils.normalizeVector3(getNormalBuffer(), i);
183     }
184
185     /**
186      * Get the normal of the triangle defined by the given vertices. Please note
187      * that result is not normalized.
188      * 
189      * @param vert1
190      *            triangle point #1
191      * @param vert2
192      *            triangle point #2
193      * @param vert3
194      *            triangle point #3
195      * @param store
196      *            Vector3f to store result in
197      * @return normal of triangle, same as store param.
198      */
199     protected Vector3f getTriangleNormal(int vert1, int vert2, int vert3,
200             Vector3f store) {
201         BufferUtils.populateFromBuffer(tempV1, getVertexBuffer(), vert1);
202         BufferUtils.populateFromBuffer(tempV2, getVertexBuffer(), vert2);
203         BufferUtils.populateFromBuffer(tempV3, getVertexBuffer(), vert3);
204
205         // Translate(v2, v1);
206         tempV2.subtractLocal(tempV1);
207
208         // Translate(v3, v1);
209         tempV3.subtractLocal(tempV1);
210
211         // Result = CrossProduct(v1, v3);
212         tempV2.cross(tempV3, store);
213
214         return store;
215     }
216
217     /**
218      * Initialize the values of the vertex, normal and texture[0] arrays. Build
219      * a SpringSystem and call setupIndices(). Then update the various buffers.
220      * 
221      * @param nodeMass
222      *            mass of individual node.
223      */
224     protected void initCloth(float nodeMass) {
225         float minX = springLength * (0 - 0.5f * (clothNodesX - 1));
226         float maxX = springLength
227                 * ((clothNodesX - 1) - 0.5f * (clothNodesX - 1));
228         float minY = springLength * (0.5f * (clothNodesY - 1) - 0);
229         float maxY = springLength
230                 * (0.5f * (clothNodesY - 1) - (clothNodesY - 1));
231         Vector3f upperLeft = new Vector3f(minX, minY, 0);
232         Vector3f lowerLeft = new Vector3f(minX, maxY, 0);
233         Vector3f lowerRight = new Vector3f(maxX, maxY, 0);
234         Vector3f upperRight = new Vector3f(maxX, minY, 0);
235
236         initCloth(nodeMass, upperLeft, lowerLeft, lowerRight, upperRight);
237     }
238
239     /**
240      * Initialize the values of the vertex, normal and texture[0] arrays. Build
241      * a SpringSystem and call setupIndices(). Then update the various buffers.
242      * 
243      * @param nodeMass
244      *            mass of individual node.
245      * @param upperLeft
246      *            the upper left corner of the rectangle.
247      * @param lowerLeft
248      *            the lower left corner of the rectangle.
249      * @param lowerRight
250      *            the lower right corner of the rectangle.
251      * @param upperRight
252      *            the upper right corner of the rectangle.
253      */
254     protected void initCloth(float nodeMass, Vector3f upperLeft,
255             Vector3f lowerLeft, Vector3f lowerRight, Vector3f upperRight) {
256         // Setup our shared vectors as a bilinear combination of the 4 corners
257         Vector2f texcoord = new Vector2f();
258         Vector3f vert = new Vector3f();
259         Vector3f topVec = new Vector3f();
260         Vector3f bottomVec = new Vector3f();
261         FloatBuffer texs = getTextureCoords().get(0).coords;
262         for (int j = 0; j < clothNodesY; j++) {
263             for (int i = 0; i < clothNodesX; i++) {
264                 int ind = getIndex(i, j);
265                 // vert.set(springLength * (i - 0.5f * (clothNodesX - 1)),
266                 // springLength * (0.5f * (clothNodesY - 1) - j), 0);
267                 float xInterpolation = ((float) i) / (clothNodesX - 1);
268                 topVec.interpolate(upperLeft, upperRight, xInterpolation);
269                 bottomVec.interpolate(lowerLeft, lowerRight, xInterpolation);
270                 vert.interpolate(topVec, bottomVec, ((float) j)
271                         / (clothNodesY - 1));
272                 BufferUtils.setInBuffer(vert, getVertexBuffer(), ind);
273                 texcoord.set((float) i / (clothNodesX - 1),
274                         (float) (clothNodesY - (j + 1)) / (clothNodesY - 1));
275                 BufferUtils.setInBuffer(texcoord, texs, ind);
276             }
277         }
278
279         system = SpringSystem.createRectField(clothNodesX, clothNodesY,
280                 getVertexBuffer(), nodeMass);
281         setupIndices();
282     }
283
284     /**
285      * Return the underlying SpringSystem.
286      * 
287      * @return SpringSystem
288      */
289     public SpringSystem getSystem() {
290         return system;
291     }
292
293     /**
294      * Return how many nodes high this cloth is.
295      * 
296      * @return int
297      */
298     public int getClothNodesY() {
299         return clothNodesY;
300     }
301
302     /**
303      * Return how many nodes wide this cloth is.
304      * 
305      * @return int
306      */
307     public int getClothNodesX() {
308         return clothNodesX;
309     }
310
311     /**
312      * Return the preset length the <i>structural</i> springs are set to. (ie.
313      * the springs running along the x and y axis connecting immediate
314      * neighbors.)
315      * 
316      * @return float
317      */
318     public float getSpringLength() {
319         return springLength;
320     }
321
322     /**
323      * Get the time dilation factor. See <code>setTimeDilation(float)</code>
324      * for more.
325      * 
326      * @return float
327      */
328     public float getTimeDilation() {
329         return timeDilation;
330     }
331
332     /**
333      * Set the timedilation factor used in <code>updateWorldData(float)</code>
334      * Normally this is set to 1. If set at 2, for example, every 25 ms of real
335      * time, the code will update the SpringSystem and cloth as if 50 ms had
336      * passed.
337      * 
338      * @param timeDilation
339      *            float
340      */
341     public void setTimeDilation(float timeDilation) {
342         this.timeDilation = timeDilation;
343     }
344
345     /**
346      * Setup the triangle indices for this cloth.
347      */
348     protected void setupIndices() {
349         getIndexBuffer().rewind();
350         for (int Y = 0; Y < clothNodesY - 1; Y++) {
351             for (int X = 0; X < clothNodesX - 1; X++) {
352                 getIndexBuffer().put(getIndex(X, Y));
353                 getIndexBuffer().put(getIndex(X, Y + 1));
354                 getIndexBuffer().put(getIndex(X + 1, Y + 1));
355
356                 getIndexBuffer().put(getIndex(X, Y));
357                 getIndexBuffer().put(getIndex(X + 1, Y + 1));
358                 getIndexBuffer().put(getIndex(X + 1, Y));
359             }
360         }
361     }
362
363     /**
364      * Convienence method for calculating the array index of a given node given
365      * it's x and y coordiates.
366      * 
367      * @param x
368      *            int
369      * @param y
370      *            int
371      * @return index
372      */
373     protected int getIndex(int x, int y) {
374         return y * clothNodesX + x;
375     }
376
377     /**
378      * Update the physics of this cloth. Updates at 40 FPS (every 25 ms)
379      * 
380      * @param dt
381      *            time since last call to this method. Used to determine if
382      *            enough time has passed to require an update of the
383      *            SpringSystem and cloth data, normals, etc.
384      */
385     public void updateWorldData(float dt) {
386         super.updateWorldData(dt);
387         sinceLast += dt;
388         if (sinceLast >= 0.025f) { // update physics at 40 FPS
389             sinceLast *= timeDilation;
390             calcForces(sinceLast);
391             doUpdate(sinceLast);
392             sinceLast = 0;
393         }
394     }
395
396     /**
397      * Calculate the forces accting on this cloth. Called by
398      * <code>updateWorldData(float)</code>
399      * 
400      * @param sinceLast
401      *            float
402      */
403     protected void calcForces(float sinceLast) {
404         system.calcForces(sinceLast);
405     }
406
407     /**
408      * Update the spring system underlying this cloth. Called by
409      * <code>updateWorldData(float)</code>
410      * 
411      * @param sinceLast
412      *            float
413      */
414     protected void doUpdate(float sinceLast) {
415         system.update(sinceLast);
416         updateVertexBufferfer();
417         updateNormals();
418     }
419
420     protected void updateVertexBufferfer() {
421         getVertexBuffer().rewind();
422         for (int x = 0; x < system.getNodeCount(); x++) {
423             SpringPoint n = system.getNode(x);
424             getVertexBuffer().put(n.position.x).put(n.position.y).put(
425                     n.position.z);
426         }
427     }
428
429     public void write(JMEExporter e) throws IOException {
430         super.write(e);
431         OutputCapsule capsule = e.getCapsule(this);
432         capsule.write(clothNodesX, "clothNodesX", 0);
433         capsule.write(clothNodesY, "clothNodesY", 0);
434         capsule.write(springLength, "springLength", 0);
435         capsule.write(system, "system", null);
436         capsule.write(timeDilation, "timeDilation", 1);
437     }
438
439     public void read(JMEImporter e) throws IOException {
440         super.read(e);
441         InputCapsule capsule = e.getCapsule(this);
442         clothNodesX = capsule.readInt("clothNodesX", 0);
443         clothNodesY = capsule.readInt("clothNodesY", 0);
444         springLength = capsule.readFloat("springLength", 0);
445         system = (SpringSystem) capsule.readSavable("system", null);
446         timeDilation = capsule.readFloat("timeDilation", 1);
447
448         if (system == null) {
449             initCloth(0);
450         }
451     }
452
453 }