2 * Copyright (c) 2003-2009 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
33 package com.jmex.effects.cloth;
35 import java.io.IOException;
36 import java.nio.FloatBuffer;
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;
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.
55 * @author Joshua Slack
56 * @version $Id: ClothPatch.java,v 1.12 2006/06/21 20:33:13 nca Exp $
58 public class ClothPatch extends TriMesh {
59 private static final long serialVersionUID = 1L;
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;
72 * Dilation factor to multiply elapsed time by for use in updating
75 protected float timeDilation = 1.0f;
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();
91 * number of nodes wide this cloth will be.
93 * number of nodes high this cloth will be.
95 * distance between each node
97 * mass of an individual node in this Cloth.
99 public ClothPatch(String name, int nodesX, int nodesY, float springLength,
102 clothNodesX = nodesX;
103 clothNodesY = nodesY;
104 this.springLength = springLength;
106 setVertexCount(clothNodesY * clothNodesX);
107 setVertexBuffer(BufferUtils.createVector3Buffer(getVertexCount()));
108 setNormalBuffer(BufferUtils.createVector3Buffer(getVertexCount()));
109 getTextureCoords().set(0,
110 new TexCoords(BufferUtils.createVector2Buffer(getVertexCount())));
112 setTriangleQuantity((clothNodesX - 1) * (clothNodesY - 1) * 2);
113 setIndexBuffer(BufferUtils.createIntBuffer(3 * getTriangleCount()));
118 public ClothPatch(String name, int nodesX, int nodesY, Vector3f upperLeft,
119 Vector3f lowerLeft, Vector3f lowerRight, Vector3f upperRight,
122 clothNodesX = nodesX;
123 clothNodesY = nodesY;
124 // this.springLength = springLength;
126 setVertexCount(clothNodesY * clothNodesX);
127 setVertexBuffer(BufferUtils.createVector3Buffer(getVertexCount()));
128 setNormalBuffer(BufferUtils.createVector3Buffer(getVertexCount()));
129 getTextureCoords().set(0,
130 new TexCoords(BufferUtils.createVector2Buffer(getVertexCount())));
132 setTriangleQuantity((clothNodesX - 1) * (clothNodesY - 1) * 2);
133 setIndexBuffer(BufferUtils.createIntBuffer(3 * getTriangleCount()));
135 initCloth(nodeMass, upperLeft, lowerLeft, lowerRight, upperRight);
139 * Add an external force to the underlying SpringSystem.
144 public void addForce(SpringPointForce force) {
145 system.addForce(force);
149 * Remove a force from the underlying SpringSystem.
153 * @return true if found and removed.
155 public boolean removeForce(SpringPointForce force) {
156 return system.removeForce(force);
160 * Update the normals in the system.
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
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);
181 for (int i = getVertexCount(); --i >= 0;)
182 BufferUtils.normalizeVector3(getNormalBuffer(), i);
186 * Get the normal of the triangle defined by the given vertices. Please note
187 * that result is not normalized.
196 * Vector3f to store result in
197 * @return normal of triangle, same as store param.
199 protected Vector3f getTriangleNormal(int vert1, int vert2, int vert3,
201 BufferUtils.populateFromBuffer(tempV1, getVertexBuffer(), vert1);
202 BufferUtils.populateFromBuffer(tempV2, getVertexBuffer(), vert2);
203 BufferUtils.populateFromBuffer(tempV3, getVertexBuffer(), vert3);
205 // Translate(v2, v1);
206 tempV2.subtractLocal(tempV1);
208 // Translate(v3, v1);
209 tempV3.subtractLocal(tempV1);
211 // Result = CrossProduct(v1, v3);
212 tempV2.cross(tempV3, store);
218 * Initialize the values of the vertex, normal and texture[0] arrays. Build
219 * a SpringSystem and call setupIndices(). Then update the various buffers.
222 * mass of individual node.
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);
236 initCloth(nodeMass, upperLeft, lowerLeft, lowerRight, upperRight);
240 * Initialize the values of the vertex, normal and texture[0] arrays. Build
241 * a SpringSystem and call setupIndices(). Then update the various buffers.
244 * mass of individual node.
246 * the upper left corner of the rectangle.
248 * the lower left corner of the rectangle.
250 * the lower right corner of the rectangle.
252 * the upper right corner of the rectangle.
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);
279 system = SpringSystem.createRectField(clothNodesX, clothNodesY,
280 getVertexBuffer(), nodeMass);
285 * Return the underlying SpringSystem.
287 * @return SpringSystem
289 public SpringSystem getSystem() {
294 * Return how many nodes high this cloth is.
298 public int getClothNodesY() {
303 * Return how many nodes wide this cloth is.
307 public int getClothNodesX() {
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
318 public float getSpringLength() {
323 * Get the time dilation factor. See <code>setTimeDilation(float)</code>
328 public float getTimeDilation() {
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
338 * @param timeDilation
341 public void setTimeDilation(float timeDilation) {
342 this.timeDilation = timeDilation;
346 * Setup the triangle indices for this cloth.
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));
356 getIndexBuffer().put(getIndex(X, Y));
357 getIndexBuffer().put(getIndex(X + 1, Y + 1));
358 getIndexBuffer().put(getIndex(X + 1, Y));
364 * Convienence method for calculating the array index of a given node given
365 * it's x and y coordiates.
373 protected int getIndex(int x, int y) {
374 return y * clothNodesX + x;
378 * Update the physics of this cloth. Updates at 40 FPS (every 25 ms)
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.
385 public void updateWorldData(float dt) {
386 super.updateWorldData(dt);
388 if (sinceLast >= 0.025f) { // update physics at 40 FPS
389 sinceLast *= timeDilation;
390 calcForces(sinceLast);
397 * Calculate the forces accting on this cloth. Called by
398 * <code>updateWorldData(float)</code>
403 protected void calcForces(float sinceLast) {
404 system.calcForces(sinceLast);
408 * Update the spring system underlying this cloth. Called by
409 * <code>updateWorldData(float)</code>
414 protected void doUpdate(float sinceLast) {
415 system.update(sinceLast);
416 updateVertexBufferfer();
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(
429 public void write(JMEExporter e) throws IOException {
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);
439 public void read(JMEImporter e) throws IOException {
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);
448 if (system == null) {