com.jme3.scene.plugins.gltf.LightsPunctualExtensionLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jme3-plugins Show documentation
Show all versions of jme3-plugins Show documentation
jMonkeyEngine is a 3-D game engine for adventurous Java developers
The newest version!
/*
* Copyright (c) 2009-2024 jMonkeyEngine
* 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 'jMonkeyEngine' 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.jme3.scene.plugins.gltf;
import com.jme3.plugins.json.JsonArray;
import com.jme3.plugins.json.JsonElement;
import com.jme3.plugins.json.JsonObject;
import com.jme3.asset.AssetLoadException;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.control.LightControl;
import java.util.HashMap;
import java.util.HashSet;
/**
* Extension loader for KHR_lights_punctual extension which allows
* for lights to be added to the node from the gltf model.
*
* Supports directional, point, and spot lights.
*
* Created by Trevor Flynn - 3/23/2021
*/
public class LightsPunctualExtensionLoader implements ExtensionLoader {
private final HashSet pendingNodes = new HashSet<>();
private final HashMap lightDefinitions = new HashMap<>();
@Override
public Object handleExtension(GltfLoader loader, String parentName, JsonElement parent, JsonElement extension, Object input) {
if (input instanceof Node) { //We are processing a node
JsonObject jsonObject = extension.getAsJsonObject();
if (jsonObject.has("light")) { //These will get run first when loading the gltf file
//Add node to queue
JsonElement indexElement = jsonObject.get("light");
int index = indexElement.getAsInt();
if (!lightDefinitions.containsKey(index)) {
pendingNodes.add(new NodeNeedingLight((Node) input, index));
} else {
addLight((Node) input, (Node) input, index);
}
} else if (jsonObject.has("lights")) { //This will get run last
//Process the light definitions
JsonArray lights = jsonObject.getAsJsonArray("lights");
for (int i = 0; i < lights.size(); i++) {
//Create light definition
JsonObject light = lights.get(i).getAsJsonObject();
String type = light.get("type").getAsString();
Light lightNode;
switch (type) {
case "point":
lightNode = buildPointLight(light);
break;
case "directional":
lightNode = buildDirectionalLight(light);
break;
case "spot":
lightNode = buildSpotLight(light);
break;
default:
throw new AssetLoadException("KHR_lights_punctual unsupported light type: " + type);
}
lightDefinitions.put(i, lightNode);
}
//Build any lights that are pending now that we have definitions
for (NodeNeedingLight nodeInNeed : pendingNodes) {
addLight((Node) input, nodeInNeed.getNode(), nodeInNeed.getLightIndex());
}
pendingNodes.clear();
} else {
throw new AssetLoadException("KHR_lights_punctual extension malformed json");
}
return input;
} else {
throw new AssetLoadException("KHR_lights_punctual extension added on unsupported element");
}
}
/**
* Build a spot light from gltf json.
* @param obj The gltf json object for the spot light
* @return A spot light representation of the gltf object
*/
private SpotLight buildSpotLight(JsonObject obj) {
//Get properties
String name = obj.has("name") ? obj.get("name").getAsString() : "";
float intensity = obj.has("intensity") ? obj.get("intensity").getAsFloat() : 1.0f;
ColorRGBA color = obj.has("color") ? GltfUtils.getAsColor(obj, "color") : new ColorRGBA(ColorRGBA.White);
color = lumensToColor(color, intensity);
float range = obj.has("range") ? obj.get("range").getAsFloat() : Float.POSITIVE_INFINITY;
//Spot specific
JsonObject spot = obj.getAsJsonObject("spot");
float innerConeAngle = spot != null && spot.has("innerConeAngle") ? spot.get("innerConeAngle").getAsFloat() : 0f;
float outerConeAngle = spot != null && spot.has("outerConeAngle") ? spot.get("outerConeAngle").getAsFloat() : ((float) Math.PI) / 4f;
/*
Correct floating point error on half PI, GLTF spec says that the outerConeAngle
can be less or equal to PI/2, but JME requires less than PI/2.
We will bring the angle within PI/2 if it is equal or larger than PI/2
*/
if (outerConeAngle >= FastMath.HALF_PI) {
outerConeAngle = FastMath.HALF_PI - 0.000001f;
}
SpotLight spotLight = new SpotLight();
spotLight.setName(name);
spotLight.setColor(color);
spotLight.setSpotRange(range);
spotLight.setSpotInnerAngle(innerConeAngle);
spotLight.setSpotOuterAngle(outerConeAngle);
spotLight.setDirection(Vector3f.UNIT_Z.negate());
return spotLight;
}
/**
* Build a directional light from gltf json.
* @param obj The gltf json object for the directional light
* @return A directional light representation of the gltf object
*/
private DirectionalLight buildDirectionalLight(JsonObject obj) {
//Get properties
String name = obj.has("name") ? obj.get("name").getAsString() : "";
float intensity = obj.has("intensity") ? obj.get("intensity").getAsFloat() : 1.0f;
ColorRGBA color = obj.has("color") ? GltfUtils.getAsColor(obj, "color") : new ColorRGBA(ColorRGBA.White);
color = lumensToColor(color, intensity);
DirectionalLight directionalLight = new DirectionalLight();
directionalLight.setName(name);
directionalLight.setColor(color);
directionalLight.setDirection(Vector3f.UNIT_Z.negate());
return directionalLight;
}
/**
* Build a point light from gltf json.
* @param obj The gltf json object for the point light
* @return A point light representation of the gltf object
*/
private PointLight buildPointLight(JsonObject obj) {
//Get properties
String name = obj.has("name") ? obj.get("name").getAsString() : "";
float intensity = obj.has("intensity") ? obj.get("intensity").getAsFloat() : 1.0f;
ColorRGBA color = obj.has("color") ? GltfUtils.getAsColor(obj, "color") : new ColorRGBA(ColorRGBA.White);
color = lumensToColor(color, intensity);
float range = obj.has("range") ? obj.get("range").getAsFloat() : Float.POSITIVE_INFINITY;
PointLight pointLight = new PointLight();
pointLight.setName(name);
pointLight.setColor(color);
pointLight.setRadius(range);
return pointLight;
}
/**
* Attach a light at the given index to the given parent node,
* and the control for the light to the given node.
* @param parent The node to attach the light to
* @param node The node to attach the light control to
* @param lightIndex The index of the light
*/
private void addLight(Node parent, Node node, int lightIndex) {
if (lightDefinitions.containsKey(lightIndex)) {
Light light = lightDefinitions.get(lightIndex);
parent.addLight(light);
LightControl control = new LightControl(light);
node.addControl(control);
} else {
throw new AssetLoadException("KHR_lights_punctual extension accessed undefined light at index " + lightIndex);
}
}
/**
* Convert a floating point lumens value into a color that
* represents both color and brightness of the light.
*
* @param color The base color of the light
* @param lumens The lumens value to convert to a color
* @return A color representing the intensity of the given lumens encoded into the given color
*/
private ColorRGBA lumensToColor(ColorRGBA color, float lumens) {
ColorRGBA brightnessModifier = lumensToColor(lumens);
return color.mult(brightnessModifier);
}
/**
* Convert a floating point lumens value into a grayscale color that
* represents a brightness.
*
* @param lumens The lumens value to convert to a color
* @return A color representing the intensity of the given lumens
*/
private ColorRGBA lumensToColor(float lumens) {
/*
Taken from /Common/ShaderLib/Hdr.glsllib
vec4 HDR_EncodeLum(in float lum){
float Le = 2.0 * log2(lum + epsilon) + 127.0;
vec4 result = vec4(0.0);
result.a = fract(Le);
result.rgb = vec3((Le - (floor(result.a * 255.0)) / 255.0) / 255.0);
return result;
*/
float epsilon = 0.0001f;
double Le = 2f * Math.log(lumens * epsilon) / Math.log(2) + 127.0;
ColorRGBA color = new ColorRGBA();
color.a = (float) (Le - Math.floor(Le)); //Get fractional part
float val = (float) ((Le - (Math.floor(color.a * 255.0)) / 255.0) / 255.0);
color.r = val;
color.g = val;
color.b = val;
return color;
}
/**
* A bean to contain the relation between a node and a light index
*/
private static class NodeNeedingLight {
private Node node;
private int lightIndex;
private NodeNeedingLight(Node node, int lightIndex) {
this.node = node;
this.lightIndex = lightIndex;
}
private Node getNode() {
return node;
}
private void setNode(Node node) {
this.node = node;
}
private int getLightIndex() {
return lightIndex;
}
private void setLightIndex(int lightIndex) {
this.lightIndex = lightIndex;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy