Jelajahi Sumber

Merge pull request #2578 from petrmohelnik/glTF-2.0-Lights-import

glTF 2.0 Lights import
Kim Kulling 6 tahun lalu
induk
melakukan
563ec1662c

+ 27 - 0
code/glTF2/glTF2Asset.h

@@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  * glTF Extensions Support:
  * glTF Extensions Support:
  *   KHR_materials_pbrSpecularGlossiness full
  *   KHR_materials_pbrSpecularGlossiness full
  *   KHR_materials_unlit full
  *   KHR_materials_unlit full
+ *   KHR_lights_punctual full
  */
  */
 #ifndef GLTF2ASSET_H_INC
 #ifndef GLTF2ASSET_H_INC
 #define GLTF2ASSET_H_INC
 #define GLTF2ASSET_H_INC
@@ -668,6 +669,28 @@ namespace glTF2
         void Read(Value& obj, Asset& r);
         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.
     //! Image data used to create a texture.
     struct Image : public Object
     struct Image : public Object
@@ -819,6 +842,7 @@ namespace glTF2
         Nullable<vec3> scale;
         Nullable<vec3> scale;
 
 
         Ref<Camera> camera;
         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.
         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.
         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_pbrSpecularGlossiness;
             bool KHR_materials_unlit;
             bool KHR_materials_unlit;
+            bool KHR_lights_punctual;
 
 
         } extensionsUsed;
         } extensionsUsed;
 
 
@@ -1063,6 +1088,7 @@ namespace glTF2
         LazyDict<Buffer>      buffers;
         LazyDict<Buffer>      buffers;
         LazyDict<BufferView>  bufferViews;
         LazyDict<BufferView>  bufferViews;
         LazyDict<Camera>      cameras;
         LazyDict<Camera>      cameras;
+        LazyDict<Light>       lights;
         LazyDict<Image>       images;
         LazyDict<Image>       images;
         LazyDict<Material>    materials;
         LazyDict<Material>    materials;
         LazyDict<Mesh>        meshes;
         LazyDict<Mesh>        meshes;
@@ -1083,6 +1109,7 @@ namespace glTF2
             , buffers       (*this, "buffers")
             , buffers       (*this, "buffers")
             , bufferViews   (*this, "bufferViews")
             , bufferViews   (*this, "bufferViews")
             , cameras       (*this, "cameras")
             , cameras       (*this, "cameras")
+            , lights        (*this, "lights", "KHR_lights_punctual")
             , images        (*this, "images")
             , images        (*this, "images")
             , materials     (*this, "materials")
             , materials     (*this, "materials")
             , meshes        (*this, "meshes")
             , meshes        (*this, "meshes")

+ 47 - 0
code/glTF2/glTF2Asset.inl

@@ -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)
 inline void Node::Read(Value& obj, Asset& r)
 {
 {
 
 
@@ -1110,6 +1143,19 @@ inline void Node::Read(Value& obj, Asset& r)
         if (this->camera)
         if (this->camera)
             this->camera->id = this->id;
             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)
 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_pbrSpecularGlossiness);
     CHECK_EXT(KHR_materials_unlit);
     CHECK_EXT(KHR_materials_unlit);
+    CHECK_EXT(KHR_lights_punctual);
 
 
     #undef CHECK_EXT
     #undef CHECK_EXT
 }
 }

+ 5 - 0
code/glTF2/glTF2AssetWriter.inl

@@ -202,6 +202,11 @@ namespace glTF2 {
 
 
     }
     }
 
 
+    inline void Write(Value& /*obj*/, Light& /*c*/, AssetWriter& /*w*/)
+    {
+
+    }
+
     inline void Write(Value& obj, Image& img, AssetWriter& w)
     inline void Write(Value& obj, Image& img, AssetWriter& w)
     {
     {
         if (img.bufferView) {
         if (img.bufferView) {

+ 80 - 4
code/glTF2/glTF2Importer.cpp

@@ -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)
 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) {
 static void GetNodeTransform(aiMatrix4x4& matrix, const glTF2::Node& node) {
     if (node.matrix.isPresent) {
     if (node.matrix.isPresent) {
         CopyValue(node.matrix.value, matrix);
         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;
         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;
     return ainode;
 }
 }
 
 
@@ -1150,6 +1225,7 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
     ImportMeshes(asset);
     ImportMeshes(asset);
 
 
     ImportCameras(asset);
     ImportCameras(asset);
+    ImportLights(asset);
 
 
     ImportNodes(asset);
     ImportNodes(asset);