// // RegisteredMetaTypes.cpp // libraries/shared/src // // Created by Stephen Birarda on 10/3/13. // Copyright 2013 High Fidelity, Inc. // Copyright 2023 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // SPDX-License-Identifier: Apache-2.0 // #include "RegisteredMetaTypes.h" #include #include #include #include #include #include #include #include #include #include #include int uint32MetaTypeId = qRegisterMetaType("uint32"); int uint16MetaTypeId = qRegisterMetaType(); int glmUint32MetaTypeId = qRegisterMetaType("glm::uint32"); int vec2MetaTypeId = qRegisterMetaType(); int u8vec3MetaTypeId = qRegisterMetaType(); int vec3MetaTypeId = qRegisterMetaType(); int vec4MetaTypeId = qRegisterMetaType(); int qVectorVec3MetaTypeId = qRegisterMetaType>(); int qVectorQuatMetaTypeId = qRegisterMetaType>(); int qVectorBoolMetaTypeId = qRegisterMetaType>(); int qVectorGLMUint32MetaTypeId = qRegisterMetaType>("QVector"); int qVectorQUuidMetaTypeId = qRegisterMetaType>(); int quatMetaTypeId = qRegisterMetaType(); int pickRayMetaTypeId = qRegisterMetaType(); int collisionMetaTypeId = qRegisterMetaType(); int qMapURLStringMetaTypeId = qRegisterMetaType>(); int socketErrorMetaTypeId = qRegisterMetaType(); int voidLambdaType = qRegisterMetaType>(); int variantLambdaType = qRegisterMetaType>(); int stencilModeMetaTypeId = qRegisterMetaType(); QVariant vec2ToVariant(const glm::vec2 &vec2) { if (vec2.x != vec2.x || vec2.y != vec2.y) { // if vec2 contains a NaN don't try to convert it return QVariant(); } QVariantMap result; result["x"] = vec2.x; result["y"] = vec2.y; return result; } glm::vec2 vec2FromVariant(const QVariant &object, bool& isValid) { isValid = false; glm::vec2 result; if (object.canConvert()) { result = glm::vec2(object.toFloat()); isValid = true; } else if (object.canConvert()) { auto qvec2 = qvariant_cast(object); result.x = qvec2.x(); result.y = qvec2.y(); isValid = true; } else { auto map = object.toMap(); auto x = map["x"]; if (!x.isValid()) { x = map["u"]; } auto y = map["y"]; if (!y.isValid()) { y = map["v"]; } if (x.isValid() && y.isValid()) { result.x = x.toFloat(&isValid); if (isValid) { result.y = y.toFloat(&isValid); } } } return result; } glm::vec2 vec2FromVariant(const QVariant &object) { bool valid; return vec2FromVariant(object, valid); } QVariant vec3toVariant(const glm::vec3& vec3) { if (vec3.x != vec3.x || vec3.y != vec3.y || vec3.z != vec3.z) { // if vec3 contains a NaN don't try to convert it return QVariant(); } QVariantMap result; result["x"] = vec3.x; result["y"] = vec3.y; result["z"] = vec3.z; return result; } glm::vec3 vec3FromVariant(const QVariant& object, bool& valid) { glm::vec3 v; valid = false; if (!object.isValid() || object.isNull()) { return v; } else if (object.canConvert()) { v = glm::vec3(object.toFloat()); valid = true; } else if (object.canConvert()) { auto qvec3 = qvariant_cast(object); v.x = qvec3.x(); v.y = qvec3.y(); v.z = qvec3.z(); valid = true; } else if (object.canConvert()) { QColor qColor(object.toString()); if (qColor.isValid()) { v.x = (uint8_t)qColor.red(); v.y = (uint8_t)qColor.green(); v.z = (uint8_t)qColor.blue(); valid = true; } } else if (object.canConvert()) { QColor qColor = qvariant_cast(object); if (qColor.isValid()) { v.x = (uint8_t)qColor.red(); v.y = (uint8_t)qColor.green(); v.z = (uint8_t)qColor.blue(); valid = true; } } else { auto map = object.toMap(); auto x = map["x"]; if (!x.isValid()) { x = map["r"]; } if (!x.isValid()) { x = map["red"]; } auto y = map["y"]; if (!y.isValid()) { y = map["g"]; } if (!y.isValid()) { y = map["green"]; } auto z = map["z"]; if (!z.isValid()) { z = map["b"]; } if (!z.isValid()) { z = map["blue"]; } if (x.canConvert() && y.canConvert() && z.canConvert()) { v.x = x.toFloat(); v.y = y.toFloat(); v.z = z.toFloat(); valid = true; } } return v; } glm::vec3 vec3FromVariant(const QVariant& object) { bool valid = false; return vec3FromVariant(object, valid); } QVariant u8vec3toVariant(const glm::u8vec3& vec3) { QVariantMap result; result["x"] = vec3.x; result["y"] = vec3.y; result["z"] = vec3.z; return result; } QVariant u8vec3ColortoVariant(const glm::u8vec3& vec3) { QVariantMap result; result["red"] = vec3.x; result["green"] = vec3.y; result["blue"] = vec3.z; return result; } glm::u8vec3 u8vec3FromVariant(const QVariant& object, bool& valid) { glm::u8vec3 v; valid = false; if (!object.isValid() || object.isNull()) { return v; } else if (object.canConvert()) { v = glm::vec3(object.toUInt()); valid = true; } else if (object.canConvert()) { auto qvec3 = qvariant_cast(object); v.x = (uint8_t)qvec3.x(); v.y = (uint8_t)qvec3.y(); v.z = (uint8_t)qvec3.z(); valid = true; } else if (object.canConvert()) { QColor qColor(object.toString()); if (qColor.isValid()) { v.x = (uint8_t)qColor.red(); v.y = (uint8_t)qColor.green(); v.z = (uint8_t)qColor.blue(); valid = true; } } else if (object.canConvert()) { QColor qColor = qvariant_cast(object); if (qColor.isValid()) { v.x = (uint8_t)qColor.red(); v.y = (uint8_t)qColor.green(); v.z = (uint8_t)qColor.blue(); valid = true; } } else { auto map = object.toMap(); auto x = map["x"]; if (!x.isValid()) { x = map["r"]; } if (!x.isValid()) { x = map["red"]; } auto y = map["y"]; if (!y.isValid()) { y = map["g"]; } if (!y.isValid()) { y = map["green"]; } auto z = map["z"]; if (!z.isValid()) { z = map["b"]; } if (!z.isValid()) { z = map["blue"]; } if (x.canConvert() && y.canConvert() && z.canConvert()) { v.x = x.toUInt(); v.y = y.toUInt(); v.z = z.toUInt(); valid = true; } } return v; } glm::u8vec3 u8vec3FromVariant(const QVariant& object) { bool valid = false; return u8vec3FromVariant(object, valid); } QVariant vec4toVariant(const glm::vec4& vec4) { if (isNaN(vec4.x) || isNaN(vec4.y) || isNaN(vec4.z) || isNaN(vec4.w)) { // if vec4 contains a NaN don't try to convert it return QVariant(); } QVariantMap result; result["x"] = vec4.x; result["y"] = vec4.y; result["z"] = vec4.z; result["w"] = vec4.w; return result; } glm::vec4 vec4FromVariant(const QVariant& object, bool& valid) { glm::vec4 v; valid = false; if (!object.isValid() || object.isNull()) { return v; } else if (object.canConvert()) { v = glm::vec4(object.toFloat()); valid = true; } else if (object.canConvert()) { auto qvec4 = qvariant_cast(object); v.x = qvec4.x(); v.y = qvec4.y(); v.z = qvec4.z(); v.w = qvec4.w(); valid = true; } else { auto map = object.toMap(); auto x = map["x"]; auto y = map["y"]; auto z = map["z"]; auto w = map["w"]; if (x.canConvert() && y.canConvert() && z.canConvert() && w.canConvert()) { v.x = x.toFloat(); v.y = y.toFloat(); v.z = z.toFloat(); v.w = w.toFloat(); valid = true; } } return v; } glm::vec4 vec4FromVariant(const QVariant& object) { bool valid = false; return vec4FromVariant(object, valid); } QVariant mat4ToVariant(const glm::mat4& mat4) { if (mat4 != mat4) { // NaN return QVariant(); } QVariantMap object; object["r0c0"] = mat4[0][0]; object["r1c0"] = mat4[0][1]; object["r2c0"] = mat4[0][2]; object["r3c0"] = mat4[0][3]; object["r0c1"] = mat4[1][0]; object["r1c1"] = mat4[1][1]; object["r2c1"] = mat4[1][2]; object["r3c1"] = mat4[1][3]; object["r0c2"] = mat4[2][0]; object["r1c2"] = mat4[2][1]; object["r2c2"] = mat4[2][2]; object["r3c2"] = mat4[2][3]; object["r0c3"] = mat4[3][0]; object["r1c3"] = mat4[3][1]; object["r2c3"] = mat4[3][2]; object["r3c3"] = mat4[3][3]; return object; } glm::mat4 mat4FromVariant(const QVariant& object, bool& valid) { glm::mat4 mat4; valid = false; if (!object.isValid() || object.isNull()) { return mat4; } else { const static auto getElement = [](const QVariantMap& map, const char * key, float& value, bool& everyConversionValid) { auto variantValue = map[key]; if (variantValue.canConvert()) { value = variantValue.toFloat(); } else { everyConversionValid = false; } }; auto map = object.toMap(); bool everyConversionValid = true; getElement(map, "r0c0", mat4[0][0], everyConversionValid); getElement(map, "r1c0", mat4[0][1], everyConversionValid); getElement(map, "r2c0", mat4[0][2], everyConversionValid); getElement(map, "r3c0", mat4[0][3], everyConversionValid); getElement(map, "r0c1", mat4[1][0], everyConversionValid); getElement(map, "r1c1", mat4[1][1], everyConversionValid); getElement(map, "r2c1", mat4[1][2], everyConversionValid); getElement(map, "r3c1", mat4[1][3], everyConversionValid); getElement(map, "r0c2", mat4[2][0], everyConversionValid); getElement(map, "r1c2", mat4[2][1], everyConversionValid); getElement(map, "r2c2", mat4[2][2], everyConversionValid); getElement(map, "r3c2", mat4[2][3], everyConversionValid); getElement(map, "r0c3", mat4[3][0], everyConversionValid); getElement(map, "r1c3", mat4[3][1], everyConversionValid); getElement(map, "r2c3", mat4[3][2], everyConversionValid); getElement(map, "r3c3", mat4[3][3], everyConversionValid); if (everyConversionValid) { valid = true; } return mat4; } } glm::mat4 mat4FromVariant(const QVariant& object) { bool valid = false; return mat4FromVariant(object, valid); } glm::quat quatFromVariant(const QVariant &object, bool& isValid) { glm::quat q; if (object.canConvert()) { auto qquat = qvariant_cast(object); q.x = qquat.x(); q.y = qquat.y(); q.z = qquat.z(); q.w = qquat.scalar(); // enforce normalized quaternion float length = glm::length(q); if (length > FLT_EPSILON) { q /= length; } else { q = glm::quat(); } isValid = true; } else { auto map = object.toMap(); q.x = map["x"].toFloat(&isValid); if (!isValid) { return glm::quat(); } q.y = map["y"].toFloat(&isValid); if (!isValid) { return glm::quat(); } q.z = map["z"].toFloat(&isValid); if (!isValid) { return glm::quat(); } q.w = map["w"].toFloat(&isValid); if (!isValid) { return glm::quat(); } } return q; } glm::quat quatFromVariant(const QVariant& object) { bool valid = false; return quatFromVariant(object, valid); } QVariant quatToVariant(const glm::quat& quat) { if (quat.x != quat.x || quat.y != quat.y || quat.z != quat.z) { // if quat contains a NaN don't try to convert it return QVariant(); } QVariantMap result; result["x"] = quat.x; result["y"] = quat.y; result["z"] = quat.z; result["w"] = quat.w; return result; } QVariant qRectToVariant(const QRect& rect) { QVariantMap obj; obj["x"] = rect.x(); obj["y"] = rect.y(); obj["width"] = rect.width(); obj["height"] = rect.height(); return obj; } QRect qRectFromVariant(const QVariant& objectVar, bool& valid) { QVariantMap object = objectVar.toMap(); QRect rect; valid = false; rect.setX(object["x"].toInt(&valid)); if (valid) { rect.setY(object["y"].toInt(&valid)); } if (valid) { rect.setWidth(object["width"].toInt(&valid)); } if (valid) { rect.setHeight(object["height"].toInt(&valid)); } return rect; } QRect qRectFromVariant(const QVariant& object) { bool valid; return qRectFromVariant(object, valid); } QVariant qRectFToVariant(const QRectF& rect) { QVariantMap obj; obj["x"] = rect.x(); obj["y"] = rect.y(); obj["width"] = rect.width(); obj["height"] = rect.height(); return obj; } QRectF qRectFFromVariant(const QVariant& objectVar, bool& valid) { QVariantMap object = objectVar.toMap(); QRectF rect; valid = false; rect.setX(object["x"].toFloat(&valid)); if (valid) { rect.setY(object["y"].toFloat(&valid)); } if (valid) { rect.setWidth(object["width"].toFloat(&valid)); } if (valid) { rect.setHeight(object["height"].toFloat(&valid)); } return rect; } QRectF qRectFFromVariant(const QVariant& object) { bool valid; return qRectFFromVariant(object, valid); } /*@jsdoc * Details of a collision between avatars and entities. * @typedef {object} Collision * @property {ContactEventType} type - The contact type of the collision event. * @property {Uuid} idA - The ID of one of the avatars or entities in the collision. * @property {Uuid} idB - The ID of the other of the avatars or entities in the collision. * @property {Vec3} penetration - The amount of penetration between the two items. * @property {Vec3} contactPoint - The point of contact. * @property {Vec3} velocityChange - The change in relative velocity of the two items, in m/s. */ void Collision::invert() { std::swap(idA, idB); contactPoint += penetration; penetration *= -1.0f; } AnimationDetails::AnimationDetails() : role(), url(), fps(0.0f), priority(0.0f), loop(false), hold(false), startAutomatically(false), firstFrame(0.0f), lastFrame(0.0f), running(false), currentFrame(0.0f) { } AnimationDetails::AnimationDetails(QString role, QUrl url, float fps, float priority, bool loop, bool hold, bool startAutomatically, float firstFrame, float lastFrame, bool running, float currentFrame, bool allowTranslation) : role(role), url(url), fps(fps), priority(priority), loop(loop), hold(hold), startAutomatically(startAutomatically), firstFrame(firstFrame), lastFrame(lastFrame), running(running), currentFrame(currentFrame), allowTranslation(allowTranslation) { } QVariantMap parseTexturesToMap(QString newTextures, const QVariantMap& defaultTextures) { // If textures are unset, revert to original textures if (newTextures.isEmpty()) { return defaultTextures; } // Legacy: a ,\n-delimited list of filename:"texturepath" if (*newTextures.cbegin() != '{') { newTextures = "{\"" + newTextures.replace(":\"", "\":\"").replace(",\n", ",\"") + "}"; } QJsonParseError error; QJsonDocument newTexturesJson = QJsonDocument::fromJson(newTextures.toUtf8(), &error); // If textures are invalid, revert to original textures if (error.error != QJsonParseError::NoError) { qWarning() << "Could not evaluate textures property value:" << newTextures; return defaultTextures; } QVariantMap newTexturesMap = newTexturesJson.toVariant().toMap(); QVariantMap toReturn = defaultTextures; for (auto& texture : newTexturesMap.keys()) { auto newURL = newTexturesMap[texture]; if (newURL.canConvert()) { toReturn[texture] = newURL.toUrl(); } else if (newURL.canConvert()) { toReturn[texture] = QUrl(newURL.toString()); } else { toReturn[texture] = newURL; } } return toReturn; }