/*******************************************************************************
* 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;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.g3d.particles.renderers.ParticleControllerRenderData;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
/** This class is used by particle batches to sort the particles before rendering.
* @author Inferno */
public abstract class ParticleSorter {
static final Vector3 TMP_V1 = new Vector3();
/** Using this class will not apply sorting */
public static class None extends ParticleSorter {
int currentCapacity = 0;
int[] indices;
@Override
public void ensureCapacity (int capacity) {
if (currentCapacity < capacity) {
indices = new int[capacity];
for (int i = 0; i < capacity; ++i)
indices[i] = i;
currentCapacity = capacity;
}
}
@Override
public <T extends ParticleControllerRenderData> int[] sort (Array<T> renderData) {
return indices;
}
}
/** This class will sort all the particles using the distance from camera. */
public static class Distance extends ParticleSorter {
private float[] distances;
private int[] particleIndices, particleOffsets;
private int currentSize = 0;
@Override
public void ensureCapacity (int capacity) {
if (currentSize < capacity) {
distances = new float[capacity];
particleIndices = new int[capacity];
particleOffsets = new int[capacity];
currentSize = capacity;
}
}
@Override
public <T extends ParticleControllerRenderData> int[] sort (Array<T> renderData) {
float[] val = camera.view.val;
float cx = val[Matrix4.M20], cy = val[Matrix4.M21], cz = val[Matrix4.M22];
int count = 0, i = 0;
for (ParticleControllerRenderData data : renderData) {
for (int k = 0, c = i + data.controller.particles.size; i < c; ++i, k += data.positionChannel.strideSize) {
distances[i] = cx * data.positionChannel.data[k + ParticleChannels.XOffset] + cy
* data.positionChannel.data[k + ParticleChannels.YOffset] + cz
* data.positionChannel.data[k + ParticleChannels.ZOffset];
particleIndices[i] = i;
}
count += data.controller.particles.size;
}
qsort(0, count - 1);
for (i = 0; i < count; ++i) {
particleOffsets[particleIndices[i]] = i;
}
return particleOffsets;
}
public void qsort (int si, int ei) {
// base case
if (si < ei) {
float tmp;
int tmpIndex, particlesPivotIndex;
// insertion
if (ei - si <= 8) {
for (int i = si; i <= ei; i++)
for (int j = i; j > si && distances[j - 1] > distances[j]; j--) {
tmp = distances[j];
distances[j] = distances[j - 1];
distances[j - 1] = tmp;
// Swap indices
tmpIndex = particleIndices[j];
particleIndices[j] = particleIndices[j - 1];
particleIndices[j - 1] = tmpIndex;
}
return;
}
// Quick
float pivot = distances[si];
int i = si + 1;
particlesPivotIndex = particleIndices[si];
// partition array
for (int j = si + 1; j <= ei; j++) {
if (pivot > distances[j]) {
if (j > i) {
// Swap distances
tmp = distances[j];
distances[j] = distances[i];
distances[i] = tmp;
// Swap indices
tmpIndex = particleIndices[j];
particleIndices[j] = particleIndices[i];
particleIndices[i] = tmpIndex;
}
i++;
}
}
// put pivot in right position
distances[si] = distances[i - 1];
distances[i - 1] = pivot;
particleIndices[si] = particleIndices[i - 1];
particleIndices[i - 1] = particlesPivotIndex;
// call qsort on right and left sides of pivot
qsort(si, i - 2);
qsort(i, ei);
}
}
}
protected Camera camera;
/** @return an array of offsets where each particle should be put in the resulting mesh (also if more than one mesh will be
* generated, this is an absolute offset considering a BIG output array). */
public abstract <T extends ParticleControllerRenderData> int[] sort (Array<T> renderData);
public void setCamera (Camera camera) {
this.camera = camera;
}
/** This method is called when the batch has increased the underlying particle buffer. In this way the sorter can increase the
* data structures used to sort the particles (i.e increase backing array size) */
public void ensureCapacity (int capacity) {
}
}