/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.graphics.g3d.particles.influencers; import java.util.Arrays; import com.badlogic.gdx.graphics.g3d.particles.ParallelArray.FloatChannel; import com.badlogic.gdx.graphics.g3d.particles.ParticleChannels; import com.badlogic.gdx.graphics.g3d.particles.ParticleController; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.JsonValue; /** It's an {@link Influencer} which controls the particles dynamics (movement, rotations). * @author Inferno */ public class DynamicsInfluencer extends Influencer { public Array<DynamicsModifier> velocities; private FloatChannel accellerationChannel, positionChannel, previousPositionChannel, rotationChannel, angularVelocityChannel; boolean hasAcceleration, has2dAngularVelocity, has3dAngularVelocity; public DynamicsInfluencer () { this.velocities = new Array<DynamicsModifier>(true, 3, DynamicsModifier.class); } public DynamicsInfluencer (DynamicsModifier... velocities) { this.velocities = new Array<DynamicsModifier>(true, velocities.length, DynamicsModifier.class); for (DynamicsModifier value : velocities) { this.velocities.add((DynamicsModifier)value.copy()); } } public DynamicsInfluencer (DynamicsInfluencer velocityInfluencer) { this((DynamicsModifier[])velocityInfluencer.velocities.toArray(DynamicsModifier.class)); } @Override public void allocateChannels () { for (int k = 0; k < velocities.size; ++k) { velocities.items[k].allocateChannels(); } // Hack, shouldn't be done but after all the modifiers allocated their channels // it's possible to check if we need to allocate previous position channel accellerationChannel = controller.particles.getChannel(ParticleChannels.Acceleration); hasAcceleration = accellerationChannel != null; if (hasAcceleration) { positionChannel = controller.particles.addChannel(ParticleChannels.Position); previousPositionChannel = controller.particles.addChannel(ParticleChannels.PreviousPosition); } // Angular velocity check angularVelocityChannel = controller.particles.getChannel(ParticleChannels.AngularVelocity2D); has2dAngularVelocity = angularVelocityChannel != null; if (has2dAngularVelocity) { rotationChannel = controller.particles.addChannel(ParticleChannels.Rotation2D); has3dAngularVelocity = false; } else { angularVelocityChannel = controller.particles.getChannel(ParticleChannels.AngularVelocity3D); has3dAngularVelocity = angularVelocityChannel != null; if (has3dAngularVelocity) rotationChannel = controller.particles.addChannel(ParticleChannels.Rotation3D); } } @Override public void set (ParticleController particleController) { super.set(particleController); for (int k = 0; k < velocities.size; ++k) { velocities.items[k].set(particleController); } } @Override public void init () { for (int k = 0; k < velocities.size; ++k) { velocities.items[k].init(); } } public void activateParticles (int startIndex, int count) { if (hasAcceleration) { // Previous position is the current position // Attention, this requires that some other influencer setting the position channel must execute before this influencer. for (int i = startIndex * positionChannel.strideSize, c = i + count * positionChannel.strideSize; i < c; i += positionChannel.strideSize) { previousPositionChannel.data[i + ParticleChannels.XOffset] = positionChannel.data[i + ParticleChannels.XOffset]; previousPositionChannel.data[i + ParticleChannels.YOffset] = positionChannel.data[i + ParticleChannels.YOffset]; previousPositionChannel.data[i + ParticleChannels.ZOffset] = positionChannel.data[i + ParticleChannels.ZOffset]; /* * //Euler intialization previousPositionChannel.data[i+ParticleChannels.XOffset] = * previousPositionChannel.data[i+ParticleChannels.YOffset] = previousPositionChannel.data[i+ParticleChannels.ZOffset] * = 0; */ } } if (has2dAngularVelocity) { // Rotation back to 0 for (int i = startIndex * rotationChannel.strideSize, c = i + count * rotationChannel.strideSize; i < c; i += rotationChannel.strideSize) { rotationChannel.data[i + ParticleChannels.CosineOffset] = 1; rotationChannel.data[i + ParticleChannels.SineOffset] = 0; } } else if (has3dAngularVelocity) { // Rotation back to 0 for (int i = startIndex * rotationChannel.strideSize, c = i + count * rotationChannel.strideSize; i < c; i += rotationChannel.strideSize) { rotationChannel.data[i + ParticleChannels.XOffset] = 0; rotationChannel.data[i + ParticleChannels.YOffset] = 0; rotationChannel.data[i + ParticleChannels.ZOffset] = 0; rotationChannel.data[i + ParticleChannels.WOffset] = 1; } } for (int k = 0; k < velocities.size; ++k) { velocities.items[k].activateParticles(startIndex, count); } } public void update () { // Clean previouse frame velocities if (hasAcceleration) Arrays.fill(accellerationChannel.data, 0, controller.particles.size * accellerationChannel.strideSize, 0); if (has2dAngularVelocity || has3dAngularVelocity) Arrays.fill(angularVelocityChannel.data, 0, controller.particles.size * angularVelocityChannel.strideSize, 0); // Sum all the forces/accelerations for (int k = 0; k < velocities.size; ++k) { velocities.items[k].update(); } // Apply the forces if (hasAcceleration) { /* * //Euler Integration for(int i=0, offset = 0; i < controller.particles.size; ++i, offset +=positionChannel.strideSize){ * previousPositionChannel.data[offset + ParticleChannels.XOffset] += accellerationChannel.data[offset + * ParticleChannels.XOffset]*controller.deltaTime; previousPositionChannel.data[offset + ParticleChannels.YOffset] += * accellerationChannel.data[offset + ParticleChannels.YOffset]*controller.deltaTime; previousPositionChannel.data[offset * + ParticleChannels.ZOffset] += accellerationChannel.data[offset + ParticleChannels.ZOffset]*controller.deltaTime; * * positionChannel.data[offset + ParticleChannels.XOffset] += previousPositionChannel.data[offset + * ParticleChannels.XOffset]*controller.deltaTime; positionChannel.data[offset + ParticleChannels.YOffset] += * previousPositionChannel.data[offset + ParticleChannels.YOffset]*controller.deltaTime; positionChannel.data[offset + * ParticleChannels.ZOffset] += previousPositionChannel.data[offset + ParticleChannels.ZOffset]*controller.deltaTime; } */ // Verlet integration for (int i = 0, offset = 0; i < controller.particles.size; ++i, offset += positionChannel.strideSize) { float x = positionChannel.data[offset + ParticleChannels.XOffset], y = positionChannel.data[offset + ParticleChannels.YOffset], z = positionChannel.data[offset + ParticleChannels.ZOffset]; positionChannel.data[offset + ParticleChannels.XOffset] = 2 * x - previousPositionChannel.data[offset + ParticleChannels.XOffset] + accellerationChannel.data[offset + ParticleChannels.XOffset] * controller.deltaTimeSqr; positionChannel.data[offset + ParticleChannels.YOffset] = 2 * y - previousPositionChannel.data[offset + ParticleChannels.YOffset] + accellerationChannel.data[offset + ParticleChannels.YOffset] * controller.deltaTimeSqr; positionChannel.data[offset + ParticleChannels.ZOffset] = 2 * z - previousPositionChannel.data[offset + ParticleChannels.ZOffset] + accellerationChannel.data[offset + ParticleChannels.ZOffset] * controller.deltaTimeSqr; previousPositionChannel.data[offset + ParticleChannels.XOffset] = x; previousPositionChannel.data[offset + ParticleChannels.YOffset] = y; previousPositionChannel.data[offset + ParticleChannels.ZOffset] = z; } } if (has2dAngularVelocity) { for (int i = 0, offset = 0; i < controller.particles.size; ++i, offset += rotationChannel.strideSize) { float rotation = angularVelocityChannel.data[i] * controller.deltaTime; if (rotation != 0) { float cosBeta = MathUtils.cosDeg(rotation), sinBeta = MathUtils.sinDeg(rotation); float currentCosine = rotationChannel.data[offset + ParticleChannels.CosineOffset]; float currentSine = rotationChannel.data[offset + ParticleChannels.SineOffset]; float newCosine = currentCosine * cosBeta - currentSine * sinBeta, newSine = currentSine * cosBeta + currentCosine * sinBeta; rotationChannel.data[offset + ParticleChannels.CosineOffset] = newCosine; rotationChannel.data[offset + ParticleChannels.SineOffset] = newSine; } } } else if (has3dAngularVelocity) { for (int i = 0, offset = 0, angularOffset = 0; i < controller.particles.size; ++i, offset += rotationChannel.strideSize, angularOffset += angularVelocityChannel.strideSize) { float wx = angularVelocityChannel.data[angularOffset + ParticleChannels.XOffset], wy = angularVelocityChannel.data[angularOffset + ParticleChannels.YOffset], wz = angularVelocityChannel.data[angularOffset + ParticleChannels.ZOffset], qx = rotationChannel.data[offset + ParticleChannels.XOffset], qy = rotationChannel.data[offset + ParticleChannels.YOffset], qz = rotationChannel.data[offset + ParticleChannels.ZOffset], qw = rotationChannel.data[offset + ParticleChannels.WOffset]; TMP_Q.set(wx, wy, wz, 0).mul(qx, qy, qz, qw).mul(0.5f * controller.deltaTime).add(qx, qy, qz, qw).nor(); rotationChannel.data[offset + ParticleChannels.XOffset] = TMP_Q.x; rotationChannel.data[offset + ParticleChannels.YOffset] = TMP_Q.y; rotationChannel.data[offset + ParticleChannels.ZOffset] = TMP_Q.z; rotationChannel.data[offset + ParticleChannels.WOffset] = TMP_Q.w; } } } @Override public DynamicsInfluencer copy () { return new DynamicsInfluencer(this); } @Override public void write (Json json) { json.writeValue("velocities", velocities, Array.class, DynamicsModifier.class); } @Override public void read (Json json, JsonValue jsonData) { velocities.addAll(json.readValue("velocities", Array.class, DynamicsModifier.class, jsonData)); } }