glTF 2.0 Lights import
Importing of lights according to glTF 2.0 extension KHR_lights_punctual https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual Since glTF lights are based on PBR they use different attenuation model than conventional lights supported by assimp. It is possible to use attenuation factors in assimp to describe inverse square law fallof. But the light structure does not provide means to save range property. Therefore I resorted to use of metadata. When range parameter is present, I put it into 'PBR_LightRange' metadata of light's node. Please, see comment in glTF2Importer file.pull/2578/head
parent
1c3e934ae9
commit
9330cca1cd
|
@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
* glTF Extensions Support:
|
||||
* KHR_materials_pbrSpecularGlossiness full
|
||||
* KHR_materials_unlit full
|
||||
* KHR_lights_punctual full
|
||||
*/
|
||||
#ifndef GLTF2ASSET_H_INC
|
||||
#define GLTF2ASSET_H_INC
|
||||
|
@ -668,6 +669,28 @@ namespace glTF2
|
|||
void Read(Value& obj, Asset& r);
|
||||
};
|
||||
|
||||
//! A light (from KHR_lights_punctual extension)
|
||||
struct Light : public Object
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
Directional,
|
||||
Point,
|
||||
Spot
|
||||
};
|
||||
|
||||
Type type;
|
||||
|
||||
vec3 color;
|
||||
float intensity;
|
||||
Nullable<float> range;
|
||||
|
||||
float innerConeAngle;
|
||||
float outerConeAngle;
|
||||
|
||||
Light() {}
|
||||
void Read(Value& obj, Asset& r);
|
||||
};
|
||||
|
||||
//! Image data used to create a texture.
|
||||
struct Image : public Object
|
||||
|
@ -819,6 +842,7 @@ namespace glTF2
|
|||
Nullable<vec3> scale;
|
||||
|
||||
Ref<Camera> camera;
|
||||
Ref<Light> light;
|
||||
|
||||
std::vector< Ref<Node> > skeletons; //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy.
|
||||
Ref<Skin> skin; //!< The ID of the skin referenced by this node.
|
||||
|
@ -1050,6 +1074,7 @@ namespace glTF2
|
|||
{
|
||||
bool KHR_materials_pbrSpecularGlossiness;
|
||||
bool KHR_materials_unlit;
|
||||
bool KHR_lights_punctual;
|
||||
|
||||
} extensionsUsed;
|
||||
|
||||
|
@ -1063,6 +1088,7 @@ namespace glTF2
|
|||
LazyDict<Buffer> buffers;
|
||||
LazyDict<BufferView> bufferViews;
|
||||
LazyDict<Camera> cameras;
|
||||
LazyDict<Light> lights;
|
||||
LazyDict<Image> images;
|
||||
LazyDict<Material> materials;
|
||||
LazyDict<Mesh> meshes;
|
||||
|
@ -1083,6 +1109,7 @@ namespace glTF2
|
|||
, buffers (*this, "buffers")
|
||||
, bufferViews (*this, "bufferViews")
|
||||
, cameras (*this, "cameras")
|
||||
, lights (*this, "lights", "KHR_lights_punctual")
|
||||
, images (*this, "images")
|
||||
, materials (*this, "materials")
|
||||
, meshes (*this, "meshes")
|
||||
|
|
|
@ -1067,6 +1067,39 @@ inline void Camera::Read(Value& obj, Asset& /*r*/)
|
|||
}
|
||||
}
|
||||
|
||||
inline void Light::Read(Value& obj, Asset& /*r*/)
|
||||
{
|
||||
#ifndef M_PI
|
||||
const float M_PI = 3.14159265358979323846f;
|
||||
#endif
|
||||
|
||||
std::string type_string;
|
||||
ReadMember(obj, "type", type_string);
|
||||
if (type_string == "directional")
|
||||
type = Light::Directional;
|
||||
else if (type_string == "point")
|
||||
type = Light::Point;
|
||||
else
|
||||
type = Light::Spot;
|
||||
|
||||
name = MemberOrDefault(obj, "name", "");
|
||||
|
||||
SetVector(color, vec3{ 1.0f, 1.0f, 1.0f });
|
||||
ReadMember(obj, "color", color);
|
||||
|
||||
intensity = MemberOrDefault(obj, "intensity", 1.0f);
|
||||
|
||||
ReadMember(obj, "range", range);
|
||||
|
||||
if (type == Light::Spot)
|
||||
{
|
||||
Value* spot = FindObject(obj, "spot");
|
||||
if (!spot) throw DeadlyImportError("GLTF: Light missing its spot parameters");
|
||||
innerConeAngle = MemberOrDefault(*spot, "innerConeAngle", 0.0f);
|
||||
outerConeAngle = MemberOrDefault(*spot, "outerConeAngle", M_PI / 4.0f);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Node::Read(Value& obj, Asset& r)
|
||||
{
|
||||
|
||||
|
@ -1110,6 +1143,19 @@ inline void Node::Read(Value& obj, Asset& r)
|
|||
if (this->camera)
|
||||
this->camera->id = this->id;
|
||||
}
|
||||
|
||||
if (Value* extensions = FindObject(obj, "extensions")) {
|
||||
if (r.extensionsUsed.KHR_lights_punctual) {
|
||||
|
||||
if (Value* ext = FindObject(*extensions, "KHR_lights_punctual")) {
|
||||
if (Value* light = FindUInt(*ext, "light")) {
|
||||
this->light = r.lights.Retrieve(light->GetUint());
|
||||
if (this->light)
|
||||
this->light->id = this->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void Scene::Read(Value& obj, Asset& r)
|
||||
|
@ -1421,6 +1467,7 @@ inline void Asset::ReadExtensionsUsed(Document& doc)
|
|||
|
||||
CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
|
||||
CHECK_EXT(KHR_materials_unlit);
|
||||
CHECK_EXT(KHR_lights_punctual);
|
||||
|
||||
#undef CHECK_EXT
|
||||
}
|
||||
|
|
|
@ -202,6 +202,11 @@ namespace glTF2 {
|
|||
|
||||
}
|
||||
|
||||
inline void Write(Value& /*obj*/, Light& /*c*/, AssetWriter& /*w*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
inline void Write(Value& obj, Image& img, AssetWriter& w)
|
||||
{
|
||||
if (img.bufferView) {
|
||||
|
|
|
@ -140,10 +140,10 @@ static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode)
|
|||
}
|
||||
}
|
||||
|
||||
//static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
|
||||
//{
|
||||
// out.r = v[0]; out.g = v[1]; out.b = v[2];
|
||||
//}
|
||||
static void CopyValue(const glTF2::vec3& v, aiColor3D& out)
|
||||
{
|
||||
out.r = v[0]; out.g = v[1]; out.b = v[2];
|
||||
}
|
||||
|
||||
static void CopyValue(const glTF2::vec4& v, aiColor4D& out)
|
||||
{
|
||||
|
@ -710,6 +710,69 @@ void glTF2Importer::ImportCameras(glTF2::Asset& r)
|
|||
}
|
||||
}
|
||||
|
||||
void glTF2Importer::ImportLights(glTF2::Asset& r)
|
||||
{
|
||||
if (!r.lights.Size())
|
||||
return;
|
||||
|
||||
mScene->mNumLights = r.lights.Size();
|
||||
mScene->mLights = new aiLight*[r.lights.Size()];
|
||||
|
||||
for (size_t i = 0; i < r.lights.Size(); ++i) {
|
||||
Light& light = r.lights[i];
|
||||
|
||||
aiLight* ail = mScene->mLights[i] = new aiLight();
|
||||
|
||||
switch (light.type)
|
||||
{
|
||||
case Light::Directional:
|
||||
ail->mType = aiLightSource_DIRECTIONAL; break;
|
||||
case Light::Point:
|
||||
ail->mType = aiLightSource_POINT; break;
|
||||
case Light::Spot:
|
||||
ail->mType = aiLightSource_SPOT; break;
|
||||
}
|
||||
|
||||
if (ail->mType != aiLightSource_POINT)
|
||||
{
|
||||
ail->mDirection = aiVector3D(0.0f, 0.0f, -1.0f);
|
||||
ail->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
|
||||
vec3 colorWithIntensity = { light.color[0] * light.intensity, light.color[1] * light.intensity, light.color[2] * light.intensity };
|
||||
CopyValue(colorWithIntensity, ail->mColorAmbient);
|
||||
CopyValue(colorWithIntensity, ail->mColorDiffuse);
|
||||
CopyValue(colorWithIntensity, ail->mColorSpecular);
|
||||
|
||||
if (ail->mType == aiLightSource_DIRECTIONAL)
|
||||
{
|
||||
ail->mAttenuationConstant = 1.0;
|
||||
ail->mAttenuationLinear = 0.0;
|
||||
ail->mAttenuationQuadratic = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//in PBR attenuation is calculated using inverse square law which can be expressed
|
||||
//using assimps equation: 1/(att0 + att1 * d + att2 * d*d) with the following parameters
|
||||
//this is correct equation for the case when range (see
|
||||
//https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual)
|
||||
//is not present. When range is not present it is assumed that it is infinite and so numerator is 1.
|
||||
//When range is present then numerator might be any value in range [0,1] and then assimps equation
|
||||
//will not suffice. In this case range is added into metadata in ImportNode function
|
||||
//and its up to implementation to read it when it wants to
|
||||
ail->mAttenuationConstant = 0.0;
|
||||
ail->mAttenuationLinear = 0.0;
|
||||
ail->mAttenuationQuadratic = 1.0;
|
||||
}
|
||||
|
||||
if (ail->mType == aiLightSource_SPOT)
|
||||
{
|
||||
ail->mAngleInnerCone = light.innerConeAngle;
|
||||
ail->mAngleOuterCone = light.outerConeAngle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GetNodeTransform(aiMatrix4x4& matrix, const glTF2::Node& node) {
|
||||
if (node.matrix.isPresent) {
|
||||
CopyValue(node.matrix.value, matrix);
|
||||
|
@ -881,6 +944,18 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
|
|||
pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
|
||||
}
|
||||
|
||||
if (node.light) {
|
||||
pScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
|
||||
|
||||
//range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
|
||||
//it is added to meta data of parent node, because there is no other place to put it
|
||||
if (node.light->range.isPresent)
|
||||
{
|
||||
ainode->mMetaData = aiMetadata::Alloc(1);
|
||||
ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value);
|
||||
}
|
||||
}
|
||||
|
||||
return ainode;
|
||||
}
|
||||
|
||||
|
@ -1150,6 +1225,7 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
|
|||
ImportMeshes(asset);
|
||||
|
||||
ImportCameras(asset);
|
||||
ImportLights(asset);
|
||||
|
||||
ImportNodes(asset);
|
||||
|
||||
|
|
Loading…
Reference in New Issue