/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include "mediamisc.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_FEATURE_COLLADA #include #include #endif #include #include using namespace ::com::sun::star; using namespace boost::property_tree; namespace avmedia { #if HAVE_FEATURE_COLLADA static void lcl_UnzipKmz(const OUString& rSourceURL, const OUString& rOutputFolderURL, OUString& o_rDaeFileURL) { o_rDaeFileURL.clear(); uno::Reference xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getProcessComponentContext(), rSourceURL); uno::Sequence< OUString > aNames = xNameAccess->getElementNames(); for( sal_Int32 i = 0; i < aNames.getLength(); ++i ) { const OUString sCopy = rOutputFolderURL + "/" + aNames[i]; if( aNames[i].endsWithIgnoreAsciiCase(".dae") ) o_rDaeFileURL = sCopy; uno::Reference xInputStream(xNameAccess->getByName(aNames[i]), uno::UNO_QUERY); ::ucbhelper::Content aCopyContent(sCopy, uno::Reference(), comphelper::getProcessComponentContext()); aCopyContent.writeStream(xInputStream, true); } } bool KmzDae2Gltf(const OUString& rSourceURL, OUString& o_rOutput) { o_rOutput.clear(); const bool bIsDAE = rSourceURL.endsWithIgnoreAsciiCase(".dae"); const bool bIsKMZ = rSourceURL.endsWithIgnoreAsciiCase(".kmz"); if( !bIsDAE && !bIsKMZ ) { SAL_WARN("avmedia.opengl", "KmzDae2Gltf converter got a file with wrong extension\n" << rSourceURL); return false; } // Create a temporary folder for conversion OUString sOutput; osl::FileBase::getFileURLFromSystemPath(::utl::TempFile::CreateTempName(), sOutput); // remove .tmp extension sOutput = sOutput.copy(0, sOutput.getLength()-4); std::shared_ptr asset(new GLTF::GLTFAsset()); asset->setBundleOutputPath(OUStringToOString( sOutput, RTL_TEXTENCODING_UTF8 ).getStr()); // If *.dae file is not in the local file system, then copy it to a temp folder for the conversion OUString sInput = rSourceURL; const INetURLObject aSourceURLObj(rSourceURL); if( aSourceURLObj.GetProtocol() != INetProtocol::File ) { try { ::ucbhelper::Content aSourceContent(rSourceURL, uno::Reference(), comphelper::getProcessComponentContext()); const OUString sTarget = sOutput + "/" + GetFilename(rSourceURL); ::ucbhelper::Content aTempContent(sTarget, uno::Reference(), comphelper::getProcessComponentContext()); aTempContent.writeStream(aSourceContent.openStream(), true); sInput = sTarget; } catch (const uno::Exception&) { SAL_WARN("avmedia.opengl", "Exception while trying to copy source file to the temp folder for conversion:\n" << sInput); return false; } } asset->setInputFilePath(OUStringToOString( sInput, RTL_TEXTENCODING_UTF8 ).getStr()); if (bIsKMZ) { OUString sDaeFilePath; lcl_UnzipKmz(sInput, sOutput, sDaeFilePath); if ( sDaeFilePath.isEmpty() ) { SAL_WARN("avmedia.opengl", "Cannot find dae file in kmz:\n" << rSourceURL); return false; } asset->setInputFilePath(OUStringToOString( sDaeFilePath, RTL_TEXTENCODING_UTF8 ).getStr()); } GLTF::COLLADA2GLTFWriter writer(asset); writer.write(); // Path to the .json file created by COLLADA2GLTFWriter o_rOutput = sOutput + "/" + GetFilename(sOutput) + ".json"; return true; } #endif static void lcl_EmbedExternals(const OUString& rSourceURL, uno::Reference xSubStorage, ::ucbhelper::Content& rContent) { // Create a temp file with which json parser can work. OUString sTempFileURL; const ::osl::FileBase::RC aErr = ::osl::FileBase::createTempFile(0, 0, &sTempFileURL); if (::osl::FileBase::E_None != aErr) { SAL_WARN("avmedia.opengl", "Cannot create temp file"); return; } try { // Write json content to the temp file ::ucbhelper::Content aTempContent(sTempFileURL, uno::Reference(), comphelper::getProcessComponentContext()); aTempContent.writeStream(rContent.openStream(), true); } catch (uno::Exception const& e) { SAL_WARN("avmedia.opengl", "Exception: '" << e.Message << "'"); return; } // Convert URL to a file path for loading const INetURLObject aURLObj(sTempFileURL); std::string sUrl = OUStringToOString( aURLObj.getFSysPath(INetURLObject::FSYS_DETECT), RTL_TEXTENCODING_UTF8 ).getStr(); // Parse json, read externals' URI and modify this relative URI's so they remain valid in the new context. std::vector vExternals; ptree aTree; try { json_parser::read_json( sUrl, aTree ); // Buffers for geometry and animations for( ptree::value_type &rVal : aTree.get_child("buffers") ) { const std::string sBufferUri(rVal.second.get("path")); vExternals.push_back(sBufferUri); // Change path: make it contain only a file name aTree.put("buffers." + rVal.first + ".path.",sBufferUri.substr(sBufferUri.find_last_of('/')+1)); } // Images for textures boost::optional< ptree& > aImages = aTree.get_child_optional("images"); if( aImages ) { for( ptree::value_type &rVal : aImages.get() ) { const std::string sImageUri(rVal.second.get("path")); if( !sImageUri.empty() ) { vExternals.push_back(sImageUri); // Change path: make it contain only a file name aTree.put("images." + rVal.first + ".path.",sImageUri.substr(sImageUri.find_last_of('/')+1)); } } } // Shaders (contains names only) for( ptree::value_type &rVal : aTree.get_child("programs") ) { vExternals.push_back(rVal.second.get("fragmentShader") + ".glsl"); vExternals.push_back(rVal.second.get("vertexShader") + ".glsl"); } // Write out modified json json_parser::write_json( sUrl, aTree ); } catch ( boost::exception const& ) { SAL_WARN("avmedia.opengl", "Exception while parsing *.json file"); return; } // Reload json with modified path to external resources rContent = ::ucbhelper::Content(sTempFileURL, uno::Reference(), comphelper::getProcessComponentContext()); // Store all external files next to the json file for( std::vector::iterator aCIter = vExternals.begin(); aCIter != vExternals.end(); ++aCIter ) { const OUString sAbsURL = INetURLObject::GetAbsURL(rSourceURL,OUString::createFromAscii(aCIter->c_str())); ::ucbhelper::Content aContent(sAbsURL, uno::Reference(), comphelper::getProcessComponentContext()); uno::Reference const xStream( CreateStream(xSubStorage, GetFilename(sAbsURL)), uno::UNO_SET_THROW); uno::Reference const xOutStream( xStream->getOutputStream(), uno::UNO_SET_THROW); if (!aContent.openStream(xOutStream)) { SAL_WARN("avmedia.opengl", "openStream to storage failed"); return; } } } bool Embed3DModel( const uno::Reference& xModel, const OUString& rSourceURL, OUString& o_rEmbeddedURL) { OUString sSource = rSourceURL; #if HAVE_FEATURE_COLLADA if( !rSourceURL.endsWithIgnoreAsciiCase(".json") ) KmzDae2Gltf(rSourceURL, sSource); #endif try { ::ucbhelper::Content aSourceContent(sSource, uno::Reference(), comphelper::getProcessComponentContext()); // Base storage uno::Reference const xSBD(xModel, uno::UNO_QUERY_THROW); uno::Reference const xStorage( xSBD->getDocumentStorage(), uno::UNO_QUERY_THROW); // Model storage const OUString sModel("Models"); uno::Reference const xModelStorage( xStorage->openStorageElement(sModel, embed::ElementModes::WRITE)); // Own storage of the corresponding model const OUString sFilename(GetFilename(sSource)); const OUString sGLTFDir(sFilename.copy(0,sFilename.lastIndexOf('.'))); uno::Reference const xSubStorage( xModelStorage->openStorageElement(sGLTFDir, embed::ElementModes::WRITE)); // Embed external resources lcl_EmbedExternals(sSource, xSubStorage, aSourceContent); // Save model file (.json) uno::Reference const xStream( CreateStream(xSubStorage, sFilename), uno::UNO_SET_THROW); uno::Reference const xOutStream( xStream->getOutputStream(), uno::UNO_SET_THROW); if (!aSourceContent.openStream(xOutStream)) { SAL_WARN("avmedia.opengl", "openStream to storage failed"); return false; } const uno::Reference xSubTransaction(xSubStorage, uno::UNO_QUERY); if (xSubTransaction.is()) { xSubTransaction->commit(); } const uno::Reference xModelTransaction(xModelStorage, uno::UNO_QUERY); if (xModelTransaction.is()) { xModelTransaction->commit(); } const uno::Reference xTransaction(xStorage, uno::UNO_QUERY); if (xTransaction.is()) { xTransaction->commit(); } o_rEmbeddedURL = "vnd.sun.star.Package:" + sModel + "/" + sGLTFDir + "/" + sFilename; return true; } catch (uno::Exception const&) { SAL_WARN("avmedia.opengl", "Exception while trying to embed model"); } return false; } bool IsModel(const OUString& rMimeType) { return rMimeType == AVMEDIA_MIMETYPE_JSON; } } // namespace avemdia /* vim:set shiftwidth=4 softtabstop=4 expandtab: */