1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.jme3.light; 34 35 import com.jme3.export.*; 36 import com.jme3.scene.Spatial; 37 import com.jme3.util.SortUtil; 38 import java.io.IOException; 39 import java.util.*; 40 41 /** 42 * <code>LightList</code> is used internally by {@link Spatial}s to manage 43 * lights that are attached to them. 44 * 45 * @author Kirill Vainer 46 */ 47 public final class LightList implements Iterable<Light>, Savable, Cloneable { 48 49 private Light[] list, tlist; 50 private float[] distToOwner; 51 private int listSize; 52 private Spatial owner; 53 54 private static final int DEFAULT_SIZE = 1; 55 56 private static final Comparator<Light> c = new Comparator<Light>() { 57 /** 58 * This assumes lastDistance have been computed in a previous step. 59 */ 60 public int compare(Light l1, Light l2) { 61 if (l1.lastDistance < l2.lastDistance) 62 return -1; 63 else if (l1.lastDistance > l2.lastDistance) 64 return 1; 65 else 66 return 0; 67 } 68 }; 69 70 /** 71 * Default constructor for serialization. Do not use 72 */ LightList()73 public LightList(){ 74 } 75 76 /** 77 * Creates a <code>LightList</code> for the given {@link Spatial}. 78 * 79 * @param owner The spatial owner 80 */ LightList(Spatial owner)81 public LightList(Spatial owner) { 82 listSize = 0; 83 list = new Light[DEFAULT_SIZE]; 84 distToOwner = new float[DEFAULT_SIZE]; 85 Arrays.fill(distToOwner, Float.NEGATIVE_INFINITY); 86 this.owner = owner; 87 } 88 89 /** 90 * Set the owner of the LightList. Only used for cloning. 91 * @param owner 92 */ setOwner(Spatial owner)93 public void setOwner(Spatial owner){ 94 this.owner = owner; 95 } 96 doubleSize()97 private void doubleSize(){ 98 Light[] temp = new Light[list.length * 2]; 99 float[] temp2 = new float[list.length * 2]; 100 System.arraycopy(list, 0, temp, 0, list.length); 101 System.arraycopy(distToOwner, 0, temp2, 0, list.length); 102 list = temp; 103 distToOwner = temp2; 104 } 105 106 /** 107 * Adds a light to the list. List size is doubled if there is no room. 108 * 109 * @param l 110 * The light to add. 111 */ add(Light l)112 public void add(Light l) { 113 if (listSize == list.length) { 114 doubleSize(); 115 } 116 list[listSize] = l; 117 distToOwner[listSize++] = Float.NEGATIVE_INFINITY; 118 } 119 120 /** 121 * Remove the light at the given index. 122 * 123 * @param index 124 */ remove(int index)125 public void remove(int index){ 126 if (index >= listSize || index < 0) 127 throw new IndexOutOfBoundsException(); 128 129 listSize --; 130 if (index == listSize){ 131 list[listSize] = null; 132 return; 133 } 134 135 for (int i = index; i < listSize; i++){ 136 list[i] = list[i+1]; 137 } 138 list[listSize] = null; 139 } 140 141 /** 142 * Removes the given light from the LightList. 143 * 144 * @param l the light to remove 145 */ remove(Light l)146 public void remove(Light l){ 147 for (int i = 0; i < listSize; i++){ 148 if (list[i] == l){ 149 remove(i); 150 return; 151 } 152 } 153 } 154 155 /** 156 * @return The size of the list. 157 */ size()158 public int size(){ 159 return listSize; 160 } 161 162 /** 163 * @return the light at the given index. 164 * @throws IndexOutOfBoundsException If the given index is outside bounds. 165 */ get(int num)166 public Light get(int num){ 167 if (num >= listSize || num < 0) 168 throw new IndexOutOfBoundsException(); 169 170 return list[num]; 171 } 172 173 /** 174 * Resets list size to 0. 175 */ clear()176 public void clear() { 177 if (listSize == 0) 178 return; 179 180 for (int i = 0; i < listSize; i++) 181 list[i] = null; 182 183 if (tlist != null) 184 Arrays.fill(tlist, null); 185 186 listSize = 0; 187 } 188 189 /** 190 * Sorts the elements in the list acording to their Comparator. 191 * There are two reasons why lights should be resorted. 192 * First, if the lights have moved, that means their distance to 193 * the spatial changed. 194 * Second, if the spatial itself moved, it means the distance from it to 195 * the individual lights might have changed. 196 * 197 * 198 * @param transformChanged Whether the spatial's transform has changed 199 */ sort(boolean transformChanged)200 public void sort(boolean transformChanged) { 201 if (listSize > 1) { 202 // resize or populate our temporary array as necessary 203 if (tlist == null || tlist.length != list.length) { 204 tlist = list.clone(); 205 } else { 206 System.arraycopy(list, 0, tlist, 0, list.length); 207 } 208 209 if (transformChanged){ 210 // check distance of each light 211 for (int i = 0; i < listSize; i++){ 212 list[i].computeLastDistance(owner); 213 } 214 } 215 216 // now merge sort tlist into list 217 SortUtil.msort(tlist, list, 0, listSize - 1, c); 218 } 219 } 220 221 /** 222 * Updates a "world-space" light list, using the spatial's local-space 223 * light list and its parent's world-space light list. 224 * 225 * @param local 226 * @param parent 227 */ update(LightList local, LightList parent)228 public void update(LightList local, LightList parent){ 229 // clear the list as it will be reconstructed 230 // using the arguments 231 clear(); 232 233 while (list.length <= local.listSize){ 234 doubleSize(); 235 } 236 237 // add the lights from the local list 238 System.arraycopy(local.list, 0, list, 0, local.listSize); 239 for (int i = 0; i < local.listSize; i++){ 240 // list[i] = local.list[i]; 241 distToOwner[i] = Float.NEGATIVE_INFINITY; 242 } 243 244 // if the spatial has a parent node, add the lights 245 // from the parent list as well 246 if (parent != null){ 247 int sz = local.listSize + parent.listSize; 248 while (list.length <= sz) 249 doubleSize(); 250 251 for (int i = 0; i < parent.listSize; i++){ 252 int p = i + local.listSize; 253 list[p] = parent.list[i]; 254 distToOwner[p] = Float.NEGATIVE_INFINITY; 255 } 256 257 listSize = local.listSize + parent.listSize; 258 }else{ 259 listSize = local.listSize; 260 } 261 } 262 263 /** 264 * Returns an iterator that can be used to iterate over this LightList. 265 * 266 * @return an iterator that can be used to iterate over this LightList. 267 */ iterator()268 public Iterator<Light> iterator() { 269 return new Iterator<Light>(){ 270 271 int index = 0; 272 273 public boolean hasNext() { 274 return index < size(); 275 } 276 277 public Light next() { 278 if (!hasNext()) 279 throw new NoSuchElementException(); 280 281 return list[index++]; 282 } 283 284 public void remove() { 285 LightList.this.remove(--index); 286 } 287 }; 288 } 289 290 @Override clone()291 public LightList clone(){ 292 try{ 293 LightList clone = (LightList) super.clone(); 294 295 clone.owner = null; 296 clone.list = list.clone(); 297 clone.distToOwner = distToOwner.clone(); 298 clone.tlist = null; // list used for sorting only 299 300 return clone; 301 }catch (CloneNotSupportedException ex){ 302 throw new AssertionError(); 303 } 304 } 305 write(JmeExporter ex)306 public void write(JmeExporter ex) throws IOException { 307 OutputCapsule oc = ex.getCapsule(this); 308 // oc.write(owner, "owner", null); 309 310 ArrayList<Light> lights = new ArrayList<Light>(); 311 for (int i = 0; i < listSize; i++){ 312 lights.add(list[i]); 313 } 314 oc.writeSavableArrayList(lights, "lights", null); 315 } 316 read(JmeImporter im)317 public void read(JmeImporter im) throws IOException { 318 InputCapsule ic = im.getCapsule(this); 319 // owner = (Spatial) ic.readSavable("owner", null); 320 321 List<Light> lights = ic.readSavableArrayList("lights", null); 322 listSize = lights.size(); 323 324 // NOTE: make sure the array has a length of at least 1 325 int arraySize = Math.max(DEFAULT_SIZE, listSize); 326 list = new Light[arraySize]; 327 distToOwner = new float[arraySize]; 328 329 for (int i = 0; i < listSize; i++){ 330 list[i] = lights.get(i); 331 } 332 333 Arrays.fill(distToOwner, Float.NEGATIVE_INFINITY); 334 } 335 336 } 337