Merge pull request #39428 from fire/gltf-lights

[3.2] Add GLTF light import
This commit is contained in:
Rémi Verschelde 2020-06-11 22:40:47 +02:00 committed by GitHub
commit 6c9b7c27d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 186 additions and 25 deletions

View File

@ -294,7 +294,16 @@ Error EditorSceneImporterGLTF::_parse_nodes(GLTFState &state) {
node->xform.basis.set_quat_scale(node->rotation, node->scale); node->xform.basis.set_quat_scale(node->rotation, node->scale);
node->xform.origin = node->translation; node->xform.origin = node->translation;
} }
if (n.has("extensions")) {
Dictionary extensions = n["extensions"];
if (extensions.has("KHR_lights_punctual")) {
Dictionary lights_punctual = extensions["KHR_lights_punctual"];
if (lights_punctual.has("light")) {
GLTFLightIndex light = lights_punctual["light"];
node->light = light;
}
}
}
if (n.has("children")) { if (n.has("children")) {
const Array &children = n["children"]; const Array &children = n["children"];
for (int j = 0; j < children.size(); j++) { for (int j = 0; j < children.size(); j++) {
@ -2234,7 +2243,6 @@ bool EditorSceneImporterGLTF::_skins_are_same(const Ref<Skin> &skin_a, const Ref
} }
for (int i = 0; i < skin_a->get_bind_count(); ++i) { for (int i = 0; i < skin_a->get_bind_count(); ++i) {
if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) { if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) {
return false; return false;
} }
@ -2264,6 +2272,58 @@ void EditorSceneImporterGLTF::_remove_duplicate_skins(GLTFState &state) {
} }
} }
Error EditorSceneImporterGLTF::_parse_lights(GLTFState &state) {
if (!state.json.has("extensions")) {
return OK;
}
Dictionary extensions = state.json["extensions"];
if (!extensions.has("KHR_lights_punctual")) {
return OK;
}
Dictionary lights_punctual = extensions["KHR_lights_punctual"];
if (!lights_punctual.has("lights")) {
return OK;
}
const Array &lights = lights_punctual["lights"];
for (GLTFLightIndex light_i = 0; light_i < lights.size(); light_i++) {
const Dictionary &d = lights[light_i];
GLTFLight light;
ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
const String &type = d["type"];
light.type = type;
if (d.has("color")) {
const Array &arr = d["color"];
ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
const Color c = Color(arr[0], arr[1], arr[2]).to_srgb();
light.color = c;
}
if (d.has("intensity")) {
light.intensity = d["intensity"];
}
if (d.has("range")) {
light.range = d["range"];
}
if (type == "spot") {
const Dictionary &spot = d["spot"];
light.inner_cone_angle = spot["innerConeAngle"];
light.outer_cone_angle = spot["outerConeAngle"];
ERR_FAIL_COND_V_MSG(light.inner_cone_angle >= light.outer_cone_angle, ERR_PARSE_ERROR, "The inner angle must be smaller than the outer angle.");
} else if (type != "point" && type != "directional") {
ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Light type is unknown.");
}
state.lights.push_back(light);
}
print_verbose("glTF: Total lights: " + itos(state.lights.size()));
return OK;
}
Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) { Error EditorSceneImporterGLTF::_parse_cameras(GLTFState &state) {
if (!state.json.has("cameras")) if (!state.json.has("cameras"))
@ -2511,6 +2571,58 @@ MeshInstance *EditorSceneImporterGLTF::_generate_mesh_instance(GLTFState &state,
return mi; return mi;
} }
Light *EditorSceneImporterGLTF::_generate_light(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) {
const GLTFNode *gltf_node = state.nodes[node_index];
ERR_FAIL_INDEX_V(gltf_node->light, state.lights.size(), nullptr);
print_verbose("glTF: Creating light for: " + gltf_node->name);
const GLTFLight &l = state.lights[gltf_node->light];
float intensity = l.intensity;
if (intensity > 10) {
// GLTF spec has the default around 1, but Blender defaults lights to 100.
// The only sane way to handle this is to check where it came from and
// handle it accordingly. If it's over 10, it probably came from Blender.
intensity /= 100;
}
if (l.type == "directional") {
DirectionalLight *light = memnew(DirectionalLight);
light->set_param(Light::PARAM_ENERGY, intensity);
light->set_color(l.color);
return light;
}
const float range = CLAMP(l.range, 0, 4096);
// Doubling the range will double the effective brightness, so we need double attenuation (half brightness).
// We want to have double intensity give double brightness, so we need half the attenuation.
const float attenuation = range / intensity;
if (l.type == "point") {
OmniLight *light = memnew(OmniLight);
light->set_param(OmniLight::PARAM_ATTENUATION, attenuation);
light->set_param(OmniLight::PARAM_RANGE, range);
light->set_color(l.color);
return light;
}
if (l.type == "spot") {
SpotLight *light = memnew(SpotLight);
light->set_param(SpotLight::PARAM_ATTENUATION, attenuation);
light->set_param(SpotLight::PARAM_RANGE, range);
light->set_param(SpotLight::PARAM_SPOT_ANGLE, Math::rad2deg(l.outer_cone_angle));
light->set_color(l.color);
// Line of best fit derived from guessing, see https://www.desmos.com/calculator/biiflubp8b
// The points in desmos are not exact, except for (1, infinity).
float angle_ratio = l.inner_cone_angle / l.outer_cone_angle;
float angle_attenuation = 0.2 / (1 - angle_ratio) - 0.1;
light->set_param(SpotLight::PARAM_SPOT_ATTENUATION, angle_attenuation);
return light;
}
return nullptr;
}
Camera *EditorSceneImporterGLTF::_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) { Camera *EditorSceneImporterGLTF::_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index) {
const GLTFNode *gltf_node = state.nodes[node_index]; const GLTFNode *gltf_node = state.nodes[node_index];
@ -2585,6 +2697,8 @@ void EditorSceneImporterGLTF::_generate_scene_node(GLTFState &state, Node *scene
current_node = _generate_mesh_instance(state, scene_parent, node_index); current_node = _generate_mesh_instance(state, scene_parent, node_index);
} else if (gltf_node->camera >= 0) { } else if (gltf_node->camera >= 0) {
current_node = _generate_camera(state, scene_parent, node_index); current_node = _generate_camera(state, scene_parent, node_index);
} else if (gltf_node->light >= 0) {
current_node = _generate_light(state, scene_parent, node_index);
} else { } else {
current_node = _generate_spatial(state, scene_parent, node_index); current_node = _generate_spatial(state, scene_parent, node_index);
} }
@ -2973,14 +3087,16 @@ Node *EditorSceneImporterGLTF::import_scene(const String &p_path, uint32_t p_fla
//binary file //binary file
//text file //text file
Error err = _parse_glb(p_path, state); Error err = _parse_glb(p_path, state);
if (err) if (err) {
return NULL; return NULL;
}
} else { } else {
//text file //text file
Error err = _parse_json(p_path, state); Error err = _parse_json(p_path, state);
if (err) if (err) {
return NULL; return NULL;
} }
}
ERR_FAIL_COND_V(!state.json.has("asset"), NULL); ERR_FAIL_COND_V(!state.json.has("asset"), NULL);
@ -2997,83 +3113,104 @@ Node *EditorSceneImporterGLTF::import_scene(const String &p_path, uint32_t p_fla
/* STEP 0 PARSE SCENE */ /* STEP 0 PARSE SCENE */
Error err = _parse_scenes(state); Error err = _parse_scenes(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 1 PARSE NODES */ /* STEP 1 PARSE NODES */
err = _parse_nodes(state); err = _parse_nodes(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 2 PARSE BUFFERS */ /* STEP 2 PARSE BUFFERS */
err = _parse_buffers(state, p_path.get_base_dir()); err = _parse_buffers(state, p_path.get_base_dir());
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 3 PARSE BUFFER VIEWS */ /* STEP 3 PARSE BUFFER VIEWS */
err = _parse_buffer_views(state); err = _parse_buffer_views(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 4 PARSE ACCESSORS */ /* STEP 4 PARSE ACCESSORS */
err = _parse_accessors(state); err = _parse_accessors(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 5 PARSE IMAGES */ /* STEP 5 PARSE IMAGES */
err = _parse_images(state, p_path.get_base_dir()); err = _parse_images(state, p_path.get_base_dir());
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 6 PARSE TEXTURES */ /* STEP 6 PARSE TEXTURES */
err = _parse_textures(state); err = _parse_textures(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 7 PARSE TEXTURES */ /* STEP 7 PARSE TEXTURES */
err = _parse_materials(state); err = _parse_materials(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 9 PARSE SKINS */ /* STEP 9 PARSE SKINS */
err = _parse_skins(state); err = _parse_skins(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 10 DETERMINE SKELETONS */ /* STEP 10 DETERMINE SKELETONS */
err = _determine_skeletons(state); err = _determine_skeletons(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 11 CREATE SKELETONS */ /* STEP 11 CREATE SKELETONS */
err = _create_skeletons(state); err = _create_skeletons(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 12 CREATE SKINS */ /* STEP 12 CREATE SKINS */
err = _create_skins(state); err = _create_skins(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 13 PARSE MESHES (we have enough info now) */ /* STEP 13 PARSE MESHES (we have enough info now) */
err = _parse_meshes(state); err = _parse_meshes(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 14 PARSE CAMERAS */ /* STEP 14 PARSE LIGHTS */
err = _parse_lights(state);
if (err != OK) {
return NULL;
}
/* STEP 15 PARSE CAMERAS */
err = _parse_cameras(state); err = _parse_cameras(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 15 PARSE ANIMATIONS */ /* STEP 16 PARSE ANIMATIONS */
err = _parse_animations(state); err = _parse_animations(state);
if (err != OK) if (err != OK) {
return NULL; return NULL;
}
/* STEP 16 ASSIGN SCENE NAMES */ /* STEP 17 ASSIGN SCENE NAMES */
_assign_scene_names(state); _assign_scene_names(state);
/* STEP 17 MAKE SCENE! */ /* STEP 18 MAKE SCENE! */
Spatial *scene = _generate_scene(state, p_bake_fps); Spatial *scene = _generate_scene(state, p_bake_fps);
return scene; return scene;

View File

@ -32,6 +32,7 @@
#define EDITOR_SCENE_IMPORTER_GLTF_H #define EDITOR_SCENE_IMPORTER_GLTF_H
#include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_scene.h"
#include "scene/3d/light.h"
#include "scene/3d/skeleton.h" #include "scene/3d/skeleton.h"
#include "scene/3d/spatial.h" #include "scene/3d/spatial.h"
@ -51,6 +52,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
typedef int GLTFImageIndex; typedef int GLTFImageIndex;
typedef int GLTFMaterialIndex; typedef int GLTFMaterialIndex;
typedef int GLTFMeshIndex; typedef int GLTFMeshIndex;
typedef int GLTFLightIndex;
typedef int GLTFNodeIndex; typedef int GLTFNodeIndex;
typedef int GLTFSkeletonIndex; typedef int GLTFSkeletonIndex;
typedef int GLTFSkinIndex; typedef int GLTFSkinIndex;
@ -115,6 +117,8 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
GLTFNodeIndex fake_joint_parent; GLTFNodeIndex fake_joint_parent;
GLTFLightIndex light;
GLTFNode() : GLTFNode() :
parent(-1), parent(-1),
height(-1), height(-1),
@ -125,7 +129,8 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
joint(false), joint(false),
translation(0, 0, 0), translation(0, 0, 0),
scale(Vector3(1, 1, 1)), scale(Vector3(1, 1, 1)),
fake_joint_parent(-1) {} fake_joint_parent(-1),
light(-1) {}
}; };
struct GLTFBufferView { struct GLTFBufferView {
@ -262,6 +267,23 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
} }
}; };
struct GLTFLight {
Color color;
float intensity;
String type;
float range;
float inner_cone_angle;
float outer_cone_angle;
GLTFLight() {
color = Color(1.0f, 1.0f, 1.0f);
intensity = 1.0f;
type = "";
range = Math_INF;
inner_cone_angle = 0.0f;
outer_cone_angle = Math_PI / 4.0;
}
};
struct GLTFAnimation { struct GLTFAnimation {
bool loop = false; bool loop = false;
@ -317,6 +339,7 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
Vector<GLTFSkin> skins; Vector<GLTFSkin> skins;
Vector<GLTFCamera> cameras; Vector<GLTFCamera> cameras;
Vector<GLTFLight> lights;
Set<String> unique_names; Set<String> unique_names;
@ -395,12 +418,13 @@ class EditorSceneImporterGLTF : public EditorSceneImporter {
void _remove_duplicate_skins(GLTFState &state); void _remove_duplicate_skins(GLTFState &state);
Error _parse_cameras(GLTFState &state); Error _parse_cameras(GLTFState &state);
Error _parse_lights(GLTFState &state);
Error _parse_animations(GLTFState &state); Error _parse_animations(GLTFState &state);
BoneAttachment *_generate_bone_attachment(GLTFState &state, Skeleton *skeleton, const GLTFNodeIndex node_index); BoneAttachment *_generate_bone_attachment(GLTFState &state, Skeleton *skeleton, const GLTFNodeIndex node_index);
MeshInstance *_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); MeshInstance *_generate_mesh_instance(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
Camera *_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); Camera *_generate_camera(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
Light *_generate_light(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
Spatial *_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index); Spatial *_generate_spatial(GLTFState &state, Node *scene_parent, const GLTFNodeIndex node_index);
void _generate_scene_node(GLTFState &state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index); void _generate_scene_node(GLTFState &state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index);