Home | History | Annotate | Download | only in light
      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      */
     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      */
     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      */
     93     public void setOwner(Spatial owner){
     94         this.owner = owner;
     95     }
     96 
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    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 
    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 
    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