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}