1/******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17package com.badlogic.gdx.graphics; 18 19import java.util.Iterator; 20import java.util.NoSuchElementException; 21 22import com.badlogic.gdx.utils.GdxRuntimeException; 23 24/** Instances of this class specify the vertex attributes of a mesh. VertexAttributes are used by {@link Mesh} instances to define 25 * its vertex structure. Vertex attributes have an order. The order is specified by the order they are added to this class. 26 * 27 * @author mzechner, Xoppa */ 28public final class VertexAttributes implements Iterable<VertexAttribute>, Comparable<VertexAttributes> { 29 /** The usage of a vertex attribute. 30 * 31 * @author mzechner */ 32 public static final class Usage { 33 public static final int Position = 1; 34 public static final int ColorUnpacked = 2; 35 public static final int ColorPacked = 4; 36 public static final int Normal = 8; 37 public static final int TextureCoordinates = 16; 38 public static final int Generic = 32; 39 public static final int BoneWeight = 64; 40 public static final int Tangent = 128; 41 public static final int BiNormal = 256; 42 } 43 44 /** the attributes in the order they were specified **/ 45 private final VertexAttribute[] attributes; 46 47 /** the size of a single vertex in bytes **/ 48 public final int vertexSize; 49 50 /** cache of the value calculated by {@link #getMask()} **/ 51 private long mask = -1; 52 53 private ReadonlyIterable<VertexAttribute> iterable; 54 55 /** Constructor, sets the vertex attributes in a specific order */ 56 public VertexAttributes (VertexAttribute... attributes) { 57 if (attributes.length == 0) throw new IllegalArgumentException("attributes must be >= 1"); 58 59 VertexAttribute[] list = new VertexAttribute[attributes.length]; 60 for (int i = 0; i < attributes.length; i++) 61 list[i] = attributes[i]; 62 63 this.attributes = list; 64 vertexSize = calculateOffsets(); 65 } 66 67 /** Returns the offset for the first VertexAttribute with the specified usage. 68 * @param usage The usage of the VertexAttribute. */ 69 public int getOffset (int usage, int defaultIfNotFound) { 70 VertexAttribute vertexAttribute = findByUsage(usage); 71 if (vertexAttribute == null) return defaultIfNotFound; 72 return vertexAttribute.offset / 4; 73 } 74 75 /** Returns the offset for the first VertexAttribute with the specified usage. 76 * @param usage The usage of the VertexAttribute. */ 77 public int getOffset (int usage) { 78 return getOffset(usage, 0); 79 } 80 81 /** Returns the first VertexAttribute for the given usage. 82 * @param usage The usage of the VertexAttribute to find. */ 83 public VertexAttribute findByUsage (int usage) { 84 int len = size(); 85 for (int i = 0; i < len; i++) 86 if (get(i).usage == usage) return get(i); 87 return null; 88 } 89 90 private int calculateOffsets () { 91 int count = 0; 92 for (int i = 0; i < attributes.length; i++) { 93 VertexAttribute attribute = attributes[i]; 94 attribute.offset = count; 95 if (attribute.usage == VertexAttributes.Usage.ColorPacked) 96 count += 4; 97 else 98 count += 4 * attribute.numComponents; 99 } 100 101 return count; 102 } 103 104 /** @return the number of attributes */ 105 public int size () { 106 return attributes.length; 107 } 108 109 /** @param index the index 110 * @return the VertexAttribute at the given index */ 111 public VertexAttribute get (int index) { 112 return attributes[index]; 113 } 114 115 public String toString () { 116 StringBuilder builder = new StringBuilder(); 117 builder.append("["); 118 for (int i = 0; i < attributes.length; i++) { 119 builder.append("("); 120 builder.append(attributes[i].alias); 121 builder.append(", "); 122 builder.append(attributes[i].usage); 123 builder.append(", "); 124 builder.append(attributes[i].numComponents); 125 builder.append(", "); 126 builder.append(attributes[i].offset); 127 builder.append(")"); 128 builder.append("\n"); 129 } 130 builder.append("]"); 131 return builder.toString(); 132 } 133 134 @Override 135 public boolean equals (final Object obj) { 136 if (obj == this) return true; 137 if (!(obj instanceof VertexAttributes)) return false; 138 VertexAttributes other = (VertexAttributes)obj; 139 if (this.attributes.length != other.attributes.length) return false; 140 for (int i = 0; i < attributes.length; i++) { 141 if (!attributes[i].equals(other.attributes[i])) return false; 142 } 143 return true; 144 } 145 146 @Override 147 public int hashCode () { 148 long result = 61 * attributes.length; 149 for (int i = 0; i < attributes.length; i++) 150 result = result * 61 + attributes[i].hashCode(); 151 return (int)(result ^ (result >> 32)); 152 } 153 154 /** Calculates a mask based on the contained {@link VertexAttribute} instances. The mask is a bit-wise or of each attributes 155 * {@link VertexAttribute#usage}. 156 * @return the mask */ 157 public long getMask () { 158 if (mask == -1) { 159 long result = 0; 160 for (int i = 0; i < attributes.length; i++) { 161 result |= attributes[i].usage; 162 } 163 mask = result; 164 } 165 return mask; 166 } 167 168 @Override 169 public int compareTo (VertexAttributes o) { 170 if (attributes.length != o.attributes.length) return attributes.length - o.attributes.length; 171 final long m1 = getMask(); 172 final long m2 = o.getMask(); 173 if (m1 != m2) return m1 < m2 ? -1 : 1; 174 for (int i = attributes.length - 1; i >= 0; --i) { 175 final VertexAttribute va0 = attributes[i]; 176 final VertexAttribute va1 = o.attributes[i]; 177 if (va0.usage != va1.usage) return va0.usage - va1.usage; 178 if (va0.unit != va1.unit) return va0.unit - va1.unit; 179 if (va0.numComponents != va1.numComponents) return va0.numComponents - va1.numComponents; 180 if (va0.normalized != va1.normalized) return va0.normalized ? 1 : -1; 181 if (va0.type != va1.type) return va0.type - va1.type; 182 } 183 return 0; 184 } 185 186 @Override 187 public Iterator<VertexAttribute> iterator () { 188 if (iterable == null) iterable = new ReadonlyIterable<VertexAttribute>(attributes); 189 return iterable.iterator(); 190 } 191 192 static private class ReadonlyIterator<T> implements Iterator<T>, Iterable<T> { 193 private final T[] array; 194 int index; 195 boolean valid = true; 196 197 public ReadonlyIterator (T[] array) { 198 this.array = array; 199 } 200 201 @Override 202 public boolean hasNext () { 203 if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested."); 204 return index < array.length; 205 } 206 207 @Override 208 public T next () { 209 if (index >= array.length) throw new NoSuchElementException(String.valueOf(index)); 210 if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested."); 211 return array[index++]; 212 } 213 214 @Override 215 public void remove () { 216 throw new GdxRuntimeException("Remove not allowed."); 217 } 218 219 public void reset () { 220 index = 0; 221 } 222 223 @Override 224 public Iterator<T> iterator () { 225 return this; 226 } 227 } 228 229 static private class ReadonlyIterable<T> implements Iterable<T> { 230 private final T[] array; 231 private ReadonlyIterator iterator1, iterator2; 232 233 public ReadonlyIterable (T[] array) { 234 this.array = array; 235 } 236 237 @Override 238 public Iterator<T> iterator () { 239 if (iterator1 == null) { 240 iterator1 = new ReadonlyIterator(array); 241 iterator2 = new ReadonlyIterator(array); 242 } 243 if (!iterator1.valid) { 244 iterator1.index = 0; 245 iterator1.valid = true; 246 iterator2.valid = false; 247 return iterator1; 248 } 249 iterator2.index = 0; 250 iterator2.valid = true; 251 iterator1.valid = false; 252 return iterator2; 253 } 254 } 255} 256