1 package com.badlogic.gdx.graphics.g3d.particles; 2 3 import java.util.Arrays; 4 5 import com.badlogic.gdx.utils.Array; 6 import com.badlogic.gdx.utils.GdxRuntimeException; 7 import com.badlogic.gdx.utils.reflect.ArrayReflection; 8 9 /** This class represents an group of elements like an array, but the properties of the elements are stored as separate arrays. 10 * These arrays are called {@link Channel} and are represented by {@link ChannelDescriptor}. 11 * It's not necessary to store primitive types in the channels but doing so will "exploit" data locality 12 * in the JVM, which is ensured for primitive types. 13 * Use {@link FloatChannel}, {@link IntChannel}, {@link ObjectChannel} to store the data. 14 * @author inferno */ 15 public class ParallelArray { 16 17 /** This class describes the content of a {@link Channel}*/ 18 public static class ChannelDescriptor{ 19 public int id; 20 public Class<?> type; 21 public int count; 22 public ChannelDescriptor(int id, Class<?> type, int count){ 23 this.id = id; 24 this.type = type; 25 this.count = count; 26 } 27 } 28 29 /** This class represents a container of values for all the elements for a given property*/ 30 public abstract class Channel{ 31 public int id; 32 public Object data; 33 public int strideSize; 34 public Channel(int id, Object data, int strideSize){ 35 this.id = id; 36 this.strideSize = strideSize; 37 this.data = data; 38 } 39 public abstract void add(int index, Object...objects); 40 public abstract void swap(int i, int k); 41 protected abstract void setCapacity (int requiredCapacity); 42 } 43 44 /** This interface is used to provide custom initialization of the {@link Channel} data */ 45 public static interface ChannelInitializer<T extends Channel>{ 46 public void init(T channel); 47 } 48 49 public class FloatChannel extends Channel{ 50 public float[] data; 51 public FloatChannel (int id, int strideSize, int size) { 52 super(id, new float[size*strideSize], strideSize); 53 this.data = (float[])super.data; 54 } 55 56 @Override 57 public void add (int index, Object...objects) { 58 for(int i=strideSize*size, c = i+strideSize, k=0; i < c; ++i, ++k){ 59 data[i] = (Float)objects[k]; 60 } 61 } 62 63 @Override 64 public void swap (int i, int k) { 65 float t; 66 i=strideSize*i; 67 k =strideSize*k; 68 for(int c = i+strideSize; i < c; ++i, ++k){ 69 t = data[i]; 70 data[i] = data[k]; 71 data[k] = t; 72 } 73 } 74 75 @Override 76 public void setCapacity (int requiredCapacity) { 77 float[] newData = new float[strideSize * requiredCapacity]; 78 System.arraycopy(data, 0, newData, 0, Math.min(data.length, newData.length)); 79 super.data = data = newData; 80 } 81 } 82 83 public class IntChannel extends Channel{ 84 public int[] data; 85 public IntChannel (int id, int strideSize, int size) { 86 super(id, new int[size*strideSize], strideSize); 87 this.data = (int[])super.data; 88 } 89 90 @Override 91 public void add (int index, Object...objects) { 92 for(int i=strideSize*size, c = i+strideSize, k=0; i < c; ++i, ++k){ 93 data[i] = (Integer)objects[k]; 94 } 95 } 96 97 @Override 98 public void swap (int i, int k) { 99 int t; 100 i=strideSize*i; 101 k =strideSize*k; 102 for(int c = i+strideSize; i < c; ++i, ++k){ 103 t = data[i]; 104 data[i] = data[k]; 105 data[k] = t; 106 } 107 } 108 109 @Override 110 public void setCapacity (int requiredCapacity) { 111 int[] newData = new int[strideSize * requiredCapacity]; 112 System.arraycopy(data, 0, newData, 0, Math.min(data.length, newData.length)); 113 super.data = data = newData; 114 } 115 } 116 117 @SuppressWarnings("unchecked") 118 public class ObjectChannel<T> extends Channel{ 119 Class<T> componentType; 120 public T[] data; 121 public ObjectChannel (int id, int strideSize, int size, Class<T> type) { 122 super(id, ArrayReflection.newInstance(type, size*strideSize), strideSize); 123 componentType = type; 124 this.data = (T[]) super.data; 125 } 126 127 @Override 128 public void add (int index, Object...objects) { 129 for(int i=strideSize*size, c = i+strideSize, k=0; i < c; ++i, ++k){ 130 this.data[i] = (T) objects[k]; 131 } 132 } 133 134 @Override 135 public void swap (int i, int k) { 136 T t; 137 i=strideSize*i; 138 k =strideSize*k; 139 for(int c = i+strideSize; i < c; ++i, ++k){ 140 t = data[i]; 141 data[i] = data[k]; 142 data[k] = t; 143 } 144 } 145 146 @Override 147 public void setCapacity (int requiredCapacity) { 148 T[] newData = (T[]) ArrayReflection.newInstance(componentType, strideSize * requiredCapacity); 149 System.arraycopy(data, 0, newData, 0, Math.min(data.length, newData.length)); 150 super.data = data = newData; 151 } 152 } 153 154 /**the channels added to the array*/ 155 Array<Channel> arrays; 156 /** the maximum amount of elements that this array can hold */ 157 public int capacity; 158 /** the current amount of defined elements, do not change manually unless you know what you are doing.*/ 159 public int size; 160 161 public ParallelArray(int capacity){ 162 arrays = new Array<Channel>(false, 2, Channel.class); 163 this.capacity = capacity; 164 size = 0; 165 } 166 167 /** Adds and returns a channel described by the channel descriptor parameter. 168 * If a channel with the same id already exists, no allocation is performed and that channel is returned. */ 169 public <T extends Channel> T addChannel(ChannelDescriptor channelDescriptor){ 170 return addChannel(channelDescriptor, null); 171 } 172 173 /** Adds and returns a channel described by the channel descriptor parameter. 174 * If a channel with the same id already exists, no allocation is performed and that channel is returned. 175 * Otherwise a new channel is allocated and initialized with the initializer. */ 176 public <T extends Channel> T addChannel(ChannelDescriptor channelDescriptor, ChannelInitializer<T> initializer){ 177 T channel = getChannel(channelDescriptor); 178 if(channel == null){ 179 channel = allocateChannel(channelDescriptor); 180 if(initializer != null) 181 initializer.init(channel); 182 arrays.add(channel); 183 } 184 return channel; 185 } 186 187 @SuppressWarnings({"unchecked", "rawtypes"}) 188 private <T extends Channel> T allocateChannel(ChannelDescriptor channelDescriptor){ 189 if(channelDescriptor.type == float.class){ 190 return (T)new FloatChannel(channelDescriptor.id, channelDescriptor.count, capacity); 191 } 192 else if(channelDescriptor.type == int.class){ 193 return (T)new IntChannel(channelDescriptor.id, channelDescriptor.count, capacity); 194 } 195 else { 196 return (T)new ObjectChannel(channelDescriptor.id, channelDescriptor.count, capacity, channelDescriptor.type); 197 } 198 } 199 200 /**Removes the channel with the given id*/ 201 public <T> void removeArray(int id){ 202 arrays.removeIndex(findIndex(id)); 203 } 204 205 private int findIndex (int id) { 206 for(int i=0; i < arrays.size;++i){ 207 Channel array = arrays.items[i]; 208 if(array.id == id) 209 return i; 210 } 211 return -1; 212 } 213 214 /**Adds an element considering the values in the same order as the current channels in the array. 215 * The n_th value must have the same type and stride of the given channel at position n*/ 216 public void addElement(Object...values){ 217 /*FIXME make it grow...*/ 218 if(size == capacity) 219 throw new GdxRuntimeException("Capacity reached, cannot add other elements"); 220 221 int k=0; 222 for(Channel strideArray : arrays){ 223 strideArray.add(k, values); 224 k+= strideArray.strideSize; 225 } 226 ++size; 227 } 228 229 /**Removes the element at the given index and swaps it with the last available element */ 230 public void removeElement(int index){ 231 int last = size -1; 232 //Swap 233 for(Channel strideArray : arrays){ 234 strideArray.swap(index, last); 235 } 236 size = last; 237 } 238 239 /**@return the channel with the same id as the one in the descriptor */ 240 @SuppressWarnings("unchecked") 241 public <T extends Channel> T getChannel (ChannelDescriptor descriptor) { 242 for(Channel array : arrays){ 243 if(array.id == descriptor.id) 244 return (T) array; 245 } 246 return null; 247 } 248 249 /** Removes all the channels and sets size to 0 */ 250 public void clear () { 251 arrays.clear(); 252 size = 0; 253 } 254 255 /** Sets the capacity. 256 * Each contained channel will be resized to match the required capacity and the current data will be preserved. */ 257 public void setCapacity (int requiredCapacity) { 258 if(capacity != requiredCapacity){ 259 for(Channel channel : arrays){ 260 channel.setCapacity(requiredCapacity); 261 } 262 capacity = requiredCapacity; 263 } 264 } 265 266 } 267