001/**
002 * Copyright (c) 2008-2014 Ardor Labs, Inc.
003 *
004 * This file is part of Ardor3D.
005 *
006 * Ardor3D is free software: you can redistribute it and/or modify it 
007 * under the terms of its license which may be found in the accompanying
008 * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
009 */
010
011package com.ardor3d.renderer.state;
012
013import java.util.Collections;
014import java.util.Comparator;
015import java.util.List;
016
017import com.ardor3d.bounding.BoundingVolume;
018import com.ardor3d.light.Light;
019import com.ardor3d.light.PointLight;
020import com.ardor3d.light.SpotLight;
021import com.ardor3d.math.Plane;
022import com.ardor3d.math.type.ReadOnlyColorRGBA;
023import com.ardor3d.math.type.ReadOnlyVector3;
024import com.ardor3d.scenegraph.Mesh;
025import com.ardor3d.scenegraph.Spatial;
026
027public abstract class LightUtil {
028    private static class LightComparator implements Comparator<Light> {
029        private Spatial _sp;
030
031        public void setSpatial(final Spatial sp) {
032            _sp = sp;
033        }
034
035        @Override
036        public int compare(final Light l1, final Light l2) {
037            final double v1 = getValueFor(l1, _sp.getWorldBound());
038            final double v2 = getValueFor(l2, _sp.getWorldBound());
039            final double cmp = v1 - v2;
040            if (0 > cmp) {
041                return 1;
042            } else if (0 < cmp) {
043                return -1;
044            } else {
045                return 0;
046            }
047        }
048    }
049
050    private static LightComparator lightComparator = new LightComparator();
051
052    public static void sort(final Mesh geometry, final List<Light> lights) {
053        lightComparator.setSpatial(geometry);
054        Collections.sort(lights, lightComparator);
055    }
056
057    protected static double getValueFor(final Light l, final BoundingVolume val) {
058        if (l == null || !l.isEnabled()) {
059            return 0;
060        } else if (l.getType() == Light.Type.Directional) {
061            return getColorValue(l);
062        } else if (l.getType() == Light.Type.Point) {
063            return getValueFor((PointLight) l, val);
064        } else if (l.getType() == Light.Type.Spot) {
065            return getValueFor((SpotLight) l, val);
066        }
067        // If a new type of light was added and this was not updated return .3
068        return .3;
069    }
070
071    protected static double getValueFor(final PointLight l, final BoundingVolume val) {
072        if (val == null) {
073            return 0;
074        }
075        if (l.isAttenuate()) {
076            final ReadOnlyVector3 location = l.getLocation();
077            final double dist = val.distanceTo(location);
078
079            final double color = getColorValue(l);
080            final double amlat = l.getConstant() + l.getLinear() * dist + l.getQuadratic() * dist * dist;
081
082            return color / amlat;
083        }
084
085        return getColorValue(l);
086    }
087
088    protected static double getValueFor(final SpotLight l, final BoundingVolume val) {
089        if (val == null) {
090            return 0;
091        }
092        final ReadOnlyVector3 direction = l.getDirection();
093        final ReadOnlyVector3 location = l.getLocation();
094        // direction is copied into Plane, not reused.
095        final Plane p = new Plane(direction, direction.dot(location));
096        if (val.whichSide(p) != Plane.Side.Inside) {
097            return getValueFor((PointLight) l, val);
098        }
099
100        return 0;
101    }
102
103    protected static double getColorValue(final Light l) {
104        return strength(l.getAmbient()) + strength(l.getDiffuse());
105    }
106
107    protected static double strength(final ReadOnlyColorRGBA color) {
108        return Math.sqrt(color.getRed() * color.getRed() + color.getGreen() * color.getGreen() + color.getBlue()
109                * color.getBlue());
110    }
111}