Strange bug in DirectionalLight?

Hello,

I think, I found a strange bug with DirectionalLight:



History:

I discovered HelloAnimation.java within the old starter guide that demonstrates a pointlight, wich rotates around a sphere.

I now wanted to use a DirectionalLight instead of a PointLight.

It should do the following:

Rotate around the Z-Axis and light a sphere that is situated at (0,0,0), just like the PointLight in HelloAnimation.java.



Ok, I tried it straight forward:



import com.jme.app.SimpleGame;
import com.jme.scene.shape.Sphere;
import com.jme.scene.Node;
import com.jme.math.Vector3f;
import com.jme.math.Quaternion;
import com.jme.math.FastMath;
import com.jme.animation.SpatialTransformer;
import com.jme.light.SimpleLightNode;
import com.jme.renderer.ColorRGBA;

import com.jme.scene.state.MaterialState;
import com.jme.light.DirectionalLight;


/**
* This class is adapted from the HelloAnimation.java from Jack Lindamood.
*/
public class PlanetAnimation2 extends SimpleGame {

public static void main(String[] args) {
PlanetAnimation2 app = new PlanetAnimation2();
app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}

protected void simpleInitGame() {
Sphere p = new Sphere("My sphere",30,30,5);

MaterialState planetMaterial = display.getRenderer().createMaterialState();
planetMaterial.setAmbient(new ColorRGBA(0.15f,0.25f,0.05f,1.0f));
planetMaterial.setDiffuse(new ColorRGBA(0.3f, 0.7f, 0.1f, 1.0f));
p.setRenderState(planetMaterial);
p.updateRenderState();

//I will rotate this pivot to move my light
Node pivot = new Node("Pivot node for sun sphere");

//This light will rotate around my sphere. Notice I don't
//give it a position
//PointLight pl=new PointLight();
DirectionalLight pl=new DirectionalLight();

//Color the light white
pl.setDiffuse(ColorRGBA.white);
//Enable the light
pl.setEnabled(true);
pl.setDirection(new Vector3f(0.0f, 0.0f, 0.0f));
//Remove the default light and attach this one
lightState.setEnabled(true);
lightState.detachAll();
lightState.attach(pl);
//This node will hold my light
SimpleLightNode ln=
new SimpleLightNode("A node for my light",pl);

ln.setLocalTranslation(new Vector3f(0.0f, 80.0f, 0.0f));
ln.updateRenderState();

//I set the light's position thru the node
pivot.setLocalTranslation(new Vector3f(0,0,0));
//I attach the light's node to my pivot
pivot.attachChild(ln);

SpatialTransformer st = new SpatialTransformer(1);
st.setObject(ln, 0, -1);
//Assign a rotation for object 0 at time 0 to rotate 0
//degrees around the z axis
Quaternion x0=new Quaternion();
x0.fromAngleAxis(0,new Vector3f(0,0,1));
st.setRotation(0,0,x0);
//Assign a rotation for object 0 at time 2 to rotate 180
//degrees around the z axis
Quaternion x180=new Quaternion();
x180.fromAngleAxis(FastMath.DEG_TO_RAD*180,new Vector3f(0,0,1));
st.setRotation(0,10,x180);
//Assign a rotation for object 0 at time 4 to rotate 360
//degrees around the z axis
Quaternion x360=new Quaternion();
x360.fromAngleAxis(FastMath.DEG_TO_RAD*360,new Vector3f(0,0,1));
st.setRotation(0,20,x360);
//Prepare my controller to start moving around
st.interpolateMissing();
//Tell my pivot it is controlled by st
pivot.addController(st);

rootNode.attachChild(pivot);
rootNode.attachChild(p);
rootNode.updateRenderState();

}
}



This did not work. But than ... I changed the quaternion-vector from (0,0,1) to (0,1,0) - wich should rotate the directional light around the Y-axis.
This did work !!!!!
And I tried (1,0,0) in the quaternion vector (Rotation around X-Axis)  like that:


//Assign a rotation for object 0 at time 0 to rotate 0
//degrees around the z axis
Quaternion x0=new Quaternion();
x0.fromAngleAxis(0,new Vector3f(1,0,0));
st.setRotation(0,0,x0);
//Assign a rotation for object 0 at time 2 to rotate 180
//degrees around the z axis
Quaternion x180=new Quaternion();
x180.fromAngleAxis(FastMath.DEG_TO_RAD*180,new Vector3f(1,0,0));
st.setRotation(0,10,x180);
//Assign a rotation for object 0 at time 4 to rotate 360
//degrees around the z axis
Quaternion x360=new Quaternion();
x360.fromAngleAxis(FastMath.DEG_TO_RAD*360,new Vector3f(1,0,0));
st.setRotation(0,20,x360);



This also worked !!!!!

But why ... why only ... does it not work with (0,0,1) ?
I tried out a lot of things, but every time it did rotate correctly with (0,1,0) and (1,0,0) but not (0,0,1).
It seems that only the projected portion relative to the X-Y-plane is used for the rotation of the directional light.

Then I discovered DirectionalLight.setDirection(), but that didn't work for my purpose.
(Or I didn't use it correctly)

Can you tell me, how to do this without using a point light at "unlimited distance" ?


Ogli

setDirection is the only relevant method for a directional light(as it has no position)…and setDirection is exactly what the SimpleLightNode does to a directional light set on it upon update…

the way you set it up it does rotate around the z-axis(it rotates around the axis it is pointing, thus you see no change)…add this to see how it behaves:



        Box box = new Box("lightBox", new Vector3f(-1,-1,-1), new Vector3f(1,1,1));

        ln.attachChild(box);

The hint with the box didn't really help me, but …

I had some free time and found a strange workaround (see code below).



I still do not yet fully understand and hope that other people can reproduce the result.



Scince the DirectionalLight.setDirection() method did not work,

I used rotation of the light node -

but with a very strange value (45 degree around the Y-axis) !!!



Can anyone please test and varify ?

Can anyone tell me, what would be a better solution ?

I simply want to let directional light rotate around the Z-axis …





import com.jme.app.SimpleGame;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.Node;
import com.jme.math.Vector3f;
import com.jme.math.Quaternion;
import com.jme.math.FastMath;
import com.jme.animation.SpatialTransformer;
import com.jme.light.SimpleLightNode;
import com.jme.renderer.ColorRGBA;

import com.jme.scene.state.MaterialState;
import com.jme.light.DirectionalLight;

public class PlanetAnimation2 extends SimpleGame {

   public static void main(String[] args) {
      PlanetAnimation2 app = new PlanetAnimation2();
      app.setDialogBehaviour(SimpleGame.ALWAYS_SHOW_PROPS_DIALOG);
      app.start();
   }
   
   protected void simpleInitGame() {
      // make a simple sphere
      Sphere p = new Sphere("My sphere",30,30,5);
      
      // some things to color the object
      MaterialState planetMaterial = display.getRenderer().createMaterialState();
      planetMaterial.setAmbient(new ColorRGBA(0.15f,0.25f,0.05f,1.0f));
      planetMaterial.setDiffuse(new ColorRGBA(0.3f, 0.7f, 0.1f, 1.0f));
      p.setRenderState(planetMaterial);
      
      //I will rotate this pivot to move my light
      Node pivot = new Node("Pivot node for sun sphere");
      
      //This light will rotate around my sphere.
      DirectionalLight pl=new DirectionalLight();
      
      //Color the light white
      pl.setDiffuse(ColorRGBA.white);
      //Enable the light
      pl.setEnabled(true);
      
      //Remove the default light and attach this one
      lightState.setEnabled(true);
      lightState.detachAll();
      lightState.attach(pl);
      
      //This node will hold my light
      SimpleLightNode ln= new SimpleLightNode("A node for my light",pl);
      
      // STRANGE VALUES FOUND BY EXPERIMENTATION:
      ln.setLocalTranslation(new Vector3f(-10.0f, 0.0f, 0.0f));
      ln.setLocalRotation((new Quaternion()).fromAngleAxis(FastMath.DEG_TO_RAD*45,new Vector3f(0,1,0)));
      ln.updateRenderState();
      
      //attach a box as suggested by MrCoder:
      Box box = new Box("lightBox", new Vector3f(-0.1f,-0.1f,-0.1f), new Vector3f(0.1f,0.1f,0.1f));
      ln.attachChild(box);
      
      //I attach the light's node to my pivot
      pivot.attachChild(ln);
      
      //create a spatial transformer to rotate the pivot node:
      SpatialTransformer st = new SpatialTransformer(1);
      st.setObject(pivot, 0, -1);
      //Assign a rotation for object 0 at time 0 to rotate 0
      //degrees around the z axis
      Quaternion x0=new Quaternion();
      x0.fromAngleAxis(0,new Vector3f(0,0,1));
      st.setRotation(0,0,x0);
      //Assign a rotation for object 0 at time 2 to rotate 180
      //degrees around the z axis
      Quaternion x180=new Quaternion();
      x180.fromAngleAxis(FastMath.DEG_TO_RAD*180,new Vector3f(0,0,1));
      st.setRotation(0,10,x180);
      //Assign a rotation for object 0 at time 4 to rotate 360
      //degrees around the z axis
      Quaternion x360=new Quaternion();
      x360.fromAngleAxis(FastMath.DEG_TO_RAD*360,new Vector3f(0,0,1));
      st.setRotation(0,20,x360);
      //Prepare my controller to start moving around
      st.interpolateMissing();
      //Tell my pivot it is controlled by st
      pivot.addController(st);
      
      rootNode.attachChild(pivot);
      rootNode.attachChild(p);
      rootNode.updateRenderState();
      
   }
}

I think you misunderstand what Mr. Coder is telling you.



DirectionalLight is an infinite light. Think of the light that the sun projects. When you rotate the light about the Z axis, this is the same axis that you are looking (your direction vector) the change is not apparent. It's simply rotating behind you (or in front of you), and because this light is infinite it continues to light the object evenly and doesn't appear to change. So, depending on where the DirectionalLight is initially positioned, rotating about one axis will have no visible affect.

First this (so you don't think I talk about vapour):

http://www.sternenschwarm.de/testmoon.html



And then:

I think, we misunderstood each other - of course, Directional Light is parallel light with direction (wich I want to modify over time).

My problem was, that I did not know, what is the best way to do this.

One simple way seemed to be, to use a SimpleLightNode and rotate this to change the light's direction over time.

So I tried it this way, in order to use SpatialTransformer as a Controller.



It did work, like it worked with a PointLight (see postings above).

The odd thing was, that the values I had to use were kind of strange for rotations around the Z-axis.

But overall the whole thing worked just fine…

You can download a running Demo (tested on WinXP and Linux, Java 1.4):

http://www.sternenschwarm.de/testmoon/testmoon1.zip



And by the way:

Your game engine is great!

And a happy new Year!



Greetings from good old germany…

To complete it, this was the point:



In order to create a directional light that “changes direction over time” (not “rotate”, ok),

the SimpleLightNode had first to be rotated 45 degrees around the X axis or Y axis

and then be rotated around the Z axis, using a SpatialTransformer.

I just expected it to be 90 degrees instead of 45



the relevant Code:

  SimpleLightNode ln= new SimpleLightNode(“A node for my light”, light);

  ln.setLocalRotation((new Quaternion()).fromAngleAxis(FastMath.DEG_TO_RAD*45,new Vector3f(0,1,0)));


just checked the simplelightnode/lightnode source…



the updateWorldData method starts with

        super.updateWorldData(time);

        lightRotate = worldRotation.mult(localRotation, lightRotate);



is this not doing double work? super.updateWorldData allready does the worldRotation*localRotation right?

that would explain why 45 degrees turns up as 90…



but in that case it's strange that someone else did'nt notice it…

and what's the deal with not just using

                dLight.setDirection(worldRotation.getRotationColumn(2, dLight.getDirection()));

just like it's using worldTranslation for the position?

shrug.  If it can work sufficiently better then let's change it :slight_smile:

nod



(does that mean you will change it? or were you trying to make me post the complete code s:smiley:

I'm still on travel, so bear with me :)  If you can specify which files and the exact changes and why, I'll have a look.  Sorry to make you do the footwork…

would be nice to have mojo or whoever is the author explain the code…it must be like that for a reason I suppose…something I'm not getting



and no prob in doin footwork  :smiley:

First of all:

Yeah! Maybe I'm not that stupid as I thought…

Thanks for having reviewed this!  :slight_smile:



And then:

MrCoder said:

and what's the deal with not just using
                dLight.setDirection(worldRotation.getRotationColumn(2, dLight.getDirection()));
just like it's using worldTranslation for the position?


I don't get it, what you are trying to explain.
(maybe I'm just too stupid)
I did not use dLight.setDirection( ), because I simply wanted to use a SpatialTransformer.
Of course, I could have written my own Controller-derived class for that purpose, but there are two problems following this method of work.

And to answer the question, why it might be that nobody discovered this problem before - it could be that simple:
Nobody had an interest in time-dependent changing directional light before.
If you look at my demo (link above), you know why I'm in a need to "rotate the sun"  :D


edit:
I could imagine that spotlights would also be affected by this.
PointLight of course would be unchanged by such a bug, scince they are so isotrop.

hehe no no no no, you are not stupid…the code I was talking about was referring to the SimpleLightNode class and the extract was from there…

Yep, looks like a bug, sorry for not believing you earlier Ogli :slight_smile: I'm sitting in a hotel room with a 400 MHz TiBook right now, so I won't be able to fix it until at least Tuesday.

too bad it isnt possible to hand out cvs commit access for only 10 minutes, so I can do it  :smiley:

Just kicking this up, because according to Ogli:



http://www.jmonkeyengine.com/jmeforum/index.php?topic=3142.msg24032#msg24032



This is still not fixed…

what's the fix needed for this?

MrCoder and you said this:

http://www.jmonkeyengine.com/jmeforum/index.php?topic=2626.msg20366#msg20366



I'm not so deep into jME source and 3D math right now, but will play the tester, if you like.

I've eliminated the extra work and it tests okay locally.  I also looked at switching directional to using rotation, but translation seems much cleaner and easier to work with, so I'm leaving it alone for now.  It will be in cvs soon.