Merge remote-tracking branch 'upstream/master' into fix_export_custom_bindpose_error

pull/2543/head
ywang 2019-07-16 13:17:04 -07:00
commit 4ff4938df4
34 changed files with 1787 additions and 349 deletions

View File

@ -0,0 +1,109 @@
/*
cencoder.c - c source to a base64 encoding algorithm implementation
This is part of the libb64 project, and has been placed in the public domain.
For details, see http://sourceforge.net/projects/libb64
*/
#include "cencode.h" // changed from <B64/cencode.h>
const int CHARS_PER_LINE = 72;
void base64_init_encodestate(base64_encodestate* state_in)
{
state_in->step = step_A;
state_in->result = 0;
state_in->stepcount = 0;
}
char base64_encode_value(char value_in)
{
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
if (value_in > 63) return '=';
return encoding[(int)value_in];
}
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
{
const char* plainchar = plaintext_in;
const char* const plaintextend = plaintext_in + length_in;
char* codechar = code_out;
char result;
char fragment;
result = state_in->result;
switch (state_in->step)
{
while (1)
{
case step_A:
if (plainchar == plaintextend)
{
state_in->result = result;
state_in->step = step_A;
return codechar - code_out;
}
fragment = *plainchar++;
result = (fragment & 0x0fc) >> 2;
*codechar++ = base64_encode_value(result);
result = (fragment & 0x003) << 4;
case step_B:
if (plainchar == plaintextend)
{
state_in->result = result;
state_in->step = step_B;
return codechar - code_out;
}
fragment = *plainchar++;
result |= (fragment & 0x0f0) >> 4;
*codechar++ = base64_encode_value(result);
result = (fragment & 0x00f) << 2;
case step_C:
if (plainchar == plaintextend)
{
state_in->result = result;
state_in->step = step_C;
return codechar - code_out;
}
fragment = *plainchar++;
result |= (fragment & 0x0c0) >> 6;
*codechar++ = base64_encode_value(result);
result = (fragment & 0x03f) >> 0;
*codechar++ = base64_encode_value(result);
++(state_in->stepcount);
if (state_in->stepcount == CHARS_PER_LINE/4)
{
*codechar++ = '\n';
state_in->stepcount = 0;
}
}
}
/* control should not reach here */
return codechar - code_out;
}
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
{
char* codechar = code_out;
switch (state_in->step)
{
case step_B:
*codechar++ = base64_encode_value(state_in->result);
*codechar++ = '=';
*codechar++ = '=';
break;
case step_C:
*codechar++ = base64_encode_value(state_in->result);
*codechar++ = '=';
break;
case step_A:
break;
}
*codechar++ = '\n';
return codechar - code_out;
}

View File

@ -0,0 +1,31 @@
/*
cencode.h - c header for a base64 encoding algorithm
This is part of the libb64 project, and has been placed in the public domain.
For details, see http://sourceforge.net/projects/libb64
*/
#ifndef BASE64_CENCODE_H
#define BASE64_CENCODE_H
typedef enum
{
step_A, step_B, step_C
} base64_encodestep;
typedef struct
{
base64_encodestep step;
char result;
int stepcount;
} base64_encodestate;
void base64_init_encodestate(base64_encodestate* state_in);
char base64_encode_value(char value_in);
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
#endif /* BASE64_CENCODE_H */

View File

@ -0,0 +1,818 @@
/*
Assimp2Json
Copyright (c) 2011, Alexander C. Gessler
Licensed under a 3-clause BSD license. See the LICENSE file for more information.
*/
#ifndef ASSIMP_BUILD_NO_EXPORT
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
#include <assimp/Importer.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/IOStream.hpp>
#include <assimp/IOSystem.hpp>
#include <assimp/scene.h>
#include <sstream>
#include <limits>
#include <cassert>
#include <memory>
#define CURRENT_FORMAT_VERSION 100
// grab scoped_ptr from assimp to avoid a dependency on boost.
//#include <assimp/../../code/BoostWorkaround/boost/scoped_ptr.hpp>
#include "mesh_splitter.h"
extern "C" {
#include "cencode.h"
}
namespace Assimp {
void ExportAssimp2Json(const char*, Assimp::IOSystem*, const aiScene*, const Assimp::ExportProperties*);
Exporter::ExportFormatEntry Assimp2Json_desc = Assimp::Exporter::ExportFormatEntry(
"json",
"Plain JSON representation of the Assimp scene data structure",
"json",
&ExportAssimp2Json,
0u
);
// small utility class to simplify serializing the aiScene to Json
class JSONWriter {
public:
enum {
Flag_DoNotIndent = 0x1,
Flag_WriteSpecialFloats = 0x2,
};
JSONWriter(Assimp::IOStream& out, unsigned int flags = 0u)
: out(out)
, first()
, flags(flags) {
// make sure that all formatting happens using the standard, C locale and not the user's current locale
buff.imbue(std::locale("C"));
}
~JSONWriter() {
Flush();
}
void Flush() {
const std::string s = buff.str();
out.Write(s.c_str(), s.length(), 1);
buff.clear();
}
void PushIndent() {
indent += '\t';
}
void PopIndent() {
indent.erase(indent.end() - 1);
}
void Key(const std::string& name) {
AddIndentation();
Delimit();
buff << '\"' + name + "\": ";
}
template<typename Literal>
void Element(const Literal& name) {
AddIndentation();
Delimit();
LiteralToString(buff, name) << '\n';
}
template<typename Literal>
void SimpleValue(const Literal& s) {
LiteralToString(buff, s) << '\n';
}
void SimpleValue(const void* buffer, size_t len) {
base64_encodestate s;
base64_init_encodestate(&s);
char* const out = new char[std::max(len * 2, static_cast<size_t>(16u))];
const int n = base64_encode_block(reinterpret_cast<const char*>(buffer), static_cast<int>(len), out, &s);
out[n + base64_encode_blockend(out + n, &s)] = '\0';
// base64 encoding may add newlines, but JSON strings may not contain 'real' newlines
// (only escaped ones). Remove any newlines in out.
for (char* cur = out; *cur; ++cur) {
if (*cur == '\n') {
*cur = ' ';
}
}
buff << '\"' << out << "\"\n";
delete[] out;
}
void StartObj(bool is_element = false) {
// if this appears as a plain array element, we need to insert a delimiter and we should also indent it
if (is_element) {
AddIndentation();
if (!first) {
buff << ',';
}
}
first = true;
buff << "{\n";
PushIndent();
}
void EndObj() {
PopIndent();
AddIndentation();
first = false;
buff << "}\n";
}
void StartArray(bool is_element = false) {
// if this appears as a plain array element, we need to insert a delimiter and we should also indent it
if (is_element) {
AddIndentation();
if (!first) {
buff << ',';
}
}
first = true;
buff << "[\n";
PushIndent();
}
void EndArray() {
PopIndent();
AddIndentation();
buff << "]\n";
first = false;
}
void AddIndentation() {
if (!(flags & Flag_DoNotIndent)) {
buff << indent;
}
}
void Delimit() {
if (!first) {
buff << ',';
}
else {
buff << ' ';
first = false;
}
}
private:
template<typename Literal>
std::stringstream& LiteralToString(std::stringstream& stream, const Literal& s) {
stream << s;
return stream;
}
std::stringstream& LiteralToString(std::stringstream& stream, const aiString& s) {
std::string t;
// escape backslashes and single quotes, both would render the JSON invalid if left as is
t.reserve(s.length);
for (size_t i = 0; i < s.length; ++i) {
if (s.data[i] == '\\' || s.data[i] == '\'' || s.data[i] == '\"') {
t.push_back('\\');
}
t.push_back(s.data[i]);
}
stream << "\"";
stream << t;
stream << "\"";
return stream;
}
std::stringstream& LiteralToString(std::stringstream& stream, float f) {
if (!std::numeric_limits<float>::is_iec559) {
// on a non IEEE-754 platform, we make no assumptions about the representation or existence
// of special floating-point numbers.
stream << f;
return stream;
}
// JSON does not support writing Inf/Nan
// [RFC 4672: "Numeric values that cannot be represented as sequences of digits
// (such as Infinity and NaN) are not permitted."]
// Nevertheless, many parsers will accept the special keywords Infinity, -Infinity and NaN
if (std::numeric_limits<float>::infinity() == fabs(f)) {
if (flags & Flag_WriteSpecialFloats) {
stream << (f < 0 ? "\"-" : "\"") + std::string("Infinity\"");
return stream;
}
// we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr
// std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl;
stream << "0.0";
return stream;
}
// f!=f is the most reliable test for NaNs that I know of
else if (f != f) {
if (flags & Flag_WriteSpecialFloats) {
stream << "\"NaN\"";
return stream;
}
// we should print this warning, but we can't - this is called from within a generic assimp exporter, we cannot use cerr
// std::cerr << "warning: cannot represent infinite number literal, substituting 0 instead (use -i flag to enforce Infinity/NaN)" << std::endl;
stream << "0.0";
return stream;
}
stream << f;
return stream;
}
private:
Assimp::IOStream& out;
std::string indent, newline;
std::stringstream buff;
bool first;
unsigned int flags;
};
void Write(JSONWriter& out, const aiVector3D& ai, bool is_elem = true) {
out.StartArray(is_elem);
out.Element(ai.x);
out.Element(ai.y);
out.Element(ai.z);
out.EndArray();
}
void Write(JSONWriter& out, const aiQuaternion& ai, bool is_elem = true) {
out.StartArray(is_elem);
out.Element(ai.w);
out.Element(ai.x);
out.Element(ai.y);
out.Element(ai.z);
out.EndArray();
}
void Write(JSONWriter& out, const aiColor3D& ai, bool is_elem = true) {
out.StartArray(is_elem);
out.Element(ai.r);
out.Element(ai.g);
out.Element(ai.b);
out.EndArray();
}
void Write(JSONWriter& out, const aiMatrix4x4& ai, bool is_elem = true) {
out.StartArray(is_elem);
for (unsigned int x = 0; x < 4; ++x) {
for (unsigned int y = 0; y < 4; ++y) {
out.Element(ai[x][y]);
}
}
out.EndArray();
}
void Write(JSONWriter& out, const aiBone& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("name");
out.SimpleValue(ai.mName);
out.Key("offsetmatrix");
Write(out, ai.mOffsetMatrix, false);
out.Key("weights");
out.StartArray();
for (unsigned int i = 0; i < ai.mNumWeights; ++i) {
out.StartArray(true);
out.Element(ai.mWeights[i].mVertexId);
out.Element(ai.mWeights[i].mWeight);
out.EndArray();
}
out.EndArray();
out.EndObj();
}
void Write(JSONWriter& out, const aiFace& ai, bool is_elem = true) {
out.StartArray(is_elem);
for (unsigned int i = 0; i < ai.mNumIndices; ++i) {
out.Element(ai.mIndices[i]);
}
out.EndArray();
}
void Write(JSONWriter& out, const aiMesh& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("name");
out.SimpleValue(ai.mName);
out.Key("materialindex");
out.SimpleValue(ai.mMaterialIndex);
out.Key("primitivetypes");
out.SimpleValue(ai.mPrimitiveTypes);
out.Key("vertices");
out.StartArray();
for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
out.Element(ai.mVertices[i].x);
out.Element(ai.mVertices[i].y);
out.Element(ai.mVertices[i].z);
}
out.EndArray();
if (ai.HasNormals()) {
out.Key("normals");
out.StartArray();
for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
out.Element(ai.mNormals[i].x);
out.Element(ai.mNormals[i].y);
out.Element(ai.mNormals[i].z);
}
out.EndArray();
}
if (ai.HasTangentsAndBitangents()) {
out.Key("tangents");
out.StartArray();
for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
out.Element(ai.mTangents[i].x);
out.Element(ai.mTangents[i].y);
out.Element(ai.mTangents[i].z);
}
out.EndArray();
out.Key("bitangents");
out.StartArray();
for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
out.Element(ai.mBitangents[i].x);
out.Element(ai.mBitangents[i].y);
out.Element(ai.mBitangents[i].z);
}
out.EndArray();
}
if (ai.GetNumUVChannels()) {
out.Key("numuvcomponents");
out.StartArray();
for (unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) {
out.Element(ai.mNumUVComponents[n]);
}
out.EndArray();
out.Key("texturecoords");
out.StartArray();
for (unsigned int n = 0; n < ai.GetNumUVChannels(); ++n) {
const unsigned int numc = ai.mNumUVComponents[n] ? ai.mNumUVComponents[n] : 2;
out.StartArray(true);
for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
for (unsigned int c = 0; c < numc; ++c) {
out.Element(ai.mTextureCoords[n][i][c]);
}
}
out.EndArray();
}
out.EndArray();
}
if (ai.GetNumColorChannels()) {
out.Key("colors");
out.StartArray();
for (unsigned int n = 0; n < ai.GetNumColorChannels(); ++n) {
out.StartArray(true);
for (unsigned int i = 0; i < ai.mNumVertices; ++i) {
out.Element(ai.mColors[n][i].r);
out.Element(ai.mColors[n][i].g);
out.Element(ai.mColors[n][i].b);
out.Element(ai.mColors[n][i].a);
}
out.EndArray();
}
out.EndArray();
}
if (ai.mNumBones) {
out.Key("bones");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumBones; ++n) {
Write(out, *ai.mBones[n]);
}
out.EndArray();
}
out.Key("faces");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumFaces; ++n) {
Write(out, ai.mFaces[n]);
}
out.EndArray();
out.EndObj();
}
void Write(JSONWriter& out, const aiNode& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("name");
out.SimpleValue(ai.mName);
out.Key("transformation");
Write(out, ai.mTransformation, false);
if (ai.mNumMeshes) {
out.Key("meshes");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumMeshes; ++n) {
out.Element(ai.mMeshes[n]);
}
out.EndArray();
}
if (ai.mNumChildren) {
out.Key("children");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumChildren; ++n) {
Write(out, *ai.mChildren[n]);
}
out.EndArray();
}
out.EndObj();
}
void Write(JSONWriter& out, const aiMaterial& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("properties");
out.StartArray();
for (unsigned int i = 0; i < ai.mNumProperties; ++i) {
const aiMaterialProperty* const prop = ai.mProperties[i];
out.StartObj(true);
out.Key("key");
out.SimpleValue(prop->mKey);
out.Key("semantic");
out.SimpleValue(prop->mSemantic);
out.Key("index");
out.SimpleValue(prop->mIndex);
out.Key("type");
out.SimpleValue(prop->mType);
out.Key("value");
switch (prop->mType) {
case aiPTI_Float:
if (prop->mDataLength / sizeof(float) > 1) {
out.StartArray();
for (unsigned int i = 0; i < prop->mDataLength / sizeof(float); ++i) {
out.Element(reinterpret_cast<float*>(prop->mData)[i]);
}
out.EndArray();
}
else {
out.SimpleValue(*reinterpret_cast<float*>(prop->mData));
}
break;
case aiPTI_Integer:
if (prop->mDataLength / sizeof(int) > 1) {
out.StartArray();
for (unsigned int i = 0; i < prop->mDataLength / sizeof(int); ++i) {
out.Element(reinterpret_cast<int*>(prop->mData)[i]);
}
out.EndArray();
} else {
out.SimpleValue(*reinterpret_cast<int*>(prop->mData));
}
break;
case aiPTI_String:
{
aiString s;
aiGetMaterialString(&ai, prop->mKey.data, prop->mSemantic, prop->mIndex, &s);
out.SimpleValue(s);
}
break;
case aiPTI_Buffer:
{
// binary data is written as series of hex-encoded octets
out.SimpleValue(prop->mData, prop->mDataLength);
}
break;
default:
assert(false);
}
out.EndObj();
}
out.EndArray();
out.EndObj();
}
void Write(JSONWriter& out, const aiTexture& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("width");
out.SimpleValue(ai.mWidth);
out.Key("height");
out.SimpleValue(ai.mHeight);
out.Key("formathint");
out.SimpleValue(aiString(ai.achFormatHint));
out.Key("data");
if (!ai.mHeight) {
out.SimpleValue(ai.pcData, ai.mWidth);
}
else {
out.StartArray();
for (unsigned int y = 0; y < ai.mHeight; ++y) {
out.StartArray(true);
for (unsigned int x = 0; x < ai.mWidth; ++x) {
const aiTexel& tx = ai.pcData[y*ai.mWidth + x];
out.StartArray(true);
out.Element(static_cast<unsigned int>(tx.r));
out.Element(static_cast<unsigned int>(tx.g));
out.Element(static_cast<unsigned int>(tx.b));
out.Element(static_cast<unsigned int>(tx.a));
out.EndArray();
}
out.EndArray();
}
out.EndArray();
}
out.EndObj();
}
void Write(JSONWriter& out, const aiLight& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("name");
out.SimpleValue(ai.mName);
out.Key("type");
out.SimpleValue(ai.mType);
if (ai.mType == aiLightSource_SPOT || ai.mType == aiLightSource_UNDEFINED) {
out.Key("angleinnercone");
out.SimpleValue(ai.mAngleInnerCone);
out.Key("angleoutercone");
out.SimpleValue(ai.mAngleOuterCone);
}
out.Key("attenuationconstant");
out.SimpleValue(ai.mAttenuationConstant);
out.Key("attenuationlinear");
out.SimpleValue(ai.mAttenuationLinear);
out.Key("attenuationquadratic");
out.SimpleValue(ai.mAttenuationQuadratic);
out.Key("diffusecolor");
Write(out, ai.mColorDiffuse, false);
out.Key("specularcolor");
Write(out, ai.mColorSpecular, false);
out.Key("ambientcolor");
Write(out, ai.mColorAmbient, false);
if (ai.mType != aiLightSource_POINT) {
out.Key("direction");
Write(out, ai.mDirection, false);
}
if (ai.mType != aiLightSource_DIRECTIONAL) {
out.Key("position");
Write(out, ai.mPosition, false);
}
out.EndObj();
}
void Write(JSONWriter& out, const aiNodeAnim& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("name");
out.SimpleValue(ai.mNodeName);
out.Key("prestate");
out.SimpleValue(ai.mPreState);
out.Key("poststate");
out.SimpleValue(ai.mPostState);
if (ai.mNumPositionKeys) {
out.Key("positionkeys");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumPositionKeys; ++n) {
const aiVectorKey& pos = ai.mPositionKeys[n];
out.StartArray(true);
out.Element(pos.mTime);
Write(out, pos.mValue);
out.EndArray();
}
out.EndArray();
}
if (ai.mNumRotationKeys) {
out.Key("rotationkeys");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumRotationKeys; ++n) {
const aiQuatKey& rot = ai.mRotationKeys[n];
out.StartArray(true);
out.Element(rot.mTime);
Write(out, rot.mValue);
out.EndArray();
}
out.EndArray();
}
if (ai.mNumScalingKeys) {
out.Key("scalingkeys");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumScalingKeys; ++n) {
const aiVectorKey& scl = ai.mScalingKeys[n];
out.StartArray(true);
out.Element(scl.mTime);
Write(out, scl.mValue);
out.EndArray();
}
out.EndArray();
}
out.EndObj();
}
void Write(JSONWriter& out, const aiAnimation& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("name");
out.SimpleValue(ai.mName);
out.Key("tickspersecond");
out.SimpleValue(ai.mTicksPerSecond);
out.Key("duration");
out.SimpleValue(ai.mDuration);
out.Key("channels");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumChannels; ++n) {
Write(out, *ai.mChannels[n]);
}
out.EndArray();
out.EndObj();
}
void Write(JSONWriter& out, const aiCamera& ai, bool is_elem = true) {
out.StartObj(is_elem);
out.Key("name");
out.SimpleValue(ai.mName);
out.Key("aspect");
out.SimpleValue(ai.mAspect);
out.Key("clipplanefar");
out.SimpleValue(ai.mClipPlaneFar);
out.Key("clipplanenear");
out.SimpleValue(ai.mClipPlaneNear);
out.Key("horizontalfov");
out.SimpleValue(ai.mHorizontalFOV);
out.Key("up");
Write(out, ai.mUp, false);
out.Key("lookat");
Write(out, ai.mLookAt, false);
out.EndObj();
}
void WriteFormatInfo(JSONWriter& out) {
out.StartObj();
out.Key("format");
out.SimpleValue("\"assimp2json\"");
out.Key("version");
out.SimpleValue(CURRENT_FORMAT_VERSION);
out.EndObj();
}
void Write(JSONWriter& out, const aiScene& ai) {
out.StartObj();
out.Key("__metadata__");
WriteFormatInfo(out);
out.Key("rootnode");
Write(out, *ai.mRootNode, false);
out.Key("flags");
out.SimpleValue(ai.mFlags);
if (ai.HasMeshes()) {
out.Key("meshes");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumMeshes; ++n) {
Write(out, *ai.mMeshes[n]);
}
out.EndArray();
}
if (ai.HasMaterials()) {
out.Key("materials");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumMaterials; ++n) {
Write(out, *ai.mMaterials[n]);
}
out.EndArray();
}
if (ai.HasAnimations()) {
out.Key("animations");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumAnimations; ++n) {
Write(out, *ai.mAnimations[n]);
}
out.EndArray();
}
if (ai.HasLights()) {
out.Key("lights");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumLights; ++n) {
Write(out, *ai.mLights[n]);
}
out.EndArray();
}
if (ai.HasCameras()) {
out.Key("cameras");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumCameras; ++n) {
Write(out, *ai.mCameras[n]);
}
out.EndArray();
}
if (ai.HasTextures()) {
out.Key("textures");
out.StartArray();
for (unsigned int n = 0; n < ai.mNumTextures; ++n) {
Write(out, *ai.mTextures[n]);
}
out.EndArray();
}
out.EndObj();
}
void ExportAssimp2Json(const char* file, Assimp::IOSystem* io, const aiScene* scene, const Assimp::ExportProperties*) {
std::unique_ptr<Assimp::IOStream> str(io->Open(file, "wt"));
if (!str) {
//throw Assimp::DeadlyExportError("could not open output file");
}
// get a copy of the scene so we can modify it
aiScene* scenecopy_tmp;
aiCopyScene(scene, &scenecopy_tmp);
try {
// split meshes so they fit into a 16 bit index buffer
MeshSplitter splitter;
splitter.SetLimit(1 << 16);
splitter.Execute(scenecopy_tmp);
// XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters
JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats);
Write(s, *scenecopy_tmp);
}
catch (...) {
aiFreeScene(scenecopy_tmp);
throw;
}
aiFreeScene(scenecopy_tmp);
}
}
#endif // ASSIMP_BUILD_NO_ASSJSON_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -0,0 +1,320 @@
/*
Assimp2Json
Copyright (c) 2011, Alexander C. Gessler
Licensed under a 3-clause BSD license. See the LICENSE file for more information.
*/
#include "mesh_splitter.h"
#include <assimp/scene.h>
// ----------------------------------------------------------------------------
// Note: this is largely based on assimp's SplitLargeMeshes_Vertex process.
// it is refactored and the coding style is slightly improved, though.
// ----------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void MeshSplitter::Execute( aiScene* pScene) {
std::vector<std::pair<aiMesh*, unsigned int> > source_mesh_map;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
SplitMesh(a, pScene->mMeshes[a],source_mesh_map);
}
const unsigned int size = static_cast<unsigned int>(source_mesh_map.size());
if (size != pScene->mNumMeshes) {
// it seems something has been split. rebuild the mesh list
delete[] pScene->mMeshes;
pScene->mNumMeshes = size;
pScene->mMeshes = new aiMesh*[size]();
for (unsigned int i = 0; i < size;++i) {
pScene->mMeshes[i] = source_mesh_map[i].first;
}
// now we need to update all nodes
UpdateNode(pScene->mRootNode,source_mesh_map);
}
}
// ------------------------------------------------------------------------------------------------
void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map) {
// TODO: should better use std::(multi)set for source_mesh_map.
// for every index in out list build a new entry
std::vector<unsigned int> aiEntries;
aiEntries.reserve(pcNode->mNumMeshes + 1);
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) {
for (unsigned int a = 0, end = static_cast<unsigned int>(source_mesh_map.size()); a < end;++a) {
if (source_mesh_map[a].second == pcNode->mMeshes[i]) {
aiEntries.push_back(a);
}
}
}
// now build the new list
delete pcNode->mMeshes;
pcNode->mNumMeshes = static_cast<unsigned int>(aiEntries.size());
pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
for (unsigned int b = 0; b < pcNode->mNumMeshes;++b) {
pcNode->mMeshes[b] = aiEntries[b];
}
// recursively update children
for (unsigned int i = 0, end = pcNode->mNumChildren; i < end;++i) {
UpdateNode ( pcNode->mChildren[i], source_mesh_map );
}
return;
}
#define WAS_NOT_COPIED 0xffffffff
typedef std::pair <unsigned int,float> PerVertexWeight;
typedef std::vector <PerVertexWeight> VertexWeightTable;
// ------------------------------------------------------------------------------------------------
VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) {
if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones) {
return nullptr;
}
VertexWeightTable* const avPerVertexWeights = new VertexWeightTable[pMesh->mNumVertices];
for (unsigned int i = 0; i < pMesh->mNumBones;++i) {
aiBone* bone = pMesh->mBones[i];
for (unsigned int a = 0; a < bone->mNumWeights;++a) {
const aiVertexWeight& weight = bone->mWeights[a];
avPerVertexWeights[weight.mVertexId].push_back( std::make_pair(i,weight.mWeight) );
}
}
return avPerVertexWeights;
}
// ------------------------------------------------------------------------------------------------
void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map) {
// TODO: should better use std::(multi)set for source_mesh_map.
if (in_mesh->mNumVertices <= LIMIT) {
source_mesh_map.push_back(std::make_pair(in_mesh,a));
return;
}
// build a per-vertex weight list if necessary
VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(in_mesh);
// we need to split this mesh into sub meshes. Estimate submesh size
const unsigned int sub_meshes = (in_mesh->mNumVertices / LIMIT) + 1;
// create a std::vector<unsigned int> to remember which vertices have already
// been copied and to which position (i.e. output index)
std::vector<unsigned int> was_copied_to;
was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED);
// Try to find a good estimate for the number of output faces
// per mesh. Add 12.5% as buffer
unsigned int size_estimated = in_mesh->mNumFaces / sub_meshes;
size_estimated += size_estimated / 8;
// now generate all submeshes
unsigned int base = 0;
while (true) {
const unsigned int out_vertex_index = LIMIT;
aiMesh* out_mesh = new aiMesh();
out_mesh->mNumVertices = 0;
out_mesh->mMaterialIndex = in_mesh->mMaterialIndex;
// the name carries the adjacency information between the meshes
out_mesh->mName = in_mesh->mName;
typedef std::vector<aiVertexWeight> BoneWeightList;
if (in_mesh->HasBones()) {
out_mesh->mBones = new aiBone*[in_mesh->mNumBones]();
}
// clear the temporary helper array
if (base) {
std::fill(was_copied_to.begin(), was_copied_to.end(), WAS_NOT_COPIED);
}
std::vector<aiFace> vFaces;
// reserve enough storage for most cases
if (in_mesh->HasPositions()) {
out_mesh->mVertices = new aiVector3D[out_vertex_index];
}
if (in_mesh->HasNormals()) {
out_mesh->mNormals = new aiVector3D[out_vertex_index];
}
if (in_mesh->HasTangentsAndBitangents()) {
out_mesh->mTangents = new aiVector3D[out_vertex_index];
out_mesh->mBitangents = new aiVector3D[out_vertex_index];
}
for (unsigned int c = 0; in_mesh->HasVertexColors(c);++c) {
out_mesh->mColors[c] = new aiColor4D[out_vertex_index];
}
for (unsigned int c = 0; in_mesh->HasTextureCoords(c);++c) {
out_mesh->mNumUVComponents[c] = in_mesh->mNumUVComponents[c];
out_mesh->mTextureCoords[c] = new aiVector3D[out_vertex_index];
}
vFaces.reserve(size_estimated);
// (we will also need to copy the array of indices)
while (base < in_mesh->mNumFaces) {
const unsigned int iNumIndices = in_mesh->mFaces[base].mNumIndices;
// doesn't catch degenerates but is quite fast
unsigned int iNeed = 0;
for (unsigned int v = 0; v < iNumIndices;++v) {
unsigned int index = in_mesh->mFaces[base].mIndices[v];
// check whether we do already have this vertex
if (WAS_NOT_COPIED == was_copied_to[index]) {
iNeed++;
}
}
if (out_mesh->mNumVertices + iNeed > out_vertex_index) {
// don't use this face
break;
}
vFaces.push_back(aiFace());
aiFace& rFace = vFaces.back();
// setup face type and number of indices
rFace.mNumIndices = iNumIndices;
rFace.mIndices = new unsigned int[iNumIndices];
// need to update the output primitive types
switch (rFace.mNumIndices)
{
case 1:
out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break;
case 2:
out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
break;
case 3:
out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break;
default:
out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
}
// and copy the contents of the old array, offset them by current base
for (unsigned int v = 0; v < iNumIndices;++v) {
const unsigned int index = in_mesh->mFaces[base].mIndices[v];
// check whether we do already have this vertex
if (WAS_NOT_COPIED != was_copied_to[index]) {
rFace.mIndices[v] = was_copied_to[index];
continue;
}
// copy positions
out_mesh->mVertices[out_mesh->mNumVertices] = (in_mesh->mVertices[index]);
// copy normals
if (in_mesh->HasNormals()) {
out_mesh->mNormals[out_mesh->mNumVertices] = (in_mesh->mNormals[index]);
}
// copy tangents/bi-tangents
if (in_mesh->HasTangentsAndBitangents()) {
out_mesh->mTangents[out_mesh->mNumVertices] = (in_mesh->mTangents[index]);
out_mesh->mBitangents[out_mesh->mNumVertices] = (in_mesh->mBitangents[index]);
}
// texture coordinates
for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
if (in_mesh->HasTextureCoords( c)) {
out_mesh->mTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index];
}
}
// vertex colors
for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) {
if (in_mesh->HasVertexColors( c)) {
out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index];
}
}
// check whether we have bone weights assigned to this vertex
rFace.mIndices[v] = out_mesh->mNumVertices;
if (avPerVertexWeights) {
VertexWeightTable& table = avPerVertexWeights[ out_mesh->mNumVertices ];
for (VertexWeightTable::const_iterator iter = table.begin(), end = table.end(); iter != end;++iter) {
// allocate the bone weight array if necessary and store it in the mBones field (HACK!)
BoneWeightList* weight_list = reinterpret_cast<BoneWeightList*>(out_mesh->mBones[(*iter).first]);
if (!weight_list) {
weight_list = new BoneWeightList();
out_mesh->mBones[(*iter).first] = reinterpret_cast<aiBone*>(weight_list);
}
weight_list->push_back(aiVertexWeight(out_mesh->mNumVertices,(*iter).second));
}
}
was_copied_to[index] = out_mesh->mNumVertices;
out_mesh->mNumVertices++;
}
base++;
if(out_mesh->mNumVertices == out_vertex_index) {
// break here. The face is only added if it was complete
break;
}
}
// check which bones we'll need to create for this submesh
if (in_mesh->HasBones()) {
aiBone** ppCurrent = out_mesh->mBones;
for (unsigned int k = 0; k < in_mesh->mNumBones;++k) {
// check whether the bone exists
BoneWeightList* const weight_list = reinterpret_cast<BoneWeightList*>(out_mesh->mBones[k]);
if (weight_list) {
const aiBone* const bone_in = in_mesh->mBones[k];
aiBone* const bone_out = new aiBone();
*ppCurrent++ = bone_out;
bone_out->mName = aiString(bone_in->mName);
bone_out->mOffsetMatrix =bone_in->mOffsetMatrix;
bone_out->mNumWeights = (unsigned int)weight_list->size();
bone_out->mWeights = new aiVertexWeight[bone_out->mNumWeights];
// copy the vertex weights
::memcpy(bone_out->mWeights, &(*weight_list)[0],bone_out->mNumWeights * sizeof(aiVertexWeight));
delete weight_list;
out_mesh->mNumBones++;
}
}
}
// copy the face list to the mesh
out_mesh->mFaces = new aiFace[vFaces.size()];
out_mesh->mNumFaces = (unsigned int)vFaces.size();
for (unsigned int p = 0; p < out_mesh->mNumFaces;++p) {
out_mesh->mFaces[p] = vFaces[p];
}
// add the newly created mesh to the list
source_mesh_map.push_back(std::make_pair(out_mesh,a));
if (base == in_mesh->mNumFaces) {
break;
}
}
// delete the per-vertex weight list again
delete[] avPerVertexWeights;
// now delete the old mesh data
delete in_mesh;
}

View File

@ -0,0 +1,61 @@
/*
Assimp2Json
Copyright (c) 2011, Alexander C. Gessler
Licensed under a 3-clause BSD license. See the LICENSE file for more information.
*/
#ifndef INCLUDED_MESH_SPLITTER
#define INCLUDED_MESH_SPLITTER
// ----------------------------------------------------------------------------
// Note: this is largely based on assimp's SplitLargeMeshes_Vertex process.
// it is refactored and the coding style is slightly improved, though.
// ----------------------------------------------------------------------------
#include <vector>
struct aiScene;
struct aiMesh;
struct aiNode;
// ---------------------------------------------------------------------------
/** Splits meshes of unique vertices into meshes with no more vertices than
* a given, configurable threshold value.
*/
class MeshSplitter
{
public:
void SetLimit(unsigned int l) {
LIMIT = l;
}
unsigned int GetLimit() const {
return LIMIT;
}
public:
// -------------------------------------------------------------------
/** 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);
private:
void UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
void SplitMesh (unsigned int index, aiMesh* mesh, std::vector<std::pair<aiMesh*, unsigned int> >& source_mesh_map);
public:
unsigned int LIMIT;
};
#endif // INCLUDED_MESH_SPLITTER

View File

@ -810,6 +810,14 @@ ADD_ASSIMP_IMPORTER( MMD
MMD/MMDVmdParser.h
)
ADD_ASSIMP_EXPORTER( Assjson
Assjson/cencode.c
Assjson/cencode.h
Assjson/json_exporter.cpp
Assjson/mesh_splitter.cpp
Assjson/mesh_splitter.h
)
# Workaround for issue #2406 - force problematic large file to be optimized to prevent string table overflow error
# Used -Os instead of -O2 as previous issues had mentioned, since -Os is roughly speaking -O2, excluding any
# optimizations that take up extra space. Given that the issue is a string table overflowing, -Os seemed appropriate

View File

@ -898,6 +898,7 @@ public:
: nfo(nfo)
, reader(reader)
, cur(reader.GetCurrentPos()) {
// empty
}
~chunk_guard() {
@ -905,7 +906,7 @@ public:
if(nfo.size != static_cast<unsigned int>(-1)) {
try {
reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur );
} catch (const DeadlyImportError& e ) {
} catch (const DeadlyImportError& ) {
// out of limit so correct the value
reader.IncPtr( reader.GetReadLimit() );
}
@ -913,15 +914,17 @@ public:
}
private:
const COB::ChunkInfo& nfo;
StreamReaderLE& reader;
long cur;
};
// ------------------------------------------------------------------------------------------------
void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader)
{
void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader) {
if (nullptr == reader) {
return;
}
while(1) {
std::string type;
type += reader -> GetI1()

View File

@ -772,10 +772,14 @@ aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::
for( size_t b = 0; b < pairCount; ++b, ++iit)
{
size_t jointIndex = iit->first;
size_t vertexIndex = iit->second;
ai_real weight = ReadFloat( weightsAcc, weights, vertexIndex, 0);
const size_t jointIndex = iit->first;
const size_t vertexIndex = iit->second;
ai_real weight = 1.0f;
if (!weights.mValues.empty()) {
weight = ReadFloat(weightsAcc, weights, vertexIndex, 0);
}
// one day I gonna kill that XSI Collada exporter
if( weight > 0.0f)

View File

@ -47,10 +47,6 @@ namespace Assimp {
aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh)
{
aiAnimMesh *animesh = new aiAnimMesh;
animesh->mVertices = NULL;
animesh->mNormals = NULL;
animesh->mTangents = NULL;
animesh->mBitangents = NULL;
animesh->mNumVertices = mesh->mNumVertices;
if (mesh->mVertices) {
animesh->mVertices = new aiVector3D[animesh->mNumVertices];

View File

@ -102,6 +102,7 @@ void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperti
void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
// ------------------------------------------------------------------------------------------------
// global array of all export formats which Assimp supports in its current build
@ -179,7 +180,11 @@ Exporter::ExportFormatEntry gExporters[] =
#endif
#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 )
Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ),
#endif
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
Exporter::ExportFormatEntry("json", "Plain JSON representation of the Assimp scene data structure", "json", &ExportAssimp2Json, 0)
#endif
};

View File

@ -364,7 +364,7 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){
for(size_t i= 0; i<deleteList.size();++i){
delete deleteList[i];
deleteList[i]=NULL;
deleteList[i]=nullptr;
}//for
}

View File

@ -420,11 +420,6 @@ namespace Assimp {
out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
//cameras are defined along positive x direction
/*out_camera->mPosition = cam.Position();
out_camera->mLookAt = (cam.InterestPosition() - out_camera->mPosition).Normalize();
out_camera->mUp = cam.UpVector();*/
out_camera->mPosition = aiVector3D(0.0f);
out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f);
out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
@ -667,8 +662,7 @@ namespace Assimp {
if ((v - all_ones).SquareLength() > zero_epsilon) {
return true;
}
}
else if (ok) {
} else if (ok) {
if (v.SquareLength() > zero_epsilon) {
return true;
}
@ -1253,10 +1247,10 @@ namespace Assimp {
ai_assert(count_faces);
ai_assert(count_vertices);
// mapping from output indices to DOM indexing, needed to resolve weights
// mapping from output indices to DOM indexing, needed to resolve weights or blendshapes
std::vector<unsigned int> reverseMapping;
std::map<unsigned int, unsigned int> translateIndexMap;
if (process_weights) {
if (process_weights || mesh.GetBlendShapes().size() > 0) {
reverseMapping.resize(count_vertices);
}
@ -1413,8 +1407,10 @@ namespace Assimp {
unsigned int count = 0;
const unsigned int* outIndices = mesh.ToOutputVertexIndex(index, count);
for (unsigned int k = 0; k < count; k++) {
unsigned int index = translateIndexMap[outIndices[k]];
unsigned int outIndex = outIndices[k];
if (translateIndexMap.find(outIndex) == translateIndexMap.end())
continue;
unsigned int index = translateIndexMap[outIndex];
animMesh->mVertices[index] += vertex;
if (animMesh->mNormals != nullptr) {
animMesh->mNormals[index] += normal;
@ -1428,6 +1424,15 @@ namespace Assimp {
}
}
const size_t numAnimMeshes = animMeshes.size();
if (numAnimMeshes > 0) {
out_mesh->mNumAnimMeshes = static_cast<unsigned int>(numAnimMeshes);
out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes];
for (size_t i = 0; i < numAnimMeshes; i++) {
out_mesh->mAnimMeshes[i] = animMeshes.at(i);
}
}
return static_cast<unsigned int>(meshes.size() - 1);
}
@ -1733,9 +1738,8 @@ namespace Assimp {
}
void FBXConverter::TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
const std::string& propName,
aiTextureType target, const MeshGeometry* const mesh)
{
const std::string& propName,
aiTextureType target, const MeshGeometry* const mesh) {
TextureMap::const_iterator it = textures.find(propName);
if (it == textures.end()) {
return;

View File

@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXUtil.h"
#include "FBXProperties.h"
#include "FBXImporter.h"
#include <assimp/anim.h>
#include <assimp/material.h>
#include <assimp/light.h>

View File

@ -627,7 +627,7 @@ public:
return content;
}
uint32_t ContentLength() const {
uint64_t ContentLength() const {
return contentLength;
}

View File

@ -963,7 +963,6 @@ void ParseVectorDataArray(std::vector<float>& out, const Element& el)
}
}
// ------------------------------------------------------------------------------------------------
// read an array of uints
void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el)
@ -1280,7 +1279,6 @@ float ParseTokenAsFloat(const Token& t)
return i;
}
// ------------------------------------------------------------------------------------------------
// wrapper around ParseTokenAsInt() with ParseError handling
int ParseTokenAsInt(const Token& t)
@ -1293,8 +1291,6 @@ int ParseTokenAsInt(const Token& t)
return i;
}
// ------------------------------------------------------------------------------------------------
// wrapper around ParseTokenAsInt64() with ParseError handling
int64_t ParseTokenAsInt64(const Token& t)

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -60,7 +58,7 @@ extern "C" {
*
* Cameras have a representation in the node graph and can be animated.
* An important aspect is that the camera itself is also part of the
* scenegraph. This means, any values such as the look-at vector are not
* scene-graph. This means, any values such as the look-at vector are not
* *absolute*, they're <b>relative</b> to the coordinate system defined
* by the node which corresponds to the camera. This allows for camera
* animations. For static cameras parameters like the 'look-at' or 'up' vectors

View File

@ -981,8 +981,12 @@ enum aiComponent
#define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT"
/**
/** @brief Specifies whether the assimp export shall be able to export point clouds
*
* When this flag is not defined the render data has to contain valid faces.
* Point clouds are only a collection of vertices which have nor spatial organization
* by a face and the validation process will remove them. Enabling this feature will
* switch off the flag and enable the functionality to export pure point clouds.
*/
#define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS"

View File

@ -120,6 +120,7 @@ SET( IMPORTERS
unit/utB3DImportExport.cpp
unit/utMDCImportExport.cpp
unit/utAssbinImportExport.cpp
unit/ImportExport/utAssjsonImportExport.cpp
unit/ImportExport/utCOBImportExport.cpp
unit/ImportExport/utOgreImportExport.cpp
unit/ImportExport/utQ3BSPFileImportExport.cpp
@ -165,11 +166,11 @@ SET( POST_PROCESSES
)
SOURCE_GROUP( UnitTests\\Compiler FILES unit/CCompilerTest.c )
SOURCE_GROUP( UnitTests\\Common FILES ${COMMON} )
SOURCE_GROUP( UnitTests\\Importers FILES ${IMPORTERS} )
SOURCE_GROUP( UnitTests\\Material FILES ${MATERIAL} )
SOURCE_GROUP( UnitTests\\Math FILES ${MATH} )
SOURCE_GROUP( UnitTests\\PostProcess FILES ${POST_PROCESSES})
SOURCE_GROUP( UnitTests\\Common FILES ${COMMON} )
SOURCE_GROUP( UnitTests\\ImportExport FILES ${IMPORTERS} )
SOURCE_GROUP( UnitTests\\Material FILES ${MATERIAL} )
SOURCE_GROUP( UnitTests\\Math FILES ${MATH} )
SOURCE_GROUP( UnitTests\\PostProcess FILES ${POST_PROCESSES})
add_executable( unit
../contrib/gtest/src/gtest-all.cc

Binary file not shown.

View File

@ -1,6 +1,6 @@
ply
format ascii 1.0
comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.649942190)
comment Created by Open Asset Import Library - http://assimp.sf.net (v4.1.993695325)
element vertex 8
property float x
property float y

View File

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2016, assimp team
Copyright (c) 2006-2019, assimp team
All rights reserved.
@ -39,17 +39,37 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
#pragma once
#ifndef AI_ABSTRACTIMPORTEXPORTBASE_H_INC
#define AI_ABSTRACTIMPORTEXPORTBASE_H_INC
#include "UnitTestPCH.h"
// ---------------------------------------------------------------------------
/** Abstract base class to test import and export
*/
// ---------------------------------------------------------------------------
class AbstractImportExportBase : public ::testing::Test {
public:
/// @brief The class destructor.
virtual ~AbstractImportExportBase();
virtual bool importerTest() = 0;
/// @brief The importer-test, will return true for successful import.
/// @return true for success, false for failure.
virtual bool importerTest();
/// @brief The exporter-test, will return true for successful import.
/// @return true for success, false for failure.
virtual bool exporterTest();
};
inline
bool AbstractImportExportBase::importerTest() {
return true;
}
inline
bool AbstractImportExportBase::exporterTest() {
return true;
}
#endif // AI_ABSTRACTIMPORTEXPORTBASE_H_INC

View File

@ -0,0 +1,69 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, 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.
---------------------------------------------------------------------------
*/
#include "UnitTestPCH.h"
#include "AbstractImportExportBase.h"
#include <assimp/Importer.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
using namespace Assimp;
#ifndef ASSIMP_BUILD_NO_EXPORT
class utAssjsonImportExport : public AbstractImportExportBase {
public:
bool exporterTest() override {
Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure);
Exporter exporter;
aiReturn res = exporter.Export(scene, "json", "./spider_test.json");
return aiReturn_SUCCESS == res;
}
};
TEST_F(utAssjsonImportExport, exportTest) {
EXPECT_TRUE(exporterTest());
}
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -53,8 +51,8 @@ using namespace Assimp;
class utAssbinImportExport : public AbstractImportExportBase {
public:
virtual bool importerTest() {
Assimp::Importer importer;
bool importerTest() override {
Importer importer;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure );
Exporter exporter;
@ -66,7 +64,7 @@ public:
};
TEST_F( utAssbinImportExport, exportAssbin3DFromFileTest ) {
Assimp::Importer importer;
Importer importer;
const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/spider.obj", aiProcess_ValidateDataStructure );
EXPECT_NE( nullptr, scene );
}

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -49,35 +47,42 @@ using namespace Assimp;
class FindDegeneratesProcessTest : public ::testing::Test {
public:
FindDegeneratesProcessTest()
: Test()
, mMesh( nullptr )
, mProcess( nullptr ) {
// empty
}
protected:
virtual void SetUp();
virtual void TearDown();
protected:
aiMesh* mesh;
FindDegeneratesProcess* process;
aiMesh* mMesh;
FindDegeneratesProcess* mProcess;
};
// ------------------------------------------------------------------------------------------------
void FindDegeneratesProcessTest::SetUp() {
mesh = new aiMesh();
process = new FindDegeneratesProcess();
mMesh = new aiMesh();
mProcess = new FindDegeneratesProcess();
mesh->mNumFaces = 1000;
mesh->mFaces = new aiFace[1000];
mMesh->mNumFaces = 1000;
mMesh->mFaces = new aiFace[1000];
mesh->mNumVertices = 5000*2;
mesh->mVertices = new aiVector3D[5000*2];
mMesh->mNumVertices = 5000*2;
mMesh->mVertices = new aiVector3D[5000*2];
for (unsigned int i = 0; i < 5000; ++i) {
mesh->mVertices[i] = mesh->mVertices[i+5000] = aiVector3D((float)i);
mMesh->mVertices[i] = mMesh->mVertices[i+5000] = aiVector3D((float)i);
}
mesh->mPrimitiveTypes = aiPrimitiveType_LINE | aiPrimitiveType_POINT |
mMesh->mPrimitiveTypes = aiPrimitiveType_LINE | aiPrimitiveType_POINT |
aiPrimitiveType_POLYGON | aiPrimitiveType_TRIANGLE;
unsigned int numOut = 0, numFaces = 0;
for (unsigned int i = 0; i < 1000; ++i) {
aiFace& f = mesh->mFaces[i];
aiFace& f = mMesh->mFaces[i];
f.mNumIndices = (i % 5)+1; // between 1 and 5
f.mIndices = new unsigned int[f.mNumIndices];
bool had = false;
@ -102,46 +107,46 @@ void FindDegeneratesProcessTest::SetUp() {
if (!had)
++numFaces;
}
mesh->mNumUVComponents[0] = numOut;
mesh->mNumUVComponents[1] = numFaces;
mMesh->mNumUVComponents[0] = numOut;
mMesh->mNumUVComponents[1] = numFaces;
}
void FindDegeneratesProcessTest::TearDown() {
delete mesh;
delete process;
delete mMesh;
delete mProcess;
}
TEST_F(FindDegeneratesProcessTest, testDegeneratesDetection) {
process->EnableInstantRemoval(false);
process->ExecuteOnMesh(mesh);
mProcess->EnableInstantRemoval(false);
mProcess->ExecuteOnMesh(mMesh);
unsigned int out = 0;
for (unsigned int i = 0; i < 1000; ++i) {
aiFace& f = mesh->mFaces[i];
aiFace& f = mMesh->mFaces[i];
out += f.mNumIndices;
}
EXPECT_EQ(1000U, mesh->mNumFaces);
EXPECT_EQ(10000U, mesh->mNumVertices);
EXPECT_EQ(out, mesh->mNumUVComponents[0]);
EXPECT_EQ(1000U, mMesh->mNumFaces);
EXPECT_EQ(10000U, mMesh->mNumVertices);
EXPECT_EQ(out, mMesh->mNumUVComponents[0]);
EXPECT_EQ(static_cast<unsigned int>(
aiPrimitiveType_LINE | aiPrimitiveType_POINT |
aiPrimitiveType_POLYGON | aiPrimitiveType_TRIANGLE),
mesh->mPrimitiveTypes);
mMesh->mPrimitiveTypes);
}
TEST_F(FindDegeneratesProcessTest, testDegeneratesRemoval) {
process->EnableAreaCheck(false);
process->EnableInstantRemoval(true);
process->ExecuteOnMesh(mesh);
mProcess->EnableAreaCheck(false);
mProcess->EnableInstantRemoval(true);
mProcess->ExecuteOnMesh(mMesh);
EXPECT_EQ(mesh->mNumUVComponents[1], mesh->mNumFaces);
EXPECT_EQ(mMesh->mNumUVComponents[1], mMesh->mNumFaces);
}
TEST_F(FindDegeneratesProcessTest, testDegeneratesRemovalWithAreaCheck) {
process->EnableAreaCheck(true);
process->EnableInstantRemoval(true);
process->ExecuteOnMesh(mesh);
mProcess->EnableAreaCheck(true);
mProcess->EnableInstantRemoval(true);
mProcess->ExecuteOnMesh(mMesh);
EXPECT_EQ(mesh->mNumUVComponents[1]-100, mesh->mNumFaces);
EXPECT_EQ(mMesh->mNumUVComponents[1]-100, mMesh->mNumFaces);
}

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -48,89 +46,100 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace std;
using namespace Assimp;
class FindInvalidDataProcessTest : public ::testing::Test
{
class utFindInvalidDataProcess : public ::testing::Test {
public:
utFindInvalidDataProcess()
: Test()
, mMesh(nullptr)
, mProcess(nullptr) {
// empty
}
protected:
virtual void SetUp();
virtual void TearDown();
protected:
aiMesh* pcMesh;
FindInvalidDataProcess* piProcess;
aiMesh* mMesh;
FindInvalidDataProcess* mProcess;
};
// ------------------------------------------------------------------------------------------------
void FindInvalidDataProcessTest::SetUp() {
void utFindInvalidDataProcess::SetUp() {
ASSERT_TRUE( AI_MAX_NUMBER_OF_TEXTURECOORDS >= 3);
piProcess = new FindInvalidDataProcess();
pcMesh = new aiMesh();
mProcess = new FindInvalidDataProcess();
mMesh = new aiMesh();
pcMesh->mNumVertices = 1000;
pcMesh->mVertices = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000;++i)
pcMesh->mVertices[i] = aiVector3D((float)i);
mMesh->mNumVertices = 1000;
mMesh->mVertices = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000; ++i) {
mMesh->mVertices[i] = aiVector3D((float)i);
}
pcMesh->mNormals = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000;++i)
pcMesh->mNormals[i] = aiVector3D((float)i+1);
mMesh->mNormals = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000; ++i) {
mMesh->mNormals[i] = aiVector3D((float)i + 1);
}
pcMesh->mTangents = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000;++i)
pcMesh->mTangents[i] = aiVector3D((float)i);
mMesh->mTangents = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000; ++i) {
mMesh->mTangents[i] = aiVector3D((float)i);
}
pcMesh->mBitangents = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000;++i)
pcMesh->mBitangents[i] = aiVector3D((float)i);
mMesh->mBitangents = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000; ++i) {
mMesh->mBitangents[i] = aiVector3D((float)i);
}
for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a)
{
pcMesh->mTextureCoords[a] = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000;++i)
pcMesh->mTextureCoords[a][i] = aiVector3D((float)i);
for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
mMesh->mTextureCoords[a] = new aiVector3D[1000];
for (unsigned int i = 0; i < 1000; ++i) {
mMesh->mTextureCoords[a][i] = aiVector3D((float)i);
}
}
}
// ------------------------------------------------------------------------------------------------
void FindInvalidDataProcessTest::TearDown()
{
delete piProcess;
delete pcMesh;
void utFindInvalidDataProcess::TearDown() {
delete mProcess;
delete mMesh;
}
// ------------------------------------------------------------------------------------------------
TEST_F(FindInvalidDataProcessTest, testStepNegativeResult)
{
::memset(pcMesh->mNormals,0,pcMesh->mNumVertices*sizeof(aiVector3D));
::memset(pcMesh->mBitangents,0,pcMesh->mNumVertices*sizeof(aiVector3D));
TEST_F(utFindInvalidDataProcess, testStepNegativeResult) {
::memset(mMesh->mNormals, 0, mMesh->mNumVertices*sizeof(aiVector3D) );
::memset(mMesh->mBitangents, 0, mMesh->mNumVertices*sizeof(aiVector3D) );
pcMesh->mTextureCoords[2][455] = aiVector3D( std::numeric_limits<float>::quiet_NaN() );
mMesh->mTextureCoords[2][455] = aiVector3D( std::numeric_limits<float>::quiet_NaN() );
piProcess->ProcessMesh(pcMesh);
mProcess->ProcessMesh(mMesh);
EXPECT_TRUE(NULL != pcMesh->mVertices);
EXPECT_TRUE(NULL == pcMesh->mNormals);
EXPECT_TRUE(NULL == pcMesh->mTangents);
EXPECT_TRUE(NULL == pcMesh->mBitangents);
EXPECT_TRUE(NULL != mMesh->mVertices);
EXPECT_EQ(NULL, mMesh->mNormals);
EXPECT_EQ(NULL, mMesh->mTangents);
EXPECT_EQ(NULL, mMesh->mBitangents);
for (unsigned int i = 0; i < 2;++i)
EXPECT_TRUE(NULL != pcMesh->mTextureCoords[i]);
for (unsigned int i = 0; i < 2; ++i) {
EXPECT_TRUE(NULL != mMesh->mTextureCoords[i]);
}
for (unsigned int i = 2; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
EXPECT_TRUE(NULL == pcMesh->mTextureCoords[i]);
for (unsigned int i = 2; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
EXPECT_EQ(NULL, mMesh->mTextureCoords[i]);
}
}
// ------------------------------------------------------------------------------------------------
TEST_F(FindInvalidDataProcessTest, testStepPositiveResult)
{
piProcess->ProcessMesh(pcMesh);
TEST_F(utFindInvalidDataProcess, testStepPositiveResult) {
mProcess->ProcessMesh(mMesh);
EXPECT_TRUE(NULL != pcMesh->mVertices);
EXPECT_NE(nullptr, mMesh->mVertices);
EXPECT_TRUE(NULL != pcMesh->mNormals);
EXPECT_TRUE(NULL != pcMesh->mTangents);
EXPECT_TRUE(NULL != pcMesh->mBitangents);
EXPECT_NE(nullptr, mMesh->mNormals);
EXPECT_NE(nullptr, mMesh->mTangents);
EXPECT_NE(nullptr, mMesh->mBitangents);
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
EXPECT_TRUE(NULL != pcMesh->mTextureCoords[i]);
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
EXPECT_NE(nullptr, mMesh->mTextureCoords[i]);
}
}

View File

@ -48,13 +48,21 @@ using namespace Assimp;
class utGenBoundingBoxesProcess : public ::testing::Test {
public:
utGenBoundingBoxesProcess()
: Test()
, mProcess(nullptr)
, mMesh(nullptr)
, mScene(nullptr) {
// empty
}
void SetUp() override {
mProcess = new GenBoundingBoxesProcess;
mMesh = new aiMesh();
mMesh->mNumVertices = 100;
mMesh->mVertices = new aiVector3D[100];
for (unsigned int i = 0; i < 100; ++i) {
mMesh->mVertices[i] = aiVector3D(i, i, i);
mMesh->mVertices[i] = aiVector3D((ai_real)i, (ai_real)i, (ai_real)i);
}
mScene = new aiScene();
mScene->mNumMeshes = 1;

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -49,8 +47,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace std;
using namespace Assimp;
class JoinVerticesTest : public ::testing::Test {
class utJoinVertices : public ::testing::Test {
public:
utJoinVertices()
: Test()
, piProcess(nullptr)
, pcMesh(nullptr) {
// empty
}
protected:
virtual void SetUp();
virtual void TearDown();
@ -60,8 +66,7 @@ protected:
};
// ------------------------------------------------------------------------------------------------
void JoinVerticesTest::SetUp()
{
void utJoinVertices::SetUp() {
// construct the process
piProcess = new JoinVerticesProcess();
@ -71,11 +76,9 @@ void JoinVerticesTest::SetUp()
pcMesh->mNumVertices = 900;
aiVector3D*& pv = pcMesh->mVertices = new aiVector3D[900];
for (unsigned int i = 0; i < 3;++i)
{
for (unsigned int i = 0; i < 3;++i) {
const unsigned int base = i*300;
for (unsigned int a = 0; a < 300;++a)
{
for (unsigned int a = 0; a < 300;++a) {
pv[base+a].x = pv[base+a].y = pv[base+a].z = (float)a;
}
}
@ -83,38 +86,37 @@ void JoinVerticesTest::SetUp()
// generate faces - each vertex is referenced once
pcMesh->mNumFaces = 300;
pcMesh->mFaces = new aiFace[300];
for (unsigned int i = 0,p = 0; i < 300;++i)
{
for (unsigned int i = 0,p = 0; i < 300;++i) {
aiFace& face = pcMesh->mFaces[i];
face.mIndices = new unsigned int[ face.mNumIndices = 3 ];
for (unsigned int a = 0; a < 3;++a)
for (unsigned int a = 0; a < 3; ++a) {
face.mIndices[a] = p++;
}
}
// generate extra members - set them to zero to make sure they're identical
pcMesh->mTextureCoords[0] = new aiVector3D[900];
for (unsigned int i = 0; i < 900;++i)pcMesh->mTextureCoords[0][i] = aiVector3D( 0.f );
pcMesh->mNormals = new aiVector3D[900];
for (unsigned int i = 0; i < 900;++i)pcMesh->mNormals[i] = aiVector3D( 0.f );
pcMesh->mTangents = new aiVector3D[900];
for (unsigned int i = 0; i < 900;++i)pcMesh->mTangents[i] = aiVector3D( 0.f );
pcMesh->mBitangents = new aiVector3D[900];
for (unsigned int i = 0; i < 900;++i)pcMesh->mBitangents[i] = aiVector3D( 0.f );
pcMesh->mNormals = new aiVector3D[900];
pcMesh->mTangents = new aiVector3D[900];
for (unsigned int i = 0; i < 900; ++i) {
pcMesh->mTextureCoords[0][i] = aiVector3D(0.f);
pcMesh->mNormals[i] = aiVector3D(0.f);
pcMesh->mTangents[i] = aiVector3D(0.f);
pcMesh->mBitangents[i] = aiVector3D(0.f);
}
}
// ------------------------------------------------------------------------------------------------
void JoinVerticesTest::TearDown()
{
void utJoinVertices::TearDown() {
delete this->pcMesh;
pcMesh = nullptr;
delete this->piProcess;
piProcess = nullptr;
}
// ------------------------------------------------------------------------------------------------
TEST_F(JoinVerticesTest, testProcess)
{
TEST_F(utJoinVertices, testProcess) {
// execute the step on the given data
piProcess->ProcessMesh(pcMesh,0);
@ -122,15 +124,14 @@ TEST_F(JoinVerticesTest, testProcess)
ASSERT_EQ(300U, pcMesh->mNumFaces);
ASSERT_EQ(300U, pcMesh->mNumVertices);
ASSERT_TRUE(NULL != pcMesh->mNormals);
ASSERT_TRUE(NULL != pcMesh->mTangents);
ASSERT_TRUE(NULL != pcMesh->mBitangents);
ASSERT_TRUE(NULL != pcMesh->mTextureCoords[0]);
ASSERT_TRUE( nullptr != pcMesh->mNormals);
ASSERT_TRUE( nullptr != pcMesh->mTangents);
ASSERT_TRUE( nullptr != pcMesh->mBitangents);
ASSERT_TRUE( nullptr != pcMesh->mTextureCoords[0]);
// the order doesn't care
float fSum = 0.f;
for (unsigned int i = 0; i < 300;++i)
{
for (unsigned int i = 0; i < 300; ++i) {
aiVector3D& v = pcMesh->mVertices[i];
fSum += v.x + v.y + v.z;
@ -141,4 +142,3 @@ TEST_F(JoinVerticesTest, testProcess)
}
EXPECT_EQ(150.f*299.f*3.f, fSum); // gaussian sum equation
}

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -50,83 +48,83 @@ using namespace Assimp;
class LimitBoneWeightsTest : public ::testing::Test {
public:
LimitBoneWeightsTest()
: Test()
, mProcess(nullptr)
, mMesh(nullptr) {
// empty
}
protected:
virtual void SetUp();
virtual void TearDown();
protected:
LimitBoneWeightsProcess* piProcess;
aiMesh* pcMesh;
LimitBoneWeightsProcess *mProcess;
aiMesh *mMesh;
};
// ------------------------------------------------------------------------------------------------
void LimitBoneWeightsTest::SetUp()
{
void LimitBoneWeightsTest::SetUp() {
// construct the process
this->piProcess = new LimitBoneWeightsProcess();
this->mProcess = new LimitBoneWeightsProcess();
// now need to create a nice mesh for testing purposes
this->pcMesh = new aiMesh();
this->mMesh = new aiMesh();
pcMesh->mNumVertices = 500;
pcMesh->mVertices = new aiVector3D[500]; // uninit.
pcMesh->mNumBones = 30;
pcMesh->mBones = new aiBone*[30];
mMesh->mNumVertices = 500;
mMesh->mVertices = new aiVector3D[500]; // uninit.
mMesh->mNumBones = 30;
mMesh->mBones = new aiBone*[30];
unsigned int iCur = 0;
for (unsigned int i = 0; i < 30;++i)
{
aiBone* pc = pcMesh->mBones[i] = new aiBone();
for (unsigned int i = 0; i < 30;++i) {
aiBone* pc = mMesh->mBones[i] = new aiBone();
pc->mNumWeights = 250;
pc->mWeights = new aiVertexWeight[pc->mNumWeights];
for (unsigned int qq = 0; qq < pc->mNumWeights;++qq)
{
for (unsigned int qq = 0; qq < pc->mNumWeights;++qq) {
aiVertexWeight& v = pc->mWeights[qq];
v.mVertexId = iCur++;
if (500 == iCur)iCur = 0;
if (500 == iCur) {
iCur = 0;
}
v.mWeight = 1.0f / 15; // each vertex should occur once in two bones
}
}
}
// ------------------------------------------------------------------------------------------------
void LimitBoneWeightsTest::TearDown()
{
delete pcMesh;
delete piProcess;
void LimitBoneWeightsTest::TearDown() {
delete mMesh;
delete mProcess;
}
// ------------------------------------------------------------------------------------------------
TEST_F(LimitBoneWeightsTest, testProcess)
{
TEST_F(LimitBoneWeightsTest, testProcess) {
// execute the step on the given data
piProcess->ProcessMesh(pcMesh);
mProcess->ProcessMesh(mMesh);
// check whether everything is ok ...
typedef std::vector<LimitBoneWeightsProcess::Weight> VertexWeightList;
VertexWeightList* asWeights = new VertexWeightList[pcMesh->mNumVertices];
VertexWeightList* asWeights = new VertexWeightList[mMesh->mNumVertices];
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
for (unsigned int i = 0; i < mMesh->mNumVertices; ++i) {
asWeights[i].reserve(4);
}
// sort back as per-vertex lists
for (unsigned int i = 0; i < pcMesh->mNumBones;++i)
{
aiBone& pcBone = **(pcMesh->mBones+i);
for (unsigned int q = 0; q < pcBone.mNumWeights;++q)
{
for (unsigned int i = 0; i < mMesh->mNumBones;++i) {
aiBone& pcBone = **(mMesh->mBones+i);
for (unsigned int q = 0; q < pcBone.mNumWeights;++q) {
aiVertexWeight weight = pcBone.mWeights[q];
asWeights[weight.mVertexId].push_back(LimitBoneWeightsProcess::Weight (i,weight.mWeight));
}
}
// now validate the size of the lists and check whether all weights sum to 1.0f
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
{
for (unsigned int i = 0; i < mMesh->mNumVertices;++i) {
EXPECT_LE(asWeights[i].size(), 4U);
float fSum = 0.0f;
for (VertexWeightList::const_iterator
iter = asWeights[i].begin();
iter != asWeights[i].end();++iter)
{
for (VertexWeightList::const_iterator iter = asWeights[i].begin(); iter != asWeights[i].end();++iter) {
fSum += (*iter).mWeight;
}
EXPECT_GE(fSum, 0.95F);

View File

@ -51,18 +51,24 @@ using namespace Assimp;
class PretransformVerticesTest : public ::testing::Test {
public:
PretransformVerticesTest()
: Test()
, mScene(nullptr)
, mProcess(nullptr) {
// empty
}
protected:
virtual void SetUp();
virtual void TearDown();
protected:
aiScene* scene;
PretransformVertices* process;
aiScene *mScene;
PretransformVertices *mProcess;
};
// ------------------------------------------------------------------------------------------------
void AddNodes(unsigned int num, aiNode* father, unsigned int depth)
{
void AddNodes(unsigned int num, aiNode* father, unsigned int depth) {
father->mChildren = new aiNode*[father->mNumChildren = 5];
for (unsigned int i = 0; i < 5; ++i) {
aiNode* nd = father->mChildren[i] = new aiNode();
@ -79,26 +85,26 @@ void AddNodes(unsigned int num, aiNode* father, unsigned int depth)
}
if (depth > 1) {
for (unsigned int i = 0; i < 5; ++i)
AddNodes(i, father->mChildren[i],depth-1);
for (unsigned int i = 0; i < 5; ++i) {
AddNodes(i, father->mChildren[i], depth - 1);
}
}
}
// ------------------------------------------------------------------------------------------------
void PretransformVerticesTest::SetUp()
{
scene = new aiScene();
void PretransformVerticesTest::SetUp() {
mScene = new aiScene();
// add 5 empty materials
scene->mMaterials = new aiMaterial*[scene->mNumMaterials = 5];
mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials = 5];
for (unsigned int i = 0; i < 5;++i) {
scene->mMaterials[i] = new aiMaterial();
mScene->mMaterials[i] = new aiMaterial();
}
// add 25 test meshes
scene->mMeshes = new aiMesh*[scene->mNumMeshes = 25];
mScene->mMeshes = new aiMesh*[mScene->mNumMeshes = 25];
for ( unsigned int i = 0; i < 25; ++i) {
aiMesh* mesh = scene->mMeshes[ i ] = new aiMesh();
aiMesh* mesh = mScene->mMeshes[ i ] = new aiMesh();
mesh->mPrimitiveTypes = aiPrimitiveType_POINT;
mesh->mFaces = new aiFace[ mesh->mNumFaces = 10+i ];
@ -124,36 +130,33 @@ void PretransformVerticesTest::SetUp()
}
// construct some nodes (1+25)
scene->mRootNode = new aiNode();
scene->mRootNode->mName.Set("Root");
AddNodes(0,scene->mRootNode,2);
mScene->mRootNode = new aiNode();
mScene->mRootNode->mName.Set("Root");
AddNodes(0, mScene->mRootNode, 2);
process = new PretransformVertices();
mProcess = new PretransformVertices();
}
// ------------------------------------------------------------------------------------------------
void PretransformVerticesTest::TearDown()
{
delete scene;
delete process;
void PretransformVerticesTest::TearDown() {
delete mScene;
delete mProcess;
}
// ------------------------------------------------------------------------------------------------
TEST_F(PretransformVerticesTest, testProcessCollapseHierarchy)
{
process->KeepHierarchy(false);
process->Execute(scene);
TEST_F(PretransformVerticesTest, testProcessCollapseHierarchy) {
mProcess->KeepHierarchy(false);
mProcess->Execute(mScene);
EXPECT_EQ(5U, scene->mNumMaterials);
EXPECT_EQ(10U, scene->mNumMeshes); // every second mesh has normals
EXPECT_EQ(5U, mScene->mNumMaterials);
EXPECT_EQ(10U, mScene->mNumMeshes); // every second mesh has normals
}
// ------------------------------------------------------------------------------------------------
TEST_F(PretransformVerticesTest, testProcessKeepHierarchy)
{
process->KeepHierarchy(true);
process->Execute(scene);
TEST_F(PretransformVerticesTest, testProcessKeepHierarchy) {
mProcess->KeepHierarchy(true);
mProcess->Execute(mScene);
EXPECT_EQ(5U, scene->mNumMaterials);
EXPECT_EQ(49U, scene->mNumMeshes); // see note on mesh 12 above
EXPECT_EQ(5U, mScene->mNumMaterials);
EXPECT_EQ(49U, mScene->mNumMeshes); // see note on mesh 12 above
}

View File

@ -50,67 +50,67 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace std;
using namespace Assimp;
class ScenePreprocessorTest : public ::testing::Test
{
class ScenePreprocessorTest : public ::testing::Test {
public:
ScenePreprocessorTest()
: Test()
, mScenePreprocessor(nullptr)
, mScene(nullptr) {
// empty
}
protected:
virtual void SetUp();
virtual void TearDown();
protected:
void CheckIfOnly(aiMesh* p, unsigned int num, unsigned flag);
void ProcessAnimation(aiAnimation* anim) { mScenePreprocessor->ProcessAnimation(anim); }
void ProcessMesh(aiMesh* mesh) { mScenePreprocessor->ProcessMesh(mesh); }
void ProcessAnimation(aiAnimation* anim) { pp->ProcessAnimation(anim); }
void ProcessMesh(aiMesh* mesh) { pp->ProcessMesh(mesh); }
ScenePreprocessor* pp;
aiScene* scene;
private:
ScenePreprocessor *mScenePreprocessor;
aiScene *mScene;
};
// ------------------------------------------------------------------------------------------------
void ScenePreprocessorTest::SetUp()
{
void ScenePreprocessorTest::SetUp() {
// setup a dummy scene with a single node
scene = new aiScene();
scene->mRootNode = new aiNode();
scene->mRootNode->mName.Set("<test>");
mScene = new aiScene();
mScene->mRootNode = new aiNode();
mScene->mRootNode->mName.Set("<test>");
// add some translation
scene->mRootNode->mTransformation.a4 = 1.f;
scene->mRootNode->mTransformation.b4 = 2.f;
scene->mRootNode->mTransformation.c4 = 3.f;
mScene->mRootNode->mTransformation.a4 = 1.f;
mScene->mRootNode->mTransformation.b4 = 2.f;
mScene->mRootNode->mTransformation.c4 = 3.f;
// and allocate a ScenePreprocessor to operate on the scene
pp = new ScenePreprocessor(scene);
mScenePreprocessor = new ScenePreprocessor(mScene);
}
// ------------------------------------------------------------------------------------------------
void ScenePreprocessorTest::TearDown()
{
delete pp;
delete scene;
void ScenePreprocessorTest::TearDown() {
delete mScenePreprocessor;
delete mScene;
}
// ------------------------------------------------------------------------------------------------
// Check whether ProcessMesh() returns flag for a mesh that consist of primitives with num indices
void ScenePreprocessorTest::CheckIfOnly(aiMesh* p, unsigned int num, unsigned int flag)
{
void ScenePreprocessorTest::CheckIfOnly(aiMesh* p, unsigned int num, unsigned int flag) {
// Triangles only
for (unsigned i = 0; i < p->mNumFaces;++i) {
p->mFaces[i].mNumIndices = num;
}
pp->ProcessMesh(p);
mScenePreprocessor->ProcessMesh(p);
EXPECT_EQ(flag, p->mPrimitiveTypes);
p->mPrimitiveTypes = 0;
}
// ------------------------------------------------------------------------------------------------
// Check whether a mesh is preprocessed correctly. Case 1: The mesh needs preprocessing
TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos)
{
aiMesh* p = new aiMesh();
TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos) {
aiMesh* p = new aiMesh;
p->mNumFaces = 100;
p->mFaces = new aiFace[p->mNumFaces];
@ -145,9 +145,8 @@ TEST_F(ScenePreprocessorTest, testMeshPreprocessingPos)
// ------------------------------------------------------------------------------------------------
// Check whether a mesh is preprocessed correctly. Case 1: The mesh doesn't need preprocessing
TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg)
{
aiMesh* p = new aiMesh();
TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg) {
aiMesh* p = new aiMesh;
p->mPrimitiveTypes = aiPrimitiveType_TRIANGLE|aiPrimitiveType_POLYGON;
ProcessMesh(p);
@ -160,8 +159,7 @@ TEST_F(ScenePreprocessorTest, testMeshPreprocessingNeg)
// ------------------------------------------------------------------------------------------------
// Make a dummy animation with a single channel, '<test>'
aiAnimation* MakeDummyAnimation()
{
aiAnimation* MakeDummyAnimation() {
aiAnimation* p = new aiAnimation();
p->mNumChannels = 1;
p->mChannels = new aiNodeAnim*[1];
@ -172,8 +170,7 @@ aiAnimation* MakeDummyAnimation()
// ------------------------------------------------------------------------------------------------
// Check whether an anim is preprocessed correctly. Case 1: The anim needs preprocessing
TEST_F(ScenePreprocessorTest, testAnimationPreprocessingPos)
{
TEST_F(ScenePreprocessorTest, testAnimationPreprocessingPos) {
aiAnimation* p = MakeDummyAnimation();
aiNodeAnim* anim = p->mChannels[0];

View File

@ -52,12 +52,20 @@ using namespace Assimp;
class SortByPTypeProcessTest : public ::testing::Test {
public:
SortByPTypeProcessTest()
: Test()
, mProcess1(nullptr)
, mScene(nullptr) {
// empty
}
protected:
virtual void SetUp();
virtual void TearDown();
protected:
SortByPTypeProcess* process1;
aiScene* scene;
SortByPTypeProcess* mProcess1;
aiScene* mScene;
};
// ------------------------------------------------------------------------------------------------
@ -75,8 +83,7 @@ static unsigned int num[10][4] = {
};
// ------------------------------------------------------------------------------------------------
static unsigned int result[10] =
{
static unsigned int result[10] = {
aiPrimitiveType_POLYGON,
aiPrimitiveType_TRIANGLE,
aiPrimitiveType_LINE,
@ -90,19 +97,16 @@ static unsigned int result[10] =
};
// ------------------------------------------------------------------------------------------------
void SortByPTypeProcessTest::SetUp()
{
// process0 = new DeterminePTypeHelperProcess();
process1 = new SortByPTypeProcess();
scene = new aiScene();
void SortByPTypeProcessTest::SetUp() {
mProcess1 = new SortByPTypeProcess();
mScene = new aiScene();
scene->mNumMeshes = 10;
scene->mMeshes = new aiMesh*[10];
mScene->mNumMeshes = 10;
mScene->mMeshes = new aiMesh*[10];
bool five = false;
for (unsigned int i = 0; i < 10; ++i)
{
aiMesh* mesh = scene->mMeshes[i] = new aiMesh();
for (unsigned int i = 0; i < 10; ++i) {
aiMesh* mesh = mScene->mMeshes[i] = new aiMesh();
mesh->mNumFaces = 1000;
aiFace* faces = mesh->mFaces = new aiFace[1000];
aiVector3D* pv = mesh->mVertices = new aiVector3D[mesh->mNumFaces*5];
@ -115,27 +119,24 @@ void SortByPTypeProcessTest::SetUp()
unsigned int remaining[4] = {num[i][0],num[i][1],num[i][2],num[i][3]};
unsigned int n = 0;
for (unsigned int m = 0; m < 1000; ++m)
{
for (unsigned int m = 0; m < 1000; ++m) {
unsigned int idx = m % 4;
while (true)
{
if (!remaining[idx])
{
if (4 == ++idx)idx = 0;
while (true) {
if (!remaining[idx]) {
if (4 == ++idx) {
idx = 0;
}
continue;
}
break;
}
faces->mNumIndices = idx+1;
if (4 == faces->mNumIndices)
{
if (4 == faces->mNumIndices) {
if(five)++faces->mNumIndices;
five = !five;
}
faces->mIndices = new unsigned int[faces->mNumIndices];
for (unsigned int q = 0; q <faces->mNumIndices;++q,++n)
{
for (unsigned int q = 0; q <faces->mNumIndices;++q,++n) {
faces->mIndices[q] = n;
float f = (float)remaining[idx];
@ -152,12 +153,11 @@ void SortByPTypeProcessTest::SetUp()
mesh->mNumVertices = n;
}
scene->mRootNode = new aiNode();
scene->mRootNode->mNumChildren = 5;
scene->mRootNode->mChildren = new aiNode*[5];
for (unsigned int i = 0; i< 5;++i )
{
aiNode* node = scene->mRootNode->mChildren[i] = new aiNode();
mScene->mRootNode = new aiNode();
mScene->mRootNode->mNumChildren = 5;
mScene->mRootNode->mChildren = new aiNode*[5];
for (unsigned int i = 0; i< 5;++i ) {
aiNode* node = mScene->mRootNode->mChildren[i] = new aiNode();
node->mNumMeshes = 2;
node->mMeshes = new unsigned int[2];
node->mMeshes[0] = (i<<1u);
@ -166,48 +166,28 @@ void SortByPTypeProcessTest::SetUp()
}
// ------------------------------------------------------------------------------------------------
void SortByPTypeProcessTest::TearDown()
{
//delete process0;
delete process1;
delete scene;
void SortByPTypeProcessTest::TearDown() {
delete mProcess1;
delete mScene;
}
// ------------------------------------------------------------------------------------------------
//TEST_F(SortByPTypeProcessTest, DeterminePTypeStep()
//{
// process0->Execute(scene);
//
// for (unsigned int i = 0; i < 10; ++i)
// {
// aiMesh* mesh = scene->mMeshes[i];
// EXPECT_TRUE(mesh->mPrimitiveTypes == result[i]);
// }
//}
// ------------------------------------------------------------------------------------------------
TEST_F(SortByPTypeProcessTest, SortByPTypeStep)
{
// process0->Execute(scene);
// and another small test for ScenePreprocessor
ScenePreprocessor s(scene);
TEST_F(SortByPTypeProcessTest, SortByPTypeStep) {
ScenePreprocessor s(mScene);
s.ProcessScene();
for (unsigned int m = 0; m< 10;++m)
EXPECT_EQ(result[m], scene->mMeshes[m]->mPrimitiveTypes);
EXPECT_EQ(result[m], mScene->mMeshes[m]->mPrimitiveTypes);
process1->Execute(scene);
mProcess1->Execute(mScene);
unsigned int idx = 0;
for (unsigned int m = 0,real = 0; m< 10;++m)
{
for (unsigned int n = 0; n < 4;++n)
{
for (unsigned int m = 0,real = 0; m< 10;++m) {
for (unsigned int n = 0; n < 4;++n) {
if ((idx = num[m][n]))
{
EXPECT_TRUE(real < scene->mNumMeshes);
EXPECT_TRUE(real < mScene->mNumMeshes);
aiMesh* mesh = scene->mMeshes[real];
aiMesh* mesh = mScene->mMeshes[real];
EXPECT_TRUE(NULL != mesh);
EXPECT_EQ(AI_PRIMITIVE_TYPE_FOR_N_INDICES(n+1), mesh->mPrimitiveTypes);
@ -218,8 +198,7 @@ TEST_F(SortByPTypeProcessTest, SortByPTypeStep)
EXPECT_TRUE(NULL != mesh->mTextureCoords[0]);
EXPECT_TRUE(mesh->mNumFaces == idx);
for (unsigned int f = 0; f < mesh->mNumFaces;++f)
{
for (unsigned int f = 0; f < mesh->mNumFaces;++f) {
aiFace& face = mesh->mFaces[f];
EXPECT_TRUE(face.mNumIndices == (n+1) || (3 == n && face.mNumIndices > 3));
}
@ -228,4 +207,3 @@ TEST_F(SortByPTypeProcessTest, SortByPTypeStep)
}
}
}