// // Copyright 2016 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #include "pxr/pxr.h" #include "pxr/usd/sdf/parserHelpers.h" #include "pxr/usd/sdf/schema.h" #include "pxr/base/gf/half.h" #include "pxr/base/gf/matrix2d.h" #include "pxr/base/gf/matrix3d.h" #include "pxr/base/gf/matrix4d.h" #include "pxr/base/gf/quatd.h" #include "pxr/base/gf/quatf.h" #include "pxr/base/gf/quath.h" #include "pxr/base/gf/vec2d.h" #include "pxr/base/gf/vec2f.h" #include "pxr/base/gf/vec2h.h" #include "pxr/base/gf/vec2i.h" #include "pxr/base/gf/vec3d.h" #include "pxr/base/gf/vec3f.h" #include "pxr/base/gf/vec3h.h" #include "pxr/base/gf/vec3i.h" #include "pxr/base/gf/vec4d.h" #include "pxr/base/gf/vec4f.h" #include "pxr/base/gf/vec4h.h" #include "pxr/base/gf/vec4i.h" #include "pxr/base/tf/iterator.h" #include "pxr/base/tf/stringUtils.h" #include "pxr/base/plug/registry.h" #include "pxr/base/vt/array.h" #include "pxr/base/vt/value.h" #include #include PXR_NAMESPACE_OPEN_SCOPE namespace Sdf_ParserHelpers { using std::string; using std::vector; // Check that there are enough values to parse so we don't overflow #define CHECK_BOUNDS(count, name) \ if (index + count > vars.size()) { \ TF_CODING_ERROR("Not enough values to parse value of type %s", \ name); \ throw boost::bad_get(); \ } inline void MakeScalarValueImpl(string *out, vector const &vars, size_t &index) { CHECK_BOUNDS(1, "string"); *out = vars[index++].Get(); } inline void MakeScalarValueImpl(TfToken *out, vector const &vars, size_t &index) { CHECK_BOUNDS(1, "token"); *out = TfToken(vars[index++].Get()); } inline void MakeScalarValueImpl(double *out, vector const &vars, size_t &index) { CHECK_BOUNDS(1, "double"); *out = vars[index++].Get(); } inline void MakeScalarValueImpl(float *out, vector const &vars, size_t &index) { CHECK_BOUNDS(1, "float"); *out = vars[index++].Get(); } inline void MakeScalarValueImpl(GfHalf *out, vector const &vars, size_t &index) { CHECK_BOUNDS(1, "half"); *out = GfHalf(vars[index++].Get()); } inline void MakeScalarValueImpl( SdfTimeCode *out, vector const &vars, size_t &index) { CHECK_BOUNDS(1, "timecode"); *out = SdfTimeCode(vars[index++].Get()); } template inline std::enable_if_t::value> MakeScalarValueImpl(Int *out, vector const &vars, size_t &index) { CHECK_BOUNDS(1, ArchGetDemangled().c_str()); *out = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec2d *out, vector const &vars, size_t &index) { CHECK_BOUNDS(2, "Vec2d"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec2f *out, vector const &vars, size_t &index) { CHECK_BOUNDS(2, "Vec2f"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec2h *out, vector const &vars, size_t &index) { CHECK_BOUNDS(2, "Vec2h"); (*out)[0] = GfHalf(vars[index++].Get()); (*out)[1] = GfHalf(vars[index++].Get()); } inline void MakeScalarValueImpl(GfVec2i *out, vector const &vars, size_t &index) { CHECK_BOUNDS(2, "Vec2i"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec3d *out, vector const &vars, size_t &index) { CHECK_BOUNDS(3, "Vec3d"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); (*out)[2] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec3f *out, vector const &vars, size_t &index) { CHECK_BOUNDS(3, "Vec3f"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); (*out)[2] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec3h *out, vector const &vars, size_t &index) { CHECK_BOUNDS(3, "Vec3h"); (*out)[0] = GfHalf(vars[index++].Get()); (*out)[1] = GfHalf(vars[index++].Get()); (*out)[2] = GfHalf(vars[index++].Get()); } inline void MakeScalarValueImpl(GfVec3i *out, vector const &vars, size_t &index) { CHECK_BOUNDS(3, "Vec3i"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); (*out)[2] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec4d *out, vector const &vars, size_t &index) { CHECK_BOUNDS(4, "Vec4d"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); (*out)[2] = vars[index++].Get(); (*out)[3] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec4f *out, vector const &vars, size_t &index) { CHECK_BOUNDS(4, "Vec4f"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); (*out)[2] = vars[index++].Get(); (*out)[3] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfVec4h *out, vector const &vars, size_t &index) { CHECK_BOUNDS(4, "Vec4h"); (*out)[0] = GfHalf(vars[index++].Get()); (*out)[1] = GfHalf(vars[index++].Get()); (*out)[2] = GfHalf(vars[index++].Get()); (*out)[3] = GfHalf(vars[index++].Get()); } inline void MakeScalarValueImpl(GfVec4i *out, vector const &vars, size_t &index) { CHECK_BOUNDS(4, "Vec4i"); (*out)[0] = vars[index++].Get(); (*out)[1] = vars[index++].Get(); (*out)[2] = vars[index++].Get(); (*out)[3] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfMatrix2d *out, vector const &vars, size_t &index) { CHECK_BOUNDS(4, "Matrix2d"); (*out)[0][0] = vars[index++].Get(); (*out)[0][1] = vars[index++].Get(); (*out)[1][0] = vars[index++].Get(); (*out)[1][1] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfMatrix3d *out, vector const &vars, size_t &index) { CHECK_BOUNDS(9, "Matrix3d"); (*out)[0][0] = vars[index++].Get(); (*out)[0][1] = vars[index++].Get(); (*out)[0][2] = vars[index++].Get(); (*out)[1][0] = vars[index++].Get(); (*out)[1][1] = vars[index++].Get(); (*out)[1][2] = vars[index++].Get(); (*out)[2][0] = vars[index++].Get(); (*out)[2][1] = vars[index++].Get(); (*out)[2][2] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfMatrix4d *out, vector const &vars, size_t &index) { CHECK_BOUNDS(16, "Matrix4d"); (*out)[0][0] = vars[index++].Get(); (*out)[0][1] = vars[index++].Get(); (*out)[0][2] = vars[index++].Get(); (*out)[0][3] = vars[index++].Get(); (*out)[1][0] = vars[index++].Get(); (*out)[1][1] = vars[index++].Get(); (*out)[1][2] = vars[index++].Get(); (*out)[1][3] = vars[index++].Get(); (*out)[2][0] = vars[index++].Get(); (*out)[2][1] = vars[index++].Get(); (*out)[2][2] = vars[index++].Get(); (*out)[2][3] = vars[index++].Get(); (*out)[3][0] = vars[index++].Get(); (*out)[3][1] = vars[index++].Get(); (*out)[3][2] = vars[index++].Get(); (*out)[3][3] = vars[index++].Get(); } inline void MakeScalarValueImpl(GfQuatd *out, vector const &vars, size_t &index) { CHECK_BOUNDS(4, "Quatd"); // Values in order are re, i, j, k. GfVec3d imag; double re; MakeScalarValueImpl(&re, vars, index); out->SetReal(re); MakeScalarValueImpl(&imag, vars, index); out->SetImaginary(imag); } inline void MakeScalarValueImpl(GfQuatf *out, vector const &vars, size_t &index) { CHECK_BOUNDS(4, "Quatf"); // Values in order are re, i, j, k. GfVec3f imag; float re; MakeScalarValueImpl(&re, vars, index); out->SetReal(re); MakeScalarValueImpl(&imag, vars, index); out->SetImaginary(imag); } inline void MakeScalarValueImpl(GfQuath *out, vector const &vars, size_t &index) { CHECK_BOUNDS(4, "Quath"); // Values in order are re, i, j, k. GfVec3h imag; GfHalf re; MakeScalarValueImpl(&re, vars, index); out->SetReal(re); MakeScalarValueImpl(&imag, vars, index); out->SetImaginary(imag); } inline void MakeScalarValueImpl( SdfAssetPath *out, vector const &vars, size_t &index) { CHECK_BOUNDS(1, "asset"); *out = vars[index++].Get(); } template inline VtValue MakeScalarValueTemplate(vector const &, vector const &vars, size_t &index, string *errStrPtr) { T t; size_t origIndex = index; try { MakeScalarValueImpl(&t, vars, index); } catch (const boost::bad_get &) { *errStrPtr = TfStringPrintf("Failed to parse value (at sub-part %zd " "if there are multiple parts)", (index - origIndex) - 1); return VtValue(); } return VtValue(t); } template inline VtValue MakeShapedValueTemplate(vector const &shape, vector const &vars, size_t &index, string *errStrPtr) { if (shape.empty()) return VtValue(VtArray()); // TF_AXIOM(shape.size() == 1); unsigned int size = 1; TF_FOR_ALL(i, shape) size *= *i; VtArray array(size); size_t shapeIndex = 0; size_t origIndex = index; try { TF_FOR_ALL(i, array) { MakeScalarValueImpl(&(*i), vars, index); shapeIndex++; } } catch (const boost::bad_get &) { *errStrPtr = TfStringPrintf("Failed to parse at element %zd " "(at sub-part %zd if there are " "multiple parts)", shapeIndex, (index - origIndex) - 1); return VtValue(); } return VtValue(array); } typedef std::map _ValueFactoryMap; // Walk through types and register factories. struct _MakeFactoryMap { explicit _MakeFactoryMap(_ValueFactoryMap *factories) : _factories(factories) {} template void add(const SdfValueTypeName& scalar, const char* alias = NULL) { static const bool isShaped = true; const SdfValueTypeName array = scalar.GetArrayType(); const std::string scalarName = alias ? std::string(alias) : scalar.GetAsToken().GetString(); const std::string arrayName = alias ? std::string(alias) + "[]" : array.GetAsToken().GetString(); _ValueFactoryMap &f = *_factories; f[scalarName] = ValueFactory( scalarName, scalar.GetDimensions(), !isShaped, MakeScalarValueTemplate); f[arrayName] = ValueFactory( arrayName, array.GetDimensions(), isShaped, MakeShapedValueTemplate); } _ValueFactoryMap *_factories; }; TF_MAKE_STATIC_DATA(_ValueFactoryMap, _valueFactories) { _MakeFactoryMap builder(_valueFactories); // XXX: Would be better if SdfValueTypeName had a method to take // a vector of VtValues and return a VtValue holding the // appropriate C++ type (which mostly involves moving the // MakeScalarValueImpl functions into the value type name // registration code). Then we could do this: // for (const auto& typeName : SdfSchema::GetInstance().GetAllTypes()) { // builder(typeName); // } // For symmetry (and I think it would actually be useful // when converting usd into other formats) there should be // a method to convert a VtValue holding the appropriate C++ // type into a vector of VtValues holding a primitive type. // E.g. a VtValue holding a GfVec3f would return three // VtValues each holding a float. builder.add(SdfValueTypeNames->Bool); builder.add(SdfValueTypeNames->UChar); builder.add(SdfValueTypeNames->Int); builder.add(SdfValueTypeNames->UInt); builder.add(SdfValueTypeNames->Int64); builder.add(SdfValueTypeNames->UInt64); builder.add(SdfValueTypeNames->Half); builder.add(SdfValueTypeNames->Float); builder.add(SdfValueTypeNames->Double); builder.add(SdfValueTypeNames->TimeCode); builder.add(SdfValueTypeNames->String); builder.add(SdfValueTypeNames->Token); builder.add(SdfValueTypeNames->Asset); builder.add(SdfValueTypeNames->Int2); builder.add(SdfValueTypeNames->Half2); builder.add(SdfValueTypeNames->Float2); builder.add(SdfValueTypeNames->Double2); builder.add(SdfValueTypeNames->Int3); builder.add(SdfValueTypeNames->Half3); builder.add(SdfValueTypeNames->Float3); builder.add(SdfValueTypeNames->Double3); builder.add(SdfValueTypeNames->Int4); builder.add(SdfValueTypeNames->Half4); builder.add(SdfValueTypeNames->Float4); builder.add(SdfValueTypeNames->Double4); builder.add(SdfValueTypeNames->Point3h); builder.add(SdfValueTypeNames->Point3f); builder.add(SdfValueTypeNames->Point3d); builder.add(SdfValueTypeNames->Vector3h); builder.add(SdfValueTypeNames->Vector3f); builder.add(SdfValueTypeNames->Vector3d); builder.add(SdfValueTypeNames->Normal3h); builder.add(SdfValueTypeNames->Normal3f); builder.add(SdfValueTypeNames->Normal3d); builder.add(SdfValueTypeNames->Color3h); builder.add(SdfValueTypeNames->Color3f); builder.add(SdfValueTypeNames->Color3d); builder.add(SdfValueTypeNames->Color4h); builder.add(SdfValueTypeNames->Color4f); builder.add(SdfValueTypeNames->Color4d); builder.add(SdfValueTypeNames->Quath); builder.add(SdfValueTypeNames->Quatf); builder.add(SdfValueTypeNames->Quatd); builder.add(SdfValueTypeNames->Matrix2d); builder.add(SdfValueTypeNames->Matrix3d); builder.add(SdfValueTypeNames->Matrix4d); builder.add(SdfValueTypeNames->Frame4d); builder.add(SdfValueTypeNames->TexCoord2f); builder.add(SdfValueTypeNames->TexCoord2d); builder.add(SdfValueTypeNames->TexCoord2h); builder.add(SdfValueTypeNames->TexCoord3f); builder.add(SdfValueTypeNames->TexCoord3d); builder.add(SdfValueTypeNames->TexCoord3h); // XXX: Backwards compatibility. These should be removed when // all assets are updated. At the time of this writing // under pxr only assets used by usdImaging need updating. // Those assets must be moved anyway for open sourcing so // I'm leaving this for now. (Also note that at least one // of those tests, testUsdImagingEmptyMesh, uses the prim // type PxVolume which is not in pxr.) Usd assets outside // pxr must also be updated. builder.add(SdfValueTypeNames->Int2, "Vec2i"); builder.add(SdfValueTypeNames->Half2, "Vec2h"); builder.add(SdfValueTypeNames->Float2, "Vec2f"); builder.add(SdfValueTypeNames->Double2, "Vec2d"); builder.add(SdfValueTypeNames->Int3, "Vec3i"); builder.add(SdfValueTypeNames->Half3, "Vec3h"); builder.add(SdfValueTypeNames->Float3, "Vec3f"); builder.add(SdfValueTypeNames->Double3, "Vec3d"); builder.add(SdfValueTypeNames->Int4, "Vec4i"); builder.add(SdfValueTypeNames->Half4, "Vec4h"); builder.add(SdfValueTypeNames->Float4, "Vec4f"); builder.add(SdfValueTypeNames->Double4, "Vec4d"); builder.add(SdfValueTypeNames->Point3f, "PointFloat"); builder.add(SdfValueTypeNames->Point3d, "Point"); builder.add(SdfValueTypeNames->Vector3f, "NormalFloat"); builder.add(SdfValueTypeNames->Vector3d, "Normal"); builder.add(SdfValueTypeNames->Normal3f, "VectorFloat"); builder.add(SdfValueTypeNames->Normal3d, "Vector"); builder.add(SdfValueTypeNames->Color3f, "ColorFloat"); builder.add(SdfValueTypeNames->Color3d, "Color"); builder.add(SdfValueTypeNames->Quath, "Quath"); builder.add(SdfValueTypeNames->Quatf, "Quatf"); builder.add(SdfValueTypeNames->Quatd, "Quatd"); builder.add(SdfValueTypeNames->Matrix2d, "Matrix2d"); builder.add(SdfValueTypeNames->Matrix3d, "Matrix3d"); builder.add(SdfValueTypeNames->Matrix4d, "Matrix4d"); builder.add(SdfValueTypeNames->Frame4d, "Frame"); builder.add(SdfValueTypeNames->Matrix4d, "Transform"); builder.add(SdfValueTypeNames->Int, "PointIndex"); builder.add(SdfValueTypeNames->Int, "EdgeIndex"); builder.add(SdfValueTypeNames->Int, "FaceIndex"); builder.add(SdfValueTypeNames->Token, "Schema"); // Set up the special None factory. (*_valueFactories)[std::string("None")] = ValueFactory( std::string(""), SdfTupleDimensions(), false, NULL); } ValueFactory const &GetValueFactoryForMenvaName(std::string const &name, bool *found) { _ValueFactoryMap::const_iterator it = _valueFactories->find(name); if (it != _valueFactories->end()) { *found = true; return it->second; } // No factory for given name. static ValueFactory const& none = (*_valueFactories)[std::string("None")]; *found = false; return none; } } // namespace Sdf_ParserHelpers bool Sdf_BoolFromString( const std::string &str, bool *parseOk ) { if (parseOk) *parseOk = true; std::string s = str; std::transform(s.begin(), s.end(), s.begin(), ::tolower); if (strcmp(s.c_str(), "false") == 0) return false; if (strcmp(s.c_str(), "true") == 0) return true; if (strcmp(s.c_str(), "no") == 0) return false; if (strcmp(s.c_str(), "yes") == 0) return true; if (strcmp(s.c_str(), "0") == 0) return false; if (strcmp(s.c_str(), "1") == 0) return true; if (parseOk) *parseOk = false; return true; } std::string Sdf_EvalQuotedString(const char* x, size_t n, size_t trimBothSides, unsigned int* numLines) { std::string ret; // Handle empty strings if (n <= 2 * trimBothSides) return ret; n -= 2 * trimBothSides; // Use local buf, or malloc one if not enough space. // (this is a little too much if there are escape chars in the string, // but we can live with it to avoid traversing the string twice) static const size_t LocalSize = 128; char localBuf[LocalSize]; char *buf = n <= LocalSize ? localBuf : (char *)malloc(n); char *s = buf; for (const char *p = x + trimBothSides, *end = x + trimBothSides + n; p != end; ++p) { if (*p != '\\') { *s++ = *p; } else { TfEscapeStringReplaceChar(&p, &s); } } // Trim to final length. std::string(buf, s-buf).swap(ret); if (buf != localBuf) free(buf); if (numLines) *numLines = std::count(ret.begin(), ret.end(), '\n'); return ret; } std::string Sdf_EvalAssetPath(const char* x, size_t n, bool tripleDelimited) { // See _StringFromAssetPath for the code that writes asset paths. // Asset paths are assumed to only contain printable characters and // no escape sequences except for the "@@@" delimiter. size_t numDelimiters = tripleDelimited ? 3 : 1; std::string ret(x + numDelimiters, n - (2 * numDelimiters)); if (tripleDelimited) { ret = TfStringReplace(ret, "\\@@@", "@@@"); } return ret; } PXR_NAMESPACE_CLOSE_SCOPE