Procházet zdrojové kódy

Merge pull request #2779 from turbosquid/fix_gltf_accessor_overflow

Fix glTF validation error related to accessor min and max values
Gordon MacPherson před 5 roky
rodič
revize
81cb2433bc

+ 2 - 2
code/glTF/glTFAsset.h

@@ -362,8 +362,8 @@ namespace glTF
         ComponentType componentType; //!< The datatype of components in the attribute. (required)
         unsigned int count;          //!< The number of attributes referenced by this accessor. (required)
         AttribType::Value type;      //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
-        std::vector<float> max;      //!< Maximum value of each component in this attribute.
-        std::vector<float> min;      //!< Minimum value of each component in this attribute.
+        std::vector<double> max;     //!< Maximum value of each component in this attribute.
+        std::vector<double> min;     //!< Minimum value of each component in this attribute.
 
         unsigned int GetNumComponents();
         unsigned int GetBytesPerComponent();

+ 21 - 5
code/glTF/glTFAssetWriter.inl

@@ -54,9 +54,9 @@ namespace glTF {
 
     namespace {
 
-        template<size_t N>
+        template<typename T, size_t N>
         inline 
-        Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) {
+        Value& MakeValue(Value& val, T(&r)[N], MemoryPoolAllocator<>& al) {
             val.SetArray();
             val.Reserve(N, al);
             for (decltype(N) i = 0; i < N; ++i) {
@@ -65,8 +65,9 @@ namespace glTF {
             return val;
         }
 
+        template<typename T>
         inline 
-        Value& MakeValue(Value& val, const std::vector<float> & r, MemoryPoolAllocator<>& al) {
+        Value& MakeValue(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) {
             val.SetArray();
             val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
             for (unsigned int i = 0; i < r.size(); ++i) {
@@ -75,6 +76,16 @@ namespace glTF {
             return val;
         }
 
+        template<typename C, typename T>
+        inline Value& MakeValueCast(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) {
+            val.SetArray();
+            val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
+            for (unsigned int i = 0; i < r.size(); ++i) {
+                val.PushBack(static_cast<C>(r[i]), al);
+            }
+            return val;
+        }
+
         template<class T>
         inline void AddRefsVector(Value& obj, const char* fieldId, std::vector< Ref<T> >& v, MemoryPoolAllocator<>& al) {
             if (v.empty()) return;
@@ -100,8 +111,13 @@ namespace glTF {
         obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
 
         Value vTmpMax, vTmpMin;
-        obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
-        obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
+		if (a.componentType == ComponentType_FLOAT) {
+			obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
+			obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
+		} else {
+			obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl);
+			obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl);
+		}
     }
 
     inline void Write(Value& obj, Animation& a, AssetWriter& w)

+ 58 - 27
code/glTF/glTFExporter.cpp

@@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // Header files, standard library.
 #include <memory>
+#include <limits>
 #include <inttypes.h>
 
 #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
@@ -173,6 +174,62 @@ static void IdentityMatrix4(glTF::mat4& o)
     o[12] = 0; o[13] = 0; o[14] = 0; o[15] = 1;
 }
 
+template<typename T>
+void SetAccessorRange(Ref<Accessor> acc, void* data, unsigned int count,
+	unsigned int numCompsIn, unsigned int numCompsOut)
+{
+	ai_assert(numCompsOut <= numCompsIn);
+
+	// Allocate and initialize with large values.
+	for (unsigned int i = 0 ; i < numCompsOut ; i++) {
+		acc->min.push_back( std::numeric_limits<double>::max());
+		acc->max.push_back(-std::numeric_limits<double>::max());
+	}
+
+	size_t totalComps = count * numCompsIn;
+	T* buffer_ptr = static_cast<T*>(data);
+	T* buffer_end = buffer_ptr + totalComps;
+
+	// Search and set extreme values.
+	for (; buffer_ptr < buffer_end ; buffer_ptr += numCompsIn) {
+		for (unsigned int j = 0 ; j < numCompsOut ; j++) {
+			double valueTmp = buffer_ptr[j];
+
+			if (valueTmp < acc->min[j]) {
+				acc->min[j] = valueTmp;
+			}
+			if (valueTmp > acc->max[j]) {
+				acc->max[j] = valueTmp;
+			}
+		}
+	}
+}
+
+inline void SetAccessorRange(ComponentType compType, Ref<Accessor> acc, void* data,
+		unsigned int count, unsigned int numCompsIn, unsigned int numCompsOut)
+{
+	switch (compType) {
+		case ComponentType_SHORT:
+			SetAccessorRange<short>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_UNSIGNED_SHORT:
+			SetAccessorRange<unsigned short>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_UNSIGNED_INT:
+			SetAccessorRange<unsigned int>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_FLOAT:
+			SetAccessorRange<float>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_BYTE:
+			SetAccessorRange<int8_t>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_UNSIGNED_BYTE:
+			SetAccessorRange<uint8_t>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+	}
+}
+
 inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
     unsigned int count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false)
 {
@@ -206,33 +263,7 @@ inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& bu
     acc->type = typeOut;
 
     // calculate min and max values
-    {
-        // Allocate and initialize with large values.
-        float float_MAX = 10000000000000.0f;
-        for (unsigned int i = 0 ; i < numCompsOut ; i++) {
-            acc->min.push_back( float_MAX);
-            acc->max.push_back(-float_MAX);
-        }
-
-        // Search and set extreme values.
-        float valueTmp;
-        for (unsigned int i = 0 ; i < count       ; i++) {
-            for (unsigned int j = 0 ; j < numCompsOut ; j++) {
-                if (numCompsOut == 1) {
-                  valueTmp = static_cast<unsigned short*>(data)[i];
-                } else {
-                  valueTmp = static_cast<aiVector3D*>(data)[i][j];
-                }
-
-                if (valueTmp < acc->min[j]) {
-                    acc->min[j] = valueTmp;
-                }
-                if (valueTmp > acc->max[j]) {
-                    acc->max[j] = valueTmp;
-                }
-            }
-        }
-    }
+	SetAccessorRange(compType, acc, data, count, numCompsIn, numCompsOut);
 
     // copy the data
     acc->WriteData(count, data, numCompsIn*bytesPerComp);

+ 2 - 2
code/glTF2/glTF2Asset.h

@@ -387,8 +387,8 @@ namespace glTF2
         ComponentType componentType; //!< The datatype of components in the attribute. (required)
         size_t count;                //!< The number of attributes referenced by this accessor. (required)
         AttribType::Value type;      //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
-        std::vector<float> max;      //!< Maximum value of each component in this attribute.
-        std::vector<float> min;      //!< Minimum value of each component in this attribute.
+        std::vector<double> max;     //!< Maximum value of each component in this attribute.
+        std::vector<double> min;     //!< Minimum value of each component in this attribute.
 
         unsigned int GetNumComponents();
         unsigned int GetBytesPerComponent();

+ 24 - 7
code/glTF2/glTF2AssetWriter.inl

@@ -54,8 +54,8 @@ namespace glTF2 {
 
     namespace {
 
-        template<size_t N>
-        inline Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) {
+        template<typename T, size_t N>
+        inline Value& MakeValue(Value& val, T(&r)[N], MemoryPoolAllocator<>& al) {
             val.SetArray();
             val.Reserve(N, al);
             for (decltype(N) i = 0; i < N; ++i) {
@@ -64,7 +64,8 @@ namespace glTF2 {
             return val;
         }
 
-        inline Value& MakeValue(Value& val, const std::vector<float> & r, MemoryPoolAllocator<>& al) {
+        template<typename T>
+        inline Value& MakeValue(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) {
             val.SetArray();
             val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
             for (unsigned int i = 0; i < r.size(); ++i) {
@@ -73,8 +74,19 @@ namespace glTF2 {
             return val;
         }
 
-        inline Value& MakeValue(Value& val, float r, MemoryPoolAllocator<>& /*al*/) {
-            val.SetDouble(r);
+        template<typename C, typename T>
+        inline Value& MakeValueCast(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) {
+            val.SetArray();
+            val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
+            for (unsigned int i = 0; i < r.size(); ++i) {
+                val.PushBack(static_cast<C>(r[i]), al);
+            }
+            return val;
+        }
+
+        template<typename T>
+        inline Value& MakeValue(Value& val, T r, MemoryPoolAllocator<>& /*al*/) {
+            val.Set(r);
 
             return val;
         }
@@ -104,8 +116,13 @@ namespace glTF2 {
         obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
 
         Value vTmpMax, vTmpMin;
-        obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
-        obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
+		if (a.componentType == ComponentType_FLOAT) {
+			obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
+			obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
+		} else {
+			obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl);
+			obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl);
+		}
     }
 
     inline void Write(Value& obj, Animation& a, AssetWriter& w)

+ 58 - 27
code/glTF2/glTF2Exporter.cpp

@@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // Header files, standard library.
 #include <memory>
+#include <limits>
 #include <inttypes.h>
 
 using namespace rapidjson;
@@ -152,6 +153,62 @@ static void IdentityMatrix4(mat4& o) {
     o[12] = 0; o[13] = 0; o[14] = 0; o[15] = 1;
 }
 
+template<typename T>
+void SetAccessorRange(Ref<Accessor> acc, void* data, size_t count,
+	unsigned int numCompsIn, unsigned int numCompsOut)
+{
+	ai_assert(numCompsOut <= numCompsIn);
+
+	// Allocate and initialize with large values.
+	for (unsigned int i = 0 ; i < numCompsOut ; i++) {
+		acc->min.push_back( std::numeric_limits<double>::max());
+		acc->max.push_back(-std::numeric_limits<double>::max());
+	}
+
+	size_t totalComps = count * numCompsIn;
+	T* buffer_ptr = static_cast<T*>(data);
+	T* buffer_end = buffer_ptr + totalComps;
+
+	// Search and set extreme values.
+	for (; buffer_ptr < buffer_end ; buffer_ptr += numCompsIn) {
+		for (unsigned int j = 0 ; j < numCompsOut ; j++) {
+			double valueTmp = buffer_ptr[j];
+
+			if (valueTmp < acc->min[j]) {
+				acc->min[j] = valueTmp;
+			}
+			if (valueTmp > acc->max[j]) {
+				acc->max[j] = valueTmp;
+			}
+		}
+	}
+}
+
+inline void SetAccessorRange(ComponentType compType, Ref<Accessor> acc, void* data,
+		size_t count, unsigned int numCompsIn, unsigned int numCompsOut)
+{
+	switch (compType) {
+		case ComponentType_SHORT:
+			SetAccessorRange<short>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_UNSIGNED_SHORT:
+			SetAccessorRange<unsigned short>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_UNSIGNED_INT:
+			SetAccessorRange<unsigned int>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_FLOAT:
+			SetAccessorRange<float>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_BYTE:
+			SetAccessorRange<int8_t>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+		case ComponentType_UNSIGNED_BYTE:
+			SetAccessorRange<uint8_t>(acc, data, count, numCompsIn, numCompsOut);
+			return;
+	}
+}
+
 inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
     size_t count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false)
 {
@@ -187,33 +244,7 @@ inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& bu
     acc->type = typeOut;
 
     // calculate min and max values
-    {
-        // Allocate and initialize with large values.
-        float float_MAX = 10000000000000.0f;
-        for (unsigned int i = 0 ; i < numCompsOut ; i++) {
-            acc->min.push_back( float_MAX);
-            acc->max.push_back(-float_MAX);
-        }
-
-        // Search and set extreme values.
-        float valueTmp;
-        for (unsigned int i = 0 ; i < count       ; i++) {
-            for (unsigned int j = 0 ; j < numCompsOut ; j++) {
-                if (numCompsOut == 1) {
-                  valueTmp = static_cast<unsigned short*>(data)[i];
-                } else {
-                  valueTmp = static_cast<aiVector3D*>(data)[i][j];
-                }
-
-                if (valueTmp < acc->min[j]) {
-                    acc->min[j] = valueTmp;
-                }
-                if (valueTmp > acc->max[j]) {
-                    acc->max[j] = valueTmp;
-                }
-            }
-        }
-    }
+	SetAccessorRange(compType, acc, data, count, numCompsIn, numCompsOut);
 
     // copy the data
     acc->WriteData(count, data, numCompsIn*bytesPerComp);