Search code examples
javajavafxlight

How to add multiple light effects on a pane?


I have a problem with JavaFX light effects. I have a game that needs multiple pointlights on the same pane, but I haven't managed to do that, if its even possible. Right now I have a pane and all the elements on top of it.

This seems to be bad method, so if someone knows better way to add lightsources for 2D game, I would really appreciate the help!

It also seems that only one light effect can be attached to a pane, because whenever I try to set a new one, the other gets deleted. One light just isn't enough for this project. If there is better way of adding lights, let me know! Maybe attach the light to a block and then somehow make it shine on the pane? Here is the code:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.effect.Light;
import javafx.scene.effect.Lighting;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class game extends Application{

@Override
public void start(Stage alku) throws Exception {
    Pane test=new Pane();
    Rectangle box = new Rectangle(200,200);
    box.setFill(Color.WHITE);
    box.setTranslateX(50);
    box.setTranslateY(50);
    test.getChildren().add(box);
    
    Rectangle box2 = new Rectangle(200,200);
    box2.setFill(Color.WHITE);
    box2.setTranslateX(50);
    box2.setTranslateY(300);
    test.getChildren().add(box2);
    
    Scene scene = new Scene(test,400,400);
    
Lighting light = new Lighting();
Light.Point l = new Light.Point();
l.xProperty().set(70);
l.yProperty().set(200);
l.setZ(50);
l.setColor(Color.GREEN);
light.setLight(l);
test.setEffect(light);


  Lighting light2 = new Lighting();
Light.Point l2 = new Light.Point();
l2.xProperty().set(20);
l2.yProperty().set(200);
l2.setZ(50);
l2.setColor(Color.RED);
light2.setLight(l2);
test.setEffect(light2);



    alku.setTitle("light test");
    alku.setScene(scene);
    alku.show();
}


public static void main(String[] args) {
    
     launch(args); 
    }


}


    

this is how it looks at the moment

So the "light" gets overwritten.


Solution

  • The effect property is a property like any other in Java. If you set it to one value, and then immediately set it to a second value, it will have the second value.

    To combine two effects, use a Blend:

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.effect.Blend;
    import javafx.scene.effect.BlendMode;
    import javafx.scene.effect.Light;
    import javafx.scene.effect.Lighting;
    import javafx.scene.layout.Pane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Rectangle;
    import javafx.stage.Stage;
    
    public class Game extends Application {
    
        @Override
        public void start(Stage alku) throws Exception {
            Pane test = new Pane();
            Rectangle box = new Rectangle(200, 200);
            box.setFill(Color.WHITE);
            box.setTranslateX(50);
            box.setTranslateY(50);
            test.getChildren().add(box);
    
            Rectangle box2 = new Rectangle(200, 200);
            box2.setFill(Color.WHITE);
            box2.setTranslateX(50);
            box2.setTranslateY(300);
            test.getChildren().add(box2);
    
            Scene scene = new Scene(test, 400, 400);
    
            Lighting light = new Lighting();
            Light.Point l = new Light.Point();
            l.xProperty().set(70);
            l.yProperty().set(200);
            l.setZ(50);
            l.setColor(Color.GREEN);
            light.setLight(l);
            //test.setEffect(light);
    
            Lighting light2 = new Lighting();
            Light.Point l2 = new Light.Point();
            l2.xProperty().set(20);
            l2.yProperty().set(200);
            l2.setZ(50);
            l2.setColor(Color.RED);
            light2.setLight(l2);
            //test.setEffect(light2);
    
            Blend blend = new Blend(BlendMode.ADD);
            blend.setTopInput(light);
            blend.setBottomInput(light2);
            
            test.setEffect(blend);
    
            alku.setTitle("light test");
            alku.setScene(scene);
            alku.show();
        }
    
        public static void main(String[] args) {
    
            launch(args);
        }
    
    }
    

    You can basically do the same thing for an arbitrary number of lights:

    Lighting[] lotsOfLights = ... ;
    
    Effect allLights = lotsOfLights[0] ;
    
    for (int i = 1 ; i < lotsOfLights.length ; i++) 
        allLights = new Blend(BlendMode.ADD, allLights, lotsOfLights[i]);
    
    someNode.setEffect(allLights);