QList<Skin> SkinFactory::installedSkins() const { QList<Skin> skins; bool skin_load_ok; const QStringList skin_directories = QDir(APP_SKIN_PATH).entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Readable); foreach (const QString &base_directory, skin_directories) { // Check skins installed in this base directory. const QStringList skin_files = QDir(APP_SKIN_PATH + QDir::separator() + base_directory).entryList(QStringList() << QSL("*.xml"), QDir::Files | QDir::Readable | QDir::NoDotAndDotDot | QDir::NoSymLinks); foreach (const QString &skin_file, skin_files) { // Check if skin file is valid and add it if it is valid. const Skin skin_info = skinInfo(base_directory + QDir::separator() + skin_file, &skin_load_ok); if (skin_load_ok) { skins.append(skin_info); } } } return skins; }
void SkinFactory::loadCurrentSkin() { QList<QString> skin_names_to_try; skin_names_to_try.append(selectedSkinName()); skin_names_to_try.append(APP_SKIN_DEFAULT); bool skin_parsed; Skin skin_data; QString skin_name; while (!skin_names_to_try.isEmpty()) { skin_name = skin_names_to_try.takeFirst(); skin_data = skinInfo(skin_name, &skin_parsed); if (skin_parsed) { loadSkinFromData(skin_data); // Set this 'Skin' object as active one. m_currentSkin = skin_data; qDebug("Skin '%s' loaded.", qPrintable(skin_name)); return; } else { qWarning("Failed to load skin '%s'.", qPrintable(skin_name)); } } qCritical("Failed to load selected or default skin. Quitting!"); }
BabylonMesh::BabylonMesh(BabylonNode* node) : BabylonAbstractMesh(node), _isEnabled(true), _isVisible(true), _billboardMode(0), _visibility(1), _skeletonId(-1), _pickable(true), _hasVertexAlpha(false), _checkCollision(false), _receiveShadows(false), _infiniteDistance(false), _autoAnimate(false), _autoAnimateFrom(0), _autoAnimateTo(0), _autoAnimateLoop(false), _showBoundingBox(false), _showSubMeshesBoundingBox(false), _applyFog(false), _alphaIndex(0) { pivotMatrix.SetIdentity(); auto fbxNode = node->fbxNode(); std::string ansiName = fbxNode->GetName(); name(std::wstring(ansiName.begin(), ansiName.end())); id(getNodeId(fbxNode)); auto parent = fbxNode->GetParent(); if (parent) { parentId(getNodeId(parent)); } pivotMatrix = ConvertToBabylonCoordinateSystem( GetGeometryTransformation(fbxNode)); auto animStack = fbxNode->GetScene()->GetSrcObject<FbxAnimStack>(0); FbxString animStackName = animStack->GetName(); FbxTakeInfo* takeInfo = fbxNode->GetScene()->GetTakeInfo(animStackName); auto animTimeMode = GlobalSettings::Current().AnimationsTimeMode; auto animFrameRate = GlobalSettings::Current().AnimationsFrameRate(); auto startFrame = takeInfo->mLocalTimeSpan.GetStart().GetFrameCount(animTimeMode); auto endFrame = takeInfo->mLocalTimeSpan.GetStop().GetFrameCount(animTimeMode); auto animLengthInFrame = endFrame - startFrame + 1; _visibility = static_cast<float>(node->fbxNode()->Visibility.Get()); auto posAnim = std::make_shared<BabylonAnimation<babylon_vector3>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"position", L"position", true, 0, static_cast<int>(animLengthInFrame), true); auto rotAnim = std::make_shared<BabylonAnimation<babylon_vector4>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"rotationQuaternion", L"rotationQuaternion", true, 0, static_cast<int>(animLengthInFrame), true); auto scaleAnim = std::make_shared<BabylonAnimation<babylon_vector3>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"scaling", L"scaling", true, 0, static_cast<int>(animLengthInFrame), true); auto visibilityAnim = std::make_shared<BabylonAnimation<float>>(BabylonAnimationBase::loopBehavior_Cycle, static_cast<int>(animFrameRate), L"visibility", L"visibility", true, 0, static_cast<int>(animLengthInFrame), true); auto mesh = fbxNode->GetMesh(); _isVisible = fbxNode->Show.Get(); auto rotCurveNode = fbxNode->LclRotation.GetCurveNode(); auto translateCurveNode = fbxNode->LclTranslation.GetCurveNode(); auto scalingCurveNode = fbxNode->LclScaling.GetCurveNode(); auto visibilityCurveNode = fbxNode->Visibility.GetCurveNode(); if (rotCurveNode || translateCurveNode || scalingCurveNode) { for (auto ix = 0; ix < animLengthInFrame; ix++) { FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); babylon_animation_key<babylon_vector3> poskey; babylon_animation_key<babylon_vector4> rotkey; babylon_animation_key<babylon_vector3> scalekey; poskey.frame = ix; rotkey.frame = ix; scalekey.frame = ix; auto currTransform = node->GetLocal(currTime); poskey.values = currTransform.translation(); rotkey.values = currTransform.rotationQuaternion(); scalekey.values = currTransform.scaling(); posAnim->appendKey(poskey); rotAnim->appendKey(rotkey); scaleAnim->appendKey(scalekey); } } if (visibilityCurveNode) { for (auto ix = 0; ix < animLengthInFrame; ix++) { FbxTime currTime; currTime.SetFrame(startFrame + ix, animTimeMode); babylon_animation_key<float> visibilityKey; visibilityKey.frame = ix; visibilityKey.values = static_cast<float>(node->fbxNode()->Visibility.EvaluateValue(currTime)); visibilityAnim->appendKey(visibilityKey); } } if (!posAnim->isConstant()){ animations.push_back(posAnim); } if (!rotAnim->isConstant()){ animations.push_back(rotAnim); } if (!scaleAnim->isConstant()){ animations.push_back(scaleAnim); } if (!visibilityAnim->isConstant()) { animations.push_back(visibilityAnim); } if (!mesh) { return; } if (mesh->GetPolygonCount() == 0){ return; } _receiveShadows = mesh->ReceiveShadow.Get(); FbxGeometryConverter conv(mesh->GetFbxManager()); conv.ComputePolygonSmoothingFromEdgeSmoothing(mesh); if (!mesh->IsTriangleMesh()) { mesh = (FbxMesh*) conv.Triangulate(mesh, true); } mesh->RemoveBadPolygons(); mesh->GenerateNormals(); FbxStringList uvSetNameList; mesh->GetUVSetNames(uvSetNameList); std::vector<std::string> uniqueUVSets; int uvCount = uvSetNameList.GetCount(); for (int i = 0; i < uvCount; ++i) { std::string value = uvSetNameList.GetStringAt(i); if (std::find(uniqueUVSets.begin(), uniqueUVSets.end(), value) == uniqueUVSets.end()) { uniqueUVSets.push_back(value); } } uvsets = uniqueUVSets; bool hasUv = uniqueUVSets.size() > 0; bool hasUv2 = uniqueUVSets.size() > 1; bool hasUv3 = uniqueUVSets.size() > 2; bool hasUv4 = uniqueUVSets.size() > 3; bool hasUv5 = uniqueUVSets.size() > 4; bool hasUv6 = uniqueUVSets.size() > 5; std::string uvSetName; std::string uv2SetName; std::string uv3SetName; std::string uv4SetName; std::string uv5SetName; std::string uv6SetName; if (hasUv) { uvSetName = uniqueUVSets[0]; } if (hasUv2) { uv2SetName = uniqueUVSets[1]; } if (hasUv3) { uv3SetName = uniqueUVSets[2]; } if (hasUv4) { uv4SetName = uniqueUVSets[3]; } if (hasUv5) { uv5SetName = uniqueUVSets[4]; } if (hasUv6) { uv6SetName = uniqueUVSets[5]; } auto colors = mesh->GetElementVertexColor(); FbxLayerElement::EMappingMode colorMappingMode; FbxLayerElement::EReferenceMode colorReferenceMode; if (colors) { colorMappingMode = colors->GetMappingMode(); colorReferenceMode = colors->GetReferenceMode(); } auto normals = mesh->GetElementNormal(); FbxGeometryElementUV* uvs = nullptr; FbxGeometryElementUV* uvs2 = nullptr; FbxGeometryElementUV* uvs3 = nullptr; FbxGeometryElementUV* uvs4 = nullptr; FbxGeometryElementUV* uvs5 = nullptr; FbxGeometryElementUV* uvs6 = nullptr; FbxLayerElement::EMappingMode uvsMappingMode; FbxLayerElement::EReferenceMode uvsReferenceMode; FbxLayerElement::EMappingMode uvs2MappingMode; FbxLayerElement::EReferenceMode uvs2ReferenceMode; FbxLayerElement::EMappingMode uvs3MappingMode; FbxLayerElement::EReferenceMode uvs3ReferenceMode; FbxLayerElement::EMappingMode uvs4MappingMode; FbxLayerElement::EReferenceMode uvs4ReferenceMode; FbxLayerElement::EMappingMode uvs5MappingMode; FbxLayerElement::EReferenceMode uvs5ReferenceMode; FbxLayerElement::EMappingMode uvs6MappingMode; FbxLayerElement::EReferenceMode uvs6ReferenceMode; if (hasUv) { uvs = mesh->GetElementUV(uvSetName.c_str()); uvsMappingMode = uvs->GetMappingMode(); uvsReferenceMode = uvs->GetReferenceMode(); } if (hasUv2) { uvs2 = mesh->GetElementUV(uv2SetName.c_str()); uvs2MappingMode = uvs2->GetMappingMode(); uvs2ReferenceMode = uvs2->GetReferenceMode(); } if (hasUv3) { uvs3 = mesh->GetElementUV(uv3SetName.c_str()); uvs3MappingMode = uvs3->GetMappingMode(); uvs3ReferenceMode = uvs3->GetReferenceMode(); } if (hasUv4) { uvs4 = mesh->GetElementUV(uv4SetName.c_str()); uvs4MappingMode = uvs4->GetMappingMode(); uvs4ReferenceMode = uvs4->GetReferenceMode(); } if (hasUv5) { uvs5 = mesh->GetElementUV(uv5SetName.c_str()); uvs5MappingMode = uvs5->GetMappingMode(); uvs5ReferenceMode = uvs5->GetReferenceMode(); } if (hasUv6) { uvs6 = mesh->GetElementUV(uv6SetName.c_str()); uvs6MappingMode = uvs6->GetMappingMode(); uvs6ReferenceMode = uvs6->GetReferenceMode(); } auto normalMappingMode = normals->GetMappingMode(); auto normalReferenceMode = normals->GetReferenceMode(); std::vector<SubmeshData> submeshes; auto materialCount = node->fbxNode()->GetMaterialCount(); if (materialCount == 0) { materialCount = 1; } submeshes.resize(materialCount); auto baseLayer = mesh->GetLayer(0); auto materials = baseLayer->GetMaterials(); FbxLayerElement::EMappingMode materialMappingMode = materials ? materials->GetMappingMode() : FbxLayerElement::eByPolygon; // extract deformers SkinInfo skinInfo(fbxNode); if (skinInfo.hasSkin()){ associatedSkeleton = std::make_shared<BabylonSkeleton>(); skinInfo.buildBabylonSkeleton(*associatedSkeleton); } auto triangleCount = mesh->GetPolygonCount(); for (int triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex) { int materialIndex = 0; if (materialCount > 0 && materials) { switch (materialMappingMode) { case FbxLayerElement::eAllSame: materialIndex = materials->GetIndexArray().GetAt(0); break; case FbxLayerElement::eByPolygon: materialIndex = materials->GetIndexArray().GetAt(triangleIndex); } } auto& submesh = submeshes[materialIndex]; triangle t; for (int cornerIndex = 0; cornerIndex < 3; ++cornerIndex) { auto controlPointIndex = mesh->GetPolygonVertex(triangleIndex, cornerIndex); auto vertexIndex = triangleIndex * 3 + cornerIndex; auto position = mesh->GetControlPoints()[controlPointIndex]; position[2] = -position[2]; BabylonVertex v; v.position = position; if (normals) { int normalMapIndex = (normalMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int normalValueIndex = (normalReferenceMode == FbxLayerElement::eDirect) ? normalMapIndex : normals->GetIndexArray().GetAt(normalMapIndex); v.normal = normals->GetDirectArray().GetAt(normalValueIndex); v.normal.z = -v.normal.z; } if (colors) { int mappingIndex = (colorMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (colorReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : colors->GetIndexArray().GetAt(mappingIndex); v.color = colors->GetDirectArray().GetAt(valueIndex); } if (uvs) { int mappingIndex = (uvsMappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvsReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs->GetIndexArray().GetAt(mappingIndex); v.uv = uvs->GetDirectArray().GetAt(valueIndex); //v.uv.y = 1 - v.uv.y; } if (uvs2) { int mappingIndex = (uvs2MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs2ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs2->GetIndexArray().GetAt(mappingIndex); v.uv2 = uvs2->GetDirectArray().GetAt(valueIndex); } if (uvs3) { int mappingIndex = (uvs3MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs3ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs3->GetIndexArray().GetAt(mappingIndex); v.uv3 = uvs3->GetDirectArray().GetAt(valueIndex); } if (uvs4) { int mappingIndex = (uvs4MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs4ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs4->GetIndexArray().GetAt(mappingIndex); v.uv4 = uvs4->GetDirectArray().GetAt(valueIndex); } if (uvs5) { int mappingIndex = (uvs5MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs5ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs5->GetIndexArray().GetAt(mappingIndex); v.uv5 = uvs5->GetDirectArray().GetAt(valueIndex); } if (uvs6) { int mappingIndex = (uvs6MappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : vertexIndex; int valueIndex = (uvs6ReferenceMode == FbxLayerElement::eDirect) ? mappingIndex : uvs6->GetIndexArray().GetAt(mappingIndex); v.uv6 = uvs6->GetDirectArray().GetAt(valueIndex); } if (skinInfo.hasSkin()){ auto& skinData = skinInfo.controlPointBoneIndicesAndWeights(controlPointIndex); for (auto boneix = 0; boneix < skinData.size()&&boneix<4; ++boneix){ v.boneIndices[boneix] = skinData[boneix].index; v.boneWeights[boneix] = static_cast<float>(skinData[boneix].weight); } for (auto boneix = skinData.size(); boneix < 4; ++boneix){ v.boneIndices[boneix] = skinInfo.bonesCount(); v.boneWeights[boneix] = 0; } } auto foundVertex = submesh.knownVertices.find(v); if (foundVertex != submesh.knownVertices.end()) { //submesh.indices.push_back(foundVertex->second); t.indices[cornerIndex] = foundVertex->second; } else { auto index = static_cast<int>(submesh.vertices.size()); submesh.vertices.push_back(v); //submesh.indices.push_back(index); submesh.knownVertices[v] = index; t.indices[cornerIndex] = index; } } if (submesh.knownTriangles.insert(t).second) { submesh.indices.push_back(t.indices[0]); submesh.indices.push_back(t.indices[1]); submesh.indices.push_back(t.indices[2]); } else { std::cout << "duplicate triangle found (and eliminated) in " << fbxNode->GetName() << std::endl; } } std::uint32_t vertexOffset = 0; for (auto matIndex = 0u; matIndex < submeshes.size(); ++matIndex) { auto& submesh = submeshes[matIndex]; BabylonSubmesh babsubmesh; babsubmesh.indexCount = static_cast<int>(submesh.indices.size()); babsubmesh.indexStart = static_cast<int>(_indices.size()); babsubmesh.materialIndex = matIndex; babsubmesh.verticesCount = static_cast<int>(submesh.vertices.size()); babsubmesh.verticesStart = static_cast<int>(_positions.size()); for (auto& v : submesh.vertices) { _positions.push_back(v.position); if (normals) { _normals.push_back(v.normal); } if (colors) { _colors.push_back(v.color); } if (uvs) { _uvs.push_back(v.uv); } if (uvs2) { _uvs2.push_back(v.uv2); } if (uvs3) { _uvs3.push_back(v.uv3); } if (uvs4) { _uvs4.push_back(v.uv4); } if (uvs5) { _uvs5.push_back(v.uv5); } if (uvs6) { _uvs6.push_back(v.uv6); } if (skinInfo.hasSkin()){ float weight0 = v.boneWeights[0]; float weight1 = v.boneWeights[1]; float weight2 = v.boneWeights[2]; int bone0 = v.boneIndices[0]; int bone1 = v.boneIndices[1]; int bone2 = v.boneIndices[2]; int bone3 = v.boneIndices[3]; _boneWeights.push_back(babylon_vector4( weight0, weight1, weight2, 1.0f - weight0 - weight1 - weight2)); _boneIndices.push_back((bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0); } } for (auto i : submesh.indices) { _indices.push_back(i + vertexOffset); } vertexOffset = static_cast<int>(_positions.size()); _submeshes.push_back(babsubmesh); } }
//@{ void* Interface_GUIWindow::create(void* kodiBase, const char* xml_filename, const char* default_skin, bool as_dialog, bool is_media) { CAddonDll* addon = static_cast<CAddonDll*>(kodiBase); if (!addon || !xml_filename || !default_skin) { CLog::Log(LOGERROR, "Interface_GUIWindow::%s - invalid handler data (xml_filename='%p', default_skin='%p') on addon '%s'", __FUNCTION__, xml_filename, default_skin, addon ? addon->ID().c_str() : "unknown"); return nullptr; } if (as_dialog && is_media) { CLog::Log(LOGWARNING, "Interface_GUIWindow::%s: %s/%s - addon tries to create dialog as media window who not allowed, contact Developer '%s' of this addon", __FUNCTION__, CAddonInfo::TranslateType(addon->Type()).c_str(), addon->Name().c_str(), addon->Author().c_str()); } RESOLUTION_INFO res; std::string strSkinPath = g_SkinInfo->GetSkinPath(xml_filename, &res); if (!XFILE::CFile::Exists(strSkinPath)) { std::string str("none"); ADDON::CAddonInfo addonInfo(str, ADDON::ADDON_SKIN); // Check for the matching folder for the skin in the fallback skins folder std::string fallbackPath = URIUtils::AddFileToFolder(addon->Path(), "resources", "skins"); std::string basePath = URIUtils::AddFileToFolder(fallbackPath, g_SkinInfo->ID()); strSkinPath = g_SkinInfo->GetSkinPath(xml_filename, &res, basePath); // Check for the matching folder for the skin in the fallback skins folder (if it exists) if (XFILE::CFile::Exists(basePath)) { addonInfo.SetPath(basePath); ADDON::CSkinInfo skinInfo(addonInfo, res); skinInfo.Start(); strSkinPath = skinInfo.GetSkinPath(xml_filename, &res); } if (!XFILE::CFile::Exists(strSkinPath)) { // Finally fallback to the DefaultSkin as it didn't exist in either the Kodi Skin folder or the fallback skin folder addonInfo.SetPath(URIUtils::AddFileToFolder(fallbackPath, default_skin)); ADDON::CSkinInfo skinInfo(addonInfo, res); skinInfo.Start(); strSkinPath = skinInfo.GetSkinPath(xml_filename, &res); if (!XFILE::CFile::Exists(strSkinPath)) { CLog::Log(LOGERROR, "Interface_GUIWindow::%s: %s/%s - XML File '%s' for Window is missing, contact Developer '%s' of this addon", __FUNCTION__, CAddonInfo::TranslateType(addon->Type()).c_str(), addon->Name().c_str(), strSkinPath.c_str(), addon->Author().c_str()); return nullptr; } } } int id = GetNextAvailableWindowId(); if (id < 0) return nullptr; CGUIWindow *window; if (!as_dialog) window = new CGUIAddonWindow(id, strSkinPath, addon, is_media); else window = new CGUIAddonWindowDialog(id, strSkinPath, addon); Interface_GUIGeneral::lock(); g_windowManager.Add(window); Interface_GUIGeneral::unlock(); if (!dynamic_cast<CGUIWindow*>(g_windowManager.GetWindow(id))) { CLog::Log(LOGERROR, "Interface_GUIWindow::%s - Requested window id '%i' does not exist for addon '%s'", __FUNCTION__, id, addon->ID().c_str()); delete window; return nullptr; } window->SetCoordsRes(res); return window; }
PyObject* WindowXML_New(PyTypeObject *type, PyObject *args, PyObject *kwds) { WindowXML *self; self = (WindowXML*)type->tp_alloc(type, 0); if (!self) return NULL; new(&self->sXMLFileName) string(); new(&self->sFallBackPath) string(); new(&self->vecControls) std::vector<Control*>(); self->iWindowId = -1; PyObject* pyOXMLname = NULL; PyObject* pyOname = NULL; PyObject* pyDName = NULL; PyObject* pyRes = NULL; string strXMLname, strFallbackPath; string strDefault = "Default"; string resolution = "720p"; if (!PyArg_ParseTuple(args, (char*)"OO|OO", &pyOXMLname, &pyOname, &pyDName, &pyRes)) return NULL; PyXBMCGetUnicodeString(strXMLname, pyOXMLname); PyXBMCGetUnicodeString(strFallbackPath, pyOname); if (pyDName) PyXBMCGetUnicodeString(strDefault, pyDName); if (pyRes) PyXBMCGetUnicodeString(resolution, pyRes); // Check to see if the XML file exists in current skin. If not use fallback path to find a skin for the script RESOLUTION_INFO res; CStdString strSkinPath = g_SkinInfo->GetSkinPath(strXMLname, &res); if (!XFILE::CFile::Exists(strSkinPath)) { // Check for the matching folder for the skin in the fallback skins folder CStdString fallbackPath = URIUtils::AddFileToFolder(strFallbackPath, "resources"); fallbackPath = URIUtils::AddFileToFolder(fallbackPath, "skins"); CStdString basePath = URIUtils::AddFileToFolder(fallbackPath, g_SkinInfo->ID()); strSkinPath = g_SkinInfo->GetSkinPath(strXMLname, &res, basePath); if (!XFILE::CFile::Exists(strSkinPath)) { // Finally fallback to the DefaultSkin as it didn't exist in either the XBMC Skin folder or the fallback skin folder CStdString str("none"); AddonProps props(str, ADDON_SKIN, "", ""); props.path = URIUtils::AddFileToFolder(fallbackPath, strDefault); CSkinInfo::TranslateResolution(resolution, res); CSkinInfo skinInfo(props, res); skinInfo.Start(); strSkinPath = skinInfo.GetSkinPath(strXMLname, &res); if (!XFILE::CFile::Exists(strSkinPath)) { PyErr_SetString(PyExc_TypeError, "XML File for Window is missing"); return NULL; } } } self->sFallBackPath = strFallbackPath; self->sXMLFileName = strSkinPath; self->bUsingXML = true; // create new GUIWindow if (!Window_CreateNewWindow((Window*)self, false)) { // error is already set by Window_CreateNewWindow, just release the memory self->vecControls.clear(); self->vecControls.~vector(); self->sFallBackPath.~string(); self->sXMLFileName.~string(); self->ob_type->tp_free((PyObject*)self); return NULL; } ((CGUIWindow*)(self->pWindow))->SetCoordsRes(res); return (PyObject*)self; }