From da2ee6b0c496763b21f2a56e78c68ee2f69ccfe0 Mon Sep 17 00:00:00 2001 From: Par Winzell Date: Sat, 15 Dec 2018 15:26:52 -0800 Subject: [PATCH] Implement KHR_lights_punctual. --- CMakeLists.txt | 2 ++ src/FBX2glTF.cpp | 3 ++ src/FBX2glTF.h | 4 +++ src/fbx/Fbx2Raw.cpp | 38 ++++++++++++++++++++++++- src/gltf/GltfModel.cpp | 5 ++++ src/gltf/GltfModel.hpp | 2 ++ src/gltf/Raw2Gltf.cpp | 47 +++++++++++++++++++++++++++++++ src/gltf/Raw2Gltf.hpp | 1 + src/gltf/properties/LightData.cpp | 43 ++++++++++++++++++++++++++++ src/gltf/properties/LightData.hpp | 33 ++++++++++++++++++++++ src/gltf/properties/NodeData.cpp | 10 +++++++ src/gltf/properties/NodeData.hpp | 2 ++ src/mathfu.hpp | 4 +++ src/raw/RawModel.cpp | 35 +++++++++++++++++++++++ src/raw/RawModel.hpp | 26 +++++++++++++++++ 15 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 src/gltf/properties/LightData.cpp create mode 100644 src/gltf/properties/LightData.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bd84916..d8ffcc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,6 +194,8 @@ set(LIB_SOURCE_FILES src/gltf/properties/CameraData.hpp src/gltf/properties/ImageData.cpp src/gltf/properties/ImageData.hpp + src/gltf/properties/LightData.cpp + src/gltf/properties/LightData.hpp src/gltf/properties/MaterialData.cpp src/gltf/properties/MaterialData.hpp src/gltf/properties/MeshData.cpp diff --git a/src/FBX2glTF.cpp b/src/FBX2glTF.cpp index 51eae33..3b0cbd1 100644 --- a/src/FBX2glTF.cpp +++ b/src/FBX2glTF.cpp @@ -94,6 +94,9 @@ int main(int argc, char *argv[]) ( "khr-materials-unlit", "Use KHR_materials_unlit extension to specify Unlit shader.", cxxopts::value(gltfOptions.useKHRMatUnlit)) + ( + "no-khr-punctual-lights", "Don't use KHR_punctual_lights extension to export lights.", + cxxopts::value(gltfOptions.useKHRPunctualLights)) ( "user-properties", "Transcribe FBX User Properties into glTF node and material 'extras'.", cxxopts::value(gltfOptions.enableUserProperties)) diff --git a/src/FBX2glTF.h b/src/FBX2glTF.h index ecc9c7c..97a5bd6 100644 --- a/src/FBX2glTF.h +++ b/src/FBX2glTF.h @@ -91,6 +91,10 @@ struct GltfOptions bool useKHRMatUnlit { false }; /** Whether to populate the pbrMetallicRoughness substruct in materials. */ bool usePBRMetRough { false }; + + /** Whether to include lights through the KHR_punctual_lights extension. */ + bool useKHRPunctualLights { true }; + /** Whether to include blend shape normals, if present according to the SDK. */ bool useBlendShapeNormals { false }; /** Whether to include blend shape tangents, if present according to the SDK. */ diff --git a/src/fbx/Fbx2Raw.cpp b/src/fbx/Fbx2Raw.cpp index 7c7439a..03125b5 100644 --- a/src/fbx/Fbx2Raw.cpp +++ b/src/fbx/Fbx2Raw.cpp @@ -380,6 +380,40 @@ double VFOV2HFOV(double v, double ar) return 2.0 * std::atan((ar) * std::tan((v * FBXSDK_PI_DIV_180) * 0.5)) * FBXSDK_180_DIV_PI; } +static void ReadLight(RawModel &raw, FbxScene *pScene, FbxNode *pNode) { + const FbxLight *pLight = pNode->GetLight(); + + int lightIx; + float intensity = (float)pLight->Intensity.Get(); + Vec3f color = toVec3f(pLight->Color.Get()); + switch (pLight->LightType.Get()) { + case FbxLight::eDirectional: { + lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_DIRECTIONAL, + color, intensity, 0, 0); + break; + } + case FbxLight::ePoint: { + lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_POINT, color, + intensity, 0, 0); + break; + } + case FbxLight::eSpot: { + lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_SPOT, color, + intensity, (float)pLight->InnerAngle.Get(), + (float)pLight->OuterAngle.Get()); + break; + } + default: { + fmt::printf("Warning:: Ignoring unsupported light type.\n"); + return; + } + } + + int nodeId = raw.GetNodeById(pNode->GetUniqueID()); + RawNode &node = raw.GetNode(nodeId); + node.lightIx = lightIx; +} + // Largely adopted from fbx example static void ReadCamera(RawModel &raw, FbxScene *pScene, FbxNode *pNode) { @@ -486,13 +520,15 @@ static void ReadNodeAttributes( ReadCamera(raw, pScene, pNode); break; } + case FbxNodeAttribute::eLight: + ReadLight(raw, pScene, pNode); + break; case FbxNodeAttribute::eUnknown: case FbxNodeAttribute::eNull: case FbxNodeAttribute::eMarker: case FbxNodeAttribute::eSkeleton: case FbxNodeAttribute::eCameraStereo: case FbxNodeAttribute::eCameraSwitcher: - case FbxNodeAttribute::eLight: case FbxNodeAttribute::eOpticalReference: case FbxNodeAttribute::eOpticalMarker: case FbxNodeAttribute::eNurbsCurve: diff --git a/src/gltf/GltfModel.cpp b/src/gltf/GltfModel.cpp index 7f19df4..780d047 100644 --- a/src/gltf/GltfModel.cpp +++ b/src/gltf/GltfModel.cpp @@ -77,4 +77,9 @@ void GltfModel::serializeHolders(json &glTFJson) serializeHolder(glTFJson, "animations", animations); serializeHolder(glTFJson, "cameras", cameras); serializeHolder(glTFJson, "nodes", nodes); + if (!lights.ptrs.empty()) { + json lightsJson = json::object(); + serializeHolder(lightsJson, "lights", lights); + glTFJson["extensions"][KHR_LIGHTS_PUNCTUAL] = lightsJson; + } } diff --git a/src/gltf/GltfModel.hpp b/src/gltf/GltfModel.hpp index 4bce9c7..ab4d384 100644 --- a/src/gltf/GltfModel.hpp +++ b/src/gltf/GltfModel.hpp @@ -19,6 +19,7 @@ #include "gltf/properties/BufferViewData.hpp" #include "gltf/properties/CameraData.hpp" #include "gltf/properties/ImageData.hpp" +#include "gltf/properties/LightData.hpp" #include "gltf/properties/MaterialData.hpp" #include "gltf/properties/MeshData.hpp" #include "gltf/properties/NodeData.hpp" @@ -150,6 +151,7 @@ public: Holder cameras; Holder nodes; Holder scenes; + Holder lights; std::shared_ptr defaultSampler; std::shared_ptr defaultBuffer; diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index dd8fb40..612144f 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -119,6 +119,8 @@ ModelData *Raw2Gltf( fmt::printf("%7d nodes\n", raw.GetNodeCount()); fmt::printf("%7d surfaces\n", (int) materialModels.size()); fmt::printf("%7d animations\n", raw.GetAnimationCount()); + fmt::printf("%7d cameras\n", raw.GetCameraCount()); + fmt::printf("%7d lights\n", raw.GetLightCount()); } std::unique_ptr gltf(new GltfModel(options)); @@ -624,6 +626,47 @@ ModelData *Raw2Gltf( } iter->second->SetCamera(camera.ix); } + + // + // lights + // + std::vector khrPunctualLights; + if (options.useKHRPunctualLights) { + for (int i = 0; i < raw.GetLightCount(); i ++) { + const RawLight &light = raw.GetLight(i); + LightData::Type type; + switch(light.type) { + case RAW_LIGHT_TYPE_DIRECTIONAL: + type = LightData::Type::Directional; + break; + case RAW_LIGHT_TYPE_POINT: + type = LightData::Type::Point; + break; + case RAW_LIGHT_TYPE_SPOT: + type = LightData::Type::Spot; + break; + } + gltf->lights.hold(new LightData( + light.name, + type, + light.color, + // FBX intensity defaults to 100, so let's call that 1.0; + // but caveat: I find nothing in the documentation to suggest + // what unit the FBX value is meant to be measured in... + light.intensity / 100, + light.innerConeAngle, + light.outerConeAngle)); + } + } + for (int i = 0; i < raw.GetNodeCount(); i++) { + const RawNode &node = raw.GetNode(i); + const auto nodeData = gltf->nodes.ptrs[i]; + + if (node.lightIx >= 0) { + // we lean on the fact that in this simple case, raw and gltf indexing are aligned + nodeData->SetLight(node.lightIx); + } + } } NodeData &rootNode = require(nodesById, raw.GetRootNode()); @@ -651,6 +694,9 @@ ModelData *Raw2Gltf( if (options.useKHRMatUnlit) { extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT); } + if (!gltf->lights.ptrs.empty()) { + extensionsUsed.push_back(KHR_LIGHTS_PUNCTUAL); + } if (options.draco.enabled) { extensionsUsed.push_back(KHR_DRACO_MESH_COMPRESSION); extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION); @@ -670,6 +716,7 @@ ModelData *Raw2Gltf( } gltf->serializeHolders(glTFJson); + gltfOutStream << glTFJson.dump(options.outputBinary ? 0 : 4); } if (options.outputBinary) { diff --git a/src/gltf/Raw2Gltf.hpp b/src/gltf/Raw2Gltf.hpp index c15278d..60e738c 100644 --- a/src/gltf/Raw2Gltf.hpp +++ b/src/gltf/Raw2Gltf.hpp @@ -21,6 +21,7 @@ const std::string KHR_DRACO_MESH_COMPRESSION = "KHR_draco_mesh_compression"; const std::string KHR_MATERIALS_CMN_UNLIT = "KHR_materials_unlit"; +const std::string KHR_LIGHTS_PUNCTUAL = "KHR_lights_punctual"; const std::string extBufferFilename = "buffer.bin"; diff --git a/src/gltf/properties/LightData.cpp b/src/gltf/properties/LightData.cpp new file mode 100644 index 0000000..d981f88 --- /dev/null +++ b/src/gltf/properties/LightData.cpp @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include "LightData.hpp" + +LightData::LightData( + std::string name, Type type, Vec3f color, float intensity, + float innerConeAngle, float outerConeAngle) + : Holdable(), + type(type), + color(color), + intensity(intensity), + innerConeAngle(innerConeAngle), + outerConeAngle(outerConeAngle) +{ +} + +json LightData::serialize() const +{ + json result { + { "name", name }, + { "color", toStdVec(color) }, + { "intensity", intensity } + }; + switch(type) { + case Directional: + result["type"] = "directional"; + break; + case Point: + result["type"] = "point"; + break; + case Spot: + result["type"] = "spot"; + break; + } + return result; +} diff --git a/src/gltf/properties/LightData.hpp b/src/gltf/properties/LightData.hpp new file mode 100644 index 0000000..737e309 --- /dev/null +++ b/src/gltf/properties/LightData.hpp @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +#include "gltf/Raw2Gltf.hpp" + +struct LightData : Holdable +{ + enum Type { + Directional, + Point, + Spot, + }; + + LightData(std::string name, Type type, Vec3f color, float intensity, + float innerConeAngle, float outerConeAngle); + + json serialize() const override; + + const std::string name; + const Type type; + const Vec3f color; + const float intensity; + const float innerConeAngle; + const float outerConeAngle; +}; diff --git a/src/gltf/properties/NodeData.cpp b/src/gltf/properties/NodeData.cpp index 6310226..4714fec 100644 --- a/src/gltf/properties/NodeData.cpp +++ b/src/gltf/properties/NodeData.cpp @@ -21,6 +21,7 @@ NodeData::NodeData( children(), mesh(-1), camera(-1), + light(-1), skin(-1) { } @@ -50,6 +51,12 @@ void NodeData::SetCamera(uint32_t cameraIndex) camera = cameraIndex; } +void NodeData::SetLight(uint32_t lightIndex) +{ + assert(!isJoint); + light = lightIndex; +} + json NodeData::serialize() const { json result = { { "name", name } }; @@ -84,6 +91,9 @@ json NodeData::serialize() const if (camera >= 0) { result["camera"] = camera; } + if (light >= 0) { + result["extensions"][KHR_LIGHTS_PUNCTUAL]["light"] = light; + } } for (const auto& i : userProperties) diff --git a/src/gltf/properties/NodeData.hpp b/src/gltf/properties/NodeData.hpp index c976e66..dbc0b86 100644 --- a/src/gltf/properties/NodeData.hpp +++ b/src/gltf/properties/NodeData.hpp @@ -19,6 +19,7 @@ struct NodeData : Holdable void SetMesh(uint32_t meshIx); void SetSkin(uint32_t skinIx); void SetCamera(uint32_t camera); + void SetLight(uint32_t light); json serialize() const override; @@ -30,6 +31,7 @@ struct NodeData : Holdable std::vector children; int32_t mesh; int32_t camera; + int32_t light; int32_t skin; std::vector skeletons; std::vector userProperties; diff --git a/src/mathfu.hpp b/src/mathfu.hpp index 3a15931..75cd7c2 100644 --- a/src/mathfu.hpp +++ b/src/mathfu.hpp @@ -76,6 +76,10 @@ template std::vector toStdVec(const mathfu::Quaternion &quat) { return std::vector { quat.vector()[0], quat.vector()[1], quat.vector()[2], quat.scalar() }; } +inline Vec3f toVec3f(const FbxDouble3 &v) { + return Vec3f((float) v[0], (float) v[1], (float) v[2]); +} + inline Vec3f toVec3f(const FbxVector4 &v) { return Vec3f((float) v[0], (float) v[1], (float) v[2]); } diff --git a/src/raw/RawModel.cpp b/src/raw/RawModel.cpp index d974893..57f9382 100644 --- a/src/raw/RawModel.cpp +++ b/src/raw/RawModel.cpp @@ -165,6 +165,40 @@ int RawModel::AddMaterial( return (int) materials.size() - 1; } +int RawModel::AddLight( + const char *name, + const RawLightType lightType, + const Vec3f color, + const float intensity, + const float innerConeAngle, + const float outerConeAngle) +{ + for (size_t i = 0; i < lights.size(); i ++) { + if (lights[i].name != name || lights[i].type != lightType) { + continue; + } + // only care about cone angles for spot + if (lights[i].type == RAW_LIGHT_TYPE_SPOT) { + if (lights[i].innerConeAngle != innerConeAngle || + lights[i].outerConeAngle != outerConeAngle) { + continue; + } + } + return (int) i; + } + RawLight light { + name, + lightType, + color, + intensity, + innerConeAngle, + outerConeAngle, + }; + lights.push_back(light); + return (int) lights.size() - 1; +} + + int RawModel::AddSurface(const RawSurface &surface) { for (size_t i = 0; i < surfaces.size(); i++) { @@ -262,6 +296,7 @@ int RawModel::AddNode(const long id, const char *name, const long parentId) joint.name = name; joint.parentId = parentId; joint.surfaceId = 0; + joint.lightIx = -1; joint.translation = Vec3f(0, 0, 0); joint.rotation = Quatf(0, 0, 0, 1); joint.scale = Vec3f(1, 1, 1); diff --git a/src/raw/RawModel.hpp b/src/raw/RawModel.hpp index 03428fa..2ef07f1 100644 --- a/src/raw/RawModel.hpp +++ b/src/raw/RawModel.hpp @@ -261,6 +261,7 @@ struct RawMetRoughMatProps : RawMatProps { } }; + struct RawMaterial { std::string name; @@ -270,6 +271,23 @@ struct RawMaterial std::vector userProperties; }; +enum RawLightType +{ + RAW_LIGHT_TYPE_DIRECTIONAL, + RAW_LIGHT_TYPE_POINT, + RAW_LIGHT_TYPE_SPOT, +}; + +struct RawLight +{ + std::string name; + RawLightType type; + Vec3f color; + float intensity; + float innerConeAngle; // only meaningful for spot + float outerConeAngle; // only meaningful for spot +}; + struct RawBlendChannel { float defaultDeform; @@ -348,6 +366,7 @@ struct RawNode Quatf rotation; Vec3f scale; long surfaceId; + long lightIx; std::vector userProperties; }; @@ -365,6 +384,8 @@ public: int AddMaterial( const char *name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX], std::shared_ptr materialInfo, const std::vector& userProperties); + int AddLight(const char *name, RawLightType lightType, Vec3f color, float intensity, + float innerConeAngle, float outerConeAngle); int AddSurface(const RawSurface &suface); int AddSurface(const char *name, long surfaceId); int AddAnimation(const RawAnimation &animation); @@ -420,6 +441,10 @@ public: int GetCameraCount() const { return (int) cameras.size(); } const RawCamera &GetCamera(const int index) const { return cameras[index]; } + // Iterate over the lights. + int GetLightCount() const { return (int) lights.size(); } + const RawLight &GetLight(const int index) const { return lights[index]; } + // Iterate over the nodes. int GetNodeCount() const { return (int) nodes.size(); } const RawNode &GetNode(const int index) const { return nodes[index]; } @@ -447,6 +472,7 @@ private: std::vector triangles; std::vector textures; std::vector materials; + std::vector lights; std::vector surfaces; std::vector animations; std::vector cameras;