• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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