1/*
2 * Copyright (c) 2009-2010 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
33package com.jme3.effect;
34
35import com.jme3.math.FastMath;
36import com.jme3.math.Matrix3f;
37import com.jme3.math.Vector3f;
38import com.jme3.renderer.Camera;
39import com.jme3.scene.VertexBuffer;
40import com.jme3.scene.VertexBuffer.Format;
41import com.jme3.scene.VertexBuffer.Usage;
42import com.jme3.util.BufferUtils;
43import com.jme3.util.SortUtil;
44import java.nio.ByteBuffer;
45import java.nio.FloatBuffer;
46import java.nio.ShortBuffer;
47
48public class ParticleTriMesh extends ParticleMesh {
49
50    private int imagesX = 1;
51    private int imagesY = 1;
52    private boolean uniqueTexCoords = false;
53    private ParticleComparator comparator = new ParticleComparator();
54    private ParticleEmitter emitter;
55    private Particle[] particlesCopy;
56
57    @Override
58    public void initParticleData(ParticleEmitter emitter, int numParticles) {
59        setMode(Mode.Triangles);
60
61        this.emitter = emitter;
62
63        particlesCopy = new Particle[numParticles];
64
65        // set positions
66        FloatBuffer pb = BufferUtils.createVector3Buffer(numParticles * 4);
67        VertexBuffer pvb = new VertexBuffer(VertexBuffer.Type.Position);
68        pvb.setupData(Usage.Stream, 3, Format.Float, pb);
69
70        //if the buffer is already set only update the data
71        VertexBuffer buf = getBuffer(VertexBuffer.Type.Position);
72        if (buf != null) {
73            buf.updateData(pb);
74        } else {
75            setBuffer(pvb);
76        }
77
78        // set colors
79        ByteBuffer cb = BufferUtils.createByteBuffer(numParticles * 4 * 4);
80        VertexBuffer cvb = new VertexBuffer(VertexBuffer.Type.Color);
81        cvb.setupData(Usage.Stream, 4, Format.UnsignedByte, cb);
82        cvb.setNormalized(true);
83
84        buf = getBuffer(VertexBuffer.Type.Color);
85        if (buf != null) {
86            buf.updateData(cb);
87        } else {
88            setBuffer(cvb);
89        }
90
91        // set texcoords
92        VertexBuffer tvb = new VertexBuffer(VertexBuffer.Type.TexCoord);
93        FloatBuffer tb = BufferUtils.createVector2Buffer(numParticles * 4);
94
95        uniqueTexCoords = false;
96        for (int i = 0; i < numParticles; i++){
97            tb.put(0f).put(1f);
98            tb.put(1f).put(1f);
99            tb.put(0f).put(0f);
100            tb.put(1f).put(0f);
101        }
102        tb.flip();
103        tvb.setupData(Usage.Static, 2, Format.Float, tb);
104
105        buf = getBuffer(VertexBuffer.Type.TexCoord);
106        if (buf != null) {
107            buf.updateData(tb);
108        } else {
109            setBuffer(tvb);
110        }
111
112        // set indices
113        ShortBuffer ib = BufferUtils.createShortBuffer(numParticles * 6);
114        for (int i = 0; i < numParticles; i++){
115            int startIdx = (i * 4);
116
117            // triangle 1
118            ib.put((short)(startIdx + 1))
119              .put((short)(startIdx + 0))
120              .put((short)(startIdx + 2));
121
122            // triangle 2
123            ib.put((short)(startIdx + 1))
124              .put((short)(startIdx + 2))
125              .put((short)(startIdx + 3));
126        }
127        ib.flip();
128
129        VertexBuffer ivb = new VertexBuffer(VertexBuffer.Type.Index);
130        ivb.setupData(Usage.Static, 3, Format.UnsignedShort, ib);
131
132        buf = getBuffer(VertexBuffer.Type.Index);
133        if (buf != null) {
134            buf.updateData(ib);
135        } else {
136            setBuffer(ivb);
137        }
138
139    }
140
141    @Override
142    public void setImagesXY(int imagesX, int imagesY) {
143        this.imagesX = imagesX;
144        this.imagesY = imagesY;
145        if (imagesX != 1 || imagesY != 1){
146            uniqueTexCoords = true;
147            getBuffer(VertexBuffer.Type.TexCoord).setUsage(Usage.Stream);
148        }
149    }
150
151    @Override
152    public void updateParticleData(Particle[] particles, Camera cam, Matrix3f inverseRotation) {
153        System.arraycopy(particles, 0, particlesCopy, 0, particlesCopy.length);
154        comparator.setCamera(cam);
155//        Arrays.sort(particlesCopy, comparator);
156//        SortUtil.qsort(particlesCopy, comparator);
157        SortUtil.msort(particles, particlesCopy, comparator);
158        particles = particlesCopy;
159
160        VertexBuffer pvb = getBuffer(VertexBuffer.Type.Position);
161        FloatBuffer positions = (FloatBuffer) pvb.getData();
162
163        VertexBuffer cvb = getBuffer(VertexBuffer.Type.Color);
164        ByteBuffer colors = (ByteBuffer) cvb.getData();
165
166        VertexBuffer tvb = getBuffer(VertexBuffer.Type.TexCoord);
167        FloatBuffer texcoords = (FloatBuffer) tvb.getData();
168
169        Vector3f camUp   = cam.getUp();
170        Vector3f camLeft = cam.getLeft();
171        Vector3f camDir  = cam.getDirection();
172
173        inverseRotation.multLocal(camUp);
174        inverseRotation.multLocal(camLeft);
175        inverseRotation.multLocal(camDir);
176
177        boolean facingVelocity = emitter.isFacingVelocity();
178
179        Vector3f up = new Vector3f(),
180                 left = new Vector3f();
181
182        if (!facingVelocity){
183            up.set(camUp);
184            left.set(camLeft);
185        }
186
187        // update data in vertex buffers
188        positions.clear();
189        colors.clear();
190        texcoords.clear();
191        Vector3f faceNormal = emitter.getFaceNormal();
192
193        for (int i = 0; i < particles.length; i++){
194            Particle p = particles[i];
195            boolean dead = p.life == 0;
196            if (dead){
197                positions.put(0).put(0).put(0);
198                positions.put(0).put(0).put(0);
199                positions.put(0).put(0).put(0);
200                positions.put(0).put(0).put(0);
201                continue;
202            }
203
204            if (facingVelocity){
205                left.set(p.velocity).normalizeLocal();
206                camDir.cross(left, up);
207                up.multLocal(p.size);
208                left.multLocal(p.size);
209            }else if (faceNormal != null){
210                up.set(faceNormal).crossLocal(Vector3f.UNIT_X);
211                faceNormal.cross(up, left);
212                up.multLocal(p.size);
213                left.multLocal(p.size);
214            }else if (p.angle != 0){
215                float cos = FastMath.cos(p.angle) * p.size;
216                float sin = FastMath.sin(p.angle) * p.size;
217
218                left.x = camLeft.x * cos + camUp.x * sin;
219                left.y = camLeft.y * cos + camUp.y * sin;
220                left.z = camLeft.z * cos + camUp.z * sin;
221
222                up.x = camLeft.x * -sin + camUp.x * cos;
223                up.y = camLeft.y * -sin + camUp.y * cos;
224                up.z = camLeft.z * -sin + camUp.z * cos;
225            }else{
226                up.set(camUp);
227                left.set(camLeft);
228                up.multLocal(p.size);
229                left.multLocal(p.size);
230            }
231
232            positions.put(p.position.x + left.x + up.x)
233                     .put(p.position.y + left.y + up.y)
234                     .put(p.position.z + left.z + up.z);
235
236            positions.put(p.position.x - left.x + up.x)
237                     .put(p.position.y - left.y + up.y)
238                     .put(p.position.z - left.z + up.z);
239
240            positions.put(p.position.x + left.x - up.x)
241                     .put(p.position.y + left.y - up.y)
242                     .put(p.position.z + left.z - up.z);
243
244            positions.put(p.position.x - left.x - up.x)
245                     .put(p.position.y - left.y - up.y)
246                     .put(p.position.z - left.z - up.z);
247
248            if (uniqueTexCoords){
249                int imgX = p.imageIndex % imagesX;
250                int imgY = (p.imageIndex - imgX) / imagesY;
251
252                float startX = ((float) imgX) / imagesX;
253                float startY = ((float) imgY) / imagesY;
254                float endX   = startX + (1f / imagesX);
255                float endY   = startY + (1f / imagesY);
256
257                texcoords.put(startX).put(endY);
258                texcoords.put(endX).put(endY);
259                texcoords.put(startX).put(startY);
260                texcoords.put(endX).put(startY);
261            }
262
263            int abgr = p.color.asIntABGR();
264            colors.putInt(abgr);
265            colors.putInt(abgr);
266            colors.putInt(abgr);
267            colors.putInt(abgr);
268        }
269
270        positions.clear();
271        colors.clear();
272        if (!uniqueTexCoords)
273            texcoords.clear();
274        else{
275            texcoords.clear();
276            tvb.updateData(texcoords);
277        }
278
279        // force renderer to re-send data to GPU
280        pvb.updateData(positions);
281        cvb.updateData(colors);
282    }
283
284}
285