Added user definable textures to LensFlare

Hi all,



At the moment, LensFlare uses the textures located in the jmetest-data jar. This is good, because this gives it a way to work right out of the box.



However, typically the user doesn’t want to include the jmetest-data jar in their game, or they just want to have different textures on their lens flare.



This is why I’ve added an additional constructor that takes a Texture[] instead. This makes LensFlare to be able to both work right out of the box, and have user definable textures.



If you like it, use it. Otherwise throw it away :slight_smile:


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
package com.jme.effects;

import java.util.Iterator;

import com.jme.image.Texture;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Geometry;
import com.jme.scene.Node;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;

/**
 * <code>LensFlare</code>
 *  First crack at a lens flare for jme.  Notice that currently,
 *  it doesn't do occlusion culling.
 *
 *   The easiest way to use this class is to attach it as a child to a lightnode.
 *   Optionally you can make it a child or a sibling of an object you wish to
 *   have a 'glint' on.  In the case of sibling, use
 *   setLocalTranslation(sibling.getLocalTranslation()) or something similar to
 *   ensure position.
 * @author Joshua Slack
 * @author Per Thulin (added functionality to have user definable textures)
 * @version $Id: LensFlare.java,v 1.15 2004/09/14 21:52:23 mojomonkey Exp $
 */
public class LensFlare extends Node {
   private static final long serialVersionUID = 1L;
   private Quad mainFlare;
   private Quad sFlare[];
   private Vector2f midPoint;
   private Vector3f flarePoint;
   
   /**
    * Creates a new LensFlare node, with the standard textures located in
    * jmetest-data.jar.
    * @param name The name of the node.
    */
   public LensFlare(String name) {
      super(name);
      initChildren();
      initLensFlare();
   }
   
   /**
    * Creates a new LensFlare node, with the specified textures.
    * @param name The name of the node.
    * @param textures The flare textures (must have four elements).
    */
   public LensFlare(String name, Texture[] textures) {
      super(name);
      if (textures.length != 4)
         System.out.println("The passed Texture[] must have four elements!");
      initChildren(textures);
      initLensFlare();
   }
   
   /**
    * initLensFlare
    */
   private void initLensFlare() {
      Iterator it = children.iterator();
      while (it.hasNext()) {
         Geometry spat = (Geometry)it.next();
         spat.setVBOVertexEnabled(true);
         spat.setVBONormalEnabled(true);
         spat.setVBOTextureEnabled(true);
         spat.setVBOColorEnabled(true);
      }
      this.setRenderQueueMode(Renderer.QUEUE_ORTHO);
      this.setLightCombineMode(LightState.OFF);
      this.setTextureCombineMode(TextureState.REPLACE);
   }
   
   /**
    * <code>updateWorldData</code> updates all the children maintained by
    * this node.  It decides where on the screen to draw each flare quad.
    * @param time the frame time.
    */
   public void updateWorldData(float time) {
      // Update flare:
      super.updateWorldData(time);
      // Locate light src on screen x,y
      flarePoint = DisplaySystem.getDisplaySystem().getScreenCoordinates(
            worldTranslation, flarePoint).subtractLocal(midPoint.x, midPoint.y, 0);
      if (flarePoint.z >= 1.0f) { // if it's behind us
         setForceCull(true);
         return;
      } else setForceCull(false);
      // define a line from light src to one opposite across the center point
      // draw main flare at src point
      Vector3f tempPoint;
      tempPoint = mainFlare.getWorldTranslation();
      tempPoint.x = flarePoint.x + midPoint.x;
      tempPoint.y = flarePoint.y + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[0].getWorldTranslation();
      tempPoint.x = flarePoint.x / .8f + midPoint.x;
      tempPoint.y = flarePoint.y / .8f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[1].getWorldTranslation();
      tempPoint.x = flarePoint.x + midPoint.x;
      tempPoint.y = flarePoint.y + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[2].getWorldTranslation();
      tempPoint.x = flarePoint.x / .8f + midPoint.x;
      tempPoint.y = flarePoint.y / .8f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[3].getWorldTranslation();
      tempPoint.x = flarePoint.x / 2f + midPoint.x;
      tempPoint.y = flarePoint.y / 2f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[4].getWorldTranslation();
      tempPoint.x = flarePoint.x / 2.2f + midPoint.x;
      tempPoint.y = flarePoint.y / 2.2f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[5].getWorldTranslation();
      tempPoint.x = flarePoint.x / 2.4f + midPoint.x;
      tempPoint.y = flarePoint.y / 2.4f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[6].getWorldTranslation();
      tempPoint.x = flarePoint.x / 3f + midPoint.x;
      tempPoint.y = flarePoint.y / 3f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[7].getWorldTranslation();
      tempPoint.x = flarePoint.x / 5f + midPoint.x;
      tempPoint.y = flarePoint.y / 5f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[8].getWorldTranslation();
      tempPoint.x = -flarePoint.x / 3f + midPoint.x;
      tempPoint.y = -flarePoint.y / 3f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[9].getWorldTranslation();
      tempPoint.x = -flarePoint.x / 2f + midPoint.x;
      tempPoint.y = -flarePoint.y / 2f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[10].getWorldTranslation();
      tempPoint.x = -flarePoint.x / 1.8f + midPoint.x;
      tempPoint.y = -flarePoint.y / 1.8f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[11].getWorldTranslation();
      tempPoint.x = -flarePoint.x / 1.5f + midPoint.x;
      tempPoint.y = -flarePoint.y / 1.5f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[12].getWorldTranslation();
      tempPoint.x = -flarePoint.x / 1.4f + midPoint.x;
      tempPoint.y = -flarePoint.y / 1.4f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[13].getWorldTranslation();
      tempPoint.x = -flarePoint.x / 1.1f + midPoint.x;
      tempPoint.y = -flarePoint.y / 1.1f + midPoint.y;
      tempPoint.z = 0;
      
      tempPoint = sFlare[14].getWorldTranslation();
      tempPoint.x = -flarePoint.x + midPoint.x;
      tempPoint.y = -flarePoint.y + midPoint.y;
      tempPoint.z = 0;
   }
   
   /**
    * initChildren
    */
   private void initChildren() {
      DisplaySystem display = DisplaySystem.getDisplaySystem();
      midPoint = new Vector2f(display.getWidth() >> 1, display.getHeight() >> 1);
      
      AlphaState as1 = display.getRenderer().createAlphaState();
      as1.setBlendEnabled(true);
      as1.setSrcFunction(AlphaState.SB_SRC_ALPHA);
      as1.setDstFunction(AlphaState.DB_ONE);
      as1.setTestEnabled(true);
      as1.setTestFunction(AlphaState.TF_GREATER);
      as1.setEnabled(true);
      
      TextureState ts = display.getRenderer().createTextureState();
      ts.setTexture(
            TextureManager.loadTexture(
                  LensFlare.class.getClassLoader().getResource(
                  "jmetest/data/texture/flare1.png"),
                  Texture.MM_LINEAR_LINEAR,
                  Texture.FM_LINEAR,
                  true));
      ts.setEnabled(true);
      ts.apply();
      
      TextureState ts2 = display.getRenderer().createTextureState();
      ts2.setTexture(
            TextureManager.loadTexture(
                  LensFlare.class.getClassLoader().getResource(
                  "jmetest/data/texture/flare2.png"),
                  Texture.MM_LINEAR_LINEAR,
                  Texture.FM_LINEAR,
                  true));
      ts2.setEnabled(true);
      ts2.apply();
      
      TextureState ts3 = display.getRenderer().createTextureState();
      ts3.setTexture(
            TextureManager.loadTexture(
                  LensFlare.class.getClassLoader().getResource(
                  "jmetest/data/texture/flare3.png"),
                  Texture.MM_LINEAR_LINEAR,
                  Texture.FM_LINEAR,
                  true));
      ts3.setEnabled(true);
      ts3.apply();
      
      TextureState ts4 = display.getRenderer().createTextureState();
      ts4.setTexture(
            TextureManager.loadTexture(
                  LensFlare.class.getClassLoader().getResource(
                  "jmetest/data/texture/flare4.png"),
                  Texture.MM_LINEAR_LINEAR,
                  Texture.FM_LINEAR,
                  true));
      ts4.setEnabled(true);
      ts4.apply();
      
      for (int i = 0; i < RenderState.RS_MAX_STATE; i++) {
         setRenderState(defaultStateList[i]);
      }
      setRenderState(as1);
      setRenderState(ts3);
      
      mainFlare = new Quad("quad", midPoint.x * .75f, midPoint.x * .75f);
      mainFlare.setRenderState(ts);
      mainFlare.setSolidColor(new ColorRGBA(.95f, .95f, .95f, 1f));
      this.attachChild(mainFlare);
      
      sFlare = new Quad[15];
      sFlare[0] = new Quad("sf0", midPoint.x * 1.25f, midPoint.x * 1.25f);
      sFlare[0].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f, 1f));
      sFlare[0].setRenderState(ts4);
      
      sFlare[1] = new Quad("sf1", midPoint.x * .75f, midPoint.x * .75f);
      sFlare[1].setSolidColor(new ColorRGBA(.8f, .8f, .8f, 1f));
      sFlare[1].setRenderState(ts2);
      
      sFlare[2] = new Quad("sf2", midPoint.x * .15f, midPoint.x * .15f);
      sFlare[2].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f, 1f));
      
      sFlare[3] = new Quad("sf3", midPoint.x * .08f, midPoint.x * .08f);
      sFlare[3].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 50f / 255f,
            1f));
      
      sFlare[4] = new Quad("sf4", midPoint.x * .40f, midPoint.x * .40f);
      sFlare[4].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 50f / 255f,
            1f));
      
      sFlare[5] = new Quad("sf5", midPoint.x * .1f, midPoint.x * .1f);
      sFlare[5].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 50f / 255f,
            1f));
      
      sFlare[6] = new Quad("sf6", midPoint.x * .25f, midPoint.x * .25f);
      sFlare[6].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 50f / 255f,
            1f));
      
      sFlare[7] = new Quad("sf7", midPoint.x * .01f, midPoint.x * .01f);
      sFlare[7].setSolidColor(new ColorRGBA(.8f, .8f, .8f, 1f));
      
      sFlare[8] = new Quad("sf8", midPoint.x * .02f, midPoint.x * .02f);
      sFlare[8].setSolidColor(new ColorRGBA(.8f, .8f, .8f, 1f));
      
      sFlare[9] = new Quad("sf9", midPoint.x * .1f, midPoint.x * .1f);
      sFlare[9].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f, 1f));
      
      sFlare[10] = new Quad("sf10", midPoint.x * .06f, midPoint.x * .06f);
      sFlare[10].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f,
            1f));
      
      sFlare[11] = new Quad("sf11", midPoint.x * .375f, midPoint.x * .375f);
      sFlare[11].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f,
            1f));
      
      sFlare[12] = new Quad("sf12", midPoint.x * .1f, midPoint.x * .1f);
      sFlare[12].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f,
            1f));
      
      sFlare[13] = new Quad("sf13", midPoint.x * .25f, midPoint.x * .25f);
      sFlare[13].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f,
            1f));
      
      sFlare[14] = new Quad("sf14", midPoint.x * .75f, midPoint.x * .75f);
      sFlare[14].setSolidColor(new ColorRGBA(.8f, .8f, .8f, 1f));
      sFlare[14].setRenderState(ts2);
      
      for (int i = 0; i < sFlare.length; i++) {
         this.attachChild(sFlare[i]);
      }
   }
   
   /**
    * initChildren
    */
   private void initChildren(Texture[] textures) {
      DisplaySystem display = DisplaySystem.getDisplaySystem();
      midPoint = new Vector2f(display.getWidth() >> 1, display.getHeight() >> 1);
      
      AlphaState as1 = display.getRenderer().createAlphaState();
      as1.setBlendEnabled(true);
      as1.setSrcFunction(AlphaState.SB_SRC_ALPHA);
      as1.setDstFunction(AlphaState.DB_ONE);
      as1.setTestEnabled(true);
      as1.setTestFunction(AlphaState.TF_GREATER);
      as1.setEnabled(true);
      
      TextureState ts = display.getRenderer().createTextureState();
      ts.setTexture(textures[0]);
      ts.setEnabled(true);
      ts.apply();
      
      TextureState ts2 = display.getRenderer().createTextureState();
      ts2.setTexture(textures[1]);
      ts2.setEnabled(true);
      ts2.apply();
      
      TextureState ts3 = display.getRenderer().createTextureState();
      ts3.setTexture(textures[2]);
      ts3.setEnabled(true);
      ts3.apply();
      
      TextureState ts4 = display.getRenderer().createTextureState();
      ts4.setTexture(textures[3]);
      ts4.setEnabled(true);
      ts4.apply();
      
      for (int i = 0; i < RenderState.RS_MAX_STATE; i++) {
         setRenderState(defaultStateList[i]);
      }
      setRenderState(as1);
      setRenderState(ts3);
      
      mainFlare = new Quad("quad", midPoint.x * .75f, midPoint.x * .75f);
      mainFlare.setRenderState(ts);
      mainFlare.setSolidColor(new ColorRGBA(.95f, .95f, .95f, 1f));
      this.attachChild(mainFlare);
      
      sFlare = new Quad[15];
      sFlare[0] = new Quad("sf0", midPoint.x * 1.25f, midPoint.x * 1.25f);
      sFlare[0].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f, 1f));
      sFlare[0].setRenderState(ts4);
      
      sFlare[1] = new Quad("sf1", midPoint.x * .75f, midPoint.x * .75f);
      sFlare[1].setSolidColor(new ColorRGBA(.8f, .8f, .8f, 1f));
      sFlare[1].setRenderState(ts2);
      
      sFlare[2] = new Quad("sf2", midPoint.x * .15f, midPoint.x * .15f);
      sFlare[2].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f, 1f));
      
      sFlare[3] = new Quad("sf3", midPoint.x * .08f, midPoint.x * .08f);
      sFlare[3].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 50f / 255f,
            1f));
      
      sFlare[4] = new Quad("sf4", midPoint.x * .40f, midPoint.x * .40f);
      sFlare[4].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 50f / 255f,
            1f));
      
      sFlare[5] = new Quad("sf5", midPoint.x * .1f, midPoint.x * .1f);
      sFlare[5].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 50f / 255f,
            1f));
      
      sFlare[6] = new Quad("sf6", midPoint.x * .25f, midPoint.x * .25f);
      sFlare[6].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 50f / 255f,
            1f));
      
      sFlare[7] = new Quad("sf7", midPoint.x * .01f, midPoint.x * .01f);
      sFlare[7].setSolidColor(new ColorRGBA(.8f, .8f, .8f, 1f));
      
      sFlare[8] = new Quad("sf8", midPoint.x * .02f, midPoint.x * .02f);
      sFlare[8].setSolidColor(new ColorRGBA(.8f, .8f, .8f, 1f));
      
      sFlare[9] = new Quad("sf9", midPoint.x * .1f, midPoint.x * .1f);
      sFlare[9].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f, 1f));
      
      sFlare[10] = new Quad("sf10", midPoint.x * .06f, midPoint.x * .06f);
      sFlare[10].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f,
            1f));
      
      sFlare[11] = new Quad("sf11", midPoint.x * .375f, midPoint.x * .375f);
      sFlare[11].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f,
            1f));
      
      sFlare[12] = new Quad("sf12", midPoint.x * .1f, midPoint.x * .1f);
      sFlare[12].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f,
            1f));
      
      sFlare[13] = new Quad("sf13", midPoint.x * .25f, midPoint.x * .25f);
      sFlare[13].setSolidColor(new ColorRGBA(30f / 255f, 30f / 255f, 0f / 255f,
            1f));
      
      sFlare[14] = new Quad("sf14", midPoint.x * .75f, midPoint.x * .75f);
      sFlare[14].setSolidColor(new ColorRGBA(.8f, .8f, .8f, 1f));
      sFlare[14].setRenderState(ts2);
      
      for (int i = 0; i < sFlare.length; i++) {
         this.attachChild(sFlare[i]);
      }
   }
   
}

I agree, the code should not require jmetest.jar.

This may be more a question for Renanse than you Per (I haven’t looked too closely at the lens flare code), but if we could have an arbitrary number of textures rather than limited to 4, that’d be great.

Ugh, that was me. :slight_smile:

That looks pretty good to me. While I haven’t had a chance to add it into my local copy, I see no issues with it. Got my approval.

New changes in. The Test also seems faster to me… Definitely faster than the webstart version anyway (not linked from the demo page, but you can call manually.)