Compare commits

..

1 Commits

Author SHA1 Message Date
Kim Kulling
a14d6a48bd Fix apt-get install command in Dockerfile 2025-11-13 21:03:38 +01:00
76 changed files with 2155 additions and 902 deletions

View File

@@ -48,7 +48,7 @@ jobs:
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
- uses: actions/checkout@v6
- uses: actions/checkout@v5
with:
submodules: true
@@ -197,13 +197,4 @@ jobs:
with:
files: |
*.zip
upload_url: '${{ steps.upload-url.outputs.url }}'
- name: Upload EXE files
if: contains(matrix.name, 'windows-msvc-hunter')
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: '${{secrets.GITHUB_TOKEN}}'
with:
files: |
build/bin/assimp*.exe
upload_url: '${{ steps.upload-url.outputs.url }}'

View File

@@ -27,7 +27,7 @@ jobs:
os: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: lukka/get-cmake@latest

View File

@@ -14,7 +14,7 @@ jobs:
name: adress-sanitizer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: lukka/get-cmake@latest
- uses: lukka/set-shell-env@v1
with:
@@ -38,7 +38,7 @@ jobs:
name: undefined-behavior-sanitizer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- uses: lukka/get-cmake@latest
- uses: lukka/set-shell-env@v1
with:
@@ -62,7 +62,7 @@ jobs:
name: printf-sanitizer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v5
- name: run scan_printf script
run: ./scripts/scan_printf.sh

View File

@@ -1,51 +1,17 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
"configurePresets": [
{
"name": "assimp",
"binaryDir": "${sourceDir}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"ASSIMP_BUILD_ASSIMP_TOOLS": "OFF"
}
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 20,
"patch": 0
},
{
"name": "assimp_static",
"binaryDir": "${sourceDir}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"BUILD_SHARED_LIBS": "OFF",
"ASSIMP_BUILD_ASSIMP_TOOLS": "OFF",
"ASSIMP_DOUBLE_PRECISION": "ON"
}
},
{
"name": "assimp_double_precision",
"binaryDir": "${sourceDir}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release",
"ASSIMP_BUILD_ASSIMP_TOOLS": "OFF",
"ASSIMP_DOUBLE_PRECISION": "ON"
}
},
{
"name": "assimp_with_tools",
"binaryDir": "${sourceDir}",
"cacheVariables": {
"ASSIMP_BUILD_ASSIMP_TOOLS": "ON"
}
},
{
"name": "assimp_all",
"binaryDir": "${sourceDir}",
"cacheVariables": {
"ASSIMP_BUILD_ASSIMP_TOOLS": "ON",
"ASSIMP_BUILD_SAMPLES": "ON",
"ASSIMP_BUILD_DOCS": "ON"
}
}
"configurePresets": [
{
"name": "assimp_with_tools",
"binaryDir": "${sourceDir}",
"cacheVariables": {
"ASSIMP_BUILD_ASSIMP_TOOLS" : "ON"
}
}
]
}

View File

@@ -1,7 +1,7 @@
FROM gcc:1.5.1.0
RUN apt-get update \
apt-get install --no-install-recommends -y ninja-build cmake zlib1g-dev
apt-get install -y ninja-build cmake zlib1g-dev
WORKDIR /app

View File

@@ -3,7 +3,7 @@ Open Asset Import Library (assimp)
Open Asset Import Library is a library that loads various 3D file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
### Current project status
### Current project status ###
![C/C++ CI](https://github.com/assimp/assimp/workflows/C/C++%20CI/badge.svg)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9973693b7bdd4543b07084d5d9cf4745)](https://www.codacy.com/gh/assimp/assimp/dashboard?utm_source=github.com&utm_medium=referral&utm_content=assimp/assimp&utm_campaign=Badge_Grade)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=assimp_assimp&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=assimp_assimp)
@@ -16,32 +16,32 @@ Open Asset Import Library is a library that loads various 3D file formats into a
APIs are provided for C and C++. Various bindings exist to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
Additionally, assimp features various __mesh post-processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
## Project activity
## Project activity ##
![Alt](https://repobeats.axiom.co/api/embed/997f84e5f9fcf772da1e687f3a4f3a8afdbf4cf0.svg "Repobeats analytics image")
### Documentation
### Documentation ###
Read [our latest documentation](https://the-asset-importer-lib-documentation.readthedocs.io/en/latest/).
### Pre-built binaries
### Pre-built binaries ###
Download binaries from [our Itch Projectspace](https://kimkulling.itch.io/the-asset-importer-lib).
### Test data
### Test data ###
Clone [our model database for testing purposes](https://github.com/assimp/assimp-mdb).
### Communities
### Communities ###
- Ask questions at [the Assimp Discussion Board](https://github.com/assimp/assimp/discussions).
- Find us on [discord](https://discord.gg/kKazXMXDy2)
- Ask [the Assimp community on Reddit](https://www.reddit.com/r/Assimp/).
- Ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest).
- Nothing has worked? File a question or an issue report at [The Assimp-Issue Tracker](https://github.com/assimp/assimp/issues)
#### Supported file formats
#### Supported file formats ####
See [the complete list of supported formats](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md).
### Building
### Building ###
Start by reading [our build instructions](https://github.com/assimp/assimp/blob/master/Build.md). We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
### Ports
### Ports ###
* [Android](port/AndroidJNI/README.md)
* [Python](port/PyAssimp/README.md)
* [.NET](https://github.com/Saalvage/AssimpNetter)
@@ -54,39 +54,35 @@ Start by reading [our build instructions](https://github.com/assimp/assimp/blob/
* [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
* [Rust](https://github.com/jkvargas/russimp)
### Other tools
### Other tools ###
[Qt5-ModelViewer](https://github.com/sharjith/ModelViewer-Qt5) is a powerful viewer based on Qt5 and Assimp's import and export abilities.<br>
[Assimp-Viewer](https://github.com/assimp/assimp_view) is an experimental implementation for an Asset-Viewer based on ImGUI and Assimp (experimental).
#### Repository structure ####
Open Asset Import Library is implemented in C++. The directory structure looks like this:
```txt
code Source code
├── AssetLib/ The asset-importer and exporter lib
├── CApi/ C-API files
├── Common/ Common code used from all modules
├── Geometry/ Geometry utilities
├── Material/ Common materials
├── Pbrt/ Physical based materials
├── PostProcessing/ Post-processing steps
├── res Resouce files
contrib Third-party libraries
doc Documentation (Doxygen source and pre-compiled docs)
fuzz Contains the test code for the Google Fuzzer project
include Public header C and C++ header files
scripts Scripts are used to generate the loading code for some formats
port Ports to other languages and scripts to maintain those.
test Unit- and regression tests, test suite of models
├── headercheck Implements headerchecks
├── models-nonbsd Non-BSP licensed headers
├── models BSP-licensed models
├── unit Unit tests
tools Tools (old assimp viewer, command line `assimp`)
samples Small number of samples to illustrate possible use cases for Assimp
```
### Contributing
/code Source code
/contrib Third-party libraries
/doc Documentation (Doxygen source and pre-compiled docs)
/fuzz Contains the test code for the Google Fuzzer project
/include Public header C and C++ header files
/scripts Scripts are used to generate the loading code for some formats
/port Ports to other languages and scripts to maintain those.
/test Unit- and regression tests, test suite of models
/tools Tools (old assimp viewer, command line `assimp`)
/samples A small number of samples to illustrate possible use cases for Assimp
The source code is organized in the following way:
code/Common The base implementation for importers and the infrastructure
code/CApi Special implementations which are only used for the C-API
code/Geometry A collection of geometry tools
code/Material The material system
code/PBR An exporter for physical-based models
code/PostProcessing The post-processing steps
code/AssetLib/<FormatName> Implementation for import and export of the format
### Contributing ###
We would greatly appreciate for you to contribute to assimp. The easiest way to get involved is to submit
a pull request with your changes against the main repository's `master` branch.

View File

@@ -0,0 +1,72 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#.rst:
# FindDevIL
# ---------
#
#
#
# This module locates the developer's image library.
# http://openil.sourceforge.net/
#
# This module sets:
#
# ::
#
# IL_LIBRARIES - the name of the IL library. These include the full path to
# the core DevIL library. This one has to be linked into the
# application.
# ILU_LIBRARIES - the name of the ILU library. Again, the full path. This
# library is for filters and effects, not actual loading. It
# doesn't have to be linked if the functionality it provides
# is not used.
# ILUT_LIBRARIES - the name of the ILUT library. Full path. This part of the
# library interfaces with OpenGL. It is not strictly needed
# in applications.
# IL_INCLUDE_DIR - where to find the il.h, ilu.h and ilut.h files.
# IL_FOUND - this is set to TRUE if all the above variables were set.
# This will be set to false if ILU or ILUT are not found,
# even if they are not needed. In most systems, if one
# library is found all the others are as well. That's the
# way the DevIL developers release it.
# TODO: Add version support.
# Tested under Linux and Windows (MSVC)
#include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
include(FindPackageHandleStandardArgs)
find_path(IL_INCLUDE_DIR il.h
PATH_SUFFIXES include IL
DOC "The path to the directory that contains il.h"
)
#message("IL_INCLUDE_DIR is ${IL_INCLUDE_DIR}")
find_library(IL_LIBRARIES
NAMES IL DEVIL
PATH_SUFFIXES lib64 lib lib32
DOC "The file that corresponds to the base il library."
)
#message("IL_LIBRARIES is ${IL_LIBRARIES}")
find_library(ILUT_LIBRARIES
NAMES ILUT
PATH_SUFFIXES lib64 lib lib32
DOC "The file that corresponds to the il (system?) utility library."
)
#message("ILUT_LIBRARIES is ${ILUT_LIBRARIES}")
find_library(ILU_LIBRARIES
NAMES ILU
PATH_SUFFIXES lib64 lib lib32
DOC "The file that corresponds to the il utility library."
)
#message("ILU_LIBRARIES is ${ILU_LIBRARIES}")
FIND_PACKAGE_HANDLE_STANDARD_ARGS(IL DEFAULT_MSG
IL_LIBRARIES IL_INCLUDE_DIR)

View File

@@ -178,7 +178,7 @@ void CopyTexture(aiMaterial &mat, Texture &texture, aiTextureType type) {
mat.AddProperty<ai_real>(&texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type, 0));
// Setup the texture mapping mode
auto mapMode = static_cast<int>(texture.mMapMode);
int mapMode = static_cast<int>(texture.mMapMode);
mat.AddProperty<int>(&mapMode, 1, AI_MATKEY_MAPPINGMODE_U(type, 0));
mat.AddProperty<int>(&mapMode, 1, AI_MATKEY_MAPPINGMODE_V(type, 0));
@@ -333,8 +333,7 @@ void Discreet3DSImporter::ConvertMeshes(aiScene *pcOut) {
for (auto i = mScene->mMeshes.begin(); i != mScene->mMeshes.end(); ++i) {
std::unique_ptr<std::vector<unsigned int>[]> aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
name.length = ASSIMP_itoa10(name.data, num);
++num;
name.length = ASSIMP_itoa10(name.data, num++);
unsigned int iNum = 0;
for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
@@ -478,7 +477,7 @@ void Discreet3DSImporter::AddNodeToGraph(aiScene *pcSOut, aiNode *pcOut, D3DS::N
// Setup the name of the node
// First instance keeps its name otherwise something might break, all others will be postfixed with their instance number
if (pcIn->mInstanceNumber > 1) {
char tmp[12] = {'\0'};
char tmp[12];
ASSIMP_itoa10(tmp, pcIn->mInstanceNumber);
std::string tempStr = pcIn->mName + "_inst_";
tempStr += tmp;
@@ -705,8 +704,7 @@ void Discreet3DSImporter::GenerateNodeGraph(aiScene *pcOut) {
// Build dummy nodes for all meshes
unsigned int a = 0;
for (unsigned int i = 0; i < pcOut->mNumMeshes; ++i, ++a) {
pcOut->mRootNode->mChildren[a] = new aiNode();
auto *pcNode = pcOut->mRootNode->mChildren[a];
aiNode *pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
pcNode->mParent = pcOut->mRootNode;
pcNode->mMeshes = new unsigned int[1];
pcNode->mMeshes[0] = i;

View File

@@ -100,7 +100,7 @@ public:
}
// -----------------------------------------
ai_real ValueAsFloat() const {
float ValueAsFloat() const {
return fast_atof(value.c_str());
}

View File

@@ -47,7 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
namespace Assimp::FBX {
namespace Assimp {
namespace FBX {
static constexpr size_t NumNullRecords = 25;
@@ -83,7 +84,8 @@ enum TransformInheritance {
TransformInheritance_MAX // end-of-enum sentinel
};
} // namespace Assimp::FBX
} // namespace FBX
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER

View File

@@ -1582,7 +1582,6 @@ void FBXExporter::WriteObjects () {
}
}
std::map<std::string, std::string> tpath_by_image;
// FbxVideo - stores images used by textures.
for (const auto &it : uid_by_image) {
FBX::Node n("Video");
@@ -1602,21 +1601,10 @@ void FBXExporter::WriteObjects () {
std::stringstream newPath;
if (embedded_texture->mFilename.length > 0) {
newPath << embedded_texture->mFilename.C_Str();
// If newPath doesn't end in an extension, add extension from embedded_texture->achFormatHint
std::string np = newPath.str();
size_t dot_pos = np.find_last_of('.');
if (dot_pos == std::string::npos || dot_pos < np.find_last_of("/\\")) {
// No extension found, add one
newPath << "." << embedded_texture->achFormatHint;
}
} else if (embedded_texture->achFormatHint[0]) {
int texture_index = std::stoi(path.substr(1, path.size() - 1));
newPath << texture_index << "." << embedded_texture->achFormatHint;
}
auto elem = tpath_by_image.find(path);
if (elem == tpath_by_image.end()) {
tpath_by_image[path] = newPath.str();
}
path = newPath.str();
// embed the texture
size_t texture_size = static_cast<size_t>(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u));
@@ -1735,17 +1723,6 @@ void FBXExporter::WriteObjects () {
unsigned int max = sizeof(aiUVTransform);
aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (ai_real *)&trafo, &max);
auto tp_elem = tpath_by_image.find(texture_path);
std::string tfile_path = texture_path;
if (tp_elem != tpath_by_image.end()) {
tfile_path = tp_elem->second;
} else {
std::stringstream err;
err << "Texture path not found for texure " << texture_path;
err << " on material " << i;
ASSIMP_LOG_WARN(err.str());
}
// now write the actual texture node
FBX::Node tnode("Texture");
// TODO: some way to determine texture name?
@@ -1766,8 +1743,8 @@ void FBXExporter::WriteObjects () {
// can't easily determine which texture path will be correct,
// so just store what we have in every field.
// these being incorrect is a common problem with FBX anyway.
tnode.AddChild("FileName", tp_elem->second);
tnode.AddChild("RelativeFilename", tp_elem->second);
tnode.AddChild("FileName", texture_path);
tnode.AddChild("RelativeFilename", texture_path);
tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0));
tnode.AddChild("ModelUVScaling", double(1.0), double(1.0));
tnode.AddChild("Texture_Alpha_Source", "None");

View File

@@ -64,7 +64,7 @@ class Parser;
class Element;
using ScopeList = std::vector<Scope*>;
using ElementMap = std::multimap< std::string, Element*>;
using ElementMap = std::fbx_unordered_multimap< std::string, Element*>;
using ElementCollection = std::pair<ElementMap::const_iterator,ElementMap::const_iterator>;
#define new_Scope new (allocator.Allocate(sizeof(Scope))) Scope

View File

@@ -50,7 +50,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string>
#include <cstring>
namespace Assimp::FBX::Util {
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
namespace Assimp {
namespace FBX {
namespace Util {
// ------------------------------------------------------------------------------------------------
const char* TokenTypeString(TokenType t)
@@ -229,5 +233,8 @@ std::string EncodeBase64(const char* data, size_t length)
return encoded_string;
}
} // namespace Assimp::FBX::Util
} // !Util
} // !FBX
} // !Assimp
#endif

View File

@@ -49,7 +49,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXTokenizer.h"
#include <stdint.h>
namespace Assimp::FBX::Util {
namespace Assimp {
namespace FBX {
namespace Util {
/** helper for std::for_each to delete all heap-allocated items in a container */
template<typename T>
@@ -70,9 +73,12 @@ struct destructor_fun {
}
};
/** Get a string representation for a #TokenType. */
const char* TokenTypeString(TokenType t);
/** Format log/error messages using a given offset in the source binary file
*
* @param offset offset within the file
@@ -123,6 +129,8 @@ char EncodeBase64(char byte);
* @return base64-encoded string*/
std::string EncodeBase64(const char* data, size_t length);
}
}
}
#endif // ! INCLUDED_AI_FBX_UTIL_H

View File

@@ -1,83 +0,0 @@
//
#include "JT/JTImporter.h"
#include <assimp/StreamReader.h>
#include <cstddef>
namespace Assimp::JT {
enum class SegmentType {
Invalid = -1, //< Predefined class: Invalid
LOGICAL_SCENE_GRAPH,//< Predefined class: Logical_Scene_Graph
JT_BREP,//< Predefined class: JT_BRep
PMI_DATA, //<Predefined class: PMI_Data
META_DATA, //< Predefined class: Meta_Data
SHAPE, //< Predefined class: Shape
SHAPE_LOD0, //< Predefined class: Shape_LOD0
SHAPE_LOD1, //< Predefined class: Logical_Scene_Graph
SHAPE_LOD2, //< Predefined class: Shape_LOD2
SHAPE_LOD3, //< Predefined class: Shape_LOD3
SHAPE_LOD4, //< Predefined class: Shape_LOD4
SHAPE_LOD5, //< Predefined class: Shape_LOD5
SHAPE_LOD6, //< Predefined class: Shape_LOD6
SHAPE_LOD7, //< Predefined class: Shape_LOD7
SHAPE_LOD8, //< Predefined class: Shape_LOD8
SHAPE_LOD9, ///< Predefined class: Shape_LOD9
XT_BREP, //< Predefined class: XT_BRep
Count //< Number of segment types
};
struct Guid {
uint32_t Data0{};
uint32_t Data1{};
uint32_t Data2{}
uint32_t Data3{};
};
struct Version {
constexpr static size_t VersionSize = 80;
uint8_t Version[VersionSize]{};
int32_t Empty{0};
uint64_t TOCOffset{0};
}
struct SegmentHeader {
Guid SegmentGuid{};
SegmentType Type{SegmentType::Invalid};
};
namespace {
void readVersion(StreamReaderLE& reader, Version& version) {
reader.ReadBytes(version.Version, Version::VersionSize);
version.Empty = reader.Read<int32_t>();
version.TOCOffset = reader.Read<uint64_t>();
}
} // Anonymous namespace
JTImporter::JTImporter() : BaseImporter() {
}
JTImporter::~JTImporter() {
}
bool JTImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
}
void JTImporter::setupProperties(const Importer* pImp) {
}
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
auto stream = std::shared_ptr<IOStream>(pIOHandler->Open(pFile, "rb"));
if (!stream) {
throw DeadlyImportError("Failed to open JT file " + pFile + " for reading.");
}
StreamReaderLE reader(stream);
Version version;
readVersion(reader, version);
}
} // Namespace Assimp

View File

@@ -1,17 +0,0 @@
#pragma once
#include <assimp/BaseImporter.h>
namespace Assimp::JT {
class JTImporter : public BaseImporter {
public:
JTImporter();
~JTImporter() override;
bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
void setupProperties(const Importer* pImp) override;
protected:
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override;
}

View File

@@ -80,7 +80,7 @@ ObjFileParser::ObjFileParser(IOStreamBuffer<char> &streamBuffer, const std::stri
m_buffer(),
m_pIO(io),
m_progress(progress),
m_originalObjFileName(originalObjFileName) {
m_originalObjFileName(originalObjFileName) {
std::fill_n(m_buffer, Buffersize, '\0');
// Create the model instance to store all the data

View File

@@ -379,12 +379,7 @@ void Q3DImporter::InternReadFile(const std::string &pFile,
light->mColorSpecular = light->mColorDiffuse;
// We don't need the rest, but we need to know where this chunk ends.
const auto t1 = stream.GetI4();
const auto t2 = stream.GetI4();
if (t1 < 0 || t2 < 0) {
throw DeadlyImportError("Quick3D: Overflow detected.");
}
const unsigned int temp = static_cast<unsigned int>(t1*t2);
unsigned int temp = (unsigned int)(stream.GetI4() * stream.GetI4());
// skip the background file name
while (stream.GetI1())

View File

@@ -919,10 +919,6 @@ private:
#pragma warning(pop)
#endif // _MSC_VER
#if _MSC_VER > 1920
#pragma warning(pop)
#endif
} // namespace STEP
} // namespace Assimp

View File

@@ -77,6 +77,7 @@ static constexpr aiImporterDesc desc = {
};
namespace Assimp {
using namespace std;
// Constructor to be privately used by Importer
USDImporter::USDImporter() :

View File

@@ -53,14 +53,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "USDLoaderImplTinyusdz.h"
namespace Assimp {
/// @brief USD Importer class
class USDImporter final : public BaseImporter {
class USDImporter : public BaseImporter {
public:
/// @brief Constructor
USDImporter();
/// @brief Destructor
~USDImporter() override = default;
/// \brief Returns whether the class can handle the format of the given file.

View File

@@ -44,33 +44,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ASSIMP_BUILD_NO_USD_IMPORTER
#include "USDLoaderImplTinyusdz.h"
#include "USDLoaderImplTinyusdzHelper.h"
#include "USDLoaderUtil.h"
#include "USDPreprocessor.h"
#include "io-util.hh" // namespace tinyusdz::io
#include "tydra/scene-access.hh"
#include "tydra/shader-network.hh"
#include <memory>
#include <sstream>
// internal headers
#include "assimp/MemoryIOWrapper.h"
#include <assimp/CreateAnimMesh.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/StreamReader.h>
#include <assimp/StringUtils.h>
#include <assimp/ai_assert.h>
#include <assimp/anim.h>
#include <assimp/fast_atof.h>
#include <assimp/importerdesc.h>
#include <assimp/CreateAnimMesh.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/IOSystem.hpp>
#include <assimp/fast_atof.h>
#include <assimp/Importer.hpp>
#include <assimp/importerdesc.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/IOSystem.hpp>
#include "assimp/MemoryIOWrapper.h"
#include <assimp/StringUtils.h>
#include <assimp/StreamReader.h>
#include "io-util.hh" // namespace tinyusdz::io
#include "tydra/scene-access.hh"
#include "tydra/shader-network.hh"
#include "USDLoaderImplTinyusdzHelper.h"
#include "USDLoaderImplTinyusdz.h"
#include "USDLoaderUtil.h"
#include "USDPreprocessor.h"
#include "../../../contrib/tinyusdz/assimp_tinyusdz_logging.inc"
@@ -79,18 +77,17 @@ namespace {
}
namespace Assimp {
using namespace std;
using Assimp::tinyUsdzMat4ToAiMat4;
using Assimp::tinyusdzNodeTypeFor;
using tinyusdz::tydra::NodeType;
using namespace tinyusdz::tydra;
void USDImporterImplTinyusdz::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
void USDImporterImplTinyusdz::InternReadFile(
const std::string &pFile,
aiScene *pScene,
IOSystem *pIOHandler) {
// Grab filename for logging purposes
size_t pos = pFile.find_last_of('/');
std::string basePath = pFile.substr(0, pos);
std::string nameWExt = pFile.substr(pos + 1);
std::stringstream ss;
string basePath = pFile.substr(0, pos);
string nameWExt = pFile.substr(pos + 1);
stringstream ss;
ss.str("");
ss << "InternReadFile(): model" << nameWExt;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
@@ -163,10 +160,9 @@ void USDImporterImplTinyusdz::InternReadFile(const std::string &pFile, aiScene *
TINYUSDZLOGE(TAG, "%s", ss.str().c_str());
return;
}
RenderScene render_scene;
RenderSceneConverter converter;
RenderSceneConverterEnv env(stage);
tinyusdz::tydra::RenderScene render_scene;
tinyusdz::tydra::RenderSceneConverter converter;
tinyusdz::tydra::RenderSceneConverterEnv env(stage);
std::string usd_basedir = tinyusdz::io::GetBaseDir(pFile);
env.set_search_paths({ usd_basedir }); // {} needed to convert to vector of char
@@ -227,8 +223,9 @@ void USDImporterImplTinyusdz::InternReadFile(const std::string &pFile, aiScene *
setupBlendShapes(render_scene, pScene, nameWExt);
}
void USDImporterImplTinyusdz::animations(const tinyusdz::tydra::RenderScene &render_scene, aiScene *pScene) {
void USDImporterImplTinyusdz::animations(
const tinyusdz::tydra::RenderScene& render_scene,
aiScene* pScene) {
if (render_scene.animations.empty()) {
return;
}
@@ -268,7 +265,7 @@ void USDImporterImplTinyusdz::animations(const tinyusdz::tydra::RenderScene &ren
for (const auto &[channelType, animChannel] : animationChannelMap) {
switch (channelType) {
case AnimationChannel::ChannelType::Rotation:
case tinyusdz::tydra::AnimationChannel::ChannelType::Rotation:
if (animChannel.rotations.static_value.has_value()) {
rotationKeys.emplace_back(0, tinyUsdzQuatToAiQuat(animChannel.rotations.static_value.value()));
}
@@ -280,7 +277,7 @@ void USDImporterImplTinyusdz::animations(const tinyusdz::tydra::RenderScene &ren
rotationKeys.emplace_back(rotationAnimSampler.t, tinyUsdzQuatToAiQuat(rotationAnimSampler.value));
}
break;
case AnimationChannel::ChannelType::Scale:
case tinyusdz::tydra::AnimationChannel::ChannelType::Scale:
if (animChannel.scales.static_value.has_value()) {
scalingKeys.emplace_back(0, tinyUsdzScaleOrPosToAssimp(animChannel.scales.static_value.value()));
}
@@ -291,7 +288,7 @@ void USDImporterImplTinyusdz::animations(const tinyusdz::tydra::RenderScene &ren
scalingKeys.emplace_back(scaleAnimSampler.t, tinyUsdzScaleOrPosToAssimp(scaleAnimSampler.value));
}
break;
case AnimationChannel::ChannelType::Transform:
case tinyusdz::tydra::AnimationChannel::ChannelType::Transform:
if (animChannel.transforms.static_value.has_value()) {
aiVector3D position;
aiVector3D scale;
@@ -317,7 +314,7 @@ void USDImporterImplTinyusdz::animations(const tinyusdz::tydra::RenderScene &ren
rotationKeys.emplace_back(transformAnimSampler.t, rotation);
}
break;
case AnimationChannel::ChannelType::Translation:
case tinyusdz::tydra::AnimationChannel::ChannelType::Translation:
if (animChannel.translations.static_value.has_value()) {
positionKeys.emplace_back(0, tinyUsdzScaleOrPosToAssimp(animChannel.translations.static_value.value()));
}
@@ -355,7 +352,7 @@ void USDImporterImplTinyusdz::meshes(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
std::stringstream ss;
stringstream ss;
pScene->mNumMeshes = static_cast<unsigned int>(render_scene.meshes.size());
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]();
ss.str("");
@@ -367,7 +364,10 @@ void USDImporterImplTinyusdz::meshes(
pScene->mMeshes[meshIdx] = new aiMesh();
pScene->mMeshes[meshIdx]->mName.Set(render_scene.meshes[meshIdx].prim_name);
ss.str("");
ss << " mesh[" << meshIdx << "]: " << render_scene.meshes[meshIdx].joint_and_weights.jointIndices.size() << " jointIndices, " << render_scene.meshes[meshIdx].joint_and_weights.jointWeights.size() << " jointWeights, elementSize: " << render_scene.meshes[meshIdx].joint_and_weights.elementSize;
ss << " mesh[" << meshIdx << "]: " <<
render_scene.meshes[meshIdx].joint_and_weights.jointIndices.size() << " jointIndices, " <<
render_scene.meshes[meshIdx].joint_and_weights.jointWeights.size() << " jointWeights, elementSize: " <<
render_scene.meshes[meshIdx].joint_and_weights.elementSize;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
ss.str("");
ss << " skel_id: " << render_scene.meshes[meshIdx].skel_id;
@@ -386,8 +386,12 @@ void USDImporterImplTinyusdz::meshes(
}
}
void USDImporterImplTinyusdz::verticesForMesh(const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene, size_t meshIdx, const std::string &) {
void USDImporterImplTinyusdz::verticesForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
UNUSED(nameWExt);
const auto numVertices = static_cast<unsigned int>(render_scene.meshes[meshIdx].points.size());
pScene->mMeshes[meshIdx]->mNumVertices = numVertices;
pScene->mMeshes[meshIdx]->mVertices = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices];
@@ -446,7 +450,7 @@ void USDImporterImplTinyusdz::verticesForMesh(const tinyusdz::tydra::RenderScene
std::swap_ranges(aiBonesVertexWeights[boneIndex].begin(), aiBonesVertexWeights[boneIndex].end(), pScene->mMeshes[meshIdx]->mBones[boneIndex]->mWeights);
}
} // Skinned mesh end
} // Skinned mesh end
for (size_t j = 0; j < pScene->mMeshes[meshIdx]->mNumVertices; ++j) {
pScene->mMeshes[meshIdx]->mVertices[j].x = render_scene.meshes[meshIdx].points[j][0];
@@ -455,8 +459,12 @@ void USDImporterImplTinyusdz::verticesForMesh(const tinyusdz::tydra::RenderScene
}
}
void USDImporterImplTinyusdz::facesForMesh(const RenderScene &render_scene, aiScene *pScene,
size_t meshIdx, const std::string &) {
void USDImporterImplTinyusdz::facesForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
UNUSED(nameWExt);
pScene->mMeshes[meshIdx]->mNumFaces = static_cast<unsigned int>(render_scene.meshes[meshIdx].faceVertexCounts().size());
pScene->mMeshes[meshIdx]->mFaces = new aiFace[pScene->mMeshes[meshIdx]->mNumFaces]();
size_t faceVertIdxOffset = 0;
@@ -471,8 +479,12 @@ void USDImporterImplTinyusdz::facesForMesh(const RenderScene &render_scene, aiSc
}
}
void USDImporterImplTinyusdz::normalsForMesh(const RenderScene &render_scene, aiScene *pScene,
size_t meshIdx, const std::string &) {
void USDImporterImplTinyusdz::normalsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
UNUSED(nameWExt);
pScene->mMeshes[meshIdx]->mNormals = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices];
const float *floatPtr = reinterpret_cast<const float *>(render_scene.meshes[meshIdx].normals.get_data().data());
for (size_t vertIdx = 0, fpj = 0; vertIdx < pScene->mMeshes[meshIdx]->mNumVertices; ++vertIdx, fpj += 3) {
@@ -482,16 +494,20 @@ void USDImporterImplTinyusdz::normalsForMesh(const RenderScene &render_scene, ai
}
}
void USDImporterImplTinyusdz::materialsForMesh(const RenderScene&, aiScene*,
size_t, const std::string&) {
// todo
void USDImporterImplTinyusdz::materialsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &nameWExt) {
UNUSED(render_scene); UNUSED(pScene); UNUSED(meshIdx); UNUSED(nameWExt);
}
void USDImporterImplTinyusdz::uvsForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string&) {
const std::string &nameWExt) {
UNUSED(nameWExt);
const size_t uvSlotsCount = render_scene.meshes[meshIdx].texcoords.size();
if (uvSlotsCount < 1) {
return;
@@ -519,8 +535,10 @@ static aiColor3D *ownedColorPtrFor(const std::array<float, 3> &color) {
return colorPtr;
}
static std::string nameForTextureWithId(const RenderScene &render_scene, const int targetId) {
std::stringstream ss;
static std::string nameForTextureWithId(
const tinyusdz::tydra::RenderScene &render_scene,
const int targetId) {
stringstream ss;
std::string texName;
for (const auto &image : render_scene.images) {
if (image.buffer_id == targetId) {
@@ -538,15 +556,16 @@ static std::string nameForTextureWithId(const RenderScene &render_scene, const i
}
static void assignTexture(
const RenderScene &render_scene,
const RenderMaterial &,
const tinyusdz::tydra::RenderScene &render_scene,
const tinyusdz::tydra::RenderMaterial &material,
aiMaterial *mat,
const int textureId,
const int aiTextureType) {
UNUSED(material);
std::string name = nameForTextureWithId(render_scene, textureId);
aiString *texName = new aiString();
texName->Set(name);
std::stringstream ss;
stringstream ss;
ss.str("");
ss << "assignTexture(): name: " << name;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
@@ -558,8 +577,9 @@ void USDImporterImplTinyusdz::materials(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
const size_t numMaterials{ render_scene.materials.size() };
std::stringstream ss;
const size_t numMaterials{render_scene.materials.size()};
(void) numMaterials; // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "materials(): model" << nameWExt << ", numMaterials: " << numMaterials;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
@@ -567,7 +587,6 @@ void USDImporterImplTinyusdz::materials(
if (render_scene.materials.empty()) {
return;
}
pScene->mMaterials = new aiMaterial *[render_scene.materials.size()];
for (const auto &material : render_scene.materials) {
ss.str("");
@@ -650,18 +669,22 @@ void USDImporterImplTinyusdz::materials(
void USDImporterImplTinyusdz::textures(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene*,
aiScene *pScene,
const std::string &nameWExt) {
const size_t numTextures{ render_scene.textures.size() };
std::stringstream ss;
UNUSED(pScene);
const size_t numTextures{render_scene.textures.size()};
UNUSED(numTextures); // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "textures(): model" << nameWExt << ", numTextures: " << numTextures;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
size_t i{ 0 };
size_t i{0};
UNUSED(i);
for (const auto &texture : render_scene.textures) {
UNUSED(texture);
ss.str("");
ss << " texture[" << i << "]: id: " << texture.texture_image_id << ", disp name: |" << texture.display_name << "|, varname_uv: " << texture.varname_uv << ", prim_name: |" << texture.prim_name << "|, abs_path: |" << texture.abs_path << "|";
ss << " texture[" << i << "]: id: " << texture.texture_image_id << ", disp name: |" << texture.display_name << "|, varname_uv: " <<
texture.varname_uv << ", prim_name: |" << texture.prim_name << "|, abs_path: |" << texture.abs_path << "|";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
++i;
}
@@ -675,12 +698,15 @@ void USDImporterImplTinyusdz::textures(
* @param nameWExt filename w/ext (use to extract file type hint)
* @return aiTexture ptr
*/
static aiTexture *ownedEmbeddedTextureFor(const RenderScene &render_scene, const TextureImage &image,
const std::string&) {
std::stringstream ss;
static aiTexture *ownedEmbeddedTextureFor(
const tinyusdz::tydra::RenderScene &render_scene,
const tinyusdz::tydra::TextureImage &image,
const std::string &nameWExt) {
UNUSED(nameWExt);
stringstream ss;
aiTexture *tex = new aiTexture();
size_t pos = image.asset_identifier.find_last_of('/');
std::string embTexName{ image.asset_identifier.substr(pos + 1) };
string embTexName{image.asset_identifier.substr(pos + 1)};
tex->mFilename.Set(image.asset_identifier.c_str());
tex->mHeight = image.height;
@@ -688,20 +714,20 @@ static aiTexture *ownedEmbeddedTextureFor(const RenderScene &render_scene, const
if (tex->mHeight == 0) {
pos = embTexName.find_last_of('.');
strncpy(tex->achFormatHint, embTexName.substr(pos + 1).c_str(), 3);
const size_t imageBytesCount{ render_scene.buffers[image.buffer_id].data.size() };
tex->pcData = (aiTexel *)new char[imageBytesCount];
const size_t imageBytesCount{render_scene.buffers[image.buffer_id].data.size()};
tex->pcData = (aiTexel *) new char[imageBytesCount];
memcpy(tex->pcData, &render_scene.buffers[image.buffer_id].data[0], imageBytesCount);
} else {
std::string formatHint{ "rgba8888" };
string formatHint{"rgba8888"};
strncpy(tex->achFormatHint, formatHint.c_str(), 8);
const size_t imageTexelsCount{ tex->mWidth * tex->mHeight };
tex->pcData = (aiTexel *)new char[imageTexelsCount * image.channels];
const size_t imageTexelsCount{tex->mWidth * tex->mHeight};
tex->pcData = (aiTexel *) new char[imageTexelsCount * image.channels];
const float *floatPtr = reinterpret_cast<const float *>(&render_scene.buffers[image.buffer_id].data[0]);
ss.str("");
ss << "ownedEmbeddedTextureFor(): manual fill...";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
for (size_t i = 0, fpi = 0; i < imageTexelsCount; ++i, fpi += 4) {
tex->pcData[i].b = static_cast<uint8_t>(floatPtr[fpi] * 255);
tex->pcData[i].b = static_cast<uint8_t>(floatPtr[fpi] * 255);
tex->pcData[i].g = static_cast<uint8_t>(floatPtr[fpi + 1] * 255);
tex->pcData[i].r = static_cast<uint8_t>(floatPtr[fpi + 2] * 255);
tex->pcData[i].a = static_cast<uint8_t>(floatPtr[fpi + 3] * 255);
@@ -713,9 +739,13 @@ static aiTexture *ownedEmbeddedTextureFor(const RenderScene &render_scene, const
return tex;
}
void USDImporterImplTinyusdz::textureImages(const RenderScene &render_scene, aiScene *pScene, const std::string &nameWExt) {
std::stringstream ss;
const size_t numTextureImages{ render_scene.images.size() };
void USDImporterImplTinyusdz::textureImages(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
stringstream ss;
const size_t numTextureImages{render_scene.images.size()};
UNUSED(numTextureImages); // Ignore unused variable when -Werror enabled
ss.str("");
ss << "textureImages(): model" << nameWExt << ", numTextureImages: " << numTextureImages;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
@@ -723,15 +753,13 @@ void USDImporterImplTinyusdz::textureImages(const RenderScene &render_scene, aiS
pScene->mNumTextures = 0;
for (const auto &image : render_scene.images) {
ss.str("");
ss << " image[" << pScene->mNumTextures << "]: |" << image.asset_identifier << "| w: "
<< image.width << ", h: " << image.height << ", channels: " << image.channels << ", miplevel: "
<< image.miplevel << ", buffer id: " << image.buffer_id << "\n"
<< " buffers.size(): " << render_scene.buffers.size() << ", data empty? "
<< render_scene.buffers[image.buffer_id].data.empty();
ss << " image[" << pScene->mNumTextures << "]: |" << image.asset_identifier << "| w: " << image.width << ", h: " << image.height <<
", channels: " << image.channels << ", miplevel: " << image.miplevel << ", buffer id: " << image.buffer_id << "\n" <<
" buffers.size(): " << render_scene.buffers.size() << ", data empty? " << render_scene.buffers[image.buffer_id].data.empty();
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
if (image.buffer_id > -1 &&
image.buffer_id < static_cast<long int>(render_scene.buffers.size()) &&
!render_scene.buffers[image.buffer_id].data.empty()) {
image.buffer_id < static_cast<long int>(render_scene.buffers.size()) &&
!render_scene.buffers[image.buffer_id].data.empty()) {
aiTexture *tex = ownedEmbeddedTextureFor(
render_scene,
image,
@@ -743,8 +771,8 @@ void USDImporterImplTinyusdz::textureImages(const RenderScene &render_scene, aiS
pScene->mTextures = new aiTexture *[render_scene.images.size()];
}
ss.str("");
ss << " pScene->mTextures[" << pScene->mNumTextures << "] name: |" << tex->mFilename.C_Str()
<< "|, w: " << tex->mWidth << ", h: " << tex->mHeight << ", hint: " << tex->achFormatHint;
ss << " pScene->mTextures[" << pScene->mNumTextures << "] name: |" << tex->mFilename.C_Str() <<
"|, w: " << tex->mWidth << ", h: " << tex->mHeight << ", hint: " << tex->achFormatHint;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
pScene->mTextures[pScene->mNumTextures++] = tex;
}
@@ -752,9 +780,12 @@ void USDImporterImplTinyusdz::textureImages(const RenderScene &render_scene, aiS
}
void USDImporterImplTinyusdz::buffers(
const RenderScene &render_scene, aiScene*, const std::string &nameWExt) {
const size_t numBuffers{ render_scene.buffers.size() };
std::stringstream ss;
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
const size_t numBuffers{render_scene.buffers.size()};
UNUSED(pScene); UNUSED(numBuffers); // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "buffers(): model" << nameWExt << ", numBuffers: " << numBuffers;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
@@ -767,9 +798,14 @@ void USDImporterImplTinyusdz::buffers(
}
}
aiNode *USDImporterImplTinyusdz::nodesRecursive(aiNode *pNodeParent, const tinyusdz::tydra::Node &node,
const std::vector<SkelHierarchy> &skeletons) {
std::stringstream ss;
using Assimp::tinyusdzNodeTypeFor;
using Assimp::tinyUsdzMat4ToAiMat4;
using tinyusdz::tydra::NodeType;
aiNode *USDImporterImplTinyusdz::nodesRecursive(
aiNode *pNodeParent,
const tinyusdz::tydra::Node &node,
const std::vector<tinyusdz::tydra::SkelHierarchy> &skeletons) {
stringstream ss;
aiNode *cNode = new aiNode();
cNode->mParent = pNodeParent;
cNode->mName.Set(node.prim_name);
@@ -782,9 +818,9 @@ aiNode *USDImporterImplTinyusdz::nodesRecursive(aiNode *pNodeParent, const tinyu
}
ss.str("");
ss << "nodesRecursive(): node " << cNode->mName.C_Str() << " type: |"
<< tinyusdzNodeTypeFor(node.nodeType)
<< "|, disp " << node.display_name << ", abs " << node.abs_path;
ss << "nodesRecursive(): node " << cNode->mName.C_Str() <<
" type: |" << tinyusdzNodeTypeFor(node.nodeType) <<
"|, disp " << node.display_name << ", abs " << node.abs_path;
if (cNode->mParent != nullptr) {
ss << " (parent " << cNode->mParent->mName.C_Str() << ")";
}
@@ -825,15 +861,15 @@ aiNode *USDImporterImplTinyusdz::nodesRecursive(aiNode *pNodeParent, const tinyu
if (skelNode != nullptr) {
// Convert USD skeleton into an Assimp node and make it the last child
cNode->mChildren[cNode->mNumChildren - 1] = skeletonNodesRecursive(cNode, *skelNode);
cNode->mChildren[cNode->mNumChildren-1] = skeletonNodesRecursive(cNode, *skelNode);
}
return cNode;
}
aiNode *USDImporterImplTinyusdz::skeletonNodesRecursive(
aiNode *pNodeParent,
const SkelNode &joint) {
aiNode* pNodeParent,
const tinyusdz::tydra::SkelNode& joint) {
auto *cNode = new aiNode(joint.joint_path);
cNode->mParent = pNodeParent;
cNode->mNumMeshes = 0; // not a mesh node
@@ -848,7 +884,7 @@ aiNode *USDImporterImplTinyusdz::skeletonNodesRecursive(
cNode->mChildren = new aiNode *[cNode->mNumChildren];
for (unsigned i = 0; i < cNode->mNumChildren; ++i) {
const SkelNode &childJoint = joint.children[i];
const tinyusdz::tydra::SkelNode &childJoint = joint.children[i];
cNode->mChildren[i] = skeletonNodesRecursive(cNode, childJoint);
}
@@ -857,7 +893,7 @@ aiNode *USDImporterImplTinyusdz::skeletonNodesRecursive(
void USDImporterImplTinyusdz::sanityCheckNodesRecursive(
aiNode *cNode) {
std::stringstream ss;
stringstream ss;
ss.str("");
ss << "sanityCheckNodesRecursive(): node " << cNode->mName.C_Str();
if (cNode->mParent != nullptr) {
@@ -870,14 +906,16 @@ void USDImporterImplTinyusdz::sanityCheckNodesRecursive(
}
}
void USDImporterImplTinyusdz::setupBlendShapes(const RenderScene &render_scene, aiScene *pScene,
void USDImporterImplTinyusdz::setupBlendShapes(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const std::string &nameWExt) {
std::stringstream ss;
stringstream ss;
ss.str("");
ss << "setupBlendShapes(): iterating over " << pScene->mNumMeshes << " meshes for model" << nameWExt;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
for (size_t meshIdx = 0; meshIdx < pScene->mNumMeshes; meshIdx++) {
blendShapesForMesh(render_scene, pScene, meshIdx, nameWExt);
blendShapesForMesh(render_scene, pScene, meshIdx, nameWExt);
}
}
@@ -885,9 +923,10 @@ void USDImporterImplTinyusdz::blendShapesForMesh(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
size_t meshIdx,
const std::string &) {
std::stringstream ss;
const unsigned int numBlendShapeTargets{ static_cast<unsigned int>(render_scene.meshes[meshIdx].targets.size()) };
const std::string &nameWExt) {
UNUSED(nameWExt);
stringstream ss;
const unsigned int numBlendShapeTargets{static_cast<unsigned int>(render_scene.meshes[meshIdx].targets.size())};
UNUSED(numBlendShapeTargets); // Ignore unused variable when -Werror enabled
ss.str("");
ss << " blendShapesForMesh(): mesh[" << meshIdx << "], numBlendShapeTargets: " << numBlendShapeTargets;
@@ -897,13 +936,15 @@ void USDImporterImplTinyusdz::blendShapesForMesh(
pScene->mMeshes[meshIdx]->mAnimMeshes = new aiAnimMesh *[pScene->mMeshes[meshIdx]->mNumAnimMeshes];
}
auto mapIter = render_scene.meshes[meshIdx].targets.begin();
size_t animMeshIdx{ 0 };
size_t animMeshIdx{0};
for (; mapIter != render_scene.meshes[meshIdx].targets.end(); ++mapIter) {
const std::string name{ mapIter->first };
const tinyusdz::tydra::ShapeTarget shapeTarget{ mapIter->second };
const std::string name{mapIter->first};
const tinyusdz::tydra::ShapeTarget shapeTarget{mapIter->second};
pScene->mMeshes[meshIdx]->mAnimMeshes[animMeshIdx] = aiCreateAnimMesh(pScene->mMeshes[meshIdx]);
ss.str("");
ss << " mAnimMeshes[" << animMeshIdx << "]: mNumVertices: " << pScene->mMeshes[meshIdx]->mAnimMeshes[animMeshIdx]->mNumVertices << ", target: " << shapeTarget.pointIndices.size() << " pointIndices, " << shapeTarget.pointOffsets.size() << " pointOffsets, " << shapeTarget.normalOffsets.size() << " normalOffsets";
ss << " mAnimMeshes[" << animMeshIdx << "]: mNumVertices: " << pScene->mMeshes[meshIdx]->mAnimMeshes[animMeshIdx]->mNumVertices <<
", target: " << shapeTarget.pointIndices.size() << " pointIndices, " << shapeTarget.pointOffsets.size() <<
" pointOffsets, " << shapeTarget.normalOffsets.size() << " normalOffsets";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
for (size_t iVert = 0; iVert < shapeTarget.pointOffsets.size(); ++iVert) {
pScene->mMeshes[meshIdx]->mAnimMeshes[animMeshIdx]->mVertices[shapeTarget.pointIndices[iVert]] +=
@@ -914,7 +955,12 @@ void USDImporterImplTinyusdz::blendShapesForMesh(
tinyUsdzScaleOrPosToAssimp(shapeTarget.normalOffsets[iVert]);
}
ss.str("");
ss << " target[" << animMeshIdx << "]: name: " << name << ", prim_name: " << shapeTarget.prim_name << ", abs_path: " << shapeTarget.abs_path << ", display_name: " << shapeTarget.display_name << ", " << shapeTarget.pointIndices.size() << " pointIndices, " << shapeTarget.pointOffsets.size() << " pointOffsets, " << shapeTarget.normalOffsets.size() << " normalOffsets, " << shapeTarget.inbetweens.size() << " inbetweens";
ss << " target[" << animMeshIdx << "]: name: " << name << ", prim_name: " <<
shapeTarget.prim_name << ", abs_path: " << shapeTarget.abs_path <<
", display_name: " << shapeTarget.display_name << ", " << shapeTarget.pointIndices.size() <<
" pointIndices, " << shapeTarget.pointOffsets.size() << " pointOffsets, " <<
shapeTarget.normalOffsets.size() << " normalOffsets, " << shapeTarget.inbetweens.size() <<
" inbetweens";
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
++animMeshIdx;
}

View File

@@ -43,29 +43,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Declaration of the USD importer class.
*/
#pragma once
#ifndef AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED
#define AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED
#include "tinyusdz.hh"
#include "tydra/render-data.hh"
#include <assimp/BaseImporter.h>
#include <assimp/scene.h>
#include <assimp/types.h>
#include <cstdint>
#include <vector>
#include <cstdint>
#include "tinyusdz.hh"
#include "tydra/render-data.hh"
namespace Assimp {
/**
* @brief USD Importer implementation using tinyusdz.
*/
class USDImporterImplTinyusdz {
public:
/// @brief The class constructor.
USDImporterImplTinyusdz() = default;
/// @brief The class destructor.
~USDImporterImplTinyusdz() = default;
void InternReadFile(
@@ -155,7 +147,5 @@ public:
size_t meshIdx,
const std::string &nameWExt);
};
} // namespace Assimp
#endif // AI_USDLOADER_IMPL_TINYUSDZ_H_INCLUDED

View File

@@ -49,6 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GLTFASSET_H_INC
#define GLTFASSET_H_INC
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_IMPORTER)
#include "AssetLib/glTFCommon/glTFCommon.h"
#include <assimp/Exceptional.h>
#include <list>
@@ -914,16 +916,29 @@ class Asset {
friend struct Buffer; // To access OpenFile
friend class AssetWriter;
private:
IOSystem *mIOSystem;
std::string mCurrentAssetDir;
size_t mSceneLength;
size_t mBodyOffset, mBodyLength;
std::vector<LazyDictBase *> mDicts;
IdMap mUsedIds;
Ref<Buffer> mBodyBuffer;
Asset(Asset &);
Asset &operator=(const Asset &);
public:
// Copy policies
Asset(Asset &) = delete;
Asset &operator=(const Asset &) = delete;
//! Keeps info about the enabled extensions
struct Extensions {
bool KHR_binary_glTF{ false };
bool KHR_materials_common{ false };
bool KHR_binary_glTF;
bool KHR_materials_common;
} extensionsUsed;
AssetMetadata asset;
@@ -948,7 +963,8 @@ public:
Ref<Scene> scene;
explicit Asset(IOSystem *io = nullptr) :
public:
Asset(IOSystem *io = nullptr) :
mIOSystem(io),
asset(),
accessors(*this, "accessors"),
@@ -965,7 +981,7 @@ public:
skins(*this, "skins"),
textures(*this, "textures"),
lights(*this, "lights", "KHR_materials_common") {
// empty
memset(&extensionsUsed, 0, sizeof(extensionsUsed));
}
//! Main function
@@ -981,17 +997,10 @@ public:
private:
void ReadBinaryHeader(IOStream &stream);
void ReadExtensionsUsed(Document &doc);
IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false);
private:
IOSystem *mIOSystem;
std::string mCurrentAssetDir;
size_t mSceneLength;
size_t mBodyOffset, mBodyLength;
std::vector<LazyDictBase *> mDicts;
IdMap mUsedIds;
Ref<Buffer> mBodyBuffer;
void ReadExtensionsUsed(Document &doc);
IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false);
};
} // namespace glTF
@@ -999,4 +1008,6 @@ private:
// Include the implementation of the methods
#include "glTFAsset.inl"
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // GLTFASSET_H_INC

View File

@@ -49,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GLTFASSETWRITER_H_INC
#define GLTFASSETWRITER_H_INC
//#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_IMPORTER)
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_IMPORTER)
#include "glTFAsset.h"
@@ -63,19 +63,8 @@ class AssetWriter
template<class T>
friend void WriteLazyDict(LazyDict<T>& d, AssetWriter& w);
public:
Document mDoc;
Asset& mAsset;
MemoryPoolAllocator<>& mAl;
AssetWriter(Asset& asset);
~AssetWriter() = default;
void WriteFile(const char* path);
void WriteGLBFile(const char* path);
private:
void WriteBinaryData(IOStream* outfile, size_t sceneLength);
void WriteMetadata();
@@ -83,6 +72,17 @@ private:
template<class T>
void WriteObjects(LazyDict<T>& d);
public:
Document mDoc;
Asset& mAsset;
MemoryPoolAllocator<>& mAl;
AssetWriter(Asset& asset);
void WriteFile(const char* path);
void WriteGLBFile(const char* path);
};
}
@@ -90,6 +90,6 @@ private:
// Include the implementation of the methods
#include "glTFAssetWriter.inl"
//#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // GLTFASSETWRITER_H_INC

View File

@@ -42,7 +42,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
#include "AssetLib/glTF/glTFExporter.h"
#include "AssetLib/glTF/glTFAsset.h"
#include "AssetLib/glTF/glTFAssetWriter.h"
#include "PostProcessing/SplitLargeMeshes.h"
@@ -63,10 +62,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <limits>
#include <inttypes.h>
#include <rapidjson/rapidjson.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
// Header files, Open3DGC.
# include <Open3DGC/o3dgcSC3DMCEncoder.h>
@@ -1069,5 +1064,6 @@ void glTFExporter::ExportAnimations()
} // End: for-loop mNumAnimations
}
#endif // ASSIMP_BUILD_NO_GLTF_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@@ -56,6 +56,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <memory>
using namespace Assimp;
using namespace glTF;
@@ -76,74 +78,6 @@ static constexpr aiImporterDesc desc = {
"gltf glb"
};
namespace {
void SetMaterialColorProperty(const std::vector<int> &embeddedTexIdxs, Asset &, TexProperty prop, aiMaterial *mat,
aiTextureType texType, const char *pKey, unsigned int type, unsigned int idx) {
if (prop.texture) {
if (prop.texture->source) {
aiString uri(prop.texture->source->uri);
if (const int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()]; texIdx != -1) { // embedded
// setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
uri.data[0] = '*';
uri.length = 1 + ASSIMP_itoa10(uri.data + 1, AI_MAXLEN - 1, texIdx);
}
mat->AddProperty(&uri, _AI_MATKEY_TEXTURE_BASE, texType, 0);
}
return;
}
aiColor4D col;
CopyValue(prop.color, col);
mat->AddProperty(&col, 1, pKey, type, idx);
}
void SetFace(aiFace &face, int a) {
face.mNumIndices = 1;
face.mIndices = new unsigned int[1];
face.mIndices[0] = a;
}
void SetFace(aiFace &face, int a, int b) {
face.mNumIndices = 2;
face.mIndices = new unsigned int[2];
face.mIndices[0] = a;
face.mIndices[1] = b;
}
void SetFace(aiFace &face, int a, int b, int c) {
face.mNumIndices = 3;
face.mIndices = new unsigned int[3];
face.mIndices[0] = a;
face.mIndices[1] = b;
face.mIndices[2] = c;
}
bool CheckValidFacesIndices(const aiFace *faces, unsigned nFaces, unsigned nVerts) {
for (unsigned i = 0; i < nFaces; ++i) {
for (unsigned j = 0; j < faces[i].mNumIndices; ++j) {
unsigned idx = faces[i].mIndices[j];
if (idx >= nVerts)
return false;
}
}
return true;
}
void createDefaultMaterial(aiScene *scene) {
if (scene == nullptr) {
return;
}
scene->mNumMaterials = 1;
scene->mMaterials = nullptr;
scene->mMaterials = new aiMaterial *[1];
scene->mMaterials[0] = new aiMaterial();
}
} // Anonymous namespace
glTFImporter::glTFImporter() :
mScene(nullptr) {
// empty
@@ -165,15 +99,33 @@ bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool
return false;
}
}
void glTFImporter::ImportMaterials(Asset &r) const {
mScene->mNumMaterials = r.materials.Size();
if (mScene->mNumMaterials == 0) {
createDefaultMaterial(mScene);
inline void SetMaterialColorProperty(const std::vector<int> &embeddedTexIdxs, Asset &, TexProperty prop, aiMaterial *mat,
aiTextureType texType, const char *pKey, unsigned int type, unsigned int idx) {
if (prop.texture) {
if (prop.texture->source) {
aiString uri(prop.texture->source->uri);
if (const int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()]; texIdx != -1) { // embedded
// setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
uri.data[0] = '*';
uri.length = 1 + ASSIMP_itoa10(uri.data + 1, AI_MAXLEN - 1, texIdx);
}
mat->AddProperty(&uri, _AI_MATKEY_TEXTURE_BASE, texType, 0);
}
return;
}
aiColor4D col;
CopyValue(prop.color, col);
mat->AddProperty(&col, 1, pKey, type, idx);
}
void glTFImporter::ImportMaterials(Asset &r) {
mScene->mNumMaterials = static_cast<unsigned int>(r.materials.Size());
mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials];
for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
aiMaterial *aimat = mScene->mMaterials[i] = new aiMaterial();
@@ -196,6 +148,47 @@ void glTFImporter::ImportMaterials(Asset &r) const {
aimat->AddProperty(&mat.shininess, 1, AI_MATKEY_SHININESS);
}
}
if (mScene->mNumMaterials == 0) {
mScene->mNumMaterials = 1;
// Delete the array of length zero created above.
delete[] mScene->mMaterials;
mScene->mMaterials = nullptr;
mScene->mMaterials = new aiMaterial *[1];
mScene->mMaterials[0] = new aiMaterial();
}
}
static void SetFace(aiFace &face, int a) {
face.mNumIndices = 1;
face.mIndices = new unsigned int[1];
face.mIndices[0] = a;
}
static void SetFace(aiFace &face, int a, int b) {
face.mNumIndices = 2;
face.mIndices = new unsigned int[2];
face.mIndices[0] = a;
face.mIndices[1] = b;
}
static void SetFace(aiFace &face, int a, int b, int c) {
face.mNumIndices = 3;
face.mIndices = new unsigned int[3];
face.mIndices[0] = a;
face.mIndices[1] = b;
face.mIndices[2] = c;
}
static bool CheckValidFacesIndices(const aiFace *faces, unsigned nFaces, unsigned nVerts) {
for (unsigned i = 0; i < nFaces; ++i) {
for (unsigned j = 0; j < faces[i].mNumIndices; ++j) {
unsigned idx = faces[i].mIndices[j];
if (idx >= nVerts)
return false;
}
}
return true;
}
void glTFImporter::ImportMeshes(Asset &r) {
@@ -459,7 +452,7 @@ void glTFImporter::ImportMeshes(Asset &r) {
CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
}
void glTFImporter::ImportCameras(Asset &r) const {
void glTFImporter::ImportCameras(Asset &r) {
if (!r.cameras.Size()) {
return;
}
@@ -467,30 +460,29 @@ void glTFImporter::ImportCameras(Asset &r) const {
mScene->mNumCameras = r.cameras.Size();
mScene->mCameras = new aiCamera *[r.cameras.Size()];
for (size_t i = 0; i < r.cameras.Size(); ++i) {
const Camera &cam = r.cameras[i];
mScene->mCameras[i] = new aiCamera();
const auto aiCameraPtr = mScene->mCameras[i];
Camera &cam = r.cameras[i];
aiCamera *aicam = mScene->mCameras[i] = new aiCamera();
if (cam.type == Camera::Perspective) {
aiCameraPtr->mAspect = cam.perspective.aspectRatio;
aiCameraPtr->mHorizontalFOV = cam.perspective.yfov * ((aiCameraPtr->mAspect == 0.f) ? 1.f : aiCameraPtr->mAspect);
aiCameraPtr->mClipPlaneFar = cam.perspective.zfar;
aiCameraPtr->mClipPlaneNear = cam.perspective.znear;
aicam->mAspect = cam.perspective.aspectRatio;
aicam->mHorizontalFOV = cam.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect);
aicam->mClipPlaneFar = cam.perspective.zfar;
aicam->mClipPlaneNear = cam.perspective.znear;
} else {
aiCameraPtr->mClipPlaneFar = cam.ortographic.zfar;
aiCameraPtr->mClipPlaneNear = cam.ortographic.znear;
aiCameraPtr->mHorizontalFOV = 0.0;
aiCameraPtr->mAspect = 1.0f;
aicam->mClipPlaneFar = cam.ortographic.zfar;
aicam->mClipPlaneNear = cam.ortographic.znear;
aicam->mHorizontalFOV = 0.0;
aicam->mAspect = 1.0f;
if (0.f != cam.ortographic.ymag) {
aiCameraPtr->mAspect = cam.ortographic.xmag / cam.ortographic.ymag;
aicam->mAspect = cam.ortographic.xmag / cam.ortographic.ymag;
}
}
}
}
void glTFImporter::ImportLights(Asset &r) const {
if (!r.lights.Size()) {
return;
}
void glTFImporter::ImportLights(Asset &r) {
if (!r.lights.Size()) return;
mScene->mNumLights = r.lights.Size();
mScene->mLights = new aiLight *[r.lights.Size()];
@@ -537,7 +529,7 @@ aiNode *ImportNode(aiScene *pScene, Asset &r, std::vector<unsigned int> &meshOff
aiNode *ainode = new aiNode(node.id);
if (!node.children.empty()) {
ainode->mNumChildren = static_cast<unsigned>(node.children.size());
ainode->mNumChildren = unsigned(node.children.size());
ainode->mChildren = new aiNode *[ainode->mNumChildren];
for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
@@ -586,7 +578,7 @@ aiNode *ImportNode(aiScene *pScene, Asset &r, std::vector<unsigned int> &meshOff
int k = 0;
for (size_t i = 0; i < node.meshes.size(); ++i) {
const int idx = node.meshes[i].GetIndex();
int idx = node.meshes[i].GetIndex();
for (unsigned int j = meshOffsets[idx]; j < meshOffsets[idx + 1]; ++j, ++k) {
ainode->mMeshes[k] = j;
}
@@ -605,14 +597,13 @@ aiNode *ImportNode(aiScene *pScene, Asset &r, std::vector<unsigned int> &meshOff
}
void glTFImporter::ImportNodes(Asset &r) {
if (!r.scene) {
return;
}
if (!r.scene) return;
std::vector<Ref<Node>> rootNodes = r.scene->nodes;
// The root nodes
if (auto numRootNodes = static_cast<unsigned>(rootNodes.size()); numRootNodes == 1) { // a single root node: use it
unsigned int numRootNodes = static_cast<unsigned>(rootNodes.size());
if (numRootNodes == 1) { // a single root node: use it
mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
} else if (numRootNodes > 1) { // more than one root node: create a fake root
aiNode *root = new aiNode("ROOT");
@@ -657,10 +648,11 @@ void glTFImporter::ImportEmbeddedTextures(Asset &r) {
tex->mFilename = img.name;
tex->mWidth = static_cast<unsigned int>(length);
tex->mHeight = 0;
tex->pcData = static_cast<aiTexel *>(data);
tex->pcData = reinterpret_cast<aiTexel *>(data);
if (!img.mimeType.empty()) {
if (const char *ext = strchr(img.mimeType.c_str(), '/') + 1) {
const char *ext = strchr(img.mimeType.c_str(), '/') + 1;
if (ext) {
if (strncmp(ext, "jpeg", 4) == 0) {
ext = "jpg";
}
@@ -674,7 +666,7 @@ void glTFImporter::ImportEmbeddedTextures(Asset &r) {
}
}
void glTFImporter::ImportCommonMetadata(const Asset &a) const {
void glTFImporter::ImportCommonMetadata(Asset &a) {
ai_assert(mScene->mMetaData == nullptr);
const bool hasVersion = !a.asset.version.empty();

View File

@@ -49,6 +49,7 @@ struct aiNode;
namespace glTF {
class Asset;
}
namespace Assimp {
@@ -57,7 +58,7 @@ namespace Assimp {
* Load the glTF format.
* https://github.com/KhronosGroup/glTF/tree/master/specification
*/
class glTFImporter final : public BaseImporter {
class glTFImporter : public BaseImporter {
public:
glTFImporter();
~glTFImporter() override = default;
@@ -69,12 +70,12 @@ protected:
private:
void ImportEmbeddedTextures(glTF::Asset &a);
void ImportMaterials(glTF::Asset &a) const;
void ImportMaterials(glTF::Asset &a);
void ImportMeshes(glTF::Asset &a);
void ImportCameras(glTF::Asset &a) const;
void ImportLights(glTF::Asset &a) const;
void ImportCameras(glTF::Asset &a);
void ImportLights(glTF::Asset &a);
void ImportNodes(glTF::Asset &a);
void ImportCommonMetadata(const glTF::Asset &a) const;
void ImportCommonMetadata(glTF::Asset &a);
private:
std::vector<unsigned int> meshOffsets;

View File

@@ -58,7 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GLTF2ASSET_H_INC
#define GLTF2ASSET_H_INC
//#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include <assimp/Exceptional.h>
@@ -1283,6 +1283,6 @@ inline std::string getContextForErrorMessages(const std::string &id, const std::
// Include the implementation of the methods
#include "glTF2Asset.inl"
//#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // GLTF2ASSET_H_INC

View File

@@ -57,6 +57,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GLTF2ASSETWRITER_H_INC
#define GLTF2ASSETWRITER_H_INC
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include "glTF2Asset.h"
namespace glTF2
@@ -96,4 +98,6 @@ public:
// Include the implementation of the methods
#include "glTF2AssetWriter.inl"
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // GLTF2ASSETWRITER_H_INC

View File

@@ -64,10 +64,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory>
#include <iostream>
#include <rapidjson/rapidjson.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
using namespace rapidjson;
using namespace Assimp;

View File

@@ -45,6 +45,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_GLTF2EXPORTER_H_INC
#define AI_GLTF2EXPORTER_H_INC
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include <assimp/material.h>
#include <assimp/types.h>
#include <assimp/defs.h>
@@ -148,4 +150,6 @@ private:
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // AI_GLTF2EXPORTER_H_INC

View File

@@ -65,7 +65,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>
#include <rapidjson/error/en.h>
using namespace Assimp;
using namespace glTF2;
@@ -1681,8 +1680,7 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset &a) {
const bool hasGenerator = !a.asset.generator.empty();
const bool hasCopyright = !a.asset.copyright.empty();
const bool hasSceneMetadata = a.scene->customExtensions;
const bool hasSceneExtras = a.scene->extras.HasExtras();
if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata || hasSceneExtras) {
if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) {
mScene->mMetaData = new aiMetadata;
if (hasVersion) {
mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
@@ -1696,9 +1694,6 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset &a) {
if (hasSceneMetadata) {
ParseExtensions(mScene->mMetaData, a.scene->customExtensions);
}
if (hasSceneExtras) {
ParseExtras(mScene->mMetaData, a.scene->extras);
}
}
}

View File

@@ -38,11 +38,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
#include "AssetLib/glTFCommon/glTFCommon.h"
namespace glTFCommon {
using namespace glTFCommon::Util;
namespace glTFCommon::Util {
namespace Util {
bool ParseDataURI(const char *const_uri, size_t uriLen, DataURI &out) {
if (nullptr == const_uri) {
@@ -107,4 +111,7 @@ bool ParseDataURI(const char *const_uri, size_t uriLen, DataURI &out) {
return true;
}
} // namespace glTFCommon::Uti
} // namespace Util
} // namespace glTFCommon
#endif

View File

@@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_GLFTCOMMON_H_INC
#define AI_GLFTCOMMON_H_INC
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
#include <assimp/Exceptional.h>
#include <assimp/DefaultLogger.hpp>
@@ -530,4 +532,6 @@ inline Value *FindNumber(Value &val, const char *id) {
} // namespace glTFCommon
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // AI_GLFTCOMMON_H_INC

View File

@@ -401,11 +401,6 @@ ADD_ASSIMP_IMPORTER( IRR
AssetLib/Irr/IRRShared.h
)
ADD_ASSIMP_IMPORTER(JT
AssetLib/JT/JTImporter.cpp
AssetLib/JT/JTImporter.h
)
ADD_ASSIMP_IMPORTER( LWO
AssetLib/LWO/LWOAnimation.cpp
AssetLib/LWO/LWOAnimation.h
@@ -607,12 +602,6 @@ ADD_ASSIMP_IMPORTER( XGL
AssetLib/XGL/XGLLoader.h
)
SET( FBX_COMMON_SRCS
AssetLib/FBX/FBXUtil.h
AssetLib/FBX/FBXUtil.cpp
AssetLib/FBX/FBXCommon.h
)
ADD_ASSIMP_IMPORTER( FBX
AssetLib/FBX/FBXImporter.cpp
AssetLib/FBX/FBXCompileConfig.h
@@ -624,6 +613,8 @@ ADD_ASSIMP_IMPORTER( FBX
AssetLib/FBX/FBXImportSettings.h
AssetLib/FBX/FBXConverter.h
AssetLib/FBX/FBXConverter.cpp
AssetLib/FBX/FBXUtil.h
AssetLib/FBX/FBXUtil.cpp
AssetLib/FBX/FBXDocument.h
AssetLib/FBX/FBXDocument.cpp
AssetLib/FBX/FBXProperties.h
@@ -637,6 +628,7 @@ ADD_ASSIMP_IMPORTER( FBX
AssetLib/FBX/FBXDeformer.cpp
AssetLib/FBX/FBXBinaryTokenizer.cpp
AssetLib/FBX/FBXDocumentUtil.cpp
AssetLib/FBX/FBXCommon.h
)
if (NOT ASSIMP_NO_EXPORT)
@@ -1356,8 +1348,6 @@ SET( assimp_src
${ASSIMP_LOADER_SRCS}
${ASSIMP_EXPORTER_SRCS}
${FBX_COMMON_SRCS}
# Third-party libraries
${unzip_compile_SRCS}
${Poly2Tri_SRCS}

View File

@@ -51,7 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>
#include <utility>
// Forward declarations
#// Forward declarations
class DeboneTest;
namespace Assimp {
@@ -72,9 +72,7 @@ public:
~DeboneProcess() override = default;
// -------------------------------------------------------------------
/**
* @brief Returns whether the processing step is present in the given flag.
*
/** Returns whether the processing step is present in the given flag.
* @param pFlags The processing flags the importer was called with.
* A bitwise combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields,
@@ -83,9 +81,7 @@ public:
bool IsActive( unsigned int pFlags) const override;
// -------------------------------------------------------------------
/**
* @brief Called prior to ExecuteOnScene().
*
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
@@ -93,41 +89,33 @@ public:
protected:
// -------------------------------------------------------------------
/**
* @brief Executes the post processing step on the given imported data.
*
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene) override;
// -------------------------------------------------------------------
/**
* @brief Counts bones total/removable in a given mesh.
*
/** Counts bones total/removable in a given mesh.
* @param pMesh The mesh to process.
* @return false in case of an error.
*/
bool ConsiderMesh( const aiMesh* pMesh);
/// @brief Splits the given mesh by bone count.
/// Splits the given mesh by bone count.
/// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split.
/// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary.
void SplitMesh(const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const;
/// @brief Recursively updates the node's mesh list to account for the changed mesh list
/// @param pNode The root node to update.
/// Recursively updates the node's mesh list to account for the changed mesh list
void UpdateNode(aiNode* pNode) const;
// -------------------------------------------------------------------
/// @brief Apply transformation to a mesh
/// @param mesh The mesh to apply the transformation
/// @param mat The transformation to appy
// Apply transformation to a mesh
void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const;
public:
/// Number of bones present in the scene.
unsigned int mNumBones;
/** Number of bones present in the scene. */
unsigned int mNumBones;
unsigned int mNumBonesCanDoWithout;
float mThreshold;

View File

@@ -1,4 +1,4 @@
/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb
/* stb_image - v2.29 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk
Do this:
@@ -48,7 +48,6 @@ LICENSE
RECENT REVISION HISTORY:
2.30 (2024-05-31) avoid erroneous gcc warning
2.29 (2023-05-xx) optimizations
2.28 (2023-01-29) many error fixes, security errors, just tons of stuff
2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes
@@ -5160,11 +5159,9 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
// non-paletted with tRNS = constant alpha. if header-scanning, we can stop now.
if (scan == STBI__SCAN_header) { ++s->img_n; return 1; }
if (z->depth == 16) {
for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning
tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
} else {
for (k = 0; k < s->img_n && k < 3; ++k)
tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
}
}
break;

View File

@@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = "The Open Asset Importer Lib"
PROJECT_NAME = "My Project"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "6.0.2"
PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF = The official Open-Asset-Importer-Library Repository. Loads 40+ 3D-file-formats into one unified and clean data structure.
PROJECT_BRIEF =
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
@@ -58,7 +58,7 @@ PROJECT_LOGO =
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = out
OUTPUT_DIRECTORY =
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and

View File

@@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2025, assimp team
Copyright (c) 2006-2020, assimp team
All rights reserved.
@@ -58,7 +58,6 @@ using AiAssertHandler = void (*)(const char* failedExpression, const char* file,
// ---------------------------------------------------------------------------
/**
* @brief Set the assert handler.
* @param handler The assertion handler to use.
*/
ASSIMP_API void setAiAssertHandler(AiAssertHandler handler);
@@ -66,18 +65,12 @@ ASSIMP_API void setAiAssertHandler(AiAssertHandler handler);
/** The assert handler which is set by default.
*
* @brief This issues a message to stderr and calls abort.
* @param failedExpression The failed expression as a string.
* @param file The name of the source file.
* @param line The line in the source file.
*/
AI_WONT_RETURN ASSIMP_API void defaultAiAssertHandler(const char* failedExpression, const char* file, int line) AI_WONT_RETURN_SUFFIX;
// ---------------------------------------------------------------------------
/**
* @brief Dispatches an assert violation to the assert handler.
* @param failedExpression The failed expression as a string.
* @param file The name of the source file.
* @param line The line in the source file.
* @brief Dispatches an assert violation to the assert handler.
*/
ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line);

View File

@@ -70,8 +70,7 @@ class BaseProcess;
class SharedPostProcessInfo;
class IOStream;
/// @def AI_MAKE_MAGIC
/// @brief Utility to do char4 to uint32 in a portable manner
// utility to do char4 to uint32 in a portable manner
#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \
(string[1] << 16) + (string[2] << 8) + string[3]))
@@ -189,7 +188,60 @@ public:
* @param extension set to collect file extensions in*/
void GetExtensionList(std::set<std::string> &extensions);
// static utilities
protected:
double importerScale = 1.0;
double fileScale = 1.0;
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure. The
* function is expected to throw an ImportErrorException if there is
* an error. If it terminates normally, the data in aiScene is
* expected to be correct. Override this function to implement the
* actual importing.
* <br>
* The output scene must meet the following requirements:<br>
* <ul>
* <li>At least a root node must be there, even if its only purpose
* is to reference one mesh.</li>
* <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives
* in the mesh are determined automatically in this case.</li>
* <li>the vertex data is stored in a pseudo-indexed "verbose" format.
* In fact this means that every vertex that is referenced by
* a face is unique. Or the other way round: a vertex index may
* not occur twice in a single aiMesh.</li>
* <li>aiAnimation::mDuration may be -1. Assimp determines the length
* of the animation automatically in this case as the length of
* the longest animation channel.</li>
* <li>aiMesh::mBitangents may be nullptr if tangents and normals are
* given. In this case bitangents are computed as the cross product
* between normal and tangent.</li>
* <li>There needn't be a material. If none is there a default material
* is generated. However, it is recommended practice for loaders
* to generate a default material for yourself that matches the
* default material setting for the file format better than Assimp's
* generic default material. Note that default materials *should*
* be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded
* or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy)
* texture. </li>
* </ul>
* If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul>
* <li> at least one mesh must be there</li>
* <li> there may be no meshes with 0 vertices or faces</li>
* </ul>
* This won't be checked (except by the validation step): Assimp will
* crash if one of the conditions is not met!
*
* @param pFile Path of the file to be imported.
* @param pScene The scene object to hold the imported data.
* nullptr is not a valid parameter.
* @param pIOHandler The IO handler to use for any file access.
* nullptr is not a valid parameter. */
virtual void InternReadFile(
const std::string &pFile,
aiScene *pScene,
IOSystem *pIOHandler) = 0;
public: // static utilities
// -------------------------------------------------------------------
/** A utility for CanRead().
*
@@ -342,59 +394,6 @@ public:
}
}
protected:
double importerScale = 1.0;
double fileScale = 1.0;
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure. The
* function is expected to throw an ImportErrorException if there is
* an error. If it terminates normally, the data in aiScene is
* expected to be correct. Override this function to implement the
* actual importing.
* <br>
* The output scene must meet the following requirements:<br>
* <ul>
* <li>At least a root node must be there, even if its only purpose
* is to reference one mesh.</li>
* <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives
* in the mesh are determined automatically in this case.</li>
* <li>the vertex data is stored in a pseudo-indexed "verbose" format.
* In fact this means that every vertex that is referenced by
* a face is unique. Or the other way round: a vertex index may
* not occur twice in a single aiMesh.</li>
* <li>aiAnimation::mDuration may be -1. Assimp determines the length
* of the animation automatically in this case as the length of
* the longest animation channel.</li>
* <li>aiMesh::mBitangents may be nullptr if tangents and normals are
* given. In this case bitangents are computed as the cross product
* between normal and tangent.</li>
* <li>There needn't be a material. If none is there a default material
* is generated. However, it is recommended practice for loaders
* to generate a default material for yourself that matches the
* default material setting for the file format better than Assimp's
* generic default material. Note that default materials *should*
* be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded
* or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy)
* texture. </li>
* </ul>
* If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul>
* <li> at least one mesh must be there</li>
* <li> there may be no meshes with 0 vertices or faces</li>
* </ul>
* This won't be checked (except by the validation step): Assimp will
* crash if one of the conditions is not met!
*
* @param pFile Path of the file to be imported.
* @param pScene The scene object to hold the imported data.
* nullptr is not a valid parameter.
* @param pIOHandler The IO handler to use for any file access.
* nullptr is not a valid parameter. */
virtual void InternReadFile(
const std::string &pFile,
aiScene *pScene,
IOSystem *pIOHandler) = 0;
private:
/* Pushes state into importer for the importer scale */
void UpdateImporterScale(Importer *pImp);

View File

@@ -147,7 +147,7 @@ private:
explicit DefaultLogger(LogSeverity severity);
// ----------------------------------------------------------------------
/** @brief Destructor */
/** @briefDestructor */
~DefaultLogger() override;
/** @brief Logs debug infos, only been written when severity level DEBUG or higher is set */

View File

@@ -52,15 +52,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cmath>
// ------------------------------------------------------------------------------------------------
/// @brief Hashing function taken from
/// http://www.azillionmonkeys.com/qed/hash.html
/// (incremental version)
///
/// This code is Copyright 2004-2008 by Paul Hsieh. It is used here in the belief that
/// Assimp's license is considered compatible with Pauls's derivative license as specified
/// on his web page.
///
/// (stdint.h should have been been included here)
// Hashing function taken from
// http://www.azillionmonkeys.com/qed/hash.html
// (incremental version)
//
// This code is Copyright 2004-2008 by Paul Hsieh. It is used here in the belief that
// Assimp's license is considered compatible with Pauls's derivative license as specified
// on his web page.
//
// (stdint.h should have been been included here)
// ------------------------------------------------------------------------------------------------
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \

View File

@@ -78,30 +78,25 @@ for(LineSplitter splitter(stream);splitter;++splitter) {
// ------------------------------------------------------------------------------------------------
class LineSplitter {
public:
/// The current line index in the data block.
using line_idx = size_t;
typedef size_t line_idx;
// -----------------------------------------
/// @brief The class constructor.
/// @note trim is *always* assumed true if skyp_empty_lines==true
/** construct from existing stream reader
note: trim is *always* assumed true if skyp_empty_lines==true
*/
LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true);
// -----------------------------------------
/// @brief The class destructor.
~LineSplitter() = default;
// -----------------------------------------
/// @brief pseudo-iterator increment
/** pseudo-iterator increment */
LineSplitter& operator++();
// -----------------------------------------
/// @brief pseudo-iterator increment
LineSplitter& operator++(int);
// -----------------------------------------
/// @brief Get a pointer to the beginning of a particular token.
/// @param idx The index into the token.
/// @return The token.
/** get a pointer to the beginning of a particular token */
const char* operator[] (size_t idx) const;
// -----------------------------------------
@@ -110,41 +105,33 @@ public:
void get_tokens(const char* (&tokens)[N]) const;
// -----------------------------------------
/// member access via -> operator.
/** member access */
const std::string* operator -> () const;
// -----------------------------------------
/// member access via * operator.
std::string operator* () const;
/// @brief Will return the end marker, end of the buffer plus one.
/// @return The end pointer marker.
const char *getEnd() const;
// -----------------------------------------
/// boolean context.
/** boolean context */
operator bool() const;
// -----------------------------------------
/// line indices are zero-based, empty lines are included
/** line indices are zero-based, empty lines are included */
operator line_idx() const;
/// @brief Will return the current index.
/// @return The current index.
line_idx get_index() const;
// -----------------------------------------
/// @brief Access the underlying stream object.
/// @return Reference to the stream reader.
/** access the underlying stream object */
StreamReaderLE& get_stream();
// -----------------------------------------
/// !strcmp((*this)->substr(0,strlen(check)),check)
/// @return true if token matches.
/** !strcmp((*this)->substr(0,strlen(check)),check) */
bool match_start(const char* check);
// -----------------------------------------
/// @brief Swallow the next call to ++, return the previous value.
/** swallow the next call to ++, return the previous value. */
void swallow_next_increment();
LineSplitter( const LineSplitter & ) = delete;
@@ -152,17 +139,19 @@ public:
LineSplitter &operator = ( const LineSplitter & ) = delete;
private:
line_idx mIdx{0};
std::string mCur{};
const char *mEnd{nullptr};
StreamReaderLE &mStream;
bool mSwallow{ false };
bool mSkip_empty_lines{ false };1
bool mTrim{ false };
line_idx mIdx;
std::string mCur;
const char *mEnd;
StreamReaderLE& mStream;
bool mSwallow, mSkip_empty_lines, mTrim;
};
AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) :
mIdx(0),
mCur(),
mEnd(nullptr),
mStream(stream),
mSwallow(),
mSkip_empty_lines(skip_empty_lines),
mTrim(trim) {
mCur.reserve(1024);

View File

@@ -69,11 +69,11 @@ class ASSIMP_API LogStream
{
protected:
/** @brief Default constructor */
LogStream() AI_NO_EXCEPT = default;
LogStream() AI_NO_EXCEPT;
public:
/** @brief Virtual destructor */
virtual ~LogStream() = default;
virtual ~LogStream();
// -------------------------------------------------------------------
/** @brief Overwrite this for your own output methods
@@ -99,6 +99,10 @@ public:
}; // !class LogStream
inline LogStream::LogStream() AI_NO_EXCEPT = default;
inline LogStream::~LogStream() = default;
} // Namespace Assimp
#endif // INCLUDED_AI_LOGSTREAM_H

View File

@@ -94,7 +94,7 @@ public:
};
/** @brief Virtual destructor */
virtual ~Logger() = default;
virtual ~Logger();
// ----------------------------------------------------------------------
/** @brief Writes a debug message
@@ -184,12 +184,12 @@ public:
protected:
/**
* @brief Default constructor
* Default constructor
*/
Logger() AI_NO_EXCEPT = default;
Logger() AI_NO_EXCEPT;
/**
* @brief Construction with a given log severity
* Construction with a given log severity
*/
explicit Logger(LogSeverity severity);
@@ -253,9 +253,18 @@ protected:
}
protected:
LogSeverity m_Severity{NORMAL};
LogSeverity m_Severity;
};
// ----------------------------------------------------------------------------------
inline Logger::Logger() AI_NO_EXCEPT :
m_Severity(NORMAL) {
// empty
}
// ----------------------------------------------------------------------------------
inline Logger::~Logger() = default;
// ----------------------------------------------------------------------------------
inline Logger::Logger(LogSeverity severity) :
m_Severity(severity) {

View File

@@ -55,16 +55,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ---------------------------------------------------------------------------
/// @brief The original illum property
// the original illum property
#define AI_MATKEY_OBJ_ILLUM "$mat.illum", 0, 0
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/// @brief Pure key names for all obj texture-related properties
// Pure key names for all obj texture-related properties
//! @cond MATS_DOC_FULL
/// @brief Support for bump -bm
// support for bump -bm
#define _AI_MATKEY_OBJ_BUMPMULT_BASE "$tex.bumpmult"
//! @endcond
@@ -80,4 +80,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//! @endcond
#endif // AI_OBJMATERIAL_H_INC
#endif

View File

@@ -59,69 +59,48 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
/// NOTE: the functions below are mostly intended as replacement for
/// std::upper, std::lower, std::isupper, std::islower, std::isspace.
/// we don't bother of locales. We don't want them. We want reliable
/// (i.e. identical) results across all locales, because we had a lot
/// of issues in the past .
// NOTE: the functions below are mostly intended as replacement for
// std::upper, std::lower, std::isupper, std::islower, std::isspace.
// we don't bother of locales. We don't want them. We want reliable
// (i.e. identical) results across all locales.
/// The functions below accept any character type, but know only
/// about ASCII. However, UTF-32 is the only safe ASCII superset to
/// use since it doesn't have multi-byte sequences.
// The functions below accept any character type, but know only
// about ASCII. However, UTF-32 is the only safe ASCII superset to
// use since it doesn't have multi-byte sequences.
static constexpr unsigned int BufferSize = 4096;
static const unsigned int BufferSize = 4096;
// ---------------------------------------------------------------------------------
/// @brief Returns true, if the character is upper-case.
/// @param in The character to test.
/// @return true if upper-case, false if not.
template <class char_t>
AI_FORCE_INLINE bool IsUpper(char_t in) {
return (in >= (char_t)'A' && in <= (char_t)'Z');
}
// ---------------------------------------------------------------------------------
/// @brief Returns true, if the character is lower-case.
/// @param in The character to test.
/// @return true if lower-case, false if not.
template <class char_t>
AI_FORCE_INLINE bool IsLower(char_t in) {
return (in >= (char_t)'a' && in <= (char_t)'z');
}
// ---------------------------------------------------------------------------------
/// @brief Returns true, if the character is a space.
/// @param in The character to test.
/// @return true if a space, false if not.
template <class char_t>
AI_FORCE_INLINE bool IsSpace(char_t in) {
return (in == (char_t)' ' || in == (char_t)'\t');
}
// ---------------------------------------------------------------------------------
/// @brief Returns true, if the character is a line end.
/// @param in The character to test.
/// @return true if a line end, false if not.
template <class char_t>
AI_FORCE_INLINE bool IsLineEnd(char_t in) {
return (in == (char_t)'\r' || in == (char_t)'\n' || in == (char_t)'\0' || in == (char_t)'\f');
}
// ---------------------------------------------------------------------------------
/// @brief Returns true, if the character is a space or a line end.
/// @param in The character to test.
/// @return true if a space or a line end, false if not.
template <class char_t>
AI_FORCE_INLINE bool IsSpaceOrNewLine(char_t in) {
return IsSpace<char_t>(in) || IsLineEnd<char_t>(in);
}
// ---------------------------------------------------------------------------------
/// @brief Will skip all spaces in a buffer.
/// @param in The incoming buffer.
/// @param out The buffer with skipped data.
/// @param end The end of the buffer.
/// @return true if valid.
template <class char_t>
AI_FORCE_INLINE bool SkipSpaces(const char_t *in, const char_t **out, const char_t *end) {
while ((*in == (char_t)' ' || *in == (char_t)'\t') && in != end) {
@@ -132,21 +111,12 @@ AI_FORCE_INLINE bool SkipSpaces(const char_t *in, const char_t **out, const char
}
// ---------------------------------------------------------------------------------
/// @brief Will skip all spaces in a buffer in-situ.
/// @param inout The in/out buffer.
/// @param end The end of the buffer.
/// @return true if valid.
template <class char_t>
AI_FORCE_INLINE bool SkipSpaces(const char_t **inout, const char_t *end) {
return SkipSpaces<char_t>(*inout, inout, end);
}
// ---------------------------------------------------------------------------------
/// @brief Will skip a line.
/// @param in The incoming buffer.
/// @param out The buffer with skipped data.
/// @param end The end of the buffer.
/// @return true if valid.
template <class char_t>
AI_FORCE_INLINE bool SkipLine(const char_t *in, const char_t **out, const char_t *end) {
while ((*in != (char_t)'\r' && *in != (char_t)'\n' && *in != (char_t)'\0') && *in != (char_t)'#' && in != end) {
@@ -162,20 +132,12 @@ AI_FORCE_INLINE bool SkipLine(const char_t *in, const char_t **out, const char_t
}
// ---------------------------------------------------------------------------------
/// @brief Will skip a line in-situ.
/// @param in The in/out buffer.
/// @param end The end of the buffer.
/// @return true if valid.
template <class char_t>
AI_FORCE_INLINE bool SkipLine(const char_t **inout, const char_t *end) {
return SkipLine<char_t>(*inout, inout, end);
}
// ---------------------------------------------------------------------------------
/// @brief Returns true, if the character is a space or a line end.
/// @param in The character to test.
/// @param out The buffer with the skipped data.
/// @return true if valid.
template <class char_t>
AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t *in, const char_t **out, const char_t *end) {
while ((*in == (char_t)' ' || *in == (char_t)'\t' || *in == (char_t)'\r' || *in == (char_t)'\n') && in != end) {
@@ -186,20 +148,12 @@ AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t *in, const char_t **out,
}
// ---------------------------------------------------------------------------------
/// @brief Returns true, if the character is a space or a line end.
/// @param in The character to test.
/// @param out The buffer with the skipped data.
/// @return true if valid.
template <class char_t>
AI_FORCE_INLINE bool SkipSpacesAndLineEnd(const char_t **inout, const char_t *end) {
return SkipSpacesAndLineEnd<char_t>(*inout, inout, end);
}
// ---------------------------------------------------------------------------------
/// @brief Will return point showing to the next line.
/// @param buffer The in buffer.
/// @param out The next line.
/// @return true if a new lne was found, else false.
template <class char_t>
AI_FORCE_INLINE bool GetNextLine(const char_t *&buffer, char_t out[BufferSize]) {
if ((char_t)'\0' == *buffer) {
@@ -221,20 +175,12 @@ AI_FORCE_INLINE bool GetNextLine(const char_t *&buffer, char_t out[BufferSize])
}
// ---------------------------------------------------------------------------------
/// @brief Returns true, if the character is a number.
/// @param in The character to test.
/// @return true if a number, false if not.
template <class char_t>
AI_FORCE_INLINE bool IsNumeric(char_t in) {
return (in >= '0' && in <= '9') || '-' == in || '+' == in;
}
// ---------------------------------------------------------------------------------
/// @brief Will check an incoming buffer for a given token.
/// @param in The incoming buffer.
/// @param token The token to check for.
/// @param len the buffer length.
/// @return true if token was found, false if not.
template <class char_t>
AI_FORCE_INLINE bool TokenMatch(char_t *&in, const char *token, unsigned int len) {
if (!::strncmp(token, in, len) && IsSpaceOrNewLine(in[len])) {
@@ -250,11 +196,11 @@ AI_FORCE_INLINE bool TokenMatch(char_t *&in, const char *token, unsigned int len
return false;
}
// ---------------------------------------------------------------------------------
/// @brief Case-ignoring version of TokenMatch
/// @param in Input
/// @param token Token to check for
/// @param len Number of characters to check
/// @return true if token was found, false if not.
/** @brief Case-ignoring version of TokenMatch
* @param in Input
* @param token Token to check for
* @param len Number of characters to check
*/
AI_FORCE_INLINE bool TokenMatchI(const char *&in, const char *token, unsigned int len) {
if (!ASSIMP_strincmp(token, in, len) && IsSpaceOrNewLine(in[len])) {
in += len + 1;
@@ -264,9 +210,6 @@ AI_FORCE_INLINE bool TokenMatchI(const char *&in, const char *token, unsigned in
}
// ---------------------------------------------------------------------------------
/// @brief Will skip the next token.
/// @param in The incoming buffer.
/// @param end The end marker of the buffer.
AI_FORCE_INLINE void SkipToken(const char *&in, const char *end) {
SkipSpaces(&in, end);
while (!IsSpaceOrNewLine(*in)) {
@@ -275,10 +218,6 @@ AI_FORCE_INLINE void SkipToken(const char *&in, const char *end) {
}
// ---------------------------------------------------------------------------------
/// @brief Will return the next token as a string.
/// @param in The incoming buffer.
/// @param end The end marker of the buffer.
/// @return The next token.
AI_FORCE_INLINE std::string GetNextToken(const char *&in, const char *end) {
SkipSpacesAndLineEnd(&in, end);
const char *cur = in;
@@ -289,11 +228,12 @@ AI_FORCE_INLINE std::string GetNextToken(const char *&in, const char *end) {
}
// ---------------------------------------------------------------------------------
/// @brief Will perform a simple tokenize.
/// @param str String to tokenize.
/// @param tokens Array with tokens, will be empty if no token was found.
/// @param delimiters Delimiter for tokenize.
/// @return Number of found token.
/** @brief Will perform a simple tokenize.
* @param str String to tokenize.
* @param tokens Array with tokens, will be empty if no token was found.
* @param delimiters Delimiter for tokenize.
* @return Number of found token.
*/
template <class string_type>
AI_FORCE_INLINE unsigned int tokenize(const string_type &str, std::vector<string_type> &tokens,
const string_type &delimiters) {
@@ -318,10 +258,6 @@ AI_FORCE_INLINE unsigned int tokenize(const string_type &str, std::vector<string
return static_cast<unsigned int>(tokens.size());
}
// ---------------------------------------------------------------------------------
/// @brief Will convert the given string to lowercase with stl-strings.
/// @param str The stl-string to convert.
/// @return The lowercase string as a stl-string.
inline std::string ai_stdStrToLower(const std::string &str) {
std::string out(str);
for (size_t i = 0; i < str.size(); ++i) {

View File

@@ -56,46 +56,45 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <map>
namespace Assimp::Profiling {
namespace Assimp {
namespace Profiling {
using namespace Formatter;
// ------------------------------------------------------------------------------------------------
/// @brief Simple wrapper around boost::timer to simplify reporting.
///
/// Timings are automatically dumped to the log file.
/** Simple wrapper around boost::timer to simplify reporting. Timings are automatically
* dumped to the log file.
*/
class Profiler {
public:
/// @brief The class constructor.
Profiler() = default;
/// @brief The class destructor.
~Profiler() = default;
/// @brief Starts a named timer.
/// @param region The profiling region name.
/** Start a named timer */
void BeginRegion(const std::string& region) {
mRegions[region] = std::chrono::system_clock::now();
regions[region] = std::chrono::system_clock::now();
ASSIMP_LOG_DEBUG("START `",region,"`");
}
/// @brief End a specific named timer and write its end time to the log.
/// @param region The profiling region name.
/** End a specific named timer and write its end time to the log */
void EndRegion(const std::string& region) {
if (auto it = mRegions.find(region); it == mRegions.end()) {
RegionMap::const_iterator it = regions.find(region);
if (it == regions.end()) {
return;
}
auto elapsedSeconds = std::chrono::system_clock::now() - mRegions[region];
std::chrono::duration<double> elapsedSeconds = std::chrono::system_clock::now() - regions[region];
ASSIMP_LOG_DEBUG("END `",region,"`, dt= ", elapsedSeconds.count()," s");
}
private:
using RegionMap = std::map<std::string,std::chrono::time_point<std::chrono::system_clock>>;
RegionMap mRegions{};
typedef std::map<std::string,std::chrono::time_point<std::chrono::system_clock>> RegionMap;
RegionMap regions;
};
} // namespace Assimp::Profiling
}
}
#endif // AI_INCLUDED_PROFILER_H

View File

@@ -62,33 +62,32 @@ namespace Assimp {
* module.
*/
class ASSIMP_API CommentRemover {
public:
// class cannot be instanced
CommentRemover() = delete;
~CommentRemover() = delete;
CommentRemover() {}
/// @brief Remove single-line comments.
/// The end of a line is expected to be either NL or CR or NLCR.
/// @param szComment The start sequence of the comment, e.g. "//"
/// @param szBuffer Buffer to work with
/// @param chReplacement Character to be used as replacement
/// for commented lines. By default this is ' '
public:
//! Remove single-line comments. The end of a line is
//! expected to be either NL or CR or NLCR.
//! \param szComment The start sequence of the comment, e.g. "//"
//! \param szBuffer Buffer to work with
//! \param chReplacement Character to be used as replacement
//! for commented lines. By default this is ' '
static void RemoveLineComments(const char* szComment,
char* szBuffer, char chReplacement = ' ');
/// @brief Remove multi-line comments.
/// The end of a line is expected to be either NL or CR or NLCR. Multi-line comments
/// may not be nested (as in C).
/// @param szCommentStart The start sequence of the comment, e.g. "/*"
/// @param szCommentEnd The end sequence of the comment, e.g. "*/"
/// @param szBuffer Buffer to work with
/// @param chReplacement Character to be used as replacement
/// for commented lines. By default this is ' '
//! Remove multi-line comments. The end of a line is
//! expected to be either NL or CR or NLCR. Multi-line comments
//! may not be nested (as in C).
//! \param szCommentStart The start sequence of the comment, e.g. "/*"
//! \param szCommentEnd The end sequence of the comment, e.g. "*/"
//! \param szBuffer Buffer to work with
//! \param chReplacement Character to be used as replacement
//! for commented lines. By default this is ' '
static void RemoveMultiLineComments(const char* szCommentStart,
const char* szCommentEnd,char* szBuffer,
char chReplacement = ' ');
};
} // ! Assimp
#endif // !! AI_REMOVE_COMMENTS_H_INC

View File

@@ -81,30 +81,32 @@ namespace Assimp {
* Describes to which node a scene must be attached to.
*/
struct AttachmentInfo {
AttachmentInfo() = default;
AttachmentInfo(aiScene *_scene, aiNode *_attachToNode) : scene(_scene), attachToNode(_attachToNode) {
// empty
}
~AttachmentInfo() = default;
AttachmentInfo() :
scene(nullptr),
attachToNode(nullptr) {}
aiScene *scene{nullptr};
aiNode *attachToNode{nullptr};
AttachmentInfo(aiScene *_scene, aiNode *_attachToNode) :
scene(_scene), attachToNode(_attachToNode) {}
aiScene *scene;
aiNode *attachToNode;
};
// ---------------------------------------------------------------------------
/// @brief Helper data structure for SceneCombiner.
struct NodeAttachmentInfo {
NodeAttachmentInfo() = default;
~NodeAttachmentInfo() = default;
NodeAttachmentInfo(aiNode *_scene, aiNode *_attachToNode, size_t idx) :
node(_scene), attachToNode(_attachToNode), src_idx(idx) {
// empty
}
NodeAttachmentInfo() :
node(nullptr),
attachToNode(nullptr),
resolved(false),
src_idx(SIZE_MAX) {}
aiNode *node{nullptr};
aiNode *attachToNode{nullptr};
bool resolved{false};
size_t src_idx{SIZE_MAX};
NodeAttachmentInfo(aiNode *_scene, aiNode *_attachToNode, size_t idx) :
node(_scene), attachToNode(_attachToNode), resolved(false), src_idx(idx) {}
aiNode *node;
aiNode *attachToNode;
bool resolved;
size_t src_idx;
};
// ---------------------------------------------------------------------------
@@ -137,7 +139,7 @@ struct NodeAttachmentInfo {
*/
#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY 0x10
using BoneSrcIndex = std::pair<aiBone *, unsigned int> ;
typedef std::pair<aiBone *, unsigned int> BoneSrcIndex;
// ---------------------------------------------------------------------------
/** @brief Helper data structure for SceneCombiner::MergeBones.

View File

@@ -331,11 +331,11 @@ private:
// --------------------------------------------------------------------------------------------
// `static` StreamReaders. Their byte order is fixed and they might be a little bit faster.
#ifdef AI_BUILD_BIG_ENDIAN
using StreamReaderLE = StreamReader<true> ;
using StreamReaderBE = StreamReader<false> ;
typedef StreamReader<true> StreamReaderLE;
typedef StreamReader<false> StreamReaderBE;
#else
using StreamReaderBE = StreamReader<true> ;
using StreamReaderLE = StreamReader<false> ;
typedef StreamReader<true> StreamReaderBE;
typedef StreamReader<false> StreamReaderLE;
#endif
// `dynamic` StreamReader. The byte order of the input data is specified in the

View File

@@ -61,13 +61,12 @@ struct find_node_by_name_predicate {
/// @brief The default constructor.
find_node_by_name_predicate() = default;
/// @brief Constructor with the predicate name
/// @param name The name.
explicit find_node_by_name_predicate(const std::string &name) : mName(name) {
// empty
}
std::string mName; ///< The name to find.
find_node_by_name_predicate(const std::string &name) :
mName(name) {
// empty
}
bool operator()(pugi::xml_node node) const {
return node.name() == mName;
@@ -79,9 +78,6 @@ struct find_node_by_name_predicate {
template <class TNodeType>
struct NodeConverter {
public:
/// @brief Will convert the attribute from the node to an int.
/// @param node The XML-node.
/// @param attribName The name of the attribute.
static int to_int(TNodeType &node, const char *attribName) {
ai_assert(nullptr != attribName);
return node.attribute(attribName).to_int();

View File

@@ -57,24 +57,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
/// @brief This class implements a ZIP archive base file system.
class ZipArchiveIOSystem : public IOSystem {
public:
/// @brief The class constructor with the zip-archive name.
/// @param pIOHandler The io handler
/// @param pFilename The archive name
/// @param pMode The access state
//! Open a Zip using the proffered IOSystem
ZipArchiveIOSystem(IOSystem* pIOHandler, const char *pFilename, const char* pMode = "r");
/// @brief The class constructor with the zip-archive name.
/// @param pIOHandler The io handler
/// @param pFilename The archive name
/// @param pMode The access state
ZipArchiveIOSystem(IOSystem* pIOHandler, const std::string& rFilename, const char* pMode = "r");
/// @brief The class destructor.
~ZipArchiveIOSystem() override;
bool Exists(const char* pFilename) const override;
char getOsSeparator() const override;
IOStream* Open(const char* pFilename, const char* pMode = "rb") override;

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, assimp team
Copyright (c) 2006-2021, assimp team
All rights reserved.

View File

@@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2025, assimp team
Copyright (c) 2006-2021, assimp team
All rights reserved.

View File

@@ -5,7 +5,7 @@
# Open Asset Import Library (ASSIMP)
# ---------------------------------------------------------------------------
#
# Copyright (c) 2006-2025, ASSIMP Development Team
# Copyright (c) 2006-2020, ASSIMP Development Team
#
# All rights reserved.
#

View File

@@ -5,7 +5,7 @@
# Open Asset Import Library (ASSIMP)
# ---------------------------------------------------------------------------
#
# Copyright (c) 2006-2025, ASSIMP Development Team
# Copyright (c) 2006-2020, ASSIMP Development Team
#
# All rights reserved.
#

View File

@@ -147,6 +147,8 @@ SET( IMPORTERS
unit/utBlendImportMaterials.cpp
unit/utBlenderWork.cpp
unit/utBVHImportExport.cpp
unit/utColladaExport.cpp
unit/utColladaImportExport.cpp
unit/utCSMImportExport.cpp
unit/utB3DImportExport.cpp
#unit/utM3DImportExport.cpp
@@ -178,18 +180,6 @@ if(ASSIMP_BUILD_USD_IMPORTER)
)
endif()
if(ASSIMP_BUILD_COLLADA_EXPORTER)
list( APPEND IMPORTERS
unit/utColladaExport.cpp
)
endif()
if(ASSIMP_BUILD_COLLADA_IMPORTER)
list( APPEND IMPORTERS
unit/utColladaImportExport.cpp
)
endif()
SET( MATERIAL
unit/utMaterialSystem.cpp
)

68
test/other/streamload.py Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env python3
"""Read all test files for a particular file format using a single
importer instance. Read them again in reversed order. This is used
to verify that a loader does proper cleanup and can be called
repeatedly."""
import sys
import os
import subprocess
# hack-load utils.py and settings.py from ../regression
sys.path.append(os.path.join('..','regression'))
import utils
import settings
def process_dir(thisdir):
"""Process /thisdir/ recursively"""
res = []
shellparams = {'stdin':subprocess.PIPE,'stdout':sys.stdout,'shell':True}
command = [utils.assimp_bin_path,"testbatchload"]
for f in os.listdir(thisdir):
if os.path.splitext(f)[-1] in settings.exclude_extensions:
continue
fullpath = os.path.join(thisdir, f)
if os.path.isdir(fullpath):
if f != ".svn":
res += process_dir(fullpath)
continue
# import twice, importing the same file again introduces extra risk
# to crash due to garbage data lying around in the importer.
command.append(fullpath)
command.append(fullpath)
if len(command)>2:
# testbatchload returns always 0 if more than one file in the list worked.
# however, if it should segfault, the OS will return something not 0.
command += reversed(command[2:])
if subprocess.call(command, **shellparams):
res.append(thisdir)
return res
def main():
"""Run the test on all registered test repositories"""
utils.find_assimp_or_die()
res = []
for tp in settings.model_directories:
res += process_dir(tp)
[print(f) for f in res]
return 0
if __name__ == '__main__':
res = main()
input('All done, waiting for keystroke ')
sys.exit(res)
# vim: ai ts=4 sts=4 et sw=4

17
test/regression/.project Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>assimp-regression</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?>
<pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
</pydev_project>

View File

@@ -0,0 +1,95 @@
Asset Importer Lib Regression Test Suite
========================================
1) How does it work?
---------------------------------------------------------------------------------
run.py checks all model in the <root>/test/models* folders and compares the result
against a regression database provided with assimp (db.zip). A few failures
are totally fine (see sections 7+). You need to worry if a huge
majority of all files in a particular format (or post-processing configuration)
fails as this might be a sign of a recent regression in assimp's codebase or
gross incompatibility with your system or compiler.
2) What do I need?
---------------------------------------------------------------------------------
- You need Python installed (2.7+, 3.x). On Windows, run the scripts using "py".
- You need to build the assimp command line tool (ASSIMP_BUILD_ASSIMP_TOOLS
CMake build flag). Both run.py and gen_db.py take the full path to the binary
as first command line parameter.
3) How to add more test files?
---------------------------------------------------------------------------------
Use the following procedure:
- Verify the correctness of your assimp build - run the regression suite.
DO NOT continue if more tests fail than usual.
- Add your additional test files to <root>/test/models/<fileformat>, where
<fileformat> is the file type (typically the file extension).
- If you test file does not meet the BSD license requirements, add it to
<root>/test/models-nonbsd/<fileformat> so people know to be careful with it.
- Rebuild the regression database:
"gen_db.py <binary> -ixyz" where .xyz is the file extension of the new file.
- Run the regression suite again. There should be no new failures and the new
file should not be among the failures.
- Include the db.zip file with your Pull Request. Travis CI enforces a passing
regression suite (with offenders whitelisted as a last resort).
4) I made a change/fix/patch to a loader, how to update the database?
---------------------------------------------------------------------------------
- Rebuild the regression database using "gen_db.py <binary> -ixyz"
where .xyz is the file extension for which the loader was patched.
- Run the regression suite again. There should be no new failures and the new
file should not be among the failures.
- Include the db.zip file with your Pull Request. Travis CI enforces a passing
regression suite (with offenders whitelisted as a last resort).
5) How to add my whole model repository to the database?
---------------------------------------------------------------------------------
Edit the reg_settings.py file and add the path to your repository to
<<model_directories>>. Then, rebuild the database.
6) So what is actually tested?
---------------------------------------------------------------------------------
The regression database includes mini dumps of the aiScene data structure, i.e.
the scene hierarchy plus the sizes of all data arrays MUST match. Floating-point
data buffers, such as vertex positions are handled less strictly: min, max and
average values are stored with low precision. This takes hardware- or
compiler-specific differences in floating-point computations into account.
Generally, almost all significant regressions will be detected while the
number of false positives is relatively low.
7) The test suite fails, what do do?
---------------------------------------------------------------------------------
Get back to <root>/test/results and look at regression_suite_failures.txt.
It contains a list of all files which failed the test. Failing dumps are copied to
<root>/test/results/tmp. Both an EXPECTED and an ACTUAL file is produced per test.
The output of "assimp cmpdump" is written to regressions_suite_output.txt. Grep
for the file name in question and locate the log for the failed comparison. It
contains a full trace of which scene elements have been compared before, which
makes it reasonably easy to locate the offending field.
8) fp:fast vs fp:precise fails the test suite (same for gcc equivalents)
---------------------------------------------------------------------------------
As mentioned above, floating-point inaccuracies between differently optimized
builds are not considered regressions and all float comparisons done by the test
suite involve an epsilon to accommodate. However compiler settings that allow
compilers to perform non-IEEE754 compliant optimizations can cause arbitrary
failures in the test suite. Even if the compiler is configured to be IEE754
comformant, there is lots of code in assimp that leaves the compiler a choice
and different compilers make different choices (for example the precision of
float intermediaries is implementation-specified).

View File

@@ -0,0 +1,293 @@
#!/usr/bin/env python3
# -*- Coding: UTF-8 -*-
# ---------------------------------------------------------------------------
# Open Asset Import Library (ASSIMP)
# ---------------------------------------------------------------------------
#
# Copyright (c) 2006-2020, ASSIMP Development 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 Development 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.
# ---------------------------------------------------------------------------
from tkinter import *
import sys
import os
import platform
import run
import subprocess
import result_checker as rc
INFO = 0
WARN = 1
ERROR = 2
# -------------------------------------------------------------------------------
def log( sev, msg ):
"""
This function is used to log info, warnings and errors.
"""
logEntry = ""
if sev == 0:
logEntry = logEntry + "[INFO]: "
elif sev == 1:
logEntry = logEntry + "[WARN]: "
elif sev == 2:
logEntry = logEntry + "[ERR] : "
logEntry = logEntry + str( msg )
print( logEntry )
# -------------------------------------------------------------------------------
class BaseDialog( Toplevel ):
"""
Helper base class for dialogs used in the UI.
"""
def __init__(self, parent, title = None, buttons=""):
"""
Constructor
"""
Toplevel.__init__( self, parent )
self.transient(parent)
if title:
self.title(title)
self.parent = parent
self.result = None
body = Frame(self)
self.initial_focus = self.body(body)
body.pack(padx=5, pady=5)
self.buttonbox(buttons)
self.grab_set()
if not self.initial_focus:
self.initial_focus = self
self.protocol("WM_DELETE_WINDOW", self.cancel)
self.geometry("+%d+%d" % (parent.winfo_rootx() + 50,
parent.winfo_rooty() + 50))
self.initial_focus.focus_set()
self.wait_window(self)
def body(self, master):
# create dialog body. return widget that should have
# initial focus. this method should be overridden
pass
def buttonbox(self, buttons):
# add standard button box. override if you don't want the
# standard buttons
box = Frame(self)
w = Button(box, text="OK", width=40, command=self.ok, default=ACTIVE)
w.pack(side=LEFT, padx=5, pady=5)
self.bind("<Return>", self.ok)
box.pack()
def ok(self, event=None):
if not self.validate():
self.initial_focus.focus_set() # put focus back
return
self.withdraw()
self.update_idletasks()
self.apply()
self.cancel()
def cancel(self, event=None):
# put focus back to the parent window
self.parent.focus_set()
self.destroy()
def validate(self):
return 1 # override
def apply(self):
pass # override
# -------------------------------------------------------------------------------
class VersionDialog( BaseDialog ):
"""
This class is used to create the info dialog.
"""
def body(self, master):
# info will be read from assimp command line tool
version = "Asset importer lib version unknown"
exe = run.getEnvVar( "assimp_path" )
if len( exe ) != 0:
command = [exe, "version" ]
log( INFO, "command = " + str(command))
stdout = subprocess.check_output(command)
for line in stdout.splitlines():
pos = str(line).find( "Version" )
if -1 != pos:
version = line
Label(master, text=version).pack()
def apply(self):
pass
# -------------------------------------------------------------------------------
class SetupDialog( BaseDialog ):
"""
This class is used to create the setup dialog.
"""
def body(self, master):
Label(master, justify=LEFT, text="Assimp: " ).grid(row=0, column=0)
Label(master, justify=LEFT, text=run.getEnvVar("assimp_path")).grid(row=0, column=1)
Label(master, text="New executable:").grid(row=1)
self.e1 = Entry(master)
self.e1.grid(row=1, column=1)
return self.e1 # initial focus
def apply(self):
exe = str( self.e1.get() )
if len( exe ) == 0:
return 0
if os.path.isfile( exe ):
log( INFO, "Set executable at " + exe)
self.assimp_bin_path = exe
run.setEnvVar("assimp_path", self.assimp_bin_path)
else:
log( ERROR, "Executable not found at "+exe )
return 0
# -------------------------------------------------------------------------------
class RegDialog( object ):
"""
This class is used to create a simplified user interface for running the regression test suite.
"""
def __init__(self, bin_path ):
"""
Constructs the dialog, you can define which executable shal be used.
@param bin_path [in] Path to assimp binary.
"""
run.setEnvVar( "assimp_path", bin_path )
self.b_run_ = None
self.b_update_ = None
self.b_res_checker_ = None
self.b_quit_ = None
if platform.system() == "Windows":
self.editor = "notepad"
elif platform.system() == "Linux":
self.editor = "vim"
self.root = None
self.width=40
def run_reg(self):
log(INFO, "Starting regression test suite.")
run.run_test()
rc.run()
self.b_update_.config( state=ACTIVE )
return 0
def reg_update(self):
assimp_exe = run.getEnvVar( "assimp_path" )
if len( assimp_exe ) == 0:
return 1
exe = "python"
command = [ exe, "gen_db.py", assimp_exe ]
log(INFO, "command = " + str(command))
stdout = subprocess.call(command)
log(INFO, stdout)
return 0
def shop_diff( self ):
log(WARN, "ToDo!")
return 0
def open_log(self):
command = [ self.editor, "../results/run_regression_suite_output.txt", ]
log(INFO, "command = " + str( command ) )
r = subprocess.call(command)
return 0
def show_version( self ):
d = VersionDialog( self.root )
return 0
def setup(self):
d = SetupDialog( self.root )
return 0
def quit(self):
log( INFO, "quit" )
sys.exit( 0 )
def initUi(self):
# create the frame with buttons
self.root = Tk()
self.root.title( "Assimp-Regression UI")
self.b_run_ = Button( self.root, text="Run regression ", command=self.run_reg, width = self.width )
self.b_update_ = Button( self.root, text="Update database", command=self.reg_update, width = self.width )
self.b_show_diff_ = Button( self.root, text="Show diff", command=self.shop_diff, width = self.width )
self.b_log_ = Button( self.root, text="Open log", command=self.open_log, width = self.width )
self.b_setup_ = Button( self.root, text="Setup", command=self.setup, width = self.width )
self.b_version_ = Button( self.root, text="Show version", command=self.show_version, width = self.width )
self.b_quit_ = Button( self.root, text="Quit", command=self.quit, width = self.width )
# define the used grid
self.b_run_.grid( row=0, column=0, sticky=W+E )
self.b_update_.grid( row=1, column=0, sticky=W+E )
self.b_show_diff_.grid( row=2, column=0, sticky=W+E )
self.b_log_.grid( row=3, column=0, sticky=W+E )
self.b_setup_.grid( row=4, column=0, sticky=W+E )
self.b_version_.grid( row=5, column=0, sticky=W+E )
self.b_quit_.grid( row=6, column=0, sticky=W+E )
#self.b_update_.config( state=DISABLED )
self.b_show_diff_.config( state=DISABLED )
# run mainloop
self.root.mainloop()
# -------------------------------------------------------------------------------
def getDefaultExecutable():
assimp_bin_path = ""
if platform.system() == "Windows":
assimp_bin_path = '..\\..\\bin\\debug\\assimpd.exe'
elif platform.system() == "Linux":
assimp_bin_path = '../../bin/assimp'
return assimp_bin_path
# -------------------------------------------------------------------------------
if __name__ == "__main__":
if len(sys.argv) > 1:
assimp_bin_path = sys.argv[1]
else:
assimp_bin_path = getDefaultExecutable()
log( INFO, 'Using assimp binary: ' + assimp_bin_path )
dlg = RegDialog(assimp_bin_path)
dlg.initUi()
# vim: ai ts=4 sts=4 et sw=4

225
test/regression/gen_db.py Normal file
View File

@@ -0,0 +1,225 @@
#!/usr/bin/env python3
# -*- Coding: UTF-8 -*-
# ---------------------------------------------------------------------------
# Open Asset Import Library (ASSIMP)
# ---------------------------------------------------------------------------
#
# Copyright (c) 2006-2020, ASSIMP Development 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 Development 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.
# ---------------------------------------------------------------------------
"""
Generate the regression database db.zip from the files in the <root>/test/models
directory. Older databases are overwritten with no prompt but can be restored
using Git as needed.
Use --help for usage.
On Windows, use ``py run.py <arguments>`` to make sure command line parameters
are forwarded to the script.
"""
import sys
import os
import subprocess
import zipfile
import settings
import utils
usage = """gen_db [assimp_binary] [-i=...] [-e=...] [-p] [-n]
The assimp_cmd (or assimp) binary to use is specified by the first
command line argument and defaults to ``assimp``.
To build, set ``ASSIMP_BUILD_ASSIMP_TOOLS=ON`` in CMake. If generating
configs for an IDE, make sure to build the assimp_cmd project.
-i,--include: List of file extensions to update dumps for. If omitted,
all file extensions are updated except those in `exclude`.
Example: -ixyz,abc
-i.xyz,.abc
--include=xyz,abc
-e,--exclude: Merged with settings.exclude_extensions to produce a
list of all file extensions to ignore. If dumps exist,
they are not altered. If not, theu are not created.
-p,--preview: Preview list of file extensions touched by the update.
Dont' change anything.
-n,--nozip: Don't pack to ZIP archive. Keep all dumps in individual files.
"""
# -------------------------------------------------------------------------------
def process_dir(d, outfile, file_filter):
""" Generate small dump records for all files in 'd' """
print("Processing directory " + d)
num = 0
for f in os.listdir(d):
fullp = os.path.join(d, f)
if os.path.isdir(fullp) and not f == ".svn":
num += process_dir(fullp, outfile, file_filter)
continue
if file_filter(f):
for pp in settings.pp_configs_to_test:
num += 1
print("DUMP " + fullp + "\n post-processing: " + pp)
outf = os.path.join(os.getcwd(), settings.database_name,
utils.hashing(fullp, pp))
cmd = [ assimp_bin_path, "dump", fullp, outf, "-b", "-s", "-l" ] + pp.split()
outfile.write("assimp dump "+"-"*80+"\n")
outfile.flush()
if subprocess.call(cmd, stdout=outfile, stderr=outfile, shell=False):
print("Failure processing " + fullp)
# spit out an empty file to indicate that this failure is expected
with open(outf,'wb') as f:
pass
return num
# -------------------------------------------------------------------------------
def make_zip():
"""Zip the contents of ./<settings.database_name>
to <settings.database_name>.zip using DEFLATE
compression to minimize the file size. """
num = 0
zipout = zipfile.ZipFile(settings.database_name + ".zip", "w", zipfile.ZIP_DEFLATED)
for f in os.listdir(settings.database_name):
p = os.path.join(settings.database_name, f)
zipout.write(p, f)
if settings.remove_old:
os.remove(p)
num += 1
if settings.remove_old:
os.rmdir(settings.database_name)
bad = zipout.testzip()
assert bad is None
print("="*60)
print("Database contains {0} entries".format(num))
# -------------------------------------------------------------------------------
def extract_zip():
"""Unzip <settings.database_name>.zip to
./<settings.database_name>"""
try:
zipout = zipfile.ZipFile(settings.database_name + ".zip", "r", 0)
zipout.extractall(path=settings.database_name)
except (RuntimeError,IOError) as r:
print(r)
print("failed to extract previous ZIP contents. "\
"DB is generated from scratch.")
# -------------------------------------------------------------------------------
def gen_db(ext_list,outfile):
"""Generate the crash dump database in
./<settings.database_name>"""
try:
os.mkdir(settings.database_name)
except OSError:
pass
num = 0
for tp in settings.model_directories:
num += process_dir(tp, outfile,
lambda x: os.path.splitext(x)[1].lower() in ext_list and not x in settings.files_to_ignore)
print("="*60)
print("Updated {0} entries".format(num))
# -------------------------------------------------------------------------------
if __name__ == "__main__":
def clean(f):
f = f.strip("* \'")
return "."+f if f[:1] != '.' else f
if len(sys.argv) <= 1 or sys.argv[1] == "--help" or sys.argv[1] == "-h":
print(usage)
sys.exit(0)
assimp_bin_path = sys.argv[1]
ext_list, preview, nozip = None, False, False
for m in sys.argv[2:]:
if m[:10]=="--exclude=":
settings.exclude_extensions += map(clean, m[10:].split(","))
elif m[:2]=="-e":
settings.exclude_extensions += map(clean, m[2:].split(","))
elif m[:10]=="--include=":
ext_list = m[10:].split(",")
elif m[:2]=="-i":
ext_list = m[2:].split(",")
elif m=="-p" or m == "--preview":
preview = True
elif m=="-n" or m == "--nozip":
nozip = True
else:
print("Unrecognized parameter: " + m)
sys.exit(-1)
outfile = open(os.path.join("..", "results", "gen_regression_db_output.txt"), "w")
if ext_list is None:
(ext_list, err) = subprocess.Popen([assimp_bin_path, "listext"],
stdout=subprocess.PIPE).communicate()
ext_list = str(ext_list.strip()).lower().split(";")
# todo: Fix for multi dot extensions like .skeleton.xml
ext_list = list(filter(lambda f: not f in settings.exclude_extensions,
map(clean, ext_list)))
print('File extensions processed: ' + ', '.join(ext_list))
if preview:
sys.exit(1)
extract_zip()
gen_db(ext_list,outfile)
make_zip()
print("="*60)
input("Press any key to continue")
sys.exit(0)
# vim: ai ts=4 sts=4 et sw=4

View File

@@ -0,0 +1,113 @@
#!/usr/bin/env python3
# -*- Coding: UTF-8 -*-
# ---------------------------------------------------------------------------
# Open Asset Import Library (ASSIMP)
# ---------------------------------------------------------------------------
#
# Copyright (c) 2006-2020, ASSIMP Development 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 Development 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.
# ---------------------------------------------------------------------------
"""
This script runs as part of the Travis CI build on Github and controls
whether a patch passes the regression test suite.
Given the failures encountered by the regression suite runner (run.py) in
../results/whitelist.csv
and the current whitelist of failures considered acceptable in
./run_regression_suite_failures_whitelisted.csv
determine PASSED or FAILED.
"""
import sys
import os
RESULTS_FILE = os.path.join('..', 'results', 'run_regression_suite_failures.csv')
WHITELIST_FILE = os.path.join('whitelist.csv')
BANNER = """
*****************************************************************
Regression suite result checker
(test/regression/result_checker.py)
*****************************************************************
"""
def passed(message):
print('\n\n**PASSED: {0}.\n'.format(message))
return 0
def failed(message):
print('\n\n**FAILED: {0}. \nFor more information see test/regression/README.\n'
.format(message))
return -1
def read_results_csv(filename):
parse = lambda line: map(str.strip, line.split(';')[:2])
try:
with open(filename, 'rt') as results:
return dict(parse(line) for line in results.readlines()[1:])
except IOError:
print('Failed to read {0}.'.format(filename))
return None
def run():
print(BANNER)
print('Reading input files.')
result_dict = read_results_csv(RESULTS_FILE)
whitelist_dict = read_results_csv(WHITELIST_FILE)
if result_dict is None or whitelist_dict is None:
return failed('Could not locate input files')
if not result_dict:
return passed('No failures encountered')
print('Failures:\n' + '\n'.join(sorted(result_dict.keys())))
print('Whitelisted:\n' + '\n'.join(sorted(whitelist_dict.keys())))
non_whitelisted_failures = set(result_dict.keys()) - set(whitelist_dict.keys())
print('Failures not whitelisted:\n' + '\n'.join(sorted(non_whitelisted_failures)))
if not non_whitelisted_failures:
return passed('All failures are whitelisted and considered acceptable \n' +
'due to implementation differences, library shortcomings and bugs \n' +
'that have not been fixed for a long time')
return failed('Encountered new regression failures that are not whitelisted. \n' +
'Please carefully review the changes you made and use the gen_db.py script\n' +
'to update the regression database for the affected files')
if __name__ == "__main__":
sys.exit(run())
# vim: ai ts=4 sts=4 et sw=4

320
test/regression/run.py Executable file
View File

@@ -0,0 +1,320 @@
#!/usr/bin/env python3
# -*- Coding: UTF-8 -*-
# ---------------------------------------------------------------------------
# Open Asset Import Library (ASSIMP)
# ---------------------------------------------------------------------------
#
# Copyright (c) 2006-2020, ASSIMP Development 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 Development 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.
# ---------------------------------------------------------------------------
"""
Run the regression test suite using settings from settings.py.
The assimp_cmd (or assimp) binary to use is specified by the first
command line argument and defaults to ``assimp``.
To build, set ``ASSIMP_BUILD_ASSIMP_TOOLS=ON`` in CMake. If generating
configs for an IDE, make sure to build the assimp_cmd project.
On Windows, use ``py run.py <path to assimp>`` to make sure the command
line parameter is forwarded to the script.
"""
import sys
import os
import subprocess
import zipfile
import collections
import multiprocessing
import settings
import utils
# -------------------------------------------------------------------------------
EXPECTED_FAILURE_NOT_MET, DATABASE_LENGTH_MISMATCH, \
DATABASE_VALUE_MISMATCH, IMPORT_FAILURE, \
FILE_NOT_READABLE, COMPARE_SUCCESS, EXPECTED_FAILURE = range(7)
messages = collections.defaultdict(lambda: "<unknown", {
EXPECTED_FAILURE_NOT_MET:
"""Unexpected success during import\n\
\tReturn code was 0""",
DATABASE_LENGTH_MISMATCH:
"""Database mismatch: lengths don't match\n\
\tExpected: {0} Actual: {1}""",
DATABASE_VALUE_MISMATCH:
"""Database mismatch: """,
IMPORT_FAILURE:
"""Unexpected failure during import\n\
\tReturn code was {0}""",
FILE_NOT_READABLE:
"""Unexpected failure reading file""",
COMPARE_SUCCESS:
"""Results match archived reference dump in database\n\
\tNumber of bytes compared: {0}""",
EXPECTED_FAILURE:
"""Expected failure was met.""",
})
outfilename_output = "run_regression_suite_output.txt"
outfilename_failur = "run_regression_suite_failures.csv"
Environment = {}
# -------------------------------------------------------------------------------
class results:
""" Handle formatting of results"""
def __init__(self, zipin):
"""Init, given a ZIPed database """
self.failures = []
self.success = []
self.zipin = zipin
def fail(self, failfile, filename_expect, pp, msg, *args):
"""
Report failure of a sub-test
File f failed a test for pp config pp, failure notice is msg,
*args is format()ting args for msg
"""
print("[FAILURE] " + messages[msg].format(*args))
self.failures.append((failfile, filename_expect, pp))
def ok(self, f, pp, msg, *args):
"""
Report success of a sub-test
File f passed the test, msg is a happy success note,
*args is format()ing args for msg.
"""
print("[SUCCESS] " + messages[msg].format(*args))
self.success.append(f)
def report_results(self):
"""Write results to ../results/run_regression_suite_failures.txt"""
count_success = len(self.success)
count_fail = len(self.failures)
percent_good = float(count_success) / (count_success + count_fail)
print("\n" + ('='*60) + "\n" + "SUCCESS: {0}\nFAILURE: {1}\nPercentage good: {2}".format(
count_success, count_fail, percent_good) +
"\n" + ('='*60) + "\n")
with open(os.path.join('..', 'results',outfilename_failur), "wt") as f:
f.write("ORIGINAL FILE;EXPECTED DUMP\n")
f.writelines(map(
lambda x: x[0] + ' ' + x[2] + ";" + x[1] + "\n", self.failures))
if self.failures:
print("\nSee " + settings.results + "\\" + outfilename_failur
+ " for more details\n\n")
def hasFailures( self ):
""" Return True, if any failures there. """
return 0 != len( self.failures )
# -------------------------------------------------------------------------------
def setEnvVar( var, value ):
print ( "set var " + var +" to" + value)
Environment[ var ] = value
# -------------------------------------------------------------------------------
def getEnvVar( var ):
if var in Environment:
return Environment[ var ]
else:
print ( "Error: cannot find " + var )
return ""
# -------------------------------------------------------------------------------
def prepare_output_dir(fullpath, myhash, app):
outfile = os.path.join(settings.results, "tmp", os.path.split(fullpath)[1] + "_" + myhash)
try:
os.mkdir(outfile)
except OSError:
pass
outfile = os.path.join(outfile, app)
return outfile
# -------------------------------------------------------------------------------
def process_dir(d, outfile_results, zipin, result ):
shellparams = {'stdout':outfile_results, 'stderr':outfile_results, 'shell':False}
print("Processing directory " + d)
all = ""
for f in sorted(os.listdir(d)):
fullpath = os.path.join(d, f)
if os.path.isdir(fullpath) and not f[:1] == '.':
process_dir(fullpath, outfile_results, zipin, result)
continue
if f in settings.files_to_ignore or os.path.splitext(f)[1] in settings.exclude_extensions:
print("Ignoring " + f)
return
for pppreset in settings.pp_configs_to_test:
filehash = utils.hashing(fullpath, pppreset)
failure = False
try:
input_expected = zipin.open(filehash, "r").read()
# empty dump files indicate 'expected import failure'
if not len(input_expected):
failure = True
except KeyError:
# TODO(acgessler): Keep track of this and report as error in the end.
print("Didn't find "+fullpath+" (Hash is "+filehash+") in database. Outdated "+\
"regression database? Use gen_db.zip to re-generate.")
continue
print("-"*60 + "\n " + os.path.realpath(fullpath) + " pp: " + pppreset)
outfile_actual = prepare_output_dir(fullpath, filehash, "ACTUAL")
outfile_expect = prepare_output_dir(fullpath, filehash, "EXPECT")
outfile_results.write("assimp dump "+"-"*80+"\n")
outfile_results.flush()
assimp_bin_path = getEnvVar("assimp_path")
command = [assimp_bin_path,
"dump",
fullpath, outfile_actual, "-b", "-s", "-l" ] +\
pppreset.split()
print( "command = " + str( command ) )
r = subprocess.call(command, **shellparams)
outfile_results.flush()
if r and not failure:
result.fail(fullpath, outfile_expect, pppreset, IMPORT_FAILURE, r)
outfile_results.write("Failed to import\n")
continue
elif failure and not r:
result.fail(fullpath, outfile_expect, pppreset, EXPECTED_FAILURE_NOT_MET)
outfile_results.write("Expected import to fail\n")
continue
elif failure and r:
result.ok(fullpath, pppreset, EXPECTED_FAILURE)
outfile_results.write("Failed as expected, skipping.\n")
continue
with open(outfile_expect, "wb") as s:
s.write(input_expected)
try:
with open(outfile_actual, "rb") as s:
input_actual = s.read()
except IOError:
continue
outfile_results.write("Expected data length: {0}\n".format(len(input_expected)))
outfile_results.write("Actual data length: {0}\n".format(len(input_actual)))
failed = False
if len(input_expected) != len(input_actual):
result.fail(fullpath, outfile_expect, pppreset, DATABASE_LENGTH_MISMATCH,
len(input_expected), len(input_actual))
# Still compare the dumps to see what the difference is
failed = True
outfile_results.write("assimp cmpdump "+"-"*80+"\n")
outfile_results.flush()
command = [ assimp_bin_path, 'cmpdump', outfile_actual, outfile_expect ]
if subprocess.call(command, **shellparams) != 0:
if not failed:
result.fail(fullpath, outfile_expect, pppreset, DATABASE_VALUE_MISMATCH)
continue
result.ok(fullpath, pppreset, COMPARE_SUCCESS, len(input_expected))
# -------------------------------------------------------------------------------
def del_folder_with_contents(folder):
for root, dirs, files in os.walk(folder, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
# -------------------------------------------------------------------------------
def run_test():
tmp_target_path = os.path.join(settings.results, "tmp")
try:
print( "try to make " + tmp_target_path )
os.mkdir(tmp_target_path)
except OSError as oerr:
# clear contents if tmp folder exists already
del_folder_with_contents(tmp_target_path)
try:
zipin = zipfile.ZipFile(settings.database_name + ".zip",
"r", zipfile.ZIP_STORED)
except IOError:
print("Regression database ", settings.database_name,
".zip was not found")
return
res = results(zipin)
with open(os.path.join(settings.results, outfilename_output), "wt") as outfile:
for tp in settings.model_directories:
process_dir(tp, outfile, zipin, res)
res.report_results()
if res.hasFailures():
return 1
return 0
# -------------------------------------------------------------------------------
if __name__ == "__main__":
if len(sys.argv) > 1:
assimp_bin_path = sys.argv[1]
else:
assimp_bin_path = 'assimp'
setEnvVar("assimp_path", assimp_bin_path)
print('Using assimp binary: ' + assimp_bin_path)
sys.exit( run_test() )
# vim: ai ts=4 sts=4 et sw=4

134
test/regression/settings.py Normal file
View File

@@ -0,0 +1,134 @@
#!/usr/bin/env python3
# -*- Coding: UTF-8 -*-
# ---------------------------------------------------------------------------
# Open Asset Import Library (ASSIMP)
# ---------------------------------------------------------------------------
#
# Copyright (c) 2006-2020, ASSIMP Development 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 Development 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.
# ---------------------------------------------------------------------------
"""Shared settings for the regression suite (bold builder and
test scripts rely on this)
"""
import os
# -------------------------------------------------------------------------------
# Files to ignore (with reason)
#
# pond.0.ply - loads with 40k identical triangles, causing postprocessing
# to have quadratic runtime.
# -------------------------------------------------------------------------------
files_to_ignore = ["pond.0.ply"]
# -------------------------------------------------------------------------------
# List of file extensions to be excluded from the regression suite
# File extensions are case insensitive
# -------------------------------------------------------------------------------
exclude_extensions = [
".assbin", ".assxml", ".txt", ".md",
".jpeg", ".jpg", ".png", ".gif", ".tga", ".bmp",
".skeleton", ".skeleton.xml", ".license", ".mtl", ".material", ".pk3"
]
# -------------------------------------------------------------------------------
# Post processing configurations to be included in the test. The
# strings are parameters for assimp_cmd, see assimp_cmd's doxydoc
# for more details.
# The defaults are (validate-data-structure is always enabled, for
# self-explanatory reasons :-):
#
# '-cfull' :apply all post processing except 'og' and 'ptv' (optimize-scenegraph)
# '-og -om' :run optimize-scenegraph in combination with optimize-meshes.
# '-vds -jiv' :join-identical-vertices alone. This is a hotspot where
# floating-point inaccuracies can cause severe damage.
# '-ptv': transform all meshes to world-space
# As you can see, not all possible combinations of pp steps are covered -
# but at least each step is executed at least once on each model.
# -------------------------------------------------------------------------------
pp_configs_to_test = [
"-cfull",
"-og -om -vds",
"-vds -jiv",
"-ptv -gsn -cts -db",
# this is especially important: if no failures are present with this
# preset, the regression is most likely caused by the post
# processing pipeline.
""
]
# -------------------------------------------------------------------------------
# Name of the regression database file to be used
# gen_db.py writes to this directory, run.py checks against this directory.
# If a zip file with the same name exists, its contents are favoured to a
# normal directory, so in order to test against unzipped files the ZIP needs
# to be deleted.
# -------------------------------------------------------------------------------
database_name = "db"
# -------------------------------------------------------------------------------
# List of directories to be processed. Paths are processed recursively.
# -------------------------------------------------------------------------------
model_directories = [
os.path.join("..","models"),
os.path.join("..","models-nonbsd")
]
# -------------------------------------------------------------------------------
# Remove the original database files after the ZIP has been built?
# -------------------------------------------------------------------------------
remove_old = True
# -------------------------------------------------------------------------------
# Bytes to skip at the beginning of a dump. This skips the file header, which
# is currently the same 500 bytes header for both assbin, assxml and minidumps.
# -------------------------------------------------------------------------------
dump_header_skip = 500
# -------------------------------------------------------------------------------
# Directory to write all results and logs to. The dumps pertaining to failed
# tests are written to a subfolder of this directory ('tmp').
# -------------------------------------------------------------------------------
results = os.path.join("..","results")
# Create results directory if it does not exist
if not os.path.exists(results):
os.makedirs(results)
# vim: ai ts=4 sts=4 et sw=4

62
test/regression/utils.py Normal file
View File

@@ -0,0 +1,62 @@
#!/usr/bin/env python3
# -*- Coding: UTF-8 -*-
# ---------------------------------------------------------------------------
# Open Asset Import Library (ASSIMP)
# ---------------------------------------------------------------------------
#
# Copyright (c) 2006-2020, ASSIMP Development 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 Development 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.
# ---------------------------------------------------------------------------
"""Shared stuff for the gen_db and run scripts"""
# -------------------------------------------------------------------------------
def hashing(file,pp):
""" Map an input file and a postprocessing config to an unique hash.
The hash is used to store the item in the database. It
needs to be persistent across different python implementations
and platforms, so we implement the hashing manually.
"""
file = file.lower()
file = file.replace('\\','/')+":"+pp
# SDBM hash
res = 0
for t in file:
res = (ord(t) + (res<<6) + (res<<16) - res) % 2**32
return '{:x}'.format(res)
# vim: ai ts=4 sts=4 et sw=4

View File

@@ -0,0 +1,32 @@
ORIGINAL FILE;EXPECTED DUMP
../models/AC/SphereWithLightUvScaling4X.ac -cfull;../results/tmp/SphereWithLightUvScaling4X.ac_30edbbcd/EXPECT
../models/AC/SphereWithLightUvScaling4X.ac -vds -jiv;../results/tmp/SphereWithLightUvScaling4X.ac_a627320a/EXPECT
../models/AC/sample_subdiv.ac -cfull;../results/tmp/sample_subdiv.ac_669c529e/EXPECT
../models/AC/sample_subdiv.ac -vds -jiv;../results/tmp/sample_subdiv.ac_d5987f9/EXPECT
../models/BLEND/CubeHierarchy_248.blend -cfull;../results/tmp/CubeHierarchy_248.blend_4e04cc99/EXPECT
../models/BLEND/CubeHierarchy_248.blend -og -om -vds;../results/tmp/CubeHierarchy_248.blend_5e78ea9c/EXPECT
../models/BLEND/CubeHierarchy_248.blend -vds -jiv;../results/tmp/CubeHierarchy_248.blend_8aca7a3e/EXPECT
../models/BLEND/CubeHierarchy_248.blend -ptv -gsn -cts -db;../results/tmp/CubeHierarchy_248.blend_1f2b8aa4/EXPECT
../models/BLEND/CubeHierarchy_248.blend ;../results/tmp/CubeHierarchy_248.blend_b827f814/EXPECT
../models/BLEND/SuzanneSubdiv_252.blend -cfull;../results/tmp/SuzanneSubdiv_252.blend_4a8dd3d3/EXPECT
../models/BLEND/SuzanneSubdiv_252.blend -vds -jiv;../results/tmp/SuzanneSubdiv_252.blend_7f41fe84/EXPECT
../models/STL/Spider_ascii.stl -cfull;../results/tmp/Spider_ascii.stl_8047fd91/EXPECT
../models/STL/Spider_ascii.stl -vds -jiv;../results/tmp/Spider_ascii.stl_d4858346/EXPECT
../models/STL/Spider_binary.stl -cfull;../results/tmp/Spider_binary.stl_dd4fe21/EXPECT
../models/STL/Spider_binary.stl -vds -jiv;../results/tmp/Spider_binary.stl_2c8deeb6/EXPECT
../models-nonbsd/BLEND/Bob.blend -cfull;../results/tmp/Bob.blend_7f8349ba/EXPECT
../models-nonbsd/BLEND/Bob.blend -og -om -vds;../results/tmp/Bob.blend_4f4a63d/EXPECT
../models-nonbsd/BLEND/Bob.blend -vds -jiv;../results/tmp/Bob.blend_ab1aa5dd/EXPECT
../models-nonbsd/BLEND/Bob.blend -ptv -gsn -cts -db;../results/tmp/Bob.blend_6a5ec4c5/EXPECT
../models-nonbsd/BLEND/Bob.blend ;../results/tmp/Bob.blend_2fc376b5/EXPECT
../models-nonbsd/BLEND/fleurOptonl.blend -cfull;../results/tmp/fleurOptonl.blend_6e87e039/EXPECT
../models-nonbsd/BLEND/fleurOptonl.blend -og -om -vds;../results/tmp/fleurOptonl.blend_95048e3c/EXPECT
../models-nonbsd/BLEND/fleurOptonl.blend -vds -jiv;../results/tmp/fleurOptonl.blend_52481e9e/EXPECT
../models-nonbsd/BLEND/fleurOptonl.blend -ptv -gsn -cts -db;../results/tmp/fleurOptonl.blend_5e7be44/EXPECT
../models-nonbsd/BLEND/fleurOptonl.blend ;../results/tmp/fleurOptonl.blend_18ca7bb4/EXPECT
../models-nonbsd/LWS/QuickDraw v.2.2_oldformat_56.lws -ptv -gsn -cts -db;../results/tmp/QuickDraw v.2.2_oldformat_56.lws_da6fa6e2/EXPECT
../models-nonbsd/LWS/QuickDraw v.2.2_oldformat_56.lws ;../results/tmp/QuickDraw v.2.2_oldformat_56.lws_d37dcb52/EXPECT
../models-nonbsd/Ogre/OgreSDK/ninja.mesh.xml -cfull;../results/tmp/ninja.mesh.xml_2a5e7eb6/EXPECT
../models-nonbsd/PK3/SGDTT3.pk3 -ptv -gsn -cts -db;../results/tmp/SGDTT3.pk3_591f74f1/EXPECT
../models-nonbsd/PK3/SGDTT3.pk3 ;../results/tmp/SGDTT3.pk3_ad4b2ce1/EXPECT
../models-nonbsd/PK3/SGDTT3.pk3 -cfull;../results/tmp/SGDTT3.pk3_ad4b2ce1/EXPECT
1 ORIGINAL FILE EXPECTED DUMP
2 ../models/AC/SphereWithLightUvScaling4X.ac -cfull ../results/tmp/SphereWithLightUvScaling4X.ac_30edbbcd/EXPECT
3 ../models/AC/SphereWithLightUvScaling4X.ac -vds -jiv ../results/tmp/SphereWithLightUvScaling4X.ac_a627320a/EXPECT
4 ../models/AC/sample_subdiv.ac -cfull ../results/tmp/sample_subdiv.ac_669c529e/EXPECT
5 ../models/AC/sample_subdiv.ac -vds -jiv ../results/tmp/sample_subdiv.ac_d5987f9/EXPECT
6 ../models/BLEND/CubeHierarchy_248.blend -cfull ../results/tmp/CubeHierarchy_248.blend_4e04cc99/EXPECT
7 ../models/BLEND/CubeHierarchy_248.blend -og -om -vds ../results/tmp/CubeHierarchy_248.blend_5e78ea9c/EXPECT
8 ../models/BLEND/CubeHierarchy_248.blend -vds -jiv ../results/tmp/CubeHierarchy_248.blend_8aca7a3e/EXPECT
9 ../models/BLEND/CubeHierarchy_248.blend -ptv -gsn -cts -db ../results/tmp/CubeHierarchy_248.blend_1f2b8aa4/EXPECT
10 ../models/BLEND/CubeHierarchy_248.blend ../results/tmp/CubeHierarchy_248.blend_b827f814/EXPECT
11 ../models/BLEND/SuzanneSubdiv_252.blend -cfull ../results/tmp/SuzanneSubdiv_252.blend_4a8dd3d3/EXPECT
12 ../models/BLEND/SuzanneSubdiv_252.blend -vds -jiv ../results/tmp/SuzanneSubdiv_252.blend_7f41fe84/EXPECT
13 ../models/STL/Spider_ascii.stl -cfull ../results/tmp/Spider_ascii.stl_8047fd91/EXPECT
14 ../models/STL/Spider_ascii.stl -vds -jiv ../results/tmp/Spider_ascii.stl_d4858346/EXPECT
15 ../models/STL/Spider_binary.stl -cfull ../results/tmp/Spider_binary.stl_dd4fe21/EXPECT
16 ../models/STL/Spider_binary.stl -vds -jiv ../results/tmp/Spider_binary.stl_2c8deeb6/EXPECT
17 ../models-nonbsd/BLEND/Bob.blend -cfull ../results/tmp/Bob.blend_7f8349ba/EXPECT
18 ../models-nonbsd/BLEND/Bob.blend -og -om -vds ../results/tmp/Bob.blend_4f4a63d/EXPECT
19 ../models-nonbsd/BLEND/Bob.blend -vds -jiv ../results/tmp/Bob.blend_ab1aa5dd/EXPECT
20 ../models-nonbsd/BLEND/Bob.blend -ptv -gsn -cts -db ../results/tmp/Bob.blend_6a5ec4c5/EXPECT
21 ../models-nonbsd/BLEND/Bob.blend ../results/tmp/Bob.blend_2fc376b5/EXPECT
22 ../models-nonbsd/BLEND/fleurOptonl.blend -cfull ../results/tmp/fleurOptonl.blend_6e87e039/EXPECT
23 ../models-nonbsd/BLEND/fleurOptonl.blend -og -om -vds ../results/tmp/fleurOptonl.blend_95048e3c/EXPECT
24 ../models-nonbsd/BLEND/fleurOptonl.blend -vds -jiv ../results/tmp/fleurOptonl.blend_52481e9e/EXPECT
25 ../models-nonbsd/BLEND/fleurOptonl.blend -ptv -gsn -cts -db ../results/tmp/fleurOptonl.blend_5e7be44/EXPECT
26 ../models-nonbsd/BLEND/fleurOptonl.blend ../results/tmp/fleurOptonl.blend_18ca7bb4/EXPECT
27 ../models-nonbsd/LWS/QuickDraw v.2.2_oldformat_56.lws -ptv -gsn -cts -db ../results/tmp/QuickDraw v.2.2_oldformat_56.lws_da6fa6e2/EXPECT
28 ../models-nonbsd/LWS/QuickDraw v.2.2_oldformat_56.lws ../results/tmp/QuickDraw v.2.2_oldformat_56.lws_d37dcb52/EXPECT
29 ../models-nonbsd/Ogre/OgreSDK/ninja.mesh.xml -cfull ../results/tmp/ninja.mesh.xml_2a5e7eb6/EXPECT
30 ../models-nonbsd/PK3/SGDTT3.pk3 -ptv -gsn -cts -db ../results/tmp/SGDTT3.pk3_591f74f1/EXPECT
31 ../models-nonbsd/PK3/SGDTT3.pk3 ../results/tmp/SGDTT3.pk3_ad4b2ce1/EXPECT
32 ../models-nonbsd/PK3/SGDTT3.pk3 -cfull ../results/tmp/SGDTT3.pk3_ad4b2ce1/EXPECT

View File

@@ -5,6 +5,8 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2025, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@@ -46,7 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
class utLineSplitter : public ::testing::Test {};
class utLineSplitter : public ::testing::Test {
// empty
};
TEST_F(utLineSplitter, tokenizetest) {
DefaultIOSystem fs;

View File

@@ -49,7 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
static constexpr float VertComponents[24 * 3] = {
static const float VertComponents[24 * 3] = {
-0.500000, 0.500000, 0.500000,
-0.500000, 0.500000, -0.500000,
-0.500000, -0.500000, -0.500000,
@@ -76,7 +76,7 @@ static constexpr float VertComponents[24 * 3] = {
0.500000, -0.500000, 0.500000f
};
static constexpr char ObjModel[] =
static const char *ObjModel =
"o 1\n"
"\n"
"# Vertex list\n"
@@ -103,7 +103,7 @@ static constexpr char ObjModel[] =
"\n"
"# End of file\n";
static constexpr char ObjModel_Issue1111[] =
static const char *ObjModel_Issue1111 =
"o 1\n"
"\n"
"# Vertex list\n"
@@ -408,7 +408,7 @@ TEST_F(utObjImportExport, homogeneous_coordinates_divide_by_zero_Test) {
EXPECT_EQ(nullptr, scene);
}
TEST_F(utObjImportExport, zero_based_array_Test) {
TEST_F(utObjImportExport, 0based_array_Test) {
static const char *curObjModel =
"v -0.500000 0.000000 0.400000\n"
"v -0.500000 0.000000 -0.800000\n"

View File

@@ -38,19 +38,22 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
#include "AssetLib/Obj/ObjFileParser.h"
#include "AssetLib/Obj/ObjTools.h"
#include "UnitTestPCH.h"
using namespace ::Assimp;
class utObjTools : public ::testing::Test {};
class utObjTools : public ::testing::Test {
// empty
};
class TestObjFileParser : public ObjFileParser {
public:
TestObjFileParser() = default;
TestObjFileParser() :
ObjFileParser() {
// empty
}
~TestObjFileParser() = default;
@@ -81,7 +84,7 @@ TEST_F(utObjTools, skipDataLine_TwoLines_Success) {
buffer.resize(data.size());
::memcpy(&buffer[0], &data[0], data.size());
test_parser.setBuffer(buffer);
static constexpr size_t Size = 4096UL;
static const size_t Size = 4096UL;
char data_buffer[Size];
test_parser.testCopyNextWord(data_buffer, Size);
@@ -109,5 +112,3 @@ TEST_F(utObjTools, countComponents_TwoLines_Success) {
size_t numComps = test_parser.testGetNumComponentsInDataDefinition();
EXPECT_EQ(3U, numComps);
}
#endif // ASSIMP_BUILD_NO_OBJ_IMPORTER

View File

@@ -52,16 +52,22 @@ using namespace ::Assimp;
class utSIBImporter : public AbstractImportExportBase {
public:
virtual bool importerTest() {
Importer importer;
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/SIB/heffalump.sib", aiProcess_ValidateDataStructure);
return nullptr != scene;
}
};
TEST_F(utSIBImporter, importTest) {
#ifdef ASSIMP_BUILD_NO_SIB_IMPORTER
EXPECT_FALSE(importerTest());
#else
EXPECT_TRUE(importerTest());
#endif
TEST_F(utSIBImporter, createTest) {
bool ok(true);
try {
SIBImporter myImporter;
} catch (...) {
ok = false;
}
EXPECT_TRUE(ok);
}
TEST_F(utSIBImporter, importTest) {
EXPECT_TRUE(importerTest());
}

View File

@@ -56,12 +56,18 @@ public:
}
};
TEST_F(utSMDImporter, createTest) {
bool ok(true);
try {
SMDImporter myImporter;
} catch (...) {
ok = false;
}
EXPECT_TRUE(ok);
}
TEST_F(utSMDImporter, importTest) {
#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
EXPECT_TRUE(importerTest());
#else
EXPECT_FALSE(importerTest());
#endif
}
TEST_F(utSMDImporter, issue_899_Texture_garbage_at_end_of_string_Test) {

View File

@@ -0,0 +1,40 @@
/*
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.
----------------------------------------------------------------------
*/
//