Problem/Confusion about lights and nodes

Hi,

I am confused about how lights work, or at least how people get them to work such that they illuminate all geometry. In my program I can get lighting to work but it doesn’t seem to work the way I want.

So everything is attached to nodes. Lets say I have 3 nodes:

rootNode, lightNode, and mapNode.

rootNode is the rootNode.
lightNode contains my lights (mostly point lights).
mapNode contains the geometry of my game area. Its currently just quads and cubes.

if lightNode is a child of the rootNode, and the mapNode is a child of lightNode, then my Map is illuminated, but if I manipulate the lightNode the mapNode changes in the same way. (so if I have the lightNode rotate to make it look like the point lights are orbiting the origin the mapNode rotates with it) If I instead make both child nodes direct children of the root they are now independent, but my map is not longer illuminated. I understand why it does this and I understand that its working as intended. What I am looking for (I think) is a way to make changes to a parent without changing a child.

As another example, If my game has cars and the cars have headlights, and I have nodes related such that rootNode->mapNode->carNode. mapNode holds the geometry for the world the player is in, and carNode holds the car model and lights. The car can now rotate and move without rotating and moving the Map, but it would seem that the lights on the car would not illuminate the map. To get the lights to illuminate the map, the carNode has to be the parent, but then if the car rotates the map rotates with it.

It would seem that I would have to keep lights at or near the rootNode and them manipulate them individually instead of through the Node its in.

Lights affect the node and children that are attached to. So it sounds like your scene graph organization is kind of funny.

You don’t usually use node attachment to position a light. Node attachment controls what the light affects.

If you want to control the light’s position based on the position of another node/spatial then you should add the light to the scene graph where you want the affect to be and then add a separate node to be the light’s position and add a control to it that moves the light’s position to the node’s position. I think there is even a control that does this already.

Others may have more experience with this, though. I can only definitely tell you where you are supposed to addLight()… and that it’s not about position.

I think this code example best shows what I mean. I created 2 orbs in 2 different Nodes. One of those nodes has a red point light in it. It only illuminates the one orb, not both, which is what I would want. I know this is the expected behavior but I cant figure out what the correct way to do this is. Do you have an example or tutorial for light controls?

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Sphere;
import com.jme3.util.TangentBinormalGenerator;
import com.jme3.scene.Node;
import com.jme3.light.PointLight;

public class Main extends SimpleApplication {

Node ChildNodeA = new Node("ChildNodeA");
Node ChildNodeB = new Node("ChildNodeB");

public static void main(String[] args) {
Main app = new Main();
app.start();
}

@Override
public void simpleInitApp() {

rootNode.attachChild(ChildNodeA);
rootNode.attachChild(ChildNodeB);


/** A bumpy rock with a shiny light effect */
Sphere rock = new Sphere(32,32, 2f);
rock.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
TangentBinormalGenerator.generate(rock); 

Material mat_lit = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
mat_lit.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"));
mat_lit.setTexture("NormalMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
mat_lit.setBoolean("UseMaterialColors",true);    
mat_lit.setColor("Specular",ColorRGBA.White);
mat_lit.setColor("Diffuse",ColorRGBA.White);
mat_lit.setFloat("Shininess", 5f); // [1,128] 

Geometry shiny_rockA = new Geometry("Shiny rockA", rock);   
shiny_rockA.setMaterial(mat_lit);
shiny_rockA.setLocalTranslation(3,2,-2); // Move it a bit
shiny_rockA.rotate(1.6f, 0, 0);          // Rotate it a bit
ChildNodeA.attachChild(shiny_rockA);

Geometry shiny_rockB = new Geometry("Shiny rockB", rock);   
shiny_rockB.setMaterial(mat_lit);
shiny_rockB.setLocalTranslation(-3,2,-2); // Move it a bit
shiny_rockB.rotate(0, 1.6f, 0);          // Rotate it a bit
ChildNodeB.attachChild(shiny_rockB);

/** Must add a light to make the lit object visible! */    
PointLight lamp_light = new PointLight();
lamp_light.setColor(ColorRGBA.Red);
lamp_light.setRadius(100f);
lamp_light.setPosition(new Vector3f(Vector3f.ZERO.add(25, 10, 0)));
ChildNodeA.addLight(lamp_light);

/** Must add a light to make the lit object visible! */
DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(1,0,-2).normalizeLocal());
sun.setColor(ColorRGBA.White);
rootNode.addLight(sun);

}

  @Override
public void simpleUpdate(float tpf) {
    
    ChildNodeA.rotate(1f*tpf, 0, 0);
    ChildNodeB.rotate(-1f*tpf, 0, 0);
}

}

I cannot parse this text: “which is what I would want. I know this is the expected behavior but I cant figure out what the correct way to do this is.”

…it seems ambiguous and/or contradictory. “It’s what I want but how do I do what I want”

Maybe you can reword it?

" It only illuminates the one orb, not both, which is what I would want."
I want to illuminate both orbs.

I think I figured it out.

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Sphere;
import com.jme3.util.TangentBinormalGenerator;
import com.jme3.scene.Node;
import com.jme3.light.PointLight;
import com.jme3.scene.control.LightControl;

public class Main extends SimpleApplication {

Node ChildNodeA = new Node("ChildNodeA");
Node ChildNodeB = new Node("ChildNodeB");
Node LightNode = new Node("LightNode");

public static void main(String[] args) {
Main app = new Main();
app.start();
}

@Override
public void simpleInitApp() {

rootNode.attachChild(ChildNodeA);
rootNode.attachChild(ChildNodeB);
ChildNodeB.attachChild(LightNode);                                                                    // <------------------------------- new code

/** A bumpy rock with a shiny light effect */
Sphere rock = new Sphere(32,32, 2f);
rock.setTextureMode(Sphere.TextureMode.Projected); // better quality on spheres
TangentBinormalGenerator.generate(rock); 

Material mat_lit = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
mat_lit.setTexture("DiffuseMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond.jpg"));
mat_lit.setTexture("NormalMap", assetManager.loadTexture("Textures/Terrain/Pond/Pond_normal.png"));
mat_lit.setBoolean("UseMaterialColors",true);    
mat_lit.setColor("Specular",ColorRGBA.White);
mat_lit.setColor("Diffuse",ColorRGBA.White);
mat_lit.setFloat("Shininess", 5f); // [1,128] 

Geometry shiny_rockA = new Geometry("Shiny rockA", rock);   
shiny_rockA.setMaterial(mat_lit);
shiny_rockA.rotate(1.6f, 0, 0);          // Rotate it a bit
ChildNodeA.attachChild(shiny_rockA);

Geometry shiny_rockB = new Geometry("Shiny rockB", rock);   
shiny_rockB.setMaterial(mat_lit);
shiny_rockB.rotate(0, 1.6f, 0);          // Rotate it a bit
ChildNodeB.attachChild(shiny_rockB);

/** Must add a light to make the lit object visible! */    
PointLight lamp_light = new PointLight();
lamp_light.setColor(ColorRGBA.Red);
lamp_light.setRadius(50f);
rootNode.addLight(lamp_light);

LightControl lightControl = new LightControl(lamp_light); 
LightNode.addControl(lightControl);                                                         // <------------------------------- new code
LightNode.setLocalTranslation(0, 0, 15);

ChildNodeA.setLocalTranslation(5, 0, 0);
ChildNodeB.setLocalTranslation(-5, 0, 0);

}

  @Override
public void simpleUpdate(float tpf) {
      
    ChildNodeA.rotate(0, 1f*tpf, 0);
    ChildNodeB.rotate(1f*tpf, 0 , 0);
}

}

One new question that I have is that it seems that when I map a light to a control, the light must be located at the position of the Node that’s controlling it. When I tried to attach the light directly to Node ChildNodeB in the above example and then offset it a distance the light seemed to stay at the xyz coordinates of ChildNodeB. So if Node ChildNodeB is at xyz 3,0,0 and I set the lights location to be xyz 3,1,0, when I setup the light control the light seems to move to xyz 3,0,0 and even if I try to move it manually afterwords it always stays @ 3,0,0 , the location of the Node controlling it. To get around this I had to create a new Node “LightNode”, make it the child of ChildNodeB, made LightNode the controller for the point light (instead of ChildNodeB) and then move LightNode to the desired coordinates xyz 3,1,0.

Is this the correct way to do this? If I had for example a Car in a game and I wanted to add a headlight to it, am I correct that I wouldn’t set the light control to the Node of the car, but would instead need to create a child Node of the Car Node and position child node where I wanted the headlight to be, and then create a light control to the child Node?

Random aside: please learn to use the code blocks. Also, you will make everyone happier if you stop naming your local variables like classes (ie: proper casing)

It may sound picky but if you follow the java style conventions and use code blocks then it makes it much easier for us to quickly scan your code and see what the problem might be.

The easier it is for us to help you, the more likely we are to have time to do it…