404 lines
13 KiB
C++
404 lines
13 KiB
C++
|
/*
|
||
|
Open Asset Import Library (assimp)
|
||
|
----------------------------------------------------------------------
|
||
|
|
||
|
Copyright (c) 2006-2020, assimp team
|
||
|
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use of this software in source and binary forms,
|
||
|
with or without modification, are permitted provided that the
|
||
|
following conditions are met:
|
||
|
|
||
|
* Redistributions of source code must retain the above
|
||
|
copyright notice, this list of conditions and the
|
||
|
following disclaimer.
|
||
|
|
||
|
* Redistributions in binary form must reproduce the above
|
||
|
copyright notice, this list of conditions and the
|
||
|
following disclaimer in the documentation and/or other
|
||
|
materials provided with the distribution.
|
||
|
|
||
|
* Neither the name of the assimp team, nor the names of its
|
||
|
contributors may be used to endorse or promote products
|
||
|
derived from this software without specific prior
|
||
|
written permission of the assimp team.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
----------------------------------------------------------------------
|
||
|
*/
|
||
|
#ifndef ASSIMP_BUILD_NO_EXPORT
|
||
|
#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
|
||
|
|
||
|
#include "D3MFExporter.h"
|
||
|
|
||
|
#include <assimp/Exceptional.h>
|
||
|
#include <assimp/StringUtils.h>
|
||
|
#include <assimp/scene.h>
|
||
|
#include <assimp/DefaultLogger.hpp>
|
||
|
#include <assimp/Exporter.hpp>
|
||
|
#include <assimp/IOStream.hpp>
|
||
|
#include <assimp/IOSystem.hpp>
|
||
|
|
||
|
#include "3MFXmlTags.h"
|
||
|
#include "D3MFOpcPackage.h"
|
||
|
|
||
|
#ifdef ASSIMP_USE_HUNTER
|
||
|
#include <zip/zip.h>
|
||
|
#else
|
||
|
#include <contrib/zip/src/zip.h>
|
||
|
#endif
|
||
|
|
||
|
namespace Assimp {
|
||
|
|
||
|
void ExportScene3MF(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties * /*pProperties*/) {
|
||
|
if (nullptr == pIOSystem) {
|
||
|
throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
|
||
|
}
|
||
|
D3MF::D3MFExporter myExporter(pFile, pScene);
|
||
|
if (myExporter.validate()) {
|
||
|
if (pIOSystem->Exists(pFile)) {
|
||
|
if (!pIOSystem->DeleteFile(pFile)) {
|
||
|
throw DeadlyExportError("File exists, cannot override : " + std::string(pFile));
|
||
|
}
|
||
|
}
|
||
|
bool ok = myExporter.exportArchive(pFile);
|
||
|
if (!ok) {
|
||
|
throw DeadlyExportError("Could not export 3MP archive: " + std::string(pFile));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
namespace D3MF {
|
||
|
|
||
|
D3MFExporter::D3MFExporter(const char *pFile, const aiScene *pScene) :
|
||
|
mArchiveName(pFile), m_zipArchive(nullptr), mScene(pScene), mModelOutput(), mRelOutput(), mContentOutput(), mBuildItems(), mRelations() {
|
||
|
// empty
|
||
|
}
|
||
|
|
||
|
D3MFExporter::~D3MFExporter() {
|
||
|
for (size_t i = 0; i < mRelations.size(); ++i) {
|
||
|
delete mRelations[i];
|
||
|
}
|
||
|
mRelations.clear();
|
||
|
}
|
||
|
|
||
|
bool D3MFExporter::validate() {
|
||
|
if (mArchiveName.empty()) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (nullptr == mScene) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool D3MFExporter::exportArchive(const char *file) {
|
||
|
bool ok(true);
|
||
|
|
||
|
m_zipArchive = zip_open(file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
|
||
|
if (nullptr == m_zipArchive) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ok |= exportContentTypes();
|
||
|
ok |= export3DModel();
|
||
|
ok |= exportRelations();
|
||
|
|
||
|
zip_close(m_zipArchive);
|
||
|
m_zipArchive = nullptr;
|
||
|
|
||
|
return ok;
|
||
|
}
|
||
|
|
||
|
bool D3MFExporter::exportContentTypes() {
|
||
|
mContentOutput.clear();
|
||
|
|
||
|
mContentOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
||
|
mContentOutput << std::endl;
|
||
|
mContentOutput << "<Types xmlns = \"http://schemas.openxmlformats.org/package/2006/content-types\">";
|
||
|
mContentOutput << std::endl;
|
||
|
mContentOutput << "<Default Extension = \"rels\" ContentType = \"application/vnd.openxmlformats-package.relationships+xml\" />";
|
||
|
mContentOutput << std::endl;
|
||
|
mContentOutput << "<Default Extension = \"model\" ContentType = \"application/vnd.ms-package.3dmanufacturing-3dmodel+xml\" />";
|
||
|
mContentOutput << std::endl;
|
||
|
mContentOutput << "</Types>";
|
||
|
mContentOutput << std::endl;
|
||
|
exportContentTyp(XmlTag::CONTENT_TYPES_ARCHIVE);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool D3MFExporter::exportRelations() {
|
||
|
mRelOutput.clear();
|
||
|
|
||
|
mRelOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
||
|
mRelOutput << std::endl;
|
||
|
mRelOutput << "<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">";
|
||
|
|
||
|
for (size_t i = 0; i < mRelations.size(); ++i) {
|
||
|
if (mRelations[i]->target[0] == '/') {
|
||
|
mRelOutput << "<Relationship Target=\"" << mRelations[i]->target << "\" ";
|
||
|
} else {
|
||
|
mRelOutput << "<Relationship Target=\"/" << mRelations[i]->target << "\" ";
|
||
|
}
|
||
|
mRelOutput << "Id=\"" << mRelations[i]->id << "\" ";
|
||
|
mRelOutput << "Type=\"" << mRelations[i]->type << "\" />";
|
||
|
mRelOutput << std::endl;
|
||
|
}
|
||
|
mRelOutput << "</Relationships>";
|
||
|
mRelOutput << std::endl;
|
||
|
|
||
|
writeRelInfoToFile("_rels", ".rels");
|
||
|
mRelOutput.flush();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool D3MFExporter::export3DModel() {
|
||
|
mModelOutput.clear();
|
||
|
|
||
|
writeHeader();
|
||
|
mModelOutput << "<" << XmlTag::model << " " << XmlTag::model_unit << "=\"millimeter\""
|
||
|
<< " xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">"
|
||
|
<< std::endl;
|
||
|
mModelOutput << "<" << XmlTag::resources << ">";
|
||
|
mModelOutput << std::endl;
|
||
|
|
||
|
writeMetaData();
|
||
|
|
||
|
writeBaseMaterials();
|
||
|
|
||
|
writeObjects();
|
||
|
|
||
|
mModelOutput << "</" << XmlTag::resources << ">";
|
||
|
mModelOutput << std::endl;
|
||
|
writeBuild();
|
||
|
|
||
|
mModelOutput << "</" << XmlTag::model << ">\n";
|
||
|
|
||
|
OpcPackageRelationship *info = new OpcPackageRelationship;
|
||
|
info->id = "rel0";
|
||
|
info->target = "/3D/3DModel.model";
|
||
|
info->type = XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
|
||
|
mRelations.push_back(info);
|
||
|
|
||
|
writeModelToArchive("3D", "3DModel.model");
|
||
|
mModelOutput.flush();
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeHeader() {
|
||
|
mModelOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
|
||
|
mModelOutput << std::endl;
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeMetaData() {
|
||
|
if (nullptr == mScene->mMetaData) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const unsigned int numMetaEntries(mScene->mMetaData->mNumProperties);
|
||
|
if (0 == numMetaEntries) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const aiString *key = nullptr;
|
||
|
const aiMetadataEntry *entry(nullptr);
|
||
|
for (size_t i = 0; i < numMetaEntries; ++i) {
|
||
|
mScene->mMetaData->Get(i, key, entry);
|
||
|
std::string k(key->C_Str());
|
||
|
aiString value;
|
||
|
mScene->mMetaData->Get(k, value);
|
||
|
mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">";
|
||
|
mModelOutput << value.C_Str();
|
||
|
mModelOutput << "</" << XmlTag::meta << ">" << std::endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeBaseMaterials() {
|
||
|
mModelOutput << "<basematerials id=\"1\">\n";
|
||
|
std::string strName, hexDiffuseColor, tmp;
|
||
|
for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
|
||
|
aiMaterial *mat = mScene->mMaterials[i];
|
||
|
aiString name;
|
||
|
if (mat->Get(AI_MATKEY_NAME, name) != aiReturn_SUCCESS) {
|
||
|
strName = "basemat_" + to_string(i);
|
||
|
} else {
|
||
|
strName = name.C_Str();
|
||
|
}
|
||
|
aiColor4D color;
|
||
|
if (mat->Get(AI_MATKEY_COLOR_DIFFUSE, color) == aiReturn_SUCCESS) {
|
||
|
hexDiffuseColor.clear();
|
||
|
tmp.clear();
|
||
|
// rgbs %
|
||
|
if (color.r <= 1 && color.g <= 1 && color.b <= 1 && color.a <= 1) {
|
||
|
|
||
|
hexDiffuseColor = Rgba2Hex(
|
||
|
(int)((ai_real)color.r) * 255,
|
||
|
(int)((ai_real)color.g) * 255,
|
||
|
(int)((ai_real)color.b) * 255,
|
||
|
(int)((ai_real)color.a) * 255,
|
||
|
true);
|
||
|
|
||
|
} else {
|
||
|
hexDiffuseColor = "#";
|
||
|
tmp = DecimalToHexa((ai_real)color.r);
|
||
|
hexDiffuseColor += tmp;
|
||
|
tmp = DecimalToHexa((ai_real)color.g);
|
||
|
hexDiffuseColor += tmp;
|
||
|
tmp = DecimalToHexa((ai_real)color.b);
|
||
|
hexDiffuseColor += tmp;
|
||
|
tmp = DecimalToHexa((ai_real)color.a);
|
||
|
hexDiffuseColor += tmp;
|
||
|
}
|
||
|
} else {
|
||
|
hexDiffuseColor = "#FFFFFFFF";
|
||
|
}
|
||
|
|
||
|
mModelOutput << "<base name=\"" + strName + "\" " + " displaycolor=\"" + hexDiffuseColor + "\" />\n";
|
||
|
}
|
||
|
mModelOutput << "</basematerials>\n";
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeObjects() {
|
||
|
if (nullptr == mScene->mRootNode) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
aiNode *root = mScene->mRootNode;
|
||
|
for (unsigned int i = 0; i < root->mNumChildren; ++i) {
|
||
|
aiNode *currentNode(root->mChildren[i]);
|
||
|
if (nullptr == currentNode) {
|
||
|
continue;
|
||
|
}
|
||
|
mModelOutput << "<" << XmlTag::object << " id=\"" << i + 2 << "\" type=\"model\">";
|
||
|
mModelOutput << std::endl;
|
||
|
for (unsigned int j = 0; j < currentNode->mNumMeshes; ++j) {
|
||
|
aiMesh *currentMesh = mScene->mMeshes[currentNode->mMeshes[j]];
|
||
|
if (nullptr == currentMesh) {
|
||
|
continue;
|
||
|
}
|
||
|
writeMesh(currentMesh);
|
||
|
}
|
||
|
mBuildItems.push_back(i);
|
||
|
|
||
|
mModelOutput << "</" << XmlTag::object << ">";
|
||
|
mModelOutput << std::endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeMesh(aiMesh *mesh) {
|
||
|
if (nullptr == mesh) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mModelOutput << "<" << XmlTag::mesh << ">" << std::endl;
|
||
|
mModelOutput << "<" << XmlTag::vertices << ">" << std::endl;
|
||
|
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
|
||
|
writeVertex(mesh->mVertices[i]);
|
||
|
}
|
||
|
mModelOutput << "</" << XmlTag::vertices << ">" << std::endl;
|
||
|
|
||
|
const unsigned int matIdx(mesh->mMaterialIndex);
|
||
|
|
||
|
writeFaces(mesh, matIdx);
|
||
|
|
||
|
mModelOutput << "</" << XmlTag::mesh << ">" << std::endl;
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeVertex(const aiVector3D &pos) {
|
||
|
mModelOutput << "<" << XmlTag::vertex << " x=\"" << pos.x << "\" y=\"" << pos.y << "\" z=\"" << pos.z << "\" />";
|
||
|
mModelOutput << std::endl;
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeFaces(aiMesh *mesh, unsigned int matIdx) {
|
||
|
if (nullptr == mesh) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!mesh->HasFaces()) {
|
||
|
return;
|
||
|
}
|
||
|
mModelOutput << "<" << XmlTag::triangles << ">" << std::endl;
|
||
|
for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
|
||
|
aiFace ¤tFace = mesh->mFaces[i];
|
||
|
mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[0] << "\" v2=\""
|
||
|
<< currentFace.mIndices[1] << "\" v3=\"" << currentFace.mIndices[2]
|
||
|
<< "\" pid=\"1\" p1=\"" + to_string(matIdx) + "\" />";
|
||
|
mModelOutput << std::endl;
|
||
|
}
|
||
|
mModelOutput << "</" << XmlTag::triangles << ">";
|
||
|
mModelOutput << std::endl;
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeBuild() {
|
||
|
mModelOutput << "<" << XmlTag::build << ">" << std::endl;
|
||
|
|
||
|
for (size_t i = 0; i < mBuildItems.size(); ++i) {
|
||
|
mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 2 << "\"/>";
|
||
|
mModelOutput << std::endl;
|
||
|
}
|
||
|
mModelOutput << "</" << XmlTag::build << ">";
|
||
|
mModelOutput << std::endl;
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::exportContentTyp(const std::string &filename) {
|
||
|
if (nullptr == m_zipArchive) {
|
||
|
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||
|
}
|
||
|
const std::string entry = filename;
|
||
|
zip_entry_open(m_zipArchive, entry.c_str());
|
||
|
|
||
|
const std::string &exportTxt(mContentOutput.str());
|
||
|
zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
|
||
|
|
||
|
zip_entry_close(m_zipArchive);
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeModelToArchive(const std::string &folder, const std::string &modelName) {
|
||
|
if (nullptr == m_zipArchive) {
|
||
|
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||
|
}
|
||
|
const std::string entry = folder + "/" + modelName;
|
||
|
zip_entry_open(m_zipArchive, entry.c_str());
|
||
|
|
||
|
const std::string &exportTxt(mModelOutput.str());
|
||
|
zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
|
||
|
|
||
|
zip_entry_close(m_zipArchive);
|
||
|
}
|
||
|
|
||
|
void D3MFExporter::writeRelInfoToFile(const std::string &folder, const std::string &relName) {
|
||
|
if (nullptr == m_zipArchive) {
|
||
|
throw DeadlyExportError("3MF-Export: Zip archive not valid, nullptr.");
|
||
|
}
|
||
|
const std::string entry = folder + "/" + relName;
|
||
|
zip_entry_open(m_zipArchive, entry.c_str());
|
||
|
|
||
|
const std::string &exportTxt(mRelOutput.str());
|
||
|
zip_entry_write(m_zipArchive, exportTxt.c_str(), exportTxt.size());
|
||
|
|
||
|
zip_entry_close(m_zipArchive);
|
||
|
}
|
||
|
|
||
|
} // Namespace D3MF
|
||
|
} // Namespace Assimp
|
||
|
|
||
|
#endif // ASSIMP_BUILD_NO_3MF_EXPORTER
|
||
|
#endif // ASSIMP_BUILD_NO_EXPORT
|