Implement KHR_lights_punctual.

This commit is contained in:
Par Winzell 2018-12-15 15:26:52 -08:00 committed by Pär Winzell
parent 09089a7d79
commit 5389d848e2
14 changed files with 252 additions and 1 deletions

View File

@ -94,6 +94,9 @@ int main(int argc, char *argv[])
(
"khr-materials-unlit", "Use KHR_materials_unlit extension to specify Unlit shader.",
cxxopts::value<bool>(gltfOptions.useKHRMatUnlit))
(
"no-khr-punctual-lights", "Don't use KHR_punctual_lights extension to export lights.",
cxxopts::value<bool>(gltfOptions.useKHRPunctualLights))
(
"user-properties", "Transcribe FBX User Properties into glTF node and material 'extras'.",
cxxopts::value<bool>(gltfOptions.enableUserProperties))

View File

@ -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. */

View File

@ -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:

View File

@ -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;
}
}

View File

@ -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<CameraData> cameras;
Holder<NodeData> nodes;
Holder<SceneData> scenes;
Holder<LightData> lights;
std::shared_ptr<SamplerData> defaultSampler;
std::shared_ptr<BufferData> defaultBuffer;

View File

@ -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<GltfModel> gltf(new GltfModel(options));
@ -624,6 +626,47 @@ ModelData *Raw2Gltf(
}
iter->second->SetCamera(camera.ix);
}
//
// lights
//
std::vector<json> 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) {

View File

@ -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";

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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)

View File

@ -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<uint32_t> children;
int32_t mesh;
int32_t camera;
int32_t light;
int32_t skin;
std::vector<std::string> skeletons;
std::vector<std::string> userProperties;

View File

@ -76,6 +76,10 @@ template<class T> std::vector<T> toStdVec(const mathfu::Quaternion<T> &quat) {
return std::vector<T> { 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]);
}

View File

@ -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);

View File

@ -261,6 +261,7 @@ struct RawMetRoughMatProps : RawMatProps {
}
};
struct RawMaterial
{
std::string name;
@ -270,6 +271,23 @@ struct RawMaterial
std::vector<std::string> 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<std::string> userProperties;
};
@ -365,6 +384,8 @@ public:
int AddMaterial(
const char *name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX],
std::shared_ptr<RawMatProps> materialInfo, const std::vector<std::string>& 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<RawTriangle> triangles;
std::vector<RawTexture> textures;
std::vector<RawMaterial> materials;
std::vector<RawLight> lights;
std::vector<RawSurface> surfaces;
std::vector<RawAnimation> animations;
std::vector<RawCamera> cameras;