// ------------------------------------------------------------------------------------------------ aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing ) { ASSIMP_BEGIN_EXCEPTION_REGION(); pimpl->mError = ""; for (size_t i = 0; i < pimpl->mExporters.size(); ++i) { const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i]; if (!strcmp(exp.mDescription.id,pFormatId)) { try { // Always create a full copy of the scene. We might optimize this one day, // but for now it is the most pragmatic way. aiScene* scenecopy_tmp; SceneCombiner::CopyScene(&scenecopy_tmp,pScene); std::auto_ptr<aiScene> scenecopy(scenecopy_tmp); const ScenePrivateData* const priv = ScenePriv(pScene); // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the // original state before the step was applied first. When checking which steps we don't need // to run, those are excluded. const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded; // Erase all pp steps that were already applied to this scene unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv ? (priv->mPPStepsApplied & ~nonIdempotentSteps) : 0u); // If no extra postprocessing was specified, and we obtained this scene from an // Assimp importer, apply the reverse steps automatically. if (!pPreprocessing && priv) { pp |= (nonIdempotentSteps & priv->mPPStepsApplied); } // If the input scene is not in verbose format, but there is at least postprocessing step that relies on it, // we need to run the MakeVerboseFormat step first. if (scenecopy->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { bool verbosify = false; for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { BaseProcess* const p = pimpl->mPostProcessingSteps[a]; if (p->IsActive(pp) && p->RequireVerboseFormat()) { verbosify = true; break; } } if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { DefaultLogger::get()->debug("export: Scene data not in verbose format, applying MakeVerboseFormat step first"); MakeVerboseFormatProcess proc; proc.Execute(scenecopy.get()); } } if (pp) { // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout { FlipWindingOrderProcess step; if (step.IsActive(pp)) { step.Execute(scenecopy.get()); } } { FlipUVsProcess step; if (step.IsActive(pp)) { step.Execute(scenecopy.get()); } } { MakeLeftHandedProcess step; if (step.IsActive(pp)) { step.Execute(scenecopy.get()); } } // dispatch other processes for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { BaseProcess* const p = pimpl->mPostProcessingSteps[a]; if (p->IsActive(pp) && !dynamic_cast<FlipUVsProcess*>(p) && !dynamic_cast<FlipWindingOrderProcess*>(p) && !dynamic_cast<MakeLeftHandedProcess*>(p)) { p->Execute(scenecopy.get()); } } ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); ai_assert(privOut); privOut->mPPStepsApplied |= pp; } exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get()); } catch (DeadlyExportError& err) { pimpl->mError = err.what(); return AI_FAILURE; } return AI_SUCCESS; } } pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; ASSIMP_END_EXCEPTION_REGION(aiReturn); return AI_FAILURE; }
// ------------------------------------------------------------------------------------------------ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing, const ExportProperties* pProperties) { ASSIMP_BEGIN_EXCEPTION_REGION(); // when they create scenes from scratch, users will likely create them not in verbose // format. They will likely not be aware that there is a flag in the scene to indicate // this, however. To avoid surprises and bug reports, we check for duplicates in // meshes upfront. const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene); pimpl->mProgressHandler->UpdateFileWrite(0, 4); pimpl->mError = ""; for (size_t i = 0; i < pimpl->mExporters.size(); ++i) { const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i]; if (!strcmp(exp.mDescription.id,pFormatId)) { try { // Always create a full copy of the scene. We might optimize this one day, // but for now it is the most pragmatic way. aiScene* scenecopy_tmp = nullptr; SceneCombiner::CopyScene(&scenecopy_tmp,pScene); pimpl->mProgressHandler->UpdateFileWrite(1, 4); std::unique_ptr<aiScene> scenecopy(scenecopy_tmp); const ScenePrivateData* const priv = ScenePriv(pScene); // steps that are not idempotent, i.e. we might need to run them again, usually to get back to the // original state before the step was applied first. When checking which steps we don't need // to run, those are excluded. const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded; // Erase all pp steps that were already applied to this scene const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy ? (priv->mPPStepsApplied & ~nonIdempotentSteps) : 0u); // If no extra post-processing was specified, and we obtained this scene from an // Assimp importer, apply the reverse steps automatically. // TODO: either drop this, or document it. Otherwise it is just a bad surprise. //if (!pPreprocessing && priv) { // pp |= (nonIdempotentSteps & priv->mPPStepsApplied); //} // If the input scene is not in verbose format, but there is at least post-processing step that relies on it, // we need to run the MakeVerboseFormat step first. bool must_join_again = false; if (!is_verbose_format) { bool verbosify = false; for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { BaseProcess* const p = pimpl->mPostProcessingSteps[a]; if (p->IsActive(pp) && p->RequireVerboseFormat()) { verbosify = true; break; } } if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { ASSIMP_LOG_DEBUG("export: Scene data not in verbose format, applying MakeVerboseFormat step first"); MakeVerboseFormatProcess proc; proc.Execute(scenecopy.get()); if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) { must_join_again = true; } } } pimpl->mProgressHandler->UpdateFileWrite(2, 4); if (pp) { // the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout { FlipWindingOrderProcess step; if (step.IsActive(pp)) { step.Execute(scenecopy.get()); } } { FlipUVsProcess step; if (step.IsActive(pp)) { step.Execute(scenecopy.get()); } } { MakeLeftHandedProcess step; if (step.IsActive(pp)) { step.Execute(scenecopy.get()); } } bool exportPointCloud(false); if (nullptr != pProperties) { exportPointCloud = pProperties->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); } // dispatch other processes for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { BaseProcess* const p = pimpl->mPostProcessingSteps[a]; if (p->IsActive(pp) && !dynamic_cast<FlipUVsProcess*>(p) && !dynamic_cast<FlipWindingOrderProcess*>(p) && !dynamic_cast<MakeLeftHandedProcess*>(p)) { if (dynamic_cast<PretransformVertices*>(p) && exportPointCloud) { continue; } p->Execute(scenecopy.get()); } } ScenePrivateData* const privOut = ScenePriv(scenecopy.get()); ai_assert(nullptr != privOut); privOut->mPPStepsApplied |= pp; } pimpl->mProgressHandler->UpdateFileWrite(3, 4); if(must_join_again) { JoinVerticesProcess proc; proc.Execute(scenecopy.get()); } ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry. exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties); pimpl->mProgressHandler->UpdateFileWrite(4, 4); } catch (DeadlyExportError& err) { pimpl->mError = err.what(); return AI_FAILURE; } return AI_SUCCESS; } } pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId; ASSIMP_END_EXCEPTION_REGION(aiReturn); return AI_FAILURE; }