Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / avmedia / source / framework / modeltools.cxx
blob03d4647849de9605a0e130f7e10977e369ee1027
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <avmedia/modeltools.hxx>
11 #include <avmedia/mediaitem.hxx>
12 #include "mediamisc.hxx"
14 #include <com/sun/star/embed/ElementModes.hpp>
15 #include <com/sun/star/embed/XTransactedObject.hpp>
16 #include <com/sun/star/document/XStorageBasedDocument.hpp>
17 #include <com/sun/star/embed/XStorage.hpp>
18 #include <com/sun/star/packages/zip/ZipFileAccess.hpp>
19 #include <osl/file.hxx>
20 #include <comphelper/processfactory.hxx>
21 #include <tools/urlobj.hxx>
22 #include <ucbhelper/content.hxx>
23 #include <unotools/tempfile.hxx>
24 #include <unotools/ucbstreamhelper.hxx>
26 #include <boost/property_tree/ptree.hpp>
27 #include <boost/property_tree/json_parser.hpp>
28 #include <boost/optional.hpp>
30 #include <config_features.h>
32 #if HAVE_FEATURE_COLLADA
33 #include <collada_headers.hxx>
34 #include <GLTFAsset.h>
35 #endif
37 #include <string>
38 #include <vector>
40 using namespace ::com::sun::star;
41 using namespace boost::property_tree;
43 namespace avmedia {
45 #if HAVE_FEATURE_COLLADA
47 static void lcl_UnzipKmz(const OUString& rSourceURL, const OUString& rOutputFolderURL, OUString& o_rDaeFileURL)
49 o_rDaeFileURL.clear();
50 uno::Reference<packages::zip::XZipFileAccess2> xNameAccess =
51 packages::zip::ZipFileAccess::createWithURL(comphelper::getProcessComponentContext(), rSourceURL);
52 uno::Sequence< OUString > aNames = xNameAccess->getElementNames();
53 for( sal_Int32 i = 0; i < aNames.getLength(); ++i )
55 const OUString sCopy = rOutputFolderURL + "/" + aNames[i];
56 if( aNames[i].endsWithIgnoreAsciiCase(".dae") )
57 o_rDaeFileURL = sCopy;
59 uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName(aNames[i]), uno::UNO_QUERY);
61 ::ucbhelper::Content aCopyContent(sCopy,
62 uno::Reference<ucb::XCommandEnvironment>(),
63 comphelper::getProcessComponentContext());
65 aCopyContent.writeStream(xInputStream, true);
70 bool KmzDae2Gltf(const OUString& rSourceURL, OUString& o_rOutput)
72 o_rOutput.clear();
73 const bool bIsDAE = rSourceURL.endsWithIgnoreAsciiCase(".dae");
74 const bool bIsKMZ = rSourceURL.endsWithIgnoreAsciiCase(".kmz");
75 if( !bIsDAE && !bIsKMZ )
77 SAL_WARN("avmedia.opengl", "KmzDae2Gltf converter got a file with wrong extension " << rSourceURL);
78 return false;
81 // Create a temporary folder for conversion
82 OUString sOutput;
83 osl::FileBase::getFileURLFromSystemPath(::utl::TempFile::CreateTempName(), sOutput);
84 // remove .tmp extension
85 sOutput = sOutput.copy(0, sOutput.getLength()-4);
87 std::shared_ptr <GLTF::GLTFAsset> asset(new GLTF::GLTFAsset());
88 asset->setBundleOutputPath(OUStringToOString( sOutput, RTL_TEXTENCODING_UTF8 ).getStr());
90 // If *.dae file is not in the local file system, then copy it to a temp folder for the conversion
91 OUString sInput = rSourceURL;
92 const INetURLObject aSourceURLObj(rSourceURL);
93 if( aSourceURLObj.GetProtocol() != INetProtocol::File )
95 try
97 ::ucbhelper::Content aSourceContent(rSourceURL,
98 uno::Reference<ucb::XCommandEnvironment>(),
99 comphelper::getProcessComponentContext());
101 const OUString sTarget = sOutput + "/" + GetFilename(rSourceURL);
102 ::ucbhelper::Content aTempContent(sTarget,
103 uno::Reference<ucb::XCommandEnvironment>(),
104 comphelper::getProcessComponentContext());
106 aTempContent.writeStream(aSourceContent.openStream(), true);
107 sInput = sTarget;
109 catch (const uno::Exception&)
111 SAL_WARN("avmedia.opengl", "Exception while trying to copy source file to the temp folder for conversion: " << sInput);
112 return false;
116 asset->setInputFilePath(OUStringToOString( sInput, RTL_TEXTENCODING_UTF8 ).getStr());
118 if (bIsKMZ)
120 OUString sDaeFilePath;
121 lcl_UnzipKmz(sInput, sOutput, sDaeFilePath);
122 if ( sDaeFilePath.isEmpty() )
124 SAL_WARN("avmedia.opengl", "Cannot find the file in kmz: " << rSourceURL);
125 return false;
128 asset->setInputFilePath(OUStringToOString( sDaeFilePath, RTL_TEXTENCODING_UTF8 ).getStr());
131 GLTF::COLLADA2GLTFWriter writer(asset);
132 writer.write();
133 // Path to the .json file created by COLLADA2GLTFWriter
134 o_rOutput = sOutput + "/" + GetFilename(sOutput) + ".json";
135 return true;
137 #endif // HAVE_FEATURE_COLLADA
140 static void lcl_EmbedExternals(const OUString& rSourceURL, const uno::Reference<embed::XStorage>& xSubStorage, ::ucbhelper::Content& rContent)
142 // Create a temp file with which json parser can work.
143 OUString sTempFileURL;
144 const ::osl::FileBase::RC aErr =
145 ::osl::FileBase::createTempFile(nullptr, nullptr, &sTempFileURL);
146 if (aErr != ::osl::FileBase::E_None)
148 SAL_WARN("avmedia.opengl", "Cannot create temp file");
149 return;
153 // Write json content to the temp file
154 ::ucbhelper::Content aTempContent(sTempFileURL,
155 uno::Reference<ucb::XCommandEnvironment>(),
156 comphelper::getProcessComponentContext());
157 aTempContent.writeStream(rContent.openStream(), true);
159 catch (uno::Exception const& e)
161 SAL_WARN("avmedia.opengl", "Exception: '" << e.Message << "'");
162 return;
165 // Convert URL to a file path for loading
166 const INetURLObject aURLObj(sTempFileURL);
167 std::string sUrl = OUStringToOString( aURLObj.getFSysPath(FSysStyle::Detect), RTL_TEXTENCODING_UTF8 ).getStr();
169 // Parse json, read externals' URI and modify this relative URI's so they remain valid in the new context.
170 std::vector<std::string> vExternals;
171 ptree aTree;
174 json_parser::read_json( sUrl, aTree );
176 // Buffers for geometry and animations
177 for( ptree::value_type &rVal : aTree.get_child("buffers") )
179 const std::string sBufferUri(rVal.second.get<std::string>("path"));
180 vExternals.push_back(sBufferUri);
181 // Change path: make it contain only a file name
182 aTree.put("buffers." + rVal.first + ".path.",sBufferUri.substr(sBufferUri.find_last_of('/')+1));
184 // Images for textures
185 boost::optional< ptree& > aImages = aTree.get_child_optional("images");
186 if( aImages )
188 for( ptree::value_type &rVal : aImages.get() )
190 const std::string sImageUri(rVal.second.get<std::string>("path"));
191 if( !sImageUri.empty() )
193 vExternals.push_back(sImageUri);
194 // Change path: make it contain only a file name
195 aTree.put("images." + rVal.first + ".path.",sImageUri.substr(sImageUri.find_last_of('/')+1));
199 // Shaders (contains names only)
200 for( ptree::value_type &rVal : aTree.get_child("programs") )
202 vExternals.push_back(rVal.second.get<std::string>("fragmentShader") + ".glsl");
203 vExternals.push_back(rVal.second.get<std::string>("vertexShader") + ".glsl");
206 // Write out modified json
207 json_parser::write_json( sUrl, aTree );
209 catch ( boost::exception const& )
211 SAL_WARN("avmedia.opengl", "Exception while parsing *.json file");
212 return;
215 // Reload json with modified path to external resources
216 rContent = ::ucbhelper::Content(sTempFileURL,
217 uno::Reference<ucb::XCommandEnvironment>(),
218 comphelper::getProcessComponentContext());
220 // Store all external files next to the json file
221 for( std::vector<std::string>::iterator aCIter = vExternals.begin(); aCIter != vExternals.end(); ++aCIter )
223 const OUString sAbsURL = INetURLObject::GetAbsURL(rSourceURL,OUString::createFromAscii(aCIter->c_str()));
225 ::ucbhelper::Content aContent(sAbsURL,
226 uno::Reference<ucb::XCommandEnvironment>(),
227 comphelper::getProcessComponentContext());
229 uno::Reference<io::XStream> const xStream(
230 CreateStream(xSubStorage, GetFilename(sAbsURL)), uno::UNO_SET_THROW);
231 uno::Reference<io::XOutputStream> const xOutStream(
232 xStream->getOutputStream(), uno::UNO_SET_THROW);
234 if (!aContent.openStream(xOutStream))
236 SAL_WARN("avmedia.opengl", "openStream to storage failed");
237 return;
243 bool Embed3DModel( const uno::Reference<frame::XModel>& xModel,
244 const OUString& rSourceURL, OUString& o_rEmbeddedURL)
246 OUString sSource = rSourceURL;
248 #if HAVE_FEATURE_COLLADA
249 if( !rSourceURL.endsWithIgnoreAsciiCase(".json") )
250 KmzDae2Gltf(rSourceURL, sSource);
251 #endif
255 ::ucbhelper::Content aSourceContent(sSource,
256 uno::Reference<ucb::XCommandEnvironment>(),
257 comphelper::getProcessComponentContext());
259 // Base storage
260 uno::Reference<document::XStorageBasedDocument> const xSBD(xModel,
261 uno::UNO_QUERY_THROW);
262 uno::Reference<embed::XStorage> const xStorage(
263 xSBD->getDocumentStorage(), uno::UNO_QUERY_THROW);
265 // Model storage
266 const OUString sModel("Models");
267 uno::Reference<embed::XStorage> const xModelStorage(
268 xStorage->openStorageElement(sModel, embed::ElementModes::WRITE));
270 // Own storage of the corresponding model
271 const OUString sFilename(GetFilename(sSource));
272 const OUString sGLTFDir(sFilename.copy(0,sFilename.lastIndexOf('.')));
273 uno::Reference<embed::XStorage> const xSubStorage(
274 xModelStorage->openStorageElement(sGLTFDir, embed::ElementModes::WRITE));
276 // Embed external resources
277 lcl_EmbedExternals(sSource, xSubStorage, aSourceContent);
279 // Save model file (.json)
280 uno::Reference<io::XStream> const xStream(
281 CreateStream(xSubStorage, sFilename), uno::UNO_SET_THROW);
282 uno::Reference<io::XOutputStream> const xOutStream(
283 xStream->getOutputStream(), uno::UNO_SET_THROW);
285 if (!aSourceContent.openStream(xOutStream))
287 SAL_WARN("avmedia.opengl", "openStream to storage failed");
288 return false;
291 const uno::Reference<embed::XTransactedObject> xSubTransaction(xSubStorage, uno::UNO_QUERY);
292 if (xSubTransaction.is())
294 xSubTransaction->commit();
296 const uno::Reference<embed::XTransactedObject> xModelTransaction(xModelStorage, uno::UNO_QUERY);
297 if (xModelTransaction.is())
299 xModelTransaction->commit();
301 const uno::Reference<embed::XTransactedObject> xTransaction(xStorage, uno::UNO_QUERY);
302 if (xTransaction.is())
304 xTransaction->commit();
307 o_rEmbeddedURL = "vnd.sun.star.Package:" + sModel + "/" + sGLTFDir + "/" + sFilename;
308 return true;
310 catch (uno::Exception const&)
312 SAL_WARN("avmedia.opengl", "Exception while trying to embed model");
314 return false;
318 bool IsModel(const OUString& rMimeType)
320 return rMimeType == AVMEDIA_MIMETYPE_JSON;
323 } // namespace avmedia
325 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */