Compare commits

..

12 Commits

Author SHA1 Message Date
Kim Kulling
30c4784619 Use constexpr 2025-11-24 20:13:43 +01:00
Kim Kulling
f7d2de1efc Replace string copy by using pure iterators. 2025-11-23 20:16:40 +01:00
Kim Kulling
74a72a20e2 SIB: Fix unittests 2025-11-16 19:54:45 +01:00
Kim Kulling
07f24a7e93 Update glTF2Exporter.cpp 2025-11-16 17:19:49 +01:00
Kim Kulling
122884557e Update glTF2AssetWriter.h 2025-11-16 17:19:29 +01:00
Kim Kulling
18d84b5d20 Update glTFAsset.h 2025-11-16 17:18:45 +01:00
Kim Kulling
2b3dfb9164 Merge branch 'master' into bugfix/fix_linkage_no_importers_issue-5971 2025-11-16 17:07:58 +01:00
Kim Kulling
9143651e2e Remove commented out GLTF importer guard 2025-11-11 15:36:17 +01:00
Kim Kulling
bec3e1a089 Refactor glTFCommon namespace structure
Refactor namespace declarations for consistency.
2025-11-11 15:35:25 +01:00
Kim Kulling
0e6af01232 Remove unused preprocessor directives in glTF2Exporter 2025-11-11 15:33:42 +01:00
Kim Kulling
c54522f3c1 Merge branch 'master' into bugfix/fix_linkage_no_importers_issue-5971 2025-11-11 09:32:27 +01:00
Kim Kulling
5e084cd1e8 Fix linkage 2025-11-10 21:36:40 +01:00
34 changed files with 1475 additions and 351 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

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,34 +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_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"
}
}
]
"configurePresets": [
{
"name": "assimp_with_tools",
"binaryDir": "${sourceDir}",
"cacheVariables": {
"ASSIMP_BUILD_ASSIMP_TOOLS" : "ON"
}
}
]
}

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

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

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

@@ -140,9 +140,11 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
if (insideCstype) {
switch (*m_DataIt) {
case 'e': {
std::string name;
getNameNoSpace(m_DataIt, m_DataItEnd, name);
insideCstype = name != "end";
char *name{nullptr};
size_t len{0};
getNameNoSpace(m_DataIt, m_DataItEnd, &name, len);
//insideCstype = name != "end";
insideCstype = strncmp(name, "end", len) == 0;
} break;
}
goto pf_skip_line;
@@ -198,32 +200,37 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
case 'u': // Parse a material desc. setter
{
std::string name;
//std::string name;
char *name{nullptr};
size_t len{ 0 };
getNameNoSpace(m_DataIt, m_DataItEnd, &name, len);
getNameNoSpace(m_DataIt, m_DataItEnd, name);
//size_t nextSpace = name.find(' ');
//if (nextSpace != std::string::npos)
// name = name.substr(0, nextSpace);
size_t nextSpace = name.find(' ');
if (nextSpace != std::string::npos)
name = name.substr(0, nextSpace);
if (name == "usemtl") {
//if (name == "usemtl") {
if (strncmp(name, "usemtl", len) == 0) {
getMaterialDesc();
}
} break;
case 'm': // Parse a material library or merging group ('mg')
{
std::string name;
// std::string name;
char *name{nullptr};
size_t len{ 0 };
getNameNoSpace(m_DataIt, m_DataItEnd, &name, len);
getNameNoSpace(m_DataIt, m_DataItEnd, name);
//size_t nextSpace = name.find(' ');
//if (nextSpace != std::string::npos)
// name = name.substr(0, nextSpace);
size_t nextSpace = name.find(' ');
if (nextSpace != std::string::npos)
name = name.substr(0, nextSpace);
if (name == "mg")
if (strncmp(name, "mg", len) == 0)
//if (name == "mg")
getGroupNumberAndResolution();
else if (name == "mtllib")
//else if (name == "mtllib")
else if (strncmp(name, "mtllib", len) == 0)
getMaterialLib();
else
goto pf_skip_line;
@@ -246,9 +253,12 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
case 'c': // handle cstype section start
{
std::string name;
getNameNoSpace(m_DataIt, m_DataItEnd, name);
insideCstype = name == "cstype";
//std::string name;
char *name{nullptr};
size_t len{0};
getNameNoSpace(m_DataIt, m_DataItEnd, &name, len);
//insideCstype = name == "cstype";
insideCstype = strncmp(name, "cstype", len) == 0;
goto pf_skip_line;
}

View File

@@ -177,8 +177,9 @@ inline char_t getName(char_t it, char_t end, std::string &name) {
* @return Current-iterator with new position
*/
template <class char_t>
inline char_t getNameNoSpace(char_t it, char_t end, std::string &name) {
name = "";
inline char_t getNameNoSpace(char_t it, char_t end, char **name, size_t &len) {
*name = nullptr;
len = 0;
if (isEndOfBuffer(it, end)) {
return end;
}
@@ -186,22 +187,23 @@ inline char_t getNameNoSpace(char_t it, char_t end, std::string &name) {
char *pStart = &(*it);
while (!isEndOfBuffer(it, end) && !IsLineEnd(*it) && !IsSpaceOrNewLine(*it)) {
++it;
++len;
}
while (isEndOfBuffer(it, end) || IsLineEnd(*it) || IsSpaceOrNewLine(*it)) {
--it;
--len;
}
++it;
++len;
// Get name
// if there is no name, and the previous char is a separator, come back to start
while (&(*it) < pStart) {
++it;
++len;
}
std::string strName(pStart, &(*it));
if (!strName.empty()) {
name = strName;
}
*name = pStart;
return it;
}
@@ -239,7 +241,7 @@ inline char_t CopyNextWord(char_t it, char_t end, char *pBuffer, size_t length)
*/
template <class char_t>
inline char_t getFloat(char_t it, char_t end, ai_real &value) {
static const size_t BUFFERSIZE = 1024;
static constexpr size_t BUFFERSIZE = 1024;
char buffer[BUFFERSIZE] = {};
it = CopyNextWord<char_t>(it, end, buffer, BUFFERSIZE);
value = (ai_real)fast_atof(buffer);

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

@@ -61,7 +61,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "assimp/MemoryIOWrapper.h"
#include <assimp/StringUtils.h>
#include <assimp/StreamReader.h>
#include <assimp/metadata.h>
#include "io-util.hh" // namespace tinyusdz::io
#include "tydra/scene-access.hh"
@@ -220,9 +219,6 @@ void USDImporterImplTinyusdz::InternReadFile(
textures(render_scene, pScene, nameWExt);
textureImages(render_scene, pScene, nameWExt);
buffers(render_scene, pScene, nameWExt);
std::map<size_t, tinyusdz::tydra::Node> meshNodes;
setupNodes(render_scene, pScene, stage, meshNodes, nameWExt);
pScene->mRootNode = nodesRecursive(nullptr, render_scene.nodes[0], render_scene.skeletons);
setupBlendShapes(render_scene, pScene, nameWExt);
@@ -802,50 +798,11 @@ void USDImporterImplTinyusdz::buffers(
}
}
void USDImporterImplTinyusdz::setupNodes(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const tinyusdz::Stage &usdStage,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes,
const std::string &nameWExt) {
stringstream ss;
pScene->mRootNode = nodes(render_scene, usdStage, meshNodes, nameWExt);
pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
pScene->mRootNode->mMeshes = new unsigned int[pScene->mRootNode->mNumMeshes];
ss.str("");
ss << "setupNodes(): pScene->mNumMeshes: " << pScene->mNumMeshes;
if (pScene->mRootNode != nullptr) {
ss << ", mRootNode->mNumMeshes: " << pScene->mRootNode->mNumMeshes;
}
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
for (unsigned int meshIdx = 0; meshIdx < pScene->mNumMeshes; meshIdx++) {
pScene->mRootNode->mMeshes[meshIdx] = meshIdx;
}
}
aiNode *USDImporterImplTinyusdz::nodes(
const tinyusdz::tydra::RenderScene &render_scene,
const tinyusdz::Stage &usdStage,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes,
const std::string &nameWExt) {
const size_t numNodes{render_scene.nodes.size()};
(void) numNodes; // Ignore unused variable when -Werror enabled
stringstream ss;
ss.str("");
ss << "nodes(): model" << nameWExt << ", numNodes: " << numNodes;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
return nodesRecursive(nullptr, usdStage.root_prims()[0], render_scene.nodes[0], meshNodes);
}
using Assimp::tinyusdzNodeTypeFor;
using Assimp::tinyUsdzMat4ToAiMat4;
using tinyusdz::tydra::NodeType;
aiNode *USDImporterImplTinyusdz::nodesRecursive(
aiNode *pNodeParent,
const tinyusdz::Prim& prim,
const tinyusdz::tydra::Node &node,
const std::vector<tinyusdz::tydra::SkelHierarchy> &skeletons) {
stringstream ss;
@@ -887,22 +844,6 @@ aiNode *USDImporterImplTinyusdz::nodesRecursive(
}
}
// Composition: references
if (prim.metas().references.has_value())
{
const tinyusdz::ListEditQual referenceQual = prim.metas().references->first;
const std::vector<tinyusdz::Reference> references = prim.metas().references->second;
for (const auto &reference : references)
{
cNode->mMetaData = aiMetadata::Alloc(1);
cNode->mMetaData->Add("ref", aiString(reference.asset_path.GetAssetPath()));
}
}
size_t i{0};
for (const auto &childNode: node.children) {
cNode->mChildren[i] = nodesRecursive(cNode, prim.children()[i], childNode, meshNodes);
cNode->mNumChildren = numChildren;
// Done. No more children.

View File

@@ -124,23 +124,8 @@ public:
aiScene *pScene,
const std::string &nameWExt);
void setupNodes(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
const tinyusdz::Stage &usdStage,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes,
const std::string &nameWExt
);
aiNode *nodes(
const tinyusdz::tydra::RenderScene &render_scene,
const tinyusdz::Stage &usdStage,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes,
const std::string &nameWExt);
aiNode *nodesRecursive(
aiNode *pNodeParent,
const tinyusdz::Prim &prim,
const tinyusdz::tydra::Node &node,
const std::vector<tinyusdz::tydra::SkelHierarchy> &skeletons);

View File

@@ -1681,8 +1681,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 +1695,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

@@ -58,7 +58,6 @@ SET( COMPILER_HEADERS
SOURCE_GROUP( Compiler FILES ${COMPILER_HEADERS})
SET( PUBLIC_HEADERS
${HEADER_PATH}/allocator.h
${HEADER_PATH}/anim.h
${HEADER_PATH}/aabb.h
${HEADER_PATH}/ai_assert.h
@@ -168,7 +167,6 @@ SET( Logging_SRCS
SOURCE_GROUP(Logging FILES ${Logging_SRCS})
SET( Common_SRCS
Common/allocator.cpp
Common/StbCommon.h
Common/Compression.cpp
Common/Compression.h

View File

@@ -69,7 +69,7 @@ using namespace Assimp;
namespace Assimp {
// underlying structure for aiPropertyStore
using PropertyMap = BatchLoader::PropertyMap ;
using PropertyMap = BatchLoader::PropertyMap ;
#if defined(__has_warning)
# if __has_warning("-Wordered-compare-function-pointers")

View File

@@ -1,12 +0,0 @@
#include <assimp/allocator.h>
aiAllocator gAllocatoMod;
void *aiAlloc(size_t size) {
return aiAllocator::allocFunc(size);
}
void aiFree(void *ptr) {
aiAllocator::freeFunc(ptr);
}

View File

@@ -79,14 +79,13 @@ 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
LineSplitter(StreamReaderLE& stream, bool skip_empty_lines = true, bool trim = true);
// -----------------------------------------
/// @brief The class destructor.
~LineSplitter() = default;
@@ -99,9 +98,7 @@ public:
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,11 +107,9 @@ 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.
@@ -122,11 +117,11 @@ public:
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.
@@ -134,17 +129,15 @@ public:
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;

View File

@@ -1,97 +0,0 @@
/*
---------------------------------------------------------------------------
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,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#ifndef AI_ALLOCATOR_H_INC
#define AI_ALLOCATOR_H_INC
#include <assimp/types.h>
#include <stddef.h>
#include <stdlib.h>
#include <memory.h>
typedef void* (*alloc_func)(size_t size);
typedef void (*free_func)(void* ptr);
static void *aiDefaultAlloc(size_t size) {
return malloc(size);
}
static void aiDefaultFree(void *ptr) {
free(ptr);
}
struct aiAllocator {
static alloc_func allocFunc;
static free_func freeFunc;
static void DefaultInit() {
allocFunc = aiDefaultAlloc;
freeFunc = aiDefaultFree;
}
static void init(alloc_func alloc_fn, free_func free_fn) {
allocFunc = alloc_fn;
freeFunc = free_fn;
}
} gAllocatoMod;
static void aiRegisterAllocator(alloc_func alloc_fn, free_func free_fn) {
aiAllocator::init(alloc_fn, free_fn);
}
ASSIMP_API void *aiAlloc(size_t size);
ASSIMP_API void aiFree(void *ptr);
#ifdef __cplusplus
extern "C" {
#endif
#define AI_NEW(ai_type, ...) new ai_type(__VA_ARGS__)
#define AI_NEW_ARRAY(ai_type, count) new ai_type[count]()
#define AI_DELETE(ptr) delete ptr
#define AI_DELETE_ARRAY(ptr) delete[] ptr
#ifdef __cplusplus
}
#endif
#endif // AI_ALLOCATOR_H_INC

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