• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
ChannelDescriptor(int id, Class<?> type, 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;
Channel(int id, Object data, int strideSize)34 		public Channel(int id, Object data, int strideSize){
35 			this.id = id;
36 			this.strideSize = strideSize;
37 			this.data = data;
38 		}
add(int index, Object...objects)39 		public abstract void add(int index, Object...objects);
swap(int i, int k)40 		public abstract void swap(int i, int k);
setCapacity(int requiredCapacity)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>{
init(T channel)46 		public void init(T channel);
47 	}
48 
49 	public class FloatChannel extends Channel{
50 		public float[] data;
FloatChannel(int id, int strideSize, int size)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
add(int index, Object...objects)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
swap(int i, int k)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
setCapacity(int requiredCapacity)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;
IntChannel(int id, int strideSize, int size)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
add(int index, Object...objects)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
swap(int i, int k)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
setCapacity(int requiredCapacity)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;
ObjectChannel(int id, int strideSize, int size, Class<T> type)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
add(int index, Object...objects)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
swap(int i, int k)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
setCapacity(int requiredCapacity)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 
ParallelArray(int capacity)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. */
addChannel(ChannelDescriptor channelDescriptor)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. */
addChannel(ChannelDescriptor channelDescriptor, ChannelInitializer<T> 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"})
allocateChannel(ChannelDescriptor channelDescriptor)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*/
removeArray(int id)201 	public <T> void removeArray(int id){
202 		arrays.removeIndex(findIndex(id));
203 	}
204 
findIndex(int id)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*/
addElement(Object...values)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 */
removeElement(int index)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")
getChannel(ChannelDescriptor descriptor)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 */
clear()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. */
setCapacity(int requiredCapacity)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